mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 16:21:52 +01:00
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.
This commit is contained in:
parent
f72b81fcea
commit
6edf89b538
@ -97,6 +97,7 @@ add_library(skyline SHARED
|
|||||||
${source_DIR}/skyline/services/timesrv/ITimeZoneService.cpp
|
${source_DIR}/skyline/services/timesrv/ITimeZoneService.cpp
|
||||||
${source_DIR}/skyline/services/fssrv/IFileSystemProxy.cpp
|
${source_DIR}/skyline/services/fssrv/IFileSystemProxy.cpp
|
||||||
${source_DIR}/skyline/services/fssrv/IFileSystem.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/fssrv/IStorage.cpp
|
||||||
${source_DIR}/skyline/services/nvdrv/INvDrvServices.cpp
|
${source_DIR}/skyline/services/nvdrv/INvDrvServices.cpp
|
||||||
${source_DIR}/skyline/services/nvdrv/devices/nvmap.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/socket/bsd/IClient.cpp
|
||||||
${source_DIR}/skyline/services/ssl/ISslService.cpp
|
${source_DIR}/skyline/services/ssl/ISslService.cpp
|
||||||
${source_DIR}/skyline/services/prepo/IPrepoService.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/partition_filesystem.cpp
|
||||||
${source_DIR}/skyline/vfs/rom_filesystem.cpp
|
${source_DIR}/skyline/vfs/rom_filesystem.cpp
|
||||||
${source_DIR}/skyline/vfs/os_backing.cpp
|
${source_DIR}/skyline/vfs/os_backing.cpp
|
||||||
|
@ -24,7 +24,7 @@ void signalHandler(int signal) {
|
|||||||
FaultCount++;
|
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;
|
Halt = false;
|
||||||
FaultCount = 0;
|
FaultCount = 0;
|
||||||
fps = 0;
|
fps = 0;
|
||||||
@ -41,13 +41,17 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
|
|||||||
|
|
||||||
auto jvmManager = std::make_shared<skyline::JvmManager>(env, instance);
|
auto jvmManager = std::make_shared<skyline::JvmManager>(env, instance);
|
||||||
auto settings = std::make_shared<skyline::Settings>(preferenceFd);
|
auto settings = std::make_shared<skyline::Settings>(preferenceFd);
|
||||||
auto logger = std::make_shared<skyline::Logger>(logFd, static_cast<skyline::Logger::LogLevel>(std::stoi(settings->GetString("log_level"))));
|
|
||||||
|
auto appFilesPath = env->GetStringUTFChars(appFilesPathJstring, nullptr);
|
||||||
|
auto logger = std::make_shared<skyline::Logger>(std::string(appFilesPath) + "skyline.log", static_cast<skyline::Logger::LogLevel>(std::stoi(settings->GetString("log_level"))));
|
||||||
//settings->List(logger); // (Uncomment when you want to print out all settings strings)
|
//settings->List(logger); // (Uncomment when you want to print out all settings strings)
|
||||||
|
|
||||||
auto start = std::chrono::steady_clock::now();
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
try {
|
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);
|
auto romUri = env->GetStringUTFChars(romUriJstring, nullptr);
|
||||||
logger->Info("Launching ROM {}", romUri);
|
logger->Info("Launching ROM {}", romUri);
|
||||||
env->ReleaseStringUTFChars(romUriJstring, romUri);
|
env->ReleaseStringUTFChars(romUriJstring, romUri);
|
||||||
|
@ -120,8 +120,8 @@ namespace skyline {
|
|||||||
logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first));
|
logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first));
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Logger(int fd, LogLevel configLevel) : configLevel(configLevel) {
|
Logger::Logger(const std::string &path, LogLevel configLevel) : configLevel(configLevel) {
|
||||||
logFile.__open(fd, std::ios::app);
|
logFile.open(path, std::ios::app);
|
||||||
WriteHeader("Logging started");
|
WriteHeader("Logging started");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ namespace skyline {
|
|||||||
// Status codes
|
// Status codes
|
||||||
namespace status {
|
namespace status {
|
||||||
constexpr u32 Success = 0x0; //!< "Success"
|
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 NoMessages = 0x680; //!< "No message available"
|
||||||
constexpr u32 ServiceInvName = 0xC15; //!< "Invalid name"
|
constexpr u32 ServiceInvName = 0xC15; //!< "Invalid name"
|
||||||
constexpr u32 ServiceNotReg = 0xE15; //!< "Service not registered"
|
constexpr u32 ServiceNotReg = 0xE15; //!< "Service not registered"
|
||||||
@ -220,10 +222,10 @@ namespace skyline {
|
|||||||
LogLevel configLevel; //!< The level of logs to write
|
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
|
* @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
|
* @brief Writes the termination message to the log file
|
||||||
|
@ -10,7 +10,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) : state(this, process, jvmManager, settings, logger), memory(state), serviceManager(state) {}
|
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &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) {
|
void OS::Execute(int romFd, loader::RomFormat romType) {
|
||||||
auto romFile = std::make_shared<vfs::OsBacking>(romFd);
|
auto romFile = std::make_shared<vfs::OsBacking>(romFd);
|
||||||
|
@ -25,13 +25,14 @@ namespace skyline::kernel {
|
|||||||
std::shared_ptr<type::KProcess> process; //!< The KProcess object for the emulator, representing the guest process
|
std::shared_ptr<type::KProcess> process; //!< The KProcess object for the emulator, representing the guest process
|
||||||
service::ServiceManager serviceManager; //!< This manages all of the service functions
|
service::ServiceManager serviceManager; //!< This manages all of the service functions
|
||||||
MemoryManager memory; //!< The MemoryManager object for this process
|
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 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);
|
OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, const std::string &appFilesPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Execute a particular ROM file. This launches the main process and calls the NCE class to handle execution.
|
* @brief Execute a particular ROM file. This launches the main process and calls the NCE class to handle execution.
|
||||||
|
@ -58,6 +58,7 @@ namespace skyline::service {
|
|||||||
timesrv_ISteadyClock,
|
timesrv_ISteadyClock,
|
||||||
fssrv_IFileSystemProxy,
|
fssrv_IFileSystemProxy,
|
||||||
fssrv_IFileSystem,
|
fssrv_IFileSystem,
|
||||||
|
fssrv_IFile,
|
||||||
fssrv_IStorage,
|
fssrv_IStorage,
|
||||||
nvdrv_INvDrvServices,
|
nvdrv_INvDrvServices,
|
||||||
visrv_IManagerRootService,
|
visrv_IManagerRootService,
|
||||||
|
73
app/src/main/cpp/skyline/services/fssrv/IFile.cpp
Normal file
73
app/src/main/cpp/skyline/services/fssrv/IFile.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <kernel/types/KProcess.h>
|
||||||
|
#include "IFile.h"
|
||||||
|
|
||||||
|
namespace skyline::service::fssrv {
|
||||||
|
IFile::IFile(std::shared_ptr<vfs::Backing> &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<u32>();
|
||||||
|
request.Skip<u32>();
|
||||||
|
auto offset = request.Pop<i64>();
|
||||||
|
auto size = request.Pop<i64>();
|
||||||
|
|
||||||
|
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<u32>(static_cast<u32>(backing->Read(state.process->GetPointer<u8>(request.outputBuf.at(0).address), offset, size)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IFile::Write(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
auto writeOption = request.Pop<u32>();
|
||||||
|
request.Skip<u32>();
|
||||||
|
auto offset = request.Pop<i64>();
|
||||||
|
auto size = request.Pop<i64>();
|
||||||
|
|
||||||
|
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<u8>(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<u64>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void IFile::GetSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
response.Push<u64>(backing->size);
|
||||||
|
}
|
||||||
|
}
|
41
app/src/main/cpp/skyline/services/fssrv/IFile.h
Normal file
41
app/src/main/cpp/skyline/services/fssrv/IFile.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <services/base_service.h>
|
||||||
|
#include <services/serviceman.h>
|
||||||
|
#include <vfs/backing.h>
|
||||||
|
|
||||||
|
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<vfs::Backing> backing; //!< The backing of the IFile
|
||||||
|
|
||||||
|
public:
|
||||||
|
IFile(std::shared_ptr<vfs::Backing> &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);
|
||||||
|
};
|
||||||
|
}
|
@ -1,8 +1,55 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <kernel/types/KProcess.h>
|
||||||
|
#include <vfs/filesystem.h>
|
||||||
|
#include "IFile.h"
|
||||||
#include "IFileSystem.h"
|
#include "IFileSystem.h"
|
||||||
|
|
||||||
namespace skyline::service::fssrv {
|
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<vfs::FileSystem> 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<char>(request.inputBuf.at(0).address));
|
||||||
|
auto mode = request.Pop<u64>();
|
||||||
|
auto size = request.Pop<u32>();
|
||||||
|
|
||||||
|
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<char>(request.inputBuf.at(0).address));
|
||||||
|
|
||||||
|
auto type = backing->GetEntryType(path);
|
||||||
|
|
||||||
|
if (type.has_value()) {
|
||||||
|
response.Push(type.value());
|
||||||
|
} else {
|
||||||
|
response.Push<u32>(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<char>(request.inputBuf.at(0).address));
|
||||||
|
auto mode = request.Pop<vfs::Backing::Mode>();
|
||||||
|
|
||||||
|
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<IFile>(file, state, manager), session, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IFileSystem::Commit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {}
|
||||||
}
|
}
|
||||||
|
@ -3,26 +3,39 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vfs/filesystem.h>
|
||||||
#include <services/base_service.h>
|
#include <services/base_service.h>
|
||||||
#include <services/serviceman.h>
|
#include <services/serviceman.h>
|
||||||
|
|
||||||
namespace skyline::service::fssrv {
|
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)
|
* @brief IFileSystem is used to interact with a filesystem (https://switchbrew.org/wiki/Filesystem_services#IFileSystem)
|
||||||
*/
|
*/
|
||||||
class IFileSystem : public BaseService {
|
class IFileSystem : public BaseService {
|
||||||
public:
|
private:
|
||||||
FsType type; //!< The type of filesystem this class represents
|
std::shared_ptr<vfs::FileSystem> backing;
|
||||||
|
|
||||||
IFileSystem(FsType type, const DeviceState &state, ServiceManager &manager);
|
public:
|
||||||
|
IFileSystem(std::shared_ptr<vfs::FileSystem> 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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <os.h>
|
||||||
|
#include <vfs/os_filesystem.h>
|
||||||
#include <loader/loader.h>
|
#include <loader/loader.h>
|
||||||
#include "IFileSystemProxy.h"
|
#include "IFileSystemProxy.h"
|
||||||
#include "IStorage.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", {
|
IFileSystemProxy::IFileSystemProxy(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::fssrv_IFileSystemProxy, "fssrv:IFileSystemProxy", {
|
||||||
{0x1, SFUNC(IFileSystemProxy::SetCurrentProcess)},
|
{0x1, SFUNC(IFileSystemProxy::SetCurrentProcess)},
|
||||||
{0x12, SFUNC(IFileSystemProxy::OpenSdCardFileSystem)},
|
{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) {
|
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) {
|
void IFileSystemProxy::OpenSdCardFileSystem(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
manager.RegisterService(std::make_shared<IFileSystem>(FsType::SdCard, state, manager), session, response);
|
manager.RegisterService(std::make_shared<IFileSystem>(std::make_shared<vfs::OsFileSystem>(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<SaveDataSpaceId>();
|
||||||
|
auto attribute = request.Pop<SaveDataAttribute>();
|
||||||
|
|
||||||
|
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<IFileSystem>(std::make_shared<vfs::OsFileSystem>(state.os->appFilesPath + "/switch" + saveDataPath), state, manager), session, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystemProxy::OpenDataStorageByCurrentProcess(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
void IFileSystemProxy::OpenDataStorageByCurrentProcess(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
@ -26,4 +69,8 @@ namespace skyline::service::fssrv {
|
|||||||
else
|
else
|
||||||
throw exception("Tried to call OpenDataStorageByCurrentProcess without a valid RomFS");
|
throw exception("Tried to call OpenDataStorageByCurrentProcess without a valid RomFS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IFileSystemProxy::GetGlobalAccessLogMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
response.Push<u32>(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,57 @@
|
|||||||
|
|
||||||
#include <services/base_service.h>
|
#include <services/base_service.h>
|
||||||
#include <services/serviceman.h>
|
#include <services/serviceman.h>
|
||||||
|
#include <services/account/IAccountServiceForApplication.h>
|
||||||
#include "IFileSystem.h"
|
#include "IFileSystem.h"
|
||||||
|
|
||||||
namespace skyline::service::fssrv {
|
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)
|
* @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);
|
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
|
* @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);
|
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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,20 @@ namespace skyline::service::fssrv {
|
|||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
void IStorage::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
void IStorage::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
auto offset = request.Pop<u64>();
|
auto offset = request.Pop<i64>();
|
||||||
auto size = request.Pop<u64>();
|
auto size = request.Pop<i64>();
|
||||||
|
|
||||||
|
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<u8>(request.outputBuf.at(0).address), offset, size);
|
backing->Read(state.process->GetPointer<u8>(request.outputBuf.at(0).address), offset, size);
|
||||||
}
|
}
|
||||||
|
@ -61,5 +61,37 @@ namespace skyline::vfs {
|
|||||||
inline size_t Read(T *output, size_t offset = 0, size_t size = 0) {
|
inline size_t Read(T *output, size_t offset = 0, size_t size = 0) {
|
||||||
return Read(reinterpret_cast<u8 *>(output), offset, size ? size : sizeof(T));
|
return Read(reinterpret_cast<u8 *>(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<typename T>
|
||||||
|
inline size_t Write(T *output, size_t offset = 0, size_t size = 0) {
|
||||||
|
return Write(reinterpret_cast<u8 *>(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");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,26 @@ namespace skyline::vfs {
|
|||||||
|
|
||||||
virtual ~FileSystem() = default;
|
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
|
* @brief Opens a file from the specified path in the filesystem
|
||||||
* @param path The path to the file
|
* @param path The path to the file
|
||||||
@ -30,12 +50,32 @@ namespace skyline::vfs {
|
|||||||
*/
|
*/
|
||||||
virtual std::shared_ptr<Backing> OpenFile(std::string path, Backing::Mode mode = {true, false, false}) = 0;
|
virtual std::shared_ptr<Backing> 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<Directory::EntryType> GetEntryType(std::string path) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Checks if a given file exists in a filesystem
|
* @brief Checks if a given file exists in a filesystem
|
||||||
* @param path The path to the file
|
* @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
|
* @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
|
* @param listMode The list mode for the directory
|
||||||
* @return A shared pointer to a Directory object of the directory
|
* @return A shared pointer to a Directory object of the directory
|
||||||
*/
|
*/
|
||||||
virtual std::shared_ptr<Directory> OpenDirectory(std::string path, Directory::ListMode listMode) = 0;
|
virtual std::shared_ptr<Directory> OpenDirectory(std::string path, Directory::ListMode listMode) {
|
||||||
|
throw exception("This filesystem does not support opening directories");
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -22,16 +22,18 @@ namespace skyline::vfs {
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(ApplicationTitle) == 0x300);
|
static_assert(sizeof(ApplicationTitle) == 0x300);
|
||||||
|
|
||||||
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief This struct encapsulates all the data within an NACP file
|
* @brief This struct encapsulates all the data within an NACP file
|
||||||
*/
|
*/
|
||||||
struct NacpData {
|
struct NacpData {
|
||||||
std::array<ApplicationTitle, 0x10> titleEntries; //!< Title entries for each language
|
std::array<ApplicationTitle, 0x10> 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{};
|
} nacpContents{};
|
||||||
static_assert(sizeof(NacpData) == 0x4000);
|
static_assert(sizeof(NacpData) == 0x4000);
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
/**
|
||||||
* @param backing The backing for the NACP
|
* @param backing The backing for the NACP
|
||||||
*/
|
*/
|
||||||
|
@ -7,24 +7,46 @@
|
|||||||
#include "os_backing.h"
|
#include "os_backing.h"
|
||||||
|
|
||||||
namespace skyline::vfs {
|
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;
|
struct stat fileInfo;
|
||||||
|
|
||||||
if (fstat(fd, &fileInfo))
|
if (fstat(fd, &fileInfo))
|
||||||
throw exception("Failed to stat fd: {}", strerror(errno));
|
throw exception("Failed to stat fd: {}", strerror(errno));
|
||||||
|
|
||||||
size = fileInfo.st_size;
|
size = fileInfo.st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OsBacking::~OsBacking() {
|
||||||
|
if (closable)
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
size_t OsBacking::Read(u8 *output, size_t offset, size_t size) {
|
size_t OsBacking::Read(u8 *output, size_t offset, size_t size) {
|
||||||
if (!mode.read)
|
if (!mode.read)
|
||||||
throw exception("Attempting to read a backing that is not readable");
|
throw exception("Attempting to read a backing that is not readable");
|
||||||
|
|
||||||
auto ret = pread64(fd, output, size, offset);
|
auto ret = pread64(fd, output, size, offset);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
throw exception("Failed to read from fd: {}", strerror(errno));
|
throw exception("Failed to read from fd: {}", strerror(errno));
|
||||||
|
|
||||||
return static_cast<size_t>(ret);
|
return static_cast<size_t>(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<size_t>(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;
|
||||||
|
}
|
||||||
}
|
}
|
@ -12,13 +12,20 @@ namespace skyline::vfs {
|
|||||||
class OsBacking : public Backing {
|
class OsBacking : public Backing {
|
||||||
private:
|
private:
|
||||||
int fd; //!< An FD to the backing
|
int fd; //!< An FD to the backing
|
||||||
|
bool closable; //!< Whether the FD can be closed when the backing is destroyed
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @param fd The file descriptor of the backing
|
* @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 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);
|
||||||
};
|
};
|
||||||
}
|
}
|
88
app/src/main/cpp/skyline/vfs/os_filesystem.cpp
Normal file
88
app/src/main/cpp/skyline/vfs/os_filesystem.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#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<Backing> 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<OsBacking>(fd, true, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Directory::EntryType> 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;
|
||||||
|
}
|
||||||
|
}
|
27
app/src/main/cpp/skyline/vfs/os_filesystem.h
Normal file
27
app/src/main/cpp/skyline/vfs/os_filesystem.h
Normal file
@ -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<Backing> OpenFile(std::string path, Backing::Mode mode = {true, false, false});
|
||||||
|
|
||||||
|
std::optional<Directory::EntryType> GetEntryType(std::string path);
|
||||||
|
};
|
||||||
|
}
|
@ -40,8 +40,11 @@ namespace skyline::vfs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PartitionFileSystem::FileExists(std::string path) {
|
std::optional<Directory::EntryType> PartitionFileSystem::GetEntryType(std::string path) {
|
||||||
return fileMap.count(path);
|
if (fileMap.count(path))
|
||||||
|
return Directory::EntryType::File;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Directory> PartitionFileSystem::OpenDirectory(std::string path, Directory::ListMode listMode) {
|
std::shared_ptr<Directory> PartitionFileSystem::OpenDirectory(std::string path, Directory::ListMode listMode) {
|
||||||
|
@ -54,7 +54,7 @@ namespace skyline::vfs {
|
|||||||
|
|
||||||
std::shared_ptr<Backing> OpenFile(std::string path, Backing::Mode mode = {true, false, false});
|
std::shared_ptr<Backing> OpenFile(std::string path, Backing::Mode mode = {true, false, false});
|
||||||
|
|
||||||
bool FileExists(std::string path);
|
std::optional<Directory::EntryType> GetEntryType(std::string path);
|
||||||
|
|
||||||
std::shared_ptr<Directory> OpenDirectory(std::string path, Directory::ListMode listMode);
|
std::shared_ptr<Directory> OpenDirectory(std::string path, Directory::ListMode listMode);
|
||||||
};
|
};
|
||||||
|
@ -61,8 +61,13 @@ namespace skyline::vfs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RomFileSystem::FileExists(std::string path) {
|
std::optional<Directory::EntryType> RomFileSystem::GetEntryType(std::string path) {
|
||||||
return fileMap.count(path);
|
if (fileMap.count(path))
|
||||||
|
return Directory::EntryType::File;
|
||||||
|
else if (directoryMap.count(path))
|
||||||
|
return Directory::EntryType::Directory;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Directory> RomFileSystem::OpenDirectory(std::string path, Directory::ListMode listMode) {
|
std::shared_ptr<Directory> RomFileSystem::OpenDirectory(std::string path, Directory::ListMode listMode) {
|
||||||
|
@ -81,7 +81,7 @@ namespace skyline {
|
|||||||
|
|
||||||
std::shared_ptr<Backing> OpenFile(std::string path, Backing::Mode mode = {true, false, false});
|
std::shared_ptr<Backing> OpenFile(std::string path, Backing::Mode mode = {true, false, false});
|
||||||
|
|
||||||
bool FileExists(std::string path);
|
std::optional<Directory::EntryType> GetEntryType(std::string path);
|
||||||
|
|
||||||
std::shared_ptr<Directory> OpenDirectory(std::string path, Directory::ListMode listMode);
|
std::shared_ptr<Directory> OpenDirectory(std::string path, Directory::ListMode listMode);
|
||||||
};
|
};
|
||||||
|
@ -34,11 +34,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
|||||||
*/
|
*/
|
||||||
private lateinit var preferenceFd : ParcelFileDescriptor
|
private lateinit var preferenceFd : ParcelFileDescriptor
|
||||||
|
|
||||||
/**
|
|
||||||
* The file descriptor of the Log file
|
|
||||||
*/
|
|
||||||
private lateinit var logFd : ParcelFileDescriptor
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The surface object used for displaying frames
|
* 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 romType The type of the ROM as an enum value
|
||||||
* @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 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
|
* 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
|
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
|
* @param rom The URI of the ROM to execute
|
||||||
*/
|
*/
|
||||||
@ -102,7 +97,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
|||||||
while ((surface == null))
|
while ((surface == null))
|
||||||
Thread.yield()
|
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)
|
if (shouldFinish)
|
||||||
runOnUiThread { finish() }
|
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")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onCreate(savedInstanceState : Bundle?) {
|
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")
|
val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml")
|
||||||
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
|
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)
|
game_view.holder.addCallback(this)
|
||||||
|
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
@ -186,7 +178,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
|||||||
|
|
||||||
romFd.close()
|
romFd.close()
|
||||||
preferenceFd.close()
|
preferenceFd.close()
|
||||||
logFd.close()
|
|
||||||
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ class LogActivity : AppCompatActivity() {
|
|||||||
log_list.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
|
log_list.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logFile = File("${applicationInfo.dataDir}/skyline.log")
|
logFile = File(applicationContext.filesDir.canonicalPath + "/skyline.log")
|
||||||
|
|
||||||
logFile.forEachLine {
|
logFile.forEachLine {
|
||||||
adapter.add(it)
|
adapter.add(it)
|
||||||
|
@ -93,7 +93,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
|
|||||||
private fun refreshAdapter(tryLoad : Boolean) {
|
private fun refreshAdapter(tryLoad : Boolean) {
|
||||||
if (tryLoad) {
|
if (tryLoad) {
|
||||||
try {
|
try {
|
||||||
adapter.load(File("${applicationInfo.dataDir}/roms.bin"))
|
adapter.load(File(applicationContext.filesDir.canonicalPath + "/roms.bin"))
|
||||||
return
|
return
|
||||||
} catch (e : Exception) {
|
} catch (e : Exception) {
|
||||||
Log.w("refreshFiles", "Ran into exception while loading: ${e.message}")
|
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))
|
if (!foundRoms) adapter.addHeader(getString(R.string.no_rom))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
adapter.save(File("${applicationInfo.dataDir}/roms.bin"))
|
adapter.save(File(applicationContext.filesDir.canonicalPath + "/roms.bin"))
|
||||||
} catch (e : IOException) {
|
} catch (e : IOException) {
|
||||||
Log.w("refreshFiles", "Ran into exception while saving: ${e.message}")
|
Log.w("refreshFiles", "Ran into exception while saving: ${e.message}")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user