summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt8
-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
6 files changed, 236 insertions, 9 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 13ed68b3f..aecb46872 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -155,6 +155,14 @@ if (WIN32)
155 target_link_libraries(common PRIVATE ntdll) 155 target_link_libraries(common PRIVATE ntdll)
156endif() 156endif()
157 157
158if(ANDROID)
159 target_sources(common
160 PRIVATE
161 fs/fs_android.cpp
162 fs/fs_android.h
163 )
164endif()
165
158if(ARCHITECTURE_x86_64) 166if(ARCHITECTURE_x86_64)
159 target_sources(common 167 target_sources(common
160 PRIVATE 168 PRIVATE
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 @@
5 5
6#include "common/fs/file.h" 6#include "common/fs/file.h"
7#include "common/fs/fs.h" 7#include "common/fs/fs.h"
8#ifdef ANDROID
9#include "common/fs/fs_android.h"
10#endif
8#include "common/logging/log.h" 11#include "common/logging/log.h"
9 12
10#ifdef _WIN32 13#ifdef _WIN32
@@ -252,6 +255,23 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File
252 } else { 255 } else {
253 _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type)); 256 _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
254 } 257 }
258#elif ANDROID
259 if (Android::IsContentUri(path)) {
260 ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!");
261 const auto fd = Android::OpenContentUri(path, Android::OpenMode::Read);
262 if (fd != -1) {
263 file = fdopen(fd, "r");
264 const auto error_num = errno;
265 if (error_num != 0 && file == nullptr) {
266 LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(),
267 strerror(error_num));
268 }
269 } else {
270 LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str());
271 }
272 } else {
273 file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
274 }
255#else 275#else
256 file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); 276 file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
257#endif 277#endif
@@ -372,6 +392,23 @@ u64 IOFile::GetSize() const {
372 // Flush any unwritten buffered data into the file prior to retrieving the file size. 392 // Flush any unwritten buffered data into the file prior to retrieving the file size.
373 std::fflush(file); 393 std::fflush(file);
374 394
395#if ANDROID
396 u64 file_size = 0;
397 if (Android::IsContentUri(file_path)) {
398 file_size = Android::GetSize(file_path);
399 } else {
400 std::error_code ec;
401
402 file_size = fs::file_size(file_path, ec);
403
404 if (ec) {
405 LOG_ERROR(Common_Filesystem,
406 "Failed to retrieve the file size of path={}, ec_message={}",
407 PathToUTF8String(file_path), ec.message());
408 return 0;
409 }
410 }
411#else
375 std::error_code ec; 412 std::error_code ec;
376 413
377 const auto file_size = fs::file_size(file_path, ec); 414 const auto file_size = fs::file_size(file_path, ec);
@@ -381,6 +418,7 @@ u64 IOFile::GetSize() const {
381 PathToUTF8String(file_path), ec.message()); 418 PathToUTF8String(file_path), ec.message());
382 return 0; 419 return 0;
383 } 420 }
421#endif
384 422
385 return file_size; 423 return file_size;
386} 424}
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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/fs/fs_android.h"
5
6namespace Common::FS::Android {
7
8JNIEnv* GetEnvForThread() {
9 thread_local static struct OwnedEnv {
10 OwnedEnv() {
11 status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
12 if (status == JNI_EDETACHED)
13 g_jvm->AttachCurrentThread(&env, nullptr);
14 }
15
16 ~OwnedEnv() {
17 if (status == JNI_EDETACHED)
18 g_jvm->DetachCurrentThread();
19 }
20
21 int status;
22 JNIEnv* env = nullptr;
23 } owned;
24 return owned.env;
25}
26
27void RegisterCallbacks(JNIEnv* env, jclass clazz) {
28 env->GetJavaVM(&g_jvm);
29 native_library = clazz;
30
31#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
32 F(JMethodID, JMethodName, Signature)
33#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
34 F(JMethodID, JMethodName, Signature)
35#define F(JMethodID, JMethodName, Signature) \
36 JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
37 ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
38 ANDROID_STORAGE_FUNCTIONS(FS)
39#undef F
40#undef FS
41#undef FR
42}
43
44void UnRegisterCallbacks() {
45#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
46#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
47#define F(JMethodID) JMethodID = nullptr;
48 ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
49 ANDROID_STORAGE_FUNCTIONS(FS)
50#undef F
51#undef FS
52#undef FR
53}
54
55bool IsContentUri(const std::string& path) {
56 constexpr std::string_view prefix = "content://";
57 if (path.size() < prefix.size()) [[unlikely]] {
58 return false;
59 }
60
61 return path.find(prefix) == 0;
62}
63
64int OpenContentUri(const std::string& filepath, OpenMode openmode) {
65 if (open_content_uri == nullptr)
66 return -1;
67
68 const char* mode = "";
69 switch (openmode) {
70 case OpenMode::Read:
71 mode = "r";
72 break;
73 default:
74 UNIMPLEMENTED();
75 return -1;
76 }
77 auto env = GetEnvForThread();
78 jstring j_filepath = env->NewStringUTF(filepath.c_str());
79 jstring j_mode = env->NewStringUTF(mode);
80 return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode);
81}
82
83#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
84 F(FunctionName, ReturnValue, JMethodID, Caller)
85#define F(FunctionName, ReturnValue, JMethodID, Caller) \
86 ReturnValue FunctionName(const std::string& filepath) { \
87 if (JMethodID == nullptr) { \
88 return 0; \
89 } \
90 auto env = GetEnvForThread(); \
91 jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
92 return env->Caller(native_library, JMethodID, j_filepath); \
93 }
94ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
95#undef F
96#undef FR
97
98} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7#include <vector>
8#include <jni.h>
9
10#define ANDROID_STORAGE_FUNCTIONS(V) \
11 V(OpenContentUri, int, (const std::string& filepath, OpenMode openmode), open_content_uri, \
12 "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I")
13
14#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \
15 V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J")
16
17namespace Common::FS::Android {
18
19static JavaVM* g_jvm = nullptr;
20static jclass native_library = nullptr;
21
22#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
23#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
24#define F(JMethodID) static jmethodID JMethodID = nullptr;
25ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
26ANDROID_STORAGE_FUNCTIONS(FS)
27#undef F
28#undef FS
29#undef FR
30
31enum class OpenMode {
32 Read,
33 Write,
34 ReadWrite,
35 WriteAppend,
36 WriteTruncate,
37 ReadWriteAppend,
38 ReadWriteTruncate,
39 Never
40};
41
42void RegisterCallbacks(JNIEnv* env, jclass clazz);
43
44void UnRegisterCallbacks();
45
46bool IsContentUri(const std::string& path);
47
48#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
49 F(FunctionName, Parameters, ReturnValue)
50#define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters;
51ANDROID_STORAGE_FUNCTIONS(FS)
52#undef F
53#undef FS
54
55#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
56 F(FunctionName, ReturnValue)
57#define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath);
58ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
59#undef F
60#undef FR
61
62} // 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 @@
6#include <unordered_map> 6#include <unordered_map>
7 7
8#include "common/fs/fs.h" 8#include "common/fs/fs.h"
9#ifdef ANDROID
10#include "common/fs/fs_android.h"
11#endif
9#include "common/fs/fs_paths.h" 12#include "common/fs/fs_paths.h"
10#include "common/fs/path_util.h" 13#include "common/fs/path_util.h"
11#include "common/logging/log.h" 14#include "common/logging/log.h"
@@ -80,9 +83,7 @@ public:
80 yuzu_paths.insert_or_assign(yuzu_path, new_path); 83 yuzu_paths.insert_or_assign(yuzu_path, new_path);
81 } 84 }
82 85
83private: 86 void Reinitialize(fs::path yuzu_path = {}) {
84 PathManagerImpl() {
85 fs::path yuzu_path;
86 fs::path yuzu_path_cache; 87 fs::path yuzu_path_cache;
87 fs::path yuzu_path_config; 88 fs::path yuzu_path_config;
88 89
@@ -96,12 +97,9 @@ private:
96 yuzu_path_cache = yuzu_path / CACHE_DIR; 97 yuzu_path_cache = yuzu_path / CACHE_DIR;
97 yuzu_path_config = yuzu_path / CONFIG_DIR; 98 yuzu_path_config = yuzu_path / CONFIG_DIR;
98#elif ANDROID 99#elif ANDROID
99 // On Android internal storage is mounted as "/sdcard" 100 ASSERT(!yuzu_path.empty());
100 if (Exists("/sdcard")) { 101 yuzu_path_cache = yuzu_path / CACHE_DIR;
101 yuzu_path = "/sdcard/yuzu-emu"; 102 yuzu_path_config = yuzu_path / CONFIG_DIR;
102 yuzu_path_cache = yuzu_path / CACHE_DIR;
103 yuzu_path_config = yuzu_path / CONFIG_DIR;
104 }
105#else 103#else
106 yuzu_path = GetCurrentDir() / PORTABLE_DIR; 104 yuzu_path = GetCurrentDir() / PORTABLE_DIR;
107 105
@@ -129,6 +127,11 @@ private:
129 GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR); 127 GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
130 } 128 }
131 129
130private:
131 PathManagerImpl() {
132 Reinitialize();
133 }
134
132 ~PathManagerImpl() = default; 135 ~PathManagerImpl() = default;
133 136
134 void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) { 137 void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
@@ -217,6 +220,10 @@ fs::path RemoveTrailingSeparators(const fs::path& path) {
217 return fs::path{string_path}; 220 return fs::path{string_path};
218} 221}
219 222
223void SetAppDirectory(const std::string& app_directory) {
224 PathManagerImpl::GetInstance().Reinitialize(app_directory);
225}
226
220const fs::path& GetYuzuPath(YuzuPath yuzu_path) { 227const fs::path& GetYuzuPath(YuzuPath yuzu_path) {
221 return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path); 228 return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path);
222} 229}
@@ -357,6 +364,12 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) {
357 364
358std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { 365std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
359 std::string path(path_); 366 std::string path(path_);
367#ifdef ANDROID
368 if (Android::IsContentUri(path)) {
369 return path;
370 }
371#endif // ANDROID
372
360 char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\'; 373 char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
361 char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/'; 374 char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
362 375
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>
181#endif 181#endif
182 182
183/** 183/**
184 * Sets the directory used for application storage. Used on Android where we do not know internal
185 * storage until informed by the frontend.
186 *
187 * @param app_directory Directory to use for application storage.
188 */
189void SetAppDirectory(const std::string& app_directory);
190
191/**
184 * Gets the filesystem path associated with the YuzuPath enum. 192 * Gets the filesystem path associated with the YuzuPath enum.
185 * 193 *
186 * @param yuzu_path YuzuPath enum 194 * @param yuzu_path YuzuPath enum