From d4e309bb19b74c4b21ca19952c9b1cdd067c667a Mon Sep 17 00:00:00 2001 From: slipstream/RoL Date: Sun, 19 Feb 2017 01:19:35 +0000 Subject: Initial commit of the fork Forking gba-link-cable-dumper to gba-gen3multiboot --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'README.md') diff --git a/README.md b/README.md index 91d29d0..4483a83 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ -# GBA Link Cable Dumper -A GC and Wii Homebrew App to get GBA BIOS, ROMs and saves via the GC GBA Link Cable. -Save Support based on SendSave by Chishm. -GBA BIOS Dumper by Dark Fader. +# GBA Gen3 Multiboot +A GC and Wii homebrew app that sends a binary to the GBA using the different multiboot protocol used by the third generation of Pokémon games (Ruby, Sapphire, Emerald, FireRed, LeafGreen). # Usage -Just have a GC Controller in Port 1 and a GBA without a game inserted or aborted game launch by holding select+start in Port 2. -The bin, gba and sav files dumped will be placed in a folder called "dumps" on your main device (SD Gecko on gamecube and SD/USB on Wii). Please note that dumping GBA ROMs can take a long time (32mb takes about 48 minutes) because of the cable protocol limitations, a estimation will be displayed on screen before you dump it as a reference. +Have a GC Controller in Port 1 and a GBA with Gen3 game in Port 2. +Put your payload code to do some interesting stuff with the Pokémon game you have in `gba` dir. Example code that changes first character of player name to 'z' on Pokémon Ruby English (v1.0-1.2) provided. +Recompile, send to Wii or GC, turn on your GBA, hope that your code runs after the initial copyright screen of the game. +(Code execution rate is for some reason not 100% reliable, PR to fix would be greatly appreciated. Sometimes KeyC derivation fails, sometimes GBA ignores the sent multiboot image, could be due to failure of a few different sends) + +# Acknowledgements +Thanks to FIX94 for your multiboot game dumper, which the multiboot code is loosely based on (differences in crypto & protocol...) +Without it, this would have taken longer to do than the 2 days or so that it took. \ No newline at end of file -- cgit 1.4.1 From a73a0f06e04ddac7d5a122d9de6aaa43c2cc06f4 Mon Sep 17 00:00:00 2001 From: slipstream/RoL Date: Wed, 22 Feb 2017 02:28:26 +0000 Subject: update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'README.md') diff --git a/README.md b/README.md index 4483a83..bee310a 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,10 @@ A GC and Wii homebrew app that sends a binary to the GBA using the different mul # Usage Have a GC Controller in Port 1 and a GBA with Gen3 game in Port 2. -Put your payload code to do some interesting stuff with the Pokémon game you have in `gba` dir. Example code that changes first character of player name to 'z' on Pokémon Ruby English (v1.0-1.2) provided. +Put your payload code to do some interesting stuff with the Pokémon game you have in `gba` dir. Example code that changes first character of player name to 'z' provided. Recompile, send to Wii or GC, turn on your GBA, hope that your code runs after the initial copyright screen of the game. (Code execution rate is for some reason not 100% reliable, PR to fix would be greatly appreciated. Sometimes KeyC derivation fails, sometimes GBA ignores the sent multiboot image, could be due to failure of a few different sends) # Acknowledgements Thanks to FIX94 for your multiboot game dumper, which the multiboot code is loosely based on (differences in crypto & protocol...) -Without it, this would have taken longer to do than the 2 days or so that it took. \ No newline at end of file +Without it, this would have taken longer to do than the 2 days or so that it took. -- cgit 1.4.1 From e37cc7de9583b00166e96a6632ba25edefff43ca Mon Sep 17 00:00:00 2001 From: slipstream/RoL Date: Sun, 26 Feb 2017 14:27:20 +0000 Subject: Major changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added saveblock structures. - Changed example payload, now it warps to Hall of Fame. - Added helper library for Pokémon manipulation, checksum calculation, etc. - Removed libSave, it's not needed. --- README.md | 2 +- gba/source/libSave.c | 596 ----------------------------------- gba/source/libSave.h | 27 -- gba/source/libpayload.c | 312 +++++++++++++++++++ gba/source/libpayload.h | 160 ++++++++++ gba/source/payload.c | 60 +++- gba/source/payload.h | 1 + gba/source/saveblocks.h | 338 +++++++++++++++++++- gba/source/savestructs.h | 793 +++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1657 insertions(+), 632 deletions(-) delete mode 100644 gba/source/libSave.c delete mode 100644 gba/source/libSave.h create mode 100644 gba/source/libpayload.c create mode 100644 gba/source/libpayload.h create mode 100644 gba/source/savestructs.h (limited to 'README.md') diff --git a/README.md b/README.md index 4483a83..b833c50 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A GC and Wii homebrew app that sends a binary to the GBA using the different mul # Usage Have a GC Controller in Port 1 and a GBA with Gen3 game in Port 2. -Put your payload code to do some interesting stuff with the Pokémon game you have in `gba` dir. Example code that changes first character of player name to 'z' on Pokémon Ruby English (v1.0-1.2) provided. +Put your payload code to do some interesting stuff with the Pokémon game you have in `gba` dir. Example code that warps the character to the Hall of Fame, adding a Bad Egg if the save has no Pokémon provided. Recompile, send to Wii or GC, turn on your GBA, hope that your code runs after the initial copyright screen of the game. (Code execution rate is for some reason not 100% reliable, PR to fix would be greatly appreciated. Sometimes KeyC derivation fails, sometimes GBA ignores the sent multiboot image, could be due to failure of a few different sends) diff --git a/gba/source/libSave.c b/gba/source/libSave.c deleted file mode 100644 index e3bda1d..0000000 --- a/gba/source/libSave.c +++ /dev/null @@ -1,596 +0,0 @@ -/* - libSave - Cartridge backup memory save routines. To use, call the required - routine with a pointer to an appropriately sized array of data to - be read from or written to the cartridge. - Data types are from wintermute's gba_types.h libgba library. - Original file from SendSave by Chishm -*/ - -#include -#include -#include -#include - -#define EEPROM_ADDRESS (0xDFFFF00) -#define REG_EEPROM *(vu16 *)(EEPROM_ADDRESS) -#define REG_DMA3CNT_H *(vu16 *)(REG_BASE + 0x0de) -#define REG_WAITCNT *(vu16 *)(REG_BASE + 0x204) - -//----------------------------------------------------------------------- -// Common EEPROM Routines -//----------------------------------------------------------------------- - -void EEPROM_SendPacket( u16* packet, int size ) -{ - REG_WAITCNT = (REG_WAITCNT & 0xF8FF) | 0x0300; - REG_DMA3SAD = (u32)packet; - REG_DMA3DAD = EEPROM_ADDRESS; - REG_DMA3CNT = 0x80000000 + size; - while((REG_DMA3CNT_H & 0x8000) != 0) ; -} - -void EEPROM_ReceivePacket( u16* packet, int size ) -{ - REG_WAITCNT = (REG_WAITCNT & 0xF8FF) | 0x0300; - REG_DMA3SAD = EEPROM_ADDRESS; - REG_DMA3DAD = (u32)packet; - REG_DMA3CNT = 0x80000000 + size; - while((REG_DMA3CNT_H & 0x8000) != 0) ; -} - -//----------------------------------------------------------------------- -// Routines for 512B EEPROM -//----------------------------------------------------------------------- - -void EEPROM_Read_512B( volatile u8 offset, u8* dest ) // dest must point to 8 bytes -{ - u16 packet[68]; - u8* out_pos; - u16* in_pos; - u8 out_byte; - int byte, bit; - - memset( packet, 0, 68*2 ); - - // Read request - packet[0] = 1; - packet[1] = 1; - - // 6 bits eeprom address (MSB first) - packet[2] = (offset>>5)&1; - packet[3] = (offset>>4)&1; - packet[4] = (offset>>3)&1; - packet[5] = (offset>>2)&1; - packet[6] = (offset>>1)&1; - packet[7] = (offset)&1; - - // End of request - packet[8] = 0; - - // Do transfers - EEPROM_SendPacket( packet, 9 ); - memset( packet, 0, 68*2 ); - EEPROM_ReceivePacket( packet, 68 ); - - // Extract data - in_pos = &packet[4]; - out_pos = dest; - for( byte = 7; byte >= 0; --byte ) - { - out_byte = 0; - for( bit = 7; bit >= 0; --bit ) - { -// out_byte += (*in_pos++)<>5)&1; - packet[3] = (offset>>4)&1; - packet[4] = (offset>>3)&1; - packet[5] = (offset>>2)&1; - packet[6] = (offset>>1)&1; - packet[7] = (offset)&1; - - // Extract data - in_pos = source; - out_pos = &packet[8]; - for( byte = 7; byte >= 0; --byte ) - { - in_byte = *in_pos++; - for( bit = 7; bit >= 0; --bit ) - { - *out_pos++ = (in_byte>>bit)&1; - } - } - - // End of request - packet[72] = 0; - - // Do transfers - EEPROM_SendPacket( packet, 73 ); - - // Wait for EEPROM to finish (should timeout after 10 ms) - while( (REG_EEPROM & 1) == 0 ); -} - -//--------------------------------------------------------------------------------- -void GetSave_EEPROM_512B(u8* data) -//--------------------------------------------------------------------------------- -{ - volatile u8 x; - u32 sleep; - - for (x=0;x<64;++x){ - EEPROM_Read_512B(x,&data[x*8]); - for(sleep=0;sleep<512000;sleep++); - } -} - -//--------------------------------------------------------------------------------- -void PutSave_EEPROM_512B(u8* data) -//--------------------------------------------------------------------------------- -{ - volatile u8 x; - u32 sleep; - - for (x=0;x<64;x++){ - EEPROM_Write_512B(x,&data[x*8]); - for(sleep=0;sleep<512000;sleep++); - } -} -//----------------------------------------------------------------------- -// Routines for 8KB EEPROM -//----------------------------------------------------------------------- - -void EEPROM_Read_8KB( volatile u16 offset, u8* dest ) // dest must point to 8 bytes -{ - u16 packet[68]; - u8* out_pos; - u16* in_pos; - u8 out_byte; - int byte, bit; - - memset( packet, 0, 68*2 ); - - // Read request - packet[0] = 1; - packet[1] = 1; - - // 14 bits eeprom address (MSB first) - packet[2] = (offset>>13)&1; - packet[3] = (offset>>12)&1; - packet[4] = (offset>>11)&1; - packet[5] = (offset>>10)&1; - packet[6] = (offset>>9)&1; - packet[7] = (offset>>8)&1; - packet[8] = (offset>>7)&1; - packet[9] = (offset>>6)&1; - packet[10] = (offset>>5)&1; - packet[11] = (offset>>4)&1; - packet[12] = (offset>>3)&1; - packet[13] = (offset>>2)&1; - packet[14] = (offset>>1)&1; - packet[15] = (offset)&1; - - // End of request - packet[16] = 0; - - // Do transfers - EEPROM_SendPacket( packet, 17 ); - memset( packet, 0, 68*2 ); - EEPROM_ReceivePacket( packet, 68 ); - - // Extract data - in_pos = &packet[4]; - out_pos = dest; - for( byte = 7; byte >= 0; --byte ) - { - out_byte = 0; - for( bit = 7; bit >= 0; --bit ) - { -// out_byte += (*in_pos++)<>13)&1; - packet[3] = (offset>>12)&1; - packet[4] = (offset>>11)&1; - packet[5] = (offset>>10)&1; - packet[6] = (offset>>9)&1; - packet[7] = (offset>>8)&1; - packet[8] = (offset>>7)&1; - packet[9] = (offset>>6)&1; - packet[10] = (offset>>5)&1; - packet[11] = (offset>>4)&1; - packet[12] = (offset>>3)&1; - packet[13] = (offset>>2)&1; - packet[14] = (offset>>1)&1; - packet[15] = (offset)&1; - - // Extract data - in_pos = source; - out_pos = &packet[16]; - for( byte = 7; byte >= 0; --byte ) - { - in_byte = *in_pos++; - for( bit = 7; bit >= 0; --bit ) - { - *out_pos++ = (in_byte>>bit)&1; - } - } - - // End of request - packet[80] = 0; - - // Do transfers - EEPROM_SendPacket( packet, 81 ); - - // Wait for EEPROM to finish (should timeout after 10 ms) - while( (REG_EEPROM & 1) == 0 ); -} - -//--------------------------------------------------------------------------------- -void GetSave_EEPROM_8KB(u8* data) -//--------------------------------------------------------------------------------- -{ - volatile u16 x; - u32 sleep; - - for (x=0;x<1024;x++){ - EEPROM_Read_8KB(x,&data[x*8]); - for(sleep=0;sleep<512000;sleep++); - } - -} - -//--------------------------------------------------------------------------------- -void PutSave_EEPROM_8KB(u8* data) -//--------------------------------------------------------------------------------- -{ - volatile u16 x; - u32 sleep; - - for (x=0;x<1024;x++){ - EEPROM_Write_8KB(x,&data[x*8]); - for(sleep=0;sleep<512000;sleep++); - } -} - -//--------------------------------------------------------------------------------- -void GetSave_SRAM_32KB(u8* data) -//--------------------------------------------------------------------------------- -{ - volatile u8 *sram= (u8*) 0x0E000000; - volatile u16 x; - - for (x = 0; x < 0x8000; ++x) - { - data[x] = sram[x]; - } - -} - -//--------------------------------------------------------------------------------- -void PutSave_SRAM_32KB(u8* data) -//--------------------------------------------------------------------------------- -{ - volatile u8 *sram= (u8*) 0x0E000000; - volatile u16 x; - - for (x = 0; x < 0x8000; ++x) - { - sram[x] = data[x]; - } -} - -//--------------------------------------------------------------------------------- -void GetSave_FLASH_64KB(u8* data) -//--------------------------------------------------------------------------------- -{ - volatile u8 *sram= (u8*) 0x0E000000; - volatile u32 x; - - for (x = 0; x < 0x10000; ++x) - { - data[x] = sram[x]; - } -} - -//--------------------------------------------------------------------------------- -void PutSave_FLASH_64KB(u8* foo) -//--------------------------------------------------------------------------------- -{ - volatile u8 *fctrl0 = (u8*) 0xE005555; - volatile u8 *fctrl1 = (u8*) 0xE002AAA; - volatile u8 *fctrl2 = (u8*) 0xE000000; - - //init flash - *fctrl0 = 0xAA; - *fctrl1 = 0x55; - *fctrl0 = 0x90; - *fctrl2 = 0xF0; - - //erase chip - *fctrl0 = 0xAA; - *fctrl1 = 0x55; - *fctrl0 = 0x80; - *fctrl0 = 0xAA; - *fctrl1 = 0x55; - *fctrl0 = 0x10; - - //wait for erase done - u8 val1; - u8 val2; - val1 = *fctrl2; - val2 = *fctrl2; - while (val1 != val2) { - val1 = *fctrl2; - val2 = *fctrl2; - } - val1 = *fctrl2; - val2 = *fctrl2; - while (val1 != val2) { - val1 = *fctrl2; - val2 = *fctrl2; - } - - volatile u8 *data = fctrl2; - u32 i; - //write data - for (i=0; i<65536; i++) { - *fctrl0 = 0xAA; - *fctrl1 = 0x55; - *fctrl0 = 0xA0; - data [i] = foo [ i ]; - val1 = data [ i ]; - val2 = data [ i ]; - - while (val1 != val2) { - val1 = data [ i ]; - val2 = data [ i ]; - } - val1 = data [ i ]; - val2 = data [ i ]; - while (val1 != val2) { - val1 = data [ i ]; - val2 = data [ i ]; - } - val1 = data [ i ]; - val2 = data [ i ]; - while (val1 != val2) { - val1 = data [ i ]; - val2 = data [ i ]; - } - } -} - -//--------------------------------------------------------------------------------- -void GetSave_FLASH_128KB(u8* data) -//--------------------------------------------------------------------------------- -{ - const u32 size = 0x10000; - - volatile u8 *fctrl0 = (u8*) 0xE005555; - volatile u8 *fctrl1 = (u8*) 0xE002AAA; - volatile u8 *fctrl2 = (u8*) 0xE000000; - volatile u32 i; - volatile u8 *sram= (u8*) 0x0E000000; - - //init flash - *fctrl0 = 0xAA; - *fctrl1 = 0x55; - *fctrl0 = 0x90; - *fctrl2 = 0xF0; - - // read first bank - *fctrl0 = 0xAA; - *fctrl1 = 0x55; - *fctrl0 = 0xB0; - *fctrl2 = 0x00; - - for (i=0; i=0;x--){ - switch (pak[x]) { - case 0x53414C46: - if (pak[x+1] == 0x5F4D3148){ - return 0x20000; // FLASH_128KB - } else if ((pak[x+1] & 0x0000FFFF) == 0x00005F48){ - return 0x10000; // FLASH_64KB - } else if (pak[x+1] == 0x32313548){ - return 0x10000; // FLASH_64KB - } - break; - case 0x52504545: - if ((pak[x+1] & 0x00FFFFFF) == 0x005F4D4F){ - GetSave_EEPROM_8KB(data); - for(i = 8; i < 0x800; i += 8) { - if(memcmp(data, data+i, 8) != 0) - return 0x2000; // EEPROM_8KB - } - return 0x200; // EEPROM_512B - } - break; - case 0x4D415253: - if ((pak[x+1] & 0x000000FF) == 0x0000005F){ - return 0x8000; // SRAM_32KB - } - } - } - return 0; -} - diff --git a/gba/source/libSave.h b/gba/source/libSave.h deleted file mode 100644 index 5ecf822..0000000 --- a/gba/source/libSave.h +++ /dev/null @@ -1,27 +0,0 @@ - - -//--------------------------------------------------------------------------------- -#ifdef __cplusplus -extern "C" { -#endif -//--------------------------------------------------------------------------------- - -void GetSave_EEPROM_512B(u8* data); -void PutSave_EEPROM_512B(u8* data); -void GetSave_EEPROM_8KB(u8* data); -void PutSave_EEPROM_8KB(u8* data); -void GetSave_SRAM_32KB(u8* data); -void PutSave_SRAM_32KB(u8* data); -void GetSave_FLASH_64KB(u8* data); -void PutSave_FLASH_64KB(u8* foo); -void GetSave_FLASH_128KB(u8* data); -void PutSave_FLASH_128KB(u8* foo); - -u32 SaveSize(u8* data, s32 gamesize); - - -//--------------------------------------------------------------------------------- -#ifdef __cplusplus -} // extern "C" -#endif -//--------------------------------------------------------------------------------- diff --git a/gba/source/libpayload.c b/gba/source/libpayload.c new file mode 100644 index 0000000..f13a34f --- /dev/null +++ b/gba/source/libpayload.c @@ -0,0 +1,312 @@ +/* + * Example Gen3-multiboot payload by slipstream/RoL 2017. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * libpayload.c: contains helper functions for the payload + */ + +#include +#include "payload.h" + +// private functions +static void CryptBoxPokemon(struct BoxPokemon* pkm); +static u16 crc16(const u8* data, u16 len); +static u32 CalculateRamScriptDataChecksum(struct RamScriptData* ramScript); + +/** + * Decrypts the substructures of a Pokémon structure, so they can be viewed or modified easily. + * Remember to call EncryptPokemon() afterwards. + * @param struct Pokemon* pkm The Pokémon to decrypt the substructures of. +*/ +void DecryptPokemon(struct Pokemon* pkm) { + struct BoxPokemon* boxMon = &(pkm->box); + CryptBoxPokemon(boxMon); +} + +/** + * Private function that does the actual work of crypting the substructures of a BoxPokemon. + * @param struct BoxPokemon* pkm The BoxPokemon whose substructures will be crypted. +*/ +static void CryptBoxPokemon(struct BoxPokemon* pkm) { + for (u32 i = 0; i < 12; i++) { + pkm->secure.raw[i] ^= (pkm->otId ^ pkm->personality); + } +} + +/** + * Decrypts the substructures of a core Pokémon structure, so they can be viewed or modified easily. + * Used by DecryptPokemon(). + * Remember to call EncryptPokemon() afterwards. + * @param struct BoxPokemon* pkm The BoxPokemon to decrypt the substructures of. +*/ +void DecryptBoxPokemon(struct BoxPokemon* pkm) { + CryptBoxPokemon(pkm); +} + +/** + * Encrypts the substructures of a Pokémon structure, and fixes the checksum. + * Must be used after DecryptPokemon() has been called, otherwise the Pokémon you decrypted and forgot to re-encrypt will become a Bad Egg. + * @param struct Pokemon* pkm The Pokémon to encrypt the substructures and fix the checksum of. +*/ +void EncryptPokemon(struct Pokemon* pkm) { + struct BoxPokemon* boxMon = &(pkm->box); + EncryptBoxPokemon(boxMon); +} + +/** + * Encrypts the substructures of a core Pokémon structure, and fixes the checksum. + * Must be used after DecryptBoxPokemon() has been called, otherwise the Pokémon you decrypted and forgot to re-encrypt will become a Bad Egg. + * @param struct BoxPokemon* pkm The BoxPokemon to encrypt the substructures and fix the checksum of. +*/ +void EncryptBoxPokemon(struct BoxPokemon* pkm) { + FixBoxPokemonChecksum(pkm); + CryptBoxPokemon(pkm); +} + +/** + * Gets a substructure of a Pokémon structure. + * Call DecryptPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokemon to get a substructure of. + * @param u8 substructId The substructure to get. + * @return union PokemonSubstruct* The substructure. +*/ +union PokemonSubstruct* GetPokemonSubstruct(struct Pokemon* pkm,u8 substructId) { + struct BoxPokemon* boxMon = &(pkm->box); + return GetBoxPokemonSubstruct(boxMon,substructId); +} + +/** + * Gets a substructure of a core Pokémon structure. + * Call DecryptBoxPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokemon to get a substructure of. + * @param u8 substructId The substructure to get. + * @return union PokemonSubstruct* The substructure. +*/ +union PokemonSubstruct* GetBoxPokemonSubstruct(struct BoxPokemon* pkm,u8 substructId) { + if (substructId > 3) return NULL; + u32 personality = pkm->personality; + // Staring at the substruct indexes, I noticed the last two columns are the reverse of the first two! + // that is, substructId==2 column is the reverse of substructId==1, substructId==3 is the reverse of substructId==0 + // At least that means there's no need to hardcode all four. + u8 substruct_idxes[2][24] = { + { 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3 }, + { 1, 1, 2, 3, 2, 3, 0, 0, 0, 0, 0, 0, 2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2 } + }; + u32 modulo = (personality % 24); + if (substructId < 2) { + return &( pkm->secure.substructs[ substruct_idxes[substructId][modulo] ] ); + } + return &( pkm->secure.substructs[ substruct_idxes[3 - substructId][23 - modulo] ] ); +} + +/** + * Gets the checksum of a core Pokémon structure. + * @param struct BoxPokemon* pkm The BoxPokemon to calculate the checksum of. + * @return u16 The checksum. +*/ +u16 CalculateBoxPokemonChecksum(struct BoxPokemon* pkm) { + u16 checksum = 0; + union PokemonSubstruct* substructs[4] = { + GetBoxPokemonSubstruct(pkm,0), + GetBoxPokemonSubstruct(pkm,1), + GetBoxPokemonSubstruct(pkm,2), + GetBoxPokemonSubstruct(pkm,3) + }; + + for (int substruct = 0; substruct < 4; substruct++) { + for (int i = 0; i < 6; i++) { + checksum += substructs[substruct]->raw[i]; + } + } + return checksum; +} + +/** + * Fixes the checksum of a core Pokémon structure. + * @param struct BoxPokemon* pkm The BoxPokemon to fix the checksum of. +*/ +void FixBoxPokemonChecksum(struct BoxPokemon* pkm) { + pkm->checksum = CalculateBoxPokemonChecksum(pkm); +} + +/** + * Gets the zeroth substructure ("Growth") of a Pokémon structure. + * Call DecryptPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokémon to get a substructure of. + * @return struct PokemonSubstruct0* The substructure. +*/ +struct PokemonSubstruct0* GetPokemonSubstruct0(struct Pokemon* pkm) { + struct BoxPokemon* boxMon = &(pkm->box); + return GetBoxPokemonSubstruct0(boxMon); +} + +/** + * Gets the zeroth substructure ("Growth") of a core Pokémon structure. + * Call DecryptBoxPokemon() first or the substructure data will be encrypted. + * @param struct BoxPokemon* pkm The BoxPokemon to get the substructure of. + * @return struct PokemonSubstruct0* The substructure. +*/ +struct PokemonSubstruct0* GetBoxPokemonSubstruct0(struct BoxPokemon* pkm) { + return &( GetBoxPokemonSubstruct(pkm,0)->type0 ); +} + +/** + * Gets the first substructure ("Attacks") of a Pokémon structure. + * Call DecryptPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokémon to get a substructure of. + * @return struct PokemonSubstruct0* The substructure. +*/ +struct PokemonSubstruct1* GetPokemonSubstruct1(struct Pokemon* pkm) { + struct BoxPokemon* boxMon = &(pkm->box); + return GetBoxPokemonSubstruct1(boxMon); +} + +/** + * Gets the first substructure ("Attacks") of a core Pokémon structure. + * Call DecryptBoxPokemon() first or the substructure data will be encrypted. + * @param struct BoxPokemon* pkm The BoxPokemon to get the substructure of. + * @return struct PokemonSubstruct1* The substructure. +*/ +struct PokemonSubstruct1* GetBoxPokemonSubstruct1(struct BoxPokemon* pkm) { + return &( GetBoxPokemonSubstruct(pkm,1)->type1 ); +} + +/** + * Gets the second substructure ("EVs & Condition") of a Pokémon structure. + * Call DecryptPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokémon to get a substructure of. + * @return struct PokemonSubstruct0* The substructure. +*/ +struct PokemonSubstruct2* GetPokemonSubstruct2(struct Pokemon* pkm) { + struct BoxPokemon* boxMon = &(pkm->box); + return GetBoxPokemonSubstruct2(boxMon); +} + +/** + * Gets the second substructure ("EVs & Condition") of a core Pokémon structure. + * Call DecryptBoxPokemon() first or the substructure data will be encrypted. + * @param struct BoxPokemon* pkm The BoxPokemon to get the substructure of. + * @return struct PokemonSubstruct2* The substructure. +*/ +struct PokemonSubstruct2* GetBoxPokemonSubstruct2(struct BoxPokemon* pkm) { + return &( GetBoxPokemonSubstruct(pkm,2)->type2 ); +} + +/** + * Gets the third substructure ("Miscellaneous") of a Pokémon structure. + * Call DecryptPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokémon to get a substructure of. + * @return struct PokemonSubstruct0* The substructure. +*/ +struct PokemonSubstruct3* GetPokemonSubstruct3(struct Pokemon* pkm) { + struct BoxPokemon* boxMon = &(pkm->box); + return GetBoxPokemonSubstruct3(boxMon); +} + +/** + * Gets the third substructure ("Miscellaneous") of a core Pokémon structure. + * Call DecryptBoxPokemon() first or the substructure data will be encrypted. + * @param struct BoxPokemon* pkm The BoxPokemon to get the substructure of. + * @return struct PokemonSubstruct3* The substructure. +*/ +struct PokemonSubstruct3* GetBoxPokemonSubstruct3(struct BoxPokemon* pkm) { + return &( GetBoxPokemonSubstruct(pkm,3)->type3 ); +} + +/** + * Calculates the checksum for an R/S-specific Enigma Berry structure in SaveBlock1. + * @param struct EnigmaBerry* enigmaBerry The R/S-specific Enigma Berry to calculate the checksum for. + * @return u32 The checksum. +*/ +u32 CalculateEnigmaBerryChecksumRS(struct EnigmaBerry* enigmaBerry) { + if (!GAME_RS) return 0; // Enigma Berry structure changed in FR/LG, use CalculateEnigmaBerryChecksumFRLGE() instead. + u32 checksum = 0; + // Save off and zero out the original Enigma Berry description pointers. + const u8* description[2] = { enigmaBerry->berry.description1, enigmaBerry->berry.description2 }; + enigmaBerry->berry.description1 = enigmaBerry->berry.description2 = NULL; + u8* enigmaBerryBytes = (u8*)enigmaBerry; + // Calculate the checksum. + for (u32 i = 0; i < 1324; i++) { + checksum += enigmaBerryBytes[i]; + } + // Restore the saved description. + enigmaBerry->berry.description1 = description[0]; + enigmaBerry->berry.description2 = description[1]; + return checksum; +} + +/** + * Calculates the checksum for an FR/LG/Emerald-specific Enigma Berry structure in SaveBlock1. + * @param struct EnigmaBerryFRLGE* enigmaBerry The FR/LG/Emerald-specific Enigma Berry to calculate the checksum for. + * @return u32 The checksum. +*/ +u32 CalculateEnigmaBerryChecksumFRLGE(struct EnigmaBerryFRLGE* enigmaBerry) { + if (GAME_RS) return 0; // Enigma Berry structure is different in R/S, use CalculateEnigmaBerryChecksumRS() instead. + u32 checksum = 0; + u8* enigmaBerryBytes = (u8*)enigmaBerry; + for (int i = 0; i < 0x30; i++) { + checksum += enigmaBerryBytes[i]; + } + return checksum; +} + +/** + * Calculates the checksum for an unspecified Enigma Berry structure in SaveBlock1. (detected by current game) + * @param void* enigmaBerry The Enigma Berry structure to calculate the checksum for. + * @return u32 The checksum. + */ +u32 CalculateEnigmaBerryChecksum(void* enigmaBerry) { + if (GAME_RS) return CalculateEnigmaBerryChecksumRS( (struct EnigmaBerry*) enigmaBerry ); + return CalculateEnigmaBerryChecksumFRLGE( (struct EnigmaBerryFRLGE*) enigmaBerry ); +} + +/** + * Calculates the checksum for a RAM script structure in SaveBlock1. + * @param struct RamScript* ramScript The RAM script structure to calculate the checksum for. + * @return u32 The checksum. + */ +u32 CalculateRamScriptChecksum(struct RamScript* ramScript) { + asm(""); // make sure the call to CalculateRamScriptDataChecksum() is *not* inlined. + // For some reason, if it gets inlined, something happens, and the wrong length is used when checksumming... + return CalculateRamScriptDataChecksum(&ramScript->data); +} + +/** + * Calculates the checksum for a RAM script structure in SaveBlock1. + * @param struct RamScript* ramScript The RAM script structure to calculate the checksum for. + * @return u32 The checksum. + */ +static __attribute__ ((noinline)) u32 CalculateRamScriptDataChecksum(struct RamScriptData* ramScript) { + u32 checksum = 0; + u32 i = 0; + u8* ramScriptBytes = (u8*)(ramScript); + if (GAME_RS) { + for (i = 0; i < sizeof(struct RamScriptData); i++) checksum += ramScriptBytes[i]; + return checksum; + } + + return (u32)crc16(ramScriptBytes,sizeof(struct RamScriptData) + 1); +} + +/** + * Private function to CRC-16, (used by FR/LG/Emerald ramscript checksum). Adapted from http://forums.glitchcity.info/index.php?topic=7114.0 + * @param u8* Data to checksum + * @param u16 Length of data + * @return u16 The checksum +*/ +static u16 crc16(const u8* data, u16 len) { + u16 crc = 0x1121; + for (u16 i = 0; i < len; ++i) { + crc ^= data[i]; + for (u16 j = 0; j < 8; ++j) { + if(crc & 1) { + crc = (crc >> 1) ^ 0x8408; + } else { + crc >>= 1; + } + } + } + return ~crc; +} \ No newline at end of file diff --git a/gba/source/libpayload.h b/gba/source/libpayload.h new file mode 100644 index 0000000..4be6fb3 --- /dev/null +++ b/gba/source/libpayload.h @@ -0,0 +1,160 @@ +/* + * Example Gen3-multiboot payload by slipstream/RoL 2017. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * libpayload.h: header for payload helper functions + */ + +/** + * Decrypts the substructures of a Pokémon structure, so they can be viewed or modified easily. + * Remember to call EncryptPokemon() afterwards. + * @param struct Pokemon* pkm The Pokémon to decrypt the substructures of. +*/ +void DecryptPokemon(struct Pokemon* pkm); + +/** + * Decrypts the substructures of a core Pokémon structure, so they can be viewed or modified easily. + * Used by DecryptPokemon(). + * Remember to call EncryptPokemon() afterwards. + * @param struct BoxPokemon* pkm The BoxPokemon to decrypt the substructures of. +*/ +void DecryptBoxPokemon(struct BoxPokemon* pkm); + +/** + * Encrypts the substructures of a Pokémon structure, and fixes the checksum. + * Must be used after DecryptPokemon() has been called, otherwise the Pokémon you decrypted and forgot to re-encrypt will become a Bad Egg. + * @param struct Pokemon* pkm The Pokémon to encrypt the substructures and fix the checksum of. +*/ +void EncryptPokemon(struct Pokemon* pkm); + +/** + * Encrypts the substructures of a core Pokémon structure, and fixes the checksum. + * Must be used after DecryptBoxPokemon() has been called, otherwise the Pokémon you decrypted and forgot to re-encrypt will become a Bad Egg. + * @param struct BoxPokemon* pkm The BoxPokemon to encrypt the substructures and fix the checksum of. +*/ +void EncryptBoxPokemon(struct BoxPokemon* pkm); + +/** + * Gets a substructure of a Pokémon structure. + * Call DecryptPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokemon to get a substructure of. + * @param u8 substructId The substructure to get. + * @return union PokemonSubstruct* The substructure. +*/ +union PokemonSubstruct* GetPokemonSubstruct(struct Pokemon* pkm,u8 substructId); + +/** + * Gets a substructure of a core Pokémon structure. + * Call DecryptBoxPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokemon to get a substructure of. + * @param u8 substructId The substructure to get. + * @return union PokemonSubstruct* The substructure. +*/ +union PokemonSubstruct* GetBoxPokemonSubstruct(struct BoxPokemon* pkm,u8 substructId); + +/** + * Gets the checksum of a core Pokémon structure. + * @param struct BoxPokemon* pkm The BoxPokemon to calculate the checksum of. + * @return u16 The checksum. +*/ +u16 CalculateBoxPokemonChecksum(struct BoxPokemon* pkm); + +/** + * Fixes the checksum of a core Pokémon structure. + * @param struct BoxPokemon* pkm The BoxPokemon to fix the checksum of. +*/ +void FixBoxPokemonChecksum(struct BoxPokemon* pkm); + +/** + * Gets the zeroth substructure ("Growth") of a Pokémon structure. + * Call DecryptPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokémon to get a substructure of. + * @return struct PokemonSubstruct0* The substructure. +*/ +struct PokemonSubstruct0* GetPokemonSubstruct0(struct Pokemon* pkm); + +/** + * Gets the zeroth substructure ("Growth") of a core Pokémon structure. + * Call DecryptBoxPokemon() first or the substructure data will be encrypted. + * @param struct BoxPokemon* pkm The BoxPokemon to get the substructure of. + * @return struct PokemonSubstruct0* The substructure. +*/ +struct PokemonSubstruct0* GetBoxPokemonSubstruct0(struct BoxPokemon* pkm); + +/** + * Gets the first substructure ("Attacks") of a Pokémon structure. + * Call DecryptPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokémon to get a substructure of. + * @return struct PokemonSubstruct0* The substructure. +*/ +struct PokemonSubstruct1* GetPokemonSubstruct1(struct Pokemon* pkm); + +/** + * Gets the first substructure ("Attacks") of a core Pokémon structure. + * Call DecryptBoxPokemon() first or the substructure data will be encrypted. + * @param struct BoxPokemon* pkm The BoxPokemon to get the substructure of. + * @return struct PokemonSubstruct1* The substructure. +*/ +struct PokemonSubstruct1* GetBoxPokemonSubstruct1(struct BoxPokemon* pkm); + +/** + * Gets the second substructure ("EVs & Condition") of a Pokémon structure. + * Call DecryptPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokémon to get a substructure of. + * @return struct PokemonSubstruct0* The substructure. +*/ +struct PokemonSubstruct2* GetPokemonSubstruct2(struct Pokemon* pkm); + +/** + * Gets the second substructure ("EVs & Condition") of a core Pokémon structure. + * Call DecryptBoxPokemon() first or the substructure data will be encrypted. + * @param struct BoxPokemon* pkm The BoxPokemon to get the substructure of. + * @return struct PokemonSubstruct2* The substructure. +*/ +struct PokemonSubstruct2* GetBoxPokemonSubstruct2(struct BoxPokemon* pkm); + +/** + * Gets the third substructure ("Miscellaneous") of a Pokémon structure. + * Call DecryptPokemon() first or the substructure data will be encrypted. + * @param struct Pokemon* pkm The Pokémon to get a substructure of. + * @return struct PokemonSubstruct0* The substructure. +*/ +struct PokemonSubstruct3* GetPokemonSubstruct3(struct Pokemon* pkm); + +/** + * Gets the third substructure ("Miscellaneous") of a core Pokémon structure. + * Call DecryptBoxPokemon() first or the substructure data will be encrypted. + * @param struct BoxPokemon* pkm The BoxPokemon to get the substructure of. + * @return struct PokemonSubstruct3* The substructure. +*/ +struct PokemonSubstruct3* GetBoxPokemonSubstruct3(struct BoxPokemon* pkm); + +/** + * Calculates the checksum for an R/S-specific Enigma Berry structure in SaveBlock1. + * @param struct EnigmaBerry* enigmaBerry The R/S-specific Enigma Berry to calculate the checksum for. + * @return u32 The checksum. +*/ +u32 CalculateEnigmaBerryChecksumRS(struct EnigmaBerry* enigmaBerry); + +/** + * Calculates the checksum for an FR/LG/Emerald-specific Enigma Berry structure in SaveBlock1. + * @param struct EnigmaBerryFRLGE* enigmaBerry The FR/LG/Emerald-specific Enigma Berry to calculate the checksum for. + * @return u32 The checksum. +*/ +u32 CalculateEnigmaBerryChecksumFRLGE(struct EnigmaBerryFRLGE* enigmaBerry); + +/** + * Calculates the checksum for an unspecified Enigma Berry structure in SaveBlock1. (detected by current game) + * @param void* enigmaBerry The Enigma Berry structure to calculate the checksum for. + * @return u32 The checksum. + */ +u32 CalculateEnigmaBerryChecksum(void* enigmaBerry); + +/** + * Calculates the checksum for a RAM script structure in SaveBlock1. + * @param struct RamScript* ramScript The RAM script structure to calculate the checksum for. + * @return u32 The checksum. + */ +u32 CalculateRamScriptChecksum(struct RamScript* ramScript); \ No newline at end of file diff --git a/gba/source/payload.c b/gba/source/payload.c index 7015774..665af70 100644 --- a/gba/source/payload.c +++ b/gba/source/payload.c @@ -12,7 +12,61 @@ // Your payload code should obviously go into the body of this, the payload function. void payload(pSaveBlock1 SaveBlock1,pSaveBlock2 SaveBlock2,pSaveBlock3 SaveBlock3) { - // This example payload will modify the first character of the player's name. - // It will change to 'z'. You can see the character encoding table here: http://bulbapedia.bulbagarden.net/wiki/Character_encoding_in_Generation_III - SaveBlock2[0] = 0xee; // 'z' + // HoF-warp example payload! + + // Structure offsets are different between R/S, FR/LG, and Emerald. + // The beginning of the structures are the same but do NOT take shortcuts here, it's not good practise. + // If you want to support multiple games, make specific checks for games, use the right structure for each game. + SaveBlock1_RS* sb1rs = &SaveBlock1->rs; + SaveBlock1_FRLG* sb1frlg = &SaveBlock1->frlg; + SaveBlock1_E* sb1e = &SaveBlock1->e; + if (GAME_FRLG) { + sb1frlg->location.mapGroup = 1; // generic indoors? + sb1frlg->location.mapNum = 80; // Hall of Fame + // set coords to the same place that the champions' room script sets them to + sb1frlg->location.x = sb1frlg->pos.x = 5; + sb1frlg->location.y = sb1frlg->pos.y = 12; + sb1frlg->mapDataId = 0xDA; // from HoF map-header + sb1frlg->location.warpId = 0xff; + // make sure the HoF script doesn't crash, which it will do if 0 pokémon + if (sb1frlg->playerPartyCount == 0) { + sb1frlg->playerPartyCount = 1; + // this isn't enough, the heal animation recalculates the party count ignoring empty spots + // so let's hack together one. i don't care about it becoming a bad egg at all. + sb1frlg->playerParty[0].box.personality++; + } + return; + } else if (GAME_RS) { + sb1rs->location.mapGroup = 16; // Ever Grande City + sb1rs->location.mapNum = 11; // Hall of Fame + // set coords to the same place that the champions' room script sets them to + sb1rs->location.x = sb1rs->pos.x = 7; + sb1rs->location.y = sb1rs->pos.y = 16; + sb1rs->mapDataId = 299; // from HoF map-header + sb1rs->location.warpId = 0xff; + // make sure the HoF script doesn't crash, which it will do if 0 pokémon + if (sb1rs->playerPartyCount == 0) { + sb1rs->playerPartyCount = 1; + // this isn't enough, the heal animation recalculates the party count ignoring empty spots + // so let's hack together one. i don't care about it becoming a bad egg at all. + sb1rs->playerParty[0].box.personality++; + } + return; + } else if (GAME_EM) { + sb1e->location.mapGroup = 16; // Ever Grande City + sb1e->location.mapNum = 11; // Hall of Fame + // set coords to the same place that the champions' room script sets them to + sb1e->location.x = sb1e->pos.x = 7; + sb1e->location.y = sb1e->pos.y = 16; + sb1e->mapDataId = 298; // from HoF map-header + sb1e->location.warpId = 0xff; + // make sure the HoF script doesn't crash, which it will do if 0 pokémon + if (sb1e->playerPartyCount == 0) { + sb1e->playerPartyCount = 1; + // this isn't enough, the heal animation recalculates the party count ignoring empty spots + // so let's hack together one. i don't care about it becoming a bad egg at all. + sb1e->playerParty[0].box.personality++; + } + return; + } } \ No newline at end of file diff --git a/gba/source/payload.h b/gba/source/payload.h index d95476c..ea87200 100644 --- a/gba/source/payload.h +++ b/gba/source/payload.h @@ -8,6 +8,7 @@ */ #include "saveblocks.h" +#include "libpayload.h" #define GAME_RUBY (((*(u32*)(0x80000AC)) << 8) == 'VXA\x00') #define GAME_SAPP (((*(u32*)(0x80000AC)) << 8) == 'PXA\x00') diff --git a/gba/source/saveblocks.h b/gba/source/saveblocks.h index 45c127c..cf0a5c3 100644 --- a/gba/source/saveblocks.h +++ b/gba/source/saveblocks.h @@ -7,9 +7,337 @@ * saveblocks.h: describes saveblock structures for all of Gen 3 (yay!) */ -// TODO: this entire file. Placeholders for now, fill in later, if I can be bothered. -// I don't really want to make a fork of pokeruby's headers for now... +// Most of the structures come from pokeruby, FR/LG changes come from my own research / the firered IDB on pokecommunity +#include "savestructs.h" -typedef u8 SaveBlock1, *pSaveBlock1; -typedef u8 SaveBlock2, *pSaveBlock2; -typedef u8 SaveBlock3, *pSaveBlock3; \ No newline at end of file +typedef struct +{ + /*0x00*/ struct Coords16 pos; + /*0x04*/ struct WarpData location; + /*0x0C*/ struct WarpData warp[4]; + /*0x2C*/ u16 battleMusic; + /*0x2E*/ u8 weather; + /*0x2F*/ u8 filler_2F; + /*0x30*/ u8 flashUsed; + /*0x32*/ u16 mapDataId; + /*0x34*/ u16 mapView[0x100]; + /*0x234*/ u8 playerPartyCount; + /*0x238*/ struct Pokemon playerParty[6]; + /*0x490*/ u32 money; + /*0x494*/ u16 coins; + /*0x496*/ u16 registeredItem; // registered for use with SELECT button + /*0x498*/ struct ItemSlot pcItems[50]; + /*0x560*/ struct ItemSlot bagPocket_Items[20]; + /*0x5B0*/ struct ItemSlot bagPocket_KeyItems[20]; + /*0x600*/ struct ItemSlot bagPocket_PokeBalls[16]; + /*0x640*/ struct ItemSlot bagPocket_TMHM[64]; + /*0x740*/ struct ItemSlot bagPocket_Berries[46]; + /*0x7F8*/ struct Pokeblock pokeblocks[40]; + /*0x938*/ u8 unk938[52]; // pokedex related + /*0x96C*/ u16 berryBlenderRecords[3]; + /*0x972*/ u8 filler_972[0x6]; + /*0x978*/ u16 trainerRematchStepCounter; + /*0x97A*/ u8 trainerRematches[100]; + /*0x9E0*/ struct MapObject mapObjects[16]; + /*0xC20*/ struct MapObjectTemplate mapObjectTemplates[64]; + /*0x1220*/ u8 flags[0x120]; + /*0x1340*/ u16 vars[0x100]; + /*0x1540*/ u32 gameStats[50]; + /*0x1608*/ struct BerryTree berryTrees[128]; + /*0x1A08*/ struct SecretBaseRecord secretBases[20]; + /*0x2688*/ u8 playerRoomDecor[12]; + /*0x2694*/ u8 playerRoomDecorPos[12]; + /*0x26A0*/ u8 decorDesk[10]; + /*0x26AA*/ u8 decorChair[10]; + /*0x26B4*/ u8 decorPlant[10]; + /*0x26BE*/ u8 decorOrnament[30]; + /*0x26DC*/ u8 decorMat[30]; + /*0x26FA*/ u8 decorPoster[10]; + /*0x2704*/ u8 decorDoll[40]; + /*0x272C*/ u8 decorCushion[10]; + /*0x2736*/ u8 padding_2736[2]; + /*0x2738*/ TVShow tvShows[24]; + /*0x2A98*/ u8 filler_2A98[0x64]; + /*0x2AFC*/ u16 outbreakPokemonSpecies; + /*0x2AFE*/ u8 outbreakLocationMapNum; + /*0x2AFF*/ u8 outbreakLocationMapGroup; + /*0x2B00*/ u8 outbreakPokemonLevel; + /*0x2B01*/ u8 outbreakUnk1; + /*0x2B02*/ u16 outbreakUnk2; + /*0x2B04*/ u16 outbreakPokemonMoves[4]; + /*0x2B0C*/ u8 outbreakUnk4; + /*0x2B0D*/ u8 outbreakPokemonProbability; + /*0x2B0E*/ u16 outbreakUnk5; + /*0x2B10*/ u8 filler_2B0E[0xC]; + /*0x2B1C*/ u16 unk2B1C[4]; + /*0x2B24*/ u8 filler_2B24[0x28]; + /*0x2B4C*/ struct MailStruct mail[16]; + /*0x2D8C*/ u8 filler_2D8C[0x8]; + /*0x2D94*/ OldMan oldMan; + /*0x2DC0*/ u8 unk_2DC0[0x14]; + /*0x2DD4*/ struct EasyChatPair easyChatPairs[5]; //Dewford trend [0] and some other stuff + /*0x2DFC*/ u8 filler_2DFC[0x100]; + /*0x2EFC*/ struct SB1_2EFC_Struct sb1_2EFC_struct[5]; + /*0x2F9C*/ u8 filler_2F9C[0xA0]; + /*0x303C*/ u8 filler_303C[0x38]; + /*0x3074*/ u8 filler_3074[0x42]; + /*0x30B6*/ u8 filler_30B6; + /*0x30B7*/ u8 filler_30B7[0x59]; + /*0x3110*/ u8 giftRibbons[7]; + /*0x3117*/ u8 filler_311B[0x2D]; + /*0x3144*/ struct Roamer roamer; + /*0x3158*/ u8 filler_3158[0x8]; + /*0x3160*/ struct EnigmaBerry enigmaBerry; // this is actually offset by 0x98 ... + /*0x3690*/ struct RamScript ramScript; + /*0x3A7C*/ u8 filler_3A7C[0x10]; + /*0x3A8C*/ u8 unk3A8C[52]; //pokedex related +} SaveBlock1_RS; + +typedef struct // Don't rely on the commented offsets, they'll be wrong due to elements removed in FR/LG... +{ + /*0x00*/ struct Coords16 pos; + /*0x04*/ struct WarpData location; + /*0x0C*/ struct WarpData warp[4]; + /*0x2C*/ u16 battleMusic; + /*0x2E*/ u8 weather; + /*0x2F*/ u8 filler_2F; + /*0x30*/ u8 flashUsed; + /*0x32*/ u16 mapDataId; +// /*0x34*/ u16 mapView[0x100]; // Not in fr/lg + /*0x234*/ u8 playerPartyCount; + /*0x238*/ struct Pokemon playerParty[6]; + /*0x490*/ u32 money; + /*0x494*/ u16 coins; + /*0x496*/ u16 registeredItem; // registered for use with SELECT button + /*0x498*/ struct ItemSlot pcItems[30]; + /*0x560*/ struct ItemSlot bagPocket_Items[42]; + /*0x5B0*/ struct ItemSlot bagPocket_KeyItems[30]; + /*0x600*/ struct ItemSlot bagPocket_PokeBalls[13]; + /*0x640*/ struct ItemSlot bagPocket_TMHM[58]; + /*0x740*/ struct ItemSlot bagPocket_Berries[43]; +// /*0x7F8*/ struct Pokeblock pokeblocks[40]; // Not in fr/lg + /*0x938*/ u8 unk938[52]; // pokedex related + /*0x96C*/ u8 unk_62C[12]; + /*0x972*/ u8 filler_972[0x6]; + /*0x97A*/ u8 unk_63E[98]; + /*0x9E0*/ struct MapObject mapObjects[16]; + /*0xC20*/ struct MapObjectTemplate mapObjectTemplates[64]; + /*0x1220*/ u8 flags[0x120]; + /*0x1340*/ u16 vars[0x100]; + /*0x1540*/ u32 gameStats[64]; // encrypted with saveblock2 xor-key + struct QuestStory questlog[4]; + u8 messages[12][4]; + struct NPCState npc_states[0x10]; + u8 unk_2f10[112]; + struct DaycarePokemon daycare[2]; + u8 unk_3098[56]; + struct Roamer roamer; + u8 unk_30e4[8]; + /*0x3160*/ struct EnigmaBerryFRLGE enigmaBerry; + u8 unk_3120[0x1C0]; // 4 bytes of CRC16, then 444 bytes of unknown. Mystery Gift related. + u8 unk_32E0[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_1" + u8 unk_3430[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_2" + u8 unk_368C[0x9C]; // padding? doesn't seem to be actually used + struct RamScript ramScript; + u8 unk_3A07[17]; + u8 pokemon_flags_2[52]; + u8 rivalName[8]; + u8 unk_3a54[128]; + u8 words[21][10]; + u8 unk_3ba6[570]; +} __attribute__((aligned(1))) SaveBlock1_FRLG; + +typedef struct // Don't rely on the commented offsets, they'll be wrong due to elements changed/added in Emerald... +{ + /*0x00*/ struct Coords16 pos; + /*0x04*/ struct WarpData location; + /*0x0C*/ struct WarpData warp[4]; + /*0x2C*/ u16 battleMusic; + /*0x2E*/ u8 weather; + /*0x2F*/ u8 filler_2F; + /*0x30*/ u8 flashUsed; + /*0x32*/ u16 mapDataId; + /*0x34*/ u16 mapView[0x100]; + /*0x234*/ u8 playerPartyCount; + /*0x238*/ struct Pokemon playerParty[6]; + /*0x490*/ u32 money; + /*0x494*/ u16 coins; + /*0x496*/ u16 registeredItem; // registered for use with SELECT button + /*0x498*/ struct ItemSlot pcItems[50]; + /*0x560*/ struct ItemSlot bagPocket_Items[30]; + /*0x5D8*/ struct ItemSlot bagPocket_KeyItems[30]; + /*0x650*/ struct ItemSlot bagPocket_PokeBalls[16]; + /*0x690*/ struct ItemSlot bagPocket_TMHM[64]; + /*0x790*/ struct ItemSlot bagPocket_Berries[46]; + /*0x7F8*/ struct Pokeblock pokeblocks[40]; // every offset is shifted by 0x50 from here on thanks to changed bag-counts + /*0x938*/ u8 unk938[52]; // pokedex related + /*0x96C*/ u16 berryBlenderRecords[3]; + /*0x972*/ u8 filler_972[0x6]; + /*0x978*/ u16 trainerRematchStepCounter; + /*0x97A*/ u8 trainerRematches[100]; + /*0x9E0*/ struct MapObject mapObjects[16]; + /*0xC20*/ struct MapObjectTemplate mapObjectTemplates[64]; + /*0x1220*/ u8 flags[0x12C]; + /*0x1340*/ u16 vars[0x100]; // offsets shifted by 0x5C from here on thanks to added flags + /*0x1540*/ u32 gameStats[64]; // encrypted with saveblock2 xor-key + /*0x1608*/ struct BerryTree berryTrees[128]; // offsets shifted by 0x94 from here on thanks to added 14 gamestats + /*0x1A08*/ struct SecretBaseRecord secretBases[20]; + /*0x2688*/ u8 playerRoomDecor[12]; + /*0x2694*/ u8 playerRoomDecorPos[12]; + /*0x26A0*/ u8 decorDesk[10]; + /*0x26AA*/ u8 decorChair[10]; + /*0x26B4*/ u8 decorPlant[10]; + /*0x26BE*/ u8 decorOrnament[30]; + /*0x26DC*/ u8 decorMat[30]; + /*0x26FA*/ u8 decorPoster[10]; + /*0x2704*/ u8 decorDoll[40]; + /*0x272C*/ u8 decorCushion[10]; + // /*0x2736*/ u8 padding_2736[2]; + /*0x2738*/ TVShow tvShows[24]; + /*0x2A98*/ u8 filler_2A98[0x64]; + /*0x2AFC*/ u16 outbreakPokemonSpecies; // offset by 0x94 + /*0x2AFE*/ u8 outbreakLocationMapNum; + /*0x2AFF*/ u8 outbreakLocationMapGroup; + /*0x2B00*/ u8 outbreakPokemonLevel; + /*0x2B01*/ u8 outbreakUnk1; + /*0x2B02*/ u16 outbreakUnk2; + /*0x2B04*/ u16 outbreakPokemonMoves[4]; + /*0x2B0C*/ u8 outbreakUnk4; + /*0x2B0D*/ u8 outbreakPokemonProbability; + /*0x2B0E*/ u16 outbreakUnk5; + /*0x2B10*/ u8 filler_2B0E[0xC]; + /*0x2B1C*/ u16 unk2B1C[4]; + /*0x2B24*/ u8 filler_2B24[0x28]; + /*0x2B4C*/ struct MailStruct mail[16]; // offset by 0x94 + /*0x2D8C*/ u8 filler_2D8C[0x8]; + /*0x2D94*/ OldMan oldMan; + /*0x2DC0*/ u8 unk_2DC0[0x14]; + /*0x2DD4*/ struct EasyChatPair easyChatPairs[5]; //Dewford trend [0] and some other stuff + // /*0x2DFC*/ u8 filler_2DFC[0x100]; + /*0x2EFC*/ struct SB1_2EFC_Struct sb1_2EFC_struct[12]; + u8 unk_3010[0x198]; // no idea if any of this is actually used. + /*0x3110*/ u8 giftRibbons[7]; + /*0x3117*/ u8 filler_311B[0x2D]; + /*0x3144*/ struct Roamer roamer; + /*0x3158*/ u8 filler_3158[0x8]; + /*0x3160*/ struct EnigmaBerryFRLGE enigmaBerry; + u8 unk_322C[0x1C0]; // 4 bytes of CRC16, then 444 bytes of unknown. Mystery Gift related. + u8 unk_33EC[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_1" + u8 unk_353C[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_2" + u8 unk_368C[0x9C]; // padding? doesn't seem to be actually used + /*0x3690*/ struct RamScript ramScript; + /*0x3A7C*/ u8 filler_3A7C[0x10]; + /*0x3A8C*/ u8 unk3A8C[52]; //pokedex related +} SaveBlock1_E; + +// --- + +struct SaveBlock2_Sub +{ + /*0x0000, 0x00A8*/ u8 filler_000[0x4AE]; + /*0x04AE, 0x0556*/ u8 var_4AE; + /*0x04AF, 0x0557*/ u8 var_4AF; + /*0x04B0, 0x0558*/ u16 var_4B0; + /*0x04B2, 0x055A*/ u16 var_4B2; + /*0x04B4, 0x055C*/ u16 var_4B4; + /*0x04B6, 0x055E*/ u16 var_4B6; + /*0x04B8, 0x0560*/ u8 filler_4B8[0x10]; + /*0x04C8, 0x0570*/ u16 var_4C8; + /*0x04CA, 0x0572*/ u16 var_4CA; + /*0x04CC, 0x0574*/ u8 filler_4CC[0x31C]; +}; + +typedef struct +{ + /*0x00*/ u8 playerName[8]; + /*0x08*/ u8 playerGender; // MALE, FEMALE + /*0x09*/ u8 specialSaveWarp; + /*0x0A*/ u8 playerTrainerId[4]; + /*0x0E*/ u16 playTimeHours; + /*0x10*/ u8 playTimeMinutes; + /*0x11*/ u8 playTimeSeconds; + /*0x12*/ u8 playTimeVBlanks; + /*0x13*/ u8 optionsButtonMode; // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A] + /*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST] + u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes + u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO] + u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET] + u16 optionsBattleSceneOff:1; // whether battle animations are disabled + u16 regionMapZoom:1; // whether the map is zoomed in + /*0x18*/ struct Pokedex pokedex; + /*0x90*/ u8 filler_90[0x8]; + /*0x98*/ struct Time localTimeOffset; + /*0xA0*/ struct Time lastBerryTreeUpdate; + /*0xA8*/ struct SaveBlock2_Sub filler_A8; +} SaveBlock2_RS; + +typedef struct +{ + /*0x00*/ u8 playerName[8]; + /*0x08*/ u8 playerGender; // MALE, FEMALE + /*0x09*/ u8 specialSaveWarp; + /*0x0A*/ u8 playerTrainerId[4]; + /*0x0E*/ u16 playTimeHours; + /*0x10*/ u8 playTimeMinutes; + /*0x11*/ u8 playTimeSeconds; + /*0x12*/ u8 playTimeVBlanks; + /*0x13*/ u8 optionsButtonMode; // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A] + /*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST] + u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes + u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO] + u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET] + u16 optionsBattleSceneOff:1; // whether battle animations are disabled + u16 regionMapZoom:1; // whether the map is zoomed in + /*0x18*/ struct Pokedex pokedex; + /*0x90*/ u8 filler_90[0x8]; + /*0x98*/ struct Time localTimeOffset; + /*0xA0*/ struct Time lastBerryTreeUpdate; + /*0xA8*/ struct SaveBlock2_Sub filler_A8; + /*0x890*/ u8 unk_890[8]; + /*0x898*/ u8 mapdata[0x258]; + /*0xaf0*/ u16 field_af0; + /*0xaf2*/ u16 field_af2; + /*0xaf4*/ u16 field_af4; + /*0xaf6*/ u16 field_af6; + /*0xaf8*/ u8 unk_af8[0x428]; + /*0xf20*/ u32 xor_key; +} SaveBlock2_FRLG; + +typedef struct +{ + /*0x00*/ u8 playerName[8]; + /*0x08*/ u8 playerGender; // MALE, FEMALE + /*0x09*/ u8 specialSaveWarp; + /*0x0A*/ u8 playerTrainerId[4]; + /*0x0E*/ u16 playTimeHours; + /*0x10*/ u8 playTimeMinutes; + /*0x11*/ u8 playTimeSeconds; + /*0x12*/ u8 playTimeVBlanks; + /*0x13*/ u8 optionsButtonMode; // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A] + /*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST] + u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes + u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO] + u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET] + u16 optionsBattleSceneOff:1; // whether battle animations are disabled + u16 regionMapZoom:1; // whether the map is zoomed in + /*0x18*/ struct Pokedex pokedex; + /*0x90*/ u8 filler_90[0x8]; + /*0x98*/ struct Time localTimeOffset; + /*0xA0*/ struct Time lastBerryTreeUpdate; + /*0xA8*/ u32 xor_key; + /*0xAC*/ struct SaveBlock2_Sub filler_A8; +} SaveBlock2_E; + +typedef union { + SaveBlock1_RS rs; + SaveBlock1_FRLG frlg; + SaveBlock1_E e; +} SaveBlock1, *pSaveBlock1; + +typedef union { + SaveBlock2_RS rs; + SaveBlock2_FRLG frlg; + SaveBlock2_E e; +} SaveBlock2, *pSaveBlock2; + +typedef struct PokemonStorage SaveBlock3, *pSaveBlock3; \ No newline at end of file diff --git a/gba/source/savestructs.h b/gba/source/savestructs.h new file mode 100644 index 0000000..2bf4d4d --- /dev/null +++ b/gba/source/savestructs.h @@ -0,0 +1,793 @@ +/* + * Example Gen3-multiboot payload by slipstream/RoL 2017. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * saveblocks.h: describes structures used by saveblocks for all of Gen 3 + */ + +// Most of the structures come from pokeruby, FR/LG changes come from my own research / the firered IDB on pokecommunity + +#define POKEMON_NAME_LENGTH 10 +#define OT_NAME_LENGTH 7 +#define TILE_SIZE_4BPP 32 + +struct Coords16 +{ + s16 x; + s16 y; +}; + +struct UCoords16 +{ + u16 x; + u16 y; +}; + +struct SecretBaseRecord +{ + u8 sbr_field_0; // ID? + u8 sbr_field_1_0:4; + u8 gender:1; + u8 sbr_field_1_5:1; + u8 sbr_field_2[7]; // 0xFF bytes? + u8 trainerId[4]; // byte 0 is used for determining trainer class + u16 sbr_field_e; + u8 sbr_field_10; + u8 sbr_field_11; + u8 decorations[16]; + u8 sbr_field_22[16]; + u32 partyPersonality[6]; + u16 partyMoves[6 * 4]; + u16 partySpecies[6]; + u16 partyHeldItems[6]; + u8 partyLevels[6]; + u8 partyEVs[6]; +}; + +typedef void (*TilesetCB)(void); + +struct Tileset +{ + u8 isCompressed; + u8 isSecondary; + void *tiles; + void *palettes; + void *metatiles; + void *metatileAttributes; + TilesetCB callback; +}; + +struct MapData +{ + s32 width; + s32 height; + u16 *border; + u16 *map; + struct Tileset *primaryTileset; + struct Tileset *secondaryTileset; +}; + +struct MapObjectTemplate +{ + /*0x00*/ u8 localId; + /*0x01*/ u8 graphicsId; + /*0x02*/ u8 unk2; + /*0x04*/ s16 x; + /*0x06*/ s16 y; + /*0x08*/ u8 elevation; + /*0x09*/ u8 movementType; + /*0x0A*/ u8 unkA_0:4; + u8 unkA_4:4; + ///*0x0B*/ u8 fillerB[1]; + /*0x0C*/ u16 unkC; + /*0x0E*/ u16 unkE; + /*0x10*/ u8 *script; + /*0x14*/ u16 flagId; + /*0x16*/ u8 filler_16[2]; +}; /*size = 0x18*/ + +struct WarpEvent +{ + s16 x, y; + s8 warpId; + u8 mapGroup; + u8 mapNum; + u8 unk7; +}; + +struct CoordEvent +{ + s16 x, y; + u8 unk4; + u8 filler_5; + u16 trigger; + u16 index; + u8 filler_A[0x2]; + u8 *script; +}; + +struct BgEvent +{ + s16 x, y; + u8 unk4; + u8 kind; + s16 filler_6; + u8 *script; +}; + +struct MapEvents +{ + u8 mapObjectCount; + u8 warpCount; + u8 coordEventCount; + u8 bgEventCount; + + struct MapObjectTemplate *mapObjects; + struct WarpEvent *warps; + struct CoordEvent *coordEvents; + struct BgEvent *bgEvents; +}; + +struct MapConnection +{ + u8 direction; + u32 offset; + u8 mapGroup; + u8 mapNum; +}; + +struct MapConnections +{ + s32 count; + struct MapConnection *connections; +}; + +struct MapHeader +{ + struct MapData *mapData; + struct MapEvents *events; + u8 *mapScripts; + struct MapConnections *connections; + u16 music; + u16 mapDataId; + u8 name; + u8 cave; + u8 weather; + /* 0x17 */ u8 mapType; + u8 filler_18; + u8 escapeRope; + u8 flags; + u8 battleType; +}; + +struct MapObject +{ + /*0x00*/ u32 active:1; + u32 mapobj_bit_1:1; + u32 mapobj_bit_2:1; + u32 mapobj_bit_3:1; + u32 mapobj_bit_4:1; + u32 mapobj_bit_5:1; + u32 mapobj_bit_6:1; + u32 mapobj_bit_7:1; + /*0x01*/ u32 mapobj_bit_8:1; + u32 mapobj_bit_9:1; + u32 mapobj_bit_10:1; + u32 mapobj_bit_11:1; + u32 mapobj_bit_12:1; + u32 mapobj_bit_13:1; + u32 mapobj_bit_14:1; + u32 mapobj_bit_15:1; + /*0x02*/ u32 mapobj_bit_16:1; + u32 mapobj_bit_17:1; + u32 mapobj_bit_18:1; + u32 mapobj_bit_19:1; + u32 mapobj_bit_20:1; + u32 mapobj_bit_21:1; + u32 mapobj_bit_22:1; + u32 mapobj_bit_23:1; + /*0x03*/ u32 mapobj_bit_24:1; + u32 mapobj_bit_25:1; + u32 mapobj_bit_26:1; + u32 mapobj_bit_27:1; + u32 mapobj_bit_28:1; + u32 mapobj_bit_29:1; + u32 mapobj_bit_30:1; + u32 mapobj_bit_31:1; + /*0x04*/ u8 spriteId; + /*0x05*/ u8 graphicsId; + /*0x06*/ u8 animPattern; + /*0x07*/ u8 trainerType; + /*0x08*/ u8 localId; + /*0x09*/ u8 mapNum; + /*0x0A*/ u8 mapGroup; + /*0x0B*/ u8 mapobj_unk_0B_0:4; + u8 elevation:4; + /*0x0C*/ struct Coords16 coords1; + /*0x10*/ struct Coords16 coords2; + /*0x14*/ struct Coords16 coords3; + /*0x18*/ u8 mapobj_unk_18:4; //current direction? + /*0x18*/ u8 placeholder18:4; + /*0x19*/ u8 mapobj_unk_19; + /*0x1A*/ u8 mapobj_unk_1A; + /*0x1B*/ u8 mapobj_unk_1B; + /*0x1C*/ u8 mapobj_unk_1C; + /*0x1D*/ u8 trainerRange_berryTreeId; + /*0x1E*/ u8 mapobj_unk_1E; + /*0x1F*/ u8 mapobj_unk_1F; + /*0x20*/ u8 mapobj_unk_20; + /*0x21*/ u8 mapobj_unk_21; + /*0x22*/ u8 animId; + /*size = 0x24*/ +}; + +struct Berry +{ + const u8 name[7]; + u8 firmness; + u16 size; + u8 maxYield; + u8 minYield; + const u8 *description1; + const u8 *description2; + u8 stageDuration; + u8 spicy; + u8 dry; + u8 sweet; + u8 bitter; + u8 sour; + u8 smoothness; +}; + +struct EnigmaBerry +{ + struct Berry berry; + u8 pic[(6 * 6) * TILE_SIZE_4BPP]; + u16 palette[16]; + u8 description1[45]; + u8 description2[45]; + u8 itemEffect[18]; + u8 holdEffect; + u8 holdEffectParam; + u32 checksum; +}; + +struct BattleEnigmaBerry +{ + u8 name[7]; + u8 holdEffect; + u8 itemEffect[18]; + u8 holdEffectParam; +}; + +struct EnigmaBerryFRLGE { + struct Berry berry; // 0x00 + u8 itemEffect[18]; // 0x1C + u8 holdEffect; // 0x2E + u8 holdEffectParam; // 0x2F + u32 checksum; // 0x30 +}; + +struct __attribute__((aligned(4))) BerryTree +{ + u8 berry; + u8 stage:7; + u8 growthSparkle:1; + u16 secondsUntilNextStage; + u8 berryYield; + u8 regrowthCount:4; + u8 watered1:1; + u8 watered2:1; + u8 watered3:1; + u8 watered4:1; +}; + +struct PokemonSubstruct0 +{ + u16 species; + u16 heldItem; + u32 experience; + u8 ppBonuses; + u8 friendship; +}; + +struct PokemonSubstruct1 +{ + u16 moves[4]; + u8 pp[4]; +}; + +struct PokemonSubstruct2 +{ + u8 hpEV; + u8 attackEV; + u8 defenseEV; + u8 speedEV; + u8 spAttackEV; + u8 spDefenseEV; + u8 cool; + u8 beauty; + u8 cute; + u8 smart; + u8 tough; + u8 sheen; +}; + +struct PokemonSubstruct3 +{ + /* 0x00 */ u8 pokerus; + /* 0x01 */ u8 metLocation; + + /* 0x02 */ u16 metLevel:7; + /* 0x02 */ u16 metGame:4; + /* 0x03 */ u16 pokeball:4; + /* 0x03 */ u16 otGender:1; + + /* 0x04 */ u32 hpIV:5; + /* 0x04 */ u32 attackIV:5; + /* 0x05 */ u32 defenseIV:5; + /* 0x05 */ u32 speedIV:5; + /* 0x05 */ u32 spAttackIV:5; + /* 0x06 */ u32 spDefenseIV:5; + /* 0x07 */ u32 isEgg:1; + /* 0x07 */ u32 altAbility:1; + + /* 0x08 */ u32 coolRibbon:3; + /* 0x08 */ u32 beautyRibbon:3; + /* 0x08 */ u32 cuteRibbon:3; + /* 0x09 */ u32 smartRibbon:3; + /* 0x09 */ u32 toughRibbon:3; + /* 0x09 */ u32 championRibbon:1; + /* 0x0A */ u32 winningRibbon:1; + /* 0x0A */ u32 victoryRibbon:1; + /* 0x0A */ u32 artistRibbon:1; + /* 0x0A */ u32 effortRibbon:1; + /* 0x0A */ u32 giftRibbon1:1; + /* 0x0A */ u32 giftRibbon2:1; + /* 0x0A */ u32 giftRibbon3:1; + /* 0x0A */ u32 giftRibbon4:1; + /* 0x0B */ u32 giftRibbon5:1; + /* 0x0B */ u32 giftRibbon6:1; + /* 0x0B */ u32 giftRibbon7:1; + /* 0x0B */ u32 fatefulEncounter:5; // unused in Ruby/Sapphire, but the high bit must be set for Mew/Deoxys to obey in FR/LG/Emerald +}; + +union PokemonSubstruct +{ + struct PokemonSubstruct0 type0; + struct PokemonSubstruct1 type1; + struct PokemonSubstruct2 type2; + struct PokemonSubstruct3 type3; + u16 raw[6]; +}; + +struct BoxPokemon +{ + u32 personality; + u32 otId; + u8 nickname[POKEMON_NAME_LENGTH]; + u8 language; + u8 isBadEgg:1; + u8 hasSpecies:1; + u8 isEgg:1; + u8 unused:5; + u8 otName[OT_NAME_LENGTH]; + u8 markings; + u16 checksum; + u16 unknown; + + union + { + u32 raw[12]; + union PokemonSubstruct substructs[4]; + } secure; +}; + +struct Pokemon +{ + struct BoxPokemon box; + u32 status; + u8 level; + u8 pokerus; + u16 hp; + u16 maxHP; + u16 attack; + u16 defense; + u16 speed; + u16 spAttack; + u16 spDefense; +}; + +struct UnknownPokemonStruct +{ + u16 species; + u16 heldItem; + u16 moves[4]; + u8 level; + u8 ppBonuses; + u8 hpEV; + u8 attackEV; + u8 defenseEV; + u8 speedEV; + u8 spAttackEV; + u8 spDefenseEV; + u32 otId; + u32 hpIV:5; + u32 attackIV:5; + u32 defenseIV:5; + u32 speedIV:5; + u32 spAttackIV:5; + u32 spDefenseIV:5; + u32 gap:1; + u32 altAbility:1; + u32 personality; + u8 nickname[POKEMON_NAME_LENGTH + 1]; + u8 friendship; +}; + +struct BattlePokemon +{ + /* 0x00 */ u16 species; + /* 0x02 */ u16 attack; + /* 0x04 */ u16 defense; + /* 0x06 */ u16 speed; + /* 0x08 */ u16 spAttack; + /* 0x0A */ u16 spDefense; + /* 0x0C */ u16 moves[4]; + /* 0x14 */ u32 hpIV:5; + /* 0x14 */ u32 attackIV:5; + /* 0x15 */ u32 defenseIV:5; + /* 0x15 */ u32 speedIV:5; + /* 0x16 */ u32 spAttackIV:5; + /* 0x17 */ u32 spDefenseIV:5; + /* 0x17 */ u32 isEgg:1; + /* 0x17 */ u32 altAbility:1; + /* 0x18 */ s8 statStages[8]; + /* 0x20 */ u8 ability; + /* 0x21 */ u8 type1; + /* 0x22 */ u8 type2; + /* 0x23 */ u8 unknown; + /* 0x24 */ u8 pp[4]; + /* 0x28 */ u16 hp; + /* 0x2A */ u8 level; + /* 0x2B */ u8 friendship; + /* 0x2C */ u16 maxHP; + /* 0x2E */ u16 item; + /* 0x30 */ u8 nickname[POKEMON_NAME_LENGTH + 1]; + /* 0x3B */ u8 ppBonuses; + /* 0x3C */ u8 otName[8]; + /* 0x44 */ u32 experience; + /* 0x48 */ u32 personality; + /* 0x4C */ u32 status1; + /* 0x50 */ u32 status2; + /* 0x54 */ u32 otId; +}; + +struct BaseStats +{ + /* 0x00 */ u8 baseHP; + /* 0x01 */ u8 baseAttack; + /* 0x02 */ u8 baseDefense; + /* 0x03 */ u8 baseSpeed; + /* 0x04 */ u8 baseSpAttack; + /* 0x05 */ u8 baseSpDefense; + /* 0x06 */ u8 type1; + /* 0x07 */ u8 type2; + /* 0x08 */ u8 catchRate; + /* 0x09 */ u8 expYield; + /* 0x0A */ u16 evYield_HP:2; + /* 0x0A */ u16 evYield_Attack:2; + /* 0x0A */ u16 evYield_Defense:2; + /* 0x0A */ u16 evYield_Speed:2; + /* 0x0B */ u16 evYield_SpAttack:2; + /* 0x0B */ u16 evYield_SpDefense:2; + /* 0x0C */ u16 item1; + /* 0x0E */ u16 item2; + /* 0x10 */ u8 genderRatio; + /* 0x11 */ u8 eggCycles; + /* 0x12 */ u8 friendship; + /* 0x13 */ u8 growthRate; + /* 0x14 */ u8 eggGroup1; + /* 0x15 */ u8 eggGroup2; + /* 0x16 */ u8 ability1; + /* 0x17 */ u8 ability2; + /* 0x18 */ u8 safariZoneFleeRate; + /* 0x19 */ u8 bodyColor; +}; + +struct BattleMove +{ + u8 effect; + u8 power; + u8 type; + u8 accuracy; + u8 pp; + u8 secondaryEffectChance; + u8 target; + u8 priority; + u32 flags; +}; + +struct PokemonStorage +{ + /* 0x00 */ u8 currentBox; + /* 0x01 */ struct BoxPokemon boxes[14][30]; + u8 boxNames[14][9]; + u8 boxBackground[14]; +}; + +struct WarpData +{ + s8 mapGroup; + s8 mapNum; + s8 warpId; + s16 x, y; +}; + +struct ItemSlot +{ + u16 itemId; + u16 quantity; +}; + +struct __attribute__((aligned(2))) Pokeblock +{ + u8 color; + u8 spicy; + u8 dry; + u8 sweet; + u8 bitter; + u8 sour; + u8 feel; +}; + +struct Roamer +{ + /*0x00*/ u32 ivs; + /*0x04*/ u32 personality; + /*0x08*/ u16 species; + /*0x0A*/ u16 hp; + /*0x0C*/ u8 level; + /*0x0D*/ u8 status; + /*0x0E*/ u8 cool; + /*0x0F*/ u8 beauty; + /*0x10*/ u8 cute; + /*0x11*/ u8 smart; + /*0x12*/ u8 tough; + /*0x13*/ u8 active; +}; + +struct RamScriptData +{ + u8 magic; + u8 mapGroup; + u8 mapNum; + u8 objectId; + u8 script[995]; +} __attribute__((aligned(1),packed)); + +struct RamScript +{ + u32 checksum; + struct RamScriptData data; +} __attribute__((aligned(1),packed)); + +struct SB1_2EFC_Struct +{ + u8 unknown[0x20]; +}; + +struct EasyChatPair +{ + u16 unk0_0:7; + u16 unk0_7:7; + u16 unk1_6:1; + u16 unk2; + u16 words[2]; +}; /*size = 0x8*/ + +struct TVShowCommon { + /*0x00*/ u8 var00; + /*0x01*/ u8 var01; +}; + +struct TVShowFanClubLetter { + /*0x00*/ u8 var00; + /*0x01*/ u8 var01; + /*0x02*/ u16 species; + u8 pad04[12]; + /*0x10*/ u8 playerName[8]; + /*0x18*/ u8 var18; +}; + +struct TVShowRecentHappenings { + /*0x00*/ u8 var00; + /*0x01*/ u8 var01; + /*0x02*/ u16 var02; + u8 pad04[12]; + /*0x10*/ u8 var10[8]; + /*0x18*/ u8 var18; + u8 pad19[10]; +}; + +struct TVShowFanclubOpinions { + /*0x00*/ u8 var00; + /*0x01*/ u8 var01; + /*0x02*/ u16 var02; + /*0x04*/ u8 var04A:4; + u8 var04B:4; + /*0x04*/ u8 var05[8]; + /*0x0D*/ u8 var0D; + /*0x0E*/ u8 var0E; + /*0x0F*/ u8 var0F; + /*0x10*/ u8 var10[8]; +}; + +struct TVShowNameRaterShow { + /*0x00*/ u8 var00; + /*0x01*/ u8 var01; + /*0x02*/ u16 species; + /*0x04*/ u8 pokemonName[11]; + /*0x0F*/ u8 trainerName[11]; + /*0x1A*/ u8 random; + /*0x1B*/ u8 random2; + /*0x1C*/ u16 var1C; + /*0x1E*/ u8 language; + /*0x1F*/ u8 var1F; +}; + +struct TVShowMassOutbreak { + /*0x00*/ u8 var00; + /*0x01*/ u8 var01; + /*0x02*/ u8 var02; + /*0x03*/ u8 var03; + /*0x04*/ u16 moves[4]; + /*0x0C*/ u16 species; + /*0x0E*/ u16 var0E; + /*0x10*/ u8 locationMapNum; + /*0x11*/ u8 locationMapGroup; + /*0x12*/ u8 var12; + /*0x13*/ u8 probability; + /*0x14*/ u8 level; + /*0x15*/ u8 var15; + /*0x16*/ u16 var16; + /*0x18*/ u8 var18; + u8 pad19[11]; +}; + +typedef union TVShow { + struct TVShowCommon common; + struct TVShowFanClubLetter fanclubLetter; + struct TVShowRecentHappenings recentHappenings; + struct TVShowFanclubOpinions fanclubOpinions; + struct TVShowNameRaterShow nameRaterShow; + struct TVShowMassOutbreak massOutbreak; +} TVShow; + +struct __attribute__((aligned(4))) MailStruct +{ + /*0x00*/ u16 words[9]; + /*0x12*/ u8 playerName[8]; + /*0x1A*/ u8 trainerId[4]; + /*0x1E*/ u16 species; + /*0x20*/ u16 itemId; +}; + +struct UnkMauvilleOldManStruct +{ + u8 unk_2D94; + u8 unk_2D95; + /*0x2D96*/ u16 mauvilleOldMan_ecArray[6]; + /*0x2DA2*/ u16 mauvilleOldMan_ecArray2[6]; + /*0x2DAE*/ u8 playerName[8]; + /*0x2DB6*/ u8 filler_2DB6[0x3]; + /*0x2DB9*/ u8 playerTrainerId[4]; + u8 unk_2DBD; + /* size = 0x2C */ +}; + +struct UnkMauvilleOldManStruct2 +{ + u8 filler0; + u8 unk1; + u8 unk2; + u16 mauvilleOldMan_ecArray[10]; + u16 mauvilleOldMan_ecArray2[6]; + u8 fillerF[0x2]; + /* size = 0x2C */ +}; + +typedef union OldMan { + struct UnkMauvilleOldManStruct oldMan1; + struct UnkMauvilleOldManStruct2 oldMan2; +} OldMan; + +struct QuestStoryNPC { + u16 bitfield; + u8 direction; + u8 height; + u8 type_id; + u8 running_behaviour_or_picture_id; + u8 is_trainer; + u8 local_id; + u8 local_mapnumber; + u8 local_mapbank; + u16 x; + u16 y; + u8 sight_distance; + u8 role_from; + u8 unknown_decrement_on_step; + u8 unk_11; + u16 padding_12; +}; + +struct QuestStory { + u8 active; + u8 bank; + u8 map; + u8 warpId; + u16 x; + u16 y; + struct QuestStoryNPC npc[0x10]; + u8 unk_148[0x51f]; +}; + +struct NPCState { + u8 bitfield; + u8 obj_anim_and_vis_control; + u8 unk_2; + u8 unk_3; + u8 oamid; + u8 type_id; + u8 running_behaviour_or_picture_id; + u8 is_trainer; + u8 local_id; + u8 local_mapnumber; + u8 local_mapbank; + u8 height; + struct Coords16 stay_around; + struct Coords16 to; + struct Coords16 from; + u8 direction; + u8 movement_area; + u8 objid_surfing; + u8 objid_1B; + u8 idx_movement_behaviour; + u8 sight_distance; + u8 role_to; + u8 role_from; + u8 unk_20; + u8 unknown_decrement_on_step; + u8 unk_22; + u8 unk_23; +}; + +struct DaycarePokemon { + struct BoxPokemon pokemon; + u8 unk_50[56]; + u32 steps; +}; + + +struct Time +{ + /*0x00*/ s16 days; + /*0x02*/ s8 hours; + /*0x03*/ s8 minutes; + /*0x04*/ s8 seconds; +}; + +struct Pokedex +{ + /*0x00*/ u8 order; + /*0x01*/ u8 unknown1; + /*0x02*/ u8 nationalMagic; // must equal 0xDA in order to have National mode + /*0x03*/ u8 unknown2; + /*0x04*/ u32 unownPersonality; // set when you first see Unown + /*0x08*/ u32 spindaPersonality; // set when you first see Spinda + /*0x0C*/ u32 unknown3; + /*0x10*/ u8 owned[52]; + /*0x44*/ u8 seen[52]; +}; \ No newline at end of file -- cgit 1.4.1 From d642a84189db1ec39514bc1731b2b1ac5c704547 Mon Sep 17 00:00:00 2001 From: slipstream/RoL Date: Sun, 26 Feb 2017 14:33:30 +0000 Subject: Fix readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'README.md') diff --git a/README.md b/README.md index afba524..3f84f0f 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ A GC and Wii homebrew app that sends a binary to the GBA using the different multiboot protocol used by the third generation of Pokémon games (Ruby, Sapphire, Emerald, FireRed, LeafGreen). # Usage -Have a GC Controller in Port 1 and a GBA with Gen3 game in Port 2. -Put your payload code to do some interesting stuff with the Pokémon game you have in `gba` dir. Example code that warps the character to the Hall of Fame, adding a Bad Egg if the save has no Pokémon provided. -Recompile, send to Wii or GC, turn on your GBA, hope that your code runs after the initial copyright screen of the game. +Have a GC Controller in Port 1 and a GBA with Gen3 game in Port 2. +Put your payload code to do some interesting stuff with the Pokémon game you have in `gba` dir. Example code that warps the character to the Hall of Fame, adding a Bad Egg if the save has no Pokémon provided. +Recompile, send to Wii or GC, turn on your GBA, hope that your code runs after the initial copyright screen of the game. (Code execution rate is for some reason not 100% reliable, PR to fix would be greatly appreciated. Sometimes KeyC derivation fails, sometimes GBA ignores the sent multiboot image, could be due to failure of a few different sends) # Acknowledgements -Thanks to FIX94 for your multiboot game dumper, which the multiboot code is loosely based on (differences in crypto & protocol...) +Thanks to FIX94 for your multiboot game dumper, which the multiboot code is loosely based on (differences in crypto & protocol...) Without it, this would have taken longer to do than the 2 days or so that it took. \ No newline at end of file -- cgit 1.4.1