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()