From bd99e4a287686ccbe8e2ff6140aa515ac50468ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Jul 2021 00:33:40 +0200 Subject: [PATCH 01/22] Greatly speed up content URI file listings by using DocumentContract queries. --- Common/File/AndroidStorage.cpp | 8 ++- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 52 ++++++++++++++++--- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index 277498ab50..73de8b7b27 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -114,10 +114,11 @@ bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName return env->CallBooleanMethod(g_nativeActivity, contentUriRenameFileTo, paramFileUri, paramNewName); } +// NOTE: Does not set fullName - you're supposed to already know it. static bool ParseFileInfo(const std::string &line, File::FileInfo *fileInfo) { std::vector parts; SplitString(line, '|', parts); - if (parts.size() != 5) { + if (parts.size() != 4) { ERROR_LOG(FILESYS, "Bad format: %s", line.c_str()); return false; } @@ -125,7 +126,6 @@ static bool ParseFileInfo(const std::string &line, File::FileInfo *fileInfo) { fileInfo->isDirectory = parts[0][0] == 'D'; fileInfo->exists = true; sscanf(parts[1].c_str(), "%" PRIu64, &fileInfo->size); - fileInfo->fullName = Path(parts[3]); fileInfo->isWritable = true; // TODO: Should be passed as part of the string. fileInfo->access = fileInfo->isDirectory ? 0666 : 0777; // TODO: For read-only mappings, reflect that here, similarly as with isWritable. @@ -155,6 +155,8 @@ bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *fileInfo) { } const char *charArray = env->GetStringUTFChars(str, 0); bool retval = ParseFileInfo(std::string(charArray), fileInfo); + fileInfo->fullName = Path(fileUri); + env->DeleteLocalRef(str); return retval && fileInfo->exists; } @@ -189,6 +191,8 @@ std::vector Android_ListContentUri(const std::string &path) { if (charArray) { // paranoia File::FileInfo info; if (ParseFileInfo(std::string(charArray), &info)) { + // We can just reconstruct the URI. + info.fullName = Path(path) / info.name; items.push_back(info); } } diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 7d7ad42489..d80a05cd38 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -9,6 +9,8 @@ import android.os.Looper; import android.os.ParcelFileDescriptor; import android.util.Log; import android.os.storage.StorageManager; +import android.content.ContentResolver; +import android.database.Cursor; import android.provider.DocumentsContract; import androidx.documentfile.provider.DocumentFile; import java.util.ArrayList; @@ -136,6 +138,7 @@ public class PpssppActivity extends NativeActivity { } private static String fileInfoToString(DocumentFile file) { + // TODO: Replace with a DocumentsContract query. String str = "F|"; if (file.isVirtual()) { // This we don't want to see. @@ -144,7 +147,7 @@ public class PpssppActivity extends NativeActivity { } else if (file.isDirectory()) { str = "D|"; } - str += file.length() + "|" + file.getName() + "|" + file.getUri() + "|" + file.lastModified(); + str += file.length() + "|" + file.getName() + "|" + file.lastModified(); return str; } @@ -154,13 +157,46 @@ public class PpssppActivity extends NativeActivity { public String[] listContentUriDir(String uriString) { try { Uri uri = Uri.parse(uriString); - DocumentFile documentFile = DocumentFile.fromTreeUri(this, uri); - DocumentFile[] children = documentFile.listFiles(); - ArrayList listing = new ArrayList(); - // Encode entries into strings for JNI simplicity. - for (DocumentFile file : children) { - String str = fileInfoToString(file); - listing.add(str); + + final ContentResolver resolver = getContentResolver(); + final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, + DocumentsContract.getDocumentId(uri)); + final ArrayList listing = new ArrayList<>(); + Cursor c = null; + try { + c = resolver.query(childrenUri, new String[] { + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + DocumentsContract.Document.COLUMN_SIZE, + DocumentsContract.Document.COLUMN_FLAGS, + DocumentsContract.Document.COLUMN_MIME_TYPE, // check for MIME_TYPE_DIR + DocumentsContract.Document.COLUMN_LAST_MODIFIED + }, null, null, null); + while (c.moveToNext()) { + final String documentName = c.getString(0); + final long size = c.getLong(1); + final int flags = c.getInt(2); + final String mimeType = c.getString(3); + final long lastModified = c.getLong(4); + + final boolean isDirectory = mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR); + + // Filter out any nonsense. + if ((flags & (DocumentsContract.Document.FLAG_PARTIAL | DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT)) != 0) { + continue; + } + + String str = "F|"; + if (isDirectory) { + str = "D|"; + } + str += size + "|" + documentName + "|" + lastModified; + + listing.add(str); + } + } catch (Exception e) { + Log.w(TAG, "Failed query: " + e); + } finally { + c.close(); } // Is ArrayList weird or what? String[] strings = new String[listing.size()]; From 14e24b28adb757a4e19e2ac79dbb5da306e394b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Jul 2021 00:51:17 +0200 Subject: [PATCH 02/22] Use a DocumentsContract query for single-file GetFileInfo too. --- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 86 +++++++++---------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index d80a05cd38..3d9e48aa83 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -137,18 +137,34 @@ public class PpssppActivity extends NativeActivity { } } - private static String fileInfoToString(DocumentFile file) { - // TODO: Replace with a DocumentsContract query. + private static final String[] columns = new String[] { + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + DocumentsContract.Document.COLUMN_SIZE, + DocumentsContract.Document.COLUMN_FLAGS, + DocumentsContract.Document.COLUMN_MIME_TYPE, // check for MIME_TYPE_DIR + DocumentsContract.Document.COLUMN_LAST_MODIFIED + }; + + private String cursorToString(Cursor c) { + final int flags = c.getInt(2); + // Filter out any virtual or partial nonsense. + // There's a bunch of potentially-interesting flags here btw, + // to figure out how to set access flags better, etc. + if ((flags & (DocumentsContract.Document.FLAG_PARTIAL | DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT)) != 0) { + return null; + } + + final String mimeType = c.getString(3); + final boolean isDirectory = mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR); + final String documentName = c.getString(0); + final long size = c.getLong(1); + final long lastModified = c.getLong(4); + String str = "F|"; - if (file.isVirtual()) { - // This we don't want to see. - str = "V|"; - Log.e(TAG, "Got virtual file: " + file.getUri()); - } else if (file.isDirectory()) { + if (isDirectory) { str = "D|"; } - str += file.length() + "|" + file.getName() + "|" + file.lastModified(); - return str; + return str + size + "|" + documentName + "|" + lastModified; } // TODO: Maybe add a cheaper version that doesn't extract all the file information? @@ -164,39 +180,19 @@ public class PpssppActivity extends NativeActivity { final ArrayList listing = new ArrayList<>(); Cursor c = null; try { - c = resolver.query(childrenUri, new String[] { - DocumentsContract.Document.COLUMN_DISPLAY_NAME, - DocumentsContract.Document.COLUMN_SIZE, - DocumentsContract.Document.COLUMN_FLAGS, - DocumentsContract.Document.COLUMN_MIME_TYPE, // check for MIME_TYPE_DIR - DocumentsContract.Document.COLUMN_LAST_MODIFIED - }, null, null, null); + c = resolver.query(childrenUri, columns, null, null, null); while (c.moveToNext()) { - final String documentName = c.getString(0); - final long size = c.getLong(1); - final int flags = c.getInt(2); - final String mimeType = c.getString(3); - final long lastModified = c.getLong(4); - - final boolean isDirectory = mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR); - - // Filter out any nonsense. - if ((flags & (DocumentsContract.Document.FLAG_PARTIAL | DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT)) != 0) { - continue; + String str = cursorToString(c); + if (str != null) { + listing.add(str); } - - String str = "F|"; - if (isDirectory) { - str = "D|"; - } - str += size + "|" + documentName + "|" + lastModified; - - listing.add(str); } } catch (Exception e) { Log.w(TAG, "Failed query: " + e); } finally { - c.close(); + if (c != null) { + c.close(); + } } // Is ArrayList weird or what? String[] strings = new String[listing.size()]; @@ -294,22 +290,24 @@ public class PpssppActivity extends NativeActivity { } public String contentUriGetFileInfo(String fileName) { + Cursor c = null; try { Uri uri = Uri.parse(fileName); - DocumentFile documentFile = DocumentFile.fromSingleUri(this, uri); - if (documentFile != null) { - if (documentFile.exists()) { - String str = fileInfoToString(documentFile); - return str; - } else { - return null; - } + final ContentResolver resolver = getContentResolver(); + c = resolver.query(uri, columns, null, null, null); + if (c.moveToNext()) { + String str = cursorToString(c); + return str; } else { return null; } } catch (Exception e) { Log.e(TAG, "contentUriGetFileInfo exception: " + e.toString()); return null; + } finally { + if (c != null) { + c.close(); + } } } From 6b0851cb739893e1dbc92a3923ef33b20e63c65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Jul 2021 11:57:19 +0200 Subject: [PATCH 03/22] Use a simpler method that actually works to get free storage space from content URI. --- Core/FileSystems/DirectoryFileSystem.cpp | 3 --- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 27 ++++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 645ef15df6..06fea1f5a9 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -203,7 +203,6 @@ bool DirectoryFileHandle::Open(const Path &basePath, std::string &fileName, File #endif Path fullName = GetLocalPath(basePath, fileName); - INFO_LOG(FILESYS, "Actually opening %s", fullName.c_str()); // 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. @@ -302,8 +301,6 @@ bool DirectoryFileHandle::Open(const Path &basePath, std::string &fileName, File } } - INFO_LOG(FILESYS, "Opening '%s' straight", fullName.c_str()); - int flags = 0; if (access & FILEACCESS_APPEND) { flags |= O_APPEND; diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 3d9e48aa83..9db14467c1 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -8,6 +8,8 @@ import android.os.Bundle; import android.os.Looper; import android.os.ParcelFileDescriptor; import android.util.Log; +import android.system.StructStatVfs; +import android.system.Os; import android.os.storage.StorageManager; import android.content.ContentResolver; import android.database.Cursor; @@ -314,22 +316,23 @@ public class PpssppActivity extends NativeActivity { // The example in Android documentation uses this.getFilesDir for path. // There's also a way to beg the OS for more space, which might clear caches, but // let's just not bother with that for now. - public long contentUriGetFreeStorageSpace(String uriString) { + public long contentUriGetFreeStorageSpace(String fileName) { try { + Uri uri = Uri.parse(fileName); StorageManager storageManager = getApplicationContext().getSystemService(StorageManager.class); - // In 29 and later, we can directly get the UUID for the storage volume - // through the URI. - UUID volumeUUID; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - Uri uri = Uri.parse(uriString); - volumeUUID = UUID.fromString(storageManager.getStorageVolume(uri).getUuid()); - } else { - volumeUUID = storageManager.getUuidForPath(this.getFilesDir()); + ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "r"); + if (pfd == null) { + Log.w(TAG, "Failed to get free storage space from URI: " + fileName); + return -1; } - long availableBytes = storageManager.getAllocatableBytes(volumeUUID); - return availableBytes; - } catch (Exception e) { + StructStatVfs stats = Os.fstatvfs(pfd.getFileDescriptor()); + long freeSpace = stats.f_bavail * stats.f_bsize; + pfd.close(); + return freeSpace; + } catch (Exception e) { + // FileNotFoundException | ErrnoException e + // Log.getStackTraceString(e) Log.e(TAG, "contentUriGetFreeStorageSpace exception: " + e.toString()); return -1; } From d5781824eb7635a03450d35a4b3e5e51c1b6bfde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Jul 2021 12:32:08 +0200 Subject: [PATCH 04/22] Fix lastModified, speed up Android_FileExists. Add Android_CopyFile (unused) --- Common/File/AndroidStorage.cpp | 15 +++- Common/File/AndroidStorage.h | 2 + .../src/org/ppsspp/ppsspp/PpssppActivity.java | 84 ++++++++++++------- 3 files changed, 68 insertions(+), 33 deletions(-) diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index 73de8b7b27..35cfcee09c 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -11,6 +11,7 @@ static jmethodID openContentUri; static jmethodID listContentUriDir; static jmethodID contentUriCreateFile; static jmethodID contentUriCreateDirectory; +static jmethodID contentUriCopyFile; static jmethodID contentUriRemoveFile; static jmethodID contentUriRenameFileTo; static jmethodID contentUriGetFileInfo; @@ -33,6 +34,8 @@ 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"); + _dbg_assert_(contentUriCopyFile); contentUriRemoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriRemoveFile", "(Ljava/lang/String;)Z"); _dbg_assert_(contentUriRemoveFile); contentUriRenameFileTo = env->GetMethodID(env->GetObjectClass(obj), "contentUriRenameFileTo", "(Ljava/lang/String;Ljava/lang/String;)Z"); @@ -95,6 +98,16 @@ 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) { + if (!g_nativeActivity) { + return false; + } + 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); +} + bool Android_RemoveFile(const std::string &fileUri) { if (!g_nativeActivity) { return false; @@ -130,7 +143,7 @@ static bool ParseFileInfo(const std::string &line, File::FileInfo *fileInfo) { fileInfo->access = fileInfo->isDirectory ? 0666 : 0777; // TODO: For read-only mappings, reflect that here, similarly as with isWritable. uint64_t lastModifiedMs = 0; - sscanf(parts[4].c_str(), "%" PRIu64, &lastModifiedMs); + sscanf(parts[3].c_str(), "%" PRIu64, &lastModifiedMs); // Convert from milliseconds uint32_t lastModified = lastModifiedMs / 1000; diff --git a/Common/File/AndroidStorage.h b/Common/File/AndroidStorage.h index 5370cb2be2..a217b3d4e0 100644 --- a/Common/File/AndroidStorage.h +++ b/Common/File/AndroidStorage.h @@ -25,6 +25,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_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); bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info); @@ -44,6 +45,7 @@ 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_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 bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info) { return false; } diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 9db14467c1..aba075a2f7 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -173,35 +173,36 @@ public class PpssppActivity extends NativeActivity { // TODO: Replace with a proper query: // * https://stackoverflow.com/questions/42186820/documentfile-is-very-slow public String[] listContentUriDir(String uriString) { + Cursor c = null; try { Uri uri = Uri.parse(uriString); - final ContentResolver resolver = getContentResolver(); - final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, - DocumentsContract.getDocumentId(uri)); + final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree( + uri, DocumentsContract.getDocumentId(uri)); final ArrayList listing = new ArrayList<>(); - Cursor c = null; - try { - c = resolver.query(childrenUri, columns, null, null, null); - while (c.moveToNext()) { - String str = cursorToString(c); - if (str != null) { - listing.add(str); - } - } - } catch (Exception e) { - Log.w(TAG, "Failed query: " + e); - } finally { - if (c != null) { - c.close(); + c = resolver.query(childrenUri, columns, null, null, null); + while (c.moveToNext()) { + String str = cursorToString(c); + if (str != null) { + listing.add(str); } } // Is ArrayList weird or what? String[] strings = new String[listing.size()]; return listing.toArray(strings); - } catch (Exception e) { + } + catch (IllegalArgumentException e) { + // Due to sloppy exception handling in resolver.query, we get this wrapping + // a FileNotFoundException if the directory doesn't exist. + return new String[]{}; + } + catch (Exception e) { Log.e(TAG, "listContentUriDir exception: " + e.toString()); return new String[]{}; + } finally { + if (c != null) { + c.close(); + } } } @@ -255,15 +256,27 @@ public class PpssppActivity extends NativeActivity { } } + // 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) { + try { + Uri srcUri = Uri.parse(srcFileUri); + Uri dstParentUri = Uri.parse(dstParentDirUri); + DocumentsContract.copyDocument(getContentResolver(), srcUri, dstParentUri); + return true; + } catch (Exception e) { + Log.e(TAG, "contentUriCopyFile exception: " + e.toString()); + return false; + } + } + public boolean 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); - // Log.i(TAG, "New uri: " + newUri.toString()); return true; } catch (Exception e) { Log.e(TAG, "contentUriRenameFile exception: " + e.toString()); @@ -271,23 +284,30 @@ public class PpssppActivity extends NativeActivity { } } - // Possibly faster than contentUriGetFileInfo. + private static void closeQuietly(AutoCloseable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (RuntimeException rethrown) { + throw rethrown; + } catch (Exception ignored) { + } + } + } + + // Probably slightly faster than contentUriGetFileInfo. + // Smaller difference now than before I changed that one to a query... public boolean contentUriFileExists(String fileUri) { + Cursor c = null; try { Uri uri = Uri.parse(fileUri); - DocumentFile documentFile = DocumentFile.fromSingleUri(this, uri); - if (documentFile != null) { - if (documentFile.exists()) { - return true; - } else { - return false; - } - } else { - return false; - } + c = getContentResolver().query(uri, new String[] { DocumentsContract.Document.COLUMN_DOCUMENT_ID }, null, null, null); + return c.getCount() > 0; } catch (Exception e) { - Log.e(TAG, "contentUriFileExists exception: " + e.toString()); + // Log.w(TAG, "Failed query: " + e); return false; + } finally { + closeQuietly(c); } } From 9ae00b80b1d6214e936457dbdf126d62761dd115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 24 Jul 2021 10:31:15 +0200 Subject: [PATCH 05/22] Cleanup the ownership model in MetaFileSystem. The old one was error-prone spaghetti. --- Core/FileSystems/ISOFileSystem.h | 5 ++- Core/FileSystems/MetaFileSystem.cpp | 69 +++++++++++------------------ Core/FileSystems/MetaFileSystem.h | 27 +++++++---- Core/HLE/sceIo.cpp | 31 +++++-------- Core/PSPLoaders.cpp | 29 ++++++------ Core/System.cpp | 1 + 6 files changed, 73 insertions(+), 89 deletions(-) diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index 676964f079..0b8c00512a 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -19,6 +19,7 @@ #include #include +#include #include "FileSystem.h" @@ -100,7 +101,7 @@ private: // the filenames to "", to achieve this. class ISOBlockSystem : public IFileSystem { public: - ISOBlockSystem(ISOFileSystem *isoFileSystem) : isoFileSystem_(isoFileSystem) {} + ISOBlockSystem(std::shared_ptr isoFileSystem) : isoFileSystem_(isoFileSystem) {} void DoState(PointerWrap &p) override { // This is a bit iffy, as block device savestates already are iffy (loads/saves multiple times for multiple mounts..) @@ -150,5 +151,5 @@ public: bool RemoveFile(const std::string &filename) override { return false; } private: - ISOFileSystem *isoFileSystem_; + std::shared_ptr isoFileSystem_; }; diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index 662d99bad8..b8ebc9238e 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -172,10 +172,11 @@ IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle) for (size_t i = 0; i < fileSystems.size(); i++) { if (fileSystems[i].system->OwnsHandle(handle)) - return fileSystems[i].system; //got it! + return fileSystems[i].system.get(); } - //none found? - return 0; + + // Not found + return nullptr; } int MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpath, MountPoint **system) @@ -274,41 +275,38 @@ std::string MetaFileSystem::NormalizePrefix(std::string prefix) const { return prefix; } -void MetaFileSystem::Mount(std::string prefix, IFileSystem *system) { +void MetaFileSystem::Mount(std::string prefix, std::shared_ptr system) { std::lock_guard guard(lock); + MountPoint x; x.prefix = prefix; x.system = system; + for (auto &it : fileSystems) { + if (it.prefix == prefix) { + // Overwrite the old mount. Don't create a new one. + it = x; + return; + } + } + + // Prefix not yet mounted, do so. fileSystems.push_back(x); } -void MetaFileSystem::Unmount(std::string prefix, IFileSystem *system) { - std::lock_guard guard(lock); - MountPoint x; - x.prefix = prefix; - x.system = system; - fileSystems.erase(std::remove(fileSystems.begin(), fileSystems.end(), x), fileSystems.end()); +void MetaFileSystem::UnmountAll() { + fileSystems.clear(); + currentDir.clear(); } -void MetaFileSystem::Remount(std::string prefix, IFileSystem *newSystem) { +bool MetaFileSystem::Remount(std::string prefix, std::shared_ptr system) { std::lock_guard guard(lock); - IFileSystem *oldSystem = nullptr; for (auto &it : fileSystems) { if (it.prefix == prefix) { - oldSystem = it.system; - it.system = newSystem; + it.system = system; + return true; } } - - bool delOldSystem = true; - for (auto &it : fileSystems) { - if (it.system == oldSystem) { - delOldSystem = false; - } - } - - if (delOldSystem) - delete oldSystem; + return false; } IFileSystem *MetaFileSystem::GetSystemFromFilename(const std::string &filename) { @@ -321,31 +319,16 @@ IFileSystem *MetaFileSystem::GetSystemFromFilename(const std::string &filename) IFileSystem *MetaFileSystem::GetSystem(const std::string &prefix) { for (auto it = fileSystems.begin(); it != fileSystems.end(); ++it) { if (it->prefix == NormalizePrefix(prefix)) - return it->system; + return it->system.get(); } return NULL; } -void MetaFileSystem::Shutdown() -{ +void MetaFileSystem::Shutdown() { std::lock_guard guard(lock); - current = 6; - // Ownership is a bit convoluted. Let's just delete everything once. - - std::set toDelete; - for (size_t i = 0; i < fileSystems.size(); i++) { - toDelete.insert(fileSystems[i].system); - } - - for (auto iter = toDelete.begin(); iter != toDelete.end(); ++iter) - { - delete *iter; - } - - fileSystems.clear(); - currentDir.clear(); - startingDirectory = ""; + UnmountAll(); + Reset(); } int MetaFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index a1152e1abd..84ea60a44a 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "Core/FileSystems/FileSystem.h" @@ -28,13 +29,14 @@ private: s32 current; struct MountPoint { std::string prefix; - IFileSystem *system; + std::shared_ptr system; bool operator == (const MountPoint &other) const { return prefix == other.prefix && system == other.system; } }; + // The order of this vector is meaningful - lookups are always a linear search from the start. std::vector fileSystems; typedef std::map currentDir_t; @@ -43,26 +45,34 @@ private: std::string startingDirectory; std::recursive_mutex lock; // must be recursive -public: - MetaFileSystem() { + void Reset() { // This used to be 6, probably an attempt to replicate PSP handles. // However, that's an artifact of using psplink anyway... current = 1; + startingDirectory.clear(); } - void Mount(std::string prefix, IFileSystem *system); - void Unmount(std::string prefix, IFileSystem *system); - void Remount(std::string prefix, IFileSystem *newSystem); +public: + MetaFileSystem() { + Reset(); + } + void Mount(std::string prefix, std::shared_ptr system); + // Fails if there's not already a file system at prefix. + bool Remount(std::string prefix, std::shared_ptr system); + + void UnmountAll(); + + // The pointer returned from these are for temporary usage only. Do not store. IFileSystem *GetSystem(const std::string &prefix); IFileSystem *GetSystemFromFilename(const std::string &filename); + IFileSystem *GetHandleOwner(u32 handle); FileSystemFlags FlagsFromFilename(const std::string &filename) { IFileSystem *sys = GetSystemFromFilename(filename); return sys ? sys->Flags() : FileSystemFlags::NONE; } void ThreadEnded(int threadID); - void Shutdown(); u32 GetNewHandle() override { @@ -77,14 +87,13 @@ public: void DoState(PointerWrap &p) override; - IFileSystem *GetHandleOwner(u32 handle); int MapFilePath(const std::string &inpath, std::string &outpath, MountPoint **system); inline int MapFilePath(const std::string &_inpath, std::string &outpath, IFileSystem **system) { MountPoint *mountPoint; int error = MapFilePath(_inpath, outpath, &mountPoint); if (error == 0) { - *system = mountPoint->system; + *system = mountPoint->system.get(); return error; } diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 73702ae4e3..7128bfa73d 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "Common/Thread/ThreadUtil.h" #include "Common/Profiler/Profiler.h" @@ -557,7 +558,6 @@ static void __IoAsyncEndCallback(SceUID threadID, SceUID prevCallbackId) { } } -static DirectoryFileSystem *memstickSystem = nullptr; static DirectoryFileSystem *exdataSystem = nullptr; #if defined(USING_WIN_UI) || defined(APPLE) static DirectoryFileSystem *flash0System = nullptr; @@ -631,12 +631,15 @@ void __IoInit() { asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify); syncNotifyEvent = CoreTiming::RegisterEvent("IoSyncNotify", __IoSyncNotify); - memstickSystem = new DirectoryFileSystem(&pspFileSystem, g_Config.memStickDirectory, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); + auto memstickSystem = std::shared_ptr(new DirectoryFileSystem(&pspFileSystem, g_Config.memStickDirectory, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD)); #if defined(USING_WIN_UI) || defined(APPLE) - flash0System = new DirectoryFileSystem(&pspFileSystem, g_Config.flash0Directory, FileSystemFlags::FLASH); + auto flash0System = std::shared_ptr(new DirectoryFileSystem(&pspFileSystem, g_Config.flash0Directory, FileSystemFlags::FLASH)); #else - flash0System = new VFSFileSystem(&pspFileSystem, "flash0"); + auto flash0System = std::shared_ptr(new VFSFileSystem(&pspFileSystem, "flash0")); #endif + + // 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); @@ -647,7 +650,7 @@ void __IoInit() { const std::string gameId = g_paramSFO.GetDiscID(); const Path exdataPath = GetSysDirectory(DIRECTORY_EXDATA) / gameId; if (File::Exists(exdataPath)) { - exdataSystem = new DirectoryFileSystem(&pspFileSystem, exdataPath, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); + auto exdataSystem = std::shared_ptr(new DirectoryFileSystem(&pspFileSystem, exdataPath, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD)); pspFileSystem.Mount("exdata0:", exdataSystem); INFO_LOG(SCEIO, "Mounted exdata/%s/ under memstick for exdata0:/", gameId.c_str()); } else { @@ -763,23 +766,9 @@ void __IoShutdown() { } asyncDefaultPriority = -1; - pspFileSystem.Unmount("ms0:", memstickSystem); - pspFileSystem.Unmount("fatms0:", memstickSystem); - pspFileSystem.Unmount("fatms:", memstickSystem); - pspFileSystem.Unmount("pfat0:", memstickSystem); - pspFileSystem.Unmount("flash0:", flash0System); - - if (g_RemasterMode && exdataSystem) { - pspFileSystem.Unmount("exdata0:", exdataSystem); - delete exdataSystem; - exdataSystem = nullptr; - } - - delete memstickSystem; - memstickSystem = nullptr; - delete flash0System; - flash0System = nullptr; + 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/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 869c6f8492..73a6a8e746 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -80,11 +80,11 @@ void InitMemoryForGameISO(FileLoader *fileLoader) { return; } - IFileSystem *fileSystem = nullptr; - IFileSystem *blockSystem = nullptr; + std::shared_ptr fileSystem; + std::shared_ptr blockSystem; if (fileLoader->IsDirectory()) { - fileSystem = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->GetPath()); + fileSystem = std::shared_ptr(new VirtualDiscFileSystem(&pspFileSystem, fileLoader->GetPath())); blockSystem = fileSystem; } else { auto bd = constructBlockDevice(fileLoader); @@ -92,9 +92,9 @@ void InitMemoryForGameISO(FileLoader *fileLoader) { if (!bd) return; - ISOFileSystem *iso = new ISOFileSystem(&pspFileSystem, bd); + std::shared_ptr iso = std::shared_ptr(new ISOFileSystem(&pspFileSystem, bd)); fileSystem = iso; - blockSystem = new ISOBlockSystem(iso); + blockSystem = std::shared_ptr(new ISOBlockSystem(iso)); } pspFileSystem.Mount("umd0:", blockSystem); @@ -148,20 +148,20 @@ bool ReInitMemoryForGameISO(FileLoader *fileLoader) { return false; } - IFileSystem *fileSystem = nullptr; - IFileSystem *blockSystem = nullptr; + std::shared_ptr fileSystem; + std::shared_ptr blockSystem; if (fileLoader->IsDirectory()) { - fileSystem = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->GetPath()); + fileSystem = std::shared_ptr(new VirtualDiscFileSystem(&pspFileSystem, fileLoader->GetPath())); blockSystem = fileSystem; } else { auto bd = constructBlockDevice(fileLoader); if (!bd) return false; - ISOFileSystem *iso = new ISOFileSystem(&pspFileSystem, bd); + std::shared_ptr iso = std::shared_ptr(new ISOFileSystem(&pspFileSystem, bd)); fileSystem = iso; - blockSystem = new ISOBlockSystem(iso); + blockSystem = std::shared_ptr(new ISOBlockSystem(iso)); } pspFileSystem.Remount("umd0:", blockSystem); @@ -367,14 +367,15 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { if (PSP_CoreParameter().mountIsoLoader != nullptr) { auto bd = constructBlockDevice(PSP_CoreParameter().mountIsoLoader); if (bd != NULL) { - ISOFileSystem *umd2 = new ISOFileSystem(&pspFileSystem, bd); - ISOBlockSystem *blockSystem = new ISOBlockSystem(umd2); + std::shared_ptr umd2 = std::shared_ptr(new ISOFileSystem(&pspFileSystem, bd)); + std::shared_ptr blockSystem = std::shared_ptr(new ISOBlockSystem(umd2)); pspFileSystem.Mount("umd1:", blockSystem); pspFileSystem.Mount("disc0:", umd2); pspFileSystem.Mount("umd:", blockSystem); } } + Path full_path = fileLoader->GetPath(); std::string path = full_path.GetDirectory(); std::string extension = full_path.GetFileExtension(); @@ -411,7 +412,7 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { pspFileSystem.SetStartingDirectory(ms_path); } - DirectoryFileSystem *fs = new DirectoryFileSystem(&pspFileSystem, Path(path), FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); + std::shared_ptr fs = std::shared_ptr(new DirectoryFileSystem(&pspFileSystem, Path(path), FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD)); pspFileSystem.Mount("umd0:", fs); std::string finalName = ms_path + file; @@ -469,7 +470,7 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { } bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) { - BlobFileSystem *umd = new BlobFileSystem(&pspFileSystem, fileLoader, "data.ppdmp"); + std::shared_ptr umd = std::shared_ptr(new BlobFileSystem(&pspFileSystem, fileLoader, "data.ppdmp")); pspFileSystem.Mount("disc0:", umd); PSPLoaders_Shutdown(); diff --git a/Core/System.cpp b/Core/System.cpp index aace6ccfa7..b9f2ccf0e8 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -351,6 +351,7 @@ void CPU_Shutdown() { if (coreParameter.enableSound) { Audio_Shutdown(); } + pspFileSystem.Shutdown(); mipsr4k.Shutdown(); Memory::Shutdown(); From 156ae2e81f58dd1d401ad0c3f41663ec0772485a Mon Sep 17 00:00:00 2001 From: Stuart Kenny Date: Sat, 24 Jul 2021 18:59:58 +0100 Subject: [PATCH 06/22] Fix English language mapping after recent i18n additions. --- libretro/libretro.cpp | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index e70af303b1..52a23330ba 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -307,6 +307,38 @@ static int get_language_auto(void) } } +static std::string map_psp_language_to_i18n_locale(int val) +{ + switch (val) + { + default: + case PSP_SYSTEMPARAM_LANGUAGE_ENGLISH: + return "en_US"; + case PSP_SYSTEMPARAM_LANGUAGE_JAPANESE: + return "ja_JP"; + case PSP_SYSTEMPARAM_LANGUAGE_FRENCH: + return "fr_FR"; + case PSP_SYSTEMPARAM_LANGUAGE_GERMAN: + return "de_DE"; + case PSP_SYSTEMPARAM_LANGUAGE_SPANISH: + return "es_ES"; + case PSP_SYSTEMPARAM_LANGUAGE_ITALIAN: + return "it_IT"; + case PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE: + return "pt_PT"; + case PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN: + return "ru_RU"; + case PSP_SYSTEMPARAM_LANGUAGE_DUTCH: + return "nl_NL"; + case PSP_SYSTEMPARAM_LANGUAGE_KOREAN: + return "ko_KR"; + case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL: + return "zh_TW"; + case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED: + return "zh_CN"; + } +} + static void check_variables(CoreParameter &coreParam) { bool updated = false; @@ -348,15 +380,7 @@ static void check_variables(CoreParameter &coreParam) if (g_Config.iLanguage < 0) g_Config.iLanguage = get_language_auto(); - g_Config.sLanguageIni = "en_US"; - auto langValuesMapping = GetLangValuesMapping(); - for (auto i = langValuesMapping.begin(); i != langValuesMapping.end(); ++i) - { - if (i->second.second == g_Config.iLanguage) - { - g_Config.sLanguageIni = i->first; - } - } + g_Config.sLanguageIni = map_psp_language_to_i18n_locale(g_Config.iLanguage); i18nrepo.LoadIni(g_Config.sLanguageIni); if (!PSP_IsInited() && ppsspp_internal_resolution.Update(&g_Config.iInternalResolution)) 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 07/22] 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 08/22] 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 09/22] 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 10/22] 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 11/22] 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 12/22] 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 13/22] 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 14/22] 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; } } From 5d047a394bd15b0d8fca4230a7ef083fea55224f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Jul 2021 15:25:22 +0200 Subject: [PATCH 15/22] Scoped storage: Make the home button simply go to the root of the current directory tree. --- Common/File/Path.cpp | 1 - UI/MainScreen.cpp | 15 ++++++++++----- UI/MainScreen.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Common/File/Path.cpp b/Common/File/Path.cpp index 96110fc2d3..5f0e905dda 100644 --- a/Common/File/Path.cpp +++ b/Common/File/Path.cpp @@ -279,7 +279,6 @@ Path Path::GetRootVolume() const { return Path(path); } #endif - return Path("/"); } diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 8596294209..6f7872289a 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -540,10 +540,15 @@ UI::EventReturn GameBrowser::StorageClick(UI::EventParams &e) { return UI::EVENT_DONE; } -UI::EventReturn GameBrowser::HomeClick(UI::EventParams &e) { - if (System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) { - if (path_.GetPath().Type() == PathType::CONTENT_URI) { - path_.SetPath(path_.GetPath().GetRootVolume()); +UI::EventReturn GameBrowser::OnHomeClick(UI::EventParams &e) { + if (path_.GetPath().Type() == PathType::CONTENT_URI) { + Path rootPath = path_.GetPath().GetRootVolume(); + if (rootPath != path_.GetPath()) { + SetPath(rootPath); + return UI::EVENT_DONE; + } + if (System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) { + // There'll be no sensible home, ignore. return UI::EVENT_DONE; } } @@ -669,7 +674,7 @@ void GameBrowser::Refresh() { if (browseFlags_ & BrowseFlags::NAVIGATE) { topBar->Add(new Spacer(2.0f)); topBar->Add(new TextView(path_.GetFriendlyPath().c_str(), ALIGN_VCENTER | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f))); - topBar->Add(new Choice(ImageID("I_HOME"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick); + topBar->Add(new Choice(ImageID("I_HOME"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::OnHomeClick); if (System_GetPropertyBool(SYSPROP_HAS_ADDITIONAL_STORAGE)) { topBar->Add(new Choice(ImageID("I_SDCARD"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::StorageClick); } diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 9a19321755..6b15f06427 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -74,7 +74,7 @@ private: UI::EventReturn LastClick(UI::EventParams &e); UI::EventReturn BrowseClick(UI::EventParams &e); UI::EventReturn StorageClick(UI::EventParams &e); - UI::EventReturn HomeClick(UI::EventParams &e); + UI::EventReturn OnHomeClick(UI::EventParams &e); UI::EventReturn PinToggleClick(UI::EventParams &e); UI::EventReturn GridSettingsClick(UI::EventParams &e); UI::EventReturn OnRecentClear(UI::EventParams &e); From d14a90001b9aa0f8ee741018094d3cb3d1e2b875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Jul 2021 17:49:35 +0200 Subject: [PATCH 16/22] Less "Fd x for content URI" logspam --- Core/FileLoaders/LocalFileLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/FileLoaders/LocalFileLoader.cpp b/Core/FileLoaders/LocalFileLoader.cpp index 96d3412cf6..2e2caeff74 100644 --- a/Core/FileLoaders/LocalFileLoader.cpp +++ b/Core/FileLoaders/LocalFileLoader.cpp @@ -60,7 +60,7 @@ LocalFileLoader::LocalFileLoader(const Path &filename) #if PPSSPP_PLATFORM(ANDROID) 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()); + VERBOSE_LOG(SYSTEM, "Fd %d for content URI: '%s'", fd, filename.c_str()); if (fd < 0) { ERROR_LOG(FILESYS, "LoadFileLoader failed to open content URI: '%s'", filename.c_str()); return; From b3c2252b28dc2da546a42f97875a8da0cec5fede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Jul 2021 18:14:46 +0200 Subject: [PATCH 17/22] Fix further issues when the memstick dir is the PSP dir --- Core/FileSystems/DirectoryFileSystem.cpp | 6 ++++-- Core/System.cpp | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 4a7bde8f8b..4e2ab9b2d8 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -187,7 +187,7 @@ Path DirectoryFileHandle::GetLocalPath(const Path &basePath, std::string localpa localpath.erase(0, 1); if (fileSystemFlags_ & FileSystemFlags::STRIP_PSP) { - if (startsWith(localpath, "/PSP")) { + if (startsWith(localpath, "PSP/")) { localpath = localpath.substr(4); } } @@ -203,7 +203,7 @@ Path DirectoryFileSystem::GetLocalPath(std::string internalPath) const { internalPath.erase(0, 1); if (flags & FileSystemFlags::STRIP_PSP) { - if (startsWith(internalPath, "/PSP")) { + if (startsWith(internalPath, "PSP/")) { internalPath = internalPath.substr(4); } } @@ -638,6 +638,7 @@ bool DirectoryFileSystem::RemoveFile(const std::string &filename) { int DirectoryFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) { OpenFileEntry entry; + entry.hFile.fileSystemFlags_ = flags; u32 err = 0; bool success = entry.hFile.Open(basePath, filename, access, err); if (err == 0 && !success) { @@ -964,6 +965,7 @@ void DirectoryFileSystem::DoState(PointerWrap &p) { CloseAll(); u32 key; OpenFileEntry entry; + entry.hFile.fileSystemFlags_ = flags; for (u32 i = 0; i < num; i++) { Do(p, key); Do(p, entry.guestFilename); diff --git a/Core/System.cpp b/Core/System.cpp index d2427d43fd..d11fb469fe 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -708,8 +708,8 @@ void InitSysDirectories() { // Create the default directories that a real PSP creates. Good for homebrew so they can // expect a standard environment. Skipping THEME though, that's pointless. - File::CreateDir(g_Config.memStickDirectory / "PSP"); - File::CreateDir(g_Config.memStickDirectory / "PSP/COMMON"); + File::CreateDir(GetSysDirectory(DIRECTORY_PSP)); + File::CreateDir(GetSysDirectory(DIRECTORY_PSP) / "COMMON"); File::CreateDir(GetSysDirectory(DIRECTORY_GAME)); File::CreateDir(GetSysDirectory(DIRECTORY_SAVEDATA)); File::CreateDir(GetSysDirectory(DIRECTORY_SAVESTATE)); From 4489f1b312a77f39836e4a7ea1b33859da6b9666 Mon Sep 17 00:00:00 2001 From: iota97 Date: Mon, 26 Jul 2021 08:28:46 +0200 Subject: [PATCH 18/22] Accept numpad enter as well --- Common/UI/View.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index dbb206185d..c3fc74ffdc 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -972,6 +972,7 @@ bool TextEdit::Key(const KeyInput &input) { } break; case NKCODE_ENTER: + case NKCODE_NUMPAD_ENTER: { EventParams e{}; e.v = this; From 0fcf68632dbbd5c816967fe784d405047b7877f2 Mon Sep 17 00:00:00 2001 From: Linblow <8434205+Linblow@users.noreply.github.com> Date: Mon, 26 Jul 2021 11:47:21 +0200 Subject: [PATCH 19/22] Detect CwCheat file UTF-8 BOM sequence and ignore it --- Core/CwCheat.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index 0a348cf089..d1a12f98d6 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -98,6 +98,9 @@ bool CheatFileParser::Parse() { if (!tempLine) continue; + // Detect UTF-8 BOM sequence, and ignore it. + if (line_ == 1 && memcmp(tempLine, "\xEF\xBB\xBF", 3) == 0) + tempLine += 3; std::string line = TrimString(tempLine); // Minimum length 5 is shortest possible _ lines name of the game "_G N+" From 4205ce7c421a33d253fd1dcc06fd3ee5e48c4c1a Mon Sep 17 00:00:00 2001 From: iota97 Date: Mon, 26 Jul 2021 21:11:21 +0200 Subject: [PATCH 20/22] Trim username on join as well --- Core/HLE/proAdhoc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index c99c22590e..7a7033b91d 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1591,8 +1591,9 @@ int friendFinder(){ } // Update HUD User Count + name = (char*)packet->name.data; incoming = ""; - incoming.append((char*)packet->name.data); + incoming.append(name.substr(0, 8)); incoming.append(" Joined "); //do we need ip? //joined.append((char *)packet->ip); From 3f1a5a148f8decb8a09842628142516214462b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 26 Jul 2021 21:54:23 +0200 Subject: [PATCH 21/22] Add a missing java function (fix a little merge mistake) --- android/src/org/ppsspp/ppsspp/PpssppActivity.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index bb5673084f..761cc156ab 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -14,6 +14,7 @@ import android.os.storage.StorageManager; import android.content.ContentResolver; import android.database.Cursor; import android.provider.DocumentsContract; +import android.os.Environment; import androidx.documentfile.provider.DocumentFile; import java.util.ArrayList; import java.util.UUID; @@ -391,4 +392,14 @@ public class PpssppActivity extends NativeActivity { return -1; } } + + public boolean isExternalStoragePreservedLegacy() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // In 29 and later, we can check whether we got preserved storage legacy. + return Environment.isExternalStorageLegacy(); + } else { + // In 28 and earlier, we won't call this - we'll still request an exception. + return false; + } + } } From f53eb4ae0d6e3a828a1e4e4132b1bd6d4ab96dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 27 Jul 2021 22:55:02 +0200 Subject: [PATCH 22/22] Fix silly filesystem mounting bug introduced in #14638 Fixes #14650. --- Core/FileSystems/MetaFileSystem.cpp | 9 +++++++++ Core/FileSystems/MetaFileSystem.h | 1 + Core/HLE/sceIo.cpp | 7 ++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index b8ebc9238e..6ecd36226c 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -298,6 +298,15 @@ void MetaFileSystem::UnmountAll() { currentDir.clear(); } +void MetaFileSystem::Unmount(std::string prefix) { + for (auto iter = fileSystems.begin(); iter != fileSystems.end(); iter++) { + if (iter->prefix == prefix) { + fileSystems.erase(iter); + return; + } + } +} + bool MetaFileSystem::Remount(std::string prefix, std::shared_ptr system) { std::lock_guard guard(lock); for (auto &it : fileSystems) { diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index 84ea60a44a..f980fc1900 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -62,6 +62,7 @@ public: bool Remount(std::string prefix, std::shared_ptr system); void UnmountAll(); + void Unmount(std::string prefix); // The pointer returned from these are for temporary usage only. Do not store. IFileSystem *GetSystem(const std::string &prefix); diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 3b99c2d2d3..c856794110 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -775,7 +775,12 @@ void __IoShutdown() { } asyncDefaultPriority = -1; - pspFileSystem.UnmountAll(); + pspFileSystem.Unmount("ms0:"); + pspFileSystem.Unmount("fatms0:"); + pspFileSystem.Unmount("fatms:"); + pspFileSystem.Unmount("pfat0:"); + pspFileSystem.Unmount("flash0:"); + pspFileSystem.Unmount("exdata0:"); MemoryStick_Shutdown(); memStickCallbacks.clear();