akmenu-next/arm9/source/gbapatcher.cpp
2024-10-13 02:11:12 -07:00

1625 lines
71 KiB
C++

/*
gbapatcher.cpp
Copyright (C) 2008-2009 somebody
Copyright (C) 2009 yellow wood goblin
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 <http://www.gnu.org/licenses/>.
*/
#include "gbapatcher.h"
#include <stdarg.h>
#ifdef __TEST
#include <stdio.h>
#else
#include "globalsettings.h"
#endif
#define sizeofa(array) (sizeof(array) / sizeof(array[0]))
#define CHECK_SIZES(n, c, s) \
struct __check_##n##_sizeof { \
char c1[c - s]; \
char c2[s - c]; \
char e; \
} // tnx to yjh
#define CHECK_SIZEOF(n, s) CHECK_SIZES(n, sizeof(n), s)
CGbaWriter::~CGbaWriter() {}
CGbaPatcher::CGbaPatcher(u32 aSize, CGbaWriter* aWriter, u32* aData, u8 aRamPage)
: iCount(0),
iCount2(0),
iCount3(0),
iCount4(0),
iSize(aSize),
iData(aData),
iTrimSize(aSize),
iResultSize(aSize),
iSaveSize(128 * 1024),
iWriter(aWriter),
iRamPage(aRamPage) {
/*
#ifdef __TEST
iData=(u32*)globalBuffer;
#else
iData=(u32*)0x08060000;
#endif
*/
}
#ifdef __TEST
void CGbaPatcher::DumpPatchInfo(void) {
for (u32 ii = 0; ii < iCount; ii++) {
switch (iPatchInfo[ii].iType) {
case ESRAMLabel:
printf("ESRAMLabel");
break;
case EEEPROMLabel:
printf("EEEPROMLabel");
break;
case EEEPROMRead:
printf("EEEPROMRead");
break;
case EEEPROMWrite:
printf("EEEPROMWrite");
break;
case EEEPROMWrite124:
printf("EEEPROMWrite124");
break;
case EEEPROMWrite126:
printf("EEEPROMWrite126");
break;
case EFLASH1M10XLabel:
printf("EFLASH1M10XLabel");
break;
case EFLASH1M102_1:
printf("EFLASH1M102_1");
break;
case EFLASH1M102_2:
printf("EFLASH1M102_2");
break;
case EFLASH1M102_3:
printf("EFLASH1M102_3");
break;
case EFLASH1M102_4:
printf("EFLASH1M102_4");
break;
case EFLASH1M102_5:
printf("EFLASH1M102_5");
break;
case EFLASH1M103_1:
printf("EFLASH1M103_1");
break;
case EFLASH1M103_2:
printf("EFLASH1M103_2");
break;
case EFLASH1M103_3:
printf("EFLASH1M103_3");
break;
case EFLASH1M103_4:
printf("EFLASH1M103_4");
break;
case EFLASHLabel:
printf("EFLASHLabel");
break;
case EFLASH_1:
printf("EFLASH_1");
break;
case EFLASH_2:
printf("EFLASH_2");
break;
case EFLASH_3:
printf("EFLASH_3");
break;
case EFLASH_4:
printf("EFLASH_4");
break;
case EFLASHV123_1:
printf("EFLASHV123_1");
break;
case EFLASHV123_2:
printf("EFLASHV123_2");
break;
case EFLASHV123_3:
printf("EFLASHV123_3");
break;
case EFLASHV123_4:
printf("EFLASHV123_4");
break;
case EFLASHV123_5:
printf("EFLASHV123_5");
break;
case EFLASH512Label:
printf("EFLASH512Label");
break;
case EFLASH512_1:
printf("EFLASH512_1");
break;
case EFLASH512_2:
printf("EFLASH512_2");
break;
default:
printf("Unknown_%d", iPatchInfo[ii].iType);
break;
}
printf(": 0x%x\n", iPatchInfo[ii].iOffset * 4);
}
}
#endif
inline int test_printf(const char* format, ...) {
#ifdef __TEST
va_list args;
va_start(args, format);
int ret = vprintf(format, args);
va_end(args);
return ret;
#else
return 0;
#endif //__TEST
}
void CGbaPatcher::Add(u32 anOffset, TPatchType aType) {
if (iCount < EMax) {
SPatchInfo info = {anOffset, aType};
iPatchInfo[iCount++] = info;
}
}
void CGbaPatcher::Add2(u32 anOffset, u32 aValue) {
if (iCount2 < (EMax * 2)) {
SPatchInfo2 info = {anOffset, aValue};
iPatchInfo2[iCount2++] = info;
}
}
void CGbaPatcher::Add3(u32 aLocation, u32 anAddress, u32 aRa, u32 aRb) {
if (iCount3 < EMax) {
SPatchInfo3 info = {aLocation, anAddress, aRa, aRb};
iPatchInfo3[iCount3++] = info;
}
}
void CGbaPatcher::Add4(u32 anOffset, u16 aValue) {
if (iCount4 < EMax) {
SPatchInfo4 info = {anOffset, aValue};
iPatchInfo4[iCount4++] = info;
}
}
u32 CGbaPatcher::Patch(void) {
switch (setjmp(iJumpBuf)) {
case 0:
PatchInternal();
break;
case 1:
break;
default:
break;
}
return iSaveSize;
}
void CGbaPatcher::Error(const char* anError) {
#ifdef __TEST
printf("error: %s\n", anError);
#else
#endif
longjmp(iJumpBuf, 1);
}
void CGbaPatcher::PatchInternal(void) {
u32 search_size = iSize / 4;
for (u32 ii = 0; ii < search_size; ii++) {
switch (Data()[ii]) {
case 0x4d415253: // SRAM_V|SRAM_F_V
if (Data()[ii + 1] == 0x565f465f || (Data()[ii + 1] & 0xffff) == 0x565f) {
Add(ii, ESRAMLabel);
ii++;
}
break;
case 0x52504545: // EEPROM_V
if (Data()[ii + 1] == 0x565f4d4f) {
Add(ii, EEEPROMLabel);
ii++;
}
case 0xb0a2b570: // eeprom 121/122/124 read
if (Data()[ii + 1] == 0x04001c0d && Data()[ii + 2] == 0x48030c03 &&
Data()[ii + 3] == 0x88806800 && Data()[ii + 4] == 0xd3054283) {
Add(ii, EEEPROMRead);
ii += 4;
}
break;
case 0xb0a9b530: // eeprom 121/122 write
if (Data()[ii + 1] == 0x04001c0d && Data()[ii + 2] == 0x48030c04 &&
Data()[ii + 3] == 0x88806800 && Data()[ii + 4] == 0xd3054284) {
Add(ii, EEEPROMWrite);
ii += 4;
}
break;
case 0xb0acb5f0: // eeprom 124 write
if (Data()[ii + 1] == 0x04001c0d && Data()[ii + 2] == 0x06120c01 &&
Data()[ii + 3] == 0x48030e17 && Data()[ii + 4] == 0x88806800 &&
Data()[ii + 5] == 0xd3054281) {
Add(ii, EEEPROMWrite124);
ii += 5;
}
break;
case 0x4647b5f0: // eeprom 126 write
if (Data()[ii + 1] == 0xb0acb480 && Data()[ii + 2] == 0x04001c0e &&
Data()[ii + 3] == 0x06120c05 && Data()[ii + 4] == 0x46900e12 &&
Data()[ii + 5] == 0x68004803 && Data()[ii + 6] == 0x42858880 &&
Data()[ii + 7] == 0x4802d306) {
Add(ii, EEEPROMWrite126);
ii += 7;
}
break;
case 0x53414c46:
// FLASH1M_V10X
if (Data()[ii + 1] == 0x5f4d3148 && (Data()[ii + 2] & 0xfeffffff) == 0x32303156) {
Add(ii, EFLASH1M10XLabel);
ii += 2;
}
// FLASH_V1
else if (Data()[ii + 1] == 0x31565f48) {
Add(ii, EFLASHLabel);
ii++;
}
// FLASH512_V13
else if (Data()[ii + 1] == 0x32313548 && Data()[ii + 2] == 0x3331565f) {
Add(ii, EFLASH512Label);
ii += 2;
}
break;
case 0x0e000600: // FLASH1M_V102 code1
if (Data()[ii + 1] == 0x21aa4b05 && Data()[ii + 2] == 0x4a057019) {
Add(ii, EFLASH1M102_1);
ii += 2;
}
break;
case 0xfd88f7ff: // FLASH1M_V102 code2
if (Data()[ii + 1] == 0x0c030400 && Data()[ii + 2] == 0x24014a03 &&
Data()[ii + 3] == 0x0000e007) {
Add(ii, EFLASH1M102_2);
ii += 2;
}
break;
case 0xb090b5f0:
// FLASH1M_V102 code3
if (Data()[ii + 1] == 0x0c060400 && Data()[ii + 2] == 0x68004803 &&
Data()[ii + 3] == 0x42868940 && Data()[ii + 4] == 0x4802d306 &&
Data()[ii + 5] == 0x0000e052) {
Add(ii, EFLASH1M102_3);
ii += 5;
}
// FLASH1M_V103 code3
else if (Data()[ii + 1] == 0x0c060400 && Data()[ii + 2] == 0x68004803 &&
Data()[ii + 3] == 0x42868940 && Data()[ii + 4] == 0x4802d306 &&
Data()[ii + 5] == 0x0000e054) {
Add(ii, EFLASH1M103_3);
ii += 5;
}
// FLASH1M_V103 code4
else if (Data()[ii + 1] == 0x04011c0e && Data()[ii + 2] == 0x06120c0c &&
Data()[ii + 3] == 0x4d180e17 && Data()[ii + 4] == 0x68406828 &&
Data()[ii + 5] == 0xd2374286) {
Add(ii, EFLASH1M103_4);
ii += 5;
}
// FLASH1M_V102 code5
else if (Data()[ii + 1] == 0x04001c0f && Data()[ii + 2] == 0x48030c04 &&
Data()[ii + 3] == 0x89406800 && Data()[ii + 4] == 0xd3054284 &&
Data()[ii + 5] == 0xe0404801) {
Add(ii, EFLASH1M102_5);
ii += 5;
}
// FLASH_V123 code5
else if (Data()[ii + 1] == 0x04001c0f && Data()[ii + 2] == 0x2c0f0c04 &&
Data()[ii + 3] == 0x4801d904) {
Add(ii, EFLASHV123_5);
ii += 3;
}
// FLASH512_V13 code5
else if (Data()[ii + 1] == 0x04001c0f && Data()[ii + 2] == 0x48030c04 &&
Data()[ii + 3] == 0x89406800 && Data()[ii + 4] == 0xd3054284 &&
Data()[ii + 5] == 0xe0414801) {
Add(ii, EFLASH512_2);
ii += 5;
}
break;
case 0x4c0ab510: // FLASH1M_V102 code4
if (Data()[ii + 1] == 0x702222aa && Data()[ii + 2] == 0x22554b09 &&
Data()[ii + 3] == 0x22a0701a && Data()[ii + 4] == 0x78027022 &&
Data()[ii + 5] == 0x4b07700a && Data()[ii + 6] == 0x681b7802 &&
Data()[ii + 7] == 0xf0002001) {
Add(ii, EFLASH1M102_4);
ii += 7;
}
break;
case 0xb093b590: // FLASH_V1 code1
if (Data()[ii + 1] == 0x1d39466f && Data()[ii + 2] == 0xf0001c08 &&
(Data()[ii + 3] & 0xffffff00) == 0x1d38f900 && Data()[ii + 4] == 0x64791c41 &&
Data()[ii + 5] == 0x21aa4809 && Data()[ii + 6] == 0x48097001 &&
Data()[ii + 7] == 0x70012155) {
Add(ii, EFLASH_1);
ii += 7;
}
break;
case 0xb092b580: // FLASH_V1 code2
if (Data()[ii + 1] == 0x481f466f && Data()[ii + 2] == 0x880a491e &&
Data()[ii + 3] == 0x1c114b1e && Data()[ii + 4] == 0x4b1e4019 &&
Data()[ii + 5] == 0x8a12681a && Data()[ii + 6] == 0x1c0a4311 &&
Data()[ii + 7] == 0x481c8002) {
Add(ii, EFLASH_2);
ii += 7;
}
break;
case 0xb094b580:
// FLASH_V1 code3
if (Data()[ii + 1] == 0x1c39466f && Data()[ii + 2] == 0x1c388008 &&
Data()[ii + 3] == 0x290f8801 && Data()[ii + 4] == 0x4801d904 &&
Data()[ii + 5] == 0x0000e056 && Data()[ii + 6] == 0x000080ff &&
Data()[ii + 7] == 0x49234823) {
Add(ii, EFLASH_3);
ii += 7;
}
// FLASH_V1 code4
else if (Data()[ii + 1] == 0x6079466f && Data()[ii + 2] == 0x80081c39 &&
Data()[ii + 3] == 0x88011c38 && Data()[ii + 4] == 0xd903290f &&
Data()[ii + 5] == 0xe0734800 && Data()[ii + 6] == 0x000080ff &&
Data()[ii + 7] == 0x88011c38) {
Add(ii, EFLASH_4);
ii += 7;
}
break;
case 0xffaaf7ff: // FLASH_V123 code1
if (Data()[ii + 1] == 0x0c030400 && Data()[ii + 2] == 0x24014a03 &&
Data()[ii + 3] == 0x0000e007) {
Add(ii, EFLASHV123_1);
ii += 3;
}
break;
case 0xb0a0b5f0:
// FLASH_V123 code2
if (Data()[ii + 1] == 0x1c161c0d && Data()[ii + 2] == 0x04001c1f &&
Data()[ii + 3] == 0x4a080c04) {
Add(ii, EFLASHV123_2);
ii += 3;
}
// FLASH512_V13 code5
else if (Data()[ii + 1] == 0x1c161c0d && Data()[ii + 2] == 0x04031C1F &&
Data()[ii + 3] == 0x4a0f0c1c) {
Add(ii, EFLASH512_1);
ii += 3;
}
break;
case 0xb090b570: // FLASH_V123 code3
if (Data()[ii + 1] == 0x88294d15 && Data()[ii + 2] == 0x40314e15 &&
Data()[ii + 3] == 0x68004815) {
Add(ii, EFLASHV123_3);
ii += 3;
}
break;
case 0x4646b570: // FLASH_V123 code4
if (Data()[ii + 1] == 0xb090b440 && Data()[ii + 2] == 0x0c030400 &&
Data()[ii + 3] == 0xd83b2b0f) {
Add(ii, EFLASHV123_4);
ii += 3;
}
break;
case 0x701020aa: // FLASH1M_V103 code1
if (Data()[ii + 1] == 0x20554905 && Data()[ii + 2] == 0x20907008 &&
Data()[ii + 3] == 0xa9107010) {
Add(ii, EFLASH1M103_1);
ii += 3;
}
break;
case 0xf0010500: // FLASH1M_V103 code2a
if (Data()[ii + 1] == 0x0600f8d3 && Data()[ii + 2] == 0x43040e00 &&
Data()[ii + 3] == 0x20aa4907 && Data()[ii + 4] == 0x4a077008 &&
Data()[ii + 5] == 0x70102055 && Data()[ii + 6] == 0x700820f0 &&
Data()[ii + 7] == 0xa9107008) {
Add(ii, EFLASH1M103_2);
ii += 7;
}
break;
case 0xf0050500: // FLASH1M_V103 code2b
if (Data()[ii + 1] == 0x0600F945 && Data()[ii + 2] == 0x43040e00 &&
Data()[ii + 3] == 0x20aa4907 && Data()[ii + 4] == 0x4a077008 &&
Data()[ii + 5] == 0x70102055 && Data()[ii + 6] == 0x700820f0 &&
Data()[ii + 7] == 0xa9107008) {
Add(ii, EFLASH1M103_2);
ii += 7;
}
break;
}
}
#ifdef __TEST
DumpPatchInfo();
#endif
iWriter->Open();
if (!PatchNes() && !PatchSRAM() && !PatchEEPROM() && !PatchFLASH1M_V10X()) PatchFLASH();
PatchDragonBallZ();
if (!PatchSleep()) {
#ifdef __TEST
if (__COMMON_SLEEP) {
#else
if (gs().gbaSleepHack) {
#endif
CommonSleepSearch();
CommonSleepPatch();
}
}
iWriter->Close();
}
bool CGbaPatcher::PatchSRAM(void) {
test_printf("CGbaPatcher::PatchSRAM\n");
bool res = false;
for (u32 ii = 0; ii < iCount; ii++) {
if (iPatchInfo[ii].iType == ESRAMLabel) {
res = true;
iSaveSize = 32 * 1024;
break;
}
}
return res;
}
bool CGbaPatcher::PatchEEPROM(void) {
test_printf("CGbaPatcher::PatchEEPROM\n");
bool res = false;
const u8 patch_eeprom_1[] = {
0x00, 0x04, // LSL R0, R0, #0x10
0x0a, 0x1c, // ADD R2, R1, #0
0x40, 0x0b, // LSR R0, R0, #0xD
0xe0, 0x21, 0x09, 0x05, // MOVL R1, 0xE000000
0x41, 0x18, // ADD R1, R0, R1
0x07, 0x31, // ADD R1, #7
0x00, 0x23, // MOV R3, #0
// l1:
0x08, 0x78, // LDRB R0, [R1]
0x10, 0x70, // STRB R0, [R2]
0x01, 0x33, // ADD R3, #1
0x01, 0x32, // ADD R2, #1
0x01, 0x39, // SUB R1, #1
0x07, 0x2b, // CMP R3, #7
0xf8, 0xd9, // BLS l1
0x00, 0x20, // MOV R0, #0
0x70, 0x47 // BX LR
};
const u8 patch_eeprom_2[] = {
0x00, 0x04, // LSL R0, R0, #0x10
0x0a, 0x1c, // ADD R2, R1, #0
0x40, 0x0b, // LSR R0, R0, #0xD
0xe0, 0x21, 0x09, 0x05, // MOVL R1, 0xE000000
0x41, 0x18, // ADD R1, R0, R1
0x07, 0x31, // ADD R1, #7
0x00, 0x23, // MOV R3, #0
// l1:
0x10, 0x78, // LDRB R0, [R2]
0x08, 0x70, // STRB R0, [R1]
0x01, 0x33, // ADD R3, #1
0x01, 0x32, // ADD R2, #1
0x01, 0x39, // SUB R1, #1
0x07, 0x2b, // CMP R3, #7
0xf8, 0xd9, // BLS l1
0x00, 0x20, // MOV R0, #0
0x70, 0x47 // BX LR
};
u32 eeprom_ver = 0, eeprom_count = 0;
for (u32 ii = 0; ii < iCount; ii++) {
if (iPatchInfo[ii].iType == EEEPROMLabel) {
u32 ver = Data()[iPatchInfo[ii].iOffset + 2] & 0xffffff;
ver = 100 * ((ver & 0xff) - '0') + 10 * (((ver & 0xff00) >> 8) - '0') +
(((ver & 0xff0000) >> 16) - '0');
test_printf("eeprom ver: %d\n", ver);
if (eeprom_count) {
if (ver != eeprom_ver) Error("EEPROM: two different versions");
} else {
eeprom_ver = ver;
}
eeprom_count++;
}
}
if (eeprom_ver == 111) {
res = true;
iSaveSize = 512;
PatchEEPROM111();
} else if (eeprom_count) {
res = true;
iSaveSize = 8 * 1024;
for (u32 ii = 0; ii < iCount; ii++) {
switch (iPatchInfo[ii].iType) {
case EEEPROMRead:
if (eeprom_ver == 120 || eeprom_ver == 121 || eeprom_ver == 122 ||
eeprom_ver == 124 || eeprom_ver == 126)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_eeprom_1,
sizeof(patch_eeprom_1));
else
Error("EEEPROMRead: invalid version");
break;
case EEEPROMWrite:
if (eeprom_ver == 120 || eeprom_ver == 121 || eeprom_ver == 122)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_eeprom_2,
sizeof(patch_eeprom_2));
else
Error("EEEPROMWrite: invalid version");
break;
case EEEPROMWrite124:
if (eeprom_ver == 124)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_eeprom_2,
sizeof(patch_eeprom_2));
else
Error("EEEPROMWrite124: invalid version");
break;
case EEEPROMWrite126:
if (eeprom_ver == 126)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_eeprom_2,
sizeof(patch_eeprom_2));
else
Error("EEEPROMWrite126: invalid version");
break;
default:
break;
}
}
}
return res;
}
bool CGbaPatcher::PatchFLASH1M_V10X(void) {
test_printf("CGbaPatcher::PatchFLASH1M_V10X\n");
bool res = false;
const u8 patch_flash_1[] = {
0x00, 0xb5, // PUSH {LR}
0x00, 0xf0, 0x55, 0xfb, // BL Save3a
0x02, 0xbc, // POP {R1}
0x08, 0x47 // BX R1
};
const u8 patch_flash_2[] = {
0x13, 0x23, 0x1b, 0x02, // MOVL R3, 0x1300
0x62, 0x20, // MOV R0, #0x62
0x03, 0x43 // ORR R3, R0
};
const u8 patch_flash_3[] = {
0x00, 0xb5, // PUSH {LR}
0x00, 0x20, // MOV R0, #0
0x02, 0xbc, // POP {R1}
0x08, 0x47, // BX R1
// Save3a:
0x70, 0xb4, // PUSH {R4-R6}
0x0a, 0x49, // LDR R1, =0x9FE0000
0xd2, 0x22, 0x12, 0x02, // MOVL R2, 0xD200
0x0a, 0x80, // STRH R2, [R1]
0x09, 0x49, // LDR R1, =0x8000000
0x15, 0x23, 0x1b, 0x02, // MOVL R3, 0x1500
0x0b, 0x80, // STRH R3, [R1]
0x08, 0x49, // LDR R1, =0x8020000
0x0a, 0x80, // STRH R2, [R1]
0x08, 0x49, // LDR R1, =0x8040000
0x0b, 0x80, // STRH R3, [R1]
0x08, 0x49, // LDR R1, =0x9C00000
0x00, 0x01, // LSL R0, R0, #4
0x10, 0x30, // ADD R0, #0x10
0x08, 0x80, // STRH R0, [R1]
0x07, 0x49, // LDR R1, =0x9FC0000
0x0b, 0x80, // STRH R3, [R1]
0x70, 0xbc, // POP {R4-R6}
0x70, 0x47, // BX LR
0x00, // DCB 0
0x00, // DCB 0
0x00, 0x00, 0xfe, 0x09, // DCD 0x9FE0000
0x00, 0x00, 0x00, 0x08, // DCD 0x8000000
0x00, 0x00, 0x02, 0x08, // DCD 0x8020000
0x00, 0x00, 0x04, 0x08, // DCD 0x8040000
0x00, 0x00, 0xc0, 0x09, // DCD 0x9C00000
0x00, 0x00, 0xfc, 0x09 // DCD 0x9FC0000
};
const u8 patch_flash_4[] = {
0x00, 0x00 // LSL R0, R0, #0
};
const u32 patch_flash_4_offsets[] = {0x06, 0x0c, 0x10};
const u8 patch_flash_5[] = {
0x7c, 0xb5, // PUSH {R2-R6,LR}
0x00, 0x06, // LSL R0, R0, #0x18
0x04, 0x0f, // LSR R4, R0, #0x1C
0x00, 0x01, // LSL R0, R0, #4
0x00, 0x0c, // LSR R0, R0, #0x10
0x0a, 0x1c, // ADD R2, R1, #0
0xe0, 0x21, 0x09, 0x05, // MOVL R1, 0xE000000
0x09, 0x18, // ADD R1, R1, R0
0x01, 0x23, 0x1b, 0x03, // MOVL R3, 0x1000
0x0e, 0x48, // LDR R0, =0x9FE0000
0xd2, 0x25, 0x2d, 0x02, // MOVL R5, 0xD200
0x05, 0x80, // STRH R5, [R0]
0x0d, 0x48, // LDR R0, =0x8000000
0x15, 0x26, 0x36, 0x02, // MOVL R6, 0x1500
0x06, 0x80, // STRH R6, [R0]
0x0c, 0x48, // LDR R0, =0x8020000
0x05, 0x80, // STRH R5, [R0]
0x0c, 0x48, // LDR R0, =0x8040000
0x06, 0x80, // STRH R6, [R0]
0x0c, 0x48, // LDR R0, =0x9C00000
0x24, 0x01, // LSL R4, R4, #4
0x10, 0x34, // ADD R4, #0x10
0x04, 0x80, // STRH R4, [R0]
0x0b, 0x48, // LDR R0, =0x9FC0000
0x06, 0x80, // STRH R6, [R0]
// l1:
0x10, 0x78, // LDRB R0, [R2]
0x08, 0x70, // STRB R0, [R1]
0x01, 0x3b, // SUB R3, #1
0x01, 0x32, // ADD R2, #1
0x01, 0x31, // ADD R1, #1
0x00, 0x2b, // CMP R3, #0
0xf8, 0xd1, // BNE l1
0x00, 0x20, // MOV R0, #0
0x7c, 0xbc, // POP {R2-R6}
0x02, 0xbc, // POP {R1}
0x08, 0x47, // BX R1
0x00, 0x00, 0xfe, 0x09, // DCD 0x9FE0000
0x00, 0x00, 0x00, 0x08, // DCD 0x8000000
0x00, 0x00, 0x02, 0x08, // DCD 0x8020000
0x00, 0x00, 0x04, 0x08, // DCD 0x8040000
0x00, 0x00, 0xc0, 0x09, // DCD 0x9C00000
0x00, 0x00, 0xfc, 0x09 // DCD 0x9FC0000
};
const u8 patch_flash_6[] = {
0x00, 0xb5, // PUSH {LR}
0x00, 0xf0, 0x5b, 0xfb, // BL Save6a
0x02, 0xbc, // POP {R1}
0x08, 0x47 // BX R1
};
const u8 patch_flash_7a[] = {
0x10, 0x78 // LDRB R0, [R2]
};
const u8 patch_flash_7b[] = {
0x08, 0x78 // LDRB R0, [R1]
};
const u8 patch_flash_8[] = {
0x1b, 0x24, 0x24, 0x01, // MOVL R4, 0x1B0
0x24, 0x01, // LSL R4, R4, #4
0x32, 0x20, // MOV R0, #0x32
0x04, 0x43, // ORR R4, R0
0x07, 0x49, // LDR R1, =0xE005555
0xaa, 0x20, // MOV R0, #0xAA
0x08, 0x78, // LDRB R0, [R1]
0x07, 0x4a, // LDR R2, =0xE002AAA
0x55, 0x20, // MOV R0, #0x55
0x10, 0x78, // LDRB R0, [R2]
0xf0, 0x20, // MOV R0, #0xF0
0x08, 0x78, // LDRB R0, [R1]
0x08, 0x78 // LDRB R0, [R1]
};
const u8 patch_flash_9[] = {
0x00, 0xb5, // PUSH {LR}
0x00, 0x20, // MOV R0, #0
0x02, 0xbc, // POP {R1}
0x08, 0x47 // BX R1
};
const u8 patch_flash_a[] = {
0x78, 0xb5, // PUSH {R3-R6,LR}
0x00, 0x06, // LSL R0, R0, #0x18
0x03, 0x0f, // LSR R3, R0, #0x1C
0x00, 0x01, // LSL R0, R0, #4
0x00, 0x04, // LSL R0, R0, #0x10
0x40, 0x18, // ADD R0, R0, R1
0xe0, 0x21, 0x09, 0x05, // MOVL R1, 0xE000000
0x09, 0x18, // ADD R1, R1, R0
0x0b, 0x48, // LDR R0, =0x9FE0000
0xd2, 0x25, 0x2d, 0x02, // MOVL R5, 0xD200
0x05, 0x80, // STRH R5, [R0]
0x0a, 0x48, // LDR R0, =0x8000000
0x15, 0x26, 0x36, 0x02, // MOVL R6, 0x1500
0x06, 0x80, // STRH R6, [R0]
0x09, 0x48, // LDR R0, =0x8020000
0x05, 0x80, // STRH R5, [R0]
0x09, 0x48, // LDR R0, =0x8040000
0x06, 0x80, // STRH R6, [R0]
0x09, 0x48, // LDR R0, =0x9C00000
0x1b, 0x01, // LSL R3, R3, #4
0x10, 0x33, // ADD R3, #0x10
0x03, 0x80, // STRH R3, [R0]
0x08, 0x48, // LDR R0, =0x9FC0000
0x06, 0x80, // STRH R6, [R0]
0x0a, 0x70, // STRB R2, [R1]
0x00, 0x20, // MOV R0, #0
0x78, 0xbc, // POP {R3-R6}
0x02, 0xbc, // POP {R1}
0x08, 0x47, // BX R1
0x00, 0x00, 0xfe, 0x09, // DCD 0x9FE0000
0x00, 0x00, 0x00, 0x08, // DCD 0x8000000
0x00, 0x00, 0x02, 0x08, // DCD 0x8020000
0x00, 0x00, 0x04, 0x08, // DCD 0x8040000
0x00, 0x00, 0xc0, 0x09, // DCD 0x9C00000
0x00, 0x00, 0xfc, 0x09 // DCD 0x9FC0000
};
u32 flash_ver = 0, flash_count = 0;
for (u32 ii = 0; ii < iCount; ii++) {
if (iPatchInfo[ii].iType == EFLASH1M10XLabel) {
u32 ver = Data()[iPatchInfo[ii].iOffset + 2] & 0xffffff00;
ver = 100 * (((ver & 0xff00) >> 8) - '0') + 10 * (((ver & 0xff0000) >> 16) - '0') +
(((ver & 0xff000000) >> 24) - '0');
test_printf("flash ver: %d\n", ver);
if (flash_count) {
if (ver != flash_ver) Error("FLASH1M: two different versions");
} else {
flash_ver = ver;
}
flash_count++;
break;
}
}
if (flash_count) {
res = true;
iSaveSize = 128 * 1024;
for (u32 ii = 0; ii < iCount; ii++) {
switch (iPatchInfo[ii].iType) {
case EFLASH1M102_1:
if (flash_ver == 102)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_1,
sizeof(patch_flash_1));
else if (flash_ver == 103)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_6,
sizeof(patch_flash_6));
else
Error("EFLASH1M102_1: invalid version");
break;
case EFLASH1M102_2:
if (flash_ver == 102 || flash_ver == 103)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_2,
sizeof(patch_flash_2));
else
Error("EFLASH1M102_2: invalid version");
break;
case EFLASH1M102_3:
if (flash_ver == 102) {
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_3,
sizeof(patch_flash_3));
u8 patch_fix[2] = {iRamPage, 0x30};
iWriter->Write(iPatchInfo[ii].iOffset * 4 + 38, patch_fix,
sizeof(patch_fix));
} else
Error("EFLASH1M102_3: invalid version");
break;
case EFLASH1M102_4:
if (flash_ver == 102) {
for (u32 jj = 0; jj < sizeofa(patch_flash_4_offsets); jj++)
iWriter->Write(iPatchInfo[ii].iOffset * 4 + patch_flash_4_offsets[jj],
patch_flash_4, sizeof(patch_flash_4));
} else if (flash_ver == 103)
; // do nothing
else
Error("EFLASH1M102_4: invalid version");
break;
case EFLASH1M102_5:
if (flash_ver == 102 || flash_ver == 103) {
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_5,
sizeof(patch_flash_5));
u8 patch_fix[2] = {iRamPage, 0x34};
iWriter->Write(iPatchInfo[ii].iOffset * 4 + 50, patch_fix,
sizeof(patch_fix));
} else
Error("EFLASH1M102_5: invalid version");
break;
case EFLASH1M103_1:
if (flash_ver == 102)
; // do nothing
else if (flash_ver == 103) {
iWriter->Write(iPatchInfo[ii].iOffset * 4 + 0x02, patch_flash_7a,
sizeof(patch_flash_7a));
iWriter->Write(iPatchInfo[ii].iOffset * 4 + 0x08, patch_flash_7b,
sizeof(patch_flash_7b));
iWriter->Write(iPatchInfo[ii].iOffset * 4 + 0x0c, patch_flash_7a,
sizeof(patch_flash_7a));
} else
Error("EFLASH1M103_1: invalid version");
break;
case EFLASH1M103_2:
if (flash_ver == 103) {
iWriter->Write(iPatchInfo[ii].iOffset * 4 + 2, patch_flash_8,
sizeof(patch_flash_8));
} else
Error("EFLASH1M103_2: invalid version");
break;
case EFLASHV123_3:
if (flash_ver == 102)
; // do nothing
else if (flash_ver == 103) {
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_9,
sizeof(patch_flash_9));
} else
Error("FLASH1M103, EFLASHV123_3: invalid version");
break;
case EFLASH1M103_3:
if (flash_ver == 103) {
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_3,
sizeof(patch_flash_3));
u8 patch_fix[2] = {iRamPage, 0x30};
iWriter->Write(iPatchInfo[ii].iOffset * 4 + 38, patch_fix,
sizeof(patch_fix));
} else
Error("EFLASH1M103_3: invalid version");
break;
case EFLASH1M103_4:
if (flash_ver == 103) {
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_a,
sizeof(patch_flash_a));
u8 patch_fix[2] = {iRamPage, 0x33};
iWriter->Write(iPatchInfo[ii].iOffset * 4 + 46, patch_fix,
sizeof(patch_fix));
} else
Error("EFLASH1M103_4: invalid version");
break;
default:
break;
}
}
}
return res;
}
bool CGbaPatcher::PatchFLASH(void) {
test_printf("CGbaPatcher::PatchFLASH\n");
bool res = false;
const u8 patch_flash_1[] = {
0x00, 0xb5, // PUSH {LR}
0x1b, 0x20, 0x00, 0x02, // MOVL R0, 0x1B00
0x32, 0x21, // MOV R1, #0x32
0x08, 0x43, // ORR R0, R1
0x02, 0xbc, // POP {R1}
0x08, 0x47 // BX R1
};
const u8 patch_flash_2[] = {
0x00, 0xb5, // PUSH {LR}
0x00, 0x20, // MOV R0, #0
0x02, 0xbc, // POP {R1}
0x08, 0x47 // BX R1
};
const u8 patch_flash_3[] = {
0x1b, 0x23, 0x1b, 0x02, // MOVL R3, 0x1B00
0x32, 0x20, // MOV R0, #0x32
0x03, 0x43 // ORR R3, R0
};
const u8 patch_flash_4[] = {
0x7c, 0xb5, // PUSH {R2-R6,LR}
0x00, 0x07, // LSL R0, R0, #0x1C
0x00, 0x0c, // LSR R0, R0, #0x10
0x0a, 0x1c, // ADD R2, R1, #0
0xe0, 0x21, 0x09, 0x05, // MOVL R1, 0xE000000
0x09, 0x18, // ADD R1, R1, R0
0x01, 0x23, 0x1b, 0x03, // MOVL R3, 0x1000
// l1:
0x10, 0x78, // LDRB R0, [R2]
0x08, 0x70, // STRB R0, [R1]
0x01, 0x3b, // SUB R3, #1
0x01, 0x32, // ADD R2, #1
0x01, 0x31, // ADD R1, #1
0x00, 0x2b, // CMP R3, #0
0xf8, 0xd1, // BNE l1
0x00, 0x20, // MOV R0, #0
0x7c, 0xbc, // POP {R2-R6}
0x02, 0xbc, // POP {R1}
0x08, 0x47 // BX R1
};
const u8 patch_flash_5[] = {
0x70, 0xb5, // PUSH {R4-R6,LR}
0xa0, 0xb0, // SUB SP, SP, #0x80
0x00, 0x03, // LSL R0, R0, #0xC
0x40, 0x18, // ADD R0, R0, R1
0xe0, 0x21, 0x09, 0x05, // MOVL R1, 0xE000000
0x09, 0x18, // ADD R1, R1, R0
// l1:
0x08, 0x78, // LDRB R0, [R1]
0x10, 0x70, // STRB R0, [R2]
0x01, 0x3b, // SUB R3, #1
0x01, 0x32, // ADD R2, #1
0x01, 0x31, // ADD R1, #1
0x00, 0x2b, // CMP R3, #0
0xf8, 0xd1, // BNE l1
0x00, 0x20, // MOV R0, #0
0x20, 0xb0, // ADD SP, SP, #0x80
0x70, 0xbc, // POP {R4-R6}
0x02, 0xbc, // POP {R1}
0x08, 0x47 // BX R1
};
const u8 patch_flash_6[] = {
0x7c, 0xb5, // PUSH {R2-R6,LR}
0x90, 0xb0, // SUB SP, SP, #0x40
0x00, 0x03, // LSL R0, R0, #0xC
0x0a, 0x1c, // ADD R2, R1, #0
0xe0, 0x21, 0x09, 0x05, // MOVL R1, 0xE000000
0x09, 0x18, // ADD R1, R1, R0
0x01, 0x23, 0x1b, 0x03, // MOVL R3, 0x1000
// l1
0x10, 0x78, // LDRB R0, [R2]
0x08, 0x70, // STRB R0, [R1]
0x01, 0x3b, // SUB R3, #1
0x01, 0x32, // ADD R2, #1
0x01, 0x31, // ADD R1, #1
0x00, 0x2b, // CMP R3, #0
0xf8, 0xd1, // BNE l1
0x00, 0x20, // MOV R0, #0
0x10, 0xb0, // ADD SP, SP, #0x40
0x7c, 0xbc, // POP {R2-R6}
0x02, 0xbc, // POP {R1}
0x08, 0x47 // BX R1
};
u32 flash_ver = 0, flash_count = 0;
for (u32 ii = 0; ii < iCount; ii++) {
if (iPatchInfo[ii].iType == EFLASHLabel) {
u32 ver = Data()[iPatchInfo[ii].iOffset + 2] & 0xffff;
ver = 100 * (((Data()[iPatchInfo[ii].iOffset + 1] & 0xff000000) >> 24) - '0') +
10 * ((ver & 0xff) - '0') + (((ver & 0xff00) >> 8) - '0');
test_printf("flash ver: %d\n", ver);
if (flash_count) {
if (ver != flash_ver) Error("FLASH: two different versions");
} else {
flash_ver = ver;
}
flash_count++;
} else if (iPatchInfo[ii].iType == EFLASH512Label) {
u32 ver = 130 + ((Data()[iPatchInfo[ii].iOffset + 3] & 0xff) - '0');
test_printf("flash 512 ver: %d\n", ver);
if (flash_count) {
if (ver != flash_ver) Error("FLASH512: two different versions");
} else {
flash_ver = ver;
}
flash_count++;
}
}
if (flash_count) {
res = true;
iSaveSize = 64 * 1024;
for (u32 ii = 0; ii < iCount; ii++) {
switch (iPatchInfo[ii].iType) {
case EFLASH1M102_2:
if (flash_ver == 130 || flash_ver == 131 || flash_ver == 133)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_3,
sizeof(patch_flash_3));
else
Error("EFLASH512/EFLASH1M102_2: invalid version");
break;
case EFLASH_1:
if (flash_ver == 120 || flash_ver == 121)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_1,
sizeof(patch_flash_1));
else
Error("EFLASH_1: invalid version");
break;
case EFLASH_2:
if (flash_ver == 120 || flash_ver == 121)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_2,
sizeof(patch_flash_2));
else
Error("EFLASH_2: invalid version");
break;
case EFLASH_3:
if (flash_ver == 120 || flash_ver == 121)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_2,
sizeof(patch_flash_2));
else
Error("EFLASH_3: invalid version");
break;
case EFLASH_4:
if (flash_ver == 120 || flash_ver == 121)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_4,
sizeof(patch_flash_4));
else
Error("EFLASH_4: invalid version");
break;
case EFLASHV123_1:
if (flash_ver == 123 || flash_ver == 124 || flash_ver == 125 ||
flash_ver == 126)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_3,
sizeof(patch_flash_3));
else
Error("EFLASHV123_1: invalid version");
break;
case EFLASHV123_2:
if (flash_ver == 123 || flash_ver == 124 || flash_ver == 125 ||
flash_ver == 126)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_5,
sizeof(patch_flash_5));
else
Error("EFLASHV123_2: invalid version");
break;
case EFLASHV123_3:
if (flash_ver == 123 || flash_ver == 124 || flash_ver == 125 ||
flash_ver == 126 || flash_ver == 130 || flash_ver == 131 ||
flash_ver == 133)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_2,
sizeof(patch_flash_2));
else
Error("EFLASHV123_3: invalid version");
break;
case EFLASHV123_4:
if (flash_ver == 123 || flash_ver == 124 || flash_ver == 125 ||
flash_ver == 126 || flash_ver == 130 || flash_ver == 131 ||
flash_ver == 133)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_2,
sizeof(patch_flash_2));
else
Error("EFLASHV123_4: invalid version");
break;
case EFLASHV123_5:
if (flash_ver == 123 || flash_ver == 124 || flash_ver == 125 ||
flash_ver == 126)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_6,
sizeof(patch_flash_6));
else
Error("EFLASHV123_5: invalid version");
break;
case EFLASH512_1:
if (flash_ver == 130 || flash_ver == 131 || flash_ver == 133)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_5,
sizeof(patch_flash_5));
else
Error("EFLASH512_1: invalid version");
break;
case EFLASH512_2:
if (flash_ver == 130 || flash_ver == 131 || flash_ver == 133)
iWriter->Write(iPatchInfo[ii].iOffset * 4, patch_flash_6,
sizeof(patch_flash_6));
else
Error("EFLASH512_2: invalid version");
break;
default:
break;
}
}
}
return res;
}
bool CGbaPatcher::PatchSleep(void) {
test_printf("CGbaPatcher::PatchSleep\n");
bool res = false;
const u32 baldur_style = 0;
const u32 rayman3_style = 10;
const u32 zelda2_style = 16;
const u32 mario_style = 33;
const u32 drilldozer_style = 47;
const u32 drmario_style = 48;
const u32 coloris_style = 50;
const u32 dialhex_style = 51;
const u32 goldensun2_style = 52;
const u32 nes_style = 57;
const u32 goldensun_e_style = 69;
const u32 goldensun_style = 70;
const u32 prince_style = 74;
const u32 scurge_style = 76;
const u32 game_codes[] = {
// 0
0x50444742, // Baldur's Gate - Dark Alliance (Europe) (En,Fr,De,Es,It).gba
0x45444742, // Baldur's Gate - Dark Alliance (USA).gba
0x45574342, // Catwoman (USA, Europe) (En,Fr,De,Es,It,Nl).gba
0x50434142, // Action Man - Robot Atak (Europe) (En,Fr,De,Es,It).gba
0x45384842, // Harry Potter and the Goblet of Fire (USA, Europe)
// (En,Fr,De,Es,It,Nl,Da).gba
0x45385242, // Ghost Rider (USA, Europe) (En,Fr,De,Es,It,Nl).gba
0x50373242, // Top Spin 2 (Europe) (En,Fr,De,Es,It).gba
0x45373242, // Top Spin 2 (USA) (En,Fr,De,Es,It).gba
0x45504842, // Harry Potter - Quidditch World Cup (USA, Europe)
// (En,Fr,De,Es,It,Nl,Da).gba
0x4A504842, // Harry Potter - Quidditch World Cup (Japan).gba
// 10
0x505a5941, // Rayman 3 (Europe) (En,Fr,De,Es,It,Nl,Sv,No,Da,Fi).gba
0x455a5941, // Rayman 3 (USA) (En,Fr,Es).gba
0x50355842, // Rayman 10th Anniversary - Rayman Advance + Rayman 3 (Europe)
// (En,Fr,De,Es,It+En,Fr,De,Es,It,Nl,Sv,No,Da,Fi).gba
0x45355842, // Rayman 10th Anniversary - Rayman Advance + Rayman 3 (USA)
// (En,Fr,De,Es,It+En,Fr,Es).gba
0x505A5742, // 2 Games in 1 - Winnie the Pooh's Rumbly Tumbly Adventure + Rayman 3
// (Europe) (En,Fr,De,Es,It,Nl+En,Fr,De,Es,It,Nl,Sv,No,Da,Fi).gba
0x4a553341, // Mother 3 (Japan).gba
// 16
0x50584d42, // Metroid - Zero Mission (Europe) (En,Fr,De,Es,It).gba
0x4a584d42, // Metroid - Zero Mission (Japan).gba
0x45584d42, // Metroid - Zero Mission (USA).gba
0x43584d42, // Miteluode - Lingdian Renwu (China).gba
0x504d5a42, // Legend of Zelda, The - The Minish Cap (Europe) (En,Fr,De,Es,It).gba
0x454d5a42, // Legend of Zelda, The - The Minish Cap (USA).gba
0x45485a42, // Legend of Zelda, The - The Minish Cap (USA) (Kiosk Demo).gba
0x4a4d5a42, // Zelda no Densetsu - Fushigi no Boushi (Japan).gba
0x50544d41, // Metroid Fusion (Europe) (En,Fr,De,Es,It).gba
0x4a544d41, // Metroid Fusion (Japan).gba
0x45544d41, // Metroid Fusion (USA, Australia).gba
0x43544d41, // Miteluode Ronghe (China).gba
0x504c5a41, // Legend of Zelda, The - A Link to the Past & Four Swords (Europe)
// (En,Fr,De,Es,It).gba
0x454c5a41, // Legend of Zelda, The - A Link to the Past & Four Swords (USA,
// Australia).gba
0x4a4c5a41, // Zelda no Densetsu - Kamigami no Triforce & 4tsu no Tsurugi (Japan).gba
0x4a324141, // Super Mario Advance 2 - Super Mario World + Mario Brothers (Japan).gba
0x45383842, // Mario & Luigi - Superstar Saga (USA) (Kiosk Demo).gba
// 33
0x50413341, // Super Mario Advance 3 - Yoshi's Island (Europe) (En,Fr,De,Es,It).gba
0x45413341, // Super Mario Advance 3 - Yoshi's Island (USA).gba
0x4a413341, // Super Mario Advance 3 - Yoshi's Island + Mario Brothers (Japan).gba
0x43413341, // Yaoxi Dao (China).gba
0x4a345841, // Super Mario Advance 4 - Super Mario 3 + Mario Brothers (Japan).gba
0x4a345841, // Super Mario Advance 4 - Super Mario 3 + Mario Brothers (Japan) (Rev
// 2).gba
0x50345841, // Super Mario Advance 4 - Super Mario Bros. 3 (Europe)
// (En,Fr,De,Es,It).gba
0x50345841, // Super Mario Advance 4 - Super Mario Bros. 3 (Europe) (En,Fr,De,Es,It)
// (Rev 1).gba
0x45345841, // Super Mario Advance 4 - Super Mario Bros. 3 (USA).gba
0x45345841, // Super Mario Advance 4 - Super Mario Bros. 3 (USA, Australia) (Rev 1).gba
0x50383841, // Mario & Luigi - Superstar Saga (Europe) (En,Fr,De,Es,It).gba
0x45383841, // Mario & Luigi - Superstar Saga (USA, Australia).gba
0x4a383841, // Mario & Luigi RPG (Japan).gba
0x4a345841, // Super Mario Advance 4 - Super Mario 3 + Mario Brothers (Japan) (Rev
// 1).gba
// 47
0x45393456, // Drill Dozer (USA).gba
// 48
0x50505a42, // 2 Games in 1 - Dr. Mario & Puzzle League (Europe) (En,Fr,De,Es,It).gba
0x45505a42, // 2 Games in 1 - Dr. Mario & Puzzle League (USA, Australia).gba
// 50
0x4a415642, // bit Generations - Coloris (Japan).gba
// 51
0x4a425642, // bit Generations - Dialhex (Japan).gba
// 52
0x45464741, // Golden Sun - The Lost Age (USA, Europe).gba
0x44464741, // Golden Sun - Die vergessene Epoche (Germany).gba
0x46464741, // Golden Sun - L'Age Perdu (France).gba
0x49464741, // Golden Sun - L'Era Perduta (Italy).gba
0x53464741, // Golden Sun - La Edad Perdida (Spain).gba
// 57
0x454d4246, // Classic NES Series - Bomberman (USA, Europe).gba
0x45444146, // Classic NES Series - Castlevania (USA, Europe).gba
0x454b4446, // Classic NES Series - Donkey Kong (USA, Europe).gba
0x454d4446, // Classic NES Series - Dr. Mario (USA, Europe).gba
0x45424546, // Classic NES Series - Excitebike (USA, Europe).gba
0x45434946, // Classic NES Series - Ice Climber (USA, Europe).gba
0x454c5a46, // Classic NES Series - Legend of Zelda (USA, Europe).gba
0x45524d46, // Classic NES Series - Metroid (USA, Europe).gba
0x45375046, // Classic NES Series - Pac-Man (USA, Europe).gba
0x454d5346, // Classic NES Series - Super Mario Bros. (USA, Europe).gba
0x45565846, // Classic NES Series - Xevious (USA, Europe).gba
0x45424c46, // Classic NES Series - Zelda II - The Adventure of Link (USA, Europe).gba
// 69
0x45534741, // Golden Sun (USA, Europe).gba
// 70
0x46534741, // Golden Sun (France).gba
0x44534741, // Golden Sun (Germany).gba
0x49534741, // Golden Sun (Italy).gba
0x53534741, // Golden Sun (Spain).gba
// 74
0x50595042, // Prince of Persia - The Sands of Time (Europe) (En,Fr,De,Es,It,Nl).gba
0x45595042, // Prince of Persia - The Sands of Time (USA) (En,Fr,Es).gba
// 76
0x50564842, // Scurge - Hive (Europe) (En,Fr,De,Es,It).gba
0x45564842 // Scurge - Hive (USA) (En,Fr,Es).gba
// 78
};
const u8 game_rev[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // baldur style
0, 0, 0, 0, 0, 0, // rayman3 style
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // zelda2 style
0, 0, 0, 0, 0, 2, 0, 1, 0, 1, 0, 0, 0, 1, // mario style
0, // drilldozer style
0, 0, // drmario style
0, // coloris
0, // dialhex
0, 0, 0, 0, 0, // goldensun2 style
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // nes style
0, 0, 0, 0, 0, // goldensun style
0, 0, // prince style
0, 0 // scurge - hive
};
CHECK_SIZEOF(game_codes, 78 * 4);
CHECK_SIZEOF(game_rev, 78);
const u32 offsets_baldur[] = {0x6a32b4, 0x6a32b8, 0x2e320, 0x29f98, 0x6012c,
0x2e19c, 0x5e744, 0x5e780, 0x5e07a2, 0x5e07a2};
const u8 patch_sleep_baldur[] = {
0x08, 0x46 // MOV R0, R1
};
const u32 offsets_rayman3[] = {0x15aba, 0x15aca, 0x15ac2, 0x15ad2, 0x815aba, 0x188e};
const u8 patch_sleep_rayman3[] = {
0x40, 0x22 // MOV R2, #0x40
};
const u32 offsets_zelda2[] = {0x721f4, 0x720c4, 0x720a8, 0x75350, 0x55e04, 0x56284,
0x561dc, 0x56108, 0x7ee26, 0x7f72a, 0x7ee26, 0x7f72a,
0x68f1e, 0x68352, 0x68e8a, 0x4ca, 0x366e};
const u8 patch_sleep_zelda2[] = {
0x40, 0x21 // MOV R1, #0x40
};
const u32 offsets_mario[] = {
0x2a1c, 0xfd9d2, 0x2978, 0xfb8e6, 0x2978, 0xfb686, 0x2978,
0xfc02e, 0x56e, 0xb4ea8, 0x56e, 0xb67b8, 0x56e, 0xb9220,
0x56e, 0xb9700, 0x56e, 0xb61c0, 0x56e, 0xb68a0, 0x18aae,
0xf504c4, 0x18a9a, 0xf504c4, 0x18aae, 0xf504c4, 0x56e, 0xb6368,
};
const u8 patch_sleep_drilldozer[] = {
0x40, 0x23 // MOV R3, #0x40
};
const u8 patch_sleep_drilldozer2[] = {
0x30, 0x88 // LDRH R0, [R6]
};
const u32 offsets_drmario[] = {0x91830, 0x422d0a, 0x91830, 0x41b5d0};
const u8 patch_sleep_puzzleleague[] = {
0x88, 0x21 // MOV R1, #0x88
};
const u8 patch_sleep_drmario[] = {
0xc0, 0x46 // NOP
};
const u8 patch_sleep_coloris1[] = {
0xc0, 0x1a // SUB R0, R0, R3
};
const u8 patch_sleep_coloris2[] = {
0x40, 0x18 // ADD R0, R0, R1
};
const u8 patch_sleep_coloris3[] = {
0x00, 0x10 // DCD 0x1000
};
const u8 patch_sleep_dialhex1[] = {
0x2f, 0x88 // LDRH R7, [R5]
};
const u8 patch_sleep_dialhex2[] = {
0x10, 0x1c, // ADD R0, R2, #0
0xc0, 0x46 // NOP
};
const u8 patch_sleep_dialhex3[] = {
0x38, 0x1c, // ADD R0, R7, #0
0x00, 0x27, // MOV R7, #0
0x17, 0x80, // STRH R7, [R2]
0xc0, 0x46 // NOP
};
const u32 offsets_goldensun2[] = {0x137ac, 0x17270, 0x137d8, 0x1729c};
const u8 patch_sleep_goldensun2[] = {
0x03, 0xf0, 0x64, 0xfd, // BL xxx
0x03, 0xdf, // SWI 3
0x03, 0xf0, 0x63, 0xfd // BL yyy
};
const u8 patch_sleep_common[] = {
0x00, 0x20, // MOV R0, #0
0x00, 0xe0, // B loc_6
0x01, 0x20, // MOV R0, #1
// loc_6:
0x03, 0x4b, // LDR R3, =0x4000200
0x03, 0x49, // LDR R1, =0x2000
0x1a, 0x88, // LDRH R2, [R3]
0x4a, 0x40, // EOR R2, R1
0x1a, 0x80, // STRH R2, [R3]
0x19, 0xdf, // SWI 0x19
0x70, 0x47, // BX LR
0x00, 0x02, 0x00, 0x04, // dword_14 DCD 0x4000200
0x00, 0x20, 0x00, 0x00 // dword_18 DCD 0x2000
};
const u32 offsets_nes[] = {0xbf8, 0x1788, 0x870, 0xc54, 0x7ec, 0x85c,
0xac4, 0xb78, 0xb10, 0x85c, 0x9b8, 0x2e2c};
const u8 patch_sleep_nes1[] = {
0x00, 0x00, 0x9f, 0xe5, // LDR R0, =0x80FFB40
0x10, 0xff, 0x2f, 0xe1, // BX R0
0x40, 0xfb, 0x0f, 0x08 // DCD 0x80FFB40
};
const u8 patch_sleep_nes2[] = {
0x30, 0x40, 0x2d, 0xe9, // STMFD SP!, {R4,R5,LR}
0x28, 0x50, 0x9f, 0xe5, // LDR R5, =0x4000200
0x01, 0x3a, 0xa0, 0xe3, // MOV R3, #0x1000
0xb0, 0x40, 0xd5, 0xe1, // LDRH R4, [R5]
0xb0, 0x30, 0xc5, 0xe1, // STRH R3, [R5]
0x00, 0x00, 0xa0, 0xe3, // MOV R0, #0
0x00, 0x00, 0x19, 0xef, // SWI 0x190000
0x00, 0x00, 0x03, 0xef, // SWI 0x30000
0x01, 0x00, 0xa0, 0xe3, // MOV R0, #1
0x00, 0x00, 0x19, 0xef, // SWI 0x190000
0xb0, 0x40, 0xc5, 0xe1, // STRH R4, [R5]
0x30, 0x40, 0xbd, 0xe8, // LDMFD SP!, {R4,R5,LR}
0x1e, 0xff, 0x2f, 0xe1, // BX LR
0x00, 0x02, 0x00, 0x04 // DCD 0x4000200
};
const u32 offsets_goldensun[] = {0x33fe, 0x6cec, 0x341e, 0x6d0c,
0x342e, 0x6d1c, 0x344e, 0x6d3c};
const u8 patch_sleep_goldensun[] = {
0x03, 0xf0, 0x79, 0xfc, // BL xxx
0x03, 0xdf, // SWI 3
0x03, 0xf0, 0x78, 0xfc // BL yyy
};
const u32 offsets_prince[] = {0x72a40, 0x72a40};
const u8 patch_sleep_prince[] = {
0x40, 0x26 // MOV R6, #0x40
};
s32 index = -1;
u32 code = Data()[43];
u8 rev = (Data()[47]) & 0xff;
for (u32 ii = 0; ii < sizeofa(game_codes); ii++) {
if (code == game_codes[ii] && rev == game_rev[ii]) {
index = ii;
break;
}
}
if (index != -1) {
res = true;
switch (index) {
case baldur_style:
case baldur_style + 1:
case baldur_style + 2:
case baldur_style + 3:
case baldur_style + 4:
case baldur_style + 5:
case baldur_style + 6:
case baldur_style + 7:
case baldur_style + 8:
case baldur_style + 9:
iWriter->Write(offsets_baldur[index - baldur_style], patch_sleep_baldur,
sizeof(patch_sleep_baldur));
break;
case rayman3_style:
case rayman3_style + 1:
case rayman3_style + 2:
case rayman3_style + 3:
case rayman3_style + 4:
case rayman3_style + 5:
iWriter->Write(offsets_rayman3[index - rayman3_style], patch_sleep_rayman3,
sizeof(patch_sleep_rayman3));
break;
case zelda2_style:
case zelda2_style + 1:
case zelda2_style + 2:
case zelda2_style + 3:
case zelda2_style + 4:
case zelda2_style + 5:
case zelda2_style + 6:
case zelda2_style + 7:
case zelda2_style + 8:
case zelda2_style + 9:
case zelda2_style + 10:
case zelda2_style + 11:
case zelda2_style + 12:
case zelda2_style + 13:
case zelda2_style + 14:
case zelda2_style + 15:
case zelda2_style + 16:
iWriter->Write(offsets_zelda2[index - zelda2_style], patch_sleep_zelda2,
sizeof(patch_sleep_zelda2));
break;
case mario_style:
case mario_style + 1:
case mario_style + 2:
case mario_style + 3:
case mario_style + 4:
case mario_style + 5:
case mario_style + 6:
case mario_style + 7:
case mario_style + 8:
case mario_style + 9:
case mario_style + 10:
case mario_style + 11:
case mario_style + 12:
case mario_style + 13: {
u32 pos = (index - mario_style) * 2;
iWriter->Write(offsets_mario[pos++], patch_sleep_zelda2,
sizeof(patch_sleep_zelda2));
iWriter->Write(offsets_mario[pos], patch_sleep_zelda2, sizeof(patch_sleep_zelda2));
} break;
case drilldozer_style: {
u32 pos = 0x2bd70;
iWriter->Write(pos, patch_sleep_drilldozer, sizeof(patch_sleep_drilldozer));
iWriter->Write(pos + 6, patch_sleep_baldur, sizeof(patch_sleep_baldur));
iWriter->Write(pos + 104, patch_sleep_drilldozer2, sizeof(patch_sleep_drilldozer2));
} break;
case drmario_style:
case drmario_style + 1: {
u32 pos = (index - drmario_style) * 2;
iWriter->Write(offsets_drmario[pos++], patch_sleep_puzzleleague,
sizeof(patch_sleep_puzzleleague));
iWriter->Write(offsets_drmario[pos], patch_sleep_drmario,
sizeof(patch_sleep_drmario));
} break;
case coloris_style:
iWriter->Write(0x7fd8, patch_sleep_coloris1, sizeof(patch_sleep_coloris1));
iWriter->Write(0x7fda, patch_sleep_drmario, sizeof(patch_sleep_drmario));
iWriter->Write(0x7ff8, patch_sleep_coloris2, sizeof(patch_sleep_coloris2));
iWriter->Write(0x803c, patch_sleep_coloris3, sizeof(patch_sleep_coloris3));
break;
case dialhex_style:
iWriter->Write(0x7f1e, patch_sleep_dialhex1, sizeof(patch_sleep_dialhex1));
iWriter->Write(0x7f24, patch_sleep_dialhex2, sizeof(patch_sleep_dialhex2));
iWriter->Write(0x7f46, patch_sleep_dialhex3, sizeof(patch_sleep_dialhex3));
break;
case goldensun2_style:
case goldensun2_style + 1:
case goldensun2_style + 2:
case goldensun2_style + 3:
case goldensun2_style + 4: {
u32 pos = (index - goldensun2_style) ? 2 : 0;
res = false;
for (u32 ii = 0; ii < iCount; ii++) {
if (iPatchInfo[ii].iType == EFLASHV123_3 &&
iPatchInfo[ii].iOffset * 4 == offsets_goldensun2[pos + 1]) {
res = true;
iWriter->Write(offsets_goldensun2[pos++], patch_sleep_goldensun2,
sizeof(patch_sleep_goldensun2));
iWriter->Write(offsets_goldensun2[pos] + 8, patch_sleep_common,
sizeof(patch_sleep_common));
}
}
} break;
case nes_style:
case nes_style + 1:
case nes_style + 2:
case nes_style + 3:
case nes_style + 4:
case nes_style + 5:
case nes_style + 6:
case nes_style + 7:
case nes_style + 8:
case nes_style + 9:
case nes_style + 10:
case nes_style + 11:
iWriter->Write(offsets_nes[index - nes_style], patch_sleep_nes1,
sizeof(patch_sleep_nes1));
iWriter->Write(0xffb40, patch_sleep_nes2, sizeof(patch_sleep_nes2));
break;
case goldensun_e_style:
// yep, bug in game, no any patching needed :)
break;
case goldensun_style: // goldensun style
case goldensun_style + 1:
case goldensun_style + 2:
case goldensun_style + 3: {
u32 pos = (index - goldensun_style) * 2;
res = false;
for (u32 ii = 0; ii < iCount; ii++) {
if (iPatchInfo[ii].iType == EFLASHV123_3 &&
iPatchInfo[ii].iOffset * 4 == offsets_goldensun[pos + 1]) {
res = true;
iWriter->Write(offsets_goldensun[pos++], patch_sleep_goldensun,
sizeof(patch_sleep_goldensun));
iWriter->Write(offsets_goldensun[pos] + 8, patch_sleep_common,
sizeof(patch_sleep_common));
}
}
} break;
case prince_style:
case prince_style + 1:
iWriter->Write(offsets_prince[index - prince_style], patch_sleep_prince,
sizeof(patch_sleep_prince));
iWriter->Write(offsets_prince[index - prince_style] + 6, patch_sleep_baldur,
sizeof(patch_sleep_baldur));
break;
case scurge_style:
case scurge_style + 1: {
u8 byte = 0x10;
iWriter->Write(0x6349, &byte, sizeof(byte));
} break;
}
}
return res;
}
void CGbaPatcher::PatchEEPROM111(void) {
const u32 game_codes[] = {
0x4a414d41, // 0002
0x45485441, // 0033
0x45595241, // 0046
0x50595241, // 0051
0x44485441, // 0053
0x46485441 // 0059
};
const u32 patch_1_offsets[] = {0xb666c, 0x37570, 0x485ec, 0x485ec, 0x37580, 0x37520};
const u32 patch_1_data[] = {0x083eba71, 0x087ffaf1, 0x087b75b1,
0x087b71a1, 0x087ff641, 0x087fe2a1};
const u32 patch_2_offsets[] = {0xb6940, 0x37844, 0x488c0, 0x488c0, 0x37854, 0x377f4};
const u32 patch_3_offsets[] = {0x3eba70, 0x7ffaf0, 0x7b75b0, 0x7b71a0, 0x7ff640, 0x7fe2a0};
const u32 patch_3_data[] = {0x080b668d, 0x08037591, 0x0804860d,
0x0804860d, 0x080375a1, 0x08037541};
const u8 patch_3[] = {
0x39, 0x68, 0x27, 0x48, 0x81, 0x42, 0x23, 0xd0, 0x89, 0x1c, 0x08, 0x88, 0x01, 0x28,
0x02, 0xd1, 0x24, 0x48, 0x78, 0x60, 0x33, 0xe0, 0x00, 0x23, 0x00, 0x22, 0x89, 0x1c,
0x10, 0xb4, 0x01, 0x24, 0x08, 0x68, 0x20, 0x40, 0x5b, 0x00, 0x03, 0x43, 0x89, 0x1c,
0x52, 0x1c, 0x06, 0x2a, 0xf7, 0xd1, 0x10, 0xbc, 0x39, 0x60, 0xdb, 0x01, 0x02, 0x20,
0x00, 0x02, 0x1b, 0x18, 0x0e, 0x20, 0x00, 0x06, 0x1b, 0x18, 0x7b, 0x60, 0x39, 0x1c,
0x08, 0x31, 0x08, 0x88, 0x09, 0x38, 0x08, 0x80, 0x16, 0xe0, 0x15, 0x49, 0x00, 0x23,
0x00, 0x22, 0x10, 0xb4, 0x01, 0x24, 0x08, 0x68, 0x20, 0x40, 0x5b, 0x00, 0x03, 0x43,
0x89, 0x1c, 0x52, 0x1c, 0x06, 0x2a, 0xf7, 0xd1, 0x10, 0xbc, 0xdb, 0x01, 0x02, 0x20,
0x00, 0x02, 0x1b, 0x18, 0x0e, 0x20, 0x00, 0x06, 0x1b, 0x18, 0x08, 0x3b, 0x3b, 0x60,
0x0b, 0x48, 0x39, 0x68, 0x01, 0x60, 0x0a, 0x48, 0x79, 0x68, 0x01, 0x60, 0x0a, 0x48,
0x39, 0x1c, 0x08, 0x31, 0x0a, 0x88, 0x80, 0x21, 0x09, 0x06, 0x0a, 0x43, 0x02, 0x60,
0x07, 0x48, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e,
0x04, 0x00, 0x00, 0x0e, 0xd4, 0x00, 0x00, 0x04, 0xd8, 0x00, 0x00, 0x04, 0xdc, 0x00,
0x00, 0x04};
s32 index = -1;
for (u32 ii = 0; ii < sizeofa(game_codes); ii++) {
if (Data()[43] == game_codes[ii] && Data()[patch_1_offsets[ii] / 4] == 0x6839480e) {
index = ii;
break;
}
}
if (index == -1) return;
u32 patch_1_2[2];
patch_1_2[0] = 0x47004800;
patch_1_2[1] = patch_1_data[index];
iWriter->Write(patch_1_offsets[index], (u8*)patch_1_2, sizeof(patch_1_2));
patch_1_2[0] = 0x20e0e027;
patch_1_2[1] = 0x88010500;
iWriter->Write(patch_2_offsets[index], (u8*)patch_1_2, sizeof(patch_1_2));
iWriter->Write(patch_3_offsets[index], patch_3, sizeof(patch_3));
iWriter->Write(patch_3_offsets[index] + sizeof(patch_3), (u8*)(patch_3_data + index),
sizeof(patch_3_data[0]));
}
bool CGbaPatcher::PatchDragonBallZ(void) {
bool res = false;
const u32 game_codes[] = {
0x45464c42, // 2 Games in 1 - Dragon Ball Z - The Legacy of Goku I & II (USA).gba
0x50474c41, // Dragon Ball Z - The Legacy of Goku (Europe) (En,Fr,De,Es,It).gba
0x45474c41, // Dragon Ball Z - The Legacy of Goku (USA).gba
0x50464c41, // Dragon Ball Z - The Legacy of Goku II (Europe) (En,Fr,De,Es,It).gba
0x45464c41, // Dragon Ball Z - The Legacy of Goku II (USA).gba
0x4a464c41 // Dragon Ball Z - The Legacy of Goku II International (Japan).gba
};
const u32 patch_1_offsets[] = {0x033C, 0x0340, 0x0356, 0x035A, 0x035E, 0x0384, 0x0388,
0x494C, 0x4950, 0x4978, 0x497C, 0x998E, 0x9992};
const u32 patch_2_offsets[] = {0x356, 0x35e, 0x37e, 0x382};
s32 index = -1;
for (u32 ii = 0; ii < sizeofa(game_codes); ii++) {
if (Data()[43] == game_codes[ii]) {
index = ii;
break;
}
}
if (index != -1) {
u16 patch = 0;
res = true;
switch (index) {
case 0: // 2in1 us
for (u32 ii = 0; ii < sizeofa(patch_2_offsets); ii++) {
iWriter->Write(patch_2_offsets[ii] + 0x40000, (u8*)&patch, sizeof(patch));
}
patch = 0x1001;
iWriter->Write(0xbb9016, (u8*)&patch, sizeof(patch));
break;
case 1: // I eu
patch = 0x46c0;
for (u32 ii = 0; ii < sizeofa(patch_1_offsets); ii++) {
iWriter->Write(patch_1_offsets[ii], (u8*)&patch, sizeof(patch));
}
break;
case 2: // I us
for (u32 ii = 0; ii < sizeofa(patch_2_offsets); ii++) {
iWriter->Write(patch_2_offsets[ii], (u8*)&patch, sizeof(patch));
}
break;
case 3: // II eu
patch = 0x1001;
iWriter->Write(0x6f42b2, (u8*)&patch, sizeof(patch));
break;
case 4: // II us
patch = 0x1001;
iWriter->Write(0x3b8e9e, (u8*)&patch, sizeof(patch));
break;
case 5: // II jp
patch = 0x1001;
iWriter->Write(0x3fc8f6, (u8*)&patch, sizeof(patch));
break;
default:
res = false;
break;
}
}
return res;
}