diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 0a53855..5699906 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 0a538553330ea5fdb1673708704bb92a854241b9 +Subproject commit 5699906f34fcc82905303092d081ad92aa74f926 diff --git a/patches.toml b/patches.toml index 6d928be..2206f23 100644 --- a/patches.toml +++ b/patches.toml @@ -14,3 +14,5 @@ data_reference_syms_files = [ "Zelda64RecompSyms/mm.us.rev1.datasyms.toml", "Zel output_binary_path = "patches/patches.bin" # Do not emit warnings for unpaired LO16 values, as clang produces many of them. unpaired_lo16_warnings = false +# Allow exporting functions and events for mods to use. +allow_exports = true diff --git a/patches/autosaving.c b/patches/autosaving.c index 46f1e8a..809c98d 100644 --- a/patches/autosaving.c +++ b/patches/autosaving.c @@ -67,10 +67,10 @@ RECOMP_PATCH void KaleidoSetup_Update(PlayState* play) { void Sram_SyncWriteToFlash(SramContext* sramCtx, s32 curPage, s32 numPages); -void autosave_reset_timer(); -void autosave_reset_timer_slow(); +void recomp_reset_autosave_timer(); +void recomp_reset_autosave_timer_slow(); -void do_autosave(PlayState* play) { +RECOMP_EXPORT void recomp_do_autosave(PlayState* play) { // Transfer the scene flags into the cycle flags. Play_SaveCycleSceneFlags(&play->state); // Transfer the cycle flags into the save buffer. Logic copied from func_8014546C. @@ -176,7 +176,7 @@ RECOMP_PATCH void func_8014546C(SramContext* sramCtx) { // @recomp Delete the owl save. delete_owl_save(sramCtx, gSaveContext.fileNum); // @recomp Reset the autosave timer. - autosave_reset_timer(); + recomp_reset_autosave_timer(); for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) { gSaveContext.save.saveInfo.permanentSceneFlags[i].chest = gSaveContext.cycleSceneFlags[i].chest; gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0 = gSaveContext.cycleSceneFlags[i].switch0; @@ -344,11 +344,11 @@ void draw_autosave_icon(PlayState* play) { CLOSE_DISPS(play->state.gfxCtx); } -void show_autosave_icon() { +RECOMP_EXPORT void recomp_show_autosave_icon() { autosave_icon_counter = AUTOSAVE_ICON_TOTAL_FRAMES; } -u32 recomp_autosave_interval() { +RECOMP_EXPORT u32 recomp_autosave_interval() { return 2 * 60 * 1000; } @@ -367,12 +367,12 @@ bool reached_final_three_hours() { return false; } -void autosave_reset_timer() { +RECOMP_EXPORT void recomp_reset_autosave_timer() { last_autosave_time = osGetTime(); extra_autosave_delay_milliseconds = 0; } -void autosave_reset_timer_slow() { +RECOMP_EXPORT void recomp_reset_autosave_timer_slow() { // Set the most recent autosave time in the future to give extra time before an autosave triggers. last_autosave_time = osGetTime(); extra_autosave_delay_milliseconds = 2 * 60 * 1000; @@ -425,20 +425,20 @@ void autosave_post_play_update(PlayState* play) { frames_since_autosave_ready >= MIN_FRAMES_SINCE_READY && time_now - last_autosave_time > (OS_USEC_TO_CYCLES(1000 * (recomp_autosave_interval() + extra_autosave_delay_milliseconds))) ) { - do_autosave(play); - show_autosave_icon(); - autosave_reset_timer(); + recomp_do_autosave(play); + recomp_show_autosave_icon(); + recomp_reset_autosave_timer(); } } else { // Update the last autosave time to the current time to prevent autosaving immediately if autosaves are turned back on. - autosave_reset_timer(); + recomp_reset_autosave_timer(); } gCanPause = false; } void autosave_init() { - autosave_reset_timer_slow(); + recomp_reset_autosave_timer_slow(); Lib_MemCpy(&prev_save_ctx, &gSaveContext, offsetof(SaveContext, fileNum)); } @@ -705,7 +705,7 @@ RECOMP_PATCH void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) { gSaveContext.jinxTimer = 0; // @recomp Use the slow autosave timer to give the player extra time to respond to the moon crashing to decide if they want to reload their autosave. - autosave_reset_timer_slow(); + recomp_reset_autosave_timer_slow(); } diff --git a/patches/patches.h b/patches/patches.h index f0f2b5e..a93a6fd 100644 --- a/patches/patches.h +++ b/patches/patches.h @@ -4,6 +4,11 @@ #define RECOMP_EXPORT __attribute__((section(".recomp_export"))) #define RECOMP_PATCH __attribute__((section(".recomp_patch"))) #define RECOMP_FORCE_PATCH __attribute__((section(".recomp_force_patch"))) +#define RECOMP_DECLARE_EVENT(func) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wunused-parameter\"") \ + __attribute__((noinline, weak, used, section(".recomp_event"))) void func {} \ + _Pragma("GCC diagnostic pop") // TODO fix renaming symbols in patch recompilation #define osCreateMesgQueue osCreateMesgQueue_recomp diff --git a/patches/patches.ld b/patches/patches.ld index b0a3b7f..fad9148 100644 --- a/patches/patches.ld +++ b/patches/patches.ld @@ -1,5 +1,5 @@ RAMBASE = 0x80801000; /* Used to hold any new symbols */ -EXTRA_RAM_SIZE = 0x01000000; /* Amount of extra ram allocated by recomp */ +PATCH_RAM_END = 0x81000000; /* Amount of extra ram allocated by recomp */ MEMORY { extram : ORIGIN = RAMBASE, LENGTH = 64M @@ -12,10 +12,11 @@ SECTIONS { .text : { *(.text*) } >extram AT >rom .recomp_patch : { *(.recomp_patch*) *(.recomp_force_patch*) } >extram AT >rom .recomp_export : { *(.recomp_export*) } >extram AT >rom + .recomp_event : { *(.recomp_event*) } >extram AT >rom .rodata : { *(.rodata*) } >extram AT >rom .data : { *(.data*) } >extram AT >rom .bss (NOLOAD) : { *(.bss*) *(COMMON) } >extram - ASSERT(. < RAMBASE + EXTRA_RAM_SIZE, "Maxed out recomp extra ram") + ASSERT(. <= PATCH_RAM_END, "Maxed out recomp extra ram") .reloc 0 : { *(.reloc*) } .symtab 0 : { *(.symtab) } diff --git a/patches/play_patches.c b/patches/play_patches.c index e3d7c84..eedda5d 100644 --- a/patches/play_patches.c +++ b/patches/play_patches.c @@ -4,6 +4,10 @@ extern Input D_801F6C18; +RECOMP_DECLARE_EVENT(recomp_on_play_main(PlayState* play)); +RECOMP_DECLARE_EVENT(recomp_before_play_update(PlayState* play)); +RECOMP_DECLARE_EVENT(recomp_after_play_update(PlayState* play)); + void controls_play_update(PlayState* play) { gSaveContext.options.zTargetSetting = recomp_get_targeting_mode(); } @@ -12,6 +16,7 @@ void controls_play_update(PlayState* play) { RECOMP_PATCH void Play_Main(GameState* thisx) { static Input* prevInput = NULL; PlayState* this = (PlayState*)thisx; + recomp_on_play_main(this); // @recomp debug_play_update(this); @@ -32,7 +37,9 @@ RECOMP_PATCH void Play_Main(GameState* thisx) { this->state.gfxCtx = NULL; } camera_pre_play_update(this); + recomp_before_play_update(this); Play_Update(this); + recomp_after_play_update(this); camera_post_play_update(this); analog_cam_post_play_update(this); autosave_post_play_update(this); diff --git a/patches/print.c b/patches/print.c index 0fec414..d5216b3 100644 --- a/patches/print.c +++ b/patches/print.c @@ -6,7 +6,7 @@ void* proutPrintf(void* dst, const char* fmt, size_t size) { return (void*)1; } -int recomp_printf(const char* fmt, ...) { +RECOMP_EXPORT int recomp_printf(const char* fmt, ...) { va_list args; va_start(args, fmt); diff --git a/patches/specific_actor_transform_tagging.c b/patches/specific_actor_transform_tagging.c index 9169303..78d2463 100644 --- a/patches/specific_actor_transform_tagging.c +++ b/patches/specific_actor_transform_tagging.c @@ -1266,7 +1266,7 @@ extern Gfx ovl_En_Tanron1_DL_001888[]; extern Gfx ovl_En_Tanron1_DL_001900[]; // @recomp Patched to interpolate the moths that circle torches. -void func_80BB5AAC(EnTanron1* this, PlayState* play) { +RECOMP_PATCH void func_80BB5AAC(EnTanron1* this, PlayState* play) { EnTanron1Struct* ptrBase = &this->unk_160[0]; s16 i; u8 flag = 0; diff --git a/src/main/main.cpp b/src/main/main.cpp index d350b6d..0fbf6f6 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -27,6 +27,7 @@ #include "zelda_render.h" #include "ovl_patches.hpp" #include "librecomp/game.hpp" +#include "librecomp/mods.hpp" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -327,6 +328,7 @@ std::vector supported_games = { .rom_hash = 0xEF18B4A9E2386169ULL, .internal_name = "ZELDA MAJORA'S MASK", .game_id = u8"mm.n64.us.1.0", + .mod_game_id = "mm", .is_enabled = true, .entrypoint_address = get_entrypoint_address(), .entrypoint = recomp_entrypoint, @@ -568,6 +570,8 @@ int main(int argc, char** argv) { fprintf(stderr, "Failed to load controller mappings: %s\n", SDL_GetError()); } + recomp::register_config_path(zelda64::get_app_folder_path()); + // Register supported games and patches for (const auto& game : supported_games) { recomp::register_game(game); @@ -575,8 +579,6 @@ int main(int argc, char** argv) { zelda64::register_overlays(); zelda64::register_patches(); - - recomp::register_config_path(zelda64::get_app_folder_path()); zelda64::load_config(); recomp::rsp::callbacks_t rsp_callbacks{ @@ -619,7 +621,10 @@ int main(int argc, char** argv) { .get_game_thread_name = zelda64::get_game_thread_name, }; + recomp::mods::scan_mods(); + recomp::start( + 64 * 1024 * 1024, // 64MB to have plenty of room for loading mods {}, rsp_callbacks, renderer_callbacks, diff --git a/src/main/register_patches.cpp b/src/main/register_patches.cpp index 12c5725..8725f81 100644 --- a/src/main/register_patches.cpp +++ b/src/main/register_patches.cpp @@ -7,4 +7,6 @@ void zelda64::register_patches() { recomp::overlays::register_patches(mm_patches_bin, sizeof(mm_patches_bin), section_table, ARRLEN(section_table)); + recomp::overlays::register_base_exports(export_table); + recomp::overlays::register_base_events(event_name_table); } diff --git a/src/ui/ui_renderer.cpp b/src/ui/ui_renderer.cpp index 8fb93d4..a234d7c 100644 --- a/src/ui/ui_renderer.cpp +++ b/src/ui/ui_renderer.cpp @@ -1287,6 +1287,12 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s static recompui::Menu prev_menu = recompui::Menu::None; recompui::Menu cur_menu = open_menu.load(); + // Return to the launcher if no menu is open and the game isn't started. + if (cur_menu == recompui::Menu::None && !ultramodern::is_game_started()) { + cur_menu = recompui::Menu::Launcher; + recompui::set_current_menu(cur_menu); + } + if (reload_sheets) { ui_context->rml.load_documents(); prev_menu = recompui::Menu::None; diff --git a/us.rev1.toml b/us.rev1.toml index 7e776f4..7257691 100644 --- a/us.rev1.toml +++ b/us.rev1.toml @@ -5,6 +5,7 @@ entrypoint = 0x80080000 # Paths are relative to the location of this config file. output_func_path = "RecompiledFuncs" relocatable_sections_path = "overlays.us.rev1.txt" +# elf_path = "mm.us.rev1.rom_uncompressed.elf" symbols_file_path = "Zelda64RecompSyms/mm.us.rev1.syms.toml" rom_file_path = "mm.us.rev1.rom_uncompressed.z64"