From 995dfb81dfac8cd6c981ba2d080afa4cdf372dec Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sun, 22 Jan 2023 14:23:28 +0200 Subject: [PATCH] ExtSaveData work --- include/fs/archive_base.hpp | 25 ++++++++++++++++++++++--- include/fs/archive_ext_save_data.hpp | 2 +- include/fs/archive_ncch.hpp | 2 +- include/fs/archive_save_data.hpp | 2 +- include/fs/archive_sdmc.hpp | 2 +- include/io_file.hpp | 7 +++++++ include/services/fs.hpp | 2 +- src/core/fs/archive_ext_save_data.cpp | 17 ++++++++++++++--- src/core/fs/archive_ncch.cpp | 8 ++++---- src/core/fs/archive_save_data.cpp | 10 +++++----- src/core/fs/archive_sdmc.cpp | 4 ++-- src/core/services/fs.cpp | 15 +++++++++------ 12 files changed, 68 insertions(+), 28 deletions(-) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 9a08b4de..0120b7aa 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -72,14 +73,24 @@ struct FSPath { } }; -class ArchiveBase; +struct FilePerms { + u32 raw; + FilePerms(u32 val) : raw(val) {} + bool read() const { return (raw & 1) != 0; } + bool write() const { return (raw & 2) != 0; } + bool create() const { return (raw & 4) != 0; } +}; + +class ArchiveBase; struct FileSession { ArchiveBase* archive = nullptr; + FILE* fd = nullptr; // File descriptor for file sessions that require them. FSPath path; bool isOpen; - FileSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(path), isOpen(isOpen) {} + FileSession(ArchiveBase* archive, const FSPath& filePath, FILE* fd, bool isOpen = true) : + archive(archive), path(path), fd(fd), isOpen(isOpen) {} }; struct ArchiveSession { @@ -90,9 +101,16 @@ struct ArchiveSession { ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(path), isOpen(isOpen) {} }; +// Represents a file descriptor obtained from OpenFile. If the optional is nullopt, opening the file failed. +// Otherwise the fd of the opened file is returned (or nullptr if the opened file doesn't require one) +using FileDescriptor = std::optional; + class ArchiveBase { protected: using Handle = u32; + static constexpr FileDescriptor NoFile = nullptr; + static constexpr FileDescriptor FileError = std::nullopt; + Memory& mem; // Returns if a specified 3DS path in UTF16 or ASCII format is safe or not @@ -143,7 +161,8 @@ protected: public: virtual std::string name() = 0; virtual u64 getFreeBytes() = 0; - virtual bool openFile(const FSPath& path) = 0; + // Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed) + virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0; virtual ArchiveBase* openArchive(const FSPath& path) = 0; diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index 973b2061..fecb2a5b 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -8,8 +8,8 @@ public: u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; } std::string name() override { return "ExtSaveData"; } - bool openFile(const FSPath& path) override; ArchiveBase* openArchive(const FSPath& path) override; + FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; bool isShared = false; diff --git a/include/fs/archive_ncch.hpp b/include/fs/archive_ncch.hpp index b079e60d..ed20a4c6 100644 --- a/include/fs/archive_ncch.hpp +++ b/include/fs/archive_ncch.hpp @@ -8,8 +8,8 @@ public: u64 getFreeBytes() override { return 0; } std::string name() override { return "SelfNCCH"; } - bool openFile(const FSPath& path) override; ArchiveBase* openArchive(const FSPath& path) override; + FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; // Returns whether the cart has a RomFS diff --git a/include/fs/archive_save_data.hpp b/include/fs/archive_save_data.hpp index 5ff9d8ba..4b2fd5d8 100644 --- a/include/fs/archive_save_data.hpp +++ b/include/fs/archive_save_data.hpp @@ -8,8 +8,8 @@ public: u64 getFreeBytes() override { Helpers::panic("SaveData::GetFreeBytes unimplemented"); return 0; } std::string name() override { return "SaveData"; } - bool openFile(const FSPath& path) override; ArchiveBase* openArchive(const FSPath& path) override; + FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; // Returns whether the cart has save data or not diff --git a/include/fs/archive_sdmc.hpp b/include/fs/archive_sdmc.hpp index fc271881..37cadd60 100644 --- a/include/fs/archive_sdmc.hpp +++ b/include/fs/archive_sdmc.hpp @@ -8,7 +8,7 @@ public: u64 getFreeBytes() override { Helpers::panic("SDMC::GetFreeBytes unimplemented"); return 0; } std::string name() override { return "SDMC"; } - bool openFile(const FSPath& path) override; ArchiveBase* openArchive(const FSPath& path) override; + FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; }; \ No newline at end of file diff --git a/include/io_file.hpp b/include/io_file.hpp index 58618db1..459171b8 100644 --- a/include/io_file.hpp +++ b/include/io_file.hpp @@ -24,6 +24,9 @@ class IOFile { public: IOFile() {} IOFile(FILE* handle) : handle(handle) {} + IOFile(const std::filesystem::path& path, const char* permissions = "rb") { + open(path, permissions); + } bool isOpen() { return handle != nullptr; @@ -86,6 +89,10 @@ public: return seek(0, SEEK_SET); } + FILE* getHandle() { + return handle; + } + static void setAppDataDir(const char* dir) { if (!dir) Helpers::panic("Failed to set app data directory"); appData = std::filesystem::path(dir); diff --git a/include/services/fs.hpp b/include/services/fs.hpp index fbfc141a..50c917c3 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -26,7 +26,7 @@ class FSService { ArchiveBase* getArchiveFromID(u32 id); std::optional openArchiveHandle(u32 archiveID, const FSPath& path); - std::optional openFileHandle(ArchiveBase* archive, const FSPath& path); + std::optional openFileHandle(ArchiveBase* archive, const FSPath& path, const FilePerms& perms); FSPath readPath(u32 type, u32 pointer, u32 size); // Service commands diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index e125951c..479d555f 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -3,18 +3,27 @@ namespace fs = std::filesystem; -bool ExtSaveDataArchive::openFile(const FSPath& path) { +FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) { if (path.type == PathType::UTF16) { if (!isPathSafe(path)) Helpers::panic("Unsafe path in ExtSaveData::OpenFile"); + if (perms.create()) + Helpers::panic("[ExtSaveData] CAn't open file with create flag"); + fs::path p = IOFile::getAppData() / "NAND"; p += fs::path(path.utf16_string).make_preferred(); - return false; + + if (fs::exists(p)) { // Return file descriptor if the file exists + IOFile file(p.string().c_str(), "r+b"); // According to Citra, this ignores the OpenFile flags and always opens as r+b? TODO: Check + return file.isOpen() ? file.getHandle() : FileError; + } else { + return FileError; + } } Helpers::panic("ExtSaveDataArchive::OpenFile: Failed"); - return false; + return FileError; } ArchiveBase* ExtSaveDataArchive::openArchive(const FSPath& path) { @@ -22,6 +31,8 @@ ArchiveBase* ExtSaveDataArchive::openArchive(const FSPath& path) { Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive"); } + if (path.binary[0] != 0) Helpers::panic("ExtSaveData: Tried to access something other than NAND"); + return this; } diff --git a/src/core/fs/archive_ncch.cpp b/src/core/fs/archive_ncch.cpp index 9514de94..cd1bd895 100644 --- a/src/core/fs/archive_ncch.cpp +++ b/src/core/fs/archive_ncch.cpp @@ -1,15 +1,15 @@ #include "fs/archive_ncch.hpp" #include -bool SelfNCCHArchive::openFile(const FSPath& path) { +FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& perms) { if (!hasRomFS()) { printf("Tried to open a SelfNCCH file without a RomFS\n"); - return false; + return FileError; } if (path.type != PathType::Binary || path.binary.size() != 12) { printf("Invalid SelfNCCH path type\n"); - return false; + return FileError; } // Where to read the file from. (https://www.3dbrew.org/wiki/Filesystem_services#SelfNCCH_File_Path_Data_Format) @@ -19,7 +19,7 @@ bool SelfNCCHArchive::openFile(const FSPath& path) { Helpers::panic("Read from NCCH's non-RomFS section!"); } - return true; + return NoFile; // No file descriptor needed for RomFS } ArchiveBase* SelfNCCHArchive::openArchive(const FSPath& path) { diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index 071d5264..86541f94 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -2,23 +2,23 @@ #include #include -bool SaveDataArchive::openFile(const FSPath& path) { +FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) { if (!cartHasSaveData()) { printf("Tried to read SaveData FS without save data\n"); - return false; + return FileError; } if (path.type == PathType::UTF16 /* && path.utf16_string == u"/game_header" */) { printf("Opened file from the SaveData archive \n"); - return true; + return NoFile; } if (path.type != PathType::Binary) { printf("Unimplemented SaveData path type: %d\n", path.type); - return false; + return FileError; } - return true; + return NoFile; } ArchiveBase* SaveDataArchive::openArchive(const FSPath& path) { diff --git a/src/core/fs/archive_sdmc.cpp b/src/core/fs/archive_sdmc.cpp index 71506e81..2ef619ca 100644 --- a/src/core/fs/archive_sdmc.cpp +++ b/src/core/fs/archive_sdmc.cpp @@ -1,9 +1,9 @@ #include "fs/archive_sdmc.hpp" #include -bool SDMCArchive::openFile(const FSPath& path) { +FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms) { printf("SDMCArchive::OpenFile: Failed"); - return false; + return FileError; } ArchiveBase* SDMCArchive::openArchive(const FSPath& path) { diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 1d4b2ca1..bbdcd74a 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -39,12 +39,13 @@ ArchiveBase* FSService::getArchiveFromID(u32 id) { } } -std::optional FSService::openFileHandle(ArchiveBase* archive, const FSPath& path) { - bool opened = archive->openFile(path); - if (opened) { +std::optional FSService::openFileHandle(ArchiveBase* archive, const FSPath& path, const FilePerms& perms) { + FileDescriptor opened = archive->openFile(path, perms); + if (opened.has_value()) { // If opened doesn't have a value, we failed to open the file auto handle = kernel.makeObject(KernelObjectType::File); + auto& file = kernel.getObjects()[handle]; - file.data = new FileSession(archive, path); + file.data = new FileSession(archive, path, opened.value()); return handle; } else { @@ -164,8 +165,9 @@ void FSService::openFile(u32 messagePointer) { ArchiveBase* archive = archiveObject->getData()->archive; auto filePath = readPath(filePathType, filePathPointer, filePathSize); + const FilePerms perms(openFlags); - std::optional handle = openFileHandle(archive, filePath); + std::optional handle = openFileHandle(archive, filePath, perms); if (!handle.has_value()) { printf("OpenFile failed\n"); mem.write32(messagePointer + 4, Result::FileNotFound); @@ -196,13 +198,14 @@ void FSService::openFileDirectly(u32 messagePointer) { auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize); auto filePath = readPath(filePathType, filePathPointer, filePathSize); + const FilePerms perms(openFlags); archive = archive->openArchive(archivePath); if (archive == nullptr) [[unlikely]] { Helpers::panic("OpenFileDirectly: Failed to open archive with given path"); } - std::optional handle = openFileHandle(archive, filePath); + std::optional handle = openFileHandle(archive, filePath, perms); if (!handle.has_value()) { Helpers::panic("OpenFileDirectly: Failed to open file with given path"); } else {