Add exports to get the return value of functions from return hooks (#116)
Some checks failed
validate / ubuntu (arm64, Debug) (push) Has been cancelled
validate / ubuntu (arm64, Release) (push) Has been cancelled
validate / ubuntu (x64, Debug) (push) Has been cancelled
validate / ubuntu (x64, Release) (push) Has been cancelled
validate / windows (x64, Debug) (push) Has been cancelled
validate / windows (x64, Release) (push) Has been cancelled
validate / macos (arm64, Debug) (push) Has been cancelled
validate / macos (arm64, Release) (push) Has been cancelled
validate / macos (x64, Debug) (push) Has been cancelled
validate / macos (x64, Release) (push) Has been cancelled

This commit is contained in:
Wiseguy 2025-06-23 01:42:05 -04:00 committed by GitHub
parent 4ba1b54d22
commit 94b30d7061
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 73 additions and 2 deletions

View file

@ -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<char> &manifest_data, std::string &error_param);

View file

@ -21,6 +21,10 @@ struct HookTableEntry {
// Vector of individual hooks for each hook slot.
std::vector<HookTableEntry> 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<recomp_context> 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<HookEntry>& 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);
}

View file

@ -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");