From 277cb5d7d3c6cfbe804cef655b15e1631c0e4245 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Fri, 21 Feb 2025 00:07:13 -0500 Subject: [PATCH] Implement actor extension data and use it for transform tagging --- CMakeLists.txt | 1 + include/recomp_data.h | 9 ++ patches/actor_data.c | 91 ++++++++++++ patches/actor_transform_tagging.c | 74 +++++++--- patches/extended_actors.h | 14 ++ patches/mem_funcs.h | 14 ++ patches/required_patches.c | 4 + patches/syms.ld | 7 + patches/transform_ids.h | 61 ++------ src/game/recomp_mem_api.cpp | 232 ++++++++++++++++++++++++++++++ src/main/main.cpp | 2 + src/ui/elements/ui_types.h | 1 + 12 files changed, 445 insertions(+), 65 deletions(-) create mode 100644 include/recomp_data.h create mode 100644 patches/actor_data.c create mode 100644 patches/extended_actors.h create mode 100644 patches/mem_funcs.h create mode 100644 src/game/recomp_mem_api.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f0ef7fa..637e8b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,6 +153,7 @@ set (SOURCES ${CMAKE_SOURCE_DIR}/src/game/debug.cpp ${CMAKE_SOURCE_DIR}/src/game/quicksaving.cpp ${CMAKE_SOURCE_DIR}/src/game/recomp_api.cpp + ${CMAKE_SOURCE_DIR}/src/game/recomp_mem_api.cpp ${CMAKE_SOURCE_DIR}/src/game/rom_decompression.cpp ${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp diff --git a/include/recomp_data.h b/include/recomp_data.h new file mode 100644 index 0000000..87693c7 --- /dev/null +++ b/include/recomp_data.h @@ -0,0 +1,9 @@ +#ifndef __RECOMP_DATA_H__ +#define __RECOMP_DATA_H__ + +namespace recomp { + void init_extended_actor_data(); + void reset_actor_data(); +} + +#endif diff --git a/patches/actor_data.c b/patches/actor_data.c new file mode 100644 index 0000000..33c4d9f --- /dev/null +++ b/patches/actor_data.c @@ -0,0 +1,91 @@ +#include "patches.h" +#include "extended_actors.h" +#include "transform_ids.h" +#include "mem_funcs.h" + +// Use 32 bits of compiler-inserted padding to hold the actor's slot. +// 0x22 between halfDaysBits and world +#define actorIdByte0(actor) ((u8*)(actor))[0x22] +// 0x23 between halfDaysBits and world +#define actorIdByte1(actor) ((u8*)(actor))[0x23] +// 0x3A between audioFlags and focus +#define actorIdByte2(actor) ((u8*)(actor))[0x3A] +// 0x3B between audioFlags and focus +#define actorIdByte3(actor) ((u8*)(actor))[0x3B] + +u32 actor_get_slot(Actor* actor) { + return (actorIdByte0(actor) << 24) | (actorIdByte1(actor) << 16) | (actorIdByte2(actor) << 8) | (actorIdByte3(actor) << 0); +} + +void actor_set_slot(Actor* actor, ActorExtensionId slot) { + u32 b0 = (slot >> 24) & 0xFF; + u32 b1 = (slot >> 16) & 0xFF; + u32 b2 = (slot >> 8) & 0xFF; + u32 b3 = (slot >> 0) & 0xFF; + + actorIdByte0(actor) = b0; + actorIdByte1(actor) = b1; + actorIdByte2(actor) = b2; + actorIdByte3(actor) = b3; +} + +RECOMP_EXPORT ActorExtensionId z64recomp_extend_actor(s16 actor_id, u32 size) { + return recomp_register_actor_extension(actor_id, size); +} + +RECOMP_EXPORT ActorExtensionId z64recomp_extend_actor_all(u32 size) { + return recomp_register_actor_extension_generic(size); +} + +RECOMP_EXPORT void* z64recomp_get_extended_actor_data(Actor* actor, ActorExtensionId extension) { + return recomp_get_actor_data(actor_get_slot(actor), extension, actor->id); +} + +RECOMP_EXPORT u32 z64recomp_get_actor_spawn_index(Actor* actor) { + return recomp_get_actor_spawn_index(actor_get_slot(actor)); +} + +RECOMP_EXPORT u32 actor_transform_id(Actor* actor) { + u32 spawn_index = z64recomp_get_actor_spawn_index(actor); + + return (spawn_index * ACTOR_TRANSFORM_ID_COUNT) + ACTOR_TRANSFORM_ID_START; +} + +typedef enum { + ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED = 1 << 0, + ACTOR_CUSTOM_FLAG_1 = 1 << 1, +} CustomActorFlags; + +typedef struct { + CustomActorFlags flags; +} BaseActorExtensionData; + +ActorExtensionId base_actor_extension_handle; + +void register_base_actor_extensions() { + base_actor_extension_handle = z64recomp_extend_actor_all(sizeof(BaseActorExtensionData)); +} + +BaseActorExtensionData* get_base_extension_data(Actor* actor) { + return (BaseActorExtensionData*)z64recomp_get_extended_actor_data(actor, base_actor_extension_handle); +} + +RECOMP_EXPORT u32 actor_get_interpolation_skipped(Actor* actor) { + return (get_base_extension_data(actor)->flags & ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED) != 0; +} + +RECOMP_EXPORT void actor_set_interpolation_skipped(Actor* actor) { + get_base_extension_data(actor)->flags |= ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED; +} + +RECOMP_EXPORT void actor_clear_interpolation_skipped(Actor* actor) { + get_base_extension_data(actor)->flags &= ~ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED; +} + +void actor_set_custom_flag_1(Actor* actor) { + get_base_extension_data(actor)->flags |= ACTOR_CUSTOM_FLAG_1; +} + +bool actor_get_custom_flag_1(Actor* actor) { + return (get_base_extension_data(actor)->flags & ACTOR_CUSTOM_FLAG_1) != 0; +} diff --git a/patches/actor_transform_tagging.c b/patches/actor_transform_tagging.c index 7e5e8b6..c38d0a3 100644 --- a/patches/actor_transform_tagging.c +++ b/patches/actor_transform_tagging.c @@ -1,13 +1,16 @@ #include "patches.h" #include "fault.h" #include "transform_ids.h" +#include "extended_actors.h" #include "z64actor.h" - -u16 next_actor_transform = 0; +#include "mem_funcs.h" extern FaultClient sActorFaultClient; +void Actor_Destroy(Actor* actor, PlayState* play); Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play); void ZeldaArena_Free(void* ptr); +Actor* Actor_RemoveFromCategory(PlayState* play, ActorContext* actorCtx, Actor* actorToRemove); +void Actor_FreeOverlay(ActorOverlay* entry); RECOMP_PATCH void Actor_CleanupContext(ActorContext* actorCtx, PlayState* play) { s32 i; @@ -34,26 +37,22 @@ RECOMP_PATCH void Actor_CleanupContext(ActorContext* actorCtx, PlayState* play) actorCtx->absoluteSpace = NULL; } - // @recomp Reset the actor transform IDs as all actors have been deleted. - next_actor_transform = 0; + // @recomp Reset the actor extension data. + recomp_clear_all_actor_data(); Play_SaveCycleSceneFlags(&play->state); ActorOverlayTable_Cleanup(); } -u32 create_actor_transform_id() { - u32 ret = next_actor_transform; - next_actor_transform++; - - return ret; -} - RECOMP_DECLARE_EVENT(recomp_should_actor_init(PlayState* play, Actor* actor, bool* should)); RECOMP_DECLARE_EVENT(recomp_after_actor_init(PlayState* play, Actor* actor)); RECOMP_DECLARE_EVENT(recomp_should_actor_update(PlayState* play, Actor* actor, bool* should)); RECOMP_DECLARE_EVENT(recomp_after_actor_update(PlayState* play, Actor* actor)); RECOMP_PATCH void Actor_Init(Actor* actor, PlayState* play) { + // @recomp Allocate the actor's extension data. + actor_set_slot(actor, recomp_create_actor_data(actor->id)); + Actor_SetWorldToHome(actor); Actor_SetShapeRotToWorld(actor); Actor_SetFocus(actor, 0.0f); @@ -88,11 +87,47 @@ RECOMP_PATCH void Actor_Init(Actor* actor, PlayState* play) { Actor_Kill(actor); } } +} + +RECOMP_PATCH Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play) { + s32 pad; + Player* player = GET_PLAYER(play); + Actor* newHead; + ActorOverlay* overlayEntry = actor->overlayEntry; + + if ((player != NULL) && (actor == player->lockOnActor)) { + Player_Untarget(player); + Camera_ChangeMode(Play_GetCamera(play, Play_GetActiveCamId(play)), CAM_MODE_NORMAL); + } + + if (actor == actorCtx->targetCtx.fairyActor) { + actorCtx->targetCtx.fairyActor = NULL; + } + + if (actor == actorCtx->targetCtx.forcedTargetActor) { + actorCtx->targetCtx.forcedTargetActor = NULL; + } + + if (actor == actorCtx->targetCtx.bgmEnemy) { + actorCtx->targetCtx.bgmEnemy = NULL; + } + + AudioSfx_StopByPos(&actor->projectedPos); + Actor_Destroy(actor, play); + + newHead = Actor_RemoveFromCategory(play, actorCtx, actor); + + // @recomp Destroy the actor's extension data. + recomp_destroy_actor_data(actor_get_slot(actor)); - // @recomp Pick a transform ID for this actor and encode it into struct padding - u32 cur_transform_id = create_actor_transform_id(); - actorIdByte0(actor) = (cur_transform_id >> 0) & 0xFF; - actorIdByte1(actor) = (cur_transform_id >> 8) & 0xFF;; + ZeldaArena_Free(actor); + + if (overlayEntry->vramStart != NULL) { + overlayEntry->numLoaded--; + Actor_FreeOverlay(overlayEntry); + } + + return newHead; } // @recomp Copied from z_actor.c @@ -106,8 +141,6 @@ typedef struct { /* 0x18 */ u32 updateActorFlagsMask; // Actor will update only if at least 1 actor flag is set in this bitmask } UpdateActor_Params; // size = 0x1C -void Actor_Destroy(Actor* actor, PlayState* play); - RECOMP_PATCH Actor* Actor_UpdateActor(UpdateActor_Params* params) { PlayState* play = params->play; Actor* actor = params->actor; @@ -1403,3 +1436,10 @@ RECOMP_PATCH void Actor_Draw(PlayState* play, Actor* actor) { CLOSE_DISPS(play->state.gfxCtx); } + +ActorExtensionId z64recomp_extend_actor(s16 actor_id, u32 size); +ActorExtensionId z64recomp_extend_actor_all(u32 size); + +void* z64recomp_get_extended_actor_data(Actor* actor, ActorExtensionId extension); +u32 z64recomp_get_actor_spawn_index(Actor* actor); + diff --git a/patches/extended_actors.h b/patches/extended_actors.h new file mode 100644 index 0000000..19c12a4 --- /dev/null +++ b/patches/extended_actors.h @@ -0,0 +1,14 @@ +#ifndef __EXTENDED_ACTORS_H__ +#define __EXTENDED_ACTORS_H__ + +#include "global.h" + +typedef u32 ActorExtensionId; + +ActorExtensionId z64recomp_extend_actor(s16 actor_id, u32 size); +ActorExtensionId z64recomp_extend_actor_all(u32 size); + +void* z64recomp_get_extended_actor_data(Actor* actor, ActorExtensionId extension); +u32 z64recomp_get_actor_spawn_index(Actor* actor); + +#endif diff --git a/patches/mem_funcs.h b/patches/mem_funcs.h new file mode 100644 index 0000000..6c091e1 --- /dev/null +++ b/patches/mem_funcs.h @@ -0,0 +1,14 @@ +#ifndef __MEM_FUNCS_H__ +#define __MEM_FUNCS_H__ + +#include "patch_helpers.h" + +DECLARE_FUNC(u32, recomp_register_actor_extension, u32 actor_type, u32 size); +DECLARE_FUNC(u32, recomp_register_actor_extension_generic, u32 size); +DECLARE_FUNC(void, recomp_clear_all_actor_data); +DECLARE_FUNC(u32, recomp_create_actor_data, u32 actor_type); +DECLARE_FUNC(void, recomp_destroy_actor_data, u32 actor_handle); +DECLARE_FUNC(void*, recomp_get_actor_data, u32 actor_handle, u32 extension_handle, u32 actor_type); +DECLARE_FUNC(u32, recomp_get_actor_spawn_index, u32 actor_handle); + +#endif diff --git a/patches/required_patches.c b/patches/required_patches.c index 7aada58..8fbb140 100644 --- a/patches/required_patches.c +++ b/patches/required_patches.c @@ -1,5 +1,6 @@ #include "patches.h" #include "misc_funcs.h" +#include "transform_ids.h" #include "loadfragment.h" void Main_ClearMemory(void* begin, void* end); @@ -16,6 +17,9 @@ RECOMP_PATCH void Main_Init(void) { OSMesg msg[1]; size_t prevSize; + // @recomp Register base actor extensions. + register_base_actor_extensions(); + // @recomp_event recomp_on_init(): Allow mods to initialize themselves once. recomp_on_init(); diff --git a/patches/syms.ld b/patches/syms.ld index 2e80775..2efa2c9 100644 --- a/patches/syms.ld +++ b/patches/syms.ld @@ -46,3 +46,10 @@ recomp_get_resolution_scale = 0x8F0000AC; recomp_get_analog_inverted_axes = 0x8F0000B0; recomp_get_window_resolution = 0x8F0000B4; recomp_run_ui_callbacks = 0x8F0000B8; +recomp_register_actor_extension = 0x8F0000BC; +recomp_register_actor_extension_generic = 0x8F0000C0; +recomp_clear_all_actor_data = 0x8F0000C4; +recomp_create_actor_data = 0x8F0000C8; +recomp_destroy_actor_data = 0x8F0000CC; +recomp_get_actor_data = 0x8F0000D0; +recomp_get_actor_spawn_index = 0x8F0000D4; diff --git a/patches/transform_ids.h b/patches/transform_ids.h index 0562b73..8cd9f15 100644 --- a/patches/transform_ids.h +++ b/patches/transform_ids.h @@ -1,6 +1,8 @@ #ifndef __TRANSFORM_IDS_H__ #define __TRANSFORM_IDS_H__ +#include "extended_actors.h" + #define CAMERA_TRANSFORM_ID 0x10U #define CIRCLE_OVERLAY_TRANSFORM_ID 0x11U #define CIRCLE_OVERLAY_TRANSFORM_PROJECTION_ID 0x12U @@ -40,55 +42,18 @@ #define ACTOR_TRANSFORM_ID_COUNT (ACTOR_TRANSFORM_LIMB_COUNT * 2) // One ID for each limb and another for each post-draw #define ACTOR_TRANSFORM_ID_START 0x1000000U -// Use 16 bits of compiler-inserted padding to hold the actor's transform ID. -// 0x22 between halfDaysBits and world -#define actorIdByte0(actor) ((u8*)(actor))[0x22] -// 0x23 between halfDaysBits and world -#define actorIdByte1(actor) ((u8*)(actor))[0x23] -// 0x3A between audioFlags and focus -#define actorIdByte2(actor) ((u8*)(actor))[0x3A] - -// Other unused padding: -// 0x3B between audioFlags and focus - -static inline u32 actor_transform_id(Actor* actor) { - u32 actor_id = - (actorIdByte0(actor) << 0) | - (actorIdByte1(actor) << 8); - - return (actor_id * ACTOR_TRANSFORM_ID_COUNT) + ACTOR_TRANSFORM_ID_START; -} - -typedef enum { - ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED = 1 << 0, - ACTOR_CUSTOM_FLAG_1 = 1 << 1, -} CustomActorFlags; - -static inline u32 actor_get_interpolation_skipped(Actor* actor) { - return (actorIdByte2(actor) & ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED) != 0; -} - -static inline void actor_set_interpolation_skipped(Actor* actor) { - actorIdByte2(actor) |= ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED; -} - -static inline void actor_clear_interpolation_skipped(Actor* actor) { - actorIdByte2(actor) &= ~ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED; -} - -static inline void actor_set_custom_flag_1(Actor* actor) { - actorIdByte2(actor) |= ACTOR_CUSTOM_FLAG_1; -} - -static inline void actor_clear_custom_flag_1(Actor* actor) { - actorIdByte2(actor) &= ~ACTOR_CUSTOM_FLAG_1; -} - -static inline bool actor_get_custom_flag_1(Actor* actor) { - return (actorIdByte2(actor) & ACTOR_CUSTOM_FLAG_1) != 0; -} - +u32 actor_transform_id(Actor* actor); +u32 actor_get_interpolation_skipped(Actor* actor); +void actor_set_interpolation_skipped(Actor* actor); +void actor_clear_interpolation_skipped(Actor* actor); +void actor_set_custom_flag_1(Actor* actor); +bool actor_get_custom_flag_1(Actor* actor); void force_camera_interpolation(); void force_camera_skip_interpolation(); +ActorExtensionId actor_get_slot(Actor* actor); +void actor_set_slot(Actor* actor, ActorExtensionId); + +void register_base_actor_extensions(); + #endif diff --git a/src/game/recomp_mem_api.cpp b/src/game/recomp_mem_api.cpp new file mode 100644 index 0000000..e0426b4 --- /dev/null +++ b/src/game/recomp_mem_api.cpp @@ -0,0 +1,232 @@ +#include +#include + +#include "slot_map.h" + +#include "librecomp/helpers.hpp" +#include "librecomp/addresses.hpp" +#include "ultramodern/error_handling.hpp" +#include "recomp_ui.h" +#include "recomp_data.h" +#include "../patches/mem_funcs.h" + +struct ExtensionInfo { + // Either the actor's type ID, or 0xFFFFFFFF if this is for generic data. + uint32_t actor_type; + // The offset from either the start of the actor's data or the start of the actor's specific extension data depending on the value of actor_type. + uint32_t data_offset; +}; + +struct ExtensionData { + uint32_t actor_spawn_index; + PTR(void) data_addr; +}; + +std::mutex actor_data_mutex{}; +// The total size of actor-specific extension data for each actor type. +std::vector actor_data_sizes{}; +// The total size of all generic actor extension data. +uint32_t generic_data_size; +// The registered actor extensions. +std::vector actor_extensions{}; +// The extension data for every actor. +using actor_data_map_t = dod::slot_map32; +actor_data_map_t actor_data{}; +// The number of actors spawned since the last reset. +uint32_t actor_spawn_count = 0; +// Whether or not extensions can be registered at this time. +bool can_register = false; + +// Debug counters. +size_t alloc_count = 0; +size_t free_count = 0; + +void recomp::init_extended_actor_data() { + std::lock_guard lock{ actor_data_mutex }; + + actor_data_sizes.clear(); + generic_data_size = 0; + actor_extensions.clear(); + actor_data.reset(); + actor_spawn_count = 0; + can_register = true; + // Create a dummy extension so the first extension handle is nonzero, should help catch bugs. + actor_extensions.push_back({}); +} + +void recomp::reset_actor_data() { + std::lock_guard lock{ actor_data_mutex }; + actor_data.reset(); + actor_spawn_count = 0; + + assert(alloc_count == free_count); + alloc_count = 0; + free_count = 0; +} + +constexpr uint32_t round_up_16(uint32_t value) { + return (value + 15) & (~15); +} + +extern "C" void recomp_register_actor_extension(uint8_t* rdram, recomp_context* ctx) { + u32 actor_type = _arg<0, u32>(rdram, ctx); + u32 size = _arg<1, u32>(rdram, ctx); + + if (!can_register) { + recompui::message_box("Fatal error in mod - attempted to register actor extension data after actors have been spawned."); + assert(false); + ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__); + } + + if (actor_data_sizes.size() <= actor_type) { + actor_data_sizes.resize(2 * actor_type); + } + + // Increase the actor type's extension data size by the provided size (rounded up to a multiple of 16). + uint32_t data_offset = actor_data_sizes[actor_type]; + actor_data_sizes[actor_type] += round_up_16(size); + + // Register the extension. + uint32_t ret = static_cast(actor_extensions.size()); + actor_extensions.emplace_back(ExtensionInfo{.actor_type = actor_type, .data_offset = data_offset}); + + // printf("Registered actor extension data for type %u (size 0x%08X, offset 0x%08X)\n", actor_type, size, data_offset); + + _return(ctx, ret); +} + +extern "C" void recomp_register_actor_extension_generic(uint8_t* rdram, recomp_context* ctx) { + u32 size = _arg<0, u32>(rdram, ctx); + + // Increase the generic extension data size by the provided size (rounded up to a multiple of 16). + uint32_t data_offset = generic_data_size; + generic_data_size += round_up_16(size); + + // Register the extension. + uint32_t ret = static_cast(actor_extensions.size()); + actor_extensions.emplace_back(ExtensionInfo{.actor_type = 0xFFFFFFFFU, .data_offset = data_offset}); + + // printf("Registered generic actor extension data (size 0x%08X, offset 0x%08X)\n", size, data_offset); + _return(ctx, ret); +} + +extern "C" void recomp_clear_all_actor_data(uint8_t* rdram, recomp_context* ctx) { + (void)rdram; + (void)ctx; + recomp::reset_actor_data(); +} + +extern "C" void recomp_create_actor_data(uint8_t* rdram, recomp_context* ctx) { + std::lock_guard lock{ actor_data_mutex }; + + can_register = false; + + // Determine the number of bytes to allocate based on the actor type's extensions and the generic extensions. + u32 actor_type = _arg<0, u32>(rdram, ctx); + u32 alloc_size = generic_data_size; + [[maybe_unused]] u32 type_data_size = 0; + + if (actor_type < actor_data_sizes.size()) { + type_data_size = actor_data_sizes[actor_type]; + alloc_size += type_data_size; + } + + // Allocate the extension data if it's of nonzero size. + PTR(void) data_ptr = NULLPTR; + if (alloc_size != 0) { + void* data = recomp::alloc(rdram, alloc_size); + alloc_count++; + data_ptr = reinterpret_cast(data) - rdram + 0xFFFFFFFF80000000U; + } + + // Add the actor's fields to the actor data slotmap. + u32 spawn_index = actor_spawn_count++; + dod::slot_map_key32 key = actor_data.emplace(ExtensionData{.actor_spawn_index = spawn_index, .data_addr = data_ptr}); + + // printf("Allocated actor data: address 0x%08X with 0x%08X bytes total (0x%08X bytes generic and 0x%08X bytes specific), handle 0x%08X, spawn index %d\n", + // data_ptr, alloc_size, generic_data_size, type_data_size, key.raw, spawn_index); + + _return(ctx, key.raw); +} + +extern "C" void recomp_destroy_actor_data(uint8_t* rdram, recomp_context* ctx) { + std::lock_guard lock{ actor_data_mutex }; + + u32 actor_handle = _arg<0, u32>(rdram, ctx); + actor_data_map_t::key actor_key{actor_handle}; + + ExtensionData* data = actor_data.get(actor_key); + if (data != nullptr) { + // printf("Freeing actor data: address 0x%08X handle 0x%08X\n", data->data_addr, actor_handle); + if (data->data_addr != NULLPTR) { + recomp::free(rdram, TO_PTR(void, data->data_addr)); + free_count++; + } + actor_data.erase(actor_data_map_t::key{actor_handle}); + } + else { + // Not an irrecoverable error, but catch it in debug mode with an assert to help find bugs. + assert(false); + } +} + +extern "C" void recomp_get_actor_data(uint8_t* rdram, recomp_context* ctx) { + std::lock_guard lock{ actor_data_mutex }; + + u32 actor_handle = _arg<0, u32>(rdram, ctx); + u32 extension_handle = _arg<1, u32>(rdram, ctx); + u32 actor_type = _arg<2, u32>(rdram, ctx); + + // Check if the extension handle is valid. + if (extension_handle == 0 || extension_handle >= actor_extensions.size()) { + _return(ctx, NULLPTR); + return; + } + + ExtensionInfo& extension = actor_extensions[extension_handle]; + bool generic_extension = extension.actor_type == 0xFFFFFFFFU; + + // Check if the extension is generic or for the provided actor type. + if (!generic_extension && extension.actor_type != actor_type) { + _return(ctx, NULLPTR); + return; + } + + actor_data_map_t::key actor_key{actor_handle}; + ExtensionData* data = actor_data.get(actor_key); + + // Check if actor handle is valid. + if (data == nullptr) { + _return(ctx, NULLPTR); + return; + } + + // Calculate the address for this specific extension's data. + PTR(void) base_address = data->data_addr; + u32 offset = extension.data_offset; + // Specific actor data is after generic actor data, so increase the offset by the total generic actor data if this isn't generic data. + if (!generic_extension) { + offset += generic_data_size; + } + + PTR(void) ret = base_address + offset; + _return(ctx, ret); +} + +extern "C" void recomp_get_actor_spawn_index(uint8_t* rdram, recomp_context* ctx) { + std::lock_guard lock{ actor_data_mutex }; + + u32 actor_handle = _arg<0, u32>(rdram, ctx); + + actor_data_map_t::key actor_key{actor_handle}; + ExtensionData* data = actor_data.get(actor_key); + + // Check if actor handle is valid. + if (data == nullptr) { + _return(ctx, 0xFFFFFFFFU); + return; + } + + _return(ctx, data->actor_spawn_index); +} + diff --git a/src/main/main.cpp b/src/main/main.cpp index b57f00a..b85c949 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -26,6 +26,7 @@ #include "zelda_sound.h" #include "zelda_render.h" #include "zelda_game.h" +#include "recomp_data.h" #include "ovl_patches.hpp" #include "librecomp/game.hpp" #include "librecomp/mods.hpp" @@ -624,6 +625,7 @@ int main(int argc, char** argv) { zelda64::register_overlays(); zelda64::register_patches(); + recomp::init_extended_actor_data(); zelda64::load_config(); recomp::rsp::callbacks_t rsp_callbacks{ diff --git a/src/ui/elements/ui_types.h b/src/ui/elements/ui_types.h index 47de3bf..9442955 100644 --- a/src/ui/elements/ui_types.h +++ b/src/ui/elements/ui_types.h @@ -21,6 +21,7 @@ namespace recompui { Pointer }; + // These two enums must be kept in sync with patches/recompui_event_structs.h! enum class EventType { None, Click,