From 76e8e899258151ee7c87fce05996ae533454c2fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Jul 2021 14:55:56 +0200 Subject: [PATCH 1/8] Add support for FILE_APPEND. --- Common/File/FileUtil.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index 5468386096..14ffec9aa4 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -223,6 +223,12 @@ int OpenFD(const Path &path, OpenFlag flags) { if (descriptor < 0) { ERROR_LOG(COMMON, "Android_OpenContentUriFd failed: '%s'", path.c_str()); } + + if (flags & OPEN_APPEND) { + // Simply seek to the end of the file to simulate append mode. + lseek(descriptor, 0, SEEK_END); + } + return descriptor; } From c6c5a93bf457bd9a4bd33fe846a9e5235b93198b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 24 Jul 2021 18:10:38 +0200 Subject: [PATCH 2/8] Couple of minor things, crashfix on deserialization failure --- Common/File/Path.cpp | 2 +- Common/File/Path.h | 2 +- Common/Serialize/Serializer.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Common/File/Path.cpp b/Common/File/Path.cpp index a45112599e..96110fc2d3 100644 --- a/Common/File/Path.cpp +++ b/Common/File/Path.cpp @@ -301,7 +301,7 @@ bool Path::IsAbsolute() const { return false; } -std::string Path::PathTo(const Path &other) { +std::string Path::PathTo(const Path &other) const { if (!other.StartsWith(*this)) { // Can't do this. Should return an error. return std::string(); diff --git a/Common/File/Path.h b/Common/File/Path.h index 3b0c911203..afec18ac7e 100644 --- a/Common/File/Path.h +++ b/Common/File/Path.h @@ -82,7 +82,7 @@ public: // For Android directory trees, navigates to the root of the tree. Path GetRootVolume() const; - std::string PathTo(const Path &child); + std::string PathTo(const Path &child) const; bool operator ==(const Path &other) const { return path_ == other.path_ && type_ == other.type_; diff --git a/Common/Serialize/Serializer.h b/Common/Serialize/Serializer.h index cfe14f01d8..77f024975c 100644 --- a/Common/Serialize/Serializer.h +++ b/Common/Serialize/Serializer.h @@ -143,7 +143,8 @@ public: if (p.error != p.ERROR_FAILURE) { return ERROR_NONE; } else { - *errorString = std::string("Failure at ") + p.GetBadSectionTitle(); + std::string badSectionTitle = p.GetBadSectionTitle() ? p.GetBadSectionTitle() : "(unknown bad section)"; + *errorString = std::string("Failure at ") + badSectionTitle; return ERROR_BROKEN_STATE; } } From 729496b60a3152266fea4281d76a0938d66136a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Jul 2021 12:55:33 +0200 Subject: [PATCH 3/8] Error code support preparations --- Common/File/AndroidStorage.cpp | 21 +++++++++++++++++++ Common/File/AndroidStorage.h | 20 ++++++++++++++++++ Common/File/FileUtil.cpp | 4 ++-- Core/FileLoaders/LocalFileLoader.cpp | 2 +- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 6 ++++++ 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index 35cfcee09c..980bfaa9e2 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -18,6 +18,7 @@ static jmethodID contentUriGetFileInfo; static jmethodID contentUriFileExists; static jmethodID contentUriGetFreeStorageSpace; static jmethodID filePathGetFreeStorageSpace; +static jmethodID isExternalStoragePreservedLegacy; static jobject g_nativeActivity; @@ -48,6 +49,8 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) { _dbg_assert_(contentUriGetFreeStorageSpace); filePathGetFreeStorageSpace = env->GetMethodID(env->GetObjectClass(obj), "filePathGetFreeStorageSpace", "(Ljava/lang/String;)J"); _dbg_assert_(filePathGetFreeStorageSpace); + isExternalStoragePreservedLegacy = env->GetMethodID(env->GetObjectClass(obj), "isExternalStoragePreservedLegacy", "()Z"); + _dbg_assert_(isExternalStoragePreservedLegacy); } bool Android_IsContentUri(const std::string &filename) { @@ -241,4 +244,22 @@ int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath) { return env->CallLongMethod(g_nativeActivity, filePathGetFreeStorageSpace, param); } +bool Android_IsExternalStoragePreservedLegacy() { + if (!g_nativeActivity) { + return false; + } + auto env = getEnv(); + return env->CallBooleanMethod(g_nativeActivity, isExternalStoragePreservedLegacy); +} + +const char *Android_ErrorToString(ContentError error) { + switch (error) { + case ContentError::SUCCESS: return "SUCCESS"; + case ContentError::OTHER: return "OTHER"; + case ContentError::NOT_FOUND: return "NOT_FOUND"; + case ContentError::DISK_FULL: return "DISK_FULL"; + default: return "(UNKNOWN)"; + } +} + #endif diff --git a/Common/File/AndroidStorage.h b/Common/File/AndroidStorage.h index a217b3d4e0..55d49da478 100644 --- a/Common/File/AndroidStorage.h +++ b/Common/File/AndroidStorage.h @@ -13,6 +13,22 @@ enum class Android_OpenContentUriMode { READ_WRITE_TRUNCATE = 2, // "rwt" }; +// Matches the constants in PpssppActivity.java. +enum class ContentError { + SUCCESS = 0, + OTHER = -1, + NOT_FOUND = -2, + DISK_FULL = -3, +}; + +inline ContentError ContentErrorFromInt(int ival) { + if (ival >= 0) { + return ContentError::SUCCESS; + } else { + return (ContentError)ival; + } +} + #if PPSSPP_PLATFORM(ANDROID) && !defined(__LIBRETRO__) #include @@ -32,6 +48,8 @@ bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info); bool Android_FileExists(const std::string &fileUri); int64_t Android_GetFreeSpaceByContentUri(const std::string &uri); int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath); +bool Android_IsExternalStoragePreservedLegacy(); +const char *Android_ErrorToString(ContentError error); std::vector Android_ListContentUri(const std::string &uri); @@ -52,6 +70,8 @@ inline bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info inline bool Android_FileExists(const std::string &fileUri) { return false; } inline int64_t Android_GetFreeSpaceByContentUri(const std::string &uri) { return -1; } inline int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath) { return -1; } +inline bool Android_IsExternalStoragePreservedLegacy() { return false; } +inline const char *Android_ErrorToString(ContentError error) { return ""; } inline std::vector Android_ListContentUri(const std::string &uri) { return std::vector(); } diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index 14ffec9aa4..f43e443c21 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -107,7 +107,7 @@ FILE *OpenCFile(const Path &path, const char *mode) { INFO_LOG(COMMON, "Opening content file for read: '%s'", path.c_str()); // Read, let's support this - easy one. int descriptor = Android_OpenContentUriFd(path.ToString(), Android_OpenContentUriMode::READ); - if (descriptor == -1) { + if (descriptor < 0) { return nullptr; } return fdopen(descriptor, "rb"); @@ -133,7 +133,7 @@ FILE *OpenCFile(const Path &path, const char *mode) { // TODO: Support append modes and stuff... For now let's go with the most common one. int descriptor = Android_OpenContentUriFd(path.ToString(), Android_OpenContentUriMode::READ_WRITE_TRUNCATE); - if (descriptor == -1) { + if (descriptor < 0) { INFO_LOG(COMMON, "Opening '%s' for write failed", path.ToString().c_str()); return nullptr; } diff --git a/Core/FileLoaders/LocalFileLoader.cpp b/Core/FileLoaders/LocalFileLoader.cpp index d0c4c6828c..96d3412cf6 100644 --- a/Core/FileLoaders/LocalFileLoader.cpp +++ b/Core/FileLoaders/LocalFileLoader.cpp @@ -61,7 +61,7 @@ LocalFileLoader::LocalFileLoader(const Path &filename) if (filename.Type() == PathType::CONTENT_URI) { int fd = Android_OpenContentUriFd(filename.ToString(), Android_OpenContentUriMode::READ); INFO_LOG(SYSTEM, "Fd %d for content URI: '%s'", fd, filename.c_str()); - if (fd == -1) { + if (fd < 0) { ERROR_LOG(FILESYS, "LoadFileLoader failed to open content URI: '%s'", filename.c_str()); return; } diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index aba075a2f7..02c733ec5c 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -31,6 +31,12 @@ public class PpssppActivity extends NativeActivity { public static boolean libraryLoaded = false; + // Matches the enum in AndroidStorage.h. + private static final int CONTENT_ERROR_SUCCESS = 0; + private static final int CONTENT_ERROR_OTHER = -1; + private static final int CONTENT_ERROR_NOT_FOUND = -2; + private static final int CONTENT_ERROR_DISK_FULL = -3; + @SuppressWarnings("deprecation") public static void CheckABIAndLoadLibrary() { if (Build.CPU_ABI.equals("armeabi")) { From ab69b896c3b2bd383990056ec0bca88424f90555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Jul 2021 15:32:15 +0200 Subject: [PATCH 4/8] Add and hook up Android_MoveFile --- Common/File/AndroidStorage.cpp | 19 ++++++++++ Common/File/AndroidStorage.h | 4 ++ Common/File/FileUtil.cpp | 37 ++++++++++++------- UWP/CommonUWP/CommonUWP.vcxproj | 4 +- UWP/CommonUWP/CommonUWP.vcxproj.filters | 8 +++- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 17 ++++++++- 6 files changed, 72 insertions(+), 17 deletions(-) diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index 980bfaa9e2..d7de92c64e 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -12,6 +12,7 @@ static jmethodID listContentUriDir; static jmethodID contentUriCreateFile; static jmethodID contentUriCreateDirectory; static jmethodID contentUriCopyFile; +static jmethodID contentUriMoveFile; static jmethodID contentUriRemoveFile; static jmethodID contentUriRenameFileTo; static jmethodID contentUriGetFileInfo; @@ -39,6 +40,8 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) { _dbg_assert_(contentUriCopyFile); contentUriRemoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriRemoveFile", "(Ljava/lang/String;)Z"); _dbg_assert_(contentUriRemoveFile); + contentUriMoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriMoveFile", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); + _dbg_assert_(contentUriMoveFile); contentUriRenameFileTo = env->GetMethodID(env->GetObjectClass(obj), "contentUriRenameFileTo", "(Ljava/lang/String;Ljava/lang/String;)Z"); _dbg_assert_(contentUriRenameFileTo); contentUriGetFileInfo = env->GetMethodID(env->GetObjectClass(obj), "contentUriGetFileInfo", "(Ljava/lang/String;)Ljava/lang/String;"); @@ -111,6 +114,17 @@ bool Android_CopyFile(const std::string &fileUri, const std::string &destParentU return env->CallBooleanMethod(g_nativeActivity, contentUriCopyFile, paramFileName, paramDestParentUri); } +bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { + if (!g_nativeActivity) { + return false; + } + auto env = getEnv(); + jstring paramFileName = env->NewStringUTF(fileUri.c_str()); + jstring paramSrcParentUri = env->NewStringUTF(srcParentUri.c_str()); + jstring paramDestParentUri = env->NewStringUTF(destParentUri.c_str()); + return env->CallBooleanMethod(g_nativeActivity, contentUriMoveFile, paramFileName, paramSrcParentUri, paramDestParentUri); +} + bool Android_RemoveFile(const std::string &fileUri) { if (!g_nativeActivity) { return false; @@ -262,4 +276,9 @@ const char *Android_ErrorToString(ContentError error) { } } +#else + +// This string should never appear except on Android. +std::string g_extFilesDir = "(IF YOU SEE THIS THERE'S A BUG)"; + #endif diff --git a/Common/File/AndroidStorage.h b/Common/File/AndroidStorage.h index 55d49da478..9decabaf48 100644 --- a/Common/File/AndroidStorage.h +++ b/Common/File/AndroidStorage.h @@ -41,6 +41,7 @@ bool Android_IsContentUri(const std::string &uri); int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode); bool Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName); bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName); +bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri); bool Android_CopyFile(const std::string &fileUri, const std::string &destParentUri); bool Android_RemoveFile(const std::string &fileUri); bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName); @@ -57,12 +58,15 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj); #else +extern std::string g_extFilesDir; + // Stub out the Android Storage wrappers, so that we can avoid ifdefs everywhere. inline bool Android_IsContentUri(const std::string &uri) { return false; } inline int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode) { return -1; } inline bool Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName) { return false; } inline bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { return false; } +inline bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { return false; } inline bool Android_CopyFile(const std::string &fileUri, const std::string &destParentUri) { return false; } inline bool Android_RemoveFile(const std::string &fileUri) { return false; } inline bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName) { return false; } diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index f43e443c21..8560694a48 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -571,6 +571,7 @@ bool Rename(const Path &srcFilename, const Path &destFilename) { break; case PathType::CONTENT_URI: // Content URI: Can only rename if in the same folder. + // TODO: Fallback to move + rename? Or do we even care about that use case? if (srcFilename.GetDirectory() != destFilename.GetDirectory()) { INFO_LOG(COMMON, "Content URI rename: Directories not matching, failing. %s --> %s", srcFilename.c_str(), destFilename.c_str()); return false; @@ -600,26 +601,23 @@ bool Rename(const Path &srcFilename, const Path &destFilename) { } // copies file srcFilename to destFilename, returns true on success -bool Copy(const Path &srcFilename, const Path &destFilename) -{ +bool Copy(const Path &srcFilename, const Path &destFilename) { switch (srcFilename.Type()) { case PathType::NATIVE: break; // OK case PathType::CONTENT_URI: - ERROR_LOG_REPORT_ONCE(copyUriNotSupported, COMMON, "Copying files by Android URI is not yet supported"); + if (destFilename.Type() == PathType::CONTENT_URI && destFilename.CanNavigateUp()) { + Path destParent = destFilename.NavigateUp(); + // Use native file copy. + if (Android_CopyFile(srcFilename.ToString(), destParent.ToString())) { + return true; + } + // Else fall through, and try using file I/O. + } break; default: return false; } - switch (destFilename.Type()) { - case PathType::NATIVE: - break; // OK - case PathType::CONTENT_URI: - ERROR_LOG_REPORT_ONCE(copyUriNotSupported, COMMON, "Copying files by Android URI is not yet supported"); - return false; - default: - return false; - } INFO_LOG(COMMON, "Copy: %s --> %s", srcFilename.c_str(), destFilename.c_str()); #ifdef _WIN32 @@ -637,7 +635,7 @@ bool Copy(const Path &srcFilename, const Path &destFilename) #else // buffer size -#define BSIZE 4096 +#define BSIZE 16384 char buffer[BSIZE]; @@ -692,6 +690,19 @@ bool Copy(const Path &srcFilename, const Path &destFilename) } bool Move(const Path &srcFilename, const Path &destFilename) { + // Try a shortcut in Android Storage scenarios. + if (srcFilename.Type() == PathType::CONTENT_URI && destFilename.Type() == PathType::CONTENT_URI && srcFilename.CanNavigateUp() && destFilename.CanNavigateUp()) { + // We do not handle simultaneous renames here. + if (srcFilename.GetFilename() == destFilename.GetFilename()) { + Path srcParent = srcFilename.NavigateUp(); + Path dstParent = destFilename.NavigateUp(); + if (Android_MoveFile(srcFilename.ToString(), srcParent.ToString(), dstParent.ToString())) { + return true; + } + // If failed, fall through and try other ways. + } + } + if (Rename(srcFilename, destFilename)) { return true; } else if (Copy(srcFilename, destFilename)) { diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index e91ec0d58b..3349afd9e6 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -386,6 +386,7 @@ + @@ -518,6 +519,7 @@ + @@ -640,4 +642,4 @@ - + \ No newline at end of file diff --git a/UWP/CommonUWP/CommonUWP.vcxproj.filters b/UWP/CommonUWP/CommonUWP.vcxproj.filters index a72807393e..740747dfd4 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj.filters +++ b/UWP/CommonUWP/CommonUWP.vcxproj.filters @@ -375,6 +375,9 @@ GPU + + File + @@ -691,6 +694,9 @@ GPU + + File + @@ -705,4 +711,4 @@ Math\lin - + \ No newline at end of file diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 02c733ec5c..89e02699c9 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -268,14 +268,27 @@ public class PpssppActivity extends NativeActivity { try { Uri srcUri = Uri.parse(srcFileUri); Uri dstParentUri = Uri.parse(dstParentDirUri); - DocumentsContract.copyDocument(getContentResolver(), srcUri, dstParentUri); - return true; + return DocumentsContract.copyDocument(getContentResolver(), srcUri, dstParentUri) != null; } catch (Exception e) { Log.e(TAG, "contentUriCopyFile exception: " + e.toString()); return false; } } + // NOTE: The destination is the parent directory! This means that contentUriCopyFile + // cannot rename things as part of the operation. + public boolean contentUriMoveFile(String srcFileUri, String srcParentDirUri, String dstParentDirUri) { + try { + Uri srcUri = Uri.parse(srcFileUri); + Uri srcParentUri = Uri.parse(srcParentDirUri); + Uri dstParentUri = Uri.parse(dstParentDirUri); + return DocumentsContract.moveDocument(getContentResolver(), srcUri, srcParentUri, dstParentUri) != null; + } catch (Exception e) { + Log.e(TAG, "contentUriMoveFile exception: " + e.toString()); + return false; + } + } + public boolean contentUriRenameFileTo(String fileUri, String newName) { try { Uri uri = Uri.parse(fileUri); From 8ab627d6e981d299bc1ffe2d446bfa97fa1e576d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 24 Jul 2021 18:16:12 +0200 Subject: [PATCH 5/8] Workaround for the /PSP path thing. Needs testing. --- Core/FileSystems/DirectoryFileSystem.cpp | 37 +++++++++++++++------- Core/FileSystems/DirectoryFileSystem.h | 15 +++++---- Core/FileSystems/FileSystem.h | 1 + Core/FileSystems/VirtualDiscFileSystem.cpp | 7 ++-- Core/FileSystems/VirtualDiscFileSystem.h | 28 +++++++++------- Core/HLE/sceIo.cpp | 16 +++++++--- Core/System.cpp | 2 ++ Core/System.h | 1 + 8 files changed, 70 insertions(+), 37 deletions(-) diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 06fea1f5a9..4a7bde8f8b 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -177,17 +177,40 @@ DirectoryFileSystem::~DirectoryFileSystem() { CloseAll(); } -Path DirectoryFileHandle::GetLocalPath(const Path &basePath, std::string localpath) -{ +// TODO(scoped): Merge the two below functions somehow. + +Path DirectoryFileHandle::GetLocalPath(const Path &basePath, std::string localpath) const { if (localpath.empty()) return basePath; if (localpath[0] == '/') localpath.erase(0, 1); + if (fileSystemFlags_ & FileSystemFlags::STRIP_PSP) { + if (startsWith(localpath, "/PSP")) { + localpath = localpath.substr(4); + } + } + return basePath / localpath; } +Path DirectoryFileSystem::GetLocalPath(std::string internalPath) const { + if (internalPath.empty()) + return basePath; + + if (internalPath[0] == '/') + internalPath.erase(0, 1); + + if (flags & FileSystemFlags::STRIP_PSP) { + if (startsWith(internalPath, "/PSP")) { + internalPath = internalPath.substr(4); + } + } + + return basePath / internalPath; +} + bool DirectoryFileHandle::Open(const Path &basePath, std::string &fileName, FileAccess access, u32 &error) { error = 0; @@ -508,16 +531,6 @@ void DirectoryFileSystem::CloseAll() { entries.clear(); } -Path DirectoryFileSystem::GetLocalPath(std::string internalPath) { - if (internalPath.empty()) - return basePath; - - if (internalPath[0] == '/') - internalPath.erase(0, 1); - - return basePath / internalPath; -} - bool DirectoryFileSystem::MkDir(const std::string &dirname) { bool result; #if HOST_IS_CASE_SENSITIVE diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index b7b71c7c34..9d8478d691 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -72,11 +72,14 @@ struct DirectoryFileHandle { s64 needsTrunc_ = -1; bool replay_ = true; bool inGameDir_ = false; + FileSystemFlags fileSystemFlags_ = (FileSystemFlags)0; - DirectoryFileHandle(Flags flags) : replay_(flags != SKIP_REPLAY) { - } + DirectoryFileHandle() {} - Path GetLocalPath(const Path &basePath, std::string localpath); + DirectoryFileHandle(Flags flags, FileSystemFlags fileSystemFlags) + : replay_(flags != SKIP_REPLAY), fileSystemFlags_(fileSystemFlags) {} + + Path GetLocalPath(const Path &basePath, std::string localpath) const; bool Open(const Path &basePath, std::string &fileName, FileAccess access, u32 &err); size_t Read(u8* pointer, s64 size); size_t Write(const u8* pointer, s64 size); @@ -114,7 +117,7 @@ public: private: struct OpenFileEntry { - DirectoryFileHandle hFile = DirectoryFileHandle::NORMAL; + DirectoryFileHandle hFile; std::string guestFilename; FileAccess access = FILEACCESS_NONE; }; @@ -124,8 +127,8 @@ private: Path basePath; IHandleAllocator *hAlloc; FileSystemFlags flags; - // In case of Windows: Translate slashes, etc. - Path GetLocalPath(std::string internalPath); + + Path GetLocalPath(std::string internalPath) const; }; // VFSFileSystem: Ability to map in Android APK paths as well! Does not support all features, only meant for fonts. diff --git a/Core/FileSystems/FileSystem.h b/Core/FileSystems/FileSystem.h index 87e8c92e07..5df227dd1f 100644 --- a/Core/FileSystems/FileSystem.h +++ b/Core/FileSystems/FileSystem.h @@ -63,6 +63,7 @@ enum class FileSystemFlags { UMD = 2, CARD = 4, FLASH = 8, + STRIP_PSP = 16, }; ENUM_CLASS_BITOPS(FileSystemFlags); diff --git a/Core/FileSystems/VirtualDiscFileSystem.cpp b/Core/FileSystems/VirtualDiscFileSystem.cpp index 276a89f1be..889bc506fc 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.cpp +++ b/Core/FileSystems/VirtualDiscFileSystem.cpp @@ -187,7 +187,7 @@ void VirtualDiscFileSystem::DoState(PointerWrap &p) for (int i = 0; i < entryCount; i++) { u32 fd = 0; - OpenFileEntry of; + OpenFileEntry of(Flags()); Do(p, fd); Do(p, of.fileIndex); @@ -214,6 +214,7 @@ void VirtualDiscFileSystem::DoState(PointerWrap &p) } } + // TODO: I think we only need to write to the map on load? entries[fd] = of; } } else { @@ -321,7 +322,7 @@ int VirtualDiscFileSystem::getFileListIndex(u32 accessBlock, u32 accessSize, boo int VirtualDiscFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) { - OpenFileEntry entry; + OpenFileEntry entry(Flags()); entry.curOffset = 0; entry.size = 0; entry.startOffset = 0; @@ -471,7 +472,7 @@ size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &u return 0; } - OpenFileEntry temp; + OpenFileEntry temp(Flags()); if (fileList[fileIndex].handler != NULL) { temp.handler = fileList[fileIndex].handler; } diff --git a/Core/FileSystems/VirtualDiscFileSystem.h b/Core/FileSystems/VirtualDiscFileSystem.h index bd8604ae3d..6c76d6b6fb 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.h +++ b/Core/FileSystems/VirtualDiscFileSystem.h @@ -87,17 +87,15 @@ private: ReadFunc Read; CloseFunc Close; - bool IsValid() const { return library != NULL; } + bool IsValid() const { return library != nullptr; } }; struct HandlerFileHandle { Handler *handler; HandlerHandle handle; - HandlerFileHandle() : handler(NULL), handle(0) { - } - HandlerFileHandle(Handler *handler_) : handler(handler_), handle(-1) { - } + HandlerFileHandle() : handler(nullptr), handle(0) {} + HandlerFileHandle(Handler *handler_) : handler(handler_), handle(-1) {} bool Open(const std::string& basePath, const std::string& fileName, FileAccess access) { // Ignore access, read only. @@ -115,7 +113,7 @@ private: } bool IsValid() { - return handler != NULL && handler->IsValid(); + return handler != nullptr && handler->IsValid(); } HandlerFileHandle &operator =(Handler *_handler) { @@ -127,13 +125,18 @@ private: typedef enum { VFILETYPE_NORMAL, VFILETYPE_LBN, VFILETYPE_ISO } VirtualFileType; struct OpenFileEntry { - DirectoryFileHandle hFile = DirectoryFileHandle::SKIP_REPLAY; + OpenFileEntry() {} + OpenFileEntry(FileSystemFlags fileSystemFlags) { + hFile = DirectoryFileHandle(DirectoryFileHandle::SKIP_REPLAY, fileSystemFlags); + } + + DirectoryFileHandle hFile; HandlerFileHandle handler; - VirtualFileType type; - u32 fileIndex; - u64 curOffset; - u64 startOffset; // only used by lbn files - u64 size; // only used by lbn files + VirtualFileType type = VFILETYPE_NORMAL; + u32 fileIndex = 0; + u64 curOffset = 0; + u64 startOffset = 0; // only used by lbn files + u64 size = 0; // only used by lbn files bool Open(const Path &basePath, std::string& fileName, FileAccess access) { // Ignored, we're read only. @@ -168,6 +171,7 @@ private: }; typedef std::map EntryMap; + EntryMap entries; IHandleAllocator *hAlloc; Path basePath; diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 7128bfa73d..3b99c2d2d3 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -631,19 +631,28 @@ void __IoInit() { asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify); syncNotifyEvent = CoreTiming::RegisterEvent("IoSyncNotify", __IoSyncNotify); - auto memstickSystem = std::shared_ptr(new DirectoryFileSystem(&pspFileSystem, g_Config.memStickDirectory, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD)); + // TODO(scoped): This won't work if memStickDirectory points at the contents of /PSP... #if defined(USING_WIN_UI) || defined(APPLE) auto flash0System = std::shared_ptr(new DirectoryFileSystem(&pspFileSystem, g_Config.flash0Directory, FileSystemFlags::FLASH)); #else auto flash0System = std::shared_ptr(new VFSFileSystem(&pspFileSystem, "flash0")); #endif + FileSystemFlags memstickFlags = FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD; + + Path pspDir = GetSysDirectory(DIRECTORY_PSP); + if (pspDir == g_Config.memStickDirectory) { + // Initially tried to do this with dual mounts, but failed due to save state compatibility issues. + INFO_LOG(SCEIO, "Enabling /PSP compatibility mode"); + memstickFlags |= FileSystemFlags::STRIP_PSP; + } + + auto memstickSystem = std::shared_ptr(new DirectoryFileSystem(&pspFileSystem, g_Config.memStickDirectory, memstickFlags)); - // TODO(scoped): This won't work if memStickDirectory points at the contents of /PSP... - // Will fix later with dual mounts (first mount ms0:/PSP/ at memstickSystem), then also mount ms0:/ on it) pspFileSystem.Mount("ms0:", memstickSystem); pspFileSystem.Mount("fatms0:", memstickSystem); pspFileSystem.Mount("fatms:", memstickSystem); pspFileSystem.Mount("pfat0:", memstickSystem); + pspFileSystem.Mount("flash0:", flash0System); if (g_RemasterMode) { @@ -768,7 +777,6 @@ void __IoShutdown() { pspFileSystem.UnmountAll(); - // All the file systems will be removed when we shut down pspFileSystem later. MemoryStick_Shutdown(); memStickCallbacks.clear(); memStickFatCallbacks.clear(); diff --git a/Core/System.cpp b/Core/System.cpp index b9f2ccf0e8..d2427d43fd 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -592,6 +592,8 @@ Path GetSysDirectory(PSPDirectories directoryType) { } switch (directoryType) { + case DIRECTORY_PSP: + return pspDirectory; case DIRECTORY_CHEATS: return pspDirectory / "Cheats"; case DIRECTORY_GAME: diff --git a/Core/System.h b/Core/System.h index 29b95bb7a4..13423a0167 100644 --- a/Core/System.h +++ b/Core/System.h @@ -37,6 +37,7 @@ enum GlobalUIState { // Use these in conjunction with GetSysDirectory. enum PSPDirectories { + DIRECTORY_PSP, DIRECTORY_CHEATS, DIRECTORY_SCREENSHOT, DIRECTORY_SYSTEM, From 2333239514361b8fa2fec03168ef0079c0b1254c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Jul 2021 14:12:57 +0200 Subject: [PATCH 6/8] Change one storage function to use an error code. --- Common/File/AndroidStorage.cpp | 19 ++++++++++--------- Common/File/AndroidStorage.h | 19 ++++++++++--------- Common/File/FileUtil.cpp | 2 +- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 16 +++++++++------- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index d7de92c64e..552be7686d 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -42,7 +42,7 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) { _dbg_assert_(contentUriRemoveFile); contentUriMoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriMoveFile", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); _dbg_assert_(contentUriMoveFile); - contentUriRenameFileTo = env->GetMethodID(env->GetObjectClass(obj), "contentUriRenameFileTo", "(Ljava/lang/String;Ljava/lang/String;)Z"); + contentUriRenameFileTo = env->GetMethodID(env->GetObjectClass(obj), "contentUriRenameFileTo", "(Ljava/lang/String;Ljava/lang/String;)I"); _dbg_assert_(contentUriRenameFileTo); contentUriGetFileInfo = env->GetMethodID(env->GetObjectClass(obj), "contentUriGetFileInfo", "(Ljava/lang/String;)Ljava/lang/String;"); _dbg_assert_(contentUriGetFileInfo); @@ -134,14 +134,14 @@ bool Android_RemoveFile(const std::string &fileUri) { return env->CallBooleanMethod(g_nativeActivity, contentUriRemoveFile, paramFileName); } -bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName) { +StorageError Android_RenameFileTo(const std::string &fileUri, const std::string &newName) { if (!g_nativeActivity) { - return false; + return StorageError::UNKNOWN; } auto env = getEnv(); jstring paramFileUri = env->NewStringUTF(fileUri.c_str()); jstring paramNewName = env->NewStringUTF(newName.c_str()); - return env->CallBooleanMethod(g_nativeActivity, contentUriRenameFileTo, paramFileUri, paramNewName); + return StorageErrorFromInt(env->CallIntMethod(g_nativeActivity, contentUriRenameFileTo, paramFileUri, paramNewName)); } // NOTE: Does not set fullName - you're supposed to already know it. @@ -266,12 +266,13 @@ bool Android_IsExternalStoragePreservedLegacy() { return env->CallBooleanMethod(g_nativeActivity, isExternalStoragePreservedLegacy); } -const char *Android_ErrorToString(ContentError error) { +const char *Android_ErrorToString(StorageError error) { switch (error) { - case ContentError::SUCCESS: return "SUCCESS"; - case ContentError::OTHER: return "OTHER"; - case ContentError::NOT_FOUND: return "NOT_FOUND"; - case ContentError::DISK_FULL: return "DISK_FULL"; + case StorageError::SUCCESS: return "SUCCESS"; + case StorageError::UNKNOWN: return "UNKNOWN"; + case StorageError::NOT_FOUND: return "NOT_FOUND"; + case StorageError::DISK_FULL: return "DISK_FULL"; + case StorageError::ALREADY_EXISTS: return "ALREADY_EXISTS"; default: return "(UNKNOWN)"; } } diff --git a/Common/File/AndroidStorage.h b/Common/File/AndroidStorage.h index 9decabaf48..6510f16985 100644 --- a/Common/File/AndroidStorage.h +++ b/Common/File/AndroidStorage.h @@ -14,18 +14,19 @@ enum class Android_OpenContentUriMode { }; // Matches the constants in PpssppActivity.java. -enum class ContentError { +enum class StorageError { SUCCESS = 0, - OTHER = -1, + UNKNOWN = -1, NOT_FOUND = -2, DISK_FULL = -3, + ALREADY_EXISTS = -4, }; -inline ContentError ContentErrorFromInt(int ival) { +inline StorageError StorageErrorFromInt(int ival) { if (ival >= 0) { - return ContentError::SUCCESS; + return StorageError::SUCCESS; } else { - return (ContentError)ival; + return (StorageError)ival; } } @@ -44,13 +45,13 @@ bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fil bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri); bool Android_CopyFile(const std::string &fileUri, const std::string &destParentUri); bool Android_RemoveFile(const std::string &fileUri); -bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName); +StorageError Android_RenameFileTo(const std::string &fileUri, const std::string &newName); bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info); bool Android_FileExists(const std::string &fileUri); int64_t Android_GetFreeSpaceByContentUri(const std::string &uri); int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath); bool Android_IsExternalStoragePreservedLegacy(); -const char *Android_ErrorToString(ContentError error); +const char *Android_ErrorToString(StorageError error); std::vector Android_ListContentUri(const std::string &uri); @@ -69,13 +70,13 @@ inline bool Android_CreateFile(const std::string &parentTreeUri, const std::stri inline bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { return false; } inline bool Android_CopyFile(const std::string &fileUri, const std::string &destParentUri) { return false; } inline bool Android_RemoveFile(const std::string &fileUri) { return false; } -inline bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName) { return false; } +inline StorageError Android_RenameFileTo(const std::string &fileUri, const std::string &newName) { return StorageError::UNKNOWN; } inline bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info) { return false; } inline bool Android_FileExists(const std::string &fileUri) { return false; } inline int64_t Android_GetFreeSpaceByContentUri(const std::string &uri) { return -1; } inline int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath) { return -1; } inline bool Android_IsExternalStoragePreservedLegacy() { return false; } -inline const char *Android_ErrorToString(ContentError error) { return ""; } +inline const char *Android_ErrorToString(StorageError error) { return ""; } inline std::vector Android_ListContentUri(const std::string &uri) { return std::vector(); } diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index 8560694a48..7c58f83f9b 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -578,7 +578,7 @@ bool Rename(const Path &srcFilename, const Path &destFilename) { } INFO_LOG(COMMON, "Content URI rename: %s --> %s", srcFilename.c_str(), destFilename.c_str()); - return Android_RenameFileTo(srcFilename.ToString(), destFilename.GetFilename()); + return Android_RenameFileTo(srcFilename.ToString(), destFilename.GetFilename()) == StorageError::SUCCESS; default: return false; } diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 89e02699c9..dedd1d549a 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -32,10 +32,11 @@ public class PpssppActivity extends NativeActivity { public static boolean libraryLoaded = false; // Matches the enum in AndroidStorage.h. - private static final int CONTENT_ERROR_SUCCESS = 0; - private static final int CONTENT_ERROR_OTHER = -1; - private static final int CONTENT_ERROR_NOT_FOUND = -2; - private static final int CONTENT_ERROR_DISK_FULL = -3; + private static final int STORAGE_ERROR_SUCCESS = 0; + private static final int STORAGE_ERROR_UNKNOWN = -1; + private static final int STORAGE_ERROR_NOT_FOUND = -2; + private static final int STORAGE_ERROR_DISK_FULL = -3; + private static final int STORAGE_ERROR_ALREADY_EXISTS = -4; @SuppressWarnings("deprecation") public static void CheckABIAndLoadLibrary() { @@ -289,17 +290,18 @@ public class PpssppActivity extends NativeActivity { } } - public boolean contentUriRenameFileTo(String fileUri, String newName) { + public int contentUriRenameFileTo(String fileUri, String newName) { try { Uri uri = Uri.parse(fileUri); // Due to a design flaw, we can't use DocumentFile.renameTo(). // Instead we use the DocumentsContract API directly. // See https://stackoverflow.com/questions/37168200/android-5-0-new-sd-card-access-api-documentfile-renameto-unsupportedoperation. Uri newUri = DocumentsContract.renameDocument(getContentResolver(), uri, newName); - return true; + return STORAGE_ERROR_SUCCESS; } catch (Exception e) { + // TODO: More detailed exception processing. Log.e(TAG, "contentUriRenameFile exception: " + e.toString()); - return false; + return STORAGE_ERROR_UNKNOWN; } } From 0997b8500c03e916e7b392f993ff305fde39af2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Jul 2021 14:46:29 +0200 Subject: [PATCH 7/8] Convert four more functions to allow for error codes. Note that they still don't actually use them... --- Common/File/AndroidStorage.cpp | 24 +++++++++---------- Common/File/AndroidStorage.h | 12 +++++----- Common/File/FileUtil.cpp | 15 ++++++++---- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 20 ++++++++-------- 4 files changed, 39 insertions(+), 32 deletions(-) diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index 552be7686d..4eb11b20e7 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -36,11 +36,11 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) { _dbg_assert_(contentUriCreateDirectory); contentUriCreateFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriCreateFile", "(Ljava/lang/String;Ljava/lang/String;)Z"); _dbg_assert_(contentUriCreateFile); - contentUriCopyFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriCopyFile", "(Ljava/lang/String;Ljava/lang/String;)Z"); + contentUriCopyFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriCopyFile", "(Ljava/lang/String;Ljava/lang/String;)I"); _dbg_assert_(contentUriCopyFile); - contentUriRemoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriRemoveFile", "(Ljava/lang/String;)Z"); + contentUriRemoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriRemoveFile", "(Ljava/lang/String;)I"); _dbg_assert_(contentUriRemoveFile); - contentUriMoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriMoveFile", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); + contentUriMoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriMoveFile", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I"); _dbg_assert_(contentUriMoveFile); contentUriRenameFileTo = env->GetMethodID(env->GetObjectClass(obj), "contentUriRenameFileTo", "(Ljava/lang/String;Ljava/lang/String;)I"); _dbg_assert_(contentUriRenameFileTo); @@ -104,34 +104,34 @@ bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fil return env->CallBooleanMethod(g_nativeActivity, contentUriCreateFile, paramRoot, paramFileName); } -bool Android_CopyFile(const std::string &fileUri, const std::string &destParentUri) { +StorageError Android_CopyFile(const std::string &fileUri, const std::string &destParentUri) { if (!g_nativeActivity) { - return false; + return StorageError::UNKNOWN; } auto env = getEnv(); jstring paramFileName = env->NewStringUTF(fileUri.c_str()); jstring paramDestParentUri = env->NewStringUTF(destParentUri.c_str()); - return env->CallBooleanMethod(g_nativeActivity, contentUriCopyFile, paramFileName, paramDestParentUri); + return StorageErrorFromInt(env->CallIntMethod(g_nativeActivity, contentUriCopyFile, paramFileName, paramDestParentUri)); } -bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { +StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { if (!g_nativeActivity) { - return false; + return StorageError::UNKNOWN; } auto env = getEnv(); jstring paramFileName = env->NewStringUTF(fileUri.c_str()); jstring paramSrcParentUri = env->NewStringUTF(srcParentUri.c_str()); jstring paramDestParentUri = env->NewStringUTF(destParentUri.c_str()); - return env->CallBooleanMethod(g_nativeActivity, contentUriMoveFile, paramFileName, paramSrcParentUri, paramDestParentUri); + return StorageErrorFromInt(env->CallIntMethod(g_nativeActivity, contentUriMoveFile, paramFileName, paramSrcParentUri, paramDestParentUri)); } -bool Android_RemoveFile(const std::string &fileUri) { +StorageError Android_RemoveFile(const std::string &fileUri) { if (!g_nativeActivity) { - return false; + return StorageError::UNKNOWN; } auto env = getEnv(); jstring paramFileName = env->NewStringUTF(fileUri.c_str()); - return env->CallBooleanMethod(g_nativeActivity, contentUriRemoveFile, paramFileName); + return StorageErrorFromInt(env->CallIntMethod(g_nativeActivity, contentUriRemoveFile, paramFileName)); } StorageError Android_RenameFileTo(const std::string &fileUri, const std::string &newName) { diff --git a/Common/File/AndroidStorage.h b/Common/File/AndroidStorage.h index 6510f16985..be766fb97e 100644 --- a/Common/File/AndroidStorage.h +++ b/Common/File/AndroidStorage.h @@ -42,9 +42,9 @@ bool Android_IsContentUri(const std::string &uri); int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode); bool Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName); bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName); -bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri); -bool Android_CopyFile(const std::string &fileUri, const std::string &destParentUri); -bool Android_RemoveFile(const std::string &fileUri); +StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri); +StorageError Android_CopyFile(const std::string &fileUri, const std::string &destParentUri); +StorageError Android_RemoveFile(const std::string &fileUri); StorageError Android_RenameFileTo(const std::string &fileUri, const std::string &newName); bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info); bool Android_FileExists(const std::string &fileUri); @@ -67,9 +67,9 @@ inline bool Android_IsContentUri(const std::string &uri) { return false; } inline int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode) { return -1; } inline bool Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName) { return false; } inline bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { return false; } -inline bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { return false; } -inline bool Android_CopyFile(const std::string &fileUri, const std::string &destParentUri) { return false; } -inline bool Android_RemoveFile(const std::string &fileUri) { return false; } +inline StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { return StorageError::UNKNOWN; } +inline StorageError Android_CopyFile(const std::string &fileUri, const std::string &destParentUri) { return StorageError::UNKNOWN; } +inline StorageError Android_RemoveFile(const std::string &fileUri) { return StorageError::UNKNOWN; } inline StorageError Android_RenameFileTo(const std::string &fileUri, const std::string &newName) { return StorageError::UNKNOWN; } inline bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info) { return false; } inline bool Android_FileExists(const std::string &fileUri) { return false; } diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index 7c58f83f9b..9684b3b26c 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -396,7 +396,7 @@ bool Delete(const Path &filename) { case PathType::NATIVE: break; // OK case PathType::CONTENT_URI: - return Android_RemoveFile(filename.ToString()); + return Android_RemoveFile(filename.ToString()) == StorageError::SUCCESS; default: return false; } @@ -439,6 +439,12 @@ bool CreateDir(const Path &path) { break; // OK case PathType::CONTENT_URI: { + // NOTE: The Android storage API will simply create a renamed directory (append a number) if it already exists. + // We want to avoid that, so let's just return true if the directory already is there. + if (File::Exists(path)) { + return true; + } + // Convert it to a "CreateDirIn" call, if possible, since that's // what we can do with the storage API. AndroidContentURI uri(path.ToString()); @@ -532,7 +538,7 @@ bool DeleteDir(const Path &path) { case PathType::NATIVE: break; // OK case PathType::CONTENT_URI: - return Android_RemoveFile(path.ToString()); + return Android_RemoveFile(path.ToString()) == StorageError::SUCCESS; default: return false; } @@ -609,7 +615,7 @@ bool Copy(const Path &srcFilename, const Path &destFilename) { if (destFilename.Type() == PathType::CONTENT_URI && destFilename.CanNavigateUp()) { Path destParent = destFilename.NavigateUp(); // Use native file copy. - if (Android_CopyFile(srcFilename.ToString(), destParent.ToString())) { + if (Android_CopyFile(srcFilename.ToString(), destParent.ToString()) == StorageError::SUCCESS) { return true; } // Else fall through, and try using file I/O. @@ -689,6 +695,7 @@ bool Copy(const Path &srcFilename, const Path &destFilename) { #endif } +// Will overwrite the target. bool Move(const Path &srcFilename, const Path &destFilename) { // Try a shortcut in Android Storage scenarios. if (srcFilename.Type() == PathType::CONTENT_URI && destFilename.Type() == PathType::CONTENT_URI && srcFilename.CanNavigateUp() && destFilename.CanNavigateUp()) { @@ -696,7 +703,7 @@ bool Move(const Path &srcFilename, const Path &destFilename) { if (srcFilename.GetFilename() == destFilename.GetFilename()) { Path srcParent = srcFilename.NavigateUp(); Path dstParent = destFilename.NavigateUp(); - if (Android_MoveFile(srcFilename.ToString(), srcParent.ToString(), dstParent.ToString())) { + if (Android_MoveFile(srcFilename.ToString(), srcParent.ToString(), dstParent.ToString()) == StorageError::SUCCESS) { return true; } // If failed, fall through and try other ways. diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index dedd1d549a..dc1869d7b2 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -248,45 +248,45 @@ public class PpssppActivity extends NativeActivity { } } - public boolean contentUriRemoveFile(String fileName) { + public int contentUriRemoveFile(String fileName) { try { Uri uri = Uri.parse(fileName); DocumentFile documentFile = DocumentFile.fromSingleUri(this, uri); if (documentFile != null) { - return documentFile.delete(); + return documentFile.delete() ? STORAGE_ERROR_SUCCESS : STORAGE_ERROR_UNKNOWN; } else { - return false; + return STORAGE_ERROR_UNKNOWN; } } catch (Exception e) { Log.e(TAG, "contentUriRemoveFile exception: " + e.toString()); - return false; + return STORAGE_ERROR_UNKNOWN; } } // NOTE: The destination is the parent directory! This means that contentUriCopyFile // cannot rename things as part of the operation. - public boolean contentUriCopyFile(String srcFileUri, String dstParentDirUri) { + public int contentUriCopyFile(String srcFileUri, String dstParentDirUri) { try { Uri srcUri = Uri.parse(srcFileUri); Uri dstParentUri = Uri.parse(dstParentDirUri); - return DocumentsContract.copyDocument(getContentResolver(), srcUri, dstParentUri) != null; + return DocumentsContract.copyDocument(getContentResolver(), srcUri, dstParentUri) != null ? STORAGE_ERROR_SUCCESS : STORAGE_ERROR_UNKNOWN; } catch (Exception e) { Log.e(TAG, "contentUriCopyFile exception: " + e.toString()); - return false; + return STORAGE_ERROR_UNKNOWN; } } // NOTE: The destination is the parent directory! This means that contentUriCopyFile // cannot rename things as part of the operation. - public boolean contentUriMoveFile(String srcFileUri, String srcParentDirUri, String dstParentDirUri) { + public int contentUriMoveFile(String srcFileUri, String srcParentDirUri, String dstParentDirUri) { try { Uri srcUri = Uri.parse(srcFileUri); Uri srcParentUri = Uri.parse(srcParentDirUri); Uri dstParentUri = Uri.parse(dstParentDirUri); - return DocumentsContract.moveDocument(getContentResolver(), srcUri, srcParentUri, dstParentUri) != null; + return DocumentsContract.moveDocument(getContentResolver(), srcUri, srcParentUri, dstParentUri) != null ? STORAGE_ERROR_SUCCESS : STORAGE_ERROR_UNKNOWN; } catch (Exception e) { Log.e(TAG, "contentUriMoveFile exception: " + e.toString()); - return false; + return STORAGE_ERROR_UNKNOWN; } } From 3ec13b844021c78368f74800c14ca00d49dd0218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Jul 2021 14:52:52 +0200 Subject: [PATCH 8/8] Two more functions --- Common/File/AndroidStorage.cpp | 16 ++++++++-------- Common/File/AndroidStorage.h | 8 ++++---- Common/File/FileUtil.cpp | 6 +++--- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 16 ++++++++-------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index 4eb11b20e7..d8d6468b91 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -32,9 +32,9 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) { _dbg_assert_(openContentUri); listContentUriDir = env->GetMethodID(env->GetObjectClass(obj), "listContentUriDir", "(Ljava/lang/String;)[Ljava/lang/String;"); _dbg_assert_(listContentUriDir); - contentUriCreateDirectory = env->GetMethodID(env->GetObjectClass(obj), "contentUriCreateDirectory", "(Ljava/lang/String;Ljava/lang/String;)Z"); + contentUriCreateDirectory = env->GetMethodID(env->GetObjectClass(obj), "contentUriCreateDirectory", "(Ljava/lang/String;Ljava/lang/String;)I"); _dbg_assert_(contentUriCreateDirectory); - contentUriCreateFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriCreateFile", "(Ljava/lang/String;Ljava/lang/String;)Z"); + contentUriCreateFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriCreateFile", "(Ljava/lang/String;Ljava/lang/String;)I"); _dbg_assert_(contentUriCreateFile); contentUriCopyFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriCopyFile", "(Ljava/lang/String;Ljava/lang/String;)I"); _dbg_assert_(contentUriCopyFile); @@ -84,24 +84,24 @@ int Android_OpenContentUriFd(const std::string &filename, Android_OpenContentUri return fd; } -bool Android_CreateDirectory(const std::string &rootTreeUri, const std::string &dirName) { +StorageError Android_CreateDirectory(const std::string &rootTreeUri, const std::string &dirName) { if (!g_nativeActivity) { - return false; + return StorageError::UNKNOWN; } auto env = getEnv(); jstring paramRoot = env->NewStringUTF(rootTreeUri.c_str()); jstring paramDirName = env->NewStringUTF(dirName.c_str()); - return env->CallBooleanMethod(g_nativeActivity, contentUriCreateDirectory, paramRoot, paramDirName); + return StorageErrorFromInt(env->CallIntMethod(g_nativeActivity, contentUriCreateDirectory, paramRoot, paramDirName)); } -bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { +StorageError Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { if (!g_nativeActivity) { - return false; + return StorageError::UNKNOWN; } auto env = getEnv(); jstring paramRoot = env->NewStringUTF(parentTreeUri.c_str()); jstring paramFileName = env->NewStringUTF(fileName.c_str()); - return env->CallBooleanMethod(g_nativeActivity, contentUriCreateFile, paramRoot, paramFileName); + return StorageErrorFromInt(env->CallIntMethod(g_nativeActivity, contentUriCreateFile, paramRoot, paramFileName)); } StorageError Android_CopyFile(const std::string &fileUri, const std::string &destParentUri) { diff --git a/Common/File/AndroidStorage.h b/Common/File/AndroidStorage.h index be766fb97e..131c0125d2 100644 --- a/Common/File/AndroidStorage.h +++ b/Common/File/AndroidStorage.h @@ -40,8 +40,8 @@ void Android_StorageSetNativeActivity(jobject nativeActivity); bool Android_IsContentUri(const std::string &uri); int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode); -bool Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName); -bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName); +StorageError Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName); +StorageError Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName); StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri); StorageError Android_CopyFile(const std::string &fileUri, const std::string &destParentUri); StorageError Android_RemoveFile(const std::string &fileUri); @@ -65,8 +65,8 @@ extern std::string g_extFilesDir; inline bool Android_IsContentUri(const std::string &uri) { return false; } inline int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode) { return -1; } -inline bool Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName) { return false; } -inline bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { return false; } +inline StorageError Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName) { return StorageError::UNKNOWN; } +inline StorageError Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { return StorageError::UNKNOWN; } inline StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { return StorageError::UNKNOWN; } inline StorageError Android_CopyFile(const std::string &fileUri, const std::string &destParentUri) { return StorageError::UNKNOWN; } inline StorageError Android_RemoveFile(const std::string &fileUri) { return StorageError::UNKNOWN; } diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index 9684b3b26c..134d84f74b 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -119,7 +119,7 @@ FILE *OpenCFile(const Path &path, const char *mode) { std::string name = path.GetFilename(); if (path.CanNavigateUp()) { Path parent = path.NavigateUp(); - if (!Android_CreateFile(parent.ToString(), name)) { + if (Android_CreateFile(parent.ToString(), name) != StorageError::SUCCESS) { WARN_LOG(COMMON, "Failed to create file '%s' in '%s'", name.c_str(), parent.c_str()); return nullptr; } @@ -189,7 +189,7 @@ int OpenFD(const Path &path, OpenFlag flags) { std::string name = path.GetFilename(); if (path.CanNavigateUp()) { Path parent = path.NavigateUp(); - if (!Android_CreateFile(parent.ToString(), name)) { + if (Android_CreateFile(parent.ToString(), name) != StorageError::SUCCESS) { WARN_LOG(COMMON, "OpenFD: Failed to create file '%s' in '%s'", name.c_str(), parent.c_str()); return -1; } @@ -451,7 +451,7 @@ bool CreateDir(const Path &path) { std::string newDirName = uri.GetLastPart(); if (uri.NavigateUp()) { INFO_LOG(COMMON, "Calling Android_CreateDirectory(%s, %s)", uri.ToString().c_str(), newDirName.c_str()); - return Android_CreateDirectory(uri.ToString(), newDirName); + return Android_CreateDirectory(uri.ToString(), newDirName) == StorageError::SUCCESS; } else { // Bad path - can't create this directory. WARN_LOG(COMMON, "CreateDir failed: '%s'", path.c_str()); diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index dc1869d7b2..bb5673084f 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -213,38 +213,38 @@ public class PpssppActivity extends NativeActivity { } } - public boolean contentUriCreateDirectory(String rootTreeUri, String dirName) { + public int contentUriCreateDirectory(String rootTreeUri, String dirName) { try { Uri uri = Uri.parse(rootTreeUri); DocumentFile documentFile = DocumentFile.fromTreeUri(this, uri); if (documentFile != null) { DocumentFile createdDir = documentFile.createDirectory(dirName); - return createdDir != null; + return createdDir != null ? STORAGE_ERROR_SUCCESS : STORAGE_ERROR_UNKNOWN; } else { Log.e(TAG, "contentUriCreateDirectory: fromTreeUri returned null"); - return false; + return STORAGE_ERROR_UNKNOWN; } } catch (Exception e) { Log.e(TAG, "contentUriCreateDirectory exception: " + e.toString()); - return false; + return STORAGE_ERROR_UNKNOWN; } } - public boolean contentUriCreateFile(String rootTreeUri, String fileName) { + public int contentUriCreateFile(String rootTreeUri, String fileName) { try { Uri uri = Uri.parse(rootTreeUri); DocumentFile documentFile = DocumentFile.fromTreeUri(this, uri); if (documentFile != null) { // TODO: Check the file extension and choose MIME type appropriately. DocumentFile createdFile = documentFile.createFile("application/octet-stream", fileName); - return createdFile != null; + return createdFile != null ? STORAGE_ERROR_SUCCESS : STORAGE_ERROR_UNKNOWN; } else { Log.e(TAG, "contentUriCreateFile: fromTreeUri returned null"); - return false; + return STORAGE_ERROR_UNKNOWN; } } catch (Exception e) { Log.e(TAG, "contentUriCreateFile exception: " + e.toString()); - return false; + return STORAGE_ERROR_UNKNOWN; } }