From 63c54207d2c1be44781b63f3b58cc7eeea2f8271 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Wed, 3 Mar 2021 20:35:24 +0000 Subject: [PATCH] Add AndroidAsset{Backing,FileSystem} for accessing built-in assets These only implement the subset of VFS needed for time, implementing more is difficult due to some issues in the AAsset API which make support quite ugly. The abstract asset filesystem can be accessed by services through the OS class allowing other implementations to be used in the future. --- app/CMakeLists.txt | 4 ++- app/src/main/cpp/emu_jni.cpp | 6 ++-- app/src/main/cpp/skyline/os.cpp | 2 +- app/src/main/cpp/skyline/os.h | 6 ++-- .../cpp/skyline/vfs/android_asset_backing.cpp | 30 ++++++++++++++++ .../cpp/skyline/vfs/android_asset_backing.h | 28 +++++++++++++++ .../skyline/vfs/android_asset_filesystem.cpp | 35 +++++++++++++++++++ .../skyline/vfs/android_asset_filesystem.h | 28 +++++++++++++++ .../java/emu/skyline/EmulationActivity.kt | 6 ++-- 9 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 app/src/main/cpp/skyline/vfs/android_asset_backing.cpp create mode 100644 app/src/main/cpp/skyline/vfs/android_asset_backing.h create mode 100644 app/src/main/cpp/skyline/vfs/android_asset_filesystem.cpp create mode 100644 app/src/main/cpp/skyline/vfs/android_asset_filesystem.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 7f82c8b9..981068d1 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -75,11 +75,13 @@ add_library(skyline SHARED ${source_DIR}/skyline/loader/nca.cpp ${source_DIR}/skyline/loader/xci.cpp ${source_DIR}/skyline/loader/nsp.cpp - ${source_DIR}/skyline/vfs/os_filesystem.cpp ${source_DIR}/skyline/vfs/partition_filesystem.cpp ${source_DIR}/skyline/vfs/ctr_encrypted_backing.cpp ${source_DIR}/skyline/vfs/rom_filesystem.cpp + ${source_DIR}/skyline/vfs/os_filesystem.cpp ${source_DIR}/skyline/vfs/os_backing.cpp + ${source_DIR}/skyline/vfs/android_asset_filesystem.cpp + ${source_DIR}/skyline/vfs/android_asset_backing.cpp ${source_DIR}/skyline/vfs/nacp.cpp ${source_DIR}/skyline/vfs/npdm.cpp ${source_DIR}/skyline/vfs/nca.cpp diff --git a/app/src/main/cpp/emu_jni.cpp b/app/src/main/cpp/emu_jni.cpp index 01369e8b..3fdf3e19 100644 --- a/app/src/main/cpp/emu_jni.cpp +++ b/app/src/main/cpp/emu_jni.cpp @@ -5,10 +5,12 @@ #include #include #include +#include #include "skyline/common.h" #include "skyline/common/signal.h" #include "skyline/common/settings.h" #include "skyline/loader/loader.h" +#include "skyline/vfs/android_asset_filesystem.h" #include "skyline/os.h" #include "skyline/jvm.h" #include "skyline/gpu.h" @@ -21,7 +23,7 @@ std::weak_ptr OsWeak; std::weak_ptr GpuWeak; std::weak_ptr InputWeak; -extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring) { +extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring, jobject assetManager) { skyline::signal::ScopedStackBlocker stackBlocker; // We do not want anything to unwind past JNI code as there are invalid stack frames which can lead to a segmentation fault Fps = FrameTime = 0; @@ -37,7 +39,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication( auto start{std::chrono::steady_clock::now()}; try { - auto os{std::make_shared(jvmManager, logger, settings, std::string(appFilesPath))}; + auto os{std::make_shared(jvmManager, logger, settings, std::string(appFilesPath), std::make_shared(AAssetManager_fromJava(env, assetManager)))}; OsWeak = os; GpuWeak = os->state.gpu; InputWeak = os->state.input; diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp index 30f0e080..095e4403 100644 --- a/app/src/main/cpp/skyline/os.cpp +++ b/app/src/main/cpp/skyline/os.cpp @@ -13,7 +13,7 @@ #include "os.h" namespace skyline::kernel { - OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings, std::string appFilesPath) : state(this, jvmManager, settings, logger), serviceManager(state), appFilesPath(std::move(appFilesPath)) {} + OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings, std::string appFilesPath, std::shared_ptr assetFileSystem) : state(this, jvmManager, settings, logger), appFilesPath(std::move(appFilesPath)), assetFileSystem(std::move(assetFileSystem)), serviceManager(state) {} void OS::Execute(int romFd, loader::RomFormat romType) { auto romFile{std::make_shared(romFd)}; diff --git a/app/src/main/cpp/skyline/os.h b/app/src/main/cpp/skyline/os.h index 802a565a..9cef2ee7 100644 --- a/app/src/main/cpp/skyline/os.h +++ b/app/src/main/cpp/skyline/os.h @@ -3,6 +3,7 @@ #pragma once +#include "vfs/filesystem.h" #include "loader/loader.h" #include "services/serviceman.h" @@ -13,15 +14,16 @@ namespace skyline::kernel { class OS { public: DeviceState state; - service::ServiceManager serviceManager; std::string appFilesPath; //!< The full path to the app's files directory + std::shared_ptr assetFileSystem; //!< A filesystem to be used for accessing emulator assets (like tzdata) + service::ServiceManager serviceManager; /** * @param logger An instance of the Logger class * @param settings An instance of the Settings class * @param window The ANativeWindow object to draw the screen to */ - OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings, std::string appFilesPath); + OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings, std::string appFilesPath, std::shared_ptr); /** * @brief Execute a particular ROM file diff --git a/app/src/main/cpp/skyline/vfs/android_asset_backing.cpp b/app/src/main/cpp/skyline/vfs/android_asset_backing.cpp new file mode 100644 index 00000000..b6713ed6 --- /dev/null +++ b/app/src/main/cpp/skyline/vfs/android_asset_backing.cpp @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include +#include "android_asset_backing.h" + +namespace skyline::vfs { + AndroidAssetBacking::AndroidAssetBacking(AAsset *backing, Mode mode) : Backing(mode), backing(backing) { + if (mode.write || mode.append) + throw exception("AndroidAssetBacking doesn't support writing"); + + size = AAsset_getLength64(backing); + } + + AndroidAssetBacking::~AndroidAssetBacking() { + AAsset_close(backing); + } + + size_t AndroidAssetBacking::ReadImpl(span output, size_t offset) { + if (AAsset_seek64(backing, offset, SEEK_SET) != offset) + throw exception("Failed to seek asset position"); + + auto ret{AAsset_read(backing, output.data(), output.size())}; + if (ret < 0) + throw exception("Failed to read from fd: {}", strerror(errno)); + + return static_cast(ret); + } +} diff --git a/app/src/main/cpp/skyline/vfs/android_asset_backing.h b/app/src/main/cpp/skyline/vfs/android_asset_backing.h new file mode 100644 index 00000000..3648c4d3 --- /dev/null +++ b/app/src/main/cpp/skyline/vfs/android_asset_backing.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include "backing.h" + +struct AAsset; + +namespace skyline::vfs { + /** + * @brief The AndroidAssetBacking class provides the backing abstractions for the AAsset Android API + * @note This is NOT thread safe NOR should it be shared across threads. + * @note This will take ownership of the backing asset passed into it + */ + class AndroidAssetBacking : public Backing { + private: + AAsset *backing; //!< The backing AAsset object we abstract + + protected: + size_t ReadImpl(span output, size_t offset) override; + + public: + AndroidAssetBacking(AAsset *backing, Mode mode = {true, false, false}); + + virtual ~AndroidAssetBacking(); + }; +} diff --git a/app/src/main/cpp/skyline/vfs/android_asset_filesystem.cpp b/app/src/main/cpp/skyline/vfs/android_asset_filesystem.cpp new file mode 100644 index 00000000..903a932a --- /dev/null +++ b/app/src/main/cpp/skyline/vfs/android_asset_filesystem.cpp @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include "android_asset_backing.h" +#include "android_asset_filesystem.h" + +namespace skyline::vfs { + AndroidAssetFileSystem::AndroidAssetFileSystem(AAssetManager *backing) : FileSystem(), backing(backing) {} + + std::shared_ptr AndroidAssetFileSystem::OpenFileImpl(const std::string &path, Backing::Mode mode) { + auto file{AAssetManager_open(backing, path.c_str(), AASSET_MODE_RANDOM)}; + if (file == nullptr) + return nullptr; + + return std::make_shared(file, mode); + } + + std::optional AndroidAssetFileSystem::GetEntryTypeImpl(const std::string &path) { + // Tries to open as a file then as a directory in order to check the type + // This is really hacky but it at least it works + if (AAssetManager_open(backing, path.c_str(), AASSET_MODE_RANDOM) != nullptr) + return Directory::EntryType::File; + + if (AAssetManager_openDir(backing, path.c_str()) != nullptr) + return Directory::EntryType::Directory; + + // Doesn't exit + return std::nullopt; + } + + std::shared_ptr AndroidAssetFileSystem::OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) { + throw exception("AndroidAssetFileSystem directories are unimplemented"); + } +} diff --git a/app/src/main/cpp/skyline/vfs/android_asset_filesystem.h b/app/src/main/cpp/skyline/vfs/android_asset_filesystem.h new file mode 100644 index 00000000..fee157a9 --- /dev/null +++ b/app/src/main/cpp/skyline/vfs/android_asset_filesystem.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include "filesystem.h" + +struct AAssetManager; + +namespace skyline::vfs { + /** + * @brief The AndroidAssetFileSystem class provides the filesystem backing abstractions for the AAsset Android API + */ + class AndroidAssetFileSystem : public FileSystem { + private: + AAssetManager *backing; //!< The backing manager of the filesystem + + protected: + std::shared_ptr OpenFileImpl(const std::string &path, Backing::Mode mode) override; + + std::optional GetEntryTypeImpl(const std::string &path) override; + + std::shared_ptr OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) override; + + public: + AndroidAssetFileSystem(AAssetManager *backing); + }; +} diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index c1d2c7e2..28d63bb9 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -8,6 +8,7 @@ package emu.skyline import android.annotation.SuppressLint import android.content.Context import android.content.Intent +import android.content.res.AssetManager import android.graphics.PointF import android.net.Uri import android.os.* @@ -64,8 +65,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo * @param romFd The file descriptor of the ROM object * @param preferenceFd The file descriptor of the Preference XML * @param appFilesPath The full path to the app files directory + * @param assetManager The asset manager used for accessing app assets */ - private external fun executeApplication(romUri : String, romType : Int, romFd : Int, preferenceFd : Int, appFilesPath : String) + private external fun executeApplication(romUri : String, romType : Int, romFd : Int, preferenceFd : Int, appFilesPath : String, assetManager : AssetManager) /** * @return If it successfully caused the [emulationThread] to gracefully stop @@ -167,7 +169,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo val preferenceFd = ParcelFileDescriptor.open(File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml"), ParcelFileDescriptor.MODE_READ_WRITE) emulationThread = Thread { - executeApplication(rom.toString(), romType, romFd.detachFd(), preferenceFd.detachFd(), applicationContext.filesDir.canonicalPath + "/") + executeApplication(rom.toString(), romType, romFd.detachFd(), preferenceFd.detachFd(), applicationContext.filesDir.canonicalPath + "/", assets) if (shouldFinish) runOnUiThread { emulationThread.join()