Implement mod events and callbacks

This commit is contained in:
Mr-Wiseguy 2024-08-31 02:25:23 -04:00
parent 904f17a872
commit 11c50bf349
5 changed files with 147 additions and 22 deletions

View file

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

View file

@ -53,6 +53,7 @@ namespace recomp {
FailedToLoadNativeCode,
InvalidReferenceSymbol,
InvalidImport,
InvalidCallbackEvent,
InvalidFunctionReplacement,
FailedToFindReplacement,
ReplacementConflict,
@ -162,10 +163,7 @@ namespace recomp {
std::unordered_set<std::string> enabled_mods;
std::unordered_map<recomp_func_t*, PatchData> patched_funcs;
std::unordered_map<std::string, size_t> loaded_mods_by_id;
// // Maps (mod id, export name) to (mod index, function index).
// std::unordered_map<std::pair<std::string, std::string>, std::pair<size_t, size_t>> mod_exports;
// // Maps (mod id, event name) to a vector of callback functions attached to that event.
// std::unordered_map<std::pair<std::string, std::string>, std::vector<GenericFunction>> 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<std::string, size_t> exports_by_name;
// List of global event indices ordered by the event's local index.
std::vector<size_t> global_event_indices;
// Mapping of event name to local index.
std::unordered_map<std::string, size_t> 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<recomp_func_t*> 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

View file

@ -0,0 +1,50 @@
#include <vector>
#include "librecomp/mods.hpp"
#include "ultramodern/error_handling.hpp"
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
// Vector of callbacks for each registered event.
std::vector<std::vector<recomp::mods::GenericFunction>> 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<recomp::mods::GenericFunction>& 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();
}

View file

@ -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:

View file

@ -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<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts>
@ -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::ModLoadErrorDetails> 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;
}