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

View file

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