From 49dcc25795e6c58c5619031907c94aa689b5027e Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Mon, 23 Jan 2023 02:35:10 +0200 Subject: [PATCH] [FS] Done with CreateFile --- include/fs/archive_base.hpp | 7 +++++++ include/fs/archive_ext_save_data.hpp | 1 + include/fs/archive_ncch.hpp | 1 + include/fs/archive_save_data.hpp | 1 + include/fs/archive_sdmc.hpp | 1 + include/io_file.hpp | 13 +++++++++++++ include/kernel/kernel.hpp | 1 + include/services/fs.hpp | 2 ++ include/services/service_manager.hpp | 1 + src/core/fs/archive_ext_save_data.cpp | 27 +++++++++++++++++++++++++++ src/core/fs/archive_ncch.cpp | 5 +++++ src/core/fs/archive_save_data.cpp | 5 +++++ src/core/fs/archive_sdmc.cpp | 5 +++++ src/core/services/fs.cpp | 17 ++++++++++++++--- src/emulator.cpp | 1 + 15 files changed, 85 insertions(+), 3 deletions(-) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 0120b7aa..3c84ac5c 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -105,6 +105,12 @@ struct ArchiveSession { // Otherwise the fd of the opened file is returned (or nullptr if the opened file doesn't require one) using FileDescriptor = std::optional; +enum class CreateFileResult : u32 { + Success = 0, + AlreadyExists = 0x82044BE, + FileTooLarge = 0x86044D2 +}; + class ArchiveBase { protected: using Handle = u32; @@ -161,6 +167,7 @@ protected: public: virtual std::string name() = 0; virtual u64 getFreeBytes() = 0; + virtual CreateFileResult createFile(const FSPath& path, u64 size) = 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; diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index fecb2a5b..40ddbb54 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -9,6 +9,7 @@ public: std::string name() override { return "ExtSaveData"; } ArchiveBase* openArchive(const FSPath& path) override; + CreateFileResult createFile(const FSPath& path, u64 size) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; diff --git a/include/fs/archive_ncch.hpp b/include/fs/archive_ncch.hpp index ed20a4c6..90ee1c02 100644 --- a/include/fs/archive_ncch.hpp +++ b/include/fs/archive_ncch.hpp @@ -9,6 +9,7 @@ public: std::string name() override { return "SelfNCCH"; } ArchiveBase* openArchive(const FSPath& path) override; + CreateFileResult createFile(const FSPath& path, u64 size) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; diff --git a/include/fs/archive_save_data.hpp b/include/fs/archive_save_data.hpp index 4b2fd5d8..2e5d070b 100644 --- a/include/fs/archive_save_data.hpp +++ b/include/fs/archive_save_data.hpp @@ -9,6 +9,7 @@ public: std::string name() override { return "SaveData"; } ArchiveBase* openArchive(const FSPath& path) override; + CreateFileResult createFile(const FSPath& path, u64 size) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; diff --git a/include/fs/archive_sdmc.hpp b/include/fs/archive_sdmc.hpp index 37cadd60..83aa1717 100644 --- a/include/fs/archive_sdmc.hpp +++ b/include/fs/archive_sdmc.hpp @@ -9,6 +9,7 @@ public: std::string name() override { return "SDMC"; } ArchiveBase* openArchive(const FSPath& path) override; + CreateFileResult createFile(const FSPath& path, u64 size) 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 459171b8..c588d70e 100644 --- a/include/io_file.hpp +++ b/include/io_file.hpp @@ -62,6 +62,19 @@ public: return read(data, count, sizeof(std::uint8_t)); } + std::pair write(const void* data, std::size_t length, std::size_t dataSize) { + if (!isOpen()) { + return { false, std::numeric_limits::max() }; + } + + if (length == 0) return { true, 0 }; + return { true, std::fwrite(data, dataSize, length, handle) }; + } + + auto writeBytes(const void* data, std::size_t count) { + return write(data, count, sizeof(std::uint8_t)); + } + std::uint64_t size() { if (!isOpen()) return 0; diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 817d1341..853adc7c 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -124,6 +124,7 @@ private: public: Kernel(CPU& cpu, Memory& mem, GPU& gpu); + void initializeFS() { return serviceManager.initializeFS(); } void setVersion(u8 major, u8 minor); void serviceSVC(u32 svc); void reset(); diff --git a/include/services/fs.hpp b/include/services/fs.hpp index ab5ac89d..b5a8ad98 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -53,4 +53,6 @@ public: void reset(); void handleSyncRequest(u32 messagePointer); + // Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM. + void initializeFilesystem(); }; \ No newline at end of file diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index a072b88a..5f119342 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -60,6 +60,7 @@ class ServiceManager { public: ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel); void reset(); + void initializeFS() { fs.initializeFilesystem(); } void handleSyncRequest(u32 messagePointer); // Forward a SendSyncRequest IPC message to the service with the respective handle diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 8d007006..b2da8adc 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -3,6 +3,33 @@ namespace fs = std::filesystem; +CreateFileResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) { + if (size == 0) + Helpers::panic("ExtSaveData file does not support size == 0"); + + if (path.type == PathType::UTF16) { + if (!isPathSafe(path)) + Helpers::panic("Unsafe path in ExtSaveData::CreateFile"); + + fs::path p = IOFile::getAppData() / "NAND"; + p += fs::path(path.utf16_string).make_preferred(); + + if (fs::exists(p)) + return CreateFileResult::AlreadyExists; + + // Create a file of size "size" by creating an empty one, seeking to size - 1 and just writing a 0 there + IOFile file(p.string().c_str(), "wb"); + if (file.seek(size - 1, SEEK_SET) && file.writeBytes("", 1).second == 1) { + return CreateFileResult::Success; + } + + return CreateFileResult::FileTooLarge; + } + + Helpers::panic("ExtSaveDataArchive::OpenFile: Failed"); + return CreateFileResult::Success; +} + FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) { if (path.type == PathType::UTF16) { if (!isPathSafe(path)) diff --git a/src/core/fs/archive_ncch.cpp b/src/core/fs/archive_ncch.cpp index cd1bd895..54ff4812 100644 --- a/src/core/fs/archive_ncch.cpp +++ b/src/core/fs/archive_ncch.cpp @@ -1,6 +1,11 @@ #include "fs/archive_ncch.hpp" #include +CreateFileResult SelfNCCHArchive::createFile(const FSPath& path, u64 size) { + Helpers::panic("[SelfNCCH] CreateFile not yet supported"); + return CreateFileResult::Success; +} + FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& perms) { if (!hasRomFS()) { printf("Tried to open a SelfNCCH file without a RomFS\n"); diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index 86541f94..36e221ac 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -2,6 +2,11 @@ #include #include +CreateFileResult SaveDataArchive::createFile(const FSPath& path, u64 size) { + Helpers::panic("[SaveData] CreateFile not yet supported"); + return CreateFileResult::Success; +} + FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) { if (!cartHasSaveData()) { printf("Tried to read SaveData FS without save data\n"); diff --git a/src/core/fs/archive_sdmc.cpp b/src/core/fs/archive_sdmc.cpp index 2ef619ca..f3bbc008 100644 --- a/src/core/fs/archive_sdmc.cpp +++ b/src/core/fs/archive_sdmc.cpp @@ -1,6 +1,11 @@ #include "fs/archive_sdmc.hpp" #include +CreateFileResult SDMCArchive::createFile(const FSPath& path, u64 size) { + Helpers::panic("[SDMC] CreateFile not yet supported"); + return CreateFileResult::Success; +} + FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms) { printf("SDMCArchive::OpenFile: Failed"); return FileError; diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 51974e5b..85178afc 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -1,5 +1,6 @@ #include "services/fs.hpp" #include "kernel/kernel.hpp" +#include "io_file.hpp" #ifdef CreateFile // windows.h defines this because of course it does. #undef CreateFile @@ -32,6 +33,16 @@ void FSService::reset() { priority = 0; } +// Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM. +void FSService::initializeFilesystem() { + const auto nandPath = IOFile::getAppData() / "NAND"; // Create NAND + // TODO: Savedata, SDMC, etc + + if (!std::filesystem::is_directory(nandPath)) { + std::filesystem::create_directories(nandPath); + } +} + ArchiveBase* FSService::getArchiveFromID(u32 id) { switch (id) { case ArchiveID::SelfNCCH: return &selfNcch; @@ -225,7 +236,7 @@ void FSService::createFile(u32 messagePointer) { const u32 filePathType = mem.read32(messagePointer + 16); const u32 filePathSize = mem.read32(messagePointer + 20); const u32 attributes = mem.read32(messagePointer + 24); - const u64 bytesToZero = mem.read64(messagePointer + 28); // Bytes to fill with zeroes in the file + const u64 size = mem.read64(messagePointer + 28); const u32 filePathPointer = mem.read32(messagePointer + 40); log("FS::CreateFile\n"); @@ -240,8 +251,8 @@ void FSService::createFile(u32 messagePointer) { ArchiveBase* archive = archiveObject->getData()->archive; auto filePath = readPath(filePathType, filePathPointer, filePathSize); - for (auto c : filePath.utf16_string) std::cout << (char)c; - Helpers::panic("Tried to create file"); + CreateFileResult res = archive->createFile(filePath, size); + mem.write32(messagePointer + 4, static_cast(res)); } void FSService::getPriority(u32 messagePointer) { diff --git a/src/emulator.cpp b/src/emulator.cpp index 41082d78..73846225 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -49,6 +49,7 @@ void Emulator::runFrame() { } bool Emulator::loadROM(const std::filesystem::path& path) { + kernel.initializeFS(); auto extension = path.extension(); if (extension == ".elf" || extension == ".axf")