CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/FileSystems/ISOFileSystem.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <cstring>18#include <cstdio>19#include <ctype.h>20#include <algorithm>2122#include "Common/CommonTypes.h"23#include "Common/Serialize/Serializer.h"24#include "Common/Serialize/SerializeFuncs.h"25#include "Core/FileSystems/ISOFileSystem.h"26#include "Core/HLE/sceKernel.h"27#include "Core/MemMap.h"28#include "Core/Reporting.h"2930const int sectorSize = 2048;3132bool parseLBN(const std::string &filename, u32 *sectorStart, u32 *readSize) {33// The format of this is: "/sce_lbn" "0x"? HEX* ANY* "_size" "0x"? HEX* ANY*34// That means that "/sce_lbn/_size1/" is perfectly valid.35// Most commonly, it looks like /sce_lbn0x10_size0x100 or /sce_lbn10_size100 (always hex.)3637// If it doesn't starts with /sce_lbn or doesn't have _size, look for a file instead.38if (filename.compare(0, sizeof("/sce_lbn") - 1, "/sce_lbn") != 0)39return false;40size_t size_pos = filename.find("_size");41if (size_pos == filename.npos)42return false;4344// TODO: Return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT when >= 32 long but passes above checks.45if (filename.size() >= 32)46return false;4748const char *filename_c = filename.c_str();49size_t pos = strlen("/sce_lbn");5051if (sscanf(filename_c + pos, "%x", sectorStart) != 1)52*sectorStart = 0;5354pos = size_pos + strlen("_size");5556if (sscanf(filename_c + pos, "%x", readSize) != 1)57*readSize = 0;5859return true;60}6162#pragma pack(push)63#pragma pack(1)64struct u32_le_be_pair {65u8 valueLE[4];66u8 valueBE[4];67operator u32() const {68return valueLE[0] + (valueLE[1] << 8) + (valueLE[2] << 16) + (valueLE[3] << 24);69}70};7172struct u16_le_be_pair {73u8 valueLE[2];74u8 valueBE[2];75operator u16() const {76return valueLE[0] + (valueLE[1] << 8);77}78};7980struct DirectoryEntry {81u8 size;82u8 sectorsInExtendedRecord;83u32_le_be_pair firstDataSector; // LBA84u32_le_be_pair dataLength; // Size85u8 years;86u8 month;87u8 day;88u8 hour;89u8 minute;90u8 second;91u8 offsetFromGMT;92u8 flags; // 2 = directory93u8 fileUnitSize;94u8 interleaveGap;95u16_le_be_pair volSeqNumber;96u8 identifierLength; //identifier comes right after97u8 firstIdChar;98};99100struct DirectorySector {101DirectoryEntry entry;102char space[2048-sizeof(DirectoryEntry)];103};104105struct VolDescriptor {106char type;107char cd001[6];108char version;109char sysid[32];110char volid[32];111char zeros[8];112u32_le_be_pair numSectors;113char morezeros[32];114u16_le_be_pair volSetSize;115u16_le_be_pair volSeqNum;116u16_le_be_pair sectorSize;117u32_le_be_pair pathTableLength;118u16_le_be_pair firstLETableSector;119u16_le_be_pair secondLETableSector;120u16_le_be_pair firstBETableSector;121u16_le_be_pair secondBETableSector;122DirectoryEntry root;123char volumeSetIdentifier[128];124char publisherIdentifier[128];125char dataPreparerIdentifier[128];126char applicationIdentifier[128];127char copyrightFileIdentifier[37];128char abstractFileIdentifier[37];129char bibliographicalFileIdentifier[37];130char volCreationDateTime[17];131char mreModDateTime[17];132char volExpireDateTime[17];133char volEffectiveDateTime[17];134char one;135char zero;136char reserved[512];137char zeroos[653];138};139140#pragma pack(pop)141142ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice) {143blockDevice = _blockDevice;144hAlloc = _hAlloc;145146VolDescriptor desc;147if (!blockDevice->ReadBlock(16, (u8*)&desc))148blockDevice->NotifyReadError();149150entireISO.name.clear();151entireISO.isDirectory = false;152entireISO.startingPosition = 0;153entireISO.size = _blockDevice->GetNumBlocks();154entireISO.flags = 0;155entireISO.parent = NULL;156157treeroot = new TreeEntry();158treeroot->isDirectory = true;159treeroot->startingPosition = 0;160treeroot->size = 0;161treeroot->flags = 0;162treeroot->parent = NULL;163treeroot->valid = false;164165if (memcmp(desc.cd001, "CD001", 5)) {166ERROR_LOG(Log::FileSystem, "ISO looks bogus, expected CD001 signature not present? Giving up...");167return;168}169170treeroot->startsector = desc.root.firstDataSector;171treeroot->dirsize = desc.root.dataLength;172}173174ISOFileSystem::~ISOFileSystem() {175delete blockDevice;176delete treeroot;177}178179std::string ISOFileSystem::TreeEntry::BuildPath() {180if (parent) {181return parent->BuildPath() + "/" + name;182} else {183return name;184}185}186187void ISOFileSystem::ReadDirectory(TreeEntry *root) {188for (u32 secnum = root->startsector, endsector = root->startsector + (root->dirsize + 2047) / 2048; secnum < endsector; ++secnum) {189u8 theSector[2048];190if (!blockDevice->ReadBlock(secnum, theSector)) {191blockDevice->NotifyReadError();192ERROR_LOG(Log::FileSystem, "Error reading block for directory '%s' in sector %d - skipping", root->name.c_str(), secnum);193root->valid = true; // Prevents re-reading194return;195}196lastReadBlock_ = secnum; // Hm, this could affect timing... but lazy loading is probably more realistic.197198for (int offset = 0; offset < 2048; ) {199DirectoryEntry &dir = *(DirectoryEntry *)&theSector[offset];200u8 sz = theSector[offset];201202// Nothing left in this sector. There might be more in the next one.203if (sz == 0)204break;205206const int IDENTIFIER_OFFSET = 33;207if (offset + IDENTIFIER_OFFSET + dir.identifierLength > 2048) {208blockDevice->NotifyReadError();209ERROR_LOG(Log::FileSystem, "Directory entry crosses sectors, corrupt iso?");210return;211}212213offset += dir.size;214215bool isFile = (dir.flags & 2) ? false : true;216bool relative;217218TreeEntry *entry = new TreeEntry();219if (dir.identifierLength == 1 && (dir.firstIdChar == '\x00' || dir.firstIdChar == '.')) {220entry->name = ".";221relative = true;222} else if (dir.identifierLength == 1 && dir.firstIdChar == '\x01') {223entry->name = "..";224relative = true;225} else {226entry->name = std::string((const char *)&dir.firstIdChar, dir.identifierLength);227relative = false;228}229230entry->size = dir.dataLength;231entry->startingPosition = dir.firstDataSector * 2048;232entry->isDirectory = !isFile;233entry->flags = dir.flags;234entry->parent = root;235entry->startsector = dir.firstDataSector;236entry->dirsize = dir.dataLength;237entry->valid = isFile; // Can pre-mark as valid if file, as we don't recurse into those.238VERBOSE_LOG(Log::FileSystem, "%s: %s %08x %08x %d", entry->isDirectory ? "D" : "F", entry->name.c_str(), (u32)dir.firstDataSector, entry->startingPosition, entry->startingPosition);239240// Round down to avoid any false reports.241if (isFile && dir.firstDataSector + (dir.dataLength / 2048) > blockDevice->GetNumBlocks()) {242blockDevice->NotifyReadError();243ERROR_LOG(Log::FileSystem, "File '%s' starts or ends outside ISO. firstDataSector: %d len: %d", entry->BuildPath().c_str(), (int)dir.firstDataSector, (int)dir.dataLength);244}245246if (entry->isDirectory && !relative) {247if (entry->startsector == root->startsector) {248blockDevice->NotifyReadError();249ERROR_LOG(Log::FileSystem, "WARNING: Appear to have a recursive file system, breaking recursion. Probably corrupt ISO.");250}251}252root->children.push_back(entry);253}254}255root->valid = true;256}257258ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(const std::string &path, bool catchError) {259const size_t pathLength = path.length();260261if (pathLength == 0) {262// Ah, the device! "umd0:"263return &entireISO;264}265266size_t pathIndex = 0;267268// Skip "./"269if (pathLength > pathIndex + 1 && path[pathIndex] == '.' && path[pathIndex + 1] == '/')270pathIndex += 2;271272// Skip "/"273if (pathLength > pathIndex && path[pathIndex] == '/')274++pathIndex;275276if (pathLength <= pathIndex)277return treeroot;278279TreeEntry *entry = treeroot;280while (true) {281if (!entry->valid) {282ReadDirectory(entry);283}284TreeEntry *nextEntry = nullptr;285std::string name = "";286if (pathLength > pathIndex) {287size_t nextSlashIndex = path.find_first_of('/', pathIndex);288if (nextSlashIndex == std::string::npos)289nextSlashIndex = pathLength;290291const std::string firstPathComponent = path.substr(pathIndex, nextSlashIndex - pathIndex);292for (size_t i = 0; i < entry->children.size(); i++) {293const std::string &n = entry->children[i]->name;294if (firstPathComponent == n) {295//yay we got it296nextEntry = entry->children[i];297name = n;298break;299}300}301}302303if (nextEntry) {304entry = nextEntry;305if (!entry->valid)306ReadDirectory(entry);307pathIndex += name.length();308if (pathIndex < pathLength && path[pathIndex] == '/')309++pathIndex;310311if (pathLength <= pathIndex)312return entry;313} else {314if (catchError)315ERROR_LOG(Log::FileSystem, "File '%s' not found", path.c_str());316317return 0;318}319}320}321322int ISOFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) {323OpenFileEntry entry;324entry.isRawSector = false;325entry.isBlockSectorMode = false;326327if (access & FILEACCESS_WRITE) {328ERROR_LOG(Log::FileSystem, "Can't open file '%s' with write access on an ISO partition", filename.c_str());329return SCE_KERNEL_ERROR_ERRNO_INVALID_FLAG;330}331332if (filename.compare(0, 8, "/sce_lbn") == 0) {333// Raw sector read.334u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;335parseLBN(filename, §orStart, &readSize);336if (sectorStart > blockDevice->GetNumBlocks()) {337WARN_LOG(Log::FileSystem, "Unable to open raw sector, out of range: '%s', sector %08x, max %08x", filename.c_str(), sectorStart, blockDevice->GetNumBlocks());338return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;339}340else if (sectorStart == blockDevice->GetNumBlocks())341{342ERROR_LOG(Log::FileSystem, "Should not be able to open the block after the last on disc! %08x", sectorStart);343}344345DEBUG_LOG(Log::FileSystem, "Got a raw sector open: '%s', sector %08x, size %08x", filename.c_str(), sectorStart, readSize);346u32 newHandle = hAlloc->GetNewHandle();347entry.seekPos = 0;348entry.file = 0;349entry.isRawSector = true;350entry.sectorStart = sectorStart;351entry.openSize = readSize;352// when open as "umd1:/sce_lbn0x0_size0x6B49D200", that mean open umd1 as a block device.353// the param in sceIoLseek and sceIoRead is lba mode. we must mark it.354if (strncmp(devicename, "umd0:", 5) == 0 || strncmp(devicename, "umd1:", 5) == 0)355entry.isBlockSectorMode = true;356357entries[newHandle] = entry;358return newHandle;359}360361// May return entireISO for "umd0:".362entry.file = GetFromPath(filename, false);363if (!entry.file) {364return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;365}366367if (entry.file == &entireISO)368entry.isBlockSectorMode = true;369370entry.seekPos = 0;371372u32 newHandle = hAlloc->GetNewHandle();373entries[newHandle] = entry;374return newHandle;375}376377void ISOFileSystem::CloseFile(u32 handle) {378EntryMap::iterator iter = entries.find(handle);379if (iter != entries.end()) {380//CloseHandle((*iter).second.hFile);381hAlloc->FreeHandle(handle);382entries.erase(iter);383} else {384//This shouldn't happen...385ERROR_LOG(Log::FileSystem, "Hey, what are you doing? Closing non-open files?");386}387}388389bool ISOFileSystem::OwnsHandle(u32 handle) {390EntryMap::iterator iter = entries.find(handle);391return (iter != entries.end());392}393394int ISOFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) {395EntryMap::iterator iter = entries.find(handle);396if (iter == entries.end()) {397ERROR_LOG(Log::FileSystem, "Ioctl on a bad file handle");398return SCE_KERNEL_ERROR_BADF;399}400401OpenFileEntry &e = iter->second;402403switch (cmd) {404// Get ISO9660 volume descriptor (from open ISO9660 file.)405case 0x01020001:406if (e.isBlockSectorMode) {407ERROR_LOG(Log::FileSystem, "Unsupported read volume descriptor command on a umd block device");408return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;409}410411if (!Memory::IsValidRange(outdataPtr, 0x800) || outlen < 0x800) {412WARN_LOG_REPORT(Log::FileSystem, "sceIoIoctl: Invalid out pointer %08x while reading ISO9660 volume descriptor", outdataPtr);413return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;414}415416INFO_LOG(Log::sceIo, "sceIoIoctl: reading ISO9660 volume descriptor read");417blockDevice->ReadBlock(16, Memory::GetPointerWriteUnchecked(outdataPtr));418return 0;419420// Get ISO9660 path table (from open ISO9660 file.)421case 0x01020002:422if (e.isBlockSectorMode) {423ERROR_LOG(Log::FileSystem, "Unsupported read path table command on a umd block device");424return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;425}426427VolDescriptor desc;428blockDevice->ReadBlock(16, (u8 *)&desc);429if (outlen < (u32)desc.pathTableLength) {430return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;431} else {432int block = (u16)desc.firstLETableSector;433u32 size = Memory::ValidSize(outdataPtr, (u32)desc.pathTableLength);434u8 *out = Memory::GetPointerWriteRange(outdataPtr, size);435436int blocks = size / blockDevice->GetBlockSize();437blockDevice->ReadBlocks(block, blocks, out);438size -= blocks * blockDevice->GetBlockSize();439out += blocks * blockDevice->GetBlockSize();440441// The remaining (or, usually, only) partial sector.442if (size > 0) {443u8 temp[2048];444blockDevice->ReadBlock(block, temp);445memcpy(out, temp, size);446}447return 0;448}449}450return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;451}452453PSPDevType ISOFileSystem::DevType(u32 handle) {454EntryMap::iterator iter = entries.find(handle);455if (iter == entries.end())456return PSPDevType::FILE;457PSPDevType type = iter->second.isBlockSectorMode ? PSPDevType::BLOCK : PSPDevType::FILE;458if (iter->second.isRawSector)459type |= PSPDevType::EMU_LBN;460return type;461}462463FileSystemFlags ISOFileSystem::Flags() {464// TODO: Here may be a good place to force things, in case users recompress games465// as PBP or CSO when they were originally the other type.466return blockDevice->IsDisc() ? FileSystemFlags::UMD : FileSystemFlags::CARD;467}468469size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)470{471int ignored;472return ReadFile(handle, pointer, size, ignored);473}474475size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {476EntryMap::iterator iter = entries.find(handle);477if (iter != entries.end()) {478OpenFileEntry &e = iter->second;479480if (size < 0) {481ERROR_LOG_REPORT(Log::FileSystem, "Invalid read for %lld bytes from umd %s", size, e.file ? e.file->name.c_str() : "device");482return 0;483}484485if (e.isBlockSectorMode) {486// Whole sectors! Shortcut to this simple code.487blockDevice->ReadBlocks(e.seekPos, (int)size, pointer);488if (abs((int)lastReadBlock_ - (int)e.seekPos) > 100) {489// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.490usec = 100000;491}492e.seekPos += (int)size;493lastReadBlock_ = e.seekPos;494return (int)size;495}496497u64 positionOnIso;498s64 fileSize;499if (e.isRawSector) {500positionOnIso = e.sectorStart * 2048ULL + e.seekPos;501fileSize = (s64)e.openSize;502} else if (e.file == nullptr) {503ERROR_LOG(Log::FileSystem, "File no longer exists (loaded savestate with different ISO?)");504return 0;505} else {506positionOnIso = e.file->startingPosition + e.seekPos;507fileSize = e.file->size;508}509510if ((s64)e.seekPos > fileSize) {511WARN_LOG(Log::FileSystem, "Read starting outside of file, at %lld / %lld", (s64)e.seekPos, fileSize);512return 0;513}514if ((s64)e.seekPos + size > fileSize) {515// Clamp to the remaining size, but read what we can.516const s64 newSize = fileSize - (s64)e.seekPos;517// Reading beyond the file is really quite normal behavior (if return value handled correctly), so518// not doing WARN here. Still, can potentially be useful to see so leaving at INFO.519if (newSize == 0) {520INFO_LOG(Log::FileSystem, "Attempted read at end of file, 0-size read simulated");521} else {522INFO_LOG(Log::FileSystem, "Reading beyond end of file from seekPos %d, clamping size %lld to %lld", e.seekPos, size, newSize);523}524size = newSize;525}526527// Okay, we have size and position, let's rock.528const int firstBlockOffset = positionOnIso & 2047;529const int firstBlockSize = firstBlockOffset == 0 ? 0 : (int)std::min(size, 2048LL - firstBlockOffset);530const int lastBlockSize = (size - firstBlockSize) & 2047;531const s64 middleSize = size - firstBlockSize - lastBlockSize;532u32 secNum = (u32)(positionOnIso / 2048);533u8 theSector[2048];534535if ((middleSize & 2047) != 0) {536ERROR_LOG(Log::FileSystem, "Remaining size should be aligned");537}538539const u8 *const start = pointer;540if (firstBlockSize > 0) {541blockDevice->ReadBlock(secNum++, theSector);542memcpy(pointer, theSector + firstBlockOffset, firstBlockSize);543pointer += firstBlockSize;544}545if (middleSize > 0) {546const u32 sectors = (u32)(middleSize / 2048);547blockDevice->ReadBlocks(secNum, sectors, pointer);548secNum += sectors;549pointer += middleSize;550}551if (lastBlockSize > 0) {552blockDevice->ReadBlock(secNum++, theSector);553memcpy(pointer, theSector, lastBlockSize);554pointer += lastBlockSize;555}556557size_t totalBytes = pointer - start;558if (abs((int)lastReadBlock_ - (int)secNum) > 100) {559// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.560usec = 100000;561}562lastReadBlock_ = secNum;563e.seekPos += (unsigned int)totalBytes;564return (size_t)totalBytes;565} else {566//This shouldn't happen...567ERROR_LOG(Log::FileSystem, "Hey, what are you doing? Reading non-open files?");568return 0;569}570}571572size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) {573ERROR_LOG(Log::FileSystem, "Hey, what are you doing? You can't write to an ISO!");574return 0;575}576577size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) {578ERROR_LOG(Log::FileSystem, "Hey, what are you doing? You can't write to an ISO!");579return 0;580}581582size_t ISOFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {583EntryMap::iterator iter = entries.find(handle);584if (iter != entries.end()) {585OpenFileEntry &e = iter->second;586switch (type)587{588case FILEMOVE_BEGIN:589e.seekPos = position;590break;591case FILEMOVE_CURRENT:592e.seekPos += position;593break;594case FILEMOVE_END:595if (e.isRawSector)596e.seekPos = e.openSize + position;597else598e.seekPos = (unsigned int)(e.file->size + position);599break;600}601return (size_t)e.seekPos;602} else {603//This shouldn't happen...604ERROR_LOG(Log::FileSystem, "Hey, what are you doing? Seeking in non-open files?");605return 0;606}607}608609PSPFileInfo ISOFileSystem::GetFileInfo(std::string filename) {610if (filename.compare(0,8,"/sce_lbn") == 0) {611u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;612parseLBN(filename, §orStart, &readSize);613614PSPFileInfo fileInfo;615fileInfo.name = filename;616fileInfo.exists = true;617fileInfo.type = FILETYPE_NORMAL;618fileInfo.size = readSize;619fileInfo.access = 0444;620fileInfo.startSector = sectorStart;621fileInfo.isOnSectorSystem = true;622fileInfo.numSectors = (readSize + sectorSize - 1) / sectorSize;623return fileInfo;624}625626TreeEntry *entry = GetFromPath(filename, false);627PSPFileInfo x;628if (entry) {629x.name = entry->name;630// Strangely, it seems to be executable even for files.631x.access = 0555;632x.size = entry->size;633x.exists = true;634x.type = entry->isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;635x.isOnSectorSystem = true;636x.startSector = entry->startingPosition / 2048;637}638return x;639}640641std::vector<PSPFileInfo> ISOFileSystem::GetDirListing(const std::string &path, bool *exists) {642std::vector<PSPFileInfo> myVector;643TreeEntry *entry = GetFromPath(path);644if (!entry) {645if (exists)646*exists = false;647return myVector;648}649if (entry == &entireISO) {650entry = GetFromPath("/");651}652653const std::string dot(".");654const std::string dotdot("..");655656for (size_t i = 0; i < entry->children.size(); i++) {657TreeEntry *e = entry->children[i];658659// do not include the relative entries in the list660if (e->name == dot || e->name == dotdot)661continue;662663PSPFileInfo x;664x.name = e->name;665// Strangely, it seems to be executable even for files.666x.access = 0555;667x.exists = true;668x.size = e->size;669x.type = e->isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;670x.isOnSectorSystem = true;671x.startSector = e->startingPosition/2048;672x.sectorSize = sectorSize;673x.numSectors = (u32)((e->size + sectorSize - 1) / sectorSize);674myVector.push_back(x);675}676if (exists)677*exists = true;678return myVector;679}680681std::string ISOFileSystem::EntryFullPath(TreeEntry *e) {682if (e == &entireISO)683return "";684685size_t fullLen = 0;686TreeEntry *cur = e;687while (cur != NULL && cur != treeroot) {688// For the "/".689fullLen += 1 + cur->name.size();690cur = cur->parent;691}692693std::string path;694path.resize(fullLen);695696cur = e;697while (cur != NULL && cur != treeroot) {698path.replace(fullLen - cur->name.size(), cur->name.size(), cur->name);699path.replace(fullLen - cur->name.size() - 1, 1, "/");700fullLen -= 1 + cur->name.size();701cur = cur->parent;702}703704return path;705}706707ISOFileSystem::TreeEntry::~TreeEntry() {708for (size_t i = 0; i < children.size(); ++i)709delete children[i];710children.clear();711}712713void ISOFileSystem::DoState(PointerWrap &p) {714auto s = p.Section("ISOFileSystem", 1, 2);715if (!s)716return;717718int n = (int) entries.size();719Do(p, n);720721if (p.mode == p.MODE_READ) {722entries.clear();723for (int i = 0; i < n; ++i) {724u32 fd = 0;725OpenFileEntry of;726727Do(p, fd);728Do(p, of.seekPos);729Do(p, of.isRawSector);730Do(p, of.isBlockSectorMode);731Do(p, of.sectorStart);732Do(p, of.openSize);733734bool hasFile = false;735Do(p, hasFile);736if (hasFile) {737std::string path;738Do(p, path);739of.file = GetFromPath(path);740} else {741of.file = NULL;742}743744entries[fd] = of;745}746} else {747for (EntryMap::iterator it = entries.begin(), end = entries.end(); it != end; ++it) {748OpenFileEntry &of = it->second;749Do(p, it->first);750Do(p, of.seekPos);751Do(p, of.isRawSector);752Do(p, of.isBlockSectorMode);753Do(p, of.sectorStart);754Do(p, of.openSize);755756bool hasFile = of.file != NULL;757Do(p, hasFile);758if (hasFile) {759std::string path = EntryFullPath(of.file);760Do(p, path);761}762}763}764765if (s >= 2) {766Do(p, lastReadBlock_);767} else {768lastReadBlock_ = 0;769}770}771772773