From 3d0a0ccc0b9cddb48059b4dc020cd7cac0aca0ac Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 11 Mar 2024 09:55:00 -0700 Subject: [PATCH] Refactor `Rust::Result` into `std::expected` The standard library already has support for this type as of C++23: https://en.cppreference.com/w/cpp/header/expected [This is also supported by all major compilers](https://en.cppreference.com/w/cpp/compiler_support#:~:text=Yes-,%3Cexpected%3E,-P0323R12%0AP2549R1) --- CMakeLists.txt | 3 +- include/fs/archive_base.hpp | 285 ++++--- include/fs/archive_ext_save_data.hpp | 20 +- include/fs/archive_ncch.hpp | 9 +- include/fs/archive_save_data.hpp | 14 +- include/fs/archive_sdmc.hpp | 4 +- include/fs/archive_self_ncch.hpp | 4 +- include/fs/archive_system_save_data.hpp | 4 +- include/fs/archive_user_save_data.hpp | 7 +- include/services/fs.hpp | 6 +- src/core/fs/archive_ext_save_data.cpp | 42 +- src/core/fs/archive_ncch.cpp | 60 +- src/core/fs/archive_save_data.cpp | 38 +- src/core/fs/archive_sdmc.cpp | 17 +- src/core/fs/archive_self_ncch.cpp | 11 +- src/core/fs/archive_system_save_data.cpp | 20 +- src/core/fs/archive_user_save_data.cpp | 30 +- src/core/services/fs.cpp | 153 ++-- third_party/result/LICENSE | 201 ----- third_party/result/README.md | 132 ---- third_party/result/include/result.hpp | 910 ----------------------- 21 files changed, 349 insertions(+), 1621 deletions(-) delete mode 100644 third_party/result/LICENSE delete mode 100644 third_party/result/README.md delete mode 100644 third_party/result/include/result.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dbe438f..5c9dd83b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ else() cmake_minimum_required(VERSION 3.10) endif() -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED True) if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 12) @@ -49,7 +49,6 @@ include_directories(third_party/imgui/) include_directories(third_party/dynarmic/src) include_directories(third_party/cryptopp/) include_directories(third_party/cityhash/include) -include_directories(third_party/result/include) include_directories(third_party/xxhash/include) include_directories(third_party/httplib) include_directories(third_party/stb) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 2843be68..b613a11d 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -2,26 +2,27 @@ #include #include #include +#include #include #include #include #include #include + #include "helpers.hpp" #include "memory.hpp" -#include "result.hpp" #include "result/result.hpp" using Result::HorizonResult; namespace PathType { - enum : u32 { - Invalid = 0, - Empty = 1, - Binary = 2, - ASCII = 3, - UTF16 = 4, - }; + enum : u32 { + Invalid = 0, + Empty = 1, + Binary = 2, + ASCII = 3, + UTF16 = 4, + }; } namespace ArchiveID { @@ -42,83 +43,81 @@ namespace ArchiveID { UserSaveData2 = 0x567890B4, }; - static std::string toString(u32 id) { - switch (id) { - case SelfNCCH: return "SelfNCCH"; - case SaveData: return "SaveData"; - case ExtSaveData: return "ExtSaveData"; - case SharedExtSaveData: return "SharedExtSaveData"; - case SystemSaveData: return "SystemSaveData"; - case SDMC: return "SDMC"; - case SDMCWriteOnly: return "SDMC (Write-only)"; - case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)"; - default: return "Unknown archive"; - } - } -} + static std::string toString(u32 id) { + switch (id) { + case SelfNCCH: return "SelfNCCH"; + case SaveData: return "SaveData"; + case ExtSaveData: return "ExtSaveData"; + case SharedExtSaveData: return "SharedExtSaveData"; + case SystemSaveData: return "SystemSaveData"; + case SDMC: return "SDMC"; + case SDMCWriteOnly: return "SDMC (Write-only)"; + case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)"; + default: return "Unknown archive"; + } + } +} // namespace ArchiveID struct FSPath { - u32 type = PathType::Invalid; + u32 type = PathType::Invalid; - std::vector binary; // Path data for binary paths - std::string string; // Path data for ASCII paths - std::u16string utf16_string; + std::vector binary; // Path data for binary paths + std::string string; // Path data for ASCII paths + std::u16string utf16_string; - FSPath() {} + FSPath() {} - FSPath(u32 type, const std::vector& vec) : type(type) { - switch (type) { - case PathType::Binary: - binary = std::move(vec); - break; + FSPath(u32 type, const std::vector& vec) : type(type) { + switch (type) { + case PathType::Binary: binary = std::move(vec); break; - case PathType::ASCII: - string.resize(vec.size() - 1); // -1 because of the null terminator - std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data - break; + case PathType::ASCII: + string.resize(vec.size() - 1); // -1 because of the null terminator + std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data + break; - case PathType::UTF16: { - const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too - utf16_string.resize(size); - std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16)); - break; - } -; } - } + case PathType::UTF16: { + const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too + utf16_string.resize(size); + std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16)); + break; + }; + } + } }; struct FilePerms { - u32 raw; + 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; } + 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; - FSPath archivePath; - u32 priority = 0; // TODO: What does this even do - bool isOpen; + ArchiveBase* archive = nullptr; + FILE* fd = nullptr; // File descriptor for file sessions that require them. + FSPath path; + FSPath archivePath; + u32 priority = 0; // TODO: What does this even do + bool isOpen; - FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) : - archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {} + FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) + : archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {} - // For cloning a file session - FileSession(const FileSession& other) : archive(other.archive), path(other.path), - archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {} + // For cloning a file session + FileSession(const FileSession& other) + : archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {} }; struct ArchiveSession { - ArchiveBase* archive = nullptr; - FSPath path; - bool isOpen; + ArchiveBase* archive = nullptr; + FSPath path; + bool isOpen; - ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {} + ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {} }; struct DirectoryEntry { @@ -156,106 +155,104 @@ struct DirectorySession { using FileDescriptor = std::optional; class ArchiveBase { -public: - struct FormatInfo { - u32 size; // Archive size - u32 numOfDirectories; // Number of directories - u32 numOfFiles; // Number of files - bool duplicateData; // Whether to duplicate data or not - }; + public: + struct FormatInfo { + u32 size; // Archive size + u32 numOfDirectories; // Number of directories + u32 numOfFiles; // Number of files + bool duplicateData; // Whether to duplicate data or not + }; -protected: - using Handle = u32; + protected: + using Handle = u32; - static constexpr FileDescriptor NoFile = nullptr; - static constexpr FileDescriptor FileError = std::nullopt; - Memory& mem; + 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 - // A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs - // And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory tree - // And access files outside of the emulator's app data folder - template - bool isPathSafe(const FSPath& path) { - static_assert(format == PathType::ASCII || format == PathType::UTF16); - using String = typename std::conditional::type; // String type for the path - using Char = typename String::value_type; // Char type for the path + // Returns if a specified 3DS path in UTF16 or ASCII format is safe or not + // A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs + // And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory + // tree And access files outside of the emulator's app data folder + template + bool isPathSafe(const FSPath& path) { + static_assert(format == PathType::ASCII || format == PathType::UTF16); + using String = typename std::conditional::type; // String type for the path + using Char = typename String::value_type; // Char type for the path - String pathString, dots; - if constexpr (std::is_same()) { - pathString = path.utf16_string; - dots = u".."; - } else { - pathString = path.string; - dots = ".."; - } + String pathString, dots; + if constexpr (std::is_same()) { + pathString = path.utf16_string; + dots = u".."; + } else { + pathString = path.string; + dots = ".."; + } - // If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe - if (pathString[0] != Char('/')) return false; + // If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe + if (pathString[0] != Char('/')) return false; - // Counts how many folders sans the root our file is nested under. - // If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox. - // If it's 0 then this is the FS root. - // If it's > 0 then we're in a subdirectory of the root. - int level = 0; + // Counts how many folders sans the root our file is nested under. + // If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox. + // If it's 0 then this is the FS root. + // If it's > 0 then we're in a subdirectory of the root. + int level = 0; - // Split the string on / characters and see how many of the substrings are ".." - size_t pos = 0; - while ((pos = pathString.find(Char('/'))) != String::npos) { - String token = pathString.substr(0, pos); - pathString.erase(0, pos + 1); + // Split the string on / characters and see how many of the substrings are ".." + size_t pos = 0; + while ((pos = pathString.find(Char('/'))) != String::npos) { + String token = pathString.substr(0, pos); + pathString.erase(0, pos + 1); - if (token == dots) { - level--; - if (level < 0) return false; - } else { - level++; - } - } + if (token == dots) { + level--; + if (level < 0) return false; + } else { + level++; + } + } - return true; - } + return true; + } -public: - virtual std::string name() = 0; - virtual u64 getFreeBytes() = 0; - virtual HorizonResult createFile(const FSPath& path, u64 size) = 0; - virtual HorizonResult deleteFile(const FSPath& path) = 0; + public: + virtual std::string name() = 0; + virtual u64 getFreeBytes() = 0; + virtual HorizonResult createFile(const FSPath& path, u64 size) = 0; + virtual HorizonResult deleteFile(const FSPath& path) = 0; - virtual Rust::Result getFormatInfo(const FSPath& path) { - Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str()); - // Return a dummy struct just to avoid the UB of not returning anything, even if we panic - return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false }); - } + virtual std::expected getFormatInfo(const FSPath& path) { + Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str()); + // Return a dummy struct just to avoid the UB of not returning anything, even if we panic + return FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false}; + } - virtual HorizonResult createDirectory(const FSPath& path) { - Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); - return Result::FS::AlreadyExists; - } + virtual HorizonResult createDirectory(const FSPath& path) { + Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); + return Result::FS::AlreadyExists; + } - // 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 Rust::Result openArchive(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 std::expected openArchive(const FSPath& path) = 0; - virtual Rust::Result openDirectory(const FSPath& path) { - Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str()); - return Err(Result::FS::FileNotFoundAlt); - } + virtual std::expected openDirectory(const FSPath& path) { + Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str()); + return std::unexpected(Result::FS::FileNotFoundAlt); + } - virtual void format(const FSPath& path, const FormatInfo& info) { - Helpers::panic("Unimplemented Format for %s archive", name().c_str()); - } + virtual void format(const FSPath& path, const FormatInfo& info) { Helpers::panic("Unimplemented Format for %s archive", name().c_str()); } - virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) { + virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) { Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str()); return Result::Success; - } + } - // Read size bytes from a file starting at offset "offset" into a certain buffer in memory - // Returns the number of bytes read, or nullopt if the read failed - virtual std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0; + // Read size bytes from a file starting at offset "offset" into a certain buffer in memory + // Returns the number of bytes read, or nullopt if the read failed + virtual std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0; - ArchiveBase(Memory& mem) : mem(mem) {} + ArchiveBase(Memory& mem) : mem(mem) {} }; struct ArchiveResource { diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index 7c8c7503..5c150d9b 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -2,11 +2,13 @@ #include "archive_base.hpp" class ExtSaveDataArchive : public ArchiveBase { -public: - ExtSaveDataArchive(Memory& mem, const std::string& folder, bool isShared = false) : ArchiveBase(mem), - isShared(isShared), backingFolder(folder) {} + public: + ExtSaveDataArchive(Memory& mem, const std::string& folder, bool isShared = false) : ArchiveBase(mem), isShared(isShared), backingFolder(folder) {} - u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; } + u64 getFreeBytes() override { + Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); + return 0; + } std::string name() override { return "ExtSaveData::" + backingFolder; } HorizonResult createDirectory(const FSPath& path) override; @@ -14,14 +16,14 @@ public: HorizonResult deleteFile(const FSPath& path) override; HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) override; - Rust::Result openArchive(const FSPath& path) override; - Rust::Result openDirectory(const FSPath& path) override; + std::expected openArchive(const FSPath& path) override; + std::expected openDirectory(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; - Rust::Result getFormatInfo(const FSPath& path) override { + std::expected getFormatInfo(const FSPath& path) override { Helpers::warn("Stubbed ExtSaveData::GetFormatInfo"); - return Ok(FormatInfo{.size = 1_GB, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false}); + return FormatInfo{.size = 1_GB, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false}; } // Takes in a binary ExtSaveData path, outputs a combination of the backing folder with the low and high save entries of the path @@ -29,5 +31,5 @@ public: std::string getExtSaveDataPathFromBinary(const FSPath& path); bool isShared = false; - std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path. + std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path. }; \ No newline at end of file diff --git a/include/fs/archive_ncch.hpp b/include/fs/archive_ncch.hpp index 275bcd20..ebe42c8b 100644 --- a/include/fs/archive_ncch.hpp +++ b/include/fs/archive_ncch.hpp @@ -2,16 +2,19 @@ #include "archive_base.hpp" class NCCHArchive : public ArchiveBase { -public: + public: NCCHArchive(Memory& mem) : ArchiveBase(mem) {} - u64 getFreeBytes() override { Helpers::panic("NCCH::GetFreeBytes unimplemented"); return 0; } + u64 getFreeBytes() override { + Helpers::panic("NCCH::GetFreeBytes unimplemented"); + return 0; + } std::string name() override { return "NCCH"; } HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; - Rust::Result openArchive(const FSPath& path) override; + std::expected 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; diff --git a/include/fs/archive_save_data.hpp b/include/fs/archive_save_data.hpp index 5b1ba489..50b604c8 100644 --- a/include/fs/archive_save_data.hpp +++ b/include/fs/archive_save_data.hpp @@ -2,7 +2,7 @@ #include "archive_base.hpp" class SaveDataArchive : public ArchiveBase { -public: + public: SaveDataArchive(Memory& mem) : ArchiveBase(mem) {} u64 getFreeBytes() override { return 32_MB; } @@ -12,21 +12,19 @@ public: HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; - Rust::Result openArchive(const FSPath& path) override; - Rust::Result openDirectory(const FSPath& path) override; + std::expected openArchive(const FSPath& path) override; + std::expected openDirectory(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; void format(const FSPath& path, const FormatInfo& info) override; - Rust::Result getFormatInfo(const FSPath& path) override; + std::expected getFormatInfo(const FSPath& path) override; - std::filesystem::path getFormatInfoPath() { - return IOFile::getAppData() / "FormatInfo" / "SaveData.format"; - } + std::filesystem::path getFormatInfoPath() { return IOFile::getAppData() / "FormatInfo" / "SaveData.format"; } // Returns whether the cart has save data or not bool cartHasSaveData() { auto cxi = mem.getCXI(); - return (cxi != nullptr && cxi->hasSaveData()); // We need to have a CXI file with more than 0 bytes of save data + return (cxi != nullptr && cxi->hasSaveData()); // We need to have a CXI file with more than 0 bytes of save data } }; \ No newline at end of file diff --git a/include/fs/archive_sdmc.hpp b/include/fs/archive_sdmc.hpp index f63731c4..f0a13889 100644 --- a/include/fs/archive_sdmc.hpp +++ b/include/fs/archive_sdmc.hpp @@ -17,8 +17,8 @@ class SDMCArchive : public ArchiveBase { HorizonResult deleteFile(const FSPath& path) override; HorizonResult createDirectory(const FSPath& path) override; - Rust::Result openArchive(const FSPath& path) override; - Rust::Result openDirectory(const FSPath& path) override; + std::expected openArchive(const FSPath& path) override; + std::expected openDirectory(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; diff --git a/include/fs/archive_self_ncch.hpp b/include/fs/archive_self_ncch.hpp index ed882a7d..053ee4a5 100644 --- a/include/fs/archive_self_ncch.hpp +++ b/include/fs/archive_self_ncch.hpp @@ -2,7 +2,7 @@ #include "archive_base.hpp" class SelfNCCHArchive : public ArchiveBase { -public: + public: SelfNCCHArchive(Memory& mem) : ArchiveBase(mem) {} u64 getFreeBytes() override { return 0; } @@ -11,7 +11,7 @@ public: HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; - Rust::Result openArchive(const FSPath& path) override; + std::expected 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; diff --git a/include/fs/archive_system_save_data.hpp b/include/fs/archive_system_save_data.hpp index c4526bec..0d374238 100644 --- a/include/fs/archive_system_save_data.hpp +++ b/include/fs/archive_system_save_data.hpp @@ -16,8 +16,8 @@ class SystemSaveDataArchive : public ArchiveBase { HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; - Rust::Result openArchive(const FSPath& path) override; - Rust::Result openDirectory(const FSPath& path) override; + std::expected openArchive(const FSPath& path) override; + std::expected openDirectory(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; diff --git a/include/fs/archive_user_save_data.hpp b/include/fs/archive_user_save_data.hpp index 56e2c0f6..58db296d 100644 --- a/include/fs/archive_user_save_data.hpp +++ b/include/fs/archive_user_save_data.hpp @@ -3,6 +3,7 @@ class UserSaveDataArchive : public ArchiveBase { u32 archiveID; + public: UserSaveDataArchive(Memory& mem, u32 archiveID) : ArchiveBase(mem), archiveID(archiveID) {} @@ -13,13 +14,13 @@ class UserSaveDataArchive : public ArchiveBase { HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; - Rust::Result openArchive(const FSPath& path) override; - Rust::Result openDirectory(const FSPath& path) override; + std::expected openArchive(const FSPath& path) override; + std::expected openDirectory(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; void format(const FSPath& path, const FormatInfo& info) override; - Rust::Result getFormatInfo(const FSPath& path) override; + std::expected getFormatInfo(const FSPath& path) override; std::filesystem::path getFormatInfoPath() { return IOFile::getAppData() / "FormatInfo" / "SaveData.format"; } diff --git a/include/services/fs.hpp b/include/services/fs.hpp index 4a613121..2f721f9d 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -38,8 +38,8 @@ class FSService { SystemSaveDataArchive systemSaveData; ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath); - Rust::Result openArchiveHandle(u32 archiveID, const FSPath& path); - Rust::Result openDirectoryHandle(ArchiveBase* archive, const FSPath& path); + std::expected openArchiveHandle(u32 archiveID, const FSPath& path); + std::expected openDirectoryHandle(ArchiveBase* archive, const FSPath& path); std::optional openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms); FSPath readPath(u32 type, u32 pointer, u32 size); @@ -81,7 +81,7 @@ class FSService { // Used for set/get priority: Not sure what sort of priority this is referring to u32 priority; -public: + public: FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config) : mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem), sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 4b57f245..b84242d7 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -1,21 +1,19 @@ #include "fs/archive_ext_save_data.hpp" + #include namespace fs = std::filesystem; HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) { - if (size == 0) - Helpers::panic("ExtSaveData file does not support size == 0"); + 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"); + if (!isPathSafe(path)) Helpers::panic("Unsafe path in ExtSaveData::CreateFile"); fs::path p = IOFile::getAppData() / backingFolder; p += fs::path(path.utf16_string).make_preferred(); - if (fs::exists(p)) - return Result::FS::AlreadyExists; + if (fs::exists(p)) return Result::FS::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"); @@ -34,8 +32,7 @@ HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) { HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) { if (path.type == PathType::UTF16) { - if (!isPathSafe(path)) - Helpers::panic("Unsafe path in ExtSaveData::DeleteFile"); + if (!isPathSafe(path)) Helpers::panic("Unsafe path in ExtSaveData::DeleteFile"); fs::path p = IOFile::getAppData() / backingFolder; p += fs::path(path.utf16_string).make_preferred(); @@ -66,17 +63,15 @@ HorizonResult ExtSaveDataArchive::deleteFile(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 (!isPathSafe(path)) Helpers::panic("Unsafe path in ExtSaveData::OpenFile"); - if (perms.create()) - Helpers::panic("[ExtSaveData] Can't open file with create flag"); + if (perms.create()) Helpers::panic("[ExtSaveData] Can't open file with create flag"); fs::path p = IOFile::getAppData() / backingFolder; p += fs::path(path.utf16_string).make_preferred(); - 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 OpenFlags field and always opens as r+b? TODO: Check + 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 OpenFlags field and always opens as r+b? TODO: Check return file.isOpen() ? file.getHandle() : FileError; } else { return FileError; @@ -155,7 +150,7 @@ std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) return backingFolder + std::to_string(saveLow) + std::to_string(saveHigh); } -Rust::Result ExtSaveDataArchive::openArchive(const FSPath& path) { +std::expected ExtSaveDataArchive::openArchive(const FSPath& path) { if (path.type != PathType::Binary || path.binary.size() != 12) { Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive"); } @@ -165,34 +160,33 @@ Rust::Result ExtSaveDataArchive::openArchive(const // fs::path formatInfopath = IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format"); // Format info not found so the archive is not formatted // if (!fs::is_regular_file(formatInfopath)) { - // return isShared ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid); + // return isShared ? std::unexpected(Result::FS::NotFormatted) : std::unexpected(Result::FS::NotFoundInvalid); //} - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } -Rust::Result ExtSaveDataArchive::openDirectory(const FSPath& path) { +std::expected ExtSaveDataArchive::openDirectory(const FSPath& path) { if (path.type == PathType::UTF16) { - if (!isPathSafe(path)) - Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory"); + if (!isPathSafe(path)) Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory"); fs::path p = IOFile::getAppData() / backingFolder; p += fs::path(path.utf16_string).make_preferred(); if (fs::is_regular_file(p)) { printf("ExtSaveData: OpenArchive used with a file path"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (fs::is_directory(p)) { - return Ok(DirectorySession(this, p)); + return DirectorySession(this, p); } else { - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } } Helpers::panic("ExtSaveDataArchive::OpenDirectory: Unimplemented path type"); - return Err(Result::Success); + return std::unexpected(Result::Success); } std::optional ExtSaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/fs/archive_ncch.cpp b/src/core/fs/archive_ncch.cpp index d5a4bab5..d6a165fe 100644 --- a/src/core/fs/archive_ncch.cpp +++ b/src/core/fs/archive_ncch.cpp @@ -1,9 +1,11 @@ #include "fs/archive_ncch.hpp" + +#include +#include + #include "fs/bad_word_list.hpp" #include "fs/country_list.hpp" #include "fs/mii_data.hpp" -#include -#include namespace PathType { enum : u32 { @@ -14,11 +16,7 @@ namespace PathType { }; namespace MediaType { - enum : u8 { - NAND = 0, - SD = 1, - Gamecard = 2 - }; + enum : u8 { NAND = 0, SD = 1, Gamecard = 2 }; }; HorizonResult NCCHArchive::createFile(const FSPath& path, u64 size) { @@ -36,39 +34,36 @@ FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms) Helpers::panic("NCCHArchive::OpenFile: Invalid path"); } - const u32 media = *(u32*)&path.binary[0]; // 0 for NCCH, 1 for SaveData - if (media != 0) - Helpers::panic("NCCHArchive::OpenFile: Tried to read non-NCCH file"); + const u32 media = *(u32*)&path.binary[0]; // 0 for NCCH, 1 for SaveData + if (media != 0) Helpers::panic("NCCHArchive::OpenFile: Tried to read non-NCCH file"); // Third word of the binary path indicates what we're reading from. const u32 type = *(u32*)&path.binary[8]; - if (media == 0 && type > 2) - Helpers::panic("NCCHArchive::OpenFile: Invalid file path type"); + if (media == 0 && type > 2) Helpers::panic("NCCHArchive::OpenFile: Invalid file path type"); return NoFile; } -Rust::Result NCCHArchive::openArchive(const FSPath& path) { +std::expected NCCHArchive::openArchive(const FSPath& path) { if (path.type != PathType::Binary || path.binary.size() != 16) { Helpers::panic("NCCHArchive::OpenArchive: Invalid path"); } const u32 mediaType = path.binary[8]; - if (mediaType != 0) - Helpers::panic("NCCH archive. Tried to access a mediatype other than the NAND. Type: %d", mediaType); + if (mediaType != 0) Helpers::panic("NCCH archive. Tried to access a mediatype other than the NAND. Type: %d", mediaType); - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } std::optional NCCHArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { - const auto& path = file->path.binary; // Path of the file - const auto& archivePath = file->archivePath.binary; // Path of the archive + const auto& path = file->path.binary; // Path of the file + const auto& archivePath = file->archivePath.binary; // Path of the archive const auto mediaType = archivePath[8]; - const auto media = *(u32*)&path[0]; // 0 for NCCH, 1 for savedata + const auto media = *(u32*)&path[0]; // 0 for NCCH, 1 for savedata const auto partition = *(u32*)&path[4]; - const auto type = *(u32*)&path[8]; // Type of the path + const auto type = *(u32*)&path[8]; // Type of the path if (mediaType == MediaType::NAND) { const u32 lowProgramID = *(u32*)&archivePath[0]; @@ -87,26 +82,29 @@ std::optional NCCHArchive::readFile(FileSession* file, u64 offset, u32 size std::vector fileData; if (highProgramID == sharedDataArchive) { - if (lowProgramID == miiData) fileData = std::vector(std::begin(MII_DATA), std::end(MII_DATA)); - else if (lowProgramID == regionManifest) fileData = std::vector(std::begin(COUNTRY_LIST_DATA), std::end(COUNTRY_LIST_DATA)); + if (lowProgramID == miiData) + fileData = std::vector(std::begin(MII_DATA), std::end(MII_DATA)); + else if (lowProgramID == regionManifest) + fileData = std::vector(std::begin(COUNTRY_LIST_DATA), std::end(COUNTRY_LIST_DATA)); else if (lowProgramID == tlsRootCertificates) { Helpers::warn("Read from Shared Data archive 00010602"); return 0; - } - else Helpers::panic("[NCCH archive] Read unimplemented NAND file. ID: %08X", lowProgramID); + } else + Helpers::panic("[NCCH archive] Read unimplemented NAND file. ID: %08X", lowProgramID); } else if (highProgramID == systemDataArchive && lowProgramID == badWordList) { fileData = std::vector(std::begin(BAD_WORD_LIST_DATA), std::end(BAD_WORD_LIST_DATA)); } else { - Helpers::panic("[NCCH archive] Read from unimplemented NCCH archive file. High program ID: %08X, low ID: %08X", - highProgramID, lowProgramID); + Helpers::panic( + "[NCCH archive] Read from unimplemented NCCH archive file. High program ID: %08X, low ID: %08X", highProgramID, lowProgramID + ); } if (offset >= fileData.size()) { Helpers::panic("[NCCH archive] Out of bounds read from NAND file"); } - u32 availableBytes = u32(fileData.size() - offset); // How many bytes we can read from the file - u32 bytesRead = std::min(size, availableBytes); // Cap the amount of bytes to read if we're going to go out of bounds + u32 availableBytes = u32(fileData.size() - offset); // How many bytes we can read from the file + u32 bytesRead = std::min(size, availableBytes); // Cap the amount of bytes to read if we're going to go out of bounds for (u32 i = 0; i < bytesRead; i++) { mem.write8(dataPointer + i, fileData[offset + i]); } @@ -117,8 +115,7 @@ std::optional NCCHArchive::readFile(FileSession* file, u64 offset, u32 size } // Code below is for mediaType == 2 (gamecard). Currently unused - if (partition != 0) - Helpers::panic("[NCCH] Tried to read from non-zero partition"); + if (partition != 0) Helpers::panic("[NCCH] Tried to read from non-zero partition"); if (type == PathType::RomFS && !hasRomFS()) { Helpers::panic("Tried to read file from non-existent RomFS"); @@ -150,8 +147,7 @@ std::optional NCCHArchive::readFile(FileSession* file, u64 offset, u32 size break; } - default: - Helpers::panic("Unimplemented file path type for NCCH archive"); + default: Helpers::panic("Unimplemented file path type for NCCH archive"); } std::unique_ptr data(new u8[size]); diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index 0bdb9e01..0f287480 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -1,4 +1,5 @@ #include "fs/archive_save_data.hpp" + #include #include @@ -6,8 +7,7 @@ namespace fs = std::filesystem; HorizonResult SaveDataArchive::createFile(const FSPath& path, u64 size) { if (path.type == PathType::UTF16) { - if (!isPathSafe(path)) - Helpers::panic("Unsafe path in SaveData::CreateFile"); + if (!isPathSafe(path)) Helpers::panic("Unsafe path in SaveData::CreateFile"); fs::path p = IOFile::getAppData() / "SaveData"; p += fs::path(path.utf16_string).make_preferred(); @@ -110,16 +110,16 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe const char* permString = perms.write() ? "r+b" : "rb"; - if (fs::exists(p)) { // Return file descriptor if the file exists + if (fs::exists(p)) { // Return file descriptor if the file exists IOFile file(p.string().c_str(), permString); return file.isOpen() ? file.getHandle() : FileError; } else { // If the file is not found, create it if the create flag is on if (perms.create()) { - IOFile file(p.string().c_str(), "wb"); // Create file - file.close(); // Close it + IOFile file(p.string().c_str(), "wb"); // Create file + file.close(); // Close it - file.open(p.string().c_str(), permString); // Reopen with proper perms + file.open(p.string().c_str(), permString); // Reopen with proper perms return file.isOpen() ? file.getHandle() : FileError; } else { return FileError; @@ -131,7 +131,7 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe return FileError; } -Rust::Result SaveDataArchive::openDirectory(const FSPath& path) { +std::expected SaveDataArchive::openDirectory(const FSPath& path) { if (path.type == PathType::UTF16) { if (!isPathSafe(path)) { Helpers::panic("Unsafe path in SaveData::OpenDirectory"); @@ -142,27 +142,27 @@ Rust::Result SaveDataArchive::openDirectory(con if (fs::is_regular_file(p)) { printf("SaveData: OpenDirectory used with a file path"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (fs::is_directory(p)) { - return Ok(DirectorySession(this, p)); + return DirectorySession(this, p); } else { - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } } Helpers::panic("SaveDataArchive::OpenDirectory: Unimplemented path type"); - return Err(Result::Success); + return std::unexpected(Result::Success); } -Rust::Result SaveDataArchive::getFormatInfo(const FSPath& path) { +std::expected SaveDataArchive::getFormatInfo(const FSPath& path) { const fs::path formatInfoPath = getFormatInfoPath(); IOFile file(formatInfoPath, "rb"); // If the file failed to open somehow, we return that the archive is not formatted if (!file.isOpen()) { - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } FormatInfo ret; @@ -171,10 +171,10 @@ Rust::Result SaveDataArchive::getFormatI if (!success || bytesRead != sizeof(FormatInfo)) { Helpers::warn("SaveData::GetFormatInfo: Format file exists but was not properly read into the FormatInfo struct"); - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } - return Ok(ret); + return ret; } void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& info) { @@ -192,19 +192,19 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& file.close(); } -Rust::Result SaveDataArchive::openArchive(const FSPath& path) { +std::expected SaveDataArchive::openArchive(const FSPath& path) { if (path.type != PathType::Empty) { Helpers::panic("Unimplemented path type for SaveData archive: %d\n", path.type); - return Err(Result::FS::NotFoundInvalid); + return std::unexpected(Result::FS::NotFoundInvalid); } const fs::path formatInfoPath = getFormatInfoPath(); // Format info not found so the archive is not formatted if (!fs::is_regular_file(formatInfoPath)) { - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } std::optional SaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/fs/archive_sdmc.cpp b/src/core/fs/archive_sdmc.cpp index 6c34de7a..b6c75b67 100644 --- a/src/core/fs/archive_sdmc.cpp +++ b/src/core/fs/archive_sdmc.cpp @@ -1,4 +1,5 @@ #include "fs/archive_sdmc.hpp" + #include namespace fs = std::filesystem; @@ -137,10 +138,10 @@ HorizonResult SDMCArchive::createDirectory(const FSPath& path) { return success ? Result::Success : Result::FS::UnexpectedFileOrDir; } -Rust::Result SDMCArchive::openDirectory(const FSPath& path) { +std::expected SDMCArchive::openDirectory(const FSPath& path) { if (isWriteOnly) { Helpers::warn("SDMC: OpenDirectory is not allowed in SDMC Write-Only archive"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (path.type == PathType::UTF16) { @@ -153,27 +154,27 @@ Rust::Result SDMCArchive::openDirectory(const F if (fs::is_regular_file(p)) { printf("SDMC: OpenDirectory used with a file path"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (fs::is_directory(p)) { - return Ok(DirectorySession(this, p)); + return DirectorySession(this, p); } else { - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } } Helpers::panic("SDMCArchive::OpenDirectory: Unimplemented path type"); - return Err(Result::Success); + return std::unexpected(Result::Success); } -Rust::Result SDMCArchive::openArchive(const FSPath& path) { +std::expected SDMCArchive::openArchive(const FSPath& path) { // TODO: Fail here if the SD is disabled in the connfig. if (path.type != PathType::Empty) { Helpers::panic("Unimplemented path type for SDMC::OpenArchive"); } - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } std::optional SDMCArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/fs/archive_self_ncch.cpp b/src/core/fs/archive_self_ncch.cpp index 9369152d..70bba828 100644 --- a/src/core/fs/archive_self_ncch.cpp +++ b/src/core/fs/archive_self_ncch.cpp @@ -1,4 +1,5 @@ #include "fs/archive_self_ncch.hpp" + #include // The part of the NCCH archive we're trying to access. Depends on the first 4 bytes of the binary file path @@ -33,21 +34,21 @@ FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& pe // Where to read the file from. (https://www.3dbrew.org/wiki/Filesystem_services#SelfNCCH_File_Path_Data_Format) // We currently only know how to read from an NCCH's RomFS, ie type = 0 - const u32 type = *(u32*)&path.binary[0]; // TODO: Get rid of UB here + const u32 type = *(u32*)&path.binary[0]; // TODO: Get rid of UB here if (type != PathType::RomFS && type != PathType::ExeFS && type != PathType::UpdateRomFS) { Helpers::panic("Read from NCCH's non-RomFS & non-exeFS section! Path type: %d", type); } - return NoFile; // No file descriptor needed for RomFS + return NoFile; // No file descriptor needed for RomFS } -Rust::Result SelfNCCHArchive::openArchive(const FSPath& path) { +std::expected SelfNCCHArchive::openArchive(const FSPath& path) { if (path.type != PathType::Empty) { Helpers::panic("Invalid path type for SelfNCCH archive: %d\n", path.type); - return Err(Result::FS::NotFoundInvalid); + return std::unexpected(Result::FS::NotFoundInvalid); } - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/fs/archive_system_save_data.cpp b/src/core/fs/archive_system_save_data.cpp index 37afb431..5264cd3f 100644 --- a/src/core/fs/archive_system_save_data.cpp +++ b/src/core/fs/archive_system_save_data.cpp @@ -1,14 +1,15 @@ -#include #include "fs/archive_system_save_data.hpp" +#include + namespace fs = std::filesystem; -Rust::Result SystemSaveDataArchive::openArchive(const FSPath& path) { +std::expected SystemSaveDataArchive::openArchive(const FSPath& path) { if (path.type != PathType::Binary) { Helpers::panic("Unimplemented path type for SystemSaveData::OpenArchive"); } - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } FileDescriptor SystemSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) { @@ -108,7 +109,6 @@ HorizonResult SystemSaveDataArchive::createDirectory(const FSPath& path) { } } - HorizonResult SystemSaveDataArchive::deleteFile(const FSPath& path) { if (path.type == PathType::UTF16) { if (!isPathSafe(path)) { @@ -142,11 +142,11 @@ HorizonResult SystemSaveDataArchive::deleteFile(const FSPath& path) { return Result::Success; } -Rust::Result SystemSaveDataArchive::openDirectory(const FSPath& path) { +std::expected SystemSaveDataArchive::openDirectory(const FSPath& path) { if (path.type == PathType::UTF16) { if (!isPathSafe(path)) { Helpers::warn("Unsafe path in SystemSaveData::OpenDirectory"); - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } fs::path p = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData"; @@ -154,16 +154,16 @@ Rust::Result SystemSaveDataArchive::openDirecto if (fs::is_regular_file(p)) { printf("SystemSaveData: OpenDirectory used with a file path"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (fs::is_directory(p)) { - return Ok(DirectorySession(this, p)); + return DirectorySession(this, p); } else { - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } } Helpers::panic("SystemSaveData::OpenDirectory: Unimplemented path type"); - return Err(Result::Success); + return std::unexpected(Result::Success); } \ No newline at end of file diff --git a/src/core/fs/archive_user_save_data.cpp b/src/core/fs/archive_user_save_data.cpp index cba9bff8..20c75145 100644 --- a/src/core/fs/archive_user_save_data.cpp +++ b/src/core/fs/archive_user_save_data.cpp @@ -1,8 +1,8 @@ +#include "fs/archive_user_save_data.hpp" + #include #include -#include "fs/archive_user_save_data.hpp" - namespace fs = std::filesystem; HorizonResult UserSaveDataArchive::createFile(const FSPath& path, u64 size) { @@ -118,7 +118,7 @@ FileDescriptor UserSaveDataArchive::openFile(const FSPath& path, const FilePerms return FileError; } -Rust::Result UserSaveDataArchive::openDirectory(const FSPath& path) { +std::expected UserSaveDataArchive::openDirectory(const FSPath& path) { if (path.type == PathType::UTF16) { if (!isPathSafe(path)) Helpers::panic("Unsafe path in UserSaveData::OpenDirectory"); @@ -127,27 +127,27 @@ Rust::Result UserSaveDataArchive::openDirectory if (fs::is_regular_file(p)) { printf("SaveData: OpenDirectory used with a file path"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (fs::is_directory(p)) { - return Ok(DirectorySession(this, p)); + return DirectorySession(this, p); } else { - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } } Helpers::panic("UserSaveDataArchive::OpenDirectory: Unimplemented path type"); - return Err(Result::Success); + return std::unexpected(Result::Success); } -Rust::Result UserSaveDataArchive::getFormatInfo(const FSPath& path) { +std::expected UserSaveDataArchive::getFormatInfo(const FSPath& path) { const fs::path formatInfoPath = getFormatInfoPath(); IOFile file(formatInfoPath, "rb"); // If the file failed to open somehow, we return that the archive is not formatted if (!file.isOpen()) { - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } FormatInfo ret; @@ -156,10 +156,10 @@ Rust::Result UserSaveDataArchive::getFor if (!success || bytesRead != sizeof(FormatInfo)) { Helpers::warn("UserSaveData::GetFormatInfo: Format file exists but was not properly read into the FormatInfo struct"); - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } - return Ok(ret); + return ret; } void UserSaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& info) { @@ -177,19 +177,19 @@ void UserSaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatIn file.close(); } -Rust::Result UserSaveDataArchive::openArchive(const FSPath& path) { +std::expected UserSaveDataArchive::openArchive(const FSPath& path) { if (path.type != PathType::Binary) { Helpers::panic("Unimplemented path type for UserSaveData archive: %d\n", path.type); - return Err(Result::FS::NotFoundInvalid); + return std::unexpected(Result::FS::NotFoundInvalid); } const fs::path formatInfoPath = getFormatInfoPath(); // Format info not found so the archive is not formatted if (!fs::is_regular_file(formatInfoPath)) { - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } std::optional UserSaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 2e102958..aa044a53 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -1,10 +1,11 @@ #include "services/fs.hpp" -#include "kernel/kernel.hpp" + #include "io_file.hpp" #include "ipc.hpp" +#include "kernel/kernel.hpp" #include "result/result.hpp" -#ifdef CreateFile // windows.h defines CreateFile & DeleteFile because of course it does. +#ifdef CreateFile // windows.h defines CreateFile & DeleteFile because of course it does. #undef CreateDirectory #undef CreateFile #undef DeleteFile @@ -47,21 +48,18 @@ namespace FSCommands { }; } -void FSService::reset() { - priority = 0; -} +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 sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory + const auto sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory const auto nandSharedpath = IOFile::getAppData() / ".." / "SharedFiles" / "NAND"; - const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData - const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info + const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData + const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info const auto systemSaveDataPath = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData"; namespace fs = std::filesystem; - if (!fs::is_directory(nandSharedpath)) { fs::create_directories(nandSharedpath); } @@ -89,25 +87,21 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { case ArchiveID::SaveData: return &saveData; case ArchiveID::UserSaveData2: return &userSaveData2; - case ArchiveID::ExtSaveData: - return &extSaveData_sdmc; + case ArchiveID::ExtSaveData: return &extSaveData_sdmc; - case ArchiveID::SharedExtSaveData: - return &sharedExtSaveData_nand; + case ArchiveID::SharedExtSaveData: return &sharedExtSaveData_nand; case ArchiveID::SystemSaveData: return &systemSaveData; case ArchiveID::SDMC: return &sdmc; case ArchiveID::SDMCWriteOnly: return &sdmcWriteOnly; - case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI - default: - Helpers::panic("Unknown archive. ID: %d\n", id); - return nullptr; + case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI + default: Helpers::panic("Unknown archive. ID: %d\n", id); return nullptr; } } std::optional FSService::openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, 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 + 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]; @@ -119,37 +113,36 @@ std::optional FSService::openFileHandle(ArchiveBase* archive, const FSPa } } -Rust::Result FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) { - Rust::Result opened = archive->openDirectory(path); - if (opened.isOk()) { // If opened doesn't have a value, we failed to open the directory +std::expected FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) { + std::expected opened = archive->openDirectory(path); + if (opened) { // If opened doesn't have a value, we failed to open the directory auto handle = kernel.makeObject(KernelObjectType::Directory); auto& object = kernel.getObjects()[handle]; - object.data = new DirectorySession(opened.unwrap()); + object.data = new DirectorySession(opened.value()); - return Ok(handle); + return handle; } else { - return Err(opened.unwrapErr()); + return std::unexpected(opened.error()); } } -Rust::Result FSService::openArchiveHandle(u32 archiveID, const FSPath& path) { +std::expected FSService::openArchiveHandle(u32 archiveID, const FSPath& path) { ArchiveBase* archive = getArchiveFromID(archiveID, path); if (archive == nullptr) [[unlikely]] { Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID); - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } - Rust::Result res = archive->openArchive(path); - if (res.isOk()) { + std::expected res = archive->openArchive(path); + if (res.has_value()) { auto handle = kernel.makeObject(KernelObjectType::Archive); auto& archiveObject = kernel.getObjects()[handle]; - archiveObject.data = new ArchiveSession(res.unwrap(), path); + archiveObject.data = new ArchiveSession(res.value(), path); - return Ok(handle); - } - else { - return Err(res.unwrapErr()); + return handle; + } else { + return std::unexpected(res.error()); } } @@ -157,8 +150,7 @@ FSPath FSService::readPath(u32 type, u32 pointer, u32 size) { std::vector data; data.resize(size); - for (u32 i = 0; i < size; i++) - data[i] = mem.read8(pointer + i); + for (u32 i = 0; i < size; i++) data[i] = mem.read8(pointer + i); return FSPath(type, data); } @@ -217,7 +209,7 @@ void FSService::initializeWithSdkVersion(u32 messagePointer) { } void FSService::closeArchive(u32 messagePointer) { - const Handle handle = static_cast(mem.read64(messagePointer + 4)); // TODO: archive handles should be 64-bit + const Handle handle = static_cast(mem.read64(messagePointer + 4)); // TODO: archive handles should be 64-bit const auto object = kernel.getObject(handle, KernelObjectType::Archive); log("FSService::CloseArchive(handle = %X)\n", handle); @@ -241,14 +233,14 @@ void FSService::openArchive(u32 messagePointer) { auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize); log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType); - Rust::Result res = openArchiveHandle(archiveID, archivePath); + std::expected res = openArchiveHandle(archiveID, archivePath); mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0)); - if (res.isOk()) { + if (res.has_value()) { mem.write32(messagePointer + 4, Result::Success); - mem.write64(messagePointer + 8, res.unwrap()); + mem.write64(messagePointer + 8, res.value()); } else { - log("FS::OpenArchive: Failed to open archive with id = %d. Error %08X\n", archiveID, (u32)res.unwrapErr()); - mem.write32(messagePointer + 4, res.unwrapErr()); + log("FS::OpenArchive: Failed to open archive with id = %d. Error %08X\n", archiveID, (u32)res.error()); + mem.write32(messagePointer + 4, res.error()); mem.write64(messagePointer + 8, 0); } } @@ -283,7 +275,7 @@ void FSService::openFile(u32 messagePointer) { mem.write32(messagePointer + 4, Result::FS::FileNotFound); } else { mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor" + mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor" mem.write32(messagePointer + 12, handle.value()); } } @@ -330,12 +322,12 @@ void FSService::openDirectory(u32 messagePointer) { auto dir = openDirectoryHandle(archive, dirPath); mem.write32(messagePointer, IPC::responseHeader(0x80B, 1, 2)); - if (dir.isOk()) { + if (dir.has_value()) { mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 12, dir.unwrap()); + mem.write32(messagePointer + 12, dir.value()); } else { printf("FS::OpenDirectory failed\n"); - mem.write32(messagePointer + 4, static_cast(dir.unwrapErr())); + mem.write32(messagePointer + 4, static_cast(dir.error())); } } @@ -360,11 +352,11 @@ void FSService::openFileDirectly(u32 messagePointer) { auto filePath = readPath(filePathType, filePathPointer, filePathSize); const FilePerms perms(openFlags); - Rust::Result res = archive->openArchive(archivePath); - if (res.isErr()) [[unlikely]] { + std::expected res = archive->openArchive(archivePath); + if (!res.has_value()) [[unlikely]] { Helpers::panic("OpenFileDirectly: Failed to open archive with given path"); } - archive = res.unwrap(); + archive = res.value(); std::optional handle = openFileHandle(archive, filePath, archivePath, perms); mem.write32(messagePointer, IPC::responseHeader(0x803, 1, 2)); @@ -451,18 +443,18 @@ void FSService::getFormatInfo(u32 messagePointer) { } mem.write32(messagePointer, IPC::responseHeader(0x845, 5, 0)); - Rust::Result res = archive->getFormatInfo(path); + std::expected res = archive->getFormatInfo(path); // If the FormatInfo was returned, write them to the output buffer. Otherwise, write an error code. - if (res.isOk()) { - ArchiveBase::FormatInfo info = res.unwrap(); + if (res.has_value()) { + ArchiveBase::FormatInfo info = res.value(); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, info.size); mem.write32(messagePointer + 12, info.numOfDirectories); mem.write32(messagePointer + 16, info.numOfFiles); mem.write8(messagePointer + 20, info.duplicateData ? 1 : 0); } else { - mem.write32(messagePointer + 4, static_cast(res.unwrapErr())); + mem.write32(messagePointer + 4, static_cast(res.error())); } } @@ -470,8 +462,7 @@ void FSService::formatSaveData(u32 messagePointer) { log("FS::FormatSaveData\n"); const u32 archiveID = mem.read32(messagePointer + 4); - if (archiveID != ArchiveID::SaveData) - Helpers::panic("FS::FormatSaveData: Archive is not SaveData"); + if (archiveID != ArchiveID::SaveData) Helpers::panic("FS::FormatSaveData: Archive is not SaveData"); // Read path and path info const u32 pathType = mem.read32(messagePointer + 8); @@ -481,21 +472,15 @@ void FSService::formatSaveData(u32 messagePointer) { // Size of a block. Seems to always be 0x200 const u32 blockSize = mem.read32(messagePointer + 16); - if (blockSize != 0x200 && blockSize != 0x1000) - Helpers::panic("FS::FormatSaveData: Invalid SaveData block size"); + if (blockSize != 0x200 && blockSize != 0x1000) Helpers::panic("FS::FormatSaveData: Invalid SaveData block size"); - const u32 directoryNum = mem.read32(messagePointer + 20); // Max number of directories - const u32 fileNum = mem.read32(messagePointer + 24); // Max number of files - const u32 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...? - const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here + const u32 directoryNum = mem.read32(messagePointer + 20); // Max number of directories + const u32 fileNum = mem.read32(messagePointer + 24); // Max number of files + const u32 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...? + const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here const bool duplicateData = mem.read8(messagePointer + 36) != 0; - ArchiveBase::FormatInfo info { - .size = blockSize * 0x200, - .numOfDirectories = directoryNum, - .numOfFiles = fileNum, - .duplicateData = duplicateData - }; + ArchiveBase::FormatInfo info{.size = blockSize * 0x200, .numOfDirectories = directoryNum, .numOfFiles = fileNum, .duplicateData = duplicateData}; saveData.format(path, info); @@ -512,8 +497,8 @@ void FSService::deleteExtSaveData(u32 messagePointer) { log("FS::DeleteExtSaveData (media type = %d, saveID = %llx) (stubbed)\n", mediaType, saveID); mem.write32(messagePointer, IPC::responseHeader(0x0852, 1, 0)); - // TODO: We can't properly implement this yet until we properly support title/save IDs. We will stub this and insert a warning for now. Required for Planet Robobot - // When we properly implement it, it will just be a recursive directory deletion + // TODO: We can't properly implement this yet until we properly support title/save IDs. We will stub this and insert a warning for now. Required + // for Planet Robobot When we properly implement it, it will just be a recursive directory deletion mem.write32(messagePointer + 4, Result::Success); } @@ -521,7 +506,8 @@ void FSService::createExtSaveData(u32 messagePointer) { Helpers::warn("Stubbed call to FS::CreateExtSaveData!"); // First 4 words of parameters are the ExtSaveData info // https://www.3dbrew.org/wiki/Filesystem_services#ExtSaveDataInfo - // This creates the ExtSaveData with the specified saveid in the specified media type. It stores the SMDH as "icon" in the root of the created directory. + // This creates the ExtSaveData with the specified saveid in the specified media type. It stores the SMDH as "icon" in the root of the created + // directory. const u8 mediaType = mem.read8(messagePointer + 4); const u64 saveID = mem.read64(messagePointer + 8); const u32 numOfDirectories = mem.read32(messagePointer + 20); @@ -541,18 +527,13 @@ void FSService::formatThisUserSaveData(u32 messagePointer) { log("FS::FormatThisUserSaveData\n"); const u32 blockSize = mem.read32(messagePointer + 4); - const u32 directoryNum = mem.read32(messagePointer + 8); // Max number of directories - const u32 fileNum = mem.read32(messagePointer + 12); // Max number of files - const u32 directoryBucketNum = mem.read32(messagePointer + 16); // Not sure what a directory bucket is...? - const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here + const u32 directoryNum = mem.read32(messagePointer + 8); // Max number of directories + const u32 fileNum = mem.read32(messagePointer + 12); // Max number of files + const u32 directoryBucketNum = mem.read32(messagePointer + 16); // Not sure what a directory bucket is...? + const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here const bool duplicateData = mem.read8(messagePointer + 24) != 0; - ArchiveBase::FormatInfo info { - .size = blockSize * 0x200, - .numOfDirectories = directoryNum, - .numOfFiles = fileNum, - .duplicateData = duplicateData - }; + ArchiveBase::FormatInfo info{.size = blockSize * 0x200, .numOfDirectories = directoryNum, .numOfFiles = fileNum, .duplicateData = duplicateData}; FSPath emptyPath; mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0)); @@ -578,18 +559,16 @@ void FSService::controlArchive(u32 messagePointer) { } switch (action) { - case 0: // Commit save data changes. Shouldn't need us to do anything + case 0: // Commit save data changes. Shouldn't need us to do anything mem.write32(messagePointer + 4, Result::Success); break; - case 1: // Retrieves a file's last-modified timestamp. Seen in DDLC, stubbed for the moment + case 1: // Retrieves a file's last-modified timestamp. Seen in DDLC, stubbed for the moment Helpers::warn("FS::ControlArchive: Tried to retrieve a file's last-modified timestamp"); mem.write32(messagePointer + 4, Result::Success); break; - default: - Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action); - break; + default: Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action); break; } } @@ -673,9 +652,9 @@ void FSService::getThisSaveDataSecureValue(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x86F, 1, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write8(messagePointer + 8, 0); // Secure value does not exist - mem.write8(messagePointer + 12, 1); // TODO: What is this? - mem.write64(messagePointer + 16, 0); // Secure value + mem.write8(messagePointer + 8, 0); // Secure value does not exist + mem.write8(messagePointer + 12, 1); // TODO: What is this? + mem.write64(messagePointer + 16, 0); // Secure value } void FSService::setThisSaveDataSecureValue(u32 messagePointer) { diff --git a/third_party/result/LICENSE b/third_party/result/LICENSE deleted file mode 100644 index 6f4e91e3..00000000 --- a/third_party/result/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {2016} {Mathieu Stefani} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/third_party/result/README.md b/third_party/result/README.md deleted file mode 100644 index 7f588045..00000000 --- a/third_party/result/README.md +++ /dev/null @@ -1,132 +0,0 @@ -# Result -This is an adaption of [https://github.com/oktal/result](https://github.com/oktal/result). Make sure to support the original library! - -## Overview - -`Result` is a template type that can be used to return and propage errors. It can be used to replace -exceptions in context where they are not allowed or too slow to be used. `Result` is an algebraic data -type of `Ok(T)` that represents success and `Err(E)` representing an error. - -Design of this class has been mainly inspired by Rust's [std::result](https://doc.rust-lang.org/std/result/) - -``` - -struct Request { -}; - -struct Error { - - enum class Kind { - Timeout, - Invalid, - TooLong - } - - Error(Kind kind, std::string text); - - Kind kind; - std::string text; -}; - -Result parseRequest(const std::string& payload) { - if (payload.size() > 512) return Err(Error(Kind::TooLong, "Request exceeded maximum allowed size (512 bytes)")); - - Request request; - return Ok(request); -} - -std::string payload = receivePayload(); -auto request = parseRequest(payload).expect("Failed to parse request"); -``` - -To return a successfull `Result`, use the `Ok()` function. To return an error one, use the `Err()` function. - -## Extract and unwrap - -To extract the value from a `Result` type, you can use the `expect()` function that will yield the value -of an `Ok(T)` or terminate the program with an error message passed as a parameter. - -``` -Result r1 = Ok(3u); - -auto val = r1.expect("Failed to retrieve the value"); -assert(val == 3); -``` - -`unwrap()` can also be used to extract the value of a `Result`, yielding the value of an `Ok(T)` value or terminating -the program otherwise: - -``` -Result r1 = Ok(3u); - -auto val = r1.unwrap(); -assert(val == 3); -``` - -Instead a terminating the program, `unwrapOr` can be used to return a default value for an `Err(E)` Result: - -``` -Result r1 = Err(9u); - -auto val = r1.unwrapOr(0); -assert(val == 0); -``` - -## Map and bind - -To transform (or map) a `Result` to a `Result`, `Result` provides a `map` member function. -`map` will apply a function to a contained `Ok(T)` value and will return the result of the transformation, -and will leave an `Err(E)` untouched: - -``` -std::string stringify(int val) { return std::to_string(val); } - -Result r1 = Ok(2u); -auto r2 = r1.map(stringify); // Maps a Result to Result - -assert(r2.unwrap(), "2"); -``` - -Note that `map` should return a simple value and not a `Result`. A function returning nothing (`void`) -applied to a `Result` will yield a `Result`. - -To map a function to a contained `Err(E)` value, use the `mapError` function. - -To *bind* a `Result` to a `Result`, you can use the `andThen` member function: - -``` -Result square(uint32_t val) { return Ok(val * val); } - -Result r1 = Ok(3u); -auto r2 = r1.andThen(square); - -assert(r2.unwrap(), 9); -``` - -Use `orElse` to apply a function to a contained `Err(E)` value: - -``` -Result identity(uint32_t val) { return Ok(val); } - -Result r1 = Err(3u); -assert(r1.andThen(identity).orElse(square).unwrap(), 9); -``` - -## The TRY macro - -Like Rust, a `TRY` macro is also provided that comes in handy when writing code that calls a lot of functions returning a `Result`. - -the `TRY` macro will simply call its argument and short-cirtcuit the function returning an `Err(E)` if the operation returned an error `Result`: - -``` -Result copy(int srcFd, const char* dstFile) { - - auto fd = TRY(open(dstFile)); - auto data = TRY(read(srcFd)); - TRY(write(fd, data)); - - return Ok(); -} -``` - -Note that this macro uses a special extension called *compound statement* only supported by gcc and clang diff --git a/third_party/result/include/result.hpp b/third_party/result/include/result.hpp deleted file mode 100644 index b75dee90..00000000 --- a/third_party/result/include/result.hpp +++ /dev/null @@ -1,910 +0,0 @@ -/* - Mathieu Stefani, 03 mai 2016 - - This header provides a Result type that can be used to replace exceptions in code - that has to handle error. - - Result can be used to return and propagate an error to the caller. Result is an algebraic - data type that can either Ok(T) to represent success or Err(E) to represent an error. -*/ - -#pragma once - -#include -#include -#include - -namespace types { - template - struct Ok { - Ok(const T& val) : val(val) { } - Ok(T&& val) : val(std::move(val)) { } - - T val; - }; - - template<> - struct Ok { }; - - template - struct Err { - Err(const E& val) : val(val) { } - Err(E&& val) : val(std::move(val)) { } - - E val; - }; -} - -template::type> -types::Ok Ok(T&& val) { - return types::Ok(std::forward(val)); -} - -inline types::Ok Ok() { - return types::Ok(); -} - -template::type> -types::Err Err(E&& val) { - return types::Err(std::forward(val)); -} - -namespace Rust { -template struct Result; -} - -namespace details { - -template struct void_t { typedef void type; }; - -namespace impl { - template struct result_of; - - template - struct result_of : public result_of { }; - - template - struct result_of { - typedef Ret type; - }; -} - -template -struct result_of : public impl::result_of { }; - -template -struct result_of { - typedef Ret type; -}; - -template -struct result_of { - typedef Ret type; -}; - -template -struct ResultOkType { typedef typename std::decay::type type; }; - -template -struct ResultOkType> { - typedef T type; -}; - -template -struct ResultErrType { typedef R type; }; - -template -struct ResultErrType> { - typedef typename std::remove_reference::type type; -}; - -template struct IsResult : public std::false_type { }; -template -struct IsResult> : public std::true_type { }; - -namespace ok { - -namespace impl { - -template struct Map; - -template -struct Map : public Map { }; - -template -struct Map : public Map { }; - -// General implementation -template -struct Map { - - static_assert(!IsResult::value, - "Can not map a callback returning a Result, use andThen instead"); - - template - static Rust::Result map(const Rust::Result& result, Func func) { - - static_assert( - std::is_same::value || - std::is_convertible::value, - "Incompatible types detected"); - - if (result.isOk()) { - auto res = func(result.storage().template get()); - return types::Ok(std::move(res)); - } - - return types::Err(result.storage().template get()); - } -}; - -// Specialization for callback returning void -template -struct Map { - - template - static Rust::Result map(const Rust::Result& result, Func func) { - - if (result.isOk()) { - func(result.storage().template get()); - return types::Ok(); - } - - return types::Err(result.storage().template get()); - } -}; - -// Specialization for a void Result -template -struct Map { - - template - static Rust::Result map(const Rust::Result& result, Func func) { - static_assert(std::is_same::value, - "Can not map a void callback on a non-void Result"); - - if (result.isOk()) { - auto ret = func(); - return types::Ok(std::move(ret)); - } - - return types::Err(result.storage().template get()); - } -}; - -// Specialization for callback returning void on a void Result -template<> -struct Map { - - template - static Rust::Result map(const Rust::Result& result, Func func) { - static_assert(std::is_same::value, - "Can not map a void callback on a non-void Result"); - - if (result.isOk()) { - func(); - return types::Ok(); - } - - return types::Err(result.storage().template get()); - } -}; - -// General specialization for a callback returning a Result -template -struct Map (Arg)> { - - template - static Rust::Result map(const Rust::Result& result, Func func) { - static_assert( - std::is_same::value || - std::is_convertible::value, - "Incompatible types detected"); - - if (result.isOk()) { - auto res = func(result.storage().template get()); - return res; - } - - return types::Err(result.storage().template get()); - } -}; - -// Specialization for a void callback returning a Result -template -struct Map (void)> { - - template - static Rust::Result map(const Rust::Result& result, Func func) { - static_assert(std::is_same::value, "Can not call a void-callback on a non-void Result"); - - if (result.isOk()) { - auto res = func(); - return res; - } - - return types::Err(result.storage().template get()); - } - -}; - -} // namespace impl - -template struct Map : public impl::Map { }; - -template -struct Map : public impl::Map { }; - -template -struct Map : public impl::Map { }; - -template -struct Map : public impl::Map { }; - -template -struct Map> : public impl::Map { }; - -} // namespace ok - - -namespace err { - -namespace impl { - -template struct Map; - -template -struct Map { - - static_assert(!IsResult::value, - "Can not map a callback returning a Result, use orElse instead"); - - template - static Rust::Result map(const Rust::Result& result, Func func) { - if (result.isErr()) { - auto res = func(result.storage().template get()); - return types::Err(res); - } - - return types::Ok(result.storage().template get()); - } - - template - static Rust::Result map(const Rust::Result& result, Func func) { - if (result.isErr()) { - auto res = func(result.storage().template get()); - return types::Err(res); - } - - return types::Ok(); - } - - -}; - -} // namespace impl - -template struct Map : public impl::Map { }; - -} // namespace err; - -namespace And { - -namespace impl { - - template struct Then; - - template - struct Then : public Then { }; - - template - struct Then : public Then { }; - - template - struct Then : public Then { }; - - template - struct Then { - static_assert(std::is_same::value, - "then() should not return anything, use map() instead"); - - template - static Rust::Result then(const Rust::Result& result, Func func) { - if (result.isOk()) { - func(result.storage().template get()); - } - return result; - } - }; - - template - struct Then { - static_assert(std::is_same::value, - "then() should not return anything, use map() instead"); - - template - static Rust::Result then(const Rust::Result& result, Func func) { - static_assert(std::is_same::value, "Can not call a void-callback on a non-void Result"); - - if (result.isOk()) { - func(); - } - - return result; - } - }; - - -} // namespace impl - -template -struct Then : public impl::Then { }; - -template -struct Then : public impl::Then { }; - -template -struct Then : public impl::Then { }; - -template -struct Then : public impl::Then { }; - -} // namespace And - -namespace Or { - -namespace impl { - - template struct Else; - - template - struct Else : public Else { }; - - template - struct Else : public Else { }; - - template - struct Else : public Else { }; - - template - struct Else (Arg)> { - - template - static Rust::Result orElse(const Rust::Result& result, Func func) { - static_assert( - std::is_same::value || - std::is_convertible::value, - "Incompatible types detected"); - - if (result.isErr()) { - auto res = func(result.storage().template get()); - return res; - } - - return types::Ok(result.storage().template get()); - } - - template - static Rust::Result orElse(const Rust::Result& result, Func func) { - if (result.isErr()) { - auto res = func(result.storage().template get()); - return res; - } - - return types::Ok(); - } - - }; - - template - struct Else (void)> { - - template - static Rust::Result orElse(const Rust::Result& result, Func func) { - static_assert(std::is_same::value, - "Can not call a void-callback on a non-void Result"); - - if (result.isErr()) { - auto res = func(); - return res; - } - - return types::Ok(result.storage().template get()); - } - - template - static Rust::Result orElse(const Rust::Result& result, Func func) { - if (result.isErr()) { - auto res = func(); - return res; - } - - return types::Ok(); - } - - }; - -} // namespace impl - -template -struct Else : public impl::Else { }; - -template -struct Else : public impl::Else { }; - -template -struct Else : public impl::Else { }; - -template -struct Else : public impl::Else { }; - -} // namespace Or - -namespace Other { - -namespace impl { - - template struct Wise; - - template - struct Wise : public Wise { }; - - template - struct Wise : public Wise { }; - - template - struct Wise : public Wise { }; - - template - struct Wise { - - template - static Rust::Result otherwise(const Rust::Result& result, Func func) { - static_assert( - std::is_same::value || - std::is_convertible::value, - "Incompatible types detected"); - - static_assert(std::is_same::value, - "callback should not return anything, use mapError() for that"); - - if (result.isErr()) { - func(result.storage().template get()); - } - return result; - } - - }; - -} // namespace impl - -template -struct Wise : public impl::Wise { }; - -template -struct Wise : public impl::Wise { }; - -template -struct Wise : public impl::Wise { }; - -template -struct Wise : public impl::Wise { }; - -} // namespace Other - -template::type - >::type, - E> - > -Ret map(const Rust::Result& result, Func func) { - return ok::Map::map(result, func); -} - -template::type - >::type - > - > -Ret mapError(const Rust::Result& result, Func func) { - return err::Map::map(result, func); -} - -template -Rust::Result then(const Rust::Result& result, Func func) { - return And::Then::then(result, func); -} - -template -Rust::Result otherwise(const Rust::Result& result, Func func) { - return Other::Wise::otherwise(result, func); -} - -template::type - >::type - > -> -Ret orElse(const Rust::Result& result, Func func) { - return Or::Else::orElse(result, func); -} - -struct ok_tag { }; -struct err_tag { }; - -template -struct Storage { - static constexpr size_t Size = sizeof(T) > sizeof(E) ? sizeof(T) : sizeof(E); - static constexpr size_t Align = sizeof(T) > sizeof(E) ? alignof(T) : alignof(E); - - typedef typename std::aligned_storage::type type; - - Storage() - : initialized_(false) - { } - - void construct(types::Ok ok) - { - new (&storage_) T(ok.val); - initialized_ = true; - } - void construct(types::Err err) - { - new (&storage_) E(err.val); - initialized_ = true; - } - - template - void rawConstruct(U&& val) { - typedef typename std::decay::type CleanU; - - new (&storage_) CleanU(std::forward(val)); - initialized_ = true; - } - - template - const U& get() const { - return *reinterpret_cast(&storage_); - } - - template - U& get() { - return *reinterpret_cast(&storage_); - } - - void destroy(ok_tag) { - if (initialized_) { - get().~T(); - initialized_ = false; - } - } - - void destroy(err_tag) { - if (initialized_) { - get().~E(); - initialized_ = false; - } - } - - type storage_; - bool initialized_; -}; - -template -struct Storage { - typedef typename std::aligned_storage::type type; - - void construct(types::Ok) - { - initialized_ = true; - } - - void construct(types::Err err) - { - new (&storage_) E(err.val); - initialized_ = true; - } - - template - void rawConstruct(U&& val) { - typedef typename std::decay::type CleanU; - - new (&storage_) CleanU(std::forward(val)); - initialized_ = true; - } - - void destroy(ok_tag) { initialized_ = false; } - void destroy(err_tag) { - if (initialized_) { - get().~E(); initialized_ = false; - } - } - - template - const U& get() const { - return *reinterpret_cast(&storage_); - } - - template - U& get() { - return *reinterpret_cast(&storage_); - } - - type storage_; - bool initialized_; -}; - -template -struct Constructor { - - static void move(Storage&& src, Storage& dst, ok_tag) { - dst.rawConstruct(std::move(src.template get())); - src.destroy(ok_tag()); - } - - static void copy(const Storage& src, Storage& dst, ok_tag) { - dst.rawConstruct(src.template get()); - } - - static void move(Storage&& src, Storage& dst, err_tag) { - dst.rawConstruct(std::move(src.template get())); - src.destroy(err_tag()); - } - - static void copy(const Storage& src, Storage& dst, err_tag) { - dst.rawConstruct(src.template get()); - } -}; - -template -struct Constructor { - static void move(Storage&& src, Storage& dst, ok_tag) { - } - - static void copy(const Storage& src, Storage& dst, ok_tag) { - } - - static void move(Storage&& src, Storage& dst, err_tag) { - dst.rawConstruct(std::move(src.template get())); - src.destroy(err_tag()); - } - - static void copy(const Storage& src, Storage& dst, err_tag) { - dst.rawConstruct(src.template get()); - } -}; - -} // namespace details - -namespace rpog { - -template struct EqualityComparable : std::false_type { }; - -template -struct EqualityComparable() == std::declval())>::type - >::type -> : std::true_type -{ -}; - - -} // namespace rpog - -namespace Rust { -template -struct Result { - - static_assert(!std::is_same::value, "void error type is not allowed"); - - typedef details::Storage storage_type; - - Result(types::Ok ok) - : ok_(true) - { - storage_.construct(std::move(ok)); - } - - Result(types::Err err) - : ok_(false) - { - storage_.construct(std::move(err)); - } - - Result(Result&& other) { - if (other.isOk()) { - details::Constructor::move(std::move(other.storage_), storage_, details::ok_tag()); - ok_ = true; - } else { - details::Constructor::move(std::move(other.storage_), storage_, details::err_tag()); - ok_ = false; - } - } - - Result(const Result& other) { - if (other.isOk()) { - details::Constructor::copy(other.storage_, storage_, details::ok_tag()); - ok_ = true; - } else { - details::Constructor::copy(other.storage_, storage_, details::err_tag()); - ok_ = false; - } - } - - ~Result() { - if (ok_) - storage_.destroy(details::ok_tag()); - else - storage_.destroy(details::err_tag()); - } - - bool isOk() const { - return ok_; - } - - bool isErr() const { - return !ok_; - } - - T expect(const char* str) const { - if (!isOk()) { - std::fprintf(stderr, "%s\n", str); - std::terminate(); - } - return expect_impl(std::is_same()); - } - - template::type - >::type, - E> - > - Ret map(Func func) const { - return details::map(*this, func); - } - - template::type - >::type - > - > - Ret mapError(Func func) const { - return details::mapError(*this, func); - } - - template - Result then(Func func) const { - return details::then(*this, func); - } - - template - Result otherwise(Func func) const { - return details::otherwise(*this, func); - } - - template::type - >::type - > - > - Ret orElse(Func func) const { - return details::orElse(*this, func); - } - - storage_type& storage() { - return storage_; - } - - const storage_type& storage() const { - return storage_; - } - - template - typename std::enable_if< - !std::is_same::value, - U - >::type - unwrapOr(const U& defaultValue) const { - if (isOk()) { - return storage().template get(); - } - return defaultValue; - } - - template - typename std::enable_if< - !std::is_same::value, - U - >::type - unwrap() const { - if (isOk()) { - return storage().template get(); - } - - std::fprintf(stderr, "Attempting to unwrap an error Result\n"); - std::terminate(); - } - - E unwrapErr() const { - if (isErr()) { - return storage().template get(); - } - - std::fprintf(stderr, "Attempting to unwrapErr an ok Result\n"); - std::terminate(); - } - -private: - T expect_impl(std::true_type) const { } - T expect_impl(std::false_type) const { return storage_.template get(); } - - bool ok_; - storage_type storage_; -}; - -template -bool operator==(const Rust::Result& lhs, const Rust::Result& rhs) { - static_assert(rpog::EqualityComparable::value, "T must be EqualityComparable for Result to be comparable"); - static_assert(rpog::EqualityComparable::value, "E must be EqualityComparable for Result to be comparable"); - - if (lhs.isOk() && rhs.isOk()) { - return lhs.storage().template get() == rhs.storage().template get(); - } - if (lhs.isErr() && rhs.isErr()) { - return lhs.storage().template get() == rhs.storage().template get(); - } -} - -template -bool operator==(const Rust::Result& lhs, types::Ok ok) { - static_assert(rpog::EqualityComparable::value, "T must be EqualityComparable for Result to be comparable"); - - if (!lhs.isOk()) return false; - - return lhs.storage().template get() == ok.val; -} - -template -bool operator==(const Rust::Result& lhs, types::Ok) { - return lhs.isOk(); -} - -template -bool operator==(const Rust::Result& lhs, types::Err err) { - static_assert(rpog::EqualityComparable::value, "E must be EqualityComparable for Result to be comparable"); - if (!lhs.isErr()) return false; - - return lhs.storage().template get() == err.val; -} -} // end namespace Rust - -#define TRY(...) \ - ({ \ - auto res = __VA_ARGS__; \ - if (!res.isOk()) { \ - typedef details::ResultErrType::type E; \ - return types::Err(res.storage().get()); \ - } \ - typedef details::ResultOkType::type T; \ - res.storage().get(); \ - })