GBA-Exploader/ndsbootloader/source/dldi_patcher.c
ApacheThunder 4ec14bae55 Fix Rumble Menu soft reset ...
* Soft Reset for Rumble Menu fixed. New scheme is now used for loading
SoftReset.xxxx. Now instead it will look for R4TF.nds (R4TF = 4
character ioType code for DLDI user is currently using with R4TF as an
example). If not found it will look for a few specific NDS files to try.
If those aren't found it will try boot.nds. If boot.nds isn't found then
rumble menu will not be available.
2024-05-19 18:24:16 -05:00

223 lines
7.9 KiB
C

/*-----------------------------------------------------------------
Copyright (C) 2005 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 2
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
------------------------------------------------------------------*/
#ifndef NO_DLDI
#include <string.h>
#include <nds.h>
#include "dldi_patcher.h"
#define FIX_ALL 0x01
#define FIX_GLUE 0x02
#define FIX_GOT 0x04
#define FIX_BSS 0x08
enum DldiOffsets {
DO_magicString = 0x00, // "\xED\xA5\x8D\xBF Chishm"
DO_magicToken = 0x00, // 0xBF8DA5ED
DO_magicShortString = 0x04, // " Chishm"
DO_version = 0x0C,
DO_driverSize = 0x0D,
DO_fixSections = 0x0E,
DO_allocatedSpace = 0x0F,
DO_friendlyName = 0x10,
DO_text_start = 0x40, // Data start
DO_data_end = 0x44, // Data end
DO_glue_start = 0x48, // Interworking glue start -- Needs address fixing
DO_glue_end = 0x4C, // Interworking glue end
DO_got_start = 0x50, // GOT start -- Needs address fixing
DO_got_end = 0x54, // GOT end
DO_bss_start = 0x58, // bss start -- Needs setting to zero
DO_bss_end = 0x5C, // bss end
// IO_INTERFACE data
DO_ioType = 0x60,
DO_features = 0x64,
DO_startup = 0x68,
DO_isInserted = 0x6C,
DO_readSectors = 0x70,
DO_writeSectors = 0x74,
DO_clearStatus = 0x78,
DO_shutdown = 0x7C,
DO_code = 0x80
};
static addr_t readAddr (data_t *mem, addr_t offset) {
return ((addr_t*)mem)[offset/sizeof(addr_t)];
}
static void writeAddr (data_t *mem, addr_t offset, addr_t value) {
((addr_t*)mem)[offset/sizeof(addr_t)] = value;
}
static addr_t quickFind (const data_t* data, const data_t* search, size_t dataLen, size_t searchLen) {
const int* dataChunk = (const int*) data;
int searchChunk = ((const int*)search)[0];
addr_t i;
addr_t dataChunkEnd = (addr_t)(dataLen / sizeof(int));
for ( i = 0; i < dataChunkEnd; i++) {
if (dataChunk[i] == searchChunk) {
if ((i*sizeof(int) + searchLen) > dataLen) {
return -1;
}
if (memcmp (&data[i*sizeof(int)], search, searchLen) == 0) {
return i*sizeof(int);
}
}
}
return -1;
}
// Strings are stored with bit 0x20 flipped (case inverted) to prevent accidental DLDI patching of them
#define DLDI_MAGIC_LEN 12
#define DLDI_MAGIC_MANGLE_VALUE 0x20
static const data_t dldiMagicStringMangled[DLDI_MAGIC_LEN] = "\xCD\x85\xAD\x9F\0cHISHM"; // Normal DLDI file
// Demangle the magic string by XORing every byte with 0x20, except the NULL terminator
static void demangleMagicString(data_t *dest, const data_t *src) {
int i;
memcpy(dest, src, DLDI_MAGIC_LEN);
for (i = 0; i < DLDI_MAGIC_LEN - 1; ++i) {
dest[i] ^= DLDI_MAGIC_MANGLE_VALUE;
}
}
#define DEVICE_TYPE_DLDI 0x49444C44
extern data_t _dldi_start[];
bool dldiPatchBinary (data_t *binData, u32 binSize) {
addr_t memOffset; // Offset of DLDI after the file is loaded into memory
addr_t patchOffset; // Position of patch destination in the file
addr_t relocationOffset; // Value added to all offsets within the patch to fix it properly
addr_t ddmemOffset; // Original offset used in the DLDI file
addr_t ddmemStart; // Start of range that offsets can be in the DLDI file
addr_t ddmemEnd; // End of range that offsets can be in the DLDI file
addr_t ddmemSize; // Size of range that offsets can be in the DLDI file
addr_t addrIter;
data_t *pDH;
data_t *pAH;
data_t dldiMagicString[DLDI_MAGIC_LEN];
size_t dldiFileSize = 0;
// Find the DLDI reserved space in the file
demangleMagicString(dldiMagicString, dldiMagicStringMangled);
patchOffset = quickFind (binData, dldiMagicString, binSize, sizeof(dldiMagicString));
if (patchOffset < 0) {
// does not have a DLDI section
return false;
}
pDH = _dldi_start;
pAH = &(binData[patchOffset]);
if (*((u32*)(pDH + DO_ioType)) == DEVICE_TYPE_DLDI) {
// No DLDI patch
return false;
}
if (pDH[DO_driverSize] > pAH[DO_allocatedSpace]) {
// Not enough space for patch
return false;
}
dldiFileSize = 1 << pDH[DO_driverSize];
memOffset = readAddr (pAH, DO_text_start);
if (memOffset == 0) {
memOffset = readAddr (pAH, DO_startup) - DO_code;
}
ddmemOffset = readAddr (pDH, DO_text_start);
relocationOffset = memOffset - ddmemOffset;
ddmemStart = readAddr (pDH, DO_text_start);
ddmemSize = (1 << pDH[DO_driverSize]);
ddmemEnd = ddmemStart + ddmemSize;
// Remember how much space is actually reserved
pDH[DO_allocatedSpace] = pAH[DO_allocatedSpace];
// Copy the DLDI patch into the application
memcpy (pAH, pDH, dldiFileSize);
// Fix the section pointers in the header
writeAddr (pAH, DO_text_start, readAddr (pAH, DO_text_start) + relocationOffset);
writeAddr (pAH, DO_data_end, readAddr (pAH, DO_data_end) + relocationOffset);
writeAddr (pAH, DO_glue_start, readAddr (pAH, DO_glue_start) + relocationOffset);
writeAddr (pAH, DO_glue_end, readAddr (pAH, DO_glue_end) + relocationOffset);
writeAddr (pAH, DO_got_start, readAddr (pAH, DO_got_start) + relocationOffset);
writeAddr (pAH, DO_got_end, readAddr (pAH, DO_got_end) + relocationOffset);
writeAddr (pAH, DO_bss_start, readAddr (pAH, DO_bss_start) + relocationOffset);
writeAddr (pAH, DO_bss_end, readAddr (pAH, DO_bss_end) + relocationOffset);
// Fix the function pointers in the header
writeAddr (pAH, DO_startup, readAddr (pAH, DO_startup) + relocationOffset);
writeAddr (pAH, DO_isInserted, readAddr (pAH, DO_isInserted) + relocationOffset);
writeAddr (pAH, DO_readSectors, readAddr (pAH, DO_readSectors) + relocationOffset);
writeAddr (pAH, DO_writeSectors, readAddr (pAH, DO_writeSectors) + relocationOffset);
writeAddr (pAH, DO_clearStatus, readAddr (pAH, DO_clearStatus) + relocationOffset);
writeAddr (pAH, DO_shutdown, readAddr (pAH, DO_shutdown) + relocationOffset);
// Put the correct DLDI magic string back into the DLDI header
memcpy (pAH, dldiMagicString, sizeof (dldiMagicString));
if (pDH[DO_fixSections] & FIX_ALL) {
// Search through and fix pointers within the data section of the file
for (addrIter = (readAddr(pDH, DO_text_start) - ddmemStart); addrIter < (readAddr(pDH, DO_data_end) - ddmemStart); addrIter++) {
if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) {
writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);
}
}
}
if (pDH[DO_fixSections] & FIX_GLUE) {
// Search through and fix pointers within the glue section of the file
for (addrIter = (readAddr(pDH, DO_glue_start) - ddmemStart); addrIter < (readAddr(pDH, DO_glue_end) - ddmemStart); addrIter++) {
if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) {
writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);
}
}
}
if (pDH[DO_fixSections] & FIX_GOT) {
// Search through and fix pointers within the Global Offset Table section of the file
for (addrIter = (readAddr(pDH, DO_got_start) - ddmemStart); addrIter < (readAddr(pDH, DO_got_end) - ddmemStart); addrIter++) {
if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) {
writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);
}
}
}
if (pDH[DO_fixSections] & FIX_BSS) {
// Initialise the BSS to 0
memset (&pAH[readAddr(pDH, DO_bss_start) - ddmemStart] , 0, readAddr(pDH, DO_bss_end) - readAddr(pDH, DO_bss_start));
}
return true;
}
#endif