diff --git a/librecomp/include/librecomp/game.hpp b/librecomp/include/librecomp/game.hpp index 8a79235..7f1533e 100644 --- a/librecomp/include/librecomp/game.hpp +++ b/librecomp/include/librecomp/game.hpp @@ -46,6 +46,14 @@ namespace recomp { int patch = -1; std::string suffix; + Version() = default; + Version(int major, int minor, int patch, std::string suffix = std::string()) { + this->major = major; + this->minor = minor; + this->patch = patch; + this->suffix = suffix; + } + std::string to_string() const { return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch) + suffix; } @@ -60,7 +68,11 @@ namespace recomp { return minor <=> rhs.minor; } return patch <=> rhs.patch; - } + } + + bool is_null() const { + return (major == -1) && (minor == -1) && (patch == -1) && suffix.empty(); + } }; enum class RomValidationError { Good, diff --git a/librecomp/include/librecomp/mods.hpp b/librecomp/include/librecomp/mods.hpp index d26b0d1..37556bd 100644 --- a/librecomp/include/librecomp/mods.hpp +++ b/librecomp/include/librecomp/mods.hpp @@ -158,7 +158,18 @@ namespace recomp { Unknown, // The mod was integrated as part of the project. - Integrated + Integrated, + + // The mod is known to have a game breaking issue, but can be fixed by updating it. + BrokenVersion, + + // The mod is known to have a game breaking issue that is not fixable. + BrokenPermanent + }; + + struct DeprecatedMod { + DeprecationStatus status; + Version maximum_version; }; struct ModFileHandle { @@ -329,15 +340,16 @@ namespace recomp { void register_game(const std::string& mod_game_id); void register_embedded_mod(const std::string& mod_id, std::span mod_bytes); - void register_deprecated_mod(const std::string& mod_id, DeprecationStatus deprecation_status); + void register_deprecated_mod(const std::string& mod_id, DeprecationStatus deprecation_status, const Version& maximum_version); std::vector scan_mod_folder(const std::filesystem::path& mod_folder); void close_mods(); void load_mods_config(); void enable_mod(const std::string& mod_id, bool enabled, bool trigger_save); bool is_mod_enabled(const std::string& mod_id) const; bool is_mod_auto_enabled(const std::string& mod_id) const; - bool is_mod_deprecated(const std::string& mod_id) const; + bool is_mod_deprecated(const std::string& mod_id, const Version& mod_version) const; DeprecationStatus get_mod_deprecation_status(const std::string& mod_id) const; + Version get_mod_deprecation_version(const std::string& mod_id) const; size_t num_opened_mods(); std::vector load_mods(const GameEntry& game_entry, const std::string& game_mode_id, uint8_t* rdram, int32_t load_address, uint32_t& ram_used); void unload_mods(); @@ -404,7 +416,7 @@ namespace recomp { std::unordered_set mod_ids; std::unordered_set enabled_mods; std::unordered_set auto_enabled_mods; - std::unordered_map deprecated_mods; + std::unordered_map deprecated_mods; std::unordered_map patched_funcs; std::unordered_map loaded_mods_by_id; std::unique_ptr mod_configuration_thread; @@ -610,7 +622,7 @@ namespace recomp { void initialize_mods(); void register_embedded_mod(const std::string& mod_id, std::span mod_bytes); - void register_deprecated_mod(const std::string &mod_id, recomp::mods::DeprecationStatus deprecation_status); + void register_deprecated_mod(const std::string& mod_id, DeprecationStatus deprecation_status, const Version &maximum_version); void scan_mods(); void close_mods(); std::filesystem::path get_mods_directory(); @@ -622,8 +634,9 @@ namespace recomp { void enable_mod(const std::string& mod_id, bool enabled); bool is_mod_enabled(const std::string& mod_id); bool is_mod_auto_enabled(const std::string& mod_id); - bool is_mod_deprecated(const std::string& mod_id); + bool is_mod_deprecated(const std::string& mod_id, const Version& mod_version); DeprecationStatus get_mod_deprecation_status(const std::string& mod_id); + Version get_mod_deprecation_version(const std::string& mod_id); std::string deprecation_status_to_message(DeprecationStatus deprecation_status); const config::ConfigSchema &get_mod_config_schema(const std::string &mod_id); config::Config *get_mod_config(const std::string &mod_id); diff --git a/librecomp/src/mods.cpp b/librecomp/src/mods.cpp index 9310160..0f7de83 100644 --- a/librecomp/src/mods.cpp +++ b/librecomp/src/mods.cpp @@ -636,8 +636,9 @@ void recomp::mods::ModContext::register_embedded_mod(const std::string &mod_id, embedded_mod_bytes.emplace(mod_id, mod_bytes); } -void recomp::mods::ModContext::register_deprecated_mod(const std::string& mod_id, DeprecationStatus deprecation_status) { - deprecated_mods[mod_id] = deprecation_status; +void recomp::mods::ModContext::register_deprecated_mod(const std::string& mod_id, DeprecationStatus deprecation_status, const Version &maximum_version) { + deprecated_mods[mod_id].status = deprecation_status; + deprecated_mods[mod_id].maximum_version = maximum_version; } void recomp::mods::ModContext::close_mods() { @@ -1042,7 +1043,7 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable } // Do nothing if this mod was deprecated. - if (is_mod_deprecated(mod_id)) { + if (is_mod_deprecated(mod.manifest.mod_id, mod.manifest.version)) { return; } @@ -1070,7 +1071,7 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable if (mod_from_stack_it != opened_mods_by_id.end()) { const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second]; for (const Dependency &dependency : mod_from_stack_handle.manifest.dependencies) { - if (!dependency.optional && !auto_enabled_mods.contains(dependency.mod_id) && !is_mod_deprecated(dependency.mod_id)) { + if (!dependency.optional && !auto_enabled_mods.contains(dependency.mod_id) && !is_mod_deprecated(dependency.mod_id, dependency.version)) { auto_enabled_mods.emplace(dependency.mod_id); mod_stack.emplace_back(dependency.mod_id); @@ -1115,7 +1116,7 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable if (mod_from_stack_it != opened_mods_by_id.end()) { const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second]; for (const Dependency &dependency : mod_from_stack_handle.manifest.dependencies) { - if (!dependency.optional && !new_auto_enabled_mods.contains(dependency.mod_id) && !is_mod_deprecated(dependency.mod_id)) { + if (!dependency.optional && !new_auto_enabled_mods.contains(dependency.mod_id) && !is_mod_deprecated(dependency.mod_id, dependency.version)) { new_auto_enabled_mods.emplace(dependency.mod_id); mod_stack.emplace_back(dependency.mod_id); } @@ -1159,20 +1160,36 @@ bool recomp::mods::ModContext::is_mod_auto_enabled(const std::string& mod_id) co return auto_enabled_mods.contains(mod_id); } -bool recomp::mods::ModContext::is_mod_deprecated(const std::string& mod_id) const { - return get_mod_deprecation_status(mod_id) != DeprecationStatus::Unknown; +bool recomp::mods::ModContext::is_mod_deprecated(const std::string& mod_id, const Version& mod_version) const { + auto it = deprecated_mods.find(mod_id); + if (it != deprecated_mods.end()) { + return (it->second.status != recomp::mods::DeprecationStatus::Unknown) && (it->second.maximum_version.is_null() || (mod_version <= it->second.maximum_version)); + } + else { + return false; + } } recomp::mods::DeprecationStatus recomp::mods::ModContext::get_mod_deprecation_status(const std::string& mod_id) const { auto it = deprecated_mods.find(mod_id); if (it != deprecated_mods.end()) { - return it->second; + return it->second.status; } else { return recomp::mods::DeprecationStatus::Unknown; } } +recomp::Version recomp::mods::ModContext::get_mod_deprecation_version(const std::string& mod_id) const { + auto it = deprecated_mods.find(mod_id); + if (it != deprecated_mods.end()) { + return it->second.maximum_version; + } + else { + return Version(); + } +} + size_t recomp::mods::ModContext::num_opened_mods() { return opened_mods.size(); } diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index e42ce19..1200f77 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -102,9 +102,9 @@ void recomp::mods::register_embedded_mod(const std::string &mod_id, std::spanregister_embedded_mod(mod_id, mod_bytes); } -void recomp::mods::register_deprecated_mod(const std::string& mod_id, recomp::mods::DeprecationStatus deprecation_status) { +void recomp::mods::register_deprecated_mod(const std::string& mod_id, recomp::mods::DeprecationStatus deprecation_status, const Version& maximum_version) { std::lock_guard lock(mod_context_mutex); - mod_context->register_deprecated_mod(mod_id, deprecation_status); + mod_context->register_deprecated_mod(mod_id, deprecation_status, maximum_version); } void recomp::mods::scan_mods() { @@ -575,9 +575,9 @@ bool recomp::mods::is_mod_auto_enabled(const std::string& mod_id) { return mod_context->is_mod_auto_enabled(mod_id); } -bool recomp::mods::is_mod_deprecated(const std::string& mod_id) { +bool recomp::mods::is_mod_deprecated(const std::string& mod_id, const recomp::Version& mod_version) { std::lock_guard lock{ mod_context_mutex }; - return mod_context->is_mod_deprecated(mod_id); + return mod_context->is_mod_deprecated(mod_id, mod_version); } recomp::mods::DeprecationStatus recomp::mods::get_mod_deprecation_status(const std::string& mod_id) { @@ -585,10 +585,19 @@ recomp::mods::DeprecationStatus recomp::mods::get_mod_deprecation_status(const s return mod_context->get_mod_deprecation_status(mod_id); } +recomp::Version recomp::mods::get_mod_deprecation_version(const std::string& mod_id) { + std::lock_guard lock{ mod_context_mutex }; + return mod_context->get_mod_deprecation_version(mod_id); +} + std::string recomp::mods::deprecation_status_to_message(DeprecationStatus deprecation_status) { switch (deprecation_status) { case DeprecationStatus::Integrated: return "This mod has already been integrated into the game"; + case DeprecationStatus::BrokenVersion: + return "This version of the mod is known to cause issues. Please update it"; + case DeprecationStatus::BrokenPermanent: + return "This mod is known to cause issues. Please uninstall it"; default: return "Reason is unknown"; }