Merge pull request #8613 from hrydgard/iso-filesystem-lazy

"Lazy load" directory information in ISOFilesystem
This commit is contained in:
Unknown W. Brackets 2016-02-28 14:31:20 -08:00
commit f9ec9d53d0
2 changed files with 97 additions and 151 deletions

View File

@ -28,7 +28,6 @@
#include "Core/MemMap.h"
#include "Core/Reporting.h"
const int sectorSize = 2048;
bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize) {
@ -63,8 +62,7 @@ bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize) {
#pragma pack(push)
#pragma pack(1)
struct DirectoryEntry
{
struct DirectoryEntry {
u8 size;
u8 sectorsInExtendedRecord;
u32_le firstDataSectorLE; // LBA
@ -114,14 +112,13 @@ struct DirectoryEntry
}
#endif
};
struct DirectorySector
{
struct DirectorySector {
DirectoryEntry entry;
char space[2048-sizeof(DirectoryEntry)];
};
struct VolDescriptor
{
struct VolDescriptor {
char type;
char cd001[6];
char version;
@ -167,13 +164,10 @@ struct VolDescriptor
#pragma pack(pop)
ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice, std::string _restrictPath)
{
if (!_restrictPath.empty())
{
ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice, std::string _restrictPath) {
if (!_restrictPath.empty()) {
size_t pos = _restrictPath.find_first_not_of('/');
while (pos != _restrictPath.npos)
{
while (pos != _restrictPath.npos) {
size_t endPos = _restrictPath.find_first_of('/', pos);
if (endPos == _restrictPath.npos)
endPos = _restrictPath.length();
@ -202,34 +196,34 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic
treeroot->size = 0;
treeroot->flags = 0;
treeroot->parent = NULL;
treeroot->valid = false;
if (memcmp(desc.cd001, "CD001", 5)) {
ERROR_LOG(FILESYS, "ISO looks bogus? Giving up...");
return;
}
u32 rootSector = desc.root.firstDataSector();
u32 rootSize = desc.root.dataLength();
ReadDirectory(rootSector, rootSize, treeroot, 0);
treeroot->startsector = desc.root.firstDataSector();
treeroot->dirsize = desc.root.dataLength();
treeroot->level = 0;
}
ISOFileSystem::~ISOFileSystem()
{
ISOFileSystem::~ISOFileSystem() {
delete blockDevice;
delete treeroot;
}
void ISOFileSystem::ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root, size_t level)
{
for (u32 secnum = startsector, endsector = dirsize/2048 + startsector; secnum < endsector; ++secnum)
{
void ISOFileSystem::ReadDirectory(TreeEntry *root) {
for (u32 secnum = root->startsector, endsector = root->startsector + root->dirsize /2048; secnum < endsector; ++secnum) {
u8 theSector[2048];
blockDevice->ReadBlock(secnum, theSector);
if (!blockDevice->ReadBlock(secnum, theSector)) {
ERROR_LOG(FILESYS, "Error reading block for directory %s - skipping", root->name.c_str());
root->valid = true; // Prevents re-reading
return;
}
lastReadBlock_ = secnum;
for (int offset = 0; offset < 2048; )
{
for (int offset = 0; offset < 2048; ) {
DirectoryEntry &dir = *(DirectoryEntry *)&theSector[offset];
u8 sz = theSector[offset];
@ -238,8 +232,7 @@ void ISOFileSystem::ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root,
break;
const int IDENTIFIER_OFFSET = 33;
if (offset + IDENTIFIER_OFFSET + dir.identifierLength > 2048)
{
if (offset + IDENTIFIER_OFFSET + dir.identifierLength > 2048) {
ERROR_LOG(FILESYS, "Directory entry crosses sectors, corrupt iso?");
return;
}
@ -249,60 +242,51 @@ void ISOFileSystem::ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root,
bool isFile = (dir.flags & 2) ? false : true;
bool relative;
TreeEntry *e = new TreeEntry();
if (dir.identifierLength == 1 && (dir.firstIdChar == '\x00' || dir.firstIdChar == '.'))
{
e->name = ".";
TreeEntry *entry = new TreeEntry();
if (dir.identifierLength == 1 && (dir.firstIdChar == '\x00' || dir.firstIdChar == '.')) {
entry->name = ".";
relative = true;
}
else if (dir.identifierLength == 1 && dir.firstIdChar == '\x01')
{
e->name = "..";
} else if (dir.identifierLength == 1 && dir.firstIdChar == '\x01') {
entry->name = "..";
relative = true;
}
else
{
e->name = std::string((const char *)&dir.firstIdChar, dir.identifierLength);
} else {
entry->name = std::string((const char *)&dir.firstIdChar, dir.identifierLength);
relative = false;
}
e->size = dir.dataLength();
e->startingPosition = dir.firstDataSector() * 2048;
e->isDirectory = !isFile;
e->flags = dir.flags;
e->parent = root;
entry->size = dir.dataLength();
entry->startingPosition = dir.firstDataSector() * 2048;
entry->isDirectory = !isFile;
entry->flags = dir.flags;
entry->parent = root;
entry->startsector = dir.firstDataSector();
entry->dirsize = dir.dataLength();
entry->level = root->level + 1;
// Let's not excessively spam the log - I commented this line out.
//DEBUG_LOG(FILESYS, "%s: %s %08x %08x %i", e->isDirectory?"D":"F", e->name.c_str(), dir.firstDataSectorLE, e->startingPosition, e->startingPosition);
if (e->isDirectory && !relative)
{
if (dir.firstDataSector() == startsector)
{
ERROR_LOG(FILESYS, "WARNING: Appear to have a recursive file system, breaking recursion");
}
else
{
if (entry->isDirectory && !relative) {
if (entry->startsector == root->startsector) {
ERROR_LOG(FILESYS, "WARNING: Appear to have a recursive file system, breaking recursion. Probably corrupt ISO.");
} else {
bool doRecurse = true;
if (!restrictTree.empty())
doRecurse = level < restrictTree.size() && restrictTree[level] == e->name;
doRecurse = root->level < restrictTree.size() && restrictTree[root->level] == entry->name;
if (doRecurse) {
ReadDirectory(dir.firstDataSector(), dir.dataLength(), e, level + 1);
} else {
if (!doRecurse) {
// The entry is not kept, must free it.
delete e;
delete entry;
continue;
}
}
}
root->children.push_back(e);
root->children.push_back(entry);
}
}
root->valid = true;
}
ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(const std::string &path, bool catchError)
{
ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(const std::string &path, bool catchError) {
const size_t pathLength = path.length();
if (pathLength == 0) {
@ -323,44 +307,39 @@ ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(const std::string &path, bo
if (pathLength <= pathIndex)
return treeroot;
TreeEntry *e = treeroot;
while (true)
{
TreeEntry *ne = nullptr;
TreeEntry *entry = treeroot;
while (true) {
if (!entry->valid) {
ReadDirectory(entry);
}
TreeEntry *nextEntry = nullptr;
std::string name = "";
if (pathLength > pathIndex)
{
if (pathLength > pathIndex) {
size_t nextSlashIndex = path.find_first_of('/', pathIndex);
if (nextSlashIndex == std::string::npos)
nextSlashIndex = pathLength;
const std::string firstPathComponent = path.substr(pathIndex, nextSlashIndex - pathIndex);
for (size_t i = 0; i < e->children.size(); i++)
{
const std::string &n = e->children[i]->name;
if (firstPathComponent == n)
{
for (size_t i = 0; i < entry->children.size(); i++) {
const std::string &n = entry->children[i]->name;
if (firstPathComponent == n) {
//yay we got it
ne = e->children[i];
nextEntry = entry->children[i];
name = n;
break;
}
}
}
if (ne)
{
e = ne;
if (nextEntry) {
entry = nextEntry;
pathIndex += name.length();
if (pathIndex < pathLength && path[pathIndex] == '/')
++pathIndex;
if (pathLength <= pathIndex)
return e;
}
else
{
return entry;
} else {
if (catchError)
ERROR_LOG(FILESYS,"File %s not found", path.c_str());
@ -369,28 +348,16 @@ ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(const std::string &path, bo
}
}
u32 ISOFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename)
{
// LBN unittest
/*
u32 a, b;
if (parseLBN("/sce_lbn0x307aa_size0xefffe000", &a, &b)) {
ERROR_LOG(FILESYS, "lbn: %08x %08x", a, b);
} else {
ERROR_LOG(FILESYS, "faillbn: %08x %08x", a, b);
}*/
u32 ISOFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) {
OpenFileEntry entry;
entry.isRawSector = false;
entry.isBlockSectorMode = false;
if (filename.compare(0,8,"/sce_lbn") == 0)
{
if (filename.compare(0, 8, "/sce_lbn") == 0) {
// Raw sector read.
u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
parseLBN(filename, &sectorStart, &readSize);
if (sectorStart > blockDevice->GetNumBlocks())
{
if (sectorStart > blockDevice->GetNumBlocks()) {
WARN_LOG(FILESYS, "Unable to open raw sector, out of range: %s, sector %08x, max %08x", filename.c_str(), sectorStart, blockDevice->GetNumBlocks());
return 0;
}
@ -408,15 +375,14 @@ u32 ISOFileSystem::OpenFile(std::string filename, FileAccess access, const char
entry.openSize = readSize;
// when open as "umd1:/sce_lbn0x0_size0x6B49D200", that mean open umd1 as a block device.
// the param in sceIoLseek and sceIoRead is lba mode. we must mark it.
if(strncmp(devicename, "umd0:", 5)==0 || strncmp(devicename, "umd1:", 5)==0)
if (strncmp(devicename, "umd0:", 5)==0 || strncmp(devicename, "umd1:", 5)==0)
entry.isBlockSectorMode = true;
entries[newHandle] = entry;
return newHandle;
}
if (access & FILEACCESS_WRITE)
{
if (access & FILEACCESS_WRITE) {
ERROR_LOG(FILESYS, "Can't open file %s with write access on an ISO partition", filename.c_str());
return 0;
}
@ -437,24 +403,19 @@ u32 ISOFileSystem::OpenFile(std::string filename, FileAccess access, const char
return newHandle;
}
void ISOFileSystem::CloseFile(u32 handle)
{
void ISOFileSystem::CloseFile(u32 handle) {
EntryMap::iterator iter = entries.find(handle);
if (iter != entries.end())
{
if (iter != entries.end()) {
//CloseHandle((*iter).second.hFile);
hAlloc->FreeHandle(handle);
entries.erase(iter);
}
else
{
} else {
//This shouldn't happen...
ERROR_LOG(FILESYS, "Hey, what are you doing? Closing non-open files?");
}
}
bool ISOFileSystem::OwnsHandle(u32 handle)
{
bool ISOFileSystem::OwnsHandle(u32 handle) {
EntryMap::iterator iter = entries.find(handle);
return (iter != entries.end());
}
@ -620,23 +581,19 @@ size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec)
}
}
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
{
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) {
ERROR_LOG(FILESYS, "Hey, what are you doing? You can't write to an ISO!");
return 0;
}
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec)
{
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) {
ERROR_LOG(FILESYS, "Hey, what are you doing? You can't write to an ISO!");
return 0;
}
size_t ISOFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
{
size_t ISOFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {
EntryMap::iterator iter = entries.find(handle);
if (iter != entries.end())
{
if (iter != entries.end()) {
OpenFileEntry &e = iter->second;
switch (type)
{
@ -654,19 +611,15 @@ size_t ISOFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
break;
}
return (size_t)e.seekPos;
}
else
{
} else {
//This shouldn't happen...
ERROR_LOG(FILESYS, "Hey, what are you doing? Seeking in non-open files?");
return 0;
}
}
PSPFileInfo ISOFileSystem::GetFileInfo(std::string filename)
{
if (filename.compare(0,8,"/sce_lbn") == 0)
{
PSPFileInfo ISOFileSystem::GetFileInfo(std::string filename) {
if (filename.compare(0,8,"/sce_lbn") == 0) {
u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
parseLBN(filename, &sectorStart, &readSize);
@ -682,13 +635,10 @@ PSPFileInfo ISOFileSystem::GetFileInfo(std::string filename)
TreeEntry *entry = GetFromPath(filename, false);
PSPFileInfo x;
if (!entry)
{
if (!entry) {
x.size = 0;
x.exists = false;
}
else
{
} else {
x.name = entry->name;
x.access = FILEACCESS_READ;
x.size = entry->size;
@ -700,8 +650,7 @@ PSPFileInfo ISOFileSystem::GetFileInfo(std::string filename)
return x;
}
std::vector<PSPFileInfo> ISOFileSystem::GetDirListing(std::string path)
{
std::vector<PSPFileInfo> ISOFileSystem::GetDirListing(std::string path) {
std::vector<PSPFileInfo> myVector;
TreeEntry *entry = GetFromPath(path);
if (! entry)
@ -710,8 +659,7 @@ std::vector<PSPFileInfo> ISOFileSystem::GetDirListing(std::string path)
const std::string dot(".");
const std::string dotdot("..");
for (size_t i = 0; i < entry->children.size(); i++)
{
for (size_t i = 0; i < entry->children.size(); i++) {
TreeEntry *e = entry->children[i];
// do not include the relative entries in the list
@ -730,15 +678,13 @@ std::vector<PSPFileInfo> ISOFileSystem::GetDirListing(std::string path)
return myVector;
}
std::string ISOFileSystem::EntryFullPath(TreeEntry *e)
{
std::string ISOFileSystem::EntryFullPath(TreeEntry *e) {
if (e == &entireISO)
return "";
size_t fullLen = 0;
TreeEntry *cur = e;
while (cur != NULL && cur != treeroot)
{
while (cur != NULL && cur != treeroot) {
// For the "/".
fullLen += 1 + cur->name.size();
cur = cur->parent;
@ -765,8 +711,7 @@ ISOFileSystem::TreeEntry::~TreeEntry() {
children.clear();
}
void ISOFileSystem::DoState(PointerWrap &p)
{
void ISOFileSystem::DoState(PointerWrap &p) {
auto s = p.Section("ISOFileSystem", 1, 2);
if (!s)
return;
@ -774,11 +719,9 @@ void ISOFileSystem::DoState(PointerWrap &p)
int n = (int) entries.size();
p.Do(n);
if (p.mode == p.MODE_READ)
{
if (p.mode == p.MODE_READ) {
entries.clear();
for (int i = 0; i < n; ++i)
{
for (int i = 0; i < n; ++i) {
u32 fd = 0;
OpenFileEntry of;
@ -801,11 +744,8 @@ void ISOFileSystem::DoState(PointerWrap &p)
entries[fd] = of;
}
}
else
{
for (EntryMap::iterator it = entries.begin(), end = entries.end(); it != end; ++it)
{
} else {
for (EntryMap::iterator it = entries.begin(), end = entries.end(); it != end; ++it) {
OpenFileEntry &of = it->second;
p.Do(it->first);
p.Do(of.seekPos);

View File

@ -56,7 +56,7 @@ public:
private:
struct TreeEntry {
TreeEntry(){}
TreeEntry() : flags(0), valid(false) {}
~TreeEntry();
std::string name;
@ -65,7 +65,13 @@ private:
s64 size;
bool isDirectory;
u32 startsector;
u32 dirsize;
int level;
TreeEntry *parent;
bool valid;
std::vector<TreeEntry *> children;
};
@ -90,7 +96,7 @@ private:
// Don't use this in the emu, not savestated.
std::vector<std::string> restrictTree;
void ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root, size_t level);
void ReadDirectory(TreeEntry *root);
TreeEntry *GetFromPath(const std::string &path, bool catchError = true);
std::string EntryFullPath(TreeEntry *e);
};