mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge branch 'master' of github.com:archanox/ppsspp-riscv
This commit is contained in:
commit
8541c446d6
28 changed files with 535 additions and 264 deletions
|
@ -11,12 +11,15 @@ static jmethodID openContentUri;
|
|||
static jmethodID listContentUriDir;
|
||||
static jmethodID contentUriCreateFile;
|
||||
static jmethodID contentUriCreateDirectory;
|
||||
static jmethodID contentUriCopyFile;
|
||||
static jmethodID contentUriMoveFile;
|
||||
static jmethodID contentUriRemoveFile;
|
||||
static jmethodID contentUriRenameFileTo;
|
||||
static jmethodID contentUriGetFileInfo;
|
||||
static jmethodID contentUriFileExists;
|
||||
static jmethodID contentUriGetFreeStorageSpace;
|
||||
static jmethodID filePathGetFreeStorageSpace;
|
||||
static jmethodID isExternalStoragePreservedLegacy;
|
||||
|
||||
static jobject g_nativeActivity;
|
||||
|
||||
|
@ -29,13 +32,17 @@ 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);
|
||||
contentUriRemoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriRemoveFile", "(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;)I");
|
||||
_dbg_assert_(contentUriRemoveFile);
|
||||
contentUriRenameFileTo = env->GetMethodID(env->GetObjectClass(obj), "contentUriRenameFileTo", "(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);
|
||||
contentUriGetFileInfo = env->GetMethodID(env->GetObjectClass(obj), "contentUriGetFileInfo", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
_dbg_assert_(contentUriGetFileInfo);
|
||||
|
@ -45,6 +52,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) {
|
||||
|
@ -75,49 +84,71 @@ 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));
|
||||
}
|
||||
|
||||
bool Android_RemoveFile(const std::string &fileUri) {
|
||||
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());
|
||||
return env->CallBooleanMethod(g_nativeActivity, contentUriRemoveFile, paramFileName);
|
||||
jstring paramDestParentUri = env->NewStringUTF(destParentUri.c_str());
|
||||
return StorageErrorFromInt(env->CallIntMethod(g_nativeActivity, contentUriCopyFile, paramFileName, paramDestParentUri));
|
||||
}
|
||||
|
||||
bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName) {
|
||||
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 StorageErrorFromInt(env->CallIntMethod(g_nativeActivity, contentUriMoveFile, paramFileName, paramSrcParentUri, paramDestParentUri));
|
||||
}
|
||||
|
||||
StorageError Android_RemoveFile(const std::string &fileUri) {
|
||||
if (!g_nativeActivity) {
|
||||
return StorageError::UNKNOWN;
|
||||
}
|
||||
auto env = getEnv();
|
||||
jstring paramFileName = env->NewStringUTF(fileUri.c_str());
|
||||
return StorageErrorFromInt(env->CallIntMethod(g_nativeActivity, contentUriRemoveFile, paramFileName));
|
||||
}
|
||||
|
||||
StorageError Android_RenameFileTo(const std::string &fileUri, const std::string &newName) {
|
||||
if (!g_nativeActivity) {
|
||||
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.
|
||||
static bool ParseFileInfo(const std::string &line, File::FileInfo *fileInfo) {
|
||||
std::vector<std::string> parts;
|
||||
SplitString(line, '|', parts);
|
||||
if (parts.size() != 5) {
|
||||
if (parts.size() != 4) {
|
||||
ERROR_LOG(FILESYS, "Bad format: %s", line.c_str());
|
||||
return false;
|
||||
}
|
||||
|
@ -125,12 +156,11 @@ 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.
|
||||
|
||||
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;
|
||||
|
@ -155,6 +185,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 +221,8 @@ std::vector<File::FileInfo> 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);
|
||||
}
|
||||
}
|
||||
|
@ -224,4 +258,28 @@ 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(StorageError error) {
|
||||
switch (error) {
|
||||
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)";
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// This string should never appear except on Android.
|
||||
std::string g_extFilesDir = "(IF YOU SEE THIS THERE'S A BUG)";
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,6 +13,23 @@ enum class Android_OpenContentUriMode {
|
|||
READ_WRITE_TRUNCATE = 2, // "rwt"
|
||||
};
|
||||
|
||||
// Matches the constants in PpssppActivity.java.
|
||||
enum class StorageError {
|
||||
SUCCESS = 0,
|
||||
UNKNOWN = -1,
|
||||
NOT_FOUND = -2,
|
||||
DISK_FULL = -3,
|
||||
ALREADY_EXISTS = -4,
|
||||
};
|
||||
|
||||
inline StorageError StorageErrorFromInt(int ival) {
|
||||
if (ival >= 0) {
|
||||
return StorageError::SUCCESS;
|
||||
} else {
|
||||
return (StorageError)ival;
|
||||
}
|
||||
}
|
||||
|
||||
#if PPSSPP_PLATFORM(ANDROID) && !defined(__LIBRETRO__)
|
||||
|
||||
#include <jni.h>
|
||||
|
@ -23,14 +40,18 @@ 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);
|
||||
bool Android_RemoveFile(const std::string &fileUri);
|
||||
bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName);
|
||||
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);
|
||||
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(StorageError error);
|
||||
|
||||
std::vector<File::FileInfo> Android_ListContentUri(const std::string &uri);
|
||||
|
||||
|
@ -38,18 +59,24 @@ 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_RemoveFile(const std::string &fileUri) { return false; }
|
||||
inline bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName) { 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; }
|
||||
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(StorageError error) { return ""; }
|
||||
inline std::vector<File::FileInfo> Android_ListContentUri(const std::string &uri) {
|
||||
return std::vector<File::FileInfo>();
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -390,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;
|
||||
}
|
||||
|
@ -433,13 +439,19 @@ 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());
|
||||
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());
|
||||
|
@ -526,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;
|
||||
}
|
||||
|
@ -565,13 +577,14 @@ 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -594,26 +607,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()) == StorageError::SUCCESS) {
|
||||
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
|
||||
|
@ -631,7 +641,7 @@ bool Copy(const Path &srcFilename, const Path &destFilename)
|
|||
#else
|
||||
|
||||
// buffer size
|
||||
#define BSIZE 4096
|
||||
#define BSIZE 16384
|
||||
|
||||
char buffer[BSIZE];
|
||||
|
||||
|
@ -685,7 +695,21 @@ 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()) {
|
||||
// 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()) == StorageError::SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
// If failed, fall through and try other ways.
|
||||
}
|
||||
}
|
||||
|
||||
if (Rename(srcFilename, destFilename)) {
|
||||
return true;
|
||||
} else if (Copy(srcFilename, destFilename)) {
|
||||
|
|
|
@ -279,7 +279,6 @@ Path Path::GetRootVolume() const {
|
|||
return Path(path);
|
||||
}
|
||||
#endif
|
||||
|
||||
return Path("/");
|
||||
}
|
||||
|
||||
|
@ -301,7 +300,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();
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -972,6 +972,7 @@ bool TextEdit::Key(const KeyInput &input) {
|
|||
}
|
||||
break;
|
||||
case NKCODE_ENTER:
|
||||
case NKCODE_NUMPAD_ENTER:
|
||||
{
|
||||
EventParams e{};
|
||||
e.v = this;
|
||||
|
|
|
@ -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+"
|
||||
|
|
|
@ -60,8 +60,8 @@ 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());
|
||||
if (fd == -1) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -203,7 +226,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 +324,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;
|
||||
|
@ -511,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
|
||||
|
@ -628,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) {
|
||||
|
@ -954,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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -63,6 +63,7 @@ enum class FileSystemFlags {
|
|||
UMD = 2,
|
||||
CARD = 4,
|
||||
FLASH = 8,
|
||||
STRIP_PSP = 16,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(FileSystemFlags);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#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<IFileSystem> 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<IFileSystem> isoFileSystem_;
|
||||
};
|
||||
|
|
|
@ -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,47 @@ 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<IFileSystem> system) {
|
||||
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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) {
|
||||
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<IFileSystem> system) {
|
||||
std::lock_guard<std::recursive_mutex> 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 +328,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<std::recursive_mutex> guard(lock);
|
||||
current = 6;
|
||||
|
||||
// Ownership is a bit convoluted. Let's just delete everything once.
|
||||
|
||||
std::set<IFileSystem *> 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)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
#include "Core/FileSystems/FileSystem.h"
|
||||
|
||||
|
@ -28,13 +29,14 @@ private:
|
|||
s32 current;
|
||||
struct MountPoint {
|
||||
std::string prefix;
|
||||
IFileSystem *system;
|
||||
std::shared_ptr<IFileSystem> 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<MountPoint> fileSystems;
|
||||
|
||||
typedef std::map<int, std::string> currentDir_t;
|
||||
|
@ -43,26 +45,35 @@ 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<IFileSystem> system);
|
||||
// Fails if there's not already a file system at prefix.
|
||||
bool Remount(std::string prefix, std::shared_ptr<IFileSystem> 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);
|
||||
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 +88,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<u32, OpenFileEntry> EntryMap;
|
||||
|
||||
EntryMap entries;
|
||||
IHandleAllocator *hAlloc;
|
||||
Path basePath;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <cstdlib>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
|
||||
#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,23 +631,35 @@ void __IoInit() {
|
|||
asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify);
|
||||
syncNotifyEvent = CoreTiming::RegisterEvent("IoSyncNotify", __IoSyncNotify);
|
||||
|
||||
memstickSystem = 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)
|
||||
flash0System = new DirectoryFileSystem(&pspFileSystem, g_Config.flash0Directory, FileSystemFlags::FLASH);
|
||||
auto flash0System = std::shared_ptr<IFileSystem>(new DirectoryFileSystem(&pspFileSystem, g_Config.flash0Directory, FileSystemFlags::FLASH));
|
||||
#else
|
||||
flash0System = new VFSFileSystem(&pspFileSystem, "flash0");
|
||||
auto flash0System = std::shared_ptr<IFileSystem>(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<IFileSystem>(new DirectoryFileSystem(&pspFileSystem, g_Config.memStickDirectory, memstickFlags));
|
||||
|
||||
pspFileSystem.Mount("ms0:", memstickSystem);
|
||||
pspFileSystem.Mount("fatms0:", memstickSystem);
|
||||
pspFileSystem.Mount("fatms:", memstickSystem);
|
||||
pspFileSystem.Mount("pfat0:", memstickSystem);
|
||||
|
||||
pspFileSystem.Mount("flash0:", flash0System);
|
||||
|
||||
if (g_RemasterMode) {
|
||||
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<IFileSystem>(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,22 +775,12 @@ 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.Unmount("ms0:");
|
||||
pspFileSystem.Unmount("fatms0:");
|
||||
pspFileSystem.Unmount("fatms:");
|
||||
pspFileSystem.Unmount("pfat0:");
|
||||
pspFileSystem.Unmount("flash0:");
|
||||
pspFileSystem.Unmount("exdata0:");
|
||||
|
||||
MemoryStick_Shutdown();
|
||||
memStickCallbacks.clear();
|
||||
|
|
|
@ -80,11 +80,11 @@ void InitMemoryForGameISO(FileLoader *fileLoader) {
|
|||
return;
|
||||
}
|
||||
|
||||
IFileSystem *fileSystem = nullptr;
|
||||
IFileSystem *blockSystem = nullptr;
|
||||
std::shared_ptr<IFileSystem> fileSystem;
|
||||
std::shared_ptr<IFileSystem> blockSystem;
|
||||
|
||||
if (fileLoader->IsDirectory()) {
|
||||
fileSystem = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->GetPath());
|
||||
fileSystem = std::shared_ptr<IFileSystem>(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<IFileSystem> iso = std::shared_ptr<IFileSystem>(new ISOFileSystem(&pspFileSystem, bd));
|
||||
fileSystem = iso;
|
||||
blockSystem = new ISOBlockSystem(iso);
|
||||
blockSystem = std::shared_ptr<IFileSystem>(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<IFileSystem> fileSystem;
|
||||
std::shared_ptr<IFileSystem> blockSystem;
|
||||
|
||||
if (fileLoader->IsDirectory()) {
|
||||
fileSystem = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->GetPath());
|
||||
fileSystem = std::shared_ptr<IFileSystem>(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<IFileSystem> iso = std::shared_ptr<IFileSystem>(new ISOFileSystem(&pspFileSystem, bd));
|
||||
fileSystem = iso;
|
||||
blockSystem = new ISOBlockSystem(iso);
|
||||
blockSystem = std::shared_ptr<IFileSystem>(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<IFileSystem> umd2 = std::shared_ptr<IFileSystem>(new ISOFileSystem(&pspFileSystem, bd));
|
||||
std::shared_ptr<IFileSystem> blockSystem = std::shared_ptr<IFileSystem>(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<IFileSystem> fs = std::shared_ptr<IFileSystem>(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<IFileSystem> umd = std::shared_ptr<IFileSystem>(new BlobFileSystem(&pspFileSystem, fileLoader, "data.ppdmp"));
|
||||
pspFileSystem.Mount("disc0:", umd);
|
||||
|
||||
PSPLoaders_Shutdown();
|
||||
|
|
|
@ -351,6 +351,7 @@ void CPU_Shutdown() {
|
|||
if (coreParameter.enableSound) {
|
||||
Audio_Shutdown();
|
||||
}
|
||||
|
||||
pspFileSystem.Shutdown();
|
||||
mipsr4k.Shutdown();
|
||||
Memory::Shutdown();
|
||||
|
@ -591,6 +592,8 @@ Path GetSysDirectory(PSPDirectories directoryType) {
|
|||
}
|
||||
|
||||
switch (directoryType) {
|
||||
case DIRECTORY_PSP:
|
||||
return pspDirectory;
|
||||
case DIRECTORY_CHEATS:
|
||||
return pspDirectory / "Cheats";
|
||||
case DIRECTORY_GAME:
|
||||
|
@ -705,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));
|
||||
|
|
|
@ -37,6 +37,7 @@ enum GlobalUIState {
|
|||
|
||||
// Use these in conjunction with GetSysDirectory.
|
||||
enum PSPDirectories {
|
||||
DIRECTORY_PSP,
|
||||
DIRECTORY_CHEATS,
|
||||
DIRECTORY_SCREENSHOT,
|
||||
DIRECTORY_SYSTEM,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -386,6 +386,7 @@
|
|||
<ClInclude Include="..\..\Common\BitScan.h" />
|
||||
<ClInclude Include="..\..\Common\BitSet.h" />
|
||||
<ClInclude Include="..\..\Common\Buffer.h" />
|
||||
<ClInclude Include="..\..\Common\File\AndroidStorage.h" />
|
||||
<ClInclude Include="..\..\Common\Net\NetBuffer.h" />
|
||||
<ClInclude Include="..\..\Common\Data\Collections\ConstMap.h" />
|
||||
<ClInclude Include="..\..\Common\Data\Collections\FixedSizeQueue.h" />
|
||||
|
@ -518,6 +519,7 @@
|
|||
<ClCompile Include="..\..\Common\ArmCPUDetect.cpp" />
|
||||
<ClCompile Include="..\..\Common\ArmEmitter.cpp" />
|
||||
<ClCompile Include="..\..\Common\Buffer.cpp" />
|
||||
<ClCompile Include="..\..\Common\File\AndroidStorage.cpp" />
|
||||
<ClCompile Include="..\..\Common\Net\NetBuffer.cpp" />
|
||||
<ClCompile Include="..\..\Common\Data\Color\RGBAUtil.cpp" />
|
||||
<ClCompile Include="..\..\Common\Data\Convert\SmallDataConvert.cpp" />
|
||||
|
@ -640,4 +642,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -375,6 +375,9 @@
|
|||
<ClCompile Include="..\..\Common\GPU\ShaderTranslation.cpp">
|
||||
<Filter>GPU</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Common\File\AndroidStorage.cpp">
|
||||
<Filter>File</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="targetver.h" />
|
||||
|
@ -691,6 +694,9 @@
|
|||
<ClInclude Include="..\..\Common\GPU\ShaderTranslation.h">
|
||||
<Filter>GPU</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Common\File\AndroidStorage.h">
|
||||
<Filter>File</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="..\..\ext\libpng17\CMakeLists.txt">
|
||||
|
@ -705,4 +711,4 @@
|
|||
<Filter>Math\lin</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -8,8 +8,13 @@ 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;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.os.Environment;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
|
@ -27,6 +32,13 @@ public class PpssppActivity extends NativeActivity {
|
|||
|
||||
public static boolean libraryLoaded = false;
|
||||
|
||||
// Matches the enum in AndroidStorage.h.
|
||||
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() {
|
||||
if (Build.CPU_ABI.equals("armeabi")) {
|
||||
|
@ -135,167 +147,234 @@ public class PpssppActivity extends NativeActivity {
|
|||
}
|
||||
}
|
||||
|
||||
private static String fileInfoToString(DocumentFile file) {
|
||||
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.getUri() + "|" + file.lastModified();
|
||||
return str;
|
||||
return str + size + "|" + documentName + "|" + lastModified;
|
||||
}
|
||||
|
||||
// TODO: Maybe add a cheaper version that doesn't extract all the file information?
|
||||
// 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);
|
||||
DocumentFile documentFile = DocumentFile.fromTreeUri(this, uri);
|
||||
DocumentFile[] children = documentFile.listFiles();
|
||||
ArrayList<String> listing = new ArrayList<String>();
|
||||
// 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<String> listing = new ArrayList<>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contentUriRenameFileTo(String fileUri, String newName) {
|
||||
// NOTE: The destination is the parent directory! This means that contentUriCopyFile
|
||||
// cannot rename things as part of the operation.
|
||||
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 ? STORAGE_ERROR_SUCCESS : STORAGE_ERROR_UNKNOWN;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "contentUriCopyFile exception: " + e.toString());
|
||||
return STORAGE_ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The destination is the parent directory! This means that contentUriCopyFile
|
||||
// cannot rename things as part of the operation.
|
||||
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 ? STORAGE_ERROR_SUCCESS : STORAGE_ERROR_UNKNOWN;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "contentUriMoveFile exception: " + e.toString());
|
||||
return STORAGE_ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
// Log.i(TAG, "New uri: " + newUri.toString());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -313,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Add table
Reference in a new issue