From 5eada509c07891e7c16dcac00c0b3121d78211bb Mon Sep 17 00:00:00 2001 From: Wiseguy <68165316+Mr-Wiseguy@users.noreply.github.com> Date: Sun, 26 Jan 2025 22:14:08 -0500 Subject: [PATCH] Rename manifest to mod.json, refactor config parsing, add display_name, description, short_description fields (#81) --- librecomp/include/librecomp/mods.hpp | 7 +- librecomp/src/mod_manifest.cpp | 287 +++++++++++++++------------ librecomp/src/mods.cpp | 3 + 3 files changed, 166 insertions(+), 131 deletions(-) diff --git a/librecomp/include/librecomp/mods.hpp b/librecomp/include/librecomp/mods.hpp index 99e7472..b3654cb 100644 --- a/librecomp/include/librecomp/mods.hpp +++ b/librecomp/include/librecomp/mods.hpp @@ -64,7 +64,6 @@ namespace recomp { NoManifest, FailedToParseManifest, InvalidManifestSchema, - UnrecognizedManifestField, IncorrectManifestFieldType, InvalidVersionString, InvalidMinimumRecompVersionString, @@ -157,6 +156,9 @@ namespace recomp { struct ModDetails { std::string mod_id; + std::string display_name; + std::string description; + std::string short_description; Version version; std::vector authors; std::vector dependencies; @@ -168,6 +170,9 @@ namespace recomp { std::vector mod_game_ids; std::string mod_id; + std::string display_name; + std::string description; + std::string short_description; std::vector authors; std::vector dependencies; std::unordered_map dependencies_by_id; diff --git a/librecomp/src/mod_manifest.cpp b/librecomp/src/mod_manifest.cpp index 1762fe5..3b2847c 100644 --- a/librecomp/src/mod_manifest.cpp +++ b/librecomp/src/mod_manifest.cpp @@ -143,6 +143,9 @@ enum class ManifestField { const std::string game_mod_id_key = "game_id"; const std::string mod_id_key = "id"; +const std::string display_name_key = "display_name"; +const std::string description_key = "description"; +const std::string short_description_key = "short_description"; const std::string version_key = "version"; const std::string authors_key = "authors"; const std::string minimum_recomp_version_key = "minimum_recomp_version"; @@ -224,6 +227,77 @@ static bool parse_dependency(const std::string& val, recomp::mods::Dependency& o return false; } +template +recomp::mods::ModOpenError try_get(T2& out, const nlohmann::json& data, const std::string& key, bool required, std::string& error_param) { + auto find_it = data.find(key); + if (find_it == data.end()) { + if (required) { + error_param = key; + return recomp::mods::ModOpenError::MissingManifestField; + } + out = {}; + return recomp::mods::ModOpenError::Good; + } + + const T1* ptr = find_it->get_ptr(); + if (ptr == nullptr) { + error_param = key; + return recomp::mods::ModOpenError::IncorrectManifestFieldType; + } + + out = *ptr; + return recomp::mods::ModOpenError::Good; +} + +recomp::mods::ModOpenError try_get_version(recomp::Version& out, const nlohmann::json& data, const std::string& key, std::string& error_param, recomp::mods::ModOpenError invalid_version_error) { + std::string version_string{}; + + recomp::mods::ModOpenError try_get_err = try_get(version_string, data, key, true, error_param); + if (try_get_err != recomp::mods::ModOpenError::Good) { + return try_get_err; + } + + if (!recomp::Version::from_string(version_string, out)) { + error_param = version_string; + return invalid_version_error; + } + + return recomp::mods::ModOpenError::Good; +} + +template +recomp::mods::ModOpenError try_get_vec(std::vector& out, const nlohmann::json& data, const std::string& key, bool required, std::string& error_param) { + auto find_it = data.find(key); + if (find_it == data.end()) { + if (required) { + error_param = key; + return recomp::mods::ModOpenError::MissingManifestField; + } + return recomp::mods::ModOpenError::Good; + } + + const nlohmann::json::array_t* ptr = find_it->get_ptr(); + if (ptr == nullptr) { + error_param = key; + return recomp::mods::ModOpenError::IncorrectManifestFieldType; + } + + out.clear(); + + for (const nlohmann::json& cur_val : *ptr) { + const T1* temp_ptr = cur_val.get_ptr(); + if (temp_ptr == nullptr) { + out.clear(); + error_param = key; + return recomp::mods::ModOpenError::IncorrectManifestFieldType; + } + + out.emplace_back(*temp_ptr); + } + + return recomp::mods::ModOpenError::Good; +} + recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const std::vector& manifest_data, std::string& error_param) { using json = nlohmann::json; json manifest_json = json::parse(manifest_data.begin(), manifest_data.end(), nullptr, false); @@ -236,138 +310,98 @@ recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const return recomp::mods::ModOpenError::InvalidManifestSchema; } - for (const auto& [key, val] : manifest_json.items()) { - const auto find_key_it = field_map.find(key); - if (find_key_it == field_map.end()) { - // Unrecognized field - error_param = key; - return recomp::mods::ModOpenError::UnrecognizedManifestField; + recomp::mods::ModOpenError current_error = recomp::mods::ModOpenError::Good; + + // Mod Game ID + std::string mod_game_id{}; + current_error = try_get(mod_game_id, manifest_json, game_mod_id_key, true, error_param); + if (current_error != recomp::mods::ModOpenError::Good) { + return current_error; + } + ret.mod_game_ids.emplace_back(std::move(mod_game_id)); + + // Mod ID + current_error = try_get(ret.mod_id, manifest_json, mod_id_key, true, error_param); + if (current_error != recomp::mods::ModOpenError::Good) { + return current_error; + } + + // Display name + current_error = try_get(ret.display_name, manifest_json, display_name_key, true, error_param); + if (current_error != recomp::mods::ModOpenError::Good) { + return current_error; + } + + // Description (optional) + current_error = try_get(ret.description, manifest_json, description_key, false, error_param); + if (current_error != recomp::mods::ModOpenError::Good) { + return current_error; + } + + // Short Description (optional) + current_error = try_get(ret.short_description, manifest_json, short_description_key, false, error_param); + if (current_error != recomp::mods::ModOpenError::Good) { + return current_error; + } + + // Version + current_error = try_get_version(ret.version, manifest_json, version_key, error_param, recomp::mods::ModOpenError::InvalidVersionString); + if (current_error != recomp::mods::ModOpenError::Good) { + return current_error; + } + + // Authors + current_error = try_get_vec(ret.authors, manifest_json, authors_key, true, error_param); + if (current_error != recomp::mods::ModOpenError::Good) { + return current_error; + } + + // Minimum recomp version + current_error = try_get_version(ret.minimum_recomp_version, manifest_json, minimum_recomp_version_key, error_param, recomp::mods::ModOpenError::InvalidMinimumRecompVersionString); + if (current_error != recomp::mods::ModOpenError::Good) { + return current_error; + } + + // Dependencies (optional) + std::vector dep_strings{}; + current_error = try_get_vec(dep_strings, manifest_json, dependencies_key, false, error_param); + if (current_error != recomp::mods::ModOpenError::Good) { + return current_error; + } + for (const std::string& dep_string : dep_strings) { + recomp::mods::Dependency cur_dep; + if (!parse_dependency(dep_string, cur_dep)) { + error_param = dep_string; + return recomp::mods::ModOpenError::InvalidDependencyString; } - ManifestField field = find_key_it->second; - switch (field) { - case ManifestField::GameModId: - { - std::string mod_game_id; - if (!get_to(val, mod_game_id)) { - error_param = key; - return recomp::mods::ModOpenError::IncorrectManifestFieldType; - } - ret.mod_game_ids.resize(1); - ret.mod_game_ids[0] = std::move(mod_game_id); - } - break; - case ManifestField::Id: - if (!get_to(val, ret.mod_id)) { - error_param = key; - return recomp::mods::ModOpenError::IncorrectManifestFieldType; - } - break; - case ManifestField::Version: - { - const std::string* version_str = val.get_ptr(); - if (version_str == nullptr) { - error_param = key; - return recomp::mods::ModOpenError::IncorrectManifestFieldType; - } - if (!recomp::Version::from_string(*version_str, ret.version)) { - error_param = *version_str; - return recomp::mods::ModOpenError::InvalidVersionString; - } - } - break; - case ManifestField::Authors: - if (!get_to_vec(val, ret.authors)) { - error_param = key; - return recomp::mods::ModOpenError::IncorrectManifestFieldType; - } - break; - case ManifestField::MinimumRecompVersion: - { - const std::string* version_str = val.get_ptr(); - if (version_str == nullptr) { - error_param = key; - return recomp::mods::ModOpenError::IncorrectManifestFieldType; - } - if (!recomp::Version::from_string(*version_str, ret.minimum_recomp_version)) { - error_param = *version_str; - return recomp::mods::ModOpenError::InvalidMinimumRecompVersionString; - } - ret.minimum_recomp_version.suffix.clear(); - } - break; - case ManifestField::Dependencies: - { - std::vector dep_strings{}; - if (!get_to_vec(val, dep_strings)) { - error_param = key; - return recomp::mods::ModOpenError::IncorrectManifestFieldType; - } + size_t dependency_index = ret.dependencies.size(); + ret.dependencies_by_id.emplace(cur_dep.mod_id, dependency_index); + ret.dependencies.emplace_back(std::move(cur_dep)); + } - for (const std::string& dep_string : dep_strings) { - recomp::mods::Dependency cur_dep; - if (!parse_dependency(dep_string, cur_dep)) { - error_param = dep_string; - return recomp::mods::ModOpenError::InvalidDependencyString; - } + // Native libraries (optional) + auto find_libs_it = manifest_json.find(native_libraries_key); + if (find_libs_it != manifest_json.end()) { + auto& val = *find_libs_it; + if (!val.is_object()) { + error_param = native_libraries_key; + return recomp::mods::ModOpenError::IncorrectManifestFieldType; + } + for (const auto& [lib_name, lib_exports] : val.items()) { + recomp::mods::NativeLibraryManifest& cur_lib = ret.native_libraries.emplace_back(); - size_t dependency_index = ret.dependencies.size(); - ret.dependencies_by_id.emplace(cur_dep.mod_id, dependency_index); - ret.dependencies.emplace_back(std::move(cur_dep)); - } - } - break; - case ManifestField::NativeLibraries: - { - if (!val.is_object()) { - error_param = key; - return recomp::mods::ModOpenError::IncorrectManifestFieldType; - } - for (const auto& [lib_name, lib_exports] : val.items()) { - recomp::mods::NativeLibraryManifest& cur_lib = ret.native_libraries.emplace_back(); - - cur_lib.name = lib_name; - if (!get_to_vec(lib_exports, cur_lib.exports)) { - error_param = key; - return recomp::mods::ModOpenError::IncorrectManifestFieldType; - } - } - } - break; + cur_lib.name = lib_name; + if (!get_to_vec(lib_exports, cur_lib.exports)) { + error_param = native_libraries_key; + return recomp::mods::ModOpenError::IncorrectManifestFieldType; + } } } return recomp::mods::ModOpenError::Good; } -recomp::mods::ModOpenError validate_manifest(const recomp::mods::ModManifest& manifest, std::string& error_param) { - using namespace recomp::mods; - - // Check for required fields. - if (manifest.mod_game_ids.empty()) { - error_param = game_mod_id_key; - return ModOpenError::MissingManifestField; - } - if (manifest.mod_id.empty()) { - error_param = mod_id_key; - return ModOpenError::MissingManifestField; - } - if (manifest.version.major == -1 || manifest.version.major == -1 || manifest.version.major == -1) { - error_param = version_key; - return ModOpenError::MissingManifestField; - } - if (manifest.authors.empty()) { - error_param = authors_key; - return ModOpenError::MissingManifestField; - } - if (manifest.minimum_recomp_version.major == -1 || manifest.minimum_recomp_version.major == -1 || manifest.minimum_recomp_version.major == -1) { - error_param = minimum_recomp_version_key; - return ModOpenError::MissingManifestField; - } - - return ModOpenError::Good; -} - recomp::mods::ModOpenError recomp::mods::ModContext::open_mod(const std::filesystem::path& mod_path, std::string& error_param, const std::vector& supported_content_types, bool requires_manifest) { ModManifest manifest{}; std::error_code ec; @@ -406,7 +440,7 @@ recomp::mods::ModOpenError recomp::mods::ModContext::open_mod(const std::filesys { bool exists; - std::vector manifest_data = manifest.file_handle->read_file("manifest.json", exists); + std::vector manifest_data = manifest.file_handle->read_file("mod.json", exists); if (!exists) { // If this container type requires a manifest then return an error. if (requires_manifest) { @@ -449,11 +483,6 @@ recomp::mods::ModOpenError recomp::mods::ModContext::open_mod(const std::filesys } mod_ids.emplace(manifest.mod_id); - ModOpenError validate_error = validate_manifest(manifest, error_param); - if (validate_error != ModOpenError::Good) { - return validate_error; - } - // Check for this mod's game ids being valid. std::vector game_indices; for (const auto& mod_game_id : manifest.mod_game_ids) { @@ -513,8 +542,6 @@ std::string recomp::mods::error_to_string(ModOpenError error) { return "Failed to parse mod's manifest.json"; case ModOpenError::InvalidManifestSchema: return "Mod's manifest.json has an invalid schema"; - case ModOpenError::UnrecognizedManifestField: - return "Unrecognized field in manifest.json"; case ModOpenError::IncorrectManifestFieldType: return "Incorrect type for field in manifest.json"; case ModOpenError::InvalidVersionString: diff --git a/librecomp/src/mods.cpp b/librecomp/src/mods.cpp index 12b9335..915edc2 100644 --- a/librecomp/src/mods.cpp +++ b/librecomp/src/mods.cpp @@ -742,6 +742,9 @@ std::vector recomp::mods::ModContext::get_mod_details( ret.emplace_back(ModDetails{ .mod_id = mod.manifest.mod_id, + .display_name = mod.manifest.display_name, + .description = mod.manifest.description, + .short_description = mod.manifest.short_description, .version = mod.manifest.version, .authors = mod.manifest.authors, .dependencies = mod.manifest.dependencies,