Further work on implementing support for cheats

This commit is contained in:
Gericom
2026-02-21 19:30:03 +01:00
parent 3300c5277c
commit 83046e2cc2
11 changed files with 286 additions and 15 deletions

View File

@@ -15,10 +15,12 @@
#include "patches/arm7/DisableArm7WramClearPatch.h"
#include "patches/arm7/sdk5/Sdk5DsiSdCardRedirectPatch.h"
#include "patches/arm7/PokemonDownloaderArm7Patch.h"
#include "patches/arm7/cheats/CheatEnginePatch.h"
#include "Arm7Patcher.h"
void* Arm7Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform) const
void* Arm7Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, u32 cheatsLength, void*& cheatsPtr) const
{
cheatsPtr = nullptr;
auto romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
auto twlRomHeader = (const nds_header_twl_t*)TWL_SHARED_MEMORY->twlRomHeader;
ModuleParamsLocator moduleParamsLocator;
@@ -91,10 +93,18 @@ void* Arm7Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform) const
patchCollection.AddPatch(new PokemonDownloaderArm7Patch());
}
CheatEnginePatch* cheatEnginePatch = nullptr;
if (cheatsLength > 0)
{
cheatEnginePatch = new CheatEnginePatch();
patchCollection.AddPatch(cheatEnginePatch);
}
if (arm7ArenaPatch->FindPatchTarget(patchContext))
{
const u32 arm7PatchSpaceSize = 0x800;
void* privateWramHeapStart = arm7ArenaPatch->GetArm7PrivateWramArenaLo();
u32 mainMemoryArenaLo = (u32)arm7ArenaPatch->GetMainMemoryArenaLo();
if (0x0380F780 - (u32)privateWramHeapStart - 0x2100 >= arm7PatchSpaceSize)
{
patchSpaceStart = privateWramHeapStart;
@@ -103,7 +113,6 @@ void* Arm7Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform) const
else
{
LOG_DEBUG("Arm 7 patches placed in main memory\n");
u32 mainMemoryArenaLo = (u32)arm7ArenaPatch->GetMainMemoryArenaLo();
if (gIsDsiMode && romHeader->unitCode == 0)
{
patchSpaceStart = (void*)(mainMemoryArenaLo & ~0xC00000); // 0x023...
@@ -112,8 +121,24 @@ void* Arm7Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform) const
{
patchSpaceStart = (void*)(mainMemoryArenaLo | 0x800000); // make sure it ends up in the right place while in 16MB mode
}
arm7ArenaPatch->SetMainMemoryArenaLo((u8*)mainMemoryArenaLo + arm7PatchSpaceSize);
mainMemoryArenaLo += arm7PatchSpaceSize;
}
if (cheatsLength > 0)
{
void* cheats;
if (gIsDsiMode && romHeader->unitCode == 0)
{
cheats = (void*)(mainMemoryArenaLo & ~0xC00000); // 0x023...
}
else
{
cheats = (void*)(mainMemoryArenaLo | 0x800000); // make sure it ends up in the right place while in 16MB mode
}
cheatsPtr = cheats;
cheatEnginePatch->SetCheats(cheats);
mainMemoryArenaLo += cheatsLength;
}
arm7ArenaPatch->SetMainMemoryArenaLo((void*)mainMemoryArenaLo);
patchContext.GetPatchHeap().AddFreeSpace(patchSpaceStart, arm7PatchSpaceSize);
}

View File

@@ -8,6 +8,8 @@ class Arm7Patcher
public:
/// @brief Applies arm7 patches using the given \p loaderPlatform.
/// @param loaderPlatform The loader platform to use.
/// @param cheatsLength The length of the cheats data, or zero when there are no cheats.
/// @param cheatsPtr Pointer to where the cheats need to be stored, or \c nullptr when there are no cheats.
/// @return A pointer to the patch space in IWRAM, or \c nullptr if the patches have been placed in main memory.
void* ApplyPatches(const LoaderPlatform* loaderPlatform) const;
void* ApplyPatches(const LoaderPlatform* loaderPlatform, u32 cheatsLength, void*& cheatsPtr) const;
};

View File

@@ -155,10 +155,12 @@ static void handleApplyArm9PatchesCommand()
ipc_sendWordDirect(1);
}
static void handleApplyArm7PatchesCommand()
static void handleApplyArm7PatchesCommand(u32 cheatsLength)
{
void* patchSpaceStart = Arm7Patcher().ApplyPatches(sLoaderPlatform);
void* cheats = nullptr;
void* patchSpaceStart = Arm7Patcher().ApplyPatches(sLoaderPlatform, cheatsLength, cheats);
ipc_sendWordDirect((u32)patchSpaceStart);
ipc_sendWordDirect((u32)cheats);
}
static void handleSetAPInfoCommand()
@@ -289,7 +291,8 @@ static void handleArm7Command(u32 command)
}
case IPC_COMMAND_ARM9_APPLY_ARM7_PATCHES:
{
handleApplyArm7PatchesCommand();
u32 cheatsLength = receiveFromArm7();
handleApplyArm7PatchesCommand(cheatsLength);
break;
}
case IPC_COMMAND_ARM9_SET_AP_INFO:

View File

@@ -0,0 +1,125 @@
#include "common.h"
#include "CheatEnginePatchCode.h"
#include "CheatEnginePatch.h"
// sdk2-4
static const u32 sVBlankIntrPatternArm0[] = { 0xE92D4000u, 0xE24DD004u, 0xE59F0018u, 0xE5900000u };
static const u32 sVBlankIntrPatternArm1[] = { 0xE92D4008u, 0xE59F0014u, 0xE5900000u, 0xE3500000u };
static const u32 sVBlankIntrPatternThumb0[] = { 0x00000000u, 0xB081B500u, 0x68004804u, 0xD0012800u }; // +4
static const u32 sVBlankIntrPatternThumb1[] = { 0x46C04770u, 0x027FFE1Du, 0x4804B508u, 0x28006800u }; // +8
bool CheatEnginePatch::FindPatchTarget(PatchContext& patchContext)
{
_vblankIrqHandler = patchContext.FindPattern32(sVBlankIntrPatternArm0, sizeof(sVBlankIntrPatternArm0));
if (_vblankIrqHandler)
{
_foundPattern = sVBlankIntrPatternArm0;
}
if (!_vblankIrqHandler)
{
_vblankIrqHandler = patchContext.FindPattern32(sVBlankIntrPatternArm1, sizeof(sVBlankIntrPatternArm1));
if (_vblankIrqHandler)
{
_foundPattern = sVBlankIntrPatternArm1;
}
}
if (!_vblankIrqHandler)
{
_vblankIrqHandler = patchContext.FindPattern32(sVBlankIntrPatternThumb0, sizeof(sVBlankIntrPatternThumb0));
if (_vblankIrqHandler)
{
_foundPattern = sVBlankIntrPatternThumb0;
_vblankIrqHandler += 1;
}
}
if (!_vblankIrqHandler)
{
_vblankIrqHandler = patchContext.FindPattern32(sVBlankIntrPatternThumb1, sizeof(sVBlankIntrPatternThumb1));
if (_vblankIrqHandler)
{
_foundPattern = sVBlankIntrPatternThumb1;
_vblankIrqHandler += 2;
}
}
if (_vblankIrqHandler)
{
LOG_DEBUG("ARM7 VBlankIntr found at 0x%p\n", _vblankIrqHandler);
}
return _vblankIrqHandler != nullptr;
}
void CheatEnginePatch::ApplyPatch(PatchContext& patchContext)
{
if (!_vblankIrqHandler || !_cheats)
return;
auto cheatEnginePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<CheatEnginePatchCode>
(
patchContext.GetPatchHeap(),
_cheats
);
if (_foundPattern == sVBlankIntrPatternArm0)
{
// push {lr}
// sub sp, sp, #4
// ldr r0,=
// ldr r0, [r0]
// cmp r0, #0
// beq 1f
// bl
// 1:
// add sp, sp, #4
// pop {lr}
// bx lr
_vblankIrqHandler[7] = 0xE59F0000; // ldr r0,= address
_vblankIrqHandler[8] = 0xE12FFF10; // bx r0
_vblankIrqHandler[9] = (u32)cheatEnginePatchCode->GetCheatEngineFunction(); // address
}
else if (_foundPattern == sVBlankIntrPatternArm1)
{
// push {r3,lr}
// ldr r0,=
// ldr r0, [r0]
// cmp r0, #0
// beq 1f
// bl
// 1:
// pop {r3,lr}
// bx lr
}
else if (_foundPattern == sVBlankIntrPatternThumb0)
{
// push {lr}
// sub sp, sp, #4
// ldr r0,=
// ldr r0, [r0]
// cmp r0, #0
// beq 1f
// bl
// 1:
// add sp, sp, #4
// pop {r3}
// bx r3
// nop
}
else if (_foundPattern == sVBlankIntrPatternThumb1)
{
// push {r3,lr}
// ldr r0,=
// ldr r0, [r0]
// cmp r0, #0
// beq 1f
// bl
// 1:
// pop {r3}
// pop {r3}
// bx r3
}
else
{
LOG_ERROR("ARM7 VBlankIntr signature not implemented\n");
}
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "patches/Patch.h"
/// @brief Arm7 patch for injecting the cheat engine in the vblank interrupt handler.
class CheatEnginePatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
void SetCheats(const void* cheats)
{
_cheats = cheats;
}
private:
const void* _cheats = nullptr;
u32* _vblankIrqHandler = nullptr;
const u32* _foundPattern = nullptr;
};

View File

@@ -0,0 +1,24 @@
#pragma once
#include "sections.h"
#include "patches/PatchCode.h"
DEFINE_SECTION_SYMBOLS(patch_cheatengine);
extern "C" void cheatengine_entry(void);
extern const void* cheatengine_cheatsPtr;
class CheatEnginePatchCode : public PatchCode
{
public:
CheatEnginePatchCode(PatchHeap& patchHeap, const void* cheatsAddress)
: PatchCode(SECTION_START(patch_cheatengine), SECTION_SIZE(patch_cheatengine), patchHeap)
{
cheatengine_cheatsPtr = cheatsAddress;
}
const void* GetCheatEngineFunction() const
{
return GetAddressAtTarget((void*)cheatengine_entry);
}
};

View File

@@ -10,16 +10,22 @@
.global cheatengine_entry
.type cheatengine_entry, %function
cheatengine_entry:
push {r4, lr}
pop {r0, r1}
mov lr, r1
push {r4, r5, lr}
ldr r4, cheatengine_cheatsPtr
ldr r5, [r4, #4] // pload_cheats_t::numberOfCheats
adds r4, #8
entry_cheats_loop:
ldmia r4!, {r0} // r0 = cheat pointer
cmp r0, #0
beq entry_end
subs r5, #1
bmi entry_end
movs r0, r4
bl cheatengine_runCheat
ldmia r4!, {r0} // r0 = length of cheat in bytes (pload_cheat_t::length)
adds r4, r0
b entry_cheats_loop
entry_end:
pop {r4}
pop {r4, r5}
pop {r3}
bx r3
@@ -38,7 +44,8 @@ cheatengine_runCheat:
mov r6, r10
mov r7, r11
push {r4, r5, r6, r7} // r8, r9, r10, r11
ldr r1, [r0] // r1 = length of cheat code
ldmia r0!, {r1} // r1 = length of cheat code
adds r1, r0
mov r8, r1 // r8 = end of cheat code
mov r9, r0 // r9 = loop start
@@ -389,7 +396,6 @@ FX_end:
.balign 4
// Pointer to list of pointers to cheat codes. Last pointer should be nullptr to terminate the list.
.global cheatengine_cheatsPtr
cheatengine_cheatsPtr:
.word 0