summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorGravatar ShizZy2013-09-23 22:04:56 -0400
committerGravatar ShizZy2013-09-23 22:04:56 -0400
commit29cdc1df39c81d7a001053ffca4aa1bc194a3c7b (patch)
tree17268fe563ea3c1cfc3812c7ac78de6f60fb3f0c /src/common
parentremoved unused commented-out code (diff)
downloadyuzu-29cdc1df39c81d7a001053ffca4aa1bc194a3c7b.tar.gz
yuzu-29cdc1df39c81d7a001053ffca4aa1bc194a3c7b.tar.xz
yuzu-29cdc1df39c81d7a001053ffca4aa1bc194a3c7b.zip
moved file_sys to common
Diffstat (limited to 'src/common')
-rw-r--r--src/common/common.vcxproj3
-rw-r--r--src/common/common.vcxproj.filters3
-rw-r--r--src/common/src/file_sys.h138
-rw-r--r--src/common/src/file_sys_directory.cpp671
-rw-r--r--src/common/src/file_sys_directory.h158
5 files changed, 973 insertions, 0 deletions
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index 9cf382c29..498ca8401 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -159,6 +159,8 @@
159 <ClInclude Include="src\extended_trace.h" /> 159 <ClInclude Include="src\extended_trace.h" />
160 <ClInclude Include="src\fifo_queue.h" /> 160 <ClInclude Include="src\fifo_queue.h" />
161 <ClInclude Include="src\file_search.h" /> 161 <ClInclude Include="src\file_search.h" />
162 <ClInclude Include="src\file_sys.h" />
163 <ClInclude Include="src\file_sys_directory.h" />
162 <ClInclude Include="src\file_util.h" /> 164 <ClInclude Include="src\file_util.h" />
163 <ClInclude Include="src\fixed_size_queue.h" /> 165 <ClInclude Include="src\fixed_size_queue.h" />
164 <ClInclude Include="src\hash.h" /> 166 <ClInclude Include="src\hash.h" />
@@ -186,6 +188,7 @@
186 <ClCompile Include="src\console_listener.cpp" /> 188 <ClCompile Include="src\console_listener.cpp" />
187 <ClCompile Include="src\extended_trace.cpp" /> 189 <ClCompile Include="src\extended_trace.cpp" />
188 <ClCompile Include="src\file_search.cpp" /> 190 <ClCompile Include="src\file_search.cpp" />
191 <ClCompile Include="src\file_sys_directory.cpp" />
189 <ClCompile Include="src\file_util.cpp" /> 192 <ClCompile Include="src\file_util.cpp" />
190 <ClCompile Include="src\hash.cpp" /> 193 <ClCompile Include="src\hash.cpp" />
191 <ClCompile Include="src\log_manager.cpp" /> 194 <ClCompile Include="src\log_manager.cpp" />
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
index ccc15d613..21e348df4 100644
--- a/src/common/common.vcxproj.filters
+++ b/src/common/common.vcxproj.filters
@@ -18,6 +18,7 @@
18 <ClCompile Include="src\timer.cpp" /> 18 <ClCompile Include="src\timer.cpp" />
19 <ClCompile Include="src\version.cpp" /> 19 <ClCompile Include="src\version.cpp" />
20 <ClCompile Include="src\utf8.cpp" /> 20 <ClCompile Include="src\utf8.cpp" />
21 <ClCompile Include="src\file_sys_directory.cpp" />
21 </ItemGroup> 22 </ItemGroup>
22 <ItemGroup> 23 <ItemGroup>
23 <ClInclude Include="src\atomic.h" /> 24 <ClInclude Include="src\atomic.h" />
@@ -57,6 +58,8 @@
57 <ClInclude Include="src\platform.h" /> 58 <ClInclude Include="src\platform.h" />
58 <ClInclude Include="src\swap.h" /> 59 <ClInclude Include="src\swap.h" />
59 <ClInclude Include="src\utf8.h" /> 60 <ClInclude Include="src\utf8.h" />
61 <ClInclude Include="src\file_sys.h" />
62 <ClInclude Include="src\file_sys_directory.h" />
60 </ItemGroup> 63 </ItemGroup>
61 <ItemGroup> 64 <ItemGroup>
62 <None Include="CMakeLists.txt" /> 65 <None Include="CMakeLists.txt" />
diff --git a/src/common/src/file_sys.h b/src/common/src/file_sys.h
new file mode 100644
index 000000000..b27e36c80
--- /dev/null
+++ b/src/common/src/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/common/src/file_sys_directory.cpp b/src/common/src/file_sys_directory.cpp
new file mode 100644
index 000000000..c20557bad
--- /dev/null
+++ b/src/common/src/file_sys_directory.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 "file_sys_directory.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 "Common/CommonWindows.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/common/src/file_sys_directory.h b/src/common/src/file_sys_directory.h
new file mode 100644
index 000000000..a11331a27
--- /dev/null
+++ b/src/common/src/file_sys_directory.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_