mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2026-05-10 19:01:53 +00:00
Implement mod events and callbacks
This commit is contained in:
parent
904f17a872
commit
11c50bf349
5 changed files with 147 additions and 22 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
50
librecomp/src/mod_events.cpp
Normal file
50
librecomp/src/mod_events.cpp
Normal 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();
|
||||
}
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue