From 374244fa7113456c9ed5e6c4ec30379547f65df7 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Fri, 5 Jul 2024 12:35:54 -0400 Subject: [PATCH] Added ability to load mods from directories instead of zip files --- librecomp/include/librecomp/mods.hpp | 30 +++++--- librecomp/src/mod_manifest.cpp | 104 +++++++++++++++++++-------- 2 files changed, 96 insertions(+), 38 deletions(-) diff --git a/librecomp/include/librecomp/mods.hpp b/librecomp/include/librecomp/mods.hpp index a854699..6326c48 100644 --- a/librecomp/include/librecomp/mods.hpp +++ b/librecomp/include/librecomp/mods.hpp @@ -18,7 +18,7 @@ namespace recomp { enum class ModLoadError { Good, DoesNotExist, - NotAFile, + NotAFileOrFolder, FileError, InvalidZip, NoManifest, @@ -28,19 +28,30 @@ namespace recomp { IncorrectManifestFieldType, }; - struct ZipModHandle { + struct ModHandle { + virtual ~ModHandle() = default; + virtual std::vector read_file(const std::string& filepath, bool& exists) = 0; + }; + + struct ZipModHandle : public ModHandle { FILE* file_handle = nullptr; std::unique_ptr archive; ZipModHandle() = default; ZipModHandle(const std::filesystem::path& mod_path, ModLoadError& error); - ZipModHandle(const ZipModHandle& rhs) = delete; - ZipModHandle& operator=(const ZipModHandle& rhs) = delete; - ZipModHandle(ZipModHandle&& rhs); - ZipModHandle& operator=(ZipModHandle&& rhs); - ~ZipModHandle(); + ~ZipModHandle() final; - std::vector read_file(const std::string& filename, bool& exists); + std::vector read_file(const std::string& filepath, bool& exists) final; + }; + + struct LooseModHandle : public ModHandle { + std::filesystem::path root_path; + + LooseModHandle() = default; + LooseModHandle(const std::filesystem::path& mod_path, ModLoadError& error); + ~LooseModHandle() final; + + std::vector read_file(const std::string& filepath, bool& exists) final; }; struct ModManifest { @@ -58,10 +69,11 @@ namespace recomp { std::string rom_patch_path; std::string rom_patch_syms_path; - ZipModHandle mod_handle; + std::unique_ptr mod_handle; }; ModManifest load_mod(const std::filesystem::path& mod_path, ModLoadError& error, std::string& error_string); + bool load_mod_(uint8_t* rdram, int32_t target_vram, const std::filesystem::path& symbol_file, const std::filesystem::path& binary_file); } }; diff --git a/librecomp/src/mod_manifest.cpp b/librecomp/src/mod_manifest.cpp index aaaa0ad..06135e2 100644 --- a/librecomp/src/mod_manifest.cpp +++ b/librecomp/src/mod_manifest.cpp @@ -16,23 +16,6 @@ recomp::mods::ZipModHandle::~ZipModHandle() { archive = {}; } -recomp::mods::ZipModHandle::ZipModHandle(ZipModHandle&& rhs) { - *this = std::move(rhs); -} - -recomp::mods::ZipModHandle& recomp::mods::ZipModHandle::operator=(ZipModHandle&& rhs) { - if (file_handle) { - fclose(file_handle); - } - file_handle = rhs.file_handle; - rhs.file_handle = nullptr; - - mz_zip_reader_end(archive.get()); - archive = std::move(rhs.archive); - - return *this; -} - recomp::mods::ZipModHandle::ZipModHandle(const std::filesystem::path& mod_path, ModLoadError& error) { #ifdef _WIN32 if (_wfopen_s(&file_handle, mod_path.c_str(), L"rb") != 0) { @@ -55,11 +38,11 @@ recomp::mods::ZipModHandle::ZipModHandle(const std::filesystem::path& mod_path, error = ModLoadError::Good; } -std::vector recomp::mods::ZipModHandle::read_file(const std::string& filename, bool& exists) { +std::vector recomp::mods::ZipModHandle::read_file(const std::string& filepath, bool& exists) { std::vector ret{}; - + mz_uint32 file_index; - if (!mz_zip_reader_locate_file_v2(archive.get(), filename.c_str(), nullptr, MZ_ZIP_FLAG_CASE_SENSITIVE, &file_index)) { + if (!mz_zip_reader_locate_file_v2(archive.get(), filepath.c_str(), nullptr, MZ_ZIP_FLAG_CASE_SENSITIVE, &file_index)) { exists = false; return ret; } @@ -80,6 +63,53 @@ std::vector recomp::mods::ZipModHandle::read_file(const std::string& filen return ret; } +recomp::mods::LooseModHandle::~LooseModHandle() { + // Nothing to do here, members will be destroyed automatically. +} + +recomp::mods::LooseModHandle::LooseModHandle(const std::filesystem::path& mod_path, ModLoadError& error) { + root_path = mod_path; + + std::error_code ec; + if (!std::filesystem::is_directory(root_path, ec)) { + error = ModLoadError::NotAFileOrFolder; + } + + if (ec) { + error = ModLoadError::FileError; + } + + error = ModLoadError::Good; +} + +std::vector recomp::mods::LooseModHandle::read_file(const std::string& filepath, bool& exists) { + std::vector ret{}; + std::filesystem::path full_path = root_path / filepath; + + std::error_code ec; + if (!std::filesystem::is_regular_file(full_path, ec) || ec) { + exists = false; + return ret; + } + + std::ifstream file{ full_path, std::ios::binary }; + + if (!file.good()) { + exists = false; + return ret; + } + + file.seekg(0, std::ios::end); + size_t file_size = file.tellg(); + file.seekg(0, std::ios::beg); + + ret.resize(file_size); + file.read(ret.data(), ret.size()); + + exists = true; + return ret; +} + enum class ManifestField { Id, MajorVersion, @@ -116,7 +146,7 @@ bool get_to(const nlohmann::json& val, T2& out) { bool parse_manifest(recomp::mods::ModManifest& ret, const std::vector& manifest_data, recomp::mods::ModLoadError& error, std::string& error_param) { using json = nlohmann::json; - json manifest_json = json::parse(manifest_data.begin(), manifest_data.end(), false); + json manifest_json = json::parse(manifest_data.begin(), manifest_data.end(), nullptr, false); if (manifest_json.is_discarded()) { error = recomp::mods::ModLoadError::FailedToParseManifest; @@ -212,23 +242,39 @@ recomp::mods::ModManifest recomp::mods::load_mod(const std::filesystem::path& mo } // TODO support symlinks? - if (!std::filesystem::is_regular_file(mod_path, ec) || ec) { - error = ModLoadError::NotAFile; + bool is_file = std::filesystem::is_regular_file(mod_path, ec); + if (ec) { + error = ModLoadError::FileError; return {}; } - // Load the zip file. - ModLoadError zip_error; - ret.mod_handle = recomp::mods::ZipModHandle(mod_path, zip_error); + bool is_directory = std::filesystem::is_directory(mod_path, ec); + if (ec) { + error = ModLoadError::FileError; + return {}; + } - if (zip_error != ModLoadError::Good) { - error = zip_error; + // Load the directory or zip file. + ModLoadError handle_error; + if (is_file) { + ret.mod_handle = std::make_unique(mod_path, handle_error); + } + else if (is_directory) { + ret.mod_handle = std::make_unique(mod_path, handle_error); + } + else { + error = ModLoadError::NotAFileOrFolder; + return {}; + } + + if (handle_error != ModLoadError::Good) { + error = handle_error; return {}; } { bool exists; - std::vector manifest_data = ret.mod_handle.read_file("manifest.json", exists); + std::vector manifest_data = ret.mod_handle->read_file("manifest.json", exists); if (!exists) { error = ModLoadError::NoManifest; return {};