From 6fb34c75f835197b31aced40f3a431e18916bac0 Mon Sep 17 00:00:00 2001 From: Mow <32942550+taxicat1@users.noreply.github.com> Date: Tue, 30 Dec 2025 16:23:04 -0500 Subject: [PATCH] Add overlay patch to fix race condition in Kirby Super Star Ultra (#81) --- arm9/source/Arm9Patcher.cpp | 10 +++++ .../KirbySuperStarUltraPatch.cpp | 41 +++++++++++++++++++ .../KirbySuperStarUltraPatch.h | 39 ++++++++++++++++++ .../KirbySuperStarUltraPatchAsm.h | 10 +++++ .../KirbySuperStarUltraPatchAsm.s | 38 +++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.cpp create mode 100644 arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.h create mode 100644 arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatchAsm.h create mode 100644 arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatchAsm.s diff --git a/arm9/source/Arm9Patcher.cpp b/arm9/source/Arm9Patcher.cpp index f69d8d0..55a4809 100644 --- a/arm9/source/Arm9Patcher.cpp +++ b/arm9/source/Arm9Patcher.cpp @@ -21,6 +21,7 @@ #include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectOverlayPatch.h" #include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectPuyoPuyo7Patch.h" #include "patches/arm9/OverlayPatches/PokemonIr/PokemonIrApPatch.h" +#include "patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.h" #include "patches/arm9/OverlayPatches/GoldenSunDarkDawn/GoldenSunDarkDawnOverlayHookPatch.h" #include "SecureSysCallsUnusedSpaceLocator.h" #include "fastSearch.h" @@ -404,6 +405,15 @@ void Arm9Patcher::AddGameSpecificPatches( patchCollection.AddPatch(new NintendoDSGuideNandSavePatch()); break; } + // Kirby Super Star Ultra + case GAMECODE("YKWE"): + case GAMECODE("YKWJ"): + case GAMECODE("YKWK"): + case GAMECODE("YKWP"): + { + overlayHookPatch->AddOverlayPatch(new KirbySuperStarUltraPatch()); + break; + } } } diff --git a/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.cpp b/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.cpp new file mode 100644 index 0000000..6e0fc59 --- /dev/null +++ b/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.cpp @@ -0,0 +1,41 @@ +#include "common.h" +#include "gameCode.h" +#include "patches/PatchContext.h" +#include "KirbySuperStarUltraPatchAsm.h" +#include "KirbySuperStarUltraPatch.h" + +const void* KirbySuperStarUltraPatch::InsertPatch(PatchContext& patchContext) +{ + kirbyultrapatch_nextAddress = next ? (const void*)next->InsertPatch(patchContext) : nullptr; + + switch (patchContext.GetGameCode()) + { + case GAMECODE("YKWE"): + { + kirbyultrapatch_offset = 0x63A8 + 0x160; + break; + } + case GAMECODE("YKWJ"): + { + kirbyultrapatch_offset = 0x6300 + 0x160; + break; + } + case GAMECODE("YKWK"): + { + kirbyultrapatch_offset = 0x63A8 + 0x160; + break; + } + case GAMECODE("YKWP"): + { + kirbyultrapatch_offset = 0x63F0 + 0x160; + break; + } + } + + u32 patchSize = SECTION_SIZE(kirbyultrapatch); + void* patchAddress = patchContext.GetPatchHeap().Alloc(patchSize); + u32 entryAddress = (u32)&kirbyultrapatch_entry - (u32)SECTION_START(kirbyultrapatch) + (u32)patchAddress; + memcpy(patchAddress, SECTION_START(kirbyultrapatch), patchSize); + + return (const void*)entryAddress; +} diff --git a/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.h b/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.h new file mode 100644 index 0000000..99dbc8f --- /dev/null +++ b/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.h @@ -0,0 +1,39 @@ +#pragma once +#include "../OverlayPatch.h" + +/// @brief Arm9 overlay patch for Kirby Super Star Ultra. +/// @details +/// This game has an issue where it softlocks on the corkboard screen after unlocking new items. +/// This is caused by a race condition where it initializes a save write while it waits for +/// 16 frames for an animation to complete. Normally, the save write finishes after the delay, +/// but if it finishes before, as is common for a flashcart, it will get stuck waiting for +/// the save write which has already completed. +/// +/// The code for this is something like: +/// if (SaveCompleteStatus() != 0 || CorkboardNewUnlocksContext->frameCounter < 16) return; +/// +/// When it should be in the other order to short circuit properly: +/// if (CorkboardNewUnlocksContext->frameCounter < 16 || SaveCompleteStatus() != 0) return; +/// +/// To fix this, the two checks are swapped. This initial assembly: +/// bl SaveCompleteStatus +/// cmp r0, #0 +/// popne {r4, pc} +/// ldrb r0, [r4, #0x50] +/// cmp r0, #0x10 +/// popcc {r4, pc} +/// +/// Is turned into this: +/// ldrb r0, [r4, #0x50] +/// cmp r0, #0x10 +/// popcc {r4, pc} +/// bl SaveCompleteStatus +/// cmp r0, #0 +/// popne {r4, pc} +/// +/// (the offset for the bl also must be adjusted by 3 since it is PC-relative) +class KirbySuperStarUltraPatch : public OverlayPatch +{ +public: + const void* InsertPatch(PatchContext& patchContext) override; +}; diff --git a/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatchAsm.h b/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatchAsm.h new file mode 100644 index 0000000..f985779 --- /dev/null +++ b/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatchAsm.h @@ -0,0 +1,10 @@ +#pragma once +#include "sections.h" + +DEFINE_SECTION_SYMBOLS(kirbyultrapatch); + +extern "C" void kirbyultrapatch_entry(); + +extern u32 kirbyultrapatch_offset; + +extern const void* kirbyultrapatch_nextAddress; // Next patch address diff --git a/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatchAsm.s b/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatchAsm.s new file mode 100644 index 0000000..7f09dec --- /dev/null +++ b/arm9/source/patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatchAsm.s @@ -0,0 +1,38 @@ +.cpu arm946e-s +.syntax unified +.section "kirbyultrapatch", "ax" +.thumb + +.global kirbyultrapatch_entry +.type kirbyultrapatch_entry, %function +kirbyultrapatch_entry: + push {r4-r7, lr} + ldm r5!, {r0, r1} // ovy_id, ram_start + cmp r0, #6 + bne continue_to_next + + ldr r0, kirbyultrapatch_offset + adds r1, r0 + ldm r1!, {r2, r3, r4, r5, r6, r7} + subs r1, #24 // Reset pointer + subs r2, #3 // Correct function call offset + stm r1!, {r5, r6, r7} // Swap the two blocks of 3 instructions + stm r1!, {r2, r3, r4} + +continue_to_next: + ldr r0, kirbyultrapatch_nextAddress + pop {r4-r7, pc} + +.balign 4 + +.global kirbyultrapatch_offset +kirbyultrapatch_offset: + .word 0 + +.global kirbyultrapatch_nextAddress +kirbyultrapatch_nextAddress: + .word 0 + +.pool + +.end