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/VirtualDiscFileSystem.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 "ppsspp_config.h"
19
#ifdef __MINGW32__
20
#include <unistd.h>
21
#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
22
#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L
23
#endif
24
#endif
25
#include <ctime>
26
27
#include "Common/File/FileUtil.h"
28
#include "Common/File/DirListing.h"
29
#include "Common/StringUtils.h"
30
#include "Common/Serialize/Serializer.h"
31
#include "Common/Serialize/SerializeFuncs.h"
32
#include "Common/SysError.h"
33
#include "Core/FileSystems/VirtualDiscFileSystem.h"
34
#include "Core/FileSystems/ISOFileSystem.h"
35
#include "Core/HLE/sceKernel.h"
36
#include "Core/Reporting.h"
37
#include "Common/Data/Encoding/Utf8.h"
38
39
#ifdef _WIN32
40
#include "Common/CommonWindows.h"
41
#include <sys/stat.h>
42
#if PPSSPP_PLATFORM(UWP)
43
#include <fileapifromapp.h>
44
#endif
45
#else
46
#include <dirent.h>
47
#include <unistd.h>
48
#include <sys/stat.h>
49
#include <ctype.h>
50
#if !PPSSPP_PLATFORM(SWITCH)
51
#include <dlfcn.h>
52
#endif
53
#endif
54
55
const std::string INDEX_FILENAME = ".ppsspp-index.lst";
56
57
VirtualDiscFileSystem::VirtualDiscFileSystem(IHandleAllocator *_hAlloc, const Path &_basePath)
58
: basePath(_basePath), currentBlockIndex(0) {
59
hAlloc = _hAlloc;
60
LoadFileListIndex();
61
}
62
63
VirtualDiscFileSystem::~VirtualDiscFileSystem() {
64
for (auto iter = entries.begin(), end = entries.end(); iter != end; ++iter) {
65
if (iter->second.type != VFILETYPE_ISO) {
66
iter->second.Close();
67
}
68
}
69
for (auto iter = handlers.begin(), end = handlers.end(); iter != end; ++iter) {
70
delete iter->second;
71
}
72
}
73
74
void VirtualDiscFileSystem::LoadFileListIndex() {
75
const Path filename = basePath / INDEX_FILENAME;
76
if (!File::Exists(filename)) {
77
return;
78
}
79
80
FILE *f = File::OpenCFile(filename, "r");
81
if (!f) {
82
return;
83
}
84
85
static const int MAX_LINE_SIZE = 2048;
86
char linebuf[MAX_LINE_SIZE]{};
87
while (fgets(linebuf, MAX_LINE_SIZE, f)) {
88
std::string line = linebuf;
89
// Strip newline from fgets.
90
if (!line.empty() && line.back() == '\n')
91
line.resize(line.size() - 1);
92
93
// Ignore any UTF-8 BOM.
94
if (line.substr(0, 3) == "\xEF\xBB\xBF") {
95
line = line.substr(3);
96
}
97
98
if (line.empty() || line[0] == ';') {
99
continue;
100
}
101
102
FileListEntry entry = {""};
103
104
// Syntax: HEXPOS filename or HEXPOS filename:handler
105
size_t filename_pos = line.find(' ');
106
if (filename_pos == line.npos) {
107
ERROR_LOG(Log::FileSystem, "Unexpected line in %s: %s", INDEX_FILENAME.c_str(), line.c_str());
108
continue;
109
}
110
111
filename_pos++;
112
// Strip any slash prefix.
113
while (filename_pos < line.length() && line[filename_pos] == '/') {
114
filename_pos++;
115
}
116
117
// Check if there's a handler specified.
118
size_t handler_pos = line.find(':', filename_pos);
119
if (handler_pos != line.npos) {
120
entry.fileName = line.substr(filename_pos, handler_pos - filename_pos);
121
122
std::string handler = line.substr(handler_pos + 1);
123
size_t trunc = handler.find_last_not_of("\r\n");
124
if (trunc != handler.npos && trunc != handler.size())
125
handler.resize(trunc + 1);
126
127
if (handlers.find(handler) == handlers.end())
128
handlers[handler] = new Handler(handler.c_str(), this);
129
if (handlers[handler]->IsValid())
130
entry.handler = handlers[handler];
131
} else {
132
entry.fileName = line.substr(filename_pos);
133
}
134
size_t trunc = entry.fileName.find_last_not_of("\r\n");
135
if (trunc != entry.fileName.npos && trunc != entry.fileName.size())
136
entry.fileName.resize(trunc + 1);
137
138
entry.firstBlock = (u32)strtol(line.c_str(), NULL, 16);
139
if (entry.handler != NULL && entry.handler->IsValid()) {
140
HandlerFileHandle temp = entry.handler;
141
if (temp.Open(basePath.ToString(), entry.fileName, FILEACCESS_READ)) {
142
entry.totalSize = (u32)temp.Seek(0, FILEMOVE_END);
143
temp.Close();
144
} else {
145
ERROR_LOG(Log::FileSystem, "Unable to open virtual file: %s", entry.fileName.c_str());
146
}
147
} else {
148
entry.totalSize = File::GetFileSize(GetLocalPath(entry.fileName));
149
}
150
151
// Try to keep currentBlockIndex sane, in case there are other files.
152
u32 nextBlock = entry.firstBlock + (entry.totalSize + 2047) / 2048;
153
if (nextBlock > currentBlockIndex) {
154
currentBlockIndex = nextBlock;
155
}
156
157
fileList.push_back(entry);
158
}
159
160
fclose(f);
161
}
162
163
void VirtualDiscFileSystem::DoState(PointerWrap &p)
164
{
165
auto s = p.Section("VirtualDiscFileSystem", 1, 2);
166
if (!s)
167
return;
168
169
int fileListSize = (int)fileList.size();
170
int entryCount = (int)entries.size();
171
172
Do(p, fileListSize);
173
Do(p, entryCount);
174
Do(p, currentBlockIndex);
175
176
FileListEntry dummy = {""};
177
fileList.resize(fileListSize, dummy);
178
179
for (int i = 0; i < fileListSize; i++)
180
{
181
Do(p, fileList[i].fileName);
182
Do(p, fileList[i].firstBlock);
183
Do(p, fileList[i].totalSize);
184
}
185
186
if (p.mode == p.MODE_READ)
187
{
188
entries.clear();
189
190
for (int i = 0; i < entryCount; i++)
191
{
192
u32 fd = 0;
193
OpenFileEntry of(Flags());
194
195
Do(p, fd);
196
Do(p, of.fileIndex);
197
Do(p, of.type);
198
Do(p, of.curOffset);
199
Do(p, of.startOffset);
200
Do(p, of.size);
201
202
// open file
203
if (of.type != VFILETYPE_ISO) {
204
if (fileList[of.fileIndex].handler != NULL) {
205
of.handler = fileList[of.fileIndex].handler;
206
}
207
208
bool success = of.Open(basePath, fileList[of.fileIndex].fileName, FILEACCESS_READ);
209
if (!success) {
210
ERROR_LOG(Log::FileSystem, "Failed to create file handle for %s.", fileList[of.fileIndex].fileName.c_str());
211
} else {
212
if (of.type == VFILETYPE_LBN) {
213
of.Seek(of.curOffset + of.startOffset, FILEMOVE_BEGIN);
214
} else {
215
of.Seek(of.curOffset, FILEMOVE_BEGIN);
216
}
217
}
218
}
219
220
// TODO: I think we only need to write to the map on load?
221
entries[fd] = of;
222
}
223
} else {
224
for (EntryMap::iterator it = entries.begin(), end = entries.end(); it != end; ++it)
225
{
226
OpenFileEntry &of = it->second;
227
228
Do(p, it->first);
229
Do(p, of.fileIndex);
230
Do(p, of.type);
231
Do(p, of.curOffset);
232
Do(p, of.startOffset);
233
Do(p, of.size);
234
}
235
}
236
237
if (s >= 2) {
238
Do(p, lastReadBlock_);
239
} else {
240
lastReadBlock_ = 0;
241
}
242
243
// We don't savestate handlers (loaded on fs load), but if they change, it may not load properly.
244
}
245
246
Path VirtualDiscFileSystem::GetLocalPath(std::string localpath) {
247
if (localpath.empty())
248
return basePath;
249
250
if (localpath[0] == '/')
251
localpath.erase(0,1);
252
//Convert slashes
253
#ifdef _WIN32
254
for (size_t i = 0; i < localpath.size(); i++) {
255
if (localpath[i] == '/')
256
localpath[i] = '\\';
257
}
258
#endif
259
return basePath / localpath;
260
}
261
262
int VirtualDiscFileSystem::getFileListIndex(std::string &fileName)
263
{
264
std::string normalized;
265
if (fileName.length() >= 1 && fileName[0] == '/') {
266
normalized = fileName.substr(1);
267
} else {
268
normalized = fileName;
269
}
270
271
for (size_t i = 0; i < fileList.size(); i++)
272
{
273
if (fileList[i].fileName == normalized)
274
return (int)i;
275
}
276
277
// unknown file - add it
278
Path fullName = GetLocalPath(fileName);
279
if (! File::Exists(fullName)) {
280
#if HOST_IS_CASE_SENSITIVE
281
if (! FixPathCase(basePath, fileName, FPC_FILE_MUST_EXIST))
282
return -1;
283
fullName = GetLocalPath(fileName);
284
285
if (! File::Exists(fullName))
286
return -1;
287
#else
288
return -1;
289
#endif
290
}
291
292
if (File::IsDirectory(fullName))
293
return -1;
294
295
FileListEntry entry = {""};
296
entry.fileName = normalized;
297
entry.totalSize = File::GetFileSize(fullName);
298
entry.firstBlock = currentBlockIndex;
299
currentBlockIndex += (entry.totalSize+2047)/2048;
300
301
fileList.push_back(entry);
302
303
return (int)fileList.size()-1;
304
}
305
306
int VirtualDiscFileSystem::getFileListIndex(u32 accessBlock, u32 accessSize, bool blockMode)
307
{
308
for (size_t i = 0; i < fileList.size(); i++)
309
{
310
if (fileList[i].firstBlock <= accessBlock)
311
{
312
u32 sectorOffset = (accessBlock-fileList[i].firstBlock)*2048;
313
u32 totalFileSize = blockMode ? (fileList[i].totalSize+2047) & ~2047 : fileList[i].totalSize;
314
315
u32 endOffset = sectorOffset+accessSize;
316
if (endOffset <= totalFileSize)
317
{
318
return (int)i;
319
}
320
}
321
}
322
323
return -1;
324
}
325
326
int VirtualDiscFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename)
327
{
328
OpenFileEntry entry(Flags());
329
entry.curOffset = 0;
330
entry.size = 0;
331
entry.startOffset = 0;
332
333
if (filename.empty())
334
{
335
entry.type = VFILETYPE_ISO;
336
entry.fileIndex = -1;
337
338
u32 newHandle = hAlloc->GetNewHandle();
339
entries[newHandle] = entry;
340
341
return newHandle;
342
}
343
344
if (filename.compare(0, 8, "/sce_lbn") == 0)
345
{
346
u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
347
parseLBN(filename, &sectorStart, &readSize);
348
349
entry.type = VFILETYPE_LBN;
350
entry.size = readSize;
351
352
int fileIndex = getFileListIndex(sectorStart,readSize);
353
if (fileIndex == -1)
354
{
355
ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem: sce_lbn used without calling fileinfo.");
356
return 0;
357
}
358
entry.fileIndex = (u32)fileIndex;
359
360
entry.startOffset = (sectorStart-fileList[entry.fileIndex].firstBlock)*2048;
361
362
// now we just need an actual file handle
363
if (fileList[entry.fileIndex].handler != NULL) {
364
entry.handler = fileList[entry.fileIndex].handler;
365
}
366
bool success = entry.Open(basePath, fileList[entry.fileIndex].fileName, FILEACCESS_READ);
367
368
if (!success) {
369
if (!(access & FILEACCESS_PPSSPP_QUIET)) {
370
#ifdef _WIN32
371
ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem::OpenFile: FAILED, %i", (int)GetLastError());
372
#else
373
ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem::OpenFile: FAILED");
374
#endif
375
}
376
return 0;
377
}
378
379
// seek to start
380
entry.Seek(entry.startOffset, FILEMOVE_BEGIN);
381
382
u32 newHandle = hAlloc->GetNewHandle();
383
entries[newHandle] = entry;
384
385
return newHandle;
386
}
387
388
entry.type = VFILETYPE_NORMAL;
389
entry.fileIndex = getFileListIndex(filename);
390
391
if (entry.fileIndex != (u32)-1 && fileList[entry.fileIndex].handler != NULL) {
392
entry.handler = fileList[entry.fileIndex].handler;
393
}
394
bool success = entry.Open(basePath, filename, (FileAccess)(access & FILEACCESS_PSP_FLAGS));
395
396
if (!success) {
397
if (!(access & FILEACCESS_PPSSPP_QUIET)) {
398
#ifdef _WIN32
399
ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem::OpenFile: FAILED, %i - access = %i", (int)GetLastError(), (int)access);
400
#else
401
ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem::OpenFile: FAILED, access = %i", (int)access);
402
#endif
403
}
404
return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;
405
} else {
406
u32 newHandle = hAlloc->GetNewHandle();
407
entries[newHandle] = entry;
408
409
return newHandle;
410
}
411
}
412
413
size_t VirtualDiscFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {
414
EntryMap::iterator iter = entries.find(handle);
415
if (iter != entries.end()) {
416
auto &entry = iter->second;
417
switch (entry.type)
418
{
419
case VFILETYPE_NORMAL:
420
{
421
return entry.Seek(position, type);
422
}
423
case VFILETYPE_LBN:
424
{
425
switch (type)
426
{
427
case FILEMOVE_BEGIN: entry.curOffset = position; break;
428
case FILEMOVE_CURRENT: entry.curOffset += position; break;
429
case FILEMOVE_END: entry.curOffset = entry.size + position; break;
430
}
431
432
u32 off = entry.startOffset + entry.curOffset;
433
entry.Seek(off, FILEMOVE_BEGIN);
434
return entry.curOffset;
435
}
436
case VFILETYPE_ISO:
437
{
438
switch (type)
439
{
440
case FILEMOVE_BEGIN: entry.curOffset = position; break;
441
case FILEMOVE_CURRENT: entry.curOffset += position; break;
442
case FILEMOVE_END: entry.curOffset = currentBlockIndex + position; break;
443
}
444
445
return entry.curOffset;
446
}
447
}
448
return 0;
449
} else {
450
//This shouldn't happen...
451
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot seek in file that hasn't been opened: %08x", handle);
452
return 0;
453
}
454
}
455
456
size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
457
int ignored;
458
return ReadFile(handle, pointer, size, ignored);
459
}
460
461
size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {
462
EntryMap::iterator iter = entries.find(handle);
463
if (iter != entries.end()) {
464
if (size < 0) {
465
ERROR_LOG_REPORT(Log::FileSystem, "Invalid read for %lld bytes from virtual umd", size);
466
return 0;
467
}
468
469
// it's the whole iso... it could reference any of the files on the disc.
470
// For now let's just open and close the files on demand. Can certainly be done
471
// better though
472
if (iter->second.type == VFILETYPE_ISO)
473
{
474
int fileIndex = getFileListIndex(iter->second.curOffset,size*2048,true);
475
if (fileIndex == -1)
476
{
477
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Reading from unknown address in %08x at %08llx", handle, iter->second.curOffset);
478
return 0;
479
}
480
481
OpenFileEntry temp(Flags());
482
if (fileList[fileIndex].handler != NULL) {
483
temp.handler = fileList[fileIndex].handler;
484
}
485
bool success = temp.Open(basePath, fileList[fileIndex].fileName, FILEACCESS_READ);
486
487
if (!success)
488
{
489
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Error opening file %s", fileList[fileIndex].fileName.c_str());
490
return 0;
491
}
492
493
u32 startOffset = (iter->second.curOffset-fileList[fileIndex].firstBlock)*2048;
494
size_t bytesRead;
495
496
temp.Seek(startOffset, FILEMOVE_BEGIN);
497
498
u32 remainingSize = fileList[fileIndex].totalSize-startOffset;
499
if (remainingSize < size * 2048)
500
{
501
// the file doesn't fill the whole last sector
502
// read what's there and zero fill the rest like on a real disc
503
bytesRead = temp.Read(pointer, remainingSize);
504
memset(&pointer[bytesRead], 0, size * 2048 - bytesRead);
505
} else {
506
bytesRead = temp.Read(pointer, size * 2048);
507
}
508
509
temp.Close();
510
511
iter->second.curOffset += size;
512
// TODO: This probably isn't enough...
513
if (abs((int)lastReadBlock_ - (int)iter->second.curOffset) > 100) {
514
// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
515
usec = 100000;
516
}
517
lastReadBlock_ = iter->second.curOffset;
518
return size;
519
}
520
521
if (iter->second.type == VFILETYPE_LBN && iter->second.curOffset + size > iter->second.size) {
522
// Clamp to the remaining size, but read what we can.
523
const s64 newSize = iter->second.size - iter->second.curOffset;
524
WARN_LOG(Log::FileSystem, "VirtualDiscFileSystem: Reading beyond end of file, clamping size %lld to %lld", size, newSize);
525
size = newSize;
526
}
527
528
size_t bytesRead = iter->second.Read(pointer, size);
529
iter->second.curOffset += bytesRead;
530
return bytesRead;
531
} else {
532
//This shouldn't happen...
533
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot read file that hasn't been opened: %08x", handle);
534
return 0;
535
}
536
}
537
538
void VirtualDiscFileSystem::CloseFile(u32 handle) {
539
EntryMap::iterator iter = entries.find(handle);
540
if (iter != entries.end()) {
541
hAlloc->FreeHandle(handle);
542
iter->second.Close();
543
entries.erase(iter);
544
} else {
545
//This shouldn't happen...
546
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot close file that hasn't been opened: %08x", handle);
547
}
548
}
549
550
bool VirtualDiscFileSystem::OwnsHandle(u32 handle) {
551
EntryMap::iterator iter = entries.find(handle);
552
return (iter != entries.end());
553
}
554
555
int VirtualDiscFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) {
556
// TODO: How to support these?
557
return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;
558
}
559
560
PSPDevType VirtualDiscFileSystem::DevType(u32 handle) {
561
EntryMap::iterator iter = entries.find(handle);
562
if (iter == entries.end())
563
return PSPDevType::FILE;
564
PSPDevType type = iter->second.type == VFILETYPE_ISO ? PSPDevType::BLOCK : PSPDevType::FILE;
565
if (iter->second.type == VFILETYPE_LBN)
566
type |= PSPDevType::EMU_LBN;
567
return type;
568
}
569
570
PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) {
571
PSPFileInfo x;
572
x.name = filename;
573
x.access = FILEACCESS_READ;
574
575
if (filename.compare(0,8,"/sce_lbn") == 0) {
576
u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
577
parseLBN(filename, &sectorStart, &readSize);
578
579
PSPFileInfo fileInfo;
580
fileInfo.name = filename;
581
fileInfo.exists = true;
582
fileInfo.type = FILETYPE_NORMAL;
583
fileInfo.size = readSize;
584
fileInfo.access = 0444;
585
fileInfo.startSector = sectorStart;
586
fileInfo.isOnSectorSystem = true;
587
fileInfo.numSectors = (readSize + 2047) / 2048;
588
return fileInfo;
589
}
590
591
int fileIndex = getFileListIndex(filename);
592
if (fileIndex != -1 && fileList[fileIndex].handler != NULL) {
593
x.type = FILETYPE_NORMAL;
594
x.isOnSectorSystem = true;
595
x.startSector = fileList[fileIndex].firstBlock;
596
x.access = 0555;
597
598
HandlerFileHandle temp = fileList[fileIndex].handler;
599
if (temp.Open(basePath.ToString(), filename, FILEACCESS_READ)) {
600
x.exists = true;
601
x.size = temp.Seek(0, FILEMOVE_END);
602
temp.Close();
603
}
604
605
// TODO: Probably should include dates or something...
606
return x;
607
}
608
609
Path fullName = GetLocalPath(filename);
610
if (!File::Exists(fullName)) {
611
#if HOST_IS_CASE_SENSITIVE
612
if (! FixPathCase(basePath, filename, FPC_FILE_MUST_EXIST))
613
return x;
614
fullName = GetLocalPath(filename);
615
616
if (! File::Exists(fullName))
617
return x;
618
#else
619
return x;
620
#endif
621
}
622
623
x.type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
624
x.exists = true;
625
x.access = 0555;
626
if (fileIndex != -1) {
627
x.isOnSectorSystem = true;
628
x.startSector = fileList[fileIndex].firstBlock;
629
}
630
631
if (x.type != FILETYPE_DIRECTORY) {
632
File::FileInfo details;
633
if (!File::GetFileInfo(fullName, &details)) {
634
ERROR_LOG(Log::FileSystem, "DirectoryFileSystem::GetFileInfo: GetFileInfo failed: %s", fullName.c_str());
635
x.size = 0;
636
x.access = 0;
637
} else {
638
x.size = details.size;
639
time_t atime = details.atime;
640
time_t ctime = details.ctime;
641
time_t mtime = details.mtime;
642
643
localtime_r((time_t*)&atime, &x.atime);
644
localtime_r((time_t*)&ctime, &x.ctime);
645
localtime_r((time_t*)&mtime, &x.mtime);
646
}
647
648
// x.startSector was set above in "if (fileIndex != -1)".
649
x.numSectors = (x.size+2047)/2048;
650
}
651
652
return x;
653
}
654
655
#ifdef _WIN32
656
#define FILETIME_FROM_UNIX_EPOCH_US 11644473600000000ULL
657
658
static void tmFromFiletime(tm &dest, const FILETIME &src)
659
{
660
u64 from_1601_us = (((u64) src.dwHighDateTime << 32ULL) + (u64) src.dwLowDateTime) / 10ULL;
661
u64 from_1970_us = from_1601_us - FILETIME_FROM_UNIX_EPOCH_US;
662
663
time_t t = (time_t) (from_1970_us / 1000000UL);
664
localtime_r(&t, &dest);
665
}
666
#endif
667
668
std::vector<PSPFileInfo> VirtualDiscFileSystem::GetDirListing(const std::string &path, bool *exists) {
669
std::vector<PSPFileInfo> myVector;
670
671
// TODO(scoped): Switch this over to GetFilesInDir!
672
673
#ifdef _WIN32
674
WIN32_FIND_DATA findData;
675
HANDLE hFind;
676
677
// TODO: Handler files that are virtual might not be listed.
678
679
std::wstring w32path = GetLocalPath(path).ToWString() + L"\\*.*";
680
681
#if PPSSPP_PLATFORM(UWP)
682
hFind = FindFirstFileExFromAppW(w32path.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0);
683
#else
684
hFind = FindFirstFileEx(w32path.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0);
685
#endif
686
if (hFind == INVALID_HANDLE_VALUE) {
687
if (exists)
688
*exists = false;
689
return myVector; //the empty list
690
}
691
692
if (exists)
693
*exists = true;
694
695
for (BOOL retval = 1; retval; retval = FindNextFile(hFind, &findData)) {
696
if (!wcscmp(findData.cFileName, L"..") || !wcscmp(findData.cFileName, L".")) {
697
continue;
698
}
699
700
PSPFileInfo entry;
701
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
702
entry.type = FILETYPE_DIRECTORY;
703
} else {
704
entry.type = FILETYPE_NORMAL;
705
}
706
707
entry.access = 0555;
708
entry.exists = true;
709
entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32);
710
entry.name = ConvertWStringToUTF8(findData.cFileName);
711
tmFromFiletime(entry.atime, findData.ftLastAccessTime);
712
tmFromFiletime(entry.ctime, findData.ftCreationTime);
713
tmFromFiletime(entry.mtime, findData.ftLastWriteTime);
714
entry.isOnSectorSystem = true;
715
716
std::string fullRelativePath = path + "/" + entry.name;
717
int fileIndex = getFileListIndex(fullRelativePath);
718
if (fileIndex != -1)
719
entry.startSector = fileList[fileIndex].firstBlock;
720
myVector.push_back(entry);
721
}
722
FindClose(hFind);
723
#else
724
dirent *dirp;
725
Path localPath = GetLocalPath(path);
726
DIR *dp = opendir(localPath.c_str());
727
728
#if HOST_IS_CASE_SENSITIVE
729
std::string fixedPath = path;
730
if(dp == NULL && FixPathCase(basePath, fixedPath, FPC_FILE_MUST_EXIST)) {
731
// May have failed due to case sensitivity, try again
732
localPath = GetLocalPath(fixedPath);
733
dp = opendir(localPath.c_str());
734
}
735
#endif
736
737
if (dp == NULL) {
738
ERROR_LOG(Log::FileSystem,"Error opening directory %s\n", path.c_str());
739
if (exists)
740
*exists = false;
741
return myVector;
742
}
743
744
if (exists)
745
*exists = true;
746
747
while ((dirp = readdir(dp)) != NULL) {
748
if (!strcmp(dirp->d_name, "..") || !strcmp(dirp->d_name, ".")) {
749
continue;
750
}
751
752
PSPFileInfo entry;
753
struct stat s;
754
std::string fullName = (localPath / std::string(dirp->d_name)).ToString();
755
stat(fullName.c_str(), &s);
756
if (S_ISDIR(s.st_mode))
757
entry.type = FILETYPE_DIRECTORY;
758
else
759
entry.type = FILETYPE_NORMAL;
760
entry.access = 0555;
761
entry.exists = true;
762
entry.name = dirp->d_name;
763
entry.size = s.st_size;
764
localtime_r((time_t*)&s.st_atime,&entry.atime);
765
localtime_r((time_t*)&s.st_ctime,&entry.ctime);
766
localtime_r((time_t*)&s.st_mtime,&entry.mtime);
767
entry.isOnSectorSystem = true;
768
769
std::string fullRelativePath = path + "/" + entry.name;
770
int fileIndex = getFileListIndex(fullRelativePath);
771
if (fileIndex != -1)
772
entry.startSector = fileList[fileIndex].firstBlock;
773
myVector.push_back(entry);
774
}
775
closedir(dp);
776
#endif
777
return myVector;
778
}
779
780
size_t VirtualDiscFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
781
{
782
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot write to file on virtual disc");
783
return 0;
784
}
785
786
size_t VirtualDiscFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec)
787
{
788
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot write to file on virtual disc");
789
return 0;
790
}
791
792
bool VirtualDiscFileSystem::MkDir(const std::string &dirname)
793
{
794
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot create directory on virtual disc");
795
return false;
796
}
797
798
bool VirtualDiscFileSystem::RmDir(const std::string &dirname)
799
{
800
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot remove directory on virtual disc");
801
return false;
802
}
803
804
int VirtualDiscFileSystem::RenameFile(const std::string &from, const std::string &to)
805
{
806
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot rename file on virtual disc");
807
return -1;
808
}
809
810
bool VirtualDiscFileSystem::RemoveFile(const std::string &filename)
811
{
812
ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot remove file on virtual disc");
813
return false;
814
}
815
816
void VirtualDiscFileSystem::HandlerLogger(void *arg, HandlerHandle handle, LogLevel level, const char *msg) {
817
VirtualDiscFileSystem *sys = static_cast<VirtualDiscFileSystem *>(arg);
818
819
// TODO: Probably could do this smarter / use a lookup.
820
const char *filename = NULL;
821
for (auto it = sys->entries.begin(), end = sys->entries.end(); it != end; ++it) {
822
if (it->second.fileIndex != (u32)-1 && it->second.handler.handle == handle) {
823
filename = sys->fileList[it->second.fileIndex].fileName.c_str();
824
break;
825
}
826
}
827
828
if (filename != NULL) {
829
GENERIC_LOG(Log::FileSystem, level, "%s: %s", filename, msg);
830
} else {
831
GENERIC_LOG(Log::FileSystem, level, "%s", msg);
832
}
833
}
834
835
VirtualDiscFileSystem::Handler::Handler(const char *filename, VirtualDiscFileSystem *const sys)
836
: sys_(sys) {
837
#if !PPSSPP_PLATFORM(SWITCH)
838
#ifdef _WIN32
839
#if PPSSPP_PLATFORM(UWP)
840
#define dlopen(name, ignore) (void *)LoadPackagedLibrary(ConvertUTF8ToWString(name).c_str(), 0)
841
#define dlsym(mod, name) GetProcAddress((HMODULE)mod, name)
842
#define dlclose(mod) FreeLibrary((HMODULE)mod)
843
#else
844
#define dlopen(name, ignore) (void *)LoadLibrary(ConvertUTF8ToWString(name).c_str())
845
#define dlsym(mod, name) GetProcAddress((HMODULE)mod, name)
846
#define dlclose(mod) FreeLibrary((HMODULE)mod)
847
#endif
848
#endif
849
850
library = dlopen(filename, RTLD_LOCAL | RTLD_NOW);
851
if (library != NULL) {
852
Init = (InitFunc)dlsym(library, "Init");
853
Shutdown = (ShutdownFunc)dlsym(library, "Shutdown");
854
Open = (OpenFunc)dlsym(library, "Open");
855
Seek = (SeekFunc)dlsym(library, "Seek");
856
Read = (ReadFunc)dlsym(library, "Read");
857
Close = (CloseFunc)dlsym(library, "Close");
858
859
VersionFunc Version = (VersionFunc)dlsym(library, "Version");
860
if (Version && Version() >= 2) {
861
ShutdownV2 = (ShutdownV2Func)Shutdown;
862
}
863
864
if (!Init || !Shutdown || !Open || !Seek || !Read || !Close) {
865
ERROR_LOG(Log::FileSystem, "Unable to find all handler functions: %s", filename);
866
dlclose(library);
867
library = NULL;
868
} else if (!Init(&HandlerLogger, sys)) {
869
ERROR_LOG(Log::FileSystem, "Unable to initialize handler: %s", filename);
870
dlclose(library);
871
library = NULL;
872
}
873
} else {
874
ERROR_LOG(Log::FileSystem, "Unable to load handler '%s': %s", filename, GetLastErrorMsg().c_str());
875
}
876
#ifdef _WIN32
877
#undef dlopen
878
#undef dlsym
879
#undef dlclose
880
#endif
881
#endif
882
}
883
884
VirtualDiscFileSystem::Handler::~Handler() {
885
if (library != NULL) {
886
if (ShutdownV2)
887
ShutdownV2(sys_);
888
else
889
Shutdown();
890
891
#if !PPSSPP_PLATFORM(UWP) && !PPSSPP_PLATFORM(SWITCH)
892
#ifdef _WIN32
893
FreeLibrary((HMODULE)library);
894
#else
895
dlclose(library);
896
#endif
897
#endif
898
}
899
}
900
901
902