From 7fe7b07b73f28af2a53f755870137648cc2ff9e9 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sun, 22 Jun 2025 14:53:02 -0400 Subject: [PATCH] Sort hooks and callbacks by mod order, with return hooks in reverse order --- librecomp/include/librecomp/mods.hpp | 13 ++++++-- librecomp/src/mod_events.cpp | 28 ++++++++++++---- librecomp/src/mod_hooks.cpp | 48 ++++++++++++++++++++++++---- librecomp/src/mods.cpp | 41 +++++++++++++++++++----- librecomp/src/recomp.cpp | 5 +++ 5 files changed, 112 insertions(+), 23 deletions(-) diff --git a/librecomp/include/librecomp/mods.hpp b/librecomp/include/librecomp/mods.hpp index 93dd958..29617cb 100644 --- a/librecomp/include/librecomp/mods.hpp +++ b/librecomp/include/librecomp/mods.hpp @@ -345,6 +345,7 @@ namespace recomp { std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename) const; std::filesystem::path get_mod_filename(const std::string& mod_id) const; size_t get_mod_order_index(const std::string& mod_id) const; + size_t get_mod_order_index(size_t mod_index) const; std::optional get_details_for_mod(const std::string& mod_id) const; std::vector get_all_mod_details(const std::string& mod_game_id); recomp::Version get_mod_version(size_t mod_index); @@ -378,6 +379,7 @@ namespace recomp { const std::unordered_map& base_patched_funcs, std::span decompressed_rom); void dirty_mod_configuration_thread_process(); + void rebuild_mod_order_lookup(); static void on_code_mod_enabled(ModContext& context, const ModHandle& mod); @@ -389,7 +391,8 @@ namespace recomp { std::vector opened_mods; std::unordered_map opened_mods_by_id; std::unordered_map opened_mods_by_filename; - std::vector opened_mods_order; + std::vector opened_mods_order; // order index -> mod index + std::vector mod_order_lookup; // mod index -> order index std::mutex opened_mods_mutex; std::unordered_set mod_ids; std::unordered_set enabled_mods; @@ -579,11 +582,14 @@ namespace recomp { }; void setup_events(size_t num_events); - void register_event_callback(size_t event_index, GenericFunction callback); + void register_event_callback(size_t event_index, size_t mod_index, GenericFunction callback); void reset_events(); void setup_hooks(size_t num_hook_slots); - void register_hook(size_t hook_slot_index, GenericFunction callback); + void set_hook_type(size_t hook_slot_index, bool is_return_hook); + void register_hook(size_t hook_slot_index, size_t mod_index, GenericFunction callback); + void finish_event_setup(const ModContext& context); + void finish_hook_setup(const ModContext& context); void reset_hooks(); void run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slot_index); @@ -611,6 +617,7 @@ namespace recomp { std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename); std::filesystem::path get_mod_filename(const std::string& mod_id); size_t get_mod_order_index(const std::string& mod_id); + size_t get_mod_order_index(size_t mod_index); ModContentTypeId register_mod_content_type(const ModContentType& type); bool register_mod_container_type(const std::string& extension, const std::vector& content_types, bool requires_manifest); diff --git a/librecomp/src/mod_events.cpp b/librecomp/src/mod_events.cpp index 538dad4..7dfc9df 100644 --- a/librecomp/src/mod_events.cpp +++ b/librecomp/src/mod_events.cpp @@ -8,8 +8,13 @@ struct overloaded : Ts... { using Ts::operator()...; }; template overloaded(Ts...) -> overloaded; +struct EventCallback { + size_t mod_index; + recomp::mods::GenericFunction func; +}; + // Vector of callbacks for each registered event. -std::vector> event_callbacks{}; +std::vector> event_callbacks{}; extern "C" { // This can stay at 0 since the base events are always first in the list. @@ -29,14 +34,14 @@ extern "C" void recomp_trigger_event(uint8_t* rdram, recomp_context* ctx, uint32 recomp_context initial_context = *ctx; // Call every callback attached to the event. - const std::vector& callbacks = event_callbacks[event_index]; - for (recomp::mods::GenericFunction func : callbacks) { + const std::vector& callbacks = event_callbacks[event_index]; + for (const EventCallback& callback : callbacks) { // Run the callback. std::visit(overloaded { [rdram, ctx](recomp_func_t* native_func) { native_func(rdram, ctx); }, - }, func); + }, callback.func); // Restore the original context. *ctx = initial_context; @@ -47,8 +52,19 @@ void recomp::mods::setup_events(size_t num_events) { event_callbacks.resize(num_events); } -void recomp::mods::register_event_callback(size_t event_index, GenericFunction callback) { - event_callbacks[event_index].emplace_back(callback); +void recomp::mods::register_event_callback(size_t event_index, size_t mod_index, GenericFunction callback) { + event_callbacks[event_index].emplace_back(EventCallback{ mod_index, callback }); +} + +void recomp::mods::finish_event_setup(const ModContext& context) { + // Sort callbacks by mod order. + for (std::vector& cur_entry : event_callbacks) { + std::sort(cur_entry.begin(), cur_entry.end(), + [&context](const EventCallback& lhs, const EventCallback& rhs) { + return context.get_mod_order_index(lhs.mod_index) < context.get_mod_order_index(rhs.mod_index); + } + ); + } } void recomp::mods::reset_events() { diff --git a/librecomp/src/mod_hooks.cpp b/librecomp/src/mod_hooks.cpp index 768d02a..86c32eb 100644 --- a/librecomp/src/mod_hooks.cpp +++ b/librecomp/src/mod_hooks.cpp @@ -8,8 +8,18 @@ struct overloaded : Ts... { using Ts::operator()...; }; template overloaded(Ts...) -> overloaded; +struct HookEntry { + size_t mod_index; + recomp::mods::GenericFunction func; +}; + +struct HookTableEntry { + std::vector hooks; + bool is_return_hook; +}; + // Vector of individual hooks for each hook slot. -std::vector> hook_table{}; +std::vector hook_table{}; void recomp::mods::run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slot_index) { // Sanity check the hook slot index. @@ -24,14 +34,14 @@ void recomp::mods::run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slo recomp_context initial_context = *ctx; // Call every hook attached to the hook slot. - const std::vector& hooks = hook_table[hook_slot_index]; - for (recomp::mods::GenericFunction func : hooks) { + const std::vector& hooks = hook_table[hook_slot_index].hooks; + for (HookEntry hook : hooks) { // Run the hook. std::visit(overloaded { [rdram, ctx](recomp_func_t* native_func) { native_func(rdram, ctx); }, - }, func); + }, hook.func); // Restore the original context. *ctx = initial_context; @@ -42,8 +52,34 @@ void recomp::mods::setup_hooks(size_t num_hook_slots) { hook_table.resize(num_hook_slots); } -void recomp::mods::register_hook(size_t hook_slot_index, GenericFunction callback) { - hook_table[hook_slot_index].emplace_back(callback); +void recomp::mods::set_hook_type(size_t hook_slot_index, bool is_return) { + hook_table[hook_slot_index].is_return_hook = is_return; +} + +void recomp::mods::register_hook(size_t hook_slot_index, size_t mod_index, GenericFunction callback) { + hook_table[hook_slot_index].hooks.emplace_back(HookEntry{ mod_index, callback }); +} + +void recomp::mods::finish_hook_setup(const ModContext& context) { + // Sort hooks by mod order (and return hooks in reverse order). + for (HookTableEntry& cur_entry : hook_table) { + // Reverse sort if this slot is a return hook. + if (cur_entry.is_return_hook) { + std::sort(cur_entry.hooks.begin(), cur_entry.hooks.end(), + [&context](const HookEntry& lhs, const HookEntry& rhs) { + return context.get_mod_order_index(lhs.mod_index) > context.get_mod_order_index(rhs.mod_index); + } + ); + } + // Otherwise sort normally. + else { + std::sort(cur_entry.hooks.begin(), cur_entry.hooks.end(), + [&context](const HookEntry& lhs, const HookEntry& rhs) { + return context.get_mod_order_index(lhs.mod_index) < context.get_mod_order_index(rhs.mod_index); + } + ); + } + } } void recomp::mods::reset_hooks() { diff --git a/librecomp/src/mods.cpp b/librecomp/src/mods.cpp index eb7eef8..36b1373 100644 --- a/librecomp/src/mods.cpp +++ b/librecomp/src/mods.cpp @@ -131,7 +131,7 @@ public: template bool get_dll_symbol(T& out, const char* name) const { - out = (T)GetProcAddress(native_handle, name); + out = (T)(void*)GetProcAddress(native_handle, name); if (out == nullptr) { return false; } @@ -638,6 +638,7 @@ void recomp::mods::ModContext::close_mods() { opened_mods_by_filename.clear(); opened_mods.clear(); opened_mods_order.clear(); + mod_order_lookup.clear(); mod_ids.clear(); enabled_mods.clear(); auto_enabled_mods.clear(); @@ -877,6 +878,8 @@ void recomp::mods::ModContext::load_mods_config() { return sort_order[i] < sort_order[j]; }); + rebuild_mod_order_lookup(); + // Enable mods that are specified in the configuration or mods that are considered new. for (size_t i = 0; i < opened_mods.size(); i++) { const ModHandle& mod = opened_mods[i]; @@ -889,6 +892,18 @@ void recomp::mods::ModContext::load_mods_config() { } } +void recomp::mods::ModContext::rebuild_mod_order_lookup() { + // Initialize the mod order lookup to all -1 so that mods that aren't enabled have an order index of -1. + mod_order_lookup.resize(opened_mods.size()); + std::fill(mod_order_lookup.begin(), mod_order_lookup.end(), static_cast(-1)); + + // Build the lookup of mod index to mod order by inverting the opened mods order list. + for (size_t mod_order_index = 0; mod_order_index < opened_mods_order.size(); mod_order_index++) { + size_t mod_index = opened_mods_order[mod_order_index]; + mod_order_lookup[mod_index] = mod_order_index; + } +} + recomp::mods::ModContext::ModContext() { // Register the code content type. ModContentType code_content_type { @@ -1128,14 +1143,18 @@ size_t recomp::mods::ModContext::get_mod_order_index(const std::string& mod_id) return static_cast(-1); } - // TODO keep a mapping of mod index to mod order index to prevent needing a lookup here. - auto find_order_it = std::find(opened_mods_order.begin(), opened_mods_order.end(), find_it->second); - if (find_order_it == opened_mods_order.end()) { + return get_mod_order_index(find_it->second); +} + +size_t recomp::mods::ModContext::get_mod_order_index(size_t mod_index) const { + size_t order_index = mod_order_lookup[mod_index]; + // Check if the mod has a proper order index and assert if it doesn't, as that means the mod isn't actually loaded. + if (order_index == static_cast(-1)) { assert(false); return static_cast(-1); } - return find_order_it - opened_mods_order.begin(); + return order_index; } std::optional recomp::mods::ModContext::get_details_for_mod(const std::string& mod_id) const { @@ -1347,6 +1366,8 @@ void recomp::mods::ModContext::set_mod_index(const std::string &mod_game_id, con opened_mods_order.push_back(mod_index); } + rebuild_mod_order_lookup(); + for (ModContentTypeId type_id : opened_mods[mod_index].content_types) { content_reordered_callback* callback = content_types[type_id.value].on_reordered; if (callback) { @@ -1655,12 +1676,13 @@ std::vector recomp::mods::ModContext::load_mo // Regenerate any remaining hook slots that weren't handled during mod recompilation. - // List of unprocessed hooks and their hook index. + // List of unprocessed hooks and their hook index. Also set up which hooks are return hooks. std::vector> unprocessed_hooks; for (const auto& [def, index] : hook_slots) { if (!processed_hook_slots[index]) { unprocessed_hooks.emplace_back(std::make_pair(def, index)); } + recomp::mods::set_hook_type(index, def.at_return); } if (!unprocessed_hooks.empty()) { @@ -1685,6 +1707,9 @@ std::vector recomp::mods::ModContext::load_mo } } + finish_event_setup(*this); + finish_hook_setup(*this); + active_game = mod_game_index; return ret; } @@ -2389,7 +2414,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci return CodeModLoadError::InvalidCallbackEvent; } - recomp::mods::register_event_callback(event_index, func); + recomp::mods::register_event_callback(event_index, mod_index, func); } // Register hooks. @@ -2411,7 +2436,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci // Register the function handle for this hook slot. GenericFunction func = mod.code_handle->get_function_handle(cur_hook.func_index); - recomp::mods::register_hook(find_it->second, func); + recomp::mods::register_hook(find_it->second, mod_index, func); } // Populate the relocated section addresses for the mod. diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index 58e1f24..2a446cb 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -585,6 +585,11 @@ size_t recomp::mods::get_mod_order_index(const std::string& mod_id) { return mod_context->get_mod_order_index(mod_id); } +size_t recomp::mods::get_mod_order_index(size_t mod_index) { + std::lock_guard lock { mod_context_mutex }; + return mod_context->get_mod_order_index(mod_index); +} + std::optional recomp::mods::get_details_for_mod(const std::string& mod_id) { std::lock_guard lock { mod_context_mutex }; return mod_context->get_details_for_mod(mod_id);