Skip to content

Commit 94b30d7

Browse files
authored
Add exports to get the return value of functions from return hooks (#116)
1 parent 4ba1b54 commit 94b30d7

3 files changed

Lines changed: 73 additions & 2 deletions

File tree

librecomp/include/librecomp/mods.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ namespace recomp {
591591
void finish_event_setup(const ModContext& context);
592592
void finish_hook_setup(const ModContext& context);
593593
void reset_hooks();
594+
void register_hook_exports();
594595
void run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slot_index);
595596

596597
ModOpenError parse_manifest(ModManifest &ret, const std::vector<char> &manifest_data, std::string &error_param);

librecomp/src/mod_hooks.cpp

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ struct HookTableEntry {
2121
// Vector of individual hooks for each hook slot.
2222
std::vector<HookTableEntry> hook_table{};
2323

24+
// Holds the recomp context to restore after running each hook. This is a vector because a hook may end up calling another hooked function,
25+
// so this acts as a stack of contexts to handle that recursion.
26+
thread_local std::vector<recomp_context> hook_contexts = { recomp_context{} };
27+
2428
void recomp::mods::run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slot_index) {
2529
// Sanity check the hook slot index.
2630
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
3135
}
3236

3337
// Copy the initial context state to restore it after running each callback.
34-
recomp_context initial_context = *ctx;
38+
hook_contexts.emplace_back(*ctx);
3539

3640
// Call every hook attached to the hook slot.
3741
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
4448
}, hook.func);
4549

4650
// Restore the original context.
47-
*ctx = initial_context;
51+
*ctx = hook_contexts.back();
4852
}
53+
54+
// Pop the context after the hook is done.
55+
hook_contexts.pop_back();
4956
}
5057

5158
void recomp::mods::setup_hooks(size_t num_hook_slots) {
@@ -85,3 +92,65 @@ void recomp::mods::finish_hook_setup(const ModContext& context) {
8592
void recomp::mods::reset_hooks() {
8693
hook_table.clear();
8794
}
95+
96+
void recomphook_get_return_s32(uint8_t* rdram, recomp_context* ctx) {
97+
ctx->r2 = (gpr)(int32_t)hook_contexts.back().r2;
98+
}
99+
100+
void recomphook_get_return_u32(uint8_t* rdram, recomp_context* ctx) {
101+
recomphook_get_return_s32(rdram, ctx);
102+
}
103+
104+
void recomphook_get_return_ptr(uint8_t* rdram, recomp_context* ctx) {
105+
recomphook_get_return_s32(rdram, ctx);
106+
}
107+
108+
void recomphook_get_return_s16(uint8_t* rdram, recomp_context* ctx) {
109+
ctx->r2 = (gpr)(int16_t)hook_contexts.back().r2;
110+
}
111+
112+
void recomphook_get_return_u16(uint8_t* rdram, recomp_context* ctx) {
113+
ctx->r2 = (gpr)(uint16_t)hook_contexts.back().r2;
114+
}
115+
116+
void recomphook_get_return_s8(uint8_t* rdram, recomp_context* ctx) {
117+
ctx->r2 = (gpr)(int8_t)hook_contexts.back().r2;
118+
}
119+
120+
void recomphook_get_return_u8(uint8_t* rdram, recomp_context* ctx) {
121+
ctx->r2 = (gpr)(uint8_t)hook_contexts.back().r2;
122+
}
123+
124+
void recomphook_get_return_s64(uint8_t* rdram, recomp_context* ctx) {
125+
ctx->r2 = (gpr)(int32_t)hook_contexts.back().r2;
126+
ctx->r3 = (gpr)(int32_t)hook_contexts.back().r3;
127+
}
128+
129+
void recomphook_get_return_u64(uint8_t* rdram, recomp_context* ctx) {
130+
recomphook_get_return_s64(rdram, ctx);
131+
}
132+
133+
void recomphook_get_return_float(uint8_t* rdram, recomp_context* ctx) {
134+
ctx->f0.fl = hook_contexts.back().f0.fl;
135+
}
136+
137+
void recomphook_get_return_double(uint8_t* rdram, recomp_context* ctx) {
138+
ctx->f0.fl = (gpr)(uint8_t)hook_contexts.back().f0.fl;
139+
ctx->f1.fl = (gpr)(uint8_t)hook_contexts.back().f1.fl;
140+
}
141+
142+
#define REGISTER_FUNC(name) recomp::overlays::register_base_export(#name, name)
143+
144+
void recomp::mods::register_hook_exports() {
145+
REGISTER_FUNC(recomphook_get_return_s32);
146+
REGISTER_FUNC(recomphook_get_return_u32);
147+
REGISTER_FUNC(recomphook_get_return_ptr);
148+
REGISTER_FUNC(recomphook_get_return_s16);
149+
REGISTER_FUNC(recomphook_get_return_u16);
150+
REGISTER_FUNC(recomphook_get_return_s8);
151+
REGISTER_FUNC(recomphook_get_return_u8);
152+
REGISTER_FUNC(recomphook_get_return_s64);
153+
REGISTER_FUNC(recomphook_get_return_u64);
154+
REGISTER_FUNC(recomphook_get_return_float);
155+
REGISTER_FUNC(recomphook_get_return_double);
156+
}

librecomp/src/recomp.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ void recomp::start(
780780

781781
recomp::register_heap_exports();
782782
recomp::mods::register_config_exports();
783+
recomp::mods::register_hook_exports();
783784

784785
std::thread game_thread{[](ultramodern::renderer::WindowHandle window_handle, uint8_t* rdram) {
785786
debug_printf("[Recomp] Starting\n");

0 commit comments

Comments
 (0)