You've already forked pico-loader
mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-01-09 16:28:35 -08:00
Add overlay patch to fix race condition in Kirby Super Star Ultra (#81)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user