2012-11-01 16:19:01 +01:00
|
|
|
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
2012-11-04 23:01:49 +01:00
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official git repository and contact information can be found at
|
|
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
|
2013-12-29 23:28:31 +01:00
|
|
|
#include <cstring>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <ctype.h>
|
2014-11-02 19:50:26 -08:00
|
|
|
#include <algorithm>
|
2013-12-29 23:28:31 +01:00
|
|
|
|
2013-12-30 10:17:11 +01:00
|
|
|
#include "Common/CommonTypes.h"
|
2020-08-10 00:12:51 -07:00
|
|
|
#include "Common/Serialize/Serializer.h"
|
|
|
|
#include "Common/Serialize/SerializeFuncs.h"
|
2013-05-26 20:23:09 -07:00
|
|
|
#include "Core/FileSystems/ISOFileSystem.h"
|
2013-12-26 14:47:17 -08:00
|
|
|
#include "Core/HLE/sceKernel.h"
|
|
|
|
#include "Core/MemMap.h"
|
2013-05-26 20:23:09 -07:00
|
|
|
#include "Core/Reporting.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
const int sectorSize = 2048;
|
|
|
|
|
2015-09-12 11:21:54 +02:00
|
|
|
bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize) {
|
2013-06-23 16:15:23 -07:00
|
|
|
// The format of this is: "/sce_lbn" "0x"? HEX* ANY* "_size" "0x"? HEX* ANY*
|
|
|
|
// That means that "/sce_lbn/_size1/" is perfectly valid.
|
|
|
|
// Most commonly, it looks like /sce_lbn0x10_size0x100 or /sce_lbn10_size100 (always hex.)
|
|
|
|
|
|
|
|
// If it doesn't starts with /sce_lbn or doesn't have _size, look for a file instead.
|
2013-01-09 00:10:52 -08:00
|
|
|
if (filename.compare(0, sizeof("/sce_lbn") - 1, "/sce_lbn") != 0)
|
2012-11-09 12:32:35 +01:00
|
|
|
return false;
|
2013-06-23 16:15:23 -07:00
|
|
|
size_t size_pos = filename.find("_size");
|
|
|
|
if (size_pos == filename.npos)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// TODO: Return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT when >= 32 long but passes above checks.
|
|
|
|
if (filename.size() >= 32)
|
|
|
|
return false;
|
2013-01-09 00:10:52 -08:00
|
|
|
|
|
|
|
const char *filename_c = filename.c_str();
|
2013-06-23 16:15:23 -07:00
|
|
|
size_t pos = strlen("/sce_lbn");
|
2013-01-09 00:10:52 -08:00
|
|
|
|
2013-06-23 16:15:23 -07:00
|
|
|
if (sscanf(filename_c + pos, "%x", sectorStart) != 1)
|
|
|
|
*sectorStart = 0;
|
2013-01-09 00:10:52 -08:00
|
|
|
|
2013-06-23 16:15:23 -07:00
|
|
|
pos = size_pos + strlen("_size");
|
2013-01-09 00:10:52 -08:00
|
|
|
|
2013-06-23 16:15:23 -07:00
|
|
|
if (sscanf(filename_c + pos, "%x", readSize) != 1)
|
|
|
|
*readSize = 0;
|
2013-01-09 00:10:52 -08:00
|
|
|
|
2012-11-09 12:32:35 +01:00
|
|
|
return true;
|
2012-11-06 18:32:28 +01:00
|
|
|
}
|
|
|
|
|
2021-01-30 12:00:40 -08:00
|
|
|
#pragma pack(push)
|
|
|
|
#pragma pack(1)
|
|
|
|
struct u32_le_be_pair {
|
|
|
|
u8 valueLE[4];
|
|
|
|
u8 valueBE[4];
|
|
|
|
operator u32() const {
|
|
|
|
return valueLE[0] + (valueLE[1] << 8) + (valueLE[2] << 16) + (valueLE[3] << 24);
|
|
|
|
}
|
2021-01-30 00:23:17 +00:00
|
|
|
};
|
|
|
|
|
2021-01-30 12:00:40 -08:00
|
|
|
struct u16_le_be_pair {
|
|
|
|
u8 valueLE[2];
|
|
|
|
u8 valueBE[2];
|
|
|
|
operator u16() const {
|
|
|
|
return valueLE[0] + (valueLE[1] << 8);
|
|
|
|
}
|
2021-01-30 00:23:17 +00:00
|
|
|
};
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
struct DirectoryEntry {
|
2012-11-01 16:19:01 +01:00
|
|
|
u8 size;
|
|
|
|
u8 sectorsInExtendedRecord;
|
2021-01-30 12:00:40 -08:00
|
|
|
u32_le_be_pair firstDataSector; // LBA
|
|
|
|
u32_le_be_pair dataLength; // Size
|
2012-11-01 16:19:01 +01:00
|
|
|
u8 years;
|
|
|
|
u8 month;
|
|
|
|
u8 day;
|
|
|
|
u8 hour;
|
|
|
|
u8 minute;
|
|
|
|
u8 second;
|
|
|
|
u8 offsetFromGMT;
|
2015-09-07 16:35:23 -07:00
|
|
|
u8 flags; // 2 = directory
|
2012-11-01 16:19:01 +01:00
|
|
|
u8 fileUnitSize;
|
|
|
|
u8 interleaveGap;
|
2021-01-30 12:00:40 -08:00
|
|
|
u16_le_be_pair volSeqNumber;
|
2015-09-07 16:35:23 -07:00
|
|
|
u8 identifierLength; //identifier comes right after
|
2012-11-01 16:19:01 +01:00
|
|
|
u8 firstIdChar;
|
|
|
|
};
|
2016-02-28 11:39:57 +01:00
|
|
|
|
|
|
|
struct DirectorySector {
|
2012-11-01 16:19:01 +01:00
|
|
|
DirectoryEntry entry;
|
|
|
|
char space[2048-sizeof(DirectoryEntry)];
|
|
|
|
};
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
struct VolDescriptor {
|
2012-11-01 16:19:01 +01:00
|
|
|
char type;
|
|
|
|
char cd001[6];
|
|
|
|
char version;
|
|
|
|
char sysid[32];
|
|
|
|
char volid[32];
|
|
|
|
char zeros[8];
|
2021-01-30 12:00:40 -08:00
|
|
|
u32_le_be_pair numSectors;
|
2012-11-01 16:19:01 +01:00
|
|
|
char morezeros[32];
|
2021-01-30 12:00:40 -08:00
|
|
|
u16_le_be_pair volSetSize;
|
|
|
|
u16_le_be_pair volSeqNum;
|
|
|
|
u16_le_be_pair sectorSize;
|
|
|
|
u32_le_be_pair pathTableLength;
|
|
|
|
u16_le_be_pair firstLETableSector;
|
|
|
|
u16_le_be_pair secondLETableSector;
|
|
|
|
u16_le_be_pair firstBETableSector;
|
|
|
|
u16_le_be_pair secondBETableSector;
|
2012-11-01 16:19:01 +01:00
|
|
|
DirectoryEntry root;
|
|
|
|
char volumeSetIdentifier[128];
|
|
|
|
char publisherIdentifier[128];
|
|
|
|
char dataPreparerIdentifier[128];
|
|
|
|
char applicationIdentifier[128];
|
|
|
|
char copyrightFileIdentifier[37];
|
|
|
|
char abstractFileIdentifier[37];
|
|
|
|
char bibliographicalFileIdentifier[37];
|
|
|
|
char volCreationDateTime[17];
|
|
|
|
char mreModDateTime[17];
|
|
|
|
char volExpireDateTime[17];
|
|
|
|
char volEffectiveDateTime[17];
|
|
|
|
char one;
|
|
|
|
char zero;
|
|
|
|
char reserved[512];
|
|
|
|
char zeroos[653];
|
|
|
|
};
|
|
|
|
|
|
|
|
#pragma pack(pop)
|
|
|
|
|
2016-02-29 01:13:57 +01:00
|
|
|
ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice) {
|
2012-11-01 16:19:01 +01:00
|
|
|
blockDevice = _blockDevice;
|
|
|
|
hAlloc = _hAlloc;
|
|
|
|
|
|
|
|
VolDescriptor desc;
|
|
|
|
blockDevice->ReadBlock(16, (u8*)&desc);
|
|
|
|
|
|
|
|
entireISO.name = "";
|
|
|
|
entireISO.isDirectory = false;
|
|
|
|
entireISO.startingPosition = 0;
|
2014-01-11 08:34:04 -08:00
|
|
|
entireISO.size = _blockDevice->GetNumBlocks();
|
2012-11-01 16:19:01 +01:00
|
|
|
entireISO.flags = 0;
|
2012-12-27 22:14:31 -08:00
|
|
|
entireISO.parent = NULL;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-04-13 12:56:33 -07:00
|
|
|
treeroot = new TreeEntry();
|
2012-12-27 22:16:23 -08:00
|
|
|
treeroot->isDirectory = true;
|
|
|
|
treeroot->startingPosition = 0;
|
|
|
|
treeroot->size = 0;
|
|
|
|
treeroot->flags = 0;
|
2012-12-27 22:14:31 -08:00
|
|
|
treeroot->parent = NULL;
|
2016-02-28 11:51:15 +01:00
|
|
|
treeroot->valid = false;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-10-01 07:53:40 -07:00
|
|
|
if (memcmp(desc.cd001, "CD001", 5)) {
|
2017-03-02 17:07:12 +01:00
|
|
|
ERROR_LOG(FILESYS, "ISO looks bogus, expected CD001 signature not present? Giving up...");
|
2013-10-01 07:53:40 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-30 12:00:40 -08:00
|
|
|
treeroot->startsector = desc.root.firstDataSector;
|
|
|
|
treeroot->dirsize = desc.root.dataLength;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2016-02-28 11:46:21 +01:00
|
|
|
ISOFileSystem::~ISOFileSystem() {
|
2012-11-01 16:19:01 +01:00
|
|
|
delete blockDevice;
|
2013-04-13 12:56:33 -07:00
|
|
|
delete treeroot;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2016-02-28 11:51:15 +01:00
|
|
|
void ISOFileSystem::ReadDirectory(TreeEntry *root) {
|
2016-06-04 22:10:47 -07:00
|
|
|
for (u32 secnum = root->startsector, endsector = root->startsector + (root->dirsize + 2047) / 2048; secnum < endsector; ++secnum) {
|
2013-02-09 21:26:55 -08:00
|
|
|
u8 theSector[2048];
|
2016-02-28 12:02:01 +01:00
|
|
|
if (!blockDevice->ReadBlock(secnum, theSector)) {
|
2018-09-26 20:29:15 -07:00
|
|
|
blockDevice->NotifyReadError();
|
2021-05-06 01:31:38 +02:00
|
|
|
ERROR_LOG(FILESYS, "Error reading block for directory '%s' in sector %d - skipping", root->name.c_str(), secnum);
|
2016-02-28 12:02:01 +01:00
|
|
|
root->valid = true; // Prevents re-reading
|
|
|
|
return;
|
|
|
|
}
|
2016-03-06 14:16:40 +01:00
|
|
|
lastReadBlock_ = secnum; // Hm, this could affect timing... but lazy loading is probably more realistic.
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
for (int offset = 0; offset < 2048; ) {
|
2013-02-09 21:26:55 -08:00
|
|
|
DirectoryEntry &dir = *(DirectoryEntry *)&theSector[offset];
|
|
|
|
u8 sz = theSector[offset];
|
2012-11-10 12:15:44 +01:00
|
|
|
|
2013-02-09 21:26:55 -08:00
|
|
|
// Nothing left in this sector. There might be more in the next one.
|
|
|
|
if (sz == 0)
|
|
|
|
break;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-02-09 21:26:55 -08:00
|
|
|
const int IDENTIFIER_OFFSET = 33;
|
2016-02-28 11:39:57 +01:00
|
|
|
if (offset + IDENTIFIER_OFFSET + dir.identifierLength > 2048) {
|
2018-09-26 20:29:15 -07:00
|
|
|
blockDevice->NotifyReadError();
|
2013-02-09 21:26:55 -08:00
|
|
|
ERROR_LOG(FILESYS, "Directory entry crosses sectors, corrupt iso?");
|
2013-03-31 21:35:30 -07:00
|
|
|
return;
|
2013-02-09 21:26:55 -08:00
|
|
|
}
|
2012-11-29 10:40:15 +01:00
|
|
|
|
2013-02-09 21:26:55 -08:00
|
|
|
offset += dir.size;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-02-09 21:26:55 -08:00
|
|
|
bool isFile = (dir.flags & 2) ? false : true;
|
|
|
|
bool relative;
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
TreeEntry *entry = new TreeEntry();
|
|
|
|
if (dir.identifierLength == 1 && (dir.firstIdChar == '\x00' || dir.firstIdChar == '.')) {
|
|
|
|
entry->name = ".";
|
2013-02-09 21:26:55 -08:00
|
|
|
relative = true;
|
2016-06-04 22:10:47 -07:00
|
|
|
} else if (dir.identifierLength == 1 && dir.firstIdChar == '\x01') {
|
2016-02-28 11:39:57 +01:00
|
|
|
entry->name = "..";
|
2013-02-09 21:26:55 -08:00
|
|
|
relative = true;
|
2016-06-04 22:10:47 -07:00
|
|
|
} else {
|
2016-02-28 11:39:57 +01:00
|
|
|
entry->name = std::string((const char *)&dir.firstIdChar, dir.identifierLength);
|
2013-02-09 21:26:55 -08:00
|
|
|
relative = false;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
2013-02-09 21:26:55 -08:00
|
|
|
|
2021-01-30 12:00:40 -08:00
|
|
|
entry->size = dir.dataLength;
|
|
|
|
entry->startingPosition = dir.firstDataSector * 2048;
|
2016-02-28 11:39:57 +01:00
|
|
|
entry->isDirectory = !isFile;
|
|
|
|
entry->flags = dir.flags;
|
|
|
|
entry->parent = root;
|
2021-01-30 12:00:40 -08:00
|
|
|
entry->startsector = dir.firstDataSector;
|
|
|
|
entry->dirsize = dir.dataLength;
|
2016-03-06 14:16:40 +01:00
|
|
|
entry->valid = isFile; // Can pre-mark as valid if file, as we don't recurse into those.
|
2021-01-30 12:00:40 -08:00
|
|
|
VERBOSE_LOG(FILESYS, "%s: %s %08x %08x %i", entry->isDirectory ? "D" : "F", entry->name.c_str(), (u32)dir.firstDataSector, entry->startingPosition, entry->startingPosition);
|
2013-02-09 21:26:55 -08:00
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
if (entry->isDirectory && !relative) {
|
2016-02-28 12:02:01 +01:00
|
|
|
if (entry->startsector == root->startsector) {
|
2018-09-26 20:29:15 -07:00
|
|
|
blockDevice->NotifyReadError();
|
2016-02-28 11:46:21 +01:00
|
|
|
ERROR_LOG(FILESYS, "WARNING: Appear to have a recursive file system, breaking recursion. Probably corrupt ISO.");
|
2013-02-09 21:26:55 -08:00
|
|
|
}
|
|
|
|
}
|
2016-02-28 11:39:57 +01:00
|
|
|
root->children.push_back(entry);
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
}
|
2016-02-28 12:02:01 +01:00
|
|
|
root->valid = true;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(const std::string &path, bool catchError) {
|
2015-09-07 18:43:10 -07:00
|
|
|
const size_t pathLength = path.length();
|
2015-09-07 18:52:17 -07:00
|
|
|
|
2015-09-07 18:43:10 -07:00
|
|
|
if (pathLength == 0) {
|
2014-07-27 23:42:46 +02:00
|
|
|
// Ah, the device! "umd0:"
|
2012-11-01 16:19:01 +01:00
|
|
|
return &entireISO;
|
|
|
|
}
|
|
|
|
|
2015-09-07 18:43:10 -07:00
|
|
|
size_t pathIndex = 0;
|
2012-11-06 19:34:17 +01:00
|
|
|
|
2015-09-07 18:43:10 -07:00
|
|
|
// Skip "./"
|
|
|
|
if (pathLength > pathIndex + 1 && path[pathIndex] == '.' && path[pathIndex + 1] == '/')
|
|
|
|
pathIndex += 2;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2015-09-07 18:43:10 -07:00
|
|
|
// Skip "/"
|
|
|
|
if (pathLength > pathIndex && path[pathIndex] == '/')
|
|
|
|
++pathIndex;
|
2015-09-07 18:52:17 -07:00
|
|
|
|
2015-09-07 18:43:10 -07:00
|
|
|
if (pathLength <= pathIndex)
|
|
|
|
return treeroot;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
TreeEntry *entry = treeroot;
|
|
|
|
while (true) {
|
2016-02-28 12:02:01 +01:00
|
|
|
if (!entry->valid) {
|
|
|
|
ReadDirectory(entry);
|
|
|
|
}
|
2016-02-28 11:39:57 +01:00
|
|
|
TreeEntry *nextEntry = nullptr;
|
2012-11-01 16:19:01 +01:00
|
|
|
std::string name = "";
|
2016-02-28 11:39:57 +01:00
|
|
|
if (pathLength > pathIndex) {
|
2015-09-07 18:43:10 -07:00
|
|
|
size_t nextSlashIndex = path.find_first_of('/', pathIndex);
|
|
|
|
if (nextSlashIndex == std::string::npos)
|
|
|
|
nextSlashIndex = pathLength;
|
2012-12-17 20:21:51 +00:00
|
|
|
|
2015-09-07 18:43:10 -07:00
|
|
|
const std::string firstPathComponent = path.substr(pathIndex, nextSlashIndex - pathIndex);
|
2016-02-28 11:39:57 +01:00
|
|
|
for (size_t i = 0; i < entry->children.size(); i++) {
|
|
|
|
const std::string &n = entry->children[i]->name;
|
|
|
|
if (firstPathComponent == n) {
|
2012-11-01 16:19:01 +01:00
|
|
|
//yay we got it
|
2016-02-28 11:39:57 +01:00
|
|
|
nextEntry = entry->children[i];
|
2012-11-01 16:19:01 +01:00
|
|
|
name = n;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-09-12 21:12:11 -07:00
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
if (nextEntry) {
|
|
|
|
entry = nextEntry;
|
2016-03-06 14:26:42 +01:00
|
|
|
if (!entry->valid)
|
|
|
|
ReadDirectory(entry);
|
2015-09-07 18:43:10 -07:00
|
|
|
pathIndex += name.length();
|
2015-09-07 18:56:18 -07:00
|
|
|
if (pathIndex < pathLength && path[pathIndex] == '/')
|
2015-09-07 18:43:10 -07:00
|
|
|
++pathIndex;
|
|
|
|
|
|
|
|
if (pathLength <= pathIndex)
|
2016-02-28 11:39:57 +01:00
|
|
|
return entry;
|
|
|
|
} else {
|
2013-01-02 01:08:18 +01:00
|
|
|
if (catchError)
|
2020-08-22 00:32:51 +02:00
|
|
|
ERROR_LOG(FILESYS, "File '%s' not found", path.c_str());
|
2015-09-07 18:43:10 -07:00
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-20 11:03:37 -07:00
|
|
|
int ISOFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) {
|
2012-11-01 16:19:01 +01:00
|
|
|
OpenFileEntry entry;
|
2013-07-08 12:35:07 +08:00
|
|
|
entry.isRawSector = false;
|
|
|
|
entry.isBlockSectorMode = false;
|
|
|
|
|
2019-10-20 10:36:13 -07:00
|
|
|
if (access & FILEACCESS_WRITE) {
|
2020-08-22 00:32:51 +02:00
|
|
|
ERROR_LOG(FILESYS, "Can't open file '%s' with write access on an ISO partition", filename.c_str());
|
2019-10-20 10:36:13 -07:00
|
|
|
return SCE_KERNEL_ERROR_ERRNO_INVALID_FLAG;
|
|
|
|
}
|
|
|
|
|
2016-02-28 11:51:15 +01:00
|
|
|
if (filename.compare(0, 8, "/sce_lbn") == 0) {
|
|
|
|
// Raw sector read.
|
2012-11-01 16:19:01 +01:00
|
|
|
u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
|
2012-11-06 18:32:28 +01:00
|
|
|
parseLBN(filename, §orStart, &readSize);
|
2016-02-28 11:39:57 +01:00
|
|
|
if (sectorStart > blockDevice->GetNumBlocks()) {
|
2020-08-22 00:32:51 +02:00
|
|
|
WARN_LOG(FILESYS, "Unable to open raw sector, out of range: '%s', sector %08x, max %08x", filename.c_str(), sectorStart, blockDevice->GetNumBlocks());
|
2019-10-20 11:03:37 -07:00
|
|
|
return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;
|
2013-01-09 01:13:38 -08:00
|
|
|
}
|
2013-11-23 12:49:32 +01:00
|
|
|
else if (sectorStart == blockDevice->GetNumBlocks())
|
|
|
|
{
|
|
|
|
ERROR_LOG(FILESYS, "Should not be able to open the block after the last on disc! %08x", sectorStart);
|
|
|
|
}
|
2013-01-09 01:13:38 -08:00
|
|
|
|
2020-08-22 00:32:51 +02:00
|
|
|
DEBUG_LOG(FILESYS, "Got a raw sector open: '%s', sector %08x, size %08x", filename.c_str(), sectorStart, readSize);
|
2012-11-01 16:19:01 +01:00
|
|
|
u32 newHandle = hAlloc->GetNewHandle();
|
|
|
|
entry.seekPos = 0;
|
|
|
|
entry.file = 0;
|
|
|
|
entry.isRawSector = true;
|
|
|
|
entry.sectorStart = sectorStart;
|
|
|
|
entry.openSize = readSize;
|
2013-07-06 23:42:49 +08:00
|
|
|
// 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.
|
2020-08-22 00:32:51 +02:00
|
|
|
if (strncmp(devicename, "umd0:", 5) == 0 || strncmp(devicename, "umd1:", 5) == 0)
|
2013-07-08 12:35:07 +08:00
|
|
|
entry.isBlockSectorMode = true;
|
2013-07-06 23:42:49 +08:00
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
entries[newHandle] = entry;
|
|
|
|
return newHandle;
|
|
|
|
}
|
|
|
|
|
2020-08-22 00:32:51 +02:00
|
|
|
// May return entireISO for "umd0:".
|
|
|
|
entry.file = GetFromPath(filename, false);
|
|
|
|
if (!entry.file) {
|
2019-10-20 11:03:37 -07:00
|
|
|
return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;
|
2013-07-08 12:35:07 +08:00
|
|
|
}
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2015-09-07 16:44:34 -07:00
|
|
|
if (entry.file == &entireISO)
|
2013-07-08 15:17:42 +08:00
|
|
|
entry.isBlockSectorMode = true;
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
entry.seekPos = 0;
|
|
|
|
|
|
|
|
u32 newHandle = hAlloc->GetNewHandle();
|
|
|
|
entries[newHandle] = entry;
|
|
|
|
return newHandle;
|
|
|
|
}
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
void ISOFileSystem::CloseFile(u32 handle) {
|
2012-11-01 16:19:01 +01:00
|
|
|
EntryMap::iterator iter = entries.find(handle);
|
2016-02-28 11:39:57 +01:00
|
|
|
if (iter != entries.end()) {
|
2012-11-01 16:19:01 +01:00
|
|
|
//CloseHandle((*iter).second.hFile);
|
|
|
|
hAlloc->FreeHandle(handle);
|
|
|
|
entries.erase(iter);
|
2016-02-28 11:39:57 +01:00
|
|
|
} else {
|
2012-11-01 16:19:01 +01:00
|
|
|
//This shouldn't happen...
|
2013-09-07 22:02:55 +02:00
|
|
|
ERROR_LOG(FILESYS, "Hey, what are you doing? Closing non-open files?");
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
bool ISOFileSystem::OwnsHandle(u32 handle) {
|
2012-11-01 16:19:01 +01:00
|
|
|
EntryMap::iterator iter = entries.find(handle);
|
|
|
|
return (iter != entries.end());
|
|
|
|
}
|
|
|
|
|
2013-12-26 14:47:17 -08:00
|
|
|
int ISOFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) {
|
|
|
|
EntryMap::iterator iter = entries.find(handle);
|
|
|
|
if (iter == entries.end()) {
|
|
|
|
ERROR_LOG(FILESYS, "Ioctl on a bad file handle");
|
|
|
|
return SCE_KERNEL_ERROR_BADF;
|
|
|
|
}
|
|
|
|
|
|
|
|
OpenFileEntry &e = iter->second;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
// Get ISO9660 volume descriptor (from open ISO9660 file.)
|
|
|
|
case 0x01020001:
|
|
|
|
if (e.isBlockSectorMode) {
|
2013-12-26 15:01:24 -08:00
|
|
|
ERROR_LOG(FILESYS, "Unsupported read volume descriptor command on a umd block device");
|
2013-12-26 14:47:17 -08:00
|
|
|
return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Memory::IsValidAddress(outdataPtr) || outlen < 0x800) {
|
|
|
|
WARN_LOG_REPORT(FILESYS, "sceIoIoctl: Invalid out pointer while reading ISO9660 volume descriptor");
|
|
|
|
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
INFO_LOG(SCEIO, "sceIoIoctl: reading ISO9660 volume descriptor read");
|
2013-12-26 15:01:24 -08:00
|
|
|
blockDevice->ReadBlock(16, Memory::GetPointer(outdataPtr));
|
2013-12-26 14:47:17 -08:00
|
|
|
return 0;
|
2013-12-26 15:01:24 -08:00
|
|
|
|
|
|
|
// Get ISO9660 path table (from open ISO9660 file.)
|
|
|
|
case 0x01020002:
|
|
|
|
if (e.isBlockSectorMode) {
|
|
|
|
ERROR_LOG(FILESYS, "Unsupported read path table command on a umd block device");
|
|
|
|
return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
VolDescriptor desc;
|
|
|
|
blockDevice->ReadBlock(16, (u8 *)&desc);
|
2021-01-30 12:00:40 -08:00
|
|
|
if (outlen < (u32)desc.pathTableLength) {
|
2013-12-26 15:01:24 -08:00
|
|
|
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
|
|
|
|
} else {
|
2021-01-30 12:00:40 -08:00
|
|
|
int block = (u16)desc.firstLETableSector;
|
2021-02-13 08:24:39 -08:00
|
|
|
u32 size = Memory::ValidSize(outdataPtr, (u32)desc.pathTableLength);
|
2013-12-26 15:01:24 -08:00
|
|
|
u8 *out = Memory::GetPointer(outdataPtr);
|
|
|
|
|
2014-11-02 19:50:26 -08:00
|
|
|
int blocks = size / blockDevice->GetBlockSize();
|
|
|
|
blockDevice->ReadBlocks(block, blocks, out);
|
|
|
|
size -= blocks * blockDevice->GetBlockSize();
|
|
|
|
out += blocks * blockDevice->GetBlockSize();
|
2013-12-26 15:01:24 -08:00
|
|
|
|
|
|
|
// The remaining (or, usually, only) partial sector.
|
|
|
|
if (size > 0) {
|
|
|
|
u8 temp[2048];
|
|
|
|
blockDevice->ReadBlock(block, temp);
|
|
|
|
memcpy(out, temp, size);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2013-12-26 14:47:17 -08:00
|
|
|
}
|
|
|
|
return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2020-05-21 17:57:41 -07:00
|
|
|
PSPDevType ISOFileSystem::DevType(u32 handle) {
|
2013-12-26 22:07:41 -08:00
|
|
|
EntryMap::iterator iter = entries.find(handle);
|
2021-04-21 20:11:23 -07:00
|
|
|
if (iter == entries.end())
|
|
|
|
return PSPDevType::FILE;
|
2020-05-21 17:57:41 -07:00
|
|
|
PSPDevType type = iter->second.isBlockSectorMode ? PSPDevType::BLOCK : PSPDevType::FILE;
|
|
|
|
if (iter->second.isRawSector)
|
|
|
|
type |= PSPDevType::EMU_LBN;
|
|
|
|
return type;
|
2013-12-26 22:07:41 -08:00
|
|
|
}
|
|
|
|
|
2020-05-21 16:10:08 -07:00
|
|
|
FileSystemFlags ISOFileSystem::Flags() {
|
|
|
|
// TODO: Here may be a good place to force things, in case users recompress games
|
|
|
|
// as PBP or CSO when they were originally the other type.
|
|
|
|
return blockDevice->IsDisc() ? FileSystemFlags::UMD : FileSystemFlags::CARD;
|
|
|
|
}
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
|
2013-12-27 16:36:51 -08:00
|
|
|
{
|
|
|
|
int ignored;
|
|
|
|
return ReadFile(handle, pointer, size, ignored);
|
|
|
|
}
|
|
|
|
|
2016-03-06 14:16:40 +01:00
|
|
|
size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {
|
2012-11-01 16:19:01 +01:00
|
|
|
EntryMap::iterator iter = entries.find(handle);
|
2015-01-09 15:50:06 -08:00
|
|
|
if (iter != entries.end()) {
|
2012-11-01 16:19:01 +01:00
|
|
|
OpenFileEntry &e = iter->second;
|
2015-01-09 15:50:06 -08:00
|
|
|
|
|
|
|
if (size < 0) {
|
|
|
|
ERROR_LOG_REPORT(FILESYS, "Invalid read for %lld bytes from umd %s", size, e.file ? e.file->name.c_str() : "device");
|
|
|
|
return 0;
|
|
|
|
}
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2015-01-09 18:34:41 -08:00
|
|
|
if (e.isBlockSectorMode) {
|
2012-11-01 16:19:01 +01:00
|
|
|
// Whole sectors! Shortcut to this simple code.
|
2014-11-02 19:50:26 -08:00
|
|
|
blockDevice->ReadBlocks(e.seekPos, (int)size, pointer);
|
2013-12-27 16:49:35 -08:00
|
|
|
if (abs((int)lastReadBlock_ - (int)e.seekPos) > 100) {
|
|
|
|
// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
|
|
|
|
usec = 100000;
|
|
|
|
}
|
2014-11-02 19:50:26 -08:00
|
|
|
e.seekPos += (int)size;
|
2013-12-27 16:49:35 -08:00
|
|
|
lastReadBlock_ = e.seekPos;
|
2014-11-02 19:50:26 -08:00
|
|
|
return (int)size;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2015-01-09 18:34:41 -08:00
|
|
|
u64 positionOnIso;
|
|
|
|
s64 fileSize;
|
|
|
|
if (e.isRawSector) {
|
|
|
|
positionOnIso = e.sectorStart * 2048ULL + e.seekPos;
|
|
|
|
fileSize = (s64)e.openSize;
|
2015-12-24 14:31:23 -08:00
|
|
|
} else if (e.file == nullptr) {
|
|
|
|
ERROR_LOG(FILESYS, "File no longer exists (loaded savestate with different ISO?)");
|
|
|
|
return 0;
|
2015-01-09 18:34:41 -08:00
|
|
|
} else {
|
2012-11-09 14:32:40 +01:00
|
|
|
positionOnIso = e.file->startingPosition + e.seekPos;
|
2015-01-09 18:34:41 -08:00
|
|
|
fileSize = e.file->size;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2015-01-09 18:34:41 -08:00
|
|
|
if ((s64)e.seekPos > fileSize) {
|
|
|
|
WARN_LOG(FILESYS, "Read starting outside of file, at %lld / %lld", (s64)e.seekPos, fileSize);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if ((s64)e.seekPos + size > fileSize) {
|
|
|
|
// Clamp to the remaining size, but read what we can.
|
|
|
|
const s64 newSize = fileSize - (s64)e.seekPos;
|
2020-08-22 00:32:51 +02:00
|
|
|
// Reading beyond the file is really quite normal behavior (if return value handled correctly), so
|
|
|
|
// not doing WARN here. Still, can potentially be useful to see so leaving at INFO.
|
|
|
|
if (newSize == 0) {
|
|
|
|
INFO_LOG(FILESYS, "Attempted read at end of file, 0-size read simulated");
|
|
|
|
} else {
|
|
|
|
INFO_LOG(FILESYS, "Reading beyond end of file from seekPos %d, clamping size %lld to %lld", e.seekPos, size, newSize);
|
|
|
|
}
|
2015-01-09 18:34:41 -08:00
|
|
|
size = newSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Okay, we have size and position, let's rock.
|
2014-11-02 19:50:26 -08:00
|
|
|
const int firstBlockOffset = positionOnIso & 2047;
|
|
|
|
const int firstBlockSize = firstBlockOffset == 0 ? 0 : (int)std::min(size, 2048LL - firstBlockOffset);
|
|
|
|
const int lastBlockSize = (size - firstBlockSize) & 2047;
|
|
|
|
const s64 middleSize = size - firstBlockSize - lastBlockSize;
|
2015-01-09 18:34:41 -08:00
|
|
|
u32 secNum = (u32)(positionOnIso / 2048);
|
2012-11-01 16:19:01 +01:00
|
|
|
u8 theSector[2048];
|
|
|
|
|
2020-07-19 17:47:02 +02:00
|
|
|
if ((middleSize & 2047) != 0) {
|
|
|
|
ERROR_LOG(FILESYS, "Remaining size should be aligned");
|
|
|
|
}
|
2014-11-02 19:50:26 -08:00
|
|
|
|
2014-11-06 08:45:33 -08:00
|
|
|
const u8 *const start = pointer;
|
2015-01-09 18:34:41 -08:00
|
|
|
if (firstBlockSize > 0) {
|
2014-11-02 19:50:26 -08:00
|
|
|
blockDevice->ReadBlock(secNum++, theSector);
|
|
|
|
memcpy(pointer, theSector + firstBlockOffset, firstBlockSize);
|
|
|
|
pointer += firstBlockSize;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
2015-01-09 18:34:41 -08:00
|
|
|
if (middleSize > 0) {
|
2014-11-02 19:50:26 -08:00
|
|
|
const u32 sectors = (u32)(middleSize / 2048);
|
|
|
|
blockDevice->ReadBlocks(secNum, sectors, pointer);
|
|
|
|
secNum += sectors;
|
|
|
|
pointer += middleSize;
|
|
|
|
}
|
2015-01-09 18:34:41 -08:00
|
|
|
if (lastBlockSize > 0) {
|
2014-11-02 19:50:26 -08:00
|
|
|
blockDevice->ReadBlock(secNum++, theSector);
|
|
|
|
memcpy(pointer, theSector, lastBlockSize);
|
|
|
|
pointer += lastBlockSize;
|
|
|
|
}
|
|
|
|
|
2014-11-06 08:45:33 -08:00
|
|
|
size_t totalBytes = pointer - start;
|
2013-12-27 16:49:35 -08:00
|
|
|
if (abs((int)lastReadBlock_ - (int)secNum) > 100) {
|
|
|
|
// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
|
|
|
|
usec = 100000;
|
|
|
|
}
|
|
|
|
lastReadBlock_ = secNum;
|
2014-11-06 08:45:33 -08:00
|
|
|
e.seekPos += (unsigned int)totalBytes;
|
|
|
|
return (size_t)totalBytes;
|
2015-01-09 18:34:41 -08:00
|
|
|
} else {
|
2012-11-01 16:19:01 +01:00
|
|
|
//This shouldn't happen...
|
2013-09-07 22:02:55 +02:00
|
|
|
ERROR_LOG(FILESYS, "Hey, what are you doing? Reading non-open files?");
|
2012-11-01 16:19:01 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) {
|
2013-12-27 16:36:51 -08:00
|
|
|
ERROR_LOG(FILESYS, "Hey, what are you doing? You can't write to an ISO!");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) {
|
2013-09-07 22:02:55 +02:00
|
|
|
ERROR_LOG(FILESYS, "Hey, what are you doing? You can't write to an ISO!");
|
2012-11-01 16:19:01 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
size_t ISOFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {
|
2012-11-01 16:19:01 +01:00
|
|
|
EntryMap::iterator iter = entries.find(handle);
|
2016-02-28 11:39:57 +01:00
|
|
|
if (iter != entries.end()) {
|
2012-11-01 16:19:01 +01:00
|
|
|
OpenFileEntry &e = iter->second;
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case FILEMOVE_BEGIN:
|
|
|
|
e.seekPos = position;
|
|
|
|
break;
|
|
|
|
case FILEMOVE_CURRENT:
|
|
|
|
e.seekPos += position;
|
|
|
|
break;
|
|
|
|
case FILEMOVE_END:
|
|
|
|
if (e.isRawSector)
|
2013-01-09 00:43:07 -08:00
|
|
|
e.seekPos = e.openSize + position;
|
2012-11-01 16:19:01 +01:00
|
|
|
else
|
2012-11-17 19:56:28 +01:00
|
|
|
e.seekPos = (unsigned int)(e.file->size + position);
|
2012-11-01 16:19:01 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (size_t)e.seekPos;
|
2016-02-28 11:39:57 +01:00
|
|
|
} else {
|
2012-11-01 16:19:01 +01:00
|
|
|
//This shouldn't happen...
|
2013-09-07 22:02:55 +02:00
|
|
|
ERROR_LOG(FILESYS, "Hey, what are you doing? Seeking in non-open files?");
|
2012-11-01 16:19:01 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
PSPFileInfo ISOFileSystem::GetFileInfo(std::string filename) {
|
|
|
|
if (filename.compare(0,8,"/sce_lbn") == 0) {
|
2012-11-06 18:32:28 +01:00
|
|
|
u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
|
|
|
|
parseLBN(filename, §orStart, &readSize);
|
|
|
|
|
|
|
|
PSPFileInfo fileInfo;
|
|
|
|
fileInfo.name = filename;
|
|
|
|
fileInfo.exists = true;
|
2020-05-21 17:57:41 -07:00
|
|
|
fileInfo.type = FILETYPE_NORMAL;
|
2012-11-06 18:32:28 +01:00
|
|
|
fileInfo.size = readSize;
|
2020-05-21 18:20:38 -07:00
|
|
|
fileInfo.access = 0444;
|
2012-11-06 18:32:28 +01:00
|
|
|
fileInfo.startSector = sectorStart;
|
|
|
|
fileInfo.isOnSectorSystem = true;
|
|
|
|
fileInfo.numSectors = (readSize + sectorSize - 1) / sectorSize;
|
|
|
|
return fileInfo;
|
|
|
|
}
|
|
|
|
|
2013-01-02 01:08:18 +01:00
|
|
|
TreeEntry *entry = GetFromPath(filename, false);
|
2012-11-01 16:19:01 +01:00
|
|
|
PSPFileInfo x;
|
2020-05-21 18:20:38 -07:00
|
|
|
if (entry) {
|
2012-11-01 16:19:01 +01:00
|
|
|
x.name = entry->name;
|
2020-05-21 18:20:38 -07:00
|
|
|
// Strangely, it seems to be executable even for files.
|
|
|
|
x.access = 0555;
|
2012-11-01 16:19:01 +01:00
|
|
|
x.size = entry->size;
|
2012-11-05 12:02:09 +01:00
|
|
|
x.exists = true;
|
2012-11-01 16:19:01 +01:00
|
|
|
x.type = entry->isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
|
|
|
|
x.isOnSectorSystem = true;
|
2015-09-07 16:44:34 -07:00
|
|
|
x.startSector = entry->startingPosition / 2048;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
std::vector<PSPFileInfo> ISOFileSystem::GetDirListing(std::string path) {
|
2012-11-01 16:19:01 +01:00
|
|
|
std::vector<PSPFileInfo> myVector;
|
|
|
|
TreeEntry *entry = GetFromPath(path);
|
2016-03-06 14:16:40 +01:00
|
|
|
if (!entry)
|
2012-11-01 16:19:01 +01:00
|
|
|
return myVector;
|
|
|
|
|
2015-09-07 18:52:17 -07:00
|
|
|
const std::string dot(".");
|
|
|
|
const std::string dotdot("..");
|
2015-09-07 16:31:37 -07:00
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
for (size_t i = 0; i < entry->children.size(); i++) {
|
2012-11-01 16:19:01 +01:00
|
|
|
TreeEntry *e = entry->children[i];
|
2012-11-10 12:15:44 +01:00
|
|
|
|
2015-09-07 18:52:17 -07:00
|
|
|
// do not include the relative entries in the list
|
|
|
|
if (e->name == dot || e->name == dotdot)
|
2012-11-10 12:15:44 +01:00
|
|
|
continue;
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
PSPFileInfo x;
|
|
|
|
x.name = e->name;
|
2020-05-21 18:20:38 -07:00
|
|
|
// Strangely, it seems to be executable even for files.
|
|
|
|
x.access = 0555;
|
2012-11-01 16:19:01 +01:00
|
|
|
x.size = e->size;
|
|
|
|
x.type = e->isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
|
|
|
|
x.isOnSectorSystem = true;
|
2013-03-03 22:05:23 -08:00
|
|
|
x.startSector = e->startingPosition/2048;
|
2016-03-06 14:16:40 +01:00
|
|
|
x.sectorSize = sectorSize;
|
2017-03-04 13:56:15 +01:00
|
|
|
x.numSectors = (u32)((e->size + sectorSize - 1) / sectorSize);
|
2012-11-01 16:19:01 +01:00
|
|
|
myVector.push_back(x);
|
|
|
|
}
|
|
|
|
return myVector;
|
|
|
|
}
|
2012-12-27 22:14:31 -08:00
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
std::string ISOFileSystem::EntryFullPath(TreeEntry *e) {
|
2013-05-12 16:37:03 -07:00
|
|
|
if (e == &entireISO)
|
|
|
|
return "";
|
|
|
|
|
2012-12-27 22:14:31 -08:00
|
|
|
size_t fullLen = 0;
|
|
|
|
TreeEntry *cur = e;
|
2016-02-28 11:39:57 +01:00
|
|
|
while (cur != NULL && cur != treeroot) {
|
2012-12-27 22:14:31 -08:00
|
|
|
// For the "/".
|
|
|
|
fullLen += 1 + cur->name.size();
|
|
|
|
cur = cur->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string path;
|
|
|
|
path.resize(fullLen);
|
|
|
|
|
|
|
|
cur = e;
|
2016-03-06 14:16:40 +01:00
|
|
|
while (cur != NULL && cur != treeroot) {
|
2012-12-27 22:14:31 -08:00
|
|
|
path.replace(fullLen - cur->name.size(), cur->name.size(), cur->name);
|
|
|
|
path.replace(fullLen - cur->name.size() - 1, 1, "/");
|
|
|
|
fullLen -= 1 + cur->name.size();
|
|
|
|
cur = cur->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2014-07-27 23:42:46 +02:00
|
|
|
ISOFileSystem::TreeEntry::~TreeEntry() {
|
|
|
|
for (size_t i = 0; i < children.size(); ++i)
|
|
|
|
delete children[i];
|
|
|
|
children.clear();
|
|
|
|
}
|
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
void ISOFileSystem::DoState(PointerWrap &p) {
|
2013-12-27 16:49:35 -08:00
|
|
|
auto s = p.Section("ISOFileSystem", 1, 2);
|
2013-09-14 20:23:03 -07:00
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
|
2012-12-27 22:14:31 -08:00
|
|
|
int n = (int) entries.size();
|
2020-08-09 21:20:42 -07:00
|
|
|
Do(p, n);
|
2012-12-27 22:14:31 -08:00
|
|
|
|
2016-02-28 11:39:57 +01:00
|
|
|
if (p.mode == p.MODE_READ) {
|
2012-12-27 22:14:31 -08:00
|
|
|
entries.clear();
|
2016-02-28 11:39:57 +01:00
|
|
|
for (int i = 0; i < n; ++i) {
|
2013-09-01 01:05:35 -07:00
|
|
|
u32 fd = 0;
|
2012-12-27 22:14:31 -08:00
|
|
|
OpenFileEntry of;
|
2013-05-12 16:37:03 -07:00
|
|
|
|
2020-08-09 21:20:42 -07:00
|
|
|
Do(p, fd);
|
|
|
|
Do(p, of.seekPos);
|
|
|
|
Do(p, of.isRawSector);
|
|
|
|
Do(p, of.isBlockSectorMode);
|
|
|
|
Do(p, of.sectorStart);
|
|
|
|
Do(p, of.openSize);
|
2013-05-12 16:37:03 -07:00
|
|
|
|
2013-07-26 22:39:35 -07:00
|
|
|
bool hasFile = false;
|
2020-08-09 21:20:42 -07:00
|
|
|
Do(p, hasFile);
|
2013-05-12 16:37:03 -07:00
|
|
|
if (hasFile) {
|
|
|
|
std::string path;
|
2020-08-09 21:20:42 -07:00
|
|
|
Do(p, path);
|
2013-05-12 16:37:03 -07:00
|
|
|
of.file = GetFromPath(path);
|
|
|
|
} else {
|
|
|
|
of.file = NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-27 22:14:31 -08:00
|
|
|
entries[fd] = of;
|
|
|
|
}
|
2016-02-28 11:39:57 +01:00
|
|
|
} else {
|
|
|
|
for (EntryMap::iterator it = entries.begin(), end = entries.end(); it != end; ++it) {
|
2013-05-12 16:37:03 -07:00
|
|
|
OpenFileEntry &of = it->second;
|
2020-08-09 21:20:42 -07:00
|
|
|
Do(p, it->first);
|
|
|
|
Do(p, of.seekPos);
|
|
|
|
Do(p, of.isRawSector);
|
|
|
|
Do(p, of.isBlockSectorMode);
|
|
|
|
Do(p, of.sectorStart);
|
|
|
|
Do(p, of.openSize);
|
2013-05-12 16:37:03 -07:00
|
|
|
|
|
|
|
bool hasFile = of.file != NULL;
|
2020-08-09 21:20:42 -07:00
|
|
|
Do(p, hasFile);
|
2013-05-12 16:37:03 -07:00
|
|
|
if (hasFile) {
|
2016-01-09 16:49:20 -03:00
|
|
|
std::string path = EntryFullPath(of.file);
|
2020-08-09 21:20:42 -07:00
|
|
|
Do(p, path);
|
2013-05-12 16:37:03 -07:00
|
|
|
}
|
2012-12-27 22:14:31 -08:00
|
|
|
}
|
|
|
|
}
|
2013-12-27 16:49:35 -08:00
|
|
|
|
|
|
|
if (s >= 2) {
|
2020-08-09 21:20:42 -07:00
|
|
|
Do(p, lastReadBlock_);
|
2013-12-27 16:49:35 -08:00
|
|
|
} else {
|
|
|
|
lastReadBlock_ = 0;
|
|
|
|
}
|
2012-12-27 22:14:31 -08:00
|
|
|
}
|