From 57afb4058710a978bd7b07a368125d04378c62f1 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Tue, 11 Jul 2017 16:37:21 -0400 Subject: started tweaking with stuff --- gba/source/gamedata.c | 534 +++++++++++++++++++++++++++++++ gba/source/gamedata.h | 22 ++ gba/source/link.c | 41 +++ gba/source/link.h | 12 + gba/source/main.c | 592 ++++++++++++++++++++++------------- gba/source/saveblocks.h | 343 ++++++++++++++++++++ gba/source/savestructs.h | 793 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 2129 insertions(+), 208 deletions(-) create mode 100644 gba/source/gamedata.c create mode 100644 gba/source/gamedata.h create mode 100644 gba/source/link.c create mode 100644 gba/source/link.h create mode 100644 gba/source/saveblocks.h create mode 100644 gba/source/savestructs.h (limited to 'gba/source') diff --git a/gba/source/gamedata.c b/gba/source/gamedata.c new file mode 100644 index 0000000..19d5100 --- /dev/null +++ b/gba/source/gamedata.c @@ -0,0 +1,534 @@ +/* + * Pokemon Gen III Data Extractor by hatkirby 2017. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + */ +#include "gamedata.h" + +void decryptSaveStructures( + 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; +} + +bool initSaveData( + pSaveBlock1* SaveBlock1, + pSaveBlock2* SaveBlock2, + pSaveBlock3* SaveBlock3) +{ + // check the ROM code, make sure this game is supported. + u8* ROM = (u8*) 0x8000000; + + u32 gamecode = (*(u32*)(&ROM[0xAC])); + + void(*loadsave)(char a1); + //void(*mainloop)(); + //void(*load_pokemon)(); + pSaveBlock1 gSaveBlock1; + pSaveBlock2 gSaveBlock2; + pSaveBlock3 gSaveBlock3; + //u32 titlemid = 0; + + // get the address of the save loading function. + switch (gamecode) + { + // --- R/S --- + case 'DVXA': // Ruby German + case 'DPXA': // Sapphire German + { + // TODO: detect debug ROM? + gSaveBlock1 = (pSaveBlock1) 0x2025734; + gSaveBlock2 = (pSaveBlock2) 0x2024EA4; + 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 + { + gSaveBlock1 = (pSaveBlock1) 0x2025734; + gSaveBlock2 = (pSaveBlock2) 0x2024EA4; + 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 + { + gSaveBlock1 = (pSaveBlock1) 0x2025734; + gSaveBlock2 = (pSaveBlock2) 0x2024EA4; + 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 + { + gSaveBlock1 = (pSaveBlock1) 0x2025734; + gSaveBlock2 = (pSaveBlock2) 0x2024EA4; + 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 + { + gSaveBlock1 = (pSaveBlock1) 0x2025734; + gSaveBlock2 = (pSaveBlock2) 0x2024EA4; + gSaveBlock3 = (pSaveBlock3) 0x20300A0; + //mainloop = (void(*)()) 0x80002A5; + + // version number + switch (ROM[0xBC]) + { + 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 false; // unsupported version + } + } + + break; + } + + case 'JVXA': // Ruby Japanese + case 'JPXA': // Sapphire Japanese + { + gSaveBlock1 = (pSaveBlock1) 0x2025494; + gSaveBlock2 = (pSaveBlock2) 0x2024C04; + 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. + // Which will need to be set up before loading the save if we want boxed Pokémon to not disappear. + // Oh, and loadsave() offset is different between FR and LG... + + case 'DRPB': // FireRed German + case 'DGPB': // LeafGreen German + { + gSaveBlock1 = (pSaveBlock1) 0x202552C; + gSaveBlock2 = (pSaveBlock2) 0x2024588; + gSaveBlock3 = (pSaveBlock3) 0x2029314; + *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; + 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 + { + gSaveBlock1 = (pSaveBlock1) 0x202552C; + gSaveBlock2 = (pSaveBlock2) 0x2024588; + gSaveBlock3 = (pSaveBlock3) 0x2029314; + *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; + 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 + { + gSaveBlock1 = (pSaveBlock1) 0x202552C; + gSaveBlock2 = (pSaveBlock2) 0x2024588; + gSaveBlock3 = (pSaveBlock3) 0x2029314; + *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; + 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 + { + gSaveBlock1 = (pSaveBlock1) 0x202552C; + gSaveBlock2 = (pSaveBlock2) 0x2024588; + gSaveBlock3 = (pSaveBlock3) 0x2029314; + *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; + 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 + { + gSaveBlock1 = (pSaveBlock1) 0x202552C; + gSaveBlock2 = (pSaveBlock2) 0x2024588; + gSaveBlock3 = (pSaveBlock3) 0x2029314; + *(pSaveBlock3*)(0x3005010) = gSaveBlock3; + + // version number + switch (ROM[0xBC]) + { + case 0: + { + loadsave = (void(*)(char)) ( GAME_FR ? 0x80da4fd : 0x80da4d1 ); + //mainloop = (void(*)()) 0x800041b; + //titlemid = 0x807927b; + //load_pokemon = (void(*)()) 0x804c231; + + break; + } + + case 1: + { + loadsave = (void(*)(char)) ( GAME_FR ? 0x80da511 : 0x80da4e5 ); + //mainloop = (void(*)()) 0x8000429; + //titlemid = 0x807928f; + //load_pokemon = (void(*)()) 0x804c245; + + break; + } + + default: + { + return false; // unsupported version + } + } + + break; + } + + case 'JRPB': // FireRed Japanese + case 'JGPB': // LeafGreen Japanese + { + gSaveBlock1 = (pSaveBlock1) 0x202548C; + gSaveBlock2 = (pSaveBlock2) 0x20244E8; + gSaveBlock3 = (pSaveBlock3) 0x202924C; + *(pSaveBlock3*)(0x3005050) = gSaveBlock3; + + // version number + switch (ROM[0xBC]) + { + case 0: + { + loadsave = (void(*)(char)) ( GAME_FR ? 0x80db4e5 : 0x80db4b9 ); + //mainloop = (void(*)()) 0x800041b; + //titlemid = 0x8078a0f; + //load_pokemon = (void(*)()) 0x804b9e9; + + break; + } + + case 1: + { + if ((gamecode << 8) == 'GPB\x00') + { + // LeafGreen v1.1 Japanese is undumped. + // Therefore, it is unsupported. + // I will make guesses at the offsets in the comments, but I will not actually implement them until LeafGreen v1.1 is dumped. + + return false; + } + + loadsave = (void(*)(char)) 0x80db529; // potential LG1.1 address: 0x80db4fd + //mainloop = (void(*)()) 0x8000417; + //titlemid = 0x8078987; + //load_pokemon = (void(*)()) 0x804b9c5; + + break; + } + + default: + { + return false; // unsupported version + } + } + + break; + } + + /// --- Emerald --- + // In Emerald, the saveblock pointer that isn't set up is saveblock1 (in FR/LG it was saveblock3). + // The initial save loading code after the copyright screen is also updated, now it sets up ASLR/crypto here before loading the save. + + case 'DEPB': // Emerald German + { + gSaveBlock1 = (pSaveBlock1) 0x2025A00; + gSaveBlock2 = (pSaveBlock2) 0x2024A54; + gSaveBlock3 = (pSaveBlock3) 0x2029808; + *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1; + loadsave = (void(*)(char)) 0x8153075; + //mainloop = (void(*)()) 0x800042b; + //titlemid = 0x816fdb5; + //load_pokemon = (void(*)()) 0x8076dd5; + + break; + } + + case 'FEPB': // Emerald French + { + gSaveBlock1 = (pSaveBlock1) 0x2025A00; + gSaveBlock2 = (pSaveBlock2) 0x2024A54; + gSaveBlock3 = (pSaveBlock3) 0x2029808; + *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1; + loadsave = (void(*)(char)) 0x815319d; + //mainloop = (void(*)()) 0x800042b; + //titlemid = 0x816fedd; + //load_pokemon = (void(*)()) 0x8076dd1; + + break; + } + + case 'IEPB': // Emerald Italian + { + gSaveBlock1 = (pSaveBlock1) 0x2025A00; + gSaveBlock2 = (pSaveBlock2) 0x2024A54; + gSaveBlock3 = (pSaveBlock3) 0x2029808; + *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1; + loadsave = (void(*)(char)) 0x8153065; + //mainloop = (void(*)()) 0x800042b; + //titlemid = 0x816fda5; + //load_pokemon = (void(*)()) 0x8076dd5; + + break; + } + + case 'SEPB': // Emerald Spanish + { + gSaveBlock1 = (pSaveBlock1) 0x2025A00; + gSaveBlock2 = (pSaveBlock2) 0x2024A54; + gSaveBlock3 = (pSaveBlock3) 0x2029808; + *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1; + loadsave = (void(*)(char)) 0x8153175; + //mainloop = (void(*)()) 0x800042b; + //titlemid = 0x816feb5; + //load_pokemon = (void(*)()) 0x8076dd1; + + break; + } + + case 'EEPB': // Emerald English + { + gSaveBlock1 = (pSaveBlock1) 0x2025A00; + gSaveBlock2 = (pSaveBlock2) 0x2024A54; + gSaveBlock3 = (pSaveBlock3) 0x2029808; + *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1; + loadsave = (void(*)(char)) 0x81534d1; + //mainloop = (void(*)()) 0x800042b; + //titlemid = 0x817014d; + //load_pokemon = (void(*)()) 0x8076dd5; + + break; + } + + case 'JEPB': // Emerald Japanese + { + gSaveBlock1 = (pSaveBlock1) 0x20256A4; + gSaveBlock2 = (pSaveBlock2) 0x20246F8; + gSaveBlock3 = (pSaveBlock3) 0x20294AC; + *(pSaveBlock1*)(0x3005aec) = gSaveBlock1; + loadsave = (void(*)(char)) 0x815340d; + //mainloop = (void(*)()) 0x800042b; + //titlemid = 0x816ff45; + //load_pokemon = (void(*)()) 0x80767dd; + + break; + } + + default: + { + return false; // 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. + decryptSaveStructures(gSaveBlock1,gSaveBlock2,gSaveBlock3); + + *SaveBlock1 = gSaveBlock1; + *SaveBlock2 = gSaveBlock2; + *SaveBlock3 = 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) + // So, better bypass the title screen and get the game to return directly to the Continue/New Game screen. + // In Emerald, the save reload happens after the Continue option was chosen, so we have no choice but to bypass everything and get the game to go straight to the overworld. + // 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/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. + // So let's just call the main-loop directly ;) + // turn the sound back on before we head back to the game + *(vu16 *)(REG_BASE + 0x84) = 0x8f; + // re-enable interrupts + REG_IME = 1; + mainloop(); + // Anything past here will not be executed. + return 0; + */ + return true; +} diff --git a/gba/source/gamedata.h b/gba/source/gamedata.h new file mode 100644 index 0000000..99dfa8e --- /dev/null +++ b/gba/source/gamedata.h @@ -0,0 +1,22 @@ +#ifndef _GAMEDATA_H_ +#define _GAMEDATA_H_ + +#include +#include "saveblocks.h" + +#define GAME_RUBY (((*(u32*)(0x80000AC)) << 8) == 'VXA\x00') +#define GAME_SAPP (((*(u32*)(0x80000AC)) << 8) == 'PXA\x00') +#define GAME_RS ((GAME_RUBY) || (GAME_SAPP)) +#define GAME_FR (((*(u32*)(0x80000AC)) << 8) == 'RPB\x00') +#define GAME_LG (((*(u32*)(0x80000AC)) << 8) == 'GPB\x00') +#define GAME_FRLG ((GAME_FR) || (GAME_LG)) +#define GAME_EM (((*(u32*)(0x80000AC)) << 8) == 'EPB\x00') + +#define LANG_JAPAN ((*(u8*)(0x80000AF)) == 'J') + +bool initSaveData( + pSaveBlock1* SaveBlock1, + pSaveBlock2* SaveBlock2, + pSaveBlock3* SaveBlock3); + +#endif diff --git a/gba/source/link.c b/gba/source/link.c new file mode 100644 index 0000000..e695622 --- /dev/null +++ b/gba/source/link.c @@ -0,0 +1,41 @@ +#include "link.h" + +#define JOY_WRITE 2 +#define JOY_READ 4 +#define JOY_RW 6 + +void waitForWriteAccess() +{ + //while ((REG_HS_CTRL & JOY_READ) == 0); + while ((REG_HS_CTRL & JOY_WRITE) == 0); + REG_HS_CTRL |= JOY_RW; +} + +void waitForAck() +{ + while ((REG_HS_CTRL & JOY_WRITE) == 0); + REG_HS_CTRL |= JOY_RW; + REG_JOYTR = 0; + while ((REG_HS_CTRL & JOY_WRITE) == 0); + REG_HS_CTRL |= JOY_RW; +} + +void sendS32(s32 val) +{ + REG_JOYTR = val; + //waitForWriteAccess(); +} + +void sendU32(u32 val) +{ + REG_JOYTR = val; + //waitForWriteAccess(); +} + +u32 recieveU32() +{ + while ((REG_HS_CTRL & JOY_WRITE) == 0); + REG_HS_CTRL |= JOY_RW; + return REG_JOYRE; +} + diff --git a/gba/source/link.h b/gba/source/link.h new file mode 100644 index 0000000..f18b38a --- /dev/null +++ b/gba/source/link.h @@ -0,0 +1,12 @@ +#ifndef _LINK_H_ +#define _LINK_H_ + +#include + +void waitForWriteAccess(); +void waitForAck(); +void sendS32(s32 val); +void sendU32(u32 val); +u32 recieveU32(); + +#endif diff --git a/gba/source/main.c b/gba/source/main.c index ee94c35..104866a 100644 --- a/gba/source/main.c +++ b/gba/source/main.c @@ -8,6 +8,8 @@ #include #include #include "libSave.h" +#include "gamedata.h" +#include "link.h" #define REG_WAITCNT *(vu16 *)(REG_BASE + 0x204) #define JOY_WRITE 2 @@ -18,224 +20,398 @@ u8 save_data[0x20000] __attribute__ ((section (".sbss"))); s32 getGameSize(void) { - if(*(vu32*)(0x08000004) != 0x51AEFF24) - return -1; - s32 i; - for(i = (1<<20); i < (1<<25); i<<=1) + if(*(vu32*)(0x08000004) != 0x51AEFF24) + return -1; + s32 i; + for(i = (1<<20); i < (1<<25); i<<=1) + { + vu16 *rompos = (vu16*)(0x08000000+i); + int j; + bool romend = true; + for(j = 0; j < 0x1000; j++) + { + if(rompos[j] != j) + { + romend = false; + break; + } + } + if(romend) break; + } + return i; +} + + +// === (from tonc_memdef.h) =========================================== + +// --- REG_DISPCNT defines --- +#define DCNT_MODE0 0x0000 +#define DCNT_MODE1 0x0001 +#define DCNT_MODE2 0x0002 +#define DCNT_MODE3 0x0003 +#define DCNT_MODE4 0x0004 +#define DCNT_MODE5 0x0005 +// layers +#define DCNT_BG0 0x0100 +#define DCNT_BG1 0x0200 +#define DCNT_BG2 0x0400 +#define DCNT_BG3 0x0800 +#define DCNT_OBJ 0x1000 +typedef u16 COLOR; +#define MEM_VRAM 0x06000000 +#define SCREEN_WIDTH 240 +#define vid_mem ((u16*)MEM_VRAM) +static inline void m3_plot(int x, int y, COLOR clr) +{ vid_mem[y*SCREEN_WIDTH+x]= clr; } +static inline COLOR RGB15(u32 red, u32 green, u32 blue) +{ return red | (green<<5) | (blue<<10); } +void plot_sqr(int x, int y, COLOR clr) +{ + /*for (int j=0;j<8; j++) { - vu16 *rompos = (vu16*)(0x08000000+i); - int j; - bool romend = true; - for(j = 0; j < 0x1000; j++) + for (int i=0;i<8; i++) { - if(rompos[j] != j) - { - romend = false; - break; - } + vid_mem[(y*8+j+32)*SCREEN_WIDTH+x*8+i+32] = clr; } - if(romend) break; } - return i; + vid_mem[(y*8+1+32)*SCREEN_WIDTH+x*8+1+32] = RGB15(31,31,31);*/ +} +void m3_fill(COLOR clr) +{ + /*int ii; + u32 *dst= (u32*)vid_mem; + u32 wd= (clr<<16) | clr; + + for(ii=0; ii>24<<24) | (d>>24<<16) | (c>>24<<8) | (b>>24)); - while((REG_HS_CTRL&JOY_READ) == 0) ; - REG_HS_CTRL |= JOY_RW; - } - //restore interrupts - REG_IME = prevIrqMask; - } - REG_JOYTR = 0; - } - Halt(); - } + + + //*(vu16 *)(REG_BASE + 0x84) = 0x8f; + //REG_IME = 1; + // the vblank interrupt must be enabled for VBlankIntrWait() to work + // since the default dispatcher handles the bios flags no vblank handler + // is required + //irqInit(); + //irqEnable(IRQ_VBLANK); + + //consoleDemoInit(); + //REG_JOYTR = 0; + + // ansi escape sequence to set print co-ordinates + // /x1b[line;columnH + //u32 i; + //iprintf("\x1b[9;2HPokemon Gen III Data Extractor\n"); + //iprintf("\x1b[10;4HPlease look at the TV\n"); + + // disable this, needs power + //SNDSTAT = 0; + //SNDBIAS = 0; + + // Set up waitstates for EEPROM access etc. + //REG_WAITCNT = 0x0317; + + //clear out previous messages + REG_HS_CTRL |= JOY_RW; + sendU32(0); + plot_sqr( 4, 5, RGB15( 0,31, 0) ); // or CLR_LIME + while (1) + { + waitForWriteAccess(); + //while (recieveU32() != 0); + //waitForAck(); + plot_sqr( 4, 6, RGB15( 0, 0,31) ); // or CLR_BLUE + // Send game size to acknowledge that an actual cart is inserted. + //s32 gamesize = getGameSize(); + //sendS32(gamesize); + //waitForAck(); + + // If the game size is illegal, start over. + //if (gamesize == -1) + //{ + // sendS32(0); + // + // continue; + //} + + // Identify the inserted game. + if (GAME_RUBY) + { + sendS32(1); + } else if (GAME_SAPP) + { + sendS32(2); + } else if (GAME_FR) + { + sendS32(3); + } else if (GAME_LG) + { + sendS32(4); + } else if (GAME_EM) + { + sendS32(5); + } else { + sendS32(-1); + waitForAck(); + + sendS32(0); + + continue; + } + plot_sqr( 5, 4, RGB15( 31, 0,31) ); + waitForAck(); + plot_sqr( 5, 5, RGB15( 16, 16,16) ); + // Get access to save data. + pSaveBlock1 SaveBlock1; + pSaveBlock2 SaveBlock2; + pSaveBlock3 SaveBlock3; + if (!initSaveData(&SaveBlock1, &SaveBlock2, &SaveBlock3)) + { + // Unsupported game version. + //iprintf("Unsupported game version\n"); + sendS32(-1); + waitForAck(); + + sendS32(0); + + continue; + } + plot_sqr( 5, 6, RGB15( 0, 31,16) ); + sendS32(1); + waitForAck(); + /* + // Send trainer name. + u8* trainerName; + + if (GAME_RS) + { + trainerName = SaveBlock2->rs.playerName; + } else if (GAME_FRLG) + { + trainerName = SaveBlock2->frlg.playerName; + } else if (GAME_EM) + { + trainerName = SaveBlock2->e.playerName; + } + iprintf("%d\n", trainerName[0]); + iprintf("%d\n", trainerName[1]); + iprintf("%d\n", trainerName[2]); + iprintf("%d\n", trainerName[3]); + iprintf("%d\n", trainerName[4]); + iprintf("%d\n", trainerName[5]); + iprintf("%d\n", trainerName[6]); + iprintf("%d\n", trainerName[7]); + + u32 tn1 = + (trainerName[0] << 24) + | (trainerName[1] << 16) + | (trainerName[2] << 8) + | (trainerName[3]); + + u32 tn2 = + (trainerName[4] << 24) + | (trainerName[5] << 16) + | (trainerName[6] << 8) + | (trainerName[7]); + + sendU32(tn1); + waitForAck(); + + sendU32(tn2); + waitForAck(); +*/ + // Send trainer ID. + u8* trainerId = 0; + + if (GAME_RS) + { + trainerId = SaveBlock2->rs.playerTrainerId; + } else if (GAME_FRLG) + { + trainerId = SaveBlock2->frlg.playerTrainerId; + } else if (GAME_EM) + { + trainerId = SaveBlock2->e.playerTrainerId; + } + + u32 tti = + (trainerId[2] << 8) + | (trainerId[3]); + + sendU32(tti); + waitForAck(); + + // Restart, because we're just testing. + sendS32(0); + //continue; + break; + +/* + + + //game in, send header + for(i = 0; i < 0xC0; i+=4) + { + REG_JOYTR = *(vu32*)(0x08000000+i); + while((REG_HS_CTRL&JOY_READ) == 0) ; + REG_HS_CTRL |= JOY_RW; + } + REG_JOYTR = 0; + //wait for other side to choose + while((REG_HS_CTRL&JOY_WRITE) == 0) ; + REG_HS_CTRL |= JOY_RW; + u32 choseval = REG_JOYRE; + if(choseval == 0) + { + REG_JOYTR = 0; + continue; //nothing to read + } + else if(choseval == 1) + { + //disable interrupts + u32 prevIrqMask = REG_IME; + REG_IME = 0; + //dump the game + for(i = 0; i < gamesize; i+=4) + { + REG_JOYTR = *(vu32*)(0x08000000+i); + while((REG_HS_CTRL&JOY_READ) == 0) ; + REG_HS_CTRL |= JOY_RW; + } + //restore interrupts + REG_IME = prevIrqMask; + } + else if(choseval == 2) + { + //disable interrupts + u32 prevIrqMask = REG_IME; + REG_IME = 0; + //backup save + switch (savesize){ + case 0x200: + GetSave_EEPROM_512B(save_data); + break; + case 0x2000: + GetSave_EEPROM_8KB(save_data); + break; + case 0x8000: + GetSave_SRAM_32KB(save_data); + break; + case 0x10000: + GetSave_FLASH_64KB(save_data); + break; + case 0x20000: + GetSave_FLASH_128KB(save_data); + break; + default: + break; + } + //restore interrupts + REG_IME = prevIrqMask; + //say gc side we read it + REG_JOYTR = savesize; + //wait for a cmd receive for safety + while((REG_HS_CTRL&JOY_WRITE) == 0) ; + REG_HS_CTRL |= JOY_RW; + //send the save + for(i = 0; i < savesize; i+=4) + { + REG_JOYTR = *(vu32*)(save_data+i); + while((REG_HS_CTRL&JOY_READ) == 0) ; + REG_HS_CTRL |= JOY_RW; + } + } + else if(choseval == 3 || choseval == 4) + { + REG_JOYTR = savesize; + if(choseval == 3) + { + //receive the save + for(i = 0; i < savesize; i+=4) + { + while((REG_HS_CTRL&JOY_WRITE) == 0) ; + REG_HS_CTRL |= JOY_RW; + *(vu32*)(save_data+i) = REG_JOYRE; + } + } + else + { + //clear the save + for(i = 0; i < savesize; i+=4) + *(vu32*)(save_data+i) = 0; + } + //disable interrupts + u32 prevIrqMask = REG_IME; + REG_IME = 0; + //write it + switch (savesize){ + case 0x200: + PutSave_EEPROM_512B(save_data); + break; + case 0x2000: + PutSave_EEPROM_8KB(save_data); + break; + case 0x8000: + PutSave_SRAM_32KB(save_data); + break; + case 0x10000: + PutSave_FLASH_64KB(save_data); + break; + case 0x20000: + PutSave_FLASH_128KB(save_data); + break; + default: + break; + } + //restore interrupts + REG_IME = prevIrqMask; + //say gc side we're done + REG_JOYTR = 0; + //wait for a cmd receive for safety + while((REG_HS_CTRL&JOY_WRITE) == 0) ; + REG_HS_CTRL |= JOY_RW; + } + REG_JOYTR = 0; + } + } else if(REG_HS_CTRL&JOY_WRITE) + { + REG_HS_CTRL |= JOY_RW; + u32 choseval = REG_JOYRE; + if(choseval == 5) + { + //disable interrupts + u32 prevIrqMask = REG_IME; + REG_IME = 0; + //dump BIOS + for (i = 0; i < 0x4000; i+=4) + { + // the lower bits are inaccurate, so just get it four times :) + u32 a = MidiKey2Freq((WaveData *)(i-4), 180-12, 0) * 2; + u32 b = MidiKey2Freq((WaveData *)(i-3), 180-12, 0) * 2; + u32 c = MidiKey2Freq((WaveData *)(i-2), 180-12, 0) * 2; + u32 d = MidiKey2Freq((WaveData *)(i-1), 180-12, 0) * 2; + REG_JOYTR = ((a>>24<<24) | (d>>24<<16) | (c>>24<<8) | (b>>24)); + while((REG_HS_CTRL&JOY_READ) == 0) ; + REG_HS_CTRL |= JOY_RW; + } + //restore interrupts + REG_IME = prevIrqMask; + } + REG_JOYTR = 0; + }*/ + Halt(); + } } diff --git a/gba/source/saveblocks.h b/gba/source/saveblocks.h new file mode 100644 index 0000000..cf0a5c3 --- /dev/null +++ b/gba/source/saveblocks.h @@ -0,0 +1,343 @@ +/* + * 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 saveblock structures for all of Gen 3 (yay!) + */ + +// Most of the structures come from pokeruby, FR/LG changes come from my own research / the firered IDB on pokecommunity +#include "savestructs.h" + +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