From 6edf89b5388c00e2d9acab089a42e380f059ed5c Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sat, 8 Aug 2020 20:38:51 +0100 Subject: [PATCH] Initial Savedata Implementation (#75) * Rework VFS to support creating and writing files and introduce OsFileSystem OsFileSystem abstracts a directory on the device using the filesystem API. This also introduces GetEntryType and changes FileExists to use it. * Implement the Horizon FileSystem APIs using our VFS framework Horizon provides access to files through its IFileSystem class, we can closely map this to our vfs::FileSystem class. * Add support for creating application savedata This implements basic savedata creation using the OsFileSystem API. The data is stored in Skyline's private directory is stored in the same format as yuzu. --- app/CMakeLists.txt | 2 + app/src/main/cpp/emu_jni.cpp | 12 ++- app/src/main/cpp/skyline/common.cpp | 4 +- app/src/main/cpp/skyline/common.h | 6 +- app/src/main/cpp/skyline/os.cpp | 2 +- app/src/main/cpp/skyline/os.h | 3 +- .../main/cpp/skyline/services/base_service.h | 1 + .../main/cpp/skyline/services/fssrv/IFile.cpp | 73 +++++++++++++++ .../main/cpp/skyline/services/fssrv/IFile.h | 41 +++++++++ .../skyline/services/fssrv/IFileSystem.cpp | 49 ++++++++++- .../cpp/skyline/services/fssrv/IFileSystem.h | 37 +++++--- .../services/fssrv/IFileSystemProxy.cpp | 51 ++++++++++- .../skyline/services/fssrv/IFileSystemProxy.h | 58 ++++++++++++ .../cpp/skyline/services/fssrv/IStorage.cpp | 16 +++- app/src/main/cpp/skyline/vfs/backing.h | 32 +++++++ app/src/main/cpp/skyline/vfs/filesystem.h | 48 +++++++++- app/src/main/cpp/skyline/vfs/nacp.h | 6 +- app/src/main/cpp/skyline/vfs/os_backing.cpp | 28 +++++- app/src/main/cpp/skyline/vfs/os_backing.h | 9 +- .../main/cpp/skyline/vfs/os_filesystem.cpp | 88 +++++++++++++++++++ app/src/main/cpp/skyline/vfs/os_filesystem.h | 27 ++++++ .../cpp/skyline/vfs/partition_filesystem.cpp | 7 +- .../cpp/skyline/vfs/partition_filesystem.h | 2 +- .../main/cpp/skyline/vfs/rom_filesystem.cpp | 21 +++-- app/src/main/cpp/skyline/vfs/rom_filesystem.h | 2 +- .../java/emu/skyline/EmulationActivity.kt | 19 ++-- app/src/main/java/emu/skyline/LogActivity.kt | 2 +- app/src/main/java/emu/skyline/MainActivity.kt | 4 +- 28 files changed, 585 insertions(+), 65 deletions(-) create mode 100644 app/src/main/cpp/skyline/services/fssrv/IFile.cpp create mode 100644 app/src/main/cpp/skyline/services/fssrv/IFile.h create mode 100644 app/src/main/cpp/skyline/vfs/os_filesystem.cpp create mode 100644 app/src/main/cpp/skyline/vfs/os_filesystem.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index bc88b3b1..b4c29c98 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -97,6 +97,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/timesrv/ITimeZoneService.cpp ${source_DIR}/skyline/services/fssrv/IFileSystemProxy.cpp ${source_DIR}/skyline/services/fssrv/IFileSystem.cpp + ${source_DIR}/skyline/services/fssrv/IFile.cpp ${source_DIR}/skyline/services/fssrv/IStorage.cpp ${source_DIR}/skyline/services/nvdrv/INvDrvServices.cpp ${source_DIR}/skyline/services/nvdrv/devices/nvmap.cpp @@ -129,6 +130,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/socket/bsd/IClient.cpp ${source_DIR}/skyline/services/ssl/ISslService.cpp ${source_DIR}/skyline/services/prepo/IPrepoService.cpp + ${source_DIR}/skyline/vfs/os_filesystem.cpp ${source_DIR}/skyline/vfs/partition_filesystem.cpp ${source_DIR}/skyline/vfs/rom_filesystem.cpp ${source_DIR}/skyline/vfs/os_backing.cpp diff --git a/app/src/main/cpp/emu_jni.cpp b/app/src/main/cpp/emu_jni.cpp index 971025d2..84b9b07c 100644 --- a/app/src/main/cpp/emu_jni.cpp +++ b/app/src/main/cpp/emu_jni.cpp @@ -24,7 +24,7 @@ void signalHandler(int signal) { FaultCount++; } -extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jint logFd) { +extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring) { Halt = false; FaultCount = 0; fps = 0; @@ -41,13 +41,17 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication( auto jvmManager = std::make_shared(env, instance); auto settings = std::make_shared(preferenceFd); - auto logger = std::make_shared(logFd, static_cast(std::stoi(settings->GetString("log_level")))); + + auto appFilesPath = env->GetStringUTFChars(appFilesPathJstring, nullptr); + auto logger = std::make_shared(std::string(appFilesPath) + "skyline.log", static_cast(std::stoi(settings->GetString("log_level")))); //settings->List(logger); // (Uncomment when you want to print out all settings strings) auto start = std::chrono::steady_clock::now(); try { - skyline::kernel::OS os(jvmManager, logger, settings); + skyline::kernel::OS os(jvmManager, logger, settings, std::string(appFilesPath)); + env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath); + auto romUri = env->GetStringUTFChars(romUriJstring, nullptr); logger->Info("Launching ROM {}", romUri); env->ReleaseStringUTFChars(romUriJstring, romUri); @@ -86,4 +90,4 @@ extern "C" JNIEXPORT jint Java_emu_skyline_EmulationActivity_getFps(JNIEnv *env, extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIEnv *env, jobject thiz) { return static_cast(frametime) / 100; -} \ No newline at end of file +} diff --git a/app/src/main/cpp/skyline/common.cpp b/app/src/main/cpp/skyline/common.cpp index 0b6cd364..461cf417 100644 --- a/app/src/main/cpp/skyline/common.cpp +++ b/app/src/main/cpp/skyline/common.cpp @@ -120,8 +120,8 @@ namespace skyline { logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first)); } - Logger::Logger(int fd, LogLevel configLevel) : configLevel(configLevel) { - logFile.__open(fd, std::ios::app); + Logger::Logger(const std::string &path, LogLevel configLevel) : configLevel(configLevel) { + logFile.open(path, std::ios::app); WriteHeader("Logging started"); } diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index 68ff61f3..b4b7e809 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -38,6 +38,8 @@ namespace skyline { // Status codes namespace status { constexpr u32 Success = 0x0; //!< "Success" + constexpr u32 PathDoesNotExist = 0x202; //!< "Path does not exist" + constexpr u32 GenericError = 0x272; //!< "Generic error" constexpr u32 NoMessages = 0x680; //!< "No message available" constexpr u32 ServiceInvName = 0xC15; //!< "Invalid name" constexpr u32 ServiceNotReg = 0xE15; //!< "Service not registered" @@ -220,10 +222,10 @@ namespace skyline { LogLevel configLevel; //!< The level of logs to write /** - * @param fd A FD to the log file + * @param path The path of the log file * @param configLevel The minimum level of logs to write */ - Logger(int fd, LogLevel configLevel); + Logger(const std::string &path, LogLevel configLevel); /** * @brief Writes the termination message to the log file diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp index bf23f6ac..c17d2935 100644 --- a/app/src/main/cpp/skyline/os.cpp +++ b/app/src/main/cpp/skyline/os.cpp @@ -10,7 +10,7 @@ #include "os.h" namespace skyline::kernel { - OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings) : state(this, process, jvmManager, settings, logger), memory(state), serviceManager(state) {} + OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings, const std::string &appFilesPath) : state(this, process, jvmManager, settings, logger), memory(state), serviceManager(state), appFilesPath(appFilesPath) {} 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 7983c660..9f937864 100644 --- a/app/src/main/cpp/skyline/os.h +++ b/app/src/main/cpp/skyline/os.h @@ -25,13 +25,14 @@ namespace skyline::kernel { std::shared_ptr process; //!< The KProcess object for the emulator, representing the guest process service::ServiceManager serviceManager; //!< This manages all of the service functions MemoryManager memory; //!< The MemoryManager object for this process + std::string appFilesPath; //!< The full path to the app's files directory /** * @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); + OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings, const std::string &appFilesPath); /** * @brief Execute a particular ROM file. This launches the main process and calls the NCE class to handle execution. diff --git a/app/src/main/cpp/skyline/services/base_service.h b/app/src/main/cpp/skyline/services/base_service.h index 861a7fa4..dd354a37 100644 --- a/app/src/main/cpp/skyline/services/base_service.h +++ b/app/src/main/cpp/skyline/services/base_service.h @@ -58,6 +58,7 @@ namespace skyline::service { timesrv_ISteadyClock, fssrv_IFileSystemProxy, fssrv_IFileSystem, + fssrv_IFile, fssrv_IStorage, nvdrv_INvDrvServices, visrv_IManagerRootService, diff --git a/app/src/main/cpp/skyline/services/fssrv/IFile.cpp b/app/src/main/cpp/skyline/services/fssrv/IFile.cpp new file mode 100644 index 00000000..920ed54b --- /dev/null +++ b/app/src/main/cpp/skyline/services/fssrv/IFile.cpp @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include "IFile.h" + +namespace skyline::service::fssrv { + IFile::IFile(std::shared_ptr &backing, const DeviceState &state, ServiceManager &manager) : backing(backing), BaseService(state, manager, Service::fssrv_IFile, "fssrv:IFile", { + {0x0, SFUNC(IFile::Read)}, + {0x1, SFUNC(IFile::Write)}, + {0x3, SFUNC(IFile::SetSize)}, + {0x4, SFUNC(IFile::GetSize)} + }) {} + + void IFile::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto readOption = request.Pop(); + request.Skip(); + auto offset = request.Pop(); + auto size = request.Pop(); + + if (offset < 0) { + state.logger->Warn("Trying to read a file with a negative offset"); + response.errorCode = constant::status::InvAddress; + return; + } + + if (size < 0) { + state.logger->Warn("Trying to read a file with a negative size"); + response.errorCode = constant::status::InvSize; + return; + } + + response.Push(static_cast(backing->Read(state.process->GetPointer(request.outputBuf.at(0).address), offset, size))); + } + + void IFile::Write(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto writeOption = request.Pop(); + request.Skip(); + auto offset = request.Pop(); + auto size = request.Pop(); + + if (offset < 0) { + state.logger->Warn("Trying to write to a file with a negative offset"); + response.errorCode = constant::status::InvAddress; + return; + } + + if (size < 0) { + state.logger->Warn("Trying to write to a file with a negative size"); + response.errorCode = constant::status::InvSize; + return; + } + + if (request.inputBuf.at(0).size < size) { + state.logger->Warn("The input buffer is not large enough to fit the requested size"); + response.errorCode = constant::status::InvSize; + return; + } + + if (backing->Write(state.process->GetPointer(request.inputBuf.at(0).address), offset, request.inputBuf.at(0).size) != size) { + state.logger->Warn("Failed to write all data to the backing"); + response.errorCode = constant::status::GenericError; + } + } + + void IFile::SetSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + backing->Resize(request.Pop()); + } + + void IFile::GetSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + response.Push(backing->size); + } +} diff --git a/app/src/main/cpp/skyline/services/fssrv/IFile.h b/app/src/main/cpp/skyline/services/fssrv/IFile.h new file mode 100644 index 00000000..fc872041 --- /dev/null +++ b/app/src/main/cpp/skyline/services/fssrv/IFile.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include +#include + +namespace skyline::service::fssrv { + /** + * @brief IFile is an interface for accessing files (https://switchbrew.org/wiki/Filesystem_services#IFile) + */ + class IFile : public BaseService { + private: + std::shared_ptr backing; //!< The backing of the IFile + + public: + IFile(std::shared_ptr &backing, const DeviceState &state, ServiceManager &manager); + + /** + * @brief This reads a buffer from a region of an IFile + */ + void Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This writes a buffer to a region of an IFile + */ + void Write(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This sets the size of an IFile + */ + void SetSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This obtains the size of an IFile + */ + void GetSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +} diff --git a/app/src/main/cpp/skyline/services/fssrv/IFileSystem.cpp b/app/src/main/cpp/skyline/services/fssrv/IFileSystem.cpp index 63fdd30e..8a8e9641 100644 --- a/app/src/main/cpp/skyline/services/fssrv/IFileSystem.cpp +++ b/app/src/main/cpp/skyline/services/fssrv/IFileSystem.cpp @@ -1,8 +1,55 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +#include +#include +#include "IFile.h" #include "IFileSystem.h" namespace skyline::service::fssrv { - IFileSystem::IFileSystem(FsType type, const DeviceState &state, ServiceManager &manager) : type(type), BaseService(state, manager, Service::fssrv_IFileSystem, "fssrv:IFileSystem", {}) {} + IFileSystem::IFileSystem(std::shared_ptr backing, const DeviceState &state, ServiceManager &manager) : backing(backing), BaseService(state, manager, Service::fssrv_IFileSystem, "fssrv:IFileSystem", { + {0x0, SFUNC(IFileSystem::CreateFile)}, + {0x7, SFUNC(IFileSystem::GetEntryType)}, + {0x8, SFUNC(IFileSystem::OpenFile)}, + {0xa, SFUNC(IFileSystem::Commit)} + }) {} + + void IFileSystem::CreateFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + std::string path = std::string(state.process->GetPointer(request.inputBuf.at(0).address)); + auto mode = request.Pop(); + auto size = request.Pop(); + + response.errorCode = backing->CreateFile(path, size) ? constant::status::Success : constant::status::PathDoesNotExist; + } + + void IFileSystem::GetEntryType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + std::string path = std::string(state.process->GetPointer(request.inputBuf.at(0).address)); + + auto type = backing->GetEntryType(path); + + if (type.has_value()) { + response.Push(type.value()); + } else { + response.Push(0); + response.errorCode = constant::status::PathDoesNotExist; + } + } + + void IFileSystem::OpenFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + std::string path = std::string(state.process->GetPointer(request.inputBuf.at(0).address)); + auto mode = request.Pop(); + + if (!backing->FileExists(path)) { + response.errorCode = constant::status::PathDoesNotExist; + return; + } + + auto file = backing->OpenFile(path, mode); + if (file == nullptr) + response.errorCode = constant::status::GenericError; + else + manager.RegisterService(std::make_shared(file, state, manager), session, response); + } + + void IFileSystem::Commit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {} } diff --git a/app/src/main/cpp/skyline/services/fssrv/IFileSystem.h b/app/src/main/cpp/skyline/services/fssrv/IFileSystem.h index 34f64683..8ed971b4 100644 --- a/app/src/main/cpp/skyline/services/fssrv/IFileSystem.h +++ b/app/src/main/cpp/skyline/services/fssrv/IFileSystem.h @@ -3,26 +3,39 @@ #pragma once +#include #include #include namespace skyline::service::fssrv { - /** - * @brief These are the possible types of the filesystem - */ - enum class FsType { - Nand, //!< The internal NAND storage - SdCard, //!< The external SDCard storage - GameCard, //!< The Game-Card of the inserted game (https://switchbrew.org/wiki/Gamecard) - }; - /** * @brief IFileSystem is used to interact with a filesystem (https://switchbrew.org/wiki/Filesystem_services#IFileSystem) */ class IFileSystem : public BaseService { - public: - FsType type; //!< The type of filesystem this class represents + private: + std::shared_ptr backing; - IFileSystem(FsType type, const DeviceState &state, ServiceManager &manager); + public: + IFileSystem(std::shared_ptr backing, const DeviceState &state, ServiceManager &manager); + + /** + * @brief This creates a file at the specified path in the filesystem + */ + void CreateFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This queries the DirectoryEntryType of the given path (https://switchbrew.org/wiki/Filesystem_services#GetEntryType) + */ + void GetEntryType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This returns an IFile handle for the requested path (https://switchbrew.org/wiki/Filesystem_services#OpenFile) + */ + void OpenFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This commits all changes to the filesystem (https://switchbrew.org/wiki/Filesystem_services#Commit) + */ + void Commit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); }; } diff --git a/app/src/main/cpp/skyline/services/fssrv/IFileSystemProxy.cpp b/app/src/main/cpp/skyline/services/fssrv/IFileSystemProxy.cpp index 4221b64f..22a9f100 100644 --- a/app/src/main/cpp/skyline/services/fssrv/IFileSystemProxy.cpp +++ b/app/src/main/cpp/skyline/services/fssrv/IFileSystemProxy.cpp @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +#include +#include #include #include "IFileSystemProxy.h" #include "IStorage.h" @@ -9,7 +11,9 @@ namespace skyline::service::fssrv { IFileSystemProxy::IFileSystemProxy(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::fssrv_IFileSystemProxy, "fssrv:IFileSystemProxy", { {0x1, SFUNC(IFileSystemProxy::SetCurrentProcess)}, {0x12, SFUNC(IFileSystemProxy::OpenSdCardFileSystem)}, - {0xc8, SFUNC(IFileSystemProxy::OpenDataStorageByCurrentProcess)} + {0x33, SFUNC(IFileSystemProxy::OpenSaveDataFileSystem)}, + {0xc8, SFUNC(IFileSystemProxy::OpenDataStorageByCurrentProcess)}, + {0x3ed, SFUNC(IFileSystemProxy::GetGlobalAccessLogMode)}, }) {} void IFileSystemProxy::SetCurrentProcess(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { @@ -17,7 +21,46 @@ namespace skyline::service::fssrv { } void IFileSystemProxy::OpenSdCardFileSystem(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - manager.RegisterService(std::make_shared(FsType::SdCard, state, manager), session, response); + manager.RegisterService(std::make_shared(std::make_shared(state.os->appFilesPath + "/switch/sdmc/"), state, manager), session, response); + } + + void IFileSystemProxy::OpenSaveDataFileSystem(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto spaceId = request.Pop(); + auto attribute = request.Pop(); + + if (attribute.programId == 0) + attribute.programId = state.loader->nacp->nacpContents.saveDataOwnerId; + + std::string saveDataPath = [spaceId, &attribute] () { + std::string spaceIdStr = [spaceId] () { + switch (spaceId) { + case SaveDataSpaceId::System: + return "/nand/system"; + case SaveDataSpaceId::User: + return "/nand/user"; + case SaveDataSpaceId::Temporary: + return "/nand/temp"; + default: + throw exception("Unsupported savedata ID: {}", spaceId); + }; + } (); + + switch (attribute.type) { + case SaveDataType::System: + return fmt::format("{}/save/{:016X}/{:016X}{:016X}/", spaceIdStr, attribute.saveDataId, attribute.userId.lower, attribute.userId.upper); + case SaveDataType::Account: + case SaveDataType::Device: + return fmt::format("{}/save/{:016X}/{:016X}{:016X}/{:016X}/", spaceIdStr, 0, attribute.userId.lower, attribute.userId.upper, attribute.programId); + case SaveDataType::Temporary: + return fmt::format("{}/{:016X}/{:016X}{:016X}/{:016X}/", spaceIdStr, 0, attribute.userId.lower, attribute.userId.upper, attribute.programId); + case SaveDataType::Cache: + return fmt::format("{}/save/cache/{:016X}/", spaceIdStr, attribute.programId); + default: + throw exception("Unsupported savedata type: {}", attribute.type); + }; + } (); + + manager.RegisterService(std::make_shared(std::make_shared(state.os->appFilesPath + "/switch" + saveDataPath), state, manager), session, response); } void IFileSystemProxy::OpenDataStorageByCurrentProcess(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { @@ -26,4 +69,8 @@ namespace skyline::service::fssrv { else throw exception("Tried to call OpenDataStorageByCurrentProcess without a valid RomFS"); } + + void IFileSystemProxy::GetGlobalAccessLogMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + response.Push(0); + } } diff --git a/app/src/main/cpp/skyline/services/fssrv/IFileSystemProxy.h b/app/src/main/cpp/skyline/services/fssrv/IFileSystemProxy.h index ecd4beed..6985ae26 100644 --- a/app/src/main/cpp/skyline/services/fssrv/IFileSystemProxy.h +++ b/app/src/main/cpp/skyline/services/fssrv/IFileSystemProxy.h @@ -5,9 +5,57 @@ #include #include +#include #include "IFileSystem.h" namespace skyline::service::fssrv { + /** + * @brief This enumerates the possible locations savedata can be stored in + */ + enum class SaveDataSpaceId : u64 { + System = 0, //!< Savedata should be stored in the EMMC system folder + User = 1, //!< Savedata should be stored in the EMMC user folder + SdSystem = 2, //!< Savedata should be stored in the SDCard system folder + Temporary = 3, //!< Savedata should be stored in a temporary folder + SdCache = 4, //!< Savedata should be stored in the SDCard system folder + ProperSystem = 100, //!< Savedata should be stored in the system partition + }; + + /** + * @brief This enumerates the types of savedata + */ + enum class SaveDataType : u8 { + System = 0, //!< This is system savedata + Account = 1, //!< This is user game savedata + Bcat = 2, //!< This is user bcat savedata + Device = 3, //!< This is device-wide savedata + Temporary = 4, //!< This is temporary savedata + Cache = 5, //!< This is cache savedata + SystemBcat = 6, //!< This is device-wide bcat savedata + }; + + /** + * @brief This enumerates the ranks of savedata + */ + enum class SaveDataRank : u8 { + Primary, //!< This is primary savedata + Secondary, //!< This is secondary savedata + }; + + /** + * @brief This stores the attributes of a savedata entry + */ + struct SaveDataAttribute { + u64 programId; //!< The program ID to store the savedata contents under + account::UserId userId; //!< The user ID of whom the applications savedata contents should be stored under + u64 saveDataId; //!< The ID of the savedata + SaveDataType type; //!< The type of savedata + SaveDataRank rank; //!< The rank of the savedata + u16 index; //!< The index of the savedata + u8 _pad_[0x1a]; + }; + static_assert(sizeof(SaveDataAttribute) == 0x40); + /** * @brief IFileSystemProxy or fsp-srv is responsible for providing handles to file systems (https://switchbrew.org/wiki/Filesystem_services#fsp-srv) */ @@ -27,9 +75,19 @@ namespace skyline::service::fssrv { */ void OpenSdCardFileSystem(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** + * @brief This returns a handle to an instance of #IFileSystem (https://switchbrew.org/wiki/Filesystem_services#IFileSystem) for the requested save data area + */ + void OpenSaveDataFileSystem(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** * @brief This returns a handle to an instance of #IStorage (https://switchbrew.org/wiki/Filesystem_services#IStorage) for the application's data storage */ void OpenDataStorageByCurrentProcess(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This returns the filesystem log access mode (https://switchbrew.org/wiki/Filesystem_services#GetGlobalAccessLogMode) + */ + void GetGlobalAccessLogMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); }; } diff --git a/app/src/main/cpp/skyline/services/fssrv/IStorage.cpp b/app/src/main/cpp/skyline/services/fssrv/IStorage.cpp index a0968a9b..dae528cb 100644 --- a/app/src/main/cpp/skyline/services/fssrv/IStorage.cpp +++ b/app/src/main/cpp/skyline/services/fssrv/IStorage.cpp @@ -11,8 +11,20 @@ namespace skyline::service::fssrv { }) {} void IStorage::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto offset = request.Pop(); - auto size = request.Pop(); + auto offset = request.Pop(); + auto size = request.Pop(); + + if (offset < 0) { + state.logger->Warn("Trying to read a file with a negative offset"); + response.errorCode = constant::status::InvAddress; + return; + } + + if (size < 0) { + state.logger->Warn("Trying to read a file with a negative size"); + response.errorCode = constant::status::InvSize; + return; + } backing->Read(state.process->GetPointer(request.outputBuf.at(0).address), offset, size); } diff --git a/app/src/main/cpp/skyline/vfs/backing.h b/app/src/main/cpp/skyline/vfs/backing.h index d0f76b9a..873098ee 100644 --- a/app/src/main/cpp/skyline/vfs/backing.h +++ b/app/src/main/cpp/skyline/vfs/backing.h @@ -61,5 +61,37 @@ namespace skyline::vfs { inline size_t Read(T *output, size_t offset = 0, size_t size = 0) { return Read(reinterpret_cast(output), offset, size ? size : sizeof(T)); } + + /** + * @brief Writes from a buffer to a particular offset in the backing + * @param input The object to write to the backing + * @param offset The offset where the input buffer should be written + * @param size The amount to write + * @return The amount of bytes written + */ + virtual size_t Write(u8 *input, size_t offset, size_t size) { + throw exception("This backing does not support being written to"); + } + + /** + * @brief Writes from a buffer to a particular offset in the backing (template version) + * @tparam T The type of object to write + * @param input The object to write to the backing + * @param offset The offset where the input buffer should be written + * @param size The amount to write + * @return The amount of bytes written + */ + template + inline size_t Write(T *output, size_t offset = 0, size_t size = 0) { + return Write(reinterpret_cast(output), offset, size ? size : sizeof(T)); + } + + /** + * @brief Resizes a backing to the given size + * @param size The new size for the backing + */ + virtual void Resize(size_t size) { + throw exception("This backing does not support being resized"); + } }; } diff --git a/app/src/main/cpp/skyline/vfs/filesystem.h b/app/src/main/cpp/skyline/vfs/filesystem.h index b7240d5d..ce47923f 100644 --- a/app/src/main/cpp/skyline/vfs/filesystem.h +++ b/app/src/main/cpp/skyline/vfs/filesystem.h @@ -22,6 +22,26 @@ namespace skyline::vfs { virtual ~FileSystem() = default; + /** + * @brief Creates a file in the filesystem with the requested size + * @param path The path where the file should be created + * @param size The size of the file to create + * @return Whether creating the file succeeded + */ + virtual bool CreateFile(std::string path, size_t size) { + throw exception("This filesystem does not support creating files"); + }; + + /** + * @brief Creates a directory in the filesystem + * @param path The path to where the directory should be created + * @param parents Whether all parent directories in the given path should be created + * @return Whether creating the directory succeeded + */ + virtual bool CreateDirectory(std::string path, bool parents) { + throw exception("This filesystem does not support creating directories"); + }; + /** * @brief Opens a file from the specified path in the filesystem * @param path The path to the file @@ -30,12 +50,32 @@ namespace skyline::vfs { */ virtual std::shared_ptr OpenFile(std::string path, Backing::Mode mode = {true, false, false}) = 0; + /** + * @brief Queries the type of the entry given by path + * @param path The path to the entry + * @return The type of the entry, if present + */ + virtual std::optional GetEntryType(std::string path) = 0; + /** * @brief Checks if a given file exists in a filesystem * @param path The path to the file - * @return A boolean containing whether the file exists + * @return Whether the file exists */ - virtual bool FileExists(std::string path) = 0; + inline bool FileExists(std::string path) { + auto entry = GetEntryType(path); + return entry.has_value() && entry.value() == Directory::EntryType::File; + } + + /** + * @brief Checks if a given directory exists in a filesystem + * @param path The path to the directory + * @return Whether the directory exists + */ + inline bool DirectoryExists(std::string path) { + auto entry = GetEntryType(path); + return entry.has_value() && entry.value() == Directory::EntryType::Directory; + } /** * @brief Opens a directory from the specified path in the filesystem @@ -43,6 +83,8 @@ namespace skyline::vfs { * @param listMode The list mode for the directory * @return A shared pointer to a Directory object of the directory */ - virtual std::shared_ptr OpenDirectory(std::string path, Directory::ListMode listMode) = 0; + virtual std::shared_ptr OpenDirectory(std::string path, Directory::ListMode listMode) { + throw exception("This filesystem does not support opening directories"); + }; }; } diff --git a/app/src/main/cpp/skyline/vfs/nacp.h b/app/src/main/cpp/skyline/vfs/nacp.h index 63b1b5a3..8a4f2cee 100644 --- a/app/src/main/cpp/skyline/vfs/nacp.h +++ b/app/src/main/cpp/skyline/vfs/nacp.h @@ -22,16 +22,18 @@ namespace skyline::vfs { }; static_assert(sizeof(ApplicationTitle) == 0x300); + public: /** * @brief This struct encapsulates all the data within an NACP file */ struct NacpData { std::array titleEntries; //!< Title entries for each language - u8 _pad_[0x4000 - (0x10 * 0x300)]; + u8 _pad0_[0x78]; + u64 saveDataOwnerId; //!< The ID that should be used for this application's savedata + u8 _pad1_[0xf80]; } nacpContents{}; static_assert(sizeof(NacpData) == 0x4000); - public: /** * @param backing The backing for the NACP */ diff --git a/app/src/main/cpp/skyline/vfs/os_backing.cpp b/app/src/main/cpp/skyline/vfs/os_backing.cpp index 6bd9e1af..f46192f8 100644 --- a/app/src/main/cpp/skyline/vfs/os_backing.cpp +++ b/app/src/main/cpp/skyline/vfs/os_backing.cpp @@ -7,24 +7,46 @@ #include "os_backing.h" namespace skyline::vfs { - OsBacking::OsBacking(int fd) : Backing(), fd(fd) { + OsBacking::OsBacking(int fd, bool closable, Mode mode) : Backing(mode), fd(fd), closable(closable) { struct stat fileInfo; - if (fstat(fd, &fileInfo)) throw exception("Failed to stat fd: {}", strerror(errno)); size = fileInfo.st_size; } + OsBacking::~OsBacking() { + if (closable) + close(fd); + } + size_t OsBacking::Read(u8 *output, size_t offset, size_t size) { if (!mode.read) throw exception("Attempting to read a backing that is not readable"); auto ret = pread64(fd, output, size, offset); - if (ret < 0) throw exception("Failed to read from fd: {}", strerror(errno)); return static_cast(ret); } + + size_t OsBacking::Write(u8 *output, size_t offset, size_t size) { + if (!mode.write) + throw exception("Attempting to write to a backing that is not writable"); + + auto ret = pwrite64(fd, output, size, offset); + if (ret < 0) + throw exception("Failed to write to fd: {}", strerror(errno)); + + return static_cast(ret); + } + + void OsBacking::Resize(size_t size) { + int ret = ftruncate(fd, size); + if (ret < 0) + throw exception("Failed to resize file: {}", strerror(errno)); + + this->size = size; + } } \ No newline at end of file diff --git a/app/src/main/cpp/skyline/vfs/os_backing.h b/app/src/main/cpp/skyline/vfs/os_backing.h index 7592bf82..f92a7adf 100644 --- a/app/src/main/cpp/skyline/vfs/os_backing.h +++ b/app/src/main/cpp/skyline/vfs/os_backing.h @@ -12,13 +12,20 @@ namespace skyline::vfs { class OsBacking : public Backing { private: int fd; //!< An FD to the backing + bool closable; //!< Whether the FD can be closed when the backing is destroyed public: /** * @param fd The file descriptor of the backing */ - OsBacking(int fd); + OsBacking(int fd, bool closable = false, Mode = {true, false, false}); + + ~OsBacking(); size_t Read(u8 *output, size_t offset, size_t size); + + size_t Write(u8 *output, size_t offset, size_t size); + + void Resize(size_t size); }; } \ No newline at end of file diff --git a/app/src/main/cpp/skyline/vfs/os_filesystem.cpp b/app/src/main/cpp/skyline/vfs/os_filesystem.cpp new file mode 100644 index 00000000..f357dd93 --- /dev/null +++ b/app/src/main/cpp/skyline/vfs/os_filesystem.cpp @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include + +#include +#include +#include "os_backing.h" +#include "os_filesystem.h" + +namespace skyline::vfs { + OsFileSystem::OsFileSystem(std::string basePath) : FileSystem(), basePath(basePath) { + if (!DirectoryExists(basePath)) + if (!CreateDirectory(basePath, true)) + throw exception("Error creating the OS filesystem backing directory"); + } + + bool OsFileSystem::CreateFile(std::string path, size_t size) { + auto fullPath = basePath + path; + + // Create a directory that will hold the file + CreateDirectory(fullPath.substr(0, fullPath.find_last_of('/')), true); + int fd = open(fullPath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) { + if (errno != ENOENT) + throw exception("Failed to create file: {}", strerror(errno)); + else + return false; + } + + // Truncate the file to desired length + int ret = ftruncate(fd, size); + close(fd); + + if (ret < 0) + throw exception("Failed to resize created file: {}", strerror(errno)); + + return true; + } + + bool OsFileSystem::CreateDirectory(std::string path, bool parents) { + if (!parents) { + int ret = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + return ret == 0 || errno == EEXIST; + } + + for (auto dir = basePath.begin(); dir != basePath.end(); dir++) { + auto nextDir = std::find(dir, basePath.end(), '/'); + auto nextPath = "/" + std::string(basePath.begin(), nextDir); + + int ret = mkdir(nextPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (ret < 0 && errno != EEXIST && errno != EPERM) + return false; + + dir = nextDir; + } + + return true; + } + + + std::shared_ptr OsFileSystem::OpenFile(std::string path, Backing::Mode mode) { + if (!(mode.read || mode.write)) + throw exception("Cannot open a file that is neither readable or writable"); + + int fd = open((basePath + path).c_str(), (mode.read && mode.write) ? O_RDWR : (mode.write ? O_WRONLY : O_RDONLY)); + if (fd < 0) + throw exception("Failed to open file: {}", strerror(errno)); + + return std::make_shared(fd, true, mode); + } + + std::optional OsFileSystem::GetEntryType(std::string path) { + auto fullPath = basePath + path; + + auto directory = opendir(fullPath.c_str()); + if (directory) { + closedir(directory); + return Directory::EntryType::Directory; + } + + if (access(fullPath.c_str(), F_OK) != -1) + return Directory::EntryType::File; + + return std::nullopt; + } +} diff --git a/app/src/main/cpp/skyline/vfs/os_filesystem.h b/app/src/main/cpp/skyline/vfs/os_filesystem.h new file mode 100644 index 00000000..015c7737 --- /dev/null +++ b/app/src/main/cpp/skyline/vfs/os_filesystem.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include "filesystem.h" + +namespace skyline::vfs { + /** + * @brief The OsFileSystem class abstracts an OS folder with the vfs::FileSystem api + */ + class OsFileSystem : public FileSystem { + private: + std::string basePath; //!< The base path for filesystem operations + + public: + OsFileSystem(std::string basePath); + + bool CreateFile(std::string path, size_t size); + + bool CreateDirectory(std::string path, bool parents); + + std::shared_ptr OpenFile(std::string path, Backing::Mode mode = {true, false, false}); + + std::optional GetEntryType(std::string path); + }; +} diff --git a/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp b/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp index ee1516b5..ec3e648b 100644 --- a/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp +++ b/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp @@ -40,8 +40,11 @@ namespace skyline::vfs { } } - bool PartitionFileSystem::FileExists(std::string path) { - return fileMap.count(path); + std::optional PartitionFileSystem::GetEntryType(std::string path) { + if (fileMap.count(path)) + return Directory::EntryType::File; + + return std::nullopt; } std::shared_ptr PartitionFileSystem::OpenDirectory(std::string path, Directory::ListMode listMode) { diff --git a/app/src/main/cpp/skyline/vfs/partition_filesystem.h b/app/src/main/cpp/skyline/vfs/partition_filesystem.h index 86a7bb32..dd70cd1c 100644 --- a/app/src/main/cpp/skyline/vfs/partition_filesystem.h +++ b/app/src/main/cpp/skyline/vfs/partition_filesystem.h @@ -54,7 +54,7 @@ namespace skyline::vfs { std::shared_ptr OpenFile(std::string path, Backing::Mode mode = {true, false, false}); - bool FileExists(std::string path); + std::optional GetEntryType(std::string path); std::shared_ptr OpenDirectory(std::string path, Directory::ListMode listMode); }; diff --git a/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp b/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp index 35b646cd..2e2f6ed1 100644 --- a/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp +++ b/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp @@ -5,7 +5,7 @@ #include "rom_filesystem.h" namespace skyline::vfs { - RomFileSystem::RomFileSystem(std::shared_ptr backing) : FileSystem(), backing(backing) { + RomFileSystem::RomFileSystem(std::shared_ptr backing) : FileSystem(), backing(backing) { backing->Read(&header); TraverseDirectory(0, ""); @@ -52,7 +52,7 @@ namespace skyline::vfs { TraverseDirectory(entry.siblingOffset, path); } - std::shared_ptr RomFileSystem::OpenFile(std::string path, Backing::Mode mode) { + std::shared_ptr RomFileSystem::OpenFile(std::string path, Backing::Mode mode) { try { const auto &entry = fileMap.at(path); return std::make_shared(backing, header.dataOffset + entry.offset, entry.size, mode); @@ -61,11 +61,16 @@ namespace skyline::vfs { } } - bool RomFileSystem::FileExists(std::string path) { - return fileMap.count(path); + std::optional RomFileSystem::GetEntryType(std::string path) { + if (fileMap.count(path)) + return Directory::EntryType::File; + else if (directoryMap.count(path)) + return Directory::EntryType::Directory; + + return std::nullopt; } - std::shared_ptr RomFileSystem::OpenDirectory(std::string path, Directory::ListMode listMode) { + std::shared_ptr RomFileSystem::OpenDirectory(std::string path, Directory::ListMode listMode) { try { auto &entry = directoryMap.at(path); return std::make_shared(backing, header, entry, listMode); @@ -74,10 +79,10 @@ namespace skyline::vfs { } } - RomFileSystemDirectory::RomFileSystemDirectory(const std::shared_ptr &backing, const RomFileSystem::RomFsHeader &header, const RomFileSystem::RomFsDirectoryEntry &ownEntry, ListMode listMode) : Directory(listMode), backing(backing), header(header), ownEntry(ownEntry) {} + RomFileSystemDirectory::RomFileSystemDirectory(const std::shared_ptr &backing, const RomFileSystem::RomFsHeader &header, const RomFileSystem::RomFsDirectoryEntry &ownEntry, ListMode listMode) : Directory(listMode), backing(backing), header(header), ownEntry(ownEntry) {} - std::vector RomFileSystemDirectory::Read() { - std::vector contents; + std::vector RomFileSystemDirectory::Read() { + std::vector contents; if (listMode.file) { RomFileSystem::RomFsFileEntry romFsFileEntry; diff --git a/app/src/main/cpp/skyline/vfs/rom_filesystem.h b/app/src/main/cpp/skyline/vfs/rom_filesystem.h index edfcff97..6e3cf198 100644 --- a/app/src/main/cpp/skyline/vfs/rom_filesystem.h +++ b/app/src/main/cpp/skyline/vfs/rom_filesystem.h @@ -81,7 +81,7 @@ namespace skyline { std::shared_ptr OpenFile(std::string path, Backing::Mode mode = {true, false, false}); - bool FileExists(std::string path); + std::optional GetEntryType(std::string path); std::shared_ptr OpenDirectory(std::string path, Directory::ListMode listMode); }; diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index c2820b75..d4c03f47 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -34,11 +34,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { */ private lateinit var preferenceFd : ParcelFileDescriptor - /** - * The file descriptor of the Log file - */ - private lateinit var logFd : ParcelFileDescriptor - /** * The surface object used for displaying frames */ @@ -61,9 +56,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { * @param romType The type of the ROM as an enum value * @param romFd The file descriptor of the ROM object * @param preferenceFd The file descriptor of the Preference XML - * @param logFd The file descriptor of the Log file + * @param appFilesPath The full path to the app files directory */ - private external fun executeApplication(romUri : String, romType : Int, romFd : Int, preferenceFd : Int, logFd : Int) + private external fun executeApplication(romUri : String, romType : Int, romFd : Int, preferenceFd : Int, appFilesPath : String) /** * This sets the halt flag in libskyline to the provided value, if set to true it causes libskyline to halt emulation @@ -90,7 +85,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { private external fun getFrametime() : Float /** - * This executes the specified ROM, [preferenceFd] and [logFd] are assumed to be valid beforehand + * This executes the specified ROM, [preferenceFd] is assumed to be valid beforehand * * @param rom The URI of the ROM to execute */ @@ -102,7 +97,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { while ((surface == null)) Thread.yield() - executeApplication(Uri.decode(rom.toString()), romType, romFd.fd, preferenceFd.fd, logFd.fd) + executeApplication(Uri.decode(rom.toString()), romType, romFd.fd, preferenceFd.fd, applicationContext.filesDir.canonicalPath + "/") if (shouldFinish) runOnUiThread { finish() } @@ -112,7 +107,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { } /** - * This makes the window fullscreen then sets up [preferenceFd] and [logFd], sets up the performance statistics and finally calls [executeApplication] for executing the application + * This makes the window fullscreen then sets up [preferenceFd], sets up the performance statistics and finally calls [executeApplication] for executing the application */ @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState : Bundle?) { @@ -136,9 +131,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml") preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE) - val log = File("${applicationInfo.dataDir}/skyline.log") - logFd = ParcelFileDescriptor.open(log, ParcelFileDescriptor.MODE_CREATE or ParcelFileDescriptor.MODE_READ_WRITE) - game_view.holder.addCallback(this) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) @@ -186,7 +178,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { romFd.close() preferenceFd.close() - logFd.close() super.onDestroy() } diff --git a/app/src/main/java/emu/skyline/LogActivity.kt b/app/src/main/java/emu/skyline/LogActivity.kt index eab05cc5..48f4bdd4 100644 --- a/app/src/main/java/emu/skyline/LogActivity.kt +++ b/app/src/main/java/emu/skyline/LogActivity.kt @@ -63,7 +63,7 @@ class LogActivity : AppCompatActivity() { log_list.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL)) try { - logFile = File("${applicationInfo.dataDir}/skyline.log") + logFile = File(applicationContext.filesDir.canonicalPath + "/skyline.log") logFile.forEachLine { adapter.add(it) diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index 6d8ef729..cf7dc347 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -93,7 +93,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener { private fun refreshAdapter(tryLoad : Boolean) { if (tryLoad) { try { - adapter.load(File("${applicationInfo.dataDir}/roms.bin")) + adapter.load(File(applicationContext.filesDir.canonicalPath + "/roms.bin")) return } catch (e : Exception) { Log.w("refreshFiles", "Ran into exception while loading: ${e.message}") @@ -121,7 +121,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener { if (!foundRoms) adapter.addHeader(getString(R.string.no_rom)) try { - adapter.save(File("${applicationInfo.dataDir}/roms.bin")) + adapter.save(File(applicationContext.filesDir.canonicalPath + "/roms.bin")) } catch (e : IOException) { Log.w("refreshFiles", "Ran into exception while saving: ${e.message}") }