summaryrefslogtreecommitdiffstats
path: root/src/common/fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/fs')
-rw-r--r--src/common/fs/file.cpp38
-rw-r--r--src/common/fs/fs_android.cpp98
-rw-r--r--src/common/fs/fs_android.h62
-rw-r--r--src/common/fs/path_util.cpp31
-rw-r--r--src/common/fs/path_util.h8
5 files changed, 228 insertions, 9 deletions
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
index 656b03cc5..b0b25eb43 100644
--- a/src/common/fs/file.cpp
+++ b/src/common/fs/file.cpp
@@ -5,6 +5,9 @@
#include "common/fs/file.h"
#include "common/fs/fs.h"
+#ifdef ANDROID
+#include "common/fs/fs_android.h"
+#endif
#include "common/logging/log.h"
#ifdef _WIN32
@@ -252,6 +255,23 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File
} else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
}
+#elif ANDROID
+ if (Android::IsContentUri(path)) {
+ ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!");
+ const auto fd = Android::OpenContentUri(path, Android::OpenMode::Read);
+ if (fd != -1) {
+ file = fdopen(fd, "r");
+ const auto error_num = errno;
+ if (error_num != 0 && file == nullptr) {
+ LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(),
+ strerror(error_num));
+ }
+ } else {
+ LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str());
+ }
+ } else {
+ file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
+ }
#else
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
#endif
@@ -372,6 +392,23 @@ u64 IOFile::GetSize() const {
// Flush any unwritten buffered data into the file prior to retrieving the file size.
std::fflush(file);
+#if ANDROID
+ u64 file_size = 0;
+ if (Android::IsContentUri(file_path)) {
+ file_size = Android::GetSize(file_path);
+ } else {
+ std::error_code ec;
+
+ file_size = fs::file_size(file_path, ec);
+
+ if (ec) {
+ LOG_ERROR(Common_Filesystem,
+ "Failed to retrieve the file size of path={}, ec_message={}",
+ PathToUTF8String(file_path), ec.message());
+ return 0;
+ }
+ }
+#else
std::error_code ec;
const auto file_size = fs::file_size(file_path, ec);
@@ -381,6 +418,7 @@ u64 IOFile::GetSize() const {
PathToUTF8String(file_path), ec.message());
return 0;
}
+#endif
return file_size;
}
diff --git a/src/common/fs/fs_android.cpp b/src/common/fs/fs_android.cpp
new file mode 100644
index 000000000..298a79bac
--- /dev/null
+++ b/src/common/fs/fs_android.cpp
@@ -0,0 +1,98 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/fs/fs_android.h"
+
+namespace Common::FS::Android {
+
+JNIEnv* GetEnvForThread() {
+ thread_local static struct OwnedEnv {
+ OwnedEnv() {
+ status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+ if (status == JNI_EDETACHED)
+ g_jvm->AttachCurrentThread(&env, nullptr);
+ }
+
+ ~OwnedEnv() {
+ if (status == JNI_EDETACHED)
+ g_jvm->DetachCurrentThread();
+ }
+
+ int status;
+ JNIEnv* env = nullptr;
+ } owned;
+ return owned.env;
+}
+
+void RegisterCallbacks(JNIEnv* env, jclass clazz) {
+ env->GetJavaVM(&g_jvm);
+ native_library = clazz;
+
+#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
+ F(JMethodID, JMethodName, Signature)
+#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
+ F(JMethodID, JMethodName, Signature)
+#define F(JMethodID, JMethodName, Signature) \
+ JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
+ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
+ ANDROID_STORAGE_FUNCTIONS(FS)
+#undef F
+#undef FS
+#undef FR
+}
+
+void UnRegisterCallbacks() {
+#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
+#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
+#define F(JMethodID) JMethodID = nullptr;
+ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
+ ANDROID_STORAGE_FUNCTIONS(FS)
+#undef F
+#undef FS
+#undef FR
+}
+
+bool IsContentUri(const std::string& path) {
+ constexpr std::string_view prefix = "content://";
+ if (path.size() < prefix.size()) [[unlikely]] {
+ return false;
+ }
+
+ return path.find(prefix) == 0;
+}
+
+int OpenContentUri(const std::string& filepath, OpenMode openmode) {
+ if (open_content_uri == nullptr)
+ return -1;
+
+ const char* mode = "";
+ switch (openmode) {
+ case OpenMode::Read:
+ mode = "r";
+ break;
+ default:
+ UNIMPLEMENTED();
+ return -1;
+ }
+ auto env = GetEnvForThread();
+ jstring j_filepath = env->NewStringUTF(filepath.c_str());
+ jstring j_mode = env->NewStringUTF(mode);
+ return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode);
+}
+
+#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
+ F(FunctionName, ReturnValue, JMethodID, Caller)
+#define F(FunctionName, ReturnValue, JMethodID, Caller) \
+ ReturnValue FunctionName(const std::string& filepath) { \
+ if (JMethodID == nullptr) { \
+ return 0; \
+ } \
+ auto env = GetEnvForThread(); \
+ jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
+ return env->Caller(native_library, JMethodID, j_filepath); \
+ }
+ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
+#undef F
+#undef FR
+
+} // namespace Common::FS::Android
diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h
new file mode 100644
index 000000000..bb8a52648
--- /dev/null
+++ b/src/common/fs/fs_android.h
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <jni.h>
+
+#define ANDROID_STORAGE_FUNCTIONS(V) \
+ V(OpenContentUri, int, (const std::string& filepath, OpenMode openmode), open_content_uri, \
+ "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I")
+
+#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \
+ V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J")
+
+namespace Common::FS::Android {
+
+static JavaVM* g_jvm = nullptr;
+static jclass native_library = nullptr;
+
+#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
+#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
+#define F(JMethodID) static jmethodID JMethodID = nullptr;
+ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
+ANDROID_STORAGE_FUNCTIONS(FS)
+#undef F
+#undef FS
+#undef FR
+
+enum class OpenMode {
+ Read,
+ Write,
+ ReadWrite,
+ WriteAppend,
+ WriteTruncate,
+ ReadWriteAppend,
+ ReadWriteTruncate,
+ Never
+};
+
+void RegisterCallbacks(JNIEnv* env, jclass clazz);
+
+void UnRegisterCallbacks();
+
+bool IsContentUri(const std::string& path);
+
+#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
+ F(FunctionName, Parameters, ReturnValue)
+#define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters;
+ANDROID_STORAGE_FUNCTIONS(FS)
+#undef F
+#undef FS
+
+#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
+ F(FunctionName, ReturnValue)
+#define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath);
+ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
+#undef F
+#undef FR
+
+} // namespace Common::FS::Android
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index ca755b053..e026a13d9 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -6,6 +6,9 @@
#include <unordered_map>
#include "common/fs/fs.h"
+#ifdef ANDROID
+#include "common/fs/fs_android.h"
+#endif
#include "common/fs/fs_paths.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
@@ -80,9 +83,7 @@ public:
yuzu_paths.insert_or_assign(yuzu_path, new_path);
}
-private:
- PathManagerImpl() {
- fs::path yuzu_path;
+ void Reinitialize(fs::path yuzu_path = {}) {
fs::path yuzu_path_cache;
fs::path yuzu_path_config;
@@ -96,12 +97,9 @@ private:
yuzu_path_cache = yuzu_path / CACHE_DIR;
yuzu_path_config = yuzu_path / CONFIG_DIR;
#elif ANDROID
- // On Android internal storage is mounted as "/sdcard"
- if (Exists("/sdcard")) {
- yuzu_path = "/sdcard/yuzu-emu";
- yuzu_path_cache = yuzu_path / CACHE_DIR;
- yuzu_path_config = yuzu_path / CONFIG_DIR;
- }
+ ASSERT(!yuzu_path.empty());
+ yuzu_path_cache = yuzu_path / CACHE_DIR;
+ yuzu_path_config = yuzu_path / CONFIG_DIR;
#else
yuzu_path = GetCurrentDir() / PORTABLE_DIR;
@@ -129,6 +127,11 @@ private:
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
}
+private:
+ PathManagerImpl() {
+ Reinitialize();
+ }
+
~PathManagerImpl() = default;
void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
@@ -217,6 +220,10 @@ fs::path RemoveTrailingSeparators(const fs::path& path) {
return fs::path{string_path};
}
+void SetAppDirectory(const std::string& app_directory) {
+ PathManagerImpl::GetInstance().Reinitialize(app_directory);
+}
+
const fs::path& GetYuzuPath(YuzuPath yuzu_path) {
return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path);
}
@@ -357,6 +364,12 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) {
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
std::string path(path_);
+#ifdef ANDROID
+ if (Android::IsContentUri(path)) {
+ return path;
+ }
+#endif // ANDROID
+
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h
index 13d713f1e..7cfe85b70 100644
--- a/src/common/fs/path_util.h
+++ b/src/common/fs/path_util.h
@@ -181,6 +181,14 @@ template <typename Path>
#endif
/**
+ * Sets the directory used for application storage. Used on Android where we do not know internal
+ * storage until informed by the frontend.
+ *
+ * @param app_directory Directory to use for application storage.
+ */
+void SetAppDirectory(const std::string& app_directory);
+
+/**
* Gets the filesystem path associated with the YuzuPath enum.
*
* @param yuzu_path YuzuPath enum