diff --git a/CMakeLists.txt b/CMakeLists.txt index 0445b74431..a508ed2eeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1622,8 +1622,6 @@ add_library(${CoreLibName} ${CoreLinkType} Core/ELF/ParamSFO.cpp Core/ELF/ParamSFO.h Core/FileSystems/tlzrc.cpp - Core/FileSystems/AndroidStorageFileSystem.cpp - Core/FileSystems/AndroidStorageFileSystem.h Core/FileSystems/BlobFileSystem.cpp Core/FileSystems/BlobFileSystem.h Core/FileSystems/BlockDevices.cpp diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index 7c3c990dfe..e236d58a2e 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -932,8 +932,7 @@ IOFile::IOFile(const Path &filename, const char openmode[]) { Open(filename, openmode); } -IOFile::~IOFile() -{ +IOFile::~IOFile() { Close(); } diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 8e6c9ff709..797daeeeb9 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -529,7 +529,6 @@ - @@ -1086,7 +1085,6 @@ - @@ -1400,4 +1398,4 @@ - \ No newline at end of file + diff --git a/Core/FileSystems/AndroidStorageFileSystem.cpp b/Core/FileSystems/AndroidStorageFileSystem.cpp deleted file mode 100644 index bf3571e603..0000000000 --- a/Core/FileSystems/AndroidStorageFileSystem.cpp +++ /dev/null @@ -1,543 +0,0 @@ -// Copyright (c) 2012- PPSSPP Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - -#include "ppsspp_config.h" - -#if PPSSPP_PLATFORM(ANDROID) -#include "android/jni/app-android.h" -#include "android/jni/AndroidContentURI.h" - -#include -#include -#include - -#include "Common/Data/Text/I18n.h" -#include "Common/Data/Encoding/Utf8.h" -#include "Common/Serialize/Serializer.h" -#include "Common/Serialize/SerializeFuncs.h" -#include "Common/StringUtils.h" -#include "Common/File/DirListing.h" -#include "Core/FileSystems/AndroidStorageFileSystem.h" -#include "Core/HLE/sceKernel.h" -#include "Core/HW/MemoryStick.h" -#include "Core/CoreTiming.h" -#include "Core/System.h" -#include "Core/Host.h" -#include "Core/Replay.h" -#include "Core/Reporting.h" - -AndroidStorageFileSystem::AndroidStorageFileSystem(IHandleAllocator *_hAlloc, std::string _basePath, FileSystemFlags _flags) : basePath(_basePath), flags(_flags) { - // File::CreateFullPath(basePath); - hAlloc = _hAlloc; -} - -AndroidStorageFileSystem::~AndroidStorageFileSystem() { - CloseAll(); -} - -bool AndroidDirectoryFileHandle::Open(const std::string &basePath, std::string &fileName, FileAccess access, u32 &error) { - error = 0; - - AndroidStorageContentURI contentUri = AndroidStorageContentURI(basePath).WithFilePath(fileName); - - // On the PSP, truncating doesn't lose data. If you seek later, you'll recover it. - // This is abnormal, so we deviate from the PSP's behavior and truncate on write/close. - // This means it's incorrectly not truncated before the write. - if (access & FILEACCESS_TRUNCATE) { - needsTrunc_ = 0; - } - - // Check for existance and directory, so we can make better decisions below. - File::FileInfo fileInfo; - if (!Android_GetFileInfo(contentUri.ToString(), &fileInfo)) { - ERROR_LOG(FILESYS, "Failed to call Android_GetFileInfo on %s", contentUri.ToString().c_str()); - return false; - } - - if (fileInfo.exists && fileInfo.isDirectory) { - ERROR_LOG(FILESYS, "AndroidDirectoryFileHandle::Open on directory %s - not allowed", contentUri.ToString().c_str()); - return false; - } - - // Android_OpenContentUriFd only has three modes: READ/READ_WRITE/READ_WRITE_TRUNCATE. - // For the modes that can create files, we have to explicitly create the file first, instead. - // My god, what a pain this is. Gonna need some sort of test suite. - - Android_OpenContentUriMode mode = Android_OpenContentUriMode::READ; - - // TODO: There are probably improvements to be made in this translation. - if (access & FILEACCESS_WRITE) { - mode = Android_OpenContentUriMode::READ_WRITE_TRUNCATE; - // Should this require EXCL too? - if (access & FILEACCESS_APPEND) { - mode = Android_OpenContentUriMode::READ_WRITE; - // Will also need to seek later. - } - } else if (access & FILEACCESS_READ) { - mode = Android_OpenContentUriMode::READ; - } - - bool success = true; - - if (access & FILEACCESS_CREATE) { - AndroidStorageContentURI parentUri(basePath); - - // We're gonna have to explicitly create the file here. - if (!Android_CreateFile(parentUri.ToString(), fileName)) { - // Something went wrong. - success = false; - } - } else { - // Requested an existing file, doesn't exist. - if (!fileInfo.exists) { - error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; - return false; - } - } - - // Not sure what to do with FILEACCESS_EXCL. - - std::string uriString = contentUri.ToString(); - - INFO_LOG(FILESYS, "AndroidDirectoryFileHandle::Open(%s)", uriString.c_str()); - - hFile = Android_OpenContentUriFd(uriString, mode); - if (hFile == -1) { - ERROR_LOG(FILESYS, "Android_OpenContentUriFd(%s) failed", uriString.c_str()); - success = false; - } - - // TODO: Experiment with fstat to see if we can extract more info. - - // Seek to end to simulate append mode, if requested. - if (access & FILEACCESS_APPEND) { - Seek(0, FILEMOVE_END); - } - - // Try to detect reads/writes to PSP/GAME to avoid them in replays. - if (basePath.find("/PSP/GAME/") != std::string::npos) { - inGameDir_ = true; - } - return success; -} - -size_t AndroidDirectoryFileHandle::Read(u8* pointer, s64 size) { - size_t bytesRead = 0; - if (needsTrunc_ != -1) { - // If the file was marked to be truncated, pretend there's nothing. - // On a PSP. it actually is truncated, but the data wasn't erased. - off_t off = (off_t)Seek(0, FILEMOVE_CURRENT); - if (needsTrunc_ <= off) { - return replay_ ? ReplayApplyDiskRead(pointer, 0, (uint32_t)size, inGameDir_, CoreTiming::GetGlobalTimeUs()) : 0; - } - if (needsTrunc_ < off + size) { - size = needsTrunc_ - off; - } - } - bytesRead = read(hFile, pointer, size); - return replay_ ? ReplayApplyDiskRead(pointer, (uint32_t)bytesRead, (uint32_t)size, inGameDir_, CoreTiming::GetGlobalTimeUs()) : bytesRead; -} - -size_t AndroidDirectoryFileHandle::Write(const u8* pointer, s64 size) -{ - size_t bytesWritten = 0; - bool diskFull = false; - - bytesWritten = write(hFile, pointer, size); - if (bytesWritten == (size_t)-1) { - diskFull = errno == ENOSPC; - } - if (needsTrunc_ != -1) { - off_t off = (off_t)Seek(0, FILEMOVE_CURRENT); - if (needsTrunc_ < off) { - needsTrunc_ = off; - } - } - - if (replay_) { - bytesWritten = ReplayApplyDiskWrite(pointer, (uint64_t)bytesWritten, (uint64_t)size, &diskFull, inGameDir_, CoreTiming::GetGlobalTimeUs()); - } - - if (diskFull) { - ERROR_LOG(FILESYS, "Disk full"); - auto err = GetI18NCategory("Error"); - host->NotifyUserMessage(err->T("Disk full while writing data")); - // We only return an error when the disk is actually full. - // When writing this would cause the disk to be full, so it wasn't written, we return 0. - if (MemoryStick_FreeSpace() == 0) { - // Sign extend on 64-bit. - return (size_t)(s64)(s32)SCE_KERNEL_ERROR_ERRNO_DEVICE_NO_FREE_SPACE; - } - } - - return bytesWritten; -} - -size_t AndroidDirectoryFileHandle::Seek(s32 position, FileMove type) -{ - if (needsTrunc_ != -1) { - // If the file is "currently truncated" move to the end based on that position. - // The actual, underlying file hasn't been truncated (yet.) - if (type == FILEMOVE_END) { - type = FILEMOVE_BEGIN; - position = needsTrunc_ + position; - } - } - - size_t result; - - int moveMethod = 0; - switch (type) { - case FILEMOVE_BEGIN: moveMethod = SEEK_SET; break; - case FILEMOVE_CURRENT: moveMethod = SEEK_CUR; break; - case FILEMOVE_END: moveMethod = SEEK_END; break; - } - result = lseek(hFile, position, moveMethod); - - return replay_ ? (size_t)ReplayApplyDisk64(ReplayAction::FILE_SEEK, result, CoreTiming::GetGlobalTimeUs()) : result; -} - -void AndroidDirectoryFileHandle::Close() -{ - if (needsTrunc_ != -1) { - // Note: it's not great that Switch cannot truncate appropriately... - if (ftruncate(hFile, (off_t)needsTrunc_) != 0) { - ERROR_LOG_REPORT(FILESYS, "Failed to truncate file."); - } - } - if (hFile != -1) - close(hFile); -} - -void AndroidStorageFileSystem::CloseAll() { - for (auto iter = entries.begin(); iter != entries.end(); ++iter) { - INFO_LOG(FILESYS, "DirectoryFileSystem::CloseAll(): Force closing %d (%s)", (int)iter->first, iter->second.guestFilename.c_str()); - iter->second.hFile.Close(); - } - entries.clear(); -} - -std::string AndroidStorageFileSystem::GetLocalPath(std::string localpath) { - if (localpath.empty()) { - return baseContentUri.ToString(); - } - - if (localpath[0] == '/') - localpath.erase(0, 1); - - return baseContentUri.WithFilePath(localpath).ToString(); -} - -bool AndroidStorageFileSystem::MkDir(const std::string &dirname) { - ERROR_LOG(FILESYS, "MkDir operation not yet supported."); - // TODO: Figure out a way to create directories in Storage... - // TODO: Use Android_CreateDirectory. Not sure how deep it can go... - bool result = false; - // bool result = File::CreateFullPath(GetLocalPath(dirname)); - return ReplayApplyDisk(ReplayAction::MKDIR, result, CoreTiming::GetGlobalTimeUs()) != 0; -} - -bool AndroidStorageFileSystem::RmDir(const std::string &dirname) { - - ERROR_LOG(FILESYS, "RmDir operation not yet supported."); - - return false; // ReplayApplyDisk(ReplayAction::RMDIR, result, CoreTiming::GetGlobalTimeUs()) != 0; -} - -int AndroidStorageFileSystem::RenameFile(const std::string &from, const std::string &to) { - std::string fullTo = to; - - // Rename ignores the path (even if specified) on to. - size_t chop_at = to.find_last_of('/'); - if (chop_at != to.npos) - fullTo = to.substr(chop_at + 1); - - // Now put it in the same directory as from. - size_t dirname_end = from.find_last_of('/'); - if (dirname_end != from.npos) - fullTo = from.substr(0, dirname_end + 1) + fullTo; - - // At this point, we should check if the paths match and give an already exists error. - if (from == fullTo) - return ReplayApplyDisk(ReplayAction::FILE_RENAME, SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS, CoreTiming::GetGlobalTimeUs()); - - std::string fullFrom = GetLocalPath(from); - - fullTo = GetLocalPath(fullTo); - const char * fullToC = fullTo.c_str(); - - bool retValue = (0 == rename(fullFrom.c_str(), fullToC)); - - // TODO: Better error codes. - int result = retValue ? 0 : (int)SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS; - return ReplayApplyDisk(ReplayAction::FILE_RENAME, result, CoreTiming::GetGlobalTimeUs()); -} - -bool AndroidStorageFileSystem::RemoveFile(const std::string &filename) { - std::string fullName = GetLocalPath(filename); - bool retValue = (0 == unlink(fullName.c_str())); - return ReplayApplyDisk(ReplayAction::FILE_REMOVE, retValue, CoreTiming::GetGlobalTimeUs()) != 0; -} - -int AndroidStorageFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) { - OpenFileEntry entry; - u32 err = 0; - bool success = entry.hFile.Open(basePath, filename, access, err); - if (err == 0 && !success) { - err = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; - } - - err = ReplayApplyDisk(ReplayAction::FILE_OPEN, err, CoreTiming::GetGlobalTimeUs()); - if (err != 0) { - ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", errno, (int)access); - return err; - } else { - if (access & FILEACCESS_APPEND) - entry.hFile.Seek(0, FILEMOVE_END); - - u32 newHandle = hAlloc->GetNewHandle(); - - entry.guestFilename = filename; - entry.access = access; - - entries[newHandle] = entry; - - return newHandle; - } -} - -void AndroidStorageFileSystem::CloseFile(u32 handle) { - EntryMap::iterator iter = entries.find(handle); - if (iter != entries.end()) { - hAlloc->FreeHandle(handle); - iter->second.hFile.Close(); - entries.erase(iter); - } else { - // This shouldn't happen... - ERROR_LOG(FILESYS, "Cannot close file that hasn't been opened: %08x", handle); - } -} - -bool AndroidStorageFileSystem::OwnsHandle(u32 handle) { - EntryMap::iterator iter = entries.find(handle); - return (iter != entries.end()); -} - -int AndroidStorageFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) { - return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; -} - -PSPDevType AndroidStorageFileSystem::DevType(u32 handle) { - return PSPDevType::FILE; -} - -size_t AndroidStorageFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) { - int ignored; - return ReadFile(handle, pointer, size, ignored); -} - -size_t AndroidStorageFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) { - EntryMap::iterator iter = entries.find(handle); - if (iter != entries.end()) { - if (size < 0) { - ERROR_LOG_REPORT(FILESYS, "Invalid read for %lld bytes from disk %s", size, iter->second.guestFilename.c_str()); - return 0; - } - - size_t bytesRead = iter->second.hFile.Read(pointer, size); - return bytesRead; - } else { - //This shouldn't happen... - ERROR_LOG(FILESYS, "Cannot read file that hasn't been opened: %08x", handle); - return 0; - } -} - -size_t AndroidStorageFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) { - int ignored; - return WriteFile(handle, pointer, size, ignored); -} - -size_t AndroidStorageFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) { - EntryMap::iterator iter = entries.find(handle); - if (iter != entries.end()) - { - size_t bytesWritten = iter->second.hFile.Write(pointer, size); - return bytesWritten; - } else { - //This shouldn't happen... - ERROR_LOG(FILESYS, "Cannot write to file that hasn't been opened: %08x", handle); - return 0; - } -} - -size_t AndroidStorageFileSystem::SeekFile(u32 handle, s32 position, FileMove type) { - EntryMap::iterator iter = entries.find(handle); - if (iter != entries.end()) { - return iter->second.hFile.Seek(position, type); - } else { - //This shouldn't happen... - ERROR_LOG(FILESYS, "Cannot seek in file that hasn't been opened: %08x", handle); - return 0; - } -} - -PSPFileInfo AndroidStorageFileSystem::GetFileInfo(std::string filename) { - PSPFileInfo x; - x.name = filename; - - std::string uri = GetLocalPath(filename); - - File::FileInfo info; - if (!Android_GetFileInfo(uri, &info)) { - return ReplayApplyDiskFileInfo(x, CoreTiming::GetGlobalTimeUs()); - } - - x.type = info.isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL; - x.exists = true; - - if (x.type != FILETYPE_DIRECTORY) { - x.size = info.size; - x.access = info.isWritable ? 0777 : 0666; - - // The only time value we get from the storage API. - int64_t lastModified = info.lastModified / 1000; - - time_t atime = lastModified; - time_t ctime = lastModified; - time_t mtime = lastModified; - - localtime_r((time_t*)&atime, &x.atime); - localtime_r((time_t*)&ctime, &x.ctime); - localtime_r((time_t*)&mtime, &x.mtime); - } - - return ReplayApplyDiskFileInfo(x, CoreTiming::GetGlobalTimeUs()); -} - -// See comment in DirectoryFileSystem. -extern std::string SimulateVFATBug(std::string filename); - -std::vector AndroidStorageFileSystem::GetDirListing(std::string path) { - std::vector myVector; - bool listingRoot = path == "/" || path == "\\"; - - std::string uri = GetLocalPath(path); - - std::vector fileInfo = Android_ListContentUri(uri); - - bool hideISOFiles = PSP_CoreParameter().compat.flags().HideISOFiles; - for (auto &info : fileInfo) { - PSPFileInfo entry; - if (info.isDirectory) { - entry.type = FILETYPE_DIRECTORY; - } else { - entry.type = FILETYPE_NORMAL; - } - entry.access = info.isWritable ? 0777 : 0666; - entry.name = info.name; - if (Flags() & FileSystemFlags::SIMULATE_FAT32) { - entry.name = SimulateVFATBug(entry.name); - } - entry.size = info.size; - - bool hideFile = false; - if (hideISOFiles && (endsWithNoCase(entry.name, ".cso") || endsWithNoCase(entry.name, ".iso"))) { - // Workaround for DJ Max Portable, see compat.ini. - hideFile = true; - } - - // TODO: Maybe do this conversion in the Android wrapper layer... - int64_t lastModified = info.lastModified / 1000; - - time_t atime = lastModified; - time_t ctime = lastModified; - time_t mtime = lastModified; - - localtime_r((time_t*)&atime, &entry.atime); - localtime_r((time_t*)&ctime, &entry.ctime); - localtime_r((time_t*)&mtime, &entry.mtime); - if (!hideFile && (!listingRoot || (strcmp(info.name.c_str(), "..") && strcmp(info.name.c_str(), ".")))) { - myVector.push_back(entry); - } - } - - return ReplayApplyDiskListing(myVector, CoreTiming::GetGlobalTimeUs()); -} - -u64 AndroidStorageFileSystem::FreeSpace(const std::string &path) { - // Can't get this, I think. - - return ReplayApplyDisk64(ReplayAction::FREESPACE, std::numeric_limits::max(), CoreTiming::GetGlobalTimeUs()); -} - -// WARNING! This must be kept compatible with DirectoryFileSystem for save state portability. -void AndroidStorageFileSystem::DoState(PointerWrap &p) { - auto s = p.Section("DirectoryFileSystem", 0, 2); - if (!s) - return; - - // Savestate layout: - // u32: number of entries - // per-entry: - // u32: handle number - // std::string filename (in guest's terms, untranslated) - // enum FileAccess file access mode - // u32 seek position - // s64 current truncate position (v2+ only) - - u32 num = (u32)entries.size(); - Do(p, num); - - if (p.mode == p.MODE_READ) { - CloseAll(); - u32 key; - OpenFileEntry entry; - for (u32 i = 0; i < num; i++) { - Do(p, key); - Do(p, entry.guestFilename); - Do(p, entry.access); - u32 err; - if (!entry.hFile.Open(basePath, entry.guestFilename, entry.access, err)) { - ERROR_LOG(FILESYS, "Failed to reopen file while loading state: %s", entry.guestFilename.c_str()); - continue; - } - u32 position; - Do(p, position); - if (position != entry.hFile.Seek(position, FILEMOVE_BEGIN)) { - ERROR_LOG(FILESYS, "Failed to restore seek position while loading state: %s", entry.guestFilename.c_str()); - continue; - } - if (s >= 2) { - Do(p, entry.hFile.needsTrunc_); - } - entries[key] = entry; - } - } else { - for (auto iter = entries.begin(); iter != entries.end(); ++iter) { - u32 key = iter->first; - Do(p, key); - Do(p, iter->second.guestFilename); - Do(p, iter->second.access); - u32 position = (u32)iter->second.hFile.Seek(0, FILEMOVE_CURRENT); - Do(p, position); - Do(p, iter->second.hFile.needsTrunc_); - } - } -} - -#endif // PPSSPP_PLATFORM(ANDROID) diff --git a/Core/FileSystems/AndroidStorageFileSystem.h b/Core/FileSystems/AndroidStorageFileSystem.h deleted file mode 100644 index 01af2bb318..0000000000 --- a/Core/FileSystems/AndroidStorageFileSystem.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -// Like DirectoryFileSystem, but uses the Android Storage Access Framework -// to access a folder tree as if it was a PSP file system. -// Unfortunately we cannot implement all the semantics like this, let's see -// how good we can get it though. - -// Copyright (c) 2012- PPSSPP Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - -#pragma once - -#include "ppsspp_config.h" - -#include - -#include "android/jni/AndroidContentURI.h" - -#include "Core/FileSystems/FileSystem.h" -#include "Core/FileSystems/AndroidStorageFileSystem.h" - -struct AndroidDirectoryFileHandle { - enum Flags { - NORMAL, - SKIP_REPLAY, - }; - - int hFile = -1; - s64 needsTrunc_ = -1; - bool replay_ = true; - bool inGameDir_ = false; - - AndroidDirectoryFileHandle(Flags flags) : replay_(flags != SKIP_REPLAY) { } - - // std::string GetLocalPath(const std::string &basePath, std::string localpath); - bool Open(const std::string &basePath, std::string &fileName, FileAccess access, u32 &err); - size_t Read(u8* pointer, s64 size); - size_t Write(const u8* pointer, s64 size); - size_t Seek(s32 position, FileMove type); - void Close(); -}; - -class AndroidStorageFileSystem : public IFileSystem { -public: - AndroidStorageFileSystem(IHandleAllocator *_hAlloc, std::string basePath, FileSystemFlags _flags = FileSystemFlags::NONE); - ~AndroidStorageFileSystem(); - - void CloseAll(); - - void DoState(PointerWrap &p) override; - std::vector GetDirListing(std::string path) override; - int OpenFile(std::string filename, FileAccess access, const char *devicename = nullptr) override; - void CloseFile(u32 handle) override; - size_t ReadFile(u32 handle, u8 *pointer, s64 size) override; - size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) override; - size_t WriteFile(u32 handle, const u8 *pointer, s64 size) override; - size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override; - size_t SeekFile(u32 handle, s32 position, FileMove type) override; - PSPFileInfo GetFileInfo(std::string filename) override; - bool OwnsHandle(u32 handle) override; - int Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) override; - PSPDevType DevType(u32 handle) override; - - bool MkDir(const std::string &dirname) override; - bool RmDir(const std::string &dirname) override; - int RenameFile(const std::string &from, const std::string &to) override; - bool RemoveFile(const std::string &filename) override; - FileSystemFlags Flags() override { return flags; } - u64 FreeSpace(const std::string &path) override; - -private: - struct OpenFileEntry { - std::string guestFilename; - FileAccess access; - AndroidDirectoryFileHandle hFile = AndroidDirectoryFileHandle::NORMAL; - }; - - typedef std::map EntryMap; - EntryMap entries; - - AndroidStorageContentURI baseContentUri; - - std::string basePath; - IHandleAllocator *hAlloc; - FileSystemFlags flags; - - // Actually, builds a content URI. - std::string GetLocalPath(std::string localpath); -}; -