diff options
Diffstat (limited to '')
-rw-r--r-- | src/common/CMakeLists.txt | 13 | ||||
-rw-r--r-- | src/common/dynamic_library.cpp | 2 | ||||
-rw-r--r-- | src/common/dynamic_library.h | 3 | ||||
-rw-r--r-- | src/common/error.cpp | 3 | ||||
-rw-r--r-- | src/common/fs/file.cpp | 38 | ||||
-rw-r--r-- | src/common/fs/fs_android.cpp | 98 | ||||
-rw-r--r-- | src/common/fs/fs_android.h | 62 | ||||
-rw-r--r-- | src/common/fs/path_util.cpp | 26 | ||||
-rw-r--r-- | src/common/fs/path_util.h | 8 | ||||
-rw-r--r-- | src/common/host_memory.cpp | 34 | ||||
-rw-r--r-- | src/common/logging/backend.cpp | 26 | ||||
-rw-r--r-- | src/common/logging/text_formatter.cpp | 35 | ||||
-rw-r--r-- | src/common/logging/text_formatter.h | 2 |
13 files changed, 344 insertions, 6 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 13ed68b3f..efc4a9fe9 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -155,6 +155,14 @@ if (WIN32) target_link_libraries(common PRIVATE ntdll) endif() +if(ANDROID) + target_sources(common + PRIVATE + fs/fs_android.cpp + fs/fs_android.h + ) +endif() + if(ARCHITECTURE_x86_64) target_sources(common PRIVATE @@ -194,6 +202,11 @@ create_target_directory_groups(common) target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle) +if (ANDROID) + # For ASharedMemory_create + target_link_libraries(common PRIVATE android) +endif() + if (YUZU_USE_PRECOMPILED_HEADERS) target_precompile_headers(common PRIVATE precompiled_headers.h) endif() diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp index 054277a2b..4fabe7e52 100644 --- a/src/common/dynamic_library.cpp +++ b/src/common/dynamic_library.cpp @@ -22,6 +22,8 @@ DynamicLibrary::DynamicLibrary(const char* filename) { void(Open(filename)); } +DynamicLibrary::DynamicLibrary(void* handle_) : handle{handle_} {} + DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept : handle{std::exchange(rhs.handle, nullptr)} {} diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h index f42bdf441..662d454d4 100644 --- a/src/common/dynamic_library.h +++ b/src/common/dynamic_library.h @@ -20,6 +20,9 @@ public: /// Automatically loads the specified library. Call IsOpen() to check validity before use. explicit DynamicLibrary(const char* filename); + /// Initializes the dynamic library with an already opened handle. + explicit DynamicLibrary(void* handle_); + /// Moves the library. DynamicLibrary(DynamicLibrary&&) noexcept; DynamicLibrary& operator=(DynamicLibrary&&) noexcept; diff --git a/src/common/error.cpp b/src/common/error.cpp index ddb03bd45..1b2009db7 100644 --- a/src/common/error.cpp +++ b/src/common/error.cpp @@ -30,7 +30,8 @@ std::string NativeErrorToString(int e) { return ret; #else char err_str[255]; -#if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)) +#if defined(ANDROID) || \ + (defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))) // Thread safe (GNU-specific) const char* str = strerror_r(e, err_str, sizeof(err_str)); return std::string(str); 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 defa3e918..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; @@ -95,6 +96,10 @@ private: yuzu_path_cache = yuzu_path / CACHE_DIR; yuzu_path_config = yuzu_path / CONFIG_DIR; +#elif ANDROID + ASSERT(!yuzu_path.empty()); + yuzu_path_cache = yuzu_path / CACHE_DIR; + yuzu_path_config = yuzu_path / CONFIG_DIR; #else yuzu_path = GetCurrentDir() / PORTABLE_DIR; @@ -122,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) { @@ -210,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); } @@ -350,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 diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 8e4f1f97a..ba22595e0 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -11,9 +11,14 @@ #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv +#ifdef ANDROID +#include <android/sharedmem.h> +#endif + #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif +#include <boost/icl/interval_set.hpp> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> @@ -366,17 +371,20 @@ public: } // Backing memory initialization -#if defined(__FreeBSD__) && __FreeBSD__ < 13 +#ifdef ANDROID + fd = ASharedMemory_create("HostMemory", backing_size); +#elif defined(__FreeBSD__) && __FreeBSD__ < 13 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 fd = shm_open(SHM_ANON, O_RDWR, 0600); #else fd = memfd_create("HostMemory", 0); #endif - if (fd == -1) { + if (fd < 0) { LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno)); throw std::bad_alloc{}; } +#ifndef ANDROID // Defined to extend the file with zeros int ret = ftruncate(fd, backing_size); if (ret != 0) { @@ -384,6 +392,7 @@ public: strerror(errno)); throw std::bad_alloc{}; } +#endif backing_base = static_cast<u8*>( mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); @@ -415,6 +424,7 @@ public: madvise(virtual_base, virtual_size, MADV_HUGEPAGE); #endif + placeholders.add({0, virtual_size}); good = true; } @@ -423,6 +433,10 @@ public: } void Map(size_t virtual_offset, size_t host_offset, size_t length) { + { + std::scoped_lock lock{placeholder_mutex}; + placeholders.subtract({virtual_offset, virtual_offset + length}); + } void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, host_offset); @@ -433,6 +447,19 @@ public: // The method name is wrong. We're still talking about the virtual range. // We don't want to unmap, we want to reserve this memory. + { + std::scoped_lock lock{placeholder_mutex}; + auto it = placeholders.find({virtual_offset - 1, virtual_offset + length + 1}); + + if (it != placeholders.end()) { + size_t prev_upper = virtual_offset + length; + virtual_offset = std::min(virtual_offset, it->lower()); + length = std::max(it->upper(), prev_upper) - virtual_offset; + } + + placeholders.add({virtual_offset, virtual_offset + length}); + } + void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); @@ -476,6 +503,9 @@ private: } int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create + + boost::icl::interval_set<size_t> placeholders; ///< Mapped placeholders + std::mutex placeholder_mutex; ///< Mutex for placeholders }; #else // ^^^ Linux ^^^ vvv Generic vvv diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index f96c7c222..6e8e8eb36 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -155,6 +155,26 @@ public: void EnableForStacktrace() override {} }; +#ifdef ANDROID +/** + * Backend that writes to the Android logcat + */ +class LogcatBackend : public Backend { +public: + explicit LogcatBackend() = default; + + ~LogcatBackend() override = default; + + void Write(const Entry& entry) override { + PrintMessageToLogcat(entry); + } + + void Flush() override {} + + void EnableForStacktrace() override {} +}; +#endif + bool initialization_in_progress_suppress_logging = true; /** @@ -260,6 +280,9 @@ private: lambda(static_cast<Backend&>(debugger_backend)); lambda(static_cast<Backend&>(color_console_backend)); lambda(static_cast<Backend&>(file_backend)); +#ifdef ANDROID + lambda(static_cast<Backend&>(lc_backend)); +#endif } static void Deleter(Impl* ptr) { @@ -272,6 +295,9 @@ private: DebuggerBackend debugger_backend{}; ColorConsoleBackend color_console_backend{}; FileBackend file_backend; +#ifdef ANDROID + LogcatBackend lc_backend{}; +#endif MPSCQueue<Entry> message_queue{}; std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index 09398ea64..2c453177b 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp @@ -8,6 +8,10 @@ #include <windows.h> #endif +#ifdef ANDROID +#include <android/log.h> +#endif + #include "common/assert.h" #include "common/logging/filter.h" #include "common/logging/log.h" @@ -106,4 +110,35 @@ void PrintColoredMessage(const Entry& entry) { #undef ESC #endif } + +void PrintMessageToLogcat(const Entry& entry) { +#ifdef ANDROID + const auto str = FormatLogMessage(entry); + + android_LogPriority android_log_priority; + switch (entry.log_level) { + case Level::Trace: + android_log_priority = ANDROID_LOG_VERBOSE; + break; + case Level::Debug: + android_log_priority = ANDROID_LOG_DEBUG; + break; + case Level::Info: + android_log_priority = ANDROID_LOG_INFO; + break; + case Level::Warning: + android_log_priority = ANDROID_LOG_WARN; + break; + case Level::Error: + android_log_priority = ANDROID_LOG_ERROR; + break; + case Level::Critical: + android_log_priority = ANDROID_LOG_FATAL; + break; + case Level::Count: + UNREACHABLE(); + } + __android_log_print(android_log_priority, "YuzuNative", "%s", str.c_str()); +#endif +} } // namespace Common::Log diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h index 0d0ec4370..68417420b 100644 --- a/src/common/logging/text_formatter.h +++ b/src/common/logging/text_formatter.h @@ -15,4 +15,6 @@ std::string FormatLogMessage(const Entry& entry); void PrintMessage(const Entry& entry); /// Prints the same message as `PrintMessage`, but colored according to the severity level. void PrintColoredMessage(const Entry& entry); +/// Formats and prints a log entry to the android logcat. +void PrintMessageToLogcat(const Entry& entry); } // namespace Common::Log |