diff options
| -rw-r--r-- | .gitignore | 7 | ||||
| -rw-r--r-- | build.bat | 2 | ||||
| -rwxr-xr-x | build.sh | 11 | ||||
| -rw-r--r-- | gba/Makefile | 12 | ||||
| -rw-r--r-- | gba/gba_pkjb.ld | 296 | ||||
| -rw-r--r-- | gba/gba_pkjb.specs | 8 | ||||
| -rw-r--r-- | gba/source/gamedata.c | 534 | ||||
| -rw-r--r-- | gba/source/gamedata.h | 22 | ||||
| -rw-r--r-- | gba/source/link.c | 41 | ||||
| -rw-r--r-- | gba/source/link.h | 12 | ||||
| -rw-r--r-- | gba/source/main.c | 592 | ||||
| -rw-r--r-- | gba/source/saveblocks.h | 343 | ||||
| -rw-r--r-- | gba/source/savestructs.h | 793 | ||||
| -rw-r--r-- | gba/start/pkjb_crt0.s | 102 | ||||
| -rw-r--r-- | source/link.c | 16 | ||||
| -rw-r--r-- | source/link.h | 8 | ||||
| -rw-r--r-- | source/main.c | 1287 |
17 files changed, 3415 insertions, 671 deletions
| diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac7cf98 --- /dev/null +++ b/.gitignore | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | *.elf | ||
| 2 | build | ||
| 3 | data | ||
| 4 | *.dol | ||
| 5 | tags | ||
| 6 | *.o | ||
| 7 | *.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 | |||
| 7 | make -f Makefile.gc | 7 | make -f Makefile.gc |
| 8 | make -f Makefile.wii clean | 8 | make -f Makefile.wii clean |
| 9 | make -f Makefile.wii | 9 | make -f Makefile.wii |
| 10 | pause \ No newline at end of file | 10 | 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 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | cd gba | ||
| 3 | make clean | ||
| 4 | make | ||
| 5 | cd .. | ||
| 6 | mkdir data | ||
| 7 | mv -f gba/gba_pkjb.gba data/gba_mb.gba | ||
| 8 | make -f Makefile.gc clean | ||
| 9 | make -f Makefile.gc | ||
| 10 | make -f Makefile.wii clean | ||
| 11 | 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)),) | |||
| 7 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM) | 7 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM) |
| 8 | endif | 8 | endif |
| 9 | 9 | ||
| 10 | %_pkjb.elf: | ||
| 11 | @echo linking pkjb | ||
| 12 | $(LD) -specs=../gba_pkjb.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ | ||
| 13 | |||
| 10 | include $(DEVKITARM)/gba_rules | 14 | include $(DEVKITARM)/gba_rules |
| 11 | 15 | ||
| 12 | #--------------------------------------------------------------------------------- | 16 | #--------------------------------------------------------------------------------- |
| @@ -16,7 +20,7 @@ include $(DEVKITARM)/gba_rules | |||
| 16 | # DATA is a list of directories containing data files | 20 | # DATA is a list of directories containing data files |
| 17 | # INCLUDES is a list of directories containing header files | 21 | # INCLUDES is a list of directories containing header files |
| 18 | #--------------------------------------------------------------------------------- | 22 | #--------------------------------------------------------------------------------- |
| 19 | TARGET := $(shell basename $(CURDIR))_mb | 23 | TARGET := $(shell basename $(CURDIR))_pkjb |
| 20 | BUILD := build | 24 | BUILD := build |
| 21 | SOURCES := source | 25 | SOURCES := source |
| 22 | DATA := | 26 | DATA := |
| @@ -29,6 +33,7 @@ INCLUDES := | |||
| 29 | ARCH := -mthumb -mthumb-interwork | 33 | ARCH := -mthumb -mthumb-interwork |
| 30 | 34 | ||
| 31 | CFLAGS := -g -Wall -O3\ | 35 | CFLAGS := -g -Wall -O3\ |
| 36 | -Wno-multichar\ | ||
| 32 | -mcpu=arm7tdmi -mtune=arm7tdmi\ | 37 | -mcpu=arm7tdmi -mtune=arm7tdmi\ |
| 33 | -fomit-frame-pointer\ | 38 | -fomit-frame-pointer\ |
| 34 | -ffast-math \ | 39 | -ffast-math \ |
| @@ -38,8 +43,8 @@ CFLAGS += $(INCLUDE) | |||
| 38 | 43 | ||
| 39 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions | 44 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions |
| 40 | 45 | ||
| 41 | ASFLAGS := $(ARCH) | 46 | ASFLAGS := -g $(ARCH) |
| 42 | LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $@).map | 47 | LDFLAGS = $(ARCH) -Wl,-Map,$(notdir $@).map |
| 43 | 48 | ||
| 44 | #--------------------------------------------------------------------------------- | 49 | #--------------------------------------------------------------------------------- |
| 45 | # any extra libraries we wish to link with the project | 50 | # any extra libraries we wish to link with the project |
| @@ -109,6 +114,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) | |||
| 109 | #--------------------------------------------------------------------------------- | 114 | #--------------------------------------------------------------------------------- |
| 110 | $(BUILD): | 115 | $(BUILD): |
| 111 | @[ -d $@ ] || mkdir -p $@ | 116 | @[ -d $@ ] || mkdir -p $@ |
| 117 | $(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) | ||
| 112 | @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile | 118 | @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile |
| 113 | 119 | ||
| 114 | all : $(BUILD) | 120 | 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 @@ | |||
| 1 | /* Linker Script Original v1.3 by Jeff Frohwein */ | ||
| 2 | /* v1.0 - Original release */ | ||
| 3 | /* v1.1 - Added proper .data section support */ | ||
| 4 | /* v1.2 - Added support for c++ & iwram overlays */ | ||
| 5 | /* - Major contributions by Jason Wilkins. */ | ||
| 6 | /* v1.3 - .ewram section now can be used when */ | ||
| 7 | /* compiling for MULTIBOOT mode. This fixes */ | ||
| 8 | /* malloc() in DevKitAdvance which depends */ | ||
| 9 | /* on __eheap_start instead of end to define*/ | ||
| 10 | /* the starting location of heap space. */ | ||
| 11 | /* External global variable __gba_iwram_heap*/ | ||
| 12 | /* support added to allow labels end, _end, */ | ||
| 13 | /* & __end__ to point to end of iwram or */ | ||
| 14 | /* the end of ewram. */ | ||
| 15 | /* Additions by WinterMute */ | ||
| 16 | /* v1.4 - .sbss section added for unitialised */ | ||
| 17 | /* data in ewram */ | ||
| 18 | /* v1.5 - padding section added to stop EZF */ | ||
| 19 | /* stripping important data */ | ||
| 20 | /* v1.6 - added memory sections */ | ||
| 21 | |||
| 22 | /* This file is released into the public domain */ | ||
| 23 | /* for commercial or non-commercial use with no */ | ||
| 24 | /* restrictions placed upon it. */ | ||
| 25 | |||
| 26 | /* NOTE!!!: This linker script defines the RAM & */ | ||
| 27 | /* ROM start addresses. In order for it to work */ | ||
| 28 | /* properly, remove -Ttext and -Tbss linker */ | ||
| 29 | /* options from your makefile if they are */ | ||
| 30 | /* present. */ | ||
| 31 | |||
| 32 | /* You can use the following to view section */ | ||
| 33 | /* addresses in your .elf file: */ | ||
| 34 | /* objdump -h file.elf */ | ||
| 35 | /* Please note that empty sections may incorrectly*/ | ||
| 36 | /* list the lma address as the vma address for */ | ||
| 37 | /* some versions of objdump. */ | ||
| 38 | |||
| 39 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") | ||
| 40 | OUTPUT_ARCH(arm) | ||
| 41 | ENTRY(_start) | ||
| 42 | |||
| 43 | MEMORY { | ||
| 44 | |||
| 45 | rom : ORIGIN = 0x08000000, LENGTH = 32M | ||
| 46 | iwram : ORIGIN = 0x03000000, LENGTH = 32K | ||
| 47 | ewram : ORIGIN = 0x02000000, LENGTH = 256K | ||
| 48 | } | ||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | __text_start = ORIGIN(ewram); | ||
| 53 | __eheap_end = ORIGIN(ewram) + LENGTH(ewram); | ||
| 54 | __iwram_start = ORIGIN(iwram); | ||
| 55 | __iwram_top = ORIGIN(iwram) + LENGTH(iwram);; | ||
| 56 | |||
| 57 | __sp_irq = __iwram_top - 0x060; | ||
| 58 | __sp_usr = __sp_irq - 0x0a0; | ||
| 59 | __irq_flags = 0x03007ff8; | ||
| 60 | |||
| 61 | SECTIONS | ||
| 62 | { | ||
| 63 | . = __text_start; | ||
| 64 | .init : | ||
| 65 | { | ||
| 66 | KEEP (*(.init)) | ||
| 67 | . = ALIGN(4); | ||
| 68 | } >ewram =0xff | ||
| 69 | |||
| 70 | .plt : | ||
| 71 | { | ||
| 72 | *(.plt) | ||
| 73 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 74 | } >ewram | ||
| 75 | |||
| 76 | .text ALIGN (4): | ||
| 77 | { | ||
| 78 | *(EXCLUDE_FILE (*.iwram*) .text) | ||
| 79 | *(.text .stub .text.* .gnu.linkonce.t.*) | ||
| 80 | KEEP (*(.text.*personality*)) | ||
| 81 | /* .gnu.warning sections are handled specially by elf32.em. */ | ||
| 82 | *(.gnu.warning) | ||
| 83 | *(.glue_7t) *(.glue_7) *(.vfp11_veneer) | ||
| 84 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 85 | } >ewram = 0xff | ||
| 86 | |||
| 87 | __text_end = .; | ||
| 88 | .fini : | ||
| 89 | { | ||
| 90 | KEEP (*(.fini)) | ||
| 91 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 92 | } >ewram =0 | ||
| 93 | |||
| 94 | .rodata : | ||
| 95 | { | ||
| 96 | *(.rodata) | ||
| 97 | *all.rodata*(*) | ||
| 98 | *(.roda) | ||
| 99 | *(.rodata.*) | ||
| 100 | *(.gnu.linkonce.r*) | ||
| 101 | SORT(CONSTRUCTORS) | ||
| 102 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 103 | } >ewram = 0xff | ||
| 104 | |||
| 105 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >ewram | ||
| 106 | __exidx_start = .; | ||
| 107 | .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >ewram | ||
| 108 | __exidx_end = .; | ||
| 109 | /* Ensure the __preinit_array_start label is properly aligned. We | ||
| 110 | could instead move the label definition inside the section, but | ||
| 111 | the linker would then create the section even if it turns out to | ||
| 112 | be empty, which isn't pretty. */ | ||
| 113 | . = ALIGN(32 / 8); | ||
| 114 | PROVIDE (__preinit_array_start = .); | ||
| 115 | .preinit_array : { KEEP (*(.preinit_array)) } >ewram = 0xff | ||
| 116 | PROVIDE (__preinit_array_end = .); | ||
| 117 | PROVIDE (__init_array_start = .); | ||
| 118 | .init_array : { KEEP (*(.init_array)) } >ewram = 0xff | ||
| 119 | PROVIDE (__init_array_end = .); | ||
| 120 | PROVIDE (__fini_array_start = .); | ||
| 121 | .fini_array : { KEEP (*(.fini_array)) } >ewram = 0xff | ||
| 122 | PROVIDE (__fini_array_end = .); | ||
| 123 | .ctors : | ||
| 124 | { | ||
| 125 | /* gcc uses crtbegin.o to find the start of the constructors, so | ||
| 126 | we make sure it is first. Because this is a wildcard, it | ||
| 127 | doesn't matter if the user does not actually link against | ||
| 128 | crtbegin.o; the linker won't look for a file to match a | ||
| 129 | wildcard. The wildcard also means that it doesn't matter which | ||
| 130 | directory crtbegin.o is in. */ | ||
| 131 | KEEP (*crtbegin.o(.ctors)) | ||
| 132 | KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) | ||
| 133 | KEEP (*(SORT(.ctors.*))) | ||
| 134 | KEEP (*(.ctors)) | ||
| 135 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 136 | } >ewram = 0 | ||
| 137 | |||
| 138 | .dtors : | ||
| 139 | { | ||
| 140 | KEEP (*crtbegin.o(.dtors)) | ||
| 141 | KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) | ||
| 142 | KEEP (*(SORT(.dtors.*))) | ||
| 143 | KEEP (*(.dtors)) | ||
| 144 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 145 | } >ewram = 0 | ||
| 146 | |||
| 147 | .jcr : { KEEP (*(.jcr)) } >ewram | ||
| 148 | .eh_frame : | ||
| 149 | { | ||
| 150 | KEEP (*(.eh_frame)) | ||
| 151 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 152 | } >ewram = 0 | ||
| 153 | |||
| 154 | .gcc_except_table : | ||
| 155 | { | ||
| 156 | *(.gcc_except_table) | ||
| 157 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 158 | } >ewram = 0 | ||
| 159 | |||
| 160 | __iwram_lma = .; | ||
| 161 | |||
| 162 | .iwram __iwram_start : AT (__iwram_lma) | ||
| 163 | { | ||
| 164 | __iwram_start__ = ABSOLUTE(.) ; | ||
| 165 | *(.iwram) | ||
| 166 | *iwram.*(.text) | ||
| 167 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 168 | __iwram_end__ = ABSOLUTE(.) ; | ||
| 169 | } >iwram = 0xff | ||
| 170 | |||
| 171 | __data_lma = __iwram_lma + SIZEOF(.iwram) ; | ||
| 172 | |||
| 173 | .bss ALIGN(4) (NOLOAD): | ||
| 174 | { | ||
| 175 | __bss_start__ = ABSOLUTE(.); | ||
| 176 | *(.dynbss) | ||
| 177 | *(.gnu.linkonce.b*) | ||
| 178 | *(.bss*) | ||
| 179 | *(COMMON) | ||
| 180 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 181 | __bss_end__ = ABSOLUTE(.) ; | ||
| 182 | } | ||
| 183 | |||
| 184 | .data ALIGN(4) : AT (__data_lma) | ||
| 185 | { | ||
| 186 | __data_start__ = ABSOLUTE(.); | ||
| 187 | *(.data) | ||
| 188 | *(.data.*) | ||
| 189 | *(.gnu.linkonce.d*) | ||
| 190 | CONSTRUCTORS | ||
| 191 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 192 | __data_end__ = ABSOLUTE(.); | ||
| 193 | } >iwram = 0xff | ||
| 194 | |||
| 195 | __iwram_overlay_lma = __data_lma + SIZEOF(.data); | ||
| 196 | |||
| 197 | PROVIDE (edata = .); | ||
| 198 | __iwram_overlay_start = . ; | ||
| 199 | |||
| 200 | OVERLAY ALIGN(4) : NOCROSSREFS AT (__iwram_overlay_lma) | ||
| 201 | { | ||
| 202 | .iwram0 { *(.iwram0) . = ALIGN(4);} | ||
| 203 | .iwram1 { *(.iwram1) . = ALIGN(4);} | ||
| 204 | .iwram2 { *(.iwram2) . = ALIGN(4);} | ||
| 205 | .iwram3 { *(.iwram3) . = ALIGN(4);} | ||
| 206 | .iwram4 { *(.iwram4) . = ALIGN(4);} | ||
| 207 | .iwram5 { *(.iwram5) . = ALIGN(4);} | ||
| 208 | .iwram6 { *(.iwram6) . = ALIGN(4);} | ||
| 209 | .iwram7 { *(.iwram7) . = ALIGN(4);} | ||
| 210 | .iwram8 { *(.iwram8) . = ALIGN(4);} | ||
| 211 | .iwram9 { *(.iwram9) . = ALIGN(4);} | ||
| 212 | } >iwram = 0xff | ||
| 213 | |||
| 214 | __ewram_lma = LOADADDR(.iwram0) + SIZEOF(.iwram0)+SIZEOF(.iwram1)+SIZEOF(.iwram2)+SIZEOF(.iwram3)+SIZEOF(.iwram4)+SIZEOF(.iwram5)+SIZEOF(.iwram6)+SIZEOF(.iwram7)+SIZEOF(.iwram8)+SIZEOF(.iwram9); | ||
| 215 | |||
| 216 | __iwram_overlay_end = __ewram_lma ; | ||
| 217 | |||
| 218 | /* v1.3 */ | ||
| 219 | __ewram_start = __ewram_lma ; | ||
| 220 | |||
| 221 | .ewram __ewram_start : AT (__ewram_lma) | ||
| 222 | { | ||
| 223 | *(.ewram) | ||
| 224 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ | ||
| 225 | __ewram_end = ABSOLUTE(.); | ||
| 226 | } >ewram = 0xff | ||
| 227 | |||
| 228 | __ewram_overlay_lma = __ewram_lma + SIZEOF(.ewram); | ||
| 229 | |||
| 230 | .sbss ALIGN(4)(NOLOAD): | ||
| 231 | { | ||
| 232 | __sbss_start__ = ABSOLUTE(.); | ||
| 233 | *(.sbss) | ||
| 234 | . = ALIGN(4); | ||
| 235 | __sbss_end__ = ABSOLUTE(.); | ||
| 236 | __end__ = ABSOLUTE(.); | ||
| 237 | __eheap_start = ABSOLUTE(.); | ||
| 238 | } | ||
| 239 | |||
| 240 | OVERLAY ALIGN(4): NOCROSSREFS AT (__ewram_overlay_lma) | ||
| 241 | { | ||
| 242 | .ewram0 { *(.ewram0) . = ALIGN(4);} | ||
| 243 | .ewram1 { *(.ewram1) . = ALIGN(4);} | ||
| 244 | .ewram2 { *(.ewram2) . = ALIGN(4);} | ||
| 245 | .ewram3 { *(.ewram3) . = ALIGN(4);} | ||
| 246 | .ewram4 { *(.ewram4) . = ALIGN(4);} | ||
| 247 | .ewram5 { *(.ewram5) . = ALIGN(4);} | ||
| 248 | .ewram6 { *(.ewram6) . = ALIGN(4);} | ||
| 249 | .ewram7 { *(.ewram7) . = ALIGN(4);} | ||
| 250 | .ewram8 { *(.ewram8) . = ALIGN(4);} | ||
| 251 | .ewram9 { *(.ewram9) . = ALIGN(4);} | ||
| 252 | } >ewram = 0xff | ||
| 253 | __ewram_overlay_end = ABSOLUTE(.); | ||
| 254 | |||
| 255 | __eheap_start = __ewram_overlay_end ; | ||
| 256 | |||
| 257 | _end = __ewram_overlay_end; | ||
| 258 | __end__ = __ewram_overlay_end; | ||
| 259 | __rom_end__ = __ewram_overlay_end; | ||
| 260 | |||
| 261 | /* Stabs debugging sections. */ | ||
| 262 | .stab 0 : { *(.stab) } | ||
| 263 | .stabstr 0 : { *(.stabstr) } | ||
| 264 | .stab.excl 0 : { *(.stab.excl) } | ||
| 265 | .stab.exclstr 0 : { *(.stab.exclstr) } | ||
| 266 | .stab.index 0 : { *(.stab.index) } | ||
| 267 | .stab.indexstr 0 : { *(.stab.indexstr) } | ||
| 268 | .comment 0 : { *(.comment) } | ||
| 269 | /* DWARF debug sections. | ||
| 270 | Symbols in the DWARF debugging sections are relative to the beginning | ||
| 271 | of the section so we begin them at 0. */ | ||
| 272 | /* DWARF 1 */ | ||
| 273 | .debug 0 : { *(.debug) } | ||
| 274 | .line 0 : { *(.line) } | ||
| 275 | /* GNU DWARF 1 extensions */ | ||
| 276 | .debug_srcinfo 0 : { *(.debug_srcinfo) } | ||
| 277 | .debug_sfnames 0 : { *(.debug_sfnames) } | ||
| 278 | /* DWARF 1.1 and DWARF 2 */ | ||
| 279 | .debug_aranges 0 : { *(.debug_aranges) } | ||
| 280 | .debug_pubnames 0 : { *(.debug_pubnames) } | ||
| 281 | /* DWARF 2 */ | ||
| 282 | .debug_info 0 : { *(.debug_info) } | ||
| 283 | .debug_abbrev 0 : { *(.debug_abbrev) } | ||
| 284 | .debug_line 0 : { *(.debug_line) } | ||
| 285 | .debug_frame 0 : { *(.debug_frame) } | ||
| 286 | .debug_str 0 : { *(.debug_str) } | ||
| 287 | .debug_loc 0 : { *(.debug_loc) } | ||
| 288 | .debug_macinfo 0 : { *(.debug_macinfo) } | ||
| 289 | /* SGI/MIPS DWARF 2 extensions */ | ||
| 290 | .debug_weaknames 0 : { *(.debug_weaknames) } | ||
| 291 | .debug_funcnames 0 : { *(.debug_funcnames) } | ||
| 292 | .debug_typenames 0 : { *(.debug_typenames) } | ||
| 293 | .debug_varnames 0 : { *(.debug_varnames) } | ||
| 294 | .stack 0x80000 : { _stack = .; *(.stack) } | ||
| 295 | /* These must appear regardless of . */ | ||
| 296 | } | ||
| 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 @@ | |||
| 1 | %rename link old_link | ||
| 2 | |||
| 3 | *link: | ||
| 4 | -T ../gba_pkjb.ld%s %(old_link) --gc-sections | ||
| 5 | |||
| 6 | *startfile: | ||
| 7 | ../start/pkjb_crt0%O%s crti%O%s crtbegin%O%s | ||
| 8 | |||
| 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 @@ | |||
| 1 | /* | ||
| 2 | * Pokemon Gen III Data Extractor by hatkirby 2017. | ||
| 3 | * | ||
| 4 | * This software may be modified and distributed under the terms | ||
| 5 | * of the MIT license. See the LICENSE file for details. | ||
| 6 | * | ||
| 7 | */ | ||
| 8 | #include "gamedata.h" | ||
| 9 | |||
| 10 | void decryptSaveStructures( | ||
| 11 | pSaveBlock1 SaveBlock1, | ||
| 12 | pSaveBlock2 SaveBlock2, | ||
| 13 | pSaveBlock3 SaveBlock3) | ||
| 14 | { | ||
| 15 | if (GAME_RS) | ||
| 16 | { | ||
| 17 | // R/S doesn't have save crypto. | ||
| 18 | return; | ||
| 19 | } | ||
| 20 | |||
| 21 | u8* sb1raw = (u8*)SaveBlock1; | ||
| 22 | u8* sb2raw = (u8*)SaveBlock2; | ||
| 23 | //u8* sb3raw = (u8*)SaveBlock3; // unused | ||
| 24 | |||
| 25 | u32* xor_key_ptr = (u32*)(&sb2raw[( GAME_EM ? 0xA8 : 0xF20 )]); | ||
| 26 | |||
| 27 | u32 xor_key = *xor_key_ptr; | ||
| 28 | u16 xor_key16 = (u16)xor_key; | ||
| 29 | if (!xor_key) | ||
| 30 | { | ||
| 31 | // xor key is zero, nothing needs to be done. | ||
| 32 | return; | ||
| 33 | } | ||
| 34 | |||
| 35 | u32* ptr_to_xor; | ||
| 36 | u32 save_offset; | ||
| 37 | int i; | ||
| 38 | u32* bag_pocket_offsets; | ||
| 39 | u32* bag_pocket_counts; | ||
| 40 | if (GAME_FRLG) | ||
| 41 | { | ||
| 42 | // loop over and decrypt various things | ||
| 43 | save_offset = 0x3D38 + 4; | ||
| 44 | for (i = 3; i >= 0; i--) | ||
| 45 | { | ||
| 46 | ptr_to_xor = (u32*)(&sb1raw[save_offset]); | ||
| 47 | *ptr_to_xor ^= xor_key; | ||
| 48 | save_offset += 12; | ||
| 49 | } | ||
| 50 | |||
| 51 | for (i = 0; i <= 0x3f; i++) | ||
| 52 | { | ||
| 53 | save_offset = 0x1200 + (i*sizeof(u32)); | ||
| 54 | ptr_to_xor = (u32*)(&sb1raw[save_offset]); | ||
| 55 | *ptr_to_xor ^= xor_key; | ||
| 56 | } | ||
| 57 | |||
| 58 | // loop over each of the bag pockets and decrypt decrypt decrypt | ||
| 59 | bag_pocket_offsets = (u32[5]) { 0x310, 0x388, 0x430, 0x464, 0x54C }; | ||
| 60 | bag_pocket_counts = (u32[5]) { 42, 30, 13, 58, 43 }; | ||
| 61 | |||
| 62 | for (i = 0; i < 5; i++) | ||
| 63 | { | ||
| 64 | for (int bag_i = 0; bag_i < bag_pocket_counts[i]; bag_i++) | ||
| 65 | { | ||
| 66 | save_offset = bag_pocket_offsets[i] + (sizeof(u32) * bag_i) + 2; | ||
| 67 | *(u16*)(&sb1raw[save_offset]) ^= xor_key16; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | // decrypt some more stuff | ||
| 72 | save_offset = 0xaf8; | ||
| 73 | ptr_to_xor = (u32*)(&sb1raw[save_offset]); | ||
| 74 | *ptr_to_xor ^= xor_key; | ||
| 75 | |||
| 76 | save_offset = 0x290; | ||
| 77 | ptr_to_xor = (u32*)(&sb1raw[save_offset]); | ||
| 78 | *ptr_to_xor ^= xor_key; | ||
| 79 | |||
| 80 | save_offset = 0x294; | ||
| 81 | *(u16*)(&sb1raw[save_offset]) ^= xor_key16; | ||
| 82 | } else { | ||
| 83 | // Emerald | ||
| 84 | |||
| 85 | // loop over and decrypt various things | ||
| 86 | for (i = 0; i <= 0x3f; i++) | ||
| 87 | { | ||
| 88 | save_offset = 0x159c + (i*sizeof(u32)); | ||
| 89 | ptr_to_xor = (u32*)(&sb1raw[save_offset]); | ||
| 90 | *ptr_to_xor ^= xor_key; | ||
| 91 | } | ||
| 92 | |||
| 93 | // loop over each of the bag pockets and decrypt decrypt decrypt | ||
| 94 | bag_pocket_offsets = (u32[5]) { 0x560, 0x5D8, 0x650, 0x690, 0x790 }; | ||
| 95 | bag_pocket_counts = (u32[5]) { 30, 30, 16, 64, 46 }; | ||
| 96 | |||
| 97 | for (i = 0; i < 5; i++) | ||
| 98 | { | ||
| 99 | for (int bag_i = 0; bag_i < bag_pocket_counts[i]; bag_i++) | ||
| 100 | { | ||
| 101 | save_offset = bag_pocket_offsets[i] + (sizeof(u32) * bag_i) + 2; | ||
| 102 | *(u16*)(&sb1raw[save_offset]) ^= xor_key16; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | // decrypt some more stuff | ||
| 107 | save_offset = 0x1F4; | ||
| 108 | ptr_to_xor = (u32*)(&sb1raw[save_offset]); | ||
| 109 | *ptr_to_xor ^= xor_key; | ||
| 110 | |||
| 111 | save_offset = 0x490; | ||
| 112 | ptr_to_xor = (u32*)(&sb1raw[save_offset]); | ||
| 113 | *ptr_to_xor ^= xor_key; | ||
| 114 | |||
| 115 | save_offset = 0x494; | ||
| 116 | *(u16*)(&sb1raw[save_offset]) ^= xor_key16; | ||
| 117 | } | ||
| 118 | |||
| 119 | *xor_key_ptr = 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | bool initSaveData( | ||
| 123 | pSaveBlock1* SaveBlock1, | ||
| 124 | pSaveBlock2* SaveBlock2, | ||
| 125 | pSaveBlock3* SaveBlock3) | ||
| 126 | { | ||
| 127 | // check the ROM code, make sure this game is supported. | ||
| 128 | u8* ROM = (u8*) 0x8000000; | ||
| 129 | |||
| 130 | u32 gamecode = (*(u32*)(&ROM[0xAC])); | ||
| 131 | |||
| 132 | void(*loadsave)(char a1); | ||
| 133 | //void(*mainloop)(); | ||
| 134 | //void(*load_pokemon)(); | ||
| 135 | pSaveBlock1 gSaveBlock1; | ||
| 136 | pSaveBlock2 gSaveBlock2; | ||
| 137 | pSaveBlock3 gSaveBlock3; | ||
| 138 | //u32 titlemid = 0; | ||
| 139 | |||
| 140 | // get the address of the save loading function. | ||
| 141 | switch (gamecode) | ||
| 142 | { | ||
| 143 | // --- R/S --- | ||
| 144 | case 'DVXA': // Ruby German | ||
| 145 | case 'DPXA': // Sapphire German | ||
| 146 | { | ||
| 147 | // TODO: detect debug ROM? | ||
| 148 | gSaveBlock1 = (pSaveBlock1) 0x2025734; | ||
| 149 | gSaveBlock2 = (pSaveBlock2) 0x2024EA4; | ||
| 150 | gSaveBlock3 = (pSaveBlock3) 0x20300A0; | ||
| 151 | loadsave = (void(*)(char)) 0x8126249; // same for v1.0 + v1.1 | ||
| 152 | //mainloop = (void(*)()) 0x80003D9; | ||
| 153 | //load_pokemon = (void(*)()) 0x8047da9; | ||
| 154 | |||
| 155 | break; | ||
| 156 | } | ||
| 157 | |||
| 158 | case 'FVXA': // Ruby French | ||
| 159 | case 'FPXA': // Sapphire French | ||
| 160 | { | ||
| 161 | gSaveBlock1 = (pSaveBlock1) 0x2025734; | ||
| 162 | gSaveBlock2 = (pSaveBlock2) 0x2024EA4; | ||
| 163 | gSaveBlock3 = (pSaveBlock3) 0x20300A0; | ||
| 164 | loadsave = (void(*)(char)) 0x8126351; // same for v1.0 + v1.1 | ||
| 165 | //mainloop = (void(*)()) 0x80003D9; | ||
| 166 | //load_pokemon = (void(*)()) 0x8047e95; | ||
| 167 | |||
| 168 | break; | ||
| 169 | } | ||
| 170 | |||
| 171 | case 'IVXA': // Ruby Italian | ||
| 172 | case 'IPXA': // Sapphire Italian | ||
| 173 | { | ||
| 174 | gSaveBlock1 = (pSaveBlock1) 0x2025734; | ||
| 175 | gSaveBlock2 = (pSaveBlock2) 0x2024EA4; | ||
| 176 | gSaveBlock3 = (pSaveBlock3) 0x20300A0; | ||
| 177 | loadsave = (void(*)(char)) 0x8126249; // same for v1.0 + v1.1 | ||
| 178 | //mainloop = (void(*)()) 0x80003D9; | ||
| 179 | //load_pokemon = (void(*)()) 0x8047dbd; | ||
| 180 | |||
| 181 | break; | ||
| 182 | } | ||
| 183 | |||
| 184 | case 'SVXA': // Ruby Spanish | ||
| 185 | case 'SPXA': // Sapphire Spanish | ||
| 186 | { | ||
| 187 | gSaveBlock1 = (pSaveBlock1) 0x2025734; | ||
| 188 | gSaveBlock2 = (pSaveBlock2) 0x2024EA4; | ||
| 189 | gSaveBlock3 = (pSaveBlock3) 0x20300A0; | ||
| 190 | loadsave = (void(*)(char)) 0x8126349; // same for v1.0 + v1.1 | ||
| 191 | //mainloop = (void(*)()) 0x80003D9; | ||
| 192 | //load_pokemon = (void(*)()) 0x8047ea5; | ||
| 193 | |||
| 194 | break; | ||
| 195 | } | ||
| 196 | |||
| 197 | case 'EVXA': // Ruby English | ||
| 198 | case 'EPXA': // Sapphire English | ||
| 199 | { | ||
| 200 | gSaveBlock1 = (pSaveBlock1) 0x2025734; | ||
| 201 | gSaveBlock2 = (pSaveBlock2) 0x2024EA4; | ||
| 202 | gSaveBlock3 = (pSaveBlock3) 0x20300A0; | ||
| 203 | //mainloop = (void(*)()) 0x80002A5; | ||
| 204 | |||
| 205 | // version number | ||
| 206 | switch (ROM[0xBC]) | ||
| 207 | { | ||
| 208 | case 0: | ||
| 209 | { | ||
| 210 | loadsave = (void(*)(char)) 0x8125EC9; | ||
| 211 | //load_pokemon = (void(*)()) 0x8047a85; | ||
| 212 | |||
| 213 | break; | ||
| 214 | } | ||
| 215 | |||
| 216 | case 1: | ||
| 217 | case 2: | ||
| 218 | { | ||
| 219 | loadsave = (void(*)(char)) 0x8125EE9; | ||
| 220 | //load_pokemon = (void(*)()) 0x8047aa5; | ||
| 221 | |||
| 222 | break; | ||
| 223 | } | ||
| 224 | |||
| 225 | default: | ||
| 226 | { | ||
| 227 | return false; // unsupported version | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | break; | ||
| 232 | } | ||
| 233 | |||
| 234 | case 'JVXA': // Ruby Japanese | ||
| 235 | case 'JPXA': // Sapphire Japanese | ||
| 236 | { | ||
| 237 | gSaveBlock1 = (pSaveBlock1) 0x2025494; | ||
| 238 | gSaveBlock2 = (pSaveBlock2) 0x2024C04; | ||
| 239 | gSaveBlock3 = (pSaveBlock3) 0x202FDBC; | ||
| 240 | loadsave = (void(*)(char)) 0x8120d05; // same for v1.0 + v1.1 | ||
| 241 | //mainloop = (void(*)()) 0x80002A9; | ||
| 242 | //load_pokemon = (void(*)()) 0x8044d55; | ||
| 243 | |||
| 244 | break; | ||
| 245 | } | ||
| 246 | |||
| 247 | /// --- FR/LG --- | ||
| 248 | // In FR/LG, the function that initialises the save-block pointers to default does not set up saveblock3. | ||
| 249 | // Which will need to be set up before loading the save if we want boxed Pokémon to not disappear. | ||
| 250 | // Oh, and loadsave() offset is different between FR and LG... | ||
| 251 | |||
| 252 | case 'DRPB': // FireRed German | ||
| 253 | case 'DGPB': // LeafGreen German | ||
| 254 | { | ||
| 255 | gSaveBlock1 = (pSaveBlock1) 0x202552C; | ||
| 256 | gSaveBlock2 = (pSaveBlock2) 0x2024588; | ||
| 257 | gSaveBlock3 = (pSaveBlock3) 0x2029314; | ||
| 258 | *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; | ||
| 259 | loadsave = (void(*)(char)) ( GAME_FR ? 0x80da721 : 0x80da6f5 ); | ||
| 260 | //mainloop = (void(*)()) 0x8000425; | ||
| 261 | //titlemid = 0x80791df; | ||
| 262 | //load_pokemon = (void(*)()) 0x804c251; | ||
| 263 | |||
| 264 | break; | ||
| 265 | } | ||
| 266 | |||
| 267 | case 'FRPB': // FireRed French | ||
| 268 | case 'FGPB': // LeafGreen French | ||
| 269 | { | ||
| 270 | gSaveBlock1 = (pSaveBlock1) 0x202552C; | ||
| 271 | gSaveBlock2 = (pSaveBlock2) 0x2024588; | ||
| 272 | gSaveBlock3 = (pSaveBlock3) 0x2029314; | ||
| 273 | *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; | ||
| 274 | loadsave = (void(*)(char)) ( GAME_FR ? 0x80da7e1 : 0x80da7b5 ); | ||
| 275 | //mainloop = (void(*)()) 0x8000417; | ||
| 276 | //titlemid = 0x807929f; | ||
| 277 | //load_pokemon = (void(*)()) 0x804c311; | ||
| 278 | |||
| 279 | break; | ||
| 280 | } | ||
| 281 | |||
| 282 | case 'IRPB': // FireRed Italian | ||
| 283 | case 'IGPB': // LeafGreen Italian | ||
| 284 | { | ||
| 285 | gSaveBlock1 = (pSaveBlock1) 0x202552C; | ||
| 286 | gSaveBlock2 = (pSaveBlock2) 0x2024588; | ||
| 287 | gSaveBlock3 = (pSaveBlock3) 0x2029314; | ||
| 288 | *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; | ||
| 289 | loadsave = (void(*)(char)) ( GAME_FR ? 0x80da721 : 0x80da6f5 ); | ||
| 290 | //mainloop = (void(*)()) 0x8000425; | ||
| 291 | //titlemid = 0x80791cb; | ||
| 292 | //load_pokemon = (void(*)()) 0x804c23d; | ||
| 293 | |||
| 294 | break; | ||
| 295 | } | ||
| 296 | |||
| 297 | case 'SRPB': // FireRed Spanish | ||
| 298 | case 'SGPB': // LeafGreen Spanish | ||
| 299 | { | ||
| 300 | gSaveBlock1 = (pSaveBlock1) 0x202552C; | ||
| 301 | gSaveBlock2 = (pSaveBlock2) 0x2024588; | ||
| 302 | gSaveBlock3 = (pSaveBlock3) 0x2029314; | ||
| 303 | *(pSaveBlock3*)(0x3004f60) = gSaveBlock3; | ||
| 304 | loadsave = (void(*)(char)) ( GAME_FR ? 0x80da809 : 0x80da7dd ); | ||
| 305 | //mainloop = (void(*)()) 0x8000417; | ||
| 306 | //titlemid = 0x80792b3; | ||
| 307 | //load_pokemon = (void(*)()) 0x804c325; | ||
| 308 | |||
| 309 | break; | ||
| 310 | } | ||
| 311 | |||
| 312 | case 'ERPB': // FireRed English | ||
| 313 | case 'EGPB': // LeafGreen English | ||
| 314 | { | ||
| 315 | gSaveBlock1 = (pSaveBlock1) 0x202552C; | ||
| 316 | gSaveBlock2 = (pSaveBlock2) 0x2024588; | ||
| 317 | gSaveBlock3 = (pSaveBlock3) 0x2029314; | ||
| 318 | *(pSaveBlock3*)(0x3005010) = gSaveBlock3; | ||
| 319 | |||
| 320 | // version number | ||
| 321 | switch (ROM[0xBC]) | ||
| 322 | { | ||
| 323 | case 0: | ||
| 324 | { | ||
| 325 | loadsave = (void(*)(char)) ( GAME_FR ? 0x80da4fd : 0x80da4d1 ); | ||
| 326 | //mainloop = (void(*)()) 0x800041b; | ||
| 327 | //titlemid = 0x807927b; | ||
| 328 | //load_pokemon = (void(*)()) 0x804c231; | ||
| 329 | |||
| 330 | break; | ||
| 331 | } | ||
| 332 | |||
| 333 | case 1: | ||
| 334 | { | ||
| 335 | loadsave = (void(*)(char)) ( GAME_FR ? 0x80da511 : 0x80da4e5 ); | ||
| 336 | //mainloop = (void(*)()) 0x8000429; | ||
| 337 | //titlemid = 0x807928f; | ||
| 338 | //load_pokemon = (void(*)()) 0x804c245; | ||
| 339 | |||
| 340 | break; | ||
| 341 | } | ||
| 342 | |||
| 343 | default: | ||
| 344 | { | ||
| 345 | return false; // unsupported version | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | break; | ||
| 350 | } | ||
| 351 | |||
| 352 | case 'JRPB': // FireRed Japanese | ||
| 353 | case 'JGPB': // LeafGreen Japanese | ||
| 354 | { | ||
| 355 | gSaveBlock1 = (pSaveBlock1) 0x202548C; | ||
| 356 | gSaveBlock2 = (pSaveBlock2) 0x20244E8; | ||
| 357 | gSaveBlock3 = (pSaveBlock3) 0x202924C; | ||
| 358 | *(pSaveBlock3*)(0x3005050) = gSaveBlock3; | ||
| 359 | |||
| 360 | // version number | ||
| 361 | switch (ROM[0xBC]) | ||
| 362 | { | ||
| 363 | case 0: | ||
| 364 | { | ||
| 365 | loadsave = (void(*)(char)) ( GAME_FR ? 0x80db4e5 : 0x80db4b9 ); | ||
| 366 | //mainloop = (void(*)()) 0x800041b; | ||
| 367 | //titlemid = 0x8078a0f; | ||
| 368 | //load_pokemon = (void(*)()) 0x804b9e9; | ||
| 369 | |||
| 370 | break; | ||
| 371 | } | ||
| 372 | |||
| 373 | case 1: | ||
| 374 | { | ||
| 375 | if ((gamecode << 8) == 'GPB\x00') | ||
| 376 | { | ||
| 377 | // LeafGreen v1.1 Japanese is undumped. | ||
| 378 | // Therefore, it is unsupported. | ||
| 379 | // I will make guesses at the offsets in the comments, but I will not actually implement them until LeafGreen v1.1 is dumped. | ||
| 380 | |||
| 381 | return false; | ||
| 382 | } | ||
| 383 | |||
| 384 | loadsave = (void(*)(char)) 0x80db529; // potential LG1.1 address: 0x80db4fd | ||
| 385 | //mainloop = (void(*)()) 0x8000417; | ||
| 386 | //titlemid = 0x8078987; | ||
| 387 | //load_pokemon = (void(*)()) 0x804b9c5; | ||
| 388 | |||
| 389 | break; | ||
| 390 | } | ||
| 391 | |||
| 392 | default: | ||
| 393 | { | ||
| 394 | return false; // unsupported version | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | break; | ||
| 399 | } | ||
| 400 | |||
| 401 | /// --- Emerald --- | ||
| 402 | // In Emerald, the saveblock pointer that isn't set up is saveblock1 (in FR/LG it was saveblock3). | ||
| 403 | // The initial save loading code after the copyright screen is also updated, now it sets up ASLR/crypto here before loading the save. | ||
| 404 | |||
| 405 | case 'DEPB': // Emerald German | ||
| 406 | { | ||
| 407 | gSaveBlock1 = (pSaveBlock1) 0x2025A00; | ||
| 408 | gSaveBlock2 = (pSaveBlock2) 0x2024A54; | ||
| 409 | gSaveBlock3 = (pSaveBlock3) 0x2029808; | ||
| 410 | *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1; | ||
| 411 | loadsave = (void(*)(char)) 0x8153075; | ||
| 412 | //mainloop = (void(*)()) 0x800042b; | ||
| 413 | //titlemid = 0x816fdb5; | ||
| 414 | //load_pokemon = (void(*)()) 0x8076dd5; | ||
| 415 | |||
| 416 | break; | ||
| 417 | } | ||
| 418 | |||
| 419 | case 'FEPB': // Emerald French | ||
| 420 | { | ||
| 421 | gSaveBlock1 = (pSaveBlock1) 0x2025A00; | ||
| 422 | gSaveBlock2 = (pSaveBlock2) 0x2024A54; | ||
| 423 | gSaveBlock3 = (pSaveBlock3) 0x2029808; | ||
| 424 | *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1; | ||
| 425 | loadsave = (void(*)(char)) 0x815319d; | ||
| 426 | //mainloop = (void(*)()) 0x800042b; | ||
| 427 | //titlemid = 0x816fedd; | ||
| 428 | //load_pokemon = (void(*)()) 0x8076dd1; | ||
| 429 | |||
| 430 | break; | ||
| 431 | } | ||
| 432 | |||
| 433 | case 'IEPB': // Emerald Italian | ||
| 434 | { | ||
| 435 | gSaveBlock1 = (pSaveBlock1) 0x2025A00; | ||
| 436 | gSaveBlock2 = (pSaveBlock2) 0x2024A54; | ||
| 437 | gSaveBlock3 = (pSaveBlock3) 0x2029808; | ||
| 438 | *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1; | ||
| 439 | loadsave = (void(*)(char)) 0x8153065; | ||
| 440 | //mainloop = (void(*)()) 0x800042b; | ||
| 441 | //titlemid = 0x816fda5; | ||
| 442 | //load_pokemon = (void(*)()) 0x8076dd5; | ||
| 443 | |||
| 444 | break; | ||
| 445 | } | ||
| 446 | |||
| 447 | case 'SEPB': // Emerald Spanish | ||
| 448 | { | ||
| 449 | gSaveBlock1 = (pSaveBlock1) 0x2025A00; | ||
| 450 | gSaveBlock2 = (pSaveBlock2) 0x2024A54; | ||
| 451 | gSaveBlock3 = (pSaveBlock3) 0x2029808; | ||
| 452 | *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1; | ||
| 453 | loadsave = (void(*)(char)) 0x8153175; | ||
| 454 | //mainloop = (void(*)()) 0x800042b; | ||
| 455 | //titlemid = 0x816feb5; | ||
| 456 | //load_pokemon = (void(*)()) 0x8076dd1; | ||
| 457 | |||
| 458 | break; | ||
| 459 | } | ||
| 460 | |||
| 461 | case 'EEPB': // Emerald English | ||
| 462 | { | ||
| 463 | gSaveBlock1 = (pSaveBlock1) 0x2025A00; | ||
| 464 | gSaveBlock2 = (pSaveBlock2) 0x2024A54; | ||
| 465 | gSaveBlock3 = (pSaveBlock3) 0x2029808; | ||
| 466 | *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1; | ||
| 467 | loadsave = (void(*)(char)) 0x81534d1; | ||
| 468 | //mainloop = (void(*)()) 0x800042b; | ||
| 469 | //titlemid = 0x817014d; | ||
| 470 | //load_pokemon = (void(*)()) 0x8076dd5; | ||
| 471 | |||
| 472 | break; | ||
| 473 | } | ||
| 474 | |||
| 475 | case 'JEPB': // Emerald Japanese | ||
| 476 | { | ||
| 477 | gSaveBlock1 = (pSaveBlock1) 0x20256A4; | ||
| 478 | gSaveBlock2 = (pSaveBlock2) 0x20246F8; | ||
| 479 | gSaveBlock3 = (pSaveBlock3) 0x20294AC; | ||
| 480 | *(pSaveBlock1*)(0x3005aec) = gSaveBlock1; | ||
| 481 | loadsave = (void(*)(char)) 0x815340d; | ||
| 482 | //mainloop = (void(*)()) 0x800042b; | ||
| 483 | //titlemid = 0x816ff45; | ||
| 484 | //load_pokemon = (void(*)()) 0x80767dd; | ||
| 485 | |||
| 486 | break; | ||
| 487 | } | ||
| 488 | |||
| 489 | default: | ||
| 490 | { | ||
| 491 | return false; // this game isn't supported | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 | loadsave(0); | ||
| 496 | |||
| 497 | // now the save is loaded, we can do what we want with the loaded blocks. | ||
| 498 | // first, we're going to want to decrypt the parts that are crypted, if applicable. | ||
| 499 | decryptSaveStructures(gSaveBlock1,gSaveBlock2,gSaveBlock3); | ||
| 500 | |||
| 501 | *SaveBlock1 = gSaveBlock1; | ||
| 502 | *SaveBlock2 = gSaveBlock2; | ||
| 503 | *SaveBlock3 = gSaveBlock3; | ||
| 504 | |||
| 505 | /* | ||
| 506 | // time to call the payload. | ||
| 507 | payload(gSaveBlock1,gSaveBlock2,gSaveBlock3); | ||
| 508 | // Now, we better call the function that sets the pokemon-related stuff from the structure elements of the loaded save again. | ||
| 509 | // Just in case the payload did something with that. | ||
| 510 | load_pokemon(); | ||
| 511 | // In FR/LG/Emerald, just returning to the game is unwise. | ||
| 512 | // The game reloads the savefile. | ||
| 513 | // 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) | ||
| 514 | // So, better bypass the title screen and get the game to return directly to the Continue/New Game screen. | ||
| 515 | // 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. | ||
| 516 | // 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. | ||
| 517 | // Here goes... | ||
| 518 | if (titlemid) { | ||
| 519 | // Function reserves an extra 4 bytes of stack space in FireRed/LeafGreen, and none in Emerald. | ||
| 520 | call_into_middle_of_titlescreen_func(titlemid,(GAME_EM ? 0 : 4)); | ||
| 521 | } | ||
| 522 | // Now we've done what we want, time to return to the game. | ||
| 523 | // Can't just return, the game will reload the save. | ||
| 524 | // So let's just call the main-loop directly ;) | ||
| 525 | // turn the sound back on before we head back to the game | ||
| 526 | *(vu16 *)(REG_BASE + 0x84) = 0x8f; | ||
| 527 | // re-enable interrupts | ||
| 528 | REG_IME = 1; | ||
| 529 | mainloop(); | ||
| 530 | // Anything past here will not be executed. | ||
| 531 | return 0; | ||
| 532 | */ | ||
| 533 | return true; | ||
| 534 | } | ||
| 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 @@ | |||
| 1 | #ifndef _GAMEDATA_H_ | ||
| 2 | #define _GAMEDATA_H_ | ||
| 3 | |||
| 4 | #include <gba.h> | ||
| 5 | #include "saveblocks.h" | ||
| 6 | |||
| 7 | #define GAME_RUBY (((*(u32*)(0x80000AC)) << 8) == 'VXA\x00') | ||
| 8 | #define GAME_SAPP (((*(u32*)(0x80000AC)) << 8) == 'PXA\x00') | ||
| 9 | #define GAME_RS ((GAME_RUBY) || (GAME_SAPP)) | ||
| 10 | #define GAME_FR (((*(u32*)(0x80000AC)) << 8) == 'RPB\x00') | ||
| 11 | #define GAME_LG (((*(u32*)(0x80000AC)) << 8) == 'GPB\x00') | ||
| 12 | #define GAME_FRLG ((GAME_FR) || (GAME_LG)) | ||
| 13 | #define GAME_EM (((*(u32*)(0x80000AC)) << 8) == 'EPB\x00') | ||
| 14 | |||
| 15 | #define LANG_JAPAN ((*(u8*)(0x80000AF)) == 'J') | ||
| 16 | |||
| 17 | bool initSaveData( | ||
| 18 | pSaveBlock1* SaveBlock1, | ||
| 19 | pSaveBlock2* SaveBlock2, | ||
| 20 | pSaveBlock3* SaveBlock3); | ||
| 21 | |||
| 22 | #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 @@ | |||
| 1 | #include "link.h" | ||
| 2 | |||
| 3 | #define JOY_WRITE 2 | ||
| 4 | #define JOY_READ 4 | ||
| 5 | #define JOY_RW 6 | ||
| 6 | |||
| 7 | void waitForWriteAccess() | ||
| 8 | { | ||
| 9 | //while ((REG_HS_CTRL & JOY_READ) == 0); | ||
| 10 | while ((REG_HS_CTRL & JOY_WRITE) == 0); | ||
| 11 | REG_HS_CTRL |= JOY_RW; | ||
| 12 | } | ||
| 13 | |||
| 14 | void waitForAck() | ||
| 15 | { | ||
| 16 | while ((REG_HS_CTRL & JOY_WRITE) == 0); | ||
| 17 | REG_HS_CTRL |= JOY_RW; | ||
| 18 | REG_JOYTR = 0; | ||
| 19 | while ((REG_HS_CTRL & JOY_WRITE) == 0); | ||
| 20 | REG_HS_CTRL |= JOY_RW; | ||
| 21 | } | ||
| 22 | |||
| 23 | void sendS32(s32 val) | ||
| 24 | { | ||
| 25 | REG_JOYTR = val; | ||
| 26 | //waitForWriteAccess(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void sendU32(u32 val) | ||
| 30 | { | ||
| 31 | REG_JOYTR = val; | ||
| 32 | //waitForWriteAccess(); | ||
| 33 | } | ||
| 34 | |||
| 35 | u32 recieveU32() | ||
| 36 | { | ||
| 37 | while ((REG_HS_CTRL & JOY_WRITE) == 0); | ||
| 38 | REG_HS_CTRL |= JOY_RW; | ||
| 39 | return REG_JOYRE; | ||
| 40 | } | ||
| 41 | |||
| 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 @@ | |||
| 1 | #ifndef _LINK_H_ | ||
| 2 | #define _LINK_H_ | ||
| 3 | |||
| 4 | #include <gba.h> | ||
| 5 | |||
| 6 | void waitForWriteAccess(); | ||
| 7 | void waitForAck(); | ||
| 8 | void sendS32(s32 val); | ||
| 9 | void sendU32(u32 val); | ||
| 10 | u32 recieveU32(); | ||
| 11 | |||
| 12 | #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 @@ | |||
| 8 | #include <stdio.h> | 8 | #include <stdio.h> |
| 9 | #include <stdlib.h> | 9 | #include <stdlib.h> |
| 10 | #include "libSave.h" | 10 | #include "libSave.h" |
| 11 | #include "gamedata.h" | ||
| 12 | #include "link.h" | ||
| 11 | 13 | ||
| 12 | #define REG_WAITCNT *(vu16 *)(REG_BASE + 0x204) | 14 | #define REG_WAITCNT *(vu16 *)(REG_BASE + 0x204) |
| 13 | #define JOY_WRITE 2 | 15 | #define JOY_WRITE 2 |
| @@ -18,224 +20,398 @@ u8 save_data[0x20000] __attribute__ ((section (".sbss"))); | |||
| 18 | 20 | ||
| 19 | s32 getGameSize(void) | 21 | s32 getGameSize(void) |
| 20 | { | 22 | { |
| 21 | if(*(vu32*)(0x08000004) != 0x51AEFF24) | 23 | if(*(vu32*)(0x08000004) != 0x51AEFF24) |
| 22 | return -1; | 24 | return -1; |
| 23 | s32 i; | 25 | s32 i; |
| 24 | for(i = (1<<20); i < (1<<25); i<<=1) | 26 | for(i = (1<<20); i < (1<<25); i<<=1) |
| 27 | { | ||
| 28 | vu16 *rompos = (vu16*)(0x08000000+i); | ||
| 29 | int j; | ||
| 30 | bool romend = true; | ||
| 31 | for(j = 0; j < 0x1000; j++) | ||
| 32 | { | ||
| 33 | if(rompos[j] != j) | ||
| 34 | { | ||
| 35 | romend = false; | ||
| 36 | break; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | if(romend) break; | ||
| 40 | } | ||
| 41 | return i; | ||
| 42 | } | ||
| 43 | |||
| 44 | |||
| 45 | // === (from tonc_memdef.h) =========================================== | ||
| 46 | |||
| 47 | // --- REG_DISPCNT defines --- | ||
| 48 | #define DCNT_MODE0 0x0000 | ||
| 49 | #define DCNT_MODE1 0x0001 | ||
| 50 | #define DCNT_MODE2 0x0002 | ||
| 51 | #define DCNT_MODE3 0x0003 | ||
| 52 | #define DCNT_MODE4 0x0004 | ||
| 53 | #define DCNT_MODE5 0x0005 | ||
| 54 | // layers | ||
| 55 | #define DCNT_BG0 0x0100 | ||
| 56 | #define DCNT_BG1 0x0200 | ||
| 57 | #define DCNT_BG2 0x0400 | ||
| 58 | #define DCNT_BG3 0x0800 | ||
| 59 | #define DCNT_OBJ 0x1000 | ||
| 60 | typedef u16 COLOR; | ||
| 61 | #define MEM_VRAM 0x06000000 | ||
| 62 | #define SCREEN_WIDTH 240 | ||
| 63 | #define vid_mem ((u16*)MEM_VRAM) | ||
| 64 | static inline void m3_plot(int x, int y, COLOR clr) | ||
| 65 | { vid_mem[y*SCREEN_WIDTH+x]= clr; } | ||
| 66 | static inline COLOR RGB15(u32 red, u32 green, u32 blue) | ||
| 67 | { return red | (green<<5) | (blue<<10); } | ||
| 68 | void plot_sqr(int x, int y, COLOR clr) | ||
| 69 | { | ||
| 70 | /*for (int j=0;j<8; j++) | ||
| 25 | { | 71 | { |
| 26 | vu16 *rompos = (vu16*)(0x08000000+i); | 72 | for (int i=0;i<8; i++) |
| 27 | int j; | ||
| 28 | bool romend = true; | ||
| 29 | for(j = 0; j < 0x1000; j++) | ||
| 30 | { | 73 | { |
| 31 | if(rompos[j] != j) | 74 | vid_mem[(y*8+j+32)*SCREEN_WIDTH+x*8+i+32] = clr; |
| 32 | { | ||
| 33 | romend = false; | ||
| 34 | break; | ||
| 35 | } | ||
| 36 | } | 75 | } |
| 37 | if(romend) break; | ||
| 38 | } | 76 | } |
| 39 | return i; | 77 | vid_mem[(y*8+1+32)*SCREEN_WIDTH+x*8+1+32] = RGB15(31,31,31);*/ |
| 78 | } | ||
| 79 | void m3_fill(COLOR clr) | ||
| 80 | { | ||
| 81 | /*int ii; | ||
| 82 | u32 *dst= (u32*)vid_mem; | ||
| 83 | u32 wd= (clr<<16) | clr; | ||
| 84 | |||
| 85 | for(ii=0; ii<SCREEN_WIDTH/4; ii++) | ||
| 86 | *dst++= wd;*/ | ||
| 40 | } | 87 | } |
| 41 | 88 | ||
| 42 | //--------------------------------------------------------------------------------- | 89 | //--------------------------------------------------------------------------------- |
| 43 | // Program entry point | 90 | // Program entry point |
| 44 | //--------------------------------------------------------------------------------- | 91 | //--------------------------------------------------------------------------------- |
| 45 | int main(void) { | 92 | int main(void) |
| 46 | //--------------------------------------------------------------------------------- | 93 | { |
| 94 | //REG_IME = 0; | ||
| 95 | //REG_DISPCNT= DCNT_MODE3 | DCNT_BG2; | ||
| 96 | m3_fill(RGB15(31,31,31)); | ||
| 97 | plot_sqr( 4, 4, RGB15(31, 0, 0) ); // or CLR_RED | ||
| 47 | 98 | ||
| 48 | // the vblank interrupt must be enabled for VBlankIntrWait() to work | 99 | |
| 49 | // since the default dispatcher handles the bios flags no vblank handler | 100 | |
| 50 | // is required | 101 | //*(vu16 *)(REG_BASE + 0x84) = 0x8f; |
| 51 | irqInit(); | 102 | //REG_IME = 1; |
| 52 | irqEnable(IRQ_VBLANK); | 103 | // the vblank interrupt must be enabled for VBlankIntrWait() to work |
| 53 | 104 | // since the default dispatcher handles the bios flags no vblank handler | |
| 54 | consoleDemoInit(); | 105 | // is required |
| 55 | REG_JOYTR = 0; | 106 | //irqInit(); |
| 56 | // ansi escape sequence to set print co-ordinates | 107 | //irqEnable(IRQ_VBLANK); |
| 57 | // /x1b[line;columnH | 108 | |
| 58 | u32 i; | 109 | //consoleDemoInit(); |
| 59 | iprintf("\x1b[9;2HGBA Link Cable Dumper v1.6\n"); | 110 | //REG_JOYTR = 0; |
| 60 | iprintf("\x1b[10;4HPlease look at the TV\n"); | 111 | |
| 61 | // disable this, needs power | 112 | // ansi escape sequence to set print co-ordinates |
| 62 | SNDSTAT = 0; | 113 | // /x1b[line;columnH |
| 63 | SNDBIAS = 0; | 114 | //u32 i; |
| 64 | // Set up waitstates for EEPROM access etc. | 115 | //iprintf("\x1b[9;2HPokemon Gen III Data Extractor\n"); |
| 65 | REG_WAITCNT = 0x0317; | 116 | //iprintf("\x1b[10;4HPlease look at the TV\n"); |
| 66 | //clear out previous messages | 117 | |
| 67 | REG_HS_CTRL |= JOY_RW; | 118 | // disable this, needs power |
| 68 | while (1) { | 119 | //SNDSTAT = 0; |
| 69 | if(REG_HS_CTRL&JOY_READ) | 120 | //SNDBIAS = 0; |
| 70 | { | 121 | |
| 71 | REG_HS_CTRL |= JOY_RW; | 122 | // Set up waitstates for EEPROM access etc. |
| 72 | s32 gamesize = getGameSize(); | 123 | //REG_WAITCNT = 0x0317; |
| 73 | u32 savesize = SaveSize(save_data,gamesize); | 124 | |
| 74 | REG_JOYTR = gamesize; | 125 | //clear out previous messages |
| 75 | //wait for a cmd receive for safety | 126 | REG_HS_CTRL |= JOY_RW; |
| 76 | while((REG_HS_CTRL&JOY_WRITE) == 0) ; | 127 | sendU32(0); |
| 77 | REG_HS_CTRL |= JOY_RW; | 128 | plot_sqr( 4, 5, RGB15( 0,31, 0) ); // or CLR_LIME |
| 78 | REG_JOYTR = savesize; | 129 | while (1) |
| 79 | //wait for a cmd receive for safety | 130 | { |
| 80 | while((REG_HS_CTRL&JOY_WRITE) == 0) ; | 131 | waitForWriteAccess(); |
| 81 | REG_HS_CTRL |= JOY_RW; | 132 | //while (recieveU32() != 0); |
| 82 | if(gamesize == -1) | 133 | //waitForAck(); |
| 83 | { | 134 | plot_sqr( 4, 6, RGB15( 0, 0,31) ); // or CLR_BLUE |
| 84 | REG_JOYTR = 0; | 135 | // Send game size to acknowledge that an actual cart is inserted. |
| 85 | continue; //nothing to read | 136 | //s32 gamesize = getGameSize(); |
| 86 | } | 137 | //sendS32(gamesize); |
| 87 | //game in, send header | 138 | //waitForAck(); |
| 88 | for(i = 0; i < 0xC0; i+=4) | 139 | |
| 89 | { | 140 | // If the game size is illegal, start over. |
| 90 | REG_JOYTR = *(vu32*)(0x08000000+i); | 141 | //if (gamesize == -1) |
| 91 | while((REG_HS_CTRL&JOY_READ) == 0) ; | 142 | //{ |
| 92 | REG_HS_CTRL |= JOY_RW; | 143 | // sendS32(0); |
| 93 | } | 144 | // |
| 94 | REG_JOYTR = 0; | 145 | // continue; |
| 95 | //wait for other side to choose | 146 | //} |
| 96 | while((REG_HS_CTRL&JOY_WRITE) == 0) ; | 147 | |
| 97 | REG_HS_CTRL |= JOY_RW; | 148 | // Identify the inserted game. |
| 98 | u32 choseval = REG_JOYRE; | 149 | if (GAME_RUBY) |
| 99 | if(choseval == 0) | 150 | { |
| 100 | { | 151 | sendS32(1); |
| 101 | REG_JOYTR = 0; | 152 | } else if (GAME_SAPP) |
| 102 | continue; //nothing to read | 153 | { |
| 103 | } | 154 | sendS32(2); |
| 104 | else if(choseval == 1) | 155 | } else if (GAME_FR) |
| 105 | { | 156 | { |
| 106 | //disable interrupts | 157 | sendS32(3); |
| 107 | u32 prevIrqMask = REG_IME; | 158 | } else if (GAME_LG) |
| 108 | REG_IME = 0; | 159 | { |
| 109 | //dump the game | 160 | sendS32(4); |
| 110 | for(i = 0; i < gamesize; i+=4) | 161 | } else if (GAME_EM) |
| 111 | { | 162 | { |
| 112 | REG_JOYTR = *(vu32*)(0x08000000+i); | 163 | sendS32(5); |
| 113 | while((REG_HS_CTRL&JOY_READ) == 0) ; | 164 | } else { |
| 114 | REG_HS_CTRL |= JOY_RW; | 165 | sendS32(-1); |
| 115 | } | 166 | waitForAck(); |
| 116 | //restore interrupts | 167 | |
| 117 | REG_IME = prevIrqMask; | 168 | sendS32(0); |
| 118 | } | 169 | |
| 119 | else if(choseval == 2) | 170 | continue; |
| 120 | { | 171 | } |
| 121 | //disable interrupts | 172 | plot_sqr( 5, 4, RGB15( 31, 0,31) ); |
| 122 | u32 prevIrqMask = REG_IME; | 173 | waitForAck(); |
| 123 | REG_IME = 0; | 174 | plot_sqr( 5, 5, RGB15( 16, 16,16) ); |
| 124 | //backup save | 175 | // Get access to save data. |
| 125 | switch (savesize){ | 176 | pSaveBlock1 SaveBlock1; |
| 126 | case 0x200: | 177 | pSaveBlock2 SaveBlock2; |
| 127 | GetSave_EEPROM_512B(save_data); | 178 | pSaveBlock3 SaveBlock3; |
| 128 | break; | 179 | if (!initSaveData(&SaveBlock1, &SaveBlock2, &SaveBlock3)) |
| 129 | case 0x2000: | 180 | { |
| 130 | GetSave_EEPROM_8KB(save_data); | 181 | // Unsupported game version. |
| 131 | break; | 182 | //iprintf("Unsupported game version\n"); |
| 132 | case 0x8000: | 183 | sendS32(-1); |
| 133 | GetSave_SRAM_32KB(save_data); | 184 | waitForAck(); |
| 134 | break; | 185 | |
| 135 | case 0x10000: | 186 | sendS32(0); |
| 136 | GetSave_FLASH_64KB(save_data); | 187 | |
| 137 | break; | 188 | continue; |
| 138 | case 0x20000: | 189 | } |
| 139 | GetSave_FLASH_128KB(save_data); | 190 | plot_sqr( 5, 6, RGB15( 0, 31,16) ); |
| 140 | break; | 191 | sendS32(1); |
| 141 | default: | 192 | waitForAck(); |
| 142 | break; | 193 | /* |
| 143 | } | 194 | // Send trainer name. |
| 144 | //restore interrupts | 195 | u8* trainerName; |
| 145 | REG_IME = prevIrqMask; | 196 | |
| 146 | //say gc side we read it | 197 | if (GAME_RS) |
| 147 | REG_JOYTR = savesize; | 198 | { |
| 148 | //wait for a cmd receive for safety | 199 | trainerName = SaveBlock2->rs.playerName; |
| 149 | while((REG_HS_CTRL&JOY_WRITE) == 0) ; | 200 | } else if (GAME_FRLG) |
| 150 | REG_HS_CTRL |= JOY_RW; | 201 | { |
| 151 | //send the save | 202 | trainerName = SaveBlock2->frlg.playerName; |
| 152 | for(i = 0; i < savesize; i+=4) | 203 | } else if (GAME_EM) |
| 153 | { | 204 | { |
| 154 | REG_JOYTR = *(vu32*)(save_data+i); | 205 | trainerName = SaveBlock2->e.playerName; |
| 155 | while((REG_HS_CTRL&JOY_READ) == 0) ; | 206 | } |
| 156 | REG_HS_CTRL |= JOY_RW; | 207 | iprintf("%d\n", trainerName[0]); |
| 157 | } | 208 | iprintf("%d\n", trainerName[1]); |
| 158 | } | 209 | iprintf("%d\n", trainerName[2]); |
| 159 | else if(choseval == 3 || choseval == 4) | 210 | iprintf("%d\n", trainerName[3]); |
| 160 | { | 211 | iprintf("%d\n", trainerName[4]); |
| 161 | REG_JOYTR = savesize; | 212 | iprintf("%d\n", trainerName[5]); |
| 162 | if(choseval == 3) | 213 | iprintf("%d\n", trainerName[6]); |
| 163 | { | 214 | iprintf("%d\n", trainerName[7]); |
| 164 | //receive the save | 215 | |
| 165 | for(i = 0; i < savesize; i+=4) | 216 | u32 tn1 = |
| 166 | { | 217 | (trainerName[0] << 24) |
| 167 | while((REG_HS_CTRL&JOY_WRITE) == 0) ; | 218 | | (trainerName[1] << 16) |
| 168 | REG_HS_CTRL |= JOY_RW; | 219 | | (trainerName[2] << 8) |
| 169 | *(vu32*)(save_data+i) = REG_JOYRE; | 220 | | (trainerName[3]); |
| 170 | } | 221 | |
| 171 | } | 222 | u32 tn2 = |
| 172 | else | 223 | (trainerName[4] << 24) |
| 173 | { | 224 | | (trainerName[5] << 16) |
| 174 | //clear the save | 225 | | (trainerName[6] << 8) |
| 175 | for(i = 0; i < savesize; i+=4) | 226 | | (trainerName[7]); |
| 176 | *(vu32*)(save_data+i) = 0; | 227 | |
| 177 | } | 228 | sendU32(tn1); |
| 178 | //disable interrupts | 229 | waitForAck(); |
| 179 | u32 prevIrqMask = REG_IME; | 230 | |
| 180 | REG_IME = 0; | 231 | sendU32(tn2); |
| 181 | //write it | 232 | waitForAck(); |
| 182 | switch (savesize){ | 233 | */ |
| 183 | case 0x200: | 234 | // Send trainer ID. |
| 184 | PutSave_EEPROM_512B(save_data); | 235 | u8* trainerId = 0; |
| 185 | break; | 236 | |
| 186 | case 0x2000: | 237 | if (GAME_RS) |
| 187 | PutSave_EEPROM_8KB(save_data); | 238 | { |
| 188 | break; | 239 | trainerId = SaveBlock2->rs.playerTrainerId; |
| 189 | case 0x8000: | 240 | } else if (GAME_FRLG) |
| 190 | PutSave_SRAM_32KB(save_data); | 241 | { |
| 191 | break; | 242 | trainerId = SaveBlock2->frlg.playerTrainerId; |
| 192 | case 0x10000: | 243 | } else if (GAME_EM) |
| 193 | PutSave_FLASH_64KB(save_data); | 244 | { |
| 194 | break; | 245 | trainerId = SaveBlock2->e.playerTrainerId; |
| 195 | case 0x20000: | 246 | } |
| 196 | PutSave_FLASH_128KB(save_data); | 247 | |
| 197 | break; | 248 | u32 tti = |
| 198 | default: | 249 | (trainerId[2] << 8) |
| 199 | break; | 250 | | (trainerId[3]); |
| 200 | } | 251 | |
| 201 | //restore interrupts | 252 | sendU32(tti); |
| 202 | REG_IME = prevIrqMask; | 253 | waitForAck(); |
| 203 | //say gc side we're done | 254 | |
| 204 | REG_JOYTR = 0; | 255 | // Restart, because we're just testing. |
| 205 | //wait for a cmd receive for safety | 256 | sendS32(0); |
| 206 | while((REG_HS_CTRL&JOY_WRITE) == 0) ; | 257 | //continue; |
| 207 | REG_HS_CTRL |= JOY_RW; | 258 | break; |
| 208 | } | 259 | |
| 209 | REG_JOYTR = 0; | 260 | /* |
| 210 | } | 261 | |
| 211 | else if(REG_HS_CTRL&JOY_WRITE) | 262 | |
| 212 | { | 263 | //game in, send header |
| 213 | REG_HS_CTRL |= JOY_RW; | 264 | for(i = 0; i < 0xC0; i+=4) |
| 214 | u32 choseval = REG_JOYRE; | 265 | { |
| 215 | if(choseval == 5) | 266 | REG_JOYTR = *(vu32*)(0x08000000+i); |
| 216 | { | 267 | while((REG_HS_CTRL&JOY_READ) == 0) ; |
| 217 | //disable interrupts | 268 | REG_HS_CTRL |= JOY_RW; |
| 218 | u32 prevIrqMask = REG_IME; | 269 | } |
| 219 | REG_IME = 0; | 270 | REG_JOYTR = 0; |
| 220 | //dump BIOS | 271 | //wait for other side to choose |
| 221 | for (i = 0; i < 0x4000; i+=4) | 272 | while((REG_HS_CTRL&JOY_WRITE) == 0) ; |
| 222 | { | 273 | REG_HS_CTRL |= JOY_RW; |
| 223 | // the lower bits are inaccurate, so just get it four times :) | 274 | u32 choseval = REG_JOYRE; |
| 224 | u32 a = MidiKey2Freq((WaveData *)(i-4), 180-12, 0) * 2; | 275 | if(choseval == 0) |
| 225 | u32 b = MidiKey2Freq((WaveData *)(i-3), 180-12, 0) * 2; | 276 | { |
| 226 | u32 c = MidiKey2Freq((WaveData *)(i-2), 180-12, 0) * 2; | 277 | REG_JOYTR = 0; |
| 227 | u32 d = MidiKey2Freq((WaveData *)(i-1), 180-12, 0) * 2; | 278 | continue; //nothing to read |
| 228 | REG_JOYTR = ((a>>24<<24) | (d>>24<<16) | (c>>24<<8) | (b>>24)); | 279 | } |
| 229 | while((REG_HS_CTRL&JOY_READ) == 0) ; | 280 | else if(choseval == 1) |
| 230 | REG_HS_CTRL |= JOY_RW; | 281 | { |
| 231 | } | 282 | //disable interrupts |
| 232 | //restore interrupts | 283 | u32 prevIrqMask = REG_IME; |
| 233 | REG_IME = prevIrqMask; | 284 | REG_IME = 0; |
| 234 | } | 285 | //dump the game |
| 235 | REG_JOYTR = 0; | 286 | for(i = 0; i < gamesize; i+=4) |
| 236 | } | 287 | { |
| 237 | Halt(); | 288 | REG_JOYTR = *(vu32*)(0x08000000+i); |
| 238 | } | 289 | while((REG_HS_CTRL&JOY_READ) == 0) ; |
| 290 | REG_HS_CTRL |= JOY_RW; | ||
| 291 | } | ||
| 292 | //restore interrupts | ||
| 293 | REG_IME = prevIrqMask; | ||
| 294 | } | ||
| 295 | else if(choseval == 2) | ||
| 296 | { | ||
| 297 | //disable interrupts | ||
| 298 | u32 prevIrqMask = REG_IME; | ||
| 299 | REG_IME = 0; | ||
| 300 | //backup save | ||
| 301 | switch (savesize){ | ||
| 302 | case 0x200: | ||
| 303 | GetSave_EEPROM_512B(save_data); | ||
| 304 | break; | ||
| 305 | case 0x2000: | ||
| 306 | GetSave_EEPROM_8KB(save_data); | ||
| 307 | break; | ||
| 308 | case 0x8000: | ||
| 309 | GetSave_SRAM_32KB(save_data); | ||
| 310 | break; | ||
| 311 | case 0x10000: | ||
| 312 | GetSave_FLASH_64KB(save_data); | ||
| 313 | break; | ||
| 314 | case 0x20000: | ||
| 315 | GetSave_FLASH_128KB(save_data); | ||
| 316 | break; | ||
| 317 | default: | ||
| 318 | break; | ||
| 319 | } | ||
| 320 | //restore interrupts | ||
| 321 | REG_IME = prevIrqMask; | ||
| 322 | //say gc side we read it | ||
| 323 | REG_JOYTR = savesize; | ||
| 324 | //wait for a cmd receive for safety | ||
| 325 | while((REG_HS_CTRL&JOY_WRITE) == 0) ; | ||
| 326 | REG_HS_CTRL |= JOY_RW; | ||
| 327 | //send the save | ||
| 328 | for(i = 0; i < savesize; i+=4) | ||
| 329 | { | ||
| 330 | REG_JOYTR = *(vu32*)(save_data+i); | ||
| 331 | while((REG_HS_CTRL&JOY_READ) == 0) ; | ||
| 332 | REG_HS_CTRL |= JOY_RW; | ||
| 333 | } | ||
| 334 | } | ||
| 335 | else if(choseval == 3 || choseval == 4) | ||
| 336 | { | ||
| 337 | REG_JOYTR = savesize; | ||
| 338 | if(choseval == 3) | ||
| 339 | { | ||
| 340 | //receive the save | ||
| 341 | for(i = 0; i < savesize; i+=4) | ||
| 342 | { | ||
| 343 | while((REG_HS_CTRL&JOY_WRITE) == 0) ; | ||
| 344 | REG_HS_CTRL |= JOY_RW; | ||
| 345 | *(vu32*)(save_data+i) = REG_JOYRE; | ||
| 346 | } | ||
| 347 | } | ||
| 348 | else | ||
| 349 | { | ||
| 350 | //clear the save | ||
| 351 | for(i = 0; i < savesize; i+=4) | ||
| 352 | *(vu32*)(save_data+i) = 0; | ||
| 353 | } | ||
| 354 | //disable interrupts | ||
| 355 | u32 prevIrqMask = REG_IME; | ||
| 356 | REG_IME = 0; | ||
| 357 | //write it | ||
| 358 | switch (savesize){ | ||
| 359 | case 0x200: | ||
| 360 | PutSave_EEPROM_512B(save_data); | ||
| 361 | break; | ||
| 362 | case 0x2000: | ||
| 363 | PutSave_EEPROM_8KB(save_data); | ||
| 364 | break; | ||
| 365 | case 0x8000: | ||
| 366 | PutSave_SRAM_32KB(save_data); | ||
| 367 | break; | ||
| 368 | case 0x10000: | ||
| 369 | PutSave_FLASH_64KB(save_data); | ||
| 370 | break; | ||
| 371 | case 0x20000: | ||
| 372 | PutSave_FLASH_128KB(save_data); | ||
| 373 | break; | ||
| 374 | default: | ||
| 375 | break; | ||
| 376 | } | ||
| 377 | //restore interrupts | ||
| 378 | REG_IME = prevIrqMask; | ||
| 379 | //say gc side we're done | ||
| 380 | REG_JOYTR = 0; | ||
| 381 | //wait for a cmd receive for safety | ||
| 382 | while((REG_HS_CTRL&JOY_WRITE) == 0) ; | ||
| 383 | REG_HS_CTRL |= JOY_RW; | ||
| 384 | } | ||
| 385 | REG_JOYTR = 0; | ||
| 386 | } | ||
| 387 | } else if(REG_HS_CTRL&JOY_WRITE) | ||
| 388 | { | ||
| 389 | REG_HS_CTRL |= JOY_RW; | ||
| 390 | u32 choseval = REG_JOYRE; | ||
| 391 | if(choseval == 5) | ||
| 392 | { | ||
| 393 | //disable interrupts | ||
| 394 | u32 prevIrqMask = REG_IME; | ||
| 395 | REG_IME = 0; | ||
| 396 | //dump BIOS | ||
| 397 | for (i = 0; i < 0x4000; i+=4) | ||
| 398 | { | ||
| 399 | // the lower bits are inaccurate, so just get it four times :) | ||
| 400 | u32 a = MidiKey2Freq((WaveData *)(i-4), 180-12, 0) * 2; | ||
| 401 | u32 b = MidiKey2Freq((WaveData *)(i-3), 180-12, 0) * 2; | ||
| 402 | u32 c = MidiKey2Freq((WaveData *)(i-2), 180-12, 0) * 2; | ||
| 403 | u32 d = MidiKey2Freq((WaveData *)(i-1), 180-12, 0) * 2; | ||
| 404 | REG_JOYTR = ((a>>24<<24) | (d>>24<<16) | (c>>24<<8) | (b>>24)); | ||
| 405 | while((REG_HS_CTRL&JOY_READ) == 0) ; | ||
| 406 | REG_HS_CTRL |= JOY_RW; | ||
| 407 | } | ||
| 408 | //restore interrupts | ||
| 409 | REG_IME = prevIrqMask; | ||
| 410 | } | ||
| 411 | REG_JOYTR = 0; | ||
| 412 | }*/ | ||
| 413 | Halt(); | ||
| 414 | } | ||
| 239 | } | 415 | } |
| 240 | 416 | ||
| 241 | 417 | ||
| 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 @@ | |||
| 1 | /* | ||
| 2 | * Example Gen3-multiboot payload by slipstream/RoL 2017. | ||
| 3 | * | ||
| 4 | * This software may be modified and distributed under the terms | ||
| 5 | * of the MIT license. See the LICENSE file for details. | ||
| 6 | * | ||
| 7 | * saveblocks.h: describes saveblock structures for all of Gen 3 (yay!) | ||
| 8 | */ | ||
| 9 | |||
| 10 | // Most of the structures come from pokeruby, FR/LG changes come from my own research / the firered IDB on pokecommunity | ||
| 11 | #include "savestructs.h" | ||
| 12 | |||
| 13 | typedef struct | ||
| 14 | { | ||
| 15 | /*0x00*/ struct Coords16 pos; | ||
| 16 | /*0x04*/ struct WarpData location; | ||
| 17 | /*0x0C*/ struct WarpData warp[4]; | ||
| 18 | /*0x2C*/ u16 battleMusic; | ||
| 19 | /*0x2E*/ u8 weather; | ||
| 20 | /*0x2F*/ u8 filler_2F; | ||
| 21 | /*0x30*/ u8 flashUsed; | ||
| 22 | /*0x32*/ u16 mapDataId; | ||
| 23 | /*0x34*/ u16 mapView[0x100]; | ||
| 24 | /*0x234*/ u8 playerPartyCount; | ||
| 25 | /*0x238*/ struct Pokemon playerParty[6]; | ||
| 26 | /*0x490*/ u32 money; | ||
| 27 | /*0x494*/ u16 coins; | ||
| 28 | /*0x496*/ u16 registeredItem; // registered for use with SELECT button | ||
| 29 | /*0x498*/ struct ItemSlot pcItems[50]; | ||
| 30 | /*0x560*/ struct ItemSlot bagPocket_Items[20]; | ||
| 31 | /*0x5B0*/ struct ItemSlot bagPocket_KeyItems[20]; | ||
| 32 | /*0x600*/ struct ItemSlot bagPocket_PokeBalls[16]; | ||
| 33 | /*0x640*/ struct ItemSlot bagPocket_TMHM[64]; | ||
| 34 | /*0x740*/ struct ItemSlot bagPocket_Berries[46]; | ||
| 35 | /*0x7F8*/ struct Pokeblock pokeblocks[40]; | ||
| 36 | /*0x938*/ u8 unk938[52]; // pokedex related | ||
| 37 | /*0x96C*/ u16 berryBlenderRecords[3]; | ||
| 38 | /*0x972*/ u8 filler_972[0x6]; | ||
| 39 | /*0x978*/ u16 trainerRematchStepCounter; | ||
| 40 | /*0x97A*/ u8 trainerRematches[100]; | ||
| 41 | /*0x9E0*/ struct MapObject mapObjects[16]; | ||
| 42 | /*0xC20*/ struct MapObjectTemplate mapObjectTemplates[64]; | ||
| 43 | /*0x1220*/ u8 flags[0x120]; | ||
| 44 | /*0x1340*/ u16 vars[0x100]; | ||
| 45 | /*0x1540*/ u32 gameStats[50]; | ||
| 46 | /*0x1608*/ struct BerryTree berryTrees[128]; | ||
| 47 | /*0x1A08*/ struct SecretBaseRecord secretBases[20]; | ||
| 48 | /*0x2688*/ u8 playerRoomDecor[12]; | ||
| 49 | /*0x2694*/ u8 playerRoomDecorPos[12]; | ||
| 50 | /*0x26A0*/ u8 decorDesk[10]; | ||
| 51 | /*0x26AA*/ u8 decorChair[10]; | ||
| 52 | /*0x26B4*/ u8 decorPlant[10]; | ||
| 53 | /*0x26BE*/ u8 decorOrnament[30]; | ||
| 54 | /*0x26DC*/ u8 decorMat[30]; | ||
| 55 | /*0x26FA*/ u8 decorPoster[10]; | ||
| 56 | /*0x2704*/ u8 decorDoll[40]; | ||
| 57 | /*0x272C*/ u8 decorCushion[10]; | ||
| 58 | /*0x2736*/ u8 padding_2736[2]; | ||
| 59 | /*0x2738*/ TVShow tvShows[24]; | ||
| 60 | /*0x2A98*/ u8 filler_2A98[0x64]; | ||
| 61 | /*0x2AFC*/ u16 outbreakPokemonSpecies; | ||
| 62 | /*0x2AFE*/ u8 outbreakLocationMapNum; | ||
| 63 | /*0x2AFF*/ u8 outbreakLocationMapGroup; | ||
| 64 | /*0x2B00*/ u8 outbreakPokemonLevel; | ||
| 65 | /*0x2B01*/ u8 outbreakUnk1; | ||
| 66 | /*0x2B02*/ u16 outbreakUnk2; | ||
| 67 | /*0x2B04*/ u16 outbreakPokemonMoves[4]; | ||
| 68 | /*0x2B0C*/ u8 outbreakUnk4; | ||
| 69 | /*0x2B0D*/ u8 outbreakPokemonProbability; | ||
| 70 | /*0x2B0E*/ u16 outbreakUnk5; | ||
| 71 | /*0x2B10*/ u8 filler_2B0E[0xC]; | ||
| 72 | /*0x2B1C*/ u16 unk2B1C[4]; | ||
| 73 | /*0x2B24*/ u8 filler_2B24[0x28]; | ||
| 74 | /*0x2B4C*/ struct MailStruct mail[16]; | ||
| 75 | /*0x2D8C*/ u8 filler_2D8C[0x8]; | ||
| 76 | /*0x2D94*/ OldMan oldMan; | ||
| 77 | /*0x2DC0*/ u8 unk_2DC0[0x14]; | ||
| 78 | /*0x2DD4*/ struct EasyChatPair easyChatPairs[5]; //Dewford trend [0] and some other stuff | ||
| 79 | /*0x2DFC*/ u8 filler_2DFC[0x100]; | ||
| 80 | /*0x2EFC*/ struct SB1_2EFC_Struct sb1_2EFC_struct[5]; | ||
| 81 | /*0x2F9C*/ u8 filler_2F9C[0xA0]; | ||
| 82 | /*0x303C*/ u8 filler_303C[0x38]; | ||
| 83 | /*0x3074*/ u8 filler_3074[0x42]; | ||
| 84 | /*0x30B6*/ u8 filler_30B6; | ||
| 85 | /*0x30B7*/ u8 filler_30B7[0x59]; | ||
| 86 | /*0x3110*/ u8 giftRibbons[7]; | ||
| 87 | /*0x3117*/ u8 filler_311B[0x2D]; | ||
| 88 | /*0x3144*/ struct Roamer roamer; | ||
| 89 | /*0x3158*/ u8 filler_3158[0x8]; | ||
| 90 | /*0x3160*/ struct EnigmaBerry enigmaBerry; // this is actually offset by 0x98 ... | ||
| 91 | /*0x3690*/ struct RamScript ramScript; | ||
| 92 | /*0x3A7C*/ u8 filler_3A7C[0x10]; | ||
| 93 | /*0x3A8C*/ u8 unk3A8C[52]; //pokedex related | ||
| 94 | } SaveBlock1_RS; | ||
| 95 | |||
| 96 | typedef struct // Don't rely on the commented offsets, they'll be wrong due to elements removed in FR/LG... | ||
| 97 | { | ||
| 98 | /*0x00*/ struct Coords16 pos; | ||
| 99 | /*0x04*/ struct WarpData location; | ||
| 100 | /*0x0C*/ struct WarpData warp[4]; | ||
| 101 | /*0x2C*/ u16 battleMusic; | ||
| 102 | /*0x2E*/ u8 weather; | ||
| 103 | /*0x2F*/ u8 filler_2F; | ||
| 104 | /*0x30*/ u8 flashUsed; | ||
| 105 | /*0x32*/ u16 mapDataId; | ||
| 106 | // /*0x34*/ u16 mapView[0x100]; // Not in fr/lg | ||
| 107 | /*0x234*/ u8 playerPartyCount; | ||
| 108 | /*0x238*/ struct Pokemon playerParty[6]; | ||
| 109 | /*0x490*/ u32 money; | ||
| 110 | /*0x494*/ u16 coins; | ||
| 111 | /*0x496*/ u16 registeredItem; // registered for use with SELECT button | ||
| 112 | /*0x498*/ struct ItemSlot pcItems[30]; | ||
| 113 | /*0x560*/ struct ItemSlot bagPocket_Items[42]; | ||
| 114 | /*0x5B0*/ struct ItemSlot bagPocket_KeyItems[30]; | ||
| 115 | /*0x600*/ struct ItemSlot bagPocket_PokeBalls[13]; | ||
| 116 | /*0x640*/ struct ItemSlot bagPocket_TMHM[58]; | ||
| 117 | /*0x740*/ struct ItemSlot bagPocket_Berries[43]; | ||
| 118 | // /*0x7F8*/ struct Pokeblock pokeblocks[40]; // Not in fr/lg | ||
| 119 | /*0x938*/ u8 unk938[52]; // pokedex related | ||
| 120 | /*0x96C*/ u8 unk_62C[12]; | ||
| 121 | /*0x972*/ u8 filler_972[0x6]; | ||
| 122 | /*0x97A*/ u8 unk_63E[98]; | ||
| 123 | /*0x9E0*/ struct MapObject mapObjects[16]; | ||
| 124 | /*0xC20*/ struct MapObjectTemplate mapObjectTemplates[64]; | ||
| 125 | /*0x1220*/ u8 flags[0x120]; | ||
| 126 | /*0x1340*/ u16 vars[0x100]; | ||
| 127 | /*0x1540*/ u32 gameStats[64]; // encrypted with saveblock2 xor-key | ||
| 128 | struct QuestStory questlog[4]; | ||
| 129 | u8 messages[12][4]; | ||
| 130 | struct NPCState npc_states[0x10]; | ||
| 131 | u8 unk_2f10[112]; | ||
| 132 | struct DaycarePokemon daycare[2]; | ||
| 133 | u8 unk_3098[56]; | ||
| 134 | struct Roamer roamer; | ||
| 135 | u8 unk_30e4[8]; | ||
| 136 | /*0x3160*/ struct EnigmaBerryFRLGE enigmaBerry; | ||
| 137 | u8 unk_3120[0x1C0]; // 4 bytes of CRC16, then 444 bytes of unknown. Mystery Gift related. | ||
| 138 | u8 unk_32E0[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_1" | ||
| 139 | u8 unk_3430[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_2" | ||
| 140 | u8 unk_368C[0x9C]; // padding? doesn't seem to be actually used | ||
| 141 | struct RamScript ramScript; | ||
| 142 | u8 unk_3A07[17]; | ||
| 143 | u8 pokemon_flags_2[52]; | ||
| 144 | u8 rivalName[8]; | ||
| 145 | u8 unk_3a54[128]; | ||
| 146 | u8 words[21][10]; | ||
| 147 | u8 unk_3ba6[570]; | ||
| 148 | } __attribute__((aligned(1))) SaveBlock1_FRLG; | ||
| 149 | |||
| 150 | typedef struct // Don't rely on the commented offsets, they'll be wrong due to elements changed/added in Emerald... | ||
| 151 | { | ||
| 152 | /*0x00*/ struct Coords16 pos; | ||
| 153 | /*0x04*/ struct WarpData location; | ||
| 154 | /*0x0C*/ struct WarpData warp[4]; | ||
| 155 | /*0x2C*/ u16 battleMusic; | ||
| 156 | /*0x2E*/ u8 weather; | ||
| 157 | /*0x2F*/ u8 filler_2F; | ||
| 158 | /*0x30*/ u8 flashUsed; | ||
| 159 | /*0x32*/ u16 mapDataId; | ||
| 160 | /*0x34*/ u16 mapView[0x100]; | ||
| 161 | /*0x234*/ u8 playerPartyCount; | ||
| 162 | /*0x238*/ struct Pokemon playerParty[6]; | ||
| 163 | /*0x490*/ u32 money; | ||
| 164 | /*0x494*/ u16 coins; | ||
| 165 | /*0x496*/ u16 registeredItem; // registered for use with SELECT button | ||
| 166 | /*0x498*/ struct ItemSlot pcItems[50]; | ||
| 167 | /*0x560*/ struct ItemSlot bagPocket_Items[30]; | ||
| 168 | /*0x5D8*/ struct ItemSlot bagPocket_KeyItems[30]; | ||
| 169 | /*0x650*/ struct ItemSlot bagPocket_PokeBalls[16]; | ||
| 170 | /*0x690*/ struct ItemSlot bagPocket_TMHM[64]; | ||
| 171 | /*0x790*/ struct ItemSlot bagPocket_Berries[46]; | ||
| 172 | /*0x7F8*/ struct Pokeblock pokeblocks[40]; // every offset is shifted by 0x50 from here on thanks to changed bag-counts | ||
| 173 | /*0x938*/ u8 unk938[52]; // pokedex related | ||
| 174 | /*0x96C*/ u16 berryBlenderRecords[3]; | ||
| 175 | /*0x972*/ u8 filler_972[0x6]; | ||
| 176 | /*0x978*/ u16 trainerRematchStepCounter; | ||
| 177 | /*0x97A*/ u8 trainerRematches[100]; | ||
| 178 | /*0x9E0*/ struct MapObject mapObjects[16]; | ||
| 179 | /*0xC20*/ struct MapObjectTemplate mapObjectTemplates[64]; | ||
| 180 | /*0x1220*/ u8 flags[0x12C]; | ||
| 181 | /*0x1340*/ u16 vars[0x100]; // offsets shifted by 0x5C from here on thanks to added flags | ||
| 182 | /*0x1540*/ u32 gameStats[64]; // encrypted with saveblock2 xor-key | ||
| 183 | /*0x1608*/ struct BerryTree berryTrees[128]; // offsets shifted by 0x94 from here on thanks to added 14 gamestats | ||
| 184 | /*0x1A08*/ struct SecretBaseRecord secretBases[20]; | ||
| 185 | /*0x2688*/ u8 playerRoomDecor[12]; | ||
| 186 | /*0x2694*/ u8 playerRoomDecorPos[12]; | ||
| 187 | /*0x26A0*/ u8 decorDesk[10]; | ||
| 188 | /*0x26AA*/ u8 decorChair[10]; | ||
| 189 | /*0x26B4*/ u8 decorPlant[10]; | ||
| 190 | /*0x26BE*/ u8 decorOrnament[30]; | ||
| 191 | /*0x26DC*/ u8 decorMat[30]; | ||
| 192 | /*0x26FA*/ u8 decorPoster[10]; | ||
| 193 | /*0x2704*/ u8 decorDoll[40]; | ||
| 194 | /*0x272C*/ u8 decorCushion[10]; | ||
| 195 | // /*0x2736*/ u8 padding_2736[2]; | ||
| 196 | /*0x2738*/ TVShow tvShows[24]; | ||
| 197 | /*0x2A98*/ u8 filler_2A98[0x64]; | ||
| 198 | /*0x2AFC*/ u16 outbreakPokemonSpecies; // offset by 0x94 | ||
| 199 | /*0x2AFE*/ u8 outbreakLocationMapNum; | ||
| 200 | /*0x2AFF*/ u8 outbreakLocationMapGroup; | ||
| 201 | /*0x2B00*/ u8 outbreakPokemonLevel; | ||
| 202 | /*0x2B01*/ u8 outbreakUnk1; | ||
| 203 | /*0x2B02*/ u16 outbreakUnk2; | ||
| 204 | /*0x2B04*/ u16 outbreakPokemonMoves[4]; | ||
| 205 | /*0x2B0C*/ u8 outbreakUnk4; | ||
| 206 | /*0x2B0D*/ u8 outbreakPokemonProbability; | ||
| 207 | /*0x2B0E*/ u16 outbreakUnk5; | ||
| 208 | /*0x2B10*/ u8 filler_2B0E[0xC]; | ||
| 209 | /*0x2B1C*/ u16 unk2B1C[4]; | ||
| 210 | /*0x2B24*/ u8 filler_2B24[0x28]; | ||
| 211 | /*0x2B4C*/ struct MailStruct mail[16]; // offset by 0x94 | ||
| 212 | /*0x2D8C*/ u8 filler_2D8C[0x8]; | ||
| 213 | /*0x2D94*/ OldMan oldMan; | ||
| 214 | /*0x2DC0*/ u8 unk_2DC0[0x14]; | ||
| 215 | /*0x2DD4*/ struct EasyChatPair easyChatPairs[5]; //Dewford trend [0] and some other stuff | ||
| 216 | // /*0x2DFC*/ u8 filler_2DFC[0x100]; | ||
| 217 | /*0x2EFC*/ struct SB1_2EFC_Struct sb1_2EFC_struct[12]; | ||
| 218 | u8 unk_3010[0x198]; // no idea if any of this is actually used. | ||
| 219 | /*0x3110*/ u8 giftRibbons[7]; | ||
| 220 | /*0x3117*/ u8 filler_311B[0x2D]; | ||
| 221 | /*0x3144*/ struct Roamer roamer; | ||
| 222 | /*0x3158*/ u8 filler_3158[0x8]; | ||
| 223 | /*0x3160*/ struct EnigmaBerryFRLGE enigmaBerry; | ||
| 224 | u8 unk_322C[0x1C0]; // 4 bytes of CRC16, then 444 bytes of unknown. Mystery Gift related. | ||
| 225 | u8 unk_33EC[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_1" | ||
| 226 | u8 unk_353C[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_2" | ||
| 227 | u8 unk_368C[0x9C]; // padding? doesn't seem to be actually used | ||
| 228 | /*0x3690*/ struct RamScript ramScript; | ||
| 229 | /*0x3A7C*/ u8 filler_3A7C[0x10]; | ||
| 230 | /*0x3A8C*/ u8 unk3A8C[52]; //pokedex related | ||
| 231 | } SaveBlock1_E; | ||
| 232 | |||
| 233 | // --- | ||
| 234 | |||
| 235 | struct SaveBlock2_Sub | ||
| 236 | { | ||
| 237 | /*0x0000, 0x00A8*/ u8 filler_000[0x4AE]; | ||
| 238 | /*0x04AE, 0x0556*/ u8 var_4AE; | ||
| 239 | /*0x04AF, 0x0557*/ u8 var_4AF; | ||
| 240 | /*0x04B0, 0x0558*/ u16 var_4B0; | ||
| 241 | /*0x04B2, 0x055A*/ u16 var_4B2; | ||
| 242 | /*0x04B4, 0x055C*/ u16 var_4B4; | ||
| 243 | /*0x04B6, 0x055E*/ u16 var_4B6; | ||
| 244 | /*0x04B8, 0x0560*/ u8 filler_4B8[0x10]; | ||
| 245 | /*0x04C8, 0x0570*/ u16 var_4C8; | ||
| 246 | /*0x04CA, 0x0572*/ u16 var_4CA; | ||
| 247 | /*0x04CC, 0x0574*/ u8 filler_4CC[0x31C]; | ||
| 248 | }; | ||
| 249 | |||
| 250 | typedef struct | ||
| 251 | { | ||
| 252 | /*0x00*/ u8 playerName[8]; | ||
| 253 | /*0x08*/ u8 playerGender; // MALE, FEMALE | ||
| 254 | /*0x09*/ u8 specialSaveWarp; | ||
| 255 | /*0x0A*/ u8 playerTrainerId[4]; | ||
| 256 | /*0x0E*/ u16 playTimeHours; | ||
| 257 | /*0x10*/ u8 playTimeMinutes; | ||
| 258 | /*0x11*/ u8 playTimeSeconds; | ||
| 259 | /*0x12*/ u8 playTimeVBlanks; | ||
| 260 | /*0x13*/ u8 optionsButtonMode; // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A] | ||
| 261 | /*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST] | ||
| 262 | u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes | ||
| 263 | u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO] | ||
| 264 | u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET] | ||
| 265 | u16 optionsBattleSceneOff:1; // whether battle animations are disabled | ||
| 266 | u16 regionMapZoom:1; // whether the map is zoomed in | ||
| 267 | /*0x18*/ struct Pokedex pokedex; | ||
| 268 | /*0x90*/ u8 filler_90[0x8]; | ||
| 269 | /*0x98*/ struct Time localTimeOffset; | ||
| 270 | /*0xA0*/ struct Time lastBerryTreeUpdate; | ||
| 271 | /*0xA8*/ struct SaveBlock2_Sub filler_A8; | ||
| 272 | } SaveBlock2_RS; | ||
| 273 | |||
| 274 | typedef struct | ||
| 275 | { | ||
| 276 | /*0x00*/ u8 playerName[8]; | ||
| 277 | /*0x08*/ u8 playerGender; // MALE, FEMALE | ||
| 278 | /*0x09*/ u8 specialSaveWarp; | ||
| 279 | /*0x0A*/ u8 playerTrainerId[4]; | ||
| 280 | /*0x0E*/ u16 playTimeHours; | ||
| 281 | /*0x10*/ u8 playTimeMinutes; | ||
| 282 | /*0x11*/ u8 playTimeSeconds; | ||
| 283 | /*0x12*/ u8 playTimeVBlanks; | ||
| 284 | /*0x13*/ u8 optionsButtonMode; // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A] | ||
| 285 | /*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST] | ||
| 286 | u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes | ||
| 287 | u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO] | ||
| 288 | u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET] | ||
| 289 | u16 optionsBattleSceneOff:1; // whether battle animations are disabled | ||
| 290 | u16 regionMapZoom:1; // whether the map is zoomed in | ||
| 291 | /*0x18*/ struct Pokedex pokedex; | ||
| 292 | /*0x90*/ u8 filler_90[0x8]; | ||
| 293 | /*0x98*/ struct Time localTimeOffset; | ||
| 294 | /*0xA0*/ struct Time lastBerryTreeUpdate; | ||
| 295 | /*0xA8*/ struct SaveBlock2_Sub filler_A8; | ||
| 296 | /*0x890*/ u8 unk_890[8]; | ||
| 297 | /*0x898*/ u8 mapdata[0x258]; | ||
| 298 | /*0xaf0*/ u16 field_af0; | ||
| 299 | /*0xaf2*/ u16 field_af2; | ||
| 300 | /*0xaf4*/ u16 field_af4; | ||
| 301 | /*0xaf6*/ u16 field_af6; | ||
| 302 | /*0xaf8*/ u8 unk_af8[0x428]; | ||
| 303 | /*0xf20*/ u32 xor_key; | ||
| 304 | } SaveBlock2_FRLG; | ||
| 305 | |||
| 306 | typedef struct | ||
| 307 | { | ||
| 308 | /*0x00*/ u8 playerName[8]; | ||
| 309 | /*0x08*/ u8 playerGender; // MALE, FEMALE | ||
| 310 | /*0x09*/ u8 specialSaveWarp; | ||
| 311 | /*0x0A*/ u8 playerTrainerId[4]; | ||
| 312 | /*0x0E*/ u16 playTimeHours; | ||
| 313 | /*0x10*/ u8 playTimeMinutes; | ||
| 314 | /*0x11*/ u8 playTimeSeconds; | ||
| 315 | /*0x12*/ u8 playTimeVBlanks; | ||
| 316 | /*0x13*/ u8 optionsButtonMode; // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A] | ||
| 317 | /*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST] | ||
| 318 | u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes | ||
| 319 | u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO] | ||
| 320 | u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET] | ||
| 321 | u16 optionsBattleSceneOff:1; // whether battle animations are disabled | ||
| 322 | u16 regionMapZoom:1; // whether the map is zoomed in | ||
| 323 | /*0x18*/ struct Pokedex pokedex; | ||
| 324 | /*0x90*/ u8 filler_90[0x8]; | ||
| 325 | /*0x98*/ struct Time localTimeOffset; | ||
| 326 | /*0xA0*/ struct Time lastBerryTreeUpdate; | ||
| 327 | /*0xA8*/ u32 xor_key; | ||
| 328 | /*0xAC*/ struct SaveBlock2_Sub filler_A8; | ||
| 329 | } SaveBlock2_E; | ||
| 330 | |||
| 331 | typedef union { | ||
| 332 | SaveBlock1_RS rs; | ||
| 333 | SaveBlock1_FRLG frlg; | ||
| 334 | SaveBlock1_E e; | ||
| 335 | } SaveBlock1, *pSaveBlock1; | ||
| 336 | |||
| 337 | typedef union { | ||
| 338 | SaveBlock2_RS rs; | ||
| 339 | SaveBlock2_FRLG frlg; | ||
| 340 | SaveBlock2_E e; | ||
| 341 | } SaveBlock2, *pSaveBlock2; | ||
| 342 | |||
| 343 | 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 @@ | |||
| 1 | /* | ||
| 2 | * Example Gen3-multiboot payload by slipstream/RoL 2017. | ||
| 3 | * | ||
| 4 | * This software may be modified and distributed under the terms | ||
| 5 | * of the MIT license. See the LICENSE file for details. | ||
| 6 | * | ||
| 7 | * saveblocks.h: describes structures used by saveblocks for all of Gen 3 | ||
| 8 | */ | ||
| 9 | |||
| 10 | // Most of the structures come from pokeruby, FR/LG changes come from my own research / the firered IDB on pokecommunity | ||
| 11 | |||
| 12 | #define POKEMON_NAME_LENGTH 10 | ||
| 13 | #define OT_NAME_LENGTH 7 | ||
| 14 | #define TILE_SIZE_4BPP 32 | ||
| 15 | |||
| 16 | struct Coords16 | ||
| 17 | { | ||
| 18 | s16 x; | ||
| 19 | s16 y; | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct UCoords16 | ||
| 23 | { | ||
| 24 | u16 x; | ||
| 25 | u16 y; | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct SecretBaseRecord | ||
| 29 | { | ||
| 30 | u8 sbr_field_0; // ID? | ||
| 31 | u8 sbr_field_1_0:4; | ||
| 32 | u8 gender:1; | ||
| 33 | u8 sbr_field_1_5:1; | ||
| 34 | u8 sbr_field_2[7]; // 0xFF bytes? | ||
| 35 | u8 trainerId[4]; // byte 0 is used for determining trainer class | ||
| 36 | u16 sbr_field_e; | ||
| 37 | u8 sbr_field_10; | ||
| 38 | u8 sbr_field_11; | ||
| 39 | u8 decorations[16]; | ||
| 40 | u8 sbr_field_22[16]; | ||
| 41 | u32 partyPersonality[6]; | ||
| 42 | u16 partyMoves[6 * 4]; | ||
| 43 | u16 partySpecies[6]; | ||
| 44 | u16 partyHeldItems[6]; | ||
| 45 | u8 partyLevels[6]; | ||
| 46 | u8 partyEVs[6]; | ||
| 47 | }; | ||
| 48 | |||
| 49 | typedef void (*TilesetCB)(void); | ||
| 50 | |||
| 51 | struct Tileset | ||
| 52 | { | ||
| 53 | u8 isCompressed; | ||
| 54 | u8 isSecondary; | ||
| 55 | void *tiles; | ||
| 56 | void *palettes; | ||
| 57 | void *metatiles; | ||
| 58 | void *metatileAttributes; | ||
| 59 | TilesetCB callback; | ||
| 60 | }; | ||
| 61 | |||
| 62 | struct MapData | ||
| 63 | { | ||
| 64 | s32 width; | ||
| 65 | s32 height; | ||
| 66 | u16 *border; | ||
| 67 | u16 *map; | ||
| 68 | struct Tileset *primaryTileset; | ||
| 69 | struct Tileset *secondaryTileset; | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct MapObjectTemplate | ||
| 73 | { | ||
| 74 | /*0x00*/ u8 localId; | ||
| 75 | /*0x01*/ u8 graphicsId; | ||
| 76 | /*0x02*/ u8 unk2; | ||
| 77 | /*0x04*/ s16 x; | ||
| 78 | /*0x06*/ s16 y; | ||
| 79 | /*0x08*/ u8 elevation; | ||
| 80 | /*0x09*/ u8 movementType; | ||
| 81 | /*0x0A*/ u8 unkA_0:4; | ||
| 82 | u8 unkA_4:4; | ||
| 83 | ///*0x0B*/ u8 fillerB[1]; | ||
| 84 | /*0x0C*/ u16 unkC; | ||
| 85 | /*0x0E*/ u16 unkE; | ||
| 86 | /*0x10*/ u8 *script; | ||
| 87 | /*0x14*/ u16 flagId; | ||
| 88 | /*0x16*/ u8 filler_16[2]; | ||
| 89 | }; /*size = 0x18*/ | ||
| 90 | |||
| 91 | struct WarpEvent | ||
| 92 | { | ||
| 93 | s16 x, y; | ||
| 94 | s8 warpId; | ||
| 95 | u8 mapGroup; | ||
| 96 | u8 mapNum; | ||
| 97 | u8 unk7; | ||
| 98 | }; | ||
| 99 | |||
| 100 | struct CoordEvent | ||
| 101 | { | ||
| 102 | s16 x, y; | ||
| 103 | u8 unk4; | ||
| 104 | u8 filler_5; | ||
| 105 | u16 trigger; | ||
| 106 | u16 index; | ||
| 107 | u8 filler_A[0x2]; | ||
| 108 | u8 *script; | ||
| 109 | }; | ||
| 110 | |||
| 111 | struct BgEvent | ||
| 112 | { | ||
| 113 | s16 x, y; | ||
| 114 | u8 unk4; | ||
| 115 | u8 kind; | ||
| 116 | s16 filler_6; | ||
| 117 | u8 *script; | ||
| 118 | }; | ||
| 119 | |||
| 120 | struct MapEvents | ||
| 121 | { | ||
| 122 | u8 mapObjectCount; | ||
| 123 | u8 warpCount; | ||
| 124 | u8 coordEventCount; | ||
| 125 | u8 bgEventCount; | ||
| 126 | |||
| 127 | struct MapObjectTemplate *mapObjects; | ||
| 128 | struct WarpEvent *warps; | ||
| 129 | struct CoordEvent *coordEvents; | ||
| 130 | struct BgEvent *bgEvents; | ||
| 131 | }; | ||
| 132 | |||
| 133 | struct MapConnection | ||
| 134 | { | ||
| 135 | u8 direction; | ||
| 136 | u32 offset; | ||
| 137 | u8 mapGroup; | ||
| 138 | u8 mapNum; | ||
| 139 | }; | ||
| 140 | |||
| 141 | struct MapConnections | ||
| 142 | { | ||
| 143 | s32 count; | ||
| 144 | struct MapConnection *connections; | ||
| 145 | }; | ||
| 146 | |||
| 147 | struct MapHeader | ||
| 148 | { | ||
| 149 | struct MapData *mapData; | ||
| 150 | struct MapEvents *events; | ||
| 151 | u8 *mapScripts; | ||
| 152 | struct MapConnections *connections; | ||
| 153 | u16 music; | ||
| 154 | u16 mapDataId; | ||
| 155 | u8 name; | ||
| 156 | u8 cave; | ||
| 157 | u8 weather; | ||
| 158 | /* 0x17 */ u8 mapType; | ||
| 159 | u8 filler_18; | ||
| 160 | u8 escapeRope; | ||
| 161 | u8 flags; | ||
| 162 | u8 battleType; | ||
| 163 | }; | ||
| 164 | |||
| 165 | struct MapObject | ||
| 166 | { | ||
| 167 | /*0x00*/ u32 active:1; | ||
| 168 | u32 mapobj_bit_1:1; | ||
| 169 | u32 mapobj_bit_2:1; | ||
| 170 | u32 mapobj_bit_3:1; | ||
| 171 | u32 mapobj_bit_4:1; | ||
| 172 | u32 mapobj_bit_5:1; | ||
| 173 | u32 mapobj_bit_6:1; | ||
| 174 | u32 mapobj_bit_7:1; | ||
| 175 | /*0x01*/ u32 mapobj_bit_8:1; | ||
| 176 | u32 mapobj_bit_9:1; | ||
| 177 | u32 mapobj_bit_10:1; | ||
| 178 | u32 mapobj_bit_11:1; | ||
| 179 | u32 mapobj_bit_12:1; | ||
| 180 | u32 mapobj_bit_13:1; | ||
| 181 | u32 mapobj_bit_14:1; | ||
| 182 | u32 mapobj_bit_15:1; | ||
| 183 | /*0x02*/ u32 mapobj_bit_16:1; | ||
| 184 | u32 mapobj_bit_17:1; | ||
| 185 | u32 mapobj_bit_18:1; | ||
| 186 | u32 mapobj_bit_19:1; | ||
| 187 | u32 mapobj_bit_20:1; | ||
| 188 | u32 mapobj_bit_21:1; | ||
| 189 | u32 mapobj_bit_22:1; | ||
| 190 | u32 mapobj_bit_23:1; | ||
| 191 | /*0x03*/ u32 mapobj_bit_24:1; | ||
| 192 | u32 mapobj_bit_25:1; | ||
| 193 | u32 mapobj_bit_26:1; | ||
| 194 | u32 mapobj_bit_27:1; | ||
| 195 | u32 mapobj_bit_28:1; | ||
| 196 | u32 mapobj_bit_29:1; | ||
| 197 | u32 mapobj_bit_30:1; | ||
| 198 | u32 mapobj_bit_31:1; | ||
| 199 | /*0x04*/ u8 spriteId; | ||
| 200 | /*0x05*/ u8 graphicsId; | ||
| 201 | /*0x06*/ u8 animPattern; | ||
| 202 | /*0x07*/ u8 trainerType; | ||
| 203 | /*0x08*/ u8 localId; | ||
| 204 | /*0x09*/ u8 mapNum; | ||
| 205 | /*0x0A*/ u8 mapGroup; | ||
| 206 | /*0x0B*/ u8 mapobj_unk_0B_0:4; | ||
| 207 | u8 elevation:4; | ||
| 208 | /*0x0C*/ struct Coords16 coords1; | ||
| 209 | /*0x10*/ struct Coords16 coords2; | ||
| 210 | /*0x14*/ struct Coords16 coords3; | ||
| 211 | /*0x18*/ u8 mapobj_unk_18:4; //current direction? | ||
| 212 | /*0x18*/ u8 placeholder18:4; | ||
| 213 | /*0x19*/ u8 mapobj_unk_19; | ||
| 214 | /*0x1A*/ u8 mapobj_unk_1A; | ||
| 215 | /*0x1B*/ u8 mapobj_unk_1B; | ||
| 216 | /*0x1C*/ u8 mapobj_unk_1C; | ||
| 217 | /*0x1D*/ u8 trainerRange_berryTreeId; | ||
| 218 | /*0x1E*/ u8 mapobj_unk_1E; | ||
| 219 | /*0x1F*/ u8 mapobj_unk_1F; | ||
| 220 | /*0x20*/ u8 mapobj_unk_20; | ||
| 221 | /*0x21*/ u8 mapobj_unk_21; | ||
| 222 | /*0x22*/ u8 animId; | ||
| 223 | /*size = 0x24*/ | ||
| 224 | }; | ||
| 225 | |||
| 226 | struct Berry | ||
| 227 | { | ||
| 228 | const u8 name[7]; | ||
| 229 | u8 firmness; | ||
| 230 | u16 size; | ||
| 231 | u8 maxYield; | ||
| 232 | u8 minYield; | ||
| 233 | const u8 *description1; | ||
| 234 | const u8 *description2; | ||
| 235 | u8 stageDuration; | ||
| 236 | u8 spicy; | ||
| 237 | u8 dry; | ||
| 238 | u8 sweet; | ||
| 239 | u8 bitter; | ||
| 240 | u8 sour; | ||
| 241 | u8 smoothness; | ||
| 242 | }; | ||
| 243 | |||
| 244 | struct EnigmaBerry | ||
| 245 | { | ||
| 246 | struct Berry berry; | ||
| 247 | u8 pic[(6 * 6) * TILE_SIZE_4BPP]; | ||
| 248 | u16 palette[16]; | ||
| 249 | u8 description1[45]; | ||
| 250 | u8 description2[45]; | ||
| 251 | u8 itemEffect[18]; | ||
| 252 | u8 holdEffect; | ||
| 253 | u8 holdEffectParam; | ||
| 254 | u32 checksum; | ||
| 255 | }; | ||
| 256 | |||
| 257 | struct BattleEnigmaBerry | ||
| 258 | { | ||
| 259 | u8 name[7]; | ||
| 260 | u8 holdEffect; | ||
| 261 | u8 itemEffect[18]; | ||
| 262 | u8 holdEffectParam; | ||
| 263 | }; | ||
| 264 | |||
| 265 | struct EnigmaBerryFRLGE { | ||
| 266 | struct Berry berry; // 0x00 | ||
| 267 | u8 itemEffect[18]; // 0x1C | ||
| 268 | u8 holdEffect; // 0x2E | ||
| 269 | u8 holdEffectParam; // 0x2F | ||
| 270 | u32 checksum; // 0x30 | ||
| 271 | }; | ||
| 272 | |||
| 273 | struct __attribute__((aligned(4))) BerryTree | ||
| 274 | { | ||
| 275 | u8 berry; | ||
| 276 | u8 stage:7; | ||
| 277 | u8 growthSparkle:1; | ||
| 278 | u16 secondsUntilNextStage; | ||
| 279 | u8 berryYield; | ||
| 280 | u8 regrowthCount:4; | ||
| 281 | u8 watered1:1; | ||
| 282 | u8 watered2:1; | ||
| 283 | u8 watered3:1; | ||
| 284 | u8 watered4:1; | ||
| 285 | }; | ||
| 286 | |||
| 287 | struct PokemonSubstruct0 | ||
| 288 | { | ||
| 289 | u16 species; | ||
| 290 | u16 heldItem; | ||
| 291 | u32 experience; | ||
| 292 | u8 ppBonuses; | ||
| 293 | u8 friendship; | ||
| 294 | }; | ||
| 295 | |||
| 296 | struct PokemonSubstruct1 | ||
| 297 | { | ||
| 298 | u16 moves[4]; | ||
| 299 | u8 pp[4]; | ||
| 300 | }; | ||
| 301 | |||
| 302 | struct PokemonSubstruct2 | ||
| 303 | { | ||
| 304 | u8 hpEV; | ||
| 305 | u8 attackEV; | ||
| 306 | u8 defenseEV; | ||
| 307 | u8 speedEV; | ||
| 308 | u8 spAttackEV; | ||
| 309 | u8 spDefenseEV; | ||
| 310 | u8 cool; | ||
| 311 | u8 beauty; | ||
| 312 | u8 cute; | ||
| 313 | u8 smart; | ||
| 314 | u8 tough; | ||
| 315 | u8 sheen; | ||
| 316 | }; | ||
| 317 | |||
| 318 | struct PokemonSubstruct3 | ||
| 319 | { | ||
| 320 | /* 0x00 */ u8 pokerus; | ||
| 321 | /* 0x01 */ u8 metLocation; | ||
| 322 | |||
| 323 | /* 0x02 */ u16 metLevel:7; | ||
| 324 | /* 0x02 */ u16 metGame:4; | ||
| 325 | /* 0x03 */ u16 pokeball:4; | ||
| 326 | /* 0x03 */ u16 otGender:1; | ||
| 327 | |||
| 328 | /* 0x04 */ u32 hpIV:5; | ||
| 329 | /* 0x04 */ u32 attackIV:5; | ||
| 330 | /* 0x05 */ u32 defenseIV:5; | ||
| 331 | /* 0x05 */ u32 speedIV:5; | ||
| 332 | /* 0x05 */ u32 spAttackIV:5; | ||
| 333 | /* 0x06 */ u32 spDefenseIV:5; | ||
| 334 | /* 0x07 */ u32 isEgg:1; | ||
| 335 | /* 0x07 */ u32 altAbility:1; | ||
| 336 | |||
| 337 | /* 0x08 */ u32 coolRibbon:3; | ||
| 338 | /* 0x08 */ u32 beautyRibbon:3; | ||
| 339 | /* 0x08 */ u32 cuteRibbon:3; | ||
| 340 | /* 0x09 */ u32 smartRibbon:3; | ||
| 341 | /* 0x09 */ u32 toughRibbon:3; | ||
| 342 | /* 0x09 */ u32 championRibbon:1; | ||
| 343 | /* 0x0A */ u32 winningRibbon:1; | ||
| 344 | /* 0x0A */ u32 victoryRibbon:1; | ||
| 345 | /* 0x0A */ u32 artistRibbon:1; | ||
| 346 | /* 0x0A */ u32 effortRibbon:1; | ||
| 347 | /* 0x0A */ u32 giftRibbon1:1; | ||
| 348 | /* 0x0A */ u32 giftRibbon2:1; | ||
| 349 | /* 0x0A */ u32 giftRibbon3:1; | ||
| 350 | /* 0x0A */ u32 giftRibbon4:1; | ||
| 351 | /* 0x0B */ u32 giftRibbon5:1; | ||
| 352 | /* 0x0B */ u32 giftRibbon6:1; | ||
| 353 | /* 0x0B */ u32 giftRibbon7:1; | ||
| 354 | /* 0x0B */ u32 fatefulEncounter:5; // unused in Ruby/Sapphire, but the high bit must be set for Mew/Deoxys to obey in FR/LG/Emerald | ||
| 355 | }; | ||
| 356 | |||
| 357 | union PokemonSubstruct | ||
| 358 | { | ||
| 359 | struct PokemonSubstruct0 type0; | ||
| 360 | struct PokemonSubstruct1 type1; | ||
| 361 | struct PokemonSubstruct2 type2; | ||
| 362 | struct PokemonSubstruct3 type3; | ||
| 363 | u16 raw[6]; | ||
| 364 | }; | ||
| 365 | |||
| 366 | struct BoxPokemon | ||
| 367 | { | ||
| 368 | u32 personality; | ||
| 369 | u32 otId; | ||
| 370 | u8 nickname[POKEMON_NAME_LENGTH]; | ||
| 371 | u8 language; | ||
| 372 | u8 isBadEgg:1; | ||
| 373 | u8 hasSpecies:1; | ||
| 374 | u8 isEgg:1; | ||
| 375 | u8 unused:5; | ||
| 376 | u8 otName[OT_NAME_LENGTH]; | ||
| 377 | u8 markings; | ||
| 378 | u16 checksum; | ||
| 379 | u16 unknown; | ||
| 380 | |||
| 381 | union | ||
| 382 | { | ||
| 383 | u32 raw[12]; | ||
| 384 | union PokemonSubstruct substructs[4]; | ||
| 385 | } secure; | ||
| 386 | }; | ||
| 387 | |||
| 388 | struct Pokemon | ||
| 389 | { | ||
| 390 | struct BoxPokemon box; | ||
| 391 | u32 status; | ||
| 392 | u8 level; | ||
| 393 | u8 pokerus; | ||
| 394 | u16 hp; | ||
| 395 | u16 maxHP; | ||
| 396 | u16 attack; | ||
| 397 | u16 defense; | ||
| 398 | u16 speed; | ||
| 399 | u16 spAttack; | ||
| 400 | u16 spDefense; | ||
| 401 | }; | ||
| 402 | |||
| 403 | struct UnknownPokemonStruct | ||
| 404 | { | ||
| 405 | u16 species; | ||
| 406 | u16 heldItem; | ||
| 407 | u16 moves[4]; | ||
| 408 | u8 level; | ||
| 409 | u8 ppBonuses; | ||
| 410 | u8 hpEV; | ||
| 411 | u8 attackEV; | ||
| 412 | u8 defenseEV; | ||
| 413 | u8 speedEV; | ||
| 414 | u8 spAttackEV; | ||
| 415 | u8 spDefenseEV; | ||
| 416 | u32 otId; | ||
| 417 | u32 hpIV:5; | ||
| 418 | u32 attackIV:5; | ||
| 419 | u32 defenseIV:5; | ||
| 420 | u32 speedIV:5; | ||
| 421 | u32 spAttackIV:5; | ||
| 422 | u32 spDefenseIV:5; | ||
| 423 | u32 gap:1; | ||
| 424 | u32 altAbility:1; | ||
| 425 | u32 personality; | ||
| 426 | u8 nickname[POKEMON_NAME_LENGTH + 1]; | ||
| 427 | u8 friendship; | ||
| 428 | }; | ||
| 429 | |||
| 430 | struct BattlePokemon | ||
| 431 | { | ||
| 432 | /* 0x00 */ u16 species; | ||
| 433 | /* 0x02 */ u16 attack; | ||
| 434 | /* 0x04 */ u16 defense; | ||
| 435 | /* 0x06 */ u16 speed; | ||
| 436 | /* 0x08 */ u16 spAttack; | ||
| 437 | /* 0x0A */ u16 spDefense; | ||
| 438 | /* 0x0C */ u16 moves[4]; | ||
| 439 | /* 0x14 */ u32 hpIV:5; | ||
| 440 | /* 0x14 */ u32 attackIV:5; | ||
| 441 | /* 0x15 */ u32 defenseIV:5; | ||
| 442 | /* 0x15 */ u32 speedIV:5; | ||
| 443 | /* 0x16 */ u32 spAttackIV:5; | ||
| 444 | /* 0x17 */ u32 spDefenseIV:5; | ||
| 445 | /* 0x17 */ u32 isEgg:1; | ||
| 446 | /* 0x17 */ u32 altAbility:1; | ||
| 447 | /* 0x18 */ s8 statStages[8]; | ||
| 448 | /* 0x20 */ u8 ability; | ||
| 449 | /* 0x21 */ u8 type1; | ||
| 450 | /* 0x22 */ u8 type2; | ||
| 451 | /* 0x23 */ u8 unknown; | ||
| 452 | /* 0x24 */ u8 pp[4]; | ||
| 453 | /* 0x28 */ u16 hp; | ||
| 454 | /* 0x2A */ u8 level; | ||
| 455 | /* 0x2B */ u8 friendship; | ||
| 456 | /* 0x2C */ u16 maxHP; | ||
| 457 | /* 0x2E */ u16 item; | ||
| 458 | /* 0x30 */ u8 nickname[POKEMON_NAME_LENGTH + 1]; | ||
| 459 | /* 0x3B */ u8 ppBonuses; | ||
| 460 | /* 0x3C */ u8 otName[8]; | ||
| 461 | /* 0x44 */ u32 experience; | ||
| 462 | /* 0x48 */ u32 personality; | ||
| 463 | /* 0x4C */ u32 status1; | ||
| 464 | /* 0x50 */ u32 status2; | ||
| 465 | /* 0x54 */ u32 otId; | ||
| 466 | }; | ||
| 467 | |||
| 468 | struct BaseStats | ||
| 469 | { | ||
| 470 | /* 0x00 */ u8 baseHP; | ||
| 471 | /* 0x01 */ u8 baseAttack; | ||
| 472 | /* 0x02 */ u8 baseDefense; | ||
| 473 | /* 0x03 */ u8 baseSpeed; | ||
| 474 | /* 0x04 */ u8 baseSpAttack; | ||
| 475 | /* 0x05 */ u8 baseSpDefense; | ||
| 476 | /* 0x06 */ u8 type1; | ||
| 477 | /* 0x07 */ u8 type2; | ||
| 478 | /* 0x08 */ u8 catchRate; | ||
| 479 | /* 0x09 */ u8 expYield; | ||
| 480 | /* 0x0A */ u16 evYield_HP:2; | ||
| 481 | /* 0x0A */ u16 evYield_Attack:2; | ||
| 482 | /* 0x0A */ u16 evYield_Defense:2; | ||
| 483 | /* 0x0A */ u16 evYield_Speed:2; | ||
| 484 | /* 0x0B */ u16 evYield_SpAttack:2; | ||
| 485 | /* 0x0B */ u16 evYield_SpDefense:2; | ||
| 486 | /* 0x0C */ u16 item1; | ||
| 487 | /* 0x0E */ u16 item2; | ||
| 488 | /* 0x10 */ u8 genderRatio; | ||
| 489 | /* 0x11 */ u8 eggCycles; | ||
| 490 | /* 0x12 */ u8 friendship; | ||
| 491 | /* 0x13 */ u8 growthRate; | ||
| 492 | /* 0x14 */ u8 eggGroup1; | ||
| 493 | /* 0x15 */ u8 eggGroup2; | ||
| 494 | /* 0x16 */ u8 ability1; | ||
| 495 | /* 0x17 */ u8 ability2; | ||
| 496 | /* 0x18 */ u8 safariZoneFleeRate; | ||
| 497 | /* 0x19 */ u8 bodyColor; | ||
| 498 | }; | ||
| 499 | |||
| 500 | struct BattleMove | ||
| 501 | { | ||
| 502 | u8 effect; | ||
| 503 | u8 power; | ||
| 504 | u8 type; | ||
| 505 | u8 accuracy; | ||
| 506 | u8 pp; | ||
| 507 | u8 secondaryEffectChance; | ||
| 508 | u8 target; | ||
| 509 | u8 priority; | ||
| 510 | u32 flags; | ||
| 511 | }; | ||
| 512 | |||
| 513 | struct PokemonStorage | ||
| 514 | { | ||
| 515 | /* 0x00 */ u8 currentBox; | ||
| 516 | /* 0x01 */ struct BoxPokemon boxes[14][30]; | ||
| 517 | u8 boxNames[14][9]; | ||
| 518 | u8 boxBackground[14]; | ||
| 519 | }; | ||
| 520 | |||
| 521 | struct WarpData | ||
| 522 | { | ||
| 523 | s8 mapGroup; | ||
| 524 | s8 mapNum; | ||
| 525 | s8 warpId; | ||
| 526 | s16 x, y; | ||
| 527 | }; | ||
| 528 | |||
| 529 | struct ItemSlot | ||
| 530 | { | ||
| 531 | u16 itemId; | ||
| 532 | u16 quantity; | ||
| 533 | }; | ||
| 534 | |||
| 535 | struct __attribute__((aligned(2))) Pokeblock | ||
| 536 | { | ||
| 537 | u8 color; | ||
| 538 | u8 spicy; | ||
| 539 | u8 dry; | ||
| 540 | u8 sweet; | ||
| 541 | u8 bitter; | ||
| 542 | u8 sour; | ||
| 543 | u8 feel; | ||
| 544 | }; | ||
| 545 | |||
| 546 | struct Roamer | ||
| 547 | { | ||
| 548 | /*0x00*/ u32 ivs; | ||
| 549 | /*0x04*/ u32 personality; | ||
| 550 | /*0x08*/ u16 species; | ||
| 551 | /*0x0A*/ u16 hp; | ||
| 552 | /*0x0C*/ u8 level; | ||
| 553 | /*0x0D*/ u8 status; | ||
| 554 | /*0x0E*/ u8 cool; | ||
| 555 | /*0x0F*/ u8 beauty; | ||
| 556 | /*0x10*/ u8 cute; | ||
| 557 | /*0x11*/ u8 smart; | ||
| 558 | /*0x12*/ u8 tough; | ||
| 559 | /*0x13*/ u8 active; | ||
| 560 | }; | ||
| 561 | |||
| 562 | struct RamScriptData | ||
| 563 | { | ||
| 564 | u8 magic; | ||
| 565 | u8 mapGroup; | ||
| 566 | u8 mapNum; | ||
| 567 | u8 objectId; | ||
| 568 | u8 script[995]; | ||
| 569 | } __attribute__((aligned(1),packed)); | ||
| 570 | |||
| 571 | struct RamScript | ||
| 572 | { | ||
| 573 | u32 checksum; | ||
| 574 | struct RamScriptData data; | ||
| 575 | } __attribute__((aligned(1),packed)); | ||
| 576 | |||
| 577 | struct SB1_2EFC_Struct | ||
| 578 | { | ||
| 579 | u8 unknown[0x20]; | ||
| 580 | }; | ||
| 581 | |||
| 582 | struct EasyChatPair | ||
| 583 | { | ||
| 584 | u16 unk0_0:7; | ||
| 585 | u16 unk0_7:7; | ||
| 586 | u16 unk1_6:1; | ||
| 587 | u16 unk2; | ||
| 588 | u16 words[2]; | ||
| 589 | }; /*size = 0x8*/ | ||
| 590 | |||
| 591 | struct TVShowCommon { | ||
| 592 | /*0x00*/ u8 var00; | ||
| 593 | /*0x01*/ u8 var01; | ||
| 594 | }; | ||
| 595 | |||
| 596 | struct TVShowFanClubLetter { | ||
| 597 | /*0x00*/ u8 var00; | ||
| 598 | /*0x01*/ u8 var01; | ||
| 599 | /*0x02*/ u16 species; | ||
| 600 | u8 pad04[12]; | ||
| 601 | /*0x10*/ u8 playerName[8]; | ||
| 602 | /*0x18*/ u8 var18; | ||
| 603 | }; | ||
| 604 | |||
| 605 | struct TVShowRecentHappenings { | ||
| 606 | /*0x00*/ u8 var00; | ||
| 607 | /*0x01*/ u8 var01; | ||
| 608 | /*0x02*/ u16 var02; | ||
| 609 | u8 pad04[12]; | ||
| 610 | /*0x10*/ u8 var10[8]; | ||
| 611 | /*0x18*/ u8 var18; | ||
| 612 | u8 pad19[10]; | ||
| 613 | }; | ||
| 614 | |||
| 615 | struct TVShowFanclubOpinions { | ||
| 616 | /*0x00*/ u8 var00; | ||
| 617 | /*0x01*/ u8 var01; | ||
| 618 | /*0x02*/ u16 var02; | ||
| 619 | /*0x04*/ u8 var04A:4; | ||
| 620 | u8 var04B:4; | ||
| 621 | /*0x04*/ u8 var05[8]; | ||
| 622 | /*0x0D*/ u8 var0D; | ||
| 623 | /*0x0E*/ u8 var0E; | ||
| 624 | /*0x0F*/ u8 var0F; | ||
| 625 | /*0x10*/ u8 var10[8]; | ||
| 626 | }; | ||
| 627 | |||
| 628 | struct TVShowNameRaterShow { | ||
| 629 | /*0x00*/ u8 var00; | ||
| 630 | /*0x01*/ u8 var01; | ||
| 631 | /*0x02*/ u16 species; | ||
| 632 | /*0x04*/ u8 pokemonName[11]; | ||
| 633 | /*0x0F*/ u8 trainerName[11]; | ||
| 634 | /*0x1A*/ u8 random; | ||
| 635 | /*0x1B*/ u8 random2; | ||
| 636 | /*0x1C*/ u16 var1C; | ||
| 637 | /*0x1E*/ u8 language; | ||
| 638 | /*0x1F*/ u8 var1F; | ||
| 639 | }; | ||
| 640 | |||
| 641 | struct TVShowMassOutbreak { | ||
| 642 | /*0x00*/ u8 var00; | ||
| 643 | /*0x01*/ u8 var01; | ||
| 644 | /*0x02*/ u8 var02; | ||
| 645 | /*0x03*/ u8 var03; | ||
| 646 | /*0x04*/ u16 moves[4]; | ||
| 647 | /*0x0C*/ u16 species; | ||
| 648 | /*0x0E*/ u16 var0E; | ||
| 649 | /*0x10*/ u8 locationMapNum; | ||
| 650 | /*0x11*/ u8 locationMapGroup; | ||
| 651 | /*0x12*/ u8 var12; | ||
| 652 | /*0x13*/ u8 probability; | ||
| 653 | /*0x14*/ u8 level; | ||
| 654 | /*0x15*/ u8 var15; | ||
| 655 | /*0x16*/ u16 var16; | ||
| 656 | /*0x18*/ u8 var18; | ||
| 657 | u8 pad19[11]; | ||
| 658 | }; | ||
| 659 | |||
| 660 | typedef union TVShow { | ||
| 661 | struct TVShowCommon common; | ||
| 662 | struct TVShowFanClubLetter fanclubLetter; | ||
| 663 | struct TVShowRecentHappenings recentHappenings; | ||
| 664 | struct TVShowFanclubOpinions fanclubOpinions; | ||
| 665 | struct TVShowNameRaterShow nameRaterShow; | ||
| 666 | struct TVShowMassOutbreak massOutbreak; | ||
| 667 | } TVShow; | ||
| 668 | |||
| 669 | struct __attribute__((aligned(4))) MailStruct | ||
| 670 | { | ||
| 671 | /*0x00*/ u16 words[9]; | ||
| 672 | /*0x12*/ u8 playerName[8]; | ||
| 673 | /*0x1A*/ u8 trainerId[4]; | ||
| 674 | /*0x1E*/ u16 species; | ||
| 675 | /*0x20*/ u16 itemId; | ||
| 676 | }; | ||
| 677 | |||
| 678 | struct UnkMauvilleOldManStruct | ||
| 679 | { | ||
| 680 | u8 unk_2D94; | ||
| 681 | u8 unk_2D95; | ||
| 682 | /*0x2D96*/ u16 mauvilleOldMan_ecArray[6]; | ||
| 683 | /*0x2DA2*/ u16 mauvilleOldMan_ecArray2[6]; | ||
| 684 | /*0x2DAE*/ u8 playerName[8]; | ||
| 685 | /*0x2DB6*/ u8 filler_2DB6[0x3]; | ||
| 686 | /*0x2DB9*/ u8 playerTrainerId[4]; | ||
| 687 | u8 unk_2DBD; | ||
| 688 | /* size = 0x2C */ | ||
| 689 | }; | ||
| 690 | |||
| 691 | struct UnkMauvilleOldManStruct2 | ||
| 692 | { | ||
| 693 | u8 filler0; | ||
| 694 | u8 unk1; | ||
| 695 | u8 unk2; | ||
| 696 | u16 mauvilleOldMan_ecArray[10]; | ||
| 697 | u16 mauvilleOldMan_ecArray2[6]; | ||
| 698 | u8 fillerF[0x2]; | ||
| 699 | /* size = 0x2C */ | ||
| 700 | }; | ||
| 701 | |||
| 702 | typedef union OldMan { | ||
| 703 | struct UnkMauvilleOldManStruct oldMan1; | ||
| 704 | struct UnkMauvilleOldManStruct2 oldMan2; | ||
| 705 | } OldMan; | ||
| 706 | |||
| 707 | struct QuestStoryNPC { | ||
| 708 | u16 bitfield; | ||
| 709 | u8 direction; | ||
| 710 | u8 height; | ||
| 711 | u8 type_id; | ||
| 712 | u8 running_behaviour_or_picture_id; | ||
| 713 | u8 is_trainer; | ||
| 714 | u8 local_id; | ||
| 715 | u8 local_mapnumber; | ||
| 716 | u8 local_mapbank; | ||
| 717 | u16 x; | ||
| 718 | u16 y; | ||
| 719 | u8 sight_distance; | ||
| 720 | u8 role_from; | ||
| 721 | u8 unknown_decrement_on_step; | ||
| 722 | u8 unk_11; | ||
| 723 | u16 padding_12; | ||
| 724 | }; | ||
| 725 | |||
| 726 | struct QuestStory { | ||
| 727 | u8 active; | ||
| 728 | u8 bank; | ||
| 729 | u8 map; | ||
| 730 | u8 warpId; | ||
| 731 | u16 x; | ||
| 732 | u16 y; | ||
| 733 | struct QuestStoryNPC npc[0x10]; | ||
| 734 | u8 unk_148[0x51f]; | ||
| 735 | }; | ||
| 736 | |||
| 737 | struct NPCState { | ||
| 738 | u8 bitfield; | ||
| 739 | u8 obj_anim_and_vis_control; | ||
| 740 | u8 unk_2; | ||
| 741 | u8 unk_3; | ||
| 742 | u8 oamid; | ||
| 743 | u8 type_id; | ||
| 744 | u8 running_behaviour_or_picture_id; | ||
| 745 | u8 is_trainer; | ||
| 746 | u8 local_id; | ||
| 747 | u8 local_mapnumber; | ||
| 748 | u8 local_mapbank; | ||
| 749 | u8 height; | ||
| 750 | struct Coords16 stay_around; | ||
| 751 | struct Coords16 to; | ||
| 752 | struct Coords16 from; | ||
| 753 | u8 direction; | ||
| 754 | u8 movement_area; | ||
| 755 | u8 objid_surfing; | ||
| 756 | u8 objid_1B; | ||
| 757 | u8 idx_movement_behaviour; | ||
| 758 | u8 sight_distance; | ||
| 759 | u8 role_to; | ||
| 760 | u8 role_from; | ||
| 761 | u8 unk_20; | ||
| 762 | u8 unknown_decrement_on_step; | ||
| 763 | u8 unk_22; | ||
| 764 | u8 unk_23; | ||
| 765 | }; | ||
| 766 | |||
| 767 | struct DaycarePokemon { | ||
| 768 | struct BoxPokemon pokemon; | ||
| 769 | u8 unk_50[56]; | ||
| 770 | u32 steps; | ||
| 771 | }; | ||
| 772 | |||
| 773 | |||
| 774 | struct Time | ||
| 775 | { | ||
| 776 | /*0x00*/ s16 days; | ||
| 777 | /*0x02*/ s8 hours; | ||
| 778 | /*0x03*/ s8 minutes; | ||
| 779 | /*0x04*/ s8 seconds; | ||
| 780 | }; | ||
| 781 | |||
| 782 | struct Pokedex | ||
| 783 | { | ||
| 784 | /*0x00*/ u8 order; | ||
| 785 | /*0x01*/ u8 unknown1; | ||
| 786 | /*0x02*/ u8 nationalMagic; // must equal 0xDA in order to have National mode | ||
| 787 | /*0x03*/ u8 unknown2; | ||
| 788 | /*0x04*/ u32 unownPersonality; // set when you first see Unown | ||
| 789 | /*0x08*/ u32 spindaPersonality; // set when you first see Spinda | ||
| 790 | /*0x0C*/ u32 unknown3; | ||
| 791 | /*0x10*/ u8 owned[52]; | ||
| 792 | /*0x44*/ u8 seen[52]; | ||
| 793 | }; \ 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 @@ | |||
| 1 | .section ".init" | ||
| 2 | .global _start | ||
| 3 | .align | ||
| 4 | .arm | ||
| 5 | @--------------------------------------------------------------------------------- | ||
| 6 | _start: | ||
| 7 | @--------------------------------------------------------------------------------- | ||
| 8 | b rom_header_end | ||
| 9 | |||
| 10 | .fill 156,1,0 @ Nintendo Logo Character Data (8000004h) | ||
| 11 | .fill 16,1,0 @ Game Title | ||
| 12 | .byte 0x30,0x31 @ Maker Code (80000B0h) | ||
| 13 | .byte 0x96 @ Fixed Value (80000B2h) | ||
| 14 | .byte 0x00 @ Main Unit Code (80000B3h) | ||
| 15 | .byte 0x00 @ Device Type (80000B4h) | ||
| 16 | .fill 7,1,0 @ unused | ||
| 17 | .byte 0x00 @ Software Version No (80000BCh) | ||
| 18 | .byte 0xf0 @ Complement Check (80000BDh) | ||
| 19 | .byte 0x00,0x00 @ Checksum (80000BEh) | ||
| 20 | |||
| 21 | @--------------------------------------------------------------------------------- | ||
| 22 | rom_header_end: | ||
| 23 | @--------------------------------------------------------------------------------- | ||
| 24 | b start_vector @ This branch must be here for proper | ||
| 25 | @ positioning of the following header. | ||
| 26 | |||
| 27 | .GLOBAL __boot_method, __slave_number | ||
| 28 | @--------------------------------------------------------------------------------- | ||
| 29 | __boot_method: | ||
| 30 | @--------------------------------------------------------------------------------- | ||
| 31 | .byte 0 @ boot method (0=ROM boot, 3=Multiplay boot) | ||
| 32 | @--------------------------------------------------------------------------------- | ||
| 33 | __slave_number: | ||
| 34 | @--------------------------------------------------------------------------------- | ||
| 35 | .byte 0 @ slave # (1=slave#1, 2=slave#2, 3=slave#3) | ||
| 36 | |||
| 37 | .byte 0 @ reserved | ||
| 38 | .byte 0 @ reserved | ||
| 39 | .word 0 @ reserved | ||
| 40 | .word 0 @ reserved | ||
| 41 | .word 0 @ reserved | ||
| 42 | .word 0 @ reserved | ||
| 43 | .word 0 @ reserved | ||
| 44 | .word 0 @ reserved | ||
| 45 | |||
| 46 | .fill 4096,1,0 @ 4kb of filler so no useful code gets overwritten when flash bytes get copied over the top. | ||
| 47 | .global start_vector | ||
| 48 | .align | ||
| 49 | @--------------------------------------------------------------------------------- | ||
| 50 | start_vector: | ||
| 51 | @--------------------------------------------------------------------------------- | ||
| 52 | |||
| 53 | @--------------------------------------------------------------------------------- | ||
| 54 | @ Enter Thumb mode | ||
| 55 | @--------------------------------------------------------------------------------- | ||
| 56 | add r0, pc, #1 | ||
| 57 | bx r0 | ||
| 58 | |||
| 59 | .thumb | ||
| 60 | @ Turn off sound | ||
| 61 | ldr r1, =0x4000084 | ||
| 62 | eor r0, r0, r0 | ||
| 63 | strh r0, [r1] | ||
| 64 | |||
| 65 | @--------------------------------------------------------------------------------- | ||
| 66 | @ set heap end | ||
| 67 | @--------------------------------------------------------------------------------- | ||
| 68 | ldr r1, =fake_heap_end | ||
| 69 | ldr r0, =__eheap_end | ||
| 70 | str r0, [r1] | ||
| 71 | @--------------------------------------------------------------------------------- | ||
| 72 | @ global constructors | ||
| 73 | @--------------------------------------------------------------------------------- | ||
| 74 | ldr r3, =__libc_init_array | ||
| 75 | push {lr} | ||
| 76 | bl _blx_r3_stub | ||
| 77 | @--------------------------------------------------------------------------------- | ||
| 78 | @ Jump to user code | ||
| 79 | @--------------------------------------------------------------------------------- | ||
| 80 | mov r0, #0 @ int argc | ||
| 81 | mov r1, #0 @ char *argv[] | ||
| 82 | ldr r3, =main | ||
| 83 | bl _blx_r3_stub | ||
| 84 | @; If we're here, turn the sound back on before we return | ||
| 85 | ldr r1, =0x4000084 | ||
| 86 | mov r0, #0x8F | ||
| 87 | strh r0, [r1] | ||
| 88 | @; Also turn interrupts back on | ||
| 89 | ldr r1, =0x4000208 | ||
| 90 | mov r0, #1 | ||
| 91 | str r0, [r1] | ||
| 92 | pop {pc} | ||
| 93 | |||
| 94 | @--------------------------------------------------------------------------------- | ||
| 95 | _blx_r3_stub: | ||
| 96 | @--------------------------------------------------------------------------------- | ||
| 97 | bx r3 | ||
| 98 | |||
| 99 | .align | ||
| 100 | .pool | ||
| 101 | .end | ||
| 102 | |||
| 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 @@ | |||
| 1 | #include "link.h" | ||
| 2 | |||
| 3 | u32 waitForButtons(u32 mask) | ||
| 4 | { | ||
| 5 | for (;;) | ||
| 6 | { | ||
| 7 | PAD_ScanPads(); | ||
| 8 | VIDEO_WaitVSync(); | ||
| 9 | |||
| 10 | u32 btns = PAD_ButtonsDown(0); | ||
| 11 | if (btns & mask) | ||
| 12 | { | ||
| 13 | return btns; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | } | ||
| 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 @@ | |||
| 1 | #ifndef _LINK_H_ | ||
| 2 | #define _LINK_H_ | ||
| 3 | |||
| 4 | #include <gccore.h> | ||
| 5 | |||
| 6 | u32 waitForButtons(u32 mask); | ||
| 7 | |||
| 8 | #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 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2017 hatkirby | ||
| 2 | * Copyright (C) 2016 FIX94 | 3 | * Copyright (C) 2016 FIX94 |
| 3 | * | 4 | * |
| 4 | * This software may be modified and distributed under the terms | 5 | * This software may be modified and distributed under the terms |
| @@ -15,6 +16,7 @@ | |||
| 15 | #include <fcntl.h> | 16 | #include <fcntl.h> |
| 16 | #include <dirent.h> | 17 | #include <dirent.h> |
| 17 | #include <fat.h> | 18 | #include <fat.h> |
| 19 | #include "link.h" | ||
| 18 | 20 | ||
| 19 | //from my tests 50us seems to be the lowest | 21 | //from my tests 50us seems to be the lowest |
| 20 | //safe si transfer delay in between calls | 22 | //safe si transfer delay in between calls |
| @@ -25,11 +27,12 @@ extern u32 gba_mb_gba_size; | |||
| 25 | 27 | ||
| 26 | void printmain() | 28 | void printmain() |
| 27 | { | 29 | { |
| 28 | printf("\x1b[2J"); | 30 | printf("\x1b[2J"); |
| 29 | printf("\x1b[37m"); | 31 | printf("\x1b[37m"); |
| 30 | printf("GBA Link Cable Dumper v1.6 by FIX94\n"); | 32 | printf("Pokemon Gen III Data Extractor by hatkirby\n"); |
| 31 | printf("Save Support based on SendSave by Chishm\n"); | 33 | printf("Based on GBA Link Cable Dumper v1.6 by FIX94\n"); |
| 32 | printf("GBA BIOS Dumper by Dark Fader\n \n"); | 34 | printf("Save Support based on SendSave by Chishm\n"); |
| 35 | printf("Save Structure based on gba-gen3multiboot by slipstream/RoL\n"); | ||
| 33 | } | 36 | } |
| 34 | 37 | ||
| 35 | u8 *resbuf,*cmdbuf; | 38 | u8 *resbuf,*cmdbuf; |
| @@ -37,520 +40,886 @@ u8 *resbuf,*cmdbuf; | |||
| 37 | volatile u32 transval = 0; | 40 | volatile u32 transval = 0; |
| 38 | void transcb(s32 chan, u32 ret) | 41 | void transcb(s32 chan, u32 ret) |
| 39 | { | 42 | { |
| 40 | transval = 1; | 43 | transval = 1; |
| 41 | } | 44 | } |
| 42 | 45 | ||
| 43 | volatile u32 resval = 0; | 46 | volatile u32 resval = 0; |
| 44 | void acb(s32 res, u32 val) | 47 | void acb(s32 res, u32 val) |
| 45 | { | 48 | { |
| 46 | resval = val; | 49 | resval = val; |
| 47 | } | 50 | } |
| 48 | 51 | /* | |
| 49 | unsigned int docrc(u32 crc, u32 val) | 52 | unsigned int docrc(u32 crc, u32 val) |
| 50 | { | 53 | { |
| 51 | int i; | 54 | int i; |
| 52 | for(i = 0; i < 0x20; i++) | 55 | for (i = 0; i < 0x20; i++) |
| 53 | { | 56 | { |
| 54 | if((crc^val)&1) | 57 | if ((crc ^ val) & 1) |
| 55 | { | 58 | { |
| 56 | crc>>=1; | 59 | crc>>=1; |
| 57 | crc^=0xa1c1; | 60 | crc^=0xa1c1; |
| 58 | } | 61 | } else { |
| 59 | else | 62 | crc>>=1; |
| 60 | crc>>=1; | 63 | } |
| 61 | val>>=1; | 64 | |
| 65 | val>>=1; | ||
| 66 | } | ||
| 67 | |||
| 68 | return crc; | ||
| 69 | }*/ | ||
| 70 | unsigned int docrc(u32 crc,u32 val) { | ||
| 71 | u32 result; | ||
| 72 | |||
| 73 | result = val ^ crc; | ||
| 74 | for (int i = 0; i < 0x20; i++) { | ||
| 75 | if (result & 1) { | ||
| 76 | result >>= 1; | ||
| 77 | result ^= 0xA1C1; | ||
| 78 | } else result >>= 1; | ||
| 62 | } | 79 | } |
| 63 | return crc; | 80 | return result; |
| 64 | } | 81 | } |
| 65 | 82 | ||
| 66 | void endproc() | ||
| 67 | { | ||
| 68 | printf("Start pressed, exit\n"); | ||
| 69 | VIDEO_WaitVSync(); | ||
| 70 | VIDEO_WaitVSync(); | ||
| 71 | exit(0); | ||
| 72 | } | ||
| 73 | void fixFName(char *str) | 83 | void fixFName(char *str) |
| 74 | { | 84 | { |
| 75 | u8 i = 0; | 85 | u8 i = 0; |
| 76 | for(i = 0; i < strlen(str); ++i) | 86 | for (i = 0; i < strlen(str); ++i) |
| 77 | { | 87 | { |
| 78 | if(str[i] < 0x20 || str[i] > 0x7F) | 88 | if (str[i] < 0x20 || str[i] > 0x7F) |
| 79 | str[i] = '_'; | 89 | { |
| 80 | else switch(str[i]) | 90 | str[i] = '_'; |
| 81 | { | 91 | } else { |
| 82 | case '\\': | 92 | switch (str[i]) |
| 83 | case '/': | 93 | { |
| 84 | case ':': | 94 | case '\\': |
| 85 | case '*': | 95 | case '/': |
| 86 | case '?': | 96 | case ':': |
| 87 | case '\"': | 97 | case '*': |
| 88 | case '<': | 98 | case '?': |
| 89 | case '>': | 99 | case '\"': |
| 90 | case '|': | 100 | case '<': |
| 91 | str[i] = '_'; | 101 | case '>': |
| 92 | break; | 102 | case '|': |
| 93 | default: | 103 | str[i] = '_'; |
| 94 | break; | 104 | break; |
| 95 | } | 105 | default: |
| 96 | } | 106 | break; |
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 97 | } | 110 | } |
| 111 | |||
| 98 | unsigned int calckey(unsigned int size) | 112 | unsigned int calckey(unsigned int size) |
| 99 | { | 113 | { |
| 100 | unsigned int ret = 0; | 114 | unsigned int ret = 0; |
| 101 | size=(size-0x200) >> 3; | 115 | size= (size - 0x200) >> 3; |
| 102 | int res1 = (size&0x3F80) << 1; | 116 | int res1 = (size & 0x3F80) << 1; |
| 103 | res1 |= (size&0x4000) << 2; | 117 | res1 |= (size & 0x4000) << 2; |
| 104 | res1 |= (size&0x7F); | 118 | res1 |= (size & 0x7F); |
| 105 | res1 |= 0x380000; | 119 | res1 |= 0x380000; |
| 106 | int res2 = res1; | 120 | int res2 = res1; |
| 107 | res1 = res2 >> 0x10; | 121 | res1 = res2 >> 0x10; |
| 108 | int res3 = res2 >> 8; | 122 | int res3 = res2 >> 8; |
| 109 | res3 += res1; | 123 | res3 += res1; |
| 110 | res3 += res2; | 124 | res3 += res2; |
| 111 | res3 <<= 24; | 125 | res3 <<= 24; |
| 112 | res3 |= res2; | 126 | res3 |= res2; |
| 113 | res3 |= 0x80808080; | 127 | res3 |= 0x80808080; |
| 114 | 128 | ||
| 115 | if((res3&0x200) == 0) | 129 | if ((res3 & 0x200) == 0) |
| 116 | { | 130 | { |
| 117 | ret |= (((res3)&0xFF)^0x4B)<<24; | 131 | ret |= (((res3) & 0xFF) ^ 0x4B) << 24; |
| 118 | ret |= (((res3>>8)&0xFF)^0x61)<<16; | 132 | ret |= (((res3>>8) & 0xFF) ^ 0x61) << 16; |
| 119 | ret |= (((res3>>16)&0xFF)^0x77)<<8; | 133 | ret |= (((res3>>16) & 0xFF) ^ 0x77) << 8; |
| 120 | ret |= (((res3>>24)&0xFF)^0x61); | 134 | ret |= (((res3>>24) & 0xFF) ^ 0x61); |
| 121 | } | 135 | } else { |
| 122 | else | 136 | ret |= (((res3) & 0xFF) ^ 0x73) << 24; |
| 123 | { | 137 | ret |= (((res3>>8) & 0xFF) ^ 0x65) << 16; |
| 124 | ret |= (((res3)&0xFF)^0x73)<<24; | 138 | ret |= (((res3>>16) & 0xFF) ^ 0x64) << 8; |
| 125 | ret |= (((res3>>8)&0xFF)^0x65)<<16; | 139 | ret |= (((res3>>24) & 0xFF) ^ 0x6F); |
| 126 | ret |= (((res3>>16)&0xFF)^0x64)<<8; | 140 | } |
| 127 | ret |= (((res3>>24)&0xFF)^0x6F); | 141 | |
| 128 | } | 142 | return ret; |
| 129 | return ret; | ||
| 130 | } | 143 | } |
| 144 | |||
| 131 | void doreset() | 145 | void doreset() |
| 132 | { | 146 | { |
| 133 | cmdbuf[0] = 0xFF; //reset | 147 | cmdbuf[0] = 0xFF; //reset |
| 134 | transval = 0; | 148 | transval = 0; |
| 135 | SI_Transfer(1,cmdbuf,1,resbuf,3,transcb,SI_TRANS_DELAY); | 149 | SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY); |
| 136 | while(transval == 0) ; | 150 | |
| 151 | while (transval == 0); | ||
| 137 | } | 152 | } |
| 153 | |||
| 138 | void getstatus() | 154 | void getstatus() |
| 139 | { | 155 | { |
| 140 | cmdbuf[0] = 0; //status | 156 | cmdbuf[0] = 0; //status |
| 141 | transval = 0; | 157 | transval = 0; |
| 142 | SI_Transfer(1,cmdbuf,1,resbuf,3,transcb,SI_TRANS_DELAY); | 158 | SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY); |
| 143 | while(transval == 0) ; | 159 | |
| 160 | while (transval == 0); | ||
| 161 | } | ||
| 162 | |||
| 163 | void endproc() | ||
| 164 | { | ||
| 165 | doreset(); | ||
| 166 | printf("Start pressed, exit\n"); | ||
| 167 | VIDEO_WaitVSync(); | ||
| 168 | VIDEO_WaitVSync(); | ||
| 169 | exit(0); | ||
| 170 | } | ||
| 171 | |||
| 172 | void fsleep(int i) | ||
| 173 | { | ||
| 174 | sleep(i); | ||
| 175 | /*PAD_ScanPads(); | ||
| 176 | if (PAD_ButtonsDown(0) & PAD_BUTTON_START) | ||
| 177 | { | ||
| 178 | getstatus(); | ||
| 179 | endproc(); | ||
| 180 | }*/ | ||
| 144 | } | 181 | } |
| 182 | |||
| 145 | u32 recv() | 183 | u32 recv() |
| 146 | { | 184 | { |
| 147 | memset(resbuf,0,32); | 185 | memset(resbuf,0,32); |
| 148 | cmdbuf[0]=0x14; //read | 186 | cmdbuf[0]=0x14; //read |
| 149 | transval = 0; | 187 | transval = 0; |
| 150 | SI_Transfer(1,cmdbuf,1,resbuf,5,transcb,SI_TRANS_DELAY); | 188 | SI_Transfer(1, cmdbuf, 1, resbuf, 5, transcb, SI_TRANS_DELAY); |
| 151 | while(transval == 0) ; | 189 | |
| 152 | return *(vu32*)resbuf; | 190 | while (transval == 0); |
| 191 | printf("%08lx\n", *(vu32*)resbuf); | ||
| 192 | return *(vu32*)resbuf; | ||
| 153 | } | 193 | } |
| 194 | |||
| 154 | void send(u32 msg) | 195 | void send(u32 msg) |
| 155 | { | 196 | { |
| 156 | cmdbuf[0]=0x15;cmdbuf[1]=(msg>>0)&0xFF;cmdbuf[2]=(msg>>8)&0xFF; | 197 | cmdbuf[0] = 0x15; |
| 157 | cmdbuf[3]=(msg>>16)&0xFF;cmdbuf[4]=(msg>>24)&0xFF; | 198 | cmdbuf[1] = (msg >> 0) & 0xFF; |
| 158 | transval = 0; | 199 | cmdbuf[2] = (msg >> 8) & 0xFF; |
| 159 | resbuf[0] = 0; | 200 | cmdbuf[3] = (msg >> 16) & 0xFF; |
| 160 | SI_Transfer(1,cmdbuf,5,resbuf,1,transcb,SI_TRANS_DELAY); | 201 | cmdbuf[4] = (msg >> 24) & 0xFF; |
| 161 | while(transval == 0) ; | 202 | |
| 203 | transval = 0; | ||
| 204 | resbuf[0] = 0; | ||
| 205 | SI_Transfer(1, cmdbuf, 5, resbuf, 1, transcb, SI_TRANS_DELAY); | ||
| 206 | |||
| 207 | while (transval == 0); | ||
| 162 | } | 208 | } |
| 209 | |||
| 163 | bool dirExists(const char *path) | 210 | bool dirExists(const char *path) |
| 164 | { | 211 | { |
| 165 | DIR *dir; | 212 | DIR *dir = opendir(path); |
| 166 | dir = opendir(path); | 213 | if (dir) |
| 167 | if(dir) | 214 | { |
| 168 | { | 215 | closedir(dir); |
| 169 | closedir(dir); | 216 | |
| 170 | return true; | 217 | return true; |
| 171 | } | 218 | } |
| 172 | return false; | 219 | |
| 220 | return false; | ||
| 173 | } | 221 | } |
| 222 | |||
| 174 | void createFile(const char *path, size_t size) | 223 | void createFile(const char *path, size_t size) |
| 175 | { | 224 | { |
| 176 | int fd = open(path, O_WRONLY|O_CREAT); | 225 | int fd = open(path, O_WRONLY | O_CREAT); |
| 177 | if(fd >= 0) | 226 | if (fd >= 0) |
| 178 | { | 227 | { |
| 179 | ftruncate(fd, size); | 228 | ftruncate(fd, size); |
| 180 | close(fd); | 229 | close(fd); |
| 181 | } | 230 | } |
| 182 | } | 231 | } |
| 232 | |||
| 183 | void warnError(char *msg) | 233 | void warnError(char *msg) |
| 184 | { | 234 | { |
| 185 | puts(msg); | 235 | puts(msg); |
| 186 | VIDEO_WaitVSync(); | 236 | VIDEO_WaitVSync(); |
| 187 | VIDEO_WaitVSync(); | 237 | VIDEO_WaitVSync(); |
| 188 | sleep(2); | 238 | sleep(2); |
| 189 | } | 239 | } |
| 240 | |||
| 190 | void fatalError(char *msg) | 241 | void fatalError(char *msg) |
| 191 | { | 242 | { |
| 192 | puts(msg); | 243 | puts(msg); |
| 193 | VIDEO_WaitVSync(); | 244 | VIDEO_WaitVSync(); |
| 194 | VIDEO_WaitVSync(); | 245 | VIDEO_WaitVSync(); |
| 195 | sleep(5); | 246 | sleep(5); |
| 196 | exit(0); | 247 | exit(0); |
| 197 | } | 248 | } |
| 198 | int main(int argc, char *argv[]) | 249 | |
| 199 | { | 250 | u32 genKeyA() { |
| 200 | void *xfb = NULL; | 251 | u32 retries = 0; |
| 201 | GXRModeObj *rmode = NULL; | 252 | while (true) { |
| 202 | VIDEO_Init(); | 253 | u32 key = 0; |
| 203 | rmode = VIDEO_GetPreferredMode(NULL); | 254 | if (retries > 32) { |
| 204 | xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); | 255 | key = 0xDD654321; |
| 205 | VIDEO_Configure(rmode); | 256 | } else { |
| 206 | VIDEO_SetNextFramebuffer(xfb); | 257 | key = (rand() & 0x00ffffff) | 0xDD000000; |
| 207 | VIDEO_SetBlack(FALSE); | 258 | } |
| 208 | VIDEO_Flush(); | 259 | u32 unk = (key % 2 != 0); |
| 209 | VIDEO_WaitVSync(); | 260 | u32 v12 = key; |
| 210 | if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); | 261 | for (u32 v13 = 1; v13 < 32; v13++) { |
| 211 | int x = 24, y = 32, w, h; | 262 | v12 >>= 1; |
| 212 | w = rmode->fbWidth - (32); | 263 | unk += (v12 % 2 != 0); |
| 213 | h = rmode->xfbHeight - (48); | 264 | } |
| 214 | CON_InitEx(rmode, x, y, w, h); | 265 | if ((unk >= 10 && unk <= 24)) { |
| 215 | VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK); | 266 | if (retries > 4) printf("KeyA retries = %ld",retries); |
| 216 | PAD_Init(); | 267 | printf("KeyA = 0x%08lx\n",key); |
| 217 | cmdbuf = memalign(32,32); | 268 | return key; |
| 218 | resbuf = memalign(32,32); | 269 | } |
| 219 | u8 *testdump = memalign(32,0x400000); | 270 | retries++; |
| 220 | if(!testdump) return 0; | ||
| 221 | if(!fatInitDefault()) | ||
| 222 | { | ||
| 223 | printmain(); | ||
| 224 | fatalError("ERROR: No usable device found to write dumped files to!"); | ||
| 225 | } | 271 | } |
| 226 | mkdir("/dumps", S_IREAD | S_IWRITE); | 272 | } |
| 227 | if(!dirExists("/dumps")) | 273 | |
| 228 | { | 274 | u32 checkKeyB(u32 KeyBRaw) { |
| 229 | printmain(); | 275 | if ((KeyBRaw & 0xFF) != 0xEE) { |
| 230 | fatalError("ERROR: Could not create dumps folder, make sure you have a supported device connected!"); | 276 | printf("Invalid KeyB - lowest 8 bits should be 0xEE, actually 0x%02x\n",((u8)(KeyBRaw))); |
| 277 | return 0; | ||
| 231 | } | 278 | } |
| 232 | int i; | 279 | u32 KeyB = KeyBRaw & 0xffffff00; |
| 233 | while(1) | 280 | u32 val = KeyB; |
| 234 | { | 281 | u32 unk = (val < 0); |
| 235 | printmain(); | 282 | for (u32 i = 1; i < 24; i++) { |
| 236 | printf("Waiting for a GBA in port 2...\n"); | 283 | val <<= 1; |
| 237 | resval = 0; | 284 | unk += (val < 0); |
| 238 | 285 | } | |
| 239 | SI_GetTypeAsync(1,acb); | 286 | if (unk > 14) { |
| 240 | while(1) | 287 | printf("Invalid KeyB - high 24 bits bad: 0x%08lx\n",KeyB); |
| 241 | { | 288 | return 0; |
| 242 | if(resval) | 289 | } |
| 243 | { | 290 | printf("Valid KeyB: 0x%08lx\n",KeyB); |
| 244 | if(resval == 0x80 || resval & 8) | 291 | return KeyB; |
| 245 | { | 292 | } |
| 246 | resval = 0; | 293 | |
| 247 | SI_GetTypeAsync(1,acb); | 294 | u32 deriveKeyC(u32 keyCderive, u32 kcrc) { |
| 248 | } | 295 | u32 keyc = 0; |
| 249 | else if(resval) | 296 | u32 keyCi = 0; |
| 250 | break; | 297 | do { |
| 251 | } | 298 | u32 v5 = 0x1000000 * keyCi - 1; |
| 252 | PAD_ScanPads(); | 299 | u32 keyCattempt = docrc(kcrc,v5); |
| 253 | VIDEO_WaitVSync(); | 300 | //printf("i = %d; keyCderive = %08x; keyCattempt = %08x\n",keyCi,keyCderive,keyCattempt); |
| 254 | if(PAD_ButtonsHeld(0)) | 301 | if (keyCderive == keyCattempt) { |
| 255 | endproc(); | 302 | keyc = v5; |
| 303 | printf("Found keyC: %08lx\n",keyc); | ||
| 304 | return keyc; | ||
| 256 | } | 305 | } |
| 257 | if(resval & SI_GBA) | 306 | keyCi++; |
| 307 | } while (keyCi < 256); | ||
| 308 | return keyc; | ||
| 309 | } | ||
| 310 | |||
| 311 | int main(int argc, char *argv[]) | ||
| 312 | { | ||
| 313 | void *xfb = NULL; | ||
| 314 | GXRModeObj *rmode = NULL; | ||
| 315 | VIDEO_Init(); | ||
| 316 | rmode = VIDEO_GetPreferredMode(NULL); | ||
| 317 | xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); | ||
| 318 | VIDEO_Configure(rmode); | ||
| 319 | VIDEO_SetNextFramebuffer(xfb); | ||
| 320 | VIDEO_SetBlack(FALSE); | ||
| 321 | VIDEO_Flush(); | ||
| 322 | VIDEO_WaitVSync(); | ||
| 323 | if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); | ||
| 324 | int x = 24, y = 32, w, h; | ||
| 325 | w = rmode->fbWidth - (32); | ||
| 326 | h = rmode->xfbHeight - (48); | ||
| 327 | CON_InitEx(rmode, x, y, w, h); | ||
| 328 | VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK); | ||
| 329 | PAD_Init(); | ||
| 330 | cmdbuf = memalign(32,32); | ||
| 331 | resbuf = memalign(32,32); | ||
| 332 | u8 *testdump = memalign(32,0x400000); | ||
| 333 | if(!testdump) return 0; | ||
| 334 | |||
| 335 | /*if (!fatInitDefault()) | ||
| 336 | { | ||
| 337 | printmain(); | ||
| 338 | fatalError("ERROR: No usable device found to write dumped files to!"); | ||
| 339 | } | ||
| 340 | |||
| 341 | mkdir("/dumps", S_IREAD | S_IWRITE); | ||
| 342 | if (!dirExists("/dumps")) | ||
| 343 | { | ||
| 344 | printmain(); | ||
| 345 | fatalError("ERROR: Could not create dumps folder, make sure you have a supported device connected!"); | ||
| 346 | }*/ | ||
| 347 | |||
| 348 | int i; | ||
| 349 | while(1) | ||
| 350 | { | ||
| 351 | printmain(); | ||
| 352 | |||
| 353 | printf("Press A to begin, press Start to quit.\n"); | ||
| 354 | if (waitForButtons(PAD_BUTTON_A | PAD_BUTTON_START) & PAD_BUTTON_START) | ||
| 258 | { | 355 | { |
| 259 | printf("GBA Found! Waiting on BIOS\n"); | 356 | endproc(); |
| 357 | } | ||
| 358 | |||
| 359 | printf("Waiting for a GBA in port 2...\n"); | ||
| 360 | resval = 0; | ||
| 361 | |||
| 362 | SI_GetTypeAsync(1,acb); | ||
| 363 | while(1) | ||
| 364 | { | ||
| 365 | if (resval) | ||
| 366 | { | ||
| 367 | if (resval == 0x80 || resval & 8) | ||
| 368 | { | ||
| 369 | resval = 0; | ||
| 370 | SI_GetTypeAsync(1,acb); | ||
| 371 | } else if (resval) | ||
| 372 | { | ||
| 373 | break; | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | PAD_ScanPads(); | ||
| 378 | VIDEO_WaitVSync(); | ||
| 379 | if (PAD_ButtonsHeld(0) & PAD_BUTTON_START) | ||
| 380 | { | ||
| 381 | getstatus(); | ||
| 382 | endproc(); | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | if (resval & SI_GBA) | ||
| 387 | { | ||
| 388 | printf("GBA Found! Waiting on BIOS\n"); | ||
| 260 | resbuf[2]=0; | 389 | resbuf[2]=0; |
| 261 | while(!(resbuf[2]&0x10)) | 390 | //u32 oldresult = 0; |
| 391 | //u32 newresult = 0; | ||
| 392 | // wait for the BIOS to hand over to the game | ||
| 393 | do { | ||
| 394 | doreset(); | ||
| 395 | |||
| 396 | } while (!(resbuf[1] > 4)); | ||
| 397 | printf("BIOS handed over to game, waiting on game\n"); | ||
| 398 | do | ||
| 262 | { | 399 | { |
| 263 | doreset(); | 400 | doreset(); |
| 264 | getstatus(); | 401 | } while((resbuf[0] != 0) || !(resbuf[2]&0x10)); |
| 265 | } | 402 | // receive the game-code from GBA side. |
| 266 | printf("Ready, sending dumper\n"); | 403 | u32 gamecode = recv(); |
| 404 | printf("Ready, sending multiboot ROM\n"); | ||
| 267 | unsigned int sendsize = ((gba_mb_gba_size+7)&~7); | 405 | unsigned int sendsize = ((gba_mb_gba_size+7)&~7); |
| 268 | unsigned int ourkey = calckey(sendsize); | 406 | // generate KeyA |
| 407 | unsigned int ourkey = genKeyA(); | ||
| 269 | //printf("Our Key: %08x\n", ourkey); | 408 | //printf("Our Key: %08x\n", ourkey); |
| 270 | //get current sessionkey | 409 | printf("Sending game code that we got: 0x%08lx\n",__builtin_bswap32(gamecode)); |
| 271 | u32 sessionkeyraw = recv(); | 410 | // send the game code back, then KeyA. |
| 272 | u32 sessionkey = __builtin_bswap32(sessionkeyraw^0x7365646F); | 411 | send(__builtin_bswap32(gamecode)); |
| 273 | //send over our own key | 412 | send(ourkey); |
| 274 | send(__builtin_bswap32(ourkey)); | 413 | // get KeyB from GBA, check it to make sure its valid, then xor with KeyA to derive the initial CRC value and the sessionkey. |
| 275 | unsigned int fcrc = 0x15a0; | 414 | u32 sessionkeyraw = 0; |
| 276 | //send over gba header | 415 | do { |
| 277 | for(i = 0; i < 0xC0; i+=4) | 416 | sessionkeyraw = recv(); |
| 278 | send(__builtin_bswap32(*(vu32*)(gba_mb_gba+i))); | 417 | } while (sessionkeyraw == gamecode); |
| 279 | //printf("Header done! Sending ROM...\n"); | 418 | sessionkeyraw = checkKeyB(__builtin_bswap32(sessionkeyraw)); |
| 280 | for(i = 0xC0; i < sendsize; i+=4) | 419 | if (sessionkeyraw == 0) |
| 281 | { | 420 | { |
| 282 | u32 enc = ((gba_mb_gba[i+3]<<24)|(gba_mb_gba[i+2]<<16)|(gba_mb_gba[i+1]<<8)|(gba_mb_gba[i])); | 421 | warnError("Cannot continue.\n"); |
| 283 | fcrc=docrc(fcrc,enc); | 422 | |
| 423 | continue; | ||
| 424 | } | ||
| 425 | |||
| 426 | u32 sessionkey = sessionkeyraw ^ ourkey; | ||
| 427 | u32 kcrc = sessionkey; | ||
| 428 | printf("start kCRC=%08lx\n",kcrc); | ||
| 429 | sessionkey = (sessionkey*0x6177614b)+1; | ||
| 430 | // send hacked up send-size in uint32s | ||
| 431 | u32 hackedupsize = (sendsize >> 3) - 1; | ||
| 432 | printf("Sending hacked up size 0x%08lx\n",hackedupsize); | ||
| 433 | send(hackedupsize); | ||
| 434 | //unsigned int fcrc = 0x00bb; | ||
| 435 | // send over multiboot binary header, in the clear until the end of the nintendo logo. | ||
| 436 | // GBA checks this, if nintendo logo does not match the one in currently inserted cart's ROM, it will not accept any more data. | ||
| 437 | for(i = 0; i < 0xA0; i+=4) { | ||
| 438 | vu32 rom_dword = *(vu32*)(gba_mb_gba+i); | ||
| 439 | send(__builtin_bswap32(rom_dword)); | ||
| 440 | } | ||
| 441 | printf("\n"); | ||
| 442 | printf("Header done! Sending ROM...\n"); | ||
| 443 | // 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. | ||
| 444 | for(i = 0xA0; i < sendsize; i+=4) | ||
| 445 | { | ||
| 446 | u32 dec = ( | ||
| 447 | (((gba_mb_gba[i+3]) << 24) & 0xff000000) | | ||
| 448 | (((gba_mb_gba[i+2]) << 16) & 0x00ff0000) | | ||
| 449 | (((gba_mb_gba[i+1]) << 8) & 0x0000ff00) | | ||
| 450 | (((gba_mb_gba[i]) << 0) & 0x000000ff) | ||
| 451 | ); | ||
| 452 | u32 enc = (dec - kcrc) ^ sessionkey; | ||
| 453 | kcrc=docrc(kcrc,dec); | ||
| 284 | sessionkey = (sessionkey*0x6177614B)+1; | 454 | sessionkey = (sessionkey*0x6177614B)+1; |
| 285 | enc^=sessionkey; | 455 | //enc^=((~(i+(0x20<<20)))+1); |
| 286 | enc^=((~(i+(0x20<<20)))+1); | 456 | //enc^=0x6f646573;//0x20796220; |
| 287 | enc^=0x20796220; | ||
| 288 | send(enc); | 457 | send(enc); |
| 289 | } | 458 | } |
| 290 | fcrc |= (sendsize<<16); | 459 | //fcrc |= (sendsize<<16); |
| 291 | //printf("ROM done! CRC: %08x\n", fcrc); | 460 | printf("ROM done! CRC: %08lx\n", kcrc); |
| 292 | //send over CRC | ||
| 293 | sessionkey = (sessionkey*0x6177614B)+1; | ||
| 294 | fcrc^=sessionkey; | ||
| 295 | fcrc^=((~(i+(0x20<<20)))+1); | ||
| 296 | fcrc^=0x20796220; | ||
| 297 | send(fcrc); | ||
| 298 | //get crc back (unused) | 461 | //get crc back (unused) |
| 299 | recv(); | 462 | // Get KeyC derivation material from GBA (eventually) |
| 300 | printf("Done!\n"); | 463 | u32 keyCderive = 0; |
| 301 | sleep(2); | 464 | do { |
| 302 | //hm | 465 | keyCderive = recv(); |
| 303 | while(1) | 466 | } while (keyCderive <= 0xfeffffff); |
| 304 | { | 467 | keyCderive = __builtin_bswap32(keyCderive); |
| 305 | printmain(); | 468 | keyCderive >>= 8; |
| 306 | printf("Press A once you have a GBA Game inserted.\n"); | 469 | printf("KeyC derivation material: %08lx\n",keyCderive); |
| 307 | printf("Press Y to backup the GBA BIOS.\n \n"); | 470 | |
| 308 | PAD_ScanPads(); | 471 | // (try to) find the KeyC, using the checksum of the multiboot image, and the derivation material that GBA sent to us |
| 309 | VIDEO_WaitVSync(); | 472 | |
| 310 | u32 btns = PAD_ButtonsDown(0); | 473 | u32 keyc = deriveKeyC(keyCderive,kcrc); |
| 311 | if(btns&PAD_BUTTON_START) | 474 | if (keyc == 0) |
| 312 | endproc(); | ||
| 313 | else if(btns&PAD_BUTTON_A) | ||
| 314 | { | 475 | { |
| 315 | if(recv() == 0) //ready | 476 | printf("Could not find keyC - kcrc=0x%08lx\n",kcrc); |
| 316 | { | 477 | warnError("Cannot continue.\n"); |
| 317 | printf("Waiting for GBA\n"); | 478 | continue; |
| 318 | VIDEO_WaitVSync(); | 479 | } |
| 319 | int gbasize = 0; | 480 | |
| 320 | while(gbasize == 0) | 481 | // 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. |
| 321 | gbasize = __builtin_bswap32(recv()); | 482 | u32 bootkey = docrc(0xBB,keyc) | 0xbb000000; |
| 322 | send(0); //got gbasize | 483 | printf("BootKey = 0x%08lx\n",bootkey); |
| 323 | u32 savesize = __builtin_bswap32(recv()); | 484 | send(bootkey); |
| 324 | send(0); //got savesize | 485 | /* |
| 325 | if(gbasize == -1) | 486 | printf("GBA Found! Waiting on BIOS...\n"); |
| 326 | { | 487 | |
| 327 | warnError("ERROR: No (Valid) GBA Card inserted!\n"); | 488 | resbuf[2]=0; |
| 328 | continue; | 489 | while (!(resbuf[2] & 0x10)) |
| 329 | } | 490 | { |
| 330 | //get rom header | 491 | doreset(); |
| 331 | for(i = 0; i < 0xC0; i+=4) | 492 | getstatus(); |
| 332 | *(vu32*)(testdump+i) = recv(); | 493 | } |
| 333 | //print out all the info from the game | 494 | |
| 334 | printf("Game Name: %.12s\n",(char*)(testdump+0xA0)); | 495 | printf("Ready, sending extractor.\n"); |
| 335 | printf("Game ID: %.4s\n",(char*)(testdump+0xAC)); | 496 | |
| 336 | printf("Company ID: %.2s\n",(char*)(testdump+0xB0)); | 497 | unsigned int sendsize = ((gba_mb_gba_size + 7) & ~7); |
| 337 | printf("ROM Size: %02.02f MB\n",((float)(gbasize/1024))/1024.f); | 498 | unsigned int ourkey = calckey(sendsize); |
| 338 | if(savesize > 0) | 499 | |
| 339 | printf("Save Size: %02.02f KB\n \n",((float)(savesize))/1024.f); | 500 | //get current sessionkey |
| 340 | else | 501 | u32 sessionkeyraw = recv(); |
| 341 | printf("No Save File\n \n"); | 502 | u32 sessionkey = __builtin_bswap32(sessionkeyraw ^ 0x7365646F); |
| 342 | //generate file paths | 503 | |
| 343 | char gamename[64]; | 504 | //send over our own key |
| 344 | sprintf(gamename,"/dumps/%.12s [%.4s%.2s].gba", | 505 | send(__builtin_bswap32(ourkey)); |
| 345 | (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0)); | 506 | unsigned int fcrc = 0x15a0; |
| 346 | fixFName(gamename+7); //fix name behind "/dumps/" | 507 | |
| 347 | char savename[64]; | 508 | //send over gba header |
| 348 | sprintf(savename,"/dumps/%.12s [%.4s%.2s].sav", | 509 | for(i = 0; i < 0xC0; i+=4) |
| 349 | (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0)); | 510 | { |
| 350 | fixFName(savename+7); //fix name behind "/dumps/" | 511 | send(__builtin_bswap32(*(vu32*)(gba_mb_gba+i))); |
| 351 | //let the user choose the option | 512 | } |
| 352 | printf("Press A to dump this game, it will take about %i minutes.\n",gbasize/1024/1024*3/2); | 513 | |
| 353 | printf("Press B if you want to cancel dumping this game.\n"); | 514 | for (i = 0xC0; i < sendsize; i+=4) |
| 354 | if(savesize > 0) | 515 | { |
| 355 | { | 516 | u32 enc = ( |
| 356 | printf("Press Y to backup this save file.\n"); | 517 | (gba_mb_gba[i+3] << 24) |
| 357 | printf("Press X to restore this save file.\n"); | 518 | | (gba_mb_gba[i+2] << 16) |
| 358 | printf("Press Z to clear the save file on the GBA Cartridge.\n\n"); | 519 | | (gba_mb_gba[i+1] << 8) |
| 359 | } | 520 | | (gba_mb_gba[i])); |
| 360 | else | 521 | |
| 361 | printf("\n"); | 522 | fcrc = docrc(fcrc,enc); |
| 362 | int command = 0; | 523 | sessionkey = (sessionkey * 0x6177614B) + 1; |
| 363 | while(1) | 524 | enc ^= sessionkey; |
| 364 | { | 525 | enc ^= ((~(i + (0x20 << 20))) + 1); |
| 365 | PAD_ScanPads(); | 526 | enc ^= 0x20796220; |
| 366 | VIDEO_WaitVSync(); | 527 | |
| 367 | u32 btns = PAD_ButtonsDown(0); | 528 | send(enc); |
| 368 | if(btns&PAD_BUTTON_START) | 529 | } |
| 369 | endproc(); | 530 | |
| 370 | else if(btns&PAD_BUTTON_A) | 531 | fcrc |= (sendsize<<16); |
| 371 | { | 532 | |
| 372 | command = 1; | 533 | //send over CRC |
| 373 | break; | 534 | sessionkey = (sessionkey * 0x6177614B) + 1; |
| 374 | } | 535 | fcrc ^= sessionkey; |
| 375 | else if(btns&PAD_BUTTON_B) | 536 | fcrc ^= ((~(i + (0x20 << 20))) + 1); |
| 376 | break; | 537 | fcrc ^= 0x20796220; |
| 377 | else if(savesize > 0) | 538 | |
| 378 | { | 539 | send(fcrc); |
| 379 | if(btns&PAD_BUTTON_Y) | 540 | |
| 380 | { | 541 | //get crc back (unused) |
| 381 | command = 2; | 542 | recv(); |
| 382 | break; | 543 | printf("Done!\n"); |
| 383 | } | 544 | sleep(2); |
| 384 | else if(btns&PAD_BUTTON_X) | 545 | |
| 385 | { | 546 | //hm |
| 386 | command = 3; | 547 | while (1) |
| 387 | break; | 548 | { |
| 388 | } | 549 | printmain(); |
| 389 | else if(btns&PAD_TRIGGER_Z) | 550 | printf("Press A once you have a GBA Game inserted.\n \n"); |
| 390 | { | 551 | |
| 391 | command = 4; | 552 | PAD_ScanPads(); |
| 392 | break; | 553 | VIDEO_WaitVSync(); |
| 393 | } | 554 | u32 btns = PAD_ButtonsDown(0); |
| 394 | } | 555 | if (btns & PAD_BUTTON_START) |
| 395 | } | 556 | { |
| 396 | if(command == 1) | 557 | endproc(); |
| 397 | { | 558 | } else if (btns & PAD_BUTTON_A) |
| 398 | FILE *f = fopen(gamename,"rb"); | 559 | {*/ |
| 399 | if(f) | 560 | sleep(1); |
| 400 | { | 561 | //recv(); |
| 401 | fclose(f); | 562 | |
| 402 | command = 0; | 563 | //if (recv() == 0) //ready |
| 403 | warnError("ERROR: Game already dumped!\n"); | 564 | { |
| 404 | } | 565 | |
| 405 | } | 566 | printf("Waiting for GBA...\n"); |
| 406 | else if(command == 2) | 567 | while (recv() != 0) {fsleep(1);}; |
| 407 | { | 568 | send(0); |
| 408 | FILE *f = fopen(savename,"rb"); | 569 | |
| 409 | if(f) | 570 | VIDEO_WaitVSync(); |
| 410 | { | 571 | |
| 411 | fclose(f); | 572 | /*int gbasize = 0; |
| 412 | command = 0; | 573 | while(gbasize == 0) |
| 413 | warnError("ERROR: Save already backed up!\n"); | 574 | { |
| 414 | } | 575 | gbasize = __builtin_bswap32(recv()); |
| 415 | } | 576 | } |
| 416 | else if(command == 3) | 577 | |
| 417 | { | 578 | send(0); //got gbasize |
| 418 | size_t readsize = 0; | 579 | while (recv()!=0) {sleep(1);}; |
| 419 | FILE *f = fopen(savename,"rb"); | 580 | |
| 420 | if(f) | 581 | //u32 savesize = __builtin_bswap32(recv()); |
| 421 | { | 582 | //send(0); //got savesize |
| 422 | fseek(f,0,SEEK_END); | 583 | |
| 423 | readsize = ftell(f); | 584 | if (gbasize == -1) |
| 424 | if(readsize != savesize) | 585 | { |
| 425 | { | 586 | warnError("ERROR: No (Valid) GBA Card inserted!\n"); |
| 426 | command = 0; | 587 | |
| 427 | warnError("ERROR: Save has the wrong size, aborting restore!\n"); | 588 | continue; |
| 428 | } | 589 | }*/ |
| 429 | else | 590 | |
| 430 | { | 591 | // Get game |
| 431 | rewind(f); | 592 | // -1 - unsupported game |
| 432 | fread(testdump,readsize,1,f); | 593 | // 1 - Ruby |
| 433 | } | 594 | // 2 - Sapphire |
| 434 | fclose(f); | 595 | // 3 - FireRed |
| 435 | } | 596 | // 4 - LeafGreen |
| 436 | else | 597 | // 5 - Emerald |
| 437 | { | 598 | u32 gameId = 0; |
| 438 | command = 0; | 599 | while (gameId == 0) |
| 439 | warnError("ERROR: No Save to restore!\n"); | ||
| 440 | } | ||
| 441 | } | ||
| 442 | send(command); | ||
| 443 | //let gba prepare | ||
| 444 | sleep(1); | ||
| 445 | if(command == 0) | ||
| 446 | continue; | ||
| 447 | else if(command == 1) | ||
| 448 | { | 600 | { |
| 449 | //create base file with size | 601 | gameId = __builtin_bswap32(recv()); |
| 450 | printf("Preparing file...\n"); | 602 | fsleep(1); |
| 451 | createFile(gamename,gbasize); | ||
| 452 | FILE *f = fopen(gamename,"wb"); | ||
| 453 | if(!f) | ||
| 454 | fatalError("ERROR: Could not create file! Exit..."); | ||
| 455 | printf("Dumping...\n"); | ||
| 456 | u32 bytes_read = 0; | ||
| 457 | while(gbasize > 0) | ||
| 458 | { | ||
| 459 | int toread = (gbasize > 0x400000 ? 0x400000 : gbasize); | ||
| 460 | int j; | ||
| 461 | for(j = 0; j < toread; j+=4) | ||
| 462 | { | ||
| 463 | *(vu32*)(testdump+j) = recv(); | ||
| 464 | bytes_read+=4; | ||
| 465 | if((bytes_read&0xFFFF) == 0) | ||
| 466 | printf("\r%02.02f MB done",(float)(bytes_read/1024)/1024.f); | ||
| 467 | } | ||
| 468 | fwrite(testdump,toread,1,f); | ||
| 469 | gbasize -= toread; | ||
| 470 | } | ||
| 471 | printf("\nClosing file\n"); | ||
| 472 | fclose(f); | ||
| 473 | printf("Game dumped!\n"); | ||
| 474 | sleep(5); | ||
| 475 | } | 603 | } |
| 476 | else if(command == 2) | 604 | |
| 605 | send(0); | ||
| 606 | while (recv()!=0) {fsleep(1);}; | ||
| 607 | //sleep(1); | ||
| 608 | |||
| 609 | if (gameId == -1) | ||
| 610 | { | ||
| 611 | warnError("ERROR: Unsupported GBA game inserted!\n"); | ||
| 612 | |||
| 613 | continue; | ||
| 614 | } | ||
| 615 | |||
| 616 | printf("\nPokemon "); | ||
| 617 | switch (gameId) | ||
| 618 | { | ||
| 619 | case 1: printf("Ruby"); break; | ||
| 620 | case 2: printf("Sapphire"); break; | ||
| 621 | case 3: printf("FireRed"); break; | ||
| 622 | case 4: printf("LeafGreen"); break; | ||
| 623 | case 5: printf("Emerald"); break; | ||
| 624 | } | ||
| 625 | |||
| 626 | printf("\n"); | ||
| 627 | VIDEO_WaitVSync(); | ||
| 628 | |||
| 629 | u32 isValid = 0; | ||
| 630 | while (isValid == 0) | ||
| 477 | { | 631 | { |
| 478 | //create base file with size | 632 | isValid = __builtin_bswap32(recv()); |
| 479 | printf("Preparing file...\n"); | 633 | fsleep(1); |
| 480 | createFile(savename,savesize); | ||
| 481 | FILE *f = fopen(savename,"wb"); | ||
| 482 | if(!f) | ||
| 483 | fatalError("ERROR: Could not create file! Exit..."); | ||
| 484 | printf("Waiting for GBA\n"); | ||
| 485 | VIDEO_WaitVSync(); | ||
| 486 | u32 readval = 0; | ||
| 487 | while(readval != savesize) | ||
| 488 | readval = __builtin_bswap32(recv()); | ||
| 489 | send(0); //got savesize | ||
| 490 | printf("Receiving...\n"); | ||
| 491 | for(i = 0; i < savesize; i+=4) | ||
| 492 | *(vu32*)(testdump+i) = recv(); | ||
| 493 | printf("Writing save...\n"); | ||
| 494 | fwrite(testdump,savesize,1,f); | ||
| 495 | fclose(f); | ||
| 496 | printf("Save backed up!\n"); | ||
| 497 | sleep(5); | ||
| 498 | } | 634 | } |
| 499 | else if(command == 3 || command == 4) | 635 | |
| 636 | if (isValid == -1) | ||
| 637 | { | ||
| 638 | //send(0); | ||
| 639 | |||
| 640 | warnError("ERROR: Unsupported game version inserted!\n"); | ||
| 641 | |||
| 642 | continue; | ||
| 643 | } | ||
| 644 | |||
| 645 | send(0); | ||
| 646 | while (recv()!=0) {fsleep(1);}; | ||
| 647 | //sleep(1); | ||
| 648 | /* | ||
| 649 | // Get trainer name | ||
| 650 | u8 trainerName[8]; | ||
| 651 | |||
| 652 | u32 tnd = recv(); | ||
| 653 | send(0); | ||
| 654 | trainerName[0] = (tnd & 0xFF000000); | ||
| 655 | trainerName[1] = (tnd & 0x00FF0000) >> 8; | ||
| 656 | trainerName[2] = (tnd & 0x0000FF00) >> 16; | ||
| 657 | trainerName[3] = (tnd & 0x000000FF) >> 24; | ||
| 658 | |||
| 659 | tnd = recv(); | ||
| 660 | send(0); | ||
| 661 | trainerName[4] = (tnd & 0xFF000000); | ||
| 662 | trainerName[5] = (tnd & 0x00FF0000) >> 8; | ||
| 663 | trainerName[6] = (tnd & 0x0000FF00) >> 16; | ||
| 664 | trainerName[7] = (tnd & 0x000000FF) >> 24; | ||
| 665 | |||
| 666 | printf("Trainer: %s", (char*) trainerName); | ||
| 667 | */ | ||
| 668 | // Get trainer ID | ||
| 669 | u32 trainerId = 0; | ||
| 670 | while (trainerId == 0) | ||
| 500 | { | 671 | { |
| 501 | u32 readval = 0; | 672 | trainerId = __builtin_bswap32(recv()); |
| 502 | while(readval != savesize) | 673 | fsleep(1); |
| 503 | readval = __builtin_bswap32(recv()); | ||
| 504 | if(command == 3) | ||
| 505 | { | ||
| 506 | printf("Sending save\n"); | ||
| 507 | VIDEO_WaitVSync(); | ||
| 508 | for(i = 0; i < savesize; i+=4) | ||
| 509 | send(__builtin_bswap32(*(vu32*)(testdump+i))); | ||
| 510 | } | ||
| 511 | printf("Waiting for GBA\n"); | ||
| 512 | while(recv() != 0) | ||
| 513 | VIDEO_WaitVSync(); | ||
| 514 | printf(command == 3 ? "Save restored!\n" : "Save cleared!\n"); | ||
| 515 | send(0); | ||
| 516 | sleep(5); | ||
| 517 | } | 674 | } |
| 518 | } | 675 | send(0); |
| 519 | } | 676 | while (recv()!=0) {fsleep(1);}; |
| 520 | else if(btns&PAD_BUTTON_Y) | 677 | //sleep(1); |
| 521 | { | 678 | |
| 522 | const char *biosname = "/dumps/gba_bios.bin"; | 679 | printf(" (%ld)\n", trainerId); |
| 523 | FILE *f = fopen(biosname,"rb"); | 680 | |
| 524 | if(f) | 681 | //continue; |
| 525 | { | 682 | |
| 526 | fclose(f); | 683 | // Wait for confirmation. |
| 527 | warnError("ERROR: BIOS already backed up!\n"); | 684 | printf("Press A to import the data from this game.\n"); |
| 528 | } | 685 | printf("Press B to cancel.\n"); |
| 529 | else | 686 | VIDEO_WaitVSync(); |
| 530 | { | 687 | |
| 531 | //create base file with size | 688 | if (waitForButtons(PAD_BUTTON_A | PAD_BUTTON_B) & PAD_BUTTON_B) |
| 532 | printf("Preparing file...\n"); | 689 | { |
| 533 | createFile(biosname,0x4000); | 690 | continue; |
| 534 | f = fopen(biosname,"wb"); | 691 | } |
| 535 | if(!f) | 692 | |
| 536 | fatalError("ERROR: Could not create file! Exit..."); | 693 | |
| 537 | //send over bios dump command | 694 | |
| 538 | send(5); | 695 | |
| 539 | //the gba might still be in a loop itself | 696 | /* |
| 540 | sleep(1); | 697 | //get rom header |
| 541 | //lets go! | 698 | for(i = 0; i < 0xC0; i+=4) |
| 542 | printf("Dumping...\n"); | 699 | *(vu32*)(testdump+i) = recv(); |
| 543 | for(i = 0; i < 0x4000; i+=4) | 700 | //print out all the info from the game |
| 544 | *(vu32*)(testdump+i) = recv(); | 701 | printf("Game Name: %.12s\n",(char*)(testdump+0xA0)); |
| 545 | fwrite(testdump,0x4000,1,f); | 702 | printf("Game ID: %.4s\n",(char*)(testdump+0xAC)); |
| 546 | printf("Closing file\n"); | 703 | printf("Company ID: %.2s\n",(char*)(testdump+0xB0)); |
| 547 | fclose(f); | 704 | printf("ROM Size: %02.02f MB\n",((float)(gbasize/1024))/1024.f); |
| 548 | printf("BIOS dumped!\n"); | 705 | if(savesize > 0) |
| 549 | sleep(5); | 706 | printf("Save Size: %02.02f KB\n \n",((float)(savesize))/1024.f); |
| 550 | } | 707 | else |
| 551 | } | 708 | printf("No Save File\n \n"); |
| 552 | } | 709 | //generate file paths |
| 553 | } | 710 | char gamename[64]; |
| 554 | } | 711 | sprintf(gamename,"/dumps/%.12s [%.4s%.2s].gba", |
| 555 | return 0; | 712 | (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0)); |
| 713 | fixFName(gamename+7); //fix name behind "/dumps/" | ||
| 714 | char savename[64]; | ||
| 715 | sprintf(savename,"/dumps/%.12s [%.4s%.2s].sav", | ||
| 716 | (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0)); | ||
| 717 | fixFName(savename+7); //fix name behind "/dumps/" | ||
| 718 | //let the user choose the option | ||
| 719 | printf("Press A to dump this game, it will take about %i minutes.\n",gbasize/1024/1024*3/2); | ||
| 720 | printf("Press B if you want to cancel dumping this game.\n"); | ||
| 721 | if(savesize > 0) | ||
| 722 | { | ||
| 723 | printf("Press Y to backup this save file.\n"); | ||
| 724 | printf("Press X to restore this save file.\n"); | ||
| 725 | printf("Press Z to clear the save file on the GBA Cartridge.\n\n"); | ||
| 726 | } | ||
| 727 | else | ||
| 728 | printf("\n"); | ||
| 729 | |||
| 730 | int command = 0; | ||
| 731 | while(1) | ||
| 732 | { | ||
| 733 | PAD_ScanPads(); | ||
| 734 | VIDEO_WaitVSync(); | ||
| 735 | u32 btns = PAD_ButtonsDown(0); | ||
| 736 | if(btns&PAD_BUTTON_START) | ||
| 737 | endproc(); | ||
| 738 | else if(btns&PAD_BUTTON_A) | ||
| 739 | { | ||
| 740 | command = 1; | ||
| 741 | break; | ||
| 742 | } | ||
| 743 | else if(btns&PAD_BUTTON_B) | ||
| 744 | break; | ||
| 745 | else if(savesize > 0) | ||
| 746 | { | ||
| 747 | if(btns&PAD_BUTTON_Y) | ||
| 748 | { | ||
| 749 | command = 2; | ||
| 750 | break; | ||
| 751 | } | ||
| 752 | else if(btns&PAD_BUTTON_X) | ||
| 753 | { | ||
| 754 | command = 3; | ||
| 755 | break; | ||
| 756 | } | ||
| 757 | else if(btns&PAD_TRIGGER_Z) | ||
| 758 | { | ||
| 759 | command = 4; | ||
| 760 | break; | ||
| 761 | } | ||
| 762 | } | ||
| 763 | } | ||
| 764 | if(command == 1) | ||
| 765 | { | ||
| 766 | FILE *f = fopen(gamename,"rb"); | ||
| 767 | if(f) | ||
| 768 | { | ||
| 769 | fclose(f); | ||
| 770 | command = 0; | ||
| 771 | warnError("ERROR: Game already dumped!\n"); | ||
| 772 | } | ||
| 773 | } | ||
| 774 | else if(command == 2) | ||
| 775 | { | ||
| 776 | FILE *f = fopen(savename,"rb"); | ||
| 777 | if(f) | ||
| 778 | { | ||
| 779 | fclose(f); | ||
| 780 | command = 0; | ||
| 781 | warnError("ERROR: Save already backed up!\n"); | ||
| 782 | } | ||
| 783 | } | ||
| 784 | else if(command == 3) | ||
| 785 | { | ||
| 786 | size_t readsize = 0; | ||
| 787 | FILE *f = fopen(savename,"rb"); | ||
| 788 | if(f) | ||
| 789 | { | ||
| 790 | fseek(f,0,SEEK_END); | ||
| 791 | readsize = ftell(f); | ||
| 792 | if(readsize != savesize) | ||
| 793 | { | ||
| 794 | command = 0; | ||
| 795 | warnError("ERROR: Save has the wrong size, aborting restore!\n"); | ||
| 796 | } | ||
| 797 | else | ||
| 798 | { | ||
| 799 | rewind(f); | ||
| 800 | fread(testdump,readsize,1,f); | ||
| 801 | } | ||
| 802 | fclose(f); | ||
| 803 | } | ||
| 804 | else | ||
| 805 | { | ||
| 806 | command = 0; | ||
| 807 | warnError("ERROR: No Save to restore!\n"); | ||
| 808 | } | ||
| 809 | } | ||
| 810 | send(command); | ||
| 811 | //let gba prepare | ||
| 812 | sleep(1); | ||
| 813 | if(command == 0) | ||
| 814 | continue; | ||
| 815 | else if(command == 1) | ||
| 816 | { | ||
| 817 | //create base file with size | ||
| 818 | printf("Preparing file...\n"); | ||
| 819 | createFile(gamename,gbasize); | ||
| 820 | FILE *f = fopen(gamename,"wb"); | ||
| 821 | if(!f) | ||
| 822 | fatalError("ERROR: Could not create file! Exit..."); | ||
| 823 | printf("Dumping...\n"); | ||
| 824 | u32 bytes_read = 0; | ||
| 825 | while(gbasize > 0) | ||
| 826 | { | ||
| 827 | int toread = (gbasize > 0x400000 ? 0x400000 : gbasize); | ||
| 828 | int j; | ||
| 829 | for(j = 0; j < toread; j+=4) | ||
| 830 | { | ||
| 831 | *(vu32*)(testdump+j) = recv(); | ||
| 832 | bytes_read+=4; | ||
| 833 | if((bytes_read&0xFFFF) == 0) | ||
| 834 | printf("\r%02.02f MB done",(float)(bytes_read/1024)/1024.f); | ||
| 835 | } | ||
| 836 | fwrite(testdump,toread,1,f); | ||
| 837 | gbasize -= toread; | ||
| 838 | } | ||
| 839 | printf("\nClosing file\n"); | ||
| 840 | fclose(f); | ||
| 841 | printf("Game dumped!\n"); | ||
| 842 | sleep(5); | ||
| 843 | } | ||
| 844 | else if(command == 2) | ||
| 845 | { | ||
| 846 | //create base file with size | ||
| 847 | printf("Preparing file...\n"); | ||
| 848 | createFile(savename,savesize); | ||
| 849 | FILE *f = fopen(savename,"wb"); | ||
| 850 | if(!f) | ||
| 851 | fatalError("ERROR: Could not create file! Exit..."); | ||
| 852 | printf("Waiting for GBA\n"); | ||
| 853 | VIDEO_WaitVSync(); | ||
| 854 | u32 readval = 0; | ||
| 855 | while(readval != savesize) | ||
| 856 | readval = __builtin_bswap32(recv()); | ||
| 857 | send(0); //got savesize | ||
| 858 | printf("Receiving...\n"); | ||
| 859 | for(i = 0; i < savesize; i+=4) | ||
| 860 | *(vu32*)(testdump+i) = recv(); | ||
| 861 | printf("Writing save...\n"); | ||
| 862 | fwrite(testdump,savesize,1,f); | ||
| 863 | fclose(f); | ||
| 864 | printf("Save backed up!\n"); | ||
| 865 | sleep(5); | ||
| 866 | } | ||
| 867 | else if(command == 3 || command == 4) | ||
| 868 | { | ||
| 869 | u32 readval = 0; | ||
| 870 | while(readval != savesize) | ||
| 871 | readval = __builtin_bswap32(recv()); | ||
| 872 | if(command == 3) | ||
| 873 | { | ||
| 874 | printf("Sending save\n"); | ||
| 875 | VIDEO_WaitVSync(); | ||
| 876 | for(i = 0; i < savesize; i+=4) | ||
| 877 | send(__builtin_bswap32(*(vu32*)(testdump+i))); | ||
| 878 | } | ||
| 879 | printf("Waiting for GBA\n"); | ||
| 880 | while(recv() != 0) | ||
| 881 | VIDEO_WaitVSync(); | ||
| 882 | printf(command == 3 ? "Save restored!\n" : "Save cleared!\n"); | ||
| 883 | send(0); | ||
| 884 | sleep(5); | ||
| 885 | }*/ | ||
| 886 | } | ||
| 887 | } | ||
| 888 | /*else if(btns&PAD_BUTTON_Y) | ||
| 889 | { | ||
| 890 | const char *biosname = "/dumps/gba_bios.bin"; | ||
| 891 | FILE *f = fopen(biosname,"rb"); | ||
| 892 | if(f) | ||
| 893 | { | ||
| 894 | fclose(f); | ||
| 895 | warnError("ERROR: BIOS already backed up!\n"); | ||
| 896 | } | ||
| 897 | else | ||
| 898 | { | ||
| 899 | //create base file with size | ||
| 900 | printf("Preparing file...\n"); | ||
| 901 | createFile(biosname,0x4000); | ||
| 902 | f = fopen(biosname,"wb"); | ||
| 903 | if(!f) | ||
| 904 | fatalError("ERROR: Could not create file! Exit..."); | ||
| 905 | //send over bios dump command | ||
| 906 | send(5); | ||
| 907 | //the gba might still be in a loop itself | ||
| 908 | sleep(1); | ||
| 909 | //lets go! | ||
| 910 | printf("Dumping...\n"); | ||
| 911 | for(i = 0; i < 0x4000; i+=4) | ||
| 912 | *(vu32*)(testdump+i) = recv(); | ||
| 913 | fwrite(testdump,0x4000,1,f); | ||
| 914 | printf("Closing file\n"); | ||
| 915 | fclose(f); | ||
| 916 | printf("BIOS dumped!\n"); | ||
| 917 | sleep(5); | ||
| 918 | } | ||
| 919 | }*/ | ||
| 920 | // } | ||
| 921 | // } | ||
| 922 | // } | ||
| 923 | } | ||
| 924 | return 0; | ||
| 556 | } | 925 | } |
