From 11c50bf34925f40aef87215b3b86ba672d17e227 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 31 Aug 2024 02:25:23 -0400 Subject: [PATCH] Implement mod events and callbacks --- librecomp/CMakeLists.txt | 1 + librecomp/include/librecomp/mods.hpp | 29 ++++++---- librecomp/src/mod_events.cpp | 50 ++++++++++++++++ librecomp/src/mod_manifest.cpp | 2 + librecomp/src/mods.cpp | 87 ++++++++++++++++++++++++---- 5 files changed, 147 insertions(+), 22 deletions(-) create mode 100644 librecomp/src/mod_events.cpp diff --git a/librecomp/CMakeLists.txt b/librecomp/CMakeLists.txt index 6aa0086..fb8c884 100644 --- a/librecomp/CMakeLists.txt +++ b/librecomp/CMakeLists.txt @@ -16,6 +16,7 @@ add_library(librecomp STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/flash.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/math_routines.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/mods.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_events.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_manifest.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/overlays.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/pak.cpp" diff --git a/librecomp/include/librecomp/mods.hpp b/librecomp/include/librecomp/mods.hpp index fb9c406..0fe9bca 100644 --- a/librecomp/include/librecomp/mods.hpp +++ b/librecomp/include/librecomp/mods.hpp @@ -53,6 +53,7 @@ namespace recomp { FailedToLoadNativeCode, InvalidReferenceSymbol, InvalidImport, + InvalidCallbackEvent, InvalidFunctionReplacement, FailedToFindReplacement, ReplacementConflict, @@ -162,10 +163,7 @@ namespace recomp { std::unordered_set enabled_mods; std::unordered_map patched_funcs; std::unordered_map loaded_mods_by_id; - // // Maps (mod id, export name) to (mod index, function index). - // std::unordered_map, std::pair> mod_exports; - // // Maps (mod id, event name) to a vector of callback functions attached to that event. - // std::unordered_map, std::vector> callbacks; + size_t num_events = 0; }; class ModCodeHandle { @@ -174,7 +172,8 @@ namespace recomp { virtual bool good() = 0; virtual void set_imported_function(size_t import_index, GenericFunction func) = 0; virtual void set_reference_symbol_pointer(size_t symbol_index, recomp_func_t* ptr) = 0; - virtual void set_event_index(size_t local_event_index, uint32_t global_event_index) = 0; + virtual void set_base_event_index(uint32_t global_event_index) = 0; + virtual uint32_t get_base_event_index() = 0; virtual void set_recomp_trigger_event_pointer(void (*ptr)(uint8_t* rdram, recomp_context* ctx, uint32_t index)) = 0; virtual void set_get_function_pointer(recomp_func_t* (*ptr)(int32_t)) = 0; virtual void set_reference_section_addresses_pointer(int32_t* ptr) = 0; @@ -202,11 +201,13 @@ namespace recomp { ModLoadError populate_exports(std::string& error_param); bool get_export_function(const std::string& export_name, GenericFunction& out) const; + ModLoadError populate_events(size_t base_event_index, std::string& error_param); + bool get_global_event_index(const std::string& event_name, size_t& event_index_out) const; private: // Mapping of export name to function index. std::unordered_map exports_by_name; - // List of global event indices ordered by the event's local index. - std::vector global_event_indices; + // Mapping of event name to local index. + std::unordered_map events_by_name; }; class DynamicLibrary; @@ -219,9 +220,12 @@ namespace recomp { void set_reference_symbol_pointer(size_t symbol_index, recomp_func_t* ptr) final { reference_symbol_funcs[symbol_index] = ptr; }; - void set_event_index(size_t local_event_index, uint32_t global_event_index) final { - event_indices[local_event_index] = global_event_index; + void set_base_event_index(uint32_t global_event_index) final { + *base_event_index = global_event_index; }; + uint32_t get_base_event_index() final { + return *base_event_index; + } void set_recomp_trigger_event_pointer(void (*ptr)(uint8_t* rdram, recomp_context* ctx, uint32_t index)) final { *recomp_trigger_event = ptr; }; @@ -244,14 +248,19 @@ namespace recomp { std::vector functions; recomp_func_t** imported_funcs; recomp_func_t** reference_symbol_funcs; - uint32_t* event_indices; + uint32_t* base_event_index; void (**recomp_trigger_event)(uint8_t* rdram, recomp_context* ctx, uint32_t index); recomp_func_t* (**get_function)(int32_t vram); int32_t** reference_section_addresses; int32_t* section_addresses; }; + void setup_events(size_t num_events); + void register_event_callback(size_t event_index, GenericFunction callback); + void reset_events(); } }; +extern "C" void recomp_trigger_event(uint8_t* rdram, recomp_context* ctx, uint32_t event_index); + #endif diff --git a/librecomp/src/mod_events.cpp b/librecomp/src/mod_events.cpp new file mode 100644 index 0000000..67ed418 --- /dev/null +++ b/librecomp/src/mod_events.cpp @@ -0,0 +1,50 @@ +#include +#include "librecomp/mods.hpp" +#include "ultramodern/error_handling.hpp" + +template +struct overloaded : Ts... { using Ts::operator()...; }; +template +overloaded(Ts...) -> overloaded; + +// Vector of callbacks for each registered event. +std::vector> event_callbacks{}; + +extern "C" void recomp_trigger_event(uint8_t* rdram, recomp_context* ctx, uint32_t event_index) { + // Sanity check the event index. + if (event_index >= event_callbacks.size()) { + printf("Event %u triggered, but only %zu events have been registered!\n", event_index, event_callbacks.size()); + assert(false); + ultramodern::error_handling::message_box("Encountered an error with loaded mods: event index out of bounds"); + ULTRAMODERN_QUICK_EXIT(); + } + + // Copy the initial context state to restore it after running each callback. + 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) { + // Run the callback. + std::visit(overloaded { + [rdram, ctx](recomp_func_t* native_func) { + native_func(rdram, ctx); + }, + }, func); + + // Restore the original context. + *ctx = initial_context; + } +} + +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::reset_events() { + event_callbacks.clear(); +} diff --git a/librecomp/src/mod_manifest.cpp b/librecomp/src/mod_manifest.cpp index 6e7566e..5af1e74 100644 --- a/librecomp/src/mod_manifest.cpp +++ b/librecomp/src/mod_manifest.cpp @@ -430,6 +430,8 @@ std::string recomp::mods::error_to_string(ModLoadError error) { return "Reference symbol does not exist"; case ModLoadError::InvalidImport: return "Imported function not found"; + case ModLoadError::InvalidCallbackEvent: + return "Event for callback not found"; case ModLoadError::InvalidFunctionReplacement: return "Function to be replaced does not exist"; case ModLoadError::FailedToFindReplacement: diff --git a/librecomp/src/mods.cpp b/librecomp/src/mods.cpp index 2cd4a36..6692d68 100644 --- a/librecomp/src/mods.cpp +++ b/librecomp/src/mods.cpp @@ -54,6 +54,26 @@ bool recomp::mods::ModHandle::get_export_function(const std::string& export_name return true; } +recomp::mods::ModLoadError recomp::mods::ModHandle::populate_events(size_t base_event_index, std::string& error_param) { + for (size_t event_index = 0; event_index < recompiler_context->event_symbols.size(); event_index++) { + const N64Recomp::EventSymbol& event = recompiler_context->event_symbols[event_index]; + events_by_name.emplace(event.base.name, event_index); + } + + code_handle->set_base_event_index(base_event_index); + return ModLoadError::Good; +} + +bool recomp::mods::ModHandle::get_global_event_index(const std::string& event_name, size_t& event_index_out) const { + auto find_it = events_by_name.find(event_name); + if (find_it == events_by_name.end()) { + return false; + } + + event_index_out = code_handle->get_base_event_index() + find_it->second; + return true; +} + template struct overloaded : Ts... { using Ts::operator()...; }; template @@ -149,7 +169,7 @@ recomp::mods::NativeCodeHandle::NativeCodeHandle(const std::filesystem::path& dl is_good = true; is_good &= dynamic_lib->get_dll_symbol(imported_funcs, "imported_funcs"); is_good &= dynamic_lib->get_dll_symbol(reference_symbol_funcs, "reference_symbol_funcs"); - is_good &= dynamic_lib->get_dll_symbol(event_indices, "event_indices"); + is_good &= dynamic_lib->get_dll_symbol(base_event_index, "base_event_index"); is_good &= dynamic_lib->get_dll_symbol(recomp_trigger_event, "recomp_trigger_event"); is_good &= dynamic_lib->get_dll_symbol(get_function, "get_function"); is_good &= dynamic_lib->get_dll_symbol(reference_section_addresses, "reference_section_addresses"); @@ -372,6 +392,9 @@ std::vector recomp::mods::ModContext::load_mo unload_mods(); return ret; } + + // Set up the event callbacks based on the number of events allocated. + recomp::mods::setup_events(num_events); // Resolve dependencies for all mods. for (size_t mod_index : active_mods) { @@ -454,15 +477,24 @@ recomp::mods::ModLoadError recomp::mods::ModContext::load_mod_code(recomp::mods: } // Populate the mod's export map. - std::string export_error_param; - ModLoadError export_error = mod.populate_exports(export_error_param); + std::string cur_error_param; + ModLoadError cur_error = mod.populate_exports(cur_error_param); - if (export_error != ModLoadError::Good) { - error_param = std::move(export_error_param); - return export_error; + if (cur_error != ModLoadError::Good) { + error_param = std::move(cur_error_param); + return cur_error; } - // TODO events + // Populate the mod's event map and set its base event index. + cur_error = mod.populate_events(num_events, cur_error_param); + + if (cur_error != ModLoadError::Good) { + error_param = std::move(cur_error_param); + return cur_error; + } + + // Allocate the event indices used by the mod. + num_events += mod.num_events(); return ModLoadError::Good; } @@ -523,12 +555,43 @@ recomp::mods::ModLoadError recomp::mods::ModContext::resolve_dependencies(recomp mod.code_handle->set_imported_function(import_index, func_handle); } - // TODO event_indices - // TODO recomp_trigger_event + // Register callbacks. + for (const N64Recomp::Callback& callback : mod.recompiler_context->callbacks) { + const N64Recomp::DependencyEvent& dependency_event = mod.recompiler_context->dependency_events[callback.dependency_event_index]; + const N64Recomp::Dependency& dependency = mod.recompiler_context->dependencies[dependency_event.dependency_index]; + GenericFunction func = mod.code_handle->get_function_handle(callback.function_index); + size_t event_index = 0; + bool did_find_event = false; + if (dependency.mod_id == N64Recomp::DependencyBaseRecomp) { + error_param = "Base recomp events not supported yet"; + return ModLoadError::InvalidCallbackEvent; + } + else if (dependency.mod_id == N64Recomp::DependencySelf) { + did_find_event = mod.get_global_event_index(dependency_event.event_name, event_index); + } + else { + auto find_mod_it = loaded_mods_by_id.find(dependency.mod_id); + if (find_mod_it == loaded_mods_by_id.end()) { + error_param = dependency.mod_id; + return ModLoadError::MissingDependency; + } + const auto& dependency_mod = opened_mods[find_mod_it->second]; + did_find_event = dependency_mod.get_global_event_index(dependency_event.event_name, event_index); + } + + if (!did_find_event) { + error_param = dependency.mod_id + ":" + dependency_event.event_name; + return ModLoadError::InvalidCallbackEvent; + } + + recomp::mods::register_event_callback(event_index, func); + } + + // Populate the mod's state fields. + mod.code_handle->set_recomp_trigger_event_pointer(recomp_trigger_event); mod.code_handle->set_get_function_pointer(get_function); mod.code_handle->set_reference_section_addresses_pointer(section_addresses); - for (size_t section_index = 0; section_index < mod.section_load_addresses.size(); section_index++) { mod.code_handle->set_local_section_address(section_index, mod.section_load_addresses[section_index]); } @@ -564,8 +627,6 @@ recomp::mods::ModLoadError recomp::mods::ModContext::resolve_dependencies(recomp // TODO perform mips32 relocations - // TODO hook up callbacks - return ModLoadError::Good; } @@ -575,4 +636,6 @@ void recomp::mods::ModContext::unload_mods() { } patched_funcs.clear(); loaded_mods_by_id.clear(); + recomp::mods::reset_events(); + num_events = 0; }