From c01e1108b21e2b749d9f520814379f99eb083938 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 4 Jan 2025 18:09:29 -0500 Subject: [PATCH] Prevent mods from replacing functions patched by the base recomp unless they're marked as Force --- librecomp/include/librecomp/mods.hpp | 6 ++--- librecomp/include/librecomp/overlays.hpp | 3 +++ librecomp/src/mod_manifest.cpp | 4 ++-- librecomp/src/mods.cpp | 26 +++++++++++++++++----- librecomp/src/overlays.cpp | 28 ++++++++++++++++++++++++ librecomp/src/recomp.cpp | 2 +- 6 files changed, 58 insertions(+), 11 deletions(-) diff --git a/librecomp/include/librecomp/mods.hpp b/librecomp/include/librecomp/mods.hpp index bdf5085..48d22a7 100644 --- a/librecomp/include/librecomp/mods.hpp +++ b/librecomp/include/librecomp/mods.hpp @@ -78,7 +78,7 @@ namespace recomp { InvalidCallbackEvent, InvalidFunctionReplacement, FailedToFindReplacement, - ReplacementConflict, + BaseRecompConflict, ModConflict, DuplicateExport, NoSpecifiedApiVersion, @@ -220,7 +220,7 @@ namespace recomp { void enable_mod(const std::string& mod_id, bool enabled); bool is_mod_enabled(const std::string& mod_id); size_t num_opened_mods(); - std::vector load_mods(const std::string& mod_game_id, uint8_t* rdram, int32_t load_address, uint32_t& ram_used); + std::vector load_mods(const GameEntry& game_entry, uint8_t* rdram, int32_t load_address, uint32_t& ram_used); void unload_mods(); std::vector get_mod_details(const std::string& mod_game_id); ModContentTypeId register_content_type(const ModContentType& type); @@ -232,7 +232,7 @@ namespace recomp { ModLoadError load_mod(recomp::mods::ModHandle& mod, std::string& error_param); void check_dependencies(recomp::mods::ModHandle& mod, std::vector>& errors); CodeModLoadError load_mod_code(uint8_t* rdram, const std::unordered_map& section_vrom_map, recomp::mods::ModHandle& mod, int32_t load_address, uint32_t& ram_used, std::string& error_param); - CodeModLoadError resolve_code_dependencies(recomp::mods::ModHandle& mod, std::string& error_param); + CodeModLoadError resolve_code_dependencies(recomp::mods::ModHandle& mod, const std::unordered_set base_patched_funcs, std::string& error_param); void add_opened_mod(ModManifest&& manifest, std::vector&& game_indices, std::vector&& detected_content_types); void close_mods(); diff --git a/librecomp/include/librecomp/overlays.hpp b/librecomp/include/librecomp/overlays.hpp index bb1bd9f..82848bd 100644 --- a/librecomp/include/librecomp/overlays.hpp +++ b/librecomp/include/librecomp/overlays.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "sections.h" namespace recomp { @@ -37,6 +38,8 @@ namespace recomp { size_t num_base_events(); void add_loaded_function(int32_t ram_addr, recomp_func_t* func); + + std::unordered_set get_base_patched_funcs(); } }; diff --git a/librecomp/src/mod_manifest.cpp b/librecomp/src/mod_manifest.cpp index 477ec52..83b7133 100644 --- a/librecomp/src/mod_manifest.cpp +++ b/librecomp/src/mod_manifest.cpp @@ -583,8 +583,8 @@ std::string recomp::mods::error_to_string(CodeModLoadError error) { return "Function to be replaced does not exist"; case CodeModLoadError::FailedToFindReplacement: return "Failed to find replacement function"; - case CodeModLoadError::ReplacementConflict: - return "Attempted to replace a function that cannot be replaced"; + case CodeModLoadError::BaseRecompConflict: + return "Attempted to replace a function that's been patched by the base recomp"; case CodeModLoadError::ModConflict: return "Conflicts with other mod"; case CodeModLoadError::DuplicateExport: diff --git a/librecomp/src/mods.cpp b/librecomp/src/mods.cpp index acc458a..21c7fb1 100644 --- a/librecomp/src/mods.cpp +++ b/librecomp/src/mods.cpp @@ -747,15 +747,18 @@ std::vector recomp::mods::ModContext::get_mod_details( return ret; } -std::vector recomp::mods::ModContext::load_mods(const std::string& mod_game_id, uint8_t* rdram, int32_t load_address, uint32_t& ram_used) { +std::vector recomp::mods::ModContext::load_mods(const GameEntry& game_entry, uint8_t* rdram, int32_t load_address, uint32_t& ram_used) { std::vector ret{}; ram_used = 0; num_events = recomp::overlays::num_base_events(); loaded_code_mods.clear(); - auto find_index_it = mod_game_ids.find(mod_game_id); + // Collect the set of functions patched by the base recomp. + std::unordered_set base_patched_funcs = recomp::overlays::get_base_patched_funcs(); + + auto find_index_it = mod_game_ids.find(game_entry.mod_game_id); if (find_index_it == mod_game_ids.end()) { - ret.emplace_back(mod_game_id, ModLoadError::InvalidGame, std::string{}); + ret.emplace_back(game_entry.mod_game_id, ModLoadError::InvalidGame, std::string{}); return ret; } @@ -845,7 +848,7 @@ std::vector recomp::mods::ModContext::load_mo for (size_t mod_index : loaded_code_mods) { auto& mod = opened_mods[mod_index]; std::string cur_error_param; - CodeModLoadError cur_error = resolve_code_dependencies(mod, cur_error_param); + CodeModLoadError cur_error = resolve_code_dependencies(mod, base_patched_funcs, cur_error_param); if (cur_error != CodeModLoadError::Good) { if (cur_error_param.empty()) { ret.emplace_back(mod.manifest.mod_id, ModLoadError::FailedToLoadCode, error_to_string(cur_error)); @@ -1082,7 +1085,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::load_mod_code(uint8_t* return CodeModLoadError::Good; } -recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependencies(recomp::mods::ModHandle& mod, std::string& error_param) { +recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependencies(recomp::mods::ModHandle& mod, const std::unordered_set base_patched_funcs, std::string& error_param) { // Reference symbols. std::string reference_syms_error_param{}; CodeModLoadError reference_syms_error = mod.code_handle->populate_reference_symbols(*mod.recompiler_context, reference_syms_error_param); @@ -1191,6 +1194,19 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci return CodeModLoadError::InvalidFunctionReplacement; } + // Check if this function has already been patched by the base recomp, but allow it if this is a force patch. + if ((replacement.flags & N64Recomp::ReplacementFlags::Force) == N64Recomp::ReplacementFlags(0)) { + auto find_it = base_patched_funcs.find(to_replace); + if (find_it != base_patched_funcs.end()) { + std::stringstream error_param_stream{}; + error_param_stream << std::hex << + "section: 0x" << replacement.original_section_vrom << + " func: 0x" << std::setfill('0') << std::setw(8) << replacement.original_vram; + error_param = error_param_stream.str(); + return CodeModLoadError::BaseRecompConflict; + } + } + // Check if this function has already been replaced. auto find_patch_it = patched_funcs.find(to_replace); if (find_patch_it != patched_funcs.end()) { diff --git a/librecomp/src/overlays.cpp b/librecomp/src/overlays.cpp index ffd7d5f..538c173 100644 --- a/librecomp/src/overlays.cpp +++ b/librecomp/src/overlays.cpp @@ -301,3 +301,31 @@ extern "C" recomp_func_t * get_function(int32_t addr) { return func_find->second; } +std::unordered_set recomp::overlays::get_base_patched_funcs() { + std::unordered_set ret{}; + + // Collect the set of all functions in the patches. + std::unordered_set all_patch_funcs{}; + for (size_t patch_section_index = 0; patch_section_index < num_patch_code_sections; patch_section_index++) { + const auto& patch_section = patch_code_sections[patch_section_index]; + for (size_t func_index = 0; func_index < patch_section.num_funcs; func_index++) { + all_patch_funcs.insert(patch_section.funcs[func_index].func); + } + } + + // Check every vanilla function against the full patch function set. + // Any functions in both are patched. + for (size_t code_section_index = 0; code_section_index < sections_info.num_code_sections; code_section_index++) { + const auto& code_section = sections_info.code_sections[code_section_index]; + for (size_t func_index = 0; func_index < code_section.num_funcs; func_index++) { + recomp_func_t* cur_func = code_section.funcs[func_index].func; + // If this function also exists in the patches function set then it's a vanilla function that was patched. + auto find_it = all_patch_funcs.find(cur_func); + if (find_it != all_patch_funcs.end()) { + ret.insert(cur_func); + } + } + } + + return ret; +} diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index 562d382..17894e4 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -536,7 +536,7 @@ bool wait_for_game_started(uint8_t* rdram, recomp_context* context) { std::vector mod_load_errors; { std::lock_guard lock { mod_context_mutex }; - mod_load_errors = mod_context->load_mods(game_entry.mod_game_id, rdram, recomp::mod_rdram_start, mod_ram_used); + mod_load_errors = mod_context->load_mods(game_entry, rdram, recomp::mod_rdram_start, mod_ram_used); } if (!mod_load_errors.empty()) {