From 696e8e6aa9e5ce402c51773180da5647df394914 Mon Sep 17 00:00:00 2001 From: Krutonium Date: Tue, 12 May 2026 17:30:07 -0400 Subject: [PATCH 01/47] Add ability to toggle off Owl Statue Markers on Map --- include/dusk/settings.h | 1 + src/d/d_map.cpp | 7 +++++++ src/d/d_menu_dmap_map.cpp | 7 +++++++ src/d/d_menu_fmap.cpp | 7 +++++++ src/dusk/settings.cpp | 4 +++- src/dusk/ui/settings.cpp | 2 ++ 6 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 366a9cd82b..7710eeceba 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -179,6 +179,7 @@ struct UserSettings { ConfigVar speedrunMode; ConfigVar liveSplitEnabled; ConfigVar recordingMode; + ConfigVar removeQuestMapMarkers; } game; struct { diff --git a/src/d/d_map.cpp b/src/d/d_map.cpp index fa9049fb00..8cecead15a 100644 --- a/src/d/d_map.cpp +++ b/src/d/d_map.cpp @@ -931,6 +931,13 @@ bool renderingAmap_c::isDrawIconSingle2(dTres_c::data_s const* i_data, bool para } break; case 5: +#if TARGET_PC + if (dusk::getSettings().game.removeQuestMapMarkers && + dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[0x190])) + { + break; + } +#endif if (((i_data->mNo == 255 || (i_data->mNo != 255 && !dComIfGs_isTbox(i_data->mNo))) && (i_data->mSwBit == 255 || (i_data->mSwBit != 255 && dComIfGs_isSwitch(i_data->mSwBit, i_data->mRoomNo)))) && diff --git a/src/d/d_menu_dmap_map.cpp b/src/d/d_menu_dmap_map.cpp index 0bb9717952..6320c09850 100644 --- a/src/d/d_menu_dmap_map.cpp +++ b/src/d/d_menu_dmap_map.cpp @@ -69,6 +69,13 @@ bool renderingDmap_c::isDrawIconSingle2(dTres_c::data_s const* i_data, bool para JUT_ASSERT(1044, FALSE); break; case 5: +#if TARGET_PC + if (dusk::getSettings().game.removeQuestMapMarkers && + dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[0x190])) + { + break; + } +#endif if ((i_data->mNo == 0xFF || (i_data->mNo != 0xFF && !dComIfGs_isTbox(i_data->mNo))) && (i_data->mSwBit == 0xFF || (i_data->mSwBit != 0xFF && dComIfGs_isSwitch(i_data->mSwBit, i_data->mRoomNo))) && param_1) { rt = true; } diff --git a/src/d/d_menu_fmap.cpp b/src/d/d_menu_fmap.cpp index d2b06e962c..e9efc9d1a7 100644 --- a/src/d/d_menu_fmap.cpp +++ b/src/d/d_menu_fmap.cpp @@ -2600,6 +2600,13 @@ void dMenu_Fmap_c::drawLightDropIcon() { } void dMenu_Fmap_c::drawBatsumarkIcon() { +#if TARGET_PC + if (dusk::getSettings().game.removeQuestMapMarkers && + dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[0x190])) + { + return; + } +#endif drawIcon(5, 0x12); } diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 888ad2da7f..6e14f01802 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -115,7 +115,8 @@ UserSettings g_userSettings = { // Tools .speedrunMode {"game.speedrunMode", false}, .liveSplitEnabled {"game.liveSplitEnabled", false}, - .recordingMode {"game.recordingMode", false} + .recordingMode {"game.recordingMode", false}, + .removeQuestMapMarkers {"game.removeQuestMapMarkers", false} }, .backend = { @@ -202,6 +203,7 @@ void registerSettings() { Register(g_userSettings.game.speedrunMode); Register(g_userSettings.game.liveSplitEnabled); Register(g_userSettings.game.recordingMode); + Register(g_userSettings.game.removeQuestMapMarkers); Register(g_userSettings.game.fastSpinner); Register(g_userSettings.game.infiniteHearts); Register(g_userSettings.game.infiniteArrows); diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index bd4b305c04..74a7ec35c9 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -955,6 +955,8 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Restores patched glitches from Wii USA 1.0, the first released version."); addOption("Enable Rotating Link Doll", getSettings().game.enableLinkDollRotation, "Enables rotating Link in the collection menu with the C-Stick."); + addOption("Hide Owl Statue Markers", getSettings().game.removeQuestMapMarkers, + "Removes completed Owl Statue markers from the map and Minimap."); leftPane.add_section("Difficulty"); leftPane.register_control( From 18cbb584c62e3c7f993e6ff6625050a6ebce0c0a Mon Sep 17 00:00:00 2001 From: Krutonium <3945538+Krutonium@users.noreply.github.com> Date: Tue, 12 May 2026 18:39:18 -0400 Subject: [PATCH 02/47] Fix the order of entries Based on the build error the CI gave, I think this is the issue. --- src/dusk/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 148cceda0b..bbcc5da5a9 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -116,8 +116,8 @@ UserSettings g_userSettings = { .speedrunMode {"game.speedrunMode", false}, .liveSplitEnabled {"game.liveSplitEnabled", false}, .recordingMode {"game.recordingMode", false}, + .removeQuestMapMarkers {"game.removeQuestMapMarkers", false}, .showSpeedrunRTATimer {"game.showSpeedrunRTATimer", true}, - .removeQuestMapMarkers {"game.removeQuestMapMarkers", false} }, .backend = { From 273af7beb61294bb2b8b643c9f045a0e0066a0c6 Mon Sep 17 00:00:00 2001 From: Krutonium Date: Tue, 12 May 2026 19:02:43 -0400 Subject: [PATCH 03/47] Figured it out, compiles now --- src/dusk/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index bbcc5da5a9..aa91e2e255 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -115,9 +115,9 @@ UserSettings g_userSettings = { // Tools .speedrunMode {"game.speedrunMode", false}, .liveSplitEnabled {"game.liveSplitEnabled", false}, + .showSpeedrunRTATimer {"game.showSpeedrunRTATimer", true}, .recordingMode {"game.recordingMode", false}, .removeQuestMapMarkers {"game.removeQuestMapMarkers", false}, - .showSpeedrunRTATimer {"game.showSpeedrunRTATimer", true}, }, .backend = { From fa660ebd0f119d6f1bcc7137e64ff4b39b678562 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Wed, 20 May 2026 03:19:25 -0700 Subject: [PATCH 04/47] fmap poe counter --- include/d/d_menu_fmap2D.h | 7 +++ include/d/d_menu_map_common.h | 5 ++ include/dusk/settings.h | 1 + src/d/d_menu_fmap.cpp | 10 ++++ src/d/d_menu_fmap2D.cpp | 49 ++++++++++++++++ src/d/d_menu_map_common.cpp | 106 ++++++++++++++++++++++++++++++++++ src/dusk/settings.cpp | 2 + src/dusk/ui/preset.cpp | 1 + src/dusk/ui/settings.cpp | 2 + 9 files changed, 183 insertions(+) diff --git a/include/d/d_menu_fmap2D.h b/include/d/d_menu_fmap2D.h index 7df78bb6d0..aa64129ccf 100644 --- a/include/d/d_menu_fmap2D.h +++ b/include/d/d_menu_fmap2D.h @@ -421,6 +421,13 @@ public: /* 0xC2 */ u8 mAlphaButtonZ; /* 0xC3 */ u8 mAlphaAnalogStick; /* 0xC4 */ u8 mAlphaDpad; + +#if TARGET_PC + J2DTextBox* mpPoeCountPane; + ResTIMG* mpPoeCountIconTex; + J2DPicture* mpPoeCountIcon; + u8 mSelectRegionNo; +#endif }; #endif /* D_MENU_D_MENU_FMAP2D_H */ diff --git a/include/d/d_menu_map_common.h b/include/d/d_menu_map_common.h index de50d775ca..55de3d0f6c 100644 --- a/include/d/d_menu_map_common.h +++ b/include/d/d_menu_map_common.h @@ -76,6 +76,11 @@ public: } #endif +#if TARGET_PC + static void getDmapPoeCount(const std::string& stageName, int& nowCount, int& totalCount); + static void getFmapPoeCount(const int regionNo, int& nowCount, int& totalCount); +#endif + struct Stage_c { // Incomplete class diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 0d85b714a5..1c77ae992b 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -133,6 +133,7 @@ struct UserSettings { ConfigVar instantText; ConfigVar sunsSong; ConfigVar autoSave; + ConfigVar showMapPoeCounter; // Preferences ConfigVar enableMirrorMode; diff --git a/src/d/d_menu_fmap.cpp b/src/d/d_menu_fmap.cpp index 72b97ddbc2..eef18ba91c 100644 --- a/src/d/d_menu_fmap.cpp +++ b/src/d/d_menu_fmap.cpp @@ -525,6 +525,16 @@ void dMenu_Fmap_c::_move() { } mpDraw2DBack->setSpotTextureFadeAlpha(mSpotTextureFadeAlpha); } + +#if TARGET_PC + u8 region = mpDraw2DBack->getSelectRegion(); + if (region != 0xFF && mpDraw2DBack->isShowRegion(region)) { + mpDraw2DTop->mSelectRegionNo = region; + } else { + mpDraw2DTop->mSelectRegionNo = 0xFF; + } + +#endif } void dMenu_Fmap_c::_draw() { diff --git a/src/d/d_menu_fmap2D.cpp b/src/d/d_menu_fmap2D.cpp index a63f6ec282..39ec07e5d7 100644 --- a/src/d/d_menu_fmap2D.cpp +++ b/src/d/d_menu_fmap2D.cpp @@ -2482,6 +2482,18 @@ dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) { set3DStickString(0x524); #endif +#if TARGET_PC + mpPoeCountIcon = JKR_NEW J2DPicture((ResTIMG*)JKRGetNameResource("ni_item_icon_pou.bti", dComIfGp_getItemIconArchive())); + + mpPoeCountPane = JKR_NEW J2DTextBox(); + if (mpPoeCountPane != nullptr) { + mpPoeCountPane->setFontSize(15.0f, 15.0f); + mpPoeCountPane->setFont(mDoExt_getMesgFont()); + } + + mSelectRegionNo = 0xFF; +#endif + setHIO(true); } @@ -2540,6 +2552,14 @@ dMenu_Fmap2DTop_c::~dMenu_Fmap2DTop_c() { } JKR_DELETE(mpAnm); mpAnm = NULL; + +#if TARGET_PC + JKR_DELETE(mpPoeCountIcon); + mpPoeCountIcon = NULL; + + JKR_DELETE(mpPoeCountPane); + mpPoeCountPane = NULL; +#endif } void dMenu_Fmap2DTop_c::_execute() { @@ -2636,6 +2656,35 @@ void dMenu_Fmap2DTop_c::draw() { ctx->scissor(mTransX, 0.0f, FB_WIDTH, FB_HEIGHT); ctx->setScissor(); mpTitleScreen->draw(mTransX, mTransY, ctx); + +#if TARGET_PC + if (dusk::getSettings().game.showMapPoeCounter) { + int nowPoeCount = 0; + int totalPoeCount = 0; + dMenuMapCommon_c::getFmapPoeCount(mSelectRegionNo, nowPoeCount, totalPoeCount); + if (dComIfGs_isEventBit(dSv_event_flag_c::F_0456) && totalPoeCount > 0) { + const f32 x = mTransX + mDoGph_gInf_c::ScaleHUDXRight(485.0f); + const f32 y = 380.0f; + constexpr f32 iconsize = 48.0f * 0.8f; + + if (mpPoeCountIcon != nullptr) + mpPoeCountIcon->draw(x - 35.0f, y - 25.0f, iconsize, iconsize, false, false, false); + + char counter_text[6]; + snprintf(counter_text, sizeof(counter_text), "%d/%d", nowPoeCount, totalPoeCount); + mpPoeCountPane->setString(counter_text); + + mpPoeCountPane->setCharColor(0x000000FF); + mpPoeCountPane->setGradColor(0x000000FF); + mpPoeCountPane->draw(x + 1, y + 1, FB_WIDTH, HBIND_LEFT); + + mpPoeCountPane->setCharColor(0xC8C8C8FF); + mpPoeCountPane->setGradColor(0xC8C8C8FF); + mpPoeCountPane->draw(x, y, FB_WIDTH, HBIND_LEFT); + } + } +#endif + ctx->scissor(scissor_left, scissor_top, scissor_width, scissor_height); ctx->setScissor(); if (mpScrnExplain) { diff --git a/src/d/d_menu_map_common.cpp b/src/d/d_menu_map_common.cpp index 0017975c4e..feed9310ef 100644 --- a/src/d/d_menu_map_common.cpp +++ b/src/d/d_menu_map_common.cpp @@ -747,3 +747,109 @@ void dMenuMapCommon_c::debugIcon() { setIconInfo(ICON_LV8_WARP_e, pos_x, pos_y, 0.0f, scale, 1.0f, 1); } } + +#if TARGET_PC +static constexpr struct { + std::string stagename; + u8 switch_no; + s8 region_id; + u8 save_id; +} l_poeInfo[] = { + // Dungeons + {"D_MN06", 0x19, -1, 0x15}, + {"D_MN06", 0x18, -1, 0x15}, + {"D_MN07", 0x54, -1, 0x16}, + {"D_MN07", 0x55, -1, 0x16}, + {"D_MN10", 0x1E, -1, 0x13}, + {"D_MN10", 0x1F, -1, 0x13}, + {"D_MN10", 0x20, -1, 0x13}, + {"D_MN10", 0x21, -1, 0x13}, + {"D_MN11", 0x72, -1, 0x14}, + {"D_MN11", 0x15, -1, 0x14}, + {"D_MN11", 0x7F, -1, 0x14}, + + // Sub-Dungeons + {"D_SB01", 0x45, 4, 0x19}, + {"D_SB01", 0x46, 4, 0x19}, + {"D_SB01", 0x47, 4, 0x19}, + {"D_SB02", 0x60, 2, 0x19}, + {"D_SB03", 0x5E, 3, 0x1A}, + {"D_SB03", 0x5F, 3, 0x1A}, + {"D_SB03", 0x5D, 3, 0x1A}, + {"D_SB05", 0xA, 3, 0x1B}, + {"D_SB05", 0xB, 3, 0x1B}, + {"D_SB07", 0xF, 4, 0x1B}, + {"D_SB07", 0x10, 4, 0x1B}, + + // Field Spots + {"F_SP108", 0x5D, 1, 0x2}, + {"F_SP109", 0x5E, 2, 0x3}, + {"F_SP109", 0x5F, 2, 0x3}, + {"F_SP110", 0x59, 2, 0x3}, + {"F_SP111", 0x57, 2, 0x3}, + {"F_SP111", 0x58, 2, 0x3}, + {"F_SP113", 0x49, 3, 0x4}, + {"F_SP113", 0x4A, 3, 0x4}, + {"F_SP114", 0x7C, 5, 0x8}, + {"F_SP114", 0x7D, 5, 0x8}, + {"F_SP114", 0x7B, 5, 0x8}, + {"F_SP114", 0x7E, 5, 0x8}, + {"F_SP114", 0x7F, 5, 0x8}, + {"F_SP115", 0x46, 3, 0x4}, + {"F_SP115", 0x47, 3, 0x4}, + {"F_SP115", 0x4B, 3, 0x4}, + {"F_SP115", 0x4C, 3, 0x4}, + {"F_SP115", 0x4D, 3, 0x4}, + {"F_SP117", 0xF, 1, 0x7}, + {"F_SP117", 0x1E, 1, 0x7}, + {"F_SP117", 0x10, 1, 0x7}, + {"F_SP117", 0x11, 1, 0x7}, + {"F_SP118", 0x78, 4, 0xA}, + {"F_SP118", 0x5A, 4, 0xA}, + {"F_SP121", 0x3A, 2, 0x6}, + {"F_SP121", 0x39, 1, 0x6}, + {"F_SP121", 0x33, 3, 0x6}, + {"F_SP121", 0x3B, 3, 0x6}, + {"F_SP122", 0x49, 3, 0x6}, + {"F_SP122", 0x30, 3, 0x6}, + {"F_SP122", 0x47, 3, 0x6}, + {"F_SP124", 0x5B, 4, 0xA}, + {"F_SP124", 0x5C, 4, 0xA}, + {"F_SP124", 0x5D, 4, 0xA}, + {"F_SP124", 0x33, 4, 0xA}, + {"F_SP126", 0x48, 3, 0x4}, + {"F_SP128", 0x40, 2, 0x3}, + {"R_SP160", 0x1F, 3, 0x9}, +}; + +void dMenuMapCommon_c::getDmapPoeCount(const std::string& stageName, int& nowCount, int& totalCount) { + nowCount = 0; + totalCount = 0; + + for (const auto& i : l_poeInfo) { + if (stageName == i.stagename) { + nowCount += dComIfGs_isSaveSwitch(i.save_id, i.switch_no); + totalCount++; + } + } +} + +void dMenuMapCommon_c::getFmapPoeCount(const int regionNo, int& nowCount, int& totalCount) { + nowCount = 0; + totalCount = 0; + + if (regionNo < 0) + return; + + for (const auto& i : l_poeInfo) { + if (regionNo == i.region_id) { + if (dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo()) == i.save_id) { + nowCount += dComIfGs_isSwitch(i.switch_no, -1); + } else { + nowCount += dComIfGs_isSaveSwitch(i.save_id, i.switch_no); + } + totalCount++; + } + } +} +#endif \ No newline at end of file diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index e9768e9250..05e1217322 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -45,6 +45,7 @@ UserSettings g_userSettings = { .instantText {"game.instantText", false}, .sunsSong {"game.sunsSong", false}, .autoSave {"game.autoSave", false}, + .showMapPoeCounter {"game.showMapPoeCounter", false}, // Preferences .enableMirrorMode {"game.enableMirrorMode", false}, @@ -211,6 +212,7 @@ void registerSettings() { Register(g_userSettings.game.instantText); Register(g_userSettings.game.sunsSong); Register(g_userSettings.game.autoSave); + Register(g_userSettings.game.showMapPoeCounter); Register(g_userSettings.game.enableMirrorMode); Register(g_userSettings.game.invertCameraXAxis); Register(g_userSettings.game.invertCameraYAxis); diff --git a/src/dusk/ui/preset.cpp b/src/dusk/ui/preset.cpp index e5af8f2d50..ef11d9b857 100644 --- a/src/dusk/ui/preset.cpp +++ b/src/dusk/ui/preset.cpp @@ -47,6 +47,7 @@ void applyPresetDusk() { s.game.shadowResolutionMultiplier.setValue(4); s.game.enableGyroAim.setValue(true); s.game.autoSave.setValue(true); + s.game.showMapPoeCounter.setValue(true); } } // namespace diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 6187f97d4b..a38003e129 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -1161,6 +1161,8 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Link will not recoil when his sword hits walls."); addOption("No 2nd Fish for Cat", getSettings().game.no2ndFishForCat, "Skip needing to catch a second fish for Sera's cat."); + addOption("Show Poe Count on Map", getSettings().game.showMapPoeCounter, + "Displays collected/total number of Poe Souls for a region on the map."); addSpeedrunDisabledOption("Sun's Song (R+X)", getSettings().game.sunsSong, "Allows Wolf Link to howl and change the time of day."); addOption("Quick Transform (R+Y)", getSettings().game.enableQuickTransform, From 5119135068a28315fdc9ec871a653334e0b807a0 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Wed, 20 May 2026 04:03:32 -0700 Subject: [PATCH 05/47] dmap poe counter --- include/d/d_menu_dmap.h | 5 +++++ include/d/d_menu_fmap2D.h | 1 - include/dusk/settings.h | 2 +- src/d/d_menu_dmap.cpp | 47 +++++++++++++++++++++++++++++++++++++++ src/d/d_menu_fmap2D.cpp | 2 +- src/dusk/settings.cpp | 4 ++-- src/dusk/ui/preset.cpp | 2 +- src/dusk/ui/settings.cpp | 2 +- 8 files changed, 58 insertions(+), 7 deletions(-) diff --git a/include/d/d_menu_dmap.h b/include/d/d_menu_dmap.h index 81baea1446..7740927f4e 100644 --- a/include/d/d_menu_dmap.h +++ b/include/d/d_menu_dmap.h @@ -183,6 +183,11 @@ public: /* 0xDD8 */ u8 field_0xdd8; /* 0xDD9 */ u8 field_0xdd9; /* 0xDDA */ u8 field_0xdda; + +#if TARGET_PC + J2DTextBox* mpPoeCountPane; + J2DPicture* mpPoeCountIcon; +#endif }; class dMenu_Dmap_c { diff --git a/include/d/d_menu_fmap2D.h b/include/d/d_menu_fmap2D.h index aa64129ccf..3c37c3da74 100644 --- a/include/d/d_menu_fmap2D.h +++ b/include/d/d_menu_fmap2D.h @@ -424,7 +424,6 @@ public: #if TARGET_PC J2DTextBox* mpPoeCountPane; - ResTIMG* mpPoeCountIconTex; J2DPicture* mpPoeCountIcon; u8 mSelectRegionNo; #endif diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 1c77ae992b..fe037cc3f3 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -133,7 +133,7 @@ struct UserSettings { ConfigVar instantText; ConfigVar sunsSong; ConfigVar autoSave; - ConfigVar showMapPoeCounter; + ConfigVar enhancedMapMenus; // Preferences ConfigVar enableMirrorMode; diff --git a/src/d/d_menu_dmap.cpp b/src/d/d_menu_dmap.cpp index 51cb11cdd1..cd6b94ed4f 100644 --- a/src/d/d_menu_dmap.cpp +++ b/src/d/d_menu_dmap.cpp @@ -135,6 +135,16 @@ dMenu_DmapBg_c::dMenu_DmapBg_c(JKRExpHeap* i_heap, STControl* i_stick) { memset(&field_0xd80, 0, 20); buttonIconScreenInit(); field_0xdd0 = 0; + +#if TARGET_PC + mpPoeCountIcon = JKR_NEW J2DPicture((ResTIMG*)JKRGetNameResource("ni_item_icon_pou.bti", dComIfGp_getItemIconArchive())); + + mpPoeCountPane = JKR_NEW J2DTextBox(); + if (mpPoeCountPane != nullptr) { + mpPoeCountPane->setFontSize(15.0f, 15.0f); + mpPoeCountPane->setFont(mDoExt_getMesgFont()); + } +#endif } void dMenu_DmapBg_c::mapScreenInit() { @@ -762,6 +772,14 @@ dMenu_DmapBg_c::~dMenu_DmapBg_c() { mDoExt_destroyExpHeap(mpTalkHeap); mpTalkHeap = NULL; } + +#if TARGET_PC + JKR_DELETE(mpPoeCountIcon); + mpPoeCountIcon = NULL; + + JKR_DELETE(mpPoeCountPane); + mpPoeCountPane = NULL; +#endif } void dMenu_DmapBg_c::setAllAlphaRate(f32 i_rate, bool param_2) { @@ -1014,6 +1032,35 @@ void dMenu_DmapBg_c::draw() { } mButtonScreen->draw(field_0xd94, field_0xd98, grafContext); + +#if TARGET_PC + if (dusk::getSettings().game.enhancedMapMenus) { + int nowPoeCount = 0; + int totalPoeCount = 0; + dMenuMapCommon_c::getDmapPoeCount(dComIfGp_getStartStageName(), nowPoeCount, totalPoeCount); + if (dComIfGs_isEventBit(dSv_event_flag_c::F_0456) && totalPoeCount > 0) { + const f32 x = field_0xd94 + mDoGph_gInf_c::ScaleHUDXLeft(80.0f); + const f32 y = 410.0f; + constexpr f32 iconsize = 48.0f * 0.8f; + + if (mpPoeCountIcon != nullptr) + mpPoeCountIcon->draw(x - 35.0f, y - 25.0f, iconsize, iconsize, false, false, false); + + char counter_text[6]; + snprintf(counter_text, sizeof(counter_text), "%d/%d", nowPoeCount, totalPoeCount); + mpPoeCountPane->setString(counter_text); + + mpPoeCountPane->setCharColor(0x000000FF); + mpPoeCountPane->setGradColor(0x000000FF); + mpPoeCountPane->draw(x + 1, y + 1, FB_WIDTH, HBIND_LEFT); + + mpPoeCountPane->setCharColor(0xC8C8C8FF); + mpPoeCountPane->setGradColor(0xC8C8C8FF); + mpPoeCountPane->draw(x, y, FB_WIDTH, HBIND_LEFT); + } + } +#endif + grafContext->scissor(scissor_left, scissor_top, scissor_width, scissor_height); grafContext->setScissor(); grafContext->setup2D(); diff --git a/src/d/d_menu_fmap2D.cpp b/src/d/d_menu_fmap2D.cpp index 39ec07e5d7..100fd1c067 100644 --- a/src/d/d_menu_fmap2D.cpp +++ b/src/d/d_menu_fmap2D.cpp @@ -2658,7 +2658,7 @@ void dMenu_Fmap2DTop_c::draw() { mpTitleScreen->draw(mTransX, mTransY, ctx); #if TARGET_PC - if (dusk::getSettings().game.showMapPoeCounter) { + if (dusk::getSettings().game.enhancedMapMenus) { int nowPoeCount = 0; int totalPoeCount = 0; dMenuMapCommon_c::getFmapPoeCount(mSelectRegionNo, nowPoeCount, totalPoeCount); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 05e1217322..3e6941b829 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -45,7 +45,7 @@ UserSettings g_userSettings = { .instantText {"game.instantText", false}, .sunsSong {"game.sunsSong", false}, .autoSave {"game.autoSave", false}, - .showMapPoeCounter {"game.showMapPoeCounter", false}, + .enhancedMapMenus {"game.enhancedMapMenus", false}, // Preferences .enableMirrorMode {"game.enableMirrorMode", false}, @@ -212,7 +212,7 @@ void registerSettings() { Register(g_userSettings.game.instantText); Register(g_userSettings.game.sunsSong); Register(g_userSettings.game.autoSave); - Register(g_userSettings.game.showMapPoeCounter); + Register(g_userSettings.game.enhancedMapMenus); Register(g_userSettings.game.enableMirrorMode); Register(g_userSettings.game.invertCameraXAxis); Register(g_userSettings.game.invertCameraYAxis); diff --git a/src/dusk/ui/preset.cpp b/src/dusk/ui/preset.cpp index ef11d9b857..00bbe08871 100644 --- a/src/dusk/ui/preset.cpp +++ b/src/dusk/ui/preset.cpp @@ -47,7 +47,7 @@ void applyPresetDusk() { s.game.shadowResolutionMultiplier.setValue(4); s.game.enableGyroAim.setValue(true); s.game.autoSave.setValue(true); - s.game.showMapPoeCounter.setValue(true); + s.game.enhancedMapMenus.setValue(true); } } // namespace diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index a38003e129..567418347b 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -1161,7 +1161,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Link will not recoil when his sword hits walls."); addOption("No 2nd Fish for Cat", getSettings().game.no2ndFishForCat, "Skip needing to catch a second fish for Sera's cat."); - addOption("Show Poe Count on Map", getSettings().game.showMapPoeCounter, + addOption("Show Poe Count on Map", getSettings().game.enhancedMapMenus, "Displays collected/total number of Poe Souls for a region on the map."); addSpeedrunDisabledOption("Sun's Song (R+X)", getSettings().game.sunsSong, "Allows Wolf Link to howl and change the time of day."); From 069650295c02a3e834953403df0462df7578e743 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Wed, 20 May 2026 04:06:33 -0700 Subject: [PATCH 06/47] dmap count fix --- src/d/d_menu_map_common.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/d/d_menu_map_common.cpp b/src/d/d_menu_map_common.cpp index feed9310ef..6c8e0b76eb 100644 --- a/src/d/d_menu_map_common.cpp +++ b/src/d/d_menu_map_common.cpp @@ -828,7 +828,11 @@ void dMenuMapCommon_c::getDmapPoeCount(const std::string& stageName, int& nowCou for (const auto& i : l_poeInfo) { if (stageName == i.stagename) { - nowCount += dComIfGs_isSaveSwitch(i.save_id, i.switch_no); + if (dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo()) == i.save_id) { + nowCount += dComIfGs_isSwitch(i.switch_no, -1); + } else { + nowCount += dComIfGs_isSaveSwitch(i.save_id, i.switch_no); + } totalCount++; } } From 716a2b402077e670703b500133dcd8f5430927d9 Mon Sep 17 00:00:00 2001 From: JaxonKEKW <39291630+JaxonKEKW@users.noreply.github.com> Date: Wed, 20 May 2026 13:55:38 -0600 Subject: [PATCH 07/47] Improve Collection/File Select screens at Widescreen/Ultrawide (#1614) * Improve Widescreen/Ultrawide Collection/File Select Menus Re-scale (unstretch) and center elements of the Collection Screen/File Select details screen for Widescreen/Ultrawide * Fix oversight Fix default behavior * Support ultrawide on Collection menu, target PC support ultrawide instead of reverting to default behavior wrap logic in target PC ifdefs (both changed functions themselves are still behind them as a whole as well) and use old function behavior otherwise * Fix icon overshift at ultrawide Icon shifted too much at ultrawide Move redundant duplicate line * Finished Collection/File Select screen changes Added settings for the scaling mode (GameCube, Wii, Dusklight) Depending on the setting in the Interface menu (Dusklight preset automatically sets scaling to Dusklight option, Classic preset sets to Gamecube, Wii/all old behavior available as well) Collection and File Select screens get scaled differently Fixed backdrop behind slots on File Select with Dusklight setting (the Magic Armor background slot seeming too low on all aspects is vanilla behavior) Fixed Fused Shadow/Mirror size and position with Dusklight setting All logic is behind TARGET_PC gates (not the logic specifically, but the functions themselves have always been) Changes dSelect_cursor_c::refreshAspectScale to take a parameter so the scale of the selection cursor can be reset to default (only ever called in TARGET_PC functions or wrapped in gates) * Ultrawide oversight * Update d_file_select.cpp copy paste oopsie * Update d_file_select.cpp im tired, never tested msvc * Menu Scaling Mode changed definitions to be more open ended header additions now in TARGET_PC ifdefs fixed/added scaling for Save/Option buttons in Collection menu with Dusklight setting, stopped scaling just the text * Update settings.cpp --- include/d/d_file_select.h | 35 +++++ include/d/d_menu_collect.h | 43 ++++++ include/d/d_select_cursor.h | 2 +- include/dusk/settings.h | 13 ++ src/d/d_file_select.cpp | 278 +++++++++++++++++++++++++++--------- src/d/d_menu_collect.cpp | 222 ++++++++++++++++++---------- src/d/d_menu_save.cpp | 2 +- src/d/d_name.cpp | 4 +- src/d/d_select_cursor.cpp | 4 +- src/dusk/config.cpp | 1 + src/dusk/settings.cpp | 2 + src/dusk/ui/preset.cpp | 2 + src/dusk/ui/settings.cpp | 44 +++++- 13 files changed, 507 insertions(+), 145 deletions(-) diff --git a/include/d/d_file_select.h b/include/d/d_file_select.h index cf081a3b10..66c4696c37 100644 --- a/include/d/d_file_select.h +++ b/include/d/d_file_select.h @@ -11,7 +11,42 @@ class dFile_info_c; class J2DPicture; +#if TARGET_PC +static bool cachedPanes = false; +struct PaneCache { + u64 tag; + f32 origTransX; + f32 origTransY; + bool cached; +}; + +static PaneCache mSelDtPanes[] = { + {MULTI_CHAR('tate_n0'), 0.0f, false}, + {MULTI_CHAR('tate_n1'), 0.0f, false}, + {MULTI_CHAR('ken_n0'), 0.0f, false}, + {MULTI_CHAR('ken_n1'), 0.0f, false}, + {MULTI_CHAR('fuku_n0'), 0.0f, false}, + {MULTI_CHAR('fuku_n1'), 0.0f, false}, + {MULTI_CHAR('fuku_n2'), 0.0f, false}, + {MULTI_CHAR('gray_n'), 0.0f, false}, + {MULTI_CHAR('b_base'), 0.0f, false}, + {MULTI_CHAR('b_base1'), 0.0f, false}, +}; + +static PaneCache fileSelPanes[] = { + {MULTI_CHAR('w_uzu00'), 0.0f, false}, + {MULTI_CHAR('w_uzu01'), 0.0f, false}, + {MULTI_CHAR('w_uzu02'), 0.0f, false}, + {MULTI_CHAR('w_uzu03'), 0.0f, false}, + {MULTI_CHAR('w_uzu04'), 0.0f, false}, + {MULTI_CHAR('w_uzu05'), 0.0f, false}, + {MULTI_CHAR('w_uzu06'), 0.0f, false}, + {MULTI_CHAR('w_uzu07'), 0.0f, false}, + {MULTI_CHAR('w_uzu08'), 0.0f, false}, + {MULTI_CHAR('w_uzu09'), 0.0f, false}, +}; +#endif class dDlst_FileSel_c : public dDlst_base_c { public: void draw(); diff --git a/include/d/d_menu_collect.h b/include/d/d_menu_collect.h index 55d5373c71..d259027a65 100644 --- a/include/d/d_menu_collect.h +++ b/include/d/d_menu_collect.h @@ -15,6 +15,49 @@ class dMenu_Fishing_c; class dMenu_Skill_c; class dMenu_Insect_c; class dSelect_cursor_c; +#if TARGET_PC +static bool cachedPanes = false; + +struct PaneCache { + u64 tag; + f32 origTransX; + f32 origTransY; + bool cached; +}; + +static PaneCache mpScreenPanes[] = { + {MULTI_CHAR('sa_tex_n'), 0.0f, false}, + {MULTI_CHAR('op_tex_n'), 0.0f, false}, + {MULTI_CHAR('heart_n'), 0.0f, false}, + {MULTI_CHAR('wolf_n'), 0.0f, false}, + {MULTI_CHAR('item_0_n'), 0.0f, false}, + {MULTI_CHAR('item_1_n'), 0.0f, false}, + {MULTI_CHAR('item_2_n'), 0.0f, false}, + {MULTI_CHAR('fish_3_n'), 0.0f, false}, + {MULTI_CHAR('lett_4_n'), 0.0f, false}, + {MULTI_CHAR('maki_5_n'), 0.0f, false}, + {MULTI_CHAR('fuku_n0'), 0.0f, false}, + {MULTI_CHAR('fuku_n1'), 0.0f, false}, + {MULTI_CHAR('fuku_n2'), 0.0f, false}, + {MULTI_CHAR('tate_n0'), 0.0f, false}, + {MULTI_CHAR('tate_n1'), 0.0f, false}, + {MULTI_CHAR('ken_n0'), 0.0f, false}, + {MULTI_CHAR('ken_n1'), 0.0f, false}, + {MULTI_CHAR('kabu_6n'), 0.0f, false}, + {MULTI_CHAR('t_t00'), 0.0f, false}, + {MULTI_CHAR('f_t00'), 0.0f, false}, + {MULTI_CHAR('itemn_n'), 0.0f, false}, + {MULTI_CHAR('infotxtn'), 0.0f, false}, + {MULTI_CHAR('sa_op_n'), 0.0f, false}, + {MULTI_CHAR('title_n'), 0.0f, false}, + {MULTI_CHAR('menu_n'), 0.0f, false}, + {MULTI_CHAR('w_er_n'), 0.0f, false}, + {MULTI_CHAR('center_n'), 0.0f, false}, + {MULTI_CHAR('info_n'), 0.0f, false}, + {MULTI_CHAR('lavel_n'), 0.0f, false}, + {MULTI_CHAR('modelbgn'), 0.0f, false}, +}; +#endif class dMenu_Collect2D_c; class dMenu_Collect2DTop_c : public dDlst_base_c { diff --git a/include/d/d_select_cursor.h b/include/d/d_select_cursor.h index 1ca226611b..e98db19731 100644 --- a/include/d/d_select_cursor.h +++ b/include/d/d_select_cursor.h @@ -51,7 +51,7 @@ public: f32 getPositionX() const { return mPositionX; } f32 getPositionY() const { return mPositionY; } - void refreshAspectScale(); + void refreshAspectScale(f32 param_0); #endif void onUpdateFlag() { mUpdateFlag = true; } diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 0d85b714a5..cc8af3556a 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -45,6 +45,12 @@ enum class FrameInterpMode : u8 { Unlimited = 2, }; +enum class MenuScaling : u8 { + GameCube = 0, + Wii = 1, + Dusklight = 2, +}; + namespace config { template <> struct ConfigEnumRange { @@ -81,6 +87,12 @@ struct ConfigEnumRange { static constexpr auto min = FrameInterpMode::Off; static constexpr auto max = FrameInterpMode::Unlimited; }; + +template <> +struct ConfigEnumRange { + static constexpr auto min = MenuScaling::GameCube; + static constexpr auto max = MenuScaling::Dusklight; +}; } // namespace config // Persistent user settings @@ -142,6 +154,7 @@ struct UserSettings { ConfigVar enableAchievementToasts; ConfigVar enableControllerToasts; ConfigVar enableDiscordPresence; + ConfigVar menuScalingMode; // Graphics ConfigVar bloomMode; diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index 17d5f2c4e4..53f2450ac8 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -3750,74 +3750,220 @@ bool dFile_select_c::yesnoWakuAlpahAnm(u8 param_1) { #if TARGET_PC void dFile_select_c::fileSelectWide() { - mYnSel.ScrYn->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - mYnSel.ScrYn->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - mYnSel.ScrYn->search(MULTI_CHAR('w_no_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mYnSel.ScrYn->search(MULTI_CHAR('f_no_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mYnSel.ScrYn->search(MULTI_CHAR('w_yes_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mYnSel.ScrYn->search(MULTI_CHAR('f_yes_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - m3mSel.Scr3m->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - m3mSel.Scr3m->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - m3mSel.Scr3m->search(MULTI_CHAR('w_sta'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('f_sta'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('w_del'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('f_del'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('w_cop_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('f_cop_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - fileSel.Scr->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - fileSel.Scr->search(MULTI_CHAR('t_for'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('t_for1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->search(MULTI_CHAR('w_btn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->search(MULTI_CHAR('w_n_bk00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->search(MULTI_CHAR('w_dat_i0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - mCpSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mCpSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mCpSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mCpSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - mSelDt.ScrDt->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Spirals - fileSel.Scr->search(MULTI_CHAR('w_uzu00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu03'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu04'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu05'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu06'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu07'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu09'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - #if TARGET_PC - if (mSelIcon) { - mSelIcon->refreshAspectScale(); + // Get pre-scale values for each pane + if (!cachedPanes) { + for (PaneCache& entry : mSelDtPanes) { + J2DPane* pane = mSelDt.ScrDt->search(entry.tag); + if (!entry.cached) { + entry.origTransX = pane->getTranslateX(); + entry.origTransY = pane->getTranslateY(); + entry.cached = true; + } + } + for (PaneCache& entry : fileSelPanes) { + J2DPane* pane = fileSel.Scr->search(entry.tag); + if (!entry.cached) { + entry.origTransX = pane->getTranslateX(); + entry.origTransY = pane->getTranslateY(); + entry.cached = true; + } + } + cachedPanes = true; } - - if (mSelIcon2) { - mSelIcon2->refreshAspectScale(); + + // Reset all panes + mSelDt.ScrDt->scale(1.0f, 1.0f); + mSelDt.ScrDt->translate(0.0f, 0.0f); + for (PaneCache& entry : mSelDtPanes) { + J2DPane* pane = mSelDt.ScrDt->search(entry.tag); + pane->setBasePosition(J2DBasePosition_4); + pane->scale(1.0f, 1.0f); + pane->translate(entry.origTransX, entry.origTransY); + } + for (PaneCache& entry : fileSelPanes) { + J2DPane* pane = fileSel.Scr->search(entry.tag); + pane->setBasePosition(J2DBasePosition_4); + pane->scale(1.0f, 1.0f); + pane->translate(entry.origTransX, entry.origTransY); + } + + bool wideScaling = dusk::getSettings().game.menuScalingMode.getValue() != dusk::MenuScaling::GameCube; + const f32 rootScale = wideScaling ? mDoGph_gInf_c::hudAspectScaleUp : 1.0f; + const f32 childScale = wideScaling ? mDoGph_gInf_c::hudAspectScaleDown : 1.0f; + const f32 rootTransX = wideScaling ? mDoGph_gInf_c::getSafeMinXF() : 0.0f; + + mYnSel.ScrYn->scale(rootScale, 1.0f); + mYnSel.ScrYn->translate(rootTransX, 0.0f); + + mYnSel.ScrYn->search(MULTI_CHAR('w_no_t'))->scale(childScale, 1.0f); + mYnSel.ScrYn->search(MULTI_CHAR('f_no_t'))->scale(childScale, 1.0f); + mYnSel.ScrYn->search(MULTI_CHAR('w_yes_t'))->scale(childScale, 1.0f); + mYnSel.ScrYn->search(MULTI_CHAR('f_yes_t'))->scale(childScale, 1.0f); + + m3mSel.Scr3m->scale(rootScale, 1.0f); + m3mSel.Scr3m->translate(rootTransX, 0.0f); + + m3mSel.Scr3m->search(MULTI_CHAR('w_sta'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('f_sta'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('w_del'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('f_del'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('w_cop_t'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('f_cop_t'))->scale(childScale, 1.0f); + + fileSel.Scr->scale(rootScale, 1.0f); + fileSel.Scr->translate(rootTransX, 0.0f); + + fileSel.Scr->search(MULTI_CHAR('t_for'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('t_for1'))->scale(childScale, 1.0f); + + fileSel.Scr->search(MULTI_CHAR('w_btn_n'))->scale(childScale, 1.0f); + + fileSel.Scr->search(MULTI_CHAR('w_n_bk00'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(childScale, 1.0f); + + fileSel.Scr->search(MULTI_CHAR('w_dat_i0'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(childScale, 1.0f); + + mCpSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(childScale, 1.0f); + mCpSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(childScale, 1.0f); + mCpSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(childScale, 1.0f); + mCpSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(childScale, 1.0f); + + switch (dusk::getSettings().game.menuScalingMode) { + case (dusk::MenuScaling::GameCube): + // Selection Cursor + if (mSelIcon) { + mSelIcon->refreshAspectScale(1.0f); + } + if (mSelIcon2) { + mSelIcon2->refreshAspectScale(1.0f); + } + break; + case (dusk::MenuScaling::Wii): + // Icons + mSelDt.ScrDt->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Spirals + fileSel.Scr->search(MULTI_CHAR('w_uzu00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu03'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu04'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu05'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu06'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu07'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu09'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Selection Cursor + if (mSelIcon) { + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + + if (mSelIcon2) { + mSelIcon2->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + break; + case (dusk::MenuScaling::Dusklight): + constexpr f32 minAspect = 4.0f / 2.94f; + constexpr f32 wideAspect = 16.0f / 9.0f + 0.05f; + constexpr f32 ultraAspect = 21.0f / 9.0f + 0.05f; + const f32 screenAspect = mDoGph_gInf_c::getAspect(); + + const f32 wideScaleFactor = 1.0f + 0.16f * (mDoGph_gInf_c::hudAspectScaleUp - 1.0f); + const f32 ultraScaleFactor = 1.0f + 0.115f * (mDoGph_gInf_c::hudAspectScaleUp - 1.0f); + + const f32 wideShiftFactor = mSelDt.ScrDt->search(MULTI_CHAR('gray_n'))->getTranslateX() * (wideScaleFactor - mDoGph_gInf_c::hudAspectScaleDown); + const f32 ultraShiftFactor = mSelDt.ScrDt->search(MULTI_CHAR('gray_n'))->getTranslateX() * (ultraScaleFactor - mDoGph_gInf_c::hudAspectScaleDown); + + for (PaneCache& entry : mSelDtPanes) { + const size_t index = &entry - mSelDtPanes; + J2DPane* pane = mSelDt.ScrDt->search(entry.tag); + pane->setBasePosition(J2DBasePosition_0); + pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + if (screenAspect >= minAspect && screenAspect <= wideAspect) { // Handle widescreen + if (entry.tag == MULTI_CHAR('b_base')) { // Slots BG + if (screenAspect > 1.75f) { + pane->translate((entry.origTransX + 11.0f) * wideScaleFactor, pane->getTranslateY()); + } else { // Between 4:3 and 16:9 + pane->translate((entry.origTransX + 8.0f) * wideScaleFactor, pane->getTranslateY()); + } + } + if (entry.tag == MULTI_CHAR('b_base1')) { // Magic Armor Slot BG + if (screenAspect > 1.75f) { + pane->translate((entry.origTransX - 21.5f) * wideScaleFactor, pane->getTranslateY()); + } else { // Between 4:3 and 16:9 + pane->translate((entry.origTransX - 12.0f) * wideScaleFactor, pane->getTranslateY()); + } + } + if (entry.tag == MULTI_CHAR('gray_n')) { // Slots + pane->translate(entry.origTransX * wideScaleFactor, pane->getTranslateY()); + } + if (index <= 6) { // Icons + pane->translate(mDoGph_gInf_c::hudAspectScaleDown * entry.origTransX + wideShiftFactor - 60.0f * (1.0f - mDoGph_gInf_c::hudAspectScaleDown), pane->getTranslateY()); + } + } else if (screenAspect >= minAspect && screenAspect >= wideAspect && screenAspect <= ultraAspect) // Handle ultrawide + { + if (entry.tag == MULTI_CHAR('b_base')) { // Slots BG + pane->translate((entry.origTransX + 18.0f) * ultraScaleFactor, pane->getTranslateY()); + } + if (entry.tag == MULTI_CHAR('b_base1')) { // Magic Armor Slot BG + pane->translate((entry.origTransX - 40.0f) * ultraScaleFactor, pane->getTranslateY()); + } + if (entry.tag == MULTI_CHAR('gray_n')) { // Slots + pane->translate(entry.origTransX * ultraScaleFactor, pane->getTranslateY()); + } + if (index <= 6) { // Icons + pane->translate(mDoGph_gInf_c::hudAspectScaleDown * entry.origTransX + ultraShiftFactor - 62.0f * (1.0f - mDoGph_gInf_c::hudAspectScaleDown), pane->getTranslateY()); + } + } else { // 4:3/default behavior + pane->setBasePosition(J2DBasePosition_4); + pane->translate(entry.origTransX, pane->getTranslateY()); + if (entry.tag == MULTI_CHAR('gray_n')) { // Slots + pane->scale(1.0f, 1.0f); + } + if (entry.tag == MULTI_CHAR('b_base')) { // Slots BG + pane->scale(1.0f, 1.0f); + } + if (entry.tag == MULTI_CHAR('b_base1')) { // Magic Armor Slot BG + pane->scale(1.0f, 1.0f); + } + if (index <= 6) { // Icons + pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + } + } + } + + // Selection Cursor + if (mSelIcon) { + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + + if (mSelIcon2) { + mSelIcon2->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + + // Spirals + fileSel.Scr->search(MULTI_CHAR('w_uzu00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu03'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu04'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu05'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu06'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu07'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu09'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + break; } - #endif } #endif diff --git a/src/d/d_menu_collect.cpp b/src/d/d_menu_collect.cpp index 612806d372..8af6dc943a 100644 --- a/src/d/d_menu_collect.cpp +++ b/src/d/d_menu_collect.cpp @@ -97,79 +97,157 @@ dMenu_Collect2D_c::~dMenu_Collect2D_c() { #if TARGET_PC void dMenu_Collect2D_c::menuCollectWide() { - // Main Canvas - mpScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - mpScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - // Pieces of Heart - mpScreen->search(MULTI_CHAR('heart_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Scents - mpScreen->search(MULTI_CHAR('wolf_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Quiver - mpScreen->search(MULTI_CHAR('item_0_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Wallet - mpScreen->search(MULTI_CHAR('item_1_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Poes - mpScreen->search(MULTI_CHAR('item_2_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Fish Bestiary - mpScreen->search(MULTI_CHAR('fish_3_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Letters - mpScreen->search(MULTI_CHAR('lett_4_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Hidden Skills - mpScreen->search(MULTI_CHAR('maki_5_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Green Tunic - mpScreen->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Zora Armor - mpScreen->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Magic Armor - mpScreen->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Ordon Shield - mpScreen->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Hylian Shield - mpScreen->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Ordon Sword - mpScreen->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Master Sword - mpScreen->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Bugs - mpScreen->search(MULTI_CHAR('kabu_6n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // "Collection" Text - mpScreen->search(MULTI_CHAR('t_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mpScreen->search(MULTI_CHAR('f_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // "Save" Text - mpScreen->search(MULTI_CHAR('sa_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // "Options" Text - mpScreen->search(MULTI_CHAR('op_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Item Name Text - mpScreen->search(MULTI_CHAR('itemn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Item Description Text - mpScreen->search(MULTI_CHAR('infotxtn'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - #if TARGET_PC - if (mpDrawCursor) { - mpDrawCursor->refreshAspectScale(); + // Get pre-scale values for each pane + if (!cachedPanes) { + for (PaneCache& entry : mpScreenPanes) { + J2DPane* pane = mpScreen->search(entry.tag); + if (!entry.cached) { + entry.origTransX = pane->getTranslateX(); + entry.origTransY = pane->getTranslateY(); + entry.cached = true; + } + } + cachedPanes = true; + } + + // Reset all panes + mpScreen->scale(1.0f, 1.0f); + mpScreen->translate(0.0f, 0.0f); + for (PaneCache& entry : mpScreenPanes) { + J2DPane* pane = mpScreen->search(entry.tag); + pane->scale(1.0f, 1.0f); + pane->translate(entry.origTransX, entry.origTransY); + } + + // Reset button overlay + mpScreenIcon->translate(0.0f, 0.0f); + + switch (dusk::getSettings().game.menuScalingMode) { + case dusk::MenuScaling::GameCube: + // Selection Cursor + if (mpDrawCursor) { + mpDrawCursor->refreshAspectScale(1.0f); + } + break; + case dusk::MenuScaling::Wii: + // Main Canvas + mpScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); + mpScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); + + // Button Overlay + mpScreenIcon->translate(-mDoGph_gInf_c::getSafeMinXF(), 0.0f); + + // "Save" Text + mpScreen->search(MULTI_CHAR('sa_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // "Options" Text + mpScreen->search(MULTI_CHAR('op_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Pieces of Heart + mpScreen->search(MULTI_CHAR('heart_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Scents + mpScreen->search(MULTI_CHAR('wolf_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Quiver + mpScreen->search(MULTI_CHAR('item_0_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Wallet + mpScreen->search(MULTI_CHAR('item_1_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Poes + mpScreen->search(MULTI_CHAR('item_2_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Fish Bestiary + mpScreen->search(MULTI_CHAR('fish_3_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Letters + mpScreen->search(MULTI_CHAR('lett_4_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Hidden Skills + mpScreen->search(MULTI_CHAR('maki_5_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Green Tunic + mpScreen->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Zora Armor + mpScreen->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Magic Armor + mpScreen->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Ordon Shield + mpScreen->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Hylian Shield + mpScreen->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Ordon Sword + mpScreen->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Master Sword + mpScreen->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Bugs + mpScreen->search(MULTI_CHAR('kabu_6n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // "Collection" Text + mpScreen->search(MULTI_CHAR('t_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mpScreen->search(MULTI_CHAR('f_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Item Name Text + mpScreen->search(MULTI_CHAR('itemn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Item Description Text + mpScreen->search(MULTI_CHAR('infotxtn'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Selection Cursor + if (mpDrawCursor) { + mpDrawCursor->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + break; + case dusk::MenuScaling::Dusklight: + // Main Canvas + mpScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); + mpScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); + + // Save/Options Buttons + mpScreen->search(MULTI_CHAR('sa_op_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // "Collection" Title Bar + mpScreen->search(MULTI_CHAR('title_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Main Central Elements + mpScreen->search(MULTI_CHAR('menu_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mpScreen->search(MULTI_CHAR('w_er_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mpScreen->search(MULTI_CHAR('center_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + const f32 leftShift = 48.0f * (mDoGph_gInf_c::hudAspectScaleUp - 1.0f); // Shifting certain items left to keep center (> 4:3 only) + + // Item Name/Description Text + J2DPane* info_n = mpScreen->search(MULTI_CHAR('info_n')); + static f32 infoTransX_orig = info_n->getTranslateX(); + info_n->translate(infoTransX_orig - leftShift, info_n->getTranslateY()); + + // Designs + J2DPane* lavel_n = mpScreen->search(MULTI_CHAR('lavel_n')); + static f32 lavelTransX_orig = lavel_n->getTranslateX(); + lavel_n->translate(lavelTransX_orig - leftShift, lavel_n->getTranslateY()); + + // Fused Shadow/Mirror Background + J2DPane* modelbgn = mpScreen->search(MULTI_CHAR('modelbgn')); + static f32 modelbgnTransX_orig = modelbgn->getTranslateX(); // Get pre-scale value + modelbgn->setBasePosition(J2DBasePosition_0); + modelbgn->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.3f); + f32 modelbgn_scaleFactor = 1.0f + 0.16f * (mDoGph_gInf_c::hudAspectScaleDown - 1.0f); + modelbgn->translate((modelbgnTransX_orig - 12.0f) * modelbgn_scaleFactor, modelbgn->getTranslateY()); + + // Selection Cursor + if (mpDrawCursor) { + mpDrawCursor->refreshAspectScale(1.0f); + } + break; } - #endif } #endif diff --git a/src/d/d_menu_save.cpp b/src/d/d_menu_save.cpp index 53dbe37ca4..23beb29b0b 100644 --- a/src/d/d_menu_save.cpp +++ b/src/d/d_menu_save.cpp @@ -2817,7 +2817,7 @@ void dMenu_save_c::menuSaveWide() { #if TARGET_PC if (mSelIcon) { - mSelIcon->refreshAspectScale(); + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); } #endif } diff --git a/src/d/d_name.cpp b/src/d/d_name.cpp index abc7512f14..3c0a7d35e1 100644 --- a/src/d/d_name.cpp +++ b/src/d/d_name.cpp @@ -1430,10 +1430,10 @@ void dName_c::selectCursorPosSet(int row) { #if TARGET_PC void dName_c::nameWide() { - //Resize Select Icon + // Resize Select Icon #if TARGET_PC if (mSelIcon) { - mSelIcon->refreshAspectScale(); + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); } #endif diff --git a/src/d/d_select_cursor.cpp b/src/d/d_select_cursor.cpp index 0773433b4d..1268865cb6 100644 --- a/src/d/d_select_cursor.cpp +++ b/src/d/d_select_cursor.cpp @@ -577,7 +577,7 @@ void dSelect_cursor_c::moveCenter(J2DPane* i_pane, f32 i_x, f32 i_y) { } #ifdef TARGET_PC -void dSelect_cursor_c::refreshAspectScale() { - mParam1 = mBaseParam1 * mDoGph_gInf_c::hudAspectScaleUp; +void dSelect_cursor_c::refreshAspectScale(f32 param_0) { + mParam1 = mBaseParam1 * param_0; } #endif diff --git a/src/dusk/config.cpp b/src/dusk/config.cpp index 1f9e7c54a6..caa65bc0b4 100644 --- a/src/dusk/config.cpp +++ b/src/dusk/config.cpp @@ -176,6 +176,7 @@ namespace dusk::config { template class ConfigImpl; template class ConfigImpl; template class ConfigImpl; + template class ConfigImpl; template class ConfigImpl; } diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index e9768e9250..2ecda7df5c 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -54,6 +54,7 @@ UserSettings g_userSettings = { .enableAchievementToasts {"game.enableAchievementToasts", true}, .enableControllerToasts {"game.enableControllerToasts", true}, .enableDiscordPresence {"game.enableDiscordPresence", true}, + .menuScalingMode {"game.menuScalingMode", MenuScaling::Wii}, // Graphics .bloomMode {"game.bloomMode", BloomMode::Dusk}, @@ -247,6 +248,7 @@ void registerSettings() { Register(g_userSettings.game.liveSplitEnabled); Register(g_userSettings.game.showSpeedrunRTATimer); Register(g_userSettings.game.recordingMode); + Register(g_userSettings.game.menuScalingMode); Register(g_userSettings.game.removeQuestMapMarkers); Register(g_userSettings.game.showInputViewer); Register(g_userSettings.game.showInputViewerGyro); diff --git a/src/dusk/ui/preset.cpp b/src/dusk/ui/preset.cpp index e5af8f2d50..de2dac84c3 100644 --- a/src/dusk/ui/preset.cpp +++ b/src/dusk/ui/preset.cpp @@ -19,6 +19,7 @@ void applyPresetClassic() { s.game.internalResolutionScale.setValue(1); s.game.shadowResolutionMultiplier.setValue(1); s.game.hideTvSettingsScreen.setValue(false); + s.game.menuScalingMode.setValue(MenuScaling::GameCube); AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT); } @@ -47,6 +48,7 @@ void applyPresetDusk() { s.game.shadowResolutionMultiplier.setValue(4); s.game.enableGyroAim.setValue(true); s.game.autoSave.setValue(true); + s.game.menuScalingMode.setValue(MenuScaling::Dusklight); } } // namespace diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 6187f97d4b..6518ede336 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -70,6 +70,12 @@ constexpr std::array kGyroInputModeLabels = { "Mouse", }; +constexpr std::array kMenuScalingModeLabels = { + "GameCube", + "Wii", + "Dusklight", +}; + bool try_parse_backend(std::string_view backend, AuroraBackend& outBackend) { if (backend == "auto") { outBackend = BACKEND_AUTO; @@ -1418,8 +1424,44 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { .helpText = "Show gyro sensor values in the input viewer.", .isDisabled = [] { return !getSettings().game.showInputViewer; }, }); - leftPane.add_section("Game"); + leftPane.register_control( + leftPane.add_select_button({ + .key = "Menu Scaling Mode", + .getValue = + [] { + return kMenuScalingModeLabels[static_cast( + getSettings().game.menuScalingMode.getValue())]; + }, + .isModified = + [] { + const auto& mode = getSettings().game.menuScalingMode; + return mode.getValue() != mode.getDefaultValue(); + }, + }), + rightPane, [](Pane& pane) { + for (int i = 0; i < static_cast(kMenuScalingModeLabels.size()); ++i) { + pane + .add_button({ + .text = kMenuScalingModeLabels[i], + .isSelected = + [i] { + return getSettings().game.menuScalingMode.getValue() == + static_cast(i); + ; + }, + }) + .on_pressed([i] { + mDoAud_seStartMenu(kSoundItemChange); + getSettings().game.menuScalingMode.setValue( + static_cast(i)); + ; + config::Save(); + }); + } + pane.add_rml("
Changes how the Collection and File Select menus scale to your " + "aspect ratio."); + }); config_bool_select(leftPane, rightPane, getSettings().game.hideTvSettingsScreen, { .key = "Skip TV Settings Screen", From 8ac2658f54872685f8746fc48a8f00ddfb4fd5d3 Mon Sep 17 00:00:00 2001 From: Nathan Mena Date: Wed, 20 May 2026 15:15:28 -0600 Subject: [PATCH 08/47] fixed dungeon map offset in mirror mode --- src/d/d_menu_dmap_map.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/d/d_menu_dmap_map.cpp b/src/d/d_menu_dmap_map.cpp index 6320c09850..c4029b766e 100644 --- a/src/d/d_menu_dmap_map.cpp +++ b/src/d/d_menu_dmap_map.cpp @@ -359,7 +359,14 @@ f32 dMenu_StageMapCtrl_c::getPixelStageSizeZ() const { f32 dMenu_StageMapCtrl_c::getPixelCenterX() const { f32 var_f31 = dMpath_c::getCenterX(); + #if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + return (1.0f / field_0xbc) * (field_0x9c + var_f31); + } + else return (1.0f / field_0xbc) * (field_0x9c - var_f31); + #else return (1.0f / field_0xbc) * (field_0x9c - var_f31); + #endif } f32 dMenu_StageMapCtrl_c::getPixelCenterZ() const { @@ -418,7 +425,18 @@ inline static f32 rightModeCnvPos(f32 param_0) { void dMenu_StageMapCtrl_c::cnvPosTo2Dpos(f32 param_0, f32 param_1, f32* param_2, f32* param_3) const { if (param_2 != NULL) { + #if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + *param_2 = + (0.5f * field_0x94) + rightModeCnvPos((1.0f / field_0xbc) * (field_0x9c + param_0)); + } else { + *param_2 = + (0.5f * field_0x94) + rightModeCnvPos((1.0f / field_0xbc) * (param_0 - field_0x9c)); + } + #else *param_2 = (0.5f * field_0x94) + rightModeCnvPos((1.0f / field_0xbc) * (param_0 - field_0x9c)); + #endif + } if (param_3 != NULL) { @@ -915,7 +933,9 @@ void dMenu_StageMapCtrl_c::move() { void dMenu_DmapMapCtrl_c::draw() { if (field_0xef != 0) { - setPos(field_0xeb, field_0xec, field_0x9c, field_0xa0, field_0xbc, true, field_0xd8); + setPos(field_0xeb, field_0xec, + IF_DUSK(dusk::getSettings().game.enableMirrorMode ? -field_0x9c :) field_0x9c, + field_0xa0, field_0xbc, true, field_0xd8); } } From 6a52cdf80e8351ba4e73797b743a54b28463298d Mon Sep 17 00:00:00 2001 From: doop <56421834+dooplecks@users.noreply.github.com> Date: Sat, 23 May 2026 00:23:22 -0400 Subject: [PATCH 09/47] UI/copy tweaks (#1756) * "Mortal Edge" description nit "Darkhammer" seems to be the more common rendering of the name. * Add missing option descriptions * Tighten spacing for version/disc info on mobile * Graphics API init failure message nit --- res/rml/prelaunch.rcss | 2 ++ src/dusk/achievements.cpp | 2 +- src/dusk/imgui/ImGuiConsole.cpp | 4 ++-- src/dusk/ui/settings.cpp | 5 +++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/res/rml/prelaunch.rcss b/res/rml/prelaunch.rcss index f5d7240d56..73efc18086 100644 --- a/res/rml/prelaunch.rcss +++ b/res/rml/prelaunch.rcss @@ -447,6 +447,7 @@ body.animate-in .intro-item { top: auto; text-align: right; font-size: 16dp; + gap: 8dp; } #disc-status { @@ -468,6 +469,7 @@ body.animate-in .intro-item { top: 32dp; text-align: right; font-size: 16dp; + gap: 8dp; } .update { diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index a8e4ff3916..5b76d7e8e8 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -442,7 +442,7 @@ std::vector AchievementSystem::makeEntries() { { "dark_hammer_one_hit", "Mortal Edge", - "Defeat Dark Hammer in a single hit.", + "Defeat Darkhammer in a single hit.", AchievementCategory::Misc, false, 0, 0, false }, diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 077b4c15bc..d8b85b2d63 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -322,8 +322,8 @@ namespace dusk { } ImGui::PushFont(ImGuiEngine::fontLarge); ImGuiTextCenter("Failed to initialize any graphics backend."); - ImGuiTextCenter("\nDusklight requires Vulkan 1.1+, or Direct X 12.0."); - ImGuiTextCenter("\nTry updating your Operating System and GPU drivers."); + ImGuiTextCenter("\nDusklight requires at least Vulkan 1.1 or Direct3D 12."); + ImGuiTextCenter("\nTry updating your operating system and GPU drivers."); const auto& style = ImGui::GetStyle(); const auto retrySize = ImGui::CalcTextSize("Retry (Auto backend)"); const auto quitSize = ImGui::CalcTextSize("Quit"); diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index e009497045..42f205f12c 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -887,14 +887,19 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { config_bool_select(leftPane, rightPane, getSettings().game.enableDepthOfField, { .key = "Enable Depth of Field", + .helpText = "Render a blurring effect for out-of-focus areas in some situations. May impact performance." }); config_bool_select(leftPane, rightPane, getSettings().game.enableMapBackground, { .key = "Enable Mini-Map Shadows", + .helpText = "Render a thick shadow around the mini-map. May impact performance." }); config_bool_select(leftPane, rightPane, getSettings().game.disableCutscenePillarboxing, { .key = "Disable Cutscene Pillarboxing", + .helpText = "Disable black bars on the left and right sides of the screen " + "during some cutscenes, particularly on ultra-wide displays. " + "Visuals beyond the original intended framing may appear buggy." }); }); From 9655b827f37fb4583f8e27579549682aa917ddb8 Mon Sep 17 00:00:00 2001 From: Irastris Date: Sat, 23 May 2026 00:23:46 -0400 Subject: [PATCH 10/47] Add KI_NUMPADENTER as input for NavCommand::Confirm (#1752) --- src/dusk/ui/ui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dusk/ui/ui.cpp b/src/dusk/ui/ui.cpp index 98c9ca5782..83a331c1fc 100644 --- a/src/dusk/ui/ui.cpp +++ b/src/dusk/ui/ui.cpp @@ -315,6 +315,7 @@ NavCommand map_nav_event(const Rml::Event& event) noexcept { case Rml::Input::KeyIdentifier::KI_ESCAPE: return NavCommand::Cancel; case Rml::Input::KeyIdentifier::KI_RETURN: + case Rml::Input::KeyIdentifier::KI_NUMPADENTER: return NavCommand::Confirm; case Rml::Input::KeyIdentifier::KI_F1: return event.GetParameter("shift_key", 0) ? NavCommand::None : NavCommand::Menu; From c49f0e5c6e574601f05538c9f3eef1ae7f8f2ddc Mon Sep 17 00:00:00 2001 From: Irastris Date: Sat, 23 May 2026 09:18:29 -0400 Subject: [PATCH 11/47] Use std::chrono for THP playback pacing (#1757) * Use std::chrono for THP playback pacing * Redundant include --- include/d/actor/d_a_movie_player.h | 2 ++ src/d/actor/d_a_movie_player.cpp | 30 +++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/include/d/actor/d_a_movie_player.h b/include/d/actor/d_a_movie_player.h index 0e666ae470..c382e0d1f7 100644 --- a/include/d/actor/d_a_movie_player.h +++ b/include/d/actor/d_a_movie_player.h @@ -5,6 +5,7 @@ #include #else #include +#include #endif #include "f_op/f_op_actor.h" #include "d/d_drawlist.h" @@ -125,6 +126,7 @@ struct daMP_THPPlayer { /* 0x0D4 */ s32 curCount; #if TARGET_PC /* 0x0D8 */ std::atomic videoDecodeCount; + std::chrono::steady_clock::time_point thpPlaybackClock; #else /* 0x0D8 */ s32 videoDecodeCount; #endif diff --git a/src/d/actor/d_a_movie_player.cpp b/src/d/actor/d_a_movie_player.cpp index 1f8bdeecd7..fcd1fb6dff 100644 --- a/src/d/actor/d_a_movie_player.cpp +++ b/src/d/actor/d_a_movie_player.cpp @@ -26,15 +26,16 @@ #include "f_op/f_op_overlap_mng.h" +#include "JSystem/JAudio2/JASCriticalSection.h" + +#if TARGET_PC #include "dusk/gx_helper.h" #include "dusk/os.h" #include "dusk/layout.hpp" - -#include "JSystem/JAudio2/JASCriticalSection.h" - #if MOVIE_SUPPORT #include "turbojpeg.h" #endif +#endif inline s32 daMP_NEXT_READ_SIZE(daMP_THPReadBuffer* readBuf) { return *(BE(s32)*)readBuf->ptr; @@ -3912,13 +3913,20 @@ static BOOL daMP_ProperTimingForGettingNextFrame() { return TRUE; } } else { - s32 frameRate = daMP_ActivePlayer.header.frameRate * 100.0f; #if TARGET_PC - // DUSK HACK: We only fire retrace callbacks *half* as often as the game expects, - // because we only run them once per frame, and normally there should be two scans - // per game frame. - frameRate *= 2; -#endif + const f32 fps = daMP_ActivePlayer.header.frameRate; + if (fps > 0.0f) { + const f32 elapsed = std::chrono::duration( + std::chrono::steady_clock::now() - daMP_ActivePlayer.thpPlaybackClock).count(); + const s32 desired = static_cast(elapsed * fps); + if (desired != daMP_ActivePlayer.prevCount) { + daMP_ActivePlayer.prevCount = desired; + daMP_ActivePlayer.curCount = desired; + return TRUE; + } + } +#else + s32 frameRate = daMP_ActivePlayer.header.frameRate * 100.0f; if (VIGetTvFormat() == VI_PAL) { daMP_ActivePlayer.curCount = daMP_ActivePlayer.retaceCount * frameRate / 5000; } else { @@ -3929,6 +3937,7 @@ static BOOL daMP_ProperTimingForGettingNextFrame() { daMP_ActivePlayer.prevCount = daMP_ActivePlayer.curCount; return TRUE; } +#endif } return FALSE; @@ -4133,6 +4142,9 @@ static BOOL daMP_THPPlayerPlay() { daMP_ActivePlayer.prevCount = 0; daMP_ActivePlayer.curCount = 0; daMP_ActivePlayer.retaceCount = -1; +#if TARGET_PC + daMP_ActivePlayer.thpPlaybackClock = std::chrono::steady_clock::now(); +#endif return TRUE; } From c804b1f6639bfe5f9e9cfddee5dac3b940f2d943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFs?= <49660929+SailorSnoW@users.noreply.github.com> Date: Sat, 23 May 2026 15:20:45 +0200 Subject: [PATCH 12/47] Rework nix flake for offline multi-platform builds (#1601) * Rework flake.nix for offline multi-platform builds (Linux & macOS) * fix typo + fmt --- flake.lock | 6 +- flake.nix | 535 ++++++++++++++++++++++++++++++++++------------------- 2 files changed, 348 insertions(+), 193 deletions(-) diff --git a/flake.lock b/flake.lock index 8ec14d2852..08dc0f458c 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1775710090, - "narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=", + "lastModified": 1778869304, + "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4c1018dae018162ec878d42fec712642d214fdfa", + "rev": "d233902339c02a9c334e7e593de68855ad26c4cb", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 29c99b10a5..90c0d21533 100644 --- a/flake.nix +++ b/flake.nix @@ -1,219 +1,374 @@ { - inputs = { - nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; - }; - outputs = { self, nixpkgs }: + description = "Dusklight — native PC port of the Twilight Princess decompilation"; + + inputs.nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + + outputs = + { self, nixpkgs }: let + inherit (nixpkgs) lib; + supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; - forAllSystems = nixpkgs.lib.genAttrs supportedSystems; - pkgsFor = system: import nixpkgs { inherit system; }; + forAllSystems = lib.genAttrs supportedSystems; - # Dependencies that are not packaged in nixpkgs (used by the Linux package build): - buildSources = pkgs: { - dawn-src = pkgs.fetchzip { - url = "https://github.com/encounter/dawn-build/releases/download/v20260423.175430/dawn-linux-x86_64.tar.gz"; + dawnVersion = "v20260423.175430"; + nodVersion = "v2.0.0-alpha.8"; + versionSuffix = "nix-" + (self.shortRev or self.dirtyShortRev or "dirty"); + + dawnInfo = { + "x86_64-linux" = { + triple = "linux-x86_64"; hash = "sha256-HXfKTLHtMPwupnFnaflCARtXVPuS/0PoCePXidjE5xs="; - stripRoot = false; }; - nod-src = pkgs.fetchzip { - url = "https://github.com/encounter/nod/releases/download/v2.0.0-alpha.8/libnod-linux-x86_64.tar.gz"; - hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE="; - stripRoot = false; + "aarch64-linux" = { + triple = "linux-aarch64"; + hash = "sha256-34yyFpfqBZUwoFXQ41F0AwAU78FaNihOSY0oriwn6B0="; }; - # The version of imgui on nixpkgs does not map cleanly. - imgui-src = pkgs.fetchFromGitHub { - owner = "ocornut"; - repo = "imgui"; - rev = "v1.91.9b-docking"; - hash = "sha256-mQOJ6jCN+7VopgZ61yzaCnt4R1QLrW7+47xxMhFRHLQ="; + "aarch64-darwin" = { + triple = "darwin-arm64"; + hash = "sha256-eQnzrBp6gjiBek1VYQ9A5W13ClYWrDDKjIqv/7eNTR4="; }; - sqlite-src = pkgs.fetchzip { - url = "https://sqlite.org/2026/sqlite-amalgamation-3510300.zip"; - hash = "sha256-pNMR8zxaaqfAzQ0AQBOXMct4usdjey1Q0Gnitg06UhM="; - }; - rmlui-src = pkgs.fetchzip { - url = "https://github.com/mikke89/RmlUi/archive/f9b8c9e2935d5df2c7dff2c190d3968e99b0c3dc.tar.gz"; - hash = "sha256-g4O/JZUrrcseOz8o2QJRt+2CeuiLnVeuDJc906xvuIg="; + "x86_64-darwin" = { + triple = "darwin-x86_64"; + hash = "sha256-QGWiGdxiI9kci3NPXH6QFFirxn16851zB/w3jqhIBJ4="; }; }; - # Dusklight Actual (Linux x86_64 only — relies on prebuilt dawn/nod binaries) - mkDusklight = pkgs: - let srcs = buildSources pkgs; - versionSuffix = if self ? shortRev && self.shortRev != null - then "nix-${self.shortRev}" - else "nix-dirty"; - in - pkgs.stdenv.mkDerivation { - name = "dusklight"; - src = ./.; - postUnpack = '' - sed -i '/add_subdirectory(tests)/d' $sourceRoot/extern/aurora/CMakeLists.txt - ''; - # Remove last line to re-enable tests - cmakeFlags = [ - "-DDUSK_VERSION_OVERRIDE=${versionSuffix}" - "-DFETCHCONTENT_FULLY_DISCONNECTED=ON" - "-DFETCHCONTENT_SOURCE_DIR_CXXOPTS=${pkgs.cxxopts.src}" - "-DFETCHCONTENT_SOURCE_DIR_JSON=${pkgs.nlohmann_json.src}" - "-DFETCHCONTENT_SOURCE_DIR_DAWN_PREBUILT=${srcs.dawn-src}" - "-DFETCHCONTENT_SOURCE_DIR_XXHASH=${pkgs.xxHash.src}" - "-DFETCHCONTENT_SOURCE_DIR_FMT=${pkgs.fmt.src}" - "-DFETCHCONTENT_SOURCE_DIR_TRACY=${pkgs.tracy.src}" - "-DAURORA_SDL3_PROVIDER=system" - "-DFETCHCONTENT_SOURCE_DIR_NOD_PREBUILT=${srcs.nod-src}" - "-DAURORA_NOD_PROVIDER=package" - "-DFETCHCONTENT_SOURCE_DIR_FREETYPE=${pkgs.freetype.src}" - "-DFETCHCONTENT_SOURCE_DIR_ZSTD=${pkgs.zstd.src}" - "-DFETCHCONTENT_SOURCE_DIR_SQLITE3=${srcs.sqlite-src}" - "-DFETCHCONTENT_SOURCE_DIR_IMGUI=${srcs.imgui-src}" - "-DFETCHCONTENT_SOURCE_DIR_RMLUI=${srcs.rmlui-src}" - "-DCMAKE_CROSSCOMPILING=ON" # Tests are not working as I didn't want to work through getting google's test suite working as well. This is the only guard I could find to disable it. - ]; - installPhase = '' - mkdir -p $out/bin - cp dusklight $out/bin/dusklight - cp -r ./res $out/bin/res + nodPrebuiltInfo = { + "x86_64-linux" = { + triple = "linux-x86_64"; + hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE="; + }; + "aarch64-darwin" = { + triple = "macos-arm64"; + hash = "sha256-UPy1ywCcv0K6VJOU3uUelJuUdBh3UNaPRlyP5LOBeDw="; + }; + }; - mkdir -p $out/share/applications - cp $src/platforms/freedesktop/dusklight.desktop $out/share/applications/dusklight.desktop + perSystem = + system: + let + pkgs = import nixpkgs { inherit system; }; + inherit (pkgs.stdenv.hostPlatform) isDarwin; + hasNodPrebuilt = nodPrebuiltInfo ? ${system}; - for size in 16 32 48 64 128 256 512 1024; do - install -Dm644 $src/platforms/freedesktop/''${size}x''${size}/apps/dusklight.png \ - $out/share/icons/hicolor/''${size}x''${size}/apps/dusklight.png - done - ''; - nativeBuildInputs = [ + aurora = pkgs.fetchFromGitHub { + owner = "encounter"; + repo = "aurora"; + rev = "10006618ee493f248b8597e4dfa1d2871d76a1d9"; + hash = "sha256-lY2xuVyB7aPJ9+2wwLRB3F5U/BuPSxdSpegdG+qNd9o="; + }; + + dawn = pkgs.fetchzip { + url = "https://github.com/encounter/dawn-build/releases/download/${dawnVersion}/dawn-${dawnInfo.${system}.triple}.tar.gz"; + hash = dawnInfo.${system}.hash; + stripRoot = false; + }; + + corrosion = pkgs.fetchFromGitHub { + owner = "corrosion-rs"; + repo = "corrosion"; + rev = "v0.6.1"; + hash = "sha256-ppuDNObfKhneD9AlnPAvyCRHKW3BidXKglD1j/LE9CM="; + }; + + nodFromSource = pkgs.stdenv.mkDerivation (finalAttrs: { + pname = "nod"; + version = nodVersion; + src = pkgs.fetchFromGitHub { + owner = "encounter"; + repo = "nod"; + rev = nodVersion; + hash = "sha256-+zrtVzjo0+X/6uMcNUn1+FaSR+jOhrcQSDNBFjw0NDs="; + }; + cargoDeps = pkgs.rustPlatform.importCargoLock { + lockFile = "${finalAttrs.src}/Cargo.lock"; + }; + postPatch = '' + substituteInPlace CMakeLists.txt \ + --replace-warn "add_subdirectory(nod-ffi/examples)" "" + ''; + nativeBuildInputs = [ + pkgs.cmake + pkgs.ninja + pkgs.rustPlatform.cargoSetupHook + pkgs.cargo + pkgs.rustc + ]; + CARGO_NET_OFFLINE = "true"; + cmakeFlags = [ + "-DFETCHCONTENT_FULLY_DISCONNECTED=ON" + "-DFETCHCONTENT_SOURCE_DIR_CORROSION=${corrosion}" + "-DNOD_ENABLE_INSTALL=ON" + "-DBUILD_SHARED_LIBS=OFF" + ]; + doCheck = false; + }); + + nod = + if hasNodPrebuilt then + pkgs.fetchzip { + url = "https://github.com/encounter/nod/releases/download/${nodVersion}/libnod-${ + nodPrebuiltInfo.${system}.triple + }.tar.gz"; + hash = nodPrebuiltInfo.${system}.hash; + stripRoot = false; + } + else + nodFromSource; + + fetchContentDirs = { + DAWN_PREBUILT = dawn; + NOD_PREBUILT = nod; + CXXOPTS = pkgs.cxxopts.src; + JSON = pkgs.nlohmann_json.src; + XXHASH = pkgs.xxHash.src; + ZSTD = pkgs.zstd.src; + FMT = pkgs.fetchzip { + url = "https://github.com/fmtlib/fmt/archive/refs/tags/11.1.4.tar.gz"; + hash = "sha256-sUbxlYi/Aupaox3JjWFqXIjcaQa0LFjclQAOleT+FRA="; + }; + TRACY = pkgs.fetchzip { + url = "https://github.com/wolfpld/tracy/archive/a64b9a20294d59421a2f57aeca3c6383d8c48169.tar.gz"; + hash = "sha256-hbNGOsGeyGSvCJ2No8RkwOib1lX2on3vNZSzyVkZdXw="; + }; + IMGUI = pkgs.fetchFromGitHub { + owner = "ocornut"; + repo = "imgui"; + rev = "v1.91.9b-docking"; + hash = "sha256-mQOJ6jCN+7VopgZ61yzaCnt4R1QLrW7+47xxMhFRHLQ="; + }; + SQLITE3 = pkgs.fetchzip { + url = "https://sqlite.org/2026/sqlite-amalgamation-3510300.zip"; + hash = "sha256-pNMR8zxaaqfAzQ0AQBOXMct4usdjey1Q0Gnitg06UhM="; + }; + RMLUI = pkgs.fetchzip { + url = "https://github.com/mikke89/RmlUi/archive/f9b8c9e2935d5df2c7dff2c190d3968e99b0c3dc.tar.gz"; + hash = "sha256-g4O/JZUrrcseOz8o2QJRt+2CeuiLnVeuDJc906xvuIg="; + }; + }; + + dusklight = pkgs.stdenv.mkDerivation { + pname = "dusklight"; + version = versionSuffix; + src = ./.; + + postUnpack = '' + chmod -R u+w "$sourceRoot" + rm -rf "$sourceRoot/extern/aurora" + mkdir -p "$sourceRoot/extern" + cp -r ${aurora} "$sourceRoot/extern/aurora" + chmod -R u+w "$sourceRoot/extern/aurora" + substituteInPlace "$sourceRoot/extern/aurora/CMakeLists.txt" \ + --replace-warn "add_subdirectory(tests)" "" + ''; + + nativeBuildInputs = [ + pkgs.cmake + pkgs.ninja + pkgs.pkg-config + pkgs.python3 + pkgs.python3Packages.markupsafe + ] + ++ lib.optionals (!isDarwin) [ pkgs.autoPatchelfHook ]; + + buildInputs = [ + pkgs.sdl3 + pkgs.freetype + pkgs.zstd + pkgs.cxxopts + pkgs.nlohmann_json + pkgs.xxHash + pkgs.abseil-cpp + pkgs.zlib + pkgs.libpng + pkgs.libjpeg_turbo + pkgs.curl + pkgs.openssl + ] + ++ lib.optionals isDarwin [ + pkgs.apple-sdk_15 + pkgs.libiconv + ] + ++ lib.optionals (!isDarwin) [ + pkgs.libGL + pkgs.libGLU + pkgs.libglvnd + pkgs.vulkan-loader + pkgs.libX11 + pkgs.libxcb + pkgs.libXcursor + pkgs.libxi + pkgs.libxrandr + pkgs.libxscrnsaver + pkgs.libxtst + pkgs.libxinerama + pkgs.libxkbcommon + pkgs.wayland + pkgs.libdecor + pkgs.alsa-lib + pkgs.libpulseaudio + pkgs.pipewire + pkgs.dbus + pkgs.udev + pkgs.libusb1 + pkgs.libunwind + pkgs.gtk3 + ]; + + cmakeBuildType = "RelWithDebInfo"; + ninjaFlags = [ "dusklight" ]; + + cmakeFlags = [ + "-DDUSK_VERSION_OVERRIDE=${versionSuffix}" + "-DFETCHCONTENT_FULLY_DISCONNECTED=ON" + "-DAURORA_DAWN_PROVIDER=package" + "-DAURORA_DAWN_LINKAGE=static" + "-DAURORA_NOD_PROVIDER=package" + "-DAURORA_NOD_LINKAGE=static" + "-DAURORA_SDL3_PROVIDER=system" + ] + ++ lib.mapAttrsToList (key: src: "-DFETCHCONTENT_SOURCE_DIR_${key}=${src}") fetchContentDirs; + + installPhase = + if isDarwin then + '' + runHook preInstall + mkdir -p "$out/Applications" + cp -r Dusklight.app "$out/Applications/Dusklight.app" + runHook postInstall + '' + else + '' + runHook preInstall + install -Dm755 dusklight "$out/bin/dusklight" + cp -r "$src/res" "$out/bin/res" + install -Dm644 "$src/platforms/freedesktop/dusklight.desktop" \ + "$out/share/applications/dusklight.desktop" + for size in 16 32 48 64 128 256 512 1024; do + install -Dm644 "$src/platforms/freedesktop/''${size}x''${size}/apps/dusklight.png" \ + "$out/share/icons/hicolor/''${size}x''${size}/apps/dusklight.png" + done + runHook postInstall + ''; + + dontStrip = true; + + meta = { + description = "Dusklight — native PC port of the Twilight Princess decompilation"; + homepage = "https://github.com/zeldaret/tp"; + platforms = supportedSystems; + mainProgram = "dusklight"; + }; + }; + + # Tooling common to every supported host (Linux and macOS). + commonDevTools = [ pkgs.cmake + pkgs.ninja pkgs.pkg-config - pkgs.wayland + pkgs.git + pkgs.python3 + pkgs.python3Packages.markupsafe + pkgs.rustc + pkgs.cargo + pkgs.sccache ]; - buildInputs = [ - pkgs.libGL - pkgs.libX11 - pkgs.libXcursor - pkgs.libxi - pkgs.libxcb - pkgs.libxrandr - pkgs.libxscrnsaver - pkgs.libxtst - pkgs.libjpeg8 - pkgs.libxkbcommon - pkgs.libglvnd + + # Linux-only system libraries — mirrors the apt deps from .github/workflows/build.yml + # so the cmake presets resolve the same set of headers as CI. + linuxDevDeps = [ + # Compilers / linkers + pkgs.clang + pkgs.lld + # C/C++ utilities + pkgs.curl + pkgs.openssl + pkgs.zlib + pkgs.libpng + pkgs.libjpeg_turbo + pkgs.freetype + pkgs.zstd + pkgs.fmt + pkgs.tracy pkgs.cxxopts pkgs.abseil-cpp pkgs.sdl3 - pkgs.fmt - pkgs.tracy - pkgs.freetype - pkgs.zstd + pkgs.ncurses + pkgs.libunwind + pkgs.libusb1 + pkgs.fuse + # Wayland / display server + pkgs.wayland + pkgs.wayland-protocols + pkgs.libxkbcommon + pkgs.libdecor + # OpenGL / Vulkan + pkgs.libGL + pkgs.libGLU + pkgs.libglvnd + pkgs.vulkan-headers + pkgs.vulkan-loader + # X11 + pkgs.libX11 + pkgs.libxcb + pkgs.libXcursor + pkgs.libxi + pkgs.libxrandr + pkgs.libxscrnsaver + pkgs.libxtst + pkgs.libxinerama + # Audio + pkgs.alsa-lib + pkgs.libpulseaudio + pkgs.pipewire + # System integration + pkgs.dbus + pkgs.udev + pkgs.gtk3 ]; + + # On macOS we deliberately avoid pulling Nix's cc-wrapper so CMake picks up + # Apple Clang and the Xcode SDK directly, matching the macOS CI workflow. + darwinShell = pkgs.mkShellNoCC { + packages = commonDevTools; + shellHook = '' + echo "Dusklight dev shell (macOS)" + echo "Requires Xcode Command Line Tools for Apple Clang and the macOS SDK." + echo "Configure: cmake --preset macos-default-relwithdebinfo" + echo "Build: cmake --build --preset macos-default-relwithdebinfo" + ''; + }; + + linuxShell = pkgs.mkShell { + packages = commonDevTools ++ linuxDevDeps; + shellHook = '' + echo "Dusklight dev shell (Linux)" + echo "Configure: cmake --preset linux-default-relwithdebinfo" + echo " cmake --preset linux-clang-relwithdebinfo" + echo "Build: cmake --build --preset " + ''; + }; + in + { + packages = { + default = dusklight; + dusklight = dusklight; + } + // lib.optionalAttrs (!hasNodPrebuilt) { nod = nodFromSource; }; + + devShells.default = if isDarwin then darwinShell else linuxShell; }; - # Tooling common to every supported host (Linux and macOS). - commonDevTools = pkgs: [ - pkgs.cmake - pkgs.ninja - pkgs.pkg-config - pkgs.git - pkgs.python3 - pkgs.python3Packages.markupsafe - pkgs.rustc - pkgs.cargo - pkgs.sccache - ]; - - # Linux-only system libraries — mirrors the apt deps from .github/workflows/build.yml - # so the cmake presets resolve the same set of headers as CI. - linuxDevDeps = pkgs: [ - # Compilers / linkers - pkgs.clang - pkgs.lld - # C/C++ utilities - pkgs.curl - pkgs.openssl - pkgs.zlib - pkgs.libpng - pkgs.libjpeg_turbo - pkgs.freetype - pkgs.zstd - pkgs.fmt - pkgs.tracy - pkgs.cxxopts - pkgs.abseil-cpp - pkgs.sdl3 - pkgs.ncurses - pkgs.libunwind - pkgs.libusb1 - pkgs.fuse - # Wayland / display server - pkgs.wayland - pkgs.wayland-protocols - pkgs.libxkbcommon - pkgs.libdecor - # OpenGL / Vulkan - pkgs.libGL - pkgs.libGLU - pkgs.libglvnd - pkgs.vulkan-headers - pkgs.vulkan-loader - # X11 - pkgs.libX11 - pkgs.libxcb - pkgs.libXcursor - pkgs.libxi - pkgs.libxrandr - pkgs.libxscrnsaver - pkgs.libxtst - pkgs.libxinerama - # Audio - pkgs.alsa-lib - pkgs.libpulseaudio - pkgs.pipewire - # System integration - pkgs.dbus - pkgs.udev - pkgs.gtk3 - ]; - - # On macOS we deliberately avoid pulling Nix's cc-wrapper so CMake picks up - # Apple Clang and the Xcode SDK directly, matching the macOS CI workflow. - mkDarwinShell = pkgs: - pkgs.mkShellNoCC { - packages = commonDevTools pkgs; - shellHook = '' - echo "Dusklight dev shell (macOS)" - echo "Requires Xcode Command Line Tools for Apple Clang and the macOS SDK." - echo "Configure: cmake --preset macos-default-relwithdebinfo" - echo "Build: cmake --build --preset macos-default-relwithdebinfo" - ''; - }; - - mkLinuxShell = pkgs: - pkgs.mkShell { - packages = (commonDevTools pkgs) ++ (linuxDevDeps pkgs); - shellHook = '' - echo "Dusklight dev shell (Linux)" - echo "Configure: cmake --preset linux-default-relwithdebinfo" - echo " cmake --preset linux-clang-relwithdebinfo" - echo "Build: cmake --build --preset " - ''; - }; - - mkDevShell = pkgs: - if pkgs.stdenv.isDarwin - then mkDarwinShell pkgs - else mkLinuxShell pkgs; - in { - packages.x86_64-linux.default = mkDusklight (pkgsFor "x86_64-linux"); - - devShells = forAllSystems (system: { - default = mkDevShell (pkgsFor system); - }); + systems = forAllSystems perSystem; + in + { + packages = lib.mapAttrs (_: s: s.packages) systems; + devShells = lib.mapAttrs (_: s: s.devShells) systems; }; } From 1711251cf5e8e74ea920b4b5fa2f3c2bcb5a5208 Mon Sep 17 00:00:00 2001 From: Carl <213154372+SiggeMcKvack@users.noreply.github.com> Date: Sat, 23 May 2026 15:22:24 +0200 Subject: [PATCH 13/47] Android: derive versionName and versionCode from git tag (#1593) --- CMakeLists.txt | 14 ++++++++++++++ platforms/android/app/build.gradle | 7 +++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf69a8e2ac..0659dc9c53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ if (DUSK_VERSION_OVERRIDE) set(DUSK_WC_DESCRIBE "${DUSK_VERSION_OVERRIDE}") set(DUSK_VERSION_STRING "0.0.0.0") set(DUSK_SHORT_VERSION_STRING "0.0.0") + set(DUSK_VERSION_CODE "1") set(DUSK_WC_REVISION "") set(DUSK_WC_BRANCH "") set(DUSK_WC_DATE "") @@ -61,6 +62,9 @@ endif () if (DUSK_WC_DESCRIBE MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)([-+].*)?$") set(DUSK_SHORT_VERSION_STRING "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") + set(_ver_major ${CMAKE_MATCH_1}) + set(_ver_minor ${CMAKE_MATCH_2}) + set(_ver_patch ${CMAKE_MATCH_3}) set(DUSK_VERSION_TWEAK "0") if (DUSK_WC_DESCRIBE MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+-([0-9]+)(-dirty)?$") set(DUSK_VERSION_TWEAK "${CMAKE_MATCH_1}") @@ -68,10 +72,19 @@ if (DUSK_WC_DESCRIBE MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)([-+].*)?$") set(DUSK_VERSION_TWEAK "${CMAKE_MATCH_1}") endif () set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.${DUSK_VERSION_TWEAK}") + if(DUSK_VERSION_TWEAK GREATER 999) + set(_tweak 999) + else() + set(_tweak ${DUSK_VERSION_TWEAK}) + endif() + # encoding: major*1e7 + minor*1e5 + patch*1e3 + tweak; collision-free for major<210, minor<100, patch<100, tweak<=999 + math(EXPR DUSK_VERSION_CODE + "${_ver_major} * 10000000 + ${_ver_minor} * 100000 + ${_ver_patch} * 1000 + ${_tweak}") else () set(DUSK_WC_DESCRIBE "UNKNOWN-VERSION") set(DUSK_VERSION_STRING "0.0.0.0") set(DUSK_SHORT_VERSION_STRING "0.0.0") + set(DUSK_VERSION_CODE "1") endif () endif () @@ -79,6 +92,7 @@ endif () # Add version information to CI environment variables if(DEFINED ENV{GITHUB_ENV}) file(APPEND "$ENV{GITHUB_ENV}" "DUSK_VERSION=${DUSK_WC_DESCRIBE}\n") + file(APPEND "$ENV{GITHUB_ENV}" "DUSK_VERSION_CODE=${DUSK_VERSION_CODE}\n") endif() message(STATUS "Dusklight version set to ${DUSK_WC_DESCRIBE}") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") diff --git a/platforms/android/app/build.gradle b/platforms/android/app/build.gradle index 2dc8575f86..8527cd1f4d 100644 --- a/platforms/android/app/build.gradle +++ b/platforms/android/app/build.gradle @@ -2,6 +2,9 @@ plugins { id 'com.android.application' } +def versionNameStr = (System.getenv("DUSK_VERSION") ?: "v0.1.0").replaceFirst("^v", "") +def versionCodeInt = (System.getenv("DUSK_VERSION_CODE") ?: "100000").toInteger() + def duskRepoDir = rootProject.projectDir.parentFile.parentFile def duskGeneratedAssetsDir = layout.buildDirectory.dir('generated/assets/dusklight').get().asFile def syncDuskAssets = tasks.register('syncDuskAssets', Sync) { @@ -20,8 +23,8 @@ android { applicationId 'dev.twilitrealm.dusk' minSdk 26 targetSdk 36 - versionCode 1 - versionName '0.1.0' + versionCode versionCodeInt + versionName versionNameStr } buildTypes { From f11d4b14d9fb9b3b2f0cc0e4ccf62b1d996f004b Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Sat, 23 May 2026 16:03:45 -0400 Subject: [PATCH 14/47] have modified tears of light count use tphd_active function --- include/dusk/settings.h | 2 ++ src/d/actor/d_a_kytag04.cpp | 14 ++++++++++---- src/d/actor/d_a_obj_drop.cpp | 10 ++++++++-- src/d/d_com_inf_game.cpp | 8 ++++++-- src/dusk/settings.cpp | 5 +++++ 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 6adac9bb1f..3cdc85520b 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -242,6 +242,8 @@ struct UserSettings { UserSettings& getSettings(); +bool tphd_active(); + void registerSettings(); // Transient settings diff --git a/src/d/actor/d_a_kytag04.cpp b/src/d/actor/d_a_kytag04.cpp index 4afb24d4d2..b496d48c54 100644 --- a/src/d/actor/d_a_kytag04.cpp +++ b/src/d/actor/d_a_kytag04.cpp @@ -10,6 +10,7 @@ #include "d/actor/d_a_player.h" #include "d/d_com_inf_game.h" #include +#include static int daKytag04_Draw(kytag04_class* i_this) { dScnKy_env_light_c* kankyo = dKy_getEnvlight(); @@ -260,8 +261,10 @@ static int daKytag04_Create(fopAc_ac_c* i_this) { a_this->mNeedDropNum = i_this->current.angle.z & 0xFF; - #if DUSK_TPHD - a_this->mNeedDropNum = 12; + #if TARGET_PC + if (dusk::tphd_active()) { + a_this->mNeedDropNum = 12; + } #endif int phase_state = dComIfG_resLoad(&a_this->mPhase, "Kytag04"); @@ -270,8 +273,11 @@ static int daKytag04_Create(fopAc_ac_c* i_this) { a_this->mStageNo = (i_this->current.angle.z >> 8) & 0xFF; a_this->mExitID = fopAcM_GetParam(i_this) & 0xFF; a_this->mNeedDropNum = i_this->current.angle.z & 0xFF; - #if DUSK_TPHD - a_this->mNeedDropNum = 12; + + #if TARGET_PC + if (dusk::tphd_active()) { + a_this->mNeedDropNum = 12; + } #endif a_this->field_0x5b5 = fopAcM_GetParam(i_this) >> 0x10; diff --git a/src/d/actor/d_a_obj_drop.cpp b/src/d/actor/d_a_obj_drop.cpp index e1c5dcc583..e49d0790d6 100644 --- a/src/d/actor/d_a_obj_drop.cpp +++ b/src/d/actor/d_a_obj_drop.cpp @@ -26,6 +26,8 @@ daObjDrop_HIO_c l_HIO; #endif +#include + static void* searchParentSub(void* pproc, void* pdata) { daObjDrop_c* pdrop = (daObjDrop_c*)pdata; fopAc_ac_c* pym = (fopAc_ac_c*)pproc; @@ -103,8 +105,12 @@ void daObjDrop_c::dropGet() { dComIfGs_setLightDropNum(dComIfGp_getStartStageDarkArea(), num + 1); if (dComIfGp_getStartStageDarkArea() == 2 && - #if DUSK_TPHD - dComIfGs_getLightDropNum(dComIfGp_getStartStageDarkArea()) == 11) + #if TARGET_PC + ((dusk::tphd_active() && + dComIfGs_getLightDropNum(dComIfGp_getStartStageDarkArea()) == 11) || + !dusk::tphd_active() && + dComIfGs_getLightDropNum(dComIfGp_getStartStageDarkArea()) == 15 + )) #else dComIfGs_getLightDropNum(dComIfGp_getStartStageDarkArea()) == 15) #endif diff --git a/src/d/d_com_inf_game.cpp b/src/d/d_com_inf_game.cpp index 845076845b..ed69f51ace 100644 --- a/src/d/d_com_inf_game.cpp +++ b/src/d/d_com_inf_game.cpp @@ -25,6 +25,7 @@ #include "m_Do/m_Do_graphic.h" #include #include +#include void dComIfG_play_c::ct() { mWindowNum = 0; @@ -2571,8 +2572,11 @@ u8 dComIfG_getNowCalcRegion() { bool dComIfGp_isLightDropMapVisible() { for (int i = 0; i < 3; i++) { - #if DUSK_TPHD - if (dComIfGs_isLightDropGetFlag(i) != FALSE && dComIfGs_getLightDropNum(i) < 12) { + #if TARGET_PC + if (dComIfGs_isLightDropGetFlag(i) != FALSE && + ((dusk::tphd_active() && dComIfGs_getLightDropNum(i) < 12) || + (!dusk::tphd_active() && dComIfGs_getLightDropNum(i) < 16))) + { #else if (dComIfGs_isLightDropGetFlag(i) != FALSE && dComIfGs_getLightDropNum(i) < 16) { #endif diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index adb5e36064..40234a055d 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -176,6 +176,11 @@ UserSettings& getSettings() { return g_userSettings; } +bool tphd_active() { + const std::string& hdPath = g_userSettings.backend.hdContentPath; + return !hdPath.empty(); +} + void registerSettings() { // Video Register(g_userSettings.video.enableFullscreen); From 05393fe7aa43d32bd1e1b0c480539eb1d4fb4a44 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 23 May 2026 15:52:21 -0600 Subject: [PATCH 15/47] Update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index 10006618ee..f8e9b14fb0 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 10006618ee493f248b8597e4dfa1d2871d76a1d9 +Subproject commit f8e9b14fb09b8ec727635c9b80bd910223ce947a From a68f4ae1e652417901935709fc0516d61bb67eca Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 23 May 2026 16:20:11 -0600 Subject: [PATCH 16/47] Update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index f8e9b14fb0..1fd905e742 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit f8e9b14fb09b8ec727635c9b80bd910223ce947a +Subproject commit 1fd905e742b901262eb32bbbccd402052578cbe4 From e4caf20a7a68c669980f5c77b3440b6bcc5baed2 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 23 May 2026 16:27:28 -0600 Subject: [PATCH 17/47] Update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index 1fd905e742..c954d044ef 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 1fd905e742b901262eb32bbbccd402052578cbe4 +Subproject commit c954d044ef902e917fd3ff124253eaf4aa7227f2 From 7a3e8977f144c3bb0bab3da2a1035694c59ba8ef Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 23 May 2026 16:31:24 -0600 Subject: [PATCH 18/47] Disabled vendored Dawn for macOS --- CMakePresets.json | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakePresets.json b/CMakePresets.json index 0201c61ed6..ce84f84149 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -430,7 +430,6 @@ "x-macos-ci" ], "cacheVariables": { - "AURORA_DAWN_PROVIDER": "vendor", "CMAKE_OSX_ARCHITECTURES": "x86_64", "Rust_CARGO_TARGET": "x86_64-apple-darwin" } From db7e2a1e7b5d5a86d5070a31099b38f2a350b66b Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 23 May 2026 17:00:08 -0600 Subject: [PATCH 19/47] Update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index c954d044ef..db9923749f 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit c954d044ef902e917fd3ff124253eaf4aa7227f2 +Subproject commit db9923749fb6d24d84a0e6fc7ee875db8fa361af From ce0185adc462cb07bb81cec7da158e1c03b96b9a Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 23 May 2026 19:32:50 -0600 Subject: [PATCH 20/47] CI: Add ARM64 Windows and Linux builds (#1778) * ci: Try arm64 Linux/Windows builds * Update aurora * Explicitly set CMAKE_SYSTEM_PROCESSOR on Windows * Update build-appimage.sh for aarch64 * Set Rust_RUSTUP_INSTALL_MISSING_TARGET=ON * Use CMAKE_SYSTEM_PROCESSOR normalization for jpeg-turbo build too * MSVC ARM64 support for freeverb --- .github/workflows/build.yml | 24 ++++++++++++------------ CMakeLists.txt | 3 +++ CMakePresets.json | 11 +++++++++-- ci/build-appimage.sh | 3 ++- cmake/WindowsTargetProcessor.cmake | 9 +++++++++ extern/aurora | 2 +- libs/freeverb/denormals.h | 15 ++++++++++++++- 7 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 cmake/WindowsTargetProcessor.cmake diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 139b0654d3..a157fb8829 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,13 +22,13 @@ jobs: matrix: include: - name: GCC x86_64 - runner: ubuntu-latest + runner: ubuntu-24.04 preset: gcc artifact_arch: x86_64 - # - name: GCC aarch64 - # runner: ubuntu-24.04-arm - # preset: gcc - # artifact_arch: aarch64 + - name: GCC aarch64 + runner: ubuntu-24.04-arm + preset: gcc + artifact_arch: aarch64 # - name: Clang x86_64 # runner: ubuntu-latest # preset: clang @@ -221,12 +221,12 @@ jobs: msvc_arch: amd64 vcpkg_arch: x64 artifact_arch: x86_64 - # - name: MSVC arm64 - # runner: windows-11-arm - # preset: arm64-msvc - # msvc_arch: arm64 - # vcpkg_arch: arm64 - # artifact_arch: arm64 + - name: MSVC arm64 + runner: windows-latest + preset: arm64-msvc + msvc_arch: amd64_arm64 + vcpkg_arch: arm64 + artifact_arch: arm64 # - name: Clang x86_64 # runner: windows-latest # preset: clang @@ -255,7 +255,7 @@ jobs: - name: Install dependencies run: | choco install ninja - vcpkg install freetype:${{matrix.vcpkg_arch}}-windows-static zstd:${{matrix.vcpkg_arch}}-windows-static + vcpkg install freetype:${{matrix.vcpkg_arch}}-windows zstd:${{matrix.vcpkg_arch}}-windows - name: Configure CMake run: cmake --preset x-windows-ci-${{matrix.preset}} diff --git a/CMakeLists.txt b/CMakeLists.txt index 0659dc9c53..e93ba3cb22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,8 @@ project(dusklight LANGUAGES C CXX VERSION ${DUSK_VERSION_STRING}) if (APPLE) enable_language(OBJC OBJCXX) endif () +# Adjust CMAKE_SYSTEM_PROCESSOR on Windows to match compiler target +include(cmake/WindowsTargetProcessor.cmake) if (APPLE AND NOT TVOS AND CMAKE_SYSTEM_NAME STREQUAL tvOS) # ios.toolchain.cmake hack for SDL set(TVOS ON) @@ -168,6 +170,7 @@ if (DUSK_MOVIE_SUPPORT) endif () set(_jpeg_cmake_args -DCMAKE_INSTALL_PREFIX=${_jpeg_install_dir} + -DCMAKE_PROJECT_INCLUDE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/WindowsTargetProcessor.cmake -DENABLE_SHARED=OFF -DWITH_TURBOJPEG=ON -DWITH_JAVA=OFF diff --git a/CMakePresets.json b/CMakePresets.json index ce84f84149..1c7f989b0c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -33,7 +33,11 @@ "value": true }, "DUSK_SENTRY_DSN": "$env{SENTRY_DSN}", - "DUSK_SENTRY_ENVIRONMENT": "production" + "DUSK_SENTRY_ENVIRONMENT": "production", + "Rust_RUSTUP_INSTALL_MISSING_TARGET": { + "type": "BOOL", + "value": true + } } }, { @@ -489,7 +493,10 @@ "inherits": [ "x-windows-ci", "windows-arm64-msvc" - ] + ], + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "arm64-windows" + } } ], "buildPresets": [ diff --git a/ci/build-appimage.sh b/ci/build-appimage.sh index 6653876f71..b401dab253 100755 --- a/ci/build-appimage.sh +++ b/ci/build-appimage.sh @@ -6,6 +6,7 @@ fi build_dir="$PWD/build" linuxdeploy="$build_dir/linuxdeploy-$(uname -m).AppImage" +lib_dir="/usr/lib/$(uname -m)-linux-gnu" # Get linuxdeploy mkdir -p "$build_dir" @@ -23,4 +24,4 @@ cp platforms/freedesktop/dusklight.desktop build/appdir/usr/share/applications cd build/install VERSION="$DUSK_VERSION" NO_STRIP=1 "$linuxdeploy" \ - -l /usr/lib/x86_64-linux-gnu/libusb-1.0.so --appdir "$build_dir/appdir" --output appimage + -l "$lib_dir/libusb-1.0.so" --appdir "$build_dir/appdir" --output appimage diff --git a/cmake/WindowsTargetProcessor.cmake b/cmake/WindowsTargetProcessor.cmake new file mode 100644 index 0000000000..0fba82add5 --- /dev/null +++ b/cmake/WindowsTargetProcessor.cmake @@ -0,0 +1,9 @@ +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + if (CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64" OR CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64EC") + set(CMAKE_SYSTEM_PROCESSOR "ARM64") + elseif (CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "x64") + set(CMAKE_SYSTEM_PROCESSOR "AMD64") + elseif (CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "X86") + set(CMAKE_SYSTEM_PROCESSOR "X86") + endif () +endif () diff --git a/extern/aurora b/extern/aurora index db9923749f..924a88cb1e 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit db9923749fb6d24d84a0e6fc7ee875db8fa361af +Subproject commit 924a88cb1edafc7ddbfb285a7872c71831685495 diff --git a/libs/freeverb/denormals.h b/libs/freeverb/denormals.h index d68cb444f7..9193798abe 100644 --- a/libs/freeverb/denormals.h +++ b/libs/freeverb/denormals.h @@ -13,20 +13,33 @@ inline denormal_state denormals_enable() } inline void denormals_restore(denormal_state saved) { _mm_setcsr(saved); } -#elif defined(__aarch64__) || defined(_M_ARM64) +#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include +#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC)) +#include +#endif using denormal_state = uint64_t; inline denormal_state denormals_enable() { +#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC)) + denormal_state saved = static_cast(_ReadStatusReg(ARM64_FPCR)); + _WriteStatusReg(ARM64_FPCR, static_cast<__int64>(saved | (1ULL << 24))); // FZ + return saved; +#else denormal_state saved; asm volatile("mrs %0, fpcr" : "=r"(saved)); asm volatile("msr fpcr, %0" :: "r"(saved | (1ULL << 24))); // FZ return saved; +#endif } inline void denormals_restore(denormal_state saved) { +#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC)) + _WriteStatusReg(ARM64_FPCR, static_cast<__int64>(saved)); +#else asm volatile("msr fpcr, %0" :: "r"(saved)); +#endif } #elif defined(__arm__) || defined(_M_ARM) From beb4146f339d47fe1a11ce9c8a41143a6387baee Mon Sep 17 00:00:00 2001 From: SuperDude88 <82904174+SuperDude88@users.noreply.github.com> Date: Sat, 23 May 2026 21:41:18 -0400 Subject: [PATCH 21/47] Rework No Heart Drops (#1448) - Check the setting once in daItem_c::_daItem_create --- src/d/actor/d_a_obj_item.cpp | 5 +++ src/f_op/f_op_actor_mng.cpp | 60 ------------------------------------ 2 files changed, 5 insertions(+), 60 deletions(-) diff --git a/src/d/actor/d_a_obj_item.cpp b/src/d/actor/d_a_obj_item.cpp index e45de1933d..2c95c040be 100644 --- a/src/d/actor/d_a_obj_item.cpp +++ b/src/d/actor/d_a_obj_item.cpp @@ -268,6 +268,11 @@ int daItem_c::_daItem_create() { } m_itemNo = daItem_prm::getItemNo(this); +#if TARGET_PC + if (dusk::getSettings().game.noHeartDrops && isHeart(m_itemNo)) { + return cPhs_ERROR_e; + } +#endif BOOL flag = dItem_data::chkFlag(m_itemNo, 2); #if DEBUG diff --git a/src/f_op/f_op_actor_mng.cpp b/src/f_op/f_op_actor_mng.cpp index 208e868db2..dca07f98ba 100644 --- a/src/f_op/f_op_actor_mng.cpp +++ b/src/f_op/f_op_actor_mng.cpp @@ -1390,12 +1390,6 @@ fpc_ProcID fopAcM_createItemForPresentDemo(cXyz const* i_pos, int i_itemNo, u8 p JUT_ASSERT(3214, 0 <= i_itemNo && i_itemNo < 256); dComIfGp_event_setGtItm(i_itemNo); - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - if (i_itemNo == dItemNo_NONE_e) { OS_REPORT("プレゼントデモ用なのに「ハズレ」です![%d]\n", i_itemNo); // Even though it is for a Present Demo, it is a 'Miss'! return fpcM_ERROR_PROCESS_ID_e; @@ -1410,12 +1404,6 @@ fpc_ProcID fopAcM_createItemForTrBoxDemo(cXyz const* i_pos, int i_itemNo, int i_ JUT_ASSERT(3259, 0 <= i_itemNo && i_itemNo < 256); dComIfGp_event_setGtItm(i_itemNo); - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - if (i_itemNo == dItemNo_NONE_e) { OS_REPORT("ゲットデモ用なのに「ハズレ」です![%d]\n", i_itemNo); // Even though it is for a Get Demo, it is a 'Miss'! return fpcM_ERROR_PROCESS_ID_e; @@ -1541,12 +1529,6 @@ fpc_ProcID fopAcM_createItemFromTable(cXyz const* i_pos, int i_itemNo, int i_ite JUT_ASSERT(3655, 0 <= i_itemNo && i_itemNo <= 255 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255); // clang-format on - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - u8 tableNum; ItemTableList* tableList; tableList = (ItemTableList*)dComIfGp_getItemTable(); @@ -1590,12 +1572,6 @@ fpc_ProcID fopAcM_createDemoItem(const cXyz* i_pos, int i_itemNo, int i_itemBitN JUT_ASSERT(3824, 0 <= i_itemNo && i_itemNo < 256 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255); // clang-format on - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - if (i_itemNo == dItemNo_NONE_e) { return fpcM_ERROR_PROCESS_ID_e; } @@ -1607,12 +1583,6 @@ fpc_ProcID fopAcM_createDemoItem(const cXyz* i_pos, int i_itemNo, int i_itemBitN fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF, f32 i_speedY, int param_8) { - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - int _ = -1; u32 params = 0xFFFF0000 | param_8 << 8 | (i_itemNo & 0xFF); @@ -1629,12 +1599,6 @@ fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomN fpc_ProcID fopAcM_createItemForMidBoss(const cXyz* i_pos, int i_itemNo, int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, int param_6, int param_7) { - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - UNUSED(i_angle); UNUSED(param_6); fpc_ProcID ret = -1; @@ -1646,12 +1610,6 @@ fpc_ProcID fopAcM_createItemForMidBoss(const cXyz* i_pos, int i_itemNo, int i_ro fopAc_ac_c* fopAcM_createItemForDirectGet(const cXyz* i_pos, int i_itemNo, int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF, f32 i_speedY) { - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return NULL; - } - #endif - fopAc_ac_c* item = fopAcM_fastCreateItem(i_pos, i_itemNo, i_roomNo, i_angle, i_scale, &i_speedF, &i_speedY, -1, 0x7, NULL); fopAc_ac_c* ret = item; @@ -1661,12 +1619,6 @@ fopAc_ac_c* fopAcM_createItemForDirectGet(const cXyz* i_pos, int i_itemNo, int i fopAc_ac_c* fopAcM_createItemForSimpleDemo(const cXyz* i_pos, int i_itemNo, int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF, f32 i_speedY) { - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return NULL; - } - #endif - fopAc_ac_c* item = fopAcM_fastCreateItem(i_pos, i_itemNo, i_roomNo, i_angle, i_scale, &i_speedF, &i_speedY, -1, 0x4, NULL); fopAc_ac_c* ret = item; @@ -1679,12 +1631,6 @@ fpc_ProcID fopAcM_createItem(const cXyz* i_pos, int i_itemNo, int i_itemBitNo, i JUT_ASSERT(4067, 0 <= i_itemNo && i_itemNo < 256 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255); // clang-format on - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - if (i_itemNo == dItemNo_NONE_e) { return fpcM_ERROR_PROCESS_ID_e; } @@ -1749,12 +1695,6 @@ fopAc_ac_c* fopAcM_fastCreateItem2(const cXyz* i_pos, int i_itemNo, int i_itemBi JUT_ASSERT(4202, 0 <= i_itemNo && i_itemNo < 256 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255); // clang-format on - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return NULL; - } - #endif - csXyz item_angle(csXyz::Zero); if (i_itemNo == dItemNo_NONE_e) { From 533c76f4d65a4237fcf9b115f20d9a71fe23b306 Mon Sep 17 00:00:00 2001 From: Carlos Manuel Date: Sat, 23 May 2026 19:43:02 -0600 Subject: [PATCH 22/47] Add support for changing save location on Android (#1520) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Enabled the Android UI path for changing the data folder - Added Android MANAGE_EXTERNAL_STORAGE permission - Added an Android flow to request “All files access” before opening the folder picker - Added code path to resume the folder selection flow after returning from Android settings - Improved the Android write-probe error message to point users at the required permission --- .../android/app/src/main/AndroidManifest.xml | 1 + .../com/twilitrealm/dusk/DuskActivity.java | 81 +++++++++++++++++++ src/dusk/data.cpp | 7 ++ src/dusk/data.hpp | 2 +- 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/platforms/android/app/src/main/AndroidManifest.xml b/platforms/android/app/src/main/AndroidManifest.xml index f5a0365856..cd687963e1 100644 --- a/platforms/android/app/src/main/AndroidManifest.xml +++ b/platforms/android/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ + { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | @@ -185,9 +204,71 @@ public class DuskActivity extends SDLActivity { finishFolderDialog(Activity.RESULT_CANCELED, null); } }); + } + + private boolean requiresManageStoragePermission() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R; + } + + private boolean hasManageStoragePermission() { + return !requiresManageStoragePermission() || Environment.isExternalStorageManager(); + } + + private boolean requestManageStoragePermission() { + if (!requiresManageStoragePermission()) { + return true; + } + + awaitingManageStoragePermission = true; + runOnUiThread(() -> { + if (tryStartManageStorageIntent( + new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) + .setData(Uri.parse("package:" + getPackageName()))) || + tryStartManageStorageIntent( + new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))) + { + return; + } + + finishFolderDialogWithError("Unable to request Android file access permission"); + }); return true; } + private boolean tryStartManageStorageIntent(Intent intent) { + try { + startActivityForResult(intent, MANAGE_STORAGE_REQUEST_CODE); + return true; + } catch (ActivityNotFoundException e) { + Log.w(TAG, "Unable to open all-files access settings.", e); + return false; + } + } + + private void resumeFolderDialogAfterPermissionGrant() { + awaitingManageStoragePermission = false; + if (folderDialogUserdata == 0) { + return; + } + + if (hasManageStoragePermission()) { + openFolderDialog(); + return; + } + + finishFolderDialogWithError( + "Allow \"All files access\" for Dusklight before choosing a custom data folder"); + } + + private void finishFolderDialogWithError(String error) { + long userdata = folderDialogUserdata; + folderDialogUserdata = 0; + awaitingManageStoragePermission = false; + if (userdata != 0) { + nativeFolderDialogResult(userdata, null, error); + } + } + private void finishFolderDialog(int resultCode, Intent data) { long userdata = folderDialogUserdata; folderDialogUserdata = 0; diff --git a/src/dusk/data.cpp b/src/dusk/data.cpp index ce61965947..dc666dd236 100644 --- a/src/dusk/data.cpp +++ b/src/dusk/data.cpp @@ -525,7 +525,14 @@ bool validate_writable_data_path(const std::filesystem::path& path, std::string* try { io::FileStream::WriteAllText(probePath, "dusk"); } catch (const std::exception& e) { +#if defined(__ANDROID__) + set_error(errorOut, + fmt::format("{} could not write to the selected folder. On Android, allow " + "\"All files access\" for Dusklight and try again.", + AppName)); +#else set_error(errorOut, fmt::format("{} could not write to the selected folder.", AppName)); +#endif Log.warn("Failed write probe for custom data folder '{}': {}", io::fs_path_to_string(path), e.what()); return false; diff --git a/src/dusk/data.hpp b/src/dusk/data.hpp index c3ce495b46..bf20035a2d 100644 --- a/src/dusk/data.hpp +++ b/src/dusk/data.hpp @@ -15,7 +15,7 @@ #define DUSK_CAN_OPEN_DATA_FOLDER 0 #endif -#if (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || defined(__ANDROID__) +#if (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST) #define DUSK_CAN_CHANGE_DATA_FOLDER 0 #else #define DUSK_CAN_CHANGE_DATA_FOLDER 1 From e9a76282184ffb12a90d8ce2b94738573e4210ad Mon Sep 17 00:00:00 2001 From: Reilly Brogan Date: Sat, 23 May 2026 20:56:24 -0500 Subject: [PATCH 23/47] linux: Fix Wayland window associations (#1718) * linux: Fix Wayland window associations `src/m_Do/m_Do_main.cpp` uses `SDL_SetAppMetadata("Dusklight", DUSK_VERSION_STRING, "dev.twilitrealm.dusk")` which results in the Wayland appId getting set to `dev.twilitrealm.dusk`. Wayland compositors associate Wayland windows to desktop files by comparing the window appId against installed desktop files by the name of the desktop files. For a given window with appId `dev.twilitrealm.dusk` it would look for `dev.twilitrealm.dusk.desktop`, and because the desktop file is not named that the compositor is unable to correctly associate the window which results in the default Wayland icon being shown for the window. It also results in windows not being associated with the given launcher if the desktop file is pinned to a task management widget of some kind. Fix this by renaming the desktop file to `dev.twilitrealm.dusk.desktop`. Also while we're at it we can rename the icons as well to match the desktop file name, which isn't required but is a bit cleaner and will fit various packaging standards better. Signed-off-by: Reilly Brogan * Use dev.twilitrealm.dusk instead --------- Signed-off-by: Reilly Brogan --- ci/build-appimage.sh | 2 +- flake.nix | 8 ++++---- .../{dusklight.png => dev.twilitrealm.dusk.png} | Bin .../{dusklight.png => dev.twilitrealm.dusk.png} | Bin .../{dusklight.png => dev.twilitrealm.dusk.png} | Bin .../{dusklight.png => dev.twilitrealm.dusk.png} | Bin .../{dusklight.png => dev.twilitrealm.dusk.png} | Bin .../{dusklight.png => dev.twilitrealm.dusk.png} | Bin .../{dusklight.png => dev.twilitrealm.dusk.png} | Bin .../{dusklight.png => dev.twilitrealm.dusk.png} | Bin ...sklight.desktop => dev.twilitrealm.dusk.desktop} | 2 +- 11 files changed, 6 insertions(+), 6 deletions(-) rename platforms/freedesktop/1024x1024/apps/{dusklight.png => dev.twilitrealm.dusk.png} (100%) rename platforms/freedesktop/128x128/apps/{dusklight.png => dev.twilitrealm.dusk.png} (100%) rename platforms/freedesktop/16x16/apps/{dusklight.png => dev.twilitrealm.dusk.png} (100%) rename platforms/freedesktop/256x256/apps/{dusklight.png => dev.twilitrealm.dusk.png} (100%) rename platforms/freedesktop/32x32/apps/{dusklight.png => dev.twilitrealm.dusk.png} (100%) rename platforms/freedesktop/48x48/apps/{dusklight.png => dev.twilitrealm.dusk.png} (100%) rename platforms/freedesktop/512x512/apps/{dusklight.png => dev.twilitrealm.dusk.png} (100%) rename platforms/freedesktop/64x64/apps/{dusklight.png => dev.twilitrealm.dusk.png} (100%) rename platforms/freedesktop/{dusklight.desktop => dev.twilitrealm.dusk.desktop} (86%) diff --git a/ci/build-appimage.sh b/ci/build-appimage.sh index b401dab253..e12f1163b9 100755 --- a/ci/build-appimage.sh +++ b/ci/build-appimage.sh @@ -20,7 +20,7 @@ for install_path in build/install/*; do cp -r "$install_path" build/appdir/usr/bin done cp -r platforms/freedesktop/{16x16,32x32,48x48,64x64,128x128,256x256,512x512,1024x1024} build/appdir/usr/share/icons/hicolor -cp platforms/freedesktop/dusklight.desktop build/appdir/usr/share/applications +cp platforms/freedesktop/dev.twilitrealm.dusk.desktop build/appdir/usr/share/applications cd build/install VERSION="$DUSK_VERSION" NO_STRIP=1 "$linuxdeploy" \ diff --git a/flake.nix b/flake.nix index 90c0d21533..0a2156a4d5 100644 --- a/flake.nix +++ b/flake.nix @@ -248,11 +248,11 @@ runHook preInstall install -Dm755 dusklight "$out/bin/dusklight" cp -r "$src/res" "$out/bin/res" - install -Dm644 "$src/platforms/freedesktop/dusklight.desktop" \ - "$out/share/applications/dusklight.desktop" + install -Dm644 "$src/platforms/freedesktop/dev.twilitrealm.dusk.desktop" \ + "$out/share/applications/dev.twilitrealm.dusk.desktop" for size in 16 32 48 64 128 256 512 1024; do - install -Dm644 "$src/platforms/freedesktop/''${size}x''${size}/apps/dusklight.png" \ - "$out/share/icons/hicolor/''${size}x''${size}/apps/dusklight.png" + install -Dm644 "$src/platforms/freedesktop/''${size}x''${size}/apps/dev.twilitrealm.dusk.png" \ + "$out/share/icons/hicolor/''${size}x''${size}/apps/dev.twilitrealm.dusk.png" done runHook postInstall ''; diff --git a/platforms/freedesktop/1024x1024/apps/dusklight.png b/platforms/freedesktop/1024x1024/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/1024x1024/apps/dusklight.png rename to platforms/freedesktop/1024x1024/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/128x128/apps/dusklight.png b/platforms/freedesktop/128x128/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/128x128/apps/dusklight.png rename to platforms/freedesktop/128x128/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/16x16/apps/dusklight.png b/platforms/freedesktop/16x16/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/16x16/apps/dusklight.png rename to platforms/freedesktop/16x16/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/256x256/apps/dusklight.png b/platforms/freedesktop/256x256/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/256x256/apps/dusklight.png rename to platforms/freedesktop/256x256/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/32x32/apps/dusklight.png b/platforms/freedesktop/32x32/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/32x32/apps/dusklight.png rename to platforms/freedesktop/32x32/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/48x48/apps/dusklight.png b/platforms/freedesktop/48x48/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/48x48/apps/dusklight.png rename to platforms/freedesktop/48x48/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/512x512/apps/dusklight.png b/platforms/freedesktop/512x512/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/512x512/apps/dusklight.png rename to platforms/freedesktop/512x512/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/64x64/apps/dusklight.png b/platforms/freedesktop/64x64/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/64x64/apps/dusklight.png rename to platforms/freedesktop/64x64/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/dusklight.desktop b/platforms/freedesktop/dev.twilitrealm.dusk.desktop similarity index 86% rename from platforms/freedesktop/dusklight.desktop rename to platforms/freedesktop/dev.twilitrealm.dusk.desktop index 4f6ba034a9..b1ad2831a2 100644 --- a/platforms/freedesktop/dusklight.desktop +++ b/platforms/freedesktop/dev.twilitrealm.dusk.desktop @@ -3,7 +3,7 @@ Name=Dusklight GenericName=Dusklight Comment=PC port of a classic adventure game Exec=dusklight -Icon=dusklight +Icon=dev.twilitrealm.dusk Terminal=false Type=Application Categories=Game; From 114e6e60bb4239d14cfdf0ff3a972eff6addc7a7 Mon Sep 17 00:00:00 2001 From: Irastris Date: Sat, 23 May 2026 22:15:32 -0400 Subject: [PATCH 24/47] build.yml revisions (#1744) * build.yml revisions * Undo push and PR changes --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a157fb8829..7c0cc1b846 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,10 @@ on: - '*LICENSE' pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('run-{0}', github.run_id) }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + env: SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: "sccache" From ac316f6288821a16e4cc287bc5992b32a25f5087 Mon Sep 17 00:00:00 2001 From: Olivia!! Date: Sun, 24 May 2026 04:16:02 +0200 Subject: [PATCH 25/47] fixes empty howl tab near certain actors (#1771) When howling suns song near actors that arent howl stones, an empty howl tab was shown. This fixes the issue by duplicating a check for sun's song howling into the relevant code branch. --- src/d/actor/d_a_alink_wolf.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/d/actor/d_a_alink_wolf.inc b/src/d/actor/d_a_alink_wolf.inc index 67fde3a7a7..43b60e9d42 100644 --- a/src/d/actor/d_a_alink_wolf.inc +++ b/src/d/actor/d_a_alink_wolf.inc @@ -4005,7 +4005,13 @@ int daAlink_c::procWolfHowlDemoInit() { } else if (name == fpcNm_Tag_WaraHowl_e) { mZ2WolfHowlMgr.setCorrectCurve(static_cast(field_0x27f4)->getTuneId()); } else { + #if TARGET_PC + if (mZ2WolfHowlMgr.getCorrectCurveID() != 9) { + mZ2WolfHowlMgr.setCorrectCurve(-1); + } + #else mZ2WolfHowlMgr.setCorrectCurve(-1); + #endif } } else { #if TARGET_PC From edc4aa0be4551b261ba606be99ed14b132e3b6c9 Mon Sep 17 00:00:00 2001 From: Olivia!! Date: Sun, 24 May 2026 05:31:19 +0200 Subject: [PATCH 26/47] Fixes linux credits crash (#1780) alpha comparison mID is u16, but indexes into a 256-item table. this causes MAT3 materials with an alpha comparison index of 0xFF crashes on linux closes #1073 closes #1531 --- libs/JSystem/include/JSystem/J3DGraphBase/J3DMatBlock.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/JSystem/include/JSystem/J3DGraphBase/J3DMatBlock.h b/libs/JSystem/include/JSystem/J3DGraphBase/J3DMatBlock.h index a959609f45..859bd6943e 100644 --- a/libs/JSystem/include/JSystem/J3DGraphBase/J3DMatBlock.h +++ b/libs/JSystem/include/JSystem/J3DGraphBase/J3DMatBlock.h @@ -1575,6 +1575,12 @@ struct J3DAlphaComp { u8 getRef1() const { return mRef1; } void load() const { +#ifdef AVOID_UB + if (mID > 255) { + J3DGDSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_OR, GX_ALWAYS, 0); + return; + } +#endif J3DGDSetAlphaCompare((GXCompare)getComp0(), mRef0, (GXAlphaOp)getOp(), (GXCompare)getComp1(), mRef1); } From 0504f1dda7b9d9317a452b4ce4238bf7d6cf172b Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 05:31:15 -0600 Subject: [PATCH 27/47] Uodate aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index 924a88cb1e..3b06e240a3 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 924a88cb1edafc7ddbfb285a7872c71831685495 +Subproject commit 3b06e240a34b15630562ffa4bb00201b91ce2a39 From af162bbd0a09bc4fef4bf54e33a4a912763e83a2 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sun, 24 May 2026 09:41:59 -0700 Subject: [PATCH 28/47] New Depth of Field implementation (#1773) --- include/dusk/settings.h | 14 ++- src/d/d_menu_map_common.cpp | 2 +- src/dusk/config.cpp | 1 + src/dusk/settings.cpp | 4 +- src/dusk/ui/graphics_tuner.cpp | 16 ++++ src/dusk/ui/graphics_tuner.hpp | 1 + src/dusk/ui/preset.cpp | 2 + src/dusk/ui/settings.cpp | 21 +++-- src/m_Do/m_Do_graphic.cpp | 158 +++++++++++++++++++++++++++------ 9 files changed, 183 insertions(+), 36 deletions(-) diff --git a/include/dusk/settings.h b/include/dusk/settings.h index fb3704fd30..e842c1177a 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -15,6 +15,12 @@ enum class BloomMode : int { Dusk = 2, }; +enum class DepthOfFieldMode : int { + Off = 0, + Classic = 1, + Dusk = 2, +}; + enum class Resampler : int { Bilinear = 0, Area = 1, @@ -58,6 +64,12 @@ struct ConfigEnumRange { static constexpr auto max = BloomMode::Dusk; }; +template <> +struct ConfigEnumRange { + static constexpr auto min = DepthOfFieldMode::Off; + static constexpr auto max = DepthOfFieldMode::Dusk; +}; + template <> struct ConfigEnumRange { static constexpr auto min = Resampler::Bilinear; @@ -160,13 +172,13 @@ struct UserSettings { // Graphics ConfigVar bloomMode; ConfigVar bloomMultiplier; + ConfigVar depthOfFieldMode; ConfigVar disableWaterRefraction; ConfigVar enableTextureReplacements; ConfigVar enableFrameInterpolation; ConfigVar internalResolutionScale; ConfigVar shadowResolutionMultiplier; ConfigVar resampler; - ConfigVar enableDepthOfField; ConfigVar enableMapBackground; ConfigVar disableCutscenePillarboxing; diff --git a/src/d/d_menu_map_common.cpp b/src/d/d_menu_map_common.cpp index 6c8e0b76eb..4168673704 100644 --- a/src/d/d_menu_map_common.cpp +++ b/src/d/d_menu_map_common.cpp @@ -750,7 +750,7 @@ void dMenuMapCommon_c::debugIcon() { #if TARGET_PC static constexpr struct { - std::string stagename; + std::string_view stagename; u8 switch_no; s8 region_id; u8 save_id; diff --git a/src/dusk/config.cpp b/src/dusk/config.cpp index caa65bc0b4..c0edd84591 100644 --- a/src/dusk/config.cpp +++ b/src/dusk/config.cpp @@ -172,6 +172,7 @@ namespace dusk::config { template class ConfigImpl; template class ConfigImpl; template class ConfigImpl; + template class ConfigImpl; template class ConfigImpl; template class ConfigImpl; template class ConfigImpl; diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 4ba40d921b..7c4e42763a 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -60,13 +60,13 @@ UserSettings g_userSettings = { // Graphics .bloomMode {"game.bloomMode", BloomMode::Dusk}, .bloomMultiplier {"game.bloomMultiplier", 1.0f}, + .depthOfFieldMode{"game.depthOfFieldMode", DepthOfFieldMode::Dusk}, .disableWaterRefraction {"game.disableWaterRefraction", false}, .enableTextureReplacements {"game.enableTextureReplacements", true}, .enableFrameInterpolation {"game.enableFrameInterpolation", FrameInterpMode::Off}, .internalResolutionScale {"game.internalResolutionScale", 0}, .shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1}, .resampler {"game.resampler", Resampler::Bilinear}, - .enableDepthOfField {"game.enableDepthOfField", true}, .enableMapBackground {"game.enableMapBackground", true}, .disableCutscenePillarboxing {"game.disableCutscenePillarboxing", false}, @@ -225,12 +225,12 @@ void registerSettings() { Register(g_userSettings.game.enableDiscordPresence); Register(g_userSettings.game.bloomMode); Register(g_userSettings.game.bloomMultiplier); + Register(g_userSettings.game.depthOfFieldMode); Register(g_userSettings.game.disableWaterRefraction); Register(g_userSettings.game.enableTextureReplacements); Register(g_userSettings.game.internalResolutionScale); Register(g_userSettings.game.resampler); Register(g_userSettings.game.shadowResolutionMultiplier); - Register(g_userSettings.game.enableDepthOfField); Register(g_userSettings.game.enableMapBackground); Register(g_userSettings.game.disableCutscenePillarboxing); Register(g_userSettings.game.enableFastIronBoots); diff --git a/src/dusk/ui/graphics_tuner.cpp b/src/dusk/ui/graphics_tuner.cpp index 0b75b27a89..8cea89e984 100644 --- a/src/dusk/ui/graphics_tuner.cpp +++ b/src/dusk/ui/graphics_tuner.cpp @@ -52,6 +52,8 @@ int get_value(GraphicsOption option) { return std::clamp( static_cast(getSettings().game.bloomMultiplier.getValue() * 100.0f + 0.5f), 0, 100); + case GraphicsOption::DepthOfFieldMode: + return static_cast(getSettings().game.depthOfFieldMode.getValue()); } return 0; } @@ -85,6 +87,10 @@ void set_value(GraphicsOption option, int value) { getSettings().game.bloomMode.setValue(static_cast(std::clamp( value, static_cast(BloomMode::Off), static_cast(BloomMode::Dusk)))); break; + case GraphicsOption::DepthOfFieldMode: + getSettings().game.depthOfFieldMode.setValue(static_cast(std::clamp( + value, static_cast(DepthOfFieldMode::Off), static_cast(DepthOfFieldMode::Dusk)))); + break; case GraphicsOption::BloomMultiplier: getSettings().game.bloomMultiplier.setValue(std::clamp(value, 0, 100) / 100.0f); break; @@ -214,6 +220,16 @@ Rml::String format_graphics_setting_value(GraphicsOption option, int value) { return "Dusklight"; } break; + case GraphicsOption::DepthOfFieldMode: + switch (static_cast(value)) { + case DepthOfFieldMode::Off: + return "Off"; + case DepthOfFieldMode::Classic: + return "Classic"; + case DepthOfFieldMode::Dusk: + return "Dusklight"; + } + break; case GraphicsOption::BloomMultiplier: return fmt::format("{}%", value); } diff --git a/src/dusk/ui/graphics_tuner.hpp b/src/dusk/ui/graphics_tuner.hpp index 6f8f113d13..2f5b9c5611 100644 --- a/src/dusk/ui/graphics_tuner.hpp +++ b/src/dusk/ui/graphics_tuner.hpp @@ -45,6 +45,7 @@ enum class GraphicsOption { Resampler, BloomMode, BloomMultiplier, + DepthOfFieldMode, }; Rml::String format_graphics_setting_value(GraphicsOption option, int value); diff --git a/src/dusk/ui/preset.cpp b/src/dusk/ui/preset.cpp index d1a7d1ab79..4d5950f1f4 100644 --- a/src/dusk/ui/preset.cpp +++ b/src/dusk/ui/preset.cpp @@ -14,6 +14,7 @@ void applyPresetClassic() { auto& s = getSettings(); s.video.lockAspectRatio.setValue(true); s.game.bloomMode.setValue(BloomMode::Classic); + s.game.depthOfFieldMode.setValue(DepthOfFieldMode::Classic); s.game.enableAchievementToasts.setValue(false); s.game.enableControllerToasts.setValue(false); s.game.internalResolutionScale.setValue(1); @@ -44,6 +45,7 @@ void applyPresetDusk() { s.game.enableFrameInterpolation.setValue(FrameInterpMode::Unlimited); s.game.sunsSong.setValue(true); s.game.bloomMode.setValue(BloomMode::Dusk); + s.game.depthOfFieldMode.setValue(DepthOfFieldMode::Dusk); s.game.internalResolutionScale.setValue(0); s.game.shadowResolutionMultiplier.setValue(4); s.game.enableGyroAim.setValue(true); diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 42f205f12c..3be9ce25e2 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -370,6 +370,9 @@ const Rml::String kBloomHelpText = "a higher-quality bloom pass."; const Rml::String kBloomBrightnessHelpText = "Configure bloom intensity. Higher values make bright areas glow more strongly."; +const Rml::String kDepthOfFieldHelpText = + "Configure the post-processing depth-of-field effect. Classic uses the original depth-of-field pass;" + " Dusklight uses a higher-quality depth-of-field pass."; const Rml::String kUnlockFramerateHelpText = "
Uses inter-frame interpolation to enable higher frame rates.

May introduce minor " "visual artifacts or animation glitches."; @@ -842,7 +845,18 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { .valueMax = 100, .defaultValue = 100, .step = 10, - }, mPrelaunch); + }, + mPrelaunch); + graphics_tuner_control(*this, leftPane, rightPane, getSettings().game.depthOfFieldMode, + GraphicsTunerProps{ + .option = GraphicsOption::DepthOfFieldMode, + .title = "Depth of Field", + .helpText = kDepthOfFieldHelpText, + .valueMin = static_cast(DepthOfFieldMode::Off), + .valueMax = static_cast(DepthOfFieldMode::Dusk), + .defaultValue = static_cast(DepthOfFieldMode::Classic), + }, + mPrelaunch); leftPane.add_section("Rendering"); config_bool_select(leftPane, rightPane, getSettings().game.enableTextureReplacements, @@ -884,11 +898,6 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { config_int_select(leftPane, rightPane, getSettings().video.maxFrameRate, "Framerate Cap", "Limit the framerate to the specified value.", 30, 540, 1, [] { return getSettings().game.enableFrameInterpolation.getValue() != FrameInterpMode::Capped; }); - config_bool_select(leftPane, rightPane, getSettings().game.enableDepthOfField, - { - .key = "Enable Depth of Field", - .helpText = "Render a blurring effect for out-of-focus areas in some situations. May impact performance." - }); config_bool_select(leftPane, rightPane, getSettings().game.enableMapBackground, { .key = "Enable Mini-Map Shadows", diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index 65a2b5b95a..caf823b065 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -928,6 +928,103 @@ void mDoGph_drawFilterQuad(s8 param_0, s8 param_1) { GXTexCoord2s8(0, 1); GXEnd(); } + +static void CopyToTexObj(GXTexObj* pDst, uintptr_t texID, u16 dstWidth, u16 dstHeight, GXTexFmt dstFmt = GX_TF_RGBA8) { + GXSetTexCopyDst(dstWidth, dstHeight, dstFmt, FALSE); + GXCopyTex((void*)texID, false); + GXInitTexObj(pDst, (void*)texID, dstWidth, dstHeight, dstFmt, GX_CLAMP, GX_CLAMP, GX_FALSE); + GXInitTexObjLOD(pDst, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); +} + +static void drawDepth_blurTex(TGXTexObj &dst) { + u32 hw = u32(JUTVideo::getManager()->getRenderWidth()) >> 1; + u32 hh = u32(JUTVideo::getManager()->getRenderHeight()) >> 1; + + Mtx44 ortho; + C_MTXOrtho(ortho, 0.0f, hh, 0.0f, hw, 0.0f, 10.0f); + GXLoadPosMtxImm(cMtx_getIdentity(), GX_PNMTX0); + GXSetProjection(ortho, GX_ORTHOGRAPHIC); + GXSetCurrentMtx(GX_PNMTX0); + GXClearVtxDesc(); + GXSetVtxDesc(GX_VA_POS, GX_DIRECT); + GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S8, 0); + + GXCreateFrameBuffer(hw, hh); + + auto divCopySrc = [&](int divNo) { + u32 w = u32(hw) >> divNo, h = u32(hh) >> divNo; + GXSetTexCopySrc(0, 0, w, h); + }; + + enum { MaxTexNum = 4 }; + TGXTexObj tmpTex[MaxTexNum]; + auto divCopyTex = [&](uintptr_t texNo, int divNo) -> GXTexObj* { + u32 w = u32(hw) >> divNo, h = u32(hh) >> divNo; + CopyToTexObj(&tmpTex[texNo], texNo, w, h); + return &tmpTex[texNo]; + }; + + auto divQuad = [&](int divNo) { + u32 w = u32(hw) >> divNo, h = u32(hh) >> divNo; + f32 x0 = 0.0f, y0 = 0.0f; + f32 x1 = w, y1 = h; + GXBegin(GX_QUADS, GX_VTXFMT0, 4); + GXPosition3f32(x0, y0, -5); + GXTexCoord2s8(0, 0); + GXPosition3f32(x1, y0, -5); + GXTexCoord2s8(1, 0); + GXPosition3f32(x1, y1, -5); + GXTexCoord2s8(1, 1); + GXPosition3f32(x0, y1, -5); + GXTexCoord2s8(0, 1); + GXEnd(); + }; + + u32 texMtxID = GX_TEXMTX0; + int angle = 0; + float blurScale = 0.003f; + GXSetNumTexGens(8); + GXSetNumTevStages(8); + for (int stage = 0; stage < 8; stage++) { + GXSetTexCoordGen((GXTexCoordID)stage, GX_TG_MTX2x4, GX_TG_TEX0, texMtxID); + mDoMtx_stack_c::transS( + (blurScale * cM_scos(angle)) * mDoGph_gInf_c::getInvScale(), blurScale * cM_ssin(angle), 0.0f); + GXLoadTexMtxImm(mDoMtx_stack_c::get(), texMtxID, GX_MTX2x4); + texMtxID += 3; + angle += 0x2000; + + GXTevStageID tevStage = (GXTevStageID)stage; + GXSetTevOrder(tevStage, (GXTexCoordID)stage, GX_TEXMAP1, GX_COLOR_NULL); + GXSetTevColorIn(tevStage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_A1, stage == 0 ? GX_CC_ZERO : GX_CC_CPREV); + GXSetTevColorOp(tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + GXSetTevAlphaIn(tevStage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); + GXSetTevAlphaOp(tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + } + GXSetTevColor(GX_TEVREG1, {0, 0, 0, 256 / 8}); + + // assume the input tex obj is in GX_TEXMAP1 + int divNum = 3; + for (int i = 0; i < divNum; i++) { + // Apply blur filter. + divQuad(i); + + // Copy to next layer. + divCopySrc(i); + + // Set up for the next pass down. + GXTexObj* blurTex = divCopyTex(i, i + 1); + GXLoadTexObj(blurTex, GX_TEXMAP1); + } + + // upsample back to half-res buffer 0 + divQuad(0); + divCopySrc(0); + CopyToTexObj(&dst, 100, hw, hh); + + GXRestoreFrameBuffer(); +} #endif static void drawDepth2(view_class* param_0, view_port_class* param_1, int param_2) { @@ -1081,6 +1178,21 @@ static void drawDepth2(view_class* param_0, view_port_class* param_1, int param_ } #endif +#if TARGET_PC + if (dusk::getSettings().game.depthOfFieldMode.getValue() == dusk::DepthOfFieldMode::Off) + return; + + if (!(l_tevColor0.a > -255 && sp8 == 1)) + return; + + TGXTexObj blurTex; + if (dusk::getSettings().game.depthOfFieldMode.getValue() == dusk::DepthOfFieldMode::Dusk) + { + drawDepth_blurTex(blurTex); + GXLoadTexObj(&blurTex, GX_TEXMAP1); + } +#endif + GXSetTevColorS10(GX_TEVREG0, l_tevColor0); GXSetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_ALPHA, GX_CH_GREEN, GX_CH_BLUE, GX_CH_RED); GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP3); @@ -1129,37 +1241,42 @@ static void drawDepth2(view_class* param_0, view_port_class* param_1, int param_ param_1->x_orig + param_1->width, 0.0f, 10.0f); GXLoadPosMtxImm(cMtx_getIdentity(), 0); - #if DEBUG +#if DEBUG mDoMtx_stack_c::transS(g_kankyoHIO.navy.demo_focus_offset_x, g_kankyoHIO.navy.demo_focus_offset_y, 0.0f); - #else +#else mDoMtx_stack_c::transS(0.0025f, 0.0025f, 0.0f); - #endif - GXLoadTexMtxImm(mDoMtx_stack_c::get(), 0x1e, GX_MTX2x4); +#endif + GXLoadTexMtxImm(mDoMtx_stack_c::get(), GX_TEXMTX0, GX_MTX2x4); - #if DEBUG +#if DEBUG mDoMtx_stack_c::transS(-g_kankyoHIO.navy.demo_focus_offset_x, -g_kankyoHIO.navy.demo_focus_offset_y, 0.0f); - #else +#else mDoMtx_stack_c::transS(-0.0025f, -0.0025f, 0.0f); - #endif - GXLoadTexMtxImm(mDoMtx_stack_c::get(), 0x21, GX_MTX2x4); +#endif + GXLoadTexMtxImm(mDoMtx_stack_c::get(), GX_TEXMTX1, GX_MTX2x4); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_S16, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_POS_XYZ, GX_S8, 0); - GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 0x3c); - GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX0, 0x1e); - GXSetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_TEX0, 0x21); + GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX0); + GXSetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX1); GXSetNumChans(0); GXSetNumTexGens(3); GXSetNumTevStages(4); - GXSetProjection(ortho, GX_ORTHOGRAPHIC); - GXSetCurrentMtx(0); -#ifdef TARGET_PC - if (dusk::getSettings().game.enableDepthOfField) + GXSetProjection(ortho, GX_ORTHOGRAPHIC); + GXSetCurrentMtx(GX_PNMTX0); + +#if TARGET_PC + if (dusk::getSettings().game.depthOfFieldMode.getValue() == dusk::DepthOfFieldMode::Dusk) { + GXSetNumTevStages(3); + GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR_NULL); + } #endif + if (l_tevColor0.a > -255 && sp8 == 1) { GXBegin(GX_QUADS, GX_VTXFMT0, 4); GXPosition3s16(x_orig, y_orig_pos, -5); @@ -1334,19 +1451,8 @@ void mDoGph_gInf_c::bloom_c::remove() { } #if TARGET_PC -static void CopyToTexObj(GXTexObj* pDst, uintptr_t texID, u16 dstWidth, u16 dstHeight, GXTexFmt dstFmt = GX_TF_RGBA8) { - GXSetTexCopyDst(dstWidth, dstHeight, dstFmt, FALSE); - GXCopyTex((void*)texID, false); - GXInitTexObj(pDst, (void*)texID, dstWidth, dstHeight, dstFmt, GX_CLAMP, GX_CLAMP, GX_FALSE); - GXInitTexObjLOD(pDst, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); -} - void mDoGph_gInf_c::bloom_c::draw2() { ZoneScoped; - // if (!dusk::getSettings().game.enableBloom) { - // return; - // } - bool enabled = mEnable; if (mMonoColor.a == 0 && !enabled) return; From a6376368eefc76f9b11aaefa92b3a562611371dc Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 24 May 2026 18:43:00 +0200 Subject: [PATCH 29/47] String safety (#1548) * Array size UB fixes * Fix ShieldD * Remove (almost) all unsafe strcpy calls Bunch of macros. C arrays are easy enough and just need a different call. For various cases where a char* is passed around bare, I've made a TEXT_SPAN macro that can store a length too for bounds checking. * Move crash handling in safe string operations to separate TU * strcat safe version * sprintf made safe too * Fix compile --- files.cmake | 1 + include/d/actor/d_a_player.h | 2 +- include/d/d_camera.h | 22 ++- include/d/d_com_inf_game.h | 4 +- include/d/d_file_sel_info.h | 8 +- include/d/d_file_select.h | 6 +- include/d/d_menu_save.h | 4 +- include/d/d_meter2_info.h | 12 +- include/d/d_msg_class.h | 15 +- include/d/d_msg_object.h | 14 +- include/d/d_msg_string_base.h | 4 +- include/d/d_msg_unit.h | 4 +- include/d/d_name.h | 6 +- include/d/d_save.h | 14 +- include/d/d_stage.h | 2 +- include/dusk/string.hpp | 126 +++++++++++--- include/f_op/f_op_msg_mng.h | 2 +- .../include/JSystem/J2DGraph/J2DTextBox.h | 4 +- .../src/J2DGraph/J2DMaterialFactory.cpp | 6 +- libs/JSystem/src/J2DGraph/J2DTextBox.cpp | 8 +- libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp | 4 +- libs/JSystem/src/JAHostIO/JAHioNode.cpp | 4 +- libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp | 8 +- libs/JSystem/src/JKernel/JKRFileCache.cpp | 1 + libs/JSystem/src/JKernel/JKRFileLoader.cpp | 5 +- libs/JSystem/src/JKernel/JKRThread.cpp | 4 +- libs/JSystem/src/JUtility/JUTConsole.cpp | 11 +- libs/JSystem/src/JUtility/JUTException.cpp | 6 +- src/Z2AudioLib/Z2SoundMgr.cpp | 2 +- src/d/actor/d_a_alink.cpp | 1 + src/d/actor/d_a_alink_demo.inc | 4 +- src/d/actor/d_a_bg_obj.cpp | 31 ++-- src/d/actor/d_a_door_dbdoor00.cpp | 2 +- src/d/actor/d_a_door_knob00.cpp | 2 +- src/d/actor/d_a_door_shutter.cpp | 4 +- src/d/actor/d_a_door_spiral.cpp | 4 +- src/d/actor/d_a_movie_player.cpp | 2 +- src/d/actor/d_a_npc_hanjo.cpp | 6 +- src/d/actor/d_a_npc_saru.cpp | 6 +- src/d/actor/d_a_npc_shop0.cpp | 6 +- src/d/actor/d_a_npc_yelia.cpp | 6 +- src/d/actor/d_a_npc_ykw.cpp | 8 +- src/d/actor/d_a_obj_flag.cpp | 2 +- src/d/actor/d_a_obj_flag2.cpp | 4 +- src/d/actor/d_a_obj_flag3.cpp | 4 +- src/d/actor/d_a_obj_gra2.cpp | 7 +- src/d/actor/d_a_obj_sekizoa.cpp | 14 +- src/d/actor/d_a_set_bgobj.cpp | 2 +- src/d/actor/d_a_tag_evt.cpp | 4 +- src/d/actor/d_a_tag_msg.cpp | 4 +- src/d/actor/d_a_title.cpp | 2 +- src/d/d_bg_parts.cpp | 10 +- src/d/d_com_inf_game.cpp | 6 +- src/d/d_com_static.cpp | 4 +- src/d/d_debug_camera.cpp | 4 +- src/d/d_ev_camera.cpp | 160 ++++++++++-------- src/d/d_event.cpp | 12 +- src/d/d_event_manager.cpp | 16 +- src/d/d_file_sel_info.cpp | 23 +-- src/d/d_file_select.cpp | 10 +- src/d/d_menu_calibration.cpp | 13 +- src/d/d_menu_collect.cpp | 42 ++--- src/d/d_menu_dmap.cpp | 12 +- src/d/d_menu_fishing.cpp | 17 +- src/d/d_menu_fmap.cpp | 47 ++--- src/d/d_menu_fmap2D.cpp | 8 +- src/d/d_menu_insect.cpp | 8 +- src/d/d_menu_item_explain.cpp | 4 +- src/d/d_menu_letter.cpp | 12 +- src/d/d_menu_option.cpp | 4 +- src/d/d_menu_ring.cpp | 2 +- src/d/d_menu_save.cpp | 8 +- src/d/d_menu_skill.cpp | 6 +- src/d/d_meter2_draw.cpp | 12 +- src/d/d_meter2_info.cpp | 20 ++- src/d/d_meter_button.cpp | 26 +-- src/d/d_meter_string.cpp | 4 +- src/d/d_msg_class.cpp | 128 +++++++------- src/d/d_msg_object.cpp | 34 ++-- src/d/d_msg_scrn_3select.cpp | 12 +- src/d/d_msg_scrn_base.cpp | 6 +- src/d/d_msg_scrn_explain.cpp | 14 +- src/d/d_msg_string_base.cpp | 6 +- src/d/d_msg_unit.cpp | 44 ++--- src/d/d_name.cpp | 14 +- src/d/d_particle.cpp | 2 +- src/d/d_s_play.cpp | 8 +- src/d/d_s_room.cpp | 2 +- src/d/d_save.cpp | 2 +- src/d/d_stage.cpp | 4 +- src/d/d_timer.cpp | 8 +- src/dusk/imgui/ImGuiProcessOverlay.cpp | 2 +- src/dusk/imgui/ImGuiSaveEditor.cpp | 12 +- src/dusk/string.cpp | 85 ++++++++++ src/dusk/ui/editor.cpp | 4 +- src/f_ap/f_ap_game.cpp | 2 +- src/f_op/f_op_actor.cpp | 4 +- src/f_op/f_op_actor_mng.cpp | 6 +- src/f_op/f_op_msg_mng.cpp | 2 +- src/m_Do/m_Do_main.cpp | 2 +- 100 files changed, 781 insertions(+), 546 deletions(-) create mode 100644 src/dusk/string.cpp diff --git a/files.cmake b/files.cmake index eb8c62bee5..e42a626ffe 100644 --- a/files.cmake +++ b/files.cmake @@ -1439,6 +1439,7 @@ set(DUSK_FILES src/dusk/logging.cpp src/dusk/settings.cpp src/dusk/speedrun.cpp + src/dusk/string.cpp src/dusk/stubs.cpp src/dusk/update_check.cpp src/dusk/update_check.hpp diff --git a/include/d/actor/d_a_player.h b/include/d/actor/d_a_player.h index 41a11ff662..698e3b03cd 100644 --- a/include/d/actor/d_a_player.h +++ b/include/d/actor/d_a_player.h @@ -77,7 +77,7 @@ private: #define PLAYER_CREATE_ANM_HEAP_F(heap, type, fmt, ...) \ { \ char pcah_name_buf[32]; \ - sprintf(pcah_name_buf, fmt, ##__VA_ARGS__); \ + snprintf(pcah_name_buf, sizeof(pcah_name_buf), fmt, ##__VA_ARGS__); \ (heap).createHeap(type, pcah_name_buf); \ \ } diff --git a/include/d/d_camera.h b/include/d/d_camera.h index c481262a46..b694933461 100644 --- a/include/d/d_camera.h +++ b/include/d/d_camera.h @@ -444,8 +444,7 @@ public: /* 0x1C */ f32 mFovy; /* 0x20 */ f32 mBank; /* 0x24 */ fopAc_ac_c* mRelActor; - /* 0x28 */ char mRelUseMask; - /* 0x29 */ char field_0x29; + /* 0x28 */ char mRelUseMask[4]; /* 0x2C */ int mTimer; /* 0x30 */ bool field_0x30; /* 0x34 */ cXyz mBasePos; @@ -526,7 +525,7 @@ public: /* 0x3C */ fopAc_ac_c* field_0x3c; /* 0x40 */ fopAc_ac_c* field_0x40; /* 0x44 */ fpc_ProcID field_0x44; - /* 0x48 */ char field_0x48; + /* 0x48 */ char field_0x48[4]; /* 0x4C */ int field_0x4c; }; @@ -541,11 +540,7 @@ public: /* 0x3C */ f32 field_0x3c; /* 0x40 */ fopAc_ac_c* mRelActor; /* 0x44 */ fpc_ProcID mRelActorID; - /* 0x48 */ char mRelUseMask; - /* 0x49 */ char field_0x49; - /* 0x4A */ char field_0x4a; - /* 0x4B */ char field_0x4b; - /* 0x4C */ u8 field_0x4c[4]; + /* 0x48 */ char mRelUseMask[8]; /* 0x50 */ int mTimer; /* 0x54 */ int mTransType; /* 0x58 */ f32 mCushion; @@ -696,8 +691,7 @@ public: /* 0x24 */ f32* field_0x24; /* 0x28 */ f32 field_0x28; /* 0x2C */ fopAc_ac_c* mRelActor; - /* 0x30 */ char mRelUseMask; - /* 0x31 */ char field_0x31; + /* 0x30 */ char mRelUseMask[4]; /* 0x34 */ int mTimer; /* 0x38 */ int field_0x38; /* 0x3C */ int mChoice; @@ -780,8 +774,7 @@ public: /* 0xAC */ f32 field_0xac; /* 0xB0 */ fopAc_ac_c* mRelActor; /* 0xB4 */ fpc_ProcID mRelActorID; - /* 0xB8 */ char mRelUseMask; - /* 0xB9 */ char field_0xb9; + /* 0xB8 */ char mRelUseMask[4]; /* 0xBC */ f32 mCushion; /* 0xC0 */ u32 field_0xc0[6]; }; @@ -917,7 +910,12 @@ public: char* getEvStringPntData(char*, char*); char* getEvStringPntData(char*); bool getEvXyzData(cXyz*, char*, cXyz); +#if TARGET_PC + template + bool getEvStringData(char (&)[N], char*, char*); +#else bool getEvStringData(char*, char*, char*); +#endif fopAc_ac_c* getEvActor(char*); fopAc_ac_c* getEvActor(char*, char*); bool pauseEvCamera(); diff --git a/include/d/d_com_inf_game.h b/include/d/d_com_inf_game.h index d81fc5bc1e..2e644b2d3e 100644 --- a/include/d/d_com_inf_game.h +++ b/include/d/d_com_inf_game.h @@ -1851,7 +1851,7 @@ inline u16 dComIfGs_getDeathCount() { } #endif -inline char* dComIfGs_getPlayerName() { +inline TEXT_SPAN dComIfGs_getPlayerName() { return g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().getPlayerName(); } @@ -1859,7 +1859,7 @@ inline void dComIfGs_setPlayerName(const char* i_name) { g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().setPlayerName(i_name); } -inline char* dComIfGs_getHorseName() { +inline TEXT_SPAN dComIfGs_getHorseName() { return g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().getHorseName(); } diff --git a/include/d/d_file_sel_info.h b/include/d/d_file_sel_info.h index 73641bc770..973349c469 100644 --- a/include/d/d_file_sel_info.h +++ b/include/d/d_file_sel_info.h @@ -46,10 +46,10 @@ private: /* 0x22 */ u8 field_0x22; /* 0x24 */ CPaneMgrAlpha* mDatBase; /* 0x28 */ CPaneMgrAlpha* mNoDatBase; - /* 0x2C */ char* mPlayerName; - /* 0x30 */ char* mSaveDate; - /* 0x34 */ char* mPlayTime; - /* 0x38 */ char* mSaveStatus; + /* 0x2C */ TEXT_SPAN mPlayerName; + /* 0x30 */ TEXT_SPAN mSaveDate; + /* 0x34 */ TEXT_SPAN mPlayTime; + /* 0x38 */ TEXT_SPAN mSaveStatus; }; typedef void (dFile_info_c::*warningFunc)(void); diff --git a/include/d/d_file_select.h b/include/d/d_file_select.h index 66c4696c37..d219a5a11e 100644 --- a/include/d/d_file_select.h +++ b/include/d/d_file_select.h @@ -565,7 +565,7 @@ public: /* 0x0130 */ int field_0x0130; /* 0x0134 */ int field_0x0134; /* 0x0138 */ CPaneMgrAlpha* mErrorMsgTxtPane[2]; - /* 0x0140 */ char* mErrorMsgStringPtr[2]; + /* 0x0140 */ TEXT_SPAN mErrorMsgStringPtr[2]; /* 0x0148 */ u8 mErrorTxtDispIdx; /* 0x0149 */ u8 field_0x0149; /* 0x014A */ bool field_0x014a; @@ -609,7 +609,7 @@ public: /* 0x020A */ u8 mFadeTimer; /* 0x020B */ u8 field_0x020b; /* 0x020C */ CPaneMgrAlpha* mHeaderTxtPane[2]; - /* 0x0214 */ char* mHeaderStringPtr[2]; + /* 0x0214 */ TEXT_SPAN mHeaderStringPtr[2]; /* 0x021C */ u8 mHeaderTxtDispIdx; /* 0x021D */ u8 field_0x021d; /* 0x021E */ u8 field_0x021e; @@ -626,7 +626,7 @@ public: /* 0x024B */ u8 field_0x024b; /* 0x024C */ u8 field_0x024c; /* 0x024B */ u8 field_0x024d[3]; - /* 0x0250 */ char* mModoruStringPtr; + /* 0x0250 */ TEXT_SPAN mModoruStringPtr; /* 0x0254 */ STControl* stick; /* 0x0258 */ u8 mIsDataNew[3]; /* 0x025B */ u8 mIsNoData[3]; diff --git a/include/d/d_menu_save.h b/include/d/d_menu_save.h index 4a70d1d045..116795f7be 100644 --- a/include/d/d_menu_save.h +++ b/include/d/d_menu_save.h @@ -313,7 +313,7 @@ private: /* 0x00B8 */ int field_0xb8; /* 0x00BC */ int field_0xbc; /* 0x00C0 */ CPaneMgrAlpha* mpErrTxtPane[2]; - /* 0x00C8 */ char* mpErrTxt[2]; + /* 0x00C8 */ TEXT_SPAN mpErrTxt[2]; /* 0x00D0 */ u8 mErrTxtType; /* 0x00D1 */ u8 mErrTxtAnmComplete; /* 0x00D2 */ u8 field_0xd2; @@ -344,7 +344,7 @@ private: /* 0x0160 */ J2DAnmTevRegKey* field_0x160; /* 0x0164 */ int field_0x164; /* 0x0168 */ CPaneMgrAlpha* mpHeaderTxtPane[2]; - /* 0x0170 */ char* mpHeaderTxt[2]; + /* 0x0170 */ TEXT_SPAN mpHeaderTxt[2]; /* 0x0178 */ u8 mHeaderTxtType; // 0: Select Menu 1: YesNo Menu /* 0x0179 */ u8 mHeaderAnmComplete; /* 0x017A */ u8 field_0x17a; diff --git a/include/d/d_meter2_info.h b/include/d/d_meter2_info.h index 51f07f4dc3..683e555386 100644 --- a/include/d/d_meter2_info.h +++ b/include/d/d_meter2_info.h @@ -67,9 +67,9 @@ public: s16 decFloatingMessageTimer(); void resetFloatingMessage(); void decMsgKeyWaitTimer(); - void getString(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry); - void getStringKana(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry); - void getStringKanji(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry); + void getString(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry); + void getStringKana(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry); + void getStringKanji(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry); f32 getStringLength(J2DTextBox* i_textbox, char* i_string); f32 getStringLength(JUTFont* i_font, f32 param_2, f32 param_3, char* i_string); void onDirectUseItem(int); @@ -348,15 +348,15 @@ inline CPaneMgr* dMeter2Info_getMeterItemPanePtr(s32 i_idx) { return g_meter2_info.getMeterItemPanePtr(i_idx); } -inline void dMeter2Info_getString(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { +inline void dMeter2Info_getString(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { g_meter2_info.getString(i_stringID, o_string, i_msgEntry); } -inline void dMeter2Info_getStringKanji(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { +inline void dMeter2Info_getStringKanji(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { g_meter2_info.getStringKanji(i_stringID, o_string, i_msgEntry); } -inline void dMeter2Info_getStringKana(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { +inline void dMeter2Info_getStringKana(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { g_meter2_info.getStringKana(i_stringID, o_string, i_msgEntry); } diff --git a/include/d/d_msg_class.h b/include/d/d_msg_class.h index 8ebf3ff5b7..307d48f291 100644 --- a/include/d/d_msg_class.h +++ b/include/d/d_msg_class.h @@ -5,6 +5,7 @@ #include "JSystem/JMessage/JMessage.h" #include "SSystem/SComponent/c_xyz.h" #include "dusk/endian.h" +#include "dusk/string.hpp" #if REGION_JPN #define D_MSG_CLASS_PAGE_CNT_MAX 30 @@ -67,7 +68,7 @@ struct jmessage_tReference : public JMessage::TReference { void pageSend(); void selectMessage(); void inputNumber(); - char* getWord(int); + TEXT_SPAN getWord(int); void resetWord(); void setCharactor(u16); void addCharactor(u16); @@ -228,11 +229,11 @@ struct jmessage_tReference : public JMessage::TReference { f32 getSelRubyCharSpace() { return mSelRubyCharSpace; } f32 getRubySize() { return mRubySize; } f32 getRubyCharSpace() { return mRubyCharSpace; } - char* getSelTextPtr(int idx) { return mSelText[idx]; } - char* getSelRubyPtr(int idx) { return mSelRuby[idx]; } - char* getTextPtr() { return mText; } - char* getTextSPtr() { return mTextS; } - char* getRubyPtr() { return mRuby; } + TEXT_SPAN getSelTextPtr(int idx) { return mSelText[idx]; } + TEXT_SPAN getSelRubyPtr(int idx) { return mSelRuby[idx]; } + TEXT_SPAN getTextPtr() { return mText; } + TEXT_SPAN getTextSPtr() { return mTextS; } + TEXT_SPAN getRubyPtr() { return mRuby; } u8 getSelectRubyFlag() { return mSelectRubyFlag; } f32 getSelTBoxWidth() { return mSelTBoxWidth; } u8 getSelectPos() { return mSelectPos; } @@ -463,7 +464,7 @@ struct jmessage_tRenderingProcessor : public JMessage::TRenderingProcessor { f32 getLineLength(int); void do_strcat(char*, bool, bool, bool); void do_rubyset(void const*, u32); - void do_rubystrcat(char*, char*, f32, f32); + void do_rubystrcat(char*, TEXT_SPAN, f32, f32); void do_name1(); void do_numset(s16); void push_word(); diff --git a/include/d/d_msg_object.h b/include/d/d_msg_object.h index d2ac4ecb2a..6cf9ea7681 100644 --- a/include/d/d_msg_object.h +++ b/include/d/d_msg_object.h @@ -53,8 +53,8 @@ public: void demoMessageGroupLocal(); void endFlowGroupLocal(); void changeGroupLocal(s16); - bool getStringLocal(u32, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, char*, - char*, char*, s16*); + bool getStringLocal(u32, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, TEXT_SPAN, + TEXT_SPAN, TEXT_SPAN, s16*); bool isGetItemMessage(); bool isKanbanMessage(); bool isHowlMessage(); @@ -121,7 +121,7 @@ public: static void endFlowGroup(); static void changeGroup(s16); static bool getString(u32, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, - char*, char*, char*, s16*); + TEXT_SPAN, TEXT_SPAN, TEXT_SPAN, s16*); static void* getMsgDtPtr(); static void setProcessID(fpc_ProcID); static msg_class* getActor(); @@ -246,12 +246,12 @@ public: static void setWord(const char* i_word); void setWordLocal(const char* i_word) { - strcpy(mWord, i_word); + SAFE_STRCPY(mWord, i_word); } static void setSelectWord(int i_no, const char* i_word); void setSelectWordLocal(int i_no, const char* i_word) { - strcpy(mSelectWord[i_no], i_word); + SAFE_STRCPY(mSelectWord[i_no], i_word); } jmessage_tSequenceProcessor* getSequenceProcessor() { return mpSeqProc; } @@ -426,8 +426,8 @@ inline void dMsgObject_setTalkActor(fopAc_ac_c* actor) { } inline bool dMsgObject_getString(u32 i_msgId, J2DTextBox* i_tbox, J2DTextBox* i_rubyTbox, - JUTFont* i_font, COutFont_c* i_outFont, char* o_text, - char* o_ruby, char* o_textS, s16* param_8) { + JUTFont* i_font, COutFont_c* i_outFont, TEXT_SPAN o_text, + TEXT_SPAN o_ruby, TEXT_SPAN o_textS, s16* param_8) { return dMsgObject_getMsgObjectClass()->getString(i_msgId, i_tbox, i_rubyTbox, i_font, i_outFont, o_text, o_ruby, o_textS, param_8); } diff --git a/include/d/d_msg_string_base.h b/include/d/d_msg_string_base.h index 6f3a751b22..4624393f06 100644 --- a/include/d/d_msg_string_base.h +++ b/include/d/d_msg_string_base.h @@ -20,12 +20,12 @@ public: f32 getStringPageLocal(u32, u8, u8, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, u8); u8 getPageMax(int); - f32 getMessageLocal(u32, char*); + f32 getMessageLocal(u32, TEXT_SPAN); virtual f32 getString(u32, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, u8); virtual f32 getStringPage(u32, u8, u8, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, u8); - virtual f32 getMessage(u32, char*); + virtual f32 getMessage(u32, TEXT_SPAN); virtual void resetStringLocal(J2DTextBox*); virtual void drawOutFontLocal(J2DTextBox*, f32); virtual void drawFontLocal(J2DTextBox*, u8, f32, f32, f32, f32, u32, u8); diff --git a/include/d/d_msg_unit.h b/include/d/d_msg_unit.h index 400df98f25..47e40e31cf 100644 --- a/include/d/d_msg_unit.h +++ b/include/d/d_msg_unit.h @@ -6,14 +6,14 @@ class dMsgUnit_c { public: dMsgUnit_c(); - void setTag(int, int, char*, bool); + void setTag(int, int, TEXT_SPAN, bool); virtual ~dMsgUnit_c(); }; extern dMsgUnit_c g_msg_unit; -inline void dMsgUnit_setTag(int param_0, int param_1, char* param_2) { +inline void dMsgUnit_setTag(int param_0, int param_1, TEXT_SPAN param_2) { g_msg_unit.setTag(param_0, param_1, param_2, true); } diff --git a/include/d/d_name.h b/include/d/d_name.h index 3ac6592457..ddd99127c6 100644 --- a/include/d/d_name.h +++ b/include/d/d_name.h @@ -127,7 +127,7 @@ public: u8 isInputEnd() { return mIsInputEnd; } char* getInputStrPtr() { return mInputStr; } void hideIcon() { mSelIcon->setAlphaRate(0.0f); } - void setNextNameStr(char* i_name) { strcpy(mNextNameStr,i_name); } + void setNextNameStr(char* i_name) { SAFE_STRCPY(mNextNameStr,i_name); } void draw() { _draw(); } private: @@ -140,9 +140,9 @@ private: /* 0x02C */ J2DAnmTextureSRTKey* mCursorTexKey; /* 0x030 */ int mCurTexAnmF; /* 0x034 */ CPaneMgrAlpha* mNameCursor[8]; - /* 0x054 */ char* mNameText[8]; + /* 0x054 */ TEXT_SPAN mNameText[8]; /* 0x074 */ CPaneMgr* mMojiIcon[65]; - /* 0x178 */ char* mMojiText[65]; + /* 0x178 */ TEXT_SPAN mMojiText[65]; /* 0x27C */ J2DPane* mMojiPane; /* 0x280 */ J2DPane* mMenuPane; /* 0x284 */ CPaneMgr* mMenuIcon[4]; diff --git a/include/d/d_save.h b/include/d/d_save.h index e2719e0a98..69d4d3174d 100644 --- a/include/d/d_save.h +++ b/include/d/d_save.h @@ -488,21 +488,25 @@ public: } #if TARGET_PC u16 getDeathCount() const { return mDeathCount; } -#endif + TEXT_SPAN getPlayerName() const { return const_cast(mPlayerName); } +#else char* getPlayerName() const { return const_cast(mPlayerName); } +#endif void setPlayerName(const char* i_name) { #if AVOID_UB - strncpy(mPlayerName, i_name, sizeof(mPlayerName) - 1); - mPlayerName[sizeof(mPlayerName) - 1] = '\0'; + dusk::SafeStringCopyTruncate(mPlayerName, i_name); #else strcpy(mPlayerName, i_name); #endif } +#if TARGET_PC + TEXT_SPAN getHorseName() const { return const_cast(mHorseName); } +#else char* getHorseName() const { return const_cast(mHorseName); } +#endif void setHorseName(const char* i_name) { #if AVOID_UB - strncpy(mHorseName, i_name, sizeof(mHorseName) - 1); - mHorseName[sizeof(mHorseName) - 1] = '\0'; + dusk::SafeStringCopyTruncate(mHorseName, i_name); #else strcpy(mHorseName, i_name); #endif diff --git a/include/d/d_stage.h b/include/d/d_stage.h index bd8e7d5728..04a6115fd0 100644 --- a/include/d/d_stage.h +++ b/include/d/d_stage.h @@ -1225,7 +1225,7 @@ public: #endif } static JKRExpHeap* getMemoryBlockHeap(int i_no) { return mMemoryBlock[i_no]; } - static char* getDemoArcName() { return mDemoArcName; } + static TEXT_SPAN getDemoArcName() { return mDemoArcName; } static void offNoChangeRoom() { mNoChangeRoom = false; } static void onNoChangeRoom() { mNoChangeRoom = true; } diff --git a/include/dusk/string.hpp b/include/dusk/string.hpp index 7de1b1ad75..af91ff8fe9 100644 --- a/include/dusk/string.hpp +++ b/include/dusk/string.hpp @@ -1,22 +1,55 @@ #ifndef DUSK_STRING_HPP #define DUSK_STRING_HPP - -#include "global.h" -#include -#include +#include namespace dusk { -inline void strncpyProxy(char* dst, const char* src, size_t count) { -#if _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4996) +struct TextSpan { + char* buffer; + size_t size; + + constexpr operator char*() const { + return buffer; + } + + constexpr TextSpan(char* buffer, size_t size) : buffer(buffer), size(size) { } + + template + constexpr TextSpan(char (&buffer)[BufSize]) : buffer(buffer), size(BufSize) { + } + + constexpr TextSpan() : buffer(nullptr), size(0) { } + + constexpr TextSpan operator++(int) { + const auto prev = *this; + + if (size > 0) [[likely]] { + size--; + } + buffer++; + + return prev; + } + + constexpr char& operator*() const { + if (size == 0) [[unlikely]] { + CrashSpawnEmpty(); + } + + return *buffer; + } + +private: + static void CrashSpawnEmpty(); +}; + +#if TARGET_PC +#define TEXT_SPAN dusk::TextSpan +#else +#define TEXT_SPAN char* #endif - strncpy(dst, src, count); -#if _MSC_VER -#pragma warning(pop) -#endif -} + +void SafeStringCopyTruncate(char* buffer, size_t bufSize, const char* src); /** * Copy a string to a fixed-size array. @@ -25,13 +58,28 @@ inline void strncpyProxy(char* dst, const char* src, size_t count) { template void SafeStringCopyTruncate(char (&buffer)[BufSize], const char* src) { static_assert(BufSize > 0, "Target buffer cannot be size zero"); + SafeStringCopyTruncate(buffer, BufSize, src); +} - if (buffer == src) { - CRASH("Cannot copy string to same buffer"); - } +void SafeStringCopy(char* buffer, size_t bufSize, const char* src); +void SafeStringCat(char* buffer, size_t bufSize, const char* src); +int SafeStringVPrintf(char* buffer, size_t bufSize, const char* src, std::va_list args); - strncpyProxy(buffer, src, BufSize); - buffer[BufSize - 1] = 0; +inline void SafeStringCopy(TextSpan dst, const char* src) { + SafeStringCopy(dst.buffer, dst.size, src); +} + +inline void SafeStringCat(TextSpan dst, const char* src) { + SafeStringCat(dst.buffer, dst.size, src); +} + +inline int SafeStringPrintf(TextSpan dst, const char* format, ...) { + std::va_list args; + va_start(args, format); + const auto ret = SafeStringVPrintf(dst.buffer, dst.size, format, args); + va_end(args); + + return ret; } /** @@ -41,18 +89,40 @@ void SafeStringCopyTruncate(char (&buffer)[BufSize], const char* src) { template void SafeStringCopy(char (&buffer)[BufSize], const char* src) { static_assert(BufSize > 0, "Target buffer cannot be size zero"); - if (buffer == src) { - CRASH("Cannot copy string to same buffer"); - } - - if (strlen(src) > BufSize - 1) { - CRASH("Destination buffer too small!"); - } - - strncpyProxy(buffer, src, BufSize); - buffer[BufSize - 1] = 0; + SafeStringCopy(buffer, BufSize, src); } +template +void SafeStringCat(char (&buffer)[BufSize], const char* src) { + static_assert(BufSize > 0, "Target buffer cannot be size zero"); + SafeStringCat(buffer, BufSize, src); +} + +template +int SafeStringPrintf(char (&buffer)[BufSize], const char* format, ...) { + static_assert(BufSize > 0, "Target buffer cannot be size zero"); + + std::va_list args; + va_start(args, format); + const auto ret = SafeStringVPrintf(buffer, BufSize, format, args); + va_end(args); + + return ret; +} + +#if TARGET_PC +#define SAFE_STRCPY dusk::SafeStringCopy +#define SAFE_STRCAT dusk::SafeStringCat +#define SAFE_SPRINTF dusk::SafeStringPrintf +#define SAFE_STRCPY_BOUNDED dusk::SafeStringCopy +#define SAFE_STRCAT_BOUNDED dusk::SafeStringCat +#else +#define SAFE_STRCPY strcpy +#define SAFE_STRCAT strcat +#define SAFE_SPRINTF sprintf +#define SAFE_STRCPY_BOUNDED strcpy +#define SAFE_STRCPY_BOUNDED strcat +#endif } #endif // DUSK_STRING_HPP diff --git a/include/f_op/f_op_msg_mng.h b/include/f_op/f_op_msg_mng.h index 9bb8bc0ff3..330347fc18 100644 --- a/include/f_op/f_op_msg_mng.h +++ b/include/f_op/f_op_msg_mng.h @@ -50,7 +50,7 @@ fpc_ProcID fopMsgM_messageSet(u32 i_msgIdx, fopAc_ac_c* i_talkActor, u32 param_2 fpc_ProcID fopMsgM_messageSet(u32 i_msgIdx, u32 param_1); fpc_ProcID fopMsgM_messageSetDemo(u32 i_msgidx); msg_class* fopMsgM_SearchByID(fpc_ProcID i_id); -char* fopMsgM_messageGet(char* i_stringBuf, u32 i_msgId); +TEXT_SPAN fopMsgM_messageGet(TEXT_SPAN i_stringBuf, u32 i_msgId); fpc_ProcID fop_Timer_create(s16 i_procName, u8 i_mode, u32 i_limitMs, u8 i_type, u8 param_4, f32 param_5, f32 param_6, f32 param_7, f32 param_8, fopMsgCreateFunc i_createFunc); diff --git a/libs/JSystem/include/JSystem/J2DGraph/J2DTextBox.h b/libs/JSystem/include/JSystem/J2DGraph/J2DTextBox.h index f4d5345507..0d875ad652 100644 --- a/libs/JSystem/include/JSystem/J2DGraph/J2DTextBox.h +++ b/libs/JSystem/include/JSystem/J2DGraph/J2DTextBox.h @@ -4,6 +4,7 @@ #include "JSystem/J2DGraph/J2DMaterial.h" #include "JSystem/J2DGraph/J2DPane.h" #include "dusk/endian.h" +#include "dusk/string.hpp" class J2DMaterial; class JUTFont; @@ -98,7 +99,8 @@ public: void initiate(ResFONT const*, char const*, s16, J2DTextBoxHBinding, J2DTextBoxVBinding); void private_readStream(J2DPane*, JSURandomInputStream*, JKRArchive*); - char* getStringPtr() const; + TEXT_SPAN getStringPtr() const; + dusk::TextSpan getSpan() const; s32 setString(s16, char const*, ...); s32 setString(char const*, ...); diff --git a/libs/JSystem/src/J2DGraph/J2DMaterialFactory.cpp b/libs/JSystem/src/J2DGraph/J2DMaterialFactory.cpp index beb9e0075a..2cede2c49a 100644 --- a/libs/JSystem/src/J2DGraph/J2DMaterialFactory.cpp +++ b/libs/JSystem/src/J2DGraph/J2DMaterialFactory.cpp @@ -8,6 +8,8 @@ #include #include +#include "dusk/string.hpp" + J2DMaterialFactory::J2DMaterialFactory(J2DMaterialBlock const& param_0) { mMaterialNum = param_0.field_0x8; mpMaterialInitData = JSUConvertOffsetToPtr(¶m_0, param_0.field_0xc); @@ -92,7 +94,7 @@ J2DMaterial* J2DMaterialFactory::create(J2DMaterial* param_0, int index, u32 par } if (local_380 == NULL && J2DScreen::getDataManage() != NULL) { char acStack_230[257]; - strcpy(acStack_230, param_3->getName(texNo)); + SAFE_STRCPY(acStack_230, param_3->getName(texNo)); local_380 = J2DScreen::getDataManage()->get(acStack_230); } } @@ -111,7 +113,7 @@ J2DMaterial* J2DMaterialFactory::create(J2DMaterial* param_0, int index, u32 par } if (local_388 == NULL && J2DScreen::getDataManage() != NULL) { char acStack_334[257]; - strcpy(acStack_334, param_4->getName(param_0->getTevBlock()->getFontNo())); + SAFE_STRCPY(acStack_334, param_4->getName(param_0->getTevBlock()->getFontNo())); local_388 = J2DScreen::getDataManage()->get(acStack_334); } } diff --git a/libs/JSystem/src/J2DGraph/J2DTextBox.cpp b/libs/JSystem/src/J2DGraph/J2DTextBox.cpp index 970f120140..d999b51b9f 100644 --- a/libs/JSystem/src/J2DGraph/J2DTextBox.cpp +++ b/libs/JSystem/src/J2DGraph/J2DTextBox.cpp @@ -309,8 +309,12 @@ void J2DTextBox::draw(f32 posX, f32 posY, f32 param_2, J2DTextBoxHBinding hBind) } } -char* J2DTextBox::getStringPtr() const { +TEXT_SPAN J2DTextBox::getStringPtr() const { +#if TARGET_PC + return { mStringPtr, mStringLength }; +#else return mStringPtr; +#endif } s32 J2DTextBox::setString(char const* string, ...) { @@ -330,7 +334,7 @@ s32 J2DTextBox::setString(char const* string, ...) { if (mStringPtr) { mStringLength = len + 1; - strcpy(mStringPtr, string); + SAFE_STRCPY_BOUNDED(mStringPtr, mStringLength, string); } va_end(args); diff --git a/libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp b/libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp index b9cd8acb15..71068a3591 100644 --- a/libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp +++ b/libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp @@ -2,6 +2,8 @@ #include #include +#include "dusk/string.hpp" + u32 JAHVirtualNode::smVirNodeNum; void JAHVirtualNode::virtualMessage(JAHControl& control) { @@ -77,7 +79,7 @@ void JAHVirtualNode::setVirNodeName(const char* name) { // clang-format off JUT_ASSERT(141, size<32); // clang-format on - strcpy(mName, name); + SAFE_STRCPY(mName, name); } JAHVirtualNode::JAHVirtualNode(const char* name) : mTree(this) { diff --git a/libs/JSystem/src/JAHostIO/JAHioNode.cpp b/libs/JSystem/src/JAHostIO/JAHioNode.cpp index 9a30991e4f..e6c342eeeb 100644 --- a/libs/JSystem/src/JAHostIO/JAHioNode.cpp +++ b/libs/JSystem/src/JAHostIO/JAHioNode.cpp @@ -4,7 +4,9 @@ #include "JSystem/JAHostIO/JAHioMessage.h" #include "JSystem/JAHostIO/JAHioMgr.h" #include "JSystem/JAHostIO/JAHioNode.h" + #include "JSystem/JHostIO/JORServer.h" +#include "dusk/string.hpp" JAHioNode* JAHioNode::smCurrentNode; @@ -32,7 +34,7 @@ void JAHioNode::updateNode() { void JAHioNode::setNodeName(const char* name) { int size = strlen(name) + 1; JUT_ASSERT(51, size < 32); - strcpy(mName, name); + SAFE_STRCPY(mName, name); } void JAHioNode::genMessage(JORMContext* mctx) { diff --git a/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp b/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp index d14ff70298..41ad35380d 100644 --- a/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp +++ b/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp @@ -9,6 +9,8 @@ #include #include +#include "dusk/string.hpp" + JASHeap* JASWaveArcLoader::sAramHeap; JASHeap* JASWaveArcLoader::getRootHeap() { @@ -22,7 +24,7 @@ char JASWaveArcLoader::sCurrentDir[DIR_MAX] = "/AudioRes/Waves/"; void JASWaveArcLoader::setCurrentDir(char const* dir) { JUT_ASSERT(40, std::strlen(dir) < DIR_MAX - 1); - strcpy(sCurrentDir, dir); + SAFE_STRCPY(sCurrentDir, dir); u32 len = strlen(sCurrentDir); if (sCurrentDir[len - 1] != '/') { JUT_ASSERT(45, len + 1 < DIR_MAX); @@ -170,8 +172,8 @@ void JASWaveArc::setFileName(char const* fileName) { length = length + strlen(fileName); char* path = JKR_NEW_ARRAY_ARGS(char, length + 1, JASKernel::getSystemHeap(), -4); JUT_ASSERT(322, path); - strcpy(path, currentDir); - strcat(path, fileName); + SAFE_STRCPY_BOUNDED(path, length + 1, currentDir); + SAFE_STRCAT_BOUNDED(path, length + 1, fileName); path[length] = '\0'; int entryNum = DVDConvertPathToEntrynum(path); JKR_DELETE_ARRAY(path); diff --git a/libs/JSystem/src/JKernel/JKRFileCache.cpp b/libs/JSystem/src/JKernel/JKRFileCache.cpp index 97b0d6fdde..1a97ddefa3 100644 --- a/libs/JSystem/src/JKernel/JKRFileCache.cpp +++ b/libs/JSystem/src/JKernel/JKRFileCache.cpp @@ -8,6 +8,7 @@ #include #include +#include "dusk/string.hpp" #include "global.h" JKRFileCache* JKRFileCache::mount(const char* path, JKRHeap* heap, const char* param_3) { diff --git a/libs/JSystem/src/JKernel/JKRFileLoader.cpp b/libs/JSystem/src/JKernel/JKRFileLoader.cpp index e0e531aaa9..4a7fa00d10 100644 --- a/libs/JSystem/src/JKernel/JKRFileLoader.cpp +++ b/libs/JSystem/src/JKernel/JKRFileLoader.cpp @@ -4,10 +4,11 @@ #define MSL_USE_INLINES 1 // needed to inline tolower call. not inlined elsewhere in the repo -#include #include +#include #include #include "JSystem/JKernel/JKRHeap.h" +#include "dusk/string.hpp" #include "global.h" JKRFileLoader* JKRFileLoader::sCurrentVolume; @@ -104,7 +105,7 @@ const char* JKRFileLoader::fetchVolumeName(char* buffer, s32 bufferSize, const c static char rootPath[2] = "/"; if (strcmp(path, "/") == 0) { - strcpy(buffer, rootPath); + SAFE_STRCPY_BOUNDED(buffer, bufferSize, rootPath); return rootPath; } diff --git a/libs/JSystem/src/JKernel/JKRThread.cpp b/libs/JSystem/src/JKernel/JKRThread.cpp index 31c2cbe6e5..8c945aba32 100644 --- a/libs/JSystem/src/JKernel/JKRThread.cpp +++ b/libs/JSystem/src/JKernel/JKRThread.cpp @@ -7,6 +7,8 @@ #include "global.h" #include +#include "dusk/string.hpp" + #if TARGET_PC #include "dusk/os.h" #endif @@ -274,7 +276,7 @@ void JKRThreadSwitch::draw(JKRThreadName_* thread_name_list, JUTConsole* console if (!thread_print_name) { char buffer[16]; - sprintf(buffer, "%d", loadInfo->getId()); + SAFE_SPRINTF(buffer, "%d", loadInfo->getId()); thread_print_name = buffer; } diff --git a/libs/JSystem/src/JUtility/JUTConsole.cpp b/libs/JSystem/src/JUtility/JUTConsole.cpp index 3be4a370ef..c2c023a2d8 100644 --- a/libs/JSystem/src/JUtility/JUTConsole.cpp +++ b/libs/JSystem/src/JUtility/JUTConsole.cpp @@ -1,13 +1,14 @@ #include "JSystem/JSystem.h" // IWYU pragma: keep -#include "JSystem/JUtility/JUTConsole.h" +#include +#include #include "JSystem/J2DGraph/J2DOrthoGraph.h" #include "JSystem/JKernel/JKRHeap.h" #include "JSystem/JUtility/JUTAssert.h" +#include "JSystem/JUtility/JUTConsole.h" #include "JSystem/JUtility/JUTDirectPrint.h" #include "JSystem/JUtility/JUTVideo.h" -#include -#include +#include "dusk/string.hpp" #include "global.h" JUTConsoleManager* JUTConsoleManager::sManager; @@ -204,10 +205,10 @@ void JUTConsole::doDraw(JUTConsole::EConsoleType consoleType) const { mFont->drawString_scale((int)f31, sp94, mFontSizeX, mFontSizeY, spA8, TRUE); f31 += mFontSizeX * 13.0f; if (sp88) { - sprintf(spB8, "ALL"); + SAFE_SPRINTF(spB8, "ALL"); } else { f32 f29 = sp8C / (f32)(sp90 - mHeight); - sprintf(spB8, "%3d%%(%dL)", (int)(100.0 * f29), sp90); + SAFE_SPRINTF(spB8, "%3d%%(%dL)", (int)(100.0 * f29), sp90); } mFont->drawString_scale(f31, sp94, mFontSizeX, mFontSizeY, spB8, TRUE); } diff --git a/libs/JSystem/src/JUtility/JUTException.cpp b/libs/JSystem/src/JUtility/JUTException.cpp index 514ec6dd8a..54b234118c 100644 --- a/libs/JSystem/src/JUtility/JUTException.cpp +++ b/libs/JSystem/src/JUtility/JUTException.cpp @@ -9,6 +9,8 @@ #include #include #include + +#include "dusk/string.hpp" #ifdef __REVOLUTION_SDK__ #include #else @@ -845,8 +847,8 @@ bool JUTException::queryMapAddress(char* mapPath, u32 address, s32 section_id, u bool begin_with_newline) { if (mapPath) { char buffer[80]; - strcpy(buffer, mapPath); - strcat(buffer, ".map"); + SAFE_STRCPY(buffer, mapPath); + SAFE_STRCAT(buffer, ".map"); if (queryMapAddress_single(buffer, address, section_id, out_addr, out_size, out_line, line_length, print, begin_with_newline) == true) { diff --git a/src/Z2AudioLib/Z2SoundMgr.cpp b/src/Z2AudioLib/Z2SoundMgr.cpp index b861475118..6978a61585 100644 --- a/src/Z2AudioLib/Z2SoundMgr.cpp +++ b/src/Z2AudioLib/Z2SoundMgr.cpp @@ -183,7 +183,7 @@ bool Z2SoundMgr::startSound(JAISoundID soundID, JAISoundHandle* handle, const JG return streamMgr_.startSound(soundID, handle, posPtr); default: char error[64]; - sprintf(error, "Unknown Sound-Type id :%08x\n", (u32)soundID); + SAFE_SPRINTF(error, "Unknown Sound-Type id :%08x\n", (u32)soundID); JUT_WARN(277, "%s", error); } diff --git a/src/d/actor/d_a_alink.cpp b/src/d/actor/d_a_alink.cpp index e2fe7a890c..9bec9da6d0 100644 --- a/src/d/actor/d_a_alink.cpp +++ b/src/d/actor/d_a_alink.cpp @@ -57,6 +57,7 @@ #include "dusk/settings.h" #include "res/Object/Alink.h" #include +#include #endif static int daAlink_Create(fopAc_ac_c* i_this); diff --git a/src/d/actor/d_a_alink_demo.inc b/src/d/actor/d_a_alink_demo.inc index 55e261cc13..b1eb71a85d 100644 --- a/src/d/actor/d_a_alink_demo.inc +++ b/src/d/actor/d_a_alink_demo.inc @@ -4359,7 +4359,7 @@ void daAlink_c::dungeonReturnWarp() { s16 angle = dComIfGs_getWarpPlayerAngleY(); s8 room = dComIfGs_getWarpRoomNo(); char name[8]; - strcpy(name, dComIfGs_getWarpStageName()); + SAFE_STRCPY(name, dComIfGs_getWarpStageName()); dComIfGs_setRestartRoom(pos, angle, room); dComIfGp_setNextStage(name, -1, dComIfGs_getRestartRoomNo(), -1, 0.0f, 12, 0, @@ -4535,7 +4535,7 @@ int daAlink_c::procCoWarpInit(int param_0, int param_1) { if (checkBossRoom() && fopAcM_GetRoomNo(this) == 50) { char stageName[32]; - strcpy(stageName, dComIfGp_getStartStageName()); + SAFE_STRCPY(stageName, dComIfGp_getStartStageName()); for (int i = 0; i < 32; i++) { if ((s64)stageName[i] == 0) { diff --git a/src/d/actor/d_a_bg_obj.cpp b/src/d/actor/d_a_bg_obj.cpp index 7d4adb9e47..026ef1a7fd 100644 --- a/src/d/actor/d_a_bg_obj.cpp +++ b/src/d/actor/d_a_bg_obj.cpp @@ -5,24 +5,25 @@ #include "d/dolzel_rel.h" // IWYU pragma: keep -#include "d/actor/d_a_bg_obj.h" -#include "JSystem/J3DGraphBase/J3DMaterial.h" #include -#include #include +#include +#include "JSystem/J3DGraphBase/J3DMaterial.h" +#include "SSystem/SComponent/c_math.h" +#include "d/actor/d_a_bg_obj.h" #include "d/actor/d_a_set_bgobj.h" #include "d/d_s_play.h" -#include "SSystem/SComponent/c_math.h" +#include "dusk/string.hpp" static const char* getBmdName(int param_0, int param_1) { static char l_bmdName[16]; switch (param_1) { case 0: - sprintf(l_bmdName, "model%d.bmd", param_0); + SAFE_SPRINTF(l_bmdName, "model%d.bmd", param_0); break; default: - sprintf(l_bmdName, "model%d_%d.bmd", param_0, param_1); + SAFE_SPRINTF(l_bmdName, "model%d_%d.bmd", param_0, param_1); break; } @@ -34,10 +35,10 @@ static const char* getBtkName(int param_0, int param_1) { switch (param_1) { case 0: - sprintf(l_btkName, "model%d.btk", param_0); + SAFE_SPRINTF(l_btkName, "model%d.btk", param_0); break; default: - sprintf(l_btkName, "model%d_%d.btk", param_0, param_1); + SAFE_SPRINTF(l_btkName, "model%d_%d.btk", param_0, param_1); break; } @@ -49,10 +50,10 @@ static const char* getBrkName(int param_0, int param_1) { switch (param_1) { case 0: - sprintf(l_brkName, "model%d.brk", param_0); + SAFE_SPRINTF(l_brkName, "model%d.brk", param_0); break; default: - sprintf(l_brkName, "model%d_%d.brk", param_0, param_1); + SAFE_SPRINTF(l_brkName, "model%d_%d.brk", param_0, param_1); break; } @@ -62,7 +63,7 @@ static const char* getBrkName(int param_0, int param_1) { static const char* getDzbName(int param_0) { static char l_dzbName[16]; - sprintf(l_dzbName, "model%d.dzb", param_0); + SAFE_SPRINTF(l_dzbName, "model%d.dzb", param_0); return l_dzbName; } @@ -95,12 +96,12 @@ u8* daBgObj_c::spec_data_c::initTexShareBlock(u8* i_dataPtr) { u8* dataPos = i_dataPtr + 8; for (; i < mTexShareNum; i++) { - strcpy(sp48, (char*)dataPos); + SAFE_STRCPY(sp48, (char*)dataPos); int len = strlen((char*)dataPos); dataPos += len + 1; if (*dataPos != 0) { - strcpy(sp8, (char*)dataPos); + SAFE_STRCPY(sp8, (char*)dataPos); dataPos += strlen((char*)dataPos) + 1; } else if (*dataPos == 0 && dataPos[1] == 1) { dataPos += 2; @@ -646,14 +647,14 @@ void daBgObj_c::doShareTexture() { u8* spec_res_name = mSpecData.mpTexShareBlock + 8; for (int i = 0; i < mSpecData.mTexShareNum; i++) { - strcpy(res_name, (char*)spec_res_name); + SAFE_STRCPY(res_name, (char*)spec_res_name); spec_res_name += strlen((char*)spec_res_name) + 1; J3DModelData* modelData = (J3DModelData*)dComIfG_getObjectRes(daSetBgObj_c::getArcName(this), res_name); if (*spec_res_name != 0) { - strcpy(share_res_name, (char*)spec_res_name); + SAFE_STRCPY(share_res_name, (char*)spec_res_name); spec_res_name += strlen((char*)spec_res_name) + 1; J3DModelData* shareModelData = diff --git a/src/d/actor/d_a_door_dbdoor00.cpp b/src/d/actor/d_a_door_dbdoor00.cpp index c659a47cb2..edac3c14f8 100644 --- a/src/d/actor/d_a_door_dbdoor00.cpp +++ b/src/d/actor/d_a_door_dbdoor00.cpp @@ -51,7 +51,7 @@ static char* l_bmd_base_name = "door-pushDouble_"; char* daDbDoor00_c::getBmdName() { static char l_bmdName[32]; - sprintf(l_bmdName, "%s%02d.bmd", l_bmd_base_name, door_param2_c::getDoorModel(this)); + SAFE_SPRINTF(l_bmdName, "%s%02d.bmd", l_bmd_base_name, door_param2_c::getDoorModel(this)); return l_bmdName; } diff --git a/src/d/actor/d_a_door_knob00.cpp b/src/d/actor/d_a_door_knob00.cpp index 50d5723261..ea4a9c8679 100644 --- a/src/d/actor/d_a_door_knob00.cpp +++ b/src/d/actor/d_a_door_knob00.cpp @@ -55,7 +55,7 @@ static char* l_bmd_base_name = "door-knob_"; char* daKnob20_c::getBmd() { static char l_bmdName[32]; - sprintf(l_bmdName, "%s%02d.bmd", l_bmd_base_name, knob_param_c::getDoorModel(this)); + SAFE_SPRINTF(l_bmdName, "%s%02d.bmd", l_bmd_base_name, knob_param_c::getDoorModel(this)); return l_bmdName; } diff --git a/src/d/actor/d_a_door_shutter.cpp b/src/d/actor/d_a_door_shutter.cpp index ebd1c9a185..ba825fe4bd 100644 --- a/src/d/actor/d_a_door_shutter.cpp +++ b/src/d/actor/d_a_door_shutter.cpp @@ -65,10 +65,10 @@ char* daDoor20_c::getBmdName() { case 10: case 12: default: - sprintf(bmdName, "door-shutter_%02d.bmd", door_param2_c::getDoorModel(this)); + SAFE_SPRINTF(bmdName, "door-shutter_%02d.bmd", door_param2_c::getDoorModel(this)); break; case 9: - sprintf(bmdName, "door-knob_%02d.bmd", door_param2_c::getDoorModel(this)); + SAFE_SPRINTF(bmdName, "door-knob_%02d.bmd", door_param2_c::getDoorModel(this)); break; } return bmdName; diff --git a/src/d/actor/d_a_door_spiral.cpp b/src/d/actor/d_a_door_spiral.cpp index b7c7567787..d0c34c6dbd 100644 --- a/src/d/actor/d_a_door_spiral.cpp +++ b/src/d/actor/d_a_door_spiral.cpp @@ -94,9 +94,9 @@ const char* daSpiral_c::getBmd(int i_type) { const char* daSpiral_c::getBmd2(int i_type) { static char bmdName[32]; if (i_type == daSpiral_TYPE_DOWN_e) { - sprintf(bmdName, "door-stairSpiralU.bmd"); + SAFE_SPRINTF(bmdName, "door-stairSpiralU.bmd"); } else { - sprintf(bmdName, "door-stairSpiralD.bmd"); + SAFE_SPRINTF(bmdName, "door-stairSpiralD.bmd"); } return bmdName; diff --git a/src/d/actor/d_a_movie_player.cpp b/src/d/actor/d_a_movie_player.cpp index fcd1fb6dff..1760fa713b 100644 --- a/src/d/actor/d_a_movie_player.cpp +++ b/src/d/actor/d_a_movie_player.cpp @@ -4494,7 +4494,7 @@ int daMP_c::daMP_c_Init() { JUT_ASSERT(9507, 0 <= movieNo && movieNo <= 99); char path[32]; - sprintf(path, "/Movie/demo_movie%02d_%02d.thp", demoNo, movieNo); + SAFE_SPRINTF(path, "/Movie/demo_movie%02d_%02d.thp", demoNo, movieNo); if (!daMP_ActivePlayer_Init(path)) { daMP_Fail_alloc = TRUE; diff --git a/src/d/actor/d_a_npc_hanjo.cpp b/src/d/actor/d_a_npc_hanjo.cpp index ef5d1671d0..175bee3d1e 100644 --- a/src/d/actor/d_a_npc_hanjo.cpp +++ b/src/d/actor/d_a_npc_hanjo.cpp @@ -15,6 +15,8 @@ #include "Z2AudioLib/Z2Instances.h" #include +#include "dusk/string.hpp" + static int l_bmdData[4][2] = { {14, 1}, {26, 2}, {25, 2}, {3, 4}, @@ -1336,8 +1338,8 @@ int daNpc_Hanjo_c::cutAppearHawker(int param_1) { home.angle.y += 0x8000; setAngle(home.angle.y); initTalk(0xcf, NULL); - strcpy(acStack_98, l_evtList[9].eventName); - strcat(acStack_98, "@"); + SAFE_STRCPY(acStack_98, l_evtList[9].eventName); + SAFE_STRCAT(acStack_98, "@"); dComIfGp_getEvent()->setSkipZev(this, acStack_98); dComIfGp_getEvent()->onSkipFade(); dComIfGp_getVibration().StartShock( 9, 15, cXyz(0.0f, 1.0f, 0.0f)); diff --git a/src/d/actor/d_a_npc_saru.cpp b/src/d/actor/d_a_npc_saru.cpp index 500daea704..366ed865a8 100644 --- a/src/d/actor/d_a_npc_saru.cpp +++ b/src/d/actor/d_a_npc_saru.cpp @@ -11,6 +11,8 @@ #include "d/actor/d_a_e_ym.h" #include +#include "dusk/string.hpp" + enum saru_TW_RES_File_ID { /* BMDR */ /* 0x4 */ BMDR_SARU_BARA_TW = 0x4, @@ -956,8 +958,8 @@ int daNpc_Saru_c::cutYmLook(int param_1) { if (dComIfGp_getEventManager().getIsAddvance(param_1) != 0) { switch (iVar1) { case 0: - strcpy(acStack_88, l_evtList[2].eventName); - strcat(acStack_88, "@"); + SAFE_STRCPY(acStack_88, l_evtList[2].eventName); + SAFE_STRCAT(acStack_88, "@"); dComIfGp_getEvent()->setSkipZev(this, acStack_88); dComIfGp_getEvent()->onSkipFade(); field_0xfd9 = 1; diff --git a/src/d/actor/d_a_npc_shop0.cpp b/src/d/actor/d_a_npc_shop0.cpp index 1dd4b3e31f..6743a1b859 100644 --- a/src/d/actor/d_a_npc_shop0.cpp +++ b/src/d/actor/d_a_npc_shop0.cpp @@ -8,6 +8,8 @@ #include "d/actor/d_a_npc_shop0.h" #include +#include "dusk/string.hpp" + static int createHeapCallBack(fopAc_ac_c* i_this) { return static_cast(i_this)->createHeap(); } @@ -184,8 +186,8 @@ int daNpc_Shop0_c::init() { static char l_fileName[21]; J3DAnmTransform* daNpc_Shop0_c::getTrnsfrmAnmP(int i_fileIndex, char** i_fileName) { - strcpy(l_fileName, i_fileName[i_fileIndex]); - strcat(l_fileName, ".bck"); + SAFE_STRCPY(l_fileName, i_fileName[i_fileIndex]); + SAFE_STRCAT(l_fileName, ".bck"); return (J3DAnmTransform*) dComIfG_getObjectRes(getResName(), l_fileName); } diff --git a/src/d/actor/d_a_npc_yelia.cpp b/src/d/actor/d_a_npc_yelia.cpp index 67b1cbcffc..80d6df02a6 100644 --- a/src/d/actor/d_a_npc_yelia.cpp +++ b/src/d/actor/d_a_npc_yelia.cpp @@ -9,6 +9,8 @@ #include "d/actor/d_a_demo_item.h" #include +#include "dusk/string.hpp" + static daNpc_GetParam1 l_bmdData[3] = { {3, 1}, {3, 4}, @@ -1137,8 +1139,8 @@ BOOL daNpc_Yelia_c::cutTakeWoodStatue(int i_staffId) { mItemId = fpcM_ERROR_PROCESS_ID_e; mEventTimer = timer; Z2GetAudioMgr()->muteSceneBgm(90, 0.0f); - strcpy(name, l_evtList[EVENT_TAKE_WOODSTATUE].eventName); - strcat(name, "@"); + SAFE_STRCPY(name, l_evtList[EVENT_TAKE_WOODSTATUE].eventName); + SAFE_STRCAT(name, "@"); dComIfGp_getEvent()->setSkipZev(this, name); dComIfGp_getEvent()->onSkipFade(); break; diff --git a/src/d/actor/d_a_npc_ykw.cpp b/src/d/actor/d_a_npc_ykw.cpp index e2c97df58a..468445063a 100644 --- a/src/d/actor/d_a_npc_ykw.cpp +++ b/src/d/actor/d_a_npc_ykw.cpp @@ -20,6 +20,8 @@ #include "m_Do/m_Do_ext.h" #include +#include "dusk/string.hpp" + #if DEBUG class daNpc_ykW_HIO_c : public mDoHIO_entry_c { public: @@ -1411,11 +1413,11 @@ int daNpc_ykW_c::cutGoIntoBossRoom(int param_0) { if (skip != 0 && (prm == 0 || prm == 4)) { if (prm == 0) { - strcpy(unkStrBuf1, l_evtList[4].eventName); + SAFE_STRCPY(unkStrBuf1, l_evtList[4].eventName); } else { - strcpy(unkStrBuf1, l_evtList[5].eventName); + SAFE_STRCPY(unkStrBuf1, l_evtList[5].eventName); } - strcat(unkStrBuf1, "@"); + SAFE_STRCAT(unkStrBuf1, "@"); dComIfGp_getEvent()->setSkipZev(this, unkStrBuf1); } } diff --git a/src/d/actor/d_a_obj_flag.cpp b/src/d/actor/d_a_obj_flag.cpp index b3f55ff1d2..dcf0640e6b 100644 --- a/src/d/actor/d_a_obj_flag.cpp +++ b/src/d/actor/d_a_obj_flag.cpp @@ -192,7 +192,7 @@ inline int daObjFlag_c::createHeap() { tmp = true; char resName[12]; - sprintf(resName, "flag%02d.bmd", angle); + SAFE_SPRINTF(resName, "flag%02d.bmd", angle); shape_angle.setall(0); current.angle.setall(0); diff --git a/src/d/actor/d_a_obj_flag2.cpp b/src/d/actor/d_a_obj_flag2.cpp index 4741f4d5a3..35e0cac3bc 100644 --- a/src/d/actor/d_a_obj_flag2.cpp +++ b/src/d/actor/d_a_obj_flag2.cpp @@ -370,7 +370,7 @@ int daObjFlag2_c::createHeap() { s8 flagNum = (u8)shape_angle.x; if (mFlagValid) { char acStack_40[16]; - sprintf(acStack_40, "flag%02d.bti", flagNum); + SAFE_SPRINTF(acStack_40, "flag%02d.bti", flagNum); shape_angle.setall(0); current.angle.setall(0); ResTIMG* image = (ResTIMG*)dComIfG_getObjectRes(mFlagName, "flag.bti"); @@ -409,7 +409,7 @@ int daObjFlag2_c::create() { mFlagValid = false; } else { mFlagValid = true; - sprintf(mFlagName, "FlagObj%02d", flagNum); + SAFE_SPRINTF(mFlagName, "FlagObj%02d", flagNum); int rv = dComIfG_resLoad(&mFlagPhase, mFlagName); if (rv != cPhs_COMPLEATE_e) { return rv; diff --git a/src/d/actor/d_a_obj_flag3.cpp b/src/d/actor/d_a_obj_flag3.cpp index fc696db1ab..501cb792f6 100644 --- a/src/d/actor/d_a_obj_flag3.cpp +++ b/src/d/actor/d_a_obj_flag3.cpp @@ -299,7 +299,7 @@ int daObjFlag3_c::createHeap() { s8 flagNum = (u8)shape_angle.x; if (mFlagValid) { char acStack_40[16]; - sprintf(acStack_40, "flag%02d.bti", flagNum); + SAFE_SPRINTF(acStack_40, "flag%02d.bti", flagNum); shape_angle.setall(0); current.angle.setall(0); ResTIMG* image = (ResTIMG*)dComIfG_getObjectRes(mFlagName, "flag.bti"); @@ -486,7 +486,7 @@ int daObjFlag3_c::create() { mFlagValid = false; } else { mFlagValid = true; - sprintf(mFlagName, "FlagObj%02d", flagNum); + SAFE_SPRINTF(mFlagName, "FlagObj%02d", flagNum); int rv = dComIfG_resLoad(&mFlagPhase, mFlagName); if (rv != cPhs_COMPLEATE_e) { return rv; diff --git a/src/d/actor/d_a_obj_gra2.cpp b/src/d/actor/d_a_obj_gra2.cpp index 550b680e06..ff52818c42 100644 --- a/src/d/actor/d_a_obj_gra2.cpp +++ b/src/d/actor/d_a_obj_gra2.cpp @@ -5,13 +5,14 @@ #include "d/dolzel_rel.h" // IWYU pragma: keep -#include "d/actor/d_a_obj_gra2.h" #include "d/actor/d_a_npc4.h" +#include "d/actor/d_a_obj_gra2.h" #include "d/actor/d_a_tag_gra.h" #include "d/d_bg_w.h" #include "d/d_cc_uty.h" -#include "d/d_com_inf_game.h" #include "d/d_com_inf_actor.h" +#include "d/d_com_inf_game.h" +#include "dusk/string.hpp" #if DEBUG #include "d/d_debug_viewer.h" #endif @@ -539,7 +540,7 @@ const char* daObj_GrA_c::getResName() { u8 daObj_GrA_c::getMode() { u32 uVar1 = fopAcM_GetParam(this) >> 28 & 3; - strcpy(field_0x744, "Obj_grA"); + SAFE_STRCPY(field_0x744, "Obj_grA"); switch (uVar1) { case 1: diff --git a/src/d/actor/d_a_obj_sekizoa.cpp b/src/d/actor/d_a_obj_sekizoa.cpp index 19f4446b87..ac0ca0d5b1 100644 --- a/src/d/actor/d_a_obj_sekizoa.cpp +++ b/src/d/actor/d_a_obj_sekizoa.cpp @@ -8,6 +8,8 @@ #include "d/actor/d_a_tag_kmsg.h" #include #include + +#include "dusk/string.hpp" #include "f_op/f_op_actor_mng.h" #include "f_op/f_op_msg.h" @@ -1364,8 +1366,8 @@ int daObj_Sekizoa_c::cutStart(int i_staffIdx) { daObj_SMTile_c* actor_4 = (daObj_SMTile_c*)mActorMngrs[4].getActorP(); actor_4->reset(); dComIfGp_getEvent()->setPt2(actor_4); - strcpy(acStack_90, l_evtList[2].eventName); - strcat(acStack_90, "@"); + SAFE_STRCPY(acStack_90, l_evtList[2].eventName); + SAFE_STRCAT(acStack_90, "@"); dComIfGp_getEvent()->setSkipZev(this, acStack_90); dComIfGp_getEvent()->onSkipFade(); if (daNpcT_getPlayerInfoFromPlayerList(1, fopAcM_GetRoomNo(this), &c_stack_9c, @@ -1856,8 +1858,8 @@ int daObj_Sekizoa_c::cutGoal(int i_staffIdx) { switch (prm) { case 0: if (mType == TYPE_0) { - strcpy(acStack_9c, l_evtList[6].eventName); - strcat(acStack_9c, "@"); + SAFE_STRCPY(acStack_9c, l_evtList[6].eventName); + SAFE_STRCAT(acStack_9c, "@"); dComIfGp_getEvent()->setSkipZev(this, acStack_9c); dComIfGp_getEvent()->onSkipFade(); } @@ -2222,8 +2224,8 @@ int daObj_Sekizoa_c::cutExtinction(int i_staffIdx) { cStack_b0 += actor_0->current.pos; daPy_getPlayerActorClass()->setPlayerPosAndAngle(&cStack_b0, actor_0->shape_angle.y - -0x8000, 0); - strcpy(acStack_a4, l_evtList[9].eventName); - strcat(acStack_a4, "@"); + SAFE_STRCPY(acStack_a4, l_evtList[9].eventName); + SAFE_STRCAT(acStack_a4, "@"); dComIfGp_getEvent()->setSkipZev(actor_0, acStack_a4); dComIfGp_getEvent()->onSkipFade(); actor_0->pullMasterSword(); diff --git a/src/d/actor/d_a_set_bgobj.cpp b/src/d/actor/d_a_set_bgobj.cpp index 792f14840a..f898f34ffb 100644 --- a/src/d/actor/d_a_set_bgobj.cpp +++ b/src/d/actor/d_a_set_bgobj.cpp @@ -26,7 +26,7 @@ int daSetBgObj_c::CreateInit() { int daSetBgObj_c::create() { fopAcM_ct(this, daSetBgObj_c); - sprintf(mArcName, "%s", getArcName(this)); + SAFE_SPRINTF(mArcName, "%s", getArcName(this)); int phase = dComIfG_resLoad(&mPhase, mArcName); if (phase == cPhs_COMPLEATE_e) { diff --git a/src/d/actor/d_a_tag_evt.cpp b/src/d/actor/d_a_tag_evt.cpp index 8dc5954ef0..e68c980b23 100644 --- a/src/d/actor/d_a_tag_evt.cpp +++ b/src/d/actor/d_a_tag_evt.cpp @@ -8,6 +8,8 @@ #include "f_op/f_op_actor_mng.h" #include +#include "dusk/string.hpp" + static char* l_evtNameList[] = { NULL, "JUMP_DEMOSTAGE", @@ -26,7 +28,7 @@ int daTag_Evt_c::create() { cPhs_Step phase = dComIfG_resLoad(&mPhase, l_resFileName); if (phase == cPhs_COMPLEATE_e) { eventInfo.setArchiveName(l_resFileName); - strcpy(field_0x568, "TagEvt"); + SAFE_STRCPY(field_0x568, "TagEvt"); getParam(); field_0x572 = -1; } diff --git a/src/d/actor/d_a_tag_msg.cpp b/src/d/actor/d_a_tag_msg.cpp index 6e052f74ea..55b135964a 100644 --- a/src/d/actor/d_a_tag_msg.cpp +++ b/src/d/actor/d_a_tag_msg.cpp @@ -11,6 +11,8 @@ #include "d/d_debug_viewer.h" #include +#include "dusk/string.hpp" + static int createHeapCallBack(fopAc_ac_c* i_this) { daTag_Msg_c* msg = (daTag_Msg_c*)i_this; return msg->createHeap(); @@ -219,7 +221,7 @@ void daTag_Msg_c::getParam() { scale.x *= 100.0f; scale.y *= 100.0f; - strcpy(mStaffName, "Tag_ms"); + SAFE_STRCPY(mStaffName, "Tag_ms"); } char* daTag_Msg_c::mEvtCutTBL[2] = { diff --git a/src/d/actor/d_a_title.cpp b/src/d/actor/d_a_title.cpp index 5c9e503ea3..cfe4003158 100644 --- a/src/d/actor/d_a_title.cpp +++ b/src/d/actor/d_a_title.cpp @@ -250,7 +250,7 @@ void daTitle_c::loadWait_proc() { text[i]->setFont(mpFont); text[i]->setString(0x80, ""); - char* msg = text[i]->getStringPtr(); + TEXT_SPAN msg = text[i]->getStringPtr(); fopMsgM_messageGet(msg, 100); } diff --git a/src/d/d_bg_parts.cpp b/src/d/d_bg_parts.cpp index 3c378bf325..9dc7be181d 100644 --- a/src/d/d_bg_parts.cpp +++ b/src/d/d_bg_parts.cpp @@ -8,6 +8,8 @@ #include "JSystem/JKernel/JKRSolidHeap.h" #include +#include "dusk/string.hpp" + void dBgp_c::material_c::draw() { material_c* material = this; do { @@ -255,7 +257,7 @@ void dBgp_c::share_c::reset() { const char* dBgp_c::share_c::getArcName() { static char arcName[8]; - sprintf(arcName, "@mt%04x", mId); + SAFE_SPRINTF(arcName, "@mt%04x", mId); return arcName; } @@ -430,7 +432,7 @@ dBgp_c::packet_c::packet_c() { void dBgp_c::create(s8 i_roomNo, void* i_data) { mPointer = i_data; mPacket.setRoomNo(i_roomNo); - strcpy(mArcName, dComIfG_getRoomArcName(i_roomNo)); + SAFE_STRCPY(mArcName, dComIfG_getRoomArcName(i_roomNo)); if (mPointer != NULL) { JKRExpHeap* block = dStage_roomControl_c::getMemoryBlock(i_roomNo); @@ -457,7 +459,7 @@ void dBgp_c::create(s8 i_roomNo, void* i_data) { unit_group_class* unitGroup = mapUnit->groups; for (int i = 0; i < mapUnit->num; i++) { char resName[16]; - sprintf(resName, "bp%04d.dzb", i); + SAFE_SPRINTF(resName, "bp%04d.dzb", i); cBgD_t* dzb = (cBgD_t*)dComIfG_getStageRes(mArcName, resName); if (dzb != NULL) { @@ -534,7 +536,7 @@ int dBgp_c::remove() { const char* dBgp_c::getArcName(u16 i_id, u16 i_arg) { static char arcName[8]; - sprintf(arcName, "@%03x%03x", i_id, i_arg); + SAFE_SPRINTF(arcName, "@%03x%03x", i_id, i_arg); return arcName; } diff --git a/src/d/d_com_inf_game.cpp b/src/d/d_com_inf_game.cpp index 7e17cb501e..93fc15ca8f 100644 --- a/src/d/d_com_inf_game.cpp +++ b/src/d/d_com_inf_game.cpp @@ -26,6 +26,8 @@ #include #include +#include "dusk/string.hpp" + void dComIfG_play_c::ct() { mWindowNum = 0; mParticle = NULL; @@ -2649,7 +2651,7 @@ static void dComIfGs_setWarpItemData(int param_0, char const* i_stage, cXyz i_po void dComIfG_play_c::setWarpItemData(char const* i_stage, cXyz i_pos, s16 i_angle, s8 i_roomNo, u8 param_4, u8 param_5) { - strcpy(mItemInfo.mWarpItemData.mWarpItemStage, i_stage); + SAFE_STRCPY(mItemInfo.mWarpItemData.mWarpItemStage, i_stage); mItemInfo.mWarpItemData.mWarpItemPos.set(i_pos); mItemInfo.mWarpItemData.mWarpItemAngle = i_angle; mItemInfo.mWarpItemData.mWarpItemRoom = i_roomNo; @@ -2736,7 +2738,7 @@ void* dComIfG_getOldStageRes(char const* i_resName) { char* dComIfG_getRoomArcName(int i_roomNo) { static char buf[32]; - sprintf(buf, "R%02d_00", i_roomNo); + SAFE_SPRINTF(buf, "R%02d_00", i_roomNo); return buf; } diff --git a/src/d/d_com_static.cpp b/src/d/d_com_static.cpp index ee05d34170..585d39869b 100644 --- a/src/d/d_com_static.cpp +++ b/src/d/d_com_static.cpp @@ -351,13 +351,13 @@ const char* daSetBgObj_c::getArcName(fopAc_ac_c* i_this) { u32 r30 = fopAcM_GetParam(i_this); u16 r29 = fopAcM_GetParam(i_this); - sprintf(arcName, "@bg%04x", r29); + SAFE_SPRINTF(arcName, "@bg%04x", r29); if (DEBUG && r30 & 0x80000000) { OS_REPORT("\e[43;30m旧仕様の地形ユニットMoveBGが残っています!!!\n\e[m"); u16 r28 = r30 >> 12 & 0x1FF; u16 r27 = r30 & 0xFFF; - sprintf(arcName, "@%03x%03x", r28, (u16)r27); + SAFE_SPRINTF(arcName, "@%03x%03x", r28, (u16)r27); } return arcName; } diff --git a/src/d/d_debug_camera.cpp b/src/d/d_debug_camera.cpp index 131a2c05ae..9ff5b0fcf8 100644 --- a/src/d/d_debug_camera.cpp +++ b/src/d/d_debug_camera.cpp @@ -1349,11 +1349,11 @@ void dDbgCamera_c::monitor() { for (var_r27 = 0; var_r27 < 4; var_r27++) { if (var_r27 == mCmdMode) { - strcat(spC0, "____ "); + SAFE_STRCAT(spC0, "____ "); break; } - strcat(spC0, " "); + SAFE_STRCAT(spC0, " "); } Report(pos_x, pos_y, 7, " %s", spC0); diff --git a/src/d/d_ev_camera.cpp b/src/d/d_ev_camera.cpp index 736f9f7a82..fad6d1a523 100644 --- a/src/d/d_ev_camera.cpp +++ b/src/d/d_ev_camera.cpp @@ -13,6 +13,8 @@ #include "d/actor/d_a_alink.h" #include +#include "dusk/string.hpp" + #ifdef __MWERKS__ #define LOAD_4BYTE_STRING_LITERAL(x) (*(u32*)(x)) #else @@ -46,7 +48,7 @@ int dCamera_c::StartEventCamera(int param_0, int param_1, ...) { for (int i = 0; i < 8; i++) { char* param_name = va_arg(args, char*); if (param_name != NULL) { - strcpy(mEventData.mEventParams[i].name, param_name); + SAFE_STRCPY(mEventData.mEventParams[i].name, param_name); mEventData.mEventParams[i].field_0x10 = va_arg(args, int); mEventData.mEventParams[i].value = va_arg(args, uintptr_t); } else { @@ -341,18 +343,23 @@ bool dCamera_c::getEvXyzData(cXyz* i_data, char* i_event, cXyz param_2) { return 1; } +#if TARGET_PC +template +bool dCamera_c::getEvStringData(char (&i_data)[N], char* i_event, char* param_2) { +#else bool dCamera_c::getEvStringData(char* i_data, char* i_event, char* param_2) { +#endif if (chkFlag(0x20000000)) { int index = searchEventArgData(i_event); if (index == -1) { - strcpy(i_data, param_2); + SAFE_STRCPY(i_data, param_2); } else { - strcpy(i_data, (char*)mEventData.mEventParams[index].value); + SAFE_STRCPY(i_data, (char*)mEventData.mEventParams[index].value); } } else if (dComIfGp_evmng_getMySubstanceNum(mEventData.mStaffIdx, i_event) != 0) { - strcpy(i_data, dComIfGp_evmng_getMyStringP(mEventData.mStaffIdx, i_event)); + SAFE_STRCPY(i_data, dComIfGp_evmng_getMyStringP(mEventData.mStaffIdx, i_event)); } else { - strcpy(i_data, param_2); + SAFE_STRCPY(i_data, param_2); #if DEBUG if (mCurCamStyleTimer == 0 && mCamSetup.CheckFlag(0x40)) { OS_REPORT("camera: event: %16s: %s (d)\n", i_event, i_data); @@ -369,6 +376,11 @@ bool dCamera_c::getEvStringData(char* i_data, char* i_event, char* param_2) { return 1; } +#if TARGET_PC +// Used in another TU, so force instantiation to avoid linker issues. +template bool dCamera_c::getEvStringData(char (&i_data)[12], char* i_event, char* param_2); +#endif + char* dCamera_c::getEvStringPntData(char* i_event, char* param_1) { char* string = NULL; @@ -520,7 +532,7 @@ bool dCamera_c::fixedFrameEvCamera() { #if DEBUG if (strlen(fframe_p->mRelUseMask) != 2) { OSReport("camera: event: bad length -> xx\n"); - strcpy(fframe_p->mRelUseMask, "xx"); + SAFE_STRCPY(fframe_p->mRelUseMask, "xx"); JUTAssertion::showAssert(JUTAssertion::getSDevice(), "d_ev_camera.cpp", 0x32e, "0"); OSPanic("d_ev_camera.cpp", 0x32e, "Halt"); } @@ -613,7 +625,7 @@ bool dCamera_c::fixedFrameEvCamera() { } fframe_p->field_0x4 = relationalPos(fframe_p->mpRelActor, &sp44); - } else if (fframe_p->mRelUseMask[1] == 116) { + } else if (fframe_p->mRelUseMask[1] == 't') { fframe_p->field_0x4 = attentionPos(fframe_p->mpRelActor) + sp44; } else { fframe_p->field_0x4 = sp44; @@ -866,7 +878,7 @@ bool dCamera_c::fixedPositionEvCamera() { getEvFloatData(&fpos_p->field_0x38, "Radius", 100000.0f); getEvFloatData(&fpos_p->field_0x34, "StartRadius", fpos_p->field_0x38); fpos_p->field_0x1 = getEvFloatData(&fpos_p->field_0x2c, "Bank", 0.0f); - getEvStringData(&fpos_p->field_0x48, "RelUseMask", "o"); + getEvStringData(fpos_p->field_0x48, "RelUseMask", "o"); fpos_p->field_0x0 = getEvIntData(&fpos_p->field_0x4c, "Timer", -1); if ((fpos_p->field_0x40 = getEvActor("Target", "@PLAYER")) == NULL) { @@ -877,7 +889,7 @@ bool dCamera_c::fixedPositionEvCamera() { fpos_p->field_0x44 = fopAcM_GetID(fpos_p->field_0x40); fpos_p->field_0x3c = getEvActor("RelActor"); - if (fpos_p->field_0x3c && isRelChar(fpos_p->field_0x48)) { + if (fpos_p->field_0x3c && isRelChar(fpos_p->field_0x48[0])) { fpos_p->field_0x4 = relationalPos(fpos_p->field_0x3c, &sp24); } else { fpos_p->field_0x4 = sp24; @@ -1021,7 +1033,7 @@ bool dCamera_c::transEvCamera(int param_1) { getEvIntData(&trans->mTransType, "TransType", 0); trans->mRelActor = getEvActor("RelActor"); - getEvStringData(&trans->mRelUseMask, "RelUseMask", "--oo"); + getEvStringData(trans->mRelUseMask, "RelUseMask", "--oo"); getEvFloatData(&trans->mCushion, "Cushion", 1.0f); if (trans->mRelActor) { @@ -1034,36 +1046,36 @@ bool dCamera_c::transEvCamera(int param_1) { mAdditionVec = MidnaAdditionVec; } - if (trans->mRelUseMask == 119) { + if (trans->mRelUseMask[0] == 'w') { trans->mStartCenter += mAdditionVec; } - if (trans->mRelUseMask == 87) { + if (trans->mRelUseMask[0] == 'W') { trans->mStartCenter -= mAdditionVec; } - if (trans->field_0x49 == 119) { + if (trans->mRelUseMask[1] == 'w') { trans->mStartEye += mAdditionVec; } - if (trans->field_0x49 == 87) { + if (trans->mRelUseMask[1] == 'W') { trans->mStartEye -= mAdditionVec; } - if (trans->field_0x4a == 119) { + if (trans->mRelUseMask[2] == 'w') { trans->mCenter += mAdditionVec; } - if (trans->field_0x4a == 87) { + if (trans->mRelUseMask[2] == 'W') { trans->mCenter -= mAdditionVec; } - if (trans->field_0x4b == 119) { + if (trans->mRelUseMask[3] == 'w') { trans->mEye += mAdditionVec; } - if (trans->field_0x4b == 87) { + if (trans->mRelUseMask[3] == 'W') { trans->mEye -= mAdditionVec; } } - if (trans->field_0x49 == 114) { + if (trans->mRelUseMask[1] == 'r') { my_vec_0 = relationalPos(trans->mRelActor, &trans->mStartCenter); if ((mTicks & 1) != 0) { trans->mStartEye.x = -trans->mStartEye.x; @@ -1075,14 +1087,14 @@ bool dCamera_c::transEvCamera(int param_1) { } } - if (trans->mRelUseMask == 110 || trans->field_0x49 == 110) { + if (trans->mRelUseMask[0] == 'n' || trans->mRelUseMask[1] == 'n') { cSGlobe cStack_7b8(mEye - positionOf(trans->mRelActor)); cSAngle acStack_898 = cStack_7b8.U() - directionOf(trans->mRelActor); if (acStack_898 < cSAngle::_0) { - if (trans->mRelUseMask == 110) { + if (trans->mRelUseMask[0] == 'n') { trans->mStartCenter.x = -trans->mStartCenter.x; } - if (trans->field_0x49 == 110) { + if (trans->mRelUseMask[1] == 'n') { trans->mStartEye.x = -trans->mStartEye.x; } } @@ -1094,15 +1106,15 @@ bool dCamera_c::transEvCamera(int param_1) { } } - if (trans->field_0x4a == 110 || trans->field_0x4b == 110) { + if (trans->mRelUseMask[2] == 'n' || trans->mRelUseMask[3] == 'n') { cSGlobe cStack_7c0(mEye - positionOf(trans->mRelActor)); cSAngle acStack_89c = cStack_7c0.U() - directionOf(trans->mRelActor); if (acStack_89c < cSAngle::_0) { - if (trans->field_0x4a == 110) { + if (trans->mRelUseMask[2] == 'n') { trans->mCenter.x = -trans->mCenter.x; } - if (trans->field_0x4b == 110) { + if (trans->mRelUseMask[3] == 'n') { trans->mEye.x = -trans->mEye.x; } } @@ -1114,15 +1126,15 @@ bool dCamera_c::transEvCamera(int param_1) { } } - if (trans->mRelUseMask == 78 || trans->field_0x49 == 78) { + if (trans->mRelUseMask[0] == 'N' || trans->mRelUseMask[1] == 'N') { cSGlobe cStack_7c8(mEye - positionOf(trans->mRelActor)); cSAngle acStack_8a0 = cStack_7c8.U() - directionOf(trans->mRelActor); if (acStack_8a0 > cSAngle::_0) { - if (trans->mRelUseMask == 78) { + if (trans->mRelUseMask[0] == 'N') { trans->mStartCenter.x = -trans->mStartCenter.x; } - if (trans->field_0x49 == 78) { + if (trans->mRelUseMask[1] == 'N') { trans->mStartEye.x = -trans->mStartEye.x; } } @@ -1134,15 +1146,15 @@ bool dCamera_c::transEvCamera(int param_1) { } } - if (trans->field_0x4a == 78 || trans->field_0x4b == 78) { + if (trans->mRelUseMask[2] == 'N' || trans->mRelUseMask[3] == 'N') { cSGlobe cStack_7d0(mEye - positionOf(trans->mRelActor)); cSAngle acStack_8a4 = cStack_7d0.U() - directionOf(trans->mRelActor); if (acStack_8a4 > cSAngle::_0) { - if (trans->field_0x4a == 78) { + if (trans->mRelUseMask[2] == 'N') { trans->mCenter.x = -trans->mCenter.x; } - if (trans->field_0x4b == 78) { + if (trans->mRelUseMask[3] == 'N') { trans->mEye.x = -trans->mEye.x; } } @@ -1154,21 +1166,21 @@ bool dCamera_c::transEvCamera(int param_1) { } } - if (trans->mRelUseMask == 102) { + if (trans->mRelUseMask[0] == 'f') { cSGlobe cStack_7d8(trans->mStartCenter); cStack_7d8.U(directionOf(trans->mRelActor) + cStack_7d8.U()); trans->mStartCenter = attentionPos(trans->mRelActor) + cStack_7d8.Xyz(); - trans->mRelUseMask = 120; + trans->mRelUseMask[0] = 'x'; } - if (trans->field_0x49 == 102) { + if (trans->mRelUseMask[1] == 'f') { cSGlobe cStack_7e0(trans->mStartEye); cStack_7e0.U(directionOf(trans->mRelActor) + cStack_7e0.U()); trans->mStartEye = attentionPos(trans->mRelActor) + cStack_7e0.Xyz(); - trans->field_0x49 = 120; + trans->mRelUseMask[1] = 'x'; } - if (trans->field_0x4a == 112) { + if (trans->mRelUseMask[2] == 'p') { cXyz sp114(trans->mCenter); cXyz sp120 = relationalPos(trans->mRelActor, &sp114); f32 fVar1 = cXyz(sp120 - positionOf(mpPlayerActor)).abs(); @@ -1178,14 +1190,14 @@ bool dCamera_c::transEvCamera(int param_1) { if (fVar1 < fVar2) { trans->mCenter.x = -trans->mCenter.x; } - } else if (trans->field_0x4a == 102) { + } else if (trans->mRelUseMask[2] == 'f') { cSGlobe cStack_7e8(trans->mCenter); cStack_7e8.U(directionOf(trans->mRelActor) + cStack_7e8.U()); trans->mCenter = attentionPos(trans->mRelActor) + cStack_7e8.Xyz(); - trans->field_0x4a = 120; + trans->mRelUseMask[2] = 'x'; } - if (trans->field_0x4b == 112) { + if (trans->mRelUseMask[3] == 'p') { cXyz sp12c = trans->mEye; cXyz sp138(relationalPos(trans->mRelActor, &sp12c)); f32 fVar3 = cXyz(sp138 - positionOf(mpPlayerActor)).abs(); @@ -1195,7 +1207,7 @@ bool dCamera_c::transEvCamera(int param_1) { if (fVar3 < fVar4) { trans->mEye.x = -trans->mEye.x; } - } else if (trans->field_0x4b == 114) { + } else if (trans->mRelUseMask[3] == 'r') { my_vec_0 = relationalPos(trans->mRelActor, &trans->mCenter); if ((mTicks & 1) != 0) { trans->mEye.x = -trans->mEye.x; @@ -1205,19 +1217,19 @@ bool dCamera_c::transEvCamera(int param_1) { if (lineBGCheck(&my_vec_0, &my_vec_1, 0x4007)) { trans->mEye.x = -trans->mEye.x; } - } else if (trans->field_0x4b == 102) { + } else if (trans->mRelUseMask[3] == 'f') { cSGlobe cStack_7f0(trans->mEye); cStack_7f0.U(directionOf(trans->mRelActor) + cStack_7f0.U()); trans->mEye = attentionPos(trans->mRelActor) + cStack_7f0.Xyz(); - trans->field_0x4b = 120; + trans->mRelUseMask[3] = 'x'; } } else { - if (trans->field_0x4a == 97) { + if (trans->mRelUseMask[2] == 'a') { cXyz cStack_320 = dCamMath::xyzRotateY(trans->mCenter, cSAngle(mViewCache.mDirection.U().Inv())); trans->mCenter = mViewCache.mCenter + cStack_320; } - if (trans->field_0x4b == 97) { + if (trans->mRelUseMask[3] == 'a') { cXyz cStack_32c = dCamMath::xyzRotateY(trans->mEye, cSAngle(mViewCache.mDirection.U().Inv())); trans->mEye = mViewCache.mEye + cStack_32c; } @@ -1246,47 +1258,47 @@ bool dCamera_c::transEvCamera(int param_1) { } if (trans->mRelActor) { - if (trans->mRelUseMask == 116) { + if (trans->mRelUseMask[0] == 't') { pos.mXyz_1 = attentionPos(trans->mRelActor) + trans->mStartCenter; - } else if (trans->mRelUseMask == 99) { + } else if (trans->mRelUseMask[0] == 'c') { cSGlobe cStack_7f8(trans->mStartCenter); cStack_7f8.U(trans->field_0x60.U() + cStack_7f8.U()); pos.mXyz_1 = attentionPos(trans->mRelActor) + cStack_7f8.Xyz(); - } else if (trans->mRelUseMask == 119 || trans->mRelUseMask == 87) { + } else if (trans->mRelUseMask[0] == 'w' || trans->mRelUseMask[0] == 'W') { pos.mXyz_1 = relationalPos(trans->mRelActor, &trans->mStartCenter); } else { - if (isRelChar(trans->mRelUseMask)) { + if (isRelChar(trans->mRelUseMask[0])) { pos.mXyz_1 = relationalPos(trans->mRelActor, &trans->mStartCenter); } else { pos.mXyz_1 = trans->mStartCenter; } } - if (trans->field_0x49 == 116) { + if (trans->mRelUseMask[1] == 't') { pos.mXyz_0 = attentionPos(trans->mRelActor) + trans->mStartEye; - } else if (trans->field_0x49 == 99) { + } else if (trans->mRelUseMask[1] == 'c') { cSGlobe cStack_800(trans->mStartEye); cStack_800.U(trans->field_0x60.U() + cStack_800.U()); pos.mXyz_0 = attentionPos(trans->mRelActor) + cStack_800.Xyz(); - } else if (trans->field_0x49 == 119 || trans->field_0x49 == 87) { + } else if (trans->mRelUseMask[1] == 'w' || trans->mRelUseMask[1] == 'W') { pos.mXyz_0 = relationalPos(trans->mRelActor, &trans->mStartEye); } else { - if (isRelChar(trans->field_0x49)) { + if (isRelChar(trans->mRelUseMask[1])) { pos.mXyz_0 = relationalPos(trans->mRelActor, &trans->mStartEye); } else { pos.mXyz_0 = trans->mStartEye; } } - if (trans->field_0x4a == 116) { + if (trans->mRelUseMask[2] == 't') { pos2.mXyz_1 = attentionPos(trans->mRelActor) + trans->mCenter; - } else if (trans->field_0x4a == 99) { + } else if (trans->mRelUseMask[2] == 'c') { cSGlobe cStack_808(trans->mCenter); cStack_808.U(trans->field_0x60.U() + cStack_808.U()); pos2.mXyz_1 = attentionPos(trans->mRelActor) + cStack_808.Xyz(); - } else if (trans->field_0x4a == 119 || trans->field_0x4a == 87) { + } else if (trans->mRelUseMask[2] == 'w' || trans->mRelUseMask[2] == 'W') { pos2.mXyz_1 = relationalPos(trans->mRelActor, &trans->mCenter); - } else if (isRelChar(trans->field_0x4a)) { + } else if (isRelChar(trans->mRelUseMask[2])) { pos2.mXyz_1 = relationalPos(trans->mRelActor, &trans->mCenter); } else if (trans->mTransType == 2) { pos2.mXyz_1 = dCamMath::xyzRotateY(trans->mCenter, directionOf(trans->mRelActor)); @@ -1294,16 +1306,16 @@ bool dCamera_c::transEvCamera(int param_1) { pos2.mXyz_1 = trans->mCenter; } - if (trans->field_0x4b == 116) { + if (trans->mRelUseMask[3] == 't') { pos2.mXyz_0 = attentionPos(trans->mRelActor) + trans->mEye; - } else if (trans->field_0x4b == 99) { + } else if (trans->mRelUseMask[3] == 'c') { cSGlobe cStack_810(trans->mEye); cStack_810.U(trans->field_0x60.U() + cStack_810.U()); pos2.mXyz_0 = attentionPos(trans->mRelActor) + cStack_810.Xyz(); } else { - if (trans->field_0x4b == 119 || trans->field_0x4b == 87) { + if (trans->mRelUseMask[3] == 'w' || trans->mRelUseMask[3] == 'W') { pos2.mXyz_0 = relationalPos(trans->mRelActor, &trans->mEye); - } else if (isRelChar(trans->field_0x4b)) { + } else if (isRelChar(trans->mRelUseMask[3])) { pos2.mXyz_0 = relationalPos(trans->mRelActor, &trans->mEye); } else if (trans->mTransType == 2) { pos2.mXyz_0 = dCamMath::xyzRotateY(trans->mEye, directionOf(trans->mRelActor)); @@ -3420,11 +3432,11 @@ bool dCamera_c::fixedFramesEvCamera() { fframes_p->field_0x0 = getEvIntData(&fframes_p->mTimer, "Timer", const_1_val); - getEvStringData(&fframes_p->mRelUseMask, "RelUseMask", "oo"); + getEvStringData(fframes_p->mRelUseMask, "RelUseMask", "oo"); #if DEBUG - if (strlen(&fframes_p->mRelUseMask) != 2) { + if (strlen(fframes_p->mRelUseMask) != 2) { OSReport("camera: event: bad length -> xx\n"); - strcpy(&fframes_p->mRelUseMask, "xx"); + SAFE_STRCPY(fframes_p->mRelUseMask, "xx"); JUTAssertion::showAssert(JUTAssertion::getSDevice(), "d_ev_camera.cpp", 0x129c, "Halt"); OSPanic("d_ev_camera.cpp", 0x129c, "Halt"); } @@ -3442,13 +3454,13 @@ bool dCamera_c::fixedFramesEvCamera() { sp30 = fframes_p->field_0x1c[1][iVar1]; sp3c = fframes_p->field_0x1c[0][iVar1]; - if (fframes_p->mRelActor && fframes_p->mRelUseMask == 111) { + if (fframes_p->mRelActor && fframes_p->mRelUseMask[0] == 111) { fframes_p->field_0x4 = relationalPos(fframes_p->mRelActor, &sp30); } else { fframes_p->field_0x4 = sp30; } - if (fframes_p->mRelActor && fframes_p->field_0x31 == 111) { + if (fframes_p->mRelActor && fframes_p->mRelUseMask[1] == 111) { fframes_p->field_0x10 = relationalPos(fframes_p->mRelActor, &sp3c); } else { fframes_p->field_0x10 = sp3c; @@ -3959,7 +3971,7 @@ bool dCamera_c::bspTransEvCamera() { bspTrans->mSet1 = 0; char use1[8]; - strcpy(use1, "xxxxxx"); + SAFE_STRCPY(use1, "xxxxxx"); iVar1 = getEvFloatListData(&bspTrans->mSet1, "Set1"); if (iVar1 != 0) { @@ -3970,7 +3982,7 @@ bool dCamera_c::bspTransEvCamera() { #if DEBUG if (strlen(use1) != 6) { OSReport("camera: event: bad length -> xxxxxx\n"); - strcpy(use1, "xxxxxx"); + SAFE_STRCPY(use1, "xxxxxx"); JUTAssertion::showAssert(JUTAssertion::getSDevice(), "d_ev_camera.cpp", 0x14f9, "0"); OSPanic("d_ev_camera.cpp", 0x14f9, "Halt"); } @@ -3979,7 +3991,7 @@ bool dCamera_c::bspTransEvCamera() { bspTrans->mSet2 = 0; char use2[8]; - strcpy(use2, "xxxxxx"); + SAFE_STRCPY(use2, "xxxxxx"); iVar1 = getEvFloatListData(&bspTrans->mSet2, "Set2"); if (iVar1 != 0) { @@ -3990,7 +4002,7 @@ bool dCamera_c::bspTransEvCamera() { #if DEBUG if (strlen(use2) != 6) { OSReport_Error("camera: event: bad length -> xxxxxx\n"); - strcpy(use2, "xxxxxx"); + SAFE_STRCPY(use2, "xxxxxx"); JUTAssertion::showAssert(JUTAssertion::getSDevice(), "d_ev_camera.cpp", 0x1509, "0"); OSPanic("d_ev_camera.cpp", 0x1509, "Halt"); } @@ -4000,12 +4012,12 @@ bool dCamera_c::bspTransEvCamera() { bspTrans->mRelActorID = -1; bspTrans->mRelActor = getEvActor("RelActor"); if (bspTrans->mRelActor) { - getEvStringData(&bspTrans->mRelUseMask, "RelUseMask", "oo"); + getEvStringData(bspTrans->mRelUseMask, "RelUseMask", "oo"); #if DEBUG - if (strlen(&bspTrans->mRelUseMask) != 2) { + if (strlen(bspTrans->mRelUseMask) != 2) { OSReport_Error("camera: event: bad length -> xx\n"); - strcpy(&bspTrans->mRelUseMask, "xx"); + SAFE_STRCPY(bspTrans->mRelUseMask, "xx"); JUTAssertion::showAssert(JUTAssertion::getSDevice(), "d_ev_camera.cpp", 0x1515, "0"); OSPanic("d_ev_camera.cpp", 0x1515, "Halt"); } @@ -4041,13 +4053,13 @@ bool dCamera_c::bspTransEvCamera() { } pos; if (bspTrans->mRelActor != NULL) { - if (isRelChar(bspTrans->mRelUseMask)) { + if (isRelChar(bspTrans->mRelUseMask[0])) { pos.sp48 = relationalPos(bspTrans->mRelActor, &bspTrans->field_0x94); } else { pos.sp48 = bspTrans->field_0x94; } - if (isRelChar(bspTrans->field_0xb9)) { + if (isRelChar(bspTrans->mRelUseMask[1])) { pos.sp3c = relationalPos(bspTrans->mRelActor, &bspTrans->field_0xa0); } else { pos.sp3c = bspTrans->field_0xa0; diff --git a/src/d/d_event.cpp b/src/d/d_event.cpp index 73891276df..3eba7aa041 100644 --- a/src/d/d_event.cpp +++ b/src/d/d_event.cpp @@ -13,6 +13,8 @@ #include "SSystem/SComponent/c_counter.h" #include +#include "dusk/string.hpp" + namespace { static u8 event_debug_evnt() { #if DEBUG @@ -756,8 +758,8 @@ int dEv_defaultSkipZev(void* actor, int parameter) { char* skipName; switch (parameter) { case 0: - strcpy(eventName, data->data.event_name); - strcat(eventName, "$0"); + SAFE_STRCPY(eventName, data->data.event_name); + SAFE_STRCAT(eventName, "$0"); eventID = dComIfGp_getEventManager().getEventIdx(eventName, 0xFF, -1); OS_REPORT("%06d: event: [%d] %s!\n", g_Counter.mCounter0, eventID, eventName); break; @@ -804,8 +806,8 @@ int dEv_defaultSkipStb(void* actor, int parameter) { char* skipName; switch (parameter) { case 0: - strcpy(eventName, data->data.event_name); - strcat(eventName, "$0"); + SAFE_STRCPY(eventName, data->data.event_name); + SAFE_STRCAT(eventName, "$0"); eventID = dComIfGp_getEventManager().getEventIdx(eventName, 0xFF, -1); OS_REPORT("%06d: event: [%d] %s!\n", g_Counter.mCounter0, eventID, eventName); break; @@ -851,7 +853,7 @@ void dEvt_control_c::setSkipProc(void* skipActor, dEvt_SkipCb skipCb, int skipPa void dEvt_control_c::setSkipZev(void* skipActor, char* eventName) { setSkipProc(skipActor, dEv_defaultSkipZev, 1); - strcpy(mSkipEventName, eventName); + SAFE_STRCPY(mSkipEventName, eventName); } void dEvt_control_c::onSkipFade() { diff --git a/src/d/d_event_manager.cpp b/src/d/d_event_manager.cpp index 598b973b84..d1d56be90f 100644 --- a/src/d/d_event_manager.cpp +++ b/src/d/d_event_manager.cpp @@ -15,6 +15,8 @@ #include "SSystem/SComponent/c_counter.h" #include +#include "dusk/string.hpp" + #if DEBUG static dEvM_HIO_c l_HIO; #endif @@ -388,7 +390,7 @@ void dEvent_manager_c::roomInit(int roomNo) { } char arcname[8]; - strcpy(arcname, dComIfG_getRoomArcName(roomNo)); + SAFE_STRCPY(arcname, dComIfG_getRoomArcName(roomNo)); char* res = (char*)dComIfG_getStageRes(arcname, DataFileName); int i; @@ -836,7 +838,7 @@ s16 dEvent_manager_c::getEventIdx(const char* eventName, u8 mapToolID, s32 roomN case dStage_MapEvent_dt_TYPE_STB: return getEventIdx(mapdata->data.event_name, 0xFF, roomNo); case dStage_MapEvent_dt_TYPE_MAPTOOLCAMERA: - sprintf(map_tool_name, "MapToolCamera%d", mapToolID); + SAFE_SPRINTF(map_tool_name, "MapToolCamera%d", mapToolID); return getEventIdx(map_tool_name, 0xFF, roomNo); default: JUT_ASSERT(1278, FALSE); @@ -876,7 +878,7 @@ s16 dEvent_manager_c::getEventIdx(fopAc_ac_c* actor, u8 mapToolID) { case dStage_MapEvent_dt_TYPE_STB: return getEventIdx(actor, mapdata->data.event_name, 0xFF); case dStage_MapEvent_dt_TYPE_MAPTOOLCAMERA: - sprintf(map_tool_name, "MapToolCamera%d", mapToolID); + SAFE_SPRINTF(map_tool_name, "MapToolCamera%d", mapToolID); return getEventIdx(actor, map_tool_name, 0xFF); default: JUT_ASSERT(1341, FALSE); @@ -901,7 +903,7 @@ s16 dEvent_manager_c::getEventIdx(fopAc_ac_c* actor, const char* eventName, u8 m case dStage_MapEvent_dt_TYPE_STB: return getEventIdx(actor, mapdata->data.event_name, 0xFF); case dStage_MapEvent_dt_TYPE_MAPTOOLCAMERA: - sprintf(map_tool_name, "MapToolCamera%d", mapToolID); + SAFE_SPRINTF(map_tool_name, "MapToolCamera%d", mapToolID); return getEventIdx(actor, map_tool_name, 0xFF); default: JUT_ASSERT(1376, FALSE); @@ -1040,7 +1042,7 @@ int dEvent_manager_c::getMyStaffId(const char* staffName, fopAc_ac_c* actor, int dEvDtStaff_c* staff = getBase().getStaffP(staff_id); if (staff->getType() != dEvDtStaff_c::TYPE_ALL) { char buf[20]; - strcpy(buf, staff->getName()); + SAFE_STRCPY(buf, staff->getName()); char* ptr = NULL; if (!hasDp) { @@ -1310,7 +1312,7 @@ void dEvent_manager_c::issueStaff(const char* staffname) { fopAcM_Search((fopAcIt_JudgeFunc)extraOnObjectCallBack, NULL); } else { char nameBuf[32]; - strcpy(nameBuf, staffname); + SAFE_STRCPY(nameBuf, staffname); fopAc_ac_c* actor = fopAcM_searchFromName4Event(nameBuf, -1); fopAcM_OnStatus(actor, fopAcStts_STAFF_EXTRA_e); } @@ -1321,7 +1323,7 @@ void dEvent_manager_c::cancelStaff(const char* staffname) { fopAcM_Search((fopAcIt_JudgeFunc)extraOffObjectCallBack, NULL); } else { char nameBuf[32]; - strcpy(nameBuf, staffname); + SAFE_STRCPY(nameBuf, staffname); fopAc_ac_c* actor = fopAcM_searchFromName4Event(nameBuf, -1); fopAcM_OffStatus(actor, fopAcStts_STAFF_EXTRA_e); } diff --git a/src/d/d_file_sel_info.cpp b/src/d/d_file_sel_info.cpp index 0c987e6d63..d54bc4424d 100644 --- a/src/d/d_file_sel_info.cpp +++ b/src/d/d_file_sel_info.cpp @@ -14,6 +14,7 @@ #include #include +#include "dusk/string.hpp" #include "dusk/version.hpp" dFile_info_c::dFile_info_c(JKRArchive* i_archive, u8 param_1) { @@ -80,9 +81,11 @@ void dFile_info_c::screenSet() { info_text[2] = (J2DTextBox*)mFileInfo.Scr->search(MULTI_CHAR('w_time01')); info_text[3] = (J2DTextBox*)mFileInfo.Scr->search(MULTI_CHAR('w_ptim01')); +#define INFO_TEXT_SIZE 0x40 + for (int i = 0; i < 4; i++) { info_text[i]->setFont(mFileInfo.mFont); - info_text[i]->setString(0x40, ""); + info_text[i]->setString(INFO_TEXT_SIZE, ""); } mPlayerName = info_text[0]->getStringPtr(); mSaveStatus = info_text[1]->getStringPtr(); @@ -99,9 +102,9 @@ int dFile_info_c::setSaveData(dSv_save_c* i_savedata, BOOL i_validChksum, u8 i_d i_savedata->getPlayer().getPlayerStatusA().setLife(dComIfGs_getLife()); setHeartCnt(i_savedata); i_savedata->getPlayer().getPlayerStatusA().setLife(12); - strcpy(mPlayerName, dComIfGs_getPlayerName()); - strcpy(mSaveDate, ""); - strcpy(mPlayTime, ""); + SAFE_STRCPY(mPlayerName, dComIfGs_getPlayerName()); + SAFE_STRCPY(mSaveDate, ""); + SAFE_STRCPY(mPlayTime, ""); dMeter2Info_getString(0x4D, mSaveStatus, NULL); // New Quest Log result = 2; } else { @@ -110,7 +113,7 @@ int dFile_info_c::setSaveData(dSv_save_c* i_savedata, BOOL i_validChksum, u8 i_d } } else { setHeartCnt(i_savedata); - strcpy(mPlayerName, player_name); + SAFE_STRCPY(mPlayerName, player_name); setSaveDate(i_savedata); setPlayTime(i_savedata); result = 0; @@ -173,13 +176,13 @@ void dFile_info_c::setSaveDate(dSv_save_c* i_savedata) { #if TARGET_PC if (dusk::version::isRegionJpn()) { - sprintf(mSaveDate, "%d.%02d.%02d %02d:%02d", time.year, time.mon + 1, time.mday, + SAFE_SPRINTF(mSaveDate, "%d.%02d.%02d %02d:%02d", time.year, time.mon + 1, time.mday, time.hour, time.min); } else if (dusk::version::isRegionPal() && dComIfGs_getPalLanguage() != dSv_player_config_c::LANGUAGE_ENGLISH) { - sprintf(mSaveDate, "%02d/%02d/%d %02d:%02d", time.mday, time.mon + 1, time.year, time.hour, + SAFE_SPRINTF(mSaveDate, "%02d/%02d/%d %02d:%02d", time.mday, time.mon + 1, time.year, time.hour, time.min); } else { - sprintf(mSaveDate, "%02d/%02d/%d %02d:%02d", time.mon + 1, time.mday, time.year, time.hour, + SAFE_SPRINTF(mSaveDate, "%02d/%02d/%d %02d:%02d", time.mon + 1, time.mday, time.year, time.hour, time.min); } #elif (VERSION == VERSION_GCN_JPN) || (VERSION == VERSION_WII_JPN) @@ -204,11 +207,11 @@ void dFile_info_c::setPlayTime(dSv_save_c* i_savedata) { // 3599940 = 999:59 in seconds if (time >= 3599940) { - sprintf(mPlayTime, "999:59"); + SAFE_SPRINTF(mPlayTime, "999:59"); } else { u32 min = (time % 3600) / 60; u32 hours = time / 3600; - sprintf(mPlayTime, "%d:%02d", hours, min); + SAFE_SPRINTF(mPlayTime, "%d:%02d", hours, min); } } diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index 53f2450ac8..40962abf0a 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -23,6 +23,8 @@ #include "m_Do/m_Do_graphic.h" #include +#include "dusk/string.hpp" + static s32 SelStartFrameTbl[3] = { 59, 99, @@ -3507,7 +3509,7 @@ void dFile_select_c::headerTxtSet(u16 i_msgId, u8 i_type, u8 param_3) { } if (i_msgId == 0xFFFF) { - strcpy(mHeaderStringPtr[dispIdx], ""); + SAFE_STRCPY(mHeaderStringPtr[dispIdx], ""); } else { static f32 fontsize[2] = {21.0f, 27.0f}; #if VERSION == VERSION_GCN_JPN @@ -4182,7 +4184,7 @@ void dFile_select_c::errDispInitSet(char* i_errMesg) { mErrorMsgTxtPane[mErrorTxtDispIdx]->setAlpha(0xFF); mErrorMsgTxtPane[mErrorTxtDispIdx ^ 1]->setAlpha(0); - strcpy(mErrorMsgStringPtr[mErrorTxtDispIdx], i_errMesg); + SAFE_STRCPY_BOUNDED(mErrorMsgStringPtr[mErrorTxtDispIdx], i_errMesg); if (field_0x014a) { errorMoveAnmInitSet(2859, 2849); @@ -4391,7 +4393,7 @@ void dFile_select_c::MemCardLoadWait() { if (mDoMemCd_getDataVersion() != 6) { char errmsg[264]; // "Savedata version is different\n\nVersion %d\n\nFormatting data." - sprintf(errmsg, "セーブデータのバージョンが違います\n\nバージョン %d\n\nデータを初期化します。", mDoMemCd_getDataVersion()); + SAFE_SPRINTF(errmsg, "セーブデータのバージョンが違います\n\nバージョン %d\n\nデータを初期化します。", mDoMemCd_getDataVersion()); errDispInitSet(errmsg); field_0x0280 = false; mWindowCloseMsgDispCb = NULL; @@ -5237,7 +5239,7 @@ void dFile_select_c::MemCardErrYesNoCursorMoveAnm() { void dFile_select_c::errorTxtSet(u16 i_msgId) { if (i_msgId == 0xffff) { - strcpy(mErrorMsgStringPtr[mErrorTxtDispIdx ^ 1], ""); + SAFE_STRCPY(mErrorMsgStringPtr[mErrorTxtDispIdx ^ 1], ""); } else { fileSel.mMessageString->getString( i_msgId, (J2DTextBox*)mErrorMsgTxtPane[mErrorTxtDispIdx ^ 1]->getPanePtr(), NULL, diff --git a/src/d/d_menu_calibration.cpp b/src/d/d_menu_calibration.cpp index 0cc238e5d2..bc256547d7 100644 --- a/src/d/d_menu_calibration.cpp +++ b/src/d/d_menu_calibration.cpp @@ -14,7 +14,10 @@ #include "m_Do/m_Do_controller_pad.h" #include -// Need 0xC bytes of padding with no symbol between dMenu_Calibration_c::__vtable and the end of .data +#include "dusk/string.hpp" + +// Need 0xC bytes of padding with no symbol between dMenu_Calibration_c::__vtable and the end of +// .data // This is likely caused by the vtable of an abstract base class getting put there and then stripped out. // Not sure which abstract base class could go there though, so we simulate it with some dummy classes for now. class dummy_abstract_class { @@ -252,7 +255,7 @@ void dMenu_Calibration_c::setCalibrationValue() { void dMenu_Calibration_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpAButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpAButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -264,7 +267,7 @@ void dMenu_Calibration_c::setAButtonString(u16 i_stringID) { void dMenu_Calibration_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpBButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpBButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -276,7 +279,7 @@ void dMenu_Calibration_c::setBButtonString(u16 i_stringID) { void dMenu_Calibration_c::setStepString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 3; i++) { - strcpy(mpStepString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpStepString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 3; i++) { @@ -288,7 +291,7 @@ void dMenu_Calibration_c::setStepString(u16 i_stringID) { void dMenu_Calibration_c::setExplainString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 3; i++) { - strcpy(mpExplainString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpExplainString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 3; i++) { diff --git a/src/d/d_menu_collect.cpp b/src/d/d_menu_collect.cpp index 8af6dc943a..e9b477d1ea 100644 --- a/src/d/d_menu_collect.cpp +++ b/src/d/d_menu_collect.cpp @@ -2245,18 +2245,18 @@ void dMenu_Collect2D_c::_draw() { if (mItemNameString == 0) { #if REGION_JPN - char* stringPtr1 = static_cast(mpScreen->search(MULTI_CHAR('i_text1')))->getStringPtr(); + TEXT_SPAN stringPtr1 = static_cast(mpScreen->search(MULTI_CHAR('i_text1')))->getStringPtr(); #else - char* stringPtr1 = static_cast(mpScreen->search(MULTI_CHAR('f_text1')))->getStringPtr(); + TEXT_SPAN stringPtr1 = static_cast(mpScreen->search(MULTI_CHAR('f_text1')))->getStringPtr(); #endif - strcpy(stringPtr1, ""); + SAFE_STRCPY(stringPtr1, ""); #if REGION_JPN - char* stringPtr0 = static_cast(mpScreen->search(MULTI_CHAR('i_text0')))->getStringPtr(); + TEXT_SPAN stringPtr0 = static_cast(mpScreen->search(MULTI_CHAR('i_text0')))->getStringPtr(); #else - char* stringPtr0 = static_cast(mpScreen->search(MULTI_CHAR('f_text0')))->getStringPtr(); + TEXT_SPAN stringPtr0 = static_cast(mpScreen->search(MULTI_CHAR('f_text0')))->getStringPtr(); #endif - strcpy(stringPtr0, ""); + SAFE_STRCPY(stringPtr0, ""); } else { #if REGION_JPN J2DTextBox* textBox1 = static_cast(mpScreen->search(MULTI_CHAR('i_text1'))); @@ -2307,13 +2307,13 @@ void dMenu_Collect2D_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreenIcon->search(text_a_tag[i]))->getStringPtr(); - strcpy(stringPtr, ""); + SAFE_STRCPY(stringPtr, ""); } } else { for (int i = 0; i < 5; i++) { - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreenIcon->search(text_a_tag[i]))->getStringPtr(); dMeter2Info_getStringKanji(i_stringID, stringPtr, NULL); } @@ -2331,13 +2331,13 @@ void dMenu_Collect2D_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreenIcon->search(text_b_tag[i]))->getStringPtr(); - strcpy(stringPtr, ""); + SAFE_STRCPY(stringPtr, ""); } } else { for (int i = 0; i < 5; i++) { - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreenIcon->search(text_b_tag[i]))->getStringPtr(); dMeter2Info_getStringKanji(i_stringID, stringPtr, NULL); } @@ -2356,7 +2356,7 @@ void dMenu_Collect2D_c::setItemNameString(u8 param_0, u8 param_1) { setItemNameStringNull(); } else { #if REGION_JPN - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreen->search(MULTI_CHAR('item_n00')))->getStringPtr(); dMeter2Info_getStringKanji(uVar6, stringPtr, NULL); stringPtr = static_cast(mpScreen->search(MULTI_CHAR('item_n01')))->getStringPtr(); @@ -2366,7 +2366,7 @@ void dMenu_Collect2D_c::setItemNameString(u8 param_0, u8 param_1) { stringPtr = static_cast(mpScreen->search(MULTI_CHAR('item_n03')))->getStringPtr(); dMeter2Info_getStringKanji(uVar6, stringPtr, NULL); #else - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreen->search(MULTI_CHAR('item_n04')))->getStringPtr(); dMeter2Info_getStringKanji(uVar6, stringPtr, NULL); stringPtr = static_cast(mpScreen->search(MULTI_CHAR('item_n05')))->getStringPtr(); @@ -2384,22 +2384,22 @@ void dMenu_Collect2D_c::setItemNameStringNull() { mItemNameString = 0; #if REGION_JPN J2DTextBox* textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n00')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n01')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n02')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n03')); #else J2DTextBox* textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n04')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n05')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n06')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n07')); #endif - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); } dMenu_Collect3D_c::dMenu_Collect3D_c(JKRExpHeap* param_0, dMenu_Collect2D_c* param_1, diff --git a/src/d/d_menu_dmap.cpp b/src/d/d_menu_dmap.cpp index cd6b94ed4f..609d8ef6be 100644 --- a/src/d/d_menu_dmap.cpp +++ b/src/d/d_menu_dmap.cpp @@ -26,6 +26,8 @@ #include "m_Do/m_Do_graphic.h" #include +#include "dusk/string.hpp" + #if (PLATFORM_WII || PLATFORM_SHIELD) #define POINTER_OPT dComIfGs_getOptPointer() #else @@ -116,9 +118,9 @@ dMenu_DmapBg_c::dMenu_DmapBg_c(JKRExpHeap* i_heap, STControl* i_stick) { mapScreenInit(); char archive_path[32]; - strcpy(archive_path, "/res/FieldMap/D_MN10.arc"); + SAFE_STRCPY(archive_path, "/res/FieldMap/D_MN10.arc"); char stage_name[8]; - strcpy(stage_name, dComIfGp_getStartStageName()); + SAFE_STRCPY(stage_name, dComIfGp_getStartStageName()); archive_path[18] = stage_name[4]; archive_path[19] = stage_name[5]; @@ -392,7 +394,7 @@ void dMenu_DmapBg_c::setAButtonString(u32 i_msgNo) { }; for (int i = 0; i < 5; i++) { if (i_msgNo == 0) { - strcpy(((J2DTextBox*)mButtonScreen->search(cont_at[i]))->getStringPtr(), ""); + SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(cont_at[i]))->getStringPtr(), ""); } else { dMeter2Info_getStringKanji(i_msgNo, ((J2DTextBox*)mButtonScreen->search(cont_at[i]))->getStringPtr(), NULL); } @@ -409,7 +411,7 @@ void dMenu_DmapBg_c::setBButtonString(u32 i_msgNo) { }; for (int i = 0; i < 5; i++) { if (i_msgNo == 0) { - strcpy(((J2DTextBox*)mButtonScreen->search(cont_bt[i]))->getStringPtr(), ""); + SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(cont_bt[i]))->getStringPtr(), ""); } else { dMeter2Info_getStringKanji(i_msgNo, ((J2DTextBox*)mButtonScreen->search(cont_bt[i]))->getStringPtr(), NULL); } @@ -441,7 +443,7 @@ void dMenu_DmapBg_c::setCButtonString(u32 i_msgNo) { if (msgNo == 0) { for (i = 0; i < 2; i++) { - strcpy(((J2DTextBox*)mButtonScreen->search(c_tag[i]))->getStringPtr(), ""); + SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(c_tag[i]))->getStringPtr(), ""); } mpCButton->setAlphaRate(0.5f); } else { diff --git a/src/d/d_menu_fishing.cpp b/src/d/d_menu_fishing.cpp index 5e1b124b38..6a3d0aefe8 100644 --- a/src/d/d_menu_fishing.cpp +++ b/src/d/d_menu_fishing.cpp @@ -16,6 +16,7 @@ #include "m_Do/m_Do_graphic.h" #include +#include "dusk/string.hpp" #include "dusk/version.hpp" typedef void (dMenu_Fishing_c::*initFunc)(); @@ -372,7 +373,7 @@ void dMenu_Fishing_c::screenSetDoIcon() { void dMenu_Fishing_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpAButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpAButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -384,7 +385,7 @@ void dMenu_Fishing_c::setAButtonString(u16 i_stringID) { void dMenu_Fishing_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpBButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpBButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -419,29 +420,29 @@ void dMenu_Fishing_c::setFishParam(int i_fishIdx, u16 i_fishCount, u8 i_fishSize dComIfGp_setMessageCountNumber(i_fishSize); mpString->getString(0x597, field_0x10c[i][i_fishIdx], NULL, NULL, NULL, 0); // "inches" char* stringPtr = field_0x10c[i][i_fishIdx]->getStringPtr(); - strcpy(strBuff1, stringPtr); + SAFE_STRCPY(strBuff1, stringPtr); int j; for (j = 0; strBuff1[j + fishSizeFigure] != 0; j++) { strBuff2[j] = strBuff1[j + fishSizeFigure]; } strBuff2[j] = 0; strBuff1[fishSizeFigure] = 0; - strcpy(field_0x10c[i][i_fishIdx]->getStringPtr(), strBuff1); - strcpy(field_0x16c[i][i_fishIdx]->getStringPtr(), strBuff2); + SAFE_STRCPY(field_0x10c[i][i_fishIdx]->getStringPtr(), strBuff1); + SAFE_STRCPY(field_0x16c[i][i_fishIdx]->getStringPtr(), strBuff2); // part two, i_fishCount dComIfGp_setMessageCountNumber(i_fishCount); mpString->getString(0x598, field_0x13c[i][i_fishIdx], NULL, NULL, NULL, 0); // "fish" stringPtr = field_0x13c[i][i_fishIdx]->getStringPtr(); - strcpy(strBuff1, stringPtr); + SAFE_STRCPY(strBuff1, stringPtr); int k; for (k = 0; strBuff1[k + fishCountFigure] != 0; k++) { strBuff2[k] = strBuff1[k + fishCountFigure]; } strBuff2[k] = 0; strBuff1[fishCountFigure] = 0; - strcpy(field_0x13c[i][i_fishIdx]->getStringPtr(), strBuff1); - strcpy(field_0x19c[i][i_fishIdx]->getStringPtr(), strBuff2); + SAFE_STRCPY(field_0x13c[i][i_fishIdx]->getStringPtr(), strBuff1); + SAFE_STRCPY(field_0x19c[i][i_fishIdx]->getStringPtr(), strBuff2); } } diff --git a/src/d/d_menu_fmap.cpp b/src/d/d_menu_fmap.cpp index eef18ba91c..b8a69ddc55 100644 --- a/src/d/d_menu_fmap.cpp +++ b/src/d/d_menu_fmap.cpp @@ -22,6 +22,7 @@ #include "d/d_msg_scrn_explain.h" #include "d/d_stage.h" #include "dusk/memory.h" +#include "dusk/string.hpp" #include "f_op/f_op_msg_mng.h" static dMf_HIO_c g_fmHIO; @@ -126,7 +127,7 @@ const char* dMenuFmap_getStartStageName(void* i_fieldData) { if (!strcmp(dComIfGp_getStartStageName(), data[i].mStageName)) { // !@bug: probably supposed to be data[i].mVirtualStageName, but doesn't matter // because the two entries have the same virtual stage name - strcpy(virtual_stage, data->mVirtualStageName); + SAFE_STRCPY(virtual_stage, data->mVirtualStageName); return virtual_stage; } } @@ -197,7 +198,7 @@ dMenu_Fmap_c::dMenu_Fmap_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i mIsWarpMap = false; mProcess = PROC_REGION_MAP; - strcpy(mMarkedStageName, ""); + SAFE_STRCPY(mMarkedStageName, ""); if (g_fmapHIO.mpArcData != NULL && g_fmapHIO.mpArcData->isMounted()) { mProcess = i_process; mpMapArchive = g_fmapHIO.mpArcData; @@ -1863,7 +1864,7 @@ bool dMenu_Fmap_c::isRoomCheck(int i_stageNo, int i_roomNo) { for (; i < mDataNumMax; i++) { if (checked_data[i]) continue; - strcpy(stage_name, stages[i].mName); + SAFE_STRCPY(stage_name, stages[i].mName); for (int k = 0; k < 64; k++) { local_e0[k] = false; @@ -2082,14 +2083,14 @@ bool dMenu_Fmap_c::readAreaData(u8 i_regionNo, bool i_isSelectedRegion) { for (; i < mDataNumMax; i++) { if (checked_data[i]) continue; - strcpy(tmp_stage_name, mTmpStageName); + SAFE_STRCPY(tmp_stage_name, mTmpStageName); resetRoomDataBit(); if (i_isSelectedRegion) { - strcpy(mTmpStageName, stages[i].mName); + SAFE_STRCPY(mTmpStageName, stages[i].mName); } else { - strcpy(mTmpStageName, tmp_stage_name); + SAFE_STRCPY(mTmpStageName, tmp_stage_name); } - strcpy(tmp_stage_name, stages[i].mName); + SAFE_STRCPY(tmp_stage_name, stages[i].mName); for (int k = 0; k < 64; k++) { local_e0[k] = false; @@ -2165,7 +2166,7 @@ bool dMenu_Fmap_c::readAreaData(u8 i_regionNo, bool i_isSelectedRegion) { mpDraw2DBack->setStageOriginXZ(mSpotNum, stages[stage_index].mOffsetX, stages[stage_index].mOffsetZ); mAreaName[mSpotNum] = stages[stage_index].mAreaName; - strcpy(mStageName[mSpotNum], stage_name); + SAFE_STRCPY(mStageName[mSpotNum], stage_name); mSpotNum++; } @@ -2213,7 +2214,7 @@ bool dMenu_Fmap_c::readRoomData(char const* i_stageName, dMenu_Fmap_stage_data_c dMenu_Fmap_stage_arc_data_c* room_data = NULL; char stage_path[20]; - sprintf(stage_path, "%s/stage.dat", i_stageName); + SAFE_SPRINTF(stage_path, "%s/stage.dat", i_stageName); if (readFieldMapData((void**)&room_data, stage_path, false, false)) { ((dMenuMapCommon_c::RoomData_c*)o_roomData)->setRoomData(room_data); } @@ -2227,7 +2228,7 @@ bool dMenu_Fmap_c::readRoomData(char const* i_stageName, dMenu_Fmap_stage_data_c void* dzs_data = NULL; char room_path[20]; - sprintf(room_path, "%s/room%d.dzs", i_stageName, room_nos[i]); + SAFE_SPRINTF(room_path, "%s/room%d.dzs", i_stageName, room_nos[i]); if (readRoomDzsData(&dzs_data, 0x1500, room_path)) { dMenu_Fmap_data_c* map_data = JKR_NEW dMenu_Fmap_data_c(); @@ -2318,9 +2319,9 @@ void dMenu_Fmap_c::decodeFieldMapData() { bool local_3f = false; if (j == 7) { local_3f = true; - sprintf(tex_path, "tex/region8.bti"); + SAFE_SPRINTF(tex_path, "tex/region8.bti"); } else { - sprintf(tex_path, "tex/region%d.bti", regions[i].mTextureReadNum); + SAFE_SPRINTF(tex_path, "tex/region%d.bti", regions[i].mTextureReadNum); } if (readFieldMapData((void**)&mRegionTexture[j], tex_path, true, local_3f)) { @@ -2438,7 +2439,7 @@ bool dMenu_Fmap_c::removeAreaData() { } for (int i = 0; i < 20; i++) { - strcpy(mStageName[i], ""); + SAFE_STRCPY(mStageName[i], ""); } field_0x305 = false; @@ -2549,11 +2550,11 @@ void dMenu_Fmap_c::drawIcon(f32 param_0, bool param_1) { fopAc_ac_c* player = daPy_getPlayerActorClass(); pos.set(dMapInfo_n::getMapPlayerPos()); angle = player->shape_angle.y; - strcpy(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); + SAFE_STRCPY(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); } else { pos = dComIfGs_getPlayerFieldLastStayPos(); angle = dComIfGs_getPlayerFieldLastStayAngleY(); - strcpy(stage_name, dComIfGs_getPlayerFieldLastStayName()); + SAFE_STRCPY(stage_name, dComIfGs_getPlayerFieldLastStayName()); } u8 is_portal_demo1 = 0; @@ -2642,11 +2643,11 @@ void dMenu_Fmap_c::drawPlayEnterIcon() { if (dComIfGs_isPlayerFieldLastStayFieldDataExistFlag()) { pos.set(dMapInfo_n::getMapRestartPos()); angle = dComIfGs_getRestartRoomAngleY(); - strcpy(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); + SAFE_STRCPY(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); } else { pos = dComIfGs_getPlayerFieldLastStayPos(); angle = dComIfGs_getPlayerFieldLastStayAngleY(); - strcpy(stage_name, dComIfGs_getPlayerFieldLastStayName()); + SAFE_STRCPY(stage_name, dComIfGs_getPlayerFieldLastStayName()); } mpDraw2DBack->setIcon2DPos(0x15, stage_name, pos.x, pos.z, cM_sht2d(angle), 0, false); } @@ -2788,10 +2789,10 @@ void dMenu_Fmap_c::arrowPosInit() { char stage_name[8]; if (dComIfGs_isPlayerFieldLastStayFieldDataExistFlag()) { pos.set(dMapInfo_n::getMapPlayerPos()); - strcpy(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); + SAFE_STRCPY(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); } else { pos = dComIfGs_getPlayerFieldLastStayPos(); - strcpy(stage_name, dComIfGs_getPlayerFieldLastStayName()); + SAFE_STRCPY(stage_name, dComIfGs_getPlayerFieldLastStayName()); } f32 fVar1 = 0.0f; @@ -2844,7 +2845,7 @@ void dMenu_Fmap_c::tableArrowPosInit(bool param_0) { } static char* stage_name[4] = {"F_SP115", "F_SP113", "F_SP109", "F_SP108"}; - strcpy(mMarkedStageName, stage_name[iVar5]); + SAFE_STRCPY(mMarkedStageName, stage_name[iVar5]); if (param_0) { f32 pos1_x, pos2_x, pos1_z, pos2_z, icon_x, icon_z; @@ -2873,7 +2874,7 @@ void dMenu_Fmap_c::yamibossArrowPosInit() { void dMenu_Fmap_c::howlArrowPosInit() { u8 type = dMeter2Info_getGoldWolfMapType(); static char* stage_name[6] = {"F_SP104", "F_SP122", "F_SP122", "F_SP124", "F_SP111", "F_SP116"}; - strcpy(mMarkedStageName, stage_name[type - 2]); + SAFE_STRCPY(mMarkedStageName, stage_name[type - 2]); static const int i_swBit[6] = {0x41, 0x29, 0x2a, 0x32, 0x79, 0x32}; f32 icon_x, icon_z; if (searchIcon(6, i_swBit[type - 2], &icon_x, &icon_z)) { @@ -2910,10 +2911,10 @@ cXyz* dMenu_Fmap_c::getPlayerPos2D() { char stage_name[8]; if (dComIfGs_isPlayerFieldLastStayFieldDataExistFlag()) { pos.set(dMapInfo_n::getMapPlayerPos()); - strcpy(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); + SAFE_STRCPY(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); } else { pos = dComIfGs_getPlayerFieldLastStayPos(); - strcpy(stage_name, dComIfGs_getPlayerFieldLastStayName()); + SAFE_STRCPY(stage_name, dComIfGs_getPlayerFieldLastStayName()); } mpDraw2DBack->calcAllMapPos2D(stage_name, pos.x - mpDraw2DBack->getStageTransX(), pos.z - mpDraw2DBack->getStageTransZ(), diff --git a/src/d/d_menu_fmap2D.cpp b/src/d/d_menu_fmap2D.cpp index 100fd1c067..0d3114172e 100644 --- a/src/d/d_menu_fmap2D.cpp +++ b/src/d/d_menu_fmap2D.cpp @@ -2730,7 +2730,7 @@ void dMenu_Fmap2DTop_c::setTitleNameString(u32 param_0) { #endif for (int i = 0; i < 7; i++) { if (param_0 == 0) { - strcpy(((J2DTextBox*)(mpTitleScreen->search(setTitleNameString_font_name[i]))) + SAFE_STRCPY(((J2DTextBox*)(mpTitleScreen->search(setTitleNameString_font_name[i]))) ->getStringPtr(), ""); } else { @@ -2753,7 +2753,7 @@ void dMenu_Fmap2DTop_c::setAreaNameString(u32 param_0) { #endif for (int i = 0; i < 3; i++) { if (param_0 == 0) { - strcpy(((J2DTextBox*)(mpTitleScreen->search(setAreaNameString_area_name[i]))) + SAFE_STRCPY(((J2DTextBox*)(mpTitleScreen->search(setAreaNameString_area_name[i]))) ->getStringPtr(), ""); } else { @@ -2866,7 +2866,7 @@ void dMenu_Fmap2DTop_c::setCrossLRString(u32 param_0) { if (param_0 == 0) { for (int i = 0; i < 5; i++) { J2DTextBox* text_box = static_cast(mpTitleScreen->search(juji_c[i])); - strcpy(text_box->getStringPtr(), ""); + SAFE_STRCPY(text_box->getStringPtr(), ""); } mpTitleScreen->search(MULTI_CHAR('juy_sha0'))->show(); mAlphaDpad = 1; @@ -2891,7 +2891,7 @@ void dMenu_Fmap2DTop_c::set3DStickString(u32 param_0) { if (param_0 == 0) { for (int i = 0; i < 5; i++) { J2DTextBox* text_box = static_cast(mpTitleScreen->search(ast_c[i])); - strcpy(text_box->getStringPtr(), ""); + SAFE_STRCPY(text_box->getStringPtr(), ""); } mpTitleScreen->search(MULTI_CHAR('as_sha0'))->show(); mAlphaAnalogStick = 1; diff --git a/src/d/d_menu_insect.cpp b/src/d/d_menu_insect.cpp index 88e05226e8..8e6ad49b5b 100644 --- a/src/d/d_menu_insect.cpp +++ b/src/d/d_menu_insect.cpp @@ -352,12 +352,12 @@ void dMenu_Insect_c::explain_open_init() { "\x1B" "CR[%d]", (int)(0.5f * (length - stringLength1))); - strcat(local_b8, local_78); + SAFE_STRCAT(local_b8, local_78); snprintf(cStack_d8, 32, "\x1B" "CR[%d]", (int)(0.5f * (length - stringLength2))); - strcat(cStack_d8, local_98); + SAFE_STRCAT(cStack_d8, local_98); mpSelect_c->setString("", local_b8, cStack_d8); mpSelect_c->setRubyString("", "", ""); mpSelect_c->selAnimeInit(2, field_0xf7 + 1, 0, length, 0); @@ -730,7 +730,7 @@ u8 dMenu_Insect_c::dpdMove() { void dMenu_Insect_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpAButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpAButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -742,7 +742,7 @@ void dMenu_Insect_c::setAButtonString(u16 i_stringID) { void dMenu_Insect_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpBButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpBButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { diff --git a/src/d/d_menu_item_explain.cpp b/src/d/d_menu_item_explain.cpp index dd3af54571..bebcb2cdb0 100644 --- a/src/d/d_menu_item_explain.cpp +++ b/src/d/d_menu_item_explain.cpp @@ -508,12 +508,12 @@ void dMenu_ItemExplain_c::move_select_init() { "\x1B" "CR[%d]", (int)(0.5f * (length - stringLength1))); - strcat(local_64, local_88); + SAFE_STRCAT(local_64, local_88); snprintf(cStack78, 20, "\x1B" "CR[%d]", (int)(0.5f * (length - stringLength2))); - strcat(cStack78, local_80); + SAFE_STRCAT(cStack78, local_80); mpSelect_c->setString("", local_64, cStack78); mpSelect_c->setRubyString("", "", ""); mpSelect_c->selAnimeInit(2, field_0xe2 + 1, 0, length, 0); diff --git a/src/d/d_menu_letter.cpp b/src/d/d_menu_letter.cpp index 4bfda4b9cd..cd7da6a6f4 100644 --- a/src/d/d_menu_letter.cpp +++ b/src/d/d_menu_letter.cpp @@ -501,10 +501,10 @@ void dMenu_Letter_c::read_open_init() { field_0x3e2 = mpString->getPageMax(D_MENU_LETTER_LINE_MAX); if (field_0x3e2 > 1) { char acStack_30[20]; - sprintf(acStack_30, "%d/%d", field_0x3e3, field_0x3e2); + SAFE_SPRINTF(acStack_30, "%d/%d", field_0x3e3, field_0x3e2); for (int i = 0; i < 2; i++) { field_0x1e4[i]->show(); - strcpy(field_0x1e4[i]->getStringPtr(), acStack_30); + SAFE_STRCPY(field_0x1e4[i]->getStringPtr(), acStack_30); } } else { for (int i = 0; i < 2; i++) { @@ -654,10 +654,10 @@ void dMenu_Letter_c::read_next_fadein_init() { D_MENU_LETTER_LINE_MAX, text2, text1, NULL, NULL, 0); } char acStack_30[10]; - sprintf(acStack_30, "%d/%d", field_0x3e3, field_0x3e2); + SAFE_SPRINTF(acStack_30, "%d/%d", field_0x3e3, field_0x3e2); for (int i = 0; i < 2; i++) { field_0x1e4[i]->show(); - strcpy(field_0x1e4[i]->getStringPtr(), acStack_30); + SAFE_STRCPY(field_0x1e4[i]->getStringPtr(), acStack_30); } } @@ -1273,7 +1273,7 @@ void dMenu_Letter_c::copyDMYMenu() { void dMenu_Letter_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpAButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpAButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -1286,7 +1286,7 @@ void dMenu_Letter_c::setAButtonString(u16 i_stringID) { void dMenu_Letter_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpBButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpBButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { diff --git a/src/d/d_menu_option.cpp b/src/d/d_menu_option.cpp index af322a1c01..62f30d2d99 100644 --- a/src/d/d_menu_option.cpp +++ b/src/d/d_menu_option.cpp @@ -2107,7 +2107,7 @@ void dMenu_Option_c::setAButtonString(u16 i_stringID) { if (stringId == 0) { for (int i = 0; i < 5; i++) { J2DTextBox* textBox = (J2DTextBox*)mpScreenIcon->search(text_a_tag[i]); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -2127,7 +2127,7 @@ void dMenu_Option_c::setBButtonString(u16 i_stringID) { if (stringId == 0) { for (int i = 0; i < 5; i++) { J2DTextBox* textBox = (J2DTextBox*)mpScreenIcon->search(text_b_tag[i]); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { diff --git a/src/d/d_menu_ring.cpp b/src/d/d_menu_ring.cpp index ba2b86b760..5cdb8fbc04 100644 --- a/src/d/d_menu_ring.cpp +++ b/src/d/d_menu_ring.cpp @@ -1203,7 +1203,7 @@ void dMenu_Ring_c::setNameString(u32 i_stringID) { if (mNameStringID != i_stringID) { for (int i = 0; i < 4; i++) { if (i_stringID == 0) { - strcpy(textBox[i]->getStringPtr(), ""); + SAFE_STRCPY(textBox[i]->getStringPtr(), ""); } else { mpString->getString(i_stringID, textBox[i], NULL, NULL, NULL, 0); } diff --git a/src/d/d_menu_save.cpp b/src/d/d_menu_save.cpp index 23beb29b0b..d73e4ff5e8 100644 --- a/src/d/d_menu_save.cpp +++ b/src/d/d_menu_save.cpp @@ -816,8 +816,8 @@ void dMenu_save_c::saveQuestion() { field_0x9c = 0; field_0x17a = 0; - strcpy(mpHeaderTxt[mHeaderTxtType], ""); - strcpy(mpHeaderTxt[mHeaderTxtType ^ 1], ""); + SAFE_STRCPY(mpHeaderTxt[mHeaderTxtType], ""); + SAFE_STRCPY(mpHeaderTxt[mHeaderTxtType ^ 1], ""); field_0x64 = 0; field_0x50 = 1; field_0x40->setFrame(field_0x50); @@ -2033,7 +2033,7 @@ void dMenu_save_c::saveYesNoCancelMove() { void dMenu_save_c::headerTxtSet(u16 msgID) { if (msgID == 0xFFFF) { - strcpy(mpHeaderTxt[mHeaderTxtType ^ 1], ""); + SAFE_STRCPY(mpHeaderTxt[mHeaderTxtType ^ 1], ""); } else { mSaveSel.mMsgString->getString( msgID, (J2DTextBox*)mpHeaderTxtPane[mHeaderTxtType ^ 1]->getPanePtr(), NULL, @@ -2230,7 +2230,7 @@ void dMenu_save_c::errYesNoCursorMoveAnm() { void dMenu_save_c::errorTxtSet(u16 msgID) { if (msgID == 0xFFFF) { - strcpy(mpErrTxt[mErrTxtType ^ 1], ""); + SAFE_STRCPY(mpErrTxt[mErrTxtType ^ 1], ""); } else { J2DTextBox* tbox = (J2DTextBox*)mpErrTxtPane[mErrTxtType ^ 1]->getPanePtr(); mSaveSel.mMsgString->getString(msgID, tbox, NULL, mSaveSel.font[0], NULL, 0); diff --git a/src/d/d_menu_skill.cpp b/src/d/d_menu_skill.cpp index 78ab82af94..2692d33c3f 100644 --- a/src/d/d_menu_skill.cpp +++ b/src/d/d_menu_skill.cpp @@ -610,7 +610,7 @@ void dMenu_Skill_c::setPageText() { void dMenu_Skill_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpAButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpAButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -622,7 +622,7 @@ void dMenu_Skill_c::setAButtonString(u16 i_stringID) { void dMenu_Skill_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpBButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpBButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -634,7 +634,7 @@ void dMenu_Skill_c::setBButtonString(u16 i_stringID) { void dMenu_Skill_c::setNameString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 4; i++) { - strcpy(mpNameString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpNameString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 4; i++) { diff --git a/src/d/d_meter2_draw.cpp b/src/d/d_meter2_draw.cpp index 8e824f583b..3c342ea7d4 100644 --- a/src/d/d_meter2_draw.cpp +++ b/src/d/d_meter2_draw.cpp @@ -2245,11 +2245,11 @@ void dMeter2Draw_c::drawButtonA(u8 i_action, f32 i_posX, f32 i_posY, f32 i_textP mp_string = getActionString(0x15, 1, NULL); for (int i = 0; i < 5; i++) { - strcpy(static_cast(mpAText[i]->getPanePtr())->getStringPtr(), mp_string); + SAFE_STRCPY(static_cast(mpAText[i]->getPanePtr())->getStringPtr(), mp_string); } } else { for (int i = 0; i < 5; i++) { - strcpy(static_cast(mpAText[i]->getPanePtr())->getStringPtr(), mp_string); + SAFE_STRCPY(static_cast(mpAText[i]->getPanePtr())->getStringPtr(), mp_string); } } @@ -2308,7 +2308,7 @@ void dMeter2Draw_c::drawButtonB(u8 i_action, bool param_1, f32 i_posX, f32 i_pos JUT_ASSERT(0, strlen(mp_string) < (64)); for (int i = 0; i < 5; i++) { - strcpy(static_cast(mpBText[i]->getPanePtr())->getStringPtr(), mp_string); + SAFE_STRCPY(static_cast(mpBText[i]->getPanePtr())->getStringPtr(), mp_string); } if (i_action == 0x26 || i_action == 0x2E) { @@ -2391,7 +2391,7 @@ void dMeter2Draw_c::drawButtonZ(u8 i_action) { JUT_ASSERT(0, strlen(mp_string) < (64)); for (int i = 0; i < 5; i++) { - strcpy(static_cast(mpXYText[i][2]->getPanePtr())->getStringPtr(), mp_string); + SAFE_STRCPY(static_cast(mpXYText[i][2]->getPanePtr())->getStringPtr(), mp_string); } mpButtonXY[2]->scale(g_drawHIO.mButtonZScale, g_drawHIO.mButtonZScale); @@ -2490,7 +2490,7 @@ void dMeter2Draw_c::drawButtonXY(int i_no, u8 i_itemNo, u8 i_action, bool param_ JUT_ASSERT(0, strlen(mp_string) < (64)); for (int i = 0; i < 5; i++) { - strcpy(static_cast(mpXYText[i][i_no]->getPanePtr())->getStringPtr(), + SAFE_STRCPY(static_cast(mpXYText[i][i_no]->getPanePtr())->getStringPtr(), mp_string); } @@ -3243,7 +3243,7 @@ char* dMeter2Draw_c::getActionString(u8 i_action, u8 i_type, u8* param_2) { }; static char i_text_buf[32]; - strcpy(i_text_buf, ""); + SAFE_STRCPY(i_text_buf, ""); if (param_2 != NULL) { *param_2 = 1; diff --git a/src/d/d_meter2_info.cpp b/src/d/d_meter2_info.cpp index bb533e6370..63e22056f5 100644 --- a/src/d/d_meter2_info.cpp +++ b/src/d/d_meter2_info.cpp @@ -15,6 +15,8 @@ #include +#include "dusk/string.hpp" + enum ITEMICON_RES_FILE_ID { ITEMICON_BTI_ARI_MESU_00=0x3, ITEMICON_BTI_ARI_OSU_00=0x4, @@ -350,8 +352,8 @@ void dMeter2Info_c::decMsgKeyWaitTimer() { } } -void dMeter2Info_c::getString(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { - strcpy(o_string, ""); +void dMeter2Info_c::getString(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { + SAFE_STRCPY(o_string, ""); u8* msgRes; if (mMsgResource == NULL) { @@ -372,7 +374,7 @@ void dMeter2Info_c::getString(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_ // check if i_stringID equals the message entry "Message ID" if (i_stringID == bmg_inf->entries[i].message_id) { string_ptr = (char*)(string_data + bmg_inf->entries[i].string_offset); // use entry "String Offset" to get string pointer - strcpy(o_string, string_ptr); + SAFE_STRCPY(o_string, string_ptr); if (i_msgEntry != NULL) { memcpy(i_msgEntry, &bmg_inf->entries[i], sizeof(JMSMesgEntry_c)); @@ -387,8 +389,8 @@ void dMeter2Info_c::getString(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_ } } -void dMeter2Info_c::getStringKana(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { - strcpy(o_string, ""); +void dMeter2Info_c::getStringKana(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { + SAFE_STRCPY(o_string, ""); u8* msgRes; if (mMsgResource == NULL) { @@ -456,8 +458,8 @@ void dMeter2Info_c::getStringKana(u32 i_stringID, char* o_string, JMSMesgEntry_c } } -void dMeter2Info_c::getStringKanji(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { - strcpy(o_string, ""); +void dMeter2Info_c::getStringKanji(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { + SAFE_STRCPY(o_string, ""); u8* msgRes; if (mMsgResource == NULL) { @@ -668,7 +670,7 @@ void dMeter2Info_c::resetMeterString() { void dMeter2Info_c::setWarpInfo(const char* i_stageName, const cXyz& i_position, s16 i_angle, u8 i_roomNo, u8 param_4, u8 i_warpPlayerNo) { - strcpy(mWarpInfo.mStageName, i_stageName); + SAFE_STRCPY(mWarpInfo.mStageName, i_stageName); mWarpInfo.mPosition = i_position; mWarpInfo.mAngle = (s16)i_angle; mWarpInfo.mRoomNo = (u8)i_roomNo; @@ -1592,7 +1594,7 @@ void dMeter2Info_c::setMiniGameCount(s8 i_count) { } void dMeter2Info_c::setSaveStageName(const char* i_stageName) { - strcpy(mSaveStageName, i_stageName); + SAFE_STRCPY(mSaveStageName, i_stageName); } s16 dMeter2Info_getNowLifeGauge() { diff --git a/src/d/d_meter_button.cpp b/src/d/d_meter_button.cpp index f89a483c39..1f47969f47 100644 --- a/src/d/d_meter_button.cpp +++ b/src/d/d_meter_button.cpp @@ -22,6 +22,8 @@ #include "dusk/string.hpp" #endif +#include "dusk/string.hpp" + #if VERSION == VERSION_GCN_JPN #define STR_BUF_LEN 528 #else @@ -257,7 +259,7 @@ void dMeterButton_c::draw() { if (mMsgID != 0xFFFF) { char tmp_buf[STR_BUF_LEN]; - strcpy(tmp_buf, static_cast(mpTm_c[0]->getPanePtr())->getStringPtr()); + SAFE_STRCPY(tmp_buf, static_cast(mpTm_c[0]->getPanePtr())->getStringPtr()); mpTextScreen->draw(0.0f, 0.0f, graf_ctx); #if VERSION == VERSION_GCN_JPN @@ -268,7 +270,7 @@ void dMeterButton_c::draw() { NULL, 8); #endif mpString_c->drawOutFont(static_cast(mpTm_c[0]->getPanePtr()), -1.0f); - strcpy(static_cast(mpTm_c[0]->getPanePtr())->getStringPtr(), tmp_buf); + SAFE_STRCPY(static_cast(mpTm_c[0]->getPanePtr())->getStringPtr(), tmp_buf); } for (int i = 0; i < 2; i++) { @@ -1210,7 +1212,7 @@ void dMeterButton_c::screenInitButton() { field_0x360 = mpTextBox[0]->getBounds().i.x; for (int i = 0; i < 2; i++) { - strcpy(mButtonText[i], ""); + SAFE_STRCPY(mButtonText[i], ""); field_0x2e8[i] = 0.0f; field_0x4be[i] = BUTTON_NONE_e; field_0x2f4[i] = 0.0f; @@ -1906,12 +1908,12 @@ void dMeterButton_c::updateText(u32 i_flags) { if (getString) { mMsgID = dMeter2Info_getFloatingMessageID(); - strcpy(static_cast(mpTm_c[0]->getPanePtr())->getStringPtr(), buf1); - strcpy(static_cast(mpTm_c[1]->getPanePtr())->getStringPtr(), buf3); + SAFE_STRCPY(static_cast(mpTm_c[0]->getPanePtr())->getStringPtr(), buf1); + SAFE_STRCPY(static_cast(mpTm_c[1]->getPanePtr())->getStringPtr(), buf3); for (int i = 0; i < 2; i++) { if (field_0x0ec[i] != NULL) { - strcpy( + SAFE_STRCPY( static_cast(field_0x0ec[i]->getPanePtr())->getStringPtr(), buf2); } @@ -3023,7 +3025,7 @@ void dMeterButton_c::setString(char* i_string, u8 i_button, u8 param_2, u8 param } } - strcpy(mButtonText[param_2], i_string); + SAFE_STRCPY(mButtonText[param_2], i_string); if (param_2 == 0) { if (param_3 != 0) { @@ -3033,7 +3035,7 @@ void dMeterButton_c::setString(char* i_string, u8 i_button, u8 param_2, u8 param } for (int i = 0; i < 5; i++) { - strcpy(mpTextBox[i]->getStringPtr(), i_string); + SAFE_STRCPY(mpTextBox[i]->getStringPtr(), i_string); } return; } @@ -3045,7 +3047,7 @@ void dMeterButton_c::setString(char* i_string, u8 i_button, u8 param_2, u8 param } for (int i = 0; i < 5; i++) { - strcpy(mpTextBox[5 + i]->getStringPtr(), i_string); + SAFE_STRCPY(mpTextBox[5 + i]->getStringPtr(), i_string); } } @@ -3300,7 +3302,7 @@ void dMeterButton_c::hide_button(u8 i_button) { field_0x4b8[1] = 0; field_0x4bc[1] = 0; - strcpy(mButtonText[1], ""); + SAFE_STRCPY(mButtonText[1], ""); mpText[0]->alphaAnimeStart(mpText[1]->getAlphaTimer()); mpText[0]->setAlphaRate(mpText[1]->getAlphaRate()); mpText[1]->alphaAnimeStart(0); @@ -3310,14 +3312,14 @@ void dMeterButton_c::hide_button(u8 i_button) { field_0x4be[0] = BUTTON_NONE_e; field_0x4b8[0] = 0; field_0x4bc[0] = 0; - strcpy(mButtonText[0], ""); + SAFE_STRCPY(mButtonText[0], ""); } } else if (field_0x4be[1] == i_button) { field_0x4be[1] = BUTTON_NONE_e; field_0x4b8[1] = 0; field_0x4bc[1] = 0; field_0x2f4[0] = 0.0f; - strcpy(mButtonText[1], ""); + SAFE_STRCPY(mButtonText[1], ""); } } diff --git a/src/d/d_meter_string.cpp b/src/d/d_meter_string.cpp index c28d6d5481..c99e7c564e 100644 --- a/src/d/d_meter_string.cpp +++ b/src/d/d_meter_string.cpp @@ -186,8 +186,8 @@ int dMeterString_c::_delete() { int dMeterString_c::createString(int i_stringID) { char str_buf[32]; dMeter2Info_getString(i_stringID, str_buf, NULL); - strcpy(static_cast(mpScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), str_buf); - strcpy(static_cast(mpScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), str_buf); + SAFE_STRCPY(static_cast(mpScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), str_buf); + SAFE_STRCPY(static_cast(mpScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), str_buf); mAnimFrame = 40.0f; mPikariAnimFrame = -1.0f; diff --git a/src/d/d_msg_class.cpp b/src/d/d_msg_class.cpp index f67d6054e6..01d2ab48e6 100644 --- a/src/d/d_msg_class.cpp +++ b/src/d/d_msg_class.cpp @@ -308,33 +308,33 @@ static u8 getOutFontNumberType(int param_0) { } #if TARGET_PC || VERSION == VERSION_GCN_PAL -static void setPlayerName(char* i_player_name, u8 param_2) { +static void setPlayerName(TEXT_SPAN i_player_name, u8 param_2) { if (param_2 != 0) { - strcpy(i_player_name, dComIfGs_getPlayerName()); + SAFE_STRCPY(i_player_name, dComIfGs_getPlayerName()); u32 name_length = strlen(i_player_name); char last = i_player_name[name_length - 1]; if (last == 0x73 || last == 0x53 || last == 0x7a || last == 0x5a || last == 0x78 || last == 0x58 || last == 0xdf) { - strcat(i_player_name, "'"); + SAFE_STRCAT(i_player_name, "'"); } else { - strcat(i_player_name, "s"); + SAFE_STRCAT(i_player_name, "s"); } } else { - strcpy(i_player_name, dComIfGs_getPlayerName()); + SAFE_STRCPY(i_player_name, dComIfGs_getPlayerName()); } } -static void setHorseName(char* i_horse_name, u8 param_2) { +static void setHorseName(TEXT_SPAN i_horse_name, u8 param_2) { if (param_2 != 0) { - strcpy(i_horse_name, dComIfGs_getHorseName()); + SAFE_STRCPY(i_horse_name, dComIfGs_getHorseName()); u32 name_length = strlen(i_horse_name); char last = i_horse_name[name_length - 1]; if (last == 0x73 || last == 0x53 || last == 0x7a || last == 0x5a || last == 0x78 || last == 0x58 || last == 0xdf) { - strcat(i_horse_name, "'"); + SAFE_STRCAT(i_horse_name, "'"); } else { - strcat(i_horse_name, "s"); + SAFE_STRCAT(i_horse_name, "s"); } } else { - strcpy(i_horse_name, dComIfGs_getHorseName()); + SAFE_STRCPY(i_horse_name, dComIfGs_getHorseName()); } } #endif @@ -707,7 +707,7 @@ void jmessage_tReference::inputNumber() { getObjectPtr()->setInputValue(new_input_val); } -char* jmessage_tReference::getWord(int i_no) { +TEXT_SPAN jmessage_tReference::getWord(int i_no) { if (i_no >= 10) { JUT_WARN(1093, "%s", "message stack over!!"); JUT_ASSERT(1094, i_no < (10)); @@ -1093,14 +1093,14 @@ bool jmessage_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_siz case MSGTAG_CURRENT_LETTER_PAGE: { char buffer[4]; int number = dComIfGp_getMessageCountNumber() / 100; - sprintf(buffer, "%d", number); + SAFE_SPRINTF(buffer, "%d", number); push_word(buffer); return true; } case MSGTAG_MAX_LETTER_PAGE: { char buffer[4]; int number = dComIfGp_getMessageCountNumber() % 100; - sprintf(buffer, "%d", number); + SAFE_SPRINTF(buffer, "%d", number); push_word(buffer); return true; } @@ -1120,7 +1120,7 @@ bool jmessage_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_siz } char player_name[100]; - strcpy(player_name, dComIfGs_getPlayerName()); + SAFE_STRCPY(player_name, dComIfGs_getPlayerName()); push_word(player_name); return true; } @@ -1130,7 +1130,7 @@ bool jmessage_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_siz } char horse_name[100]; - strcpy(horse_name, dComIfGs_getHorseName()); + SAFE_STRCPY(horse_name, dComIfGs_getHorseName()); push_word(horse_name); return true; } @@ -1779,7 +1779,7 @@ void jmessage_tMeasureProcessor::do_rubyset(void const* i_data, u32 i_size) { void jmessage_tMeasureProcessor::push_word(char* i_word) { jmessage_tReference* pReference = (jmessage_tReference*)getReference(); - strcpy(pReference->getWord(field_0x4b), i_word); + SAFE_STRCPY(pReference->getWord(field_0x4b), i_word); stack_pushCurrent(pReference->getWord(field_0x4b)); field_0x4b++; } @@ -2403,7 +2403,7 @@ bool jmessage_tSequenceProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_si u8 sel_bomb_num = objectPtr->getSelectBombNum(); dMsgUnit_setTag(7, sel_bomb_num, buffer); - strcpy((char*)pReference->getWord(field_0xb5), buffer); + SAFE_STRCPY((TEXT_SPAN)pReference->getWord(field_0xb5), buffer); push_word(); return true; } @@ -2413,7 +2413,7 @@ bool jmessage_tSequenceProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_si s16 sel_bomb_price = objectPtr->getSelectBombPrice(); dMsgUnit_setTag(1, sel_bomb_price, buffer); - strcpy((char*)pReference->getWord(field_0xb5), buffer); + SAFE_STRCPY((TEXT_SPAN)pReference->getWord(field_0xb5), buffer); push_word(); return true; } @@ -2760,13 +2760,13 @@ void jmessage_tRenderingProcessor::do_begin(void const* pEntry, char const* pszT } field_0x11c = 0; - strcpy(pReference->getTextPtr(), ""); - strcpy(pReference->getTextSPtr(), ""); - strcpy(pReference->getRubyPtr(), ""); + SAFE_STRCPY(pReference->getTextPtr(), ""); + SAFE_STRCPY(pReference->getTextSPtr(), ""); + SAFE_STRCPY(pReference->getRubyPtr(), ""); for (int i = 0; i < 3; i++) { - strcpy(pReference->getSelTextPtr(i), ""); - strcpy(pReference->getSelRubyPtr(i), ""); + SAFE_STRCPY(pReference->getSelTextPtr(i), ""); + SAFE_STRCPY(pReference->getSelRubyPtr(i), ""); } if (1.0f != pReference->getDistanceScale()) { @@ -2791,12 +2791,12 @@ void jmessage_tRenderingProcessor::do_end() { if (dMsgObject_getSelectWordFlag() != 0) { for (int i = 0; i < dMsgObject_getSelectWordFlag(); i++) { char buffer[200]; - strcpy(buffer, dMsgObject_getSelectWord(i)); + SAFE_STRCPY(buffer, dMsgObject_getSelectWord(i)); if (pReference->getSelectNum() == 2) { - strcat(pReference->getSelTextPtr(i + 1), buffer); + SAFE_STRCAT(pReference->getSelTextPtr(i + 1), buffer); } else if (pReference->getSelectNum() == 3) { - strcat(pReference->getSelTextPtr(i), buffer); + SAFE_STRCAT(pReference->getSelTextPtr(i), buffer); } } } @@ -3108,7 +3108,7 @@ bool jmessage_tRenderingProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_s char buffer[40]; u8 bombNum = pReference->getObjectPtr()->getSelectBombNum(); dMsgUnit_setTag(7, bombNum, buffer); - strcpy(pReference->getWord(field_0x14f), buffer); + SAFE_STRCPY(pReference->getWord(field_0x14f), buffer); push_word(); return 1; } @@ -3116,7 +3116,7 @@ bool jmessage_tRenderingProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_s char buffer[40]; s16 bombPrice = pReference->getObjectPtr()->getSelectBombPrice(); dMsgUnit_setTag(1, bombPrice, buffer); - strcpy(pReference->getWord(field_0x14f), buffer); + SAFE_STRCPY(pReference->getWord(field_0x14f), buffer); push_word(); return 1; } @@ -3501,7 +3501,7 @@ void jmessage_tRenderingProcessor::do_color(u8 i_colorNo) { mGCColor = getFontGCColorTable(i_colorNo, reference_p->getFukiKind()); char buffer[40]; - sprintf(buffer, + SAFE_SPRINTF(buffer, "\x1B" "CC[%08x]" "\x1B" @@ -3529,7 +3529,7 @@ void jmessage_tRenderingProcessor::do_scale(f32 param_1) { } char buffer[32]; - sprintf(buffer, + SAFE_SPRINTF(buffer, "\x1B" "FX[%d]" "\x1B" @@ -3541,7 +3541,7 @@ void jmessage_tRenderingProcessor::do_scale(f32 param_1) { void jmessage_tRenderingProcessor::do_linedown(s16 param_0) { char buffer[16]; - sprintf(buffer, "\x1B" "CD[%d]", param_0); + SAFE_SPRINTF(buffer, "\x1B" "CD[%d]", param_0); do_strcat(buffer, false, true, false); } @@ -3551,11 +3551,11 @@ void jmessage_tRenderingProcessor::do_transY(s16 i_transY, bool unused) { char buffer1[16]; if (i_transY < 0) { - sprintf(buffer0, "\x1B" "CU[%d]", -i_transY); - sprintf(buffer1, "\x1B" "CD[%d]", -i_transY); + SAFE_SPRINTF(buffer0, "\x1B" "CU[%d]", -i_transY); + SAFE_SPRINTF(buffer1, "\x1B" "CD[%d]", -i_transY); } else { - sprintf(buffer0, "\x1B" "CD[%d]", i_transY); - sprintf(buffer1, "\x1B" "CU[%d]", i_transY); + SAFE_SPRINTF(buffer0, "\x1B" "CD[%d]", i_transY); + SAFE_SPRINTF(buffer1, "\x1B" "CU[%d]", i_transY); } field_0x4c -= i_transY; @@ -3643,7 +3643,7 @@ void jmessage_tRenderingProcessor::do_strcat(char* i_str, bool param_2, bool par field_0x11c += strlen(i_str); if (field_0x14e != 0) { if (field_0x11c < 50) { - strcat(pReference->getSelTextPtr(field_0x14e - 1), i_str); + SAFE_STRCAT(pReference->getSelTextPtr(field_0x14e - 1), i_str); } else { JUT_WARN(5316, "%s", "TextBox Alloc Byte Over!!"); } @@ -3662,22 +3662,22 @@ void jmessage_tRenderingProcessor::do_strcat(char* i_str, bool param_2, bool par if (pReference->getCharAlpha() < 255.0f) { pReference->addCharAlpha(); if (field_0x148 != 0) { - char* textPtr = pReference->getTextPtr(); + TEXT_SPAN textPtr = pReference->getTextPtr(); textPtr[field_0x148] = 0; - strcat(textPtr, field_0x184); + SAFE_STRCAT(textPtr, field_0x184); } if (field_0x14a != 0) { - char* textPtr = pReference->getTextSPtr(); + TEXT_SPAN textPtr = pReference->getTextSPtr(); textPtr[field_0x14a] = 0; - strcat(textPtr, field_0x184); + SAFE_STRCAT(textPtr, field_0x184); } u32 charColor = (mCCColor & 0xFFFFFF00) | ((int)pReference->getCharAlpha() & 0xFF); u32 gradColor = (mGCColor & 0xFFFFFF00) | ((int)pReference->getCharAlpha() & 0xFF); char buffer[36]; - sprintf(buffer, "\x1b" "CC[%08x]" "\x1b" "GC[%08x]", charColor, gradColor); + SAFE_SPRINTF(buffer, "\x1b" "CC[%08x]" "\x1b" "GC[%08x]", charColor, gradColor); int length = 0; length = strlen(buffer); @@ -3686,9 +3686,9 @@ void jmessage_tRenderingProcessor::do_strcat(char* i_str, bool param_2, bool par field_0x148 = strlen(pReference->getTextPtr()); field_0x14a = strlen(pReference->getTextSPtr()); - strcpy(field_0x184, i_str); - strcat(pReference->getTextPtr(), buffer); - strcat(pReference->getTextSPtr(), buffer); + SAFE_STRCPY(field_0x184, i_str); + SAFE_STRCAT(pReference->getTextPtr(), buffer); + SAFE_STRCAT(pReference->getTextSPtr(), buffer); } else { JUT_WARN(5362, "%s", "TextBox Alloc Byte Over!!"); } @@ -3699,9 +3699,9 @@ void jmessage_tRenderingProcessor::do_strcat(char* i_str, bool param_2, bool par field_0x14a = 0; } - strcat(pReference->getTextPtr(), i_str); + SAFE_STRCAT(pReference->getTextPtr(), i_str); if (param_3) { - strcat(pReference->getTextSPtr(), i_str); + SAFE_STRCAT(pReference->getTextSPtr(), i_str); } } else { JUT_WARN(5380, "%s", "TextBox Alloc Byte Over!!"); @@ -3729,7 +3729,7 @@ void jmessage_tRenderingProcessor::do_rubyset(void const* i_data, u32 i_size) { buffer[0] = pRuby[index++]; buffer[1] = pRuby[index++]; buffer[2] = 0; - strcat(field_0x152, (const char*)buffer); + SAFE_STRCAT(field_0x152, (const char*)buffer); int character = (((char)buffer[0] & 0xFF) << 8) | ((char)buffer[1] & 0xFF); if (field_0x14e != 0) { @@ -3751,7 +3751,7 @@ void jmessage_tRenderingProcessor::do_rubyset(void const* i_data, u32 i_size) { } } -void jmessage_tRenderingProcessor::do_rubystrcat(char* i_src, char* i_dst, f32 i_charSpace, f32 param_4) { +void jmessage_tRenderingProcessor::do_rubystrcat(char* i_src, TEXT_SPAN i_dst, f32 i_charSpace, f32 param_4) { jmessage_tReference* pReference = (jmessage_tReference*)getReference(); if (pReference->isCharSend()) { if (0.0f != param_4) { @@ -3760,18 +3760,18 @@ void jmessage_tRenderingProcessor::do_rubystrcat(char* i_src, char* i_dst, f32 i if (cursor_trans >= 1.0f) { char buffer[16]; snprintf(buffer, sizeof(buffer) - 1, "\x1B" "CR[%d]", (int)cursor_trans); - strcat(i_dst, buffer); + SAFE_STRCAT(i_dst, buffer); field_0x12c += (int)cursor_trans; } else if (cursor_trans <= -1.0f) { char buffer[16]; snprintf(buffer, sizeof(buffer) - 1, "\x1B" "CL[%d]", (int)-cursor_trans); - strcat(i_dst, buffer); + SAFE_STRCAT(i_dst, buffer); field_0x12c += (int)cursor_trans; } field_0x12c += field_0x128 + i_charSpace; } - strcat(i_dst, i_src); + SAFE_STRCAT(i_dst, i_src); } } @@ -4046,13 +4046,13 @@ bool jmessage_string_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u3 break; case MSGTAG_CURRENT_LETTER_PAGE: { char buffer[4]; - sprintf(buffer, "%d", dComIfGp_getMessageCountNumber() / 100); + SAFE_SPRINTF(buffer, "%d", dComIfGp_getMessageCountNumber() / 100); stack_pushCurrent(buffer); break; } case MSGTAG_MAX_LETTER_PAGE: { char buffer[4]; - sprintf(buffer, "%d", dComIfGp_getMessageCountNumber() % 100); + SAFE_SPRINTF(buffer, "%d", dComIfGp_getMessageCountNumber() % 100); stack_pushCurrent(buffer); break; } @@ -4469,11 +4469,11 @@ void jmessage_string_tRenderingProcessor::do_begin(void const* pEntry, char cons void jmessage_string_tRenderingProcessor::do_end() { if (mpReference->getPanePtr() != NULL) { - strcpy(mpReference->getPanePtr()->getStringPtr(), field_0x54); + SAFE_STRCPY(mpReference->getPanePtr()->getStringPtr(), field_0x54); } if (mpReference->getRubyPanePtr() != NULL) { - strcpy(mpReference->getRubyPanePtr()->getStringPtr(), field_0x254); + SAFE_STRCPY(mpReference->getRubyPanePtr()->getStringPtr(), field_0x254); } } @@ -4604,13 +4604,13 @@ bool jmessage_string_tRenderingProcessor::do_tag(u32 i_tag, void const* i_data, break; case MSGTAG_CURRENT_LETTER_PAGE: { char buffer[4]; - sprintf(buffer, "%d", dComIfGp_getMessageCountNumber() / 100); + SAFE_SPRINTF(buffer, "%d", dComIfGp_getMessageCountNumber() / 100); push_word(buffer); break; } case MSGTAG_MAX_LETTER_PAGE: { char buffer[4]; - sprintf(buffer, "%d", dComIfGp_getMessageCountNumber() % 100); + SAFE_SPRINTF(buffer, "%d", dComIfGp_getMessageCountNumber() % 100); push_word(buffer); break; } @@ -5086,7 +5086,7 @@ void jmessage_string_tRenderingProcessor::do_strcat(char* i_str) { if (getLineCountNowPage() >= 0) { field_0x54e += strlen(i_str); if (field_0x54e < ARRAY_SIZE(field_0x54)) { - strcat(field_0x54, i_str); + SAFE_STRCAT(field_0x54, i_str); } else { JUT_WARN(7531, "%s", "Message Alloc Byte Over!!"); } @@ -5117,7 +5117,7 @@ void jmessage_string_tRenderingProcessor::do_rubyset(void const* i_data, u32 i_s bytes[0] = pRuby[i++]; bytes[1] = pRuby[i++]; bytes[2] = 0; - strcat(field_0x454, (const char*)bytes); + SAFE_STRCAT(field_0x454, (const char*)bytes); int character = (((char)bytes[0] & 0xFF) << 8) | ((char)bytes[1] & 0xFF); field_0x44 += charSpace + fontSize.mSizeX * ((f32)pFont->getWidth(character) / pFont->getCellWidth()); @@ -5135,7 +5135,7 @@ void jmessage_string_tRenderingProcessor::do_rubystrcat(char* i_str) { if (getLineCountNowPage() >= 0) { field_0x550 += strlen(i_str); if (field_0x550 < ARRAY_SIZE(field_0x254)) { - strcat(field_0x254, i_str); + SAFE_STRCAT(field_0x254, i_str); } else { JUT_WARN(7613, "%s", "Message Alloc Byte Over!!"); } @@ -5209,7 +5209,7 @@ void jmessage_string_tRenderingProcessor::do_color(u8 i_colorNo) { } char buffer[32]; - sprintf(buffer, "\x1b" "CC[%08x]" "\x1b" "GC[%08x]", ccColor, gcColor); + SAFE_SPRINTF(buffer, "\x1b" "CC[%08x]" "\x1b" "GC[%08x]", ccColor, gcColor); do_strcat(buffer); } @@ -5220,13 +5220,13 @@ void jmessage_string_tRenderingProcessor::do_scale(f32 i_scale) { s16 scaleY = 0.5f + fontSize.mSizeY * i_scale; char buffer[32]; - sprintf(buffer, "\x1b" "FX[%d]" "\x1b" "FY[%d]", scaleX, scaleY); + SAFE_SPRINTF(buffer, "\x1b" "FX[%d]" "\x1b" "FY[%d]", scaleX, scaleY); do_strcat(buffer); } void jmessage_string_tRenderingProcessor::do_linedown(s16 i_lineNo) { char buffer[16]; - sprintf(buffer, "\x1B" "CD[%d]", i_lineNo); + SAFE_SPRINTF(buffer, "\x1B" "CD[%d]", i_lineNo); do_strcat(buffer); } @@ -5245,6 +5245,6 @@ void jmessage_string_tRenderingProcessor::do_numset(s16 i_num) { } void jmessage_string_tRenderingProcessor::push_word(char const* i_word) { - strcpy(field_0x486, i_word); + SAFE_STRCPY(field_0x486, i_word); stack_pushCurrent(field_0x486); } diff --git a/src/d/d_msg_object.cpp b/src/d/d_msg_object.cpp index 44b5c66571..d4cff01f31 100644 --- a/src/d/d_msg_object.cpp +++ b/src/d/d_msg_object.cpp @@ -461,12 +461,12 @@ int dMsgObject_c::_execute() { mpCtrl->setMessageCode(mpRefer->getRevoMessageID()); mpRefer->setRevoMessageID(0); jmessage_tReference* pRef = (jmessage_tReference*)mpRenProc->getReference(); - strcpy(pRef->getTextPtr(), ""); - strcpy(pRef->getTextSPtr(), ""); - strcpy(pRef->getRubyPtr(), ""); + SAFE_STRCPY(pRef->getTextPtr(), ""); + SAFE_STRCPY(pRef->getTextSPtr(), ""); + SAFE_STRCPY(pRef->getRubyPtr(), ""); for (int i = 0; i < 3; i++) { - strcpy(pRef->getSelTextPtr(i), ""); - strcpy(pRef->getSelRubyPtr(i), ""); + SAFE_STRCPY(pRef->getSelTextPtr(i), ""); + SAFE_STRCPY(pRef->getSelRubyPtr(i), ""); } } field_0x4ca = mpCtrl->update(); @@ -1021,12 +1021,12 @@ void dMsgObject_c::continueProc() { offAutoMessageFlagLocal(); setMessageIndex(field_0x100->msg_idx, field_0x100->field_0xf0, true); mpScrnDraw->fukiPosCalc(pRef->getFukiPosType()); - strcpy(pRef->getTextPtr(), ""); - strcpy(pRef->getTextSPtr(), ""); - strcpy(pRef->getRubyPtr(), ""); + SAFE_STRCPY(pRef->getTextPtr(), ""); + SAFE_STRCPY(pRef->getTextSPtr(), ""); + SAFE_STRCPY(pRef->getRubyPtr(), ""); for (int i = 0; i < 3; i++) { - strcpy(pRef->getSelTextPtr(i), ""); - strcpy(pRef->getSelRubyPtr(i), ""); + SAFE_STRCPY(pRef->getSelTextPtr(i), ""); + SAFE_STRCPY(pRef->getSelRubyPtr(i), ""); } mpScrnDraw->arwAnimeInit(); mpRenProc->setTextInitPos(mpScrnDraw->getTextBoxPosX(), mpScrnDraw->getTextBoxPosY()); @@ -1737,8 +1737,8 @@ void dMsgObject_c::changeGroupLocal(s16 param_1) { } bool dMsgObject_c::getStringLocal(u32 param_1, J2DTextBox* param_2, J2DTextBox* param_3, - JUTFont* param_4, COutFont_c* param_5, char* param_6, - char* param_7, char* param_8, s16* param_9) { + JUTFont* param_4, COutFont_c* param_5, TEXT_SPAN param_6, + TEXT_SPAN param_7, TEXT_SPAN param_8, s16* param_9) { if (field_0x4cd == 0) { if (getStatusLocal() == 1) { s16 groupID = getMessageGroup(param_1); @@ -1781,13 +1781,13 @@ bool dMsgObject_c::getStringLocal(u32 param_1, J2DTextBox* param_2, J2DTextBox* *param_9 = mpRefer->getCharSoundInfo().field_0x40e; } if (param_6 != NULL) { - strcpy(param_6, pRef->getTextPtr()); + SAFE_STRCPY(param_6, pRef->getTextPtr()); } if (param_7 != NULL) { - strcpy(param_7, pRef->getRubyPtr()); + SAFE_STRCPY(param_7, pRef->getRubyPtr()); } if (param_8 != NULL) { - strcpy(param_8, pRef->getTextSPtr()); + SAFE_STRCPY(param_8, pRef->getTextSPtr()); } mpCtrl->reset(); mpCtrl->resetResourceCache(); @@ -2309,8 +2309,8 @@ void dMsgObject_c::changeGroup(s16 param_0) { } bool dMsgObject_c::getString(u32 param_0, J2DTextBox* param_1, J2DTextBox* param_2, - JUTFont* param_3, COutFont_c* param_4, char* param_5, char* param_6, - char* param_7, s16* param_8) { + JUTFont* param_3, COutFont_c* param_4, TEXT_SPAN param_5, TEXT_SPAN param_6, + TEXT_SPAN param_7, s16* param_8) { return dMsgObject_getMsgObjectClass()->getStringLocal( param_0, param_1, param_2, param_3, param_4, param_5, param_6, param_7, param_8); } diff --git a/src/d/d_msg_scrn_3select.cpp b/src/d/d_msg_scrn_3select.cpp index 27a0bf5e1b..216135b10c 100644 --- a/src/d/d_msg_scrn_3select.cpp +++ b/src/d/d_msg_scrn_3select.cpp @@ -294,15 +294,15 @@ void dMsgScrn3Select_c::setString(char* mpText0, char* mpText1, char* mpText2) { } if (mpTmSel_c[0] != NULL) { - strcpy(((J2DTextBox*)(mpTmSel_c[0]->getPanePtr()))->getStringPtr(), mpText0); + SAFE_STRCPY(((J2DTextBox*)(mpTmSel_c[0]->getPanePtr()))->getStringPtr(), mpText0); } if (mpTmSel_c[1] != NULL) { - strcpy(((J2DTextBox*)(mpTmSel_c[1]->getPanePtr()))->getStringPtr(), mpText1); + SAFE_STRCPY(((J2DTextBox*)(mpTmSel_c[1]->getPanePtr()))->getStringPtr(), mpText1); } if (mpTmSel_c[2] != NULL) { - strcpy(((J2DTextBox*)(mpTmSel_c[2]->getPanePtr()))->getStringPtr(), mpText2); + SAFE_STRCPY(((J2DTextBox*)(mpTmSel_c[2]->getPanePtr()))->getStringPtr(), mpText2); } } @@ -323,15 +323,15 @@ void dMsgScrn3Select_c::setRubyString(char* pText0, char* pText1, char* pText2) } if (mpTmrSel_c[0] != NULL) { - strcpy(((J2DTextBox*)(mpTmrSel_c[0]->getPanePtr()))->getStringPtr(), pText0); + SAFE_STRCPY(((J2DTextBox*)(mpTmrSel_c[0]->getPanePtr()))->getStringPtr(), pText0); } if (mpTmrSel_c[1] != NULL) { - strcpy(((J2DTextBox*)(mpTmrSel_c[1]->getPanePtr()))->getStringPtr(), pText1); + SAFE_STRCPY(((J2DTextBox*)(mpTmrSel_c[1]->getPanePtr()))->getStringPtr(), pText1); } if (mpTmrSel_c[2] != NULL) { - strcpy(((J2DTextBox*)(mpTmrSel_c[2]->getPanePtr()))->getStringPtr(), pText2); + SAFE_STRCPY(((J2DTextBox*)(mpTmrSel_c[2]->getPanePtr()))->getStringPtr(), pText2); } } diff --git a/src/d/d_msg_scrn_base.cpp b/src/d/d_msg_scrn_base.cpp index fdd9e57f9c..f402f8983b 100644 --- a/src/d/d_msg_scrn_base.cpp +++ b/src/d/d_msg_scrn_base.cpp @@ -108,9 +108,9 @@ void dMsgScrnBase_c::setString(char* mpText, char* i_stringB) { if (mpTm_c[i] != NULL) { JUT_ASSERT(262, ((J2DTextBox*)(mpTm_c[i]->getPanePtr()))->getStringAllocByte() > strlen(mpText)); if (i == 0) { - strcpy(((J2DTextBox*)mpTm_c[i]->getPanePtr())->getStringPtr(), mpText); + SAFE_STRCPY(((J2DTextBox*)mpTm_c[i]->getPanePtr())->getStringPtr(), mpText); } else { - strcpy(((J2DTextBox*)mpTm_c[i]->getPanePtr())->getStringPtr(), i_stringB); + SAFE_STRCPY(((J2DTextBox*)mpTm_c[i]->getPanePtr())->getStringPtr(), i_stringB); } } } @@ -120,7 +120,7 @@ void dMsgScrnBase_c::setRubyString(char* mpText) { for (int i = 0; i < 3; i++) { if (mpTmr_c[i] != NULL) { JUT_ASSERT(288, ((J2DTextBox*)(mpTmr_c[i]->getPanePtr()))->getStringAllocByte() > strlen(mpText)); - strcpy(((J2DTextBox*)mpTmr_c[i]->getPanePtr())->getStringPtr(), mpText); + SAFE_STRCPY(((J2DTextBox*)mpTmr_c[i]->getPanePtr())->getStringPtr(), mpText); } } } diff --git a/src/d/d_msg_scrn_explain.cpp b/src/d/d_msg_scrn_explain.cpp index 945f75e927..bc6275e10c 100644 --- a/src/d/d_msg_scrn_explain.cpp +++ b/src/d/d_msg_scrn_explain.cpp @@ -317,7 +317,7 @@ void dMsgScrnExplain_c::draw(J2DOrthoGraph* i_graf) { } char string_buf[STR_BUF_LEN]; - strcpy(string_buf, ((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr()); + SAFE_STRCPY(string_buf, ((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr()); mpTxScreen->draw(0.0f, 0.0f, (J2DGrafContext*)i_graf); #if VERSION == VERSION_GCN_JPN @@ -327,7 +327,7 @@ void dMsgScrnExplain_c::draw(J2DOrthoGraph* i_graf) { #endif mpString_c->drawOutFont((J2DTextBox*)mpTm_c[0]->getPanePtr(), -1.0f); - strcpy(((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr(), string_buf); + SAFE_STRCPY(((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr(), string_buf); if (mpSelect_c != NULL && (field_0x64 == 1 || field_0x64 == 2)) { // the magic numbers here are relative to the framebuffer size, but were likely @@ -370,11 +370,11 @@ void dMsgScrnExplain_c::open_request_proc() { bool rt = dMsgObject_getString(mOpenMsgId, tbox, rubyTbox, mDoExt_getMesgFont(), mpOutFont, text, ruby, textShadow, &field_0x5c); if (rt) { - strcpy(((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr(), text); - strcpy(((J2DTextBox*)mpTm_c[1]->getPanePtr())->getStringPtr(), textShadow); + SAFE_STRCPY(((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr(), text); + SAFE_STRCPY(((J2DTextBox*)mpTm_c[1]->getPanePtr())->getStringPtr(), textShadow); for (int i = 0; i < 2; i++) { if (mpTmr_c[i] != NULL) { - strcpy(((J2DTextBox*)mpTmr_c[i]->getPanePtr())->getStringPtr(), ruby); + SAFE_STRCPY(((J2DTextBox*)mpTmr_c[i]->getPanePtr())->getStringPtr(), ruby); } } @@ -483,12 +483,12 @@ void dMsgScrnExplain_c::move_select_init() { "\x1B" "CR[%d]", (int)var_f28); - strcat(string_buf_yes, msg_buf_yes); + SAFE_STRCAT(string_buf_yes, msg_buf_yes); snprintf(string_buf_no, 20, "\x1B" "CR[%d]", (int)var_f27); - strcat(string_buf_no, msg_buf_no); + SAFE_STRCAT(string_buf_no, msg_buf_no); mpSelect_c->setString("", string_buf_yes, string_buf_no); mpSelect_c->setRubyString("", "", ""); diff --git a/src/d/d_msg_string_base.cpp b/src/d/d_msg_string_base.cpp index 7346047661..003375d6b4 100644 --- a/src/d/d_msg_string_base.cpp +++ b/src/d/d_msg_string_base.cpp @@ -126,7 +126,7 @@ u8 dMsgStringBase_c::getPageMax(int param_0) { return pageMax; } -f32 dMsgStringBase_c::getMessageLocal(u32 param_1, char* param_2) { +f32 dMsgStringBase_c::getMessageLocal(u32 param_1, TEXT_SPAN param_2) { if (dMeter2Info_getMsgResource() != NULL) { if (param_1 > 5000) { if (field_0x1c != dMeter2Info_getStageMsgResource()) { @@ -146,7 +146,7 @@ f32 dMsgStringBase_c::getMessageLocal(u32 param_1, char* param_2) { mpCtrl->render(); mpCtrl->reset(); mpCtrl->resetResourceCache(); - strcpy(param_2, mpRenProc->getString()); + SAFE_STRCPY(param_2, mpRenProc->getString()); return 0.0f; } @@ -165,7 +165,7 @@ f32 dMsgStringBase_c::getStringPage(u32 param_0, u8 param_1, u8 param_2, J2DText return getStringPageLocal(param_0, param_1, param_2, param_3, param_4, param_5, param_6, param_7); } -f32 dMsgStringBase_c::getMessage(u32 param_0, char* param_1) { +f32 dMsgStringBase_c::getMessage(u32 param_0, TEXT_SPAN param_1) { return getMessageLocal(param_0, param_1); } diff --git a/src/d/d_msg_unit.cpp b/src/d/d_msg_unit.cpp index 7bbe8b3d70..55fc588d4e 100644 --- a/src/d/d_msg_unit.cpp +++ b/src/d/d_msg_unit.cpp @@ -42,21 +42,21 @@ dMsgUnit_c::dMsgUnit_c() {} dMsgUnit_c::~dMsgUnit_c() {} #if REGION_JPN -void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { +void dMsgUnit_c::setTag(int i_type, int i_value, TEXT_SPAN o_buffer, bool param_4) { *o_buffer = 0; bool stack9 = false; bool stack8 = false; int value = i_value; if (i_type == 0x10000) { - sprintf(o_buffer, "%d", i_value); + SAFE_SPRINTF(o_buffer, "%d", i_value); return; } if (i_type == 0x10001) { int tens_digit = i_value / 10; int ones_digit = i_value % 10; - sprintf(o_buffer, "%d-%d", tens_digit, ones_digit); + SAFE_SPRINTF(o_buffer, "%d-%d", tens_digit, ones_digit); return; } @@ -108,7 +108,7 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { } if (i_type == 9 && param_4 == true) { - sprintf(o_buffer, "%d", i_value); + SAFE_SPRINTF(o_buffer, "%d", i_value); stack8 = true; } @@ -170,9 +170,9 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { int uVar5Len = strlen(uVar5); if (uVar5Len == 0) { if (stack8) { - strcat(o_buffer, value2); + SAFE_STRCAT(o_buffer, value2); } else { - sprintf(o_buffer, "%d%s", i_value, value2); + SAFE_SPRINTF(o_buffer, "%d%s", i_value, value2); } } else { char unkCharArr[7]; @@ -185,11 +185,11 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { unkCharArr[6] = 0; if (stack8) { - strcat(o_buffer, unkCharArr); - strcat(o_buffer, uVar5); - strcat(o_buffer, value2); + SAFE_STRCAT(o_buffer, unkCharArr); + SAFE_STRCAT(o_buffer, uVar5); + SAFE_STRCAT(o_buffer, value2); } else { - sprintf(o_buffer, "%d%s%s%s", i_value, unkCharArr, uVar5, value2); + SAFE_SPRINTF(o_buffer, "%d%s%s%s", i_value, unkCharArr, uVar5, value2); } } } @@ -197,17 +197,17 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { if (i_type == 3 && param_4 == true) { char buffer[20]; setTag(4, 0, buffer, false); - strcat(o_buffer, buffer); + SAFE_STRCAT(o_buffer, buffer); } if (i_type == 4 && param_4 == true) { char buffer[20]; setTag(5, value, buffer, false); - strcat(o_buffer, buffer); + SAFE_STRCAT(o_buffer, buffer); } } #else -void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { +void dMsgUnit_c::setTag(int i_type, int i_value, TEXT_SPAN o_buffer, bool param_4) { *o_buffer = 0; bool stack9 = false; bool stack8 = false; @@ -218,14 +218,14 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { int minutes; // sp34 if (i_type == 0x10000) { - sprintf(o_buffer, "%d", i_value); + SAFE_SPRINTF(o_buffer, "%d", i_value); return; } if (i_type == 0x10001) { tens_digit = i_value / 10; ones_digit = i_value % 10; - sprintf(o_buffer, "%d-%d", tens_digit, ones_digit); + SAFE_SPRINTF(o_buffer, "%d-%d", tens_digit, ones_digit); return; } @@ -238,7 +238,7 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { seconds = 59; } if (minutes != 0 || seconds != 0) { - sprintf(o_buffer, "%d:%02d", minutes, seconds); + SAFE_SPRINTF(o_buffer, "%d:%02d", minutes, seconds); } return; @@ -254,11 +254,11 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { iVar8b = ((s32)(1000000.0f * dayTime) % 250000) / 1000000.0f; f32 iVar9 = 60.0f * (iVar8b / 0.25f); - sprintf(o_buffer, "%d:%02d", (s32)hour, (s32)minute); + SAFE_SPRINTF(o_buffer, "%d:%02d", (s32)hour, (s32)minute); } else { if (i_type == 9 && param_4 == true) { int value = i_value; - sprintf(o_buffer, "%d", value); + SAFE_SPRINTF(o_buffer, "%d", value); stack8 = true; } @@ -359,21 +359,21 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { } if (strcmp(uVar5, "") == 0) { - sprintf(o_buffer, "%d%s", i_value, uVar5); + SAFE_SPRINTF(o_buffer, "%d%s", i_value, uVar5); } else { - sprintf(o_buffer, "%d %s", i_value, uVar5); + SAFE_SPRINTF(o_buffer, "%d %s", i_value, uVar5); } } if (i_type == 3 && param_4 == true) { char buffer[20]; setTag(4, 0, buffer, false); - strcat(o_buffer, buffer); + SAFE_STRCAT(o_buffer, buffer); } if (i_type == 4 && param_4 == true) { char buffer[20]; setTag(5, param_2b, buffer, false); - strcat(o_buffer, buffer); + SAFE_STRCAT(o_buffer, buffer); } } } diff --git a/src/d/d_name.cpp b/src/d/d_name.cpp index 3c0a7d35e1..e4c2a0f8f0 100644 --- a/src/d/d_name.cpp +++ b/src/d/d_name.cpp @@ -908,7 +908,7 @@ void dName_c::setNameText() { #if REGION_JPN if (mChrInfo[i].mMojiSet == 2) { #endif - sprintf(mNameText[i], + SAFE_SPRINTF(mNameText[i], "\x1b" "CD\x1b" "CR\x1b" @@ -919,7 +919,7 @@ void dName_c::setNameText() { ); #if REGION_JPN } else { - sprintf(mNameText[i], + SAFE_SPRINTF(mNameText[i], "\x1b" "CD\x1b" "CR\x1b" @@ -1330,7 +1330,7 @@ void dName_c::mojiListChange() { char buf[74]; for (int i = 0; i < 65; i++) { - strcpy(buf, "\x1B" + SAFE_STRCPY(buf, "\x1B" "CD" "\x1B" "CR" @@ -1338,15 +1338,15 @@ void dName_c::mojiListChange() { "CC[000000]" "\x1B" "GM[0]"); - strcat(buf, mojiSet[i]); - strcat(buf, "\x1B" + SAFE_STRCAT(buf, mojiSet[i]); + SAFE_STRCAT(buf, "\x1B" "HM" "\x1B" "CC[ffffff]" "\x1B" "GM[0]"); - strcat(buf, mojiSet[i]); - strcpy(mMojiText[i], buf); + SAFE_STRCAT(buf, mojiSet[i]); + SAFE_STRCPY(mMojiText[i], buf); } #if TARGET_PC || REGION_PAL || REGION_JPN diff --git a/src/d/d_particle.cpp b/src/d/d_particle.cpp index 9b89afc7b1..1c53313826 100644 --- a/src/d/d_particle.cpp +++ b/src/d/d_particle.cpp @@ -1263,7 +1263,7 @@ bool dPa_control_c::readScene(u8 param_0, mDoDvdThd_toMainRam_c** param_1) { JUT_ASSERT(2647, !mSceneCount++); field_0x18 = param_0; static char jpcName[32]; - sprintf(jpcName, "/res/Particle/Pscene%03d.jpc", param_0); + SAFE_SPRINTF(jpcName, "/res/Particle/Pscene%03d.jpc", param_0); *param_1 = mDoDvdThd_toMainRam_c::create(jpcName, 0, m_resHeap); return 1; } diff --git a/src/d/d_s_play.cpp b/src/d/d_s_play.cpp index f2e7d5d926..4445d9d1bb 100644 --- a/src/d/d_s_play.cpp +++ b/src/d/d_s_play.cpp @@ -163,22 +163,22 @@ void dScnPly_reg_childHIO_c::genMessage(JORMContext* mctx) { char textbuf[8]; for (int i = 0; i < 20; i++) { - sprintf(textbuf, " F(%02d)", i); + SAFE_SPRINTF(textbuf, " F(%02d)", i); mctx->genSlider(textbuf, &mFloatReg[i], -100000.0f, 100000.0f); } for (int i = 20; i < 25; i++) { - sprintf(textbuf, " F(%02d)", i); + SAFE_SPRINTF(textbuf, " F(%02d)", i); mctx->genSlider(textbuf, &mFloatReg[i], 0.0f, 1.0f); } for (int i = 25; i < 30; i++) { - sprintf(textbuf, " F(%02d)", i); + SAFE_SPRINTF(textbuf, " F(%02d)", i); mctx->genSlider(textbuf, &mFloatReg[i], -1.0f, 1.0f); } for (int i = 0; i < 10; i++) { - sprintf(textbuf, " S(%02d)", i); + SAFE_SPRINTF(textbuf, " S(%02d)", i); mctx->genSlider(textbuf, &mShortReg[i], -0x8000, 0x7FFF); } } diff --git a/src/d/d_s_room.cpp b/src/d/d_s_room.cpp index 0fcdb58ac0..259bc37e3b 100644 --- a/src/d/d_s_room.cpp +++ b/src/d/d_s_room.cpp @@ -178,7 +178,7 @@ static int loadDemoArchive(int i_roomNo) { int bank2 = entries[dComIfG_play_c::getLayerNo(i_roomNo)].bank2; JUT_ASSERT(353, 0 <= bank2 && bank2 < 100); - sprintf(dStage_roomControl_c::getDemoArcName(), "Demo%02d_%02d", bank, bank2); + SAFE_SPRINTF(dStage_roomControl_c::getDemoArcName(), "Demo%02d_%02d", bank, bank2); if (!dComIfG_setObjectRes(dStage_roomControl_c::getDemoArcName(), 0, (JKRHeap*)NULL)) { const char* name = dStage_roomControl_c::getDemoArcName(); *dStage_roomControl_c::getDemoArcName() = 0; diff --git a/src/d/d_save.cpp b/src/d/d_save.cpp index fc1cbc4efe..af2a6db28a 100644 --- a/src/d/d_save.cpp +++ b/src/d/d_save.cpp @@ -2034,7 +2034,7 @@ void flagFile_c::listenPropertyEvent(const JORPropertyEvent* i_event) { const char* start_stage_name = dComIfGp_getStartStageName(); char filename[64]; - sprintf(filename, "_%02d%02d%02d%02d%02d-%s.zff", time.mon + 1, time.mday, time.hour, time.min, time.sec, start_stage_name); + SAFE_SPRINTF(filename, "_%02d%02d%02d%02d%02d-%s.zff", time.mon + 1, time.mday, time.hour, time.min, time.sec, start_stage_name); OS_REPORT("write to %s\n", filename); JORFile file; diff --git a/src/d/d_stage.cpp b/src/d/d_stage.cpp index f3cdfed17b..5836feccd2 100644 --- a/src/d/d_stage.cpp +++ b/src/d/d_stage.cpp @@ -482,7 +482,7 @@ void* dStage_roomControl_c::roomDzs_c::add(u8 i_no, u8 roomNo) { void** dzs = m_dzs + i_no; if (*dzs == NULL) { char dzsName[20]; - sprintf(dzsName, "%s/room%d.dzs", dComIfGp_getStartStageName(), roomNo); + SAFE_SPRINTF(dzsName, "%s/room%d.dzs", dComIfGp_getStartStageName(), roomNo); JUT_ASSERT(1167, strlen(dzsName) <= sizeof(dzsName)); u32 expandSize = @@ -2396,7 +2396,7 @@ static void readMult(dStage_dt_c* i_stage, dStage_Multi_c* multi, bool useOldRes if (dzs == NULL) { char dzsName[11]; - sprintf(dzsName, "room%d.dzs", info->mRoomNo); + SAFE_SPRINTF(dzsName, "room%d.dzs", info->mRoomNo); JUT_ASSERT(3548, strlen(dzsName) <= sizeof(dzsName)); if (useOldRes) { diff --git a/src/d/d_timer.cpp b/src/d/d_timer.cpp index cb40e3444a..8bc5cac570 100644 --- a/src/d/d_timer.cpp +++ b/src/d/d_timer.cpp @@ -1224,8 +1224,8 @@ BOOL dDlst_TimerScrnDraw_c::closeAnime() { int dDlst_TimerScrnDraw_c::createGetIn(cXyz i_pos) { char string[104]; dMeter2Info_getString(0x3E4, string, NULL); // "GOAT IN!" - strcpy(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), string); - strcpy(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), string); + SAFE_STRCPY(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), string); + SAFE_STRCPY(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), string); if (mCowID < 50) { m_getin_info[mCowID].bck_frame = 40.0f; @@ -1313,8 +1313,8 @@ int dDlst_TimerScrnDraw_c::createGetIn(cXyz i_pos) { s32 dDlst_TimerScrnDraw_c::createStart(u16 i_messageID) { char string[112]; dMeter2Info_getString(i_messageID, string, NULL); - strcpy(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), string); - strcpy(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), string); + SAFE_STRCPY(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), string); + SAFE_STRCPY(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), string); if (mCowID == 0) { m_getin_info[mCowID].bck_frame = 40.0f; diff --git a/src/dusk/imgui/ImGuiProcessOverlay.cpp b/src/dusk/imgui/ImGuiProcessOverlay.cpp index 806a9ea0f6..5b4fff28db 100644 --- a/src/dusk/imgui/ImGuiProcessOverlay.cpp +++ b/src/dusk/imgui/ImGuiProcessOverlay.cpp @@ -46,7 +46,7 @@ namespace dusk { ImGui::TableNextColumn(); char id_buf[32]; - sprintf(id_buf, "%d", proc->id); + SAFE_SPRINTF(id_buf, "%d", proc->id); int flags = ImGuiTreeNodeFlags_SpanAllColumns; bool isLayer = fpcBs_Is_JustOfType(g_fpcNd_type, proc->subtype); diff --git a/src/dusk/imgui/ImGuiSaveEditor.cpp b/src/dusk/imgui/ImGuiSaveEditor.cpp index c09267bc27..ec2802de44 100644 --- a/src/dusk/imgui/ImGuiSaveEditor.cpp +++ b/src/dusk/imgui/ImGuiSaveEditor.cpp @@ -550,7 +550,7 @@ namespace dusk { char nameBuffer[8]; snprintf(nameBuffer, sizeof(nameBuffer), "%s", playerName); if (ImGui::InputText("##PlayerNameInput", nameBuffer, 8)) { - strcpy(dComIfGs_getPlayerName(), nameBuffer); + SAFE_STRCPY(dComIfGs_getPlayerName(), nameBuffer); } const char* horseName = dComIfGs_getHorseName(); @@ -559,7 +559,7 @@ namespace dusk { char horseNameBuffer[8]; snprintf(horseNameBuffer, sizeof(horseNameBuffer), "%s", horseName); if (ImGui::InputText("##HorseNameInput", horseNameBuffer, 8)) { - strcpy(dComIfGs_getHorseName(), horseNameBuffer); + SAFE_STRCPY(dComIfGs_getHorseName(), horseNameBuffer); } ImGui::Separator(); @@ -745,8 +745,8 @@ namespace dusk { ImGui::SameLine(); char nameBuffer[8]; snprintf(nameBuffer, sizeof(nameBuffer), "%s", returnPlace.mName); - if (ImGui::InputText("##SaveStageNameInput", nameBuffer, 8)) { - strcpy(returnPlace.mName, nameBuffer); + if (ImGui::InputText("##SaveStageNameInput", nameBuffer, sizeof(nameBuffer))) { + SAFE_STRCPY(returnPlace.mName, nameBuffer); } ImGui::Text("Room: "); @@ -787,8 +787,8 @@ namespace dusk { ImGui::SameLine(); char horseStageBuffer[8]; snprintf(horseStageBuffer, sizeof(horseStageBuffer), "%s", horsePlace.mName); - if (ImGui::InputText("##HorseStageNameInput", horseStageBuffer, 8)) { - strcpy(horsePlace.mName, horseStageBuffer); + if (ImGui::InputText("##HorseStageNameInput", horseStageBuffer, sizeof(horseStageBuffer))) { + SAFE_STRCPY(horsePlace.mName, horseStageBuffer); } ImGui::Text("Room: "); diff --git a/src/dusk/string.cpp b/src/dusk/string.cpp new file mode 100644 index 0000000000..e40094ef70 --- /dev/null +++ b/src/dusk/string.cpp @@ -0,0 +1,85 @@ +#include "dusk/string.hpp" +#include "fmt/format.h" + +namespace { +void strncpyProxy(char* dst, const char* src, size_t count) { +#if _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + strncpy(dst, src, count); +#if _MSC_VER +#pragma warning(pop) +#endif +} +} // namespace + +namespace dusk { + +void TextSpan::CrashSpawnEmpty() { + CRASH("Span is empty!"); +} + +void SafeStringCopyTruncate(char* buffer, size_t bufSize, const char* src) { + if (buffer == src) [[unlikely]] { + CRASH("Cannot copy string to same buffer"); + } + + if (bufSize == 0) [[unlikely]] { + CRASH("Target buffer cannot be size zero"); + } + + strncpyProxy(buffer, src, bufSize); + buffer[bufSize - 1] = 0; +} + +void SafeStringCopy(char* buffer, size_t bufSize, const char* src) { + if (bufSize == 0) [[unlikely]] { + CRASH("Target buffer cannot be size zero"); + } + + if (buffer == src) [[unlikely]] { + CRASH("Cannot copy string to same buffer"); + } + + const auto srcSize = strlen(src); + if (srcSize > bufSize - 1) [[unlikely]] { + const auto msg = fmt::format( + "Destination buffer too small! Need %d, have %d", + srcSize + 1, + bufSize); + CRASH("%s", msg.c_str()); + } + + strncpyProxy(buffer, src, bufSize); + buffer[bufSize - 1] = 0; +} + +void SafeStringCat(char* buffer, size_t bufSize, const char* src) { + if (bufSize == 0) [[unlikely]] { + CRASH("Target buffer cannot be size zero"); + } + + if (buffer == src) [[unlikely]] { + CRASH("Cannot copy string to same buffer"); + } + + const auto dstSize = strnlen(buffer, bufSize); + const auto srcSize = strlen(src); + if (dstSize + srcSize + 1 > bufSize) [[unlikely]] { + CRASH("Destination buffer too small!"); + } + + memcpy(buffer + dstSize, src, srcSize); + buffer[dstSize + srcSize] = 0; +} + +int SafeStringVPrintf(char* buffer, size_t bufSize, const char* src, std::va_list args) { + if (bufSize == 0) [[unlikely]] { + CRASH("Target buffer cannot be size zero"); + } + + return vsnprintf(buffer, bufSize, src, args); +} + +} // namespace dusk \ No newline at end of file diff --git a/src/dusk/ui/editor.cpp b/src/dusk/ui/editor.cpp index 196c9bf719..8aa3c3894d 100644 --- a/src/dusk/ui/editor.cpp +++ b/src/dusk/ui/editor.cpp @@ -196,7 +196,7 @@ Rml::String get_player_name() { if (!has_save_data()) { return ""; } - return dComIfGs_getPlayerName(); + return dComIfGs_getPlayerName().buffer; } void set_player_name(Rml::String name) { @@ -207,7 +207,7 @@ Rml::String get_horse_name() { if (!has_save_data()) { return ""; } - return dComIfGs_getHorseName(); + return dComIfGs_getHorseName().buffer; } void set_horse_name(Rml::String name) { diff --git a/src/f_ap/f_ap_game.cpp b/src/f_ap/f_ap_game.cpp index 72219e98dd..0ee76c325b 100644 --- a/src/f_ap/f_ap_game.cpp +++ b/src/f_ap/f_ap_game.cpp @@ -203,7 +203,7 @@ char fapGm_dataMem::mCsv[0x8000]; int dumpTagObject(void* i_object, void*) { char profname_str[64]; s16 profname = fopAcM_GetProfName(i_object); - sprintf(profname_str, "%d", profname); + SAFE_SPRINTF(profname_str, "%d", profname); if (fopAcM_IsActor(i_object)) { fopAc_ac_c* a_actor = (fopAc_ac_c*)i_object; diff --git a/src/f_op/f_op_actor.cpp b/src/f_op/f_op_actor.cpp index d2240327b0..d0c89605d3 100644 --- a/src/f_op/f_op_actor.cpp +++ b/src/f_op/f_op_actor.cpp @@ -275,7 +275,7 @@ static int fopAc_Draw(void* i_this) { char message[40]; char name[dStage_NAME_LENGTH]; fopAcM_getNameString(actor, name); - sprintf(message, "%s(描画処理)", name); + SAFE_SPRINTF(message, "%s(描画処理)", name); fapGm_HIO_c::stopCpuTimer(message); #endif @@ -368,7 +368,7 @@ static int fopAc_Execute(void* i_this) { char message[40]; char name[dStage_NAME_LENGTH]; fopAcM_getNameString(actor, name); - sprintf(message, "%s(計算処理)", name); + SAFE_SPRINTF(message, "%s(計算処理)", name); fapGm_HIO_c::stopCpuTimer(message); #endif diff --git a/src/f_op/f_op_actor_mng.cpp b/src/f_op/f_op_actor_mng.cpp index dca07f98ba..c9223a4d14 100644 --- a/src/f_op/f_op_actor_mng.cpp +++ b/src/f_op/f_op_actor_mng.cpp @@ -2289,7 +2289,7 @@ fopAc_ac_c* fopAcM_findObject4EventCB(fopAc_ac_c* i_actor, void* i_data) { fopAc_ac_c* fopAcM_searchFromName4Event(char const* i_name, s16 i_eventID) { fopAcM_search4ev_prm prm; prm.event_id = i_eventID; - strcpy(prm.name, i_name); + SAFE_STRCPY(prm.name, i_name); char* chr = std::strchr(prm.name, ':'); if (chr != NULL) { @@ -2436,8 +2436,8 @@ bool fopAcM_wt_c::waterCheck(cXyz const* i_pos) { return false; } -BOOL fopAcM_getNameString(const fopAc_ac_c* i_actor, char* o_name) { - strcpy(o_name, dStage_getName(fopAcM_GetProfName(i_actor), i_actor->argument)); +BOOL fopAcM_getNameString(const fopAc_ac_c* i_actor, TEXT_SPAN o_name) { + SAFE_STRCPY(o_name, dStage_getName(fopAcM_GetProfName(i_actor), i_actor->argument)); return TRUE; } diff --git a/src/f_op/f_op_msg_mng.cpp b/src/f_op/f_op_msg_mng.cpp index c0b6dd060a..a9ce8c78d0 100644 --- a/src/f_op/f_op_msg_mng.cpp +++ b/src/f_op/f_op_msg_mng.cpp @@ -226,7 +226,7 @@ fpc_ProcID fopMsgM_messageSetDemo(u32 i_msgidx) { return 0; } -char* fopMsgM_messageGet(char* i_stringBuf, u32 i_msgId) { +TEXT_SPAN fopMsgM_messageGet(TEXT_SPAN i_stringBuf, u32 i_msgId) { dMeter2Info_getString(i_msgId, i_stringBuf, NULL); return i_stringBuf; } diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 490c0b8980..387224beb3 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -145,7 +145,7 @@ s32 LOAD_COPYDATE(void*) { memcpy(buffer, readBuf, readLen); buffer[readLen] = '\0'; } else { - strcpy(buffer, "PC PORT BUILD"); + SAFE_STRCPY(buffer, "PC PORT BUILD"); DuskLog.warn("COPYDATE file not found at {}", COPYDATE_PATH); } From 8668474a33b02bd8607a4e9dda2b7d31421d10db Mon Sep 17 00:00:00 2001 From: qwertyquerty Date: Sun, 24 May 2026 11:18:07 -0700 Subject: [PATCH 30/47] Achievements improvements v2 (#1553) * make LJA achievement more attainable glitchlessly * update loach achievement description * 3 kill rollstab achievement * update gone fishin description * gorge skip achievement * early city achievement * make goats and snowboarding safety check stage * fix indomitable requirement, add hero mode achievement * properly check skybook completion when returned * prototype ganondorf achievement * Autospin Annihilation --- include/d/actor/d_a_alink.h | 1 + include/dusk/achievements.h | 5 +- src/d/actor/d_a_alink_cut.inc | 4 + src/d/actor/d_a_b_gnd.cpp | 4 + src/d/d_cc_uty.cpp | 5 + src/dusk/achievements.cpp | 181 ++++++++++++++++++++++++++++++++-- 6 files changed, 190 insertions(+), 10 deletions(-) diff --git a/include/d/actor/d_a_alink.h b/include/d/actor/d_a_alink.h index 107afc1ed3..8f6a40ae13 100644 --- a/include/d/actor/d_a_alink.h +++ b/include/d/actor/d_a_alink.h @@ -4564,6 +4564,7 @@ public: cXyz mIBChainInterpCurrHandRoot; bool mIBChainInterpPrevValid; bool mIBChainInterpCurrValid; + bool mIsRollstab = false; #endif }; // Size: 0x385C diff --git a/include/dusk/achievements.h b/include/dusk/achievements.h index 5c2758b6b7..c93ea5d626 100644 --- a/include/dusk/achievements.h +++ b/include/dusk/achievements.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include "nlohmann/json.hpp" @@ -47,6 +47,7 @@ public: // Signals are visible to all achievement checks within the same tick, then cleared. void signal(const char* key); bool hasSignal(const char* key) const; + int signalCount(const char* key) const; std::vector getAchievements() const; @@ -62,7 +63,7 @@ private: void processEntry(Entry& e); std::vector m_entries; - std::unordered_set m_signals; + std::unordered_map m_signals; bool m_loaded = false; bool m_dirty = false; }; diff --git a/src/d/actor/d_a_alink_cut.inc b/src/d/actor/d_a_alink_cut.inc index f33d2935c5..ba3c0ba89e 100644 --- a/src/d/actor/d_a_alink_cut.inc +++ b/src/d/actor/d_a_alink_cut.inc @@ -1165,6 +1165,10 @@ int daAlink_c::procCutFinishInit(int i_type) { const daAlink_cutParamTbl* cutParams = &cutParamTable[i_type]; BOOL is_proc_frontRoll = mProcID == PROC_FRONT_ROLL; +#if TARGET_PC + mIsRollstab = (is_proc_frontRoll != FALSE) && (i_type == CUT_FINISH_PARAM_STAB); +#endif + commonProcInit(PROC_CUT_FINISH); setCutType(cutParams->m_cutType); field_0x3198 = cutParams->m_recoilAnmID; diff --git a/src/d/actor/d_a_b_gnd.cpp b/src/d/actor/d_a_b_gnd.cpp index d96e6314af..0f982e59e8 100644 --- a/src/d/actor/d_a_b_gnd.cpp +++ b/src/d/actor/d_a_b_gnd.cpp @@ -2192,6 +2192,9 @@ static void damage_check(b_gnd_class* i_this) { i_this->mDamageInvulnerabilityTimer = 100; } } + #if TARGET_PC + dusk::AchievementSystem::get().signal("ganondorf_hit"); + #endif } cXyz hitmark_size(1.0f, 1.0f, 1.0f); @@ -2218,6 +2221,7 @@ static void damage_check(b_gnd_class* i_this) { i_this->field_0xc7c = 0; dScnPly_c::setPauseTimer(7); a_this->health = 100; + dusk::AchievementSystem::get().signal("ganondorf_knocked_down"); } break; } diff --git a/src/d/d_cc_uty.cpp b/src/d/d_cc_uty.cpp index 218cb362f4..91fc6677c1 100644 --- a/src/d/d_cc_uty.cpp +++ b/src/d/d_cc_uty.cpp @@ -16,6 +16,7 @@ #if TARGET_PC #include "dusk/achievements.h" #include "dusk/settings.h" +#include "d/actor/d_a_alink.h" #endif static int plCutLRC[58] = { @@ -448,6 +449,10 @@ fopAc_ac_c* cc_at_check(fopAc_ac_c* i_enemy, dCcU_AtInfo* i_AtInfo) { #if TARGET_PC if (fopAcM_GetGroup(i_enemy) == fopAc_ENEMY_e) { dusk::AchievementSystem::get().signal("enemy_killed"); + const auto* link = static_cast(daPy_getPlayerActorClass()); + if (link != nullptr && link->mProcID == daAlink_c::PROC_CUT_FINISH && link->mIsRollstab) { + dusk::AchievementSystem::get().signal("rollstab_kill"); + } } #endif } diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 5b76d7e8e8..d06ed404cc 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -11,6 +11,7 @@ #include "d/actor/d_a_alink.h" #include "d/actor/d_a_ni.h" #include "d/actor/d_a_npc4.h" +#include "d/actor/d_a_b_gnd.h" #include "d/actor/d_a_b_ob.h" #include "d/actor/d_a_player.h" #include "d/d_demo.h" @@ -18,6 +19,7 @@ #include "f_pc/f_pc_name.h" #include "f_op/f_op_actor_mng.h" #include "f_pc/f_pc_name.h" +#include "dusk/logging.h" #include #include @@ -35,6 +37,9 @@ static void* s_cucco_play_search(void* i_actor, void*) { } static void checkGoatHerding(Achievement& a, int32_t threshMs) { + if (strcmp(dComIfGp_getStartStageName(), "F_SP00") != 0) { + return; + } if (dMeter2Info_getMaxCount() != 20 || dMeter2Info_getNowCount() != 20) { return; } @@ -65,6 +70,25 @@ std::vector AchievementSystem::makeEntries() { }, {} }, + { + { + "three_heart_clear", + "Hero Mode", + "Defeat Ganondorf with only 3 heart containers.", + AchievementCategory::Challenge, + false, 0, 0, false + }, + [](Achievement& a, json&) { + const auto* link = static_cast(daPy_getPlayerActorClass()); + if (link == nullptr || link->mProcID != daAlink_c::PROC_GANON_FINISH) { + return; + } + if (dComIfGs_getMaxLife() < 20) { + a.progress = 1; + } + }, + {} + }, { { "completionist", @@ -201,7 +225,7 @@ std::vector AchievementSystem::makeEntries() { hasAncientDoc = true; } } - if (!hasJewelRod || !hasAncientDoc) { + if (!hasJewelRod || (!hasAncientDoc && !dComIfGs_isEventBit(dSv_event_flag_c::F_0302))) { return; } @@ -265,7 +289,7 @@ std::vector AchievementSystem::makeEntries() { { "hylian_loach", "Legendary Catch", - "Catch a Hylian Loach.", + "Obtain the Hylian Loach in your fishing journal.", AchievementCategory::Collection, false, 0, 0, false }, @@ -280,7 +304,7 @@ std::vector AchievementSystem::makeEntries() { { "all_fish", "Gone Fishin'", - "Catch all 6 species of fish.", + "Obtain all 6 species of fish in your fishing journal.", AchievementCategory::Collection, true, 6, 0, false }, @@ -392,7 +416,7 @@ std::vector AchievementSystem::makeEntries() { false, 0, 0, false }, [](Achievement& a, json&) { - if (daNpcF_chkEvtBit(0x1F9) && dComIfGs_getMaxLife() <= 15) { + if (daNpcF_chkEvtBit(0x1F9) && dComIfGs_getMaxLife() < 20) { a.progress = 1; } }, @@ -506,6 +530,29 @@ std::vector AchievementSystem::makeEntries() { }, {} }, + { + { + "rollstab_triple", + "Surgical Skewer", + "Kill 3 enemies with a single rollstab.", + AchievementCategory::Misc, + false, 0, 0, false + }, + [](Achievement& a, json&) { + static int rollstabKills = 0; + const auto* link = static_cast(daPy_getPlayerActorClass()); + const bool inRollstab = link != nullptr && link->mProcID == daAlink_c::PROC_CUT_FINISH && link->mIsRollstab; + if (!inRollstab) { + rollstabKills = 0; + return; + } + rollstabKills += AchievementSystem::get().signalCount("rollstab_kill"); + if (rollstabKills >= 3) { + a.progress = 1; + } + }, + {} + }, // Minigame { { @@ -600,6 +647,9 @@ std::vector AchievementSystem::makeEntries() { false, 0, 0, false }, [](Achievement& a, json&) { + if (strcmp(dComIfGp_getStartStageName(), "F_SP114") != 0) { + return; + } const int32_t bestMs = dComIfGs_getRaceGameTime(); if (dComIfGs_isEventBit(dSv_event_flag_c::F_0481) && bestMs > 0 && bestMs <= 70000) { @@ -681,7 +731,7 @@ std::vector AchievementSystem::makeEntries() { { "long_jump_attack", "Long Jump Attack", - "Travel more than 20 meters in a single jump attack before landing.", + "Travel more than 15 meters in a single jump attack before landing.", AchievementCategory::Misc, false, 0, 0, false }, @@ -711,7 +761,7 @@ std::vector AchievementSystem::makeEntries() { inJump = false; const float dx = link->current.pos.x - startX; const float dz = link->current.pos.z - startZ; - if (dx * dx + dz * dz >= 2000.0f * 2000.0f) { + if (dx * dx + dz * dz >= 1500.0f * 1500.0f) { a.progress = 1; } } else if (link->mProcID != daAlink_c::PROC_CUT_JUMP) { @@ -800,6 +850,66 @@ std::vector AchievementSystem::makeEntries() { }, {} }, + { + { + "ganondorf_3hit", + "Autospin Annihilation", + "Finish off Ganondorf in the final duel after only 3 attacks.", + AchievementCategory::Misc, + false, 0, 0, false + }, + [](Achievement& a, json&) { + auto& sys = AchievementSystem::get(); + const auto* link = static_cast(daPy_getPlayerActorClass()); + + static int autospinCount = 0; + static int pendingHits = 0; + static bool invalidated = false; + static bool wasInFight = false; + + auto* gnd = static_cast(fopAcM_SearchByName(fpcNm_B_GND_e)); + const bool inFight = gnd != nullptr && !gnd->checkRide(); + + if (inFight && !wasInFight) { + autospinCount = 0; + pendingHits = 0; + invalidated = false; + } + wasInFight = inFight; + + if (!inFight) { + return; + } + + const bool hitOccurred = sys.hasSignal("ganondorf_hit"); + const bool knockedDown = sys.hasSignal("ganondorf_knocked_down"); + + if (hitOccurred && knockedDown) { + // Spin completing an autospin: pendingHits should be exactly 1 (the spin attack) + if (pendingHits == 1) { + autospinCount++; + pendingHits = 0; + } else { + invalidated = true; + } + } else if (hitOccurred) { + pendingHits++; + if (pendingHits > 1) { + invalidated = true; + } + } + + if (link != nullptr && link->mProcID == daAlink_c::PROC_GANON_FINISH) { + if (!invalidated && autospinCount == 3) { + a.progress = 1; + } + autospinCount = 0; + pendingHits = 0; + invalidated = false; + } + }, + {} + }, // Glitched { { @@ -1012,6 +1122,55 @@ std::vector AchievementSystem::makeEntries() { a.progress = 1; }, {} + }, + { + { + "early_city", + "Early City", + "Obtain the Double Clawshots without obtaining the Dominion Rod.", + AchievementCategory::Glitched, + false, 0, 0, false + }, + [](Achievement& a, json&) { + if (daPy_getPlayerActorClass() == nullptr) { + return; + } + bool hasDoubleClawshot = false; + bool hasDominionRod = false; + for (int i = 0; i < 24; ++i) { + const auto item = dComIfGs_getItem(i, false); + if (item == dItemNo_W_HOOKSHOT_e) { + hasDoubleClawshot = true; + } + if (item == dItemNo_COPY_ROD_e || item == dItemNo_COPY_ROD_2_e) { + hasDominionRod = true; + } + } + if (hasDoubleClawshot && !hasDominionRod) { + a.progress = 1; + } + }, + {} + }, + { + { + "early_kakariko", + "Gorge Skip", + "Collect the Kakariko warp portal without warping the gorge bridge.", + AchievementCategory::Glitched, + false, 0, 0, false + }, + [](Achievement& a, json&) { + if (dComIfGs_isEventBit(dSv_event_flag_c::M_018)) { + return; + } + const bool savedPortal = g_dComIfG_gameInfo.info.getSavedata().getSave(dStage_SaveTbl_ELDIN).getBit().isSwitch(31); + const bool livePortal = dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo()) == dStage_SaveTbl_ELDIN && dComIfGs_isSaveSwitch(31); + if (savedPortal || livePortal) { + a.progress = 1; + } + }, + {} } }; } @@ -1088,11 +1247,17 @@ void AchievementSystem::clearAll() { } void AchievementSystem::signal(const char* key) { - m_signals.insert(key); + m_signals[key]++; } bool AchievementSystem::hasSignal(const char* key) const { - return m_signals.count(key) > 0; + const auto it = m_signals.find(key); + return it != m_signals.end() && it->second > 0; +} + +int AchievementSystem::signalCount(const char* key) const { + const auto it = m_signals.find(key); + return it != m_signals.end() ? it->second : 0; } void AchievementSystem::clearOne(const char* key) { From 78e1a05aef41d1ba264e14d4fb520fd3e0a52f37 Mon Sep 17 00:00:00 2001 From: Irastris Date: Sun, 24 May 2026 14:31:04 -0400 Subject: [PATCH 31/47] Various mirror mode fixes (#1725) * Fix mirror mode stage map scrolling * Fix Link's arrow rotation on fullscreen map * Fix fishing in mirror mode * Fix cucco controls in mirror mode * Only stick_x is necessary --------- Co-authored-by: TakaRikka <38417346+TakaRikka@users.noreply.github.com> Co-authored-by: Luke Street --- src/d/actor/d_a_mg_rod.cpp | 43 +++++++++++++++++++++++++++++++------ src/d/actor/d_a_ni.cpp | 5 +++++ src/d/d_menu_fmap2D.cpp | 5 +++++ src/d/d_menu_map_common.cpp | 13 +++++++++++ 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/d/actor/d_a_mg_rod.cpp b/src/d/actor/d_a_mg_rod.cpp index 956a9e3cff..3471cd342a 100644 --- a/src/d/actor/d_a_mg_rod.cpp +++ b/src/d/actor/d_a_mg_rod.cpp @@ -25,7 +25,10 @@ #include #include +#if TARGET_PC +#include "dusk/settings.h" #include "dusk/version.hpp" +#endif class dmg_rod_HIO_c : public JORReflexible { public: @@ -1137,8 +1140,14 @@ static int lure_standby(dmg_rod_class* i_this) { dComIfGp_setDoStatusForce(42, 0); } - i_this->rod_stick_x = mDoCPd_c::getStickX3D(PAD_1) * mDoCPd_c::getStickX3D(PAD_1); - if (mDoCPd_c::getStickX3D(PAD_1) < 0.0f) { + f32 stick_x = mDoCPd_c::getStickX3D(PAD_1); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + stick_x = -stick_x; + } +#endif + i_this->rod_stick_x = stick_x * stick_x; + if (stick_x < 0.0f) { i_this->rod_stick_x *= -1.0f; } @@ -3671,7 +3680,13 @@ static void uki_standby(dmg_rod_class* i_this) { cLib_addCalc2(&i_this->field_0x150c, substickX, 0.5f, 0.2f); if (i_this->field_0x1508 > 0.3f && i_this->play_cam_mode < 5) { - ANGLE_ADD(i_this->field_0x1418, (-500.0f + VREG_F(3)) * mDoCPd_c::getStickX3D(PAD_1)); + f32 stick_x = mDoCPd_c::getStickX3D(PAD_1); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + stick_x = -stick_x; + } +#endif + ANGLE_ADD(i_this->field_0x1418, (-500.0f + VREG_F(3)) * stick_x); } cMtx_YrotS(*calc_mtx, i_this->field_0x1418); @@ -5043,8 +5058,15 @@ static void play_camera(dmg_rod_class* i_this) { static f32 old_stick_x = 0.0f; static f32 old_stick_sx = 0.0f; + f32 stick_x = mDoCPd_c::getStickX3D(PAD_1); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + stick_x = -stick_x; + } +#endif + if ( - (mDoCPd_c::getStickX3D(PAD_1) >= 0.8f && old_stick_x < 0.8f) || (mDoCPd_c::getStickX3D(PAD_1) <= -0.8f && old_stick_x > -0.8f) + (stick_x >= 0.8f && old_stick_x < 0.8f) || (stick_x <= -0.8f && old_stick_x > -0.8f) #if VERSION != VERSION_SHIELD_DEBUG || (mDoCPd_c::getSubStickX3D(PAD_1) >= 0.8f && old_stick_sx < 0.8f) || (mDoCPd_c::getSubStickX3D(PAD_1) <= -0.8f && old_stick_sx > -0.8f) #endif @@ -5060,7 +5082,7 @@ static void play_camera(dmg_rod_class* i_this) { } if (i_this->play_cam_timer >= 15) { - if (mDoCPd_c::getStickX3D(PAD_1) >= 0.5f + if (stick_x >= 0.5f #if VERSION != VERSION_SHIELD_DEBUG || mDoCPd_c::getSubStickX3D(PAD_1) >= 0.5f #endif @@ -5082,8 +5104,8 @@ static void play_camera(dmg_rod_class* i_this) { } } - old_stick_x = mDoCPd_c::getStickX3D(PAD_1); - old_stick_sx = mDoCPd_c::getSubStickX(PAD_1); + old_stick_x = stick_x; + old_stick_sx = mDoCPd_c::getSubStickX3D(PAD_1); if (i_this->play_cam_timer == 1) { if (i_this->field_0xf81 == 0) { @@ -5788,7 +5810,14 @@ static int dmg_rod_Execute(dmg_rod_class* i_this) { i_this->rod_stick_x = mDoCPd_c::getStickX3D(PAD_1); i_this->rod_stick_y = mDoCPd_c::getStickY(PAD_1); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + i_this->rod_stick_x = -i_this->rod_stick_x; + } + i_this->rod_substick_x = mDoCPd_c::getSubStickX3D(PAD_1); +#else i_this->rod_substick_x = mDoCPd_c::getSubStickX(PAD_1); +#endif i_this->prev_rod_substick_y = i_this->rod_substick_y; i_this->rod_substick_y = mDoCPd_c::getSubStickY(PAD_1); diff --git a/src/d/actor/d_a_ni.cpp b/src/d/actor/d_a_ni.cpp index 8d97925078..b569b28e04 100644 --- a/src/d/actor/d_a_ni.cpp +++ b/src/d/actor/d_a_ni.cpp @@ -943,6 +943,11 @@ static int ni_play(ni_class* i_this) { s16 var_r28 = 0x4000; i_this->mPadMainStickX = mDoCPd_c::getStickX3D(PAD_1); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + i_this->mPadMainStickX = -i_this->mPadMainStickX; + } +#endif i_this->mPadMainStickY = mDoCPd_c::getStickY(PAD_1); i_this->mPadSubStickY = mDoCPd_c::getSubStickY(PAD_1); i_this->mPadSubStickX = mDoCPd_c::getSubStickX(PAD_1); diff --git a/src/d/d_menu_fmap2D.cpp b/src/d/d_menu_fmap2D.cpp index 0d3114172e..cbfffc3fc2 100644 --- a/src/d/d_menu_fmap2D.cpp +++ b/src/d/d_menu_fmap2D.cpp @@ -1973,6 +1973,11 @@ void dMenu_Fmap2DBack_c::stageMapMove(STControl* i_stick, u8 param_1, bool param if (stick_value >= slow_bound && param_2 && field_0x1238 != 2) { bVar6 = true; s16 angle = i_stick->getAngleStick(); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + angle = -angle; + } +#endif f32 local_68 = mTexMaxX - mTexMinX; f32 spot_zoom = getSpotMapZoomRate(); f32 region_zoom = getRegionMapZoomRate(mRegionCursor); diff --git a/src/d/d_menu_map_common.cpp b/src/d/d_menu_map_common.cpp index 4168673704..5ae820931a 100644 --- a/src/d/d_menu_map_common.cpp +++ b/src/d/d_menu_map_common.cpp @@ -394,8 +394,21 @@ void dMenuMapCommon_c::drawIcon(f32 i_posX, f32 i_posY, f32 param_3, f32 param_4 icon_size_y *= _c7c; } +#if TARGET_PC + f32 rotation = mIconInfo[info_idx].rotation; + if (dusk::getSettings().game.enableMirrorMode && + (mIconInfo[info_idx].icon_no == ICON_LINK_e || + mIconInfo[info_idx].icon_no == ICON_LINK_ENTER_e)) + { + rotation = -rotation; + } + + mPictures[mIconInfo[info_idx].icon_no]->rotate(icon_size_x / 2, icon_size_y / 2, ROTATE_Z, + rotation); +#else mPictures[mIconInfo[info_idx].icon_no]->rotate(icon_size_x / 2, icon_size_y / 2, ROTATE_Z, mIconInfo[info_idx].rotation); +#endif if (mIconInfo[info_idx].icon_no == ICON_LIGHT_DROP_e) { mPictures[mIconInfo[info_idx].icon_no]->setAlpha((180.0f * _c80) / 255.0f); From d73b0be801ef639829458322f9632faefada05b6 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 12:57:06 -0600 Subject: [PATCH 32/47] Update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index 3b06e240a3..8f1ea3e126 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 3b06e240a34b15630562ffa4bb00201b91ce2a39 +Subproject commit 8f1ea3e1266ca6f2aa9c4009a328bfc270a01b89 From c207150ae9cb9b9ef34f88a4903b5a9f31a9810d Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 13:00:41 -0600 Subject: [PATCH 33/47] Fix achievement logic startup crash --- src/dusk/achievements.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index d06ed404cc..6c4de43487 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -1161,7 +1161,7 @@ std::vector AchievementSystem::makeEntries() { false, 0, 0, false }, [](Achievement& a, json&) { - if (dComIfGs_isEventBit(dSv_event_flag_c::M_018)) { + if (dComIfGs_isEventBit(dSv_event_flag_c::M_018) || dComIfGp_getStageStagInfo() == nullptr) { return; } const bool savedPortal = g_dComIfG_gameInfo.info.getSavedata().getSave(dStage_SaveTbl_ELDIN).getBit().isSwitch(31); From 8905fbc1ebf7872246408e456807811760aa44f9 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 17:43:39 -0600 Subject: [PATCH 34/47] Downgrade SDL3 for Android --- CMakePresets.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakePresets.json b/CMakePresets.json index 1c7f989b0c..748c4df396 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -326,7 +326,9 @@ "BUILD_SHARED_LIBS": { "type": "BOOL", "value": false - } + }, + "AURORA_SDL3_VERSION": "3.4.8", + "AURORA_SDL3_REF": "refs/tags/release-3.4.8" } }, { From 1f1f7e324b6776618e2ab1ce3fb0cb617303f34b Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 17:43:51 -0600 Subject: [PATCH 35/47] Use absl::flat_hash_map in frame_interpolation --- src/dusk/frame_interpolation.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dusk/frame_interpolation.cpp b/src/dusk/frame_interpolation.cpp index b2ef0309e3..be03d51e96 100644 --- a/src/dusk/frame_interpolation.cpp +++ b/src/dusk/frame_interpolation.cpp @@ -1,14 +1,15 @@ #include "dusk/frame_interpolation.h" -#include -#include "mtx.h" #include "f_op/f_op_camera_mng.h" #include "m_Do/m_Do_graphic.h" +#include "mtx.h" + +#include namespace { struct Recording { - std::unordered_map matrix_values; + absl::flat_hash_map matrix_values; }; bool s_initialized = false; @@ -26,7 +27,7 @@ uint64_t g_sim_tick_seq = 0; Recording g_current_recording; Recording g_previous_recording; -std::unordered_map g_replacements; +absl::flat_hash_map g_replacements; struct CameraSnapshot { cXyz eye{}; From fa074a23115636fdb5b52ad086e0b0a3c3a02880 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 17:44:05 -0600 Subject: [PATCH 36/47] Disable STUB_LOG in release --- include/dusk/logging.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/dusk/logging.h b/include/dusk/logging.h index 9b31b96bf2..938e494662 100644 --- a/include/dusk/logging.h +++ b/include/dusk/logging.h @@ -19,7 +19,11 @@ extern bool StubLogEnabled; extern aurora::Module DuskLog; +#ifndef NDEBUG #define STUB_LOG() DuskLog.debug("{} is a stub", __FUNCTION__) +#else +#define STUB_LOG() +#endif #if TARGET_PC #define STUB_RET(...) \ From a8a2f5c84ccdbe5cc12fd97db4e258b073b0f4a1 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 17:44:43 -0600 Subject: [PATCH 37/47] Disable OSReport in release builds --- src/m_Do/m_Do_machine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_Do/m_Do_machine.cpp b/src/m_Do/m_Do_machine.cpp index a8f4f6140f..ae22c5dbe2 100644 --- a/src/m_Do/m_Do_machine.cpp +++ b/src/m_Do/m_Do_machine.cpp @@ -754,7 +754,7 @@ void myGXVerifyCallback(GXWarningLevel param_1, u32 param_2, const char* param_3 #endif int mDoMch_Create() { -#if !TARGET_PC // We want crash logs. +#ifdef NDEBUG if (mDoMain::developmentMode == 0 || !(OSGetConsoleType() & 0x10000000)) { OSReportDisable(); } From 8aaf451708073c5d69c0f70a2deb9dcf0adf18d7 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 17:44:54 -0600 Subject: [PATCH 38/47] Update Android SDLActivity --- .../app/src/main/java/org/libsdl/app/SDLActivity.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platforms/android/app/src/main/java/org/libsdl/app/SDLActivity.java b/platforms/android/app/src/main/java/org/libsdl/app/SDLActivity.java index 42f5a911f7..5c54cde863 100644 --- a/platforms/android/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/platforms/android/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -61,7 +61,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh private static final String TAG = "SDL"; private static final int SDL_MAJOR_VERSION = 3; private static final int SDL_MINOR_VERSION = 4; - private static final int SDL_MICRO_VERSION = 4; + private static final int SDL_MICRO_VERSION = 8; /* // Display InputType.SOURCE/CLASS of events and devices // @@ -570,7 +570,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static int getNaturalOrientation() { int result = SDL_ORIENTATION_UNKNOWN; - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity != null) { Configuration config = activity.getResources().getConfiguration(); Display display = activity.getWindowManager().getDefaultDisplay(); @@ -590,7 +590,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static int getCurrentRotation() { int result = 0; - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity != null) { Display display = activity.getWindowManager().getDefaultDisplay(); switch (display.getRotation()) { @@ -1292,7 +1292,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static double getDiagonal() { DisplayMetrics metrics = new DisplayMetrics(); - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity == null) { return 0.0; } @@ -1940,7 +1940,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh return; } - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { activity.requestPermissions(new String[]{permission}, requestCode); } else { From 1f970eb2dc76c44f938860b89e0b1968f3ad9112 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 17:45:04 -0600 Subject: [PATCH 39/47] More ZoneScoped --- libs/JSystem/src/JParticle/JPABaseShape.cpp | 14 ++++++++++++++ libs/JSystem/src/JParticle/JPAExTexShape.cpp | 2 ++ 2 files changed, 16 insertions(+) diff --git a/libs/JSystem/src/JParticle/JPABaseShape.cpp b/libs/JSystem/src/JParticle/JPABaseShape.cpp index ff346984cd..add61135fe 100644 --- a/libs/JSystem/src/JParticle/JPABaseShape.cpp +++ b/libs/JSystem/src/JParticle/JPABaseShape.cpp @@ -76,6 +76,7 @@ void JPARegistAlpha(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { } void JPARegistPrmAlpha(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { + ZoneScoped; JPABaseEmitter* emtr = work->mpEmtr; GXColor prm = ptcl->mPrmClr; prm.r = COLOR_MULTI(prm.r, emtr->mGlobalPrmClr.r); @@ -87,6 +88,7 @@ void JPARegistPrmAlpha(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { } void JPARegistPrmAlphaEnv(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { + ZoneScoped; JPABaseEmitter* emtr = work->mpEmtr; GXColor prm = ptcl->mPrmClr; GXColor env = ptcl->mEnvClr; @@ -225,6 +227,7 @@ void JPAGenTexCrdMtxPrj(JPAEmitterWorkData* param_0) { } void JPAGenCalcTexCrdMtxAnm(JPAEmitterWorkData* work) { + ZoneScoped; JPABaseShape* shape = work->mpRes->getBsp(); f32 dVar16 = work->mpEmtr->mTick; f32 dVar15 = 0.5f * (1.0f + shape->getTilingS()); @@ -256,6 +259,7 @@ void JPAGenCalcTexCrdMtxAnm(JPAEmitterWorkData* work) { } void JPALoadCalcTexCrdMtxAnm(JPAEmitterWorkData* work, JPABaseParticle* param_1) { + ZoneScoped; JPABaseShape* shape = work->mpRes->getBsp(); f32 dVar16 = param_1->mAge; f32 dVar15 = 0.5f * (1.0f + shape->getTilingS()); @@ -286,14 +290,17 @@ void JPALoadCalcTexCrdMtxAnm(JPAEmitterWorkData* work, JPABaseParticle* param_1) } void JPALoadTex(JPAEmitterWorkData* work) { + ZoneScoped; work->mpResMgr->load(work->mpRes->getTexIdx(work->mpRes->getBsp()->getTexIdx()), GX_TEXMAP0); } void JPALoadTexAnm(JPAEmitterWorkData* work) { + ZoneScoped; work->mpResMgr->load(work->mpRes->getTexIdx(work->mpEmtr->mTexAnmIdx), GX_TEXMAP0); } void JPALoadTexAnm(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { + ZoneScoped; work->mpResMgr->load(work->mpRes->getTexIdx(ptcl->mTexAnmIdx), GX_TEXMAP0); } @@ -446,6 +453,7 @@ void JPADrawBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { return; } + ZoneScoped; JGeometry::TVec3 pos; #if TARGET_PC Mtx ptclPosMtx; @@ -475,6 +483,7 @@ void JPADrawRotBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { return; } + ZoneScoped; if (work->mpRes->getUsrIdx() == 0x89d7) { int a = 0; } @@ -518,6 +527,7 @@ void JPADrawYBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) { return; } + ZoneScoped; JGeometry::TVec3 local_48; MTXMultVec(work->mPosCamMtx, ¶m_1->mPosition, &local_48); Mtx local_38; @@ -542,6 +552,7 @@ void JPADrawRotYBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) { return; } + ZoneScoped; JGeometry::TVec3 local_48; MTXMultVec(work->mPosCamMtx, ¶m_1->mPosition, &local_48); f32 sinRot = JMASSin(param_1->mRotateAngle); @@ -1268,6 +1279,8 @@ void JPADrawStripeX(JPAEmitterWorkData* param_0) { } void JPADrawEmitterCallBackB(JPAEmitterWorkData* work) { + ZoneScoped; + JPABaseEmitter* emtr = work->mpEmtr; if (emtr->mpEmtrCallBack == NULL) { return; @@ -1282,6 +1295,7 @@ void JPADrawParticleCallBack(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { return; } + ZoneScoped; emtr->mpPtclCallBack->draw(emtr, ptcl); } diff --git a/libs/JSystem/src/JParticle/JPAExTexShape.cpp b/libs/JSystem/src/JParticle/JPAExTexShape.cpp index bb9510050e..cba10eb119 100644 --- a/libs/JSystem/src/JParticle/JPAExTexShape.cpp +++ b/libs/JSystem/src/JParticle/JPAExTexShape.cpp @@ -6,6 +6,8 @@ #include void JPALoadExTex(JPAEmitterWorkData* work) { + ZoneScoped; + JPAExTexShape* ets = work->mpRes->getEts(); GXTexCoordID secTexCoordID = GX_TEXCOORD1; From 326ef70afa114139d6ae5214c9be348bf40d1655 Mon Sep 17 00:00:00 2001 From: Ash Date: Mon, 25 May 2026 06:08:37 +0200 Subject: [PATCH 40/47] feat: more info in player info window (#1641) * wip: more info in player info window * wip: 3d speed + vel vec + ui change * wip: add epona velocity vec * wip: remove dead code + add format indexing --- src/dusk/imgui/ImGuiMenuTools.cpp | 81 ++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index 3045e09df1..02326eceea 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -208,6 +208,27 @@ namespace dusk { daAlink_c* player = (daAlink_c*)dComIfGp_getPlayer(0); daHorse_c* horse = dComIfGp_getHorseActor(); + double speedXzy = 0.0; + if (player != nullptr) { + speedXzy = sqrtf(player->speed.x * player->speed.x + + player->speed.z * player->speed.z + + player->speed.y * player->speed.y); + } + + ImGui::Text("Global"); + ImGuiStringViewText( + player != nullptr + ? fmt::format("Stage: {}\n", dComIfGp_getStartStageName()) + : "Stage: ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Layer: {0}\n", dComIfG_play_c::getLayerNo(0)) + : "Layer: ?\n" + ); + + ImGui::Separator(); ImGui::Text("Link"); ImGuiStringViewText( player != nullptr @@ -217,14 +238,38 @@ namespace dusk { ImGuiStringViewText( player != nullptr - ? fmt::format("Angle: {0}\n", player->shape_angle.y) - : "Angle: ?\n" + ? fmt::format("Velocity (XYZ): {: .4f}, {: .4f}, {: .4f}\n", player->speed.x, player->speed.y, player->speed.z) + : "Velocity (XYZ): ?, ?, ?\n" ); ImGuiStringViewText( player != nullptr - ? fmt::format("Speed: {: .4f}\n", player->speedF) - : "Speed: ?\n" + ? fmt::format("Speed (SpeedF): {: .4f}\n", player->speedF) + : "Speed (SpeedF): ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Speed (3D): {: .4f}\n", speedXzy) + : "Speed (3D): ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Angle: {0}\n", player->shape_angle.y) + : "Angle: ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Room: {0}\n", fopAcM_GetRoomNo(player)) + : "Room: ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Entry: {0}\n", dComIfGp_getStartStagePoint()) + : "Entry: ?\n" ); ImGui::Separator(); @@ -235,6 +280,18 @@ namespace dusk { : "Position: ?, ?, ?\n" ); + ImGuiStringViewText( + horse != nullptr + ? fmt::format("Velocity (XYZ): {: .4f}, {: .4f}, {: .4f}\n", horse->speed.x, horse->speed.y, horse->speed.z) + : "Velocity (XYZ): ?, ?, ?\n" + ); + + ImGuiStringViewText( + horse != nullptr + ? fmt::format("Speed (SpeedF): {: .4f}\n", horse->speedF) + : "Speed (SpeedF): ?\n" + ); + ImGuiStringViewText( horse != nullptr ? fmt::format("Angle: {0}\n", horse->shape_angle.y) @@ -243,8 +300,20 @@ namespace dusk { ImGuiStringViewText( horse != nullptr - ? fmt::format("Speed: {: .4f}\n", horse->speedF) - : "Speed: ?\n" + ? fmt::format("Room: {0}\n", fopAcM_GetRoomNo(horse)) + : "Room: ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Saved Stage: {}\n", dComIfGs_getHorseRestartStageName()) + : "Saved Stage: ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Saved Room: {0}\n", dComIfGs_getHorseRestartRoomNo()) + : "Saved Room: ?\n" ); ShowCornerContextMenu(m_playerInfoOverlayCorner, m_debugOverlayCorner); From 2a3bc722d92b1a06e126df0d8a72614f573b5acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFs?= <49660929+SailorSnoW@users.noreply.github.com> Date: Mon, 25 May 2026 06:11:45 +0200 Subject: [PATCH 41/47] Add in-process crash handler with symbolizable backtrace (#1536) --- CMakeLists.txt | 8 + files.cmake | 1 + include/dusk/crash_handler.h | 7 + include/dusk/logging.h | 1 + src/dusk/crash_handler.cpp | 754 +++++++++++++++++++++++++++++++++++ src/dusk/logging.cpp | 13 + src/m_Do/m_Do_main.cpp | 2 + 7 files changed, 786 insertions(+) create mode 100644 include/dusk/crash_handler.h create mode 100644 src/dusk/crash_handler.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e93ba3cb22..696e8a74bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -355,6 +355,10 @@ endif () if (WIN32) list(APPEND GAME_LIBS Ws2_32) + if (CMAKE_BUILD_TYPE STREQUAL Debug) + list(APPEND GAME_LIBS dbghelp) + list(APPEND GAME_COMPILE_DEFS DUSK_CRASH_DBGHELP=1) + endif () endif () set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/no_backend.cpp) @@ -491,6 +495,10 @@ if (ANDROID) target_link_options(dusklight PRIVATE "-Wl,-u,SDL_main") endif () +if (CMAKE_SYSTEM_NAME STREQUAL Linux) + target_link_options(dusklight PRIVATE "-Wl,--build-id=sha1") +endif () + if (NOT APPLE) add_custom_command(TARGET dusklight POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory diff --git a/files.cmake b/files.cmake index e42a626ffe..ebfa843b24 100644 --- a/files.cmake +++ b/files.cmake @@ -1420,6 +1420,7 @@ set(DUSK_FILES src/d/actor/d_a_alink_dusk.cpp src/dusk/asserts.cpp src/dusk/config.cpp + src/dusk/crash_handler.cpp src/dusk/crash_reporting.cpp src/dusk/data.cpp src/dusk/data.hpp diff --git a/include/dusk/crash_handler.h b/include/dusk/crash_handler.h new file mode 100644 index 0000000000..c2cfadf5d1 --- /dev/null +++ b/include/dusk/crash_handler.h @@ -0,0 +1,7 @@ +#pragma once + +namespace dusk::crash_handler { + +void install(); + +} // namespace dusk::crash_handler diff --git a/include/dusk/logging.h b/include/dusk/logging.h index 938e494662..54350af4bf 100644 --- a/include/dusk/logging.h +++ b/include/dusk/logging.h @@ -12,6 +12,7 @@ namespace dusk { void InitializeFileLogging(const std::filesystem::path& configDir, AuroraLogLevel logLevel); void ShutdownFileLogging(); const char* GetLogFilePath(); + int GetLogFileDescriptor(); void SendToStubLog(AuroraLogLevel level, const char* module, const char* message); } diff --git a/src/dusk/crash_handler.cpp b/src/dusk/crash_handler.cpp new file mode 100644 index 0000000000..c79e2e16c8 --- /dev/null +++ b/src/dusk/crash_handler.cpp @@ -0,0 +1,754 @@ +#if !defined(_WIN32) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#include "dusk/crash_handler.h" + +#include "dusk/logging.h" +#include "version.h" + +#include +#include +#include +#include + +#if defined(_WIN32) + +#include + +#include + +#if defined(DUSK_CRASH_DBGHELP) +#include +#endif + +#else + +#include +#include +#include +#include +#include +#include + +#if defined(__APPLE__) +#include +#include +#else +#include +#include +#ifndef NT_GNU_BUILD_ID +#define NT_GNU_BUILD_ID 3 +#endif +#endif + +#endif + +#ifndef DUSK_ARCH +#define DUSK_ARCH "unknown" +#endif + +namespace dusk::crash_handler { +namespace { + +constexpr int kStderrFd = 2; +constexpr int kMaxFrames = 128; +constexpr char kHexDigits[] = "0123456789abcdef"; + +struct CrashContext { + uintptr_t moduleBase = 0; + char modulePath[1024] = {}; + uint8_t buildId[64] = {}; + unsigned buildIdLen = 0; + unsigned pdbAge = 0; +}; +CrashContext g_ctx; + +void rawWrite(int fd, const char* data, size_t len) { + if (fd < 0) { + return; + } +#if defined(_WIN32) + _write(fd, data, static_cast(len)); +#else + while (len > 0) { + const ssize_t written = ::write(fd, data, len); + if (written <= 0) { + return; + } + data += written; + len -= static_cast(written); + } +#endif +} + +void writeStr(int fd, const char* s) { + if (s != nullptr) { + rawWrite(fd, s, std::strlen(s)); + } +} + +void writeHex(int fd, unsigned long long value) { + char buf[2 + 16]; + size_t o = sizeof(buf); + do { + buf[--o] = kHexDigits[value & 0xF]; + value >>= 4; + } while (value != 0); + buf[--o] = 'x'; + buf[--o] = '0'; + rawWrite(fd, buf + o, sizeof(buf) - o); +} + +void writeDec(int fd, unsigned int value) { + char buf[10]; + size_t o = sizeof(buf); + do { + buf[--o] = static_cast('0' + value % 10); + value /= 10; + } while (value != 0); + rawWrite(fd, buf + o, sizeof(buf) - o); +} + +void writeHexBytes(int fd, const uint8_t* data, unsigned len) { + char buf[2]; + for (unsigned i = 0; i < len; ++i) { + buf[0] = kHexDigits[data[i] >> 4]; + buf[1] = kHexDigits[data[i] & 0xF]; + rawWrite(fd, buf, 2); + } +} + +const char* moduleName() { + const char* name = g_ctx.modulePath; + for (const char* p = g_ctx.modulePath; *p != '\0'; ++p) { + if (*p == '/' || *p == '\\') { + name = p + 1; + } + } + return name[0] != '\0' ? name : "(unknown)"; +} + +const char* symbolFor(uintptr_t pc, unsigned long long* disp) { +#if defined(_WIN32) && defined(DUSK_CRASH_DBGHELP) + alignas(SYMBOL_INFO) static char storage[sizeof(SYMBOL_INFO) + 512]; + auto* sym = reinterpret_cast(storage); + sym->SizeOfStruct = sizeof(SYMBOL_INFO); + sym->MaxNameLen = 511; + DWORD64 d = 0; + if (SymFromAddr(GetCurrentProcess(), pc, &d, sym)) { + *disp = d; + return sym->Name; + } + return nullptr; +#elif defined(_WIN32) + (void)pc; + (void)disp; + return nullptr; +#else + Dl_info info; + if (dladdr(reinterpret_cast(pc), &info) != 0 && info.dli_sname != nullptr) { + const auto base = reinterpret_cast(info.dli_saddr); + *disp = pc >= base ? pc - base : 0; + return info.dli_sname; + } + return nullptr; +#endif +} + +void emitFrame(int fd, int index, uintptr_t pc) { + writeStr(fd, "#"); + if (index < 10) { + writeStr(fd, "0"); + } + writeDec(fd, static_cast(index)); + writeStr(fd, " abs="); + writeHex(fd, pc); + writeStr(fd, " rva="); + writeHex(fd, pc >= g_ctx.moduleBase ? pc - g_ctx.moduleBase : 0ull); + writeStr(fd, " "); + writeStr(fd, moduleName()); + unsigned long long disp = 0; + const char* sym = symbolFor(pc, &disp); + if (sym != nullptr && sym[0] != '\0') { + writeStr(fd, " "); + writeStr(fd, sym); + writeStr(fd, "+"); + writeHex(fd, disp); + } + writeStr(fd, "\n"); +} + +void emitHeader(int fd, const char* reason, unsigned long long code, bool hasCode, + uintptr_t faultAddr, uintptr_t crashPc, bool crashPcKnown) { + writeStr(fd, "\n==================== DUSKLIGHT CRASHED ====================\n"); + writeStr(fd, "Build: " DUSK_WC_DESCRIBE " (" DUSK_WC_BRANCH ")\n"); + writeStr(fd, "Revision: " DUSK_WC_REVISION " Date: " DUSK_WC_DATE + " Type: " DUSK_BUILD_TYPE "\n"); + writeStr(fd, "Platform: " DUSK_PLATFORM_NAME " / " DUSK_ARCH "\n"); + writeStr(fd, "Module: "); + writeStr(fd, g_ctx.modulePath[0] != '\0' ? g_ctx.modulePath : "(unknown)"); + writeStr(fd, "\nModule base: "); + writeHex(fd, g_ctx.moduleBase); + writeStr(fd, "\nBuild-ID: "); + if (g_ctx.buildIdLen != 0) { + writeHexBytes(fd, g_ctx.buildId, g_ctx.buildIdLen); +#if defined(_WIN32) + if (g_ctx.pdbAge != 0) { + writeStr(fd, " (Age="); + writeDec(fd, g_ctx.pdbAge); + writeStr(fd, ")"); + } +#endif + } else { + writeStr(fd, "(unavailable)"); + } + writeStr(fd, "\nReason: "); + writeStr(fd, reason); + if (hasCode) { + writeStr(fd, " ("); + writeHex(fd, code); + writeStr(fd, ")"); + } + writeStr(fd, "\nFault addr: "); + writeHex(fd, faultAddr); + writeStr(fd, "\nCrash PC: "); + if (crashPcKnown) { + writeHex(fd, crashPc); + writeStr(fd, " rva="); + writeHex(fd, crashPc >= g_ctx.moduleBase ? crashPc - g_ctx.moduleBase : 0ull); + } else { + writeStr(fd, "(unavailable on this platform)"); + } + writeStr(fd, "\n"); + writeStr(fd, "Backtrace:\n"); +} + +void emitFooter(int fd) { + writeStr(fd, "========================================================\n"); +} + +#if defined(_WIN32) + +LONG g_inHandler = 0; +LPTOP_LEVEL_EXCEPTION_FILTER g_prevFilter = nullptr; + +void captureBuildId() { + const auto* base = reinterpret_cast(g_ctx.moduleBase); + if (base == nullptr) { + return; + } + const auto* dos = reinterpret_cast(base); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) { + return; + } + const auto* nt = reinterpret_cast(base + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) { + return; + } + const IMAGE_DATA_DIRECTORY& dir = + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; + if (dir.VirtualAddress == 0 || dir.Size == 0) { + return; + } + const auto* dbg = reinterpret_cast(base + dir.VirtualAddress); + const unsigned count = dir.Size / sizeof(IMAGE_DEBUG_DIRECTORY); + for (unsigned i = 0; i < count; ++i) { + if (dbg[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW) { + continue; + } + const auto* cv = base + dbg[i].AddressOfRawData; + if (std::memcmp(cv, "RSDS", 4) != 0) { + continue; + } + std::memcpy(g_ctx.buildId, cv + 4, sizeof(GUID)); + g_ctx.buildIdLen = sizeof(GUID); + std::memcpy(&g_ctx.pdbAge, cv + 4 + sizeof(GUID), sizeof(g_ctx.pdbAge)); + break; + } +} + +const char* exceptionName(DWORD code) { + switch (code) { + case EXCEPTION_ACCESS_VIOLATION: + return "EXCEPTION_ACCESS_VIOLATION"; + case EXCEPTION_ILLEGAL_INSTRUCTION: + return "EXCEPTION_ILLEGAL_INSTRUCTION"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + return "EXCEPTION_INT_DIVIDE_BY_ZERO"; + case EXCEPTION_STACK_OVERFLOW: + return "EXCEPTION_STACK_OVERFLOW"; + case EXCEPTION_DATATYPE_MISALIGNMENT: + return "EXCEPTION_DATATYPE_MISALIGNMENT"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; + default: + return "EXCEPTION"; + } +} + +int captureBacktraceWin(CONTEXT ctx, uintptr_t* out, int cap) { + int n = 0; + while (n < cap) { +#if defined(_M_X64) + const DWORD64 ip = ctx.Rip; +#elif defined(_M_ARM64) + const DWORD64 ip = ctx.Pc; +#else + const DWORD64 ip = 0; +#endif + if (ip == 0) { + break; + } + out[n++] = static_cast(ip); +#if defined(_M_X64) || defined(_M_ARM64) + DWORD64 imageBase = 0; + PRUNTIME_FUNCTION fn = RtlLookupFunctionEntry(ip, &imageBase, nullptr); + if (fn != nullptr) { + PVOID handlerData = nullptr; + DWORD64 establisherFrame = 0; + RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, ip, fn, &ctx, &handlerData, + &establisherFrame, nullptr); + continue; + } +#if defined(_M_X64) + if (ctx.Rsp == 0) { + break; + } + ctx.Rip = *reinterpret_cast(ctx.Rsp); + ctx.Rsp += sizeof(DWORD64); +#else + if (ctx.Lr == 0 || ctx.Lr == ip) { + break; + } + ctx.Pc = ctx.Lr; + ctx.Lr = 0; +#endif +#else + break; +#endif + } + return n; +} + +void emit(int fd, EXCEPTION_POINTERS* ep) { + if (fd < 0) { + return; + } + + const DWORD code = ep->ExceptionRecord->ExceptionCode; + const uintptr_t pc = reinterpret_cast(ep->ExceptionRecord->ExceptionAddress); + uintptr_t faultAddr = 0; + if (code == EXCEPTION_ACCESS_VIOLATION && ep->ExceptionRecord->NumberParameters >= 2) { + faultAddr = static_cast(ep->ExceptionRecord->ExceptionInformation[1]); + } + + emitHeader(fd, exceptionName(code), code, true, faultAddr, pc, true); + + uintptr_t frames[kMaxFrames]; + const int frameCount = captureBacktraceWin(*ep->ContextRecord, frames, kMaxFrames); + for (int i = 0; i < frameCount; ++i) { + emitFrame(fd, i, frames[i]); + } + + emitFooter(fd); +} + +LONG WINAPI windowsHandler(EXCEPTION_POINTERS* ep) { + if (InterlockedCompareExchange(&g_inHandler, 1, 0) != 0) { + return EXCEPTION_CONTINUE_SEARCH; + } + emit(kStderrFd, ep); + const int logFd = dusk::GetLogFileDescriptor(); + if (logFd >= 0) { + emit(logFd, ep); + } + if (g_prevFilter != nullptr) { + return g_prevFilter(ep); + } + return EXCEPTION_CONTINUE_SEARCH; +} + +#else + +constexpr int kSignals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE}; +constexpr int kSignalCount = static_cast(sizeof(kSignals) / sizeof(kSignals[0])); +constexpr int kAltStackSize = 128 * 1024; + +volatile std::sig_atomic_t g_inHandler = 0; +char g_altStack[kAltStackSize]; +struct sigaction g_prev[kSignalCount]; +std::terminate_handler g_prevTerminate = nullptr; + +void crashRegs(void* ucv, uintptr_t& pc, uintptr_t& lr, uintptr_t& fp) { + pc = 0; + lr = 0; + fp = 0; + if (ucv == nullptr) { + return; + } + auto* uc = static_cast(ucv); +#if defined(__APPLE__) +#if defined(__aarch64__) || defined(__arm64__) + pc = static_cast(uc->uc_mcontext->__ss.__pc); + lr = static_cast(uc->uc_mcontext->__ss.__lr); + fp = static_cast(uc->uc_mcontext->__ss.__fp); +#elif defined(__x86_64__) + pc = static_cast(uc->uc_mcontext->__ss.__rip); + fp = static_cast(uc->uc_mcontext->__ss.__rbp); +#endif +#elif defined(__ANDROID__) +#if defined(__aarch64__) + pc = static_cast(uc->uc_mcontext.pc); + lr = static_cast(uc->uc_mcontext.regs[30]); + fp = static_cast(uc->uc_mcontext.regs[29]); +#elif defined(__x86_64__) + pc = static_cast(uc->uc_mcontext.gregs[REG_RIP]); + fp = static_cast(uc->uc_mcontext.gregs[REG_RBP]); +#elif defined(__arm__) + pc = static_cast(uc->uc_mcontext.arm_pc); + lr = static_cast(uc->uc_mcontext.arm_lr); + fp = static_cast(uc->uc_mcontext.arm_fp); +#elif defined(__i386__) + pc = static_cast(uc->uc_mcontext.gregs[REG_EIP]); + fp = static_cast(uc->uc_mcontext.gregs[REG_EBP]); +#endif +#elif defined(__linux__) +#if defined(__x86_64__) + pc = static_cast(uc->uc_mcontext.gregs[REG_RIP]); + fp = static_cast(uc->uc_mcontext.gregs[REG_RBP]); +#elif defined(__aarch64__) + pc = static_cast(uc->uc_mcontext.pc); + lr = static_cast(uc->uc_mcontext.regs[30]); + fp = static_cast(uc->uc_mcontext.regs[29]); +#elif defined(__i386__) + pc = static_cast(uc->uc_mcontext.gregs[REG_EIP]); + fp = static_cast(uc->uc_mcontext.gregs[REG_EBP]); +#endif +#endif +} + +bool pcNearFunctionEntry(uintptr_t pc) { + constexpr uintptr_t kPrologueWindow = 20; + Dl_info info; + if (dladdr(reinterpret_cast(pc), &info) == 0 || info.dli_saddr == nullptr) { + return false; + } + const auto start = reinterpret_cast(info.dli_saddr); + return pc >= start && pc - start <= kPrologueWindow; +} + +int captureBacktraceFP(uintptr_t pc, uintptr_t lr, uintptr_t fp, uintptr_t* out, int cap) { + int n = 0; + if (pc != 0 && n < cap) { + out[n++] = pc; + } + bool dedupeLr = false; + if (lr != 0 && lr != pc && n < cap && pcNearFunctionEntry(pc)) { + out[n++] = lr; + dedupeLr = true; + } + uintptr_t cur = fp; + uintptr_t prev = 0; + constexpr uintptr_t kMaxFrameSpan = 16u << 20; + while (n < cap) { + if (cur == 0 || (cur & (sizeof(uintptr_t) - 1)) != 0 || cur <= prev) { + break; + } + const auto* slot = reinterpret_cast(cur); + const uintptr_t next = slot[0]; + const uintptr_t ret = slot[1]; + if (ret == 0) { + break; + } + const bool skip = dedupeLr && ret == lr; + dedupeLr = false; + if (!skip) { + out[n++] = ret; + } + if (next != 0 && next > cur && next - cur > kMaxFrameSpan) { + break; + } + prev = cur; + cur = next; + } + return n; +} + +struct UnwindState { + uintptr_t* pcs; + int count; + int cap; + int skip; +}; + +_Unwind_Reason_Code unwindCb(struct _Unwind_Context* ctx, void* arg) { + auto* s = static_cast(arg); + const uintptr_t ip = static_cast(_Unwind_GetIP(ctx)); + if (ip == 0) { + return _URC_END_OF_STACK; + } + if (s->skip > 0) { + --s->skip; + return _URC_NO_REASON; + } + if (s->count >= s->cap) { + return _URC_END_OF_STACK; + } + s->pcs[s->count++] = ip; + return _URC_NO_REASON; +} + +int captureBacktrace(uintptr_t* pcs, int cap, int skip) { + UnwindState s{pcs, 0, cap, skip}; + _Unwind_Backtrace(&unwindCb, &s); + return s.count; +} + +void prewarmUnwinder() { + uintptr_t warm[4]; + captureBacktrace(warm, 4, 0); +} + +#if defined(__APPLE__) + +void captureBuildId() { + const auto* header = reinterpret_cast(g_ctx.moduleBase); + if (header == nullptr || header->magic != MH_MAGIC_64) { + return; + } + const auto* lc = reinterpret_cast( + reinterpret_cast(header) + sizeof(struct mach_header_64)); + for (uint32_t i = 0; i < header->ncmds; ++i) { + if (lc->cmd == LC_UUID) { + const auto* uuid = reinterpret_cast(lc); + std::memcpy(g_ctx.buildId, uuid->uuid, sizeof(uuid->uuid)); + g_ctx.buildIdLen = sizeof(uuid->uuid); + return; + } + lc = reinterpret_cast( + reinterpret_cast(lc) + lc->cmdsize); + } +} + +#else + +bool segmentContains(const dl_phdr_info* info, uintptr_t addr) { + for (int i = 0; i < info->dlpi_phnum; ++i) { + const ElfW(Phdr)& ph = info->dlpi_phdr[i]; + if (ph.p_type != PT_LOAD) { + continue; + } + const uintptr_t start = info->dlpi_addr + ph.p_vaddr; + if (addr >= start && addr < start + ph.p_memsz) { + return true; + } + } + return false; +} + +bool readGnuBuildId(const dl_phdr_info* info) { + for (int i = 0; i < info->dlpi_phnum; ++i) { + const ElfW(Phdr)& ph = info->dlpi_phdr[i]; + if (ph.p_type != PT_NOTE) { + continue; + } + const auto* p = reinterpret_cast(info->dlpi_addr + ph.p_vaddr); + const uint8_t* end = p + ph.p_memsz; + while (p + sizeof(ElfW(Nhdr)) <= end) { + const auto* nh = reinterpret_cast(p); + const char* name = reinterpret_cast(nh + 1); + const uint8_t* desc = + reinterpret_cast(name + ((nh->n_namesz + 3) & ~3u)); + if (nh->n_type == NT_GNU_BUILD_ID && nh->n_namesz == 4 && + std::memcmp(name, "GNU", 4) == 0) { + unsigned n = nh->n_descsz; + if (n > sizeof(g_ctx.buildId)) { + n = sizeof(g_ctx.buildId); + } + std::memcpy(g_ctx.buildId, desc, n); + g_ctx.buildIdLen = n; + return true; + } + p = desc + ((nh->n_descsz + 3) & ~3u); + } + } + return false; +} + +int elfBuildIdCallback(dl_phdr_info* info, size_t, void* arg) { + const auto self = *static_cast(arg); + if (!segmentContains(info, self)) { + return 0; + } + readGnuBuildId(info); + return 1; +} + +void captureBuildId() { + uintptr_t self = reinterpret_cast(&install); + dl_iterate_phdr(&elfBuildIdCallback, &self); +} + +#endif + +const char* signalName(int sig) { + switch (sig) { + case SIGSEGV: + return "SIGSEGV (segmentation fault)"; + case SIGBUS: + return "SIGBUS (bus error)"; + case SIGABRT: + return "SIGABRT (abort)"; + case SIGILL: + return "SIGILL (illegal instruction)"; + case SIGFPE: + return "SIGFPE (floating point exception)"; + default: + return "unknown signal"; + } +} + +void emit(int fd, int sig, siginfo_t* info, const uintptr_t* frames, int frameCount, + uintptr_t pc) { + if (fd < 0) { + return; + } + const uintptr_t faultAddr = + info != nullptr ? reinterpret_cast(info->si_addr) : 0; + emitHeader(fd, signalName(sig), 0, false, faultAddr, pc, pc != 0); + for (int i = 0; i < frameCount; ++i) { + emitFrame(fd, i, frames[i]); + } + emitFooter(fd); +} + +void chainPrevious(int sig, siginfo_t* info, void* uc) { + for (int i = 0; i < kSignalCount; ++i) { + if (kSignals[i] != sig) { + continue; + } + const struct sigaction& o = g_prev[i]; + if ((o.sa_flags & SA_SIGINFO) != 0) { + if (o.sa_sigaction != nullptr) { + o.sa_sigaction(sig, info, uc); + return; + } + } else { + if (o.sa_handler == SIG_IGN) { + return; + } + if (o.sa_handler != SIG_DFL && o.sa_handler != nullptr) { + o.sa_handler(sig); + return; + } + } + break; + } + ::signal(sig, SIG_DFL); + ::raise(sig); +} + +void handler(int sig, siginfo_t* info, void* ucv) { + if (g_inHandler != 0) { + _exit(128 + sig); + } + g_inHandler = 1; + + uintptr_t pc = 0; + uintptr_t lr = 0; + uintptr_t fp = 0; + crashRegs(ucv, pc, lr, fp); + uintptr_t frames[kMaxFrames]; + int frameCount = captureBacktraceFP(pc, lr, fp, frames, kMaxFrames); + if (frameCount < 2) { + frameCount = captureBacktrace(frames, kMaxFrames, 2); + } + + emit(kStderrFd, sig, info, frames, frameCount, pc); + const int logFd = dusk::GetLogFileDescriptor(); + if (logFd >= 0) { + emit(logFd, sig, info, frames, frameCount, pc); + ::fsync(logFd); + } + + chainPrevious(sig, info, ucv); +} + +void writeTerminateMessage(int fd, const char* body, const char* what) { + writeStr(fd, "\nterminate: "); + writeStr(fd, body); + writeStr(fd, what); + writeStr(fd, "\n"); +} + +void onTerminate() { + const char* body = "unknown reason"; + const char* what = nullptr; + if (std::exception_ptr ep = std::current_exception()) { + try { + std::rethrow_exception(ep); + } catch (const std::exception& e) { + body = "uncaught exception: "; + what = e.what(); + } catch (...) { + body = "uncaught non-std exception"; + } + } else { + body = "no active exception"; + } + writeTerminateMessage(kStderrFd, body, what); + writeTerminateMessage(dusk::GetLogFileDescriptor(), body, what); + if (g_prevTerminate != nullptr) { + g_prevTerminate(); + } + std::abort(); +} + +#endif + +} // namespace + +void install() { +#if defined(_WIN32) + g_ctx.moduleBase = reinterpret_cast(GetModuleHandleW(nullptr)); + GetModuleFileNameA(nullptr, g_ctx.modulePath, sizeof(g_ctx.modulePath) - 1); + captureBuildId(); +#if defined(DUSK_CRASH_DBGHELP) + SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); + SymInitialize(GetCurrentProcess(), nullptr, TRUE); +#endif + g_prevFilter = SetUnhandledExceptionFilter(&windowsHandler); +#else + Dl_info moduleInfo; + if (dladdr(reinterpret_cast(&install), &moduleInfo) != 0) { + g_ctx.moduleBase = reinterpret_cast(moduleInfo.dli_fbase); + if (moduleInfo.dli_fname != nullptr) { + std::strncpy(g_ctx.modulePath, moduleInfo.dli_fname, + sizeof(g_ctx.modulePath) - 1); + } + } + captureBuildId(); + prewarmUnwinder(); + + static stack_t altStack; + altStack.ss_sp = g_altStack; + altStack.ss_size = sizeof(g_altStack); + altStack.ss_flags = 0; + sigaltstack(&altStack, nullptr); + + struct sigaction sa; + std::memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = &handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + + for (int i = 0; i < kSignalCount; ++i) { + sigaction(kSignals[i], &sa, &g_prev[i]); + } + + g_prevTerminate = std::set_terminate(&onTerminate); +#endif +} + +} // namespace dusk::crash_handler diff --git a/src/dusk/logging.cpp b/src/dusk/logging.cpp index 4319d2dc7e..b24f54878a 100644 --- a/src/dusk/logging.cpp +++ b/src/dusk/logging.cpp @@ -33,10 +33,17 @@ static constexpr std::string_view StubFragments[] = { "but selective updates are not implemented"sv, }; +#if _WIN32 +#define DUSK_FILENO _fileno +#else +#define DUSK_FILENO fileno +#endif + namespace { // On macOS, std::mutex becomes poisoned when its dtor is run. // We use this to check if the LogState is destroyed before attempting to acquire it. std::atomic g_logStateAlive(true); +std::atomic g_logFd(-1); struct LogState { std::mutex mutex; @@ -54,6 +61,7 @@ struct LogState { } std::lock_guard lock(mutex); if (file != nullptr) { + g_logFd.store(-1, std::memory_order_release); std::fflush(file); std::fclose(file); file = nullptr; @@ -232,6 +240,7 @@ void dusk::InitializeFileLogging(const std::filesystem::path& configDir, AuroraL } g_logState.filePath = logPath.u8string(); + g_logFd.store(DUSK_FILENO(g_logState.file), std::memory_order_release); aurora::g_config.logCallback = &aurora_log_callback; aurora::g_config.logLevel = logLevel; WriteLogLine(g_logState.file, "INFO", "dusk", "File logging initialized", 24); @@ -252,3 +261,7 @@ const char* dusk::GetLogFilePath() { return reinterpret_cast( g_logState.filePath.empty() ? nullptr : g_logState.filePath.c_str()); } + +int dusk::GetLogFileDescriptor() { + return g_logFd.load(std::memory_order_acquire); +} diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 387224beb3..fbd872efc4 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -48,6 +48,7 @@ #include #include "SSystem/SComponent/c_API.h" #include "dusk/app_info.hpp" +#include "dusk/crash_handler.h" #include "dusk/crash_reporting.h" #include "dusk/data.hpp" #include "dusk/dusk.h" @@ -548,6 +549,7 @@ int game_main(int argc, char* argv[]) { } ApplyCVarOverrides(parsed_arg_options["cvar"]); dusk::crash_reporting::initialize(); + dusk::crash_handler::install(); // TODO: How to handle this? // PADSetDefaultMapping(&defaultPadMapping, PAD_TYPE_STANDARD); From 498868c17d167a6961a1ae438fe27206a1455f50 Mon Sep 17 00:00:00 2001 From: SuperDude88 <82904174+SuperDude88@users.noreply.github.com> Date: Mon, 25 May 2026 00:12:36 -0400 Subject: [PATCH 42/47] Invert Air/Swim Axes (#1155) * Invert Air/Swim Axes Apparently this is a setting in TPHD, so for parity's sake I'll throw it in here * Invert Air Controls Thanks @Abzol for knowing exactly where this is --- include/dusk/settings.h | 2 ++ src/d/actor/d_a_alink_swim.inc | 4 ++-- src/d/actor/d_a_kago.cpp | 9 +++++++++ src/dusk/settings.cpp | 4 ++++ src/dusk/ui/settings.cpp | 4 ++++ 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/dusk/settings.h b/include/dusk/settings.h index e842c1177a..923a1e9fc6 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -202,6 +202,8 @@ struct UserSettings { ConfigVar invertCameraYAxis; ConfigVar invertFirstPersonXAxis; ConfigVar invertFirstPersonYAxis; + ConfigVar invertAirSwimX; + ConfigVar invertAirSwimY; ConfigVar freeCameraSensitivity; ConfigVar debugFlyCam; ConfigVar debugFlyCamLockEvents; diff --git a/src/d/actor/d_a_alink_swim.inc b/src/d/actor/d_a_alink_swim.inc index 038d710e80..19131e404a 100644 --- a/src/d/actor/d_a_alink_swim.inc +++ b/src/d/actor/d_a_alink_swim.inc @@ -216,7 +216,7 @@ void daAlink_c::setSpeedAndAngleSwim() { if (checkEventRun()) { var_r28 = mMoveAngle; } else { - var_r28 = shape_angle.y + (16384.0f * cM_ssin(mStickAngle)); + var_r28 = shape_angle.y + (16384.0f * cM_ssin(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertAirSwimX ? -1.0f : 1.0f))); } cLib_addCalcAngleS(&shape_angle.y, var_r28, mpHIO->mSwim.m.mUnderwaterTurnRate, mpHIO->mSwim.m.mUnderwaterMaxTurn, mpHIO->mSwim.m.mUnderwaterMinTurn); @@ -835,7 +835,7 @@ void daAlink_c::setSwimMoveAnime() { } else { s16 var_r24; if (checkInputOnR() && !checkEventRun()) { - var_r24 = 13653.0f * cM_scos(mStickAngle); + var_r24 = 13653.0f * cM_scos(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertAirSwimY ? -1.0f : 1.0f)); } else { var_r24 = 0; } diff --git a/src/d/actor/d_a_kago.cpp b/src/d/actor/d_a_kago.cpp index 9444237f12..efdad44516 100644 --- a/src/d/actor/d_a_kago.cpp +++ b/src/d/actor/d_a_kago.cpp @@ -3530,6 +3530,15 @@ void daKago_c::action() { #endif mStickY = mDoCPd_c::getStickY(PAD_1); +#ifdef TARGET_PC + if(dusk::getSettings().game.invertAirSwimX) { + mStickX = -mStickX; + } + if(dusk::getSettings().game.invertAirSwimY) { + mStickY = -mStickY; + } +#endif + u8 prevIsWaterfall = mIsWaterfall; mIsWaterfall = FALSE; fpcM_Search(s_waterfall, this); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 7c4e42763a..ef639e2b05 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -90,6 +90,8 @@ UserSettings g_userSettings = { .invertCameraYAxis {"game.invertCameraYAxis", false}, .invertFirstPersonXAxis {"game.invertFirstPersonXAxis", false}, .invertFirstPersonYAxis {"game.invertFirstPersonYAxis", false}, + .invertAirSwimX {"game.invertAirSwimX", false}, + .invertAirSwimY {"game.invertAirSwimY", false}, .freeCameraSensitivity {"game.freeCameraSensitivity", 1.0f}, .debugFlyCam {"game.debugFlyCam", false}, .debugFlyCamLockEvents {"game.debugFlyCamLockEvents", true}, @@ -219,6 +221,8 @@ void registerSettings() { Register(g_userSettings.game.invertCameraYAxis); Register(g_userSettings.game.invertFirstPersonXAxis); Register(g_userSettings.game.invertFirstPersonYAxis); + Register(g_userSettings.game.invertAirSwimX); + Register(g_userSettings.game.invertAirSwimY); Register(g_userSettings.game.freeCameraSensitivity); Register(g_userSettings.game.minimalHUD); Register(g_userSettings.game.pauseOnFocusLost); diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 3be9ce25e2..d6545f5121 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -957,6 +957,10 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Invert horizontal movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings)."); addOption("Invert First Person Y Axis", getSettings().game.invertFirstPersonYAxis, "Invert vertical movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings)."); + addOption("Invert Air/Swim X Axis", getSettings().game.invertAirSwimX, + "Invert horizontal movement while flying or swimming."); + addOption("Invert Air/Swim Y Axis", getSettings().game.invertAirSwimY, + "Invert vertical movement while flying or swimming."); leftPane.add_section("Gyro"); leftPane.register_control( From 2e1cc7cb26fcbac5ae99e41567064a7a395a41ee Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 22:17:24 -0600 Subject: [PATCH 43/47] Save config when hiding controller config window --- src/dusk/ui/controller_config.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dusk/ui/controller_config.cpp b/src/dusk/ui/controller_config.cpp index a0b2b6baa9..20c599ecfd 100644 --- a/src/dusk/ui/controller_config.cpp +++ b/src/dusk/ui/controller_config.cpp @@ -275,6 +275,7 @@ ControllerConfigWindow::ControllerConfigWindow(bool prelaunch) { void ControllerConfigWindow::hide(bool close) { stop_rumble_test(); cancel_pending_binding(); + config::Save(); Window::hide(close); } From f03bd71612072c56865f48a2f879409f2a7bde44 Mon Sep 17 00:00:00 2001 From: Olivia!! Date: Mon, 25 May 2026 06:20:32 +0200 Subject: [PATCH 44/47] store config to disk when destroying ui (#1791) --- src/dusk/ui/ui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dusk/ui/ui.cpp b/src/dusk/ui/ui.cpp index 83a331c1fc..072bf70b66 100644 --- a/src/dusk/ui/ui.cpp +++ b/src/dusk/ui/ui.cpp @@ -15,6 +15,7 @@ #include "input.hpp" #include "prelaunch.hpp" #include "window.hpp" +#include "dusk/config.hpp" namespace dusk::ui { namespace { @@ -60,6 +61,7 @@ bool initialize() noexcept { } void shutdown() noexcept { + config::Save(); sDocumentStack.clear(); sPassiveDocuments.clear(); sConnectedGamepads.clear(); From 8b6f989315a6085a0ca73e3997dbfa412b92e86a Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 24 May 2026 22:56:06 -0600 Subject: [PATCH 45/47] Update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index 8f1ea3e126..cb2c340d6c 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 8f1ea3e1266ca6f2aa9c4009a328bfc270a01b89 +Subproject commit cb2c340d6cde6827387f14c31ce19e5f28a40e09 From a7790d7323aefec1cea60211168d24dc2967756c Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 25 May 2026 16:25:21 +0200 Subject: [PATCH 46/47] Change "verify your dump" instructions in readme (#1809) * Change "verify your dump" instructions in readme No need to do this manually as the game already does it. Instead let's link to a guide for dumping the game legitimately. * Gotta shill --- README.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 106d2c0a0e..5a9701bede 100644 --- a/README.md +++ b/README.md @@ -22,16 +22,11 @@ It aims to be as accurate as possible to the original while also providing new o > [!IMPORTANT] > At a minimum, Dusklight requires a GPU with support for either D3D12, Vulkan, or Metal. Your experience with specific hardware, operating systems, and drivers may vary. In particular, older Intel iGPUs have a high likelihood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them. -### 1. Verify your dump +### 1. Dump your game -First, make sure your dump of the game is clean and supported by Dusklight. You can do this by checking the SHA-1 hash of your dump against this list of supported versions: +You must dump your own copy of the game, please see [this article](https://wiki.dolphin-emu.org/index.php?title=Ripping_Games) for instructions. After dumping, you can use a program like [Dolphin](https://dolphin-emu.org/) or [nodtool](https://github.com/encounter/nod/releases) to convert the `.iso` to a `.rvz` to save space. -| Version | SHA-1 hash | -|--------------| ------------------------------------------ | -| GameCube USA | `75edd3ddff41f125d1b4ce1a40378f1b565519e7` | -| GameCube EUR | `2601822a488eeb86fb89db16ca8f29c2c953e1ca` | - -*Support for other versions of the game is planned in the future. +Currently, only the GameCube USA and EUR releases are supported. Support for other versions of the game is planned in the future. ### 2. Download [Dusklight](https://github.com/TwilitRealm/dusklight/releases) From 63a86a456fe250273c4cc0ef75957e8841765320 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Mon, 25 May 2026 14:37:44 -0400 Subject: [PATCH 47/47] update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index a2889a0b0e..f00825d9f5 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit a2889a0b0eb6dcb0c48b4cbe0de9c8238cdb759a +Subproject commit f00825d9f50abc0fcc355cc06a10012ad3f8b2ed