Skip to content

Commit 4ba1b54

Browse files
authored
Sort hooks and callbacks by mod order, with return hooks in reverse order (#115)
1 parent c5e268a commit 4ba1b54

File tree

5 files changed

+112
-23
lines changed

5 files changed

+112
-23
lines changed

librecomp/include/librecomp/mods.hpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ namespace recomp {
345345
std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename) const;
346346
std::filesystem::path get_mod_filename(const std::string& mod_id) const;
347347
size_t get_mod_order_index(const std::string& mod_id) const;
348+
size_t get_mod_order_index(size_t mod_index) const;
348349
std::optional<ModDetails> get_details_for_mod(const std::string& mod_id) const;
349350
std::vector<ModDetails> get_all_mod_details(const std::string& mod_game_id);
350351
recomp::Version get_mod_version(size_t mod_index);
@@ -378,6 +379,7 @@ namespace recomp {
378379
const std::unordered_map<recomp_func_t*, overlays::BasePatchedFunction>& base_patched_funcs,
379380
std::span<const uint8_t> decompressed_rom);
380381
void dirty_mod_configuration_thread_process();
382+
void rebuild_mod_order_lookup();
381383

382384
static void on_code_mod_enabled(ModContext& context, const ModHandle& mod);
383385

@@ -389,7 +391,8 @@ namespace recomp {
389391
std::vector<ModHandle> opened_mods;
390392
std::unordered_map<std::string, size_t> opened_mods_by_id;
391393
std::unordered_map<std::filesystem::path::string_type, size_t> opened_mods_by_filename;
392-
std::vector<size_t> opened_mods_order;
394+
std::vector<size_t> opened_mods_order; // order index -> mod index
395+
std::vector<size_t> mod_order_lookup; // mod index -> order index
393396
std::mutex opened_mods_mutex;
394397
std::unordered_set<std::string> mod_ids;
395398
std::unordered_set<std::string> enabled_mods;
@@ -579,11 +582,14 @@ namespace recomp {
579582
};
580583

581584
void setup_events(size_t num_events);
582-
void register_event_callback(size_t event_index, GenericFunction callback);
585+
void register_event_callback(size_t event_index, size_t mod_index, GenericFunction callback);
583586
void reset_events();
584587

585588
void setup_hooks(size_t num_hook_slots);
586-
void register_hook(size_t hook_slot_index, GenericFunction callback);
589+
void set_hook_type(size_t hook_slot_index, bool is_return_hook);
590+
void register_hook(size_t hook_slot_index, size_t mod_index, GenericFunction callback);
591+
void finish_event_setup(const ModContext& context);
592+
void finish_hook_setup(const ModContext& context);
587593
void reset_hooks();
588594
void run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slot_index);
589595

@@ -611,6 +617,7 @@ namespace recomp {
611617
std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename);
612618
std::filesystem::path get_mod_filename(const std::string& mod_id);
613619
size_t get_mod_order_index(const std::string& mod_id);
620+
size_t get_mod_order_index(size_t mod_index);
614621
ModContentTypeId register_mod_content_type(const ModContentType& type);
615622
bool register_mod_container_type(const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest);
616623

librecomp/src/mod_events.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ struct overloaded : Ts... { using Ts::operator()...; };
88
template<class... Ts>
99
overloaded(Ts...) -> overloaded<Ts...>;
1010

11+
struct EventCallback {
12+
size_t mod_index;
13+
recomp::mods::GenericFunction func;
14+
};
15+
1116
// Vector of callbacks for each registered event.
12-
std::vector<std::vector<recomp::mods::GenericFunction>> event_callbacks{};
17+
std::vector<std::vector<EventCallback>> event_callbacks{};
1318

1419
extern "C" {
1520
// 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
2934
recomp_context initial_context = *ctx;
3035

3136
// Call every callback attached to the event.
32-
const std::vector<recomp::mods::GenericFunction>& callbacks = event_callbacks[event_index];
33-
for (recomp::mods::GenericFunction func : callbacks) {
37+
const std::vector<EventCallback>& callbacks = event_callbacks[event_index];
38+
for (const EventCallback& callback : callbacks) {
3439
// Run the callback.
3540
std::visit(overloaded {
3641
[rdram, ctx](recomp_func_t* native_func) {
3742
native_func(rdram, ctx);
3843
},
39-
}, func);
44+
}, callback.func);
4045

4146
// Restore the original context.
4247
*ctx = initial_context;
@@ -47,8 +52,19 @@ void recomp::mods::setup_events(size_t num_events) {
4752
event_callbacks.resize(num_events);
4853
}
4954

50-
void recomp::mods::register_event_callback(size_t event_index, GenericFunction callback) {
51-
event_callbacks[event_index].emplace_back(callback);
55+
void recomp::mods::register_event_callback(size_t event_index, size_t mod_index, GenericFunction callback) {
56+
event_callbacks[event_index].emplace_back(EventCallback{ mod_index, callback });
57+
}
58+
59+
void recomp::mods::finish_event_setup(const ModContext& context) {
60+
// Sort callbacks by mod order.
61+
for (std::vector<EventCallback>& cur_entry : event_callbacks) {
62+
std::sort(cur_entry.begin(), cur_entry.end(),
63+
[&context](const EventCallback& lhs, const EventCallback& rhs) {
64+
return context.get_mod_order_index(lhs.mod_index) < context.get_mod_order_index(rhs.mod_index);
65+
}
66+
);
67+
}
5268
}
5369

5470
void recomp::mods::reset_events() {

librecomp/src/mod_hooks.cpp

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@ struct overloaded : Ts... { using Ts::operator()...; };
88
template<class... Ts>
99
overloaded(Ts...) -> overloaded<Ts...>;
1010

11+
struct HookEntry {
12+
size_t mod_index;
13+
recomp::mods::GenericFunction func;
14+
};
15+
16+
struct HookTableEntry {
17+
std::vector<HookEntry> hooks;
18+
bool is_return_hook;
19+
};
20+
1121
// Vector of individual hooks for each hook slot.
12-
std::vector<std::vector<recomp::mods::GenericFunction>> hook_table{};
22+
std::vector<HookTableEntry> hook_table{};
1323

1424
void recomp::mods::run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slot_index) {
1525
// 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
2434
recomp_context initial_context = *ctx;
2535

2636
// Call every hook attached to the hook slot.
27-
const std::vector<recomp::mods::GenericFunction>& hooks = hook_table[hook_slot_index];
28-
for (recomp::mods::GenericFunction func : hooks) {
37+
const std::vector<HookEntry>& hooks = hook_table[hook_slot_index].hooks;
38+
for (HookEntry hook : hooks) {
2939
// Run the hook.
3040
std::visit(overloaded {
3141
[rdram, ctx](recomp_func_t* native_func) {
3242
native_func(rdram, ctx);
3343
},
34-
}, func);
44+
}, hook.func);
3545

3646
// Restore the original context.
3747
*ctx = initial_context;
@@ -42,8 +52,34 @@ void recomp::mods::setup_hooks(size_t num_hook_slots) {
4252
hook_table.resize(num_hook_slots);
4353
}
4454

45-
void recomp::mods::register_hook(size_t hook_slot_index, GenericFunction callback) {
46-
hook_table[hook_slot_index].emplace_back(callback);
55+
void recomp::mods::set_hook_type(size_t hook_slot_index, bool is_return) {
56+
hook_table[hook_slot_index].is_return_hook = is_return;
57+
}
58+
59+
void recomp::mods::register_hook(size_t hook_slot_index, size_t mod_index, GenericFunction callback) {
60+
hook_table[hook_slot_index].hooks.emplace_back(HookEntry{ mod_index, callback });
61+
}
62+
63+
void recomp::mods::finish_hook_setup(const ModContext& context) {
64+
// Sort hooks by mod order (and return hooks in reverse order).
65+
for (HookTableEntry& cur_entry : hook_table) {
66+
// Reverse sort if this slot is a return hook.
67+
if (cur_entry.is_return_hook) {
68+
std::sort(cur_entry.hooks.begin(), cur_entry.hooks.end(),
69+
[&context](const HookEntry& lhs, const HookEntry& rhs) {
70+
return context.get_mod_order_index(lhs.mod_index) > context.get_mod_order_index(rhs.mod_index);
71+
}
72+
);
73+
}
74+
// Otherwise sort normally.
75+
else {
76+
std::sort(cur_entry.hooks.begin(), cur_entry.hooks.end(),
77+
[&context](const HookEntry& lhs, const HookEntry& rhs) {
78+
return context.get_mod_order_index(lhs.mod_index) < context.get_mod_order_index(rhs.mod_index);
79+
}
80+
);
81+
}
82+
}
4783
}
4884

4985
void recomp::mods::reset_hooks() {

librecomp/src/mods.cpp

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class recomp::mods::DynamicLibrary {
131131

132132
template <typename T>
133133
bool get_dll_symbol(T& out, const char* name) const {
134-
out = (T)GetProcAddress(native_handle, name);
134+
out = (T)(void*)GetProcAddress(native_handle, name);
135135
if (out == nullptr) {
136136
return false;
137137
}
@@ -638,6 +638,7 @@ void recomp::mods::ModContext::close_mods() {
638638
opened_mods_by_filename.clear();
639639
opened_mods.clear();
640640
opened_mods_order.clear();
641+
mod_order_lookup.clear();
641642
mod_ids.clear();
642643
enabled_mods.clear();
643644
auto_enabled_mods.clear();
@@ -877,6 +878,8 @@ void recomp::mods::ModContext::load_mods_config() {
877878
return sort_order[i] < sort_order[j];
878879
});
879880

881+
rebuild_mod_order_lookup();
882+
880883
// Enable mods that are specified in the configuration or mods that are considered new.
881884
for (size_t i = 0; i < opened_mods.size(); i++) {
882885
const ModHandle& mod = opened_mods[i];
@@ -889,6 +892,18 @@ void recomp::mods::ModContext::load_mods_config() {
889892
}
890893
}
891894

895+
void recomp::mods::ModContext::rebuild_mod_order_lookup() {
896+
// Initialize the mod order lookup to all -1 so that mods that aren't enabled have an order index of -1.
897+
mod_order_lookup.resize(opened_mods.size());
898+
std::fill(mod_order_lookup.begin(), mod_order_lookup.end(), static_cast<size_t>(-1));
899+
900+
// Build the lookup of mod index to mod order by inverting the opened mods order list.
901+
for (size_t mod_order_index = 0; mod_order_index < opened_mods_order.size(); mod_order_index++) {
902+
size_t mod_index = opened_mods_order[mod_order_index];
903+
mod_order_lookup[mod_index] = mod_order_index;
904+
}
905+
}
906+
892907
recomp::mods::ModContext::ModContext() {
893908
// Register the code content type.
894909
ModContentType code_content_type {
@@ -1128,14 +1143,18 @@ size_t recomp::mods::ModContext::get_mod_order_index(const std::string& mod_id)
11281143
return static_cast<size_t>(-1);
11291144
}
11301145

1131-
// TODO keep a mapping of mod index to mod order index to prevent needing a lookup here.
1132-
auto find_order_it = std::find(opened_mods_order.begin(), opened_mods_order.end(), find_it->second);
1133-
if (find_order_it == opened_mods_order.end()) {
1146+
return get_mod_order_index(find_it->second);
1147+
}
1148+
1149+
size_t recomp::mods::ModContext::get_mod_order_index(size_t mod_index) const {
1150+
size_t order_index = mod_order_lookup[mod_index];
1151+
// Check if the mod has a proper order index and assert if it doesn't, as that means the mod isn't actually loaded.
1152+
if (order_index == static_cast<size_t>(-1)) {
11341153
assert(false);
11351154
return static_cast<size_t>(-1);
11361155
}
11371156

1138-
return find_order_it - opened_mods_order.begin();
1157+
return order_index;
11391158
}
11401159

11411160
std::optional<recomp::mods::ModDetails> 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
13471366
opened_mods_order.push_back(mod_index);
13481367
}
13491368

1369+
rebuild_mod_order_lookup();
1370+
13501371
for (ModContentTypeId type_id : opened_mods[mod_index].content_types) {
13511372
content_reordered_callback* callback = content_types[type_id.value].on_reordered;
13521373
if (callback) {
@@ -1655,12 +1676,13 @@ std::vector<recomp::mods::ModLoadErrorDetails> recomp::mods::ModContext::load_mo
16551676

16561677
// Regenerate any remaining hook slots that weren't handled during mod recompilation.
16571678

1658-
// List of unprocessed hooks and their hook index.
1679+
// List of unprocessed hooks and their hook index. Also set up which hooks are return hooks.
16591680
std::vector<std::pair<recomp::mods::HookDefinition, size_t>> unprocessed_hooks;
16601681
for (const auto& [def, index] : hook_slots) {
16611682
if (!processed_hook_slots[index]) {
16621683
unprocessed_hooks.emplace_back(std::make_pair(def, index));
16631684
}
1685+
recomp::mods::set_hook_type(index, def.at_return);
16641686
}
16651687

16661688
if (!unprocessed_hooks.empty()) {
@@ -1685,6 +1707,9 @@ std::vector<recomp::mods::ModLoadErrorDetails> recomp::mods::ModContext::load_mo
16851707
}
16861708
}
16871709

1710+
finish_event_setup(*this);
1711+
finish_hook_setup(*this);
1712+
16881713
active_game = mod_game_index;
16891714
return ret;
16901715
}
@@ -2389,7 +2414,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci
23892414
return CodeModLoadError::InvalidCallbackEvent;
23902415
}
23912416

2392-
recomp::mods::register_event_callback(event_index, func);
2417+
recomp::mods::register_event_callback(event_index, mod_index, func);
23932418
}
23942419

23952420
// Register hooks.
@@ -2411,7 +2436,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci
24112436

24122437
// Register the function handle for this hook slot.
24132438
GenericFunction func = mod.code_handle->get_function_handle(cur_hook.func_index);
2414-
recomp::mods::register_hook(find_it->second, func);
2439+
recomp::mods::register_hook(find_it->second, mod_index, func);
24152440
}
24162441

24172442
// Populate the relocated section addresses for the mod.

librecomp/src/recomp.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,11 @@ size_t recomp::mods::get_mod_order_index(const std::string& mod_id) {
585585
return mod_context->get_mod_order_index(mod_id);
586586
}
587587

588+
size_t recomp::mods::get_mod_order_index(size_t mod_index) {
589+
std::lock_guard lock { mod_context_mutex };
590+
return mod_context->get_mod_order_index(mod_index);
591+
}
592+
588593
std::optional<recomp::mods::ModDetails> recomp::mods::get_details_for_mod(const std::string& mod_id) {
589594
std::lock_guard lock { mod_context_mutex };
590595
return mod_context->get_details_for_mod(mod_id);

0 commit comments

Comments
 (0)