From 3600f4249f39ec37e41e57aed70c8af620ff847c Mon Sep 17 00:00:00 2001 From: slipstream/RoL Date: Tue, 21 Feb 2017 20:50:41 +0000 Subject: New features and bugfixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed FireRed v1.0 (Japan) support - Now reloads the Pokémon from the loaded savefile after calling the payload, so the payload can modify those parts of saveBlock1 directly - Decrypts "secure" save data areas in FireRed, LeafGreen and Emerald --- gba/source/main.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 11 deletions(-) (limited to 'gba/source') diff --git a/gba/source/main.c b/gba/source/main.c index 5e2b708..4e1e31f 100644 --- a/gba/source/main.c +++ b/gba/source/main.c @@ -11,6 +11,92 @@ void call_into_middle_of_titlescreen_func(u32 addr,u32 stackspace); +void decrypt_save_structures(pSaveBlock1 SaveBlock1,pSaveBlock2 SaveBlock2,pSaveBlock3 SaveBlock3) { + if (GAME_RS) { + // R/S doesn't have save crypto. + return; + } + u8* sb1raw = (u8*)SaveBlock1; + u8* sb2raw = (u8*)SaveBlock2; + //u8* sb3raw = (u8*)SaveBlock3; // unused + + u32* xor_key_ptr = (u32*)(&sb2raw[( GAME_EM ? 0xA8 : 0xF20 )]); + + u32 xor_key = *xor_key_ptr; + u16 xor_key16 = (u16)xor_key; + if (!xor_key) { + // xor key is zero, nothing needs to be done. + return; + } + + u32* ptr_to_xor; + u32 save_offset; + int i; + u32* bag_pocket_offsets; + u32* bag_pocket_counts; + if (GAME_FRLG) { + // loop over and decrypt various things + save_offset = 0x3D38 + 4; + for (i = 3; i >= 0; i--) { + ptr_to_xor = (u32*)(&sb1raw[save_offset]); + *ptr_to_xor ^= xor_key; + save_offset += 12; + } + for (i = 0; i <= 0x3f; i++) { + save_offset = 0x1200 + (i*sizeof(u32)); + ptr_to_xor = (u32*)(&sb1raw[save_offset]); + *ptr_to_xor ^= xor_key; + } + // loop over each of the bag pockets and decrypt decrypt decrypt + bag_pocket_offsets = (u32[5]) { 0x310, 0x388, 0x430, 0x464, 0x54C }; + bag_pocket_counts = (u32[5]) { 42, 30, 13, 58, 43 }; + for (i = 0; i < 5; i++) { + for (int bag_i = 0; bag_i < bag_pocket_counts[i]; bag_i++) { + save_offset = bag_pocket_offsets[i] + (sizeof(u32) * bag_i) + 2; + *(u16*)(&sb1raw[save_offset]) ^= xor_key16; + } + } + // decrypt some more stuff + save_offset = 0xaf8; + ptr_to_xor = (u32*)(&sb1raw[save_offset]); + *ptr_to_xor ^= xor_key; + save_offset = 0x290; + ptr_to_xor = (u32*)(&sb1raw[save_offset]); + *ptr_to_xor ^= xor_key; + save_offset = 0x294; + *(u16*)(&sb1raw[save_offset]) ^= xor_key16; + } else { // Emerald + // loop over and decrypt various things + for (i = 0; i <= 0x3f; i++) { + save_offset = 0x159c + (i*sizeof(u32)); + ptr_to_xor = (u32*)(&sb1raw[save_offset]); + *ptr_to_xor ^= xor_key; + } + // loop over each of the bag pockets and decrypt decrypt decrypt + bag_pocket_offsets = (u32[5]) { 0x560, 0x5D8, 0x650, 0x690, 0x790 }; + bag_pocket_counts = (u32[5]) { 30, 30, 16, 64, 46 }; + for (i = 0; i < 5; i++) { + for (int bag_i = 0; bag_i < bag_pocket_counts[i]; bag_i++) { + save_offset = bag_pocket_offsets[i] + (sizeof(u32) * bag_i) + 2; + *(u16*)(&sb1raw[save_offset]) ^= xor_key16; + } + } + // decrypt some more stuff + save_offset = 0x1F4; + ptr_to_xor = (u32*)(&sb1raw[save_offset]); + *ptr_to_xor ^= xor_key; + save_offset = 0x490; + ptr_to_xor = (u32*)(&sb1raw[save_offset]); + *ptr_to_xor ^= xor_key; + save_offset = 0x494; + *(u16*)(&sb1raw[save_offset]) ^= xor_key16; + } + + *xor_key_ptr = 0; + return; + +} + int main(void) { // check the ROM code, make sure this game is supported. u8* ROM = (u8*) 0x8000000; @@ -19,6 +105,7 @@ int main(void) { void(*loadsave)(char a1); void(*mainloop)(); + void(*load_pokemon)(); pSaveBlock1 gSaveBlock1; pSaveBlock2 gSaveBlock2; pSaveBlock3 gSaveBlock3; @@ -34,6 +121,7 @@ int main(void) { gSaveBlock3 = (pSaveBlock3) 0x20300A0; loadsave = (void(*)(char)) 0x8126249; // same for v1.0 + v1.1 mainloop = (void(*)()) 0x80003D9; + load_pokemon = (void(*)()) 0x8047da9; break; case 'FVXA': // Ruby French case 'FPXA': // Sapphire French @@ -42,6 +130,7 @@ int main(void) { gSaveBlock3 = (pSaveBlock3) 0x20300A0; loadsave = (void(*)(char)) 0x8126351; // same for v1.0 + v1.1 mainloop = (void(*)()) 0x80003D9; + load_pokemon = (void(*)()) 0x8047e95; break; case 'IVXA': // Ruby Italian case 'IPXA': // Sapphire Italian @@ -50,6 +139,7 @@ int main(void) { gSaveBlock3 = (pSaveBlock3) 0x20300A0; loadsave = (void(*)(char)) 0x8126249; // same for v1.0 + v1.1 mainloop = (void(*)()) 0x80003D9; + load_pokemon = (void(*)()) 0x8047dbd; break; case 'SVXA': // Ruby Spanish case 'SPXA': // Sapphire Spanish @@ -58,6 +148,7 @@ int main(void) { gSaveBlock3 = (pSaveBlock3) 0x20300A0; loadsave = (void(*)(char)) 0x8126349; // same for v1.0 + v1.1 mainloop = (void(*)()) 0x80003D9; + load_pokemon = (void(*)()) 0x8047ea5; break; case 'EVXA': // Ruby English case 'EPXA': // Sapphire English @@ -68,10 +159,12 @@ int main(void) { switch (ROM[0xBC]) { // version number case 0: loadsave = (void(*)(char)) 0x8125EC9; + load_pokemon = (void(*)()) 0x8047a85; break; case 1: case 2: loadsave = (void(*)(char)) 0x8125EE9; + load_pokemon = (void(*)()) 0x8047aa5; break; default: return 0; // unsupported version @@ -84,6 +177,7 @@ int main(void) { gSaveBlock3 = (pSaveBlock3) 0x202FDBC; loadsave = (void(*)(char)) 0x8120d05; // same for v1.0 + v1.1 mainloop = (void(*)()) 0x80002A9; + load_pokemon = (void(*)()) 0x8044d55; break; /// --- FR/LG --- // In FR/LG, the function that initialises the save-block pointers to default does not set up saveblock3. @@ -95,9 +189,10 @@ int main(void) { gSaveBlock2 = (pSaveBlock2) 0x2024588; gSaveBlock3 = (pSaveBlock3) 0x2029314; *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; - loadsave = (void(*)(char)) ( (gamecode << 8) == 'RPB\x00' ? 0x80da721 : 0x80da6f5 ); + loadsave = (void(*)(char)) ( GAME_FR ? 0x80da721 : 0x80da6f5 ); mainloop = (void(*)()) 0x8000425; titlemid = 0x80791df; + load_pokemon = (void(*)()) 0x804c251; break; case 'FRPB': // FireRed French case 'FGPB': // LeafGreen French @@ -105,9 +200,10 @@ int main(void) { gSaveBlock2 = (pSaveBlock2) 0x2024588; gSaveBlock3 = (pSaveBlock3) 0x2029314; *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; - loadsave = (void(*)(char)) ( (gamecode << 8) == 'RPB\x00' ? 0x80da7e1 : 0x80da7b5 ); + loadsave = (void(*)(char)) ( GAME_FR ? 0x80da7e1 : 0x80da7b5 ); mainloop = (void(*)()) 0x8000417; titlemid = 0x807929f; + load_pokemon = (void(*)()) 0x804c311; break; case 'IRPB': // FireRed Italian case 'IGPB': // LeafGreen Italian @@ -115,9 +211,10 @@ int main(void) { gSaveBlock2 = (pSaveBlock2) 0x2024588; gSaveBlock3 = (pSaveBlock3) 0x2029314; *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; - loadsave = (void(*)(char)) ( (gamecode << 8) == 'RPB\x00' ? 0x80da721 : 0x80da6f5 ); + loadsave = (void(*)(char)) ( GAME_FR ? 0x80da721 : 0x80da6f5 ); mainloop = (void(*)()) 0x8000425; titlemid = 0x80791cb; + load_pokemon = (void(*)()) 0x804c23d; break; case 'SRPB': // FireRed Spanish case 'SGPB': // LeafGreen Spanish @@ -125,9 +222,10 @@ int main(void) { gSaveBlock2 = (pSaveBlock2) 0x2024588; gSaveBlock3 = (pSaveBlock3) 0x2029314; *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; - loadsave = (void(*)(char)) ( (gamecode << 8) == 'RPB\x00' ? 0x80da809 : 0x80da7dd ); + loadsave = (void(*)(char)) ( GAME_FR ? 0x80da809 : 0x80da7dd ); mainloop = (void(*)()) 0x8000417; titlemid = 0x80792b3; + load_pokemon = (void(*)()) 0x804c325; break; case 'ERPB': // FireRed English case 'EGPB': // LeafGreen English @@ -137,14 +235,16 @@ int main(void) { *(pSaveBlock3*)(0x3005010) = gSaveBlock3; switch (ROM[0xBC]) { // version number case 0: - loadsave = (void(*)(char)) ( (gamecode << 8) == 'RPB\x00' ? 0x80da4fd : 0x80da4d1 ); + loadsave = (void(*)(char)) ( GAME_FR ? 0x80da4fd : 0x80da4d1 ); mainloop = (void(*)()) 0x800041b; titlemid = 0x807927b; + load_pokemon = (void(*)()) 0x804c231; break; case 1: - loadsave = (void(*)(char)) ( (gamecode << 8) == 'RPB\x00' ? 0x80da511 : 0x80da4e5 ); + loadsave = (void(*)(char)) ( GAME_FR ? 0x80da511 : 0x80da4e5 ); mainloop = (void(*)()) 0x8000429; titlemid = 0x807928f; + load_pokemon = (void(*)()) 0x804c245; break; default: return 0; // unsupported version @@ -158,9 +258,10 @@ int main(void) { *(pSaveBlock3*)(0x3005050) = gSaveBlock3; switch (ROM[0xBC]) { // version number case 0: - loadsave = (void(*)(char)) ( (gamecode << 8) == 'RPB\x00' ? 0x80db4e5 : 0x80db4b9 ); + loadsave = (void(*)(char)) ( GAME_FR ? 0x80db4e5 : 0x80db4b9 ); mainloop = (void(*)()) 0x800041b; - titlemid = ( (gamecode << 8) == 'RPB\x00' ? 0x8078a0d : 0x8078a0f ); + titlemid = 0x8078a0f; + load_pokemon = (void(*)()) 0x804b9e9; break; case 1: if ((gamecode << 8) == 'GPB\x00') { @@ -171,7 +272,8 @@ int main(void) { } loadsave = (void(*)(char)) 0x80db529; // potential LG1.1 address: 0x80db4fd mainloop = (void(*)()) 0x8000417; - titlemid = 0x8078987; // potential LG1.1 address: 0x8078989 + titlemid = 0x8078987; + load_pokemon = (void(*)()) 0x804b9c5; break; default: return 0; // unsupported version @@ -188,6 +290,7 @@ int main(void) { loadsave = (void(*)(char)) 0x8153075; mainloop = (void(*)()) 0x800042b; titlemid = 0x816fdb5; + load_pokemon = (void(*)()) 0x8076dd5; break; case 'FEPB': // Emerald French gSaveBlock1 = (pSaveBlock1) 0x2025A00; @@ -197,6 +300,7 @@ int main(void) { loadsave = (void(*)(char)) 0x815319d; mainloop = (void(*)()) 0x800042b; titlemid = 0x816fedd; + load_pokemon = (void(*)()) 0x8076dd1; break; case 'IEPB': // Emerald Italian gSaveBlock1 = (pSaveBlock1) 0x2025A00; @@ -206,6 +310,7 @@ int main(void) { loadsave = (void(*)(char)) 0x8153065; mainloop = (void(*)()) 0x800042b; titlemid = 0x816fda5; + load_pokemon = (void(*)()) 0x8076dd5; break; case 'SEPB': // Emerald Spanish gSaveBlock1 = (pSaveBlock1) 0x2025A00; @@ -215,6 +320,7 @@ int main(void) { loadsave = (void(*)(char)) 0x8153175; mainloop = (void(*)()) 0x800042b; titlemid = 0x816feb5; + load_pokemon = (void(*)()) 0x8076dd1; break; case 'EEPB': // Emerald English gSaveBlock1 = (pSaveBlock1) 0x2025A00; @@ -224,6 +330,7 @@ int main(void) { loadsave = (void(*)(char)) 0x81534d1; mainloop = (void(*)()) 0x800042b; titlemid = 0x817014d; + load_pokemon = (void(*)()) 0x8076dd5; break; case 'JEPB': // Emerald Japanese gSaveBlock1 = (pSaveBlock1) 0x20256A4; @@ -233,14 +340,20 @@ int main(void) { loadsave = (void(*)(char)) 0x815340d; mainloop = (void(*)()) 0x800042b; titlemid = 0x816ff45; + load_pokemon = (void(*)()) 0x80767dd; break; default: return 0; // this game isn't supported } loadsave(0); // now the save is loaded, we can do what we want with the loaded blocks. + // first, we're going to want to decrypt the parts that are crypted, if applicable. + decrypt_save_structures(gSaveBlock1,gSaveBlock2,gSaveBlock3); // time to call the payload. payload(gSaveBlock1,gSaveBlock2,gSaveBlock3); + // Now, we better call the function that sets the pokemon-related stuff from the structure elements of the loaded save again. + // Just in case the payload did something with that. + load_pokemon(); // In FR/LG/Emerald, just returning to the game is unwise. // The game reloads the savefile. // In FR/LG, this is done at the title screen after setting ASLR/saveblock-crypto up. (probably because at initial save-load, SaveBlock3 ptr isn't set up lol) @@ -249,8 +362,8 @@ int main(void) { // Easiest way to do this is to call into the middle of the function we want, using an ASM wrapper to set up the stack. // Here goes... if (titlemid) { - // Function reserves an extra 4 bytes of stack space in FireRed, and none in Emerald. - call_into_middle_of_titlescreen_func(titlemid,((gamecode << 8) == 'EPB\x00' ? 0 : 4)); + // Function reserves an extra 4 bytes of stack space in FireRed/LeafGreen, and none in Emerald. + call_into_middle_of_titlescreen_func(titlemid,(GAME_EM ? 0 : 4)); } // Now we've done what we want, time to return to the game. // Can't just return, the game will reload the save. -- cgit 1.4.1