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 --- .gitignore | 7 + build.bat | 2 +- build.sh | 11 + gba/Makefile | 12 +- gba/gba_pkjb.ld | 296 +++++++++++ gba/gba_pkjb.specs | 8 + 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 ++++++++++++++++++++++++++++ gba/start/pkjb_crt0.s | 102 ++++ source/link.c | 16 + source/link.h | 8 + source/main.c | 1287 +++++++++++++++++++++++++++++----------------- 17 files changed, 3415 insertions(+), 671 deletions(-) create mode 100644 .gitignore create mode 100755 build.sh create mode 100644 gba/gba_pkjb.ld create mode 100644 gba/gba_pkjb.specs 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 create mode 100644 gba/start/pkjb_crt0.s create mode 100644 source/link.c create mode 100644 source/link.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac7cf98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.elf +build +data +*.dol +tags +*.o +*.d diff --git a/build.bat b/build.bat index aece78f..c962421 100644 --- a/build.bat +++ b/build.bat @@ -7,4 +7,4 @@ make -f Makefile.gc clean make -f Makefile.gc make -f Makefile.wii clean make -f Makefile.wii -pause \ No newline at end of file +pause diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b521d27 --- /dev/null +++ b/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash +cd gba +make clean +make +cd .. +mkdir data +mv -f gba/gba_pkjb.gba data/gba_mb.gba +make -f Makefile.gc clean +make -f Makefile.gc +make -f Makefile.wii clean +make -f Makefile.wii diff --git a/gba/Makefile b/gba/Makefile index 99dfbb6..e417104 100644 --- a/gba/Makefile +++ b/gba/Makefile @@ -7,6 +7,10 @@ ifeq ($(strip $(DEVKITARM)),) $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) endif +%_pkjb.elf: + @echo linking pkjb + $(LD) -specs=../gba_pkjb.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + include $(DEVKITARM)/gba_rules #--------------------------------------------------------------------------------- @@ -16,7 +20,7 @@ include $(DEVKITARM)/gba_rules # DATA is a list of directories containing data files # INCLUDES is a list of directories containing header files #--------------------------------------------------------------------------------- -TARGET := $(shell basename $(CURDIR))_mb +TARGET := $(shell basename $(CURDIR))_pkjb BUILD := build SOURCES := source DATA := @@ -29,6 +33,7 @@ INCLUDES := ARCH := -mthumb -mthumb-interwork CFLAGS := -g -Wall -O3\ + -Wno-multichar\ -mcpu=arm7tdmi -mtune=arm7tdmi\ -fomit-frame-pointer\ -ffast-math \ @@ -38,8 +43,8 @@ CFLAGS += $(INCLUDE) CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -ASFLAGS := $(ARCH) -LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $@).map +ASFLAGS := -g $(ARCH) +LDFLAGS = $(ARCH) -Wl,-Map,$(notdir $@).map #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project @@ -109,6 +114,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) #--------------------------------------------------------------------------------- $(BUILD): @[ -d $@ ] || mkdir -p $@ + $(CC) -MMD -MP -MF start/pkjb_crt0.d -x assembler-with-cpp $(ASFLAGS) -c start/pkjb_crt0.s -o start/pkjb_crt0.o $(ERROR_FILTER) @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile all : $(BUILD) diff --git a/gba/gba_pkjb.ld b/gba/gba_pkjb.ld new file mode 100644 index 0000000..102cb42 --- /dev/null +++ b/gba/gba_pkjb.ld @@ -0,0 +1,296 @@ +/* Linker Script Original v1.3 by Jeff Frohwein */ +/* v1.0 - Original release */ +/* v1.1 - Added proper .data section support */ +/* v1.2 - Added support for c++ & iwram overlays */ +/* - Major contributions by Jason Wilkins. */ +/* v1.3 - .ewram section now can be used when */ +/* compiling for MULTIBOOT mode. This fixes */ +/* malloc() in DevKitAdvance which depends */ +/* on __eheap_start instead of end to define*/ +/* the starting location of heap space. */ +/* External global variable __gba_iwram_heap*/ +/* support added to allow labels end, _end, */ +/* & __end__ to point to end of iwram or */ +/* the end of ewram. */ +/* Additions by WinterMute */ +/* v1.4 - .sbss section added for unitialised */ +/* data in ewram */ +/* v1.5 - padding section added to stop EZF */ +/* stripping important data */ +/* v1.6 - added memory sections */ + +/* This file is released into the public domain */ +/* for commercial or non-commercial use with no */ +/* restrictions placed upon it. */ + +/* NOTE!!!: This linker script defines the RAM & */ +/* ROM start addresses. In order for it to work */ +/* properly, remove -Ttext and -Tbss linker */ +/* options from your makefile if they are */ +/* present. */ + +/* You can use the following to view section */ +/* addresses in your .elf file: */ +/* objdump -h file.elf */ +/* Please note that empty sections may incorrectly*/ +/* list the lma address as the vma address for */ +/* some versions of objdump. */ + +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) + +MEMORY { + + rom : ORIGIN = 0x08000000, LENGTH = 32M + iwram : ORIGIN = 0x03000000, LENGTH = 32K + ewram : ORIGIN = 0x02000000, LENGTH = 256K +} + + + +__text_start = ORIGIN(ewram); +__eheap_end = ORIGIN(ewram) + LENGTH(ewram); +__iwram_start = ORIGIN(iwram); +__iwram_top = ORIGIN(iwram) + LENGTH(iwram);; + +__sp_irq = __iwram_top - 0x060; +__sp_usr = __sp_irq - 0x0a0; +__irq_flags = 0x03007ff8; + +SECTIONS +{ + . = __text_start; + .init : + { + KEEP (*(.init)) + . = ALIGN(4); + } >ewram =0xff + + .plt : + { + *(.plt) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ewram + + .text ALIGN (4): + { + *(EXCLUDE_FILE (*.iwram*) .text) + *(.text .stub .text.* .gnu.linkonce.t.*) + KEEP (*(.text.*personality*)) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ewram = 0xff + + __text_end = .; + .fini : + { + KEEP (*(.fini)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ewram =0 + + .rodata : + { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r*) + SORT(CONSTRUCTORS) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ewram = 0xff + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >ewram + __exidx_start = .; + .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >ewram + __exidx_end = .; + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { KEEP (*(.preinit_array)) } >ewram = 0xff + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { KEEP (*(.init_array)) } >ewram = 0xff + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { KEEP (*(.fini_array)) } >ewram = 0xff + PROVIDE (__fini_array_end = .); + .ctors : + { + /* gcc uses crtbegin.o to find the start of the constructors, so + we make sure it is first. Because this is a wildcard, it + doesn't matter if the user does not actually link against + crtbegin.o; the linker won't look for a file to match a + wildcard. The wildcard also means that it doesn't matter which + directory crtbegin.o is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ewram = 0 + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ewram = 0 + + .jcr : { KEEP (*(.jcr)) } >ewram + .eh_frame : + { + KEEP (*(.eh_frame)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ewram = 0 + + .gcc_except_table : + { + *(.gcc_except_table) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ewram = 0 + + __iwram_lma = .; + + .iwram __iwram_start : AT (__iwram_lma) + { + __iwram_start__ = ABSOLUTE(.) ; + *(.iwram) + *iwram.*(.text) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __iwram_end__ = ABSOLUTE(.) ; + } >iwram = 0xff + + __data_lma = __iwram_lma + SIZEOF(.iwram) ; + + .bss ALIGN(4) (NOLOAD): + { + __bss_start__ = ABSOLUTE(.); + *(.dynbss) + *(.gnu.linkonce.b*) + *(.bss*) + *(COMMON) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __bss_end__ = ABSOLUTE(.) ; + } + + .data ALIGN(4) : AT (__data_lma) + { + __data_start__ = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + CONSTRUCTORS + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __data_end__ = ABSOLUTE(.); + } >iwram = 0xff + + __iwram_overlay_lma = __data_lma + SIZEOF(.data); + + PROVIDE (edata = .); + __iwram_overlay_start = . ; + + OVERLAY ALIGN(4) : NOCROSSREFS AT (__iwram_overlay_lma) + { + .iwram0 { *(.iwram0) . = ALIGN(4);} + .iwram1 { *(.iwram1) . = ALIGN(4);} + .iwram2 { *(.iwram2) . = ALIGN(4);} + .iwram3 { *(.iwram3) . = ALIGN(4);} + .iwram4 { *(.iwram4) . = ALIGN(4);} + .iwram5 { *(.iwram5) . = ALIGN(4);} + .iwram6 { *(.iwram6) . = ALIGN(4);} + .iwram7 { *(.iwram7) . = ALIGN(4);} + .iwram8 { *(.iwram8) . = ALIGN(4);} + .iwram9 { *(.iwram9) . = ALIGN(4);} + } >iwram = 0xff + + __ewram_lma = LOADADDR(.iwram0) + SIZEOF(.iwram0)+SIZEOF(.iwram1)+SIZEOF(.iwram2)+SIZEOF(.iwram3)+SIZEOF(.iwram4)+SIZEOF(.iwram5)+SIZEOF(.iwram6)+SIZEOF(.iwram7)+SIZEOF(.iwram8)+SIZEOF(.iwram9); + + __iwram_overlay_end = __ewram_lma ; + + /* v1.3 */ + __ewram_start = __ewram_lma ; + + .ewram __ewram_start : AT (__ewram_lma) + { + *(.ewram) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __ewram_end = ABSOLUTE(.); + } >ewram = 0xff + + __ewram_overlay_lma = __ewram_lma + SIZEOF(.ewram); + + .sbss ALIGN(4)(NOLOAD): + { + __sbss_start__ = ABSOLUTE(.); + *(.sbss) + . = ALIGN(4); + __sbss_end__ = ABSOLUTE(.); + __end__ = ABSOLUTE(.); + __eheap_start = ABSOLUTE(.); + } + + OVERLAY ALIGN(4): NOCROSSREFS AT (__ewram_overlay_lma) + { + .ewram0 { *(.ewram0) . = ALIGN(4);} + .ewram1 { *(.ewram1) . = ALIGN(4);} + .ewram2 { *(.ewram2) . = ALIGN(4);} + .ewram3 { *(.ewram3) . = ALIGN(4);} + .ewram4 { *(.ewram4) . = ALIGN(4);} + .ewram5 { *(.ewram5) . = ALIGN(4);} + .ewram6 { *(.ewram6) . = ALIGN(4);} + .ewram7 { *(.ewram7) . = ALIGN(4);} + .ewram8 { *(.ewram8) . = ALIGN(4);} + .ewram9 { *(.ewram9) . = ALIGN(4);} + } >ewram = 0xff + __ewram_overlay_end = ABSOLUTE(.); + + __eheap_start = __ewram_overlay_end ; + + _end = __ewram_overlay_end; + __end__ = __ewram_overlay_end; + __rom_end__ = __ewram_overlay_end; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + .stack 0x80000 : { _stack = .; *(.stack) } + /* These must appear regardless of . */ +} diff --git a/gba/gba_pkjb.specs b/gba/gba_pkjb.specs new file mode 100644 index 0000000..7c9027b --- /dev/null +++ b/gba/gba_pkjb.specs @@ -0,0 +1,8 @@ +%rename link old_link + +*link: +-T ../gba_pkjb.ld%s %(old_link) --gc-sections + +*startfile: +../start/pkjb_crt0%O%s crti%O%s crtbegin%O%s + 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 diff --git a/gba/start/pkjb_crt0.s b/gba/start/pkjb_crt0.s new file mode 100644 index 0000000..0d9d657 --- /dev/null +++ b/gba/start/pkjb_crt0.s @@ -0,0 +1,102 @@ + .section ".init" + .global _start + .align + .arm +@--------------------------------------------------------------------------------- +_start: +@--------------------------------------------------------------------------------- + b rom_header_end + + .fill 156,1,0 @ Nintendo Logo Character Data (8000004h) + .fill 16,1,0 @ Game Title + .byte 0x30,0x31 @ Maker Code (80000B0h) + .byte 0x96 @ Fixed Value (80000B2h) + .byte 0x00 @ Main Unit Code (80000B3h) + .byte 0x00 @ Device Type (80000B4h) + .fill 7,1,0 @ unused + .byte 0x00 @ Software Version No (80000BCh) + .byte 0xf0 @ Complement Check (80000BDh) + .byte 0x00,0x00 @ Checksum (80000BEh) + +@--------------------------------------------------------------------------------- +rom_header_end: +@--------------------------------------------------------------------------------- + b start_vector @ This branch must be here for proper + @ positioning of the following header. + + .GLOBAL __boot_method, __slave_number +@--------------------------------------------------------------------------------- +__boot_method: +@--------------------------------------------------------------------------------- + .byte 0 @ boot method (0=ROM boot, 3=Multiplay boot) +@--------------------------------------------------------------------------------- +__slave_number: +@--------------------------------------------------------------------------------- + .byte 0 @ slave # (1=slave#1, 2=slave#2, 3=slave#3) + + .byte 0 @ reserved + .byte 0 @ reserved + .word 0 @ reserved + .word 0 @ reserved + .word 0 @ reserved + .word 0 @ reserved + .word 0 @ reserved + .word 0 @ reserved + + .fill 4096,1,0 @ 4kb of filler so no useful code gets overwritten when flash bytes get copied over the top. + .global start_vector + .align +@--------------------------------------------------------------------------------- +start_vector: +@--------------------------------------------------------------------------------- + +@--------------------------------------------------------------------------------- +@ Enter Thumb mode +@--------------------------------------------------------------------------------- + add r0, pc, #1 + bx r0 + + .thumb +@ Turn off sound + ldr r1, =0x4000084 + eor r0, r0, r0 + strh r0, [r1] + +@--------------------------------------------------------------------------------- +@ set heap end +@--------------------------------------------------------------------------------- + ldr r1, =fake_heap_end + ldr r0, =__eheap_end + str r0, [r1] +@--------------------------------------------------------------------------------- +@ global constructors +@--------------------------------------------------------------------------------- + ldr r3, =__libc_init_array + push {lr} + bl _blx_r3_stub +@--------------------------------------------------------------------------------- +@ Jump to user code +@--------------------------------------------------------------------------------- + mov r0, #0 @ int argc + mov r1, #0 @ char *argv[] + ldr r3, =main + bl _blx_r3_stub +@; If we're here, turn the sound back on before we return + ldr r1, =0x4000084 + mov r0, #0x8F + strh r0, [r1] +@; Also turn interrupts back on + ldr r1, =0x4000208 + mov r0, #1 + str r0, [r1] + pop {pc} + +@--------------------------------------------------------------------------------- +_blx_r3_stub: +@--------------------------------------------------------------------------------- + bx r3 + + .align + .pool + .end + diff --git a/source/link.c b/source/link.c new file mode 100644 index 0000000..7229191 --- /dev/null +++ b/source/link.c @@ -0,0 +1,16 @@ +#include "link.h" + +u32 waitForButtons(u32 mask) +{ + for (;;) + { + PAD_ScanPads(); + VIDEO_WaitVSync(); + + u32 btns = PAD_ButtonsDown(0); + if (btns & mask) + { + return btns; + } + } +} diff --git a/source/link.h b/source/link.h new file mode 100644 index 0000000..4e536b4 --- /dev/null +++ b/source/link.h @@ -0,0 +1,8 @@ +#ifndef _LINK_H_ +#define _LINK_H_ + +#include + +u32 waitForButtons(u32 mask); + +#endif diff --git a/source/main.c b/source/main.c index 7fd5683..79fd2fc 100644 --- a/source/main.c +++ b/source/main.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2017 hatkirby * Copyright (C) 2016 FIX94 * * This software may be modified and distributed under the terms @@ -15,6 +16,7 @@ #include #include #include +#include "link.h" //from my tests 50us seems to be the lowest //safe si transfer delay in between calls @@ -25,11 +27,12 @@ extern u32 gba_mb_gba_size; void printmain() { - printf("\x1b[2J"); - printf("\x1b[37m"); - printf("GBA Link Cable Dumper v1.6 by FIX94\n"); - printf("Save Support based on SendSave by Chishm\n"); - printf("GBA BIOS Dumper by Dark Fader\n \n"); + printf("\x1b[2J"); + printf("\x1b[37m"); + printf("Pokemon Gen III Data Extractor by hatkirby\n"); + printf("Based on GBA Link Cable Dumper v1.6 by FIX94\n"); + printf("Save Support based on SendSave by Chishm\n"); + printf("Save Structure based on gba-gen3multiboot by slipstream/RoL\n"); } u8 *resbuf,*cmdbuf; @@ -37,520 +40,886 @@ u8 *resbuf,*cmdbuf; volatile u32 transval = 0; void transcb(s32 chan, u32 ret) { - transval = 1; + transval = 1; } volatile u32 resval = 0; void acb(s32 res, u32 val) { - resval = val; + resval = val; } - +/* unsigned int docrc(u32 crc, u32 val) { - int i; - for(i = 0; i < 0x20; i++) - { - if((crc^val)&1) - { - crc>>=1; - crc^=0xa1c1; - } - else - crc>>=1; - val>>=1; + int i; + for (i = 0; i < 0x20; i++) + { + if ((crc ^ val) & 1) + { + crc>>=1; + crc^=0xa1c1; + } else { + crc>>=1; + } + + val>>=1; + } + + return crc; +}*/ +unsigned int docrc(u32 crc,u32 val) { + u32 result; + + result = val ^ crc; + for (int i = 0; i < 0x20; i++) { + if (result & 1) { + result >>= 1; + result ^= 0xA1C1; + } else result >>= 1; } - return crc; + return result; } -void endproc() -{ - printf("Start pressed, exit\n"); - VIDEO_WaitVSync(); - VIDEO_WaitVSync(); - exit(0); -} void fixFName(char *str) { - u8 i = 0; - for(i = 0; i < strlen(str); ++i) - { - if(str[i] < 0x20 || str[i] > 0x7F) - str[i] = '_'; - else switch(str[i]) - { - case '\\': - case '/': - case ':': - case '*': - case '?': - case '\"': - case '<': - case '>': - case '|': - str[i] = '_'; - break; - default: - break; - } - } + u8 i = 0; + for (i = 0; i < strlen(str); ++i) + { + if (str[i] < 0x20 || str[i] > 0x7F) + { + str[i] = '_'; + } else { + switch (str[i]) + { + case '\\': + case '/': + case ':': + case '*': + case '?': + case '\"': + case '<': + case '>': + case '|': + str[i] = '_'; + break; + default: + break; + } + } + } } + unsigned int calckey(unsigned int size) { - unsigned int ret = 0; - size=(size-0x200) >> 3; - int res1 = (size&0x3F80) << 1; - res1 |= (size&0x4000) << 2; - res1 |= (size&0x7F); - res1 |= 0x380000; - int res2 = res1; - res1 = res2 >> 0x10; - int res3 = res2 >> 8; - res3 += res1; - res3 += res2; - res3 <<= 24; - res3 |= res2; - res3 |= 0x80808080; - - if((res3&0x200) == 0) - { - ret |= (((res3)&0xFF)^0x4B)<<24; - ret |= (((res3>>8)&0xFF)^0x61)<<16; - ret |= (((res3>>16)&0xFF)^0x77)<<8; - ret |= (((res3>>24)&0xFF)^0x61); - } - else - { - ret |= (((res3)&0xFF)^0x73)<<24; - ret |= (((res3>>8)&0xFF)^0x65)<<16; - ret |= (((res3>>16)&0xFF)^0x64)<<8; - ret |= (((res3>>24)&0xFF)^0x6F); - } - return ret; + unsigned int ret = 0; + size= (size - 0x200) >> 3; + int res1 = (size & 0x3F80) << 1; + res1 |= (size & 0x4000) << 2; + res1 |= (size & 0x7F); + res1 |= 0x380000; + int res2 = res1; + res1 = res2 >> 0x10; + int res3 = res2 >> 8; + res3 += res1; + res3 += res2; + res3 <<= 24; + res3 |= res2; + res3 |= 0x80808080; + + if ((res3 & 0x200) == 0) + { + ret |= (((res3) & 0xFF) ^ 0x4B) << 24; + ret |= (((res3>>8) & 0xFF) ^ 0x61) << 16; + ret |= (((res3>>16) & 0xFF) ^ 0x77) << 8; + ret |= (((res3>>24) & 0xFF) ^ 0x61); + } else { + ret |= (((res3) & 0xFF) ^ 0x73) << 24; + ret |= (((res3>>8) & 0xFF) ^ 0x65) << 16; + ret |= (((res3>>16) & 0xFF) ^ 0x64) << 8; + ret |= (((res3>>24) & 0xFF) ^ 0x6F); + } + + return ret; } + void doreset() { - cmdbuf[0] = 0xFF; //reset - transval = 0; - SI_Transfer(1,cmdbuf,1,resbuf,3,transcb,SI_TRANS_DELAY); - while(transval == 0) ; + cmdbuf[0] = 0xFF; //reset + transval = 0; + SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY); + + while (transval == 0); } + void getstatus() { - cmdbuf[0] = 0; //status - transval = 0; - SI_Transfer(1,cmdbuf,1,resbuf,3,transcb,SI_TRANS_DELAY); - while(transval == 0) ; + cmdbuf[0] = 0; //status + transval = 0; + SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY); + + while (transval == 0); +} + +void endproc() +{ + doreset(); + printf("Start pressed, exit\n"); + VIDEO_WaitVSync(); + VIDEO_WaitVSync(); + exit(0); +} + +void fsleep(int i) +{ + sleep(i); + /*PAD_ScanPads(); + if (PAD_ButtonsDown(0) & PAD_BUTTON_START) + { + getstatus(); + endproc(); + }*/ } + u32 recv() { - memset(resbuf,0,32); - cmdbuf[0]=0x14; //read - transval = 0; - SI_Transfer(1,cmdbuf,1,resbuf,5,transcb,SI_TRANS_DELAY); - while(transval == 0) ; - return *(vu32*)resbuf; + memset(resbuf,0,32); + cmdbuf[0]=0x14; //read + transval = 0; + SI_Transfer(1, cmdbuf, 1, resbuf, 5, transcb, SI_TRANS_DELAY); + + while (transval == 0); + printf("%08lx\n", *(vu32*)resbuf); + return *(vu32*)resbuf; } + void send(u32 msg) { - cmdbuf[0]=0x15;cmdbuf[1]=(msg>>0)&0xFF;cmdbuf[2]=(msg>>8)&0xFF; - cmdbuf[3]=(msg>>16)&0xFF;cmdbuf[4]=(msg>>24)&0xFF; - transval = 0; - resbuf[0] = 0; - SI_Transfer(1,cmdbuf,5,resbuf,1,transcb,SI_TRANS_DELAY); - while(transval == 0) ; + cmdbuf[0] = 0x15; + cmdbuf[1] = (msg >> 0) & 0xFF; + cmdbuf[2] = (msg >> 8) & 0xFF; + cmdbuf[3] = (msg >> 16) & 0xFF; + cmdbuf[4] = (msg >> 24) & 0xFF; + + transval = 0; + resbuf[0] = 0; + SI_Transfer(1, cmdbuf, 5, resbuf, 1, transcb, SI_TRANS_DELAY); + + while (transval == 0); } + bool dirExists(const char *path) { - DIR *dir; - dir = opendir(path); - if(dir) - { - closedir(dir); - return true; - } - return false; + DIR *dir = opendir(path); + if (dir) + { + closedir(dir); + + return true; + } + + return false; } + void createFile(const char *path, size_t size) { - int fd = open(path, O_WRONLY|O_CREAT); - if(fd >= 0) - { - ftruncate(fd, size); - close(fd); - } + int fd = open(path, O_WRONLY | O_CREAT); + if (fd >= 0) + { + ftruncate(fd, size); + close(fd); + } } + void warnError(char *msg) { - puts(msg); - VIDEO_WaitVSync(); - VIDEO_WaitVSync(); - sleep(2); + puts(msg); + VIDEO_WaitVSync(); + VIDEO_WaitVSync(); + sleep(2); } + void fatalError(char *msg) { - puts(msg); - VIDEO_WaitVSync(); - VIDEO_WaitVSync(); - sleep(5); - exit(0); + puts(msg); + VIDEO_WaitVSync(); + VIDEO_WaitVSync(); + sleep(5); + exit(0); } -int main(int argc, char *argv[]) -{ - void *xfb = NULL; - GXRModeObj *rmode = NULL; - VIDEO_Init(); - rmode = VIDEO_GetPreferredMode(NULL); - xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); - VIDEO_Configure(rmode); - VIDEO_SetNextFramebuffer(xfb); - VIDEO_SetBlack(FALSE); - VIDEO_Flush(); - VIDEO_WaitVSync(); - if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); - int x = 24, y = 32, w, h; - w = rmode->fbWidth - (32); - h = rmode->xfbHeight - (48); - CON_InitEx(rmode, x, y, w, h); - VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK); - PAD_Init(); - cmdbuf = memalign(32,32); - resbuf = memalign(32,32); - u8 *testdump = memalign(32,0x400000); - if(!testdump) return 0; - if(!fatInitDefault()) - { - printmain(); - fatalError("ERROR: No usable device found to write dumped files to!"); + +u32 genKeyA() { + u32 retries = 0; + while (true) { + u32 key = 0; + if (retries > 32) { + key = 0xDD654321; + } else { + key = (rand() & 0x00ffffff) | 0xDD000000; + } + u32 unk = (key % 2 != 0); + u32 v12 = key; + for (u32 v13 = 1; v13 < 32; v13++) { + v12 >>= 1; + unk += (v12 % 2 != 0); + } + if ((unk >= 10 && unk <= 24)) { + if (retries > 4) printf("KeyA retries = %ld",retries); + printf("KeyA = 0x%08lx\n",key); + return key; + } + retries++; } - mkdir("/dumps", S_IREAD | S_IWRITE); - if(!dirExists("/dumps")) - { - printmain(); - fatalError("ERROR: Could not create dumps folder, make sure you have a supported device connected!"); +} + +u32 checkKeyB(u32 KeyBRaw) { + if ((KeyBRaw & 0xFF) != 0xEE) { + printf("Invalid KeyB - lowest 8 bits should be 0xEE, actually 0x%02x\n",((u8)(KeyBRaw))); + return 0; } - int i; - while(1) - { - printmain(); - printf("Waiting for a GBA in port 2...\n"); - resval = 0; - - SI_GetTypeAsync(1,acb); - while(1) - { - if(resval) - { - if(resval == 0x80 || resval & 8) - { - resval = 0; - SI_GetTypeAsync(1,acb); - } - else if(resval) - break; - } - PAD_ScanPads(); - VIDEO_WaitVSync(); - if(PAD_ButtonsHeld(0)) - endproc(); + u32 KeyB = KeyBRaw & 0xffffff00; + u32 val = KeyB; + u32 unk = (val < 0); + for (u32 i = 1; i < 24; i++) { + val <<= 1; + unk += (val < 0); + } + if (unk > 14) { + printf("Invalid KeyB - high 24 bits bad: 0x%08lx\n",KeyB); + return 0; + } + printf("Valid KeyB: 0x%08lx\n",KeyB); + return KeyB; +} + +u32 deriveKeyC(u32 keyCderive, u32 kcrc) { + u32 keyc = 0; + u32 keyCi = 0; + do { + u32 v5 = 0x1000000 * keyCi - 1; + u32 keyCattempt = docrc(kcrc,v5); + //printf("i = %d; keyCderive = %08x; keyCattempt = %08x\n",keyCi,keyCderive,keyCattempt); + if (keyCderive == keyCattempt) { + keyc = v5; + printf("Found keyC: %08lx\n",keyc); + return keyc; } - if(resval & SI_GBA) + keyCi++; + } while (keyCi < 256); + return keyc; +} + +int main(int argc, char *argv[]) +{ + void *xfb = NULL; + GXRModeObj *rmode = NULL; + VIDEO_Init(); + rmode = VIDEO_GetPreferredMode(NULL); + xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); + VIDEO_Configure(rmode); + VIDEO_SetNextFramebuffer(xfb); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); + int x = 24, y = 32, w, h; + w = rmode->fbWidth - (32); + h = rmode->xfbHeight - (48); + CON_InitEx(rmode, x, y, w, h); + VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK); + PAD_Init(); + cmdbuf = memalign(32,32); + resbuf = memalign(32,32); + u8 *testdump = memalign(32,0x400000); + if(!testdump) return 0; + + /*if (!fatInitDefault()) + { + printmain(); + fatalError("ERROR: No usable device found to write dumped files to!"); + } + + mkdir("/dumps", S_IREAD | S_IWRITE); + if (!dirExists("/dumps")) + { + printmain(); + fatalError("ERROR: Could not create dumps folder, make sure you have a supported device connected!"); + }*/ + + int i; + while(1) + { + printmain(); + + printf("Press A to begin, press Start to quit.\n"); + if (waitForButtons(PAD_BUTTON_A | PAD_BUTTON_START) & PAD_BUTTON_START) { - printf("GBA Found! Waiting on BIOS\n"); + endproc(); + } + + printf("Waiting for a GBA in port 2...\n"); + resval = 0; + + SI_GetTypeAsync(1,acb); + while(1) + { + if (resval) + { + if (resval == 0x80 || resval & 8) + { + resval = 0; + SI_GetTypeAsync(1,acb); + } else if (resval) + { + break; + } + } + + PAD_ScanPads(); + VIDEO_WaitVSync(); + if (PAD_ButtonsHeld(0) & PAD_BUTTON_START) + { + getstatus(); + endproc(); + } + } + + if (resval & SI_GBA) + { + printf("GBA Found! Waiting on BIOS\n"); resbuf[2]=0; - while(!(resbuf[2]&0x10)) + //u32 oldresult = 0; + //u32 newresult = 0; + // wait for the BIOS to hand over to the game + do { + doreset(); + + } while (!(resbuf[1] > 4)); + printf("BIOS handed over to game, waiting on game\n"); + do { doreset(); - getstatus(); - } - printf("Ready, sending dumper\n"); + } while((resbuf[0] != 0) || !(resbuf[2]&0x10)); + // receive the game-code from GBA side. + u32 gamecode = recv(); + printf("Ready, sending multiboot ROM\n"); unsigned int sendsize = ((gba_mb_gba_size+7)&~7); - unsigned int ourkey = calckey(sendsize); + // generate KeyA + unsigned int ourkey = genKeyA(); //printf("Our Key: %08x\n", ourkey); - //get current sessionkey - u32 sessionkeyraw = recv(); - u32 sessionkey = __builtin_bswap32(sessionkeyraw^0x7365646F); - //send over our own key - send(__builtin_bswap32(ourkey)); - unsigned int fcrc = 0x15a0; - //send over gba header - for(i = 0; i < 0xC0; i+=4) - send(__builtin_bswap32(*(vu32*)(gba_mb_gba+i))); - //printf("Header done! Sending ROM...\n"); - for(i = 0xC0; i < sendsize; i+=4) + printf("Sending game code that we got: 0x%08lx\n",__builtin_bswap32(gamecode)); + // send the game code back, then KeyA. + send(__builtin_bswap32(gamecode)); + send(ourkey); + // get KeyB from GBA, check it to make sure its valid, then xor with KeyA to derive the initial CRC value and the sessionkey. + u32 sessionkeyraw = 0; + do { + sessionkeyraw = recv(); + } while (sessionkeyraw == gamecode); + sessionkeyraw = checkKeyB(__builtin_bswap32(sessionkeyraw)); + if (sessionkeyraw == 0) { - u32 enc = ((gba_mb_gba[i+3]<<24)|(gba_mb_gba[i+2]<<16)|(gba_mb_gba[i+1]<<8)|(gba_mb_gba[i])); - fcrc=docrc(fcrc,enc); + warnError("Cannot continue.\n"); + + continue; + } + + u32 sessionkey = sessionkeyraw ^ ourkey; + u32 kcrc = sessionkey; + printf("start kCRC=%08lx\n",kcrc); + sessionkey = (sessionkey*0x6177614b)+1; + // send hacked up send-size in uint32s + u32 hackedupsize = (sendsize >> 3) - 1; + printf("Sending hacked up size 0x%08lx\n",hackedupsize); + send(hackedupsize); + //unsigned int fcrc = 0x00bb; + // send over multiboot binary header, in the clear until the end of the nintendo logo. + // GBA checks this, if nintendo logo does not match the one in currently inserted cart's ROM, it will not accept any more data. + for(i = 0; i < 0xA0; i+=4) { + vu32 rom_dword = *(vu32*)(gba_mb_gba+i); + send(__builtin_bswap32(rom_dword)); + } + printf("\n"); + printf("Header done! Sending ROM...\n"); + // Add each uint32 of the multiboot image to the checksum, encrypt the uint32 with the session key, increment the session key, send the encrypted uint32. + for(i = 0xA0; i < sendsize; i+=4) + { + u32 dec = ( + (((gba_mb_gba[i+3]) << 24) & 0xff000000) | + (((gba_mb_gba[i+2]) << 16) & 0x00ff0000) | + (((gba_mb_gba[i+1]) << 8) & 0x0000ff00) | + (((gba_mb_gba[i]) << 0) & 0x000000ff) + ); + u32 enc = (dec - kcrc) ^ sessionkey; + kcrc=docrc(kcrc,dec); sessionkey = (sessionkey*0x6177614B)+1; - enc^=sessionkey; - enc^=((~(i+(0x20<<20)))+1); - enc^=0x20796220; + //enc^=((~(i+(0x20<<20)))+1); + //enc^=0x6f646573;//0x20796220; send(enc); } - fcrc |= (sendsize<<16); - //printf("ROM done! CRC: %08x\n", fcrc); - //send over CRC - sessionkey = (sessionkey*0x6177614B)+1; - fcrc^=sessionkey; - fcrc^=((~(i+(0x20<<20)))+1); - fcrc^=0x20796220; - send(fcrc); + //fcrc |= (sendsize<<16); + printf("ROM done! CRC: %08lx\n", kcrc); //get crc back (unused) - recv(); - printf("Done!\n"); - sleep(2); - //hm - while(1) - { - printmain(); - printf("Press A once you have a GBA Game inserted.\n"); - printf("Press Y to backup the GBA BIOS.\n \n"); - PAD_ScanPads(); - VIDEO_WaitVSync(); - u32 btns = PAD_ButtonsDown(0); - if(btns&PAD_BUTTON_START) - endproc(); - else if(btns&PAD_BUTTON_A) + // Get KeyC derivation material from GBA (eventually) + u32 keyCderive = 0; + do { + keyCderive = recv(); + } while (keyCderive <= 0xfeffffff); + keyCderive = __builtin_bswap32(keyCderive); + keyCderive >>= 8; + printf("KeyC derivation material: %08lx\n",keyCderive); + + // (try to) find the KeyC, using the checksum of the multiboot image, and the derivation material that GBA sent to us + + u32 keyc = deriveKeyC(keyCderive,kcrc); + if (keyc == 0) { - if(recv() == 0) //ready - { - printf("Waiting for GBA\n"); - VIDEO_WaitVSync(); - int gbasize = 0; - while(gbasize == 0) - gbasize = __builtin_bswap32(recv()); - send(0); //got gbasize - u32 savesize = __builtin_bswap32(recv()); - send(0); //got savesize - if(gbasize == -1) - { - warnError("ERROR: No (Valid) GBA Card inserted!\n"); - continue; - } - //get rom header - for(i = 0; i < 0xC0; i+=4) - *(vu32*)(testdump+i) = recv(); - //print out all the info from the game - printf("Game Name: %.12s\n",(char*)(testdump+0xA0)); - printf("Game ID: %.4s\n",(char*)(testdump+0xAC)); - printf("Company ID: %.2s\n",(char*)(testdump+0xB0)); - printf("ROM Size: %02.02f MB\n",((float)(gbasize/1024))/1024.f); - if(savesize > 0) - printf("Save Size: %02.02f KB\n \n",((float)(savesize))/1024.f); - else - printf("No Save File\n \n"); - //generate file paths - char gamename[64]; - sprintf(gamename,"/dumps/%.12s [%.4s%.2s].gba", - (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0)); - fixFName(gamename+7); //fix name behind "/dumps/" - char savename[64]; - sprintf(savename,"/dumps/%.12s [%.4s%.2s].sav", - (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0)); - fixFName(savename+7); //fix name behind "/dumps/" - //let the user choose the option - printf("Press A to dump this game, it will take about %i minutes.\n",gbasize/1024/1024*3/2); - printf("Press B if you want to cancel dumping this game.\n"); - if(savesize > 0) - { - printf("Press Y to backup this save file.\n"); - printf("Press X to restore this save file.\n"); - printf("Press Z to clear the save file on the GBA Cartridge.\n\n"); - } - else - printf("\n"); - int command = 0; - while(1) - { - PAD_ScanPads(); - VIDEO_WaitVSync(); - u32 btns = PAD_ButtonsDown(0); - if(btns&PAD_BUTTON_START) - endproc(); - else if(btns&PAD_BUTTON_A) - { - command = 1; - break; - } - else if(btns&PAD_BUTTON_B) - break; - else if(savesize > 0) - { - if(btns&PAD_BUTTON_Y) - { - command = 2; - break; - } - else if(btns&PAD_BUTTON_X) - { - command = 3; - break; - } - else if(btns&PAD_TRIGGER_Z) - { - command = 4; - break; - } - } - } - if(command == 1) - { - FILE *f = fopen(gamename,"rb"); - if(f) - { - fclose(f); - command = 0; - warnError("ERROR: Game already dumped!\n"); - } - } - else if(command == 2) - { - FILE *f = fopen(savename,"rb"); - if(f) - { - fclose(f); - command = 0; - warnError("ERROR: Save already backed up!\n"); - } - } - else if(command == 3) - { - size_t readsize = 0; - FILE *f = fopen(savename,"rb"); - if(f) - { - fseek(f,0,SEEK_END); - readsize = ftell(f); - if(readsize != savesize) - { - command = 0; - warnError("ERROR: Save has the wrong size, aborting restore!\n"); - } - else - { - rewind(f); - fread(testdump,readsize,1,f); - } - fclose(f); - } - else - { - command = 0; - warnError("ERROR: No Save to restore!\n"); - } - } - send(command); - //let gba prepare - sleep(1); - if(command == 0) - continue; - else if(command == 1) + printf("Could not find keyC - kcrc=0x%08lx\n",kcrc); + warnError("Cannot continue.\n"); + continue; + } + + // derive the boot key from the found KeyC, and send to GBA. if this is not correct, GBA will not jump to the multiboot image it was sent. + u32 bootkey = docrc(0xBB,keyc) | 0xbb000000; + printf("BootKey = 0x%08lx\n",bootkey); + send(bootkey); + /* + printf("GBA Found! Waiting on BIOS...\n"); + + resbuf[2]=0; + while (!(resbuf[2] & 0x10)) + { + doreset(); + getstatus(); + } + + printf("Ready, sending extractor.\n"); + + unsigned int sendsize = ((gba_mb_gba_size + 7) & ~7); + unsigned int ourkey = calckey(sendsize); + + //get current sessionkey + u32 sessionkeyraw = recv(); + u32 sessionkey = __builtin_bswap32(sessionkeyraw ^ 0x7365646F); + + //send over our own key + send(__builtin_bswap32(ourkey)); + unsigned int fcrc = 0x15a0; + + //send over gba header + for(i = 0; i < 0xC0; i+=4) + { + send(__builtin_bswap32(*(vu32*)(gba_mb_gba+i))); + } + + for (i = 0xC0; i < sendsize; i+=4) + { + u32 enc = ( + (gba_mb_gba[i+3] << 24) + | (gba_mb_gba[i+2] << 16) + | (gba_mb_gba[i+1] << 8) + | (gba_mb_gba[i])); + + fcrc = docrc(fcrc,enc); + sessionkey = (sessionkey * 0x6177614B) + 1; + enc ^= sessionkey; + enc ^= ((~(i + (0x20 << 20))) + 1); + enc ^= 0x20796220; + + send(enc); + } + + fcrc |= (sendsize<<16); + + //send over CRC + sessionkey = (sessionkey * 0x6177614B) + 1; + fcrc ^= sessionkey; + fcrc ^= ((~(i + (0x20 << 20))) + 1); + fcrc ^= 0x20796220; + + send(fcrc); + + //get crc back (unused) + recv(); + printf("Done!\n"); + sleep(2); + + //hm + while (1) + { + printmain(); + printf("Press A once you have a GBA Game inserted.\n \n"); + + PAD_ScanPads(); + VIDEO_WaitVSync(); + u32 btns = PAD_ButtonsDown(0); + if (btns & PAD_BUTTON_START) + { + endproc(); + } else if (btns & PAD_BUTTON_A) + {*/ + sleep(1); + //recv(); + + //if (recv() == 0) //ready + { + + printf("Waiting for GBA...\n"); + while (recv() != 0) {fsleep(1);}; + send(0); + + VIDEO_WaitVSync(); + + /*int gbasize = 0; + while(gbasize == 0) + { + gbasize = __builtin_bswap32(recv()); + } + + send(0); //got gbasize + while (recv()!=0) {sleep(1);}; + + //u32 savesize = __builtin_bswap32(recv()); + //send(0); //got savesize + + if (gbasize == -1) + { + warnError("ERROR: No (Valid) GBA Card inserted!\n"); + + continue; + }*/ + + // Get game + // -1 - unsupported game + // 1 - Ruby + // 2 - Sapphire + // 3 - FireRed + // 4 - LeafGreen + // 5 - Emerald + u32 gameId = 0; + while (gameId == 0) { - //create base file with size - printf("Preparing file...\n"); - createFile(gamename,gbasize); - FILE *f = fopen(gamename,"wb"); - if(!f) - fatalError("ERROR: Could not create file! Exit..."); - printf("Dumping...\n"); - u32 bytes_read = 0; - while(gbasize > 0) - { - int toread = (gbasize > 0x400000 ? 0x400000 : gbasize); - int j; - for(j = 0; j < toread; j+=4) - { - *(vu32*)(testdump+j) = recv(); - bytes_read+=4; - if((bytes_read&0xFFFF) == 0) - printf("\r%02.02f MB done",(float)(bytes_read/1024)/1024.f); - } - fwrite(testdump,toread,1,f); - gbasize -= toread; - } - printf("\nClosing file\n"); - fclose(f); - printf("Game dumped!\n"); - sleep(5); + gameId = __builtin_bswap32(recv()); + fsleep(1); } - else if(command == 2) + + send(0); + while (recv()!=0) {fsleep(1);}; + //sleep(1); + + if (gameId == -1) + { + warnError("ERROR: Unsupported GBA game inserted!\n"); + + continue; + } + + printf("\nPokemon "); + switch (gameId) + { + case 1: printf("Ruby"); break; + case 2: printf("Sapphire"); break; + case 3: printf("FireRed"); break; + case 4: printf("LeafGreen"); break; + case 5: printf("Emerald"); break; + } + + printf("\n"); + VIDEO_WaitVSync(); + + u32 isValid = 0; + while (isValid == 0) { - //create base file with size - printf("Preparing file...\n"); - createFile(savename,savesize); - FILE *f = fopen(savename,"wb"); - if(!f) - fatalError("ERROR: Could not create file! Exit..."); - printf("Waiting for GBA\n"); - VIDEO_WaitVSync(); - u32 readval = 0; - while(readval != savesize) - readval = __builtin_bswap32(recv()); - send(0); //got savesize - printf("Receiving...\n"); - for(i = 0; i < savesize; i+=4) - *(vu32*)(testdump+i) = recv(); - printf("Writing save...\n"); - fwrite(testdump,savesize,1,f); - fclose(f); - printf("Save backed up!\n"); - sleep(5); + isValid = __builtin_bswap32(recv()); + fsleep(1); } - else if(command == 3 || command == 4) + + if (isValid == -1) + { + //send(0); + + warnError("ERROR: Unsupported game version inserted!\n"); + + continue; + } + + send(0); + while (recv()!=0) {fsleep(1);}; + //sleep(1); + /* + // Get trainer name + u8 trainerName[8]; + + u32 tnd = recv(); + send(0); + trainerName[0] = (tnd & 0xFF000000); + trainerName[1] = (tnd & 0x00FF0000) >> 8; + trainerName[2] = (tnd & 0x0000FF00) >> 16; + trainerName[3] = (tnd & 0x000000FF) >> 24; + + tnd = recv(); + send(0); + trainerName[4] = (tnd & 0xFF000000); + trainerName[5] = (tnd & 0x00FF0000) >> 8; + trainerName[6] = (tnd & 0x0000FF00) >> 16; + trainerName[7] = (tnd & 0x000000FF) >> 24; + + printf("Trainer: %s", (char*) trainerName); +*/ + // Get trainer ID + u32 trainerId = 0; + while (trainerId == 0) { - u32 readval = 0; - while(readval != savesize) - readval = __builtin_bswap32(recv()); - if(command == 3) - { - printf("Sending save\n"); - VIDEO_WaitVSync(); - for(i = 0; i < savesize; i+=4) - send(__builtin_bswap32(*(vu32*)(testdump+i))); - } - printf("Waiting for GBA\n"); - while(recv() != 0) - VIDEO_WaitVSync(); - printf(command == 3 ? "Save restored!\n" : "Save cleared!\n"); - send(0); - sleep(5); + trainerId = __builtin_bswap32(recv()); + fsleep(1); } - } - } - else if(btns&PAD_BUTTON_Y) - { - const char *biosname = "/dumps/gba_bios.bin"; - FILE *f = fopen(biosname,"rb"); - if(f) - { - fclose(f); - warnError("ERROR: BIOS already backed up!\n"); - } - else - { - //create base file with size - printf("Preparing file...\n"); - createFile(biosname,0x4000); - f = fopen(biosname,"wb"); - if(!f) - fatalError("ERROR: Could not create file! Exit..."); - //send over bios dump command - send(5); - //the gba might still be in a loop itself - sleep(1); - //lets go! - printf("Dumping...\n"); - for(i = 0; i < 0x4000; i+=4) - *(vu32*)(testdump+i) = recv(); - fwrite(testdump,0x4000,1,f); - printf("Closing file\n"); - fclose(f); - printf("BIOS dumped!\n"); - sleep(5); - } - } - } - } - } - return 0; + send(0); + while (recv()!=0) {fsleep(1);}; + //sleep(1); + + printf(" (%ld)\n", trainerId); + + //continue; + + // Wait for confirmation. + printf("Press A to import the data from this game.\n"); + printf("Press B to cancel.\n"); + VIDEO_WaitVSync(); + + if (waitForButtons(PAD_BUTTON_A | PAD_BUTTON_B) & PAD_BUTTON_B) + { + continue; + } + + + + +/* + //get rom header + for(i = 0; i < 0xC0; i+=4) + *(vu32*)(testdump+i) = recv(); + //print out all the info from the game + printf("Game Name: %.12s\n",(char*)(testdump+0xA0)); + printf("Game ID: %.4s\n",(char*)(testdump+0xAC)); + printf("Company ID: %.2s\n",(char*)(testdump+0xB0)); + printf("ROM Size: %02.02f MB\n",((float)(gbasize/1024))/1024.f); + if(savesize > 0) + printf("Save Size: %02.02f KB\n \n",((float)(savesize))/1024.f); + else + printf("No Save File\n \n"); + //generate file paths + char gamename[64]; + sprintf(gamename,"/dumps/%.12s [%.4s%.2s].gba", + (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0)); + fixFName(gamename+7); //fix name behind "/dumps/" + char savename[64]; + sprintf(savename,"/dumps/%.12s [%.4s%.2s].sav", + (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0)); + fixFName(savename+7); //fix name behind "/dumps/" + //let the user choose the option + printf("Press A to dump this game, it will take about %i minutes.\n",gbasize/1024/1024*3/2); + printf("Press B if you want to cancel dumping this game.\n"); + if(savesize > 0) + { + printf("Press Y to backup this save file.\n"); + printf("Press X to restore this save file.\n"); + printf("Press Z to clear the save file on the GBA Cartridge.\n\n"); + } + else + printf("\n"); + + int command = 0; + while(1) + { + PAD_ScanPads(); + VIDEO_WaitVSync(); + u32 btns = PAD_ButtonsDown(0); + if(btns&PAD_BUTTON_START) + endproc(); + else if(btns&PAD_BUTTON_A) + { + command = 1; + break; + } + else if(btns&PAD_BUTTON_B) + break; + else if(savesize > 0) + { + if(btns&PAD_BUTTON_Y) + { + command = 2; + break; + } + else if(btns&PAD_BUTTON_X) + { + command = 3; + break; + } + else if(btns&PAD_TRIGGER_Z) + { + command = 4; + break; + } + } + } + if(command == 1) + { + FILE *f = fopen(gamename,"rb"); + if(f) + { + fclose(f); + command = 0; + warnError("ERROR: Game already dumped!\n"); + } + } + else if(command == 2) + { + FILE *f = fopen(savename,"rb"); + if(f) + { + fclose(f); + command = 0; + warnError("ERROR: Save already backed up!\n"); + } + } + else if(command == 3) + { + size_t readsize = 0; + FILE *f = fopen(savename,"rb"); + if(f) + { + fseek(f,0,SEEK_END); + readsize = ftell(f); + if(readsize != savesize) + { + command = 0; + warnError("ERROR: Save has the wrong size, aborting restore!\n"); + } + else + { + rewind(f); + fread(testdump,readsize,1,f); + } + fclose(f); + } + else + { + command = 0; + warnError("ERROR: No Save to restore!\n"); + } + } + send(command); + //let gba prepare + sleep(1); + if(command == 0) + continue; + else if(command == 1) + { + //create base file with size + printf("Preparing file...\n"); + createFile(gamename,gbasize); + FILE *f = fopen(gamename,"wb"); + if(!f) + fatalError("ERROR: Could not create file! Exit..."); + printf("Dumping...\n"); + u32 bytes_read = 0; + while(gbasize > 0) + { + int toread = (gbasize > 0x400000 ? 0x400000 : gbasize); + int j; + for(j = 0; j < toread; j+=4) + { + *(vu32*)(testdump+j) = recv(); + bytes_read+=4; + if((bytes_read&0xFFFF) == 0) + printf("\r%02.02f MB done",(float)(bytes_read/1024)/1024.f); + } + fwrite(testdump,toread,1,f); + gbasize -= toread; + } + printf("\nClosing file\n"); + fclose(f); + printf("Game dumped!\n"); + sleep(5); + } + else if(command == 2) + { + //create base file with size + printf("Preparing file...\n"); + createFile(savename,savesize); + FILE *f = fopen(savename,"wb"); + if(!f) + fatalError("ERROR: Could not create file! Exit..."); + printf("Waiting for GBA\n"); + VIDEO_WaitVSync(); + u32 readval = 0; + while(readval != savesize) + readval = __builtin_bswap32(recv()); + send(0); //got savesize + printf("Receiving...\n"); + for(i = 0; i < savesize; i+=4) + *(vu32*)(testdump+i) = recv(); + printf("Writing save...\n"); + fwrite(testdump,savesize,1,f); + fclose(f); + printf("Save backed up!\n"); + sleep(5); + } + else if(command == 3 || command == 4) + { + u32 readval = 0; + while(readval != savesize) + readval = __builtin_bswap32(recv()); + if(command == 3) + { + printf("Sending save\n"); + VIDEO_WaitVSync(); + for(i = 0; i < savesize; i+=4) + send(__builtin_bswap32(*(vu32*)(testdump+i))); + } + printf("Waiting for GBA\n"); + while(recv() != 0) + VIDEO_WaitVSync(); + printf(command == 3 ? "Save restored!\n" : "Save cleared!\n"); + send(0); + sleep(5); + }*/ + } + } + /*else if(btns&PAD_BUTTON_Y) + { + const char *biosname = "/dumps/gba_bios.bin"; + FILE *f = fopen(biosname,"rb"); + if(f) + { + fclose(f); + warnError("ERROR: BIOS already backed up!\n"); + } + else + { + //create base file with size + printf("Preparing file...\n"); + createFile(biosname,0x4000); + f = fopen(biosname,"wb"); + if(!f) + fatalError("ERROR: Could not create file! Exit..."); + //send over bios dump command + send(5); + //the gba might still be in a loop itself + sleep(1); + //lets go! + printf("Dumping...\n"); + for(i = 0; i < 0x4000; i+=4) + *(vu32*)(testdump+i) = recv(); + fwrite(testdump,0x4000,1,f); + printf("Closing file\n"); + fclose(f); + printf("BIOS dumped!\n"); + sleep(5); + } + }*/ +// } + // } +// } + } + return 0; } -- cgit 1.4.1