Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/FileSystems/ISOFileSystem.cpp
3186 views
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <algorithm>
19
#include <cstring>
20
#include <cstdio>
21
22
#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"
29
30
const int sectorSize = 2048;
31
32
bool 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.)
36
37
// If it doesn't starts with /sce_lbn or doesn't have _size, look for a file instead.
38
if (filename.compare(0, sizeof("/sce_lbn") - 1, "/sce_lbn") != 0)
39
return false;
40
size_t size_pos = filename.find("_size");
41
if (size_pos == filename.npos)
42
return false;
43
44
// TODO: Return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT when >= 32 long but passes above checks.
45
if (filename.size() >= 32)
46
return false;
47
48
const char *filename_c = filename.c_str();
49
size_t pos = strlen("/sce_lbn");
50
51
if (sscanf(filename_c + pos, "%x", sectorStart) != 1)
52
*sectorStart = 0;
53
54
pos = size_pos + strlen("_size");
55
56
if (sscanf(filename_c + pos, "%x", readSize) != 1)
57
*readSize = 0;
58
59
return true;
60
}
61
62
#pragma pack(push)
63
#pragma pack(1)
64
struct u32_le_be_pair {
65
u8 valueLE[4];
66
u8 valueBE[4];
67
operator u32() const {
68
return valueLE[0] + (valueLE[1] << 8) + (valueLE[2] << 16) + (valueLE[3] << 24);
69
}
70
};
71
72
struct u16_le_be_pair {
73
u8 valueLE[2];
74
u8 valueBE[2];
75
operator u16() const {
76
return valueLE[0] + (valueLE[1] << 8);
77
}
78
};
79
80
struct DirectoryEntry {
81
u8 size;
82
u8 sectorsInExtendedRecord;
83
u32_le_be_pair firstDataSector; // LBA
84
u32_le_be_pair dataLength; // Size
85
u8 years;
86
u8 month;
87
u8 day;
88
u8 hour;
89
u8 minute;
90
u8 second;
91
u8 offsetFromGMT;
92
u8 flags; // 2 = directory
93
u8 fileUnitSize;
94
u8 interleaveGap;
95
u16_le_be_pair volSeqNumber;
96
u8 identifierLength; //identifier comes right after
97
u8 firstIdChar;
98
};
99
100
struct DirectorySector {
101
DirectoryEntry entry;
102
char space[2048-sizeof(DirectoryEntry)];
103
};
104
105
struct VolDescriptor {
106
char type;
107
char cd001[6];
108
char version;
109
char sysid[32];
110
char volid[32];
111
char zeros[8];
112
u32_le_be_pair numSectors;
113
char morezeros[32];
114
u16_le_be_pair volSetSize;
115
u16_le_be_pair volSeqNum;
116
u16_le_be_pair sectorSize;
117
u32_le_be_pair pathTableLength;
118
u16_le_be_pair firstLETableSector;
119
u16_le_be_pair secondLETableSector;
120
u16_le_be_pair firstBETableSector;
121
u16_le_be_pair secondBETableSector;
122
DirectoryEntry root;
123
char volumeSetIdentifier[128];
124
char publisherIdentifier[128];
125
char dataPreparerIdentifier[128];
126
char applicationIdentifier[128];
127
char copyrightFileIdentifier[37];
128
char abstractFileIdentifier[37];
129
char bibliographicalFileIdentifier[37];
130
char volCreationDateTime[17];
131
char mreModDateTime[17];
132
char volExpireDateTime[17];
133
char volEffectiveDateTime[17];
134
char one;
135
char zero;
136
char reserved[512];
137
char zeroos[653];
138
};
139
140
#pragma pack(pop)
141
142
ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice) {
143
blockDevice = _blockDevice;
144
hAlloc = _hAlloc;
145
146
VolDescriptor desc;
147
if (!blockDevice->ReadBlock(16, (u8*)&desc))
148
blockDevice->NotifyReadError();
149
150
entireISO.name.clear();
151
entireISO.isDirectory = false;
152
entireISO.startingPosition = 0;
153
entireISO.size = _blockDevice->GetNumBlocks();
154
entireISO.flags = 0;
155
entireISO.parent = NULL;
156
157
treeroot = new TreeEntry();
158
treeroot->isDirectory = true;
159
treeroot->startingPosition = 0;
160
treeroot->size = 0;
161
treeroot->flags = 0;
162
treeroot->parent = NULL;
163
treeroot->valid = false;
164
165
if (memcmp(desc.cd001, "CD001", 5)) {
166
ERROR_LOG(Log::FileSystem, "ISO looks bogus, expected CD001 signature not present? Giving up...");
167
return;
168
}
169
170
treeroot->startsector = desc.root.firstDataSector;
171
treeroot->dirsize = desc.root.dataLength;
172
}
173
174
ISOFileSystem::~ISOFileSystem() {
175
delete blockDevice;
176
delete treeroot;
177
}
178
179
std::string ISOFileSystem::TreeEntry::BuildPath() {
180
if (parent) {
181
return parent->BuildPath() + "/" + name;
182
} else {
183
return name;
184
}
185
}
186
187
void ISOFileSystem::ReadDirectory(TreeEntry *root) {
188
for (u32 secnum = root->startsector, endsector = root->startsector + (root->dirsize + 2047) / 2048; secnum < endsector; ++secnum) {
189
u8 theSector[2048];
190
if (!blockDevice->ReadBlock(secnum, theSector)) {
191
blockDevice->NotifyReadError();
192
ERROR_LOG(Log::FileSystem, "Error reading block for directory '%s' in sector %d - skipping", root->name.c_str(), secnum);
193
root->valid = true; // Prevents re-reading
194
return;
195
}
196
lastReadBlock_ = secnum; // Hm, this could affect timing... but lazy loading is probably more realistic.
197
198
for (int offset = 0; offset < 2048; ) {
199
DirectoryEntry &dir = *(DirectoryEntry *)&theSector[offset];
200
u8 sz = theSector[offset];
201
202
// Nothing left in this sector. There might be more in the next one.
203
if (sz == 0)
204
break;
205
206
const int IDENTIFIER_OFFSET = 33;
207
if (offset + IDENTIFIER_OFFSET + dir.identifierLength > 2048) {
208
blockDevice->NotifyReadError();
209
ERROR_LOG(Log::FileSystem, "Directory entry crosses sectors, corrupt iso?");
210
return;
211
}
212
213
offset += dir.size;
214
215
bool isFile = (dir.flags & 2) ? false : true;
216
bool relative;
217
218
TreeEntry *entry = new TreeEntry();
219
if (dir.identifierLength == 1 && (dir.firstIdChar == '\x00' || dir.firstIdChar == '.')) {
220
entry->name = ".";
221
relative = true;
222
} else if (dir.identifierLength == 1 && dir.firstIdChar == '\x01') {
223
entry->name = "..";
224
relative = true;
225
} else {
226
entry->name = std::string((const char *)&dir.firstIdChar, dir.identifierLength);
227
relative = false;
228
}
229
230
entry->size = dir.dataLength;
231
entry->startingPosition = dir.firstDataSector * 2048;
232
entry->isDirectory = !isFile;
233
entry->flags = dir.flags;
234
entry->parent = root;
235
entry->startsector = dir.firstDataSector;
236
entry->dirsize = dir.dataLength;
237
entry->valid = isFile; // Can pre-mark as valid if file, as we don't recurse into those.
238
VERBOSE_LOG(Log::FileSystem, "%s: %s %08x %08x %d", entry->isDirectory ? "D" : "F", entry->name.c_str(), (u32)dir.firstDataSector, entry->startingPosition, entry->startingPosition);
239
240
// Round down to avoid any false reports.
241
if (isFile && dir.firstDataSector + (dir.dataLength / 2048) > blockDevice->GetNumBlocks()) {
242
blockDevice->NotifyReadError();
243
ERROR_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
}
245
246
if (entry->isDirectory && !relative) {
247
if (entry->startsector == root->startsector) {
248
blockDevice->NotifyReadError();
249
ERROR_LOG(Log::FileSystem, "WARNING: Appear to have a recursive file system, breaking recursion. Probably corrupt ISO.");
250
}
251
}
252
root->children.push_back(entry);
253
}
254
}
255
root->valid = true;
256
}
257
258
ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(const std::string &path, bool catchError) {
259
const size_t pathLength = path.length();
260
261
if (pathLength == 0) {
262
// Ah, the device! "umd0:"
263
return &entireISO;
264
}
265
266
size_t pathIndex = 0;
267
268
// Skip "./"
269
if (pathLength > pathIndex + 1 && path[pathIndex] == '.' && path[pathIndex + 1] == '/')
270
pathIndex += 2;
271
272
// Skip "/"
273
if (pathLength > pathIndex && path[pathIndex] == '/')
274
++pathIndex;
275
276
if (pathLength <= pathIndex)
277
return treeroot;
278
279
TreeEntry *entry = treeroot;
280
while (true) {
281
if (!entry->valid) {
282
ReadDirectory(entry);
283
}
284
TreeEntry *nextEntry = nullptr;
285
std::string name = "";
286
if (pathLength > pathIndex) {
287
size_t nextSlashIndex = path.find_first_of('/', pathIndex);
288
if (nextSlashIndex == std::string::npos)
289
nextSlashIndex = pathLength;
290
291
const std::string firstPathComponent = path.substr(pathIndex, nextSlashIndex - pathIndex);
292
for (size_t i = 0; i < entry->children.size(); i++) {
293
const std::string &n = entry->children[i]->name;
294
if (firstPathComponent == n) {
295
//yay we got it
296
nextEntry = entry->children[i];
297
name = n;
298
break;
299
}
300
}
301
}
302
303
if (nextEntry) {
304
entry = nextEntry;
305
if (!entry->valid)
306
ReadDirectory(entry);
307
pathIndex += name.length();
308
if (pathIndex < pathLength && path[pathIndex] == '/')
309
++pathIndex;
310
311
if (pathLength <= pathIndex)
312
return entry;
313
} else {
314
if (catchError)
315
ERROR_LOG(Log::FileSystem, "File '%s' not found", path.c_str());
316
317
return 0;
318
}
319
}
320
}
321
322
int ISOFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) {
323
OpenFileEntry entry;
324
entry.isRawSector = false;
325
entry.isBlockSectorMode = false;
326
327
if (access & FILEACCESS_WRITE) {
328
ERROR_LOG(Log::FileSystem, "Can't open file '%s' with write access on an ISO partition", filename.c_str());
329
return SCE_KERNEL_ERROR_ERRNO_INVALID_FLAG;
330
}
331
332
if (filename.compare(0, 8, "/sce_lbn") == 0) {
333
// Raw sector read.
334
u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
335
parseLBN(filename, &sectorStart, &readSize);
336
if (sectorStart > blockDevice->GetNumBlocks()) {
337
WARN_LOG(Log::FileSystem, "Unable to open raw sector, out of range: '%s', sector %08x, max %08x", filename.c_str(), sectorStart, blockDevice->GetNumBlocks());
338
return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;
339
}
340
else if (sectorStart == blockDevice->GetNumBlocks())
341
{
342
ERROR_LOG(Log::FileSystem, "Should not be able to open the block after the last on disc! %08x", sectorStart);
343
}
344
345
DEBUG_LOG(Log::FileSystem, "Got a raw sector open: '%s', sector %08x, size %08x", filename.c_str(), sectorStart, readSize);
346
u32 newHandle = hAlloc->GetNewHandle();
347
entry.seekPos = 0;
348
entry.file = 0;
349
entry.isRawSector = true;
350
entry.sectorStart = sectorStart;
351
entry.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.
354
if (strncmp(devicename, "umd0:", 5) == 0 || strncmp(devicename, "umd1:", 5) == 0)
355
entry.isBlockSectorMode = true;
356
357
entries[newHandle] = entry;
358
return newHandle;
359
}
360
361
// May return entireISO for "umd0:".
362
entry.file = GetFromPath(filename, false);
363
if (!entry.file) {
364
return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;
365
}
366
367
if (entry.file == &entireISO)
368
entry.isBlockSectorMode = true;
369
370
entry.seekPos = 0;
371
372
u32 newHandle = hAlloc->GetNewHandle();
373
entries[newHandle] = entry;
374
return newHandle;
375
}
376
377
void ISOFileSystem::CloseFile(u32 handle) {
378
EntryMap::iterator iter = entries.find(handle);
379
if (iter != entries.end()) {
380
//CloseHandle((*iter).second.hFile);
381
hAlloc->FreeHandle(handle);
382
entries.erase(iter);
383
} else {
384
//This shouldn't happen...
385
ERROR_LOG(Log::FileSystem, "Hey, what are you doing? Closing non-open files?");
386
}
387
}
388
389
bool ISOFileSystem::OwnsHandle(u32 handle) {
390
EntryMap::iterator iter = entries.find(handle);
391
return (iter != entries.end());
392
}
393
394
int ISOFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) {
395
EntryMap::iterator iter = entries.find(handle);
396
if (iter == entries.end()) {
397
ERROR_LOG(Log::FileSystem, "Ioctl on a bad file handle");
398
return SCE_KERNEL_ERROR_BADF;
399
}
400
401
OpenFileEntry &e = iter->second;
402
403
switch (cmd) {
404
// Get ISO9660 volume descriptor (from open ISO9660 file.)
405
case 0x01020001:
406
if (e.isBlockSectorMode) {
407
ERROR_LOG(Log::FileSystem, "Unsupported read volume descriptor command on a umd block device");
408
return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;
409
}
410
411
if (!Memory::IsValidRange(outdataPtr, 0x800) || outlen < 0x800) {
412
WARN_LOG_REPORT(Log::FileSystem, "sceIoIoctl: Invalid out pointer %08x while reading ISO9660 volume descriptor", outdataPtr);
413
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
414
}
415
416
INFO_LOG(Log::sceIo, "sceIoIoctl: reading ISO9660 volume descriptor read");
417
blockDevice->ReadBlock(16, Memory::GetPointerWriteUnchecked(outdataPtr));
418
return 0;
419
420
// Get ISO9660 path table (from open ISO9660 file.)
421
case 0x01020002:
422
if (e.isBlockSectorMode) {
423
ERROR_LOG(Log::FileSystem, "Unsupported read path table command on a umd block device");
424
return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;
425
}
426
427
VolDescriptor desc;
428
blockDevice->ReadBlock(16, (u8 *)&desc);
429
if (outlen < (u32)desc.pathTableLength) {
430
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
431
} else {
432
int block = (u16)desc.firstLETableSector;
433
u32 size = Memory::ValidSize(outdataPtr, (u32)desc.pathTableLength);
434
u8 *out = Memory::GetPointerWriteRange(outdataPtr, size);
435
436
int blocks = size / blockDevice->GetBlockSize();
437
blockDevice->ReadBlocks(block, blocks, out);
438
size -= blocks * blockDevice->GetBlockSize();
439
out += blocks * blockDevice->GetBlockSize();
440
441
// The remaining (or, usually, only) partial sector.
442
if (size > 0) {
443
u8 temp[2048];
444
blockDevice->ReadBlock(block, temp);
445
memcpy(out, temp, size);
446
}
447
return 0;
448
}
449
}
450
return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;
451
}
452
453
PSPDevType ISOFileSystem::DevType(u32 handle) {
454
EntryMap::iterator iter = entries.find(handle);
455
if (iter == entries.end())
456
return PSPDevType::FILE;
457
PSPDevType type = iter->second.isBlockSectorMode ? PSPDevType::BLOCK : PSPDevType::FILE;
458
if (iter->second.isRawSector)
459
type |= PSPDevType::EMU_LBN;
460
return type;
461
}
462
463
FileSystemFlags ISOFileSystem::Flags() const {
464
// TODO: Here may be a good place to force things, in case users recompress games
465
// as PBP or CSO when they were originally the other type.
466
return blockDevice->IsDisc() ? FileSystemFlags::UMD : FileSystemFlags::CARD;
467
}
468
469
size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
470
{
471
int ignored;
472
return ReadFile(handle, pointer, size, ignored);
473
}
474
475
size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {
476
EntryMap::iterator iter = entries.find(handle);
477
if (iter != entries.end()) {
478
OpenFileEntry &e = iter->second;
479
480
if (size < 0) {
481
ERROR_LOG(Log::FileSystem, "Invalid read for %lld bytes from umd %s", size, e.file ? e.file->name.c_str() : "device");
482
return 0;
483
}
484
485
if (e.isBlockSectorMode) {
486
// Whole sectors! Shortcut to this simple code.
487
blockDevice->ReadBlocks(e.seekPos, (int)size, pointer);
488
if (abs((int)lastReadBlock_ - (int)e.seekPos) > 100) {
489
// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
490
usec = 100000;
491
}
492
e.seekPos += (int)size;
493
lastReadBlock_ = e.seekPos;
494
return (int)size;
495
}
496
497
u64 positionOnIso;
498
s64 fileSize;
499
if (e.isRawSector) {
500
positionOnIso = e.sectorStart * 2048ULL + e.seekPos;
501
fileSize = (s64)e.openSize;
502
} else if (e.file == nullptr) {
503
ERROR_LOG(Log::FileSystem, "File no longer exists (loaded savestate with different ISO?)");
504
return 0;
505
} else {
506
positionOnIso = e.file->startingPosition + e.seekPos;
507
fileSize = e.file->size;
508
}
509
510
if ((s64)e.seekPos > fileSize) {
511
WARN_LOG(Log::FileSystem, "Read starting outside of file, at %lld / %lld", (s64)e.seekPos, fileSize);
512
return 0;
513
}
514
if ((s64)e.seekPos + size > fileSize) {
515
// Clamp to the remaining size, but read what we can.
516
const s64 newSize = fileSize - (s64)e.seekPos;
517
// Reading beyond the file is really quite normal behavior (if return value handled correctly), so
518
// not doing WARN here.
519
if (newSize == 0) {
520
DEBUG_LOG(Log::FileSystem, "Attempted read at end of file, 0-size read simulated");
521
} else {
522
DEBUG_LOG(Log::FileSystem, "Reading beyond end of file from seekPos %d, clamping size %lld to %lld", e.seekPos, size, newSize);
523
}
524
size = newSize;
525
}
526
527
// Okay, we have size and position, let's rock.
528
const int firstBlockOffset = positionOnIso & 2047;
529
const int firstBlockSize = firstBlockOffset == 0 ? 0 : (int)std::min(size, 2048LL - firstBlockOffset);
530
const int lastBlockSize = (size - firstBlockSize) & 2047;
531
const s64 middleSize = size - firstBlockSize - lastBlockSize;
532
u32 secNum = (u32)(positionOnIso / 2048);
533
u8 theSector[2048];
534
535
if ((middleSize & 2047) != 0) {
536
ERROR_LOG(Log::FileSystem, "Remaining size should be aligned");
537
}
538
539
const u8 *const start = pointer;
540
if (firstBlockSize > 0) {
541
blockDevice->ReadBlock(secNum++, theSector);
542
memcpy(pointer, theSector + firstBlockOffset, firstBlockSize);
543
pointer += firstBlockSize;
544
}
545
if (middleSize > 0) {
546
const u32 sectors = (u32)(middleSize / 2048);
547
blockDevice->ReadBlocks(secNum, sectors, pointer);
548
secNum += sectors;
549
pointer += middleSize;
550
}
551
if (lastBlockSize > 0) {
552
blockDevice->ReadBlock(secNum++, theSector);
553
memcpy(pointer, theSector, lastBlockSize);
554
pointer += lastBlockSize;
555
}
556
557
size_t totalBytes = pointer - start;
558
if (abs((int)lastReadBlock_ - (int)secNum) > 100) {
559
// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
560
usec = 100000;
561
}
562
lastReadBlock_ = secNum;
563
e.seekPos += (unsigned int)totalBytes;
564
return (size_t)totalBytes;
565
} else {
566
//This shouldn't happen...
567
ERROR_LOG(Log::FileSystem, "Hey, what are you doing? Reading non-open files?");
568
return 0;
569
}
570
}
571
572
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) {
573
ERROR_LOG(Log::FileSystem, "Hey, what are you doing? You can't write to an ISO!");
574
return 0;
575
}
576
577
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) {
578
ERROR_LOG(Log::FileSystem, "Hey, what are you doing? You can't write to an ISO!");
579
return 0;
580
}
581
582
size_t ISOFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {
583
EntryMap::iterator iter = entries.find(handle);
584
if (iter != entries.end()) {
585
OpenFileEntry &e = iter->second;
586
switch (type)
587
{
588
case FILEMOVE_BEGIN:
589
e.seekPos = position;
590
break;
591
case FILEMOVE_CURRENT:
592
e.seekPos += position;
593
break;
594
case FILEMOVE_END:
595
if (e.isRawSector)
596
e.seekPos = e.openSize + position;
597
else
598
e.seekPos = (unsigned int)(e.file->size + position);
599
break;
600
}
601
return (size_t)e.seekPos;
602
} else {
603
//This shouldn't happen...
604
ERROR_LOG(Log::FileSystem, "Hey, what are you doing? Seeking in non-open files?");
605
return 0;
606
}
607
}
608
609
PSPFileInfo ISOFileSystem::GetFileInfo(std::string filename) {
610
if (filename.compare(0,8,"/sce_lbn") == 0) {
611
u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
612
parseLBN(filename, &sectorStart, &readSize);
613
614
PSPFileInfo fileInfo;
615
fileInfo.name = filename;
616
fileInfo.exists = true;
617
fileInfo.type = FILETYPE_NORMAL;
618
fileInfo.size = readSize;
619
fileInfo.access = 0444;
620
fileInfo.startSector = sectorStart;
621
fileInfo.isOnSectorSystem = true;
622
fileInfo.numSectors = (readSize + sectorSize - 1) / sectorSize;
623
return fileInfo;
624
}
625
626
TreeEntry *entry = GetFromPath(filename, false);
627
PSPFileInfo x;
628
if (entry) {
629
x.name = entry->name;
630
// Strangely, it seems to be executable even for files.
631
x.access = 0555;
632
x.size = entry->size;
633
x.exists = true;
634
x.type = entry->isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
635
x.isOnSectorSystem = true;
636
x.startSector = entry->startingPosition / 2048;
637
}
638
return x;
639
}
640
641
PSPFileInfo ISOFileSystem::GetFileInfoByHandle(u32 handle) {
642
auto iter = entries.find(handle);
643
PSPFileInfo x;
644
if (iter != entries.end()) {
645
const TreeEntry *entry = iter->second.file;
646
x.name = entry->name;
647
// Strangely, it seems to be executable even for files.
648
x.access = 0555;
649
x.size = entry->size;
650
x.exists = true;
651
x.type = entry->isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
652
x.isOnSectorSystem = true;
653
x.startSector = entry->startingPosition / 2048;
654
}
655
return x;
656
}
657
658
std::vector<PSPFileInfo> ISOFileSystem::GetDirListing(const std::string &path, bool *exists) {
659
std::vector<PSPFileInfo> myVector;
660
TreeEntry *entry = GetFromPath(path);
661
if (!entry) {
662
if (exists)
663
*exists = false;
664
return myVector;
665
}
666
if (entry == &entireISO) {
667
entry = GetFromPath("/");
668
}
669
670
const std::string dot(".");
671
const std::string dotdot("..");
672
673
for (size_t i = 0; i < entry->children.size(); i++) {
674
TreeEntry *e = entry->children[i];
675
676
// do not include the relative entries in the list
677
if (e->name == dot || e->name == dotdot)
678
continue;
679
680
PSPFileInfo x;
681
x.name = e->name;
682
// Strangely, it seems to be executable even for files.
683
x.access = 0555;
684
x.exists = true;
685
x.size = e->size;
686
x.type = e->isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
687
x.isOnSectorSystem = true;
688
x.startSector = e->startingPosition/2048;
689
x.sectorSize = sectorSize;
690
x.numSectors = (u32)((e->size + sectorSize - 1) / sectorSize);
691
myVector.push_back(x);
692
}
693
if (exists)
694
*exists = true;
695
return myVector;
696
}
697
698
std::string ISOFileSystem::EntryFullPath(TreeEntry *e) {
699
if (e == &entireISO)
700
return "";
701
702
size_t fullLen = 0;
703
TreeEntry *cur = e;
704
while (cur != NULL && cur != treeroot) {
705
// For the "/".
706
fullLen += 1 + cur->name.size();
707
cur = cur->parent;
708
}
709
710
std::string path;
711
path.resize(fullLen);
712
713
cur = e;
714
while (cur != NULL && cur != treeroot) {
715
path.replace(fullLen - cur->name.size(), cur->name.size(), cur->name);
716
path.replace(fullLen - cur->name.size() - 1, 1, "/");
717
fullLen -= 1 + cur->name.size();
718
cur = cur->parent;
719
}
720
721
return path;
722
}
723
724
ISOFileSystem::TreeEntry::~TreeEntry() {
725
for (size_t i = 0; i < children.size(); ++i)
726
delete children[i];
727
children.clear();
728
}
729
730
void ISOFileSystem::DoState(PointerWrap &p) {
731
auto s = p.Section("ISOFileSystem", 1, 2);
732
if (!s)
733
return;
734
735
int n = (int) entries.size();
736
Do(p, n);
737
738
if (p.mode == p.MODE_READ) {
739
entries.clear();
740
for (int i = 0; i < n; ++i) {
741
u32 fd = 0;
742
OpenFileEntry of;
743
744
Do(p, fd);
745
Do(p, of.seekPos);
746
Do(p, of.isRawSector);
747
Do(p, of.isBlockSectorMode);
748
Do(p, of.sectorStart);
749
Do(p, of.openSize);
750
751
bool hasFile = false;
752
Do(p, hasFile);
753
if (hasFile) {
754
std::string path;
755
Do(p, path);
756
of.file = GetFromPath(path);
757
} else {
758
of.file = NULL;
759
}
760
761
entries[fd] = of;
762
}
763
} else {
764
for (EntryMap::iterator it = entries.begin(), end = entries.end(); it != end; ++it) {
765
OpenFileEntry &of = it->second;
766
Do(p, it->first);
767
Do(p, of.seekPos);
768
Do(p, of.isRawSector);
769
Do(p, of.isBlockSectorMode);
770
Do(p, of.sectorStart);
771
Do(p, of.openSize);
772
773
bool hasFile = of.file != NULL;
774
Do(p, hasFile);
775
if (hasFile) {
776
std::string path = EntryFullPath(of.file);
777
Do(p, path);
778
}
779
}
780
}
781
782
if (s >= 2) {
783
Do(p, lastReadBlock_);
784
} else {
785
lastReadBlock_ = 0;
786
}
787
}
788
789