Initial commit

This commit is contained in:
Gericom
2025-11-22 11:08:28 +01:00
commit 9cf3ffbfcf
358 changed files with 58350 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(patch_carditaskthread);
extern "C" void __patch_carditaskthread_entry();
extern "C" void __patch_carditaskthread_entry_sdk4();
extern u16 __patch_carditaskthread_mov_cardicommon_to_r4;
extern u16 __patch_carditaskthread_mov_command_to_r0;
extern u32 __patch_carditaskthread_failoffset;
extern u32 __patch_carditaskthread_successoffset;
extern u32 __patch_carditaskthread_readsave_asm_address;
extern u32 __patch_carditaskthread_writesave_asm_address;

View File

@@ -0,0 +1,123 @@
.cpu arm7tdmi
.section "patch_carditaskthread", "ax"
.syntax unified
.thumb
.global __patch_carditaskthread_entry_sdk4
.type __patch_carditaskthread_entry_sdk4, %function
__patch_carditaskthread_entry_sdk4:
push {r4,lr}
beq end_success
b __patch_carditaskthread_mov_cardicommon_to_r4
.global __patch_carditaskthread_entry
.type __patch_carditaskthread_entry, %function
__patch_carditaskthread_entry:
push {r4,lr}
.global __patch_carditaskthread_mov_cardicommon_to_r4
__patch_carditaskthread_mov_cardicommon_to_r4:
mov r4, r9
.global __patch_carditaskthread_mov_command_to_r0
__patch_carditaskthread_mov_command_to_r0:
ldr r0, [r4, #4] // r0 = command
cmp r0, #2
beq identify_backup
cmp r0, #6
beq read_backup
cmp r0, #7
beq write_backup
cmp r0, #8
beq write_backup
cmp r0, #9
beq verify_backup
// erase
cmp r0, #10
beq end_success
cmp r0, #11
beq end_success
cmp r0, #12
beq end_success
cmp r0, #15
beq end_success
end_fail:
ldr r0, __patch_carditaskthread_failoffset
b end
end_success:
ldr r0, __patch_carditaskthread_successoffset
end:
pop {r4}
pop {r3}
adds r3, r0
bx r3
identify_backup:
b end_success
read_backup:
ldr r0, [r4]
movs r1, #0
str r1, [r0] // result
ldr r1, [r0, #0xC] // src
ldr r2, [r0, #0x10] // dst
ldr r3, [r0, #0x14] // len
cmp r3, #0
beq end_success
ldr r0, __patch_carditaskthread_readsave_asm_address
bl blx_r0
b end_success
write_backup:
ldr r0, [r4]
movs r1, #0
str r1, [r0] // result
ldr r1, [r0, #0xC] // src
ldr r2, [r0, #0x10] // dst
ldr r3, [r0, #0x14] // len
cmp r3, #0
beq end_success
ldr r0, __patch_carditaskthread_writesave_asm_address
bl blx_r0
b end_success
verify_backup:
ldr r0, [r4]
movs r1, #0
str r1, [r0] // result
b end_success
blx_r0:
bx r0
.balign 4
.global __patch_carditaskthread_failoffset
__patch_carditaskthread_failoffset:
.word 0
.global __patch_carditaskthread_successoffset
__patch_carditaskthread_successoffset:
.word 4
.global __patch_carditaskthread_readsave_asm_address
__patch_carditaskthread_readsave_asm_address:
.word 0
.global __patch_carditaskthread_writesave_asm_address
__patch_carditaskthread_writesave_asm_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,39 @@
#include "common.h"
#include "../PatchContext.h"
#include "sharedMemory.h"
#include "ndsHeader.h"
#include "DisableArm7WramClearPatch.h"
bool DisableArm7WramClearPatch::FindPatchTarget(PatchContext& patchContext)
{
auto romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
_clearEndPool = nullptr;
for (u32 i = 0; i < 0x100; i += 4)
{
u32* ptr = (u32*)(romHeader->arm7LoadAddress + i);
if (*ptr == 0x380FF00)
{
_clearEndPool = ptr;
break;
}
}
if (_clearEndPool)
{
LOG_DEBUG("Arm7 wram clear end pool value found at 0x%p\n", _clearEndPool);
}
else
{
LOG_WARNING("Arm7 wram clear end pool value not found\n");
}
return _clearEndPool != nullptr;
}
void DisableArm7WramClearPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_clearEndPool)
return;
*_clearEndPool = 0;
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "../Patch.h"
/// @brief Arm7 patch to disabling clearing of wram.
class DisableArm7WramClearPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _clearEndPool = nullptr;
};

View File

@@ -0,0 +1,97 @@
#include "common.h"
#include "../PatchContext.h"
#include "OsGetInitArenaLoPatch.h"
static const u32 sOSGetInitArenaLoPatternOld[] = { 0xE3A0050Eu, 0xE59F1014u, 0xE351050Eu, 0x81A00001u }; // -0x34
static const u32 sOSGetInitArenaLoPattern[] = { 0xE59F1018u, 0xE3A0050Eu, 0xE351050Eu, 0x81A00001u }; // -0x34
static const u32 sOSGetInitArenaLoPattern2[] = { 0xE59F101Cu, 0xE3A0050Eu, 0xE351050Eu, 0x81A00001u }; // -0x3C
static const u32 sOSGetInitArenaLoPatternThumb[] = { 0xD0042801u, 0xD0042807u, 0xD0092808u, 0x4809E00Fu };
static const u32 sOSGetInitArenaLoPatternThumbSdk50[] = { 0x2801B508u, 0x2807D004u, 0x2808D006u, 0xE010D00Au };
bool OsGetInitArenaLoPatch::FindPatchTarget(PatchContext& patchContext)
{
if (_osGetInitArenaLo)
return true;
_osGetInitArenaLo = patchContext.FindPattern32(sOSGetInitArenaLoPattern, sizeof(sOSGetInitArenaLoPattern));
if (!_osGetInitArenaLo)
_osGetInitArenaLo = patchContext.FindPattern32(sOSGetInitArenaLoPatternOld, sizeof(sOSGetInitArenaLoPatternOld));
if (_osGetInitArenaLo)
{
_osGetInitArenaLo -= 13;
_wramBssOffset = 0x54;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32(sOSGetInitArenaLoPattern2, sizeof(sOSGetInitArenaLoPattern2));
if (_osGetInitArenaLo)
{
_osGetInitArenaLo -= 15;
_wramBssOffset = 0x60;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32(sOSGetInitArenaLoPatternThumb, sizeof(sOSGetInitArenaLoPatternThumb));
if (_osGetInitArenaLo)
{
_wramBssOffset = 0x38;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32(sOSGetInitArenaLoPatternThumbSdk50, sizeof(sOSGetInitArenaLoPatternThumbSdk50));
if (_osGetInitArenaLo)
{
_wramBssOffset = 0x40;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32Twl(sOSGetInitArenaLoPattern, sizeof(sOSGetInitArenaLoPattern));
if (!_osGetInitArenaLo)
_osGetInitArenaLo = patchContext.FindPattern32Twl(sOSGetInitArenaLoPatternOld, sizeof(sOSGetInitArenaLoPatternOld));
if (_osGetInitArenaLo)
{
_osGetInitArenaLo -= 13;
_wramBssOffset = 0x54;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32Twl(sOSGetInitArenaLoPattern2, sizeof(sOSGetInitArenaLoPattern2));
if (_osGetInitArenaLo)
{
_osGetInitArenaLo -= 15;
_wramBssOffset = 0x60;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32Twl(sOSGetInitArenaLoPatternThumb, sizeof(sOSGetInitArenaLoPatternThumb));
if (_osGetInitArenaLo)
{
_wramBssOffset = 0x38;
}
}
}
}
}
}
}
if (_osGetInitArenaLo)
{
LOG_DEBUG("Found OS_GetInitArenaLo at %p\n", _osGetInitArenaLo);
_wramBssEnd = (void*)*(u32*)((u8*)_osGetInitArenaLo + _wramBssOffset);
LOG_DEBUG("Bss end: 0x%p\n", _wramBssEnd);
_mainMemoryArenaLo = (void*)*(u32*)((u8*)_osGetInitArenaLo + _wramBssOffset - 4);
LOG_DEBUG("Main memory arena lo: 0x%p\n", _mainMemoryArenaLo);
}
else
{
LOG_WARNING("OS_GetInitArenaLo not found\n");
}
return _osGetInitArenaLo != nullptr;
}
void OsGetInitArenaLoPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_osGetInitArenaLo)
return;
*(u32*)((u8*)_osGetInitArenaLo + _wramBssOffset) = (u32)_wramBssEnd;
*(u32*)((u8*)_osGetInitArenaLo + _wramBssOffset - 4) = (u32)_mainMemoryArenaLo;
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include "../Patch.h"
/// @brief Arm7 patch to get and update the arena low addresses for wram and main memory.
class OsGetInitArenaLoPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
void* GetArm7PrivateWramArenaLo() const { return _wramBssEnd; }
void SetArm7PrivateWramArenaLo(void* wramBssEnd) { _wramBssEnd = wramBssEnd; }
void* GetMainMemoryArenaLo() const { return _mainMemoryArenaLo; }
void SetMainMemoryArenaLo(void* mainMemoryArenaLo) { _mainMemoryArenaLo = mainMemoryArenaLo; }
private:
u32* _osGetInitArenaLo = nullptr;
u32 _wramBssOffset = 0;
void* _wramBssEnd = nullptr;
void* _mainMemoryArenaLo = nullptr;
};

View File

@@ -0,0 +1,27 @@
#include "common.h"
#include "../PatchContext.h"
#include "PokemonDownloaderArm7PatchAsm.h"
#include "PokemonDownloaderArm7Patch.h"
static const u32 sBootFunctionPattern[] = { 0xE92D4070u, 0xE59F0098u, 0xE5904004u, 0xE3540000u };
bool PokemonDownloaderArm7Patch::FindPatchTarget(PatchContext& patchContext)
{
_bootFunction = patchContext.FindPattern32(sBootFunctionPattern, sizeof(sBootFunctionPattern));
return _bootFunction != nullptr;
}
void PokemonDownloaderArm7Patch::ApplyPatch(PatchContext& patchContext)
{
if (!_bootFunction)
{
return;
}
auto patchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<PokemonDownloaderArm7PatchCode>(
patchContext.GetPatchHeap());
_bootFunction[0] = 0xE59F0000; // ldr r0,= address
_bootFunction[1] = 0xE12FFF10; // bx r0
_bootFunction[2] = (u32)patchCode->GetBoot7Function(); // address
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "../Patch.h"
#include "LoaderInfo.h"
/// @brief Arm7 patch to make the Pokemon downloader reboot into Pico Loader.
class PokemonDownloaderArm7Patch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _bootFunction = nullptr;
};

View File

@@ -0,0 +1,19 @@
#pragma once
#include "../PatchCode.h"
#include "sections.h"
DEFINE_SECTION_SYMBOLS(patch_pokemondownloader7);
extern "C" void patch_pokemondownloader7_entry(void);
class PokemonDownloaderArm7PatchCode : public PatchCode
{
public:
explicit PokemonDownloaderArm7PatchCode(PatchHeap& patchHeap)
: PatchCode(SECTION_START(patch_pokemondownloader7), SECTION_SIZE(patch_pokemondownloader7), patchHeap) { }
const void* GetBoot7Function() const
{
return GetAddressAtTarget((void*)patch_pokemondownloader7_entry);
}
};

View File

@@ -0,0 +1,25 @@
.cpu arm7tdmi
.section "patch_pokemondownloader7", "ax"
.syntax unified
.thumb
.global patch_pokemondownloader7_entry
.type patch_pokemondownloader7_entry, %function
patch_pokemondownloader7_entry:
ldr r0,= 0x04000180 // REG_IPC_SYNC
movs r1, #1
strb r1, [r0, #1]
1:
ldrb r7, [r0] // ipc sync
cmp r7, #1
bne 1b // while ipc sync from arm9 is not 1
ldr r0,= 0x06000000
ldr r0, [r0]
bx r0
.balign 4
.pool
.end

View File

@@ -0,0 +1,31 @@
#pragma once
#include "sections.h"
#include "../PatchCode.h"
#include "../SectorRemapPatchCode.h"
#include "../platform/SdReadPatchCode.h"
DEFINE_SECTION_SYMBOLS(readsave);
extern "C" void readsave_asm(u32, u32 saveSrc, void* memoryDst, u32 byteLength);
extern u32 readsave_tmpBufferPtr;
extern u32 readsave_save_offset_to_sd_sector_asm_address;
extern u32 readsave_sdread_asm_address;
class ReadSavePatchCode : public PatchCode
{
public:
ReadSavePatchCode(PatchHeap& patchHeap, const SectorRemapPatchCode* sectorRemapPatchCode,
const SdReadPatchCode* sdReadPatchCode, void* tmpBuffer)
: PatchCode(SECTION_START(readsave), SECTION_SIZE(readsave), patchHeap)
{
readsave_save_offset_to_sd_sector_asm_address = (u32)sectorRemapPatchCode->GetRemapFunction();
readsave_sdread_asm_address = (u32)sdReadPatchCode->GetSdReadFunction();
readsave_tmpBufferPtr = (u32)tmpBuffer;
}
const void* GetReadSaveFunction() const
{
return GetAddressAtTarget((void*)readsave_asm);
}
};

View File

@@ -0,0 +1,75 @@
.cpu arm7tdmi
.section "readsave", "ax"
.syntax unified
.thumb
// r1 = save src
// r2 = memory dst
// r3 = byte length
.global readsave_asm
.type readsave_asm, %function
readsave_asm:
push {r4,r5,r6,r7,lr}
movs r4, r3 // remaining bytes
movs r5, r1
movs r6, r2
1:
movs r0, r5 // will be rounded down by function
ldr r3, readsave_save_offset_to_sd_sector_asm_address
bl blx_r3
cmp r0, #0
beq end // out of bounds
ldr r1, readsave_tmpBufferPtr
movs r2, #1 // single sector
lsls r7, r2, #9 // 512
ldr r3, readsave_sdread_asm_address
bl blx_r3
// copy bytes
lsls r2, r5, #23
lsrs r2, r2, #23 // r2 = byte offset in sector
subs r0, r6, r2
subs r7, r7, r2 // remaining in sector
cmp r7, r4 // if remaining in sector > requested read length
bls 2f
movs r7, r4 // clamp to requested read length
2:
subs r4, r7
adds r5, r7
adds r6, r7
ldr r1, readsave_tmpBufferPtr
3:
ldrb r3, [r1, r2]
strb r3, [r0, r2]
adds r2, #1
subs r7, #1
bne 3b
cmp r4, #0
bne 1b
end:
pop {r4,r5,r6,r7,pc}
blx_r3:
bx r3
.balign 4
.global readsave_tmpBufferPtr
readsave_tmpBufferPtr:
.word 0
.global readsave_save_offset_to_sd_sector_asm_address
readsave_save_offset_to_sd_sector_asm_address:
.word 0
.global readsave_sdread_asm_address
readsave_sdread_asm_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,25 @@
#pragma once
#include "sections.h"
#include "../SectorRemapPatchCode.h"
#include "fileInfo.h"
DEFINE_SECTION_SYMBOLS(saveoffsettosdsector);
extern "C" u32 save_offset_to_sd_sector_asm(u32 saveOffset);
extern u32 saveoffsettosdsector_fatDataPtr;
class SaveOffsetToSdSectorPatchCode : public SectorRemapPatchCode
{
public:
SaveOffsetToSdSectorPatchCode(PatchHeap& patchHeap, const save_file_info_t* fatDataPtr)
: SectorRemapPatchCode(SECTION_START(saveoffsettosdsector), SECTION_SIZE(saveoffsettosdsector), patchHeap)
{
saveoffsettosdsector_fatDataPtr = (u32)fatDataPtr;
}
const void* GetRemapFunction() const override
{
return GetAddressAtTarget((void*)save_offset_to_sd_sector_asm);
}
};

View File

@@ -0,0 +1,50 @@
.cpu arm7tdmi
.section "saveoffsettosdsector", "ax"
.syntax unified
.thumb
// r0 = save offset
// returns sd sector in r0
// returns cluster shift in r1
// returns remaining sectors in cluster in lr
.global save_offset_to_sd_sector_asm
.type save_offset_to_sd_sector_asm, %function
save_offset_to_sd_sector_asm:
push {r4,r5,lr}
retry:
ldr r5, saveoffsettosdsector_fatDataPtr
ldmia r5!, {r1,r2,r3,r4} // clusterShift, database, clusterMask, clusterMap[0]
lsrs r0, r0, #9
adds r4, r3, #1
ands r3, r0
subs r4, r3
mov lr, r4 // remaining sectors in cluster
adds r3, r2
lsrs r0, r1 // cl
1:
ldmia r5!, {r2,r4} // ncl, startSector
cmp r2, #0
beq out_of_bounds
subs r0, r2 // cl -= ncl
bcs 1b
adds r0, r2
adds r0, r4 // + startSector
subs r0, #2 // - 2
lsls r0, r1
adds r0, r3 // r0 = src sector
pop {r4,r5,pc}
out_of_bounds:
movs r0, #0
pop {r4,r5,pc}
.balign 4
.global saveoffsettosdsector_fatDataPtr
saveoffsettosdsector_fatDataPtr:
.word 0x027FFA00
.pool
.end

View File

@@ -0,0 +1,34 @@
#pragma once
#include "sections.h"
#include "../PatchCode.h"
#include "../SectorRemapPatchCode.h"
#include "../platform/SdReadPatchCode.h"
#include "../platform/SdWritePatchCode.h"
DEFINE_SECTION_SYMBOLS(writesave);
extern "C" void writesave_asm(u32, const void* memorySrc, u32 saveDst, u32 byteLength);
extern u32 writesave_tmpBufferPtr;
extern u32 writesave_save_offset_to_sd_sector_asm_address;
extern u32 writesave_sdread_asm_address;
extern u32 writesave_sdwrite_asm_address;
class WriteSavePatchCode : public PatchCode
{
public:
WriteSavePatchCode(PatchHeap& patchHeap, const SectorRemapPatchCode* sectorRemapPatchCode,
const SdReadPatchCode* sdReadPatchCode, const SdWritePatchCode* sdWritePatchCode, void* tmpBuffer)
: PatchCode(SECTION_START(writesave), SECTION_SIZE(writesave), patchHeap)
{
writesave_save_offset_to_sd_sector_asm_address = (u32)sectorRemapPatchCode->GetRemapFunction();
writesave_sdread_asm_address = (u32)sdReadPatchCode->GetSdReadFunction();
writesave_sdwrite_asm_address = (u32)sdWritePatchCode->GetSdWriteFunction();
writesave_tmpBufferPtr = (u32)tmpBuffer;
}
const void* GetWriteSaveFunction() const
{
return GetAddressAtTarget((void*)writesave_asm);
}
};

View File

@@ -0,0 +1,86 @@
.cpu arm7tdmi
.section "writesave", "ax"
.syntax unified
.thumb
// r1 = memory src
// r2 = save dst
// r3 = byte length
.global writesave_asm
.type writesave_asm, %function
writesave_asm:
push {r4,r5,r6,r7,lr}
movs r4, r3 // remaining bytes
movs r5, r2
movs r6, r1
1:
movs r0, r5 // will be rounded down by function
ldr r3, writesave_save_offset_to_sd_sector_asm_address
bl blx_r3
cmp r0, #0
beq end // out of bounds
push {r0}
ldr r1, writesave_tmpBufferPtr
movs r2, #1 // single sector
lsls r7, r2, #9 // 512
ldr r3, writesave_sdread_asm_address
bl blx_r3
// copy bytes
lsls r2, r5, #23
lsrs r2, r2, #23 // r2 = byte offset in sector
subs r0, r6, r2
subs r7, r7, r2 // remaining in sector
cmp r7, r4 // if remaining in sector > requested read length
bls 2f
movs r7, r4 // clamp to requested read length
2:
subs r4, r7
adds r5, r7
adds r6, r7
ldr r1, writesave_tmpBufferPtr
3:
ldrb r3, [r0, r2]
strb r3, [r1, r2]
adds r2, #1
subs r7, #1
bne 3b
pop {r0}
movs r2, #1 // single sector
ldr r3, writesave_sdwrite_asm_address
bl blx_r3
cmp r4, #0
bne 1b
end:
pop {r4,r5,r6,r7,pc}
blx_r3:
bx r3
.balign 4
.global writesave_tmpBufferPtr
writesave_tmpBufferPtr:
.word 0
.global writesave_save_offset_to_sd_sector_asm_address
writesave_save_offset_to_sd_sector_asm_address:
.word 0
.global writesave_sdread_asm_address
writesave_sdread_asm_address:
.word 0
.global writesave_sdwrite_asm_address
writesave_sdwrite_asm_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,285 @@
#include "common.h"
#include <algorithm>
#include <array>
#include "fileInfo.h"
#include "patches/PatchContext.h"
#include "patches/arm7/ReadSaveAsm.h"
#include "patches/arm7/WriteSaveAsm.h"
#include "patches/FunctionSignature.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/arm7/SaveOffsetToSdSectorAsm.h"
#include "patches/OffsetToSectorRemapAsm.h"
#include "patches/arm7/CardiTaskThreadPatchAsm.h"
#include "thumbInstructions.h"
#include "CardiTaskThreadPatch.h"
static constexpr auto sSignaturesArm = std::to_array<const FunctionSignature>
({
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59FA108u, 0xE28A5040u }, 0x020029A0, 0x02005015 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59FA10Cu, 0xE28A5040u }, 0x02004F50, 0x02017532 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59F91DCu, 0xE2895044u }, 0x02017532, 0x02017532 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59F91E0u, 0xE2895044u }, 0x02017532, 0x02027534 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59F91E0u, 0xE2895048u }, 0x030028A0, 0x03004E86 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59F91D4u, 0xE2895048u }, 0x03004F4C, 0x03017534 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59F91F8u, 0xE2895048u }, 0x03017530, 0x04017530 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEADCu }, 0x04002774, 0x04017533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEACEu }, 0x04007531, 0x04007531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEABFu }, 0x04007530, 0x04007531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEAAAu }, 0x04017530, 0x04017533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEA94u }, 0x04017530, 0x04017533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEA8Du }, 0x04017531, 0x04017533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEA77u }, 0x04017533, 0x04017533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA70u }, 0x04027530, 0x04027539 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA8Du }, 0x04027530, 0x04027539 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA6Cu }, 0x04027531, 0x04027531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA77u }, 0x04027531, 0x04027539 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA5Au }, 0x04027533, 0x04027533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFFAD7u }, 0x03017531, 0x03017531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFFAA5u }, 0x03027531, 0x03027531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEADBu }, 0x03027531, 0x03027531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA68u }, 0x04027533, 0x04027533 }
});
static constexpr auto sSignaturesThumb = std::to_array<const FunctionSignature>
({
{ (const u32[]) { 0xB081B5F0u, 0x1C2E4D2Du, 0x27103640u, 0xF7FC2400u }, 0x02004FB0, 0x02004FB4 },
{ (const u32[]) { 0xB081B5F0u, 0x1C2E4D2Bu, 0x27103640u, 0xF7FC2400u }, 0x02007531, 0x02017532 },
{ (const u32[]) { 0xB081B5F0u, 0x1C264C57u, 0x27103644u, 0xF7FC2500u }, 0x02027532, 0x02027535 },
{ (const u32[]) { 0xB081B5F0u, 0x1C264C55u, 0x27103648u, 0xF7FC2500u }, 0x03007530, 0x03012776 },
{ (const u32[]) { 0xB081B5F0u, 0x1C264C5Cu, 0x27033648u, 0xF7FC2500u }, 0x03017531, 0x03027532 },
{ (const u32[]) { 0x4C5CB5F8u, 0xF7FC2500u, 0x1C26FAB3u, 0x36481C07u }, 0x04007531, 0x04007531 },
{ (const u32[]) { 0x4C5CB5F8u, 0xF7FC2500u, 0x1C26FA97u, 0x36481C07u }, 0x04007531, 0x04007531 },
{ (const u32[]) { 0x4C5CB5F8u, 0xF7FC2500u, 0x1C26FA4Fu, 0x36481C07u }, 0x04017530, 0x04017533 },
{ (const u32[]) { 0x4C5CB5F8u, 0xF7FC2500u, 0x1C26FA23u, 0x36481C07u }, 0x04017531, 0x04017531 },
{ (const u32[]) { 0x4D62B5F8u, 0xF7FC2400u, 0x1C2EFA2Du, 0x36481C07u }, 0x04027531, 0x04027539 }
});
bool CardiTaskThreadPatch::FindPatchTarget(PatchContext& patchContext)
{
for (const auto& signature : sSignaturesArm)
{
if (CheckSignature(patchContext, signature))
{
if (signature.GetPattern()[2] == 0xE59F91DCu)
{
_peach = true;
}
else if (signature.GetPattern()[3] == 0xEBFFFAD7u
|| signature.GetPattern()[3] == 0xEBFFFAA5u
|| signature.GetPattern()[3] == 0xEBFFEADBu)
{
_pokemonDownloader = true;
}
break;
}
}
if (!_cardiTaskThread)
{
for (const auto& signature : sSignaturesThumb)
{
if (CheckSignature(patchContext, signature))
{
_thumb = true;
break;
}
}
}
if (!_cardiTaskThread)
{
LOG_FATAL("CARDi_TaskThread not found\n");
}
return _cardiTaskThread != nullptr;
}
void CardiTaskThreadPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_cardiTaskThread)
return;
//0xA4 = ADDLS PC, PC, R0,LSL#2
u32 patch1Size = SECTION_SIZE(patch_carditaskthread);
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
auto loaderPlatform = patchContext.GetLoaderPlatform();
const SdReadPatchCode* readPatchCode;
const SdWritePatchCode* writePatchCode;
const SectorRemapPatchCode* sectorRemapPatchCode;
readPatchCode = loaderPlatform->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
writePatchCode = loaderPlatform->CreateSdWritePatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
sectorRemapPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<SaveOffsetToSdSectorPatchCode>
(
patchContext.GetPatchHeap(),
(const save_file_info_t*)((u32)SHARED_SAVE_FILE_INFO - 0x02F00000 + 0x02700000)
);
void* tmpBuffer = patchContext.GetPatchHeap().Alloc(512);
auto readSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<ReadSavePatchCode>
(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
readPatchCode,
tmpBuffer
);
auto writeSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<WriteSavePatchCode>
(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
readPatchCode,
writePatchCode,
tmpBuffer
);
__patch_carditaskthread_readsave_asm_address = (u32)readSavePatchCode->GetReadSaveFunction();
__patch_carditaskthread_writesave_asm_address = (u32)writeSavePatchCode->GetWriteSaveFunction();
u32 entryAddress;
u32 patchOffset;
if (_thumb)
{
if (patchContext.GetSdkVersion() >= 0x4027531)
{
patchOffset = 0x90;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOVS_REG(THUMB_R4, THUMB_R5);
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x0C) + 9;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x14) + 9;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 4);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x02) = THUMB_MOV_HIREG(THUMB_HI_LR, THUMB_HI_PC);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x04) = THUMB_BX(THUMB_R1);
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x08) = entryAddress;
}
else if (patchContext.GetSdkVersion() >= 0x4007530)
{
patchOffset = 0x90;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x0C) + 9;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x14) + 9;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 4);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x02) = THUMB_MOV_HIREG(THUMB_HI_LR, THUMB_HI_PC);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x04) = THUMB_BX(THUMB_R1);
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x08) = entryAddress;
}
else if (patchContext.GetSdkVersion() >= 0x3027531)
{
patchOffset = 0x98;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x0E) + 8;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x18) + 8;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 4);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x02) = THUMB_MOV_HIREG(THUMB_HI_LR, THUMB_HI_PC);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x04) = THUMB_BX(THUMB_R1);
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x08) = entryAddress;
}
else if (patchContext.GetSdkVersion() >= 0x3012776
|| (patchContext.GetSdkVersion() >= 0x2027532 && patchContext.GetSdkVersion() <= 0x2027535))
{
patchOffset = 0x82;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x0E) + 8;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x18) + 8;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 4);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x02) = THUMB_MOV_HIREG(THUMB_HI_LR, THUMB_HI_PC);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x04) = THUMB_BX(THUMB_R1);
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x06) = entryAddress;
}
else if (patchContext.GetSdkVersion() >= 0x2004FB0)
{
patchOffset = patchContext.GetSdkVersion() >= 0x2007531 ? 0x60 : 0x64;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOVS_REG(THUMB_R4, THUMB_R5);
__patch_carditaskthread_failoffset = 0;
__patch_carditaskthread_successoffset = 0;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 0);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x02) = 0xE001; // jump over entryAddress
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x04) = entryAddress;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x08) = THUMB_NOP;
}
else
{
LOG_FATAL("Unsupported thumb CARDi_TaskThread\n");
while(1);
}
}
else
{
if (patchContext.GetSdkVersion() < 0x2020000 && !_peach)
{
// uses a table dispatch
//0xA4 = ldr r1,= table
__patch_carditaskthread_failoffset = 4;
__patch_carditaskthread_successoffset = 4;
if (patchContext.GetSdkVersion() >= 0x2005015)//0x2007532)//0x2012774)
patchOffset = 0xA4;
else
patchOffset = 0xA0;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOV_HIREG(THUMB_R4, THUMB_HI_R10);
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x00) = 0xe59f0004; // ldr r0,= entryAddress
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x04) = 0xe1a0e00f; // mov lr, pc
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x08) = 0xe12fff10; // bx r0
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x0C) = entryAddress;
}
else
{
if (patchContext.GetSdkVersion() >= 0x4027531)
{
patchOffset = 0xA8;
entryAddress = (u32)&__patch_carditaskthread_entry_sdk4 - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
}
else if (patchContext.GetSdkVersion() >= 0x4007530 || _pokemonDownloader)
{
patchOffset = 0xA8;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
}
else if (patchContext.GetSdkVersion() >= 0x3017531)
{
patchOffset = 0xB4;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOV_HIREG(THUMB_R4, THUMB_HI_R9);
}
else
{
patchOffset = 0x9C;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOV_HIREG(THUMB_R4, THUMB_HI_R9);
}
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x00) = 0xe59f000C; // ldr r0,= entryAddress
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x04) = 0xe1a0e00f; // mov lr, pc
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x08) = 0xe12fff10; // bx r0
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x14) = entryAddress;
}
}
memcpy(patch1Address, SECTION_START(patch_carditaskthread), patch1Size);
}
bool CardiTaskThreadPatch::CheckSignature(const PatchContext& patchContext, const FunctionSignature& signature)
{
if (patchContext.GetSdkVersion() >= signature.GetMinimumSdkVersion() &&
patchContext.GetSdkVersion() <= signature.GetMaximumSdkVersion())
{
_cardiTaskThread = patchContext.FindPattern32(signature.GetPattern(), 16);
if (_cardiTaskThread)
{
LOG_DEBUG("CARDi_TaskThread found at 0x%p\n", _cardiTaskThread);
return true;
}
}
return false;
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "patches/Patch.h"
class FunctionSignature;
/// @brief Arm7 patch for redirecting save reads and writes on SDK 2-4.
class CardiTaskThreadPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _cardiTaskThread = nullptr;
u16 _thumb = false;
u16 _peach = false;
u16 _pokemonDownloader = false;
bool CheckSignature(const PatchContext& patchContext, const FunctionSignature& signature);
};

View File

@@ -0,0 +1,139 @@
#include "common.h"
#include "patches/PatchContext.h"
#include "thumbInstructions.h"
#include "fileInfo.h"
#include "patches/arm7/ReadSaveAsm.h"
#include "patches/arm7/WriteSaveAsm.h"
#include "patches/arm7/SaveOffsetToSdSectorAsm.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/OffsetToSectorRemapAsm.h"
#include "patches/arm7/CardiTaskThreadPatchAsm.h"
#include "CardiDoTaskFromArm9Patch.h"
static const u32 sCARDiDoTaskFromARM9Pattern5007538[] = { 0xE92D4038u, 0xE59F4190u, 0xE3A01001u, 0xE5943000u }; // this one has branches instead of an offset table
static const u32 sCARDiDoTaskFromARM9Pattern5017537[] = { 0xE92D4070u, 0xE59F5168u, 0xE3A01001u, 0xE5953000u };
static const u32 sCARDiDoTaskFromARM9Pattern5057530[] = { 0xE92D4070u, 0xE59F5174u, 0xE3A01001u, 0xE5953000u };
static const u32 sCARDiDoTaskFromARM9Pattern5017537Thumb[] = { 0x4C3FB538u, 0x68214A3Fu, 0x23016E92u, 0x40936D88u };
static const u32 sCARDiDoTaskFromARM9Pattern5037531Thumb[] = { 0x4C3EB538u, 0x68214A3Eu, 0x23016E92u, 0x40936D88u };
static const u32 sCARDiDoTaskFromARM9Pattern5057530Thumb[] = { 0x4C40B538u, 0x68214A40u, 0x23016E92u, 0x40936D88u };
void CardiDoTaskFromArm9Patch::TryPattern(PatchContext& patchContext, const u32* pattern)
{
_cardiDoTaskFromArm9 = patchContext.FindPattern32(pattern, 16);
if (_cardiDoTaskFromArm9)
{
_foundPattern = pattern;
}
}
bool CardiDoTaskFromArm9Patch::FindPatchTarget(PatchContext& patchContext)
{
if (patchContext.GetSdkVersion() <= 0x5027535)
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5007538);
if (!_cardiDoTaskFromArm9 && patchContext.GetSdkVersion() >= 0x5017537 && patchContext.GetSdkVersion() <= 0x5057534)
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5017537);
if (!_cardiDoTaskFromArm9 && patchContext.GetSdkVersion() >= 0x5057530)
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5057530);
if (!_cardiDoTaskFromArm9)
{
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5057530Thumb);
if (!_cardiDoTaskFromArm9)
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5037531Thumb);
if (!_cardiDoTaskFromArm9)
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5017537Thumb);
if (_cardiDoTaskFromArm9)
{
_thumb = true;
}
}
if (_cardiDoTaskFromArm9)
{
LOG_DEBUG("CARDi_DoTaskFromARM9 found at 0x%p\n", _cardiDoTaskFromArm9);
}
return _cardiDoTaskFromArm9 != nullptr;
}
void CardiDoTaskFromArm9Patch::ApplyPatch(PatchContext& patchContext)
{
if (!_cardiDoTaskFromArm9)
return;
u32 patch1Size = SECTION_SIZE(patch_carditaskthread);
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
auto loaderPlatform = patchContext.GetLoaderPlatform();
auto readPatchCode = loaderPlatform->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto writePatchCode = loaderPlatform->CreateSdWritePatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto sectorRemapPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<SaveOffsetToSdSectorPatchCode>(
patchContext.GetPatchHeap(), SHARED_SAVE_FILE_INFO);
void* tmpBuffer = patchContext.GetPatchHeap().Alloc(512);
auto readSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<ReadSavePatchCode>(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
readPatchCode,
tmpBuffer
);
auto writeSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<WriteSavePatchCode>(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
readPatchCode,
writePatchCode,
tmpBuffer
);
__patch_carditaskthread_readsave_asm_address = (u32)readSavePatchCode->GetReadSaveFunction();
__patch_carditaskthread_writesave_asm_address = (u32)writeSavePatchCode->GetWriteSaveFunction();
u32 patchOffset;
if (_thumb)
{
patchOffset = _foundPattern == sCARDiDoTaskFromARM9Pattern5057530Thumb ? 0x3A : 0x36;
u32 entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
__patch_carditaskthread_mov_command_to_r0 = THUMB_NOP;
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x0C) + 9;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x14) + 9;
*(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 4);
*(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x02) = THUMB_MOV_HIREG(THUMB_HI_LR, THUMB_HI_PC);
*(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x04) = THUMB_BX(THUMB_R1);
*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x06) = entryAddress;
}
else
{
if (_foundPattern == sCARDiDoTaskFromARM9Pattern5007538)
{
// 0x4C = cmp r0, #0xF
patchOffset = 0x4C;
__patch_carditaskthread_successoffset = 4;
__patch_carditaskthread_failoffset = ((*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x08) & 0xFFFFFF) << 2) + 4;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP; // already in r4
__patch_carditaskthread_mov_command_to_r0 = THUMB_NOP;
}
else
{
// 0x58 = add r0, pc, r0, lsl #1
if (_foundPattern == sCARDiDoTaskFromARM9Pattern5057530)
patchOffset = 0x58;
else
patchOffset = 0x54;
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x0C) + 4;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x14) + 4;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOVS_REG(THUMB_R4, THUMB_R5);
__patch_carditaskthread_mov_command_to_r0 = THUMB_NOP;
}
u32 entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x00) = 0xe59f1004; // ldr r1,= entryAddress
*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x04) = 0xe1a0e00f; // mov lr, pc
*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x08) = 0xe12fff11; // bx r1
*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x0C) = entryAddress;
}
memcpy(patch1Address, SECTION_START(patch_carditaskthread), patch1Size);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "patches/Patch.h"
/// @brief Arm7 patch for redirecting save reads and writes on SDK 5.
class CardiDoTaskFromArm9Patch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _cardiDoTaskFromArm9 = nullptr;
const u32* _foundPattern = nullptr;
u16 _thumb = false;
void TryPattern(PatchContext& patchContext, const u32* pattern);
};

View File

@@ -0,0 +1,14 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(patch_cardidotaskfromarm9);
extern "C" void __patch_cardidotaskfromarm9_entry();
extern u32 __patch_cardidotaskfromarm9_failoffset;
extern u32 __patch_cardidotaskfromarm9_successoffset;
extern u16 __patch_cardidotaskfromarm9_move_statereg;
extern u32 __patch_cardidotaskfromarm9_readsave_asm_address;
extern u32 __patch_cardidotaskfromarm9_writesave_asm_address;

View File

@@ -0,0 +1,82 @@
#include "common.h"
#include "patches/platform/LoaderPlatform.h"
#include "Sdk5DsiSdCardRedirectPatchAsm.h"
#include "Sdk5DsiSdCardRedirectPatch.h"
static const u32 sAttachFunctionPattern[] = { 0xE92D4018u, 0xE24DDF5Du, 0xE24DDB01u, 0xE59FE050u };
static const u32 sAttachFunctionPatternThumb[] = { 0xB0FFB518u, 0xB0DFB0FFu, 0x4A0E490Du, 0x64CA4469u };
bool Sdk5DsiSdCardRedirectPatch::FindPatchTarget(PatchContext& patchContext)
{
_attachFunction = patchContext.FindPattern32Twl(sAttachFunctionPattern, sizeof(sAttachFunctionPattern));
if (!_attachFunction)
{
_attachFunction = patchContext.FindPattern32Twl(sAttachFunctionPatternThumb, sizeof(sAttachFunctionPatternThumb));
if (_attachFunction)
{
_thumb = true;
}
}
if (_attachFunction)
{
LOG_DEBUG("Found FATFSi_sdmcRtfsAttach at %p\n", _attachFunction);
}
else
{
LOG_WARNING("FATFSi_sdmcRtfsAttach not found\n");
}
return _attachFunction != nullptr;
}
static u32 getArmBlAddress(const u32* instructionPointer)
{
u32 blInstruction = *instructionPointer;
return (u32)instructionPointer + 8 + ((int)((blInstruction & 0xFFFFFF) << 8) >> 6);
}
static u32 getThumbBlAddress(const u32* instructionPointer)
{
u32 blInstruction1 = ((u16*)instructionPointer)[0];
u32 blInstruction2 = ((u16*)instructionPointer)[1];
return (u32)instructionPointer + 5 + ((int)((((blInstruction1 & 0x7FF) << 11) | (blInstruction2 & 0x7FF)) << 10) >> 9);
}
void Sdk5DsiSdCardRedirectPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_attachFunction)
return;
u32 getDriveStructAddress;
if (_thumb)
{
getDriveStructAddress = getThumbBlAddress((u32*)((u8*)_attachFunction - 0x20));
}
else
{
getDriveStructAddress = getArmBlAddress((u32*)((u8*)_attachFunction - 0x20));
}
auto sdRead = patchContext.GetLoaderPlatform()->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto sdWrite = patchContext.GetLoaderPlatform()->CreateSdWritePatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto patch = patchContext.GetPatchCodeCollection().AddUniquePatchCode<Sdk5DsiSdCardRedirectPatchCode>
(
patchContext.GetPatchHeap(),
sdRead,
sdWrite,
getDriveStructAddress
);
if (_thumb)
{
*(u32*)((u8*)_attachFunction + 0x44) = (u32)patch->GetIoFunction();
*(u32*)((u8*)_attachFunction + 0x48) = (u32)patch->GetControlFunction();
}
else
{
*(u32*)((u8*)_attachFunction + 0x64) = (u32)patch->GetIoFunction();
*(u32*)((u8*)_attachFunction + 0x68) = (u32)patch->GetControlFunction();
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "patches/Patch.h"
/// @brief Arm7 patch for redirecting access to the DSi sd card to the flashcard sd card.
class Sdk5DsiSdCardRedirectPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _attachFunction = nullptr;
u16 _thumb = false;
};

View File

@@ -0,0 +1,37 @@
#pragma once
#include "patches/PatchCode.h"
#include "sections.h"
#include "patches/platform/SdReadPatchCode.h"
#include "patches/platform/SdWritePatchCode.h"
DEFINE_SECTION_SYMBOLS(patch_dsisdredirect);
extern "C" bool __patch_dsisdredirect_io(u32 driveNumber, u32 startSector, void* buffer, u32 sectorCount, bool isRead);
extern "C" u32 __patch_dsisdredirect_control(u32 driveNumber, u32 command, void* argumentBuffer);
extern u32 __patch_dsisdredirect_io_readsd_asm_address;
extern u32 __patch_dsisdredirect_io_writesd_asm_address;
extern u32 __patch_dsisdredirect_control_get_drive_struct_address;
class Sdk5DsiSdCardRedirectPatchCode : public PatchCode
{
public:
Sdk5DsiSdCardRedirectPatchCode(PatchHeap& patchHeap, const SdReadPatchCode* sdReadPatchCode,
const SdWritePatchCode* sdWritePatchCode, u32 getDriveStructAddress)
: PatchCode(SECTION_START(patch_dsisdredirect), SECTION_SIZE(patch_dsisdredirect), patchHeap)
{
__patch_dsisdredirect_io_readsd_asm_address = (u32)sdReadPatchCode->GetSdReadFunction();
__patch_dsisdredirect_io_writesd_asm_address = (u32)sdWritePatchCode->GetSdWriteFunction();
__patch_dsisdredirect_control_get_drive_struct_address = getDriveStructAddress;
}
const void* GetIoFunction() const
{
return GetAddressAtTarget((void*)__patch_dsisdredirect_io);
}
const void* GetControlFunction() const
{
return GetAddressAtTarget((void*)__patch_dsisdredirect_control);
}
};

View File

@@ -0,0 +1,80 @@
.cpu arm7tdmi
.section "patch_dsisdredirect", "ax"
.syntax unified
.thumb
// r0 = drive number
// r1 = start sector
// r2 = source/destination buffer
// r3 = sector count
// [sp] = isReading (0 = write, otherwise read)
.global __patch_dsisdredirect_io
.type __patch_dsisdredirect_io, %function
__patch_dsisdredirect_io:
push {r4,lr}
movs r0, r1
movs r1, r2
movs r2, r3
ldr r4, [sp, #8] // reading
cmp r4, #0
bne 1f
ldr r3, __patch_dsisdredirect_io_writesd_asm_address
bl blx_r3
movs r0, #1 // success
b return
1:
ldr r3, __patch_dsisdredirect_io_readsd_asm_address
bl blx_r3
movs r0, #1 // success
return:
pop {r4}
pop {r3}
blx_r3:
bx r3
// r0 = drive number
// r1 = command
// r2 = argument buffer
.global __patch_dsisdredirect_control
.type __patch_dsisdredirect_control, %function
__patch_dsisdredirect_control:
push {lr}
cmp r1, #1 // startup
bne returnZero
ldr r3, __patch_dsisdredirect_control_get_drive_struct_address
bl blx_r3
ldr r1,= 0x4B4
movs r2, #0
str r2, [r0, r1] // partition 0
subs r1, r1, #4
ldr r2, [r0, r1]
movs r3, #0x83 // valid, partitioned, inserted
orrs r2, r2, r3
str r2, [r0, r1]
returnZero:
movs r0, #0
pop {r3}
bx r3
.balign 4
.global __patch_dsisdredirect_io_readsd_asm_address
__patch_dsisdredirect_io_readsd_asm_address:
.word 0
.global __patch_dsisdredirect_io_writesd_asm_address
__patch_dsisdredirect_io_writesd_asm_address:
.word 0
.global __patch_dsisdredirect_control_get_drive_struct_address
__patch_dsisdredirect_control_get_drive_struct_address:
.word 0
.pool
.end