You've already forked pico-loader
mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-01-09 16:28:35 -08:00
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);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user