From 4311f91c37350457c647bb37b6c3aff0e9755579 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 5 Apr 2026 22:45:46 +0200 Subject: [PATCH 01/16] Add "lock aspect ratio" setting Uses new Aurora functionality: https://github.com/encounter/aurora/pull/97 Fixes #138 --- include/dusk/dusk.h | 8 ++++++++ include/dusk/settings.h | 1 + src/dusk/imgui/ImGuiMenuGame.cpp | 12 +++++++++++- src/dusk/settings.cpp | 1 + src/m_Do/m_Do_main.cpp | 4 ++-- 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/dusk/dusk.h b/include/dusk/dusk.h index d84e496827..744b0f9152 100644 --- a/include/dusk/dusk.h +++ b/include/dusk/dusk.h @@ -5,4 +5,12 @@ extern AuroraInfo auroraInfo; +constexpr u32 defaultWindowWidth = 608; +constexpr u32 defaultWindowHeight = 448; + +constexpr u32 defaultAspectRatioW = 19; +constexpr u32 defaultAspectRatioH = 14; + +static_assert(defaultWindowWidth / defaultAspectRatioW == defaultWindowHeight / defaultAspectRatioH); + #endif // DUSK_DUSK_H diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 07047fad99..b4ded9f83e 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -11,6 +11,7 @@ struct UserSettings { struct { // Video bool enableFullscreen; + bool lockAspectRatio; } video; struct { diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index cd1d5a06f3..b1849a6856 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -6,8 +6,9 @@ #include #include "JSystem/JUtility/JUTGamePad.h" -#include "dusk/audio/DuskDsp.hpp" #include "dusk/audio/DuskAudioSystem.h" +#include "dusk/audio/DuskDsp.hpp" +#include "dusk/dusk.h" #include "dusk/hotkeys.h" #include "dusk/settings.h" #include "m_Do/m_Do_controller_pad.h" @@ -29,6 +30,15 @@ namespace dusk { VISetWindowFullscreen(getSettings().video.enableFullscreen); } + bool& lockAspect = getSettings().video.lockAspectRatio; + if (ImGui::Checkbox("Lock Aspect Ratio", &lockAspect)) { + if (lockAspect) { + VILockAspectRatio(defaultAspectRatioW, defaultAspectRatioH); + } else { + VIUnlockAspectRatio(); + } + } + ImGui::EndMenu(); } diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 38fa455e47..5625b284e3 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -8,6 +8,7 @@ UserSettings g_userSettings = { // Video .video = { .enableFullscreen = false, + .lockAspectRatio = false, }, // Audio diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index b8eeba892e..84c50a5f52 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -269,8 +269,8 @@ int game_main(int argc, char* argv[]) { config.appName = "Dusk"; config.windowPosX = -1; config.windowPosY = -1; - config.windowWidth = 608 * 2; - config.windowHeight = 448 * 2; + config.windowWidth = defaultWindowWidth * 2; + config.windowHeight = defaultWindowHeight * 2; config.desiredBackend = ParseAuroraBackend(parsed_arg_options["backend"].as()); config.logCallback = &aurora_log_callback; config.logLevel = (AuroraLogLevel)parsed_arg_options["log-level"].as(); From ebc708d968e83687f5922354561d74bf236757dd Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Tue, 7 Apr 2026 19:54:51 +0200 Subject: [PATCH 02/16] Hide mouse cursor when imgui hidden Fixes https://github.com/TwilitRealm/dusk/issues/224 --- src/dusk/imgui/ImGuiConsole.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index fe5d3e7c9a..d27d033034 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -13,6 +13,7 @@ #include "ImGuiConsole.hpp" #include "JSystem/JUtility/JUTGamePad.h" +#include "SDL3/SDL_mouse.h" #include "dusk/config.hpp" #include "dusk/settings.h" @@ -198,9 +199,14 @@ namespace dusk { if (CheckMenuViewToggle(ImGuiKey_F1, m_isHidden)) { ShowToasts(); + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; + SDL_HideCursor(); return; } + ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; + // Imgui will re-show cursor. + // TODO: we need to be able to render the menu bar & any overlays separately // The code currently ties them all together, so hiding the menu hides all windows From ee5ab7978e2b5420acbf97a7c5be6729c5e1e4d9 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 7 Apr 2026 13:56:23 -0400 Subject: [PATCH 03/16] Fixes audio volume at boot if changed (#270) * Fixes audio volume at boot if changed * Update Settings refactor for settings we want to update in the future * remove unused include --------- Co-authored-by: MelonSpeedruns --- include/dusk/audio/DuskAudioSystem.h | 2 ++ src/dusk/audio/DuskAudioSystem.cpp | 6 ++++++ src/dusk/imgui/ImGuiConsole.cpp | 9 ++++++++- src/dusk/imgui/ImGuiConsole.hpp | 1 + src/dusk/imgui/ImGuiMenuGame.cpp | 3 --- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/include/dusk/audio/DuskAudioSystem.h b/include/dusk/audio/DuskAudioSystem.h index 1b31db6e7c..724776f5f1 100644 --- a/include/dusk/audio/DuskAudioSystem.h +++ b/include/dusk/audio/DuskAudioSystem.h @@ -8,6 +8,8 @@ namespace dusk::audio { */ void Initialize(); + void SetEnableReverb(bool value); + void SetMasterVolume(f32 value); u32 GetResetCount(int channelIdx); diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index c7313749c2..7b464e4f0e 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -76,6 +76,12 @@ void dusk::audio::SetMasterVolume(const f32 value) { MasterVolume = value; } +void dusk::audio::SetEnableReverb(const bool value) { + JASCriticalSection section; + + EnableReverb = value; +} + void SDLCALL GetNewAudio( void*, SDL_AudioStream*, diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index fe5d3e7c9a..4329849800 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -15,6 +15,7 @@ #include "JSystem/JUtility/JUTGamePad.h" #include "dusk/config.hpp" #include "dusk/settings.h" +#include "dusk/audio/DuskAudioSystem.h" #if _WIN32 #define NOMINMAX @@ -178,13 +179,19 @@ namespace dusk { ImGuiConsole::ImGuiConsole() {} + void ImGuiConsole::UpdateSettings() { + dusk::audio::SetMasterVolume(dusk::getSettings().audio.masterVolume / 100.0f); + dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb); + getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind && ImGui::IsKeyDown(ImGuiKey_Tab); + } + void ImGuiConsole::PreDraw() { if (!m_isLaunchInitialized) { m_toasts.emplace_back("Press F1 to toggle menu"s, 5.f); m_isLaunchInitialized = true; } - getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind && ImGui::IsKeyDown(ImGuiKey_Tab); + UpdateSettings(); if ((ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) && ImGui::IsKeyPressed(ImGuiKey_R)) diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index 27032ed39b..2ab61a0ffb 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -14,6 +14,7 @@ namespace dusk { class ImGuiConsole { public: ImGuiConsole(); + void UpdateSettings(); void PreDraw(); void PostDraw(); diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index f200f734d4..1608209bac 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -78,9 +78,6 @@ namespace dusk { } */ - audio::SetMasterVolume(getSettings().audio.masterVolume / 100.0f); - audio::EnableReverb = getSettings().audio.enableReverb; - ImGui::EndMenu(); } From 63ade15e2cc3e4090727c0ff23c3dae3676ee7fc Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Tue, 7 Apr 2026 20:06:29 +0200 Subject: [PATCH 04/16] Cleanly shut down JKRDecomp thread Properly join the thread to ensure it's shut down before we try to exit. Fixes #268 --- include/m_Do/m_Do_machine.h | 3 +++ .../include/JSystem/JFramework/JFWSystem.h | 3 +++ .../JSystem/include/JSystem/JKernel/JKRAram.h | 3 +++ .../include/JSystem/JKernel/JKRDecomp.h | 3 +++ libs/JSystem/src/JFramework/JFWSystem.cpp | 6 ++++++ libs/JSystem/src/JKernel/JKRAram.cpp | 6 ++++++ libs/JSystem/src/JKernel/JKRDecomp.cpp | 21 +++++++++++++------ src/m_Do/m_Do_machine.cpp | 6 ++++++ src/m_Do/m_Do_main.cpp | 2 ++ 9 files changed, 47 insertions(+), 6 deletions(-) diff --git a/include/m_Do/m_Do_machine.h b/include/m_Do/m_Do_machine.h index 53c47c07a7..eeea7e77ce 100644 --- a/include/m_Do/m_Do_machine.h +++ b/include/m_Do/m_Do_machine.h @@ -15,6 +15,9 @@ void my_SysPrintHeap(char const*, void*, u32); void mDoMch_HeapCheckAll(); void mDoMch_HeapFreeFillAll(); int mDoMch_Create(); +#if TARGET_PC +void mDoMch_Destroy(); +#endif extern GXRenderModeObj g_ntscZeldaProg; diff --git a/libs/JSystem/include/JSystem/JFramework/JFWSystem.h b/libs/JSystem/include/JSystem/JFramework/JFWSystem.h index bb4dfa01cd..288b5f03d8 100644 --- a/libs/JSystem/include/JSystem/JFramework/JFWSystem.h +++ b/libs/JSystem/include/JSystem/JFramework/JFWSystem.h @@ -33,6 +33,9 @@ struct JFWSystem { static void firstInit(); static void init(); +#if TARGET_PC + static void shutdown(); +#endif static JUTConsole* getSystemConsole() { return systemConsole; } static JKRExpHeap* getSystemHeap() { return systemHeap; } diff --git a/libs/JSystem/include/JSystem/JKernel/JKRAram.h b/libs/JSystem/include/JSystem/JKernel/JKRAram.h index 9c8ebd88d1..dd3bf54f9c 100644 --- a/libs/JSystem/include/JSystem/JKernel/JKRAram.h +++ b/libs/JSystem/include/JSystem/JKernel/JKRAram.h @@ -39,6 +39,9 @@ public: public: static JKRAram* create(u32, u32, s32, s32, s32); +#if TARGET_PC + static void destroy(); +#endif static void checkOkAddress(u8*, u32, JKRAramBlock*, u32); static void changeGroupIdIfNeed(u8*, int); static JKRAramBlock* mainRamToAram(u8*, u32, u32, JKRExpandSwitch, u32, JKRHeap*, int, u32*); diff --git a/libs/JSystem/include/JSystem/JKernel/JKRDecomp.h b/libs/JSystem/include/JSystem/JKernel/JKRDecomp.h index 9131ecfbaa..2e7ce0ef1c 100644 --- a/libs/JSystem/include/JSystem/JKernel/JKRDecomp.h +++ b/libs/JSystem/include/JSystem/JKernel/JKRDecomp.h @@ -48,6 +48,9 @@ private: public: static JKRDecomp* create(s32); +#if TARGET_PC + static void destroy(); +#endif static JKRDecompCommand* prepareCommand(u8*, u8*, u32, u32, JKRDecompCommand::AsyncCallback); static void sendCommand(JKRDecompCommand*); static bool sync(JKRDecompCommand*, int); diff --git a/libs/JSystem/src/JFramework/JFWSystem.cpp b/libs/JSystem/src/JFramework/JFWSystem.cpp index b4effd547a..d6322f34c9 100644 --- a/libs/JSystem/src/JFramework/JFWSystem.cpp +++ b/libs/JSystem/src/JFramework/JFWSystem.cpp @@ -116,3 +116,9 @@ void JFWSystem::init() { void* buffer = systemHeap->alloc(CSetUpParam::exConsoleBufferSize, 4); JUTException::createConsole(buffer, CSetUpParam::exConsoleBufferSize); } + +#if TARGET_PC +void JFWSystem::shutdown() { + JKRAram::destroy(); +} +#endif diff --git a/libs/JSystem/src/JKernel/JKRAram.cpp b/libs/JSystem/src/JKernel/JKRAram.cpp index 32afe1cef4..2770328afa 100644 --- a/libs/JSystem/src/JKernel/JKRAram.cpp +++ b/libs/JSystem/src/JKernel/JKRAram.cpp @@ -42,6 +42,12 @@ JKRAram* JKRAram::create(u32 aram_audio_buffer_size, u32 aram_audio_graph_size, return sAramObject; } +#if TARGET_PC +void JKRAram::destroy() { + JKRDecomp::destroy(); +} +#endif + OSMessage JKRAram::sMessageBuffer[4] = { NULL, NULL, diff --git a/libs/JSystem/src/JKernel/JKRDecomp.cpp b/libs/JSystem/src/JKernel/JKRDecomp.cpp index 776823531d..a97edea0a3 100644 --- a/libs/JSystem/src/JKernel/JKRDecomp.cpp +++ b/libs/JSystem/src/JKernel/JKRDecomp.cpp @@ -20,6 +20,15 @@ JKRDecomp* JKRDecomp::create(s32 priority) { return sDecompObject; } +#if TARGET_PC +void JKRDecomp::destroy() { + if (sDecompObject) { + OSSendMessage(&sMessageQueue, nullptr, OS_MESSAGE_NOBLOCK); + OSJoinThread(sDecompObject->getThreadRecord(), nullptr); + } +} +#endif + OSMessage JKRDecomp::sMessageBuffer[8] = {0}; OSMessageQueue JKRDecomp::sMessageQueue = {0}; @@ -34,15 +43,15 @@ void* JKRDecomp::run() { OSInitMessageQueue(&sMessageQueue, sMessageBuffer, 8); for (;;) { OSMessage message; -#ifdef TARGET_PC - if (!OSReceiveMessage(&sMessageQueue, &message, OS_MESSAGE_BLOCK)) { - break; - } -#else OSReceiveMessage(&sMessageQueue, &message, OS_MESSAGE_BLOCK); -#endif JKRDecompCommand* command = (JKRDecompCommand*)message; +#if TARGET_PC + if (!command) { + break; + } +#endif + decode(command->mSrcBuffer, command->mDstBuffer, command->mSrcLength, command->mDstLength); if (command->field_0x20 != 0) { diff --git a/src/m_Do/m_Do_machine.cpp b/src/m_Do/m_Do_machine.cpp index fdab588957..6b186a77e6 100644 --- a/src/m_Do/m_Do_machine.cpp +++ b/src/m_Do/m_Do_machine.cpp @@ -1010,3 +1010,9 @@ int mDoMch_Create() { return 1; } + +#if TARGET_PC +void mDoMch_Destroy() { + JFWSystem::shutdown(); +} +#endif diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 9cb7e41764..8742436c4e 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -366,6 +366,8 @@ int game_main(int argc, char* argv[]) { fflush(stdout); fflush(stderr); + mDoMch_Destroy(); + // Notifies all CVs and causes threads to exit OSResetSystem(OS_RESET_SHUTDOWN, 0, 0); From 1a22cc02c07209b5999b24b5674bfd99c7255252 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Tue, 7 Apr 2026 20:12:52 +0200 Subject: [PATCH 05/16] Fix movie running at uncapped FPS Fixes #267 --- src/d/actor/d_a_movie_player.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d/actor/d_a_movie_player.cpp b/src/d/actor/d_a_movie_player.cpp index 4573684b2f..c3074ac877 100644 --- a/src/d/actor/d_a_movie_player.cpp +++ b/src/d/actor/d_a_movie_player.cpp @@ -4473,7 +4473,9 @@ int daMP_c::daMP_c_Get_arg_movieNo() { int daMP_c::daMP_c_Init() { JUT_ASSERT(9469, m_myObj == NULL); +#if !TARGET_PC // We don't properly simulate retrace interval so this doesn't work. mDoGph_gInf_c::setFrameRate(1); +#endif daMP_Fail_alloc = FALSE; int demoNo = daMP_c_Get_arg_demoNo(); From 8aa6ce950c2d6fb2e246a79d4998808b98501cfb Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Tue, 7 Apr 2026 20:36:44 +0200 Subject: [PATCH 06/16] Fix bigger wallet type display in collection screen Fixes https://github.com/TwilitRealm/dusk/issues/223 --- include/d/d_menu_collect.h | 4 ++++ src/d/d_menu_collect.cpp | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/d/d_menu_collect.h b/include/d/d_menu_collect.h index 587fd5c103..55d5373c71 100644 --- a/include/d/d_menu_collect.h +++ b/include/d/d_menu_collect.h @@ -50,7 +50,11 @@ public: void changeShield(); void changeClothe(); void setArrowMaxNum(u8); +#if TARGET_PC + void setWalletSizeNum(u16); +#else void setWalletMaxNum(u16); +#endif void setSmellType(); void setHeartPiece(); void setPohMaxNum(u8); diff --git a/src/d/d_menu_collect.cpp b/src/d/d_menu_collect.cpp index ed5586e866..72a390a0cc 100644 --- a/src/d/d_menu_collect.cpp +++ b/src/d/d_menu_collect.cpp @@ -590,6 +590,15 @@ void dMenu_Collect2D_c::screenSet() { field_0x184[4][2] = 0x196; field_0x184[5][2] = 0x195; field_0x184[6][2] = 0; +#if TARGET_PC // Since we allow changing wallet sizes, do something more robust. + if (dComIfGs_getWalletSize() == WALLET) { + field_0x184[0][3] = 0x199; + } else if (dComIfGs_getWalletSize() == BIG_WALLET) { + field_0x184[0][3] = 0x19a; + } else { + field_0x184[0][3] = 0x19b; + } +#else if (dComIfGs_getRupeeMax() == WALLET_MAX) { field_0x184[0][3] = 0x199; } else if (dComIfGs_getRupeeMax() == BIG_WALLET_MAX) { @@ -597,6 +606,7 @@ void dMenu_Collect2D_c::screenSet() { } else { field_0x184[0][3] = 0x19b; } +#endif if (dComIfGs_getArrowMax() == QUIVER_MAX) { field_0x184[1][3] = 0x1b9; } else if (dComIfGs_getArrowMax() == BIG_QUIVER_MAX) { @@ -757,7 +767,11 @@ void dMenu_Collect2D_c::screenSet() { setItemNameString(mCursorX, mCursorY); cursorPosSet(); setArrowMaxNum(dComIfGs_getArrowMax()); +#if TARGET_PC + setWalletSizeNum(dComIfGs_getWalletSize()); +#else setWalletMaxNum(dComIfGs_getRupeeMax()); +#endif setSmellType(); setHeartPiece(); setPohMaxNum(dComIfGs_getPohSpiritNum()); @@ -1242,6 +1256,27 @@ void dMenu_Collect2D_c::setArrowMaxNum(u8 param_0) { } } +#if TARGET_PC +void dMenu_Collect2D_c::setWalletSizeNum(u16 i_walletSize) { + switch (i_walletSize) { + case WALLET: + mpScreen->search(MULTI_CHAR('item_1_0'))->show(); + mpScreen->search(MULTI_CHAR('item_1_1'))->hide(); + mpScreen->search(MULTI_CHAR('item_1_2'))->hide(); + break; + case BIG_WALLET: + mpScreen->search(MULTI_CHAR('item_1_0'))->hide(); + mpScreen->search(MULTI_CHAR('item_1_1'))->show(); + mpScreen->search(MULTI_CHAR('item_1_2'))->hide(); + break; + case GIANT_WALLET: + mpScreen->search(MULTI_CHAR('item_1_0'))->hide(); + mpScreen->search(MULTI_CHAR('item_1_1'))->hide(); + mpScreen->search(MULTI_CHAR('item_1_2'))->show(); + break; + } +} +#else void dMenu_Collect2D_c::setWalletMaxNum(u16 i_walletSize) { switch (i_walletSize) { case 300: @@ -1261,6 +1296,7 @@ void dMenu_Collect2D_c::setWalletMaxNum(u16 i_walletSize) { break; } } +#endif void dMenu_Collect2D_c::setSmellType() { static const u64 smell_tag[5] = { From 74942f3c76891e8b2081ce6192e11837bc48f154 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Tue, 7 Apr 2026 21:43:38 +0200 Subject: [PATCH 07/16] Fix JUTResFont performance Just store the GXTexObjs in a separately allocated hashmap, so they can be persisted cross-frame. Easy. Fixes #227 --- .../include/JSystem/JUtility/JUTResFont.h | 8 ++++ libs/JSystem/src/JUtility/JUTResFont.cpp | 45 +++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/libs/JSystem/include/JSystem/JUtility/JUTResFont.h b/libs/JSystem/include/JSystem/JUtility/JUTResFont.h index 09d3303da0..8709822d7b 100644 --- a/libs/JSystem/include/JSystem/JUtility/JUTResFont.h +++ b/libs/JSystem/include/JSystem/JUtility/JUTResFont.h @@ -18,6 +18,10 @@ struct BlockHeader { BE(u32) size; }; +#if TARGET_PC +struct GlyphTextures; +#endif + /** * @ingroup jsystem-jutility * @@ -64,7 +68,11 @@ public: // some types uncertain, may need to be fixed /* 0x1C */ int mWidth; /* 0x20 */ int mHeight; +#if TARGET_PC + GlyphTextures* mGlyphTextures; +#else /* 0x24 */ TGXTexObj mTexObj; +#endif /* 0x44 */ int mTexPageIdx; /* 0x48 */ const ResFONT* mResFont; /* 0x4C */ ResFONT::INF1* mInf1Ptr; diff --git a/libs/JSystem/src/JUtility/JUTResFont.cpp b/libs/JSystem/src/JUtility/JUTResFont.cpp index e6410defdc..10443d6404 100644 --- a/libs/JSystem/src/JUtility/JUTResFont.cpp +++ b/libs/JSystem/src/JUtility/JUTResFont.cpp @@ -6,19 +6,39 @@ #include "JSystem/JUtility/JUTAssert.h" #include "JSystem/JUtility/JUTConsole.h" #include +#include "absl/container/node_hash_map.h" + +#if TARGET_PC +struct GlyphTextures { + absl::node_hash_map textures; +}; +#endif JUTResFont::JUTResFont() { +#if TARGET_PC + mGlyphTextures = new GlyphTextures(); +#endif initialize_state(); JUTFont::initialize_state(); } JUTResFont::JUTResFont(const ResFONT* pFont, JKRHeap* pHeap) { +#if TARGET_PC + mGlyphTextures = new GlyphTextures(); +#endif initialize_state(); JUTFont::initialize_state(); initiate(pFont, pHeap); } JUTResFont::~JUTResFont() { +#if TARGET_PC + for (auto& pair : mGlyphTextures->textures) { + pair.second.reset(); + } + delete mGlyphTextures; +#endif + if (mValid) { delete_and_initialize(); JUTFont::initialize_state(); @@ -414,12 +434,30 @@ void JUTResFont::loadImage(int code, GXTexMapID id){ mWidth = cellCol * cellW; mHeight = cellRow * cellH; +#if TARGET_PC + const auto found = mGlyphTextures->textures.find(code); + GXTexObj* texObj; + if (found == mGlyphTextures->textures.end()) { + texObj = &mGlyphTextures->textures[code]; + void* pImg = &mpGlyphBlocks[i]->data[pageIdx * mpGlyphBlocks[i]->textureSize]; + GXInitTexObj(texObj, pImg, mpGlyphBlocks[i]->textureWidth, + mpGlyphBlocks[i]->textureHeight, (GXTexFmt)(u16)mpGlyphBlocks[i]->textureFormat, + GX_CLAMP, GX_CLAMP, 0); + + GXInitTexObjLOD(texObj, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, 0U, 0U, GX_ANISO_1); + } else { + texObj = &found->second; + } + + GXLoadTexObj(texObj, id); + + // Gets used by some other code. + mTexPageIdx = pageIdx; + field_0x66 = i; +#else if (pageIdx != mTexPageIdx || i != field_0x66) { void* pImg = &mpGlyphBlocks[i]->data[pageIdx * mpGlyphBlocks[i]->textureSize]; -#ifdef TARGET_PC - mTexObj.reset(); -#endif GXInitTexObj(&mTexObj, pImg, mpGlyphBlocks[i]->textureWidth, mpGlyphBlocks[i]->textureHeight, (GXTexFmt)(u16)mpGlyphBlocks[i]->textureFormat, GX_CLAMP, GX_CLAMP, 0); @@ -430,6 +468,7 @@ void JUTResFont::loadImage(int code, GXTexMapID id){ } GXLoadTexObj(&mTexObj, id); +#endif } } From 44844122ba77c67868ace2c47e45f0a4bbcbe322 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 7 Apr 2026 16:03:12 -0400 Subject: [PATCH 08/16] Don't play 2 sounds when starting a save with TV Settings off --- src/d/d_file_select.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index f63403d886..1f6318dad1 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -1193,7 +1193,13 @@ void dFile_select_c::menuSelect() { // Handles copy / start / delete actions depending on which menu is selected from menuSelect void dFile_select_c::menuSelectStart() { - mDoAud_seStart(Z2SE_SY_CURSOR_OK, NULL, 0, 0); + #if TARGET_PC + if (!dusk::getSettings().game.hideTvSettingsScreen || mSelectMenuNum != 1) { + mDoAud_seStart(Z2SE_SY_CURSOR_OK, NULL, 0, 0); + } + #else + mDoAud_seStart(Z2SE_SY_CURSOR_OK, NULL, 0, 0); + #endif if (mSelectMenuNum == 1) { dComIfGs_setCardToMemory((u8*)mSaveData, mSelectNum); From ddab2e87f8375c25ab9593fbc30ae1df226e6c34 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 7 Apr 2026 16:04:14 -0400 Subject: [PATCH 09/16] fix line denting --- src/d/d_file_select.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index 1f6318dad1..8d4f98187b 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -1195,8 +1195,8 @@ void dFile_select_c::menuSelect() { void dFile_select_c::menuSelectStart() { #if TARGET_PC if (!dusk::getSettings().game.hideTvSettingsScreen || mSelectMenuNum != 1) { - mDoAud_seStart(Z2SE_SY_CURSOR_OK, NULL, 0, 0); - } + mDoAud_seStart(Z2SE_SY_CURSOR_OK, NULL, 0, 0); + } #else mDoAud_seStart(Z2SE_SY_CURSOR_OK, NULL, 0, 0); #endif From 8f77ca72b1c4fc370e31d23181aca6b3bc6ab33b Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Tue, 7 Apr 2026 22:17:48 +0200 Subject: [PATCH 10/16] Fix Aurora draw call display in performance overlay It was getting reset at the start of the frame... Just cache the values. --- include/dusk/dusk.h | 6 ++++++ src/dusk/imgui/ImGuiMenuTools.cpp | 27 ++++++++++++++------------- src/m_Do/m_Do_main.cpp | 2 ++ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/include/dusk/dusk.h b/include/dusk/dusk.h index 7fc2a23070..832b656160 100644 --- a/include/dusk/dusk.h +++ b/include/dusk/dusk.h @@ -3,7 +3,13 @@ #include +#include "aurora/gfx.h" + extern AuroraInfo auroraInfo; extern const char* configPath; +namespace dusk { + extern AuroraStats lastFrameAuroraStats; +} + #endif // DUSK_DUSK_H diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index 7478adc0e5..4a455672a9 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -10,6 +10,7 @@ #include "d/actor/d_a_alink.h" #include "d/actor/d_a_horse.h" #include "d/d_com_inf_game.h" +#include "dusk/dusk.h" #include "m_Do/m_Do_main.h" namespace dusk { @@ -109,31 +110,31 @@ namespace dusk { } hasPrevious = true; - AuroraStats const* stats = aurora_get_stats(); + const auto& stats = lastFrameAuroraStats; ImGuiStringViewText( - fmt::format(FMT_STRING("Queued pipelines: {}\n"), stats->queuedPipelines)); + fmt::format(FMT_STRING("Queued pipelines: {}\n"), stats.queuedPipelines)); ImGuiStringViewText( - fmt::format(FMT_STRING("Done pipelines: {}\n"), stats->createdPipelines)); + fmt::format(FMT_STRING("Done pipelines: {}\n"), stats.createdPipelines)); ImGuiStringViewText( - fmt::format(FMT_STRING("Draw call count: {}\n"), stats->drawCallCount)); + fmt::format(FMT_STRING("Draw call count: {}\n"), stats.drawCallCount)); ImGuiStringViewText(fmt::format(FMT_STRING("Merged draw calls: {}\n"), - stats->mergedDrawCallCount)); + stats.mergedDrawCallCount)); ImGuiStringViewText(fmt::format(FMT_STRING("Vertex size: {}\n"), - BytesToString(stats->lastVertSize))); + BytesToString(stats.lastVertSize))); ImGuiStringViewText(fmt::format(FMT_STRING("Uniform size: {}\n"), - BytesToString(stats->lastUniformSize))); + BytesToString(stats.lastUniformSize))); ImGuiStringViewText(fmt::format(FMT_STRING("Index size: {}\n"), - BytesToString(stats->lastIndexSize))); + BytesToString(stats.lastIndexSize))); ImGuiStringViewText(fmt::format(FMT_STRING("Storage size: {}\n"), - BytesToString(stats->lastStorageSize))); + BytesToString(stats.lastStorageSize))); ImGuiStringViewText(fmt::format(FMT_STRING("Tex upload size: {}\n"), - BytesToString(stats->lastTextureUploadSize))); + BytesToString(stats.lastTextureUploadSize))); ImGuiStringViewText(fmt::format( FMT_STRING("Total: {}\n"), - BytesToString(stats->lastVertSize + stats->lastUniformSize + - stats->lastIndexSize + stats->lastStorageSize + - stats->lastTextureUploadSize))); + BytesToString(stats.lastVertSize + stats.lastUniformSize + + stats.lastIndexSize + stats.lastStorageSize + + stats.lastTextureUploadSize))); // TODO: persist to config ShowCornerContextMenu(m_debugOverlayCorner, m_cameraOverlayCorner); diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 8742436c4e..e42e6ea98b 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -110,6 +110,7 @@ s32 LOAD_COPYDATE(void*) { } AuroraInfo auroraInfo; +AuroraStats dusk::lastFrameAuroraStats; const char* configPath; void main01(void) { @@ -182,6 +183,7 @@ void main01(void) { VIWaitForRetrace(); #if TARGET_PC + dusk::lastFrameAuroraStats = *aurora_get_stats(); if (!aurora_begin_frame()) { DuskLog.debug("aurora_begin_frame returned false, skipping draw this frame"); continue; From 2b45b01fcc122b91f7be39ed2510bfd4f637a224 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Tue, 7 Apr 2026 22:37:57 +0200 Subject: [PATCH 11/16] Fix minimap "don't render" code not always working due to rounding errors Significant performance boost in some areas like Gerudo Desert when map isn't visible. --- src/d/d_meter_map.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d/d_meter_map.cpp b/src/d/d_meter_map.cpp index 6b78e2242b..b5900a4be5 100644 --- a/src/d/d_meter_map.cpp +++ b/src/d/d_meter_map.cpp @@ -596,7 +596,8 @@ void dMeterMap_c::_draw() { #if TARGET_PC // Optimization: don't draw map if it's off-screen/invisible. // Especially useful in debug builds on Hyrule field etc., it's slow! - if ((!mMapIsInside && mSlidePositionOffset == getDispPosOutSide_OffsetX()) || mMapAlpha == 0) { + // That +3 is an arbitrary bias to avoid rounding issues causing this to fail. + if ((!mMapIsInside && mSlidePositionOffset <= getDispPosOutSide_OffsetX() + 3) || mMapAlpha == 0) { return; } #endif From 581b5046581f814eb2bc392ab572f5a0888c343c Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 7 Apr 2026 18:22:41 -0400 Subject: [PATCH 12/16] rename name of button to "Force 4:3 Aspect Ratio" --- src/dusk/imgui/ImGuiMenuGame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 50877caad9..d9b521d98a 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -54,7 +54,7 @@ namespace dusk { } bool lockAspect = getSettings().video.lockAspectRatio; - if (ImGui::Checkbox("Lock Aspect Ratio", &lockAspect)) { + if (ImGui::Checkbox("Force 4:3 Aspect Ratio", &lockAspect)) { getSettings().video.lockAspectRatio.setValue(lockAspect); if (lockAspect) { From a14cd3da06a683d66586b3bb6bc357f99449be94 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 7 Apr 2026 18:49:24 -0400 Subject: [PATCH 13/16] Move audio settings to init and set rather than update, improving performance --- src/dusk/imgui/ImGuiConfig.hpp | 20 ++++++++++++++++---- src/dusk/imgui/ImGuiConsole.cpp | 5 +++-- src/dusk/imgui/ImGuiMenuGame.cpp | 9 +++++++-- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/dusk/imgui/ImGuiConfig.hpp b/src/dusk/imgui/ImGuiConfig.hpp index fc5aa34bbd..e3e80b9920 100644 --- a/src/dusk/imgui/ImGuiConfig.hpp +++ b/src/dusk/imgui/ImGuiConfig.hpp @@ -5,36 +5,48 @@ #include "imgui.h" namespace dusk::config { - inline void ImGuiCheckbox(const char* title, ConfigVar& var) { + inline bool ImGuiCheckbox(const char* title, ConfigVar& var) { bool copy = var.getValue(); if (ImGui::Checkbox(title, ©)) { var.setValue(copy); Save(); + return true; } + + return false; } - static void ImGuiSliderFloat(const char* label, ConfigVar& var, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0) { + static bool ImGuiSliderFloat(const char* label, ConfigVar& var, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0) { float val = var; if (ImGui::SliderFloat(label, &val, v_min, v_max, format, flags)) { var.setValue(val); Save(); + return true; } + + return false; } - static void ImGuiSliderInt(const char* label, ConfigVar& var, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0) { + static bool ImGuiSliderInt(const char* label, ConfigVar& var, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0) { int val = var; if (ImGui::SliderInt(label, &val, v_min, v_max, format, flags)) { var.setValue(val); Save(); + return true; } + + return false; } - static void ImGuiMenuItem(const char* label, const char* shortcut, ConfigVar& p_selected, bool enabled = true) { + static bool ImGuiMenuItem(const char* label, const char* shortcut, ConfigVar& p_selected, bool enabled = true) { bool copy = p_selected.getValue(); if (ImGui::MenuItem(label, shortcut, ©, enabled)) { p_selected.setValue(copy); Save(); + return true; } + + return false; } } diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index e0ca351e4f..35609f641c 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -186,11 +186,12 @@ namespace dusk { } else { VIUnlockAspectRatio(); } + + dusk::audio::SetMasterVolume(dusk::getSettings().audio.masterVolume / 100.0f); + dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb); } void ImGuiConsole::UpdateSettings() { - dusk::audio::SetMasterVolume(dusk::getSettings().audio.masterVolume / 100.0f); - dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb); getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind && ImGui::IsKeyDown(ImGuiKey_Tab); } diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index d9b521d98a..050e5c5c77 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -71,8 +71,13 @@ namespace dusk { if (ImGui::BeginMenu("Audio")) { ImGui::Text("Master Volume"); - config::ImGuiSliderInt("##masterVolume", getSettings().audio.masterVolume, 0, 100); - config::ImGuiCheckbox("Enable Reverb", getSettings().audio.enableReverb); + if (config::ImGuiSliderInt("##masterVolume", getSettings().audio.masterVolume, 0, 100)) { + dusk::audio::SetMasterVolume(dusk::getSettings().audio.masterVolume / 100.0f); + } + + if (config::ImGuiCheckbox("Enable Reverb", getSettings().audio.enableReverb)) { + dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb); + } /* // TODO: Implement additional settings ImGui::Text("Main Music Volume"); From e8e5b3eb967ec4507319096f8d3b8cca54e2220e Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 7 Apr 2026 18:51:04 -0400 Subject: [PATCH 14/16] remove useless dusk calls --- src/dusk/imgui/ImGuiConsole.cpp | 4 ++-- src/dusk/imgui/ImGuiMenuGame.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 35609f641c..bc86b015d8 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -187,8 +187,8 @@ namespace dusk { VIUnlockAspectRatio(); } - dusk::audio::SetMasterVolume(dusk::getSettings().audio.masterVolume / 100.0f); - dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb); + dusk::audio::SetMasterVolume(getSettings().audio.masterVolume / 100.0f); + dusk::audio::SetEnableReverb(getSettings().audio.enableReverb); } void ImGuiConsole::UpdateSettings() { diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 050e5c5c77..cbe26ba81c 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -72,11 +72,11 @@ namespace dusk { if (ImGui::BeginMenu("Audio")) { ImGui::Text("Master Volume"); if (config::ImGuiSliderInt("##masterVolume", getSettings().audio.masterVolume, 0, 100)) { - dusk::audio::SetMasterVolume(dusk::getSettings().audio.masterVolume / 100.0f); + dusk::audio::SetMasterVolume(getSettings().audio.masterVolume / 100.0f); } if (config::ImGuiCheckbox("Enable Reverb", getSettings().audio.enableReverb)) { - dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb); + dusk::audio::SetEnableReverb(getSettings().audio.enableReverb); } /* // TODO: Implement additional settings From 3cc9ca56582162ffa2df908d5545a6edc8e36f95 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 7 Apr 2026 19:19:28 -0400 Subject: [PATCH 15/16] make the game over screen widescreen --- src/d/d_gameover.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/d/d_gameover.cpp b/src/d/d_gameover.cpp index 693bcf371e..baec970a32 100644 --- a/src/d/d_gameover.cpp +++ b/src/d/d_gameover.cpp @@ -432,7 +432,14 @@ void dDlst_GameOverScrnDraw_c::draw() { img_white.a = 255; mpBackImg->setBlackWhite(img_black, img_white); + + #if TARGET_PC + mpBackImg->draw(mDoGph_gInf_c::getMinXF(), mDoGph_gInf_c::getMinYF(), + mDoGph_gInf_c::getWidthF(), mDoGph_gInf_c::getHeightF(), false, false, + false); + #else mpBackImg->draw(0.0f, 0.0f, FB_WIDTH, FB_HEIGHT, false, false, false); + #endif } else { JUtility::TColor img_black; JUtility::TColor img_white; @@ -447,7 +454,13 @@ void dDlst_GameOverScrnDraw_c::draw() { img_white.b = l_HIO.mWhite.b; img_white.a = l_HIO.mWhite.a; + #if TARGET_PC + mpBackImg->draw(mDoGph_gInf_c::getMinXF(), mDoGph_gInf_c::getMinYF(), + mDoGph_gInf_c::getWidthF(), mDoGph_gInf_c::getHeightF(), false, false, + false); + #else mpBackImg->draw(0.0f, 0.0f, FB_WIDTH, FB_HEIGHT, false, false, false); + #endif static f32 offset[8] = {-138.0f, -96.0f, -56.0f, -18.0f, 42.0f, 75.0f, 110.0f, 143.0f}; From 0d46665f33bc19ce33f93c86e3b0281a19173cdf Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 7 Apr 2026 19:23:41 -0400 Subject: [PATCH 16/16] Also fix buttons placement in the item menu. --- src/d/d_meter_HIO.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/d/d_meter_HIO.cpp b/src/d/d_meter_HIO.cpp index 94b1c79420..dbfe11cf53 100644 --- a/src/d/d_meter_HIO.cpp +++ b/src/d/d_meter_HIO.cpp @@ -2292,6 +2292,7 @@ void dMeter_drawHIO_c::updateOnWide() { g_drawHIO = {}; // this might be a bad idea g_drawHIO.mMainHUDButtonsPosX = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mMainHUDButtonsPosX); + g_drawHIO.mRingHUDButtonsPosX = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mRingHUDButtonsPosX); g_drawHIO.mRupeeKeyPosX = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mRupeeKeyPosX); g_drawHIO.mButtonCrossOFFPosX = mDoGph_gInf_c::ScaleHUDXLeft(g_drawHIO.mButtonCrossOFFPosX); g_drawHIO.mButtonCrossONPosX = mDoGph_gInf_c::ScaleHUDXLeft(g_drawHIO.mButtonCrossONPosX);