diff --git a/librecomp/include/librecomp/mods.hpp b/librecomp/include/librecomp/mods.hpp index 29617cb..5f1020a 100644 --- a/librecomp/include/librecomp/mods.hpp +++ b/librecomp/include/librecomp/mods.hpp @@ -591,6 +591,7 @@ namespace recomp { void finish_event_setup(const ModContext& context); void finish_hook_setup(const ModContext& context); void reset_hooks(); + void register_hook_exports(); void run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slot_index); ModOpenError parse_manifest(ModManifest &ret, const std::vector &manifest_data, std::string &error_param); diff --git a/librecomp/src/mod_hooks.cpp b/librecomp/src/mod_hooks.cpp index 86c32eb..596b6e8 100644 --- a/librecomp/src/mod_hooks.cpp +++ b/librecomp/src/mod_hooks.cpp @@ -21,6 +21,10 @@ struct HookTableEntry { // Vector of individual hooks for each hook slot. std::vector hook_table{}; +// Holds the recomp context to restore after running each hook. This is a vector because a hook may end up calling another hooked function, +// so this acts as a stack of contexts to handle that recursion. +thread_local std::vector hook_contexts = { recomp_context{} }; + void recomp::mods::run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slot_index) { // Sanity check the hook slot index. if (hook_slot_index >= hook_table.size()) { @@ -31,7 +35,7 @@ void recomp::mods::run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slo } // Copy the initial context state to restore it after running each callback. - recomp_context initial_context = *ctx; + hook_contexts.emplace_back(*ctx); // Call every hook attached to the hook slot. const std::vector& hooks = hook_table[hook_slot_index].hooks; @@ -44,8 +48,11 @@ void recomp::mods::run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slo }, hook.func); // Restore the original context. - *ctx = initial_context; + *ctx = hook_contexts.back(); } + + // Pop the context after the hook is done. + hook_contexts.pop_back(); } void recomp::mods::setup_hooks(size_t num_hook_slots) { @@ -85,3 +92,65 @@ void recomp::mods::finish_hook_setup(const ModContext& context) { void recomp::mods::reset_hooks() { hook_table.clear(); } + +void recomphook_get_return_s32(uint8_t* rdram, recomp_context* ctx) { + ctx->r2 = (gpr)(int32_t)hook_contexts.back().r2; +} + +void recomphook_get_return_u32(uint8_t* rdram, recomp_context* ctx) { + recomphook_get_return_s32(rdram, ctx); +} + +void recomphook_get_return_ptr(uint8_t* rdram, recomp_context* ctx) { + recomphook_get_return_s32(rdram, ctx); +} + +void recomphook_get_return_s16(uint8_t* rdram, recomp_context* ctx) { + ctx->r2 = (gpr)(int16_t)hook_contexts.back().r2; +} + +void recomphook_get_return_u16(uint8_t* rdram, recomp_context* ctx) { + ctx->r2 = (gpr)(uint16_t)hook_contexts.back().r2; +} + +void recomphook_get_return_s8(uint8_t* rdram, recomp_context* ctx) { + ctx->r2 = (gpr)(int8_t)hook_contexts.back().r2; +} + +void recomphook_get_return_u8(uint8_t* rdram, recomp_context* ctx) { + ctx->r2 = (gpr)(uint8_t)hook_contexts.back().r2; +} + +void recomphook_get_return_s64(uint8_t* rdram, recomp_context* ctx) { + ctx->r2 = (gpr)(int32_t)hook_contexts.back().r2; + ctx->r3 = (gpr)(int32_t)hook_contexts.back().r3; +} + +void recomphook_get_return_u64(uint8_t* rdram, recomp_context* ctx) { + recomphook_get_return_s64(rdram, ctx); +} + +void recomphook_get_return_float(uint8_t* rdram, recomp_context* ctx) { + ctx->f0.fl = hook_contexts.back().f0.fl; +} + +void recomphook_get_return_double(uint8_t* rdram, recomp_context* ctx) { + ctx->f0.fl = (gpr)(uint8_t)hook_contexts.back().f0.fl; + ctx->f1.fl = (gpr)(uint8_t)hook_contexts.back().f1.fl; +} + +#define REGISTER_FUNC(name) recomp::overlays::register_base_export(#name, name) + +void recomp::mods::register_hook_exports() { + REGISTER_FUNC(recomphook_get_return_s32); + REGISTER_FUNC(recomphook_get_return_u32); + REGISTER_FUNC(recomphook_get_return_ptr); + REGISTER_FUNC(recomphook_get_return_s16); + REGISTER_FUNC(recomphook_get_return_u16); + REGISTER_FUNC(recomphook_get_return_s8); + REGISTER_FUNC(recomphook_get_return_u8); + REGISTER_FUNC(recomphook_get_return_s64); + REGISTER_FUNC(recomphook_get_return_u64); + REGISTER_FUNC(recomphook_get_return_float); + REGISTER_FUNC(recomphook_get_return_double); +} diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index 2a446cb..ddf7a02 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -780,6 +780,7 @@ void recomp::start( recomp::register_heap_exports(); recomp::mods::register_config_exports(); + recomp::mods::register_hook_exports(); std::thread game_thread{[](ultramodern::renderer::WindowHandle window_handle, uint8_t* rdram) { debug_printf("[Recomp] Starting\n");