Add overlay patch to fix race condition in Kirby Super Star Ultra (#81)

This commit is contained in:
Mow
2025-12-30 16:23:04 -05:00
committed by GitHub
parent 992e2d1053
commit 6fb34c75f8
5 changed files with 138 additions and 0 deletions

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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