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.
This commit is contained in:
Billy Laws 2021-03-03 20:35:24 +00:00 committed by ◱ Mark
parent a0f6cc161c
commit 63c54207d2
9 changed files with 137 additions and 8 deletions

View File

@ -75,11 +75,13 @@ add_library(skyline SHARED
${source_DIR}/skyline/loader/nca.cpp ${source_DIR}/skyline/loader/nca.cpp
${source_DIR}/skyline/loader/xci.cpp ${source_DIR}/skyline/loader/xci.cpp
${source_DIR}/skyline/loader/nsp.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/partition_filesystem.cpp
${source_DIR}/skyline/vfs/ctr_encrypted_backing.cpp ${source_DIR}/skyline/vfs/ctr_encrypted_backing.cpp
${source_DIR}/skyline/vfs/rom_filesystem.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/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/nacp.cpp
${source_DIR}/skyline/vfs/npdm.cpp ${source_DIR}/skyline/vfs/npdm.cpp
${source_DIR}/skyline/vfs/nca.cpp ${source_DIR}/skyline/vfs/nca.cpp

View File

@ -5,10 +5,12 @@
#include <pthread.h> #include <pthread.h>
#include <unistd.h> #include <unistd.h>
#include <android/log.h> #include <android/log.h>
#include <android/asset_manager_jni.h>
#include "skyline/common.h" #include "skyline/common.h"
#include "skyline/common/signal.h" #include "skyline/common/signal.h"
#include "skyline/common/settings.h" #include "skyline/common/settings.h"
#include "skyline/loader/loader.h" #include "skyline/loader/loader.h"
#include "skyline/vfs/android_asset_filesystem.h"
#include "skyline/os.h" #include "skyline/os.h"
#include "skyline/jvm.h" #include "skyline/jvm.h"
#include "skyline/gpu.h" #include "skyline/gpu.h"
@ -21,7 +23,7 @@ std::weak_ptr<skyline::kernel::OS> OsWeak;
std::weak_ptr<skyline::gpu::GPU> GpuWeak; std::weak_ptr<skyline::gpu::GPU> GpuWeak;
std::weak_ptr<skyline::input::Input> InputWeak; std::weak_ptr<skyline::input::Input> 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 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; Fps = FrameTime = 0;
@ -37,7 +39,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
auto start{std::chrono::steady_clock::now()}; auto start{std::chrono::steady_clock::now()};
try { try {
auto os{std::make_shared<skyline::kernel::OS>(jvmManager, logger, settings, std::string(appFilesPath))}; auto os{std::make_shared<skyline::kernel::OS>(jvmManager, logger, settings, std::string(appFilesPath), std::make_shared<skyline::vfs::AndroidAssetFileSystem>(AAssetManager_fromJava(env, assetManager)))};
OsWeak = os; OsWeak = os;
GpuWeak = os->state.gpu; GpuWeak = os->state.gpu;
InputWeak = os->state.input; InputWeak = os->state.input;

View File

@ -13,7 +13,7 @@
#include "os.h" #include "os.h"
namespace skyline::kernel { namespace skyline::kernel {
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, std::string appFilesPath) : state(this, jvmManager, settings, logger), serviceManager(state), appFilesPath(std::move(appFilesPath)) {} OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, std::string appFilesPath, std::shared_ptr<vfs::FileSystem> assetFileSystem) : state(this, jvmManager, settings, logger), appFilesPath(std::move(appFilesPath)), assetFileSystem(std::move(assetFileSystem)), serviceManager(state) {}
void OS::Execute(int romFd, loader::RomFormat romType) { void OS::Execute(int romFd, loader::RomFormat romType) {
auto romFile{std::make_shared<vfs::OsBacking>(romFd)}; auto romFile{std::make_shared<vfs::OsBacking>(romFd)};

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include "vfs/filesystem.h"
#include "loader/loader.h" #include "loader/loader.h"
#include "services/serviceman.h" #include "services/serviceman.h"
@ -13,15 +14,16 @@ namespace skyline::kernel {
class OS { class OS {
public: public:
DeviceState state; DeviceState state;
service::ServiceManager serviceManager;
std::string appFilesPath; //!< The full path to the app's files directory std::string appFilesPath; //!< The full path to the app's files directory
std::shared_ptr<vfs::FileSystem> assetFileSystem; //!< A filesystem to be used for accessing emulator assets (like tzdata)
service::ServiceManager serviceManager;
/** /**
* @param logger An instance of the Logger class * @param logger An instance of the Logger class
* @param settings An instance of the Settings class * @param settings An instance of the Settings class
* @param window The ANativeWindow object to draw the screen to * @param window The ANativeWindow object to draw the screen to
*/ */
OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, std::string appFilesPath); OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, std::string appFilesPath, std::shared_ptr<vfs::FileSystem>);
/** /**
* @brief Execute a particular ROM file * @brief Execute a particular ROM file

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <unistd.h>
#include <android/asset_manager.h>
#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<u8> 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<size_t>(ret);
}
}

View File

@ -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<u8> output, size_t offset) override;
public:
AndroidAssetBacking(AAsset *backing, Mode mode = {true, false, false});
virtual ~AndroidAssetBacking();
};
}

View File

@ -0,0 +1,35 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <android/asset_manager.h>
#include "android_asset_backing.h"
#include "android_asset_filesystem.h"
namespace skyline::vfs {
AndroidAssetFileSystem::AndroidAssetFileSystem(AAssetManager *backing) : FileSystem(), backing(backing) {}
std::shared_ptr<Backing> 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<AndroidAssetBacking>(file, mode);
}
std::optional<Directory::EntryType> 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<Directory> AndroidAssetFileSystem::OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) {
throw exception("AndroidAssetFileSystem directories are unimplemented");
}
}

View File

@ -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<Backing> OpenFileImpl(const std::string &path, Backing::Mode mode) override;
std::optional<Directory::EntryType> GetEntryTypeImpl(const std::string &path) override;
std::shared_ptr<Directory> OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) override;
public:
AndroidAssetFileSystem(AAssetManager *backing);
};
}

View File

@ -8,6 +8,7 @@ package emu.skyline
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.AssetManager
import android.graphics.PointF import android.graphics.PointF
import android.net.Uri import android.net.Uri
import android.os.* import android.os.*
@ -64,8 +65,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
* @param romFd The file descriptor of the ROM object * @param romFd The file descriptor of the ROM object
* @param preferenceFd The file descriptor of the Preference XML * @param preferenceFd The file descriptor of the Preference XML
* @param appFilesPath The full path to the app files directory * @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 * @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) val preferenceFd = ParcelFileDescriptor.open(File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml"), ParcelFileDescriptor.MODE_READ_WRITE)
emulationThread = Thread { 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) if (shouldFinish)
runOnUiThread { runOnUiThread {
emulationThread.join() emulationThread.join()