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