/*
main.arm7.c
By Michael Chisholm (Chishm)
All resetMemory and startBinary functions are based
on the MultiNDS loader by Darkain.
Original source available at:
http://cvs.sourceforge.net/viewcvs.py/ndslib/ndslib/examples/loader/boot/main.cpp
License:
NitroHax -- Cheat tool for the Nintendo DS
Copyright (C) 2008 Michael "Chishm" Chisholm
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#ifndef ARM7
# define ARM7
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef NULL
#define NULL 0
#endif
#include "common.h"
#include "tonccpy.h"
#include "read_card.h"
#include "module_params.h"
#include "find.h"
#include "cheat.h"
void arm7_clearmem (void* loc, size_t len);
extern void ensureBinaryDecompressed(const tNDSHeader* ndsHeader, module_params_t* moduleParams);
// Module params
static const u32 moduleParamsSignature[2] = {0xDEC00621, 0x2106C0DE};
static u32 chipID;
static module_params_t* moduleParams;
u32* findModuleParamsOffset(const tNDSHeader* ndsHeader) {
u32* moduleParamsOffset = findOffset((u32*)ndsHeader->arm9destination, ndsHeader->arm9binarySize, moduleParamsSignature, 2);
return moduleParamsOffset;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Important things
#define NDS_HEADER 0x027FFE00
#define NDS_HEADER_SDK5 0x02FFFE00 // __NDSHeader
#define NDS_HEADER_POKEMON 0x027FF000
#define DSI_HEADER 0x027FE000
#define DSI_HEADER_SDK5 0x02FFE000 // __DSiHeader
// #define CHEAT_ENGINE_LOCATION 0x027FE000
#define CHEAT_ENGINE_LOCATION 0x023FE800
#define CHEAT_DATA_LOCATION 0x06030000
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Used for debugging purposes
static void debugOutput (u32 code) {
// Wait until the ARM9 is ready
while (arm9_stateFlag != ARM9_READY);
// Set the error code, then tell ARM9 to display it
arm9_errorCode = code;
arm9_stateFlag = ARM9_DISPERR;
// Wait for completion
while (arm9_stateFlag != ARM9_READY);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Firmware stuff
static void my_readUserSettings(tNDSHeader* ndsHeader) {
PERSONAL_DATA slot1;
PERSONAL_DATA slot2;
short slot1count, slot2count; //u8
short slot1CRC, slot2CRC;
u32 userSettingsBase;
// Get settings location
readFirmware(0x20, &userSettingsBase, 2);
u32 slot1Address = userSettingsBase * 8;
u32 slot2Address = userSettingsBase * 8 + 0x100;
// Reload DS Firmware settings
readFirmware(slot1Address, &slot1, sizeof(PERSONAL_DATA)); //readFirmware(slot1Address, personalData, 0x70);
readFirmware(slot2Address, &slot2, sizeof(PERSONAL_DATA)); //readFirmware(slot2Address, personalData, 0x70);
readFirmware(slot1Address + 0x70, &slot1count, 2); //readFirmware(slot1Address + 0x70, &slot1count, 1);
readFirmware(slot2Address + 0x70, &slot2count, 2); //readFirmware(slot1Address + 0x70, &slot2count, 1);
readFirmware(slot1Address + 0x72, &slot1CRC, 2);
readFirmware(slot2Address + 0x72, &slot2CRC, 2);
// Default to slot 1 user settings
void *currentSettings = &slot1;
short calc1CRC = swiCRC16(0xFFFF, &slot1, sizeof(PERSONAL_DATA));
short calc2CRC = swiCRC16(0xFFFF, &slot2, sizeof(PERSONAL_DATA));
// Bail out if neither slot is valid
if (calc1CRC != slot1CRC && calc2CRC != slot2CRC)return;
// If both slots are valid pick the most recent
if (calc1CRC == slot1CRC && calc2CRC == slot2CRC) {
currentSettings = (slot2count == ((slot1count + 1) & 0x7f) ? &slot2 : &slot1);
//if ((slot1count & 0x7F) == ((slot2count + 1) & 0x7F)) {
} else {
if (calc2CRC == slot2CRC)currentSettings = &slot2;
}
PERSONAL_DATA* personalData = (PERSONAL_DATA*)((u32)__NDSHeader - (u32)ndsHeader + (u32)PersonalData); //(u8*)((u32)ndsHeader - 0x180)
tonccpy(PersonalData, currentSettings, sizeof(PERSONAL_DATA));
if (personalData->language != 6 && ndsHeader->reserved1[8] == 0x80) {
ndsHeader->reserved1[8] = 0; // Patch iQue game to be region-free
ndsHeader->headerCRC16 = swiCRC16(0xFFFF, ndsHeader, 0x15E); // Fix CRC
}
}
void memset_addrs_arm7(u32 start, u32 end) { toncset((u32*)start, 0, ((int)end - (int)start)); }
/*-------------------------------------------------------------------------
arm7_resetMemory
Clears all of the NDS's RAM that is visible to the ARM7
Written by Darkain.
Modified by Chishm:
* Added STMIA clear mem loop
--------------------------------------------------------------------------*/
void arm7_resetMemory (void) {
int i, reg;
REG_IME = 0;
for (i=0; i<16; i++) {
SCHANNEL_CR(i) = 0;
SCHANNEL_TIMER(i) = 0;
SCHANNEL_SOURCE(i) = 0;
SCHANNEL_LENGTH(i) = 0;
}
REG_SOUNDCNT = 0;
// Clear out ARM7 DMA channels and timers
for (i=0; i<4; i++) {
DMA_CR(i) = 0;
DMA_SRC(i) = 0;
DMA_DEST(i) = 0;
TIMER_CR(i) = 0;
TIMER_DATA(i) = 0;
if ((REG_SCFG_EXT & BIT(31)))for(reg=0; reg<0x1c; reg+=4)*((u32*)(0x04004104 + ((i*0x1c)+reg))) = 0; //Reset NDMA.
}
// Clear out FIFO
REG_IPC_SYNC = 0;
REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR;
REG_IPC_FIFO_CR = 0;
// clear IWRAM - 037F:8000 to 0380:FFFF, total 96KiB
toncset ((void*)0x037F8000, 0, 96*1024);
memset_addrs_arm7(0x03000000, 0x0380FFC0);
memset_addrs_arm7(0x0380FFD0, 0x03800000 + 0x10000);
// clear most of EXRAM - except before 0x023F0000, which has the cheat data
toncset ((void*)0x02004000, 0, 0x3EC000);
// clear more of EXRAM, skipping the cheat data section
toncset ((void*)0x023F8000, 0, 0x6000);
// clear last part of EXRAM
toncset ((void*)0x02400000, 0, 0xC00000);
REG_IE = 0;
REG_IF = ~0;
REG_AUXIE = 0;
REG_AUXIF = ~0;
(*(vu32*)(0x04000000-4)) = 0; //IRQ_HANDLER ARM7 version
(*(vu32*)(0x04000000-8)) = ~0; //VBLANK_INTR_WAIT_FLAGS, ARM7 version
REG_POWERCNT = 1; //turn off power to stuffs
}
// SDK 5
static bool ROMsupportsDSiMode(const tNDSHeader* ndsHeader) { return (ndsHeader->unitCode > 0); }
// SDK 5
static bool ROMisDSiEnhanced(const tNDSHeader* ndsHeader) { return (ndsHeader->unitCode == 0x02); }
// SDK 5
static bool ROMisDSiExclusive(const tNDSHeader* ndsHeader) { return (ndsHeader->unitCode == 0x03); }
int arm7_loadBinary (const tDSiHeader* dsiHeaderTemp) {
u32 errorCode;
// Init card
errorCode = cardInit((sNDSHeaderExt*)dsiHeaderTemp, &chipID);
if (errorCode)return errorCode;
// Fix Pokemon games needing header data.
tonccpy((u32*)NDS_HEADER_POKEMON, (u32*)NDS_HEADER, 0x170);
char* romTid = (char*)NDS_HEADER_POKEMON+0xC;
if (
memcmp(romTid, "ADA", 3) == 0 // Diamond
|| memcmp(romTid, "APA", 3) == 0 // Pearl
|| memcmp(romTid, "CPU", 3) == 0 // Platinum
|| memcmp(romTid, "IPK", 3) == 0 // HG
|| memcmp(romTid, "IPG", 3) == 0 // SS
) {
// Make the Pokemon game code ADAJ.
const char gameCodePokemon[] = { 'A', 'D', 'A', 'J' };
tonccpy((char*)NDS_HEADER_POKEMON+0xC, gameCodePokemon, 4);
}
cardRead(dsiHeaderTemp->ndshdr.arm9romOffset, (u32*)dsiHeaderTemp->ndshdr.arm9destination, dsiHeaderTemp->ndshdr.arm9binarySize);
cardRead(dsiHeaderTemp->ndshdr.arm7romOffset, (u32*)dsiHeaderTemp->ndshdr.arm7destination, dsiHeaderTemp->ndshdr.arm7binarySize);
moduleParams = (module_params_t*)findModuleParamsOffset(&dsiHeaderTemp->ndshdr);
return ERR_NONE;
}
static tNDSHeader* loadHeader(tDSiHeader* dsiHeaderTemp) {
tNDSHeader* ndsHeader = (tNDSHeader*)(isSdk5(moduleParams) ? NDS_HEADER_SDK5 : NDS_HEADER);
*ndsHeader = dsiHeaderTemp->ndshdr;
return ndsHeader;
}
/*-------------------------------------------------------------------------
arm7_startBinary
Jumps to the ARM7 NDS binary in sync with the display and ARM9
Written by Darkain, modified by Chishm.
--------------------------------------------------------------------------*/
void arm7_startBinary (void) {
REG_IME = 0;
// Get the ARM9 to boot
arm9_stateFlag = ARM9_BOOTBIN;
while(REG_VCOUNT!=191);
while(REG_VCOUNT==191);
// Start ARM7
VoidFn arm7code = (VoidFn)ndsHeader->arm7executeAddress;
arm7code();
}
static void setMemoryAddress(const tNDSHeader* ndsHeader) {
if (ROMsupportsDSiMode(ndsHeader)) {
tonccpy((u32*)0x02FFFA80, (u32*)NDS_HEADER_SDK5, 0x160); // Make a duplicate of DS header
*(u32*)(0x02FFA680) = 0x02FD4D80;
*(u32*)(0x02FFA684) = 0x00000000;
*(u32*)(0x02FFA688) = 0x00001980;
*(u32*)(0x02FFF00C) = 0x0000007F;
*(u32*)(0x02FFF010) = 0x550E25B8;
*(u32*)(0x02FFF014) = 0x02FF4000;
}
// Set memory values expected by loaded NDS
// from NitroHax, thanks to Chism
*((u32*)(isSdk5(moduleParams) ? 0x02fff800 : 0x027ff800)) = chipID; // CurrentCardID
*((u32*)(isSdk5(moduleParams) ? 0x02fff804 : 0x027ff804)) = chipID; // Command10CardID
*((u16*)(isSdk5(moduleParams) ? 0x02fff808 : 0x027ff808)) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh]
*((u16*)(isSdk5(moduleParams) ? 0x02fff80a : 0x027ff80a)) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh]
// Copies of above
*((u32*)(isSdk5(moduleParams) ? 0x02fffc00 : 0x027ffc00)) = chipID; // CurrentCardID
*((u32*)(isSdk5(moduleParams) ? 0x02fffc04 : 0x027ffc04)) = chipID; // Command10CardID
*((u16*)(isSdk5(moduleParams) ? 0x02fffc08 : 0x027ffc08)) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh]
*((u16*)(isSdk5(moduleParams) ? 0x02fffc0a : 0x027ffc0a)) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh]
*((u16*)(isSdk5(moduleParams) ? 0x02fffc40 : 0x027ffc40)) = 0x1; // Boot Indicator (Booted from card for SDK5) -- EXTREMELY IMPORTANT!!! Thanks to cReDiAr
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Main function
void arm7_main (void) {
if (REG_SNDEXTCNT != 0) {
if ((REG_SCFG_EXT & BIT(31))) {
REG_MBK9=0xFCFFFF0F;
*((vu32*)REG_MBK1)=0x8D898581;
*((vu32*)REG_MBK2)=0x8C888480;
*((vu32*)REG_MBK3)=0x9C989490;
*((vu32*)REG_MBK4)=0x8C888480;
*((vu32*)REG_MBK5)=0x9C989490;
REG_MBK6=0x09403900;
REG_MBK7=0x09803940;
REG_MBK8=0x09C03980;
}
}
int errorCode;
// Wait for ARM9 to at least start
while (arm9_stateFlag < ARM9_START);
// debugOutput (ERR_STS_CLR_MEM);
// Get ARM7 to clear RAM
arm7_resetMemory();
// debugOutput (ERR_STS_LOAD_BIN);
if ((REG_SCFG_EXT & BIT(31)))REG_SCFG_ROM = 0x703;
tDSiHeader* dsiHeaderTemp = (tDSiHeader*)0x02FFC000;
// Load the NDS file
errorCode = arm7_loadBinary(dsiHeaderTemp);
if (errorCode)debugOutput(errorCode);
// Override some settings depending on if DSi Enhanced cart or DSi Exclusive cart is inserted
// if (ROMisDSiEnhanced(&dsiHeaderTemp->ndshdr)) { extendRam = true; } // Required for TWL carts to boot properly. Disabled by default for NTR carts to allow WoodR4 to operate correctly.
/*if (twlMode) {
if (dsiHeaderTemp->arm9ibinarySize > 0) {
cardRead((u32)dsiHeaderTemp->arm9iromOffset, (u32*)dsiHeaderTemp->arm9idestination, dsiHeaderTemp->arm9ibinarySize);
}
if (dsiHeaderTemp->arm7ibinarySize > 0) {
cardRead((u32)dsiHeaderTemp->arm7iromOffset, (u32*)dsiHeaderTemp->arm7idestination, dsiHeaderTemp->arm7ibinarySize);
}
}*/
ndsHeader = loadHeader(dsiHeaderTemp);
if (!ROMisDSiExclusive(&dsiHeaderTemp->ndshdr)) { tonccpy((u32*)0x023FF000, (u32*)(isSdk5(moduleParams) ? 0x02FFF000 : 0x027FF000), 0x1000); }
my_readUserSettings(ndsHeader); // Header has to be loaded first
toncset ((void*)0x023F0000, 0, 0x8000); // Clear cheat data from main memory
// debugOutput (ERR_STS_START);
if (ROMisDSiExclusive(&dsiHeaderTemp->ndshdr)) { REG_SCFG_CLK = 0x0184; } else { REG_SCFG_CLK = 0x0100; }
if (REG_SNDEXTCNT != 0) {
if ((REG_SCFG_EXT & BIT(31)))REG_SCFG_EXT = 0x12A00000;
//REG_SCFG_EXT &= ~(1UL << 31);
}
while (arm9_stateFlag != ARM9_READY);
arm9_stateFlag = ARM9_SETSCFG;
while (arm9_stateFlag != ARM9_READY);
setMemoryAddress(ndsHeader);
// Load the cheat engine and hook it into the ARM7 binary
errorCode = arm7_hookGame(ndsHeader, (const u32*)CHEAT_DATA_LOCATION, (u32*)CHEAT_ENGINE_LOCATION);
if (errorCode != ERR_NONE && errorCode != ERR_NOCHEAT)debugOutput(errorCode);
arm7_startBinary();
while (1);
}