From 670bd610673432beead61b1b8d41c78f316afde1 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Fri, 5 Jul 2024 21:24:43 -0400 Subject: [PATCH 01/30] Updated RT64 and enabled RT64 extended RDRAM mode --- patches/ui_patches.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/patches/ui_patches.c b/patches/ui_patches.c index a057655..0177611 100644 --- a/patches/ui_patches.c +++ b/patches/ui_patches.c @@ -56,9 +56,10 @@ RECOMP_PATCH void Graph_SetNextGfxPool(GraphicsContext* gfxCtx) { gSPEndDisplayList(&gGfxMasterDL->disps[4]); gSPBranchList(&gGfxMasterDL->debugDisp[0], bigger_pool->debugBuffer); - // @recomp Enable RT64 extended GBI mode and set the current framerate + // @recomp Enable RT64 extended GBI mode and extended rdram. OPEN_DISPS(gfxCtx); gEXEnable(POLY_OPA_DISP++); + gEXSetRDRAMExtended(POLY_OPA_DISP++, 1); CLOSE_DISPS(gfxCtx); } From cf1943fe6af3655eff0e9c80170c4a311dfb8b1e Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sun, 11 Aug 2024 12:51:26 -0400 Subject: [PATCH 02/30] Removed shader cache --- CMakeLists.txt | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 62bce6f..d4ef3ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,16 +138,6 @@ add_custom_target(DownloadGameControllerDB add_executable(Zelda64Recompiled) add_dependencies(Zelda64Recompiled DownloadGameControllerDB) -# Generate mm_shader_cache.c from the MM shader cache if it exists -if (EXISTS ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin) - set(HAS_MM_SHADER_CACHE TRUE) - target_compile_definitions(Zelda64Recompiled PRIVATE HAS_MM_SHADER_CACHE) - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.h - COMMAND file_to_c ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin mm_shader_cache_bytes ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.h - DEPENDS ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin - ) -endif() - set (SOURCES ${CMAKE_SOURCE_DIR}/src/main/main.cpp ${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp @@ -174,10 +164,6 @@ set (SOURCES ${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends/RmlUi_Platform_SDL.cpp ) -if (HAS_MM_SHADER_CACHE) - list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c) -endif() - target_include_directories(Zelda64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/lib/concurrentqueue From ba39a73dca67898b2afb32f2ceb2c9d53b74e513 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 2 Sep 2024 12:01:22 -0400 Subject: [PATCH 03/30] Update modern runtime for mod support, add some exports and events in the patches --- lib/N64ModernRuntime | 2 +- patches.toml | 2 ++ patches/autosaving.c | 28 ++++++++++++++-------------- patches/patches.h | 5 +++++ patches/patches.ld | 5 +++-- patches/play_patches.c | 7 +++++++ patches/print.c | 2 +- src/main/main.cpp | 9 +++++++-- src/main/register_patches.cpp | 2 ++ src/ui/ui_renderer.cpp | 6 ++++++ us.rev1.toml | 1 + 11 files changed, 49 insertions(+), 20 deletions(-) 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/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" From 2c23d5f29b78c6ed3713dceef87ceb71f4f3bcf5 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 2 Sep 2024 12:04:14 -0400 Subject: [PATCH 04/30] Update N64Recomp commit for CI --- .github/workflows/validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 4440cc2..442d031 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -9,7 +9,7 @@ on: N64RECOMP_COMMIT: type: string required: false - default: '5b17bf8bb556d2544c6161487232a455eae8f188' + default: 'd33d38161798167929b114c2b0fd445f9670e10a' DXC_CHECKSUM: type: string required: false From be70a2a8f175e185c22403938c5e4ceb02e98916 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 2 Sep 2024 20:00:47 -0400 Subject: [PATCH 05/30] Parse version number from a string and provide it to the runtime --- assets/launcher.rml | 2 +- lib/N64ModernRuntime | 2 +- src/main/main.cpp | 9 +++++++++ src/ui/ui_launcher.cpp | 6 ++++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/assets/launcher.rml b/assets/launcher.rml index d4a77d1..6496117 100644 --- a/assets/launcher.rml +++ b/assets/launcher.rml @@ -58,7 +58,7 @@
- +
diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 5699906..09f5759 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 5699906f34fcc82905303092d081ad92aa74f926 +Subproject commit 09f5759d3a68437d407d54922e3fddf0c52f4dad diff --git a/src/main/main.cpp b/src/main/main.cpp index 0fbf6f6..cc34fa8 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -37,6 +37,8 @@ #include "../../lib/rt64/src/contrib/stb/stb_image.h" +const std::string version_string = "1.2.0-dev"; + template void exit_error(const char* str, Ts ...args) { // TODO pop up an error @@ -527,6 +529,12 @@ void release_preload(PreloadContext& context) { #endif int main(int argc, char** argv) { + recomp::Version project_version{}; + if (!recomp::Version::from_string(version_string, project_version)) { + ultramodern::error_handling::message_box(("Invalid version string: " + version_string).c_str()); + return EXIT_FAILURE; + } + // Map this executable into memory and lock it, which should keep it in physical memory. This ensures // that there are no stutters from the OS having to load new pages of the executable whenever a new code page is run. PreloadContext preload_context; @@ -625,6 +633,7 @@ int main(int argc, char** argv) { recomp::start( 64 * 1024 * 1024, // 64MB to have plenty of room for loading mods + project_version, {}, rsp_callbacks, renderer_callbacks, diff --git a/src/ui/ui_launcher.cpp b/src/ui/ui_launcher.cpp index b4b6312..f699eb7 100644 --- a/src/ui/ui_launcher.cpp +++ b/src/ui/ui_launcher.cpp @@ -6,7 +6,7 @@ #include "nfd.h" #include -std::string version_number = "v1.1.1"; +static std::string version_string; Rml::DataModelHandle model_handle; bool mm_rom_valid = false; @@ -103,7 +103,9 @@ public: Rml::DataModelConstructor constructor = context->CreateDataModel("launcher_model"); constructor.Bind("mm_rom_valid", &mm_rom_valid); - constructor.Bind("version_number", &version_number); + + version_string = recomp::get_project_version().to_string(); + constructor.Bind("version_number", &version_string); model_handle = constructor.GetModelHandle(); } From e6892248c73849024c75782baf457698b5927629 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 2 Sep 2024 23:46:38 -0400 Subject: [PATCH 06/30] Update runtime for new mod manifest schema --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 09f5759..3718758 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 09f5759d3a68437d407d54922e3fddf0c52f4dad +Subproject commit 3718758cd529c779018a880ab645cec9d9888803 From ea0cc6e6be76e40d189e9e39ec077dae8efdf9f4 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Tue, 3 Sep 2024 00:04:17 -0400 Subject: [PATCH 07/30] Update runtime to have mod list functionality --- lib/N64ModernRuntime | 2 +- src/main/main.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 3718758..986881e 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 3718758cd529c779018a880ab645cec9d9888803 +Subproject commit 986881e4e1b19d39ad0efd5a301be7ed439434b1 diff --git a/src/main/main.cpp b/src/main/main.cpp index cc34fa8..579071a 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -631,6 +631,12 @@ int main(int argc, char** argv) { recomp::mods::scan_mods(); + printf("Found mods:\n"); + for (const auto& mod : recomp::mods::get_mod_details("mm")) { + printf(" %s(%s)\n", mod.mod_id.c_str(), mod.version.to_string().c_str()); + } + printf("\n"); + recomp::start( 64 * 1024 * 1024, // 64MB to have plenty of room for loading mods project_version, From 60b8d58e734a47d2bcf94b792aef78881e7493c4 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Wed, 4 Sep 2024 02:29:01 -0400 Subject: [PATCH 08/30] Update runtime and print mod authors and dependencies --- lib/N64ModernRuntime | 2 +- src/main/main.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 986881e..b9592c6 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 986881e4e1b19d39ad0efd5a301be7ed439434b1 +Subproject commit b9592c625d87ab45ddd12d9ad30c886ee77a5d68 diff --git a/src/main/main.cpp b/src/main/main.cpp index 579071a..910f545 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -634,6 +634,22 @@ int main(int argc, char** argv) { printf("Found mods:\n"); for (const auto& mod : recomp::mods::get_mod_details("mm")) { printf(" %s(%s)\n", mod.mod_id.c_str(), mod.version.to_string().c_str()); + if (!mod.authors.empty()) { + printf(" Authors: %s", mod.authors[0].c_str()); + for (size_t author_index = 1; author_index < mod.authors.size(); author_index++) { + const std::string& author = mod.authors[author_index]; + printf(", %s", author.c_str()); + } + printf("\n"); + } + if (!mod.dependencies.empty()) { + printf(" Dependencies: %s:%s", mod.dependencies[0].mod_id.c_str(), mod.dependencies[0].version.to_string().c_str()); + for (size_t dep_index = 1; dep_index < mod.dependencies.size(); dep_index++) { + const recomp::mods::Dependency& dep = mod.dependencies[dep_index]; + printf(", %s:%s", dep.mod_id.c_str(), dep.version.to_string().c_str()); + } + printf("\n"); + } } printf("\n"); From 359f8a9279a7b429e1314d1e6be872277cc1f431 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sun, 8 Sep 2024 22:45:37 -0400 Subject: [PATCH 09/30] Update runtime for mod loading on posix systems, enable all mods temporarily --- include/recomp_input.h | 5 +++-- lib/N64ModernRuntime | 2 +- src/main/main.cpp | 2 ++ src/main/register_patches.cpp | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/recomp_input.h b/include/recomp_input.h index db9b160..8599628 100644 --- a/include/recomp_input.h +++ b/include/recomp_input.h @@ -120,7 +120,8 @@ namespace recomp { std::vector apply_menu; }; - constexpr const std::vector& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) { + inline const std::vector& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) { + static const std::vector empty_input_field{}; switch (input) { case GameInput::A: return defaults.a; case GameInput::B: return defaults.b; @@ -143,7 +144,7 @@ namespace recomp { case GameInput::TOGGLE_MENU: return defaults.toggle_menu; case GameInput::ACCEPT_MENU: return defaults.accept_menu; case GameInput::APPLY_MENU: return defaults.apply_menu; - default: return std::vector(); + default: return empty_input_field; } } diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index b9592c6..a7e5a77 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit b9592c625d87ab45ddd12d9ad30c886ee77a5d68 +Subproject commit a7e5a7770f2344aa504725af6465e2db4565292c diff --git a/src/main/main.cpp b/src/main/main.cpp index 910f545..6007ffa 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -650,6 +650,8 @@ int main(int argc, char** argv) { } printf("\n"); } + // TODO load all mods as a temporary solution to not having a UI yet. + recomp::mods::enable_mod(mod.mod_id, true); } printf("\n"); diff --git a/src/main/register_patches.cpp b/src/main/register_patches.cpp index 8725f81..ef800fd 100644 --- a/src/main/register_patches.cpp +++ b/src/main/register_patches.cpp @@ -8,5 +8,5 @@ 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); + recomp::overlays::register_base_events(event_names); } From 2463fe4613a855d899d672fb992e33327cf5efc1 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 9 Sep 2024 02:02:47 -0400 Subject: [PATCH 10/30] Update N64ModernRuntime for mod mips32 relocs and function lookups --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index a7e5a77..26a0543 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit a7e5a7770f2344aa504725af6465e2db4565292c +Subproject commit 26a05439c2215ea9e1c0b0d572b46b3584a90a4f From 765d1d32f0acd9b920ca99b210df2c38f6460416 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 9 Sep 2024 23:23:57 -0400 Subject: [PATCH 11/30] Update runtime after merge --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 26a0543..45e9f7a 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 26a05439c2215ea9e1c0b0d572b46b3584a90a4f +Subproject commit 45e9f7a6cb46b743cb4ada142be3b398445a8b20 From eda1979bf0883f9da148f39df4999c330c2e0dfc Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Thu, 12 Sep 2024 00:10:29 -0400 Subject: [PATCH 12/30] Update runtime to fix reference symbols --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 45e9f7a..81ce18d 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 45e9f7a6cb46b743cb4ada142be3b398445a8b20 +Subproject commit 81ce18d0d3448308d29ca1cc1a835344571126c9 From 33806821e287d898f2e48423b990728c139fac6d Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Thu, 12 Sep 2024 00:55:35 -0400 Subject: [PATCH 13/30] Update runtime for populating missing runtime pointers in offline compiled mods --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 81ce18d..c4ee705 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 81ce18d0d3448308d29ca1cc1a835344571126c9 +Subproject commit c4ee705250e53af3d84678bcccafeb50e4655e06 From a568261064f1490440f2c40372f70da4c71ab4b8 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Thu, 12 Sep 2024 19:24:04 -0400 Subject: [PATCH 14/30] Update runtime after merge --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index c4ee705..356b9f9 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit c4ee705250e53af3d84678bcccafeb50e4655e06 +Subproject commit 356b9f901e65dcde3b7368116f5a7e84087b07ef From 5dc260f35aef040253bd20f9d1cdece2630e5c34 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Fri, 27 Sep 2024 00:57:49 -0400 Subject: [PATCH 15/30] Update runtime to add DLL search path, enable strict mode in patch recompilation --- lib/N64ModernRuntime | 2 +- patches.toml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 356b9f9..46797d6 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 356b9f901e65dcde3b7368116f5a7e84087b07ef +Subproject commit 46797d6cadca6ff78377e7065d073a176d988025 diff --git a/patches.toml b/patches.toml index 2206f23..38910ee 100644 --- a/patches.toml +++ b/patches.toml @@ -16,3 +16,5 @@ output_binary_path = "patches/patches.bin" unpaired_lo16_warnings = false # Allow exporting functions and events for mods to use. allow_exports = true +# # Enable strict patch mode, validates that patched symbols exist and that non-patch functions aren't symbols. +strict_patch_mode = true From 5a8a1cb2ba6936a10a3d4a78df1e8e6f0919740a Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 28 Sep 2024 16:50:58 -0400 Subject: [PATCH 16/30] Update runtime for custom mod content types and add RT64 texture pack support --- include/zelda_render.h | 8 +++++ lib/N64ModernRuntime | 2 +- src/game/config.cpp | 1 - src/main/main.cpp | 23 ++++++++++++++ src/main/rt64_render_context.cpp | 54 ++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/include/zelda_render.h b/include/zelda_render.h index 684672b..2d909e4 100644 --- a/include/zelda_render.h +++ b/include/zelda_render.h @@ -1,8 +1,12 @@ #ifndef __ZELDA_RENDER_H__ #define __ZELDA_RENDER_H__ +#include +#include + #include "common/rt64_user_configuration.h" #include "ultramodern/renderer_context.hpp" +#include "librecomp/mods.hpp" namespace RT64 { struct Application; @@ -29,6 +33,7 @@ namespace zelda64 { protected: std::unique_ptr app; + std::unordered_set enabled_texture_packs; }; std::unique_ptr create_render_context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode); @@ -36,6 +41,9 @@ namespace zelda64 { RT64::UserConfiguration::Antialiasing RT64MaxMSAA(); bool RT64SamplePositionsSupported(); bool RT64HighPrecisionFBEnabled(); + + void enable_texture_pack(const recomp::mods::ModHandle& mod); + void disable_texture_pack(const recomp::mods::ModHandle& mod); } } diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 46797d6..a4f5ee1 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 46797d6cadca6ff78377e7065d073a176d988025 +Subproject commit a4f5ee10c8303474d9028468e84845b192e9297d diff --git a/src/game/config.cpp b/src/game/config.cpp index ac8ebff..ab24e0a 100644 --- a/src/game/config.cpp +++ b/src/game/config.cpp @@ -19,7 +19,6 @@ constexpr std::u8string_view general_filename = u8"general.json"; constexpr std::u8string_view graphics_filename = u8"graphics.json"; constexpr std::u8string_view controls_filename = u8"controls.json"; constexpr std::u8string_view sound_filename = u8"sound.json"; -constexpr std::u8string_view program_id = u8"Zelda64Recompiled"; constexpr auto res_default = ultramodern::renderer::Resolution::Auto; constexpr auto hr_default = ultramodern::renderer::HUDRatioMode::Clamp16x9; diff --git a/src/main/main.cpp b/src/main/main.cpp index 6007ffa..cbeb63a 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -528,6 +528,16 @@ void release_preload(PreloadContext& context) { #endif +void enable_texture_pack(recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod) { + (void)context; + zelda64::renderer::enable_texture_pack(mod); +} + +void disable_texture_pack(recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod) { + (void)context; + zelda64::renderer::disable_texture_pack(mod); +} + int main(int argc, char** argv) { recomp::Version project_version{}; if (!recomp::Version::from_string(version_string, project_version)) { @@ -629,6 +639,18 @@ int main(int argc, char** argv) { .get_game_thread_name = zelda64::get_game_thread_name, }; + // Register the texture pack content type with rt64.json as its content file. + recomp::mods::ModContentType texture_pack_content_type{ + .content_filename = "rt64.json", + .allow_runtime_toggle = true, + .on_enabled = enable_texture_pack, + .on_disabled = disable_texture_pack, + }; + auto texture_pack_content_type_id = recomp::mods::register_mod_content_type(texture_pack_content_type); + + // Register the .rtz texture pack file format with the previous content type as its only allowed content type. + recomp::mods::register_mod_container_type("rtz", std::vector{ texture_pack_content_type_id }, false); + recomp::mods::scan_mods(); printf("Found mods:\n"); @@ -641,6 +663,7 @@ int main(int argc, char** argv) { printf(", %s", author.c_str()); } printf("\n"); + printf(" Runtime toggleable: %d\n", mod.runtime_toggleable); } if (!mod.dependencies.empty()) { printf(" Dependencies: %s:%s", mod.dependencies[0].mod_id.c_str(), mod.dependencies[0].version.to_string().c_str()); diff --git a/src/main/rt64_render_context.cpp b/src/main/rt64_render_context.cpp index 497e504..8d5c735 100644 --- a/src/main/rt64_render_context.cpp +++ b/src/main/rt64_render_context.cpp @@ -1,5 +1,6 @@ #include #include +#include #define HLSL_CPU #include "hle/rt64_application.h" @@ -10,6 +11,13 @@ #include "zelda_render.h" #include "recomp_ui.h" +#include "concurrentqueue.h" + +// Helper class for variant visiting. +template +struct overloaded : Ts... { using Ts::operator()...; }; +template +overloaded(Ts...) -> overloaded; static RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None; static bool sample_positions_supported = false; @@ -18,6 +26,18 @@ static bool high_precision_fb_enabled = false; static uint8_t DMEM[0x1000]; static uint8_t IMEM[0x1000]; +struct TexturePackEnableAction { + std::filesystem::path path; +}; + +struct TexturePackDisableAction { + std::filesystem::path path; +}; + +using TexturePackAction = std::variant; + +static moodycamel::ConcurrentQueue texture_pack_action_queue; + unsigned int MI_INTR_REG = 0; unsigned int DPC_START_REG = 0; @@ -286,6 +306,32 @@ zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::rendere zelda64::renderer::RT64Context::~RT64Context() = default; void zelda64::renderer::RT64Context::send_dl(const OSTask* task) { + bool packs_disabled = false; + TexturePackAction cur_action; + while (texture_pack_action_queue.try_dequeue(cur_action)) { + std::visit(overloaded{ + [&](TexturePackDisableAction& to_disable) { + enabled_texture_packs.erase(to_disable.path); + packs_disabled = true; + }, + [&](TexturePackEnableAction& to_enable) { + enabled_texture_packs.insert(to_enable.path); + // Load the pack now if no packs have been disabled. + if (!packs_disabled) { + app->textureCache->loadReplacementDirectory(to_enable.path); + } + } + }, cur_action); + } + + // If any packs were disabled, unload all packs and load all the active ones. + if (packs_disabled) { + app->textureCache->clearReplacementDirectories(); + for (const std::filesystem::path& cur_pack_path : enabled_texture_packs) { + app->textureCache->loadReplacementDirectory(cur_pack_path); + } + } + app->state->rsp->reset(); app->interpreter->loadUCodeGBI(task->t.ucode & 0x3FFFFFF, task->t.ucode_data & 0x3FFFFFF, true); app->processDisplayLists(app->core.RDRAM, task->t.data_ptr & 0x3FFFFFF, 0, true); @@ -376,3 +422,11 @@ bool zelda64::renderer::RT64SamplePositionsSupported() { bool zelda64::renderer::RT64HighPrecisionFBEnabled() { return high_precision_fb_enabled; } + +void zelda64::renderer::enable_texture_pack(const recomp::mods::ModHandle& mod) { + texture_pack_action_queue.enqueue(TexturePackEnableAction{mod.manifest.mod_root_path}); +} + +void zelda64::renderer::disable_texture_pack(const recomp::mods::ModHandle& mod) { + texture_pack_action_queue.enqueue(TexturePackDisableAction{mod.manifest.mod_root_path}); +} From 24c436a572be8934d44e66362a977783302b5f04 Mon Sep 17 00:00:00 2001 From: LittleCube Date: Fri, 4 Oct 2024 00:01:23 -0400 Subject: [PATCH 17/30] add exports and events for moon crash save and owl save (#487) * add exports and events for moon crash save and owl save * fix recomp_on_owl_save event, add exports and callbacks for save loading * add more flexible owl events * add init event * fix init event to not be terrible * rename a couple events * use deletes instead of resets * use better names, add better annotations * use full signature for event annotations --- patches/autosaving.c | 104 ++++++++++++++++++++++++++++++------- patches/play_patches.c | 12 ++++- patches/required_patches.c | 5 ++ 3 files changed, 100 insertions(+), 21 deletions(-) diff --git a/patches/autosaving.c b/patches/autosaving.c index 809c98d..bcf5c3c 100644 --- a/patches/autosaving.c +++ b/patches/autosaving.c @@ -99,13 +99,22 @@ RECOMP_EXPORT void recomp_do_autosave(PlayState* play) { gSaveContext.save.isOwlSave = false; } -// @recomp Do not clear the save if the save was an autosave. +bool loading_deletes_owl_save = true; + +// @recomp_export void recomp_set_loading_deletes_owl_save(bool new_val): Set whether loading an owl save should also delete it. +RECOMP_EXPORT void recomp_set_loading_deletes_owl_save(bool new_val) +{ + loading_deletes_owl_save = new_val; +} + +// @recomp Do not clear the save if the save was an autosave, or if mods have disabled save deletion. RECOMP_PATCH void func_80147314(SramContext* sramCtx, s32 fileNum) { s32 save_type = gSaveContext.save.isOwlSave; gSaveContext.save.isOwlSave = false; - // @recomp Prevent owl save/autosave deletion if autosaving is enabled. - if (!recomp_autosave_enabled()) { + // @recomp Prevent owl save/autosave deletion if autosaving is enabled, and... + // @recomp_use_export_var loading_deletes_owl_save: Prevent owl save deletion if mods disable it. + if (!recomp_autosave_enabled() && loading_deletes_owl_save) { gSaveContext.save.saveInfo.playerData.newf[0] = '\0'; gSaveContext.save.saveInfo.playerData.newf[1] = '\0'; gSaveContext.save.saveInfo.playerData.newf[2] = '\0'; @@ -522,6 +531,9 @@ s32 spawn_entrance_from_autosave_entrance(s16 autosave_entrance) { } } +RECOMP_DECLARE_EVENT(recomp_on_load_save(FileSelectState* fileSelect, SramContext* sramCtx)); +RECOMP_DECLARE_EVENT(recomp_after_load_save(FileSelectState* fileSelect, SramContext* sramCtx)); + // @recomp Patched to change the entrance for autosaves and initialize autosaves. RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) { s32 i; @@ -530,6 +542,9 @@ RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCt s32 pad1; s32 fileNum; + // @recomp_event recomp_on_load_save(FileSelectState* fileSelect, SramContext* sramCtx): A save-file was just chosen. + recomp_on_load_save(fileSelect, sramCtx); + if (gSaveContext.flashSaveAvailable) { bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); @@ -653,29 +668,49 @@ RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCt // @recomp Initialize the autosave state tracking. autosave_init(); + + // @recomp_event recomp_after_load_save(FileSelectState* fileSelect, SramContext* sramCtx): The save has finished loading. + recomp_after_load_save(fileSelect, sramCtx); +} + +bool moon_crash_resets_save = true; + +// @recomp_export void recomp_set_moon_crash_resets_save(bool new_val): Set whether a moon crash should revert the player's save data. +RECOMP_EXPORT void recomp_set_moon_crash_resets_save(bool new_val) +{ + moon_crash_resets_save = new_val; } extern s32 Actor_ProcessTalkRequest(Actor* actor, GameState* gameState); +RECOMP_DECLARE_EVENT(recomp_on_moon_crash(SramContext* sramCtx)); +RECOMP_DECLARE_EVENT(recomp_after_moon_crash(SramContext* sramCtx)); + // @recomp Reset the autosave timer when the moon crashes. RECOMP_PATCH void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) { s32 i; s32 cutsceneIndex = gSaveContext.save.cutsceneIndex; - bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + // @recomp_event recomp_on_moon_crash(SramContext* sramCtx): A moon crash has just been triggered. + recomp_on_moon_crash(sramCtx); - if (SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2], - gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) { - SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1], - gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]); + if (moon_crash_resets_save) + { + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + + if (SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2], + gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) { + SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1], + gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]); + } + Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save)); + if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf)) { + SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1], + gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]); + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, sizeof(Save)); + } + gSaveContext.save.cutsceneIndex = cutsceneIndex; } - Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save)); - if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf)) { - SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1], - gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]); - Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, sizeof(Save)); - } - gSaveContext.save.cutsceneIndex = cutsceneIndex; for (i = 0; i < ARRAY_COUNT(gSaveContext.eventInf); i++) { gSaveContext.eventInf[i] = 0; @@ -706,24 +741,55 @@ RECOMP_PATCH void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) { // @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. recomp_reset_autosave_timer_slow(); + + // @recomp_event recomp_after_moon_crash(SramContext* sramCtx): The effects of moon crash have been written. + recomp_after_moon_crash(sramCtx); } -// @recomp If autosave is enabled, skip the part of the owl statue dialog that talks about the file being deleted on load, since it's not true. +bool owls_save_and_quit = true; + +// @recomp_export void recomp_set_owls_save_and_quit(bool new_val): Set if owls should use their code to save and quit. If false is passed, owl saves now do nothing. +RECOMP_EXPORT void recomp_set_owls_save_and_quit(bool new_val) +{ + owls_save_and_quit = new_val; +} + +RECOMP_DECLARE_EVENT(recomp_on_owl_update(ObjWarpstone* this, PlayState* play)); +RECOMP_DECLARE_EVENT(recomp_on_owl_save(ObjWarpstone* this, PlayState* play)); +RECOMP_DECLARE_EVENT(recomp_after_owl_save(ObjWarpstone* this, PlayState* play)); + +// @recomp If autosave is enabled or owl save deletion is disabled, skip the part of the owl statue dialog that talks about the file being deleted on load, since it's not true. RECOMP_PATCH void ObjWarpstone_Update(Actor* thisx, PlayState* play) { ObjWarpstone* this = (ObjWarpstone*)thisx; s32 pad; + // @recomp_event recomp_on_owl_update(ObjWarpstone* this, PlayState* play): Allow mods to handle owl update frames. + recomp_on_owl_update(this, play); + if (this->isTalking) { if (Actor_TextboxIsClosing(&this->dyna.actor, play)) { this->isTalking = false; } else if ((Message_GetState(&play->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) { if (play->msgCtx.choiceIndex != 0) { + // @recomp_event recomp_on_owl_save(ObjWarpstone* this, PlayState* play): The player chose to save from an owl statue. + recomp_on_owl_save(this, play); + Audio_PlaySfx_MessageDecide(); - play->msgCtx.msgMode = MSGMODE_OWL_SAVE_0; + + // @recomp_use_export_var owls_save_and_quit: Only use normal owl save if quit flag is set. + if (owls_save_and_quit) { + play->msgCtx.msgMode = MSGMODE_OWL_SAVE_0; + } else { + Message_CloseTextbox(play); + } + play->msgCtx.unk120D6 = 0; play->msgCtx.unk120D4 = 0; gSaveContext.save.owlWarpId = OBJ_WARPSTONE_GET_OWL_WARP_ID(&this->dyna.actor); + + // @recomp_event recomp_after_owl_save(ObjWarpstone* this, PlayState* play): Owl save is finished. + recomp_after_owl_save(this, play); } else { Message_CloseTextbox(play); } @@ -734,8 +800,8 @@ RECOMP_PATCH void ObjWarpstone_Update(Actor* thisx, PlayState* play) { Actor_OfferTalkNearColChkInfoCylinder(&this->dyna.actor, play); } - // @recomp Skip the text talking about the save being deleted on load, if autosave is enabled. - if (recomp_autosave_enabled()) { + // @recomp_use_export_var loading_deletes_owl_save: Skip the text talking about the save being deleted on load, if autosave is enabled or if owl save deletion is disabled. + if (recomp_autosave_enabled() || !loading_deletes_owl_save) { if (this->isTalking && play->msgCtx.currentTextId == 0xC01 && play->msgCtx.msgBufPos == 269) { play->msgCtx.msgBufPos = 530; } diff --git a/patches/play_patches.c b/patches/play_patches.c index eedda5d..db5deeb 100644 --- a/patches/play_patches.c +++ b/patches/play_patches.c @@ -5,7 +5,7 @@ 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_on_play_update(PlayState* play)); RECOMP_DECLARE_EVENT(recomp_after_play_update(PlayState* play)); void controls_play_update(PlayState* play) { @@ -16,6 +16,8 @@ void controls_play_update(PlayState* play) { RECOMP_PATCH void Play_Main(GameState* thisx) { static Input* prevInput = NULL; PlayState* this = (PlayState*)thisx; + + // @recomp_event recomp_on_play_main(PlayState* play): Allow mods to execute code every frame. recomp_on_play_main(this); // @recomp @@ -37,9 +39,15 @@ RECOMP_PATCH void Play_Main(GameState* thisx) { this->state.gfxCtx = NULL; } camera_pre_play_update(this); - recomp_before_play_update(this); + + // @recomp_event recomp_on_play_update(PlayState* play): Play_Update is about to be called. + recomp_on_play_update(this); + Play_Update(this); + + // @recomp_event recomp_after_play_update(PlayState* play): Play_Update was called. 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/required_patches.c b/patches/required_patches.c index 721a41a..7aada58 100644 --- a/patches/required_patches.c +++ b/patches/required_patches.c @@ -7,6 +7,8 @@ void Main_InitMemory(void); void Main_InitScreen(void); +RECOMP_DECLARE_EVENT(recomp_on_init()); + // @recomp Patched to load the code segment in the recomp runtime. RECOMP_PATCH void Main_Init(void) { DmaRequest dmaReq; @@ -14,6 +16,9 @@ RECOMP_PATCH void Main_Init(void) { OSMesg msg[1]; size_t prevSize; + // @recomp_event recomp_on_init(): Allow mods to initialize themselves once. + recomp_on_init(); + osCreateMesgQueue(&mq, msg, ARRAY_COUNT(msg)); prevSize = gDmaMgrDmaBuffSize; From cd12bc0c2053c1235bd0aa9fdf84ac67e4f6e9cc Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 7 Oct 2024 01:35:56 -0400 Subject: [PATCH 18/30] Disable identical code folding to prevent mods from conflicting with themselves due to merged functions --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d4ef3ef..0b900bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,6 +196,11 @@ else() ) endif() +if (MSVC) + # Disable identical code folding, since this breaks mod function patching as multiple functions can get merged into one. + target_link_options(Zelda64Recompiled PRIVATE /OPT:NOICF) +endif() + if (WIN32) include(FetchContent) From aeefc1b6bb4470df88ee4e8f160b8af582b9545b Mon Sep 17 00:00:00 2001 From: David Chavez Date: Tue, 8 Oct 2024 05:27:13 +0200 Subject: [PATCH 19/30] chore(ci): Bundle Windows PDBs (#492) * update runners * Save PDB * Build RelWithDebInfo * Update copy on runners * Archive PDB separately * Update copy * Update PDB archive name --- .github/workflows/validate.yml | 13 ++++++++++--- README.md | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 442d031..b9f7c96 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -22,7 +22,7 @@ concurrency: cancel-in-progress: true jobs: build-linux: - runs-on: ${{ matrix.os }} + runs-on: ${{ format('blaze/compute/{0}-amd64', matrix.os) }} container: image: dcvz/n64recomp:ubuntu-18.04 volumes: @@ -108,7 +108,7 @@ jobs: name: Zelda64Recompiled-AppImage-X64-${{ matrix.type }} path: Zelda64Recompiled-*.AppImage build-linux-arm64: - runs-on: ${{ format('blaze/{0}', matrix.os) }} + runs-on: ${{ format('blaze/compute/{0}', matrix.os) }} strategy: matrix: type: [ Debug, Release ] @@ -199,7 +199,7 @@ jobs: runs-on: windows-latest strategy: matrix: - type: [ Debug, Release ] + type: [ Debug, RelWithDebInfo ] name: windows (${{ matrix.type }}) steps: - name: Checkout @@ -264,6 +264,7 @@ jobs: Move-Item -Path "cmake-build/dxcompiler.dll" -Destination "dxcompiler.dll" Move-Item -Path "cmake-build/dxil.dll" -Destination "dxil.dll" Move-Item -Path "cmake-build/SDL2.dll" -Destination "SDL2.dll" + Move-Item -Path "cmake-build/Zelda64Recompiled.pdb" -Destination "Zelda64Recompiled.pdb" Remove-Item -Path "assets/scss" -Recurse -Force - name: Archive Zelda64Recomp uses: actions/upload-artifact@v4 @@ -276,3 +277,9 @@ jobs: SDL2.dll assets/ gamecontrollerdb.txt + - name: Archive Debug Files + uses: actions/upload-artifact@v4 + with: + name: Zelda64Recompiled-PDB-${{ matrix.type }} + path: | + Zelda64Recompiled.pdb diff --git a/README.md b/README.md index d840f9e..95f9aa3 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Zelda 64: Recompiled is a project that uses [N64: Recompiled](https://github.com
-_Thank you [Blaze](https://runblaze.dev) for supporting this project by providing Linux ARM64 and Apple Silicon macOS Github Action Runners!_ +_Special thanks to [Blaze](https://runblaze.dev) for their support of this project. They provide high-performance Linux (AMD64 & ARM64) and Apple Silicon macOS runners for GitHub Actions, greatly reducing our automated build times._ From fdbdf5edb07f4150ac2a127a11a6d0d4ea8678b8 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Fri, 11 Oct 2024 21:15:11 -0400 Subject: [PATCH 20/30] Add custom symbols toml and made non-relocatable versions of original gamestate functions, added base patch for Play_Init --- patches.toml | 2 +- patches/custom_syms.toml | 16 ++ patches/options.c | 5 +- patches/play_patches.c | 326 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+), 2 deletions(-) create mode 100644 patches/custom_syms.toml diff --git a/patches.toml b/patches.toml index 38910ee..18a8856 100644 --- a/patches.toml +++ b/patches.toml @@ -9,7 +9,7 @@ single_file_output = true use_absolute_symbols = true # Point the recompiler at the symbol files so that it can resolve relocations during recompilation. func_reference_syms_file = "Zelda64RecompSyms/mm.us.rev1.syms.toml" -data_reference_syms_files = [ "Zelda64RecompSyms/mm.us.rev1.datasyms.toml", "Zelda64RecompSyms/mm.us.rev1.datasyms_static.toml" ] +data_reference_syms_files = [ "Zelda64RecompSyms/mm.us.rev1.datasyms.toml", "Zelda64RecompSyms/mm.us.rev1.datasyms_static.toml", "patches/custom_syms.toml" ] # Tell the recompiler to write the output binary. Doing this instead of using objcopy allows the recompiler to patch MIPS32 relocs. output_binary_path = "patches/patches.bin" # Do not emit warnings for unpaired LO16 values, as clang produces many of them. diff --git a/patches/custom_syms.toml b/patches/custom_syms.toml new file mode 100644 index 0000000..9128278 --- /dev/null +++ b/patches/custom_syms.toml @@ -0,0 +1,16 @@ +# Custom symbols used for patches. +[[section]] +name = "ABSOLUTE_SYMS" +vram = 0x00000000 +size = 0x0 + +symbols = [ + # Alternate references to symbols that would otherwise be relocated + { name = "Setup_Init_NORELOCATE", vram = 0x80173338 }, # Not relocatable, but here for uniformity with the other gamestate funcs + { name = "MapSelect_Init_NORELOCATE", vram = 0x80801B4C }, + { name = "ConsoleLogo_Init_NORELOCATE", vram = 0x8080074C }, + { name = "Play_Init_NORELOCATE", vram = 0x8016A2C8 }, + { name = "TitleSetup_Init_NORELOCATE", vram = 0x80803F30 }, + { name = "FileSelect_Init_NORELOCATE", vram = 0x80813C98 }, + { name = "DayTelop_Init_NORELOCATE", vram = 0x80815820 }, +] diff --git a/patches/options.c b/patches/options.c index c26133c..07c0a23 100644 --- a/patches/options.c +++ b/patches/options.c @@ -1036,6 +1036,9 @@ RECOMP_PATCH void FileSelect_ConfirmFile(GameState *thisx) { } } +// Non-relocatable reference to the original address of Play_Init. +void Play_Init_NORELOCATE(GameState*); + /** * Load the save for the appropriate file and start the game. * Update function for `SM_LOAD_GAME` @@ -1060,7 +1063,7 @@ RECOMP_PATCH void FileSelect_LoadGame(GameState* thisx) { gSaveContext.gameMode = GAMEMODE_NORMAL; STOP_GAMESTATE(&this->state); - SET_NEXT_GAMESTATE(&this->state, Play_Init, sizeof(PlayState)); + SET_NEXT_GAMESTATE(&this->state, Play_Init_NORELOCATE, sizeof(PlayState)); gSaveContext.respawnFlag = 0; gSaveContext.respawn[RESPAWN_MODE_DOWN].entrance = ENTR_LOAD_OPENING; diff --git a/patches/play_patches.c b/patches/play_patches.c index db5deeb..712b612 100644 --- a/patches/play_patches.c +++ b/patches/play_patches.c @@ -1,6 +1,30 @@ #include "play_patches.h" #include "z64debug_display.h" #include "input.h" +#include "prevent_bss_reordering.h" +#include "z64.h" +#include "regs.h" +#include "functions.h" +#include "z64vismono.h" +#include "z64visfbuf.h" +#include "buffers.h" + +#include "variables.h" +#include "macros.h" +#include "buffers.h" +#include "idle.h" +#include "sys_cfb.h" +#include "z64bombers_notebook.h" +#include "z64quake.h" +#include "z64rumble.h" +#include "z64shrink_window.h" +#include "z64view.h" + +#include "overlays/gamestates/ovl_daytelop/z_daytelop.h" +#include "overlays/gamestates/ovl_opening/z_opening.h" +#include "overlays/gamestates/ovl_file_choose/z_file_select.h" +#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" +#include "debug.h" extern Input D_801F6C18; @@ -98,3 +122,305 @@ RECOMP_PATCH s32 Room_HandleLoadCallbacks(PlayState* play, RoomContext* roomCtx) return 1; } + +void ZeldaArena_Init(void* start, size_t size); + +void Play_SpawnScene(PlayState* this, s32 sceneId, s32 spawn); +void Play_InitMotionBlur(void); + +extern s16 sTransitionFillTimer; +extern Input D_801F6C18; +extern TransitionTile sTransitionTile; +extern s32 gTransitionTileState; +extern VisMono sPlayVisMono; +extern Color_RGBA8_u32 gVisMonoColor; +extern VisFbuf sPlayVisFbuf; +extern VisFbuf* sPlayVisFbufInstance; +extern BombersNotebook sBombersNotebook; +extern u8 sBombersNotebookOpen; +extern u8 sMotionBlurStatus; + +extern s32 gDbgCamEnabled; +extern u8 D_801D0D54; + +extern u8 gPictoPhotoI8[]; +extern u8 D_80784600[]; + +// Non-relocatable references to the original addresses of these game state functions. +void DayTelop_Init_NORELOCATE(GameState*); +void TitleSetup_Init_NORELOCATE(GameState*); + +RECOMP_PATCH void Play_Init(GameState* thisx) { + PlayState* this = (PlayState*)thisx; + GraphicsContext* gfxCtx = this->state.gfxCtx; + s32 pad; + uintptr_t zAlloc; + s32 zAllocSize; + Player* player; + s32 i; + s32 spawn; + u8 sceneLayer; + s32 scene; + + if ((gSaveContext.respawnFlag == -4) || (gSaveContext.respawnFlag == -0x63)) { + if (CHECK_EVENTINF(EVENTINF_TRIGGER_DAYTELOP)) { + CLEAR_EVENTINF(EVENTINF_TRIGGER_DAYTELOP); + STOP_GAMESTATE(&this->state); + // Use non-relocatable reference to DayTelop_Init instead. + SET_NEXT_GAMESTATE(&this->state, DayTelop_Init_NORELOCATE, sizeof(DayTelopState)); + return; + } + + gSaveContext.unk_3CA7 = 1; + if (gSaveContext.respawnFlag == -0x63) { + gSaveContext.respawnFlag = 2; + } + } else { + gSaveContext.unk_3CA7 = 0; + } + + if (gSaveContext.save.entrance == -1) { + gSaveContext.save.entrance = 0; + STOP_GAMESTATE(&this->state); + // Use non-relocatable reference to TitleSetup_Init instead. + SET_NEXT_GAMESTATE(&this->state, TitleSetup_Init_NORELOCATE, sizeof(TitleSetupState)); + return; + } + + if ((gSaveContext.nextCutsceneIndex == 0xFFEF) || (gSaveContext.nextCutsceneIndex == 0xFFF0)) { + scene = ((void)0, gSaveContext.save.entrance) >> 9; + spawn = (((void)0, gSaveContext.save.entrance) >> 4) & 0x1F; + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_SNOWHEAD_TEMPLE)) { + if (scene == ENTR_SCENE_MOUNTAIN_VILLAGE_WINTER) { + scene = ENTR_SCENE_MOUNTAIN_VILLAGE_SPRING; + } else if (scene == ENTR_SCENE_GORON_VILLAGE_WINTER) { + scene = ENTR_SCENE_GORON_VILLAGE_SPRING; + } else if (scene == ENTR_SCENE_PATH_TO_GORON_VILLAGE_WINTER) { + scene = ENTR_SCENE_PATH_TO_GORON_VILLAGE_SPRING; + } else if ((scene == ENTR_SCENE_SNOWHEAD) || (scene == ENTR_SCENE_PATH_TO_SNOWHEAD) || + (scene == ENTR_SCENE_PATH_TO_MOUNTAIN_VILLAGE) || (scene == ENTR_SCENE_GORON_SHRINE) || + (scene == ENTR_SCENE_GORON_RACETRACK)) { + gSaveContext.nextCutsceneIndex = 0xFFF0; + } + } + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE)) { + if (scene == ENTR_SCENE_SOUTHERN_SWAMP_POISONED) { + scene = ENTR_SCENE_SOUTHERN_SWAMP_CLEARED; + } else if (scene == ENTR_SCENE_WOODFALL) { + gSaveContext.nextCutsceneIndex = 0xFFF1; + } + } + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE) && (scene == ENTR_SCENE_IKANA_CANYON)) { + gSaveContext.nextCutsceneIndex = 0xFFF2; + } + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_GREAT_BAY_TEMPLE) && + ((scene == ENTR_SCENE_GREAT_BAY_COAST) || (scene == ENTR_SCENE_ZORA_CAPE))) { + gSaveContext.nextCutsceneIndex = 0xFFF0; + } + + // "First cycle" Termina Field + if (INV_CONTENT(ITEM_OCARINA_OF_TIME) != ITEM_OCARINA_OF_TIME) { + if ((scene == ENTR_SCENE_TERMINA_FIELD) && + (((void)0, gSaveContext.save.entrance) != ENTRANCE(TERMINA_FIELD, 10))) { + gSaveContext.nextCutsceneIndex = 0xFFF4; + } + } + //! FAKE: + gSaveContext.save.entrance = + Entrance_Create(((void)0, scene), spawn, ((void)0, gSaveContext.save.entrance) & 0xF); + } + + GameState_Realloc(&this->state, 0); + KaleidoManager_Init(this); + ShrinkWindow_Init(); + View_Init(&this->view, gfxCtx); + Audio_SetExtraFilter(0); + Quake_Init(); + Distortion_Init(this); + + for (i = 0; i < ARRAY_COUNT(this->cameraPtrs); i++) { + this->cameraPtrs[i] = NULL; + } + + Camera_Init(&this->mainCamera, &this->view, &this->colCtx, this); + Camera_ChangeStatus(&this->mainCamera, CAM_STATUS_ACTIVE); + + for (i = 0; i < ARRAY_COUNT(this->subCameras); i++) { + Camera_Init(&this->subCameras[i], &this->view, &this->colCtx, this); + Camera_ChangeStatus(&this->subCameras[i], CAM_STATUS_INACTIVE); + } + + this->cameraPtrs[CAM_ID_MAIN] = &this->mainCamera; + this->cameraPtrs[CAM_ID_MAIN]->uid = CAM_ID_MAIN; + this->activeCamId = CAM_ID_MAIN; + + Camera_OverwriteStateFlags(&this->mainCamera, CAM_STATE_0 | CAM_STATE_CHECK_WATER | CAM_STATE_2 | CAM_STATE_3 | + CAM_STATE_4 | CAM_STATE_DISABLE_MODE_CHANGE | CAM_STATE_6); + Sram_Alloc(&this->state, &this->sramCtx); + Regs_InitData(this); + Message_Init(this); + GameOver_Init(this); + SoundSource_InitAll(this); + EffFootmark_Init(this); + Effect_Init(this); + EffectSS_Init(this, 100); + CollisionCheck_InitContext(this, &this->colChkCtx); + AnimationContext_Reset(&this->animationCtx); + Cutscene_InitContext(this, &this->csCtx); + + if (gSaveContext.nextCutsceneIndex != 0xFFEF) { + gSaveContext.save.cutsceneIndex = gSaveContext.nextCutsceneIndex; + gSaveContext.nextCutsceneIndex = 0xFFEF; + } + + if (gSaveContext.save.cutsceneIndex == 0xFFFD) { + gSaveContext.save.cutsceneIndex = 0; + } + + if (gSaveContext.nextDayTime != NEXT_TIME_NONE) { + gSaveContext.save.time = gSaveContext.nextDayTime; + gSaveContext.skyboxTime = gSaveContext.nextDayTime; + } + + if ((CURRENT_TIME >= CLOCK_TIME(18, 0)) || (CURRENT_TIME < CLOCK_TIME(6, 30))) { + gSaveContext.save.isNight = true; + } else { + gSaveContext.save.isNight = false; + } + + func_800EDDB0(this); + + if (((gSaveContext.gameMode != GAMEMODE_NORMAL) && (gSaveContext.gameMode != GAMEMODE_TITLE_SCREEN)) || + (gSaveContext.save.cutsceneIndex >= 0xFFF0)) { + gSaveContext.nayrusLoveTimer = 0; + Magic_Reset(this); + gSaveContext.sceneLayer = (gSaveContext.save.cutsceneIndex & 0xF) + 1; + + // Set saved cutscene to 0 so it doesn't immediately play, but instead let the `CutsceneManager` handle it. + gSaveContext.save.cutsceneIndex = 0; + } else { + gSaveContext.sceneLayer = 0; + } + + sceneLayer = gSaveContext.sceneLayer; + + Play_SpawnScene( + this, Entrance_GetSceneIdAbsolute(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer)), + Entrance_GetSpawnNum(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer))); + KaleidoScopeCall_Init(this); + Interface_Init(this); + + if (gSaveContext.nextDayTime != NEXT_TIME_NONE) { + if (gSaveContext.nextDayTime == NEXT_TIME_DAY) { + gSaveContext.save.day++; + gSaveContext.save.eventDayCount++; + gSaveContext.dogIsLost = true; + gSaveContext.nextDayTime = NEXT_TIME_DAY_SET; + } else { + gSaveContext.nextDayTime = NEXT_TIME_NIGHT_SET; + } + } + + Play_InitMotionBlur(); + + R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF; + R_PICTO_PHOTO_STATE = PICTO_PHOTO_STATE_OFF; + + PreRender_Init(&this->pauseBgPreRender); + PreRender_SetValuesSave(&this->pauseBgPreRender, gCfbWidth, gCfbHeight, NULL, NULL, NULL); + PreRender_SetValues(&this->pauseBgPreRender, gCfbWidth, gCfbHeight, NULL, NULL); + + this->unk_18E64 = gWorkBuffer; + this->pictoPhotoI8 = gPictoPhotoI8; + this->unk_18E68 = D_80784600; + this->unk_18E58 = D_80784600; + this->unk_18E60 = D_80784600; + gTransitionTileState = TRANS_TILE_OFF; + this->transitionMode = TRANS_MODE_OFF; + D_801D0D54 = false; + + FrameAdvance_Init(&this->frameAdvCtx); + Rand_Seed(osGetTime()); + Matrix_Init(&this->state); + + this->state.main = Play_Main; + this->state.destroy = Play_Destroy; + + this->transitionTrigger = TRANS_TRIGGER_END; + this->worldCoverAlpha = 0; + this->bgCoverAlpha = 0; + this->haltAllActors = false; + this->unk_18844 = false; + + if (gSaveContext.gameMode != GAMEMODE_TITLE_SCREEN) { + if (gSaveContext.nextTransitionType == TRANS_NEXT_TYPE_DEFAULT) { + this->transitionType = + (Entrance_GetTransitionFlags(((void)0, gSaveContext.save.entrance) + sceneLayer) >> 7) & 0x7F; + } else { + this->transitionType = gSaveContext.nextTransitionType; + gSaveContext.nextTransitionType = TRANS_NEXT_TYPE_DEFAULT; + } + } else { + this->transitionType = TRANS_TYPE_FADE_BLACK; + } + + TransitionFade_Init(&this->unk_18E48); + TransitionFade_SetType(&this->unk_18E48, 3); + TransitionFade_SetColor(&this->unk_18E48, RGBA8(160, 160, 160, 255)); + TransitionFade_Start(&this->unk_18E48); + VisMono_Init(&sPlayVisMono); + + gVisMonoColor.a = 0; + sPlayVisFbufInstance = &sPlayVisFbuf; + VisFbuf_Init(sPlayVisFbufInstance); + sPlayVisFbufInstance->lodProportion = 0.0f; + sPlayVisFbufInstance->mode = VIS_FBUF_MODE_GENERAL; + sPlayVisFbufInstance->primColor.r = 0; + sPlayVisFbufInstance->primColor.g = 0; + sPlayVisFbufInstance->primColor.b = 0; + sPlayVisFbufInstance->primColor.a = 0; + sPlayVisFbufInstance->envColor.r = 0; + sPlayVisFbufInstance->envColor.g = 0; + sPlayVisFbufInstance->envColor.b = 0; + sPlayVisFbufInstance->envColor.a = 0; + CutsceneFlags_UnsetAll(this); + THA_GetRemaining(&this->state.tha); + zAllocSize = THA_GetRemaining(&this->state.tha); + zAlloc = (uintptr_t)THA_AllocTailAlign16(&this->state.tha, zAllocSize); + + //! @bug: Incorrect ALIGN16s + ZeldaArena_Init((void*)((zAlloc + 8) & ~0xF), (zAllocSize - ((zAlloc + 8) & ~0xF)) + zAlloc); + + Actor_InitContext(this, &this->actorCtx, this->linkActorEntry); + + while (!Room_HandleLoadCallbacks(this, &this->roomCtx)) {} + + if ((CURRENT_DAY != 0) && ((this->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_1) || + (this->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_5))) { + Actor_Spawn(&this->actorCtx, this, ACTOR_EN_TEST4, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0); + } + + player = GET_PLAYER(this); + + Camera_InitFocalActorSettings(&this->mainCamera, &player->actor); + gDbgCamEnabled = false; + + if (PLAYER_GET_BG_CAM_INDEX(&player->actor) != 0xFF) { + Camera_ChangeActorCsCamIndex(&this->mainCamera, PLAYER_GET_BG_CAM_INDEX(&player->actor)); + } + + CutsceneManager_StoreCamera(&this->mainCamera); + Interface_SetSceneRestrictions(this); + Environment_PlaySceneSequence(this); + gSaveContext.seqId = this->sequenceCtx.seqId; + gSaveContext.ambienceId = this->sequenceCtx.ambienceId; + AnimationContext_Update(this, &this->animationCtx); + Cutscene_HandleEntranceTriggers(this); + gSaveContext.respawnFlag = 0; + sBombersNotebookOpen = false; + BombersNotebook_Init(&sBombersNotebook); +} From e5699e59a1cfb0f7af363123a483748a87f74efe Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 12 Oct 2024 01:12:02 -0400 Subject: [PATCH 21/30] Update runtime to fix mod section alignment --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index a4f5ee1..9e9ae17 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit a4f5ee10c8303474d9028468e84845b192e9297d +Subproject commit 9e9ae173ee2c378c6ff5f7bbfe452503a80212d9 From 8c8f5b889fcbc4fcdf91a93a373c43fffe802656 Mon Sep 17 00:00:00 2001 From: LittleCube Date: Sat, 12 Oct 2024 23:13:42 -0400 Subject: [PATCH 22/30] Add events and exports for Play_Init (#495) --- patches/play_patches.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/patches/play_patches.c b/patches/play_patches.c index 712b612..1d38632 100644 --- a/patches/play_patches.c +++ b/patches/play_patches.c @@ -150,6 +150,15 @@ extern u8 D_80784600[]; void DayTelop_Init_NORELOCATE(GameState*); void TitleSetup_Init_NORELOCATE(GameState*); +RECOMP_DECLARE_EVENT(recomp_on_play_init(PlayState* this)); + +bool allow_no_ocarina_tf = false; + +// @recomp_export void recomp_set_allow_no_ocarina(bool new_val): Set whether to force Termina Field to load normally even if Link has no ocarina. +RECOMP_EXPORT void recomp_set_allow_no_ocarina(bool new_val) { + allow_no_ocarina_tf = new_val; +} + RECOMP_PATCH void Play_Init(GameState* thisx) { PlayState* this = (PlayState*)thisx; GraphicsContext* gfxCtx = this->state.gfxCtx; @@ -162,6 +171,9 @@ RECOMP_PATCH void Play_Init(GameState* thisx) { u8 sceneLayer; s32 scene; + // @recomp_event recomp_on_play_init(PlayState* this): A new PlayState is being constructed. + recomp_on_play_init(this); + if ((gSaveContext.respawnFlag == -4) || (gSaveContext.respawnFlag == -0x63)) { if (CHECK_EVENTINF(EVENTINF_TRIGGER_DAYTELOP)) { CLEAR_EVENTINF(EVENTINF_TRIGGER_DAYTELOP); @@ -223,7 +235,8 @@ RECOMP_PATCH void Play_Init(GameState* thisx) { } // "First cycle" Termina Field - if (INV_CONTENT(ITEM_OCARINA_OF_TIME) != ITEM_OCARINA_OF_TIME) { + // @recomp_use_export_var allow_no_ocarina_tf: Skip loading into "First cycle" Termina Field if mods enable it. + if (!allow_no_ocarina_tf && INV_CONTENT(ITEM_OCARINA_OF_TIME) != ITEM_OCARINA_OF_TIME) { if ((scene == ENTR_SCENE_TERMINA_FIELD) && (((void)0, gSaveContext.save.entrance) != ENTRANCE(TERMINA_FIELD, 10))) { gSaveContext.nextCutsceneIndex = 0xFFF4; From 21ca074bf7489e615c382dd553b43ed075104a40 Mon Sep 17 00:00:00 2001 From: LittleCube Date: Sun, 13 Oct 2024 00:02:08 -0400 Subject: [PATCH 23/30] Add exports for fd anywhere and epona fix patches (#494) * add exports for fd anywhere and epona fix patches * use better logic for restoreHudVisibility * cleanup * fix a couple edge cases (being sent the bow while riding Epona) --- patches/input.c | 95 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 17 deletions(-) diff --git a/patches/input.c b/patches/input.c index 53f7eb3..f65d2cc 100644 --- a/patches/input.c +++ b/patches/input.c @@ -534,6 +534,13 @@ RECOMP_PATCH void Player_Action_86(Player *this, PlayState *play) { func_808550D0(play, this, this->unk_B10[4], this->unk_B10[5], (this->transformation == PLAYER_FORM_HUMAN) ? 0 : 1); } +bool no_bow_epona_fix = false; + +// @recomp_export void recomp_set_no_bow_epona_fix(bool new_val): Set whether to enable the fix for getting on Epona without a bow. +RECOMP_EXPORT void recomp_set_no_bow_epona_fix(bool new_val) { + no_bow_epona_fix = new_val; +} + extern s16 sPictoState; extern s16 sPictoPhotoBeingTaken; extern void* gWorkBuffer; @@ -603,24 +610,46 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED; set_extra_item_slot_status(BTN_DISABLED); } else { - BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW; + // @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix. + if (no_bow_epona_fix) { + if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_BOW) { + BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW; + BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED; + } + } else { + BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW; + } if (play->unk_1887C >= 2) { Interface_LoadItemIconImpl(play, EQUIP_SLOT_B); } else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE) { - BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE; + // @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix. + if (no_bow_epona_fix) { + gSaveContext.buttonStatus[EQUIP_SLOT_B] = BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B); + BUTTON_STATUS(EQUIP_SLOT_B) = BTN_DISABLED; + } else { + BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE; + } } else { Interface_LoadItemIconImpl(play, EQUIP_SLOT_B); } - BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED; - BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED; - BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED; - set_extra_item_slot_status(BTN_DISABLED); - Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE); + // @recomp_use_export_var no_bow_epona_fix: If the B button does not contain a sword, don't disable the UI. + if (!no_bow_epona_fix || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) < ITEM_SWORD_KOKIRI || + BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) > ITEM_SWORD_GILDED) { + BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED; + BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED; + BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED; + set_extra_item_slot_status(BTN_DISABLED); + Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE); + } } } + if (BUTTON_STATUS(EQUIP_SLOT_B) == BTN_DISABLED && BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) { + BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED; + } + if (play->transitionMode != TRANS_MODE_OFF) { Interface_SetHudVisibility(HUD_VISIBILITY_NONE); } else if ((gSaveContext.minigameStatus == MINIGAME_STATUS_ACTIVE) && @@ -662,27 +691,51 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED; set_extra_item_slot_status(BTN_DISABLED); } else { - BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW; + // @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix. + if (no_bow_epona_fix) { + if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_BOW) { + BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW; + BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED; + } + } else { + BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW; + } } if (play->unk_1887C >= 2) { Interface_LoadItemIconImpl(play, EQUIP_SLOT_B); } else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE) { - BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE; + // @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix. + if (no_bow_epona_fix) { + gSaveContext.buttonStatus[EQUIP_SLOT_B] = BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B); + BUTTON_STATUS(EQUIP_SLOT_B) = BTN_DISABLED; + } else { + BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE; + } } else { Interface_LoadItemIconImpl(play, EQUIP_SLOT_B); } if (BUTTON_STATUS(EQUIP_SLOT_B) == BTN_DISABLED) { - BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED; - restoreHudVisibility = true; + // @recomp_use_export_var no_bow_epona_fix: Don't enable the B button unless it is being used for the bow. + if (!no_bow_epona_fix || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) { + BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED; + } + + // @recomp_use_export_var no_bow_epona_fix: Don't restore hud visibility from Epona without a sword. + if (!no_bow_epona_fix || (player->stateFlags1 & PLAYER_STATE1_800000) == 0) { + restoreHudVisibility = true; + } } - BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED; - BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED; - BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED; - set_extra_item_slot_status(BTN_DISABLED); - Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE); + // @recomp_use_export_var no_bow_epona_fix: If the B button does not contain the bow, don't disable the UI. + if (!no_bow_epona_fix || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) { + BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED; + BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED; + BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED; + set_extra_item_slot_status(BTN_DISABLED); + Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE); + } if (play->transitionMode != TRANS_MODE_OFF) { Interface_SetHudVisibility(HUD_VISIBILITY_NONE); @@ -802,6 +855,13 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { } } +bool fd_anywhere = false; + +// @recomp_export void recomp_set_fd_anywhere(bool new_val): Set whether the Fierce Deity's Mask has scene restrictions. +RECOMP_EXPORT void recomp_set_fd_anywhere(bool new_val) { + fd_anywhere = new_val; +} + /** * A continuation of the if-else chain from Interface_UpdateButtonsPart1 * Also used directly when opening the pause menu i.e. skips part 1 @@ -1181,7 +1241,8 @@ RECOMP_PATCH void Interface_UpdateButtonsPart2(PlayState* play) { } } else if (GET_CUR_FORM_BTN_ITEM(i) == ITEM_MASK_FIERCE_DEITY) { // Fierce Deity's Mask is equipped - if ((play->sceneId != SCENE_MITURIN_BS) && (play->sceneId != SCENE_HAKUGIN_BS) && + // @recomp_use_export_var fd_anywhere: Allow the player to use the Fierce Deity's Mask anywhere if mods enable it. + if (!fd_anywhere && (play->sceneId != SCENE_MITURIN_BS) && (play->sceneId != SCENE_HAKUGIN_BS) && (play->sceneId != SCENE_SEA_BS) && (play->sceneId != SCENE_INISIE_BS) && (play->sceneId != SCENE_LAST_BS)) { if (BUTTON_STATUS(i) != BTN_DISABLED) { From 0f924106203c2b2a0153acce151cdbc93b423a9f Mon Sep 17 00:00:00 2001 From: LittleCube Date: Mon, 14 Oct 2024 16:53:30 -0400 Subject: [PATCH 24/30] Add recomp_after_play_init event (#496) * fix signature for recomp_set_allow_no_ocarina_tf, add new event recomp_after_play_init * say initialize instead of construct --- patches/play_patches.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/patches/play_patches.c b/patches/play_patches.c index 1d38632..04f7b88 100644 --- a/patches/play_patches.c +++ b/patches/play_patches.c @@ -151,11 +151,12 @@ void DayTelop_Init_NORELOCATE(GameState*); void TitleSetup_Init_NORELOCATE(GameState*); RECOMP_DECLARE_EVENT(recomp_on_play_init(PlayState* this)); +RECOMP_DECLARE_EVENT(recomp_after_play_init(PlayState* this)); bool allow_no_ocarina_tf = false; -// @recomp_export void recomp_set_allow_no_ocarina(bool new_val): Set whether to force Termina Field to load normally even if Link has no ocarina. -RECOMP_EXPORT void recomp_set_allow_no_ocarina(bool new_val) { +// @recomp_export void recomp_set_allow_no_ocarina_tf(bool new_val): Set whether to force Termina Field to load normally even if Link has no ocarina. +RECOMP_EXPORT void recomp_set_allow_no_ocarina_tf(bool new_val) { allow_no_ocarina_tf = new_val; } @@ -171,7 +172,7 @@ RECOMP_PATCH void Play_Init(GameState* thisx) { u8 sceneLayer; s32 scene; - // @recomp_event recomp_on_play_init(PlayState* this): A new PlayState is being constructed. + // @recomp_event recomp_on_play_init(PlayState* this): A new PlayState is being initialized. recomp_on_play_init(this); if ((gSaveContext.respawnFlag == -4) || (gSaveContext.respawnFlag == -0x63)) { @@ -436,4 +437,7 @@ RECOMP_PATCH void Play_Init(GameState* thisx) { gSaveContext.respawnFlag = 0; sBombersNotebookOpen = false; BombersNotebook_Init(&sBombersNotebook); + + // @recomp_event recomp_after_play_init(PlayState* this): The new PlayState has finished initializing. + recomp_after_play_init(this); } From e862a38135a73fa7df4de31ac13257bb8c304dae Mon Sep 17 00:00:00 2001 From: LittleCube Date: Sun, 20 Oct 2024 19:35:41 -0400 Subject: [PATCH 25/30] More vanilla bug fix exports (#497) --- patches/input.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/patches/input.c b/patches/input.c index f65d2cc..2962204 100644 --- a/patches/input.c +++ b/patches/input.c @@ -541,11 +541,24 @@ RECOMP_EXPORT void recomp_set_no_bow_epona_fix(bool new_val) { no_bow_epona_fix = new_val; } +bool h_and_d_no_sword_fix = false; + +// @recomp_export void recomp_set_h_and_d_no_sword_fix(bool new_val): Set whether to enable the fix for playing Honey and Darling without a sword. +RECOMP_EXPORT void recomp_set_h_and_d_no_sword_fix(bool new_val) { + h_and_d_no_sword_fix = new_val; +} + extern s16 sPictoState; extern s16 sPictoPhotoBeingTaken; extern void* gWorkBuffer; u16 func_801A5100(void); +#define ON_EPONA (player->stateFlags1 & PLAYER_STATE1_800000) +#define EPONA_FIX_ACTIVE (no_bow_epona_fix && ON_EPONA) + +#define AT_H_AND_D (play->sceneId == SCENE_BOWLING) +#define H_AND_D_FIX_ACTIVE (h_and_d_no_sword_fix && AT_H_AND_D) + // @recomp Patched to update status of extra buttons via set_extra_item_slot_status. RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { InterfaceContext* interfaceCtx = &play->interfaceCtx; @@ -555,11 +568,11 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { if (gSaveContext.save.cutsceneIndex < 0xFFF0) { gSaveContext.hudVisibilityForceButtonAlphasByStatus = false; - if ((player->stateFlags1 & PLAYER_STATE1_800000) || CHECK_WEEKEVENTREG(WEEKEVENTREG_08_01) || + if (ON_EPONA || CHECK_WEEKEVENTREG(WEEKEVENTREG_08_01) || (!CHECK_EVENTINF(EVENTINF_41) && (play->unk_1887C >= 2))) { // Riding Epona OR Honey & Darling minigame OR Horseback balloon minigame OR related to swamp boat // (non-minigame?) - if ((player->stateFlags1 & PLAYER_STATE1_800000) && (player->currentMask == PLAYER_MASK_BLAST) && + if (ON_EPONA && (player->currentMask == PLAYER_MASK_BLAST) && (gSaveContext.bButtonStatus == BTN_DISABLED)) { // Riding Epona with blast mask? restoreHudVisibility = true; @@ -611,7 +624,7 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { set_extra_item_slot_status(BTN_DISABLED); } else { // @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix. - if (no_bow_epona_fix) { + if (EPONA_FIX_ACTIVE) { if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_BOW) { BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW; BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED; @@ -624,7 +637,7 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { Interface_LoadItemIconImpl(play, EQUIP_SLOT_B); } else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE) { // @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix. - if (no_bow_epona_fix) { + if (EPONA_FIX_ACTIVE) { gSaveContext.buttonStatus[EQUIP_SLOT_B] = BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B); BUTTON_STATUS(EQUIP_SLOT_B) = BTN_DISABLED; } else { @@ -635,7 +648,7 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { } // @recomp_use_export_var no_bow_epona_fix: If the B button does not contain a sword, don't disable the UI. - if (!no_bow_epona_fix || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) < ITEM_SWORD_KOKIRI || + if (!EPONA_FIX_ACTIVE || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) < ITEM_SWORD_KOKIRI || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) > ITEM_SWORD_GILDED) { BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED; BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED; @@ -669,12 +682,12 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED; set_extra_item_slot_status(BTN_DISABLED); Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP); - } else if (player->stateFlags1 & PLAYER_STATE1_800000) { + } else if (ON_EPONA) { Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP); } } } else { - if (player->stateFlags1 & PLAYER_STATE1_800000) { + if (ON_EPONA) { Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP); } @@ -686,13 +699,16 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { } else { BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW; } + if (h_and_d_no_sword_fix) { + BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED; + } BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED; BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED; BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED; set_extra_item_slot_status(BTN_DISABLED); } else { // @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix. - if (no_bow_epona_fix) { + if (EPONA_FIX_ACTIVE) { if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_BOW) { BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW; BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED; @@ -704,9 +720,9 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { if (play->unk_1887C >= 2) { Interface_LoadItemIconImpl(play, EQUIP_SLOT_B); - } else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE) { + } else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE && !H_AND_D_FIX_ACTIVE) { // @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix. - if (no_bow_epona_fix) { + if (EPONA_FIX_ACTIVE) { gSaveContext.buttonStatus[EQUIP_SLOT_B] = BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B); BUTTON_STATUS(EQUIP_SLOT_B) = BTN_DISABLED; } else { @@ -729,7 +745,7 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { } // @recomp_use_export_var no_bow_epona_fix: If the B button does not contain the bow, don't disable the UI. - if (!no_bow_epona_fix || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) { + if ((!no_bow_epona_fix || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) && !H_AND_D_FIX_ACTIVE) { BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED; BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED; BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED; @@ -753,8 +769,11 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) { BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED; set_extra_item_slot_status(BTN_DISABLED); Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP); - } else if (player->stateFlags1 & PLAYER_STATE1_800000) { + } else if (ON_EPONA) { Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP); + } else if (H_AND_D_FIX_ACTIVE) { + Interface_LoadItemIconImpl(play, EQUIP_SLOT_B); + Interface_SetHudVisibility(HUD_VISIBILITY_B); } } } else if (sPictoState != PICTO_BOX_STATE_OFF) { From db4d9c668d7b9e3aa996c0ba4957df3326090134 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 21 Oct 2024 22:25:16 -0400 Subject: [PATCH 26/30] Update runtime for heap allocator --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 9e9ae17..3e39c2e 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 9e9ae173ee2c378c6ff5f7bbfe452503a80212d9 +Subproject commit 3e39c2ec34340e245a5b7e353560b5a793b9990b From 79d75037047926940240df92fdf9b5c19524d36f Mon Sep 17 00:00:00 2001 From: dcvz Date: Mon, 7 Oct 2024 23:20:43 +0200 Subject: [PATCH 27/30] Add macOS Support --- .github/macos/Info.plist.in | 33 +++++++ .github/macos/MoltenVK_icd.json | 8 ++ .github/macos/fixup_bundle.cmake | 11 +++ .github/macos/macports.yaml | 14 +++ .github/workflows/validate.yml | 128 ++++++++++++++++++++++++++- CMakeLists.txt | 146 ++++++++++++++++++++++++++++++- include/zelda_support.h | 14 +++ lib/rt64 | 2 +- src/game/config.cpp | 10 ++- src/main/main.cpp | 22 ++++- src/main/rt64_render_context.cpp | 2 +- src/main/support.cpp | 10 +++ src/main/support_apple.mm | 13 +++ src/ui/ui_config.cpp | 8 +- src/ui/ui_launcher.cpp | 70 ++++++++------- src/ui/ui_renderer.cpp | 14 ++- 16 files changed, 458 insertions(+), 47 deletions(-) create mode 100644 .github/macos/Info.plist.in create mode 100644 .github/macos/MoltenVK_icd.json create mode 100644 .github/macos/fixup_bundle.cmake create mode 100644 .github/macos/macports.yaml create mode 100644 include/zelda_support.h create mode 100644 src/main/support.cpp create mode 100644 src/main/support_apple.mm diff --git a/.github/macos/Info.plist.in b/.github/macos/Info.plist.in new file mode 100644 index 0000000..44a1a14 --- /dev/null +++ b/.github/macos/Info.plist.in @@ -0,0 +1,33 @@ + + + + + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleExecutable + Zelda64Recompiled + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + LSApplicationCategoryType + public.app-category.games + CFBundlePackageType + APPL + LSMinimumSystemVersion + 11 + LSEnvironment + + MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS + 1 + MVK_CONFIG_USE_METAL_PRIVATE_API + 1 + MVK_CONFIG_RESUME_LOST_DEVICE + 1 + + + diff --git a/.github/macos/MoltenVK_icd.json b/.github/macos/MoltenVK_icd.json new file mode 100644 index 0000000..ffcdbd9 --- /dev/null +++ b/.github/macos/MoltenVK_icd.json @@ -0,0 +1,8 @@ +{ + "file_format_version": "1.0.0", + "ICD": { + "library_path": "../../../Frameworks/libMoltenVK.dylib", + "api_version": "1.2.0", + "is_portability_driver": true + } +} \ No newline at end of file diff --git a/.github/macos/fixup_bundle.cmake b/.github/macos/fixup_bundle.cmake new file mode 100644 index 0000000..080806f --- /dev/null +++ b/.github/macos/fixup_bundle.cmake @@ -0,0 +1,11 @@ +include(BundleUtilities) + +# Use generator expressions to get the absolute path to the bundle and frameworks +set(APPS "Zelda64Recompiled.app/Contents/MacOS/Zelda64Recompiled") +set(DIRS "Zelda64Recompiled.app/Contents/Frameworks") + +# The fixup_bundle command needs an absolute path +file(REAL_PATH ${APPS} APPS) +file(REAL_PATH ${DIRS} DIRS) + +fixup_bundle("${APPS}" "" "${DIRS}") \ No newline at end of file diff --git a/.github/macos/macports.yaml b/.github/macos/macports.yaml new file mode 100644 index 0000000..a51ccec --- /dev/null +++ b/.github/macos/macports.yaml @@ -0,0 +1,14 @@ +version: '2.9.3' +prefix: '/opt/local' +variants: + select: + - aqua + - metal + deselect: x11 +ports: + - name: clang-18 + - name: llvm-18 + - name: libsdl2 + select: universal + - name: freetype + select: universal diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index b9f7c96..e40960d 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -14,6 +14,14 @@ on: type: string required: false default: '4e6f4e52989aca69739880b40b9f988357f15d10ca03284377b81f1502463ff5' + VULKAN_SDK_VERSION: + type: string + required: false + default: '1.3.296.0' + MOLTENVK_COMMIT: + type: string + required: false + default: '' secrets: ZRE_REPO_WITH_PAT: required: true @@ -95,7 +103,7 @@ jobs: rm -rf assets/scss tar -czf Zelda64Recompiled.tar.gz Zelda64Recompiled assets/ gamecontrollerdb.txt - name: Archive Zelda64Recomp - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Zelda64Recompiled-${{ runner.os }}-X64-${{ matrix.type }} path: Zelda64Recompiled.tar.gz @@ -103,7 +111,7 @@ jobs: run: |- ./.github/linux/appimage.sh - name: Zelda64Recomp AppImage - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Zelda64Recompiled-AppImage-X64-${{ matrix.type }} path: Zelda64Recompiled-*.AppImage @@ -283,3 +291,119 @@ jobs: name: Zelda64Recompiled-PDB-${{ matrix.type }} path: | Zelda64Recompiled.pdb + build-macos: + runs-on: blaze/macos-14 + strategy: + matrix: + type: [ Debug, Release ] + name: macos (x64, arm64, ${{ matrix.type }}) + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.ref }} + submodules: recursive + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ runner.os }}-z64re-ccache-${{ matrix.type }} + - name: Homebrew Setup + run: | + brew install ninja + brew uninstall --ignore-dependencies libpng freetype + - name: MacPorts Setup + uses: melusina-org/setup-macports@v1 + id: 'macports' + with: + parameters: '.github/macos/macports.yaml' + - name: Prepare Build + run: |- + git clone ${{ secrets.ZRE_REPO_WITH_PAT }} + ./zre/process.sh + cp ./zre/mm_shader_cache.bin ./shadercache/ + - name: Build N64Recomp & RSPRecomp + run: | + git clone https://github.com/Mr-Wiseguy/N64Recomp.git --recurse-submodules N64RecompSource + cd N64RecompSource + git checkout ${{ inputs.N64RECOMP_COMMIT }} + git submodule update --init --recursive + + # enable ccache + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + + # Build N64Recomp & RSPRecomp + cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build + cmake --build cmake-build --config Release --target N64Recomp -j $(sysctl -n hw.ncpu) + cmake --build cmake-build --config Release --target RSPRecomp -j $(sysctl -n hw.ncpu) + + # Copy N64Recomp & RSPRecomp to root directory + cp cmake-build/N64Recomp .. + cp cmake-build/RSPRecomp .. + - name: Run N64Recomp & RSPRecomp + run: | + ./N64Recomp us.rev1.toml + ./RSPRecomp aspMain.us.rev1.toml + ./RSPRecomp njpgdspMain.us.rev1.toml + - name: Cache Vulkan SDK + id: cache-vulkan-sdk + uses: actions/cache@v3 + with: + path: /Users/runner/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }} + key: ${{ runner.os }}-${{ matrix.type }}-vulkan-sdk-${{ inputs.VULKAN_SDK_VERSION }} + - name: Install Vulkan SDK + if: steps.cache-vulkan-sdk.outputs.cache-hit != 'true' + run: | + wget https://sdk.lunarg.com/sdk/download/${{ inputs.VULKAN_SDK_VERSION }}/mac/vulkansdk-macos-${{ inputs.VULKAN_SDK_VERSION }}.zip + unzip vulkansdk-macos-${{ inputs.VULKAN_SDK_VERSION }}.zip + sudo InstallVulkan.app/Contents/MacOS/InstallVulkan --root ~/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }} --accept-licenses --default-answer --confirm-command install + - name: Checkout MoltenVK + if: inputs.MOLTENVK_COMMIT != '' + run: | + git clone https://github.com/KhronosGroup/MoltenVK.git + cd MoltenVK + git checkout ${{ inputs.MOLTENVK_COMMIT }} + - name: Cache MoltenVK Dependencies + if: inputs.MOLTENVK_COMMIT != '' + id: cache-mvk-dependencies + uses: actions/cache@v3 + with: + path: | + MoltenVK/External/build + !MoltenVK/External/build/Intermediates + key: ${{ runner.os }}-${{ matrix.type }}-${{ hashFiles('MoltenVK/fetchDependencies','MoltenVK/ExternalRevisions/**','MoltenVK/ExternalDependencies.xcodeproj/**','MoltenVK/Scripts/**') }} + - name: Fetch MVK Dependencies (Use Built Cache) + if: inputs.MOLTENVK_COMMIT != '' && steps.cache-mvk-dependencies.outputs.cache-hit == 'true' + run: | + cd MoltenVK + ./fetchDependencies -v --none + - name: Fetch MVK Dependencies + if: inputs.MOLTENVK_COMMIT != '' && steps.cache-mvk-dependencies.outputs.cache-hit != 'true' + run: | + cd MoltenVK + ./fetchDependencies -v --macos + - name: Build MoltenVK + if: inputs.MOLTENVK_COMMIT != '' + run: | + cd MoltenVK + make macos + sudo cp Package/Latest/MoltenVK/dylib/macOS/libMoltenVK.dylib ~/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }}/macOS/lib/libMoltenVK.dylib + - name: Build ZeldaRecomp + run: |- + # enable ccache + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + + cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build \ + -DPATCHES_LD=/opt/local/bin/ld.lld-mp-18 -DPATCHES_OBJCOPY=/opt/local/bin/llvm-objcopy-mp-18 -DCMAKE_AR=/opt/local/bin/llvm-ar-mp-18 -DPATCHES_C_COMPILER=/opt/local/bin/clang-mp-18 \ + -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" + cmake --build cmake-build --config ${{ matrix.type }} --target Zelda64Recompiled -j $(sysctl -n hw.ncpu) + env: + VULKAN_SDK: /Users/runner/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }}/macOS + - name: Prepare Archive + run: | + mv cmake-build/Zelda64Recompiled.app Zelda64Recompiled.app + zip -r -y Zelda64Recompiled.zip Zelda64Recompiled.app + - name: Archive Zelda64Recomp + uses: actions/upload-artifact@v4 + with: + name: Zelda64Recompiled-${{ runner.os }}-${{ matrix.type }} + path: Zelda64Recompiled.zip diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b900bc..3de60a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,10 @@ cmake_minimum_required(VERSION 3.20) -project(Zelda64Recompiled) + +if (APPLE) # has to be set before the first project() or enable_language() + set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum OS X deployment version") +endif() + +project(Zelda64Recompiled LANGUAGES C CXX) set(CMAKE_C_STANDARD 17) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -15,6 +20,30 @@ if (WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR") endif() +if (APPLE) + enable_language(OBJC OBJCXX) + + # Check if VULKAN_SDK environment variable is set + if(NOT DEFINED ENV{VULKAN_SDK}) + message(FATAL_ERROR "VULKAN_SDK environment variable is not set.") + else() + set(VULKAN_SDK $ENV{VULKAN_SDK}) + endif() + + # Define paths to Vulkan loader and MoltenVK libraries + set(VULKAN_LOADER_PATH "${VULKAN_SDK}/lib/libvulkan.dylib") + set(MOLTENVK_PATH "${VULKAN_SDK}/lib/libMoltenVK.dylib") + + # Ensure the Vulkan loader and MoltenVK libraries exist + if(NOT EXISTS "${VULKAN_LOADER_PATH}") + message(FATAL_ERROR "Vulkan loader not found at ${VULKAN_LOADER_PATH}") + endif() + + if(NOT EXISTS "${MOLTENVK_PATH}") + message(FATAL_ERROR "MoltenVK not found at ${MOLTENVK_PATH}") + endif() +endif() + # Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24: if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") cmake_policy(SET CMP0135 NEW) @@ -135,11 +164,12 @@ add_custom_target(DownloadGameControllerDB DEPENDS ${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt) # Main executable -add_executable(Zelda64Recompiled) +add_executable(Zelda64Recompiled MACOSX_BUNDLE) add_dependencies(Zelda64Recompiled DownloadGameControllerDB) set (SOURCES ${CMAKE_SOURCE_DIR}/src/main/main.cpp + ${CMAKE_SOURCE_DIR}/src/main/support.cpp ${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp ${CMAKE_SOURCE_DIR}/src/main/register_patches.cpp ${CMAKE_SOURCE_DIR}/src/main/rt64_render_context.cpp @@ -164,6 +194,10 @@ set (SOURCES ${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends/RmlUi_Platform_SDL.cpp ) +if (APPLE) + list(APPEND SOURCES ${CMAKE_SOURCE_DIR}/src/main/support_apple.mm) +endif() + target_include_directories(Zelda64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/lib/concurrentqueue @@ -243,6 +277,114 @@ if (WIN32) target_sources(Zelda64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/icons/app.rc) endif() +if (APPLE) + find_package(SDL2 REQUIRED) + target_include_directories(Zelda64Recompiled PRIVATE ${SDL2_INCLUDE_DIRS}) + + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + set(THREADS_PREFER_PTHREAD_FLAG TRUE) + find_package(Threads REQUIRED) + + target_link_libraries(Zelda64Recompiled PRIVATE ${CMAKE_DL_LIBS} Threads::Threads) + + # Set bundle properties + set_target_properties(Zelda64Recompiled PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_BUNDLE_NAME "Zelda64Recompiled" + MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.zelda64recompiled" + MACOSX_BUNDLE_BUNDLE_VERSION "1.0" + MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0" + MACOSX_BUNDLE_ICON_FILE "AppIcon.icns" + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_BINARY_DIR}/Info.plist + ) + + set(ICON_SOURCE ${CMAKE_SOURCE_DIR}/icons/512.png) + set(ICONSET_DIR ${CMAKE_BINARY_DIR}/AppIcon.iconset) + set(ICNS_FILE ${CMAKE_BINARY_DIR}/resources/AppIcon.icns) + + # Create iconset directory and add PNG file + add_custom_command( + OUTPUT ${ICONSET_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${ICONSET_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${ICON_SOURCE} ${ICONSET_DIR}/icon_512x512.png + COMMAND ${CMAKE_COMMAND} -E copy ${ICON_SOURCE} ${ICONSET_DIR}/icon_512x512@2x.png + COMMAND touch ${ICONSET_DIR} + COMMENT "Creating iconset directory and copying PNG file" + ) + + # Convert iconset to icns + add_custom_command( + OUTPUT ${ICNS_FILE} + DEPENDS ${ICONSET_DIR} + COMMAND iconutil -c icns ${ICONSET_DIR} -o ${ICNS_FILE} + COMMENT "Converting iconset to icns" + ) + + # Custom target to ensure icns creation + add_custom_target(create_icns ALL DEPENDS ${ICNS_FILE}) + + # Set source file properties for the resulting icns file + set_source_files_properties(${ICNS_FILE} PROPERTIES + MACOSX_PACKAGE_LOCATION "Resources" + ) + + # Add the icns file to the executable target + target_sources(Zelda64Recompiled PRIVATE ${ICNS_FILE}) + + # Ensure Zelda64Recompiled depends on create_icns + add_dependencies(Zelda64Recompiled create_icns) + + # Configure Info.plist + configure_file(${CMAKE_SOURCE_DIR}/.github/macos/Info.plist.in ${CMAKE_BINARY_DIR}/Info.plist @ONLY) + + # Install the app bundle + install(TARGETS Zelda64Recompiled BUNDLE DESTINATION .) + + # Copy required frameworks to bundle + target_link_libraries(Zelda64Recompiled PRIVATE ${MOLTENVK_PATH} ${VULKAN_LOADER_PATH}) + add_custom_command(TARGET Zelda64Recompiled POST_BUILD + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/.github/macos/fixup_bundle.cmake + ) + + # Copy assets folder to the MacOS folder of the app bundle + set(TEMP_ASSETS_DIR "${CMAKE_BINARY_DIR}/temp_assets") + add_custom_command(TARGET Zelda64Recompiled POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets ${TEMP_ASSETS_DIR} + COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEMP_ASSETS_DIR}/scss + COMMAND ${CMAKE_COMMAND} -E copy_directory ${TEMP_ASSETS_DIR} $/Contents/Resources/assets + COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEMP_ASSETS_DIR} + ) + + # Copy ICD files to macOS Resources folder + add_custom_command(TARGET Zelda64Recompiled POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory $/Contents/Resources/vulkan/icd.d + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/.github/macOS/MoltenVK_icd.json $/Contents/Resources/vulkan/icd.d/ + ) + + # Copy controller db file to macOS Resources folder + add_custom_command(TARGET Zelda64Recompiled POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt $/Contents/Resources/ + ) + + # Use install_name_tool to set the RPATH after the build + add_custom_command(TARGET Zelda64Recompiled POST_BUILD + COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks/" $/Contents/MacOS/Zelda64Recompiled + ) + + # Apply JIT compilation workaround + add_custom_command(TARGET Zelda64Recompiled POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "Applying JIT compilation workaround" + COMMAND /bin/bash -c "printf '\\x07' | dd of=$ bs=1 seek=160 count=1 conv=notrunc" + VERBATIM + ) + + # Code sign the app bundle with ad-hoc identity + add_custom_command(TARGET Zelda64Recompiled POST_BUILD + COMMAND codesign --deep --force --sign - $ + COMMENT "Code signing the app bundle with ad-hoc identity" + ) +endif() + if (CMAKE_SYSTEM_NAME MATCHES "Linux") find_package(SDL2 REQUIRED) find_package(X11 REQUIRED) diff --git a/include/zelda_support.h b/include/zelda_support.h new file mode 100644 index 0000000..42f74c2 --- /dev/null +++ b/include/zelda_support.h @@ -0,0 +1,14 @@ +#ifndef __ZELDA_SUPPORT_H__ +#define __ZELDA_SUPPORT_H__ + +#include + +namespace zelda64 { + void dispatch_on_main_thread(std::function func); + +#ifdef __APPLE__ + const char* get_bundle_resource_directory(); +#endif +} + +#endif diff --git a/lib/rt64 b/lib/rt64 index 88c618c..c648ada 160000 --- a/lib/rt64 +++ b/lib/rt64 @@ -1 +1 @@ -Subproject commit 88c618c1f8d8089f94e78537e49e0d77798fc0be +Subproject commit c648adae38ed8d0342c02ccf3a6063e99d746752 diff --git a/src/game/config.cpp b/src/game/config.cpp index ab24e0a..2a1fdd4 100644 --- a/src/game/config.cpp +++ b/src/game/config.cpp @@ -13,6 +13,8 @@ #elif defined(__linux__) #include #include +#elif defined(__APPLE__) +#include "common/rt64_apple.h" #endif constexpr std::u8string_view general_filename = u8"general.json"; @@ -145,8 +147,8 @@ std::filesystem::path zelda64::get_app_folder_path() { } CoTaskMemFree(known_path); -#elif defined(__linux__) - // check for APP_FOLDER_PATH env var used by AppImage +#elif defined(__linux__) || defined(__APPLE__) + // check for APP_FOLDER_PATH env var if (getenv("APP_FOLDER_PATH") != nullptr) { return std::filesystem::path{getenv("APP_FOLDER_PATH")}; } @@ -154,7 +156,11 @@ std::filesystem::path zelda64::get_app_folder_path() { const char *homedir; if ((homedir = getenv("HOME")) == nullptr) { + #if defined(__linux__) homedir = getpwuid(getuid())->pw_dir; + #elif defined(__APPLE__) + homedir = GetHomeDirectory(); + #endif } if (homedir != nullptr) { diff --git a/src/main/main.cpp b/src/main/main.cpp index cbeb63a..de6f550 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -25,6 +25,7 @@ #include "zelda_config.h" #include "zelda_sound.h" #include "zelda_render.h" +#include "zelda_support.h" #include "ovl_patches.hpp" #include "librecomp/game.hpp" #include "librecomp/mods.hpp" @@ -44,7 +45,7 @@ void exit_error(const char* str, Ts ...args) { // TODO pop up an error ((void)fprintf(stderr, str, args), ...); assert(false); - std::quick_exit(EXIT_FAILURE); + ULTRAMODERN_QUICK_EXIT(); } ultramodern::gfx_callbacks_t::gfx_data_t create_gfx() { @@ -118,7 +119,12 @@ bool SetImageAsIcon(const char* filename, SDL_Window* window) SDL_Window* window; ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t) { - window = SDL_CreateWindow("Zelda 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, SDL_WINDOW_RESIZABLE ); + uint32_t window_flags = SDL_WINDOW_RESIZABLE; +#ifdef __APPLE__ + window_flags |= SDL_WINDOW_METAL; +#endif + + window = SDL_CreateWindow("Zelda 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, window_flags ); #if defined(__linux__) SetImageAsIcon("icons/512.png",window); if (ultramodern::renderer::get_graphics_config().wm_option == ultramodern::renderer::WindowMode::Fullscreen) { // TODO: Remove once RT64 gets native fullscreen support on Linux @@ -146,6 +152,9 @@ ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t:: } return ultramodern::renderer::WindowHandle{ wmInfo.info.x11.display, wmInfo.info.x11.window }; +#elif defined(__APPLE__) + SDL_MetalView view = SDL_Metal_CreateView(window); + return ultramodern::renderer::WindowHandle{ wmInfo.info.cocoa.window, SDL_Metal_GetLayer(view) }; #else static_assert(false && "Unimplemented"); #endif @@ -583,8 +592,13 @@ int main(int argc, char** argv) { SDL_InitSubSystem(SDL_INIT_AUDIO); reset_audio(48000); - // Source controller mappings file - if (SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt") < 0) { +#if defined(__APPLE__) + const char* resource_directory = zelda64::get_bundle_resource_directory(); + std::string mapping_file_path = std::string(resource_directory) + "gamecontrollerdb.txt"; +#else + std::string mapping_file_path = "gamecontrollerdb.txt"; +#endif + if (SDL_GameControllerAddMappingsFromFile(mapping_file_path.c_str()) < 0) { fprintf(stderr, "Failed to load controller mappings: %s\n", SDL_GetError()); } diff --git a/src/main/rt64_render_context.cpp b/src/main/rt64_render_context.cpp index 8d5c735..016cddf 100644 --- a/src/main/rt64_render_context.cpp +++ b/src/main/rt64_render_context.cpp @@ -207,7 +207,7 @@ zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::rendere appCore.window.window = window_handle.window; #elif defined(__APPLE__) appCore.window.window = window_handle.window; - appCore.window.view = window_handle.view; + appCore.window.layer = window_handle.view; #endif appCore.checkInterrupts = dummy_check_interrupts; diff --git a/src/main/support.cpp b/src/main/support.cpp new file mode 100644 index 0000000..4c7631d --- /dev/null +++ b/src/main/support.cpp @@ -0,0 +1,10 @@ +#ifndef __APPLE__ + +#include "zelda_support.h" +#include + +void zelda64::dispatch_on_main_thread(std::function func) { + func(); +} + +#endif \ No newline at end of file diff --git a/src/main/support_apple.mm b/src/main/support_apple.mm new file mode 100644 index 0000000..ceba22c --- /dev/null +++ b/src/main/support_apple.mm @@ -0,0 +1,13 @@ +#include "zelda_support.h" +#import + +void zelda64::dispatch_on_main_thread(std::function func) { + dispatch_async(dispatch_get_main_queue(), ^{ + func(); + }); +} + +const char* zelda64::get_bundle_resource_directory() { + NSString *bundlePath = [[NSBundle mainBundle] resourcePath]; + return strdup([bundlePath UTF8String]); +} diff --git a/src/ui/ui_config.cpp b/src/ui/ui_config.cpp index 12bb20e..18158cd 100644 --- a/src/ui/ui_config.cpp +++ b/src/ui/ui_config.cpp @@ -2,6 +2,7 @@ #include "recomp_input.h" #include "zelda_sound.h" #include "zelda_config.h" +#include "zelda_support.h" #include "zelda_debug.h" #include "zelda_render.h" #include "promptfont.h" @@ -519,6 +520,11 @@ public: } Rml::ElementDocument* load_document(Rml::Context* context) override { +#if defined(__APPLE__) + const Rml::String asset = "/assets/config_menu.rml"; + return context->LoadDocument(zelda64::get_bundle_resource_directory() + asset); +#endif + return context->LoadDocument("assets/config_menu.rml"); } void register_events(recompui::UiEventListenerInstancer& listener) override { @@ -725,7 +731,7 @@ public: throw std::runtime_error("Failed to make RmlUi data model for the controls config menu"); } - constructor.BindFunc("input_count", [](Rml::Variant& out) { out = recomp::get_num_inputs(); } ); + constructor.BindFunc("input_count", [](Rml::Variant& out) { out = static_cast(recomp::get_num_inputs()); } ); constructor.BindFunc("input_device_is_keyboard", [](Rml::Variant& out) { out = cur_device == recomp::InputDevice::Keyboard; } ); constructor.RegisterTransformFunc("get_input_name", [](const Rml::VariantList& inputs) { diff --git a/src/ui/ui_launcher.cpp b/src/ui/ui_launcher.cpp index f699eb7..c876674 100644 --- a/src/ui/ui_launcher.cpp +++ b/src/ui/ui_launcher.cpp @@ -1,5 +1,6 @@ #include "recomp_ui.h" #include "zelda_config.h" +#include "zelda_support.h" #include "librecomp/game.hpp" #include "ultramodern/ultramodern.hpp" #include "RmlUi/Core.h" @@ -15,41 +16,43 @@ extern std::vector supported_games; void select_rom() { nfdnchar_t* native_path = nullptr; - nfdresult_t result = NFD_OpenDialogN(&native_path, nullptr, 0, nullptr); + zelda64::dispatch_on_main_thread([&native_path] { + nfdresult_t result = NFD_OpenDialogN(&native_path, nullptr, 0, nullptr); - if (result == NFD_OKAY) { - std::filesystem::path path{native_path}; + if (result == NFD_OKAY) { + std::filesystem::path path{native_path}; - NFD_FreePathN(native_path); - native_path = nullptr; + NFD_FreePathN(native_path); + native_path = nullptr; - recomp::RomValidationError rom_error = recomp::select_rom(path, supported_games[0].game_id); - switch (rom_error) { - case recomp::RomValidationError::Good: - mm_rom_valid = true; - model_handle.DirtyVariable("mm_rom_valid"); - break; - case recomp::RomValidationError::FailedToOpen: - recompui::message_box("Failed to open ROM file."); - break; - case recomp::RomValidationError::NotARom: - recompui::message_box("This is not a valid ROM file."); - break; - case recomp::RomValidationError::IncorrectRom: - recompui::message_box("This ROM is not the correct game."); - break; - case recomp::RomValidationError::NotYet: - recompui::message_box("This game isn't supported yet."); - break; - case recomp::RomValidationError::IncorrectVersion: - recompui::message_box( - "This ROM is the correct game, but the wrong version.\nThis project requires the NTSC-U N64 version of the game."); - break; - case recomp::RomValidationError::OtherError: - recompui::message_box("An unknown error has occurred."); - break; + recomp::RomValidationError rom_error = recomp::select_rom(path, supported_games[0].game_id); + switch (rom_error) { + case recomp::RomValidationError::Good: + mm_rom_valid = true; + model_handle.DirtyVariable("mm_rom_valid"); + break; + case recomp::RomValidationError::FailedToOpen: + recompui::message_box("Failed to open ROM file."); + break; + case recomp::RomValidationError::NotARom: + recompui::message_box("This is not a valid ROM file."); + break; + case recomp::RomValidationError::IncorrectRom: + recompui::message_box("This ROM is not the correct game."); + break; + case recomp::RomValidationError::NotYet: + recompui::message_box("This game isn't supported yet."); + break; + case recomp::RomValidationError::IncorrectVersion: + recompui::message_box( + "This ROM is the correct game, but the wrong version.\nThis project requires the NTSC-U N64 version of the game."); + break; + case recomp::RomValidationError::OtherError: + recompui::message_box("An unknown error has occurred."); + break; + } } - } + }); } class LauncherMenu : public recompui::MenuController { @@ -61,6 +64,11 @@ public: } Rml::ElementDocument* load_document(Rml::Context* context) override { +#if defined(__APPLE__) + const Rml::String asset = "/assets/launcher.rml"; + return context->LoadDocument(zelda64::get_bundle_resource_directory() + asset); +#endif + return context->LoadDocument("assets/launcher.rml"); } void register_events(recompui::UiEventListenerInstancer& listener) override { diff --git a/src/ui/ui_renderer.cpp b/src/ui/ui_renderer.cpp index a234d7c..a93d7ad 100644 --- a/src/ui/ui_renderer.cpp +++ b/src/ui/ui_renderer.cpp @@ -16,6 +16,7 @@ #include "librecomp/game.hpp" #include "zelda_config.h" #include "ui_rml_hacks.hpp" +#include "zelda_support.h" #include "concurrentqueue.h" @@ -1144,8 +1145,6 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) { Rml::Debugger::Initialise(ui_context->rml.context); { - const Rml::String directory = "assets/"; - struct FontFace { const char* filename; bool fallback_face; @@ -1162,7 +1161,13 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) { }; for (const FontFace& face : font_faces) { + #if defined(__APPLE__) + const Rml::String directory = "/assets/"; + Rml::LoadFontFace(zelda64::get_bundle_resource_directory() + directory + face.filename, face.fallback_face); + #else + const Rml::String directory = "assets/"; Rml::LoadFontFace(directory + face.filename, face.fallback_face); + #endif } } @@ -1506,6 +1511,9 @@ recompui::Menu recompui::get_current_menu() { } void recompui::message_box(const char* msg) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, zelda64::program_name.data(), msg, nullptr); + std::string message(msg); + zelda64::dispatch_on_main_thread([message] { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, zelda64::program_name.data(), message.c_str(), nullptr); + }); printf("[ERROR] %s\n", msg); } From ac7ee4f13ee69b291c72d3c468a737b5bbbff8ac Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 4 Nov 2024 13:55:26 +0100 Subject: [PATCH 28/30] Improve post steps on macOS --- CMakeLists.txt | 58 ++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3de60a9..7952336 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -342,46 +342,40 @@ if (APPLE) # Copy required frameworks to bundle target_link_libraries(Zelda64Recompiled PRIVATE ${MOLTENVK_PATH} ${VULKAN_LOADER_PATH}) - add_custom_command(TARGET Zelda64Recompiled POST_BUILD - COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/.github/macos/fixup_bundle.cmake - ) - # Copy assets folder to the MacOS folder of the app bundle - set(TEMP_ASSETS_DIR "${CMAKE_BINARY_DIR}/temp_assets") + # Post-build steps for macOS bundle add_custom_command(TARGET Zelda64Recompiled POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets ${TEMP_ASSETS_DIR} - COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEMP_ASSETS_DIR}/scss - COMMAND ${CMAKE_COMMAND} -E copy_directory ${TEMP_ASSETS_DIR} $/Contents/Resources/assets - COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEMP_ASSETS_DIR} - ) + # 1. Copy and fix frameworks first + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/.github/macos/fixup_bundle.cmake - # Copy ICD files to macOS Resources folder - add_custom_command(TARGET Zelda64Recompiled POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory $/Contents/Resources/vulkan/icd.d - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/.github/macOS/MoltenVK_icd.json $/Contents/Resources/vulkan/icd.d/ - ) + # 2. Copy all resources + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets ${CMAKE_BINARY_DIR}/temp_assets + COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/temp_assets/scss + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/temp_assets $/Contents/Resources/assets + COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/temp_assets - # Copy controller db file to macOS Resources folder - add_custom_command(TARGET Zelda64Recompiled POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt $/Contents/Resources/ - ) + # 3. Copy Vulkan ICD files + COMMAND ${CMAKE_COMMAND} -E make_directory $/Contents/Resources/vulkan/icd.d + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/.github/macOS/MoltenVK_icd.json $/Contents/Resources/vulkan/icd.d/ - # Use install_name_tool to set the RPATH after the build - add_custom_command(TARGET Zelda64Recompiled POST_BUILD - COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks/" $/Contents/MacOS/Zelda64Recompiled - ) + # 4. Copy controller database + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt $/Contents/Resources/ - # Apply JIT compilation workaround - add_custom_command(TARGET Zelda64Recompiled POST_BUILD + # 5. Set RPATH + COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks/" $/Contents/MacOS/Zelda64Recompiled + + # 6. Apply JIT workaround COMMAND ${CMAKE_COMMAND} -E echo "Applying JIT compilation workaround" COMMAND /bin/bash -c "printf '\\x07' | dd of=$ bs=1 seek=160 count=1 conv=notrunc" - VERBATIM - ) - # Code sign the app bundle with ad-hoc identity - add_custom_command(TARGET Zelda64Recompiled POST_BUILD - COMMAND codesign --deep --force --sign - $ - COMMENT "Code signing the app bundle with ad-hoc identity" + # 7. Sign frameworks first + COMMAND codesign --force --sign - $/Contents/Frameworks/* + + # 8. Finally sign the whole bundle with runtime option + COMMAND codesign --deep --force --sign - --options runtime $ + + COMMENT "Performing post-build steps for macOS bundle" + VERBATIM ) endif() @@ -428,7 +422,7 @@ endif() target_link_libraries(Zelda64Recompiled PRIVATE PatchesLib RecompiledFuncs - SDL2 + SDL2::SDL2 librecomp ultramodern rt64 From 7b9091428c5d4f96c06b5fa53de5faa8d09f25a8 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 4 Nov 2024 14:26:06 +0100 Subject: [PATCH 29/30] Fixup builds --- CMakeLists.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7952336..f21bdcc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,7 @@ if (WIN32) ) target_sources(Zelda64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/icons/app.rc) + target_link_libraries(Zelda64Recompiled PRIVATE SDL2) endif() if (APPLE) @@ -285,7 +286,7 @@ if (APPLE) set(THREADS_PREFER_PTHREAD_FLAG TRUE) find_package(Threads REQUIRED) - target_link_libraries(Zelda64Recompiled PRIVATE ${CMAKE_DL_LIBS} Threads::Threads) + target_link_libraries(Zelda64Recompiled PRIVATE ${CMAKE_DL_LIBS} Threads::Threads SDL2::SDL2) # Set bundle properties set_target_properties(Zelda64Recompiled PROPERTIES @@ -345,33 +346,33 @@ if (APPLE) # Post-build steps for macOS bundle add_custom_command(TARGET Zelda64Recompiled POST_BUILD - # 1. Copy and fix frameworks first + # Copy and fix frameworks first COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/.github/macos/fixup_bundle.cmake - # 2. Copy all resources + # Copy all resources COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets ${CMAKE_BINARY_DIR}/temp_assets COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/temp_assets/scss COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/temp_assets $/Contents/Resources/assets COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/temp_assets - # 3. Copy Vulkan ICD files + # Copy Vulkan ICD files COMMAND ${CMAKE_COMMAND} -E make_directory $/Contents/Resources/vulkan/icd.d COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/.github/macOS/MoltenVK_icd.json $/Contents/Resources/vulkan/icd.d/ - # 4. Copy controller database + # Copy controller database COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt $/Contents/Resources/ - # 5. Set RPATH + # Set RPATH COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks/" $/Contents/MacOS/Zelda64Recompiled - # 6. Apply JIT workaround + # Apply JIT workaround COMMAND ${CMAKE_COMMAND} -E echo "Applying JIT compilation workaround" COMMAND /bin/bash -c "printf '\\x07' | dd of=$ bs=1 seek=160 count=1 conv=notrunc" - # 7. Sign frameworks first - COMMAND codesign --force --sign - $/Contents/Frameworks/* + # Sign frameworks first + COMMAND /bin/bash -c "for f in $/Contents/Frameworks/*; do codesign --force --sign - \"$f\"; done" - # 8. Finally sign the whole bundle with runtime option + # Finally sign the whole bundle with runtime option COMMAND codesign --deep --force --sign - --options runtime $ COMMENT "Performing post-build steps for macOS bundle" @@ -410,7 +411,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux") message(STATUS "FREETYPE_LIBRARIES = ${FREETYPE_LIBRARIES}") include_directories(${FREETYPE_LIBRARIES}) - target_link_libraries(Zelda64Recompiled PRIVATE ${FREETYPE_LIBRARIES}) + target_link_libraries(Zelda64Recompiled PRIVATE ${FREETYPE_LIBRARIES} SDL2::SDL2) set(CMAKE_THREAD_PREFER_PTHREAD TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE) @@ -422,7 +423,6 @@ endif() target_link_libraries(Zelda64Recompiled PRIVATE PatchesLib RecompiledFuncs - SDL2::SDL2 librecomp ultramodern rt64 From 5ecdc93f8eab90fb95fc5012b331a0ea5fa470f3 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 4 Nov 2024 15:52:15 +0100 Subject: [PATCH 30/30] Enable JIT entitlement --- .github/macos/entitlements.plist | 12 ++++++++++++ CMakeLists.txt | 15 ++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 .github/macos/entitlements.plist diff --git a/.github/macos/entitlements.plist b/.github/macos/entitlements.plist new file mode 100644 index 0000000..46f6756 --- /dev/null +++ b/.github/macos/entitlements.plist @@ -0,0 +1,12 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + + diff --git a/CMakeLists.txt b/CMakeLists.txt index f21bdcc..bf0903e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,6 +344,14 @@ if (APPLE) # Copy required frameworks to bundle target_link_libraries(Zelda64Recompiled PRIVATE ${MOLTENVK_PATH} ${VULKAN_LOADER_PATH}) + # Define the path to the entitlements file + set(ENTITLEMENTS_FILE ${CMAKE_SOURCE_DIR}/.github/macos/entitlements.plist) + + # Ensure the entitlements file exists + if(NOT EXISTS ${ENTITLEMENTS_FILE}) + message(FATAL_ERROR "Entitlements file not found at ${ENTITLEMENTS_FILE}") + endif() + # Post-build steps for macOS bundle add_custom_command(TARGET Zelda64Recompiled POST_BUILD # Copy and fix frameworks first @@ -369,11 +377,8 @@ if (APPLE) COMMAND ${CMAKE_COMMAND} -E echo "Applying JIT compilation workaround" COMMAND /bin/bash -c "printf '\\x07' | dd of=$ bs=1 seek=160 count=1 conv=notrunc" - # Sign frameworks first - COMMAND /bin/bash -c "for f in $/Contents/Frameworks/*; do codesign --force --sign - \"$f\"; done" - - # Finally sign the whole bundle with runtime option - COMMAND codesign --deep --force --sign - --options runtime $ + # Finally sign the whole bundle with runtime option and entitlements + COMMAND codesign --deep --force --sign - --entitlements ${ENTITLEMENTS_FILE} $ COMMENT "Performing post-build steps for macOS bundle" VERBATIM