summaryrefslogtreecommitdiff
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.cpp41
-rw-r--r--src/common/fs/fs_android.cpp98
-rw-r--r--src/common/fs/fs_android.h65
-rw-r--r--src/common/fs/fs_paths.h1
-rw-r--r--src/common/fs/fs_types.h2
-rw-r--r--src/common/fs/path_util.cpp27
-rw-r--r--src/common/fs/path_util.h9
8 files changed, 273 insertions, 8 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 @@
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.cpp b/src/common/fs/fs.cpp
index e1716c62d..36e67c145 100644
--- a/src/common/fs/fs.cpp
+++ b/src/common/fs/fs.cpp
@@ -3,6 +3,9 @@
3 3
4#include "common/fs/file.h" 4#include "common/fs/file.h"
5#include "common/fs/fs.h" 5#include "common/fs/fs.h"
6#ifdef ANDROID
7#include "common/fs/fs_android.h"
8#endif
6#include "common/fs/path_util.h" 9#include "common/fs/path_util.h"
7#include "common/logging/log.h" 10#include "common/logging/log.h"
8 11
@@ -433,7 +436,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
433 436
434 if (True(filter & DirEntryFilter::File) && 437 if (True(filter & DirEntryFilter::File) &&
435 entry.status().type() == fs::file_type::regular) { 438 entry.status().type() == fs::file_type::regular) {
436 if (!callback(entry.path())) { 439 if (!callback(entry)) {
437 callback_error = true; 440 callback_error = true;
438 break; 441 break;
439 } 442 }
@@ -441,7 +444,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
441 444
442 if (True(filter & DirEntryFilter::Directory) && 445 if (True(filter & DirEntryFilter::Directory) &&
443 entry.status().type() == fs::file_type::directory) { 446 entry.status().type() == fs::file_type::directory) {
444 if (!callback(entry.path())) { 447 if (!callback(entry)) {
445 callback_error = true; 448 callback_error = true;
446 break; 449 break;
447 } 450 }
@@ -490,7 +493,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
490 493
491 if (True(filter & DirEntryFilter::File) && 494 if (True(filter & DirEntryFilter::File) &&
492 entry.status().type() == fs::file_type::regular) { 495 entry.status().type() == fs::file_type::regular) {
493 if (!callback(entry.path())) { 496 if (!callback(entry)) {
494 callback_error = true; 497 callback_error = true;
495 break; 498 break;
496 } 499 }
@@ -498,7 +501,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
498 501
499 if (True(filter & DirEntryFilter::Directory) && 502 if (True(filter & DirEntryFilter::Directory) &&
500 entry.status().type() == fs::file_type::directory) { 503 entry.status().type() == fs::file_type::directory) {
501 if (!callback(entry.path())) { 504 if (!callback(entry)) {
502 callback_error = true; 505 callback_error = true;
503 break; 506 break;
504 } 507 }
@@ -525,15 +528,39 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
525// Generic Filesystem Operations 528// Generic Filesystem Operations
526 529
527bool Exists(const fs::path& path) { 530bool Exists(const fs::path& path) {
531#ifdef ANDROID
532 if (Android::IsContentUri(path)) {
533 return Android::Exists(path);
534 } else {
535 return fs::exists(path);
536 }
537#else
528 return fs::exists(path); 538 return fs::exists(path);
539#endif
529} 540}
530 541
531bool IsFile(const fs::path& path) { 542bool IsFile(const fs::path& path) {
543#ifdef ANDROID
544 if (Android::IsContentUri(path)) {
545 return !Android::IsDirectory(path);
546 } else {
547 return fs::is_regular_file(path);
548 }
549#else
532 return fs::is_regular_file(path); 550 return fs::is_regular_file(path);
551#endif
533} 552}
534 553
535bool IsDir(const fs::path& path) { 554bool IsDir(const fs::path& path) {
555#ifdef ANDROID
556 if (Android::IsContentUri(path)) {
557 return Android::IsDirectory(path);
558 } else {
559 return fs::is_directory(path);
560 }
561#else
536 return fs::is_directory(path); 562 return fs::is_directory(path);
563#endif
537} 564}
538 565
539fs::path GetCurrentDir() { 566fs::path GetCurrentDir() {
@@ -578,6 +605,12 @@ fs::file_type GetEntryType(const fs::path& path) {
578} 605}
579 606
580u64 GetSize(const fs::path& path) { 607u64 GetSize(const fs::path& path) {
608#ifdef ANDROID
609 if (Android::IsContentUri(path)) {
610 return Android::GetSize(path);
611 }
612#endif
613
581 std::error_code ec; 614 std::error_code ec;
582 615
583 const auto file_size = fs::file_size(path, ec); 616 const auto file_size = fs::file_size(path, ec);
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..b441c2a12
--- /dev/null
+++ b/src/common/fs/fs_android.h
@@ -0,0 +1,65 @@
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 V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \
17 "(Ljava/lang/String;)Z") \
18 V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
19
20namespace Common::FS::Android {
21
22static JavaVM* g_jvm = nullptr;
23static jclass native_library = nullptr;
24
25#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
26#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
27#define F(JMethodID) static jmethodID JMethodID = nullptr;
28ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
29ANDROID_STORAGE_FUNCTIONS(FS)
30#undef F
31#undef FS
32#undef FR
33
34enum class OpenMode {
35 Read,
36 Write,
37 ReadWrite,
38 WriteAppend,
39 WriteTruncate,
40 ReadWriteAppend,
41 ReadWriteTruncate,
42 Never
43};
44
45void RegisterCallbacks(JNIEnv* env, jclass clazz);
46
47void UnRegisterCallbacks();
48
49bool IsContentUri(const std::string& path);
50
51#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
52 F(FunctionName, Parameters, ReturnValue)
53#define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters;
54ANDROID_STORAGE_FUNCTIONS(FS)
55#undef F
56#undef FS
57
58#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
59 F(FunctionName, ReturnValue)
60#define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath);
61ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
62#undef F
63#undef FR
64
65} // namespace Common::FS::Android
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h
index c77c112f1..61bac9eba 100644
--- a/src/common/fs/fs_paths.h
+++ b/src/common/fs/fs_paths.h
@@ -10,6 +10,7 @@
10 10
11// Sub-directories contained within a yuzu data directory 11// Sub-directories contained within a yuzu data directory
12 12
13#define AMIIBO_DIR "amiibo"
13#define CACHE_DIR "cache" 14#define CACHE_DIR "cache"
14#define CONFIG_DIR "config" 15#define CONFIG_DIR "config"
15#define DUMP_DIR "dump" 16#define DUMP_DIR "dump"
diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h
index 5a4090c19..900f85d24 100644
--- a/src/common/fs/fs_types.h
+++ b/src/common/fs/fs_types.h
@@ -66,6 +66,6 @@ DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter);
66 * @returns A boolean value. 66 * @returns A boolean value.
67 * Return true to indicate whether the callback is successful, false otherwise. 67 * Return true to indicate whether the callback is successful, false otherwise.
68 */ 68 */
69using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>; 69using DirEntryCallable = std::function<bool(const std::filesystem::directory_entry& entry)>;
70 70
71} // namespace Common::FS 71} // namespace Common::FS
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index defa3e918..d71cfacc6 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
@@ -95,6 +96,10 @@ private:
95 96
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;
99#elif ANDROID
100 ASSERT(!yuzu_path.empty());
101 yuzu_path_cache = yuzu_path / CACHE_DIR;
102 yuzu_path_config = yuzu_path / CONFIG_DIR;
98#else 103#else
99 yuzu_path = GetCurrentDir() / PORTABLE_DIR; 104 yuzu_path = GetCurrentDir() / PORTABLE_DIR;
100 105
@@ -109,6 +114,7 @@ private:
109#endif 114#endif
110 115
111 GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path); 116 GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
117 GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
112 GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache); 118 GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
113 GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config); 119 GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
114 GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); 120 GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
@@ -122,6 +128,11 @@ private:
122 GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR); 128 GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
123 } 129 }
124 130
131private:
132 PathManagerImpl() {
133 Reinitialize();
134 }
135
125 ~PathManagerImpl() = default; 136 ~PathManagerImpl() = default;
126 137
127 void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) { 138 void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
@@ -210,6 +221,10 @@ fs::path RemoveTrailingSeparators(const fs::path& path) {
210 return fs::path{string_path}; 221 return fs::path{string_path};
211} 222}
212 223
224void SetAppDirectory(const std::string& app_directory) {
225 PathManagerImpl::GetInstance().Reinitialize(app_directory);
226}
227
213const fs::path& GetYuzuPath(YuzuPath yuzu_path) { 228const fs::path& GetYuzuPath(YuzuPath yuzu_path) {
214 return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path); 229 return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path);
215} 230}
@@ -350,6 +365,12 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) {
350 365
351std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { 366std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
352 std::string path(path_); 367 std::string path(path_);
368#ifdef ANDROID
369 if (Android::IsContentUri(path)) {
370 return path;
371 }
372#endif // ANDROID
373
353 char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\'; 374 char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
354 char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/'; 375 char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
355 376
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h
index 13d713f1e..ba28964d0 100644
--- a/src/common/fs/path_util.h
+++ b/src/common/fs/path_util.h
@@ -12,6 +12,7 @@ namespace Common::FS {
12 12
13enum class YuzuPath { 13enum class YuzuPath {
14 YuzuDir, // Where yuzu stores its data. 14 YuzuDir, // Where yuzu stores its data.
15 AmiiboDir, // Where Amiibo backups are stored.
15 CacheDir, // Where cached filesystem data is stored. 16 CacheDir, // Where cached filesystem data is stored.
16 ConfigDir, // Where config files are stored. 17 ConfigDir, // Where config files are stored.
17 DumpDir, // Where dumped data is stored. 18 DumpDir, // Where dumped data is stored.
@@ -181,6 +182,14 @@ template <typename Path>
181#endif 182#endif
182 183
183/** 184/**
185 * Sets the directory used for application storage. Used on Android where we do not know internal
186 * storage until informed by the frontend.
187 *
188 * @param app_directory Directory to use for application storage.
189 */
190void SetAppDirectory(const std::string& app_directory);
191
192/**
184 * Gets the filesystem path associated with the YuzuPath enum. 193 * Gets the filesystem path associated with the YuzuPath enum.
185 * 194 *
186 * @param yuzu_path YuzuPath enum 195 * @param yuzu_path YuzuPath enum