mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2026-05-17 22:31:17 +00:00
Prevent mods from replacing functions patched by the base recomp unless they're marked as Force
This commit is contained in:
parent
50029c70fd
commit
c01e1108b2
6 changed files with 58 additions and 11 deletions
|
|
@ -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<ModLoadErrorDetails> load_mods(const std::string& mod_game_id, uint8_t* rdram, int32_t load_address, uint32_t& ram_used);
|
||||
std::vector<ModLoadErrorDetails> load_mods(const GameEntry& game_entry, uint8_t* rdram, int32_t load_address, uint32_t& ram_used);
|
||||
void unload_mods();
|
||||
std::vector<ModDetails> 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<std::pair<recomp::mods::ModLoadError, std::string>>& errors);
|
||||
CodeModLoadError load_mod_code(uint8_t* rdram, const std::unordered_map<uint32_t, uint16_t>& 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<recomp_func_t*> base_patched_funcs, std::string& error_param);
|
||||
void add_opened_mod(ModManifest&& manifest, std::vector<size_t>&& game_indices, std::vector<ModContentTypeId>&& detected_content_types);
|
||||
void close_mods();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#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<recomp_func_t*> get_base_patched_funcs();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -747,15 +747,18 @@ std::vector<recomp::mods::ModDetails> recomp::mods::ModContext::get_mod_details(
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::vector<recomp::mods::ModLoadErrorDetails> 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::ModLoadErrorDetails> recomp::mods::ModContext::load_mods(const GameEntry& game_entry, uint8_t* rdram, int32_t load_address, uint32_t& ram_used) {
|
||||
std::vector<recomp::mods::ModLoadErrorDetails> 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<recomp_func_t*> 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::ModLoadErrorDetails> 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<recomp_func_t*> 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()) {
|
||||
|
|
|
|||
|
|
@ -301,3 +301,31 @@ extern "C" recomp_func_t * get_function(int32_t addr) {
|
|||
return func_find->second;
|
||||
}
|
||||
|
||||
std::unordered_set<recomp_func_t*> recomp::overlays::get_base_patched_funcs() {
|
||||
std::unordered_set<recomp_func_t*> ret{};
|
||||
|
||||
// Collect the set of all functions in the patches.
|
||||
std::unordered_set<recomp_func_t*> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ bool wait_for_game_started(uint8_t* rdram, recomp_context* context) {
|
|||
std::vector<recomp::mods::ModLoadErrorDetails> 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()) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue