summaryrefslogtreecommitdiff
path: root/src/core/file_sys
diff options
context:
space:
mode:
authorGravatar bunnei2014-04-08 19:25:03 -0400
committerGravatar bunnei2014-04-08 19:25:03 -0400
commit63e46abdb8764bc97e91bae862c8d461e61b1965 (patch)
treee73f4aa25d7b4015a265e7bbfb6004dab7561027 /src/core/file_sys
parentfixed some license headers that I missed (diff)
downloadyuzu-63e46abdb8764bc97e91bae862c8d461e61b1965.tar.gz
yuzu-63e46abdb8764bc97e91bae862c8d461e61b1965.tar.xz
yuzu-63e46abdb8764bc97e91bae862c8d461e61b1965.zip
got rid of 'src' folders in each sub-project
Diffstat (limited to 'src/core/file_sys')
-rw-r--r--src/core/file_sys/directory_file_system.cpp671
-rw-r--r--src/core/file_sys/directory_file_system.h158
-rw-r--r--src/core/file_sys/file_sys.h138
-rw-r--r--src/core/file_sys/meta_file_system.cpp520
-rw-r--r--src/core/file_sys/meta_file_system.h109
5 files changed, 1596 insertions, 0 deletions
diff --git a/src/core/file_sys/directory_file_system.cpp b/src/core/file_sys/directory_file_system.cpp
new file mode 100644
index 000000000..29369eec4
--- /dev/null
+++ b/src/core/file_sys/directory_file_system.cpp
@@ -0,0 +1,671 @@
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 "chunk_file.h"
19#include "file_util.h"
20#include "directory_file_system.h"
21//#include "ISOFileSystem.h"
22//#include "Core/HLE/sceKernel.h"
23//#include "file/zip_read.h"
24#include "utf8.h"
25
26#if EMU_PLATFORM == PLATFORM_WINDOWS
27#include <windows.h>
28#include <sys/stat.h>
29#else
30#include <dirent.h>
31#include <unistd.h>
32#include <sys/stat.h>
33#include <ctype.h>
34#endif
35
36#if HOST_IS_CASE_SENSITIVE
37static bool FixFilenameCase(const std::string &path, std::string &filename)
38{
39 // Are we lucky?
40 if (File::Exists(path + filename))
41 return true;
42
43 size_t filenameSize = filename.size(); // size in bytes, not characters
44 for (size_t i = 0; i < filenameSize; i++)
45 {
46 filename[i] = tolower(filename[i]);
47 }
48
49 //TODO: lookup filename in cache for "path"
50
51 struct dirent_large { struct dirent entry; char padding[FILENAME_MAX+1]; } diren;
52 struct dirent_large;
53 struct dirent *result = NULL;
54
55 DIR *dirp = opendir(path.c_str());
56 if (!dirp)
57 return false;
58
59 bool retValue = false;
60
61 while (!readdir_r(dirp, (dirent*) &diren, &result) && result)
62 {
63 if (strlen(result->d_name) != filenameSize)
64 continue;
65
66 size_t i;
67 for (i = 0; i < filenameSize; i++)
68 {
69 if (filename[i] != tolower(result->d_name[i]))
70 break;
71 }
72
73 if (i < filenameSize)
74 continue;
75
76 filename = result->d_name;
77 retValue = true;
78 }
79
80 closedir(dirp);
81
82 return retValue;
83}
84
85bool FixPathCase(std::string& basePath, std::string &path, FixPathCaseBehavior behavior)
86{
87 size_t len = path.size();
88
89 if (len == 0)
90 return true;
91
92 if (path[len - 1] == '/')
93 {
94 len--;
95
96 if (len == 0)
97 return true;
98 }
99
100 std::string fullPath;
101 fullPath.reserve(basePath.size() + len + 1);
102 fullPath.append(basePath);
103
104 size_t start = 0;
105 while (start < len)
106 {
107 size_t i = path.find('/', start);
108 if (i == std::string::npos)
109 i = len;
110
111 if (i > start)
112 {
113 std::string component = path.substr(start, i - start);
114
115 // Fix case and stop on nonexistant path component
116 if (FixFilenameCase(fullPath, component) == false) {
117 // Still counts as success if partial matches allowed or if this
118 // is the last component and only the ones before it are required
119 return (behavior == FPC_PARTIAL_ALLOWED || (behavior == FPC_PATH_MUST_EXIST && i >= len));
120 }
121
122 path.replace(start, i - start, component);
123
124 fullPath.append(component);
125 fullPath.append(1, '/');
126 }
127
128 start = i + 1;
129 }
130
131 return true;
132}
133
134#endif
135
136std::string DirectoryFileHandle::GetLocalPath(std::string& basePath, std::string localpath)
137{
138 if (localpath.empty())
139 return basePath;
140
141 if (localpath[0] == '/')
142 localpath.erase(0,1);
143 //Convert slashes
144#ifdef _WIN32
145 for (size_t i = 0; i < localpath.size(); i++) {
146 if (localpath[i] == '/')
147 localpath[i] = '\\';
148 }
149#endif
150 return basePath + localpath;
151}
152
153bool DirectoryFileHandle::Open(std::string& basePath, std::string& fileName, FileAccess access)
154{
155#if HOST_IS_CASE_SENSITIVE
156 if (access & (FILEACCESS_APPEND|FILEACCESS_CREATE|FILEACCESS_WRITE))
157 {
158 DEBUG_LOG(FILESYS, "Checking case for path %s", fileName.c_str());
159 if ( ! FixPathCase(basePath, fileName, FPC_PATH_MUST_EXIST) )
160 return false; // or go on and attempt (for a better error code than just 0?)
161 }
162 // else we try fopen first (in case we're lucky) before simulating case insensitivity
163#endif
164
165 std::string fullName = GetLocalPath(basePath,fileName);
166 INFO_LOG(FILESYS,"Actually opening %s", fullName.c_str());
167
168 //TODO: tests, should append seek to end of file? seeking in a file opened for append?
169#ifdef _WIN32
170 // Convert parameters to Windows permissions and access
171 DWORD desired = 0;
172 DWORD sharemode = 0;
173 DWORD openmode = 0;
174 if (access & FILEACCESS_READ) {
175 desired |= GENERIC_READ;
176 sharemode |= FILE_SHARE_READ;
177 }
178 if (access & FILEACCESS_WRITE) {
179 desired |= GENERIC_WRITE;
180 sharemode |= FILE_SHARE_WRITE;
181 }
182 if (access & FILEACCESS_CREATE) {
183 openmode = OPEN_ALWAYS;
184 } else {
185 openmode = OPEN_EXISTING;
186 }
187 //Let's do it!
188 hFile = CreateFile(ConvertUTF8ToWString(fullName).c_str(), desired, sharemode, 0, openmode, 0, 0);
189 bool success = hFile != INVALID_HANDLE_VALUE;
190#else
191 // Convert flags in access parameter to fopen access mode
192 const char *mode = NULL;
193 if (access & FILEACCESS_APPEND) {
194 if (access & FILEACCESS_READ)
195 mode = "ab+"; // append+read, create if needed
196 else
197 mode = "ab"; // append only, create if needed
198 } else if (access & FILEACCESS_WRITE) {
199 if (access & FILEACCESS_READ) {
200 // FILEACCESS_CREATE is ignored for read only, write only, and append
201 // because C++ standard fopen's nonexistant file creation can only be
202 // customized for files opened read+write
203 if (access & FILEACCESS_CREATE)
204 mode = "wb+"; // read+write, create if needed
205 else
206 mode = "rb+"; // read+write, but don't create
207 } else {
208 mode = "wb"; // write only, create if needed
209 }
210 } else { // neither write nor append, so default to read only
211 mode = "rb"; // read only, don't create
212 }
213
214 hFile = fopen(fullName.c_str(), mode);
215 bool success = hFile != 0;
216#endif
217
218#if HOST_IS_CASE_SENSITIVE
219 if (!success &&
220 !(access & FILEACCESS_APPEND) &&
221 !(access & FILEACCESS_CREATE) &&
222 !(access & FILEACCESS_WRITE))
223 {
224 if ( ! FixPathCase(basePath,fileName, FPC_PATH_MUST_EXIST) )
225 return 0; // or go on and attempt (for a better error code than just 0?)
226 fullName = GetLocalPath(basePath,fileName);
227 const char* fullNameC = fullName.c_str();
228
229 DEBUG_LOG(FILESYS, "Case may have been incorrect, second try opening %s (%s)", fullNameC, fileName.c_str());
230
231 // And try again with the correct case this time
232#ifdef _WIN32
233 hFile = CreateFile(fullNameC, desired, sharemode, 0, openmode, 0, 0);
234 success = hFile != INVALID_HANDLE_VALUE;
235#else
236 hFile = fopen(fullNameC, mode);
237 success = hFile != 0;
238#endif
239 }
240#endif
241
242 return success;
243}
244
245size_t DirectoryFileHandle::Read(u8* pointer, s64 size)
246{
247 size_t bytesRead = 0;
248#ifdef _WIN32
249 ::ReadFile(hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesRead, 0);
250#else
251 bytesRead = fread(pointer, 1, size, hFile);
252#endif
253 return bytesRead;
254}
255
256size_t DirectoryFileHandle::Write(const u8* pointer, s64 size)
257{
258 size_t bytesWritten = 0;
259#ifdef _WIN32
260 ::WriteFile(hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesWritten, 0);
261#else
262 bytesWritten = fwrite(pointer, 1, size, hFile);
263#endif
264 return bytesWritten;
265}
266
267size_t DirectoryFileHandle::Seek(s32 position, FileMove type)
268{
269#ifdef _WIN32
270 DWORD moveMethod = 0;
271 switch (type) {
272 case FILEMOVE_BEGIN: moveMethod = FILE_BEGIN; break;
273 case FILEMOVE_CURRENT: moveMethod = FILE_CURRENT; break;
274 case FILEMOVE_END: moveMethod = FILE_END; break;
275 }
276 DWORD newPos = SetFilePointer(hFile, (LONG)position, 0, moveMethod);
277 return newPos;
278#else
279 int moveMethod = 0;
280 switch (type) {
281 case FILEMOVE_BEGIN: moveMethod = SEEK_SET; break;
282 case FILEMOVE_CURRENT: moveMethod = SEEK_CUR; break;
283 case FILEMOVE_END: moveMethod = SEEK_END; break;
284 }
285 fseek(hFile, position, moveMethod);
286 return ftell(hFile);
287#endif
288}
289
290void DirectoryFileHandle::Close()
291{
292#ifdef _WIN32
293 if (hFile != (HANDLE)-1)
294 CloseHandle(hFile);
295#else
296 if (hFile != 0)
297 fclose(hFile);
298#endif
299}
300
301DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath) : basePath(_basePath) {
302 File::CreateFullPath(basePath);
303 hAlloc = _hAlloc;
304}
305
306DirectoryFileSystem::~DirectoryFileSystem() {
307 for (auto iter = entries.begin(); iter != entries.end(); ++iter) {
308 iter->second.hFile.Close();
309 }
310}
311
312std::string DirectoryFileSystem::GetLocalPath(std::string localpath) {
313 if (localpath.empty())
314 return basePath;
315
316 if (localpath[0] == '/')
317 localpath.erase(0,1);
318 //Convert slashes
319#ifdef _WIN32
320 for (size_t i = 0; i < localpath.size(); i++) {
321 if (localpath[i] == '/')
322 localpath[i] = '\\';
323 }
324#endif
325 return basePath + localpath;
326}
327
328bool DirectoryFileSystem::MkDir(const std::string &dirname) {
329#if HOST_IS_CASE_SENSITIVE
330 // Must fix case BEFORE attempting, because MkDir would create
331 // duplicate (different case) directories
332
333 std::string fixedCase = dirname;
334 if ( ! FixPathCase(basePath,fixedCase, FPC_PARTIAL_ALLOWED) )
335 return false;
336
337 return File::CreateFullPath(GetLocalPath(fixedCase));
338#else
339 return File::CreateFullPath(GetLocalPath(dirname));
340#endif
341}
342
343bool DirectoryFileSystem::RmDir(const std::string &dirname) {
344 std::string fullName = GetLocalPath(dirname);
345
346#if HOST_IS_CASE_SENSITIVE
347 // Maybe we're lucky?
348 if (File::DeleteDirRecursively(fullName))
349 return true;
350
351 // Nope, fix case and try again
352 fullName = dirname;
353 if ( ! FixPathCase(basePath,fullName, FPC_FILE_MUST_EXIST) )
354 return false; // or go on and attempt (for a better error code than just false?)
355
356 fullName = GetLocalPath(fullName);
357#endif
358
359/*#ifdef _WIN32
360 return RemoveDirectory(fullName.c_str()) == TRUE;
361#else
362 return 0 == rmdir(fullName.c_str());
363#endif*/
364 return File::DeleteDirRecursively(fullName);
365}
366
367int DirectoryFileSystem::RenameFile(const std::string &from, const std::string &to) {
368 std::string fullTo = to;
369
370 // Rename ignores the path (even if specified) on to.
371 size_t chop_at = to.find_last_of('/');
372 if (chop_at != to.npos)
373 fullTo = to.substr(chop_at + 1);
374
375 // Now put it in the same directory as from.
376 size_t dirname_end = from.find_last_of('/');
377 if (dirname_end != from.npos)
378 fullTo = from.substr(0, dirname_end + 1) + fullTo;
379
380 // At this point, we should check if the paths match and give an already exists error.
381 if (from == fullTo)
382 return -1;//SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS;
383
384 std::string fullFrom = GetLocalPath(from);
385
386#if HOST_IS_CASE_SENSITIVE
387 // In case TO should overwrite a file with different case
388 if ( ! FixPathCase(basePath,fullTo, FPC_PATH_MUST_EXIST) )
389 return -1; // or go on and attempt (for a better error code than just false?)
390#endif
391
392 fullTo = GetLocalPath(fullTo);
393 const char * fullToC = fullTo.c_str();
394
395#ifdef _WIN32
396 bool retValue = (MoveFile(ConvertUTF8ToWString(fullFrom).c_str(), ConvertUTF8ToWString(fullToC).c_str()) == TRUE);
397#else
398 bool retValue = (0 == rename(fullFrom.c_str(), fullToC));
399#endif
400
401#if HOST_IS_CASE_SENSITIVE
402 if (! retValue)
403 {
404 // May have failed due to case sensitivity on FROM, so try again
405 fullFrom = from;
406 if ( ! FixPathCase(basePath,fullFrom, FPC_FILE_MUST_EXIST) )
407 return -1; // or go on and attempt (for a better error code than just false?)
408 fullFrom = GetLocalPath(fullFrom);
409
410#ifdef _WIN32
411 retValue = (MoveFile(fullFrom.c_str(), fullToC) == TRUE);
412#else
413 retValue = (0 == rename(fullFrom.c_str(), fullToC));
414#endif
415 }
416#endif
417
418 // TODO: Better error codes.
419 return retValue ? 0 : -1;//SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS;
420}
421
422bool DirectoryFileSystem::RemoveFile(const std::string &filename) {
423 std::string fullName = GetLocalPath(filename);
424#ifdef _WIN32
425 bool retValue = (::DeleteFileA(fullName.c_str()) == TRUE);
426#else
427 bool retValue = (0 == unlink(fullName.c_str()));
428#endif
429
430#if HOST_IS_CASE_SENSITIVE
431 if (! retValue)
432 {
433 // May have failed due to case sensitivity, so try again
434 fullName = filename;
435 if ( ! FixPathCase(basePath,fullName, FPC_FILE_MUST_EXIST) )
436 return false; // or go on and attempt (for a better error code than just false?)
437 fullName = GetLocalPath(fullName);
438
439#ifdef _WIN32
440 retValue = (::DeleteFileA(fullName.c_str()) == TRUE);
441#else
442 retValue = (0 == unlink(fullName.c_str()));
443#endif
444 }
445#endif
446
447 return retValue;
448}
449
450u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) {
451 OpenFileEntry entry;
452 bool success = entry.hFile.Open(basePath,filename,access);
453
454 if (!success) {
455#ifdef _WIN32
456 ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", GetLastError(), (int)access);
457#else
458 ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, access = %i", (int)access);
459#endif
460 //wwwwaaaaahh!!
461 return 0;
462 } else {
463#ifdef _WIN32
464 if (access & FILEACCESS_APPEND)
465 entry.hFile.Seek(0,FILEMOVE_END);
466#endif
467
468 u32 newHandle = hAlloc->GetNewHandle();
469 entries[newHandle] = entry;
470
471 return newHandle;
472 }
473}
474
475void DirectoryFileSystem::CloseFile(u32 handle) {
476 EntryMap::iterator iter = entries.find(handle);
477 if (iter != entries.end()) {
478 hAlloc->FreeHandle(handle);
479 iter->second.hFile.Close();
480 entries.erase(iter);
481 } else {
482 //This shouldn't happen...
483 ERROR_LOG(FILESYS,"Cannot close file that hasn't been opened: %08x", handle);
484 }
485}
486
487bool DirectoryFileSystem::OwnsHandle(u32 handle) {
488 EntryMap::iterator iter = entries.find(handle);
489 return (iter != entries.end());
490}
491
492size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
493 EntryMap::iterator iter = entries.find(handle);
494 if (iter != entries.end())
495 {
496 size_t bytesRead = iter->second.hFile.Read(pointer,size);
497 return bytesRead;
498 } else {
499 //This shouldn't happen...
500 ERROR_LOG(FILESYS,"Cannot read file that hasn't been opened: %08x", handle);
501 return 0;
502 }
503}
504
505size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) {
506 EntryMap::iterator iter = entries.find(handle);
507 if (iter != entries.end())
508 {
509 size_t bytesWritten = iter->second.hFile.Write(pointer,size);
510 return bytesWritten;
511 } else {
512 //This shouldn't happen...
513 ERROR_LOG(FILESYS,"Cannot write to file that hasn't been opened: %08x", handle);
514 return 0;
515 }
516}
517
518size_t DirectoryFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {
519 EntryMap::iterator iter = entries.find(handle);
520 if (iter != entries.end()) {
521 return iter->second.hFile.Seek(position,type);
522 } else {
523 //This shouldn't happen...
524 ERROR_LOG(FILESYS,"Cannot seek in file that hasn't been opened: %08x", handle);
525 return 0;
526 }
527}
528
529FileInfo DirectoryFileSystem::GetFileInfo(std::string filename) {
530 FileInfo x;
531 x.name = filename;
532
533 std::string fullName = GetLocalPath(filename);
534 if (! File::Exists(fullName)) {
535#if HOST_IS_CASE_SENSITIVE
536 if (! FixPathCase(basePath,filename, FPC_FILE_MUST_EXIST))
537 return x;
538 fullName = GetLocalPath(filename);
539
540 if (! File::Exists(fullName))
541 return x;
542#else
543 return x;
544#endif
545 }
546 x.type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
547 x.exists = true;
548
549 if (x.type != FILETYPE_DIRECTORY)
550 {
551#ifdef _WIN32
552 struct _stat64i32 s;
553 _wstat64i32(ConvertUTF8ToWString(fullName).c_str(), &s);
554#else
555 struct stat s;
556 stat(fullName.c_str(), &s);
557#endif
558
559 x.size = File::GetSize(fullName);
560 x.access = s.st_mode & 0x1FF;
561 localtime_r((time_t*)&s.st_atime,&x.atime);
562 localtime_r((time_t*)&s.st_ctime,&x.ctime);
563 localtime_r((time_t*)&s.st_mtime,&x.mtime);
564 }
565
566 return x;
567}
568
569bool DirectoryFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) {
570 outpath = GetLocalPath(inpath);
571 return true;
572}
573
574#ifdef _WIN32
575#define FILETIME_FROM_UNIX_EPOCH_US 11644473600000000ULL
576
577static void tmFromFiletime(tm &dest, FILETIME &src)
578{
579 u64 from_1601_us = (((u64) src.dwHighDateTime << 32ULL) + (u64) src.dwLowDateTime) / 10ULL;
580 u64 from_1970_us = from_1601_us - FILETIME_FROM_UNIX_EPOCH_US;
581
582 time_t t = (time_t) (from_1970_us / 1000000UL);
583 localtime_r(&t, &dest);
584}
585#endif
586
587std::vector<FileInfo> DirectoryFileSystem::GetDirListing(std::string path) {
588 std::vector<FileInfo> myVector;
589#ifdef _WIN32
590 WIN32_FIND_DATA findData;
591 HANDLE hFind;
592
593 std::string w32path = GetLocalPath(path) + "\\*.*";
594
595 hFind = FindFirstFile(ConvertUTF8ToWString(w32path).c_str(), &findData);
596
597 if (hFind == INVALID_HANDLE_VALUE) {
598 return myVector; //the empty list
599 }
600
601 while (true) {
602 FileInfo entry;
603 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
604 entry.type = FILETYPE_DIRECTORY;
605 else
606 entry.type = FILETYPE_NORMAL;
607
608 // TODO: Make this more correct?
609 entry.access = entry.type == FILETYPE_NORMAL ? 0666 : 0777;
610 // TODO: is this just for .. or all subdirectories? Need to add a directory to the test
611 // to find out. Also why so different than the old test results?
612 if (!wcscmp(findData.cFileName, L"..") )
613 entry.size = 4096;
614 else
615 entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32);
616 entry.name = ConvertWStringToUTF8(findData.cFileName);
617 tmFromFiletime(entry.atime, findData.ftLastAccessTime);
618 tmFromFiletime(entry.ctime, findData.ftCreationTime);
619 tmFromFiletime(entry.mtime, findData.ftLastWriteTime);
620 myVector.push_back(entry);
621
622 int retval = FindNextFile(hFind, &findData);
623 if (!retval)
624 break;
625 }
626#else
627 dirent *dirp;
628 std::string localPath = GetLocalPath(path);
629 DIR *dp = opendir(localPath.c_str());
630
631#if HOST_IS_CASE_SENSITIVE
632 if(dp == NULL && FixPathCase(basePath,path, FPC_FILE_MUST_EXIST)) {
633 // May have failed due to case sensitivity, try again
634 localPath = GetLocalPath(path);
635 dp = opendir(localPath.c_str());
636 }
637#endif
638
639 if (dp == NULL) {
640 ERROR_LOG(FILESYS,"Error opening directory %s\n",path.c_str());
641 return myVector;
642 }
643
644 while ((dirp = readdir(dp)) != NULL) {
645 FileInfo entry;
646 struct stat s;
647 std::string fullName = GetLocalPath(path) + "/"+dirp->d_name;
648 stat(fullName.c_str(), &s);
649 if (S_ISDIR(s.st_mode))
650 entry.type = FILETYPE_DIRECTORY;
651 else
652 entry.type = FILETYPE_NORMAL;
653 entry.access = s.st_mode & 0x1FF;
654 entry.name = dirp->d_name;
655 entry.size = s.st_size;
656 localtime_r((time_t*)&s.st_atime,&entry.atime);
657 localtime_r((time_t*)&s.st_ctime,&entry.ctime);
658 localtime_r((time_t*)&s.st_mtime,&entry.mtime);
659 myVector.push_back(entry);
660 }
661 closedir(dp);
662#endif
663 return myVector;
664}
665
666void DirectoryFileSystem::DoState(PointerWrap &p) {
667 if (!entries.empty()) {
668 p.SetError(p.ERROR_WARNING);
669 ERROR_LOG(FILESYS, "FIXME: Open files during savestate, could go badly.");
670 }
671}
diff --git a/src/core/file_sys/directory_file_system.h b/src/core/file_sys/directory_file_system.h
new file mode 100644
index 000000000..a11331a27
--- /dev/null
+++ b/src/core/file_sys/directory_file_system.h
@@ -0,0 +1,158 @@
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#ifndef CORE_FILE_SYS_DIRECTORY_H_
19#define CORE_FILE_SYS_DIRECTORY_H_
20
21// TODO: Remove the Windows-specific code, FILE is fine there too.
22
23#include <map>
24
25#include "file_sys.h"
26
27#ifdef _WIN32
28typedef void * HANDLE;
29#endif
30
31#if defined(__APPLE__)
32
33#if TARGET_OS_IPHONE
34#define HOST_IS_CASE_SENSITIVE 1
35#elif TARGET_IPHONE_SIMULATOR
36#define HOST_IS_CASE_SENSITIVE 0
37#else
38// Mac OSX case sensitivity defaults off, but is user configurable (when
39// creating a filesytem), so assume the worst:
40#define HOST_IS_CASE_SENSITIVE 1
41#endif
42
43#elif defined(_WIN32) || defined(__SYMBIAN32__)
44#define HOST_IS_CASE_SENSITIVE 0
45
46#else // Android, Linux, BSD (and the rest?)
47#define HOST_IS_CASE_SENSITIVE 1
48
49#endif
50
51#if HOST_IS_CASE_SENSITIVE
52enum FixPathCaseBehavior {
53 FPC_FILE_MUST_EXIST, // all path components must exist (rmdir, move from)
54 FPC_PATH_MUST_EXIST, // all except the last one must exist - still tries to fix last one (fopen, move to)
55 FPC_PARTIAL_ALLOWED, // don't care how many exist (mkdir recursive)
56};
57
58bool FixPathCase(std::string& basePath, std::string &path, FixPathCaseBehavior behavior);
59#endif
60
61struct DirectoryFileHandle
62{
63#ifdef _WIN32
64 HANDLE hFile;
65#else
66 FILE* hFile;
67#endif
68 DirectoryFileHandle()
69 {
70#ifdef _WIN32
71 hFile = (HANDLE)-1;
72#else
73 hFile = 0;
74#endif
75 }
76
77 std::string GetLocalPath(std::string& basePath, std::string localpath);
78 bool Open(std::string& basePath, std::string& fileName, FileAccess access);
79 size_t Read(u8* pointer, s64 size);
80 size_t Write(const u8* pointer, s64 size);
81 size_t Seek(s32 position, FileMove type);
82 void Close();
83};
84
85class DirectoryFileSystem : public IFileSystem {
86public:
87 DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath);
88 ~DirectoryFileSystem();
89
90 void DoState(PointerWrap &p);
91 std::vector<FileInfo> GetDirListing(std::string path);
92 u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL);
93 void CloseFile(u32 handle);
94 size_t ReadFile(u32 handle, u8 *pointer, s64 size);
95 size_t WriteFile(u32 handle, const u8 *pointer, s64 size);
96 size_t SeekFile(u32 handle, s32 position, FileMove type);
97 FileInfo GetFileInfo(std::string filename);
98 bool OwnsHandle(u32 handle);
99
100 bool MkDir(const std::string &dirname);
101 bool RmDir(const std::string &dirname);
102 int RenameFile(const std::string &from, const std::string &to);
103 bool RemoveFile(const std::string &filename);
104 bool GetHostPath(const std::string &inpath, std::string &outpath);
105
106private:
107 struct OpenFileEntry {
108 DirectoryFileHandle hFile;
109 };
110
111 typedef std::map<u32, OpenFileEntry> EntryMap;
112 EntryMap entries;
113 std::string basePath;
114 IHandleAllocator *hAlloc;
115
116 // In case of Windows: Translate slashes, etc.
117 std::string GetLocalPath(std::string localpath);
118};
119
120// VFSFileSystem: Ability to map in Android APK paths as well! Does not support all features, only meant for fonts.
121// Very inefficient - always load the whole file on open.
122class VFSFileSystem : public IFileSystem {
123public:
124 VFSFileSystem(IHandleAllocator *_hAlloc, std::string _basePath);
125 ~VFSFileSystem();
126
127 void DoState(PointerWrap &p);
128 std::vector<FileInfo> GetDirListing(std::string path);
129 u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL);
130 void CloseFile(u32 handle);
131 size_t ReadFile(u32 handle, u8 *pointer, s64 size);
132 size_t WriteFile(u32 handle, const u8 *pointer, s64 size);
133 size_t SeekFile(u32 handle, s32 position, FileMove type);
134 FileInfo GetFileInfo(std::string filename);
135 bool OwnsHandle(u32 handle);
136
137 bool MkDir(const std::string &dirname);
138 bool RmDir(const std::string &dirname);
139 int RenameFile(const std::string &from, const std::string &to);
140 bool RemoveFile(const std::string &filename);
141 bool GetHostPath(const std::string &inpath, std::string &outpath);
142
143private:
144 struct OpenFileEntry {
145 u8 *fileData;
146 size_t size;
147 size_t seekPos;
148 };
149
150 typedef std::map<u32, OpenFileEntry> EntryMap;
151 EntryMap entries;
152 std::string basePath;
153 IHandleAllocator *hAlloc;
154
155 std::string GetLocalPath(std::string localpath);
156};
157
158#endif // CORE_FILE_SYS_DIRECTORY_H_
diff --git a/src/core/file_sys/file_sys.h b/src/core/file_sys/file_sys.h
new file mode 100644
index 000000000..b27e36c80
--- /dev/null
+++ b/src/core/file_sys/file_sys.h
@@ -0,0 +1,138 @@
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#pragma once
19
20#include "common.h"
21#include "chunk_file.h"
22
23enum FileAccess {
24 FILEACCESS_NONE=0,
25 FILEACCESS_READ=1,
26 FILEACCESS_WRITE=2,
27 FILEACCESS_APPEND=4,
28 FILEACCESS_CREATE=8
29};
30
31enum FileMove {
32 FILEMOVE_BEGIN=0,
33 FILEMOVE_CURRENT=1,
34 FILEMOVE_END=2
35};
36
37enum FileType {
38 FILETYPE_NORMAL=1,
39 FILETYPE_DIRECTORY=2
40};
41
42
43class IHandleAllocator {
44public:
45 virtual ~IHandleAllocator() {}
46 virtual u32 GetNewHandle() = 0;
47 virtual void FreeHandle(u32 handle) = 0;
48};
49
50class SequentialHandleAllocator : public IHandleAllocator {
51public:
52 SequentialHandleAllocator() : handle_(1) {}
53 virtual u32 GetNewHandle() { return handle_++; }
54 virtual void FreeHandle(u32 handle) {}
55private:
56 int handle_;
57};
58
59struct FileInfo {
60 FileInfo()
61 : size(0), access(0), exists(false), type(FILETYPE_NORMAL), isOnSectorSystem(false), startSector(0), numSectors(0) {}
62
63 void DoState(PointerWrap &p) {
64 auto s = p.Section("FileInfo", 1);
65 if (!s)
66 return;
67
68 p.Do(name);
69 p.Do(size);
70 p.Do(access);
71 p.Do(exists);
72 p.Do(type);
73 p.Do(atime);
74 p.Do(ctime);
75 p.Do(mtime);
76 p.Do(isOnSectorSystem);
77 p.Do(startSector);
78 p.Do(numSectors);
79 p.Do(sectorSize);
80 }
81
82 std::string name;
83 s64 size;
84 u32 access; //unix 777
85 bool exists;
86 FileType type;
87
88 tm atime;
89 tm ctime;
90 tm mtime;
91
92 bool isOnSectorSystem;
93 u32 startSector;
94 u32 numSectors;
95 u32 sectorSize;
96};
97
98
99class IFileSystem {
100public:
101 virtual ~IFileSystem() {}
102
103 virtual void DoState(PointerWrap &p) = 0;
104 virtual std::vector<FileInfo> GetDirListing(std::string path) = 0;
105 virtual u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) = 0;
106 virtual void CloseFile(u32 handle) = 0;
107 virtual size_t ReadFile(u32 handle, u8 *pointer, s64 size) = 0;
108 virtual size_t WriteFile(u32 handle, const u8 *pointer, s64 size) = 0;
109 virtual size_t SeekFile(u32 handle, s32 position, FileMove type) = 0;
110 virtual FileInfo GetFileInfo(std::string filename) = 0;
111 virtual bool OwnsHandle(u32 handle) = 0;
112 virtual bool MkDir(const std::string &dirname) = 0;
113 virtual bool RmDir(const std::string &dirname) = 0;
114 virtual int RenameFile(const std::string &from, const std::string &to) = 0;
115 virtual bool RemoveFile(const std::string &filename) = 0;
116 virtual bool GetHostPath(const std::string &inpath, std::string &outpath) = 0;
117};
118
119
120class EmptyFileSystem : public IFileSystem {
121public:
122 virtual void DoState(PointerWrap &p) {}
123 std::vector<FileInfo> GetDirListing(std::string path) {std::vector<FileInfo> vec; return vec;}
124 u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) {return 0;}
125 void CloseFile(u32 handle) {}
126 size_t ReadFile(u32 handle, u8 *pointer, s64 size) {return 0;}
127 size_t WriteFile(u32 handle, const u8 *pointer, s64 size) {return 0;}
128 size_t SeekFile(u32 handle, s32 position, FileMove type) {return 0;}
129 FileInfo GetFileInfo(std::string filename) {FileInfo f; return f;}
130 bool OwnsHandle(u32 handle) {return false;}
131 virtual bool MkDir(const std::string &dirname) {return false;}
132 virtual bool RmDir(const std::string &dirname) {return false;}
133 virtual int RenameFile(const std::string &from, const std::string &to) {return -1;}
134 virtual bool RemoveFile(const std::string &filename) {return false;}
135 virtual bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;}
136};
137
138
diff --git a/src/core/file_sys/meta_file_system.cpp b/src/core/file_sys/meta_file_system.cpp
new file mode 100644
index 000000000..f86c3cb18
--- /dev/null
+++ b/src/core/file_sys/meta_file_system.cpp
@@ -0,0 +1,520 @@
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 <set>
19#include <algorithm>
20#include "string_util.h"
21#include "file_sys/meta_file_system.h"
22//#include "Core/HLE/sceKernelThread.h"
23//#include "Core/Reporting.h"
24
25static bool ApplyPathStringToComponentsVector(std::vector<std::string> &vector, const std::string &pathString)
26{
27 size_t len = pathString.length();
28 size_t start = 0;
29
30 while (start < len)
31 {
32 size_t i = pathString.find('/', start);
33 if (i == std::string::npos)
34 i = len;
35
36 if (i > start)
37 {
38 std::string component = pathString.substr(start, i - start);
39 if (component != ".")
40 {
41 if (component == "..")
42 {
43 if (vector.size() != 0)
44 {
45 vector.pop_back();
46 }
47 else
48 {
49 // The PSP silently ignores attempts to .. to parent of root directory
50 WARN_LOG(FILESYS, "RealPath: ignoring .. beyond root - root directory is its own parent: \"%s\"", pathString.c_str());
51 }
52 }
53 else
54 {
55 vector.push_back(component);
56 }
57 }
58 }
59
60 start = i + 1;
61 }
62
63 return true;
64}
65
66/*
67 * Changes relative paths to absolute, removes ".", "..", and trailing "/"
68 * "drive:./blah" is absolute (ignore the dot) and "/blah" is relative (because it's missing "drive:")
69 * babel (and possibly other games) use "/directoryThatDoesNotExist/../directoryThatExists/filename"
70 */
71static bool RealPath(const std::string &currentDirectory, const std::string &inPath, std::string &outPath)
72{
73 size_t inLen = inPath.length();
74 if (inLen == 0)
75 {
76 WARN_LOG(FILESYS, "RealPath: inPath is empty");
77 outPath = currentDirectory;
78 return true;
79 }
80
81 size_t inColon = inPath.find(':');
82 if (inColon + 1 == inLen)
83 {
84 // There's nothing after the colon, e.g. umd0: - this is perfectly valid.
85 outPath = inPath;
86 return true;
87 }
88
89 bool relative = (inColon == std::string::npos);
90
91 std::string prefix, inAfterColon;
92 std::vector<std::string> cmpnts; // path components
93 size_t outPathCapacityGuess = inPath.length();
94
95 if (relative)
96 {
97 size_t curDirLen = currentDirectory.length();
98 if (curDirLen == 0)
99 {
100 ERROR_LOG(FILESYS, "RealPath: inPath \"%s\" is relative, but current directory is empty", inPath.c_str());
101 return false;
102 }
103
104 size_t curDirColon = currentDirectory.find(':');
105 if (curDirColon == std::string::npos)
106 {
107 ERROR_LOG(FILESYS, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" has no prefix", inPath.c_str(), currentDirectory.c_str());
108 return false;
109 }
110 if (curDirColon + 1 == curDirLen)
111 {
112 ERROR_LOG(FILESYS, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" is all prefix and no path. Using \"/\" as path for current directory.", inPath.c_str(), currentDirectory.c_str());
113 }
114 else
115 {
116 const std::string curDirAfter = currentDirectory.substr(curDirColon + 1);
117 if (! ApplyPathStringToComponentsVector(cmpnts, curDirAfter) )
118 {
119 ERROR_LOG(FILESYS,"RealPath: currentDirectory is not a valid path: \"%s\"", currentDirectory.c_str());
120 return false;
121 }
122
123 outPathCapacityGuess += curDirLen;
124 }
125
126 prefix = currentDirectory.substr(0, curDirColon + 1);
127 inAfterColon = inPath;
128 }
129 else
130 {
131 prefix = inPath.substr(0, inColon + 1);
132 inAfterColon = inPath.substr(inColon + 1);
133 }
134
135 // Special case: "disc0:" is different from "disc0:/", so keep track of the single slash.
136 if (inAfterColon == "/")
137 {
138 outPath = prefix + inAfterColon;
139 return true;
140 }
141
142 if (! ApplyPathStringToComponentsVector(cmpnts, inAfterColon) )
143 {
144 WARN_LOG(FILESYS, "RealPath: inPath is not a valid path: \"%s\"", inPath.c_str());
145 return false;
146 }
147
148 outPath.clear();
149 outPath.reserve(outPathCapacityGuess);
150
151 outPath.append(prefix);
152
153 size_t numCmpnts = cmpnts.size();
154 for (size_t i = 0; i < numCmpnts; i++)
155 {
156 outPath.append(1, '/');
157 outPath.append(cmpnts[i]);
158 }
159
160 return true;
161}
162
163IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
164{
165 std::lock_guard<std::mutex> guard(lock);
166 for (size_t i = 0; i < fileSystems.size(); i++)
167 {
168 if (fileSystems[i].system->OwnsHandle(handle))
169 return fileSystems[i].system; //got it!
170 }
171 //none found?
172 return 0;
173}
174
175bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpath, MountPoint **system)
176{
177 std::lock_guard<std::mutex> guard(lock);
178 std::string realpath;
179
180 // Special handling: host0:command.txt (as seen in Super Monkey Ball Adventures, for example)
181 // appears to mean the current directory on the UMD. Let's just assume the current directory.
182 std::string inpath = _inpath;
183 if (strncasecmp(inpath.c_str(), "host0:", strlen("host0:")) == 0) {
184 INFO_LOG(FILESYS, "Host0 path detected, stripping: %s", inpath.c_str());
185 inpath = inpath.substr(strlen("host0:"));
186 }
187
188 const std::string *currentDirectory = &startingDirectory;
189
190 _assert_msg_(FILESYS, false, "must implement equiv of __KernelGetCurThread");
191
192 int currentThread = 0;//__KernelGetCurThread();
193 currentDir_t::iterator it = currentDir.find(currentThread);
194 if (it == currentDir.end())
195 {
196 //Attempt to emulate SCE_KERNEL_ERROR_NOCWD / 8002032C: may break things requiring fixes elsewhere
197 if (inpath.find(':') == std::string::npos /* means path is relative */)
198 {
199 lastOpenError = -1;//SCE_KERNEL_ERROR_NOCWD;
200 WARN_LOG(FILESYS, "Path is relative, but current directory not set for thread %i. returning 8002032C(SCE_KERNEL_ERROR_NOCWD) instead.", currentThread);
201 }
202 }
203 else
204 {
205 currentDirectory = &(it->second);
206 }
207
208 if ( RealPath(*currentDirectory, inpath, realpath) )
209 {
210 for (size_t i = 0; i < fileSystems.size(); i++)
211 {
212 size_t prefLen = fileSystems[i].prefix.size();
213 if (strncasecmp(fileSystems[i].prefix.c_str(), realpath.c_str(), prefLen) == 0)
214 {
215 outpath = realpath.substr(prefLen);
216 *system = &(fileSystems[i]);
217
218 INFO_LOG(FILESYS, "MapFilePath: mapped \"%s\" to prefix: \"%s\", path: \"%s\"", inpath.c_str(), fileSystems[i].prefix.c_str(), outpath.c_str());
219
220 return true;
221 }
222 }
223 }
224
225 DEBUG_LOG(FILESYS, "MapFilePath: failed mapping \"%s\", returning false", inpath.c_str());
226 return false;
227}
228
229void MetaFileSystem::Mount(std::string prefix, IFileSystem *system)
230{
231 std::lock_guard<std::mutex> guard(lock);
232 MountPoint x;
233 x.prefix = prefix;
234 x.system = system;
235 fileSystems.push_back(x);
236}
237
238void MetaFileSystem::Unmount(std::string prefix, IFileSystem *system)
239{
240 std::lock_guard<std::mutex> guard(lock);
241 MountPoint x;
242 x.prefix = prefix;
243 x.system = system;
244 fileSystems.erase(std::remove(fileSystems.begin(), fileSystems.end(), x), fileSystems.end());
245}
246
247void MetaFileSystem::Shutdown()
248{
249 std::lock_guard<std::mutex> guard(lock);
250 current = 6;
251
252 // Ownership is a bit convoluted. Let's just delete everything once.
253
254 std::set<IFileSystem *> toDelete;
255 for (size_t i = 0; i < fileSystems.size(); i++) {
256 toDelete.insert(fileSystems[i].system);
257 }
258
259 for (auto iter = toDelete.begin(); iter != toDelete.end(); ++iter)
260 {
261 delete *iter;
262 }
263
264 fileSystems.clear();
265 currentDir.clear();
266 startingDirectory = "";
267}
268
269u32 MetaFileSystem::OpenWithError(int &error, std::string filename, FileAccess access, const char *devicename)
270{
271 std::lock_guard<std::mutex> guard(lock);
272 u32 h = OpenFile(filename, access, devicename);
273 error = lastOpenError;
274 return h;
275}
276
277u32 MetaFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename)
278{
279 std::lock_guard<std::mutex> guard(lock);
280 lastOpenError = 0;
281 std::string of;
282 MountPoint *mount;
283 if (MapFilePath(filename, of, &mount))
284 {
285 return mount->system->OpenFile(of, access, mount->prefix.c_str());
286 }
287 else
288 {
289 return 0;
290 }
291}
292
293FileInfo MetaFileSystem::GetFileInfo(std::string filename)
294{
295 std::lock_guard<std::mutex> guard(lock);
296 std::string of;
297 IFileSystem *system;
298 if (MapFilePath(filename, of, &system))
299 {
300 return system->GetFileInfo(of);
301 }
302 else
303 {
304 FileInfo bogus; // TODO
305 return bogus;
306 }
307}
308
309bool MetaFileSystem::GetHostPath(const std::string &inpath, std::string &outpath)
310{
311 std::lock_guard<std::mutex> guard(lock);
312 std::string of;
313 IFileSystem *system;
314 if (MapFilePath(inpath, of, &system)) {
315 return system->GetHostPath(of, outpath);
316 } else {
317 return false;
318 }
319}
320
321std::vector<FileInfo> MetaFileSystem::GetDirListing(std::string path)
322{
323 std::lock_guard<std::mutex> guard(lock);
324 std::string of;
325 IFileSystem *system;
326 if (MapFilePath(path, of, &system))
327 {
328 return system->GetDirListing(of);
329 }
330 else
331 {
332 std::vector<FileInfo> empty;
333 return empty;
334 }
335}
336
337void MetaFileSystem::ThreadEnded(int threadID)
338{
339 std::lock_guard<std::mutex> guard(lock);
340 currentDir.erase(threadID);
341}
342
343int MetaFileSystem::ChDir(const std::string &dir)
344{
345 std::lock_guard<std::mutex> guard(lock);
346 // Retain the old path and fail if the arg is 1023 bytes or longer.
347 if (dir.size() >= 1023)
348 return -1;//SCE_KERNEL_ERROR_NAMETOOLONG;
349
350 _assert_msg_(FILESYS, false, "must implement equiv of __KernelGetCurThread");
351
352 int curThread = 0; //__KernelGetCurThread();
353
354 std::string of;
355 MountPoint *mountPoint;
356 if (MapFilePath(dir, of, &mountPoint))
357 {
358 currentDir[curThread] = mountPoint->prefix + of;
359 return 0;
360 }
361 else
362 {
363 for (size_t i = 0; i < fileSystems.size(); i++)
364 {
365 const std::string &prefix = fileSystems[i].prefix;
366 if (strncasecmp(prefix.c_str(), dir.c_str(), prefix.size()) == 0)
367 {
368 // The PSP is completely happy with invalid current dirs as long as they have a valid device.
369 WARN_LOG(FILESYS, "ChDir failed to map path \"%s\", saving as current directory anyway", dir.c_str());
370 currentDir[curThread] = dir;
371 return 0;
372 }
373 }
374
375 WARN_LOG(FILESYS, "ChDir failed to map device for \"%s\", failing", dir.c_str());
376 return -1;//SCE_KERNEL_ERROR_NODEV;
377 }
378}
379
380bool MetaFileSystem::MkDir(const std::string &dirname)
381{
382 std::lock_guard<std::mutex> guard(lock);
383 std::string of;
384 IFileSystem *system;
385 if (MapFilePath(dirname, of, &system))
386 {
387 return system->MkDir(of);
388 }
389 else
390 {
391 return false;
392 }
393}
394
395bool MetaFileSystem::RmDir(const std::string &dirname)
396{
397 std::lock_guard<std::mutex> guard(lock);
398 std::string of;
399 IFileSystem *system;
400 if (MapFilePath(dirname, of, &system))
401 {
402 return system->RmDir(of);
403 }
404 else
405 {
406 return false;
407 }
408}
409
410int MetaFileSystem::RenameFile(const std::string &from, const std::string &to)
411{
412 std::lock_guard<std::mutex> guard(lock);
413 std::string of;
414 std::string rf;
415 IFileSystem *osystem;
416 IFileSystem *rsystem = NULL;
417 if (MapFilePath(from, of, &osystem))
418 {
419 // If it's a relative path, it seems to always use from's filesystem.
420 if (to.find(":/") != to.npos)
421 {
422 if (!MapFilePath(to, rf, &rsystem))
423 return -1;
424 }
425 else
426 {
427 rf = to;
428 rsystem = osystem;
429 }
430
431 if (osystem != rsystem)
432 return -1;//SCE_KERNEL_ERROR_XDEV;
433
434 return osystem->RenameFile(of, rf);
435 }
436 else
437 {
438 return -1;
439 }
440}
441
442bool MetaFileSystem::RemoveFile(const std::string &filename)
443{
444 std::lock_guard<std::mutex> guard(lock);
445 std::string of;
446 IFileSystem *system;
447 if (MapFilePath(filename, of, &system))
448 {
449 return system->RemoveFile(of);
450 }
451 else
452 {
453 return false;
454 }
455}
456
457void MetaFileSystem::CloseFile(u32 handle)
458{
459 std::lock_guard<std::mutex> guard(lock);
460 IFileSystem *sys = GetHandleOwner(handle);
461 if (sys)
462 sys->CloseFile(handle);
463}
464
465size_t MetaFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
466{
467 std::lock_guard<std::mutex> guard(lock);
468 IFileSystem *sys = GetHandleOwner(handle);
469 if (sys)
470 return sys->ReadFile(handle,pointer,size);
471 else
472 return 0;
473}
474
475size_t MetaFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
476{
477 std::lock_guard<std::mutex> guard(lock);
478 IFileSystem *sys = GetHandleOwner(handle);
479 if (sys)
480 return sys->WriteFile(handle,pointer,size);
481 else
482 return 0;
483}
484
485size_t MetaFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
486{
487 std::lock_guard<std::mutex> guard(lock);
488 IFileSystem *sys = GetHandleOwner(handle);
489 if (sys)
490 return sys->SeekFile(handle,position,type);
491 else
492 return 0;
493}
494
495void MetaFileSystem::DoState(PointerWrap &p)
496{
497 std::lock_guard<std::mutex> guard(lock);
498
499 auto s = p.Section("MetaFileSystem", 1);
500 if (!s)
501 return;
502
503 p.Do(current);
504
505 // Save/load per-thread current directory map
506 p.Do(currentDir);
507
508 u32 n = (u32) fileSystems.size();
509 p.Do(n);
510 if (n != (u32) fileSystems.size())
511 {
512 p.SetError(p.ERROR_FAILURE);
513 ERROR_LOG(FILESYS, "Savestate failure: number of filesystems doesn't match.");
514 return;
515 }
516
517 for (u32 i = 0; i < n; ++i)
518 fileSystems[i].system->DoState(p);
519}
520
diff --git a/src/core/file_sys/meta_file_system.h b/src/core/file_sys/meta_file_system.h
new file mode 100644
index 000000000..0de23d49c
--- /dev/null
+++ b/src/core/file_sys/meta_file_system.h
@@ -0,0 +1,109 @@
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#pragma once
19
20#include "std_mutex.h"
21#include "file_sys.h"
22
23class MetaFileSystem : public IHandleAllocator, public IFileSystem
24{
25private:
26 u32 current;
27 struct MountPoint
28 {
29 std::string prefix;
30 IFileSystem *system;
31
32 bool operator == (const MountPoint &other) const
33 {
34 return prefix == other.prefix && system == other.system;
35 }
36 };
37 std::vector<MountPoint> fileSystems;
38
39 typedef std::map<int, std::string> currentDir_t;
40 currentDir_t currentDir;
41
42 std::string startingDirectory;
43 int lastOpenError;
44 std::recursive_mutex lock;
45
46public:
47 MetaFileSystem()
48 {
49 current = 6; // what?
50 }
51
52 void Mount(std::string prefix, IFileSystem *system);
53 void Unmount(std::string prefix, IFileSystem *system);
54
55 void ThreadEnded(int threadID);
56
57 void Shutdown();
58
59 u32 GetNewHandle() {return current++;}
60 void FreeHandle(u32 handle) {}
61
62 virtual void DoState(PointerWrap &p);
63
64 IFileSystem *GetHandleOwner(u32 handle);
65 bool MapFilePath(const std::string &inpath, std::string &outpath, MountPoint **system);
66
67 inline bool MapFilePath(const std::string &_inpath, std::string &outpath, IFileSystem **system)
68 {
69 MountPoint *mountPoint;
70 if (MapFilePath(_inpath, outpath, &mountPoint))
71 {
72 *system = mountPoint->system;
73 return true;
74 }
75
76 return false;
77 }
78
79 // Only possible if a file system is a DirectoryFileSystem or similar.
80 bool GetHostPath(const std::string &inpath, std::string &outpath);
81
82 std::vector<FileInfo> GetDirListing(std::string path);
83 u32 OpenFile(std::string filename, FileAccess access, const char *devicename = NULL);
84 u32 OpenWithError(int &error, std::string filename, FileAccess access, const char *devicename = NULL);
85 void CloseFile(u32 handle);
86 size_t ReadFile(u32 handle, u8 *pointer, s64 size);
87 size_t WriteFile(u32 handle, const u8 *pointer, s64 size);
88 size_t SeekFile(u32 handle, s32 position, FileMove type);
89 FileInfo GetFileInfo(std::string filename);
90 bool OwnsHandle(u32 handle) {return false;}
91 inline size_t GetSeekPos(u32 handle)
92 {
93 return SeekFile(handle, 0, FILEMOVE_CURRENT);
94 }
95
96 virtual int ChDir(const std::string &dir);
97
98 virtual bool MkDir(const std::string &dirname);
99 virtual bool RmDir(const std::string &dirname);
100 virtual int RenameFile(const std::string &from, const std::string &to);
101 virtual bool RemoveFile(const std::string &filename);
102
103 // TODO: void IoCtl(...)
104
105 void SetStartingDirectory(const std::string &dir) {
106 std::lock_guard<std::mutex> guard(lock);
107 startingDirectory = dir;
108 }
109};