CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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