mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-06-02 09:16:49 +02:00
Initial commit
This commit is contained in:
146
arm9/source/Arm7Patcher.cpp
Normal file
146
arm9/source/Arm7Patcher.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/mem/memNtrWram.h>
|
||||
#include "ModuleParamsLocator.h"
|
||||
#include "SdkVersion.h"
|
||||
#include "sharedMemory.h"
|
||||
#include "gameCode.h"
|
||||
#include "cache.h"
|
||||
#include "errorDisplay/ErrorDisplay.h"
|
||||
#include "patches/PatchCollection.h"
|
||||
#include "patches/PatchContext.h"
|
||||
#include "patches/platform/LoaderPlatform.h"
|
||||
#include "patches/arm7/sdk2to4/CardiTaskThreadPatch.h"
|
||||
#include "patches/arm7/sdk5/CardiDoTaskFromArm9Patch.h"
|
||||
#include "patches/arm7/OsGetInitArenaLoPatch.h"
|
||||
#include "patches/arm7/DisableArm7WramClearPatch.h"
|
||||
#include "patches/arm7/sdk5/Sdk5DsiSdCardRedirectPatch.h"
|
||||
#include "patches/arm7/PokemonDownloaderArm7Patch.h"
|
||||
#include "Arm7Patcher.h"
|
||||
|
||||
void* Arm7Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform) const
|
||||
{
|
||||
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;
|
||||
auto moduleParams = moduleParamsLocator.FindModuleParams(romHeader);
|
||||
SdkVersion sdkVersion = moduleParams ? moduleParams->sdkVersion : 0u;
|
||||
if (!moduleParams && romHeader->gameCode == GAMECODE("AS2E"))
|
||||
{
|
||||
// Spider-Man 2 (USA) is probably the only game without module params
|
||||
sdkVersion = 0x02004F50;
|
||||
}
|
||||
PatchCollection patchCollection;
|
||||
LOG_DEBUG("Arm7 region: 0x%x - 0x%x\n", romHeader->arm7LoadAddress, romHeader->arm7LoadAddress + romHeader->arm7Size);
|
||||
PatchContext patchContext
|
||||
{
|
||||
(void*)romHeader->arm7LoadAddress,
|
||||
romHeader->arm7Size,
|
||||
(romHeader->IsTwlRom()) ? (void*)twlRomHeader->arm7iLoadAddress : nullptr,
|
||||
(romHeader->IsTwlRom()) ? twlRomHeader->arm7iSize : 0,
|
||||
sdkVersion,
|
||||
romHeader->gameCode,
|
||||
loaderPlatform
|
||||
};
|
||||
void* patchSpaceStart = nullptr;
|
||||
if (sdkVersion != 0)
|
||||
{
|
||||
mem_setNtrWramMapping(MEM_NTR_WRAM_ARM9, MEM_NTR_WRAM_ARM9);
|
||||
auto arm7ArenaPatch = new OsGetInitArenaLoPatch();
|
||||
patchCollection.AddPatch(arm7ArenaPatch);
|
||||
|
||||
if (romHeader->unitCode == 0) // seems only present on NITRO, not on HYBRID or LIMITED
|
||||
patchCollection.AddPatch(new DisableArm7WramClearPatch());
|
||||
|
||||
if (sdkVersion.IsTwlSdk())
|
||||
{
|
||||
if (gIsDsiMode && romHeader->IsTwlRom() && twlRomHeader->IsDsiWare())
|
||||
{
|
||||
patchCollection.AddPatch(new Sdk5DsiSdCardRedirectPatch());
|
||||
}
|
||||
else
|
||||
{
|
||||
patchCollection.AddPatch(new CardiDoTaskFromArm9Patch());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
patchCollection.AddPatch(new CardiTaskThreadPatch());
|
||||
}
|
||||
|
||||
if (*(vu32*)0x02FFF00C == GAMECODE("ADAJ") &&
|
||||
romHeader->arm9LoadAddress == 0x02004000 &&
|
||||
romHeader->arm9EntryAddress == 0x02004800)
|
||||
{
|
||||
// pokemon downloader
|
||||
patchCollection.AddPatch(new PokemonDownloaderArm7Patch());
|
||||
}
|
||||
|
||||
if (arm7ArenaPatch->FindPatchTarget(patchContext))
|
||||
{
|
||||
const u32 arm7PatchSpaceSize = 0x800;
|
||||
void* privateWramHeapStart = arm7ArenaPatch->GetArm7PrivateWramArenaLo();
|
||||
if (0x0380F780 - (u32)privateWramHeapStart - 0x2100 >= arm7PatchSpaceSize)
|
||||
{
|
||||
patchSpaceStart = privateWramHeapStart;
|
||||
arm7ArenaPatch->SetArm7PrivateWramArenaLo((u8*)patchSpaceStart + arm7PatchSpaceSize);
|
||||
}
|
||||
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...
|
||||
}
|
||||
else
|
||||
{
|
||||
patchSpaceStart = (void*)(mainMemoryArenaLo | 0x800000); // make sure it ends up in the right place while in 16MB mode
|
||||
}
|
||||
arm7ArenaPatch->SetMainMemoryArenaLo((u8*)mainMemoryArenaLo + arm7PatchSpaceSize);
|
||||
}
|
||||
patchContext.GetPatchHeap().AddFreeSpace(patchSpaceStart, arm7PatchSpaceSize);
|
||||
}
|
||||
|
||||
// The arm7 patcher uses the fact that the ntr wram is mirrored over the entire 03 region.
|
||||
// Since the reserved patch space is smaller than the ntr wram, it will never overlap.
|
||||
// As a result, the patcher can use arm7 addresses for placing patches.
|
||||
// The arm7 will copy the patch data to the actual arm7 location afterwards.
|
||||
|
||||
// If in DSi mode and the rom is a DSi rom, temporarily disable twl wram to let ntr wram cover the entire 03 region.
|
||||
u32 mbk6 = 0;
|
||||
u32 mbk7 = 0;
|
||||
u32 mbk8 = 0;
|
||||
if (gIsDsiMode && romHeader->IsTwlRom())
|
||||
{
|
||||
mbk6 = REG_MBK6;
|
||||
mbk7 = REG_MBK7;
|
||||
mbk8 = REG_MBK8;
|
||||
REG_MBK6 = 0;
|
||||
REG_MBK7 = 0;
|
||||
REG_MBK8 = 0;
|
||||
}
|
||||
|
||||
if (!patchCollection.TryPerformPatches(patchContext))
|
||||
{
|
||||
ErrorDisplay().PrintError("Failed to apply arm7 patches.");
|
||||
}
|
||||
dc_flushAll();
|
||||
dc_drainWriteBuffer();
|
||||
ic_invalidateAll();
|
||||
|
||||
// If in DSi mode and the rom is a DSi rom, restore twl wram.
|
||||
if (gIsDsiMode && romHeader->IsTwlRom())
|
||||
{
|
||||
REG_MBK6 = mbk6;
|
||||
REG_MBK7 = mbk7;
|
||||
REG_MBK8 = mbk8;
|
||||
}
|
||||
|
||||
mem_setNtrWramMapping(MEM_NTR_WRAM_ARM7, MEM_NTR_WRAM_ARM7);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG("Module params not found!\n");
|
||||
}
|
||||
return (u32)patchSpaceStart < 0x03000000 ? nullptr : patchSpaceStart;
|
||||
}
|
||||
13
arm9/source/Arm7Patcher.h
Normal file
13
arm9/source/Arm7Patcher.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
class LoaderPlatform;
|
||||
|
||||
/// @brief Class for patching the arm7 of retail roms.
|
||||
class Arm7Patcher
|
||||
{
|
||||
public:
|
||||
/// @brief Applies arm7 patches using the given \p loaderPlatform.
|
||||
/// @param loaderPlatform The loader platform to use.
|
||||
/// @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;
|
||||
};
|
||||
179
arm9/source/Arm9IoRegisterClearer.cpp
Normal file
179
arm9/source/Arm9IoRegisterClearer.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/gfx/gfx3d.h>
|
||||
#include <libtwl/gfx/gfxWindow.h>
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include <libtwl/ipc/ipcFifo.h>
|
||||
#include <libtwl/ipc/ipcSync.h>
|
||||
#include <libtwl/timer/timer.h>
|
||||
#include <libtwl/dma/dmaNitro.h>
|
||||
#include <libtwl/dma/dmaTwl.h>
|
||||
#include <libtwl/mem/memVram.h>
|
||||
#include <libtwl/card/card.h>
|
||||
#include "Arm9IoRegisterClearer.h"
|
||||
|
||||
void Arm9IoRegisterClearer::ClearNtrIoRegisters(bool isSdkResetSystem) const
|
||||
{
|
||||
REG_IME = 0;
|
||||
REG_IE = 0;
|
||||
// Important! Make sure that all sources of interrupts are disabled as much as possible.
|
||||
// It is possible to accidentally trigger an irq too early in games if bits are set in REG_IF
|
||||
// while it doesn't expect that.
|
||||
ClearGraphicsRegisters(isSdkResetSystem); // vblank, hblank, vcount, gxfifo
|
||||
ClearTimerRegisters(); // timer 0, timer 1, timer 2, timer 3
|
||||
ClearNtrDmaRegisters(); // dma 0, dma 1, dma 2, dma 3
|
||||
REG_MCCNT0 = 0; // rom transfer
|
||||
ipc_disableRecvFifoNotEmptyIrq();
|
||||
ipc_disableSendFifoEmptyIrq();
|
||||
ipc_disableArm7Irq();
|
||||
ipc_clearSendFifo();
|
||||
ipc_disableFifo();
|
||||
REG_KEYCNT = 0;
|
||||
}
|
||||
|
||||
void Arm9IoRegisterClearer::ClearTwlIoRegisters() const
|
||||
{
|
||||
REG_NDMA0SAD = 0;
|
||||
REG_NDMA0DAD = 0;
|
||||
REG_NDMA0TCNT = 0;
|
||||
REG_NDMA0WCNT = 0;
|
||||
REG_NDMA0BCNT = 0;
|
||||
REG_NDMA0FDATA = 0;
|
||||
REG_NDMA0CNT = 0;
|
||||
REG_NDMA1SAD = 0;
|
||||
REG_NDMA1DAD = 0;
|
||||
REG_NDMA1TCNT = 0;
|
||||
REG_NDMA1WCNT = 0;
|
||||
REG_NDMA1BCNT = 0;
|
||||
REG_NDMA1FDATA = 0;
|
||||
REG_NDMA1CNT = 0;
|
||||
REG_NDMA2SAD = 0;
|
||||
REG_NDMA2DAD = 0;
|
||||
REG_NDMA2TCNT = 0;
|
||||
REG_NDMA2WCNT = 0;
|
||||
REG_NDMA2BCNT = 0;
|
||||
REG_NDMA2FDATA = 0;
|
||||
REG_NDMA2CNT = 0;
|
||||
REG_NDMA3SAD = 0;
|
||||
REG_NDMA3DAD = 0;
|
||||
REG_NDMA3TCNT = 0;
|
||||
REG_NDMA3WCNT = 0;
|
||||
REG_NDMA3BCNT = 0;
|
||||
REG_NDMA3FDATA = 0;
|
||||
REG_NDMA3CNT = 0;
|
||||
}
|
||||
|
||||
void Arm9IoRegisterClearer::ClearGraphicsRegisters(bool isSdkResetSystem) const
|
||||
{
|
||||
REG_POWERCNT = 0x820F;
|
||||
REG_DISPSTAT = 0; // vblank, hblank, vcount
|
||||
if (!isSdkResetSystem)
|
||||
{
|
||||
REG_DISPCNT = 0;
|
||||
REG_DISPCNT_SUB = 0;
|
||||
REG_BG0CNT = 0;
|
||||
REG_BG0CNT_SUB = 0;
|
||||
REG_BG1CNT = 0;
|
||||
REG_BG1CNT_SUB = 0;
|
||||
REG_BG2CNT = 0;
|
||||
REG_BG2CNT_SUB = 0;
|
||||
REG_BG3CNT = 0;
|
||||
REG_BG3CNT_SUB = 0;
|
||||
REG_BG0HOFS = 0;
|
||||
REG_BG0HOFS_SUB = 0;
|
||||
REG_BG0VOFS = 0;
|
||||
REG_BG0VOFS_SUB = 0;
|
||||
REG_BG1HOFS = 0;
|
||||
REG_BG1HOFS_SUB = 0;
|
||||
REG_BG1VOFS = 0;
|
||||
REG_BG1VOFS_SUB = 0;
|
||||
REG_BG2HOFS = 0;
|
||||
REG_BG2HOFS_SUB = 0;
|
||||
REG_BG2VOFS = 0;
|
||||
REG_BG2VOFS_SUB = 0;
|
||||
REG_BG3HOFS = 0;
|
||||
REG_BG3HOFS_SUB = 0;
|
||||
REG_BG3VOFS = 0;
|
||||
REG_BG3VOFS_SUB = 0;
|
||||
REG_BG2PA = 0;
|
||||
REG_BG2PB = 0;
|
||||
REG_BG2PC = 0;
|
||||
REG_BG2PD = 0;
|
||||
REG_BG3PA = 0;
|
||||
REG_BG3PB = 0;
|
||||
REG_BG3PC = 0;
|
||||
REG_BG3PD = 0;
|
||||
REG_BG2PA_SUB = 0;
|
||||
REG_BG2PB_SUB = 0;
|
||||
REG_BG2PC_SUB = 0;
|
||||
REG_BG2PD_SUB = 0;
|
||||
REG_BG3PA_SUB = 0;
|
||||
REG_BG3PB_SUB = 0;
|
||||
REG_BG3PC_SUB = 0;
|
||||
REG_BG3PD_SUB = 0;
|
||||
gfx_setWindow0(0, 0, 0, 0);
|
||||
gfx_setWindow1(0, 0, 0, 0);
|
||||
gfx_setSubWindow0(0, 0, 0, 0);
|
||||
gfx_setSubWindow1(0, 0, 0, 0);
|
||||
REG_WININ = 0;
|
||||
REG_WININ_SUB = 0;
|
||||
REG_WINOUT = 0;
|
||||
REG_WINOUT_SUB = 0;
|
||||
REG_MOSAIC = 0;
|
||||
REG_MOSAIC_SUB = 0;
|
||||
REG_BLDCNT = 0;
|
||||
REG_BLDCNT_SUB = 0;
|
||||
REG_BLDALPHA = 0;
|
||||
REG_BLDALPHA_SUB = 0;
|
||||
REG_BLDY = 0;
|
||||
REG_BLDY_SUB = 0;
|
||||
REG_MASTER_BRIGHT = 0;
|
||||
REG_MASTER_BRIGHT_SUB = 0;
|
||||
}
|
||||
REG_DISP3DCNT = 0;
|
||||
REG_DISPCAPCNT = 0;
|
||||
REG_GXSTAT = 0; // gxfifo
|
||||
REG_CLEAR_COLOR = 0;
|
||||
REG_CLEAR_DEPTH = 0x7FFF;
|
||||
REG_DISP_1DOT_DEPTH = 0x7FFF;
|
||||
REG_CLRIMAGE_OFFSET = 0;
|
||||
REG_FOG_COLOR = 0;
|
||||
REG_FOG_OFFSET = 0;
|
||||
REG_ALPHA_TEST_REF = 0;
|
||||
// VRAM A used for arm9 code
|
||||
mem_setVramBMapping(MEM_VRAM_AB_NONE);
|
||||
// VRAM C used for arm7 code
|
||||
mem_setVramDMapping(MEM_VRAM_D_NONE);
|
||||
mem_setVramEMapping(MEM_VRAM_E_NONE);
|
||||
mem_setVramFMapping(MEM_VRAM_FG_NONE);
|
||||
mem_setVramGMapping(MEM_VRAM_FG_NONE);
|
||||
mem_setVramHMapping(MEM_VRAM_H_NONE);
|
||||
mem_setVramIMapping(MEM_VRAM_I_NONE);
|
||||
}
|
||||
|
||||
void Arm9IoRegisterClearer::ClearTimerRegisters() const
|
||||
{
|
||||
REG_TM0CNT_H = 0; // timer 0
|
||||
REG_TM0CNT_L = 0;
|
||||
REG_TM1CNT_H = 0; // timer 1
|
||||
REG_TM1CNT_L = 0;
|
||||
REG_TM2CNT_H = 0; // timer 2
|
||||
REG_TM2CNT_L = 0;
|
||||
REG_TM3CNT_H = 0; // timer 3
|
||||
REG_TM3CNT_L = 0;
|
||||
}
|
||||
|
||||
void Arm9IoRegisterClearer::ClearNtrDmaRegisters() const
|
||||
{
|
||||
REG_DMA0CNT = 0; // dma 0
|
||||
REG_DMA0SAD = 0;
|
||||
REG_DMA0DAD = 0;
|
||||
REG_DMA1CNT = 0; // dma 1
|
||||
REG_DMA1SAD = 0;
|
||||
REG_DMA1DAD = 0;
|
||||
REG_DMA2CNT = 0; // dma 2
|
||||
REG_DMA2SAD = 0;
|
||||
REG_DMA2DAD = 0;
|
||||
REG_DMA3CNT = 0; // dma 3
|
||||
REG_DMA3SAD = 0;
|
||||
REG_DMA3DAD = 0;
|
||||
}
|
||||
19
arm9/source/Arm9IoRegisterClearer.h
Normal file
19
arm9/source/Arm9IoRegisterClearer.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Class for clearing the arm9 IO registers.
|
||||
class Arm9IoRegisterClearer
|
||||
{
|
||||
public:
|
||||
/// @brief Clears the arm9 ntr IO registers.
|
||||
/// When \p isSdkResetSystem is \c true, some IO registers are not cleared to avoid graphical glitches.
|
||||
/// @param isSdkResetSystem \c true if the clear is performed context of OS_ResetSystem, or \c false otherwise.
|
||||
void ClearNtrIoRegisters(bool isSdkResetSystem) const;
|
||||
|
||||
/// @brief Clears the arm9 twl IO registers.
|
||||
void ClearTwlIoRegisters() const;
|
||||
|
||||
private:
|
||||
void ClearGraphicsRegisters(bool isSdkResetSystem) const;
|
||||
void ClearTimerRegisters() const;
|
||||
void ClearNtrDmaRegisters() const;
|
||||
};
|
||||
336
arm9/source/Arm9Patcher.cpp
Normal file
336
arm9/source/Arm9Patcher.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
#include "common.h"
|
||||
#include "ModuleParamsLocator.h"
|
||||
#include "SdkVersion.h"
|
||||
#include "patches/PatchCollection.h"
|
||||
#include "patches/PatchContext.h"
|
||||
#include "sharedMemory.h"
|
||||
#include "patches/arm9/sdk2to4/CardiReadCardPatch.h"
|
||||
#include "patches/arm9/sdk2to4/CardiTryReadCardDmaPatch.h"
|
||||
#include "patches/arm9/sdk5/CardiIsRomDmaAvailablePatch.h"
|
||||
#include "patches/arm9/sdk5/CardiReadCardWithHashInternalAsyncPatch.h"
|
||||
#include "patches/arm9/sdk5/CardiReadRomWithCpuPatch.h"
|
||||
#include "patches/arm9/CardiReadRomIdCorePatch.h"
|
||||
#include "patches/arm9/OSResetSystemPatch.h"
|
||||
#include "patches/arm9/PokemonDownloaderArm9Patch.h"
|
||||
#include "patches/arm9/OverlayPatches/FsStartOverlayHookPatch.h"
|
||||
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectOverlayPatch.h"
|
||||
#include "patches/arm9/OverlayPatches/PokemonBw1/PokemonBw1IrApPatch.h"
|
||||
#include "patches/arm9/OverlayPatches/PokemonBw2/PokemonBw2IrApPatch.h"
|
||||
#include "patches/arm9/OverlayPatches/GoldenSunDarkDawn/GoldenSunDarkDawnOverlayHookPatch.h"
|
||||
#include "SecureSysCallsUnusedSpaceLocator.h"
|
||||
#include "fastSearch.h"
|
||||
#include "gameCode.h"
|
||||
#include "cache.h"
|
||||
#include "ApList.h"
|
||||
#include "patches/platform/LoaderPlatform.h"
|
||||
#include "errorDisplay/ErrorDisplay.h"
|
||||
#include "Arm9Patcher.h"
|
||||
|
||||
#define PARENT_SECTION_START 0x02001000
|
||||
#define PARENT_SECTION_END 0x02003000
|
||||
|
||||
#define REQUIRED_PATCH_HEAP_SPACE 0x500
|
||||
|
||||
typedef void (*uncompress_func_t)(void* compressedEnd);
|
||||
|
||||
static const u32 sMiiUncompressBackwardPatternOld[] = { 0xE3500000, 0x0A000025, 0xE92D00F0, 0xE9100006 }; // mkds beta; version 0x2012774
|
||||
static const u32 sMiiUncompressBackwardPatternOld2[] = { 0xE3500000, 0x0A00002B, 0xE92D00F0, 0xE9100006 }; // asterix & obelix xxl 2; version 0x3017531
|
||||
static const u32 sMiiUncompressBackwardPattern[] = { 0xE3500000, 0x0A000027, 0xE92D00F0, 0xE9100006 }; // mkds
|
||||
static const u32 sMiiUncompressBackwardPatternHybrid[] = { 0xE3500000, 0x0A000029, 0xE92D01F0, 0xE9100006 };
|
||||
|
||||
void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApListEntry* apListEntry,
|
||||
bool isCloneBootRom, const loader_info_t* loaderInfo) const
|
||||
{
|
||||
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;
|
||||
auto moduleParams = moduleParamsLocator.FindModuleParams(romHeader);
|
||||
u32 arm9Size = romHeader->arm9Size;
|
||||
u32 arm9iSize = romHeader->IsTwlRom() ? twlRomHeader->arm9iSize : 0;
|
||||
SdkVersion sdkVersion = moduleParams ? moduleParams->sdkVersion : 0u;
|
||||
u32 compressedEnd = 0;
|
||||
PatchCollection patchCollection;
|
||||
if (moduleParams)
|
||||
{
|
||||
LOG_DEBUG("Module params found at 0x%p\n", moduleParams);
|
||||
LOG_DEBUG("Sdk version: 0x%x\n", moduleParams->sdkVersion);
|
||||
const u32* miiUncompressBackward = nullptr;
|
||||
if (moduleParams->compressedEnd)
|
||||
{
|
||||
const u32* miiUncompressBackwardPattern;
|
||||
if (sdkVersion <= 0x2017532)
|
||||
miiUncompressBackwardPattern = sMiiUncompressBackwardPatternOld;
|
||||
else
|
||||
miiUncompressBackwardPattern = sMiiUncompressBackwardPattern;
|
||||
|
||||
miiUncompressBackward = fastSearch16((const u32*)romHeader->arm9LoadAddress, 0x1000, miiUncompressBackwardPattern);
|
||||
if (!sdkVersion.IsTwlSdk() && !miiUncompressBackward)
|
||||
miiUncompressBackward = fastSearch16((const u32*)romHeader->arm9LoadAddress, 0x1000, sMiiUncompressBackwardPatternOld2);
|
||||
if (sdkVersion.IsTwlSdk() && !miiUncompressBackward)
|
||||
miiUncompressBackward = fastSearch16((const u32*)romHeader->arm9LoadAddress, 0x1000, sMiiUncompressBackwardPatternHybrid);
|
||||
|
||||
if (miiUncompressBackward)
|
||||
{
|
||||
arm9Size = moduleParams->compressedEnd + *(u32*)(moduleParams->compressedEnd - 4) - romHeader->arm9LoadAddress;
|
||||
((uncompress_func_t)miiUncompressBackward)((void*)moduleParams->compressedEnd);
|
||||
compressedEnd = moduleParams->compressedEnd;
|
||||
moduleParams->compressedEnd = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG("MIi_UncompressBackward not found\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (gIsDsiMode && romHeader->IsTwlRom())
|
||||
{
|
||||
auto arm9iModuleParams = (module_params_twl_t*)(romHeader->arm9LoadAddress + twlRomHeader->arm9iModuleParamsAddress);
|
||||
if (arm9iModuleParams->magicBigEndian == MODULE_PARAMS_TWL_MAGIC_BE &&
|
||||
arm9iModuleParams->magicLittleEndian == MODULE_PARAMS_TWL_MAGIC_LE)
|
||||
{
|
||||
if (arm9iModuleParams->compressedEnd)
|
||||
{
|
||||
LOG_DEBUG("Compressed arm9i found\n");
|
||||
if (miiUncompressBackward)
|
||||
{
|
||||
arm9iSize = arm9iModuleParams->compressedEnd + *(u32*)(arm9iModuleParams->compressedEnd - 4) - twlRomHeader->arm9iLoadAddress;
|
||||
((uncompress_func_t)miiUncompressBackward)((void*)arm9iModuleParams->compressedEnd);
|
||||
arm9iModuleParams->compressedEnd = 0;
|
||||
LOG_DEBUG("Decompressed arm9i\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG("Could not decompress arm9i\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG("Module params not found!\n");
|
||||
if (romHeader->gameCode == GAMECODE("AS2E"))
|
||||
{
|
||||
// Spider-Man 2 (USA) is probably the only game without module params
|
||||
sdkVersion = 0x02004F50;
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("Arm9 region: 0x%x - 0x%x\n", romHeader->arm9LoadAddress, romHeader->arm9LoadAddress + arm9Size);
|
||||
PatchContext patchContext
|
||||
{
|
||||
(void*)romHeader->arm9LoadAddress,
|
||||
arm9Size,
|
||||
romHeader->IsTwlRom() ? (void*)twlRomHeader->arm9iLoadAddress : nullptr,
|
||||
arm9iSize,
|
||||
sdkVersion,
|
||||
romHeader->gameCode,
|
||||
loaderPlatform
|
||||
};
|
||||
if (sdkVersion != 0)
|
||||
{
|
||||
if (*(vu32*)0x02FFF00C == GAMECODE("ADAJ") &&
|
||||
romHeader->arm9LoadAddress == 0x02004000 &&
|
||||
romHeader->arm9EntryAddress == 0x02004800)
|
||||
{
|
||||
// pokemon downloader
|
||||
patchContext.GetPatchHeap().AddFreeSpace((void*)0x023FF160, 0x6A0);
|
||||
patchCollection.AddPatch(new PokemonDownloaderArm9Patch(loaderInfo));
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 availableParentSize = 0;
|
||||
if (isCloneBootRom)
|
||||
{
|
||||
availableParentSize = GetAvailableParentSectionSpace();
|
||||
LOG_DEBUG("0x%X bytes available in .parent section\n", availableParentSize);
|
||||
}
|
||||
|
||||
if (availableParentSize >= REQUIRED_PATCH_HEAP_SPACE)
|
||||
{
|
||||
patchContext.GetPatchHeap().AddFreeSpace(
|
||||
(void*)(PARENT_SECTION_END - REQUIRED_PATCH_HEAP_SPACE),
|
||||
REQUIRED_PATCH_HEAP_SPACE);
|
||||
LOG_DEBUG("Placing patches in .parent section\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
SecureSysCallsUnusedSpaceLocator secureSysCallsUnusedSpaceLocator;
|
||||
secureSysCallsUnusedSpaceLocator.FindUnusedSpace(romHeader, patchContext.GetPatchHeap());
|
||||
}
|
||||
}
|
||||
|
||||
if (sdkVersion.IsTwlSdk())
|
||||
{
|
||||
if (!(romHeader->IsTwlRom() && twlRomHeader->IsDsiWare()))
|
||||
{
|
||||
// if ((romHeader->unitCode & 3) != 3)
|
||||
{
|
||||
patchCollection.AddPatch(new CardiIsRomDmaAvailablePatch());
|
||||
}
|
||||
patchCollection.AddPatch(new CardiReadRomWithCpuPatch());
|
||||
|
||||
if (gIsDsiMode && (romHeader->unitCode & 2))
|
||||
{
|
||||
patchCollection.AddPatch(new CardiReadCardWithHashInternalAsyncPatch());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
patchCollection.AddPatch(new CardiReadCardPatch());
|
||||
patchCollection.AddPatch(new CardiTryReadCardDmaPatch());
|
||||
}
|
||||
|
||||
patchCollection.AddPatch(new CardiReadRomIdCorePatch());
|
||||
|
||||
patchCollection.AddPatch(new OSResetSystemPatch(loaderInfo));
|
||||
|
||||
OverlayHookPatch* overlayHookPatch;
|
||||
if (romHeader->gameCode == GAMECODE("BO5P") ||
|
||||
romHeader->gameCode == GAMECODE("BO5E") ||
|
||||
romHeader->gameCode == GAMECODE("BO5J"))
|
||||
{
|
||||
overlayHookPatch = new GoldenSunDarkDawnOverlayHookPatch();
|
||||
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(334, 0, DSProtectVersion::v2_01, ~0u));
|
||||
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(335, 0, DSProtectVersion::v2_01s, ~0u));
|
||||
}
|
||||
else
|
||||
{
|
||||
overlayHookPatch = new FsStartOverlayHookPatch();
|
||||
if (apListEntry)
|
||||
{
|
||||
u32 regularOverlayId = apListEntry->GetRegularOverlayId();
|
||||
if (regularOverlayId != AP_LIST_OVERLAY_ID_INVALID)
|
||||
{
|
||||
if (regularOverlayId == AP_LIST_OVERLAY_ID_STATIC_ARM9)
|
||||
{
|
||||
LOG_WARNING("Patching DSProtect in main memory currently not supported\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(
|
||||
regularOverlayId, apListEntry->GetRegularOffset(),
|
||||
apListEntry->GetDSProtectVersion(), apListEntry->GetDSProtectFunctionMask()));
|
||||
}
|
||||
}
|
||||
u32 sOverlayId = apListEntry->GetSOverlayId();
|
||||
if (sOverlayId != AP_LIST_OVERLAY_ID_INVALID)
|
||||
{
|
||||
if (sOverlayId == AP_LIST_OVERLAY_ID_STATIC_ARM9)
|
||||
{
|
||||
LOG_WARNING("Patching DSProtect in main memory currently not supported\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto version = apListEntry->GetDSProtectVersion();
|
||||
if (version < DSProtectVersion::v2_00s)
|
||||
{
|
||||
version = (DSProtectVersion)((u32)version - (u32)DSProtectVersion::v2_00 + (u32)DSProtectVersion::v2_00s);
|
||||
}
|
||||
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(
|
||||
sOverlayId, apListEntry->GetSOffset(), version, ~0u));
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (romHeader->gameCode)
|
||||
{
|
||||
// Pokemon Black & White
|
||||
case GAMECODE("IRAD"):
|
||||
case GAMECODE("IRAF"):
|
||||
case GAMECODE("IRAI"):
|
||||
case GAMECODE("IRAJ"):
|
||||
case GAMECODE("IRAK"):
|
||||
case GAMECODE("IRAO"):
|
||||
case GAMECODE("IRAS"):
|
||||
case GAMECODE("IRBD"):
|
||||
case GAMECODE("IRBF"):
|
||||
case GAMECODE("IRBI"):
|
||||
case GAMECODE("IRBJ"):
|
||||
case GAMECODE("IRBK"):
|
||||
case GAMECODE("IRBO"):
|
||||
case GAMECODE("IRBS"):
|
||||
{
|
||||
overlayHookPatch->AddOverlayPatch(new PokemonBw1IrApPatch());
|
||||
break;
|
||||
}
|
||||
// Pokemon Black & White 2
|
||||
// todo: IRDJ and IREJ have two revisions and the first one seems to be different
|
||||
case GAMECODE("IRDD"):
|
||||
case GAMECODE("IRDF"):
|
||||
case GAMECODE("IRDI"):
|
||||
case GAMECODE("IRDK"):
|
||||
case GAMECODE("IRDO"):
|
||||
case GAMECODE("IRDS"):
|
||||
case GAMECODE("IRED"):
|
||||
case GAMECODE("IREF"):
|
||||
case GAMECODE("IREI"):
|
||||
case GAMECODE("IREK"):
|
||||
case GAMECODE("IREO"):
|
||||
case GAMECODE("IRES"):
|
||||
{
|
||||
overlayHookPatch->AddOverlayPatch(new PokemonBw2IrApPatch());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
patchCollection.AddPatch(overlayHookPatch);
|
||||
|
||||
if (moduleParams && compressedEnd != 0)
|
||||
{
|
||||
AddRestoreCompressedEndPatch(
|
||||
patchContext,
|
||||
romHeader->arm9AutoLoadDoneHookAddress,
|
||||
&moduleParams->compressedEnd,
|
||||
compressedEnd);
|
||||
}
|
||||
}
|
||||
if (!patchCollection.TryPerformPatches(patchContext))
|
||||
{
|
||||
ErrorDisplay().PrintError("Failed to apply arm9 patches.");
|
||||
}
|
||||
dc_flushAll();
|
||||
dc_drainWriteBuffer();
|
||||
ic_invalidateAll();
|
||||
}
|
||||
|
||||
void Arm9Patcher::AddRestoreCompressedEndPatch(PatchContext& patchContext,
|
||||
u32 arm9AutoLoadDoneHookAddress, u32* moduleParamsCompressedEnd, u32 originalCompressedEndValue) const
|
||||
{
|
||||
// Restore compressedEnd after first boot.
|
||||
// This is necessary to not break cloneboot.
|
||||
const u32 compressedEndFixCode[] =
|
||||
{
|
||||
0xE59F0014, // ldr r0,= moduleParamsCompressedEnd
|
||||
0xE59F1014, // ldr r1,= originalCompressedEndValue
|
||||
0xE5801000, // str r1, [r0]
|
||||
0xE59F0010, // ldr r0,= arm9AutoLoadDoneHookAddress
|
||||
0xE59F1000, // ldr r1, ret
|
||||
0xE5801000, // str r1, [r0]
|
||||
0xE12FFF1E, // ret: bx lr
|
||||
(u32)moduleParamsCompressedEnd,
|
||||
originalCompressedEndValue,
|
||||
arm9AutoLoadDoneHookAddress
|
||||
};
|
||||
|
||||
void* fixDst = patchContext.GetPatchHeap().Alloc(sizeof(compressedEndFixCode));
|
||||
memcpy(fixDst, compressedEndFixCode, sizeof(compressedEndFixCode));
|
||||
*(u32*)arm9AutoLoadDoneHookAddress = 0xEA000000u | ((((int)fixDst - (int)arm9AutoLoadDoneHookAddress - 8) >> 2) & 0xFFFFFF);
|
||||
}
|
||||
|
||||
u32 Arm9Patcher::GetAvailableParentSectionSpace() const
|
||||
{
|
||||
u32 availableParentSize = 0;
|
||||
for (u32 ptr = PARENT_SECTION_END; ptr > PARENT_SECTION_START; ptr -= 32)
|
||||
{
|
||||
u32* segment = (u32*)(ptr - 32);
|
||||
if (segment[0] != 0 || segment[1] != 0 || segment[2] != 0 || segment[3] != 0 ||
|
||||
segment[4] != 0 || segment[5] != 0 || segment[6] != 0 || segment[7] != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
availableParentSize += 32;
|
||||
}
|
||||
|
||||
return availableParentSize;
|
||||
}
|
||||
24
arm9/source/Arm9Patcher.h
Normal file
24
arm9/source/Arm9Patcher.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "LoaderInfo.h"
|
||||
|
||||
class ApListEntry;
|
||||
class LoaderPlatform;
|
||||
class PatchContext;
|
||||
|
||||
/// @brief Class for patching the arm9 of retail roms.
|
||||
class Arm9Patcher
|
||||
{
|
||||
public:
|
||||
/// @brief Applies arm9 patches using the given \p loaderPlatform.
|
||||
/// @param loaderPlatform The loader platform to use.
|
||||
/// @param apListEntry The AP list entry for the rom being loaded, or \c nullptr if there is none.
|
||||
/// @param isCloneBootRom \c true if the rom being loaded is a clone boot rom, or \c false otherwise.
|
||||
/// @param loaderInfo The loader info to use.
|
||||
void ApplyPatches(const LoaderPlatform* loaderPlatform, const ApListEntry* apListEntry,
|
||||
bool isCloneBootRom, const loader_info_t* loaderInfo) const;
|
||||
|
||||
private:
|
||||
void AddRestoreCompressedEndPatch(PatchContext& patchContext,
|
||||
u32 arm9AutoLoadDoneHookAddress, u32* moduleParamsCompressedEnd, u32 originalCompressedEndValue) const;
|
||||
u32 GetAvailableParentSectionSpace() const;
|
||||
};
|
||||
59
arm9/source/ModuleParamsLocator.cpp
Normal file
59
arm9/source/ModuleParamsLocator.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "common.h"
|
||||
#include "ModuleParamsLocator.h"
|
||||
|
||||
module_params_ntr_t* ModuleParamsLocator::FindModuleParams(const nds_header_ntr_t* romHeader)
|
||||
{
|
||||
//todo: static footer
|
||||
module_params_ntr_t* moduleParams = nullptr;
|
||||
if (romHeader->arm9ModuleParamsAddress != 0)
|
||||
{
|
||||
moduleParams = (module_params_ntr_t*)(romHeader->arm9LoadAddress + romHeader->arm9ModuleParamsAddress);
|
||||
if (moduleParams->magicBigEndian != MODULE_PARAMS_NTR_MAGIC_BE ||
|
||||
moduleParams->magicLittleEndian != MODULE_PARAMS_NTR_MAGIC_LE)
|
||||
{
|
||||
// try again by subtracting arm9 rom address
|
||||
moduleParams = (module_params_ntr_t*)((u8*)moduleParams - romHeader->arm9RomOffset);
|
||||
if (moduleParams->magicBigEndian != MODULE_PARAMS_NTR_MAGIC_BE ||
|
||||
moduleParams->magicLittleEndian != MODULE_PARAMS_NTR_MAGIC_LE)
|
||||
{
|
||||
LOG_DEBUG("Module params not found at header specified offset 0x%x\n", romHeader->arm9ModuleParamsAddress);
|
||||
moduleParams = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG("Header specifies no module params address\n");
|
||||
}
|
||||
|
||||
if (!moduleParams)
|
||||
{
|
||||
// try searching for the module params as not all roms have the address in the header
|
||||
// it should be within the first 0x1000 bytes of the arm9
|
||||
for (u32 i = 0; i < 0x1000; i += 4)
|
||||
{
|
||||
u32* ptr = (u32*)(romHeader->arm9LoadAddress + i);
|
||||
if (ptr[0] == MODULE_PARAMS_NTR_MAGIC_BE && ptr[1] == MODULE_PARAMS_NTR_MAGIC_LE)
|
||||
{
|
||||
moduleParams = (module_params_ntr_t*)((u8*)ptr + 8 - sizeof(module_params_ntr_t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!moduleParams)
|
||||
{
|
||||
// as a last resort, scan the entire arm9 binary
|
||||
for (u32 i = 0; i < romHeader->arm9Size; i += 4)
|
||||
{
|
||||
u32* ptr = (u32*)(romHeader->arm9LoadAddress + i);
|
||||
if (ptr[0] == MODULE_PARAMS_NTR_MAGIC_BE && ptr[1] == MODULE_PARAMS_NTR_MAGIC_LE)
|
||||
{
|
||||
moduleParams = (module_params_ntr_t*)((u8*)ptr + 8 - sizeof(module_params_ntr_t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return moduleParams;
|
||||
}
|
||||
13
arm9/source/ModuleParamsLocator.h
Normal file
13
arm9/source/ModuleParamsLocator.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "ndsHeader.h"
|
||||
#include "moduleParams.h"
|
||||
|
||||
/// @brief Class for finding the module params of a retail arm9.
|
||||
class ModuleParamsLocator
|
||||
{
|
||||
public:
|
||||
/// @brief Searches for the module params of the arm9 of the rom with the given \p romHeader.
|
||||
/// @param romHeader The header of the rom.
|
||||
/// @return A pointer to the found module params, or \c nullptr if the module params could not be found.
|
||||
module_params_ntr_t* FindModuleParams(const nds_header_ntr_t* romHeader);
|
||||
};
|
||||
136
arm9/source/SecureSysCallsUnusedSpaceLocator.cpp
Normal file
136
arm9/source/SecureSysCallsUnusedSpaceLocator.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "common.h"
|
||||
#include "gameCode.h"
|
||||
#include <algorithm>
|
||||
#include "patches/PatchHeap.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "SecureSysCallsUnusedSpaceLocator.h"
|
||||
|
||||
#define THUMB_MOVS_R0_R1 THUMB_MOVS_REG(0, 1)
|
||||
#define THUMB_MOVS_R2_0 THUMB_MOVS_IMM(2, 0)
|
||||
|
||||
static const u16 sSvcSoftResetPattern[] = { THUMB_SVC(0), THUMB_BX_LR };
|
||||
static const u16 sSvcWaitByLoopPattern[] = { THUMB_SVC(3), THUMB_BX_LR };
|
||||
static const u16 sSvcWaitIntrPattern[] = { THUMB_MOVS_R2_0, THUMB_SVC(4), THUMB_BX_LR };
|
||||
static const u16 sSvcWaitVBlankIntrPattern[] = { THUMB_MOVS_R2_0, THUMB_SVC(5), THUMB_BX_LR };
|
||||
static const u16 sSvcHaltPattern[] = { THUMB_SVC(6), THUMB_BX_LR };
|
||||
static const u16 sSvcDivPattern[] = { THUMB_SVC(9), THUMB_BX_LR };
|
||||
static const u16 sSvcDivRemPattern[] = { THUMB_SVC(9), THUMB_MOVS_R0_R1, THUMB_BX_LR };
|
||||
static const u16 sSvcCpuSetPattern[] = { THUMB_SVC(0xB), THUMB_BX_LR };
|
||||
static const u16 sSvcCpuSetFastPattern[] = { THUMB_SVC(0xC), THUMB_BX_LR };
|
||||
static const u16 sSvcSqrtPattern[] = { THUMB_SVC(0xD), THUMB_BX_LR };
|
||||
static const u16 sSvcGetCrc16Pattern[] = { THUMB_SVC(0xE), THUMB_BX_LR };
|
||||
static const u16 sSvcIsMainMemExpandedPattern[] = { THUMB_SVC(0xF), THUMB_BX_LR };
|
||||
static const u16 sSvcUnpackBitsPattern[] = { THUMB_SVC(0x10), THUMB_BX_LR };
|
||||
static const u16 sSvcUncompressLz8Pattern[] = { THUMB_SVC(0x11), THUMB_BX_LR };
|
||||
static const u16 sSvcUncompressLz16FromDevicePattern[] = { THUMB_SVC(0x12), THUMB_BX_LR };
|
||||
static const u16 sSvcUncompressHuffmanFromDevicePattern[] = { THUMB_SVC(0x13), THUMB_BX_LR };
|
||||
static const u16 sSvcUncompressRl8Pattern[] = { THUMB_SVC(0x14), THUMB_BX_LR };
|
||||
static const u16 sSvcUncompressRl16FromDevicePattern[] = { THUMB_SVC(0x15), THUMB_BX_LR };
|
||||
|
||||
struct svc_pattern_t
|
||||
{
|
||||
const u16* pattern;
|
||||
u32 length;
|
||||
};
|
||||
|
||||
static const std::array<const svc_pattern_t, 18> sSvcPatterns
|
||||
{
|
||||
svc_pattern_t { sSvcSoftResetPattern, sizeof(sSvcSoftResetPattern) },
|
||||
svc_pattern_t { sSvcWaitByLoopPattern, sizeof(sSvcWaitByLoopPattern) },
|
||||
svc_pattern_t { sSvcWaitIntrPattern, sizeof(sSvcWaitIntrPattern) },
|
||||
svc_pattern_t { sSvcWaitVBlankIntrPattern, sizeof(sSvcWaitVBlankIntrPattern) },
|
||||
svc_pattern_t { sSvcHaltPattern, sizeof(sSvcHaltPattern) },
|
||||
svc_pattern_t { sSvcDivPattern, sizeof(sSvcDivPattern) },
|
||||
svc_pattern_t { sSvcDivRemPattern, sizeof(sSvcDivRemPattern) },
|
||||
svc_pattern_t { sSvcCpuSetPattern, sizeof(sSvcCpuSetPattern) },
|
||||
svc_pattern_t { sSvcCpuSetFastPattern, sizeof(sSvcCpuSetFastPattern) },
|
||||
svc_pattern_t { sSvcSqrtPattern, sizeof(sSvcSqrtPattern) },
|
||||
svc_pattern_t { sSvcGetCrc16Pattern, sizeof(sSvcGetCrc16Pattern) },
|
||||
svc_pattern_t { sSvcIsMainMemExpandedPattern, sizeof(sSvcIsMainMemExpandedPattern) },
|
||||
svc_pattern_t { sSvcUnpackBitsPattern, sizeof(sSvcUnpackBitsPattern) },
|
||||
svc_pattern_t { sSvcUncompressLz8Pattern, sizeof(sSvcUncompressLz8Pattern) },
|
||||
svc_pattern_t { sSvcUncompressLz16FromDevicePattern, sizeof(sSvcUncompressLz16FromDevicePattern) },
|
||||
svc_pattern_t { sSvcUncompressHuffmanFromDevicePattern, sizeof(sSvcUncompressHuffmanFromDevicePattern) },
|
||||
svc_pattern_t { sSvcUncompressRl8Pattern, sizeof(sSvcUncompressRl8Pattern) },
|
||||
svc_pattern_t { sSvcUncompressRl16FromDevicePattern, sizeof(sSvcUncompressRl16FromDevicePattern) }
|
||||
};
|
||||
|
||||
const u16* SecureSysCallsUnusedSpaceLocator::FindPattern(const u16* data, u32 length, const u16* pattern, u32 patternLength) const
|
||||
{
|
||||
length >>= 1;
|
||||
patternLength >>= 1;
|
||||
for (u32 i = 0; i < length - patternLength; i++)
|
||||
{
|
||||
bool ok = true;
|
||||
for (u32 j = 0; j < patternLength; j++)
|
||||
{
|
||||
if (data[i + j] != pattern[j])
|
||||
{
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok)
|
||||
return &data[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SecureSysCallsUnusedSpaceLocator::FindUnusedSpace(const nds_header_ntr_t* romHeader, PatchHeap& patchHeap) const
|
||||
{
|
||||
if (romHeader->arm9RomOffset != 0x4000)
|
||||
return;
|
||||
|
||||
u32 secureStart = romHeader->arm9LoadAddress;
|
||||
|
||||
if (*(u32*)(secureStart + 0x800) == 0x4770DF00)
|
||||
{
|
||||
// secure area for development purposes has this area empty
|
||||
patchHeap.AddFreeSpace((void*)secureStart, 0x800);
|
||||
LOG_DEBUG("Added free space starting at 0x%x with size 0x%x\n", secureStart, 0x800);
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<svc_pattern_t, sSvcPatterns.size()> patternLocations;
|
||||
|
||||
for (u32 i = 0; i < sSvcPatterns.size(); i++)
|
||||
{
|
||||
patternLocations[i].pattern = FindPattern((const u16*)secureStart, 0x800, sSvcPatterns[i].pattern, sSvcPatterns[i].length);
|
||||
patternLocations[i].length = sSvcPatterns[i].length;
|
||||
}
|
||||
|
||||
std::qsort(patternLocations.begin(), patternLocations.size(), sizeof(svc_pattern_t), [](const void* a, const void* b)
|
||||
{
|
||||
const auto cmp = static_cast<const svc_pattern_t*>(a)->pattern <=> static_cast<const svc_pattern_t*>(b)->pattern;
|
||||
if (cmp < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (cmp > 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
u32 current = secureStart;
|
||||
for (u32 i = 0; i < sSvcPatterns.size(); i++)
|
||||
{
|
||||
if (!patternLocations[i].pattern)
|
||||
continue;
|
||||
u32 freeSpaceEnd = (u32)patternLocations[i].pattern & ~3;
|
||||
if (current < freeSpaceEnd)
|
||||
{
|
||||
u32 freeSpace = freeSpaceEnd - current;
|
||||
patchHeap.AddFreeSpace((void*)current, freeSpace);
|
||||
LOG_DEBUG("Added free space starting at 0x%x with size 0x%x\n", current, freeSpace);
|
||||
}
|
||||
current = ((u32)patternLocations[i].pattern + patternLocations[i].length + 3) & ~3;
|
||||
}
|
||||
if (current < secureStart + 0x800)
|
||||
{
|
||||
u32 freeSpace = secureStart + 0x800 - current;
|
||||
patchHeap.AddFreeSpace((void*)current, freeSpace);
|
||||
LOG_DEBUG("Added free space starting at 0x%x with size 0x%x\n", current, freeSpace);
|
||||
}
|
||||
}
|
||||
18
arm9/source/SecureSysCallsUnusedSpaceLocator.h
Normal file
18
arm9/source/SecureSysCallsUnusedSpaceLocator.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "ndsHeader.h"
|
||||
|
||||
class PatchHeap;
|
||||
|
||||
/// @brief Class for finding the unused space between the system calls in the arm9 secure area.
|
||||
class SecureSysCallsUnusedSpaceLocator
|
||||
{
|
||||
public:
|
||||
/// @brief Searches for the unused space between the system calls in the arm9 secure area of the rom
|
||||
/// with the given \p romHeader and adds the unused space to the given \p patchHeap.
|
||||
/// @param romHeader The rom header.
|
||||
/// @param patchHeap The patch heap to add the unused space to.
|
||||
void FindUnusedSpace(const nds_header_ntr_t* romHeader, PatchHeap& patchHeap) const;
|
||||
|
||||
private:
|
||||
const u16* FindPattern(const u16* data, u32 length, const u16* pattern, u32 patternLength) const;
|
||||
};
|
||||
9
arm9/source/arm9Clock.h
Normal file
9
arm9/source/arm9Clock.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
enum class ScfgArm9Clock
|
||||
{
|
||||
Nitro67MHz = 0,
|
||||
Twl134MHz = 1
|
||||
};
|
||||
|
||||
extern "C" void scfg_setArm9Clock(ScfgArm9Clock arm9Clock);
|
||||
32
arm9/source/arm9Clock.s
Normal file
32
arm9/source/arm9Clock.s
Normal file
@@ -0,0 +1,32 @@
|
||||
.section ".itcm", "ax"
|
||||
.arm
|
||||
|
||||
#define REG_SCFG_CLK 0x04004004
|
||||
#define SCFG_CLK_CPU_SPEED 1
|
||||
|
||||
.global scfg_setArm9Clock
|
||||
.type scfg_setArm9Clock, %function
|
||||
scfg_setArm9Clock:
|
||||
ldr r3,= REG_SCFG_CLK
|
||||
ldrh r2, [r3]
|
||||
and r1, r2, #SCFG_CLK_CPU_SPEED
|
||||
cmp r1, r0
|
||||
bxeq lr // requested speed already set
|
||||
|
||||
mrs r12, cpsr
|
||||
orr r1, r12, #0xC0 // disable irq and fiq
|
||||
msr cpsr, r1
|
||||
|
||||
bic r2, r2, #SCFG_CLK_CPU_SPEED
|
||||
orr r2, r2, r0
|
||||
|
||||
strh r2, [r3]
|
||||
|
||||
// allow the clock switch to stabilize
|
||||
mov r0, #8
|
||||
1:
|
||||
subs r0, r0, #1
|
||||
bne 1b
|
||||
|
||||
msr cpsr, r12
|
||||
bx lr
|
||||
26
arm9/source/cache.h
Normal file
26
arm9/source/cache.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Invalidates the entire instruction cache.
|
||||
extern void ic_invalidateAll(void);
|
||||
|
||||
/// @brief Drains the write buffer.
|
||||
extern void dc_drainWriteBuffer(void);
|
||||
|
||||
/// @brief Invalidates the entire data cache.
|
||||
extern void dc_invalidateAll(void);
|
||||
|
||||
/// @brief Flushes the entire data cache.
|
||||
extern void dc_flushAll(void);
|
||||
|
||||
/// @brief Invalidates the data cache in the given range.
|
||||
/// @param ptr A pointer to the memory block to invalidate. Should be 32-byte aligned.
|
||||
/// @param byteCount The number of bytes to invalidate. Will be rounded up to 32-byte multiples.
|
||||
extern void dc_invalidateRange(void* ptr, u32 byteCount);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
64
arm9/source/cache.s
Normal file
64
arm9/source/cache.s
Normal file
@@ -0,0 +1,64 @@
|
||||
.text
|
||||
.arm
|
||||
|
||||
// ARM DDI 0201D, page 3-11
|
||||
.global dc_flushAll
|
||||
.type dc_flushAll, %function
|
||||
dc_flushAll:
|
||||
// Temp register to set to 0. Needed for write buffer drain
|
||||
mov r3, #0
|
||||
// Initialize segment counter outer_loop
|
||||
mov r1, #0
|
||||
outer_loop:
|
||||
|
||||
// Initialize line counter inner_loop
|
||||
mov r0, #0
|
||||
inner_loop:
|
||||
orr r2, r1, r0 // Generate segment and line address
|
||||
mcr p15, 0, r3, c7, c10, 4 // Drain write buffer. See errata ARM946-PRDC-000592 5.0, section 4.8
|
||||
mcr p15, 0, r2, c7, c14, 2 // Clean and flush the line
|
||||
add r0, r0, #0x20 // Increment to next line
|
||||
cmp r0, #0x400 // (data cache size / entries)
|
||||
bne inner_loop
|
||||
|
||||
add r1, r1, #0x40000000 // Increment segment counter
|
||||
cmp r1, #0x0
|
||||
bne outer_loop
|
||||
|
||||
bx lr
|
||||
|
||||
.global dc_invalidateRange
|
||||
.type dc_invalidateRange, %function
|
||||
dc_invalidateRange:
|
||||
add r1, r1, r0
|
||||
bic r0, r0, #0x1F
|
||||
1:
|
||||
mcr p15, 0, r0, c7, c6, 1
|
||||
add r0, r0, #32
|
||||
cmp r0, r1
|
||||
blt 1b
|
||||
bx lr
|
||||
|
||||
.global dc_drainWriteBuffer
|
||||
.type dc_drainWriteBuffer, %function
|
||||
dc_drainWriteBuffer:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c10, 4
|
||||
bx lr
|
||||
|
||||
.global dc_invalidateAll
|
||||
.type dc_invalidateAll, %function
|
||||
dc_invalidateAll:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c6, 0
|
||||
bx lr
|
||||
|
||||
.global ic_invalidateAll
|
||||
.type ic_invalidateAll, %function
|
||||
ic_invalidateAll:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c5, 0
|
||||
bx lr
|
||||
|
||||
.pool
|
||||
.end
|
||||
22
arm9/source/common.h
Normal file
22
arm9/source/common.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <nds.h>
|
||||
|
||||
extern u16 gIsDsiMode;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "globalHeap.h"
|
||||
#include "logger/ILogger.h"
|
||||
|
||||
extern ILogger* gLogger;
|
||||
|
||||
#define MAX_COMPILED_LOG_LEVEL LogLevel::All
|
||||
|
||||
#define LOG_FATAL(...) if (LogLevel::Fatal < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Fatal, __VA_ARGS__)
|
||||
#define LOG_ERROR(...) if (LogLevel::Error < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Error, __VA_ARGS__)
|
||||
#define LOG_WARNING(...) if (LogLevel::Warning < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Warning, __VA_ARGS__)
|
||||
#define LOG_INFO(...) if (LogLevel::Info < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Info, __VA_ARGS__)
|
||||
#define LOG_DEBUG(...) if (LogLevel::Debug < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Debug, __VA_ARGS__)
|
||||
#define LOG_TRACE(...) if (LogLevel::Trace < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Trace, __VA_ARGS__)
|
||||
|
||||
#endif
|
||||
118
arm9/source/crt0.s
Normal file
118
arm9/source/crt0.s
Normal file
@@ -0,0 +1,118 @@
|
||||
.section ".crt0", "ax"
|
||||
.arm
|
||||
|
||||
.global _start
|
||||
.type _start, %function
|
||||
_start:
|
||||
// disable irqs
|
||||
ldr r0,= 0x04000208
|
||||
strb r0, [r0]
|
||||
// configure cp15
|
||||
// disable itcm, dtcm, caches and mpu
|
||||
ldr r0,= 0x00002078
|
||||
mcr p15, 0, r0, c1, c0
|
||||
mov r0, #0
|
||||
// invalidate entire icache
|
||||
mcr p15, 0, r0, c7, c5, 0
|
||||
// invalidate entire dcache
|
||||
mcr p15, 0, r0, c7, c6, 0
|
||||
// drain write buffer
|
||||
mcr p15, 0, r0, c7, c10, 4
|
||||
// move dtcm in place
|
||||
ldr r0,= __dtcm_start + 0xA
|
||||
mcr p15, 0, r0, c9, c1, 0
|
||||
// setup itcm to cover the first 32MB of memory
|
||||
mov r0, #0x20
|
||||
mcr p15, 0, r0, c9, c1, 1
|
||||
// mpu region 0: IO, Palette, VRAM, OAM (64 MB)
|
||||
ldr r0,= ((1 | (25 << 1)) + 0x04000000)
|
||||
mcr p15, 0, r0, c6, c0, 0
|
||||
// mpu region 1: Main Memory + TWL WRAM (32 MB)
|
||||
ldr r0,= ((1 | (24 << 1)) + 0x02000000)
|
||||
mcr p15, 0, r0, c6, c1, 0
|
||||
// mpu region 2: GBA slot
|
||||
ldr r0,= ((1 | (24 << 1)) + 0x08000000)
|
||||
mcr p15, 0, r0, c6, c2, 0
|
||||
// mpu region 3: Disabled
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c6, c3, 0
|
||||
// mpu region 4: Disabled
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c6, c4, 0
|
||||
// mpu region 5: ITCM (32 KB)
|
||||
ldr r0,= ((1 | (14 << 1)) + __itcm_start)
|
||||
mcr p15, 0, r0, c6, c5, 0
|
||||
// mpu region 6: DTCM (16 KB)
|
||||
ldr r0,= ((1 | (13 << 1)) + __dtcm_start)
|
||||
mcr p15, 0, r0, c6, c6, 0
|
||||
// mpu region 7: LCDC VRAM A (128 KB)
|
||||
ldr r0,= (1 | (16 << 1) | 0x06800000)
|
||||
mcr p15, 0, r0, c6, c7, 0
|
||||
// data permissions
|
||||
ldr r0,= 0x33300333
|
||||
mcr p15, 0, r0, c5, c0, 2
|
||||
// code permissions
|
||||
ldr r0,= 0x30300330
|
||||
mcr p15, 0, r0, c5, c0, 3
|
||||
// dcache
|
||||
ldr r0,= 0b10000010
|
||||
mcr p15, 0, r0, c2, c0, 0
|
||||
// icache
|
||||
ldr r0,= 0b10000010
|
||||
mcr p15, 0, r0, c2, c0, 1
|
||||
// write buffer
|
||||
ldr r0,= 0b10000010
|
||||
mcr p15, 0, r0, c3, c0, 0
|
||||
|
||||
// turn back on itcm, dtcm, cache and mpu
|
||||
// keep data cache off
|
||||
ldr r0,= 0x00057079 //0x0005707D
|
||||
mcr p15, 0, r0, c1, c0
|
||||
|
||||
// copy itcm in place
|
||||
ldr r0,= __itcm_lma
|
||||
ldr r2,= __itcm_start
|
||||
ldr r1,= __itcm_end
|
||||
subs r1, r1, r2
|
||||
beq itcm_done
|
||||
1:
|
||||
ldmia r0!, {r3-r10}
|
||||
stmia r2!, {r3-r10}
|
||||
subs r1, #0x20
|
||||
bgt 1b
|
||||
itcm_done:
|
||||
|
||||
// copy dtcm in place
|
||||
ldr r0,= __dtcm_lma
|
||||
ldr r2,= __dtcm_start
|
||||
ldr r1,= __dtcm_end
|
||||
subs r1, r1, r2
|
||||
beq dtcm_done
|
||||
1:
|
||||
ldmia r0!, {r3-r10}
|
||||
stmia r2!, {r3-r10}
|
||||
subs r1, #0x20
|
||||
bgt 1b
|
||||
dtcm_done:
|
||||
|
||||
// clear bss
|
||||
ldr r0,= __bss_start
|
||||
ldr r1,= __bss_end
|
||||
cmp r0, r1
|
||||
beq bss_done
|
||||
mov r2, #0
|
||||
1:
|
||||
str r2, [r0], #4
|
||||
cmp r0, r1
|
||||
bne 1b
|
||||
bss_done:
|
||||
msr cpsr_c, #0x13 // svc
|
||||
ldr sp,= __dtcm_start + 0x3FC0
|
||||
msr cpsr_c, #0x12 // irq
|
||||
ldr sp,= __dtcm_start + 0x3F80
|
||||
msr cpsr_c, #0x1F // sys
|
||||
ldr sp,= __dtcm_start + 0x2F7C
|
||||
b loaderMain
|
||||
|
||||
.pool
|
||||
.end
|
||||
48
arm9/source/errorDisplay/ErrorDisplay.cpp
Normal file
48
arm9/source/errorDisplay/ErrorDisplay.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/mem/memVram.h>
|
||||
#include <libtwl/gfx/gfx.h>
|
||||
#include <libtwl/gfx/gfxStatus.h>
|
||||
#include <libtwl/gfx/gfxPalette.h>
|
||||
#include <libtwl/gfx/gfxBackground.h>
|
||||
#include "nitroFont2.h"
|
||||
#include "font_nft2.h"
|
||||
#include "ErrorDisplay.h"
|
||||
|
||||
void ErrorDisplay::PrintError(const char* errorString)
|
||||
{
|
||||
mem_setVramEMapping(MEM_VRAM_E_MAIN_BG_00000);
|
||||
auto textBuffer = (u8*)0x02100000;
|
||||
memset(textBuffer, 0, 256 * 192);
|
||||
nft2_unpack((nft2_header_t*)font_nft2);
|
||||
nft2_string_render_params_t renderParams =
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 256,
|
||||
height: 192
|
||||
};
|
||||
nft2_renderString((const nft2_header_t*)font_nft2, errorString, textBuffer, 256, &renderParams);
|
||||
memcpy((void*)GFX_BG_MAIN, textBuffer, 256 * 192);
|
||||
while (gfx_getVCount() != 191);
|
||||
while (gfx_getVCount() == 191);
|
||||
// 4 bit grayscale palette
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
int gray = i * 2 + (i == 0 ? 0 : 1);
|
||||
GFX_PLTT_BG_MAIN[i] = gray | (gray << 5) | (gray << 10);
|
||||
}
|
||||
REG_BG3PA = 256;
|
||||
REG_BG3PB = 0;
|
||||
REG_BG3PC = 0;
|
||||
REG_BG3PD = 256;
|
||||
REG_BG3X = 0;
|
||||
REG_BG3Y = 0;
|
||||
REG_BG3CNT = (1 << 7) | (1 << 14);
|
||||
REG_BLDCNT = 0;
|
||||
REG_DISPCNT = 3 | (1 << 11) | (1 << 16);
|
||||
REG_MASTER_BRIGHT = 0;
|
||||
GFX_PLTT_BG_SUB[0] = 0;
|
||||
REG_MASTER_BRIGHT_SUB = 0x8010;
|
||||
REG_DISPCNT_SUB = 0x10000;
|
||||
while (true);
|
||||
}
|
||||
11
arm9/source/errorDisplay/ErrorDisplay.h
Normal file
11
arm9/source/errorDisplay/ErrorDisplay.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Class for displaying a critical error message.
|
||||
class ErrorDisplay
|
||||
{
|
||||
public:
|
||||
/// @brief Displays the given \p errorString and loops.
|
||||
/// @note This function does not return.
|
||||
/// @param errorString The error string to display.
|
||||
void PrintError(const char* errorString);
|
||||
};
|
||||
104
arm9/source/errorDisplay/nitroFont2.cpp
Normal file
104
arm9/source/errorDisplay/nitroFont2.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "common.h"
|
||||
#include "nitroFont2.h"
|
||||
|
||||
bool nft2_unpack(nft2_header_t* font)
|
||||
{
|
||||
if (font->signature != NFT2_SIGNATURE)
|
||||
return false;
|
||||
|
||||
font->glyphInfoPtr = (const nft2_glyph_t*)((u32)font + (u32)font->glyphInfoPtr);
|
||||
font->charMapPtr = (const nft2_char_map_entry_t*)((u32)font + (u32)font->charMapPtr);
|
||||
font->glyphDataPtr = (const u8*)((u32)font + (u32)font->glyphDataPtr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int nft2_findGlyphIdxForCharacter(const nft2_header_t* font, u16 character)
|
||||
{
|
||||
const nft2_char_map_entry_t* charMapEntry = font->charMapPtr;
|
||||
while (charMapEntry->count > 0)
|
||||
{
|
||||
if (charMapEntry->startChar <= character && character < charMapEntry->startChar + charMapEntry->count)
|
||||
return charMapEntry->glyphs[character - charMapEntry->startChar];
|
||||
|
||||
charMapEntry = (const nft2_char_map_entry_t*)((u32)charMapEntry + 4 + 2 * charMapEntry->count);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void renderGlyph(const nft2_header_t* font, const nft2_glyph_t* glyph,
|
||||
int xPos, int yPos, int width, int height, u8* dst, u32 stride)
|
||||
{
|
||||
int yOffset = glyph->spacingTop;
|
||||
u32 xStart = xPos < 0 ? -xPos : 0;
|
||||
u32 yStart = yPos + yOffset < 0 ? -(yPos + yOffset) : 0;
|
||||
|
||||
int xEnd = glyph->glyphWidth;
|
||||
if (xPos + xEnd > width)
|
||||
{
|
||||
// by returning we only render complete glyphs
|
||||
return;
|
||||
// old code for rendering partial glyphs
|
||||
// xEnd = width - xPos;
|
||||
}
|
||||
|
||||
int yEnd = glyph->glyphHeight;
|
||||
if (yPos + yOffset + yEnd > height)
|
||||
yEnd = height - (yPos + yOffset); // allow partial glyphs in the vertical direction
|
||||
|
||||
const u8* glyphData = &font->glyphDataPtr[glyph->dataOffset];
|
||||
glyphData += yStart * ((glyph->glyphWidth + 1) >> 1);
|
||||
for (int y = yStart; y < yEnd; y++)
|
||||
{
|
||||
for (int x = xStart; x < xEnd; x++)
|
||||
{
|
||||
u32 data = glyphData[x >> 1];
|
||||
if ((x & 1) == 0)
|
||||
data &= 0xF;
|
||||
else
|
||||
data >>= 4;
|
||||
|
||||
if (data == 0)
|
||||
continue;
|
||||
|
||||
u32 finalX = x + xPos;
|
||||
u32 finalY = y + yPos + yOffset;
|
||||
|
||||
dst[finalY * stride + finalX] = data;
|
||||
}
|
||||
glyphData += (glyph->glyphWidth + 1) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
ITCM_CODE void nft2_renderString(const nft2_header_t* font, const char* string, u8* dst, u32 stride,
|
||||
nft2_string_render_params_t* renderParams)
|
||||
{
|
||||
int xPos = renderParams->x;
|
||||
int yPos = renderParams->y;
|
||||
u32 textWidth = 0;
|
||||
while (true)
|
||||
{
|
||||
char c = *string++;
|
||||
if (c == 0)
|
||||
break;
|
||||
if (c == '\n')
|
||||
{
|
||||
xPos = renderParams->x;
|
||||
yPos += font->ascend + font->descend + 1;
|
||||
if (yPos >= (int)renderParams->height)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
int glyphIdx = nft2_findGlyphIdxForCharacter(font, c);
|
||||
const nft2_glyph_t* glyph = &font->glyphInfoPtr[glyphIdx];
|
||||
xPos += glyph->spacingLeft;
|
||||
renderGlyph(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride);
|
||||
xPos += glyph->glyphWidth;
|
||||
if (xPos > (int)textWidth)
|
||||
textWidth = xPos;
|
||||
xPos += glyph->spacingRight;
|
||||
}
|
||||
renderParams->textWidth = textWidth;
|
||||
}
|
||||
62
arm9/source/errorDisplay/nitroFont2.h
Normal file
62
arm9/source/errorDisplay/nitroFont2.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#define NFT2_SIGNATURE 0x3254464E
|
||||
|
||||
struct nft2_glyph_t
|
||||
{
|
||||
u32 dataOffset : 24;
|
||||
u32 glyphWidth : 8;
|
||||
s8 spacingLeft;
|
||||
s8 spacingRight;
|
||||
u8 glyphHeight;
|
||||
s8 spacingTop;
|
||||
};
|
||||
|
||||
struct nft2_char_map_entry_t
|
||||
{
|
||||
u16 count;
|
||||
u16 startChar;
|
||||
u16 glyphs[1];
|
||||
};
|
||||
|
||||
struct nft2_header_t
|
||||
{
|
||||
u32 signature;
|
||||
const nft2_glyph_t* glyphInfoPtr;
|
||||
const nft2_char_map_entry_t* charMapPtr;
|
||||
const u8* glyphDataPtr;
|
||||
u8 ascend;
|
||||
u8 descend;
|
||||
u16 glyphCount;
|
||||
};
|
||||
|
||||
struct nft2_string_render_params_t
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 textWidth;
|
||||
};
|
||||
|
||||
/// @brief Prepares the ntf2 data of the given \p font for runtime use.
|
||||
/// Call this method once after loading a font file.
|
||||
/// @param font The font to prepare.
|
||||
/// @return True if preparing was successful, or false otherwise.
|
||||
bool nft2_unpack(nft2_header_t* font);
|
||||
|
||||
/// @brief Finds the glyph index in the given \p font that corresponds to the given \p character.
|
||||
/// @param font The font the find the glyph index in.
|
||||
/// @param character The character to find the glyph index for.
|
||||
/// @return The glyph index if found, or 0 otherwise.
|
||||
int nft2_findGlyphIdxForCharacter(const nft2_header_t* font, u16 character);
|
||||
|
||||
/// @brief Renders the given \p string with the given \p font to the \p dst buffer
|
||||
/// with the given \p stride and \p renderParams.
|
||||
/// @param font The font to use.
|
||||
/// @param string The string to render.
|
||||
/// @param dst The destination buffer.
|
||||
/// @param stride The stride of the destination buffer.
|
||||
/// @param renderParams The render params.
|
||||
void nft2_renderString(const nft2_header_t* font, const char* string, u8* dst,
|
||||
u32 stride, nft2_string_render_params_t* renderParams);
|
||||
3
arm9/source/fastClear.h
Normal file
3
arm9/source/fastClear.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" void fastClear(void* dst, u32 length);
|
||||
22
arm9/source/fastClear.s
Normal file
22
arm9/source/fastClear.s
Normal file
@@ -0,0 +1,22 @@
|
||||
.section ".itcm", "ax"
|
||||
.arm
|
||||
|
||||
// r0: dst
|
||||
// r1: size (multiple of 32)
|
||||
.global fastClear
|
||||
.type fastClear, %function
|
||||
fastClear:
|
||||
push {r4-r8,lr}
|
||||
mov r2, #0
|
||||
mov r3, #0
|
||||
mov r4, #0
|
||||
mov r5, #0
|
||||
mov r6, #0
|
||||
mov r7, #0
|
||||
mov r8, #0
|
||||
mov lr, #0
|
||||
1:
|
||||
subs r1, r1, #32
|
||||
stmgeia r0!, {r2-r8,lr}
|
||||
bgt 1b
|
||||
pop {r4-r8,pc}
|
||||
3
arm9/source/fastSearch.h
Normal file
3
arm9/source/fastSearch.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" const u32* fastSearch16(const u32* data, u32 length, const u32* pattern);
|
||||
141
arm9/source/fastSearch.s
Normal file
141
arm9/source/fastSearch.s
Normal file
@@ -0,0 +1,141 @@
|
||||
.section ".itcm", "ax"
|
||||
.arm
|
||||
|
||||
// r0: data
|
||||
// r1: size
|
||||
// r2: pattern (4 words, each word should be unique)
|
||||
.global fastSearch16
|
||||
.type fastSearch16, %function
|
||||
fastSearch16:
|
||||
push {r4-r11,lr}
|
||||
add r1, r1, r0
|
||||
sub r1, r1, #44 // 32 bytes to load at once + 12 bytes after it
|
||||
ldmia r2, {r2-r5}
|
||||
|
||||
1:
|
||||
ldmia r0!, {r6-r12,lr}
|
||||
cmp r6, r2
|
||||
cmpne r7, r2
|
||||
cmpne r8, r2
|
||||
cmpne r9, r2
|
||||
cmpne r10, r2
|
||||
cmpne r11, r2
|
||||
cmpne r12, r2
|
||||
cmpne lr, r2
|
||||
beq fastSearch16_firstWordMatch
|
||||
fastSearch16_continueFastSearch:
|
||||
cmp r0, r1
|
||||
ble 1b
|
||||
|
||||
// only need to handle the last couple of words now
|
||||
add r1, #(44 - 16)
|
||||
2:
|
||||
ldr r6, [r0], #4
|
||||
cmp r6, r2
|
||||
beq fastSearch16_firstWordMatchLast
|
||||
fastSearch16_continueLastSearch:
|
||||
cmp r0, r1
|
||||
ble 2b
|
||||
|
||||
mov r0, #0
|
||||
pop {r4-r11,pc}
|
||||
|
||||
fastSearch16_firstWordMatchLast:
|
||||
ldr r6, [r0], #4
|
||||
cmp r6, r3
|
||||
ldreq r6, [r0], #4
|
||||
cmpeq r6, r4
|
||||
ldreq r6, [r0], #4
|
||||
cmpeq r6, r5
|
||||
bne fastSearch16_continueLastSearch
|
||||
sub r0, r0, #16
|
||||
pop {r4-r11,pc}
|
||||
|
||||
fastSearch16_firstWordMatch:
|
||||
cmp r6, r2
|
||||
beq fastSearch16_firstWordMatch_r6
|
||||
cmpne r7, r2
|
||||
beq fastSearch16_firstWordMatch_r7
|
||||
cmpne r8, r2
|
||||
beq fastSearch16_firstWordMatch_r8
|
||||
cmpne r9, r2
|
||||
beq fastSearch16_firstWordMatch_r9
|
||||
cmpne r10, r2
|
||||
beq fastSearch16_firstWordMatch_r10
|
||||
cmpne r11, r2
|
||||
beq fastSearch16_firstWordMatch_r11
|
||||
cmpne r12, r2
|
||||
beq fastSearch16_firstWordMatch_r12
|
||||
fastSearch16_firstWordMatch_lr:
|
||||
ldr r6, [r0], #4
|
||||
cmp r6, r3
|
||||
ldreq r6, [r0], #4
|
||||
cmpeq r6, r4
|
||||
ldreq r6, [r0], #4
|
||||
cmpeq r6, r5
|
||||
bne fastSearch16_continueFastSearch
|
||||
sub r0, r0, #16
|
||||
pop {r4-r11,pc}
|
||||
|
||||
fastSearch16_firstWordMatch_r6:
|
||||
sub r0, r0, #16
|
||||
cmp r7, r3
|
||||
cmpeq r8, r4
|
||||
cmpeq r9, r5
|
||||
bne fastSearch16_continueFastSearch
|
||||
sub r0, r0, #16
|
||||
pop {r4-r11,pc}
|
||||
|
||||
fastSearch16_firstWordMatch_r7:
|
||||
sub r0, r0, #12
|
||||
cmp r8, r3
|
||||
cmpeq r9, r4
|
||||
cmpeq r10, r5
|
||||
bne fastSearch16_continueFastSearch
|
||||
sub r0, r0, #16
|
||||
pop {r4-r11,pc}
|
||||
|
||||
fastSearch16_firstWordMatch_r8:
|
||||
sub r0, r0, #8
|
||||
cmp r9, r3
|
||||
cmpeq r10, r4
|
||||
cmpeq r11, r5
|
||||
bne fastSearch16_continueFastSearch
|
||||
sub r0, r0, #16
|
||||
pop {r4-r11,pc}
|
||||
|
||||
fastSearch16_firstWordMatch_r9:
|
||||
sub r0, r0, #4
|
||||
cmp r10, r3
|
||||
cmpeq r11, r4
|
||||
cmpeq r12, r5
|
||||
bne fastSearch16_continueFastSearch
|
||||
sub r0, r0, #16
|
||||
pop {r4-r11,pc}
|
||||
|
||||
fastSearch16_firstWordMatch_r10:
|
||||
cmp r11, r3
|
||||
cmpeq r12, r4
|
||||
cmpeq lr, r5
|
||||
bne fastSearch16_continueFastSearch
|
||||
sub r0, r0, #16
|
||||
pop {r4-r11,pc}
|
||||
|
||||
fastSearch16_firstWordMatch_r11:
|
||||
cmp r12, r3
|
||||
cmpeq lr, r4
|
||||
ldreq r6, [r0], #4
|
||||
cmpeq r6, r5
|
||||
bne fastSearch16_continueFastSearch
|
||||
sub r0, r0, #16
|
||||
pop {r4-r11,pc}
|
||||
|
||||
fastSearch16_firstWordMatch_r12:
|
||||
cmp lr, r3
|
||||
ldreq r6, [r0], #4
|
||||
cmpeq r6, r4
|
||||
ldreq r6, [r0], #4
|
||||
cmpeq r6, r5
|
||||
bne fastSearch16_continueFastSearch
|
||||
sub r0, r0, #16
|
||||
pop {r4-r11,pc}
|
||||
102
arm9/source/globalHeap.cpp
Normal file
102
arm9/source/globalHeap.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
#include "core/heap/tlsf.h"
|
||||
#include "globalHeap.h"
|
||||
|
||||
static tlsf_t sHeap;
|
||||
|
||||
extern "C" void* malloc(size_t size)
|
||||
{
|
||||
return tlsf_malloc(sHeap, size);
|
||||
}
|
||||
|
||||
extern "C" void* _malloc_r(struct _reent *, size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
extern "C" void free(void* ptr)
|
||||
{
|
||||
tlsf_free(sHeap, ptr);
|
||||
}
|
||||
|
||||
extern "C" void _free_r(struct _reent *, void* ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
extern "C" void* realloc(void* ptr, size_t size)
|
||||
{
|
||||
return tlsf_realloc(sHeap, ptr, size);
|
||||
}
|
||||
|
||||
extern "C" void* memalign(size_t alignment, size_t size)
|
||||
{
|
||||
return tlsf_memalign(sHeap, alignment, size);
|
||||
}
|
||||
|
||||
void* operator new(std::size_t blocksize)
|
||||
{
|
||||
return malloc(blocksize);
|
||||
}
|
||||
|
||||
void* operator new(std::size_t size, std::align_val_t al)
|
||||
{
|
||||
return memalign(static_cast<std::size_t>(al), size);
|
||||
}
|
||||
|
||||
void* operator new[](std::size_t blocksize)
|
||||
{
|
||||
return malloc(blocksize);
|
||||
}
|
||||
|
||||
void* operator new[](std::size_t size, std::align_val_t al)
|
||||
{
|
||||
return memalign(static_cast<std::size_t>(al), size);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
return free(ptr);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr, std::align_val_t align)
|
||||
{
|
||||
return free(ptr);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr, std::size_t size, std::align_val_t align)
|
||||
{
|
||||
return free(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr)
|
||||
{
|
||||
return free(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr, std::align_val_t align)
|
||||
{
|
||||
return free(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr, std::size_t size, std::align_val_t align)
|
||||
{
|
||||
return free(ptr);
|
||||
}
|
||||
|
||||
extern u8 __heap_start;
|
||||
extern u8 __heap_end;
|
||||
|
||||
[[gnu::target("thumb"), gnu::optimize("Os")]]
|
||||
void heap_init()
|
||||
{
|
||||
u32 heapStart = (u32)&__heap_start;
|
||||
heapStart = (heapStart + 31) & ~31;
|
||||
u32 heapEnd = (u32)&__heap_end;
|
||||
heapEnd = heapEnd & ~31;
|
||||
u32 tlsfSize = tlsf_size();
|
||||
memset((void*)heapStart, 0, tlsfSize);
|
||||
memset((u8*)heapStart + tlsfSize, 0xA5, heapEnd - heapStart - tlsfSize);
|
||||
sHeap = tlsf_create_with_pool((void*)heapStart, heapEnd - heapStart);
|
||||
}
|
||||
16
arm9/source/globalHeap.h
Normal file
16
arm9/source/globalHeap.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
void* operator new(std::size_t blocksize) noexcept;
|
||||
void* operator new(std::size_t size, std::align_val_t al) noexcept;
|
||||
void* operator new[](std::size_t blocksize) noexcept;
|
||||
void* operator new[](std::size_t size, std::align_val_t al) noexcept;
|
||||
void operator delete(void* ptr) noexcept;
|
||||
void operator delete(void* ptr, std::align_val_t align) noexcept;
|
||||
void operator delete(void* ptr, std::size_t size, std::align_val_t align) noexcept;
|
||||
void operator delete[](void* ptr) noexcept;
|
||||
void operator delete[](void* ptr, std::align_val_t align) noexcept;
|
||||
void operator delete[](void* ptr, std::size_t size, std::align_val_t align) noexcept;
|
||||
|
||||
constexpr std::align_val_t cache_align { 32 };
|
||||
|
||||
void heap_init();
|
||||
22
arm9/source/logger/NocashOutputStream.h
Normal file
22
arm9/source/logger/NocashOutputStream.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "logger/IOutputStream.h"
|
||||
|
||||
#define REG_NOCASH_STRING_OUT (*(vu32*)0x04FFFA10)
|
||||
#define REG_NOCASH_CHAR_OUT (*(vu32*)0x04FFFA1C)
|
||||
|
||||
class NocashOutputStream : public IOutputStream
|
||||
{
|
||||
public:
|
||||
void Write(const char* str) override
|
||||
{
|
||||
// melon ds doesn't support string addresses in itcm and dtcm
|
||||
char c;
|
||||
while ((c = *str++) != 0)
|
||||
{
|
||||
REG_NOCASH_CHAR_OUT = c;
|
||||
}
|
||||
// REG_NOCASH_STRING_OUT = (u32)str;
|
||||
}
|
||||
|
||||
void Flush() override { }
|
||||
};
|
||||
25
arm9/source/logger/PlainLogger.h
Normal file
25
arm9/source/logger/PlainLogger.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "core/mini-printf.h"
|
||||
#include "logger/ILogger.h"
|
||||
#include "logger/IOutputStream.h"
|
||||
|
||||
class PlainLogger : public ILogger
|
||||
{
|
||||
LogLevel _maxLogLevel;
|
||||
IOutputStream* _outputStream;
|
||||
|
||||
// char _logBuffer[512];
|
||||
|
||||
public:
|
||||
PlainLogger(LogLevel maxLogLevel, IOutputStream* outputStream)
|
||||
: _maxLogLevel(maxLogLevel), _outputStream(outputStream) { }
|
||||
|
||||
void LogV(LogLevel level, const char* fmt, va_list vlist) override
|
||||
{
|
||||
if (level > _maxLogLevel)
|
||||
return;
|
||||
char logBuffer[128];
|
||||
mini_vsnprintf(logBuffer, sizeof(logBuffer), fmt, vlist);
|
||||
_outputStream->Write(logBuffer);
|
||||
}
|
||||
};
|
||||
352
arm9/source/main.cpp
Normal file
352
arm9/source/main.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
#include "common.h"
|
||||
#include "ApList.h"
|
||||
#include <libtwl/gfx/gfx3d.h>
|
||||
#include <libtwl/gfx/gfx3dCmd.h>
|
||||
#include <libtwl/gfx/gfxOam.h>
|
||||
#include <libtwl/gfx/gfxPalette.h>
|
||||
#include <libtwl/gfx/gfxStatus.h>
|
||||
#include <libtwl/ipc/ipcFifo.h>
|
||||
#include <libtwl/ipc/ipcSync.h>
|
||||
#include <libtwl/mem/memNtrWram.h>
|
||||
#include <libtwl/mem/memTwlWram.h>
|
||||
#include <libtwl/mem/memVram.h>
|
||||
#include "fastClear.h"
|
||||
#include "cache.h"
|
||||
#include "sharedMemory.h"
|
||||
#include "ndsHeader.h"
|
||||
#include "logger/NocashOutputStream.h"
|
||||
#include "logger/PicoAgbAdapterOutputStream.h"
|
||||
#include "logger/PlainLogger.h"
|
||||
#include "ipcCommands.h"
|
||||
#include "Arm9IoRegisterClearer.h"
|
||||
#include "Arm9Patcher.h"
|
||||
#include "Arm7Patcher.h"
|
||||
#include "patches/platform/LoaderPlatform.h"
|
||||
#include "patches/platform/LoaderPlatformFactory.h"
|
||||
#include "arm9Clock.h"
|
||||
#include "errorDisplay/ErrorDisplay.h"
|
||||
#include "LoaderInfo.h"
|
||||
|
||||
typedef void (*entrypoint_t)(void);
|
||||
|
||||
#define HANDSHAKE_PART0 0xA
|
||||
#define HANDSHAKE_PART1 0xB
|
||||
#define HANDSHAKE_PART2 0xC
|
||||
#define HANDSHAKE_PART3 0xD
|
||||
|
||||
static NocashOutputStream/*PicoAgbAdapterOutputStream*/ sNocashOutput;
|
||||
static PlainLogger sPlainLogger { LogLevel::All, &sNocashOutput };
|
||||
ILogger* gLogger = &sPlainLogger;
|
||||
|
||||
static ApListEntry sApListEntry;
|
||||
|
||||
static LoaderPlatform* sLoaderPlatform;
|
||||
|
||||
static u32 sRomDirSector;
|
||||
static u32 sRomDirSectorOffset;
|
||||
static u16 sIsCloneBootRom;
|
||||
static loader_info_t sLoaderInfo;
|
||||
|
||||
u16 gIsDsiMode;
|
||||
|
||||
// Dummy symbol to allow linking C++ applications. This is only needed to handle
|
||||
// dynamic shared objects (.so), but they don't exist on the NDS.
|
||||
void *__dso_handle;
|
||||
|
||||
static u32 receiveFromArm7()
|
||||
{
|
||||
while (ipc_isRecvFifoEmpty());
|
||||
return ipc_recvWordDirect();
|
||||
}
|
||||
|
||||
extern "C" void __libc_init_array();
|
||||
|
||||
static void clearGraphicsMemory()
|
||||
{
|
||||
// VRAM A used for arm9 code
|
||||
mem_setVramBMapping(MEM_VRAM_AB_LCDC);
|
||||
// VRAM C and D used for arm7 code
|
||||
mem_setVramEMapping(MEM_VRAM_E_LCDC);
|
||||
mem_setVramFMapping(MEM_VRAM_FG_LCDC);
|
||||
mem_setVramGMapping(MEM_VRAM_FG_LCDC);
|
||||
mem_setVramHMapping(MEM_VRAM_H_LCDC);
|
||||
mem_setVramIMapping(MEM_VRAM_I_LCDC);
|
||||
fastClear((void*)0x06820000, 0x20000); // VRAM B
|
||||
fastClear((void*)0x06880000, 0x24000);
|
||||
fastClear((void*)GFX_PLTT_BG_MAIN, 512);
|
||||
fastClear((void*)GFX_PLTT_BG_SUB, 512);
|
||||
fastClear((void*)GFX_PLTT_OBJ_MAIN, 512);
|
||||
fastClear((void*)GFX_PLTT_OBJ_SUB, 512);
|
||||
fastClear((void*)GFX_OAM_MAIN, 1024);
|
||||
fastClear((void*)GFX_OAM_SUB, 1024);
|
||||
|
||||
// clear the vertex and polygon ram of the 3d engine
|
||||
gx_init();
|
||||
gx_swapBuffers(GX_XLU_SORT_AUTO, GX_DEPTH_MODE_Z);
|
||||
gx_swapBuffers(GX_XLU_SORT_AUTO, GX_DEPTH_MODE_Z);
|
||||
}
|
||||
|
||||
[[gnu::noinline, gnu::section(".itcm")]]
|
||||
static void bootArm9()
|
||||
{
|
||||
mem_setVramAMapping(MEM_VRAM_AB_LCDC);
|
||||
fastClear((void*)0x06800000, 0x20000); // VRAM A
|
||||
mem_setVramAMapping(MEM_VRAM_AB_NONE);
|
||||
// By now it should be safe to unmap the arm7 memory
|
||||
mem_setVramCMapping(MEM_VRAM_C_LCDC);
|
||||
mem_setVramDMapping(MEM_VRAM_D_LCDC);
|
||||
fastClear((void*)0x06840000, 0x40000); // VRAM C and D
|
||||
mem_setVramCMapping(MEM_VRAM_C_NONE);
|
||||
mem_setVramDMapping(MEM_VRAM_D_NONE);
|
||||
|
||||
if (((REG_SCFG_EXT >> 14) & 3) == 0)
|
||||
{
|
||||
// When switched to DS mode, disable vram extensions
|
||||
REG_SCFG_EXT &= ~(1 << 13);
|
||||
}
|
||||
|
||||
while (gfx_getVCount() != 191);
|
||||
while (gfx_getVCount() == 191);
|
||||
REG_IF = ~0u; // final clear of REG_IF bits
|
||||
auto romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
|
||||
((entrypoint_t)romHeader->arm9EntryAddress)();
|
||||
}
|
||||
|
||||
static void handleInitializeSdCardCommand()
|
||||
{
|
||||
REG_EXMEMCNT &= ~0x0880; // map ds and gba slot to arm9
|
||||
bool result = sLoaderPlatform->InitializeSdCard();
|
||||
REG_EXMEMCNT |= 0x0880; // map ds and gba slot to arm7
|
||||
ipc_sendWordDirect(result);
|
||||
}
|
||||
|
||||
static void handleWramConfigCommand()
|
||||
{
|
||||
REG_WRAMCNT = receiveFromArm7();
|
||||
REG_MBK6 = receiveFromArm7();
|
||||
REG_MBK7 = receiveFromArm7();
|
||||
REG_MBK8 = receiveFromArm7();
|
||||
REG_MBK1 = receiveFromArm7();
|
||||
REG_MBK2 = receiveFromArm7();
|
||||
REG_MBK3 = receiveFromArm7();
|
||||
REG_MBK4 = receiveFromArm7();
|
||||
REG_MBK5 = receiveFromArm7();
|
||||
ipc_sendWordDirect(1);
|
||||
}
|
||||
|
||||
static void handleClearMainMemCommand()
|
||||
{
|
||||
fastClear((void*)0x02000000, 0x400000);
|
||||
dc_flushAll();
|
||||
dc_drainWriteBuffer();
|
||||
dc_invalidateAll();
|
||||
ic_invalidateAll();
|
||||
ipc_sendWordDirect(1);
|
||||
}
|
||||
|
||||
static void handleApplyArm9PatchesCommand()
|
||||
{
|
||||
Arm9Patcher().ApplyPatches(
|
||||
sLoaderPlatform,
|
||||
sApListEntry.GetGameCode() == 0 ? nullptr : &sApListEntry,
|
||||
sIsCloneBootRom,
|
||||
&sLoaderInfo);
|
||||
ipc_sendWordDirect(1);
|
||||
}
|
||||
|
||||
static void handleApplyArm7PatchesCommand()
|
||||
{
|
||||
void* patchSpaceStart = Arm7Patcher().ApplyPatches(sLoaderPlatform);
|
||||
ipc_sendWordDirect((u32)patchSpaceStart);
|
||||
}
|
||||
|
||||
static void handleSetAPInfoCommand()
|
||||
{
|
||||
((u32*)&sApListEntry)[0] = receiveFromArm7();
|
||||
((u32*)&sApListEntry)[1] = receiveFromArm7();
|
||||
((u32*)&sApListEntry)[2] = receiveFromArm7();
|
||||
((u32*)&sApListEntry)[3] = receiveFromArm7();
|
||||
}
|
||||
|
||||
static void handleSetRomFileInfoCommand()
|
||||
{
|
||||
sRomDirSector = receiveFromArm7();
|
||||
sRomDirSectorOffset = receiveFromArm7();
|
||||
sIsCloneBootRom = receiveFromArm7();
|
||||
}
|
||||
|
||||
static void handleInitializeLoaderInfoCommand()
|
||||
{
|
||||
dc_invalidateRange(TWL_SHARED_MEMORY->ntrSharedMem.cardRomHeader, sizeof(loader_info_t));
|
||||
memcpy(&sLoaderInfo, TWL_SHARED_MEMORY->ntrSharedMem.cardRomHeader, sizeof(loader_info_t));
|
||||
dc_flushAll();
|
||||
dc_drainWriteBuffer();
|
||||
ipc_sendWordDirect(1);
|
||||
}
|
||||
|
||||
static void handleGetSdFunctionsCommand()
|
||||
{
|
||||
mem_setNtrWramMapping(MEM_NTR_WRAM_ARM9, MEM_NTR_WRAM_ARM9);
|
||||
PatchHeap patchHeap;
|
||||
PatchCodeCollection patchCodeCollection;
|
||||
patchHeap.AddFreeSpace((void*)0x037F8020, 16 * 1024);
|
||||
{
|
||||
auto sdReadPatchCode = sLoaderPlatform->CreateSdReadPatchCode(patchCodeCollection, patchHeap);
|
||||
auto sdWritePatchCode = sLoaderPlatform->CreateSdWritePatchCode(patchCodeCollection, patchHeap);
|
||||
*(vu32*)0x037F8000 = (u32)sdReadPatchCode->GetSdReadFunction();
|
||||
*(vu32*)0x037F8004 = (u32)sdWritePatchCode->GetSdWriteFunction();
|
||||
patchCodeCollection.CopyAllToTarget();
|
||||
}
|
||||
dc_flushAll();
|
||||
dc_drainWriteBuffer();
|
||||
mem_setNtrWramMapping(MEM_NTR_WRAM_ARM7, MEM_NTR_WRAM_ARM7);
|
||||
ipc_sendWordDirect(1);
|
||||
}
|
||||
|
||||
[[gnu::noinline, gnu::section(".itcm")]]
|
||||
static void handleSwitchToDSModeCommand()
|
||||
{
|
||||
Arm9IoRegisterClearer().ClearTwlIoRegisters();
|
||||
scfg_setArm9Clock(ScfgArm9Clock::Nitro67MHz);
|
||||
REG_SCFG_EXT = 0x83000000u | (1 << 13); // keep vram extensions on until the very end
|
||||
ipc_sendWordDirect(1);
|
||||
}
|
||||
|
||||
[[gnu::noinline, gnu::section(".itcm")]]
|
||||
static void handleBootCommand()
|
||||
{
|
||||
bool isSdkResetSystem = receiveFromArm7() != 0;
|
||||
REG_EXMEMCNT &= ~0x0880; // map ds and gba slot to arm9
|
||||
sLoaderPlatform->PrepareRomBoot(sRomDirSector, sRomDirSectorOffset);
|
||||
Arm9IoRegisterClearer().ClearNtrIoRegisters(isSdkResetSystem);
|
||||
auto ntrRomHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
|
||||
if (ntrRomHeader->IsTwlRom())
|
||||
{
|
||||
if (gIsDsiMode)
|
||||
{
|
||||
Arm9IoRegisterClearer().ClearTwlIoRegisters();
|
||||
REG_SCFG_EXT = 0x8307F100;
|
||||
scfg_setArm9Clock(ScfgArm9Clock::Twl134MHz);
|
||||
REG_SCFG_CLK = 0x87;
|
||||
REG_SCFG_RST = 1;
|
||||
}
|
||||
auto twlRomHeader = (const nds_header_twl_t*)TWL_SHARED_MEMORY->twlRomHeader;
|
||||
if (twlRomHeader->IsDsiWare())
|
||||
{
|
||||
REG_EXMEMCNT |= 0x0880; // map ds and gba slot to arm7
|
||||
}
|
||||
}
|
||||
bootArm9();
|
||||
}
|
||||
|
||||
static void handleArm7Command(u32 command)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case IPC_COMMAND_ARM9_INITIALIZE_SD_CARD:
|
||||
{
|
||||
handleInitializeSdCardCommand();
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_WRAM_CONFIG:
|
||||
{
|
||||
handleWramConfigCommand();
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_CLEAR_MAIN_MEM:
|
||||
{
|
||||
handleClearMainMemCommand();
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_APPLY_PATCHES:
|
||||
{
|
||||
handleApplyArm9PatchesCommand();
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_APPLY_ARM7_PATCHES:
|
||||
{
|
||||
handleApplyArm7PatchesCommand();
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_SET_AP_INFO:
|
||||
{
|
||||
handleSetAPInfoCommand();
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_SET_ROM_FILE_INFO:
|
||||
{
|
||||
handleSetRomFileInfoCommand();
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_INITIALIZE_LOADER_INFO:
|
||||
{
|
||||
handleInitializeLoaderInfoCommand();
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_GET_SD_FUNCTIONS:
|
||||
{
|
||||
handleGetSdFunctionsCommand();
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_DISPLAY_ERROR:
|
||||
{
|
||||
ErrorDisplay().PrintError((const char*)0x02000000);
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_SWITCH_TO_DS_MODE:
|
||||
{
|
||||
handleSwitchToDSModeCommand();
|
||||
break;
|
||||
}
|
||||
case IPC_COMMAND_ARM9_BOOT:
|
||||
{
|
||||
handleBootCommand();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void loaderMain()
|
||||
{
|
||||
__libc_init_array();
|
||||
|
||||
clearGraphicsMemory();
|
||||
|
||||
while (ipc_getArm7SyncBits() != HANDSHAKE_PART0);
|
||||
ipc_setArm9SyncBits(HANDSHAKE_PART0);
|
||||
while (ipc_getArm7SyncBits() != HANDSHAKE_PART1);
|
||||
ipc_setArm9SyncBits(HANDSHAKE_PART1);
|
||||
|
||||
REG_EXMEMCNT |= 0x8880; // everything to arm7
|
||||
// REG_EXMEMCNT &= ~0xFF;
|
||||
mem_setNtrWramMapping(MEM_NTR_WRAM_ARM7, MEM_NTR_WRAM_ARM7);
|
||||
ipc_clearSendFifo();
|
||||
ipc_ackFifoError();
|
||||
ipc_disableRecvFifoNotEmptyIrq();
|
||||
ipc_enableFifo();
|
||||
|
||||
while (ipc_getArm7SyncBits() != HANDSHAKE_PART2);
|
||||
ipc_setArm9SyncBits(HANDSHAKE_PART2);
|
||||
while (ipc_getArm7SyncBits() != HANDSHAKE_PART3);
|
||||
ipc_setArm9SyncBits((*(vu32*)0x04004000) & 3);
|
||||
gIsDsiMode = ((*(vu32*)0x04004000) & 3) == 1;
|
||||
|
||||
heap_init();
|
||||
|
||||
sLoaderPlatform = LoaderPlatformFactory().CreateLoaderPlatform();
|
||||
|
||||
sRomDirSector = 0;
|
||||
sRomDirSectorOffset = 0;
|
||||
sIsCloneBootRom = false;
|
||||
|
||||
LOG_DEBUG("Pico Loader ARM9 started\n");
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (ipc_isRecvFifoEmpty())
|
||||
continue;
|
||||
|
||||
u32 word = ipc_recvWordDirect();
|
||||
handleArm7Command(word);
|
||||
}
|
||||
}
|
||||
28
arm9/source/patches/FunctionSignature.h
Normal file
28
arm9/source/patches/FunctionSignature.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "SdkVersion.h"
|
||||
|
||||
/// @brief Class representing a function signature with a minimum and maximum SDK version.
|
||||
class FunctionSignature
|
||||
{
|
||||
public:
|
||||
consteval FunctionSignature(const u32 pattern[4], SdkVersion minSdkVersion, SdkVersion maxSdkVersion)
|
||||
: _pattern { pattern[0], pattern[1], pattern[2], pattern[3] }
|
||||
, _minSdkVersion(minSdkVersion), _maxSdkVersion(maxSdkVersion) { }
|
||||
|
||||
/// @brief Returns the pattern of the function signature.
|
||||
/// @return The pattern of the function signature.
|
||||
constexpr const u32* GetPattern() const { return _pattern; }
|
||||
|
||||
/// @brief Returns the minimum SDK version of this function signature.
|
||||
/// @return The minimum SDK version of this function signature.
|
||||
constexpr SdkVersion GetMinimumSdkVersion() const { return _minSdkVersion; }
|
||||
|
||||
/// @brief Returns the maximum SDK version of this function signature.
|
||||
/// @return The maximum SDK version of this function signature.
|
||||
constexpr SdkVersion GetMaximumSdkVersion() const { return _maxSdkVersion; }
|
||||
|
||||
private:
|
||||
const u32 _pattern[4];
|
||||
const SdkVersion _minSdkVersion;
|
||||
const SdkVersion _maxSdkVersion;
|
||||
};
|
||||
20
arm9/source/patches/OffsetToSectorRemapAsm.h
Normal file
20
arm9/source/patches/OffsetToSectorRemapAsm.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "SectorRemapPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(offsettosectorremap);
|
||||
|
||||
extern "C" u32 offset_to_sector_remap(u32 romOffset);
|
||||
|
||||
class OffsetToSectorRemapPatchCode : public SectorRemapPatchCode
|
||||
{
|
||||
public:
|
||||
explicit OffsetToSectorRemapPatchCode(PatchHeap& patchHeap)
|
||||
: SectorRemapPatchCode(SECTION_START(offsettosectorremap), SECTION_SIZE(offsettosectorremap), patchHeap)
|
||||
{ }
|
||||
|
||||
const void* GetRemapFunction() const override
|
||||
{
|
||||
return GetAddressAtTarget((void*)offset_to_sector_remap);
|
||||
}
|
||||
};
|
||||
22
arm9/source/patches/OffsetToSectorRemapAsm.s
Normal file
22
arm9/source/patches/OffsetToSectorRemapAsm.s
Normal file
@@ -0,0 +1,22 @@
|
||||
.cpu arm7tdmi
|
||||
.section "offsettosectorremap", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = rom offset
|
||||
// returns sd sector in r0
|
||||
// returns remaining sectors in cluster in lr
|
||||
.global offset_to_sector_remap
|
||||
.type offset_to_sector_remap, %function
|
||||
offset_to_sector_remap:
|
||||
push {lr}
|
||||
lsrs r0, r0, #9 // byte address to sector address
|
||||
movs r1, #1
|
||||
mov lr, r1 // read at most 1 sector at a time
|
||||
pop {pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
21
arm9/source/patches/Patch.h
Normal file
21
arm9/source/patches/Patch.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "PatchContext.h"
|
||||
|
||||
/// @brief Abstract class representing a patch intended for the arm9 or arm7 binary.
|
||||
class Patch
|
||||
{
|
||||
public:
|
||||
/// @brief Next patch in the singly linked list.
|
||||
Patch* next = nullptr;
|
||||
|
||||
/// @brief Tries to find the patch target in the region corresponding
|
||||
/// to the given patchContext.
|
||||
/// @param patchContext The patch context to use.
|
||||
/// @return True if the patch target was found, or false otherwise.
|
||||
virtual bool FindPatchTarget(PatchContext& patchContext) = 0;
|
||||
|
||||
/// @brief Applies the patch to the target that was previously found
|
||||
/// with FindPatchTarget.
|
||||
/// @param patchContext The patch context to use.
|
||||
virtual void ApplyPatch(PatchContext& patchContext) = 0;
|
||||
};
|
||||
47
arm9/source/patches/PatchCode.h
Normal file
47
arm9/source/patches/PatchCode.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include <string.h>
|
||||
#include "PatchHeap.h"
|
||||
|
||||
/// @brief Class representing a block of patch code to be inserted in a \see PatchHeap.
|
||||
class PatchCode
|
||||
{
|
||||
public:
|
||||
/// @brief Constructs a PatchCode instance for the given block of code
|
||||
/// to be inserted in the given patchHeap.
|
||||
/// @param code The start of the block of code.
|
||||
/// @param size The size of the block of code.
|
||||
/// @param patchHeap The patch heap to use.
|
||||
PatchCode(const void* code, u32 size, PatchHeap& patchHeap)
|
||||
: _code(code), _size(size), _targetAddress(patchHeap.Alloc(size)) { }
|
||||
|
||||
~PatchCode()
|
||||
{
|
||||
LOG_FATAL("Patch code must not be deleted.\n");
|
||||
while (1);
|
||||
}
|
||||
|
||||
/// @brief Converts a pointer inside the original code block
|
||||
/// to a pointer at the target location in the patch heap.
|
||||
/// @param ptr The pointer to convert.
|
||||
/// @return The converted pointer.
|
||||
const void* GetAddressAtTarget(const void* ptr) const
|
||||
{
|
||||
return (const void*)((u32)ptr - (u32)_code + (u32)_targetAddress);
|
||||
}
|
||||
|
||||
/// @brief Copies the patch code to the target address.
|
||||
void CopyToTarget() const
|
||||
{
|
||||
memcpy(_targetAddress, _code, _size);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// @brief The start of the code block.
|
||||
const void* const _code;
|
||||
|
||||
/// @brief The size of the code block.
|
||||
const u32 _size;
|
||||
|
||||
/// @brief The target address for the code block in the patch heap.
|
||||
void* const _targetAddress;
|
||||
};
|
||||
56
arm9/source/patches/PatchCodeCollection.cpp
Normal file
56
arm9/source/patches/PatchCodeCollection.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "common.h"
|
||||
#include "PatchCodeCollection.h"
|
||||
|
||||
void PatchCodeCollection::AddUniquePatchCode(PatchCode* patchCode)
|
||||
{
|
||||
for (auto& entry : _patchCodeEntries)
|
||||
{
|
||||
if (entry.type == 0)
|
||||
{
|
||||
entry.type = 0xFFFFFFFFu;
|
||||
entry.patchCode = patchCode;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_FATAL("Out of patch code entries.\n");
|
||||
while (true);
|
||||
}
|
||||
|
||||
PatchCodeCollection::PatchCodeEntry& PatchCodeCollection::GetOrAddSharedPatchCode(u32 id)//, create_patch_code_func_t factoryFunction)
|
||||
{
|
||||
PatchCodeEntry* firstFreeEntry = nullptr;
|
||||
for (auto& entry : _patchCodeEntries)
|
||||
{
|
||||
if (entry.type == 0)
|
||||
{
|
||||
firstFreeEntry = &entry;
|
||||
break;
|
||||
}
|
||||
else if (entry.type == id)
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstFreeEntry == nullptr)
|
||||
{
|
||||
LOG_FATAL("Out of patch code entries.\n");
|
||||
while (true);
|
||||
}
|
||||
|
||||
return *firstFreeEntry;
|
||||
}
|
||||
|
||||
void PatchCodeCollection::CopyAllToTarget() const
|
||||
{
|
||||
for (auto& entry : _patchCodeEntries)
|
||||
{
|
||||
if (entry.type == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
entry.patchCode->CopyToTarget();
|
||||
}
|
||||
}
|
||||
77
arm9/source/patches/PatchCodeCollection.h
Normal file
77
arm9/source/patches/PatchCodeCollection.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include "PatchCode.h"
|
||||
#include "PatchHeap.h"
|
||||
|
||||
/// @brief Class representing a collection of patch codes.
|
||||
class PatchCodeCollection
|
||||
{
|
||||
private:
|
||||
using create_patch_code_func_t = PatchCode* (*)(PatchCodeCollection& patchCodeCollection);
|
||||
|
||||
public:
|
||||
PatchCodeCollection()
|
||||
{
|
||||
_patchCodeEntries.fill({ 0, nullptr });
|
||||
}
|
||||
|
||||
PatchCodeCollection(const PatchCodeCollection&) = delete;
|
||||
|
||||
/// @brief Adds a unique patch code to the collection. If the same patch code is added another time, it will be duplicated.
|
||||
/// @tparam T The type of patch code.
|
||||
/// @tparam ...Args The argument types for the patch code constructor.
|
||||
/// @param ...args The arguments for the patch code constructor.
|
||||
/// @return A pointer to the patch code.
|
||||
template <typename T, typename... Args> requires std::derived_from<T, PatchCode>
|
||||
const T* AddUniquePatchCode(Args&&... args)
|
||||
{
|
||||
T* patchCode = new T(std::forward<Args>(args)...);
|
||||
AddUniquePatchCode(patchCode);
|
||||
return patchCode;
|
||||
}
|
||||
|
||||
/// @brief Gets or adds a shared patch code from/to the collection.
|
||||
/// If the same patch code is requested another time, the same instance will be returned.
|
||||
/// @tparam T The type of patch code.
|
||||
/// @param factoryFunction A factory function that produces a pointer to the patch code class (T*).
|
||||
/// @return A pointer to the patch code.
|
||||
template <typename T> requires std::derived_from<std::remove_pointer_t<std::invoke_result_t<T>>, PatchCode>
|
||||
auto GetOrAddSharedPatchCode(T factoryFunction)
|
||||
// return type of the factory function converted from a pointer to a pointer to const
|
||||
-> std::add_pointer_t<std::add_const_t<std::remove_pointer_t<decltype(factoryFunction())>>>
|
||||
{
|
||||
u32 id = GetPatchCodeId<std::remove_pointer_t<decltype(factoryFunction())>>();
|
||||
auto& entry = GetOrAddSharedPatchCode(id);
|
||||
if (entry.type == 0)
|
||||
{
|
||||
entry.type = id; // claim this slot before calling the factory function
|
||||
entry.patchCode = factoryFunction();
|
||||
}
|
||||
|
||||
return static_cast<decltype(factoryFunction())>(entry.patchCode);
|
||||
}
|
||||
|
||||
/// @brief Copies all patch codes to their target location.
|
||||
void CopyAllToTarget() const;
|
||||
|
||||
private:
|
||||
struct PatchCodeEntry
|
||||
{
|
||||
u32 type;
|
||||
PatchCode* patchCode;
|
||||
};
|
||||
|
||||
std::array<PatchCodeEntry, 64> _patchCodeEntries;
|
||||
|
||||
void AddUniquePatchCode(PatchCode* patchCode);
|
||||
|
||||
PatchCodeEntry& GetOrAddSharedPatchCode(u32 id);
|
||||
|
||||
template <class T>
|
||||
static u32 GetPatchCodeId()
|
||||
{
|
||||
static const char typeId = 0;
|
||||
return (u32)&typeId;
|
||||
}
|
||||
};
|
||||
28
arm9/source/patches/PatchCollection.cpp
Normal file
28
arm9/source/patches/PatchCollection.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "common.h"
|
||||
#include "PatchContext.h"
|
||||
#include "PatchCollection.h"
|
||||
|
||||
bool PatchCollection::TryPerformPatches(PatchContext& patchContext)
|
||||
{
|
||||
LOG_DEBUG("PatchCollection::PerformPatches\n");
|
||||
Patch* cur = _head;
|
||||
while (cur)
|
||||
{
|
||||
if (!cur->FindPatchTarget(patchContext))
|
||||
{
|
||||
LOG_DEBUG("Patch target not found!\n");
|
||||
return false;
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
cur = _head;
|
||||
while (cur)
|
||||
{
|
||||
cur->ApplyPatch(patchContext);
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
patchContext.GetPatchCodeCollection().CopyAllToTarget();
|
||||
return true;
|
||||
}
|
||||
32
arm9/source/patches/PatchCollection.h
Normal file
32
arm9/source/patches/PatchCollection.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "Patch.h"
|
||||
|
||||
/// @brief Class containing a linked list of patches.
|
||||
class PatchCollection
|
||||
{
|
||||
public:
|
||||
/// @brief Adds the given \p patch to the collection.
|
||||
/// @param patch The patch to add.
|
||||
void AddPatch(Patch* patch)
|
||||
{
|
||||
LOG_DEBUG("PatchCollection::AddPatch\n");
|
||||
if (!_head)
|
||||
{
|
||||
_head = patch;
|
||||
}
|
||||
if (_tail)
|
||||
{
|
||||
_tail->next = patch;
|
||||
}
|
||||
_tail = patch;
|
||||
}
|
||||
|
||||
/// @brief Tries to perform all patches in this collection.
|
||||
/// @param patchContext The patch context to use.
|
||||
/// @return \c true when all patches were successfully performed, or \c false otherwise.
|
||||
bool TryPerformPatches(PatchContext& patchContext);
|
||||
|
||||
private:
|
||||
Patch* _head = nullptr;
|
||||
Patch* _tail = nullptr;
|
||||
};
|
||||
31
arm9/source/patches/PatchContext.cpp
Normal file
31
arm9/source/patches/PatchContext.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "common.h"
|
||||
#include "fastSearch.h"
|
||||
#include "platform/LoaderPlatform.h"
|
||||
#include "PatchContext.h"
|
||||
|
||||
u32* PatchContext::FindPattern32(const u32* pattern, u32 byteLength) const
|
||||
{
|
||||
#ifdef LIBTWL_ARM9
|
||||
if (byteLength == 16)
|
||||
{
|
||||
return (u32*)fastSearch16((const u32*)_data, _dataSize, pattern);
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32* PatchContext::FindPattern32Twl(const u32* pattern, u32 byteLength) const
|
||||
{
|
||||
if (!_twlData || _twlDataSize == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef LIBTWL_ARM9
|
||||
if (byteLength == 16)
|
||||
{
|
||||
return (u32*)fastSearch16((const u32*)_twlData, _twlDataSize, pattern);
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
61
arm9/source/patches/PatchContext.h
Normal file
61
arm9/source/patches/PatchContext.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "SdkVersion.h"
|
||||
#include "PatchHeap.h"
|
||||
#include "PatchCodeCollection.h"
|
||||
|
||||
class LoaderPlatform;
|
||||
|
||||
/// @brief Class providing a context for performing patches on retail rom arm7 or arm9.
|
||||
class PatchContext
|
||||
{
|
||||
public:
|
||||
PatchContext(void* data, u32 dataSize, void* twlData, u32 twlDataSize,
|
||||
SdkVersion sdkVersion, u32 gameCode, const LoaderPlatform* loaderPlatform)
|
||||
: _data(data), _dataSize(dataSize), _twlData(twlData), _twlDataSize(twlDataSize)
|
||||
, _sdkVersion(sdkVersion), _gameCode(gameCode), _loaderPlatform(loaderPlatform) { }
|
||||
|
||||
/// @brief Tries to find the given \p pattern of the given \p byteLength in the ntr region.
|
||||
/// @param pattern The pattern to find.
|
||||
/// @param byteLength The length of the pattern.
|
||||
/// @return A pointer to the first location where the pattern was found, or \c nullptr if the pattern was not found.
|
||||
u32* FindPattern32(const u32* pattern, u32 byteLength) const;
|
||||
|
||||
/// @brief Tries to find the given \p pattern of the given \p byteLength in the twl region.
|
||||
/// @param pattern The pattern to find.
|
||||
/// @param byteLength The length of the pattern.
|
||||
/// @return A pointer to the first location where the pattern was found, or \c nullptr if the pattern was not found.
|
||||
u32* FindPattern32Twl(const u32* pattern, u32 byteLength) const;
|
||||
|
||||
/// @brief Returns the patch heap of this context.
|
||||
/// @return The patch heap of this context.
|
||||
constexpr PatchHeap& GetPatchHeap() { return _patchHeap; }
|
||||
|
||||
/// @brief Returns the patch code collection of this context.
|
||||
/// @return The patch code collection of this context.
|
||||
constexpr PatchCodeCollection& GetPatchCodeCollection() { return _patchCodeCollection; }
|
||||
|
||||
/// @brief Returns the SDK version of the rom that is being patched.
|
||||
/// @return The SDK version of the rom that is being patched.
|
||||
constexpr SdkVersion GetSdkVersion() const { return _sdkVersion; }
|
||||
|
||||
/// @brief Returns the game code of the rom that is being patched.
|
||||
/// @return The game code of the rom that is being patched.
|
||||
constexpr u32 GetGameCode() const { return _gameCode; }
|
||||
|
||||
/// @brief Returns the loader platform that should be used for the patches.
|
||||
/// @return The loader platform that should be used for the patches.
|
||||
constexpr const LoaderPlatform* GetLoaderPlatform() { return _loaderPlatform; }
|
||||
|
||||
private:
|
||||
void* _data;
|
||||
u32 _dataSize;
|
||||
void* _twlData;
|
||||
u32 _twlDataSize;
|
||||
SdkVersion _sdkVersion;
|
||||
u32 _gameCode;
|
||||
|
||||
PatchHeap _patchHeap;
|
||||
PatchCodeCollection _patchCodeCollection;
|
||||
const LoaderPlatform* _loaderPlatform;
|
||||
};
|
||||
100
arm9/source/patches/PatchHeap.cpp
Normal file
100
arm9/source/patches/PatchHeap.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "common.h"
|
||||
#include "PatchHeap.h"
|
||||
|
||||
PatchHeap::PatchHeap()
|
||||
: _freeBlocks(nullptr)
|
||||
{
|
||||
_unusedBlockPool = &_blocks[0];
|
||||
for (u32 i = 0; i < _blocks.size() - 1; i++)
|
||||
_blocks[i].next = &_blocks[i + 1];
|
||||
_blocks[_blocks.size() - 1].next = nullptr;
|
||||
}
|
||||
|
||||
void PatchHeap::AddFreeSpace(void* block, u32 size)
|
||||
{
|
||||
PatchHeapBlock* cur = _freeBlocks;
|
||||
|
||||
bool added = false;
|
||||
while (cur)
|
||||
{
|
||||
if ((u8*)cur->block + cur->size == block)
|
||||
{
|
||||
cur->size += size;
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
else if ((u8*)block + size == cur->block)
|
||||
{
|
||||
cur->block = block;
|
||||
cur->size += size;
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
if (added)
|
||||
return;
|
||||
|
||||
PatchHeapBlock* heapBlock = _unusedBlockPool;
|
||||
_unusedBlockPool = _unusedBlockPool->next;
|
||||
|
||||
heapBlock->block = block;
|
||||
heapBlock->size = size;
|
||||
heapBlock->next = _freeBlocks;
|
||||
_freeBlocks = heapBlock;
|
||||
}
|
||||
|
||||
void* PatchHeap::Alloc(u32 size)
|
||||
{
|
||||
PatchHeapBlock* prev = nullptr;
|
||||
PatchHeapBlock* cur = _freeBlocks;
|
||||
|
||||
PatchHeapBlock* bestBlock = nullptr;
|
||||
PatchHeapBlock* bestBlockPrev = nullptr;
|
||||
|
||||
while (cur)
|
||||
{
|
||||
if (cur->size >= size && (!bestBlock || cur->size < bestBlock->size))
|
||||
{
|
||||
bestBlock = cur;
|
||||
bestBlockPrev = prev;
|
||||
if (bestBlock->size == size)
|
||||
break;
|
||||
}
|
||||
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
if (!bestBlock)
|
||||
{
|
||||
LOG_FATAL("No space found to put patch of size 0x%x\n", size);
|
||||
while (1);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* result = bestBlock->block;
|
||||
|
||||
if (bestBlock->size == size)
|
||||
{
|
||||
if (bestBlockPrev)
|
||||
bestBlockPrev->next = bestBlock->next;
|
||||
|
||||
if (_freeBlocks == bestBlock)
|
||||
_freeBlocks = bestBlock->next;
|
||||
|
||||
bestBlock->next = _unusedBlockPool;
|
||||
_unusedBlockPool = bestBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
bestBlock->block = (u8*)bestBlock->block + size;
|
||||
bestBlock->size -= size;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Allocated 0x%p, size = 0x%X\n", result, size);
|
||||
|
||||
return result;
|
||||
}
|
||||
34
arm9/source/patches/PatchHeap.h
Normal file
34
arm9/source/patches/PatchHeap.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
|
||||
/// @brief Class implementing a heap intended for patch code.
|
||||
/// The heap consists of a linked list of blocks of free
|
||||
/// space. It is possible to add many small pieces.
|
||||
class PatchHeap
|
||||
{
|
||||
public:
|
||||
PatchHeap();
|
||||
|
||||
/// @brief Adds a block of free space to the patch heap.
|
||||
/// @param block The start of the block.
|
||||
/// @param size The size of the block.
|
||||
void AddFreeSpace(void* block, u32 size);
|
||||
|
||||
/// @brief Allocated a block of the given size from the patch heap.
|
||||
/// @param size The size to allocate.
|
||||
/// @return A pointer to the allocated block if successful, or nullptr otherwise.
|
||||
void* Alloc(u32 size);
|
||||
|
||||
private:
|
||||
struct PatchHeapBlock
|
||||
{
|
||||
PatchHeapBlock* next;
|
||||
void* block;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
std::array<PatchHeapBlock, 64> _blocks;
|
||||
PatchHeapBlock* _unusedBlockPool;
|
||||
|
||||
PatchHeapBlock* _freeBlocks;
|
||||
};
|
||||
11
arm9/source/patches/SectorRemapPatchCode.h
Normal file
11
arm9/source/patches/SectorRemapPatchCode.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "PatchCode.h"
|
||||
|
||||
class SectorRemapPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
SectorRemapPatchCode(const void* code, u32 size, PatchHeap& patchHeap)
|
||||
: PatchCode(code, size, patchHeap) { }
|
||||
|
||||
virtual const void* GetRemapFunction() const = 0;
|
||||
};
|
||||
15
arm9/source/patches/arm7/CardiTaskThreadPatchAsm.h
Normal file
15
arm9/source/patches/arm7/CardiTaskThreadPatchAsm.h
Normal 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;
|
||||
123
arm9/source/patches/arm7/CardiTaskThreadPatchAsm.s
Normal file
123
arm9/source/patches/arm7/CardiTaskThreadPatchAsm.s
Normal 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
|
||||
39
arm9/source/patches/arm7/DisableArm7WramClearPatch.cpp
Normal file
39
arm9/source/patches/arm7/DisableArm7WramClearPatch.cpp
Normal 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;
|
||||
}
|
||||
13
arm9/source/patches/arm7/DisableArm7WramClearPatch.h
Normal file
13
arm9/source/patches/arm7/DisableArm7WramClearPatch.h
Normal 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;
|
||||
};
|
||||
97
arm9/source/patches/arm7/OsGetInitArenaLoPatch.cpp
Normal file
97
arm9/source/patches/arm7/OsGetInitArenaLoPatch.cpp
Normal 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;
|
||||
}
|
||||
21
arm9/source/patches/arm7/OsGetInitArenaLoPatch.h
Normal file
21
arm9/source/patches/arm7/OsGetInitArenaLoPatch.h
Normal 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;
|
||||
};
|
||||
27
arm9/source/patches/arm7/PokemonDownloaderArm7Patch.cpp
Normal file
27
arm9/source/patches/arm7/PokemonDownloaderArm7Patch.cpp
Normal 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
|
||||
}
|
||||
14
arm9/source/patches/arm7/PokemonDownloaderArm7Patch.h
Normal file
14
arm9/source/patches/arm7/PokemonDownloaderArm7Patch.h
Normal 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;
|
||||
};
|
||||
19
arm9/source/patches/arm7/PokemonDownloaderArm7PatchAsm.h
Normal file
19
arm9/source/patches/arm7/PokemonDownloaderArm7PatchAsm.h
Normal 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);
|
||||
}
|
||||
};
|
||||
25
arm9/source/patches/arm7/PokemonDownloaderArm7PatchAsm.s
Normal file
25
arm9/source/patches/arm7/PokemonDownloaderArm7PatchAsm.s
Normal 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
|
||||
31
arm9/source/patches/arm7/ReadSaveAsm.h
Normal file
31
arm9/source/patches/arm7/ReadSaveAsm.h
Normal 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);
|
||||
}
|
||||
};
|
||||
75
arm9/source/patches/arm7/ReadSaveAsm.s
Normal file
75
arm9/source/patches/arm7/ReadSaveAsm.s
Normal 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
|
||||
25
arm9/source/patches/arm7/SaveOffsetToSdSectorAsm.h
Normal file
25
arm9/source/patches/arm7/SaveOffsetToSdSectorAsm.h
Normal 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);
|
||||
}
|
||||
};
|
||||
50
arm9/source/patches/arm7/SaveOffsetToSdSectorAsm.s
Normal file
50
arm9/source/patches/arm7/SaveOffsetToSdSectorAsm.s
Normal 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
|
||||
34
arm9/source/patches/arm7/WriteSaveAsm.h
Normal file
34
arm9/source/patches/arm7/WriteSaveAsm.h
Normal 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);
|
||||
}
|
||||
};
|
||||
86
arm9/source/patches/arm7/WriteSaveAsm.s
Normal file
86
arm9/source/patches/arm7/WriteSaveAsm.s
Normal 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
|
||||
285
arm9/source/patches/arm7/sdk2to4/CardiTaskThreadPatch.cpp
Normal file
285
arm9/source/patches/arm7/sdk2to4/CardiTaskThreadPatch.cpp
Normal 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;
|
||||
}
|
||||
20
arm9/source/patches/arm7/sdk2to4/CardiTaskThreadPatch.h
Normal file
20
arm9/source/patches/arm7/sdk2to4/CardiTaskThreadPatch.h
Normal 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);
|
||||
};
|
||||
139
arm9/source/patches/arm7/sdk5/CardiDoTaskFromArm9Patch.cpp
Normal file
139
arm9/source/patches/arm7/sdk5/CardiDoTaskFromArm9Patch.cpp
Normal 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);
|
||||
}
|
||||
17
arm9/source/patches/arm7/sdk5/CardiDoTaskFromArm9Patch.h
Normal file
17
arm9/source/patches/arm7/sdk5/CardiDoTaskFromArm9Patch.h
Normal 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);
|
||||
};
|
||||
14
arm9/source/patches/arm7/sdk5/CardiDoTaskFromArm9PatchAsm.h
Normal file
14
arm9/source/patches/arm7/sdk5/CardiDoTaskFromArm9PatchAsm.h
Normal 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;
|
||||
82
arm9/source/patches/arm7/sdk5/Sdk5DsiSdCardRedirectPatch.cpp
Normal file
82
arm9/source/patches/arm7/sdk5/Sdk5DsiSdCardRedirectPatch.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
14
arm9/source/patches/arm7/sdk5/Sdk5DsiSdCardRedirectPatch.h
Normal file
14
arm9/source/patches/arm7/sdk5/Sdk5DsiSdCardRedirectPatch.h
Normal 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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
142
arm9/source/patches/arm9/CardiReadRomIdCorePatch.cpp
Normal file
142
arm9/source/patches/arm9/CardiReadRomIdCorePatch.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "common.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "../PatchContext.h"
|
||||
#include "../FunctionSignature.h"
|
||||
#include "CardiReadRomIdCorePatchAsm.h"
|
||||
#include "CardiReadRomIdCorePatch.h"
|
||||
|
||||
static constexpr auto sSignaturesArm = std::to_array<const FunctionSignature>
|
||||
({
|
||||
{ (const u32[]) { 0xE92D4000u, 0xE24DD004u, 0xE3A0032Eu, 0xE3A01000u }, 0x02004F50, 0x04017531 },
|
||||
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFF0Bu }, 0x03007532, 0x03017531 },
|
||||
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFF08u }, 0x03017530, 0x03017534 },
|
||||
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFF07u }, 0x03027530, 0x04007531 },
|
||||
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFEF2u }, 0x04007531, 0x04007531 },
|
||||
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFEFDu }, 0x04007532, 0x04007532 },
|
||||
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFEFBu }, 0x04017530, 0x04027539 },
|
||||
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFEE6u }, 0x04017533, 0x04017533 },
|
||||
{ (const u32[]) { 0xE92D4010u, 0xE3A04000u, 0xE1A01004u, 0xE3A0032Eu }, 0x04027537, 0x04027539 }
|
||||
});
|
||||
|
||||
static constexpr auto sSignaturesThumb = std::to_array<const FunctionSignature>
|
||||
({
|
||||
{ (const u32[]) { 0xB081B500u, 0x2100480Bu, 0xF98AF000u, 0x6801480Au }, 0x03007530, 0x03012776 },
|
||||
{ (const u32[]) { 0xB081B500u, 0x2100480Bu, 0xF990F000u, 0x6801480Au }, 0x03007532, 0x03007532 },
|
||||
{ (const u32[]) { 0x202EB508u, 0x21000680u, 0xFE8CF7FFu, 0x68014809u }, 0x03017531, 0x03017531 },
|
||||
{ (const u32[]) { 0xB081B500u, 0x2100480Bu, 0xF98EF000u, 0x6801480Au }, 0x03017531, 0x03017534 },
|
||||
{ (const u32[]) { 0xB081B500u, 0x2100480Cu, 0xF992F000u, 0x6800480Bu }, 0x03027530, 0x03027532 },
|
||||
{ (const u32[]) { 0x202EB508u, 0x21000680u, 0xFE8AF7FFu, 0x6800480Au }, 0x03027530, 0x04007531 },
|
||||
{ (const u32[]) { 0x202EB508u, 0x21000680u, 0xFE74F7FFu, 0x6800480Au }, 0x04017530, 0x04027539 },
|
||||
{ (const u32[]) { 0x202EB508u, 0x21000680u, 0xFE62F7FFu, 0x6800480Au }, 0x04027531, 0x04027531 },
|
||||
{ (const u32[]) { 0x202EB508u, 0x21000680u, 0xFE56F7FFu, 0x6800480Au }, 0x04027537, 0x04027537 }
|
||||
});
|
||||
|
||||
static constexpr auto sSignaturesArmSdk5 = std::to_array<const FunctionSignature>
|
||||
({
|
||||
{ (const u32[]) { 0xE92D4010u, 0xE3A040B8u, 0xEBFFFFD6u, 0xE3500000u }, 0x0500753B, 0x05027534 },
|
||||
{ (const u32[]) { 0xE92D4008u, 0xE3A000B8u, 0xE3A01000u, 0xEBFFFFCEu }, 0x05017536, 0x05047531 },
|
||||
{ (const u32[]) { 0xE92D4038u, 0xE3A050B8u, 0xEBFFFFD7u, 0xE3500000u }, 0x05017537, 0x05057535 },
|
||||
{ (const u32[]) { 0xE92D4010u, 0xE3A04000u, 0xE1A01004u, 0xE3A000B8u }, 0x05037531, 0x05057535 }
|
||||
});
|
||||
|
||||
static constexpr auto sSignaturesThumbSdk5 = std::to_array<const FunctionSignature>
|
||||
({
|
||||
{ (const u32[]) { 0x24B8B510u, 0xFFC4F7FFu, 0xD0002800u, 0x1C202490u }, 0x05007538, 0x05017537 },
|
||||
{ (const u32[]) { 0x20B8B508u, 0xF7FF2100u, 0x480AFFABu, 0x480A6801u }, 0x05017537, 0x05057535 },
|
||||
{ (const u32[]) { 0x24B8B510u, 0xFFC6F7FFu, 0xD0002800u, 0x1C202490u }, 0x05037531, 0x05057534 }
|
||||
});
|
||||
|
||||
bool CardiReadRomIdCorePatch::FindPatchTarget(PatchContext& patchContext)
|
||||
{
|
||||
if (patchContext.GetSdkVersion().IsTwlSdk())
|
||||
{
|
||||
for (const auto& signature : sSignaturesArmSdk5)
|
||||
{
|
||||
if (CheckSignature(patchContext, signature))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_cardiReadRomIdCore)
|
||||
{
|
||||
for (const auto& signature : sSignaturesThumbSdk5)
|
||||
{
|
||||
if (CheckSignature(patchContext, signature))
|
||||
{
|
||||
_thumb = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& signature : sSignaturesArm)
|
||||
{
|
||||
if (CheckSignature(patchContext, signature))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_cardiReadRomIdCore)
|
||||
{
|
||||
for (const auto& signature : sSignaturesThumb)
|
||||
{
|
||||
if (CheckSignature(patchContext, signature))
|
||||
{
|
||||
_thumb = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_cardiReadRomIdCore)
|
||||
{
|
||||
LOG_WARNING("CARDi_ReadRomIDCore not found\n");
|
||||
}
|
||||
|
||||
return true; //_cardiReadRomIdCore != nullptr;
|
||||
}
|
||||
|
||||
void CardiReadRomIdCorePatch::ApplyPatch(PatchContext& patchContext)
|
||||
{
|
||||
if (!_cardiReadRomIdCore)
|
||||
return;
|
||||
|
||||
auto patchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<CardiReadRomIdCorePatchCode>
|
||||
(
|
||||
patchContext.GetPatchHeap(),
|
||||
(const void*)(patchContext.GetSdkVersion().IsTwlSdk() ? 0x02FFFC00 : 0x027FFC00)
|
||||
);
|
||||
|
||||
if (_thumb)
|
||||
{
|
||||
((u16*)_cardiReadRomIdCore)[0] = 0x4800;
|
||||
((u16*)_cardiReadRomIdCore)[1] = 0x4700;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cardiReadRomIdCore[0] = 0xE51FF004;
|
||||
}
|
||||
|
||||
_cardiReadRomIdCore[1] = (u32)patchCode->GetCardiReadRomIdCoreFunction();
|
||||
}
|
||||
|
||||
bool CardiReadRomIdCorePatch::CheckSignature(const PatchContext& patchContext, const FunctionSignature& signature)
|
||||
{
|
||||
if (patchContext.GetSdkVersion() >= signature.GetMinimumSdkVersion() &&
|
||||
patchContext.GetSdkVersion() <= signature.GetMaximumSdkVersion())
|
||||
{
|
||||
_cardiReadRomIdCore = patchContext.FindPattern32(signature.GetPattern(), 16);
|
||||
if (_cardiReadRomIdCore)
|
||||
{
|
||||
LOG_DEBUG("Found CARDi_ReadRomIDCore at %p\n", _cardiReadRomIdCore);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
18
arm9/source/patches/arm9/CardiReadRomIdCorePatch.h
Normal file
18
arm9/source/patches/arm9/CardiReadRomIdCorePatch.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "../Patch.h"
|
||||
|
||||
class FunctionSignature;
|
||||
|
||||
/// @brief Arm9 patch to avoid card id reads.
|
||||
class CardiReadRomIdCorePatch : public Patch
|
||||
{
|
||||
public:
|
||||
bool FindPatchTarget(PatchContext& patchContext) override;
|
||||
void ApplyPatch(PatchContext& patchContext) override;
|
||||
|
||||
private:
|
||||
bool CheckSignature(const PatchContext& patchContext, const FunctionSignature& signature);
|
||||
|
||||
u32* _cardiReadRomIdCore = nullptr;
|
||||
u16 _thumb = false;
|
||||
};
|
||||
24
arm9/source/patches/arm9/CardiReadRomIdCorePatchAsm.h
Normal file
24
arm9/source/patches/arm9/CardiReadRomIdCorePatchAsm.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "../PatchCode.h"
|
||||
#include "sections.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(patch_cardireadromidcore);
|
||||
|
||||
extern "C" u32 patch_cardireadromidcore_entry(void);
|
||||
|
||||
extern u32 patch_cardireadromidcore_cardid_address;
|
||||
|
||||
class CardiReadRomIdCorePatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
CardiReadRomIdCorePatchCode(PatchHeap& patchHeap, const void* cardIdPointer)
|
||||
: PatchCode(SECTION_START(patch_cardireadromidcore), SECTION_SIZE(patch_cardireadromidcore), patchHeap)
|
||||
{
|
||||
patch_cardireadromidcore_cardid_address = (u32)cardIdPointer;
|
||||
}
|
||||
|
||||
const void* GetCardiReadRomIdCoreFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)patch_cardireadromidcore_entry);
|
||||
}
|
||||
};
|
||||
21
arm9/source/patches/arm9/CardiReadRomIdCorePatchAsm.s
Normal file
21
arm9/source/patches/arm9/CardiReadRomIdCorePatchAsm.s
Normal file
@@ -0,0 +1,21 @@
|
||||
.cpu arm946e-s
|
||||
.section "patch_cardireadromidcore", "ax"
|
||||
.syntax unified
|
||||
|
||||
.thumb
|
||||
.global patch_cardireadromidcore_entry
|
||||
.type patch_cardireadromidcore_entry, %function
|
||||
patch_cardireadromidcore_entry:
|
||||
ldr r0, patch_cardireadromidcore_cardid_address
|
||||
ldr r0, [r0]
|
||||
bx lr
|
||||
|
||||
.balign 4
|
||||
|
||||
.global patch_cardireadromidcore_cardid_address
|
||||
patch_cardireadromidcore_cardid_address:
|
||||
.word 0x027FFC00
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
6
arm9/source/patches/arm9/FixCp15Asm.h
Normal file
6
arm9/source/patches/arm9/FixCp15Asm.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(fixcp15);
|
||||
|
||||
extern "C" void fix_cp15_asm();
|
||||
26
arm9/source/patches/arm9/FixCp15Asm.s
Normal file
26
arm9/source/patches/arm9/FixCp15Asm.s
Normal file
@@ -0,0 +1,26 @@
|
||||
.cpu arm946e-s
|
||||
.section "fixcp15", "ax"
|
||||
.syntax unified
|
||||
.arm
|
||||
|
||||
// to fix access to gba slot memory
|
||||
.global fix_cp15_asm
|
||||
.type fix_cp15_asm, %function
|
||||
fix_cp15_asm:
|
||||
mrc p15, 0, r0, c2, c0, 0
|
||||
bic r0, r0, #(1 << 3)
|
||||
mcr p15, 0, r0, c2, c0, 0
|
||||
mrc p15, 0, r0, c3, c0, 0
|
||||
bic r0, r0, #(1 << 3)
|
||||
mcr p15, 0, r0, c3, c0, 0
|
||||
mrc p15, 0, r0, c5, c0, 2
|
||||
bic r0, r0, #(0xF << 12)
|
||||
orr r0, r0, #(3 << 12)
|
||||
mcr p15, 0, r0, c5, c0, 2
|
||||
bx lr
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
125
arm9/source/patches/arm9/OSResetSystemPatch.cpp
Normal file
125
arm9/source/patches/arm9/OSResetSystemPatch.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#include "common.h"
|
||||
#include "../PatchContext.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "../platform/LoaderPlatform.h"
|
||||
#include "OSResetSystemPatchAsm.h"
|
||||
#include "OSResetSystemPatch.h"
|
||||
|
||||
static const u32 sOSResetSystemPatternSdk2Old[] = { 0xE59F101Cu, 0xE3A00010u, 0xE5815000u, 0xEB000005u };
|
||||
static const u32 sOSResetSystemPatternSdk2[] = { 0xE59F1018u, 0xE3A00010u, 0xE5814000u, 0xEB000004u };
|
||||
static const u32 sOSResetSystemPatternSdk3[] = { 0xE59F1014u, 0xE3A00010u, 0xE5814000u, 0xEBFFFFD9u };
|
||||
static const u32 sOSResetSystemPatternSdk4[] = { 0xE59F103Cu, 0xE59F003Cu, 0xE5814000u, 0xEBFFFFD6u };
|
||||
static const u32 sOSResetSystemPatternPokemonDownloader[] = { 0xE59F1010u, 0xE3A00010u, 0xE5814000u, 0xEBFFFFDEu };
|
||||
|
||||
static const u32 sOSResetSystemPatternSdk5Old[] = { 0xE1A04000u, 0xE1D100B0u, 0xE3500002u, 0x1A000000u };
|
||||
static const u32 sOSResetSystemPatternSdk5New[] = { 0xE1A05000u, 0xE1D100B0u, 0xE3500002u, 0x1A000000u };
|
||||
static const u32 sOSResetSystemPatternSdk5HybridOld[] = { 0xE1A04000u, 0xE1D100B0u, 0xE3500002u, 0x0A000006 };
|
||||
static const u32 sOSResetSystemPatternSdk5HybridNew[] = { 0xE1A05000u, 0xE1D100B0u, 0xE3500002u, 0x0A000006 };
|
||||
|
||||
bool OSResetSystemPatch::FindPatchTarget(PatchContext& patchContext)
|
||||
{
|
||||
_osResetSystem = nullptr;
|
||||
if (patchContext.GetSdkVersion().IsTwlSdk())
|
||||
{
|
||||
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk5Old, sizeof(sOSResetSystemPatternSdk5Old));
|
||||
if (!_osResetSystem)
|
||||
{
|
||||
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk5New, sizeof(sOSResetSystemPatternSdk5New));
|
||||
}
|
||||
if (!_osResetSystem)
|
||||
{
|
||||
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk5HybridOld, sizeof(sOSResetSystemPatternSdk5HybridOld));
|
||||
_hybrid = true;
|
||||
}
|
||||
if (!_osResetSystem)
|
||||
{
|
||||
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk5HybridNew, sizeof(sOSResetSystemPatternSdk5HybridNew));
|
||||
_hybrid = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (patchContext.GetSdkVersion() >= 0x4017530)
|
||||
{
|
||||
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk4, sizeof(sOSResetSystemPatternSdk4));
|
||||
}
|
||||
if (!_osResetSystem && patchContext.GetSdkVersion() >= 0x3017530)
|
||||
{
|
||||
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk3, sizeof(sOSResetSystemPatternSdk3));
|
||||
}
|
||||
if (!_osResetSystem && patchContext.GetSdkVersion() >= 0x2004F50)
|
||||
{
|
||||
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk2, sizeof(sOSResetSystemPatternSdk2));
|
||||
}
|
||||
if (!_osResetSystem && patchContext.GetSdkVersion() >= 0x2004EE9)
|
||||
{
|
||||
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk2Old, sizeof(sOSResetSystemPatternSdk2Old));
|
||||
}
|
||||
if (!_osResetSystem)
|
||||
{
|
||||
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternPokemonDownloader, sizeof(sOSResetSystemPatternPokemonDownloader));
|
||||
}
|
||||
}
|
||||
|
||||
if (_osResetSystem)
|
||||
{
|
||||
LOG_DEBUG("Found end of OS_ResetSystem at %p\n", _osResetSystem);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG("OS_ResetSystem not found\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OSResetSystemPatch::ApplyPatch(PatchContext& patchContext)
|
||||
{
|
||||
if (!_osResetSystem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
u32 offset;
|
||||
if (patchContext.GetSdkVersion().IsTwlSdk())
|
||||
{
|
||||
patch_osresetsystem_arm7Entry_address = 0x02FFFE34;
|
||||
if (_hybrid)
|
||||
{
|
||||
offset = 0x80;
|
||||
if (!gIsDsiMode)
|
||||
{
|
||||
patch_osresetsystem_entry_jump_to_twl_arm7_sync = THUMB_NOP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0x44;
|
||||
patch_osresetsystem_entry_jump_to_twl_arm7_sync = THUMB_NOP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0xC;
|
||||
patch_osresetsystem_entry_jump_to_twl_arm7_sync = THUMB_NOP;
|
||||
}
|
||||
|
||||
auto loaderInfoTarget = (loader_info_t*)patchContext.GetPatchHeap().Alloc(sizeof(loader_info_t));
|
||||
memcpy(loaderInfoTarget, _loaderInfo, sizeof(loader_info_t));
|
||||
|
||||
auto sdReadPatchCode = patchContext.GetLoaderPlatform()->CreateSdReadPatchCode(
|
||||
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
|
||||
auto patchCodePart2 = patchContext.GetPatchCodeCollection().AddUniquePatchCode<OSResetSystemPart2PatchCode>(
|
||||
patchContext.GetPatchHeap());
|
||||
auto patchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<OSResetSystemPatchCode>
|
||||
(
|
||||
patchContext.GetPatchHeap(),
|
||||
loaderInfoTarget,
|
||||
sdReadPatchCode,
|
||||
patchCodePart2
|
||||
);
|
||||
|
||||
*(u32*)((u8*)_osResetSystem + offset) = 0xE51FF004;
|
||||
*(u32*)((u8*)_osResetSystem + offset + 4) = (u32)patchCode->GetOSResetSystemFunction();
|
||||
}
|
||||
|
||||
19
arm9/source/patches/arm9/OSResetSystemPatch.h
Normal file
19
arm9/source/patches/arm9/OSResetSystemPatch.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "../Patch.h"
|
||||
#include "LoaderInfo.h"
|
||||
|
||||
/// @brief Arm9 patch to make OS_ResetSystem reboot into Pico Loader.
|
||||
class OSResetSystemPatch : public Patch
|
||||
{
|
||||
public:
|
||||
explicit OSResetSystemPatch(const loader_info_t* loaderInfo)
|
||||
: _loaderInfo(loaderInfo) { }
|
||||
|
||||
bool FindPatchTarget(PatchContext& patchContext) override;
|
||||
void ApplyPatch(PatchContext& patchContext) override;
|
||||
|
||||
private:
|
||||
u32* _osResetSystem = nullptr;
|
||||
u32 _hybrid = false;
|
||||
const loader_info_t* _loaderInfo;
|
||||
};
|
||||
49
arm9/source/patches/arm9/OSResetSystemPatchAsm.h
Normal file
49
arm9/source/patches/arm9/OSResetSystemPatchAsm.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "../PatchCode.h"
|
||||
#include "sections.h"
|
||||
#include "LoaderInfo.h"
|
||||
#include "../platform/SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(patch_osresetsystem);
|
||||
DEFINE_SECTION_SYMBOLS(patch_osresetsystem_boot);
|
||||
|
||||
extern "C" void patch_osresetsystem_entry(void);
|
||||
extern "C" void patch_osresetsystem_bootPicoLoader(void);
|
||||
|
||||
extern const loader_info_t* patch_osresetsystem_loader_info_address;
|
||||
extern u32 patch_osresetsystem_readSdSectors_address;
|
||||
extern u32 patch_osresetsystem_bootPicoLoader_address;
|
||||
extern u16 patch_osresetsystem_entry_jump_to_twl_arm7_sync;
|
||||
extern u32 patch_osresetsystem_arm7Entry_address;
|
||||
|
||||
class OSResetSystemPart2PatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit OSResetSystemPart2PatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(patch_osresetsystem_boot), SECTION_SIZE(patch_osresetsystem_boot), patchHeap)
|
||||
{
|
||||
}
|
||||
|
||||
const void* GetOSResetSystemPart2Function() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)patch_osresetsystem_bootPicoLoader);
|
||||
}
|
||||
};
|
||||
|
||||
class OSResetSystemPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
OSResetSystemPatchCode(PatchHeap& patchHeap, const loader_info_t* loaderInfo,
|
||||
const SdReadPatchCode* sdReadPatchCode, const OSResetSystemPart2PatchCode* part2PatchCode)
|
||||
: PatchCode(SECTION_START(patch_osresetsystem), SECTION_SIZE(patch_osresetsystem), patchHeap)
|
||||
{
|
||||
patch_osresetsystem_loader_info_address = loaderInfo;
|
||||
patch_osresetsystem_readSdSectors_address = (u32)sdReadPatchCode->GetSdReadFunction();
|
||||
patch_osresetsystem_bootPicoLoader_address = (u32)part2PatchCode->GetOSResetSystemPart2Function();
|
||||
}
|
||||
|
||||
const void* GetOSResetSystemFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)patch_osresetsystem_entry);
|
||||
}
|
||||
};
|
||||
178
arm9/source/patches/arm9/OSResetSystemPatchAsm.s
Normal file
178
arm9/source/patches/arm9/OSResetSystemPatchAsm.s
Normal file
@@ -0,0 +1,178 @@
|
||||
.cpu arm946e-s
|
||||
.section "patch_osresetsystem", "ax"
|
||||
.syntax unified
|
||||
|
||||
.thumb
|
||||
.global patch_osresetsystem_entry
|
||||
.type patch_osresetsystem_entry, %function
|
||||
patch_osresetsystem_entry:
|
||||
adr r0, regIpcSync
|
||||
// regIpcSync, arm7ResetCommand, regVramCntA, vramAbcdLcdcSetting, readSdSectors_address, loader_info_address, vramCLcdcAddress, patch_osresetsystem_bootPicoLoader_address
|
||||
ldmia r0, {r0, r1, r2, r3, r4, r5, r6, r7}
|
||||
|
||||
// r9 = readSdSectors_address
|
||||
mov r9, r4
|
||||
mov r10, r7
|
||||
|
||||
// reset arm7
|
||||
1:
|
||||
ldr r6, [r0, #4]
|
||||
lsrs r6, r6, #2 // shift bit 1 to carry
|
||||
bcs 1b // while fifo full
|
||||
|
||||
str r1, [r0, #8]
|
||||
|
||||
adds r7, #(twl_arm7_sync - patch_osresetsystem_bootPicoLoader)
|
||||
mov lr, pc
|
||||
.global patch_osresetsystem_entry_jump_to_twl_arm7_sync
|
||||
patch_osresetsystem_entry_jump_to_twl_arm7_sync:
|
||||
bx r7
|
||||
|
||||
2:
|
||||
ldrb r7, [r0] // ipc sync
|
||||
cmp r7, #1
|
||||
bne 2b // while ipc sync from arm7 is not 1
|
||||
|
||||
// map vram ABCD to LCDC
|
||||
str r3, [r2]
|
||||
|
||||
// load pico loader arm9
|
||||
ldmia r5!, {r4,r6,r7} // clusterShift, database, clusterMap[0]
|
||||
ldr r7,= 0x06800000
|
||||
mov r11, pc
|
||||
b loadData
|
||||
|
||||
// load pico loader arm7
|
||||
ldr r5, patch_osresetsystem_loader_info_address
|
||||
adds r5, #52
|
||||
ldr r7, vramCLcdcAddress
|
||||
mov r11, pc
|
||||
b loadData
|
||||
|
||||
adr r5, patch_osresetsystem_loader_info_address
|
||||
bx r10
|
||||
|
||||
loadData_loop:
|
||||
subs r3, #2
|
||||
lsls r3, r4
|
||||
adds r0, r3, r6 // start sector
|
||||
movs r1, r7 // dst
|
||||
lsls r2, r4 // sector count
|
||||
lsls r3, r2, #9
|
||||
adds r7, r3
|
||||
|
||||
blx r9 // read sectors
|
||||
|
||||
loadData:
|
||||
ldmia r5!, {r2, r3} // ncl, startSector
|
||||
cmp r2, #0
|
||||
bne loadData_loop
|
||||
mov pc, r11
|
||||
|
||||
.balign 4
|
||||
|
||||
regIpcSync:
|
||||
.word 0x04000180
|
||||
|
||||
arm7ResetCommand:
|
||||
.word 0x4000C
|
||||
|
||||
regVramCntA:
|
||||
.word 0x04000240
|
||||
|
||||
vramAbcdLcdcSetting:
|
||||
.word 0x80808080
|
||||
|
||||
.global patch_osresetsystem_readSdSectors_address
|
||||
patch_osresetsystem_readSdSectors_address:
|
||||
.word 0
|
||||
|
||||
.global patch_osresetsystem_loader_info_address
|
||||
patch_osresetsystem_loader_info_address:
|
||||
.word 0
|
||||
|
||||
vramCLcdcAddress:
|
||||
.word 0x06840000
|
||||
|
||||
.global patch_osresetsystem_bootPicoLoader_address
|
||||
patch_osresetsystem_bootPicoLoader_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.section "patch_osresetsystem_boot", "ax"
|
||||
.thumb
|
||||
.global patch_osresetsystem_bootPicoLoader
|
||||
.type patch_osresetsystem_bootPicoLoader, %function
|
||||
patch_osresetsystem_bootPicoLoader:
|
||||
// loader_info_address, vramCLcdcAddress
|
||||
ldmia r5, {r3, r5}
|
||||
ldrh r0, [r3, #2] // loader_info_t::picoLoaderBootDrive
|
||||
strh r0, [r5, #8] // pload_header7_t::bootDrive
|
||||
ldr r0, [r5] // pload_header7_t::entryPoint
|
||||
|
||||
// set NTR_SHARED_MEMORY->romHeader.arm7EntryAddress
|
||||
ldr r4, patch_osresetsystem_arm7Entry_address
|
||||
str r0, [r4]
|
||||
|
||||
// map vram CD to arm7
|
||||
ldr r0,= 0x04000240
|
||||
ldr r7,= 0x8A82
|
||||
strh r7, [r0, #2]
|
||||
|
||||
adds r0, #(0x04000180 - 0x04000240) // REG_IPC_SYNC
|
||||
movs r1, #1
|
||||
strb r1, [r0, #1]
|
||||
|
||||
1:
|
||||
ldrb r7, [r0] // ipc sync
|
||||
cmp r7, #0
|
||||
bne 1b // while ipc sync from arm7 is not 0
|
||||
|
||||
movs r1, #0
|
||||
strb r1, [r0, #1]
|
||||
|
||||
movs r0, #0x68
|
||||
lsls r0, r0, #20 // 0x06800000
|
||||
bx r0
|
||||
|
||||
twl_arm7_sync:
|
||||
ldr r7,= 0x02FFFC24
|
||||
movs r1, #1
|
||||
strh r1, [r7, #4]
|
||||
|
||||
ldrh r6, [r7, #2]
|
||||
ldrh r1, [r7]
|
||||
1:
|
||||
adds r1, #1
|
||||
strh r1, [r7]
|
||||
ldrh r4, [r7, #2]
|
||||
cmp r6, r4
|
||||
beq 1b
|
||||
adds r1, #1
|
||||
strh r1, [r7]
|
||||
|
||||
movs r1, #3
|
||||
strh r1, [r7, #4]
|
||||
|
||||
ldrh r6, [r7, #2]
|
||||
ldrh r1, [r7]
|
||||
1:
|
||||
adds r1, #1
|
||||
strh r1, [r7]
|
||||
ldrh r4, [r7, #2]
|
||||
cmp r6, r4
|
||||
beq 1b
|
||||
adds r1, #1
|
||||
strh r1, [r7]
|
||||
|
||||
mov pc, lr
|
||||
|
||||
.balign 4
|
||||
|
||||
.global patch_osresetsystem_arm7Entry_address
|
||||
patch_osresetsystem_arm7Entry_address:
|
||||
.word 0x027FFE34
|
||||
|
||||
.pool
|
||||
.end
|
||||
@@ -0,0 +1,418 @@
|
||||
#include "common.h"
|
||||
#include "../../../PatchContext.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "gameCode.h"
|
||||
#include "DSProtectOverlayPatchv1Asm.h"
|
||||
#include "DSProtectOverlayPatchv2Asm.h"
|
||||
#include "DSProtectOverlayPatchv2sAsm.h"
|
||||
#include "DSProtectOverlayPatch.h"
|
||||
|
||||
const void* DSProtectOverlayPatch::InsertPatch(PatchContext& patchContext)
|
||||
{
|
||||
if (_version <= DSProtectVersion::v1_28)
|
||||
{
|
||||
return InsertPatchV1(patchContext);
|
||||
}
|
||||
else if (_version <= DSProtectVersion::v2_05)
|
||||
{
|
||||
return InsertPatchV2(patchContext);
|
||||
}
|
||||
else if (_version <= DSProtectVersion::v2_05s)
|
||||
{
|
||||
return InsertPatchV2s(patchContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING("Unsupported DSProtect version\n");
|
||||
return next ? (const void*)next->InsertPatch(patchContext) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DSProtectOverlayPatch::SetOldV1Offsets(const u16* offsets) const
|
||||
{
|
||||
int offsetIdx = 0;
|
||||
|
||||
if (_functionMask & 0b000001)
|
||||
dsprotectpatchv1_stub_protectb1_offset = offsets[offsetIdx++];
|
||||
else
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0; // does not exist
|
||||
|
||||
if (_functionMask & 0b000010)
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = offsets[offsetIdx++];
|
||||
else
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0; // does not exist
|
||||
|
||||
if (_functionMask & 0b000100)
|
||||
dsprotectpatchv1_stub_protectb2_offset = offsets[offsetIdx++];
|
||||
else
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0; // does not exist
|
||||
|
||||
if (_functionMask & 0b001000)
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = offsets[offsetIdx++];
|
||||
else
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
|
||||
|
||||
if (_functionMask & 0b010000)
|
||||
dsprotectpatchv1_stub_protectb3_offset = offsets[offsetIdx++];
|
||||
else
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0; // does not exist
|
||||
|
||||
if (_functionMask & 0b100000)
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = offsets[offsetIdx++];
|
||||
else
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
|
||||
}
|
||||
|
||||
const void* DSProtectOverlayPatch::InsertPatchV1(PatchContext& patchContext) const
|
||||
{
|
||||
const void* nextPatch = next ? (const void*)next->InsertPatch(patchContext) : nullptr;
|
||||
|
||||
dsprotectpatchv1_overlay_id = _overlayId;
|
||||
dsprotectpatchv1_base_offset = _overlayOffset;
|
||||
|
||||
switch (_version)
|
||||
{
|
||||
case DSProtectVersion::v1_05:
|
||||
{
|
||||
static u16 v105Offsets[] = { 0x858, 0x934, 0xA10, 0xAEC, 0xBC8, 0xC94 };
|
||||
SetOldV1Offsets(v105Offsets);
|
||||
|
||||
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
|
||||
dsprotectpatchv1_protectb1_return_value = 0;
|
||||
dsprotectpatchv1_protectb2_return_value = 0;
|
||||
dsprotectpatchv1_protectb3_return_value = 0;
|
||||
dsprotectpatchv1_notprotectb1_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb2_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb3_return_value = 1;
|
||||
// Remove xor 0x320 from the callback pointer
|
||||
dsprotectpatchv1_moveCallback = 0xE230EE32; // eors lr, r0, #0x320
|
||||
break;
|
||||
}
|
||||
case DSProtectVersion::v1_06:
|
||||
{
|
||||
static u16 v106Offsets[] = { 0x860, 0x938, 0xA10, 0xAE8, 0xBC0, 0xC88 };
|
||||
SetOldV1Offsets(v106Offsets);
|
||||
|
||||
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
|
||||
dsprotectpatchv1_protectb1_return_value = 0;
|
||||
dsprotectpatchv1_protectb2_return_value = 0;
|
||||
dsprotectpatchv1_protectb3_return_value = 0;
|
||||
dsprotectpatchv1_notprotectb1_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb2_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb3_return_value = 1;
|
||||
break;
|
||||
}
|
||||
case DSProtectVersion::v1_08:
|
||||
{
|
||||
static u16 v108Offsets[] = { 0x858, 0x930, 0xA08, 0xAE0, 0xBB8, 0xC94 };
|
||||
SetOldV1Offsets(v108Offsets);
|
||||
|
||||
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
|
||||
dsprotectpatchv1_protectb1_return_value = 0;
|
||||
dsprotectpatchv1_protectb2_return_value = 0;
|
||||
dsprotectpatchv1_protectb3_return_value = 0;
|
||||
dsprotectpatchv1_notprotectb1_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb2_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb3_return_value = 1;
|
||||
break;
|
||||
}
|
||||
case DSProtectVersion::v1_10:
|
||||
{
|
||||
static u16 v110Offsets[] = { 0x860, 0x940, 0xA20, 0xB00, 0xBE0, 0xCB0 };
|
||||
SetOldV1Offsets(v110Offsets);
|
||||
|
||||
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
|
||||
dsprotectpatchv1_protectb1_return_value = 0;
|
||||
dsprotectpatchv1_protectb2_return_value = 0;
|
||||
dsprotectpatchv1_protectb3_return_value = 0;
|
||||
dsprotectpatchv1_notprotectb1_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb2_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb3_return_value = 1;
|
||||
break;
|
||||
}
|
||||
case DSProtectVersion::v1_20:
|
||||
{
|
||||
switch (_functionMask)
|
||||
{
|
||||
case 0b001111:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x864;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x94C;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0xA34;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0xB1C;
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
|
||||
break;
|
||||
|
||||
case 0b010101:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x644;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0x72C;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0x814;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
|
||||
break;
|
||||
|
||||
case 0b010110:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x644;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0x72C;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0x814;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
|
||||
break;
|
||||
|
||||
case 0b011001:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x644;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0x72C;
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0x814;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
|
||||
break;
|
||||
|
||||
case 0b110101:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x644;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0x72C;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0x814;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0x8DC;
|
||||
break;
|
||||
|
||||
case 0b111111:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x864;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x94C;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0xA34;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0xB1C;
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0xC04;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0xCCC;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_WARNING("Unsupported DSProtect function mask\n");
|
||||
return nextPatch;
|
||||
}
|
||||
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
|
||||
dsprotectpatchv1_protectb1_return_value = 0;
|
||||
dsprotectpatchv1_protectb2_return_value = 0;
|
||||
dsprotectpatchv1_protectb3_return_value = 0;
|
||||
dsprotectpatchv1_notprotectb1_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb2_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb3_return_value = 1;
|
||||
break;
|
||||
}
|
||||
case DSProtectVersion::v1_22:
|
||||
{
|
||||
switch (_functionMask)
|
||||
{
|
||||
case 0b010101:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x64C;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0x734;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0x81C;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
|
||||
break;
|
||||
|
||||
case 0b010110:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x64C;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0x734;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0x81C;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
|
||||
break;
|
||||
|
||||
case 0b010111:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x754;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x83C;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0x924;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0xA0C;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
|
||||
break;
|
||||
|
||||
case 0b011111:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x874;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x95C;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0xA44;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0xB2C;
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0xC14;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
|
||||
break;
|
||||
|
||||
case 0b111111:
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x874;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x95C;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0xA44;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0xB2C;
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0xC14;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0xCDC;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_WARNING("Unsupported DSProtect function mask\n");
|
||||
return nextPatch;
|
||||
}
|
||||
|
||||
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
|
||||
dsprotectpatchv1_protectb1_return_value = 0;
|
||||
dsprotectpatchv1_protectb2_return_value = 0;
|
||||
dsprotectpatchv1_protectb3_return_value = 0;
|
||||
dsprotectpatchv1_notprotectb1_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb2_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb3_return_value = 1;
|
||||
break;
|
||||
}
|
||||
case DSProtectVersion::v1_23:
|
||||
case DSProtectVersion::v1_23Z:
|
||||
{
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x410;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0x500;
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0x5F0;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x488;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0x578;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0x668;
|
||||
// this seems to be a decrypt function that is called from 5 static init functions
|
||||
dsprotectpatchv1_nitro_static_init_offset = 0xD78;
|
||||
dsprotectpatchv1_protectb1_return_value = 0;
|
||||
dsprotectpatchv1_protectb2_return_value = 0;
|
||||
dsprotectpatchv1_protectb3_return_value = 0;
|
||||
dsprotectpatchv1_notprotectb1_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb2_return_value = 1;
|
||||
dsprotectpatchv1_notprotectb3_return_value = 1;
|
||||
break;
|
||||
}
|
||||
case DSProtectVersion::v1_25:
|
||||
case DSProtectVersion::v1_26:
|
||||
{
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x3DC;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0x4CC;
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0x5BC;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x454;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0x544;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0x634;
|
||||
dsprotectpatchv1_nitro_static_init_offset = 0x6AC;
|
||||
break;
|
||||
}
|
||||
case DSProtectVersion::v1_27:
|
||||
{
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x3A0;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0x418;
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0x490;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x3DC;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0x454;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0x4CC;
|
||||
dsprotectpatchv1_nitro_static_init_offset = 0x508;
|
||||
dsprotectpatchv1_loadReturnValue = 0; // Return the return value of the callback
|
||||
break;
|
||||
}
|
||||
case DSProtectVersion::v1_28:
|
||||
{
|
||||
dsprotectpatchv1_stub_protectb1_offset = 0x3A0;
|
||||
dsprotectpatchv1_stub_protectb2_offset = 0x418;
|
||||
dsprotectpatchv1_stub_protectb3_offset = 0x490;
|
||||
dsprotectpatchv1_stub_notprotectb1_offset = 0x3DC;
|
||||
dsprotectpatchv1_stub_notprotectb2_offset = 0x454;
|
||||
dsprotectpatchv1_stub_notprotectb3_offset = 0x4CC;
|
||||
dsprotectpatchv1_nitro_static_init_offset = 0x508;
|
||||
dsprotectpatchv1_loadReturnValue = 0; // Return the return value of the callback
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LOG_WARNING("Unsupported DSProtect version\n");
|
||||
return nextPatch;
|
||||
}
|
||||
}
|
||||
dsprotectpatchv1_nextAddress = (u32)nextPatch;
|
||||
|
||||
u32 patch1Size = SECTION_SIZE(dsprotectpatchv1_part1);
|
||||
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
|
||||
u32 patch2Size = SECTION_SIZE(dsprotectpatchv1_part2);
|
||||
void* patch2Address = patchContext.GetPatchHeap().Alloc(patch2Size);
|
||||
|
||||
dsprotectpatchv1_part2_address = (u32)&dsprotectpatchv1_entry2 - (u32)SECTION_START(dsprotectpatchv1_part2) + (u32)patch2Address;
|
||||
|
||||
u32 entryAddress1 = (u32)&dsprotectpatchv1_entry1 - (u32)SECTION_START(dsprotectpatchv1_part1) + (u32)patch1Address;
|
||||
memcpy(patch1Address, SECTION_START(dsprotectpatchv1_part1), patch1Size);
|
||||
memcpy(patch2Address, SECTION_START(dsprotectpatchv1_part2), patch2Size);
|
||||
|
||||
return (const void*)entryAddress1;
|
||||
}
|
||||
|
||||
struct ds_protect_v2_patch_t
|
||||
{
|
||||
u32 checksumFix;
|
||||
u16 stubProtectb1Offset;
|
||||
u16 stubProtectb2Offset;
|
||||
u16 stubProtectb3Offset;
|
||||
u16 stubProtectb4Offset;
|
||||
u16 amInitOffset;
|
||||
u16 storeChecksumFix;
|
||||
};
|
||||
|
||||
static const ds_protect_v2_patch_t sDSProtectV2Patches[] =
|
||||
{
|
||||
{ 0x7CDBA8BD, 0x670, 0x6AC, 0x6E8, 0x724, 0x760, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.00
|
||||
{ 0xA6FD7CE1, 0x1120, 0x115C, 0x1198, 0x11D4, 0x1210, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.01
|
||||
{ 0x934DD0, 0x129C, 0x1324, 0x13AC, 0x1434, 0x14BC, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.03
|
||||
{ 0x32E120D5, 0x132C, 0x13D8, 0x1484, 0x1530, 0x15DC, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 8) }, // v2.05
|
||||
};
|
||||
|
||||
const void* DSProtectOverlayPatch::InsertPatchV2(PatchContext& patchContext) const
|
||||
{
|
||||
dsprotectpatchv2_nextAddress = next ? (u32)next->InsertPatch(patchContext) : 0u;
|
||||
dsprotectpatchv2_overlay_id = _overlayId;
|
||||
dsprotectpatchv2_base_offset = _overlayOffset;
|
||||
|
||||
const auto& patch = sDSProtectV2Patches[(u32)_version - (u32)DSProtectVersion::v2_00];
|
||||
dsprotectpatchv2_checksum_fix = patch.checksumFix;
|
||||
dsprotectpatchv2_stub_protectb1_offset = patch.stubProtectb1Offset;
|
||||
dsprotectpatchv2_stub_protectb2_offset = patch.stubProtectb2Offset;
|
||||
dsprotectpatchv2_stub_protectb3_offset = patch.stubProtectb3Offset;
|
||||
dsprotectpatchv2_stub_protectb4_offset = patch.stubProtectb4Offset;
|
||||
dsprotectpatchv2_am_init_offset = patch.amInitOffset;
|
||||
dsprotectpatchv2_store_checksum_fix = patch.storeChecksumFix;
|
||||
|
||||
u32 patchSize = SECTION_SIZE(dsprotectpatchv2);
|
||||
void* patchAddress = patchContext.GetPatchHeap().Alloc(patchSize);
|
||||
|
||||
u32 entryAddress = (u32)&dsprotectpatchv2_entry - (u32)SECTION_START(dsprotectpatchv2) + (u32)patchAddress;
|
||||
memcpy(patchAddress, SECTION_START(dsprotectpatchv2), patchSize);
|
||||
|
||||
return (const void*)entryAddress;
|
||||
}
|
||||
|
||||
struct ds_protect_v2s_patch_t
|
||||
{
|
||||
u32 checksumFix;
|
||||
u16 instantDetectOffset;
|
||||
u16 storeChecksumFix;
|
||||
};
|
||||
|
||||
static const ds_protect_v2s_patch_t sDSProtectV2sPatches[] =
|
||||
{
|
||||
{ 0xEDEA7F01, 0x1BC, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.00s
|
||||
{ 0xEDEA7F1F, 0x1BC, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.01s
|
||||
{ 0x167325AA, 0x20C, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.03s
|
||||
{ 0x32E120D5, 0x230, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 4) } // v2.05s
|
||||
};
|
||||
|
||||
const void* DSProtectOverlayPatch::InsertPatchV2s(PatchContext& patchContext) const
|
||||
{
|
||||
dsprotectpatchv2s_nextAddress = next ? (u32)next->InsertPatch(patchContext) : 0u;
|
||||
dsprotectpatchv2s_overlay_id = _overlayId;
|
||||
|
||||
const auto& patch = sDSProtectV2sPatches[(u32)_version - (u32)DSProtectVersion::v2_00s];
|
||||
dsprotectpatchv2s_checksum_fix = patch.checksumFix;
|
||||
dsprotectpatchv2s_stub_instantdetect_offset = patch.instantDetectOffset + _overlayOffset;
|
||||
dsprotectpatchv2s_store_checksum_fix = patch.storeChecksumFix;
|
||||
|
||||
u32 patchSize = SECTION_SIZE(dsprotectpatchv2s);
|
||||
void* patchAddress = patchContext.GetPatchHeap().Alloc(patchSize);
|
||||
u32 entryAddress = (u32)&dsprotectpatchv2s_entry - (u32)SECTION_START(dsprotectpatchv2s) + (u32)patchAddress;
|
||||
memcpy(patchAddress, SECTION_START(dsprotectpatchv2s), patchSize);
|
||||
|
||||
return (const void*)entryAddress;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "../OverlayPatch.h"
|
||||
#include "DSProtectVersion.h"
|
||||
|
||||
/// @brief Arm9 overlay patch for DS Protect.
|
||||
class DSProtectOverlayPatch : public OverlayPatch
|
||||
{
|
||||
public:
|
||||
DSProtectOverlayPatch(u32 overlayId, u32 overlayOffset, DSProtectVersion version, u32 functionMask)
|
||||
: _overlayId(overlayId), _overlayOffset(overlayOffset), _version(version), _functionMask(functionMask) { }
|
||||
|
||||
const void* InsertPatch(PatchContext& patchContext) override;
|
||||
|
||||
private:
|
||||
u32 _overlayId;
|
||||
u32 _overlayOffset;
|
||||
DSProtectVersion _version;
|
||||
u32 _functionMask;
|
||||
|
||||
void SetOldV1Offsets(const u16* offsets) const;
|
||||
const void* InsertPatchV1(PatchContext& patchContext) const;
|
||||
const void* InsertPatchV2(PatchContext& patchContext) const;
|
||||
const void* InsertPatchV2s(PatchContext& patchContext) const;
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(dsprotectpatchv1_part1);
|
||||
extern "C" void dsprotectpatchv1_entry1();
|
||||
extern u16 dsprotectpatchv1_stub_protectb1_offset;
|
||||
extern u16 dsprotectpatchv1_stub_protectb2_offset;
|
||||
extern u16 dsprotectpatchv1_stub_protectb3_offset;
|
||||
extern u16 dsprotectpatchv1_nitro_static_init_offset;
|
||||
extern u16 dsprotectpatchv1_overlay_id;
|
||||
extern u32 dsprotectpatchv1_base_offset;
|
||||
extern u32 dsprotectpatchv1_part2_address;
|
||||
extern u32 dsprotectpatchv1_protectb1_return_value;
|
||||
extern u32 dsprotectpatchv1_protectb2_return_value;
|
||||
extern u32 dsprotectpatchv1_protectb3_return_value;
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(dsprotectpatchv1_part2);
|
||||
extern "C" void dsprotectpatchv1_entry2();
|
||||
extern u16 dsprotectpatchv1_stub_notprotectb1_offset;
|
||||
extern u16 dsprotectpatchv1_stub_notprotectb2_offset;
|
||||
extern u16 dsprotectpatchv1_stub_notprotectb3_offset;
|
||||
extern u32 dsprotectpatchv1_nextAddress;
|
||||
extern u32 dsprotectpatchv1_notprotectb1_return_value;
|
||||
extern u32 dsprotectpatchv1_notprotectb2_return_value;
|
||||
extern u32 dsprotectpatchv1_notprotectb3_return_value;
|
||||
extern u32 dsprotectpatchv1_loadReturnValue;
|
||||
extern u32 dsprotectpatchv1_moveCallback;
|
||||
@@ -0,0 +1,168 @@
|
||||
.cpu arm946e-s
|
||||
.syntax unified
|
||||
.section "dsprotectpatchv1_part1", "ax"
|
||||
|
||||
.thumb
|
||||
|
||||
.global dsprotectpatchv1_entry1
|
||||
.type dsprotectpatchv1_entry1, %function
|
||||
dsprotectpatchv1_entry1:
|
||||
push {r4-r7,lr}
|
||||
adr r0, dsprotectpatchv1_stub_protectb1_offset
|
||||
ldrh r4, [r0, #(dsprotectpatchv1_overlay_id - dsprotectpatchv1_stub_protectb1_offset)]
|
||||
ldmia r5!, {r1, r2} // id, ram address
|
||||
cmp r1, r4
|
||||
bne continue_to_next
|
||||
|
||||
ldr r4, dsprotectpatchv1_base_offset
|
||||
adds r2, r4 // add base offset to overlay address
|
||||
|
||||
ldrh r4, [r0, #(dsprotectpatchv1_nitro_static_init_offset - dsprotectpatchv1_stub_protectb1_offset)]
|
||||
ldr r3,= 0xE12FFF1E // bx lr
|
||||
str r3, [r4, r2]
|
||||
|
||||
adr r4, protectbX_patch
|
||||
ldmia r4!, {r5,r6}
|
||||
|
||||
adds r3, r0, #(dsprotectpatchv1_nitro_static_init_offset - dsprotectpatchv1_stub_protectb1_offset)
|
||||
1:
|
||||
ldmia r4!, {r7}
|
||||
ldrh r1, [r0]
|
||||
adds r1, r2
|
||||
stmia r1!, {r5,r6,r7}
|
||||
adds r0, #2
|
||||
cmp r0, r3
|
||||
bne 1b
|
||||
|
||||
ldr r7, dsprotectpatchv1_part2_address
|
||||
bx r7
|
||||
|
||||
continue_to_next:
|
||||
ldr r7, dsprotectpatchv1_part2_address
|
||||
adds r7, #(dsprotectpatchv1_continue_to_next - dsprotectpatchv1_entry2)
|
||||
bx r7
|
||||
|
||||
.global dsprotectpatchv1_stub_protectb1_offset
|
||||
dsprotectpatchv1_stub_protectb1_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv1_stub_protectb2_offset
|
||||
dsprotectpatchv1_stub_protectb2_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv1_stub_protectb3_offset
|
||||
dsprotectpatchv1_stub_protectb3_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv1_nitro_static_init_offset
|
||||
dsprotectpatchv1_nitro_static_init_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv1_overlay_id
|
||||
dsprotectpatchv1_overlay_id:
|
||||
.short 0
|
||||
|
||||
.balign 4
|
||||
|
||||
.global dsprotectpatchv1_base_offset
|
||||
dsprotectpatchv1_base_offset:
|
||||
.word 0
|
||||
|
||||
.global dsprotectpatchv1_part2_address
|
||||
dsprotectpatchv1_part2_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.arm
|
||||
protectbX_patch:
|
||||
ldr r0, dsprotectpatchv1_protectb1_return_value
|
||||
bx lr
|
||||
.global dsprotectpatchv1_protectb1_return_value
|
||||
dsprotectpatchv1_protectb1_return_value:
|
||||
.word 1830601
|
||||
.global dsprotectpatchv1_protectb2_return_value
|
||||
dsprotectpatchv1_protectb2_return_value:
|
||||
.word 1830203
|
||||
.global dsprotectpatchv1_protectb3_return_value
|
||||
dsprotectpatchv1_protectb3_return_value:
|
||||
.word 1828014
|
||||
|
||||
.section "dsprotectpatchv1_part2", "ax"
|
||||
|
||||
.thumb
|
||||
|
||||
.global dsprotectpatchv1_entry2
|
||||
.type dsprotectpatchv1_entry2, %function
|
||||
dsprotectpatchv1_entry2:
|
||||
adr r0, dsprotectpatchv1_stub_notprotectb1_offset
|
||||
adds r1, r0, #(dsprotectpatchv1_stub_notprotectb3_offset - dsprotectpatchv1_stub_notprotectb1_offset + 2)
|
||||
mov lr, r1
|
||||
2:
|
||||
ldrh r1, [r0]
|
||||
adds r1, r2
|
||||
adr r3, notprotectbX_patch
|
||||
ldmia r3!, {r4,r5,r6,r7}
|
||||
stmia r1!, {r4,r5,r6,r7}
|
||||
ldmia r3!, {r4,r5,r6}
|
||||
stmia r1!, {r4,r5,r6}
|
||||
adds r0, #2
|
||||
cmp r0, lr
|
||||
bne 2b
|
||||
|
||||
ldmia r3!, {r6,r7}
|
||||
|
||||
adds r2, #0x18
|
||||
adr r0, dsprotectpatchv1_stub_notprotectb1_offset
|
||||
ldrh r1, [r0, #(dsprotectpatchv1_stub_notprotectb2_offset - dsprotectpatchv1_stub_notprotectb1_offset)]
|
||||
str r6, [r1, r2]
|
||||
ldrh r1, [r0, #(dsprotectpatchv1_stub_notprotectb3_offset - dsprotectpatchv1_stub_notprotectb1_offset)]
|
||||
str r7, [r1, r2]
|
||||
|
||||
dsprotectpatchv1_continue_to_next:
|
||||
ldr r0, dsprotectpatchv1_nextAddress
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.global dsprotectpatchv1_stub_notprotectb1_offset
|
||||
dsprotectpatchv1_stub_notprotectb1_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv1_stub_notprotectb2_offset
|
||||
dsprotectpatchv1_stub_notprotectb2_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv1_stub_notprotectb3_offset
|
||||
dsprotectpatchv1_stub_notprotectb3_offset:
|
||||
.short 0
|
||||
|
||||
.balign 4
|
||||
|
||||
.global dsprotectpatchv1_nextAddress
|
||||
dsprotectpatchv1_nextAddress:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.arm
|
||||
notprotectbX_patch:
|
||||
push {lr}
|
||||
.global dsprotectpatchv1_moveCallback
|
||||
dsprotectpatchv1_moveCallback:
|
||||
movs lr, r0
|
||||
movne r0, r1
|
||||
blxne lr
|
||||
.global dsprotectpatchv1_loadReturnValue
|
||||
dsprotectpatchv1_loadReturnValue:
|
||||
ldr r0, dsprotectpatchv1_notprotectb1_return_value
|
||||
pop {pc}
|
||||
.global dsprotectpatchv1_notprotectb1_return_value
|
||||
dsprotectpatchv1_notprotectb1_return_value:
|
||||
.word 1831551
|
||||
.global dsprotectpatchv1_notprotectb2_return_value
|
||||
dsprotectpatchv1_notprotectb2_return_value:
|
||||
.word 1830859
|
||||
.global dsprotectpatchv1_notprotectb3_return_value
|
||||
dsprotectpatchv1_notprotectb3_return_value:
|
||||
.word 1829648
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(dsprotectpatchv2);
|
||||
|
||||
extern "C" void dsprotectpatchv2_entry();
|
||||
extern u16 dsprotectpatchv2_stub_protectb1_offset;
|
||||
extern u16 dsprotectpatchv2_stub_protectb2_offset;
|
||||
extern u16 dsprotectpatchv2_stub_protectb3_offset;
|
||||
extern u16 dsprotectpatchv2_stub_protectb4_offset;
|
||||
extern u16 dsprotectpatchv2_am_init_offset;
|
||||
extern u16 dsprotectpatchv2_overlay_id;
|
||||
extern u32 dsprotectpatchv2_nextAddress;
|
||||
extern u32 dsprotectpatchv2_checksum_fix;
|
||||
extern u32 dsprotectpatchv2_base_offset;
|
||||
|
||||
extern u16 dsprotectpatchv2_store_checksum_fix;
|
||||
@@ -0,0 +1,93 @@
|
||||
.cpu arm946e-s
|
||||
.section "dsprotectpatchv2", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
.global dsprotectpatchv2_entry
|
||||
.type dsprotectpatchv2_entry, %function
|
||||
dsprotectpatchv2_entry:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldmia r5!, {r0,r1,r2}
|
||||
adds r3, r2, r1 // bss address
|
||||
|
||||
adr r2, dsprotectpatchv2_stub_protectb1_offset
|
||||
ldrh r7, [r2, #(dsprotectpatchv2_overlay_id - dsprotectpatchv2_stub_protectb1_offset)]
|
||||
cmp r0, r7
|
||||
bne continue_to_next
|
||||
|
||||
ldr r7, [r2, #(dsprotectpatchv2_base_offset - dsprotectpatchv2_stub_protectb1_offset)]
|
||||
adds r1, r7 // add base offset to overlay address
|
||||
|
||||
ldrh r0, [r2, #(dsprotectpatchv2_am_init_offset - dsprotectpatchv2_stub_protectb1_offset)]
|
||||
ldr r7,= 0xE12FFF1E // bx lr
|
||||
str r7, [r1, r0]
|
||||
|
||||
adr r0, stub_patch_code
|
||||
ldmia r0!, {r4,r5,r6,r7}
|
||||
|
||||
subs r1, #4 // write from 4 bytes before the actual function address
|
||||
adds r0, r2, #(dsprotectpatchv2_stub_protectb4_offset - dsprotectpatchv2_stub_protectb1_offset)
|
||||
mov lr, r0
|
||||
patch_loop:
|
||||
ldrh r0, [r2]
|
||||
adds r2, #2
|
||||
adds r0, r1
|
||||
stmia r0!, {r3,r4,r5,r6} // bss address + code
|
||||
.global dsprotectpatchv2_store_checksum_fix
|
||||
dsprotectpatchv2_store_checksum_fix:
|
||||
str r7, [r0, #0] // checksum fix
|
||||
cmp r2, lr
|
||||
ble patch_loop
|
||||
|
||||
continue_to_next:
|
||||
ldr r0, dsprotectpatchv2_nextAddress
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.global dsprotectpatchv2_stub_protectb1_offset
|
||||
dsprotectpatchv2_stub_protectb1_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv2_stub_protectb2_offset
|
||||
dsprotectpatchv2_stub_protectb2_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv2_stub_protectb3_offset
|
||||
dsprotectpatchv2_stub_protectb3_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv2_stub_protectb4_offset
|
||||
dsprotectpatchv2_stub_protectb4_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv2_am_init_offset
|
||||
dsprotectpatchv2_am_init_offset:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv2_overlay_id
|
||||
dsprotectpatchv2_overlay_id:
|
||||
.short 0
|
||||
|
||||
.global dsprotectpatchv2_base_offset
|
||||
dsprotectpatchv2_base_offset:
|
||||
.word 0
|
||||
|
||||
.global dsprotectpatchv2_nextAddress
|
||||
dsprotectpatchv2_nextAddress:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.arm
|
||||
stub_patch_code:
|
||||
ldr r12, (. - 4)
|
||||
ldr r2, [r12], #4
|
||||
ldr pc, [r12, r2, lsl #2]
|
||||
|
||||
.global dsprotectpatchv2_checksum_fix
|
||||
dsprotectpatchv2_checksum_fix:
|
||||
.word 0
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(dsprotectpatchv2s);
|
||||
|
||||
extern "C" void dsprotectpatchv2s_entry();
|
||||
extern u32 dsprotectpatchv2s_stub_instantdetect_offset;
|
||||
extern u32 dsprotectpatchv2s_overlay_id;
|
||||
extern u32 dsprotectpatchv2s_nextAddress;
|
||||
extern u32 dsprotectpatchv2s_checksum_fix;
|
||||
|
||||
extern u16 dsprotectpatchv2s_store_checksum_fix;
|
||||
@@ -0,0 +1,64 @@
|
||||
.cpu arm946e-s
|
||||
.section "dsprotectpatchv2s", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
.global dsprotectpatchv2s_entry
|
||||
.type dsprotectpatchv2s_entry, %function
|
||||
dsprotectpatchv2s_entry:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldmia r5!, {r0,r1,r2,r3,r4}
|
||||
|
||||
ldr r7, dsprotectpatchv2s_overlay_id
|
||||
cmp r0, r7
|
||||
bne continue_to_next
|
||||
|
||||
movs r3, #0
|
||||
movs r7, #0
|
||||
stmia r4!, {r3,r7}
|
||||
stmia r4!, {r3,r7}
|
||||
str r3, [r4]
|
||||
|
||||
adr r0, stub_patch_code
|
||||
ldmia r0!, {r3,r4,r5,r6,r7}
|
||||
|
||||
ldr r0, dsprotectpatchv2s_stub_instantdetect_offset
|
||||
adds r0, r1
|
||||
stmia r0!, {r3,r4,r5,r6} // code
|
||||
.global dsprotectpatchv2s_store_checksum_fix
|
||||
dsprotectpatchv2s_store_checksum_fix:
|
||||
str r7, [r0, #0] // checksum fix
|
||||
|
||||
continue_to_next:
|
||||
ldr r0, dsprotectpatchv2s_nextAddress
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.global dsprotectpatchv2s_stub_instantdetect_offset
|
||||
dsprotectpatchv2s_stub_instantdetect_offset:
|
||||
.word 0
|
||||
|
||||
.global dsprotectpatchv2s_overlay_id
|
||||
dsprotectpatchv2s_overlay_id:
|
||||
.word 0
|
||||
|
||||
.global dsprotectpatchv2s_nextAddress
|
||||
dsprotectpatchv2s_nextAddress:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.arm
|
||||
stub_patch_code:
|
||||
movs r3, r0
|
||||
pusheq {r0,r1,lr}
|
||||
pushne {r1,r2,r3}
|
||||
pop {r0,r1,pc}
|
||||
|
||||
.global dsprotectpatchv2s_checksum_fix
|
||||
dsprotectpatchv2s_checksum_fix:
|
||||
.word 0
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,160 @@
|
||||
#include "common.h"
|
||||
#include "../../PatchContext.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "FsStartOverlayHookPatch.h"
|
||||
|
||||
static const u32 sFSStartOverlayPatternSdk4[] = { 0xE59F10D0u, 0xE1A04000u, 0xE1D100B0u, 0xE3500002u }; // -0xC
|
||||
static const u32 sFSStartOverlayPatternSdk4Thumb[] = { 0x481F1C06u, 0x28028800u, 0x69E8D121u, 0x0E012700u }; // -8
|
||||
static const u32 sFSStartOverlayPattern[] = { 0xE3500001u, 0x0A00001Cu, 0xE595001Cu, 0xE3A03000u }; // -0x14
|
||||
static const u32 sFSStartOverlayPatternThumb[] = { 0x69E8D023u, 0x0E012600u, 0x42082002u, 0x491BD013u }; // -0x10
|
||||
static const u32 sFSStartOverlayPatternThumbHybrid[] = { 0x68691C07u, 0x42814828u, 0x4828D30Cu, 0xD2094281u }; // -8
|
||||
|
||||
extern "C" void fsstartoverlayhook_entry();
|
||||
|
||||
extern u8 __fsstartoverlayhook_start[];
|
||||
extern u8 __fsstartoverlayhook_end[];
|
||||
|
||||
extern u32 fsstartoverlayhook_dcFlushRangeOffset;
|
||||
extern u32 fsstartoverlayhook_hookFuncAddress;
|
||||
|
||||
bool FsStartOverlayHookPatch::FindPatchTarget(PatchContext& patchContext)
|
||||
{
|
||||
if (!_patchHead) // no patches
|
||||
return true;
|
||||
|
||||
if (patchContext.GetSdkVersion().GetMajor() <= 4)
|
||||
{
|
||||
_fsStartOverlay = patchContext.FindPattern32(sFSStartOverlayPatternSdk4, sizeof(sFSStartOverlayPatternSdk4));
|
||||
if (_fsStartOverlay)
|
||||
{
|
||||
_fsStartOverlay -= 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
_fsStartOverlay = patchContext.FindPattern32(sFSStartOverlayPatternSdk4Thumb, sizeof(sFSStartOverlayPatternSdk4Thumb));
|
||||
if (_fsStartOverlay)
|
||||
{
|
||||
_fsStartOverlay -= 2;
|
||||
_thumb = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_fsStartOverlay = patchContext.FindPattern32(sFSStartOverlayPattern, sizeof(sFSStartOverlayPattern));
|
||||
if (_fsStartOverlay)
|
||||
{
|
||||
_fsStartOverlay -= 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
_fsStartOverlay = patchContext.FindPattern32(sFSStartOverlayPatternThumb, sizeof(sFSStartOverlayPatternThumb));
|
||||
if (_fsStartOverlay)
|
||||
{
|
||||
_fsStartOverlay -= 4;
|
||||
_thumb = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_fsStartOverlay = patchContext.FindPattern32(sFSStartOverlayPatternThumbHybrid, sizeof(sFSStartOverlayPatternThumbHybrid));
|
||||
if (_fsStartOverlay)
|
||||
{
|
||||
_fsStartOverlay -= 2;
|
||||
_thumb = true;
|
||||
_hybrid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_fsStartOverlay)
|
||||
{
|
||||
LOG_DEBUG("FS_StartOverlay found at 0x%p\n", _fsStartOverlay);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING("FS_StartOverlay not found\n");
|
||||
}
|
||||
|
||||
return _fsStartOverlay != nullptr;
|
||||
}
|
||||
|
||||
void FsStartOverlayHookPatch::ApplyPatch(PatchContext& patchContext)
|
||||
{
|
||||
if (!_fsStartOverlay)
|
||||
return;
|
||||
|
||||
if (!_patchHead) // no patches
|
||||
return;
|
||||
|
||||
const void* firstPatch = _patchHead->InsertPatch(patchContext);
|
||||
|
||||
if (!firstPatch)
|
||||
return;
|
||||
|
||||
fsstartoverlayhook_hookFuncAddress = (u32)firstPatch;
|
||||
|
||||
u32 patchOffset;
|
||||
if (_thumb)
|
||||
{
|
||||
u32 blDcFlushRange1;
|
||||
u32 blDcFlushRange2;
|
||||
if (_hybrid)
|
||||
{
|
||||
patchOffset = 0x8E;
|
||||
blDcFlushRange1 = *(u16*)((u8*)_fsStartOverlay + 0x92);
|
||||
blDcFlushRange2 = *(u16*)((u8*)_fsStartOverlay + 0x94);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (patchContext.GetSdkVersion().GetMajor() <= 4)
|
||||
{
|
||||
patchOffset = 0x68;
|
||||
blDcFlushRange1 = *(u16*)((u8*)_fsStartOverlay + 0x6C);
|
||||
blDcFlushRange2 = *(u16*)((u8*)_fsStartOverlay + 0x6E);
|
||||
}
|
||||
else
|
||||
{
|
||||
patchOffset = 0x6C;
|
||||
blDcFlushRange1 = *(u16*)((u8*)_fsStartOverlay + 0x70);
|
||||
blDcFlushRange2 = *(u16*)((u8*)_fsStartOverlay + 0x72);
|
||||
}
|
||||
}
|
||||
fsstartoverlayhook_dcFlushRangeOffset = ((int)((((blDcFlushRange1 & 0x7FF) << 11) | (blDcFlushRange2 & 0x7FF)) << 10) >> 9) - (_hybrid ? 5 : 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
patchOffset = 0xAC;
|
||||
u32 blDcFlushRange = *(u32*)((u8*)_fsStartOverlay + 0xB0);
|
||||
fsstartoverlayhook_dcFlushRangeOffset = (int)((blDcFlushRange & 0xFFFFFF) << 8) >> 6;
|
||||
}
|
||||
|
||||
u32 patch1Size = (u32)__fsstartoverlayhook_end - (u32)__fsstartoverlayhook_start;
|
||||
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
|
||||
|
||||
u32 entryAddress = (u32)&fsstartoverlayhook_entry - (u32)__fsstartoverlayhook_start + (u32)patch1Address;
|
||||
memcpy(patch1Address, __fsstartoverlayhook_start, patch1Size);
|
||||
|
||||
if (_thumb)
|
||||
{
|
||||
if (_hybrid)
|
||||
{
|
||||
*(u16*)((u8*)_fsStartOverlay + patchOffset + 0) = THUMB_LDR_PC_IMM(THUMB_R0, 4);
|
||||
*(u16*)((u8*)_fsStartOverlay + patchOffset + 2) = THUMB_NOP;
|
||||
*(u16*)((u8*)_fsStartOverlay + patchOffset + 4) = THUMB_BLX(THUMB_R0);
|
||||
*(u32*)((u8*)_fsStartOverlay + patchOffset + 6) = entryAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
*(u16*)((u8*)_fsStartOverlay + patchOffset + 0) = THUMB_LDR_PC_IMM(THUMB_R0, 0);
|
||||
*(u16*)((u8*)_fsStartOverlay + patchOffset + 2) = THUMB_BLX(THUMB_R0);
|
||||
*(u32*)((u8*)_fsStartOverlay + patchOffset + 4) = entryAddress;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*(u32*)((u8*)_fsStartOverlay + patchOffset + 0) = 0xE59F0000; // ldr r0,= entryAddress
|
||||
*(u32*)((u8*)_fsStartOverlay + patchOffset + 4) = 0xE12FFF30; // blx r0
|
||||
*(u32*)((u8*)_fsStartOverlay + patchOffset + 8) = entryAddress;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "OverlayHookPatch.h"
|
||||
|
||||
/// @brief Arm9 patch to apply patches to overlays when they are loaded.
|
||||
class FsStartOverlayHookPatch : public OverlayHookPatch
|
||||
{
|
||||
public:
|
||||
bool FindPatchTarget(PatchContext& patchContext) override;
|
||||
void ApplyPatch(PatchContext& patchContext) override;
|
||||
|
||||
private:
|
||||
u32* _fsStartOverlay = nullptr;
|
||||
u16 _thumb = false;
|
||||
u16 _hybrid = false;
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
.cpu arm946e-s
|
||||
.section ".fsstartoverlayhook", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
.global __fsstartoverlayhook_start
|
||||
__fsstartoverlayhook_start:
|
||||
|
||||
.global fsstartoverlayhook_entry
|
||||
.type fsstartoverlayhook_entry, %function
|
||||
fsstartoverlayhook_entry:
|
||||
movs r0, #0x4
|
||||
add lr, r0
|
||||
ldr r2, fsstartoverlayhook_dcFlushRangeOffset
|
||||
add r2, lr
|
||||
push {r2,lr}
|
||||
ldr r0, fsstartoverlayhook_hookFuncAddress
|
||||
1:
|
||||
blx r0
|
||||
cmp r0, #0
|
||||
bne 1b
|
||||
|
||||
ldr r0, [r5, #4]
|
||||
ldr r1, [r5, #8]
|
||||
pop {r2}
|
||||
blx r2
|
||||
ldr r4, [r5, #0x10]
|
||||
pop {pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.global fsstartoverlayhook_dcFlushRangeOffset
|
||||
fsstartoverlayhook_dcFlushRangeOffset:
|
||||
.word 0
|
||||
|
||||
.global fsstartoverlayhook_hookFuncAddress
|
||||
fsstartoverlayhook_hookFuncAddress:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.global __fsstartoverlayhook_end
|
||||
__fsstartoverlayhook_end:
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "common.h"
|
||||
#include "../../../PatchContext.h"
|
||||
#include "GoldenSunDarkDawnOverlayHookPatchAsm.h"
|
||||
#include "GoldenSunDarkDawnOverlayHookPatch.h"
|
||||
|
||||
static const u32 sStartOverlayPattern[] = { 0xE92D4038u, 0xE1A05000u, 0xEBFFFF92u, 0xE595101Cu };
|
||||
|
||||
bool GoldenSunDarkDawnOverlayHookPatch::FindPatchTarget(PatchContext& patchContext)
|
||||
{
|
||||
_overlayStartFunc = patchContext.FindPattern32(sStartOverlayPattern, sizeof(sStartOverlayPattern));
|
||||
return _overlayStartFunc != nullptr;
|
||||
}
|
||||
|
||||
void GoldenSunDarkDawnOverlayHookPatch::ApplyPatch(PatchContext& patchContext)
|
||||
{
|
||||
if (!_overlayStartFunc)
|
||||
return;
|
||||
|
||||
if (!_patchHead) // no patches
|
||||
return;
|
||||
|
||||
gsddoverlayhookpatch_hookFuncAddress = (u32)_patchHead->InsertPatch(patchContext);
|
||||
gsddoverlayhookpatch_returnAddress = (u32)_overlayStartFunc + 0x30;
|
||||
|
||||
u32 patch1Size = SECTION_SIZE(gsddoverlayhookpatch);
|
||||
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
|
||||
|
||||
u32 entryAddress = (u32)&gsddoverlayhookpatch_entry - (u32)SECTION_START(gsddoverlayhookpatch) + (u32)patch1Address;
|
||||
|
||||
*(u32*)((u8*)_overlayStartFunc + 0x28) = 0xE51FF004; // ldr pc,= entryAddress
|
||||
*(u32*)((u8*)_overlayStartFunc + 0x2C) = entryAddress;
|
||||
|
||||
memcpy(patch1Address, SECTION_START(gsddoverlayhookpatch), patch1Size);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "../OverlayHookPatch.h"
|
||||
|
||||
/// @brief Arm9 patch to apply patches to overlays when they are loaded in Golden Sun Dark Dawn.
|
||||
class GoldenSunDarkDawnOverlayHookPatch : public OverlayHookPatch
|
||||
{
|
||||
public:
|
||||
bool FindPatchTarget(PatchContext& patchContext) override;
|
||||
void ApplyPatch(PatchContext& patchContext) override;
|
||||
|
||||
private:
|
||||
u32* _overlayStartFunc;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(gsddoverlayhookpatch);
|
||||
|
||||
extern "C" void gsddoverlayhookpatch_entry();
|
||||
|
||||
extern u32 gsddoverlayhookpatch_hookFuncAddress;
|
||||
extern u32 gsddoverlayhookpatch_returnAddress;
|
||||
@@ -0,0 +1,34 @@
|
||||
.cpu arm946e-s
|
||||
.section "gsddoverlayhookpatch", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
.global gsddoverlayhookpatch_entry
|
||||
.type gsddoverlayhookpatch_entry, %function
|
||||
gsddoverlayhookpatch_entry:
|
||||
ldr r0, gsddoverlayhookpatch_hookFuncAddress
|
||||
1:
|
||||
blx r0
|
||||
cmp r0, #0
|
||||
bne 1b
|
||||
|
||||
ldr r3, gsddoverlayhookpatch_returnAddress
|
||||
ldr r1, [r5, #8]
|
||||
movs r0, #1
|
||||
lsls r0, r0, #14 // r0 = 0x4000
|
||||
cmp r1, r0
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
|
||||
.global gsddoverlayhookpatch_hookFuncAddress
|
||||
gsddoverlayhookpatch_hookFuncAddress:
|
||||
.word 0
|
||||
|
||||
.global gsddoverlayhookpatch_returnAddress
|
||||
gsddoverlayhookpatch_returnAddress:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
24
arm9/source/patches/arm9/OverlayPatches/OverlayHookPatch.h
Normal file
24
arm9/source/patches/arm9/OverlayPatches/OverlayHookPatch.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "../../Patch.h"
|
||||
#include "OverlayPatch.h"
|
||||
|
||||
/// @brief Abstract base class for a patch that applies patches to overlays when they are loaded.
|
||||
class OverlayHookPatch : public Patch
|
||||
{
|
||||
public:
|
||||
/// @brief Adds an overlay patch to the list.
|
||||
/// @param patch The overlay pach to add.
|
||||
void AddOverlayPatch(OverlayPatch* patch)
|
||||
{
|
||||
LOG_DEBUG("OverlayHookPatch::AddOverlayPatch\n");
|
||||
if (!_patchHead)
|
||||
_patchHead = patch;
|
||||
if (_patchTail)
|
||||
_patchTail->next = patch;
|
||||
_patchTail = patch;
|
||||
}
|
||||
|
||||
protected:
|
||||
OverlayPatch* _patchHead = nullptr;
|
||||
OverlayPatch* _patchTail = nullptr;
|
||||
};
|
||||
15
arm9/source/patches/arm9/OverlayPatches/OverlayPatch.h
Normal file
15
arm9/source/patches/arm9/OverlayPatches/OverlayPatch.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
class PatchContext;
|
||||
|
||||
/// @brief Patch to be applied when an overlay is loaded.
|
||||
class OverlayPatch
|
||||
{
|
||||
public:
|
||||
/// @brief Pointer to the next overlay patch, or \c nullptr when none.
|
||||
OverlayPatch* next = nullptr;
|
||||
|
||||
/// @brief Inserts the patch using the given \p patchContext.
|
||||
/// @param patchContext The patch context to use.
|
||||
/// @return A pointer to the patch function.
|
||||
virtual const void* InsertPatch(PatchContext& patchContext) = 0;
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "common.h"
|
||||
#include "../../../PatchContext.h"
|
||||
#include "PokemonBw1IrApPatchAsm.h"
|
||||
#include "PokemonBw1IrApPatch.h"
|
||||
|
||||
const void* PokemonBw1IrApPatch::InsertPatch(PatchContext& patchContext)
|
||||
{
|
||||
return patchContext.GetPatchCodeCollection().AddUniquePatchCode<PokemonBw1IrApPatchCode>
|
||||
(
|
||||
patchContext.GetPatchHeap(),
|
||||
next ? next->InsertPatch(patchContext) : nullptr
|
||||
)->GetPatchFunction();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "../OverlayPatch.h"
|
||||
|
||||
/// @brief Arm9 overlay patch to disable the Pokemon Black & White IR-sensor anti-piracy.
|
||||
class PokemonBw1IrApPatch : public OverlayPatch
|
||||
{
|
||||
public:
|
||||
const void* InsertPatch(PatchContext& patchContext) override;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../../../PatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(pokemonbw1irappatch);
|
||||
|
||||
extern "C" void pokemonbw1irappatch_entry();
|
||||
|
||||
extern u32 pokemonbw1irappatch_nextAddress;
|
||||
|
||||
class PokemonBw1IrApPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
PokemonBw1IrApPatchCode(PatchHeap& patchHeap, const void* nextPatch)
|
||||
: PatchCode(SECTION_START(pokemonbw1irappatch), SECTION_SIZE(pokemonbw1irappatch), patchHeap)
|
||||
{
|
||||
pokemonbw1irappatch_nextAddress = (u32)nextPatch;
|
||||
}
|
||||
|
||||
const void* GetPatchFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)pokemonbw1irappatch_entry);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
.cpu arm946e-s
|
||||
.syntax unified
|
||||
.section "pokemonbw1irappatch", "ax"
|
||||
.thumb
|
||||
|
||||
.global pokemonbw1irappatch_entry
|
||||
.type pokemonbw1irappatch_entry, %function
|
||||
pokemonbw1irappatch_entry:
|
||||
push {r5,lr}
|
||||
|
||||
ldmia r5!, {r1, r2} // id, ram address
|
||||
ldr r0,= 231 // target overlay id
|
||||
cmp r0, r1
|
||||
bne continue_to_next
|
||||
ldr r1,= 0x1CA // patch offset in overlay
|
||||
movs r0, #1 // force return value of function to 1 instead of 0
|
||||
adds r2, r1
|
||||
strb r0, [r2]
|
||||
|
||||
continue_to_next:
|
||||
ldr r0, pokemonbw1irappatch_nextAddress
|
||||
pop {r5,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.global pokemonbw1irappatch_nextAddress
|
||||
pokemonbw1irappatch_nextAddress:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "common.h"
|
||||
#include "../../../PatchContext.h"
|
||||
#include "PokemonBw2IrApPatchAsm.h"
|
||||
#include "PokemonBw2IrApPatch.h"
|
||||
|
||||
const void* PokemonBw2IrApPatch::InsertPatch(PatchContext& patchContext)
|
||||
{
|
||||
return patchContext.GetPatchCodeCollection().AddUniquePatchCode<PokemonBw2IrApPatchCode>
|
||||
(
|
||||
patchContext.GetPatchHeap(),
|
||||
next ? next->InsertPatch(patchContext) : nullptr
|
||||
)->GetPatchFunction();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user