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 | } |