From 57afb4058710a978bd7b07a368125d04378c62f1 Mon Sep 17 00:00:00 2001
From: Kelly Rauchenberger <fefferburbia@gmail.com>
Date: Tue, 11 Jul 2017 16:37:21 -0400
Subject: started tweaking with stuff

---
 gba/Makefile             |  12 +-
 gba/gba_pkjb.ld          | 296 ++++++++++++++++++
 gba/gba_pkjb.specs       |   8 +
 gba/source/gamedata.c    | 534 +++++++++++++++++++++++++++++++
 gba/source/gamedata.h    |  22 ++
 gba/source/link.c        |  41 +++
 gba/source/link.h        |  12 +
 gba/source/main.c        | 592 ++++++++++++++++++++++-------------
 gba/source/saveblocks.h  | 343 ++++++++++++++++++++
 gba/source/savestructs.h | 793 +++++++++++++++++++++++++++++++++++++++++++++++
 gba/start/pkjb_crt0.s    | 102 ++++++
 11 files changed, 2544 insertions(+), 211 deletions(-)
 create mode 100644 gba/gba_pkjb.ld
 create mode 100644 gba/gba_pkjb.specs
 create mode 100644 gba/source/gamedata.c
 create mode 100644 gba/source/gamedata.h
 create mode 100644 gba/source/link.c
 create mode 100644 gba/source/link.h
 create mode 100644 gba/source/saveblocks.h
 create mode 100644 gba/source/savestructs.h
 create mode 100644 gba/start/pkjb_crt0.s

(limited to 'gba')

diff --git a/gba/Makefile b/gba/Makefile
index 99dfbb6..e417104 100644
--- a/gba/Makefile
+++ b/gba/Makefile
@@ -7,6 +7,10 @@ ifeq ($(strip $(DEVKITARM)),)
 $(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM)
 endif
 
+%_pkjb.elf:
+	@echo linking pkjb
+	$(LD) -specs=../gba_pkjb.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
+
 include $(DEVKITARM)/gba_rules
 
 #---------------------------------------------------------------------------------
@@ -16,7 +20,7 @@ include $(DEVKITARM)/gba_rules
 # DATA is a list of directories containing data files
 # INCLUDES is a list of directories containing header files
 #---------------------------------------------------------------------------------
-TARGET		:=	$(shell basename $(CURDIR))_mb
+TARGET		:=	$(shell basename $(CURDIR))_pkjb
 BUILD		:=	build
 SOURCES		:=	source
 DATA		:=
@@ -29,6 +33,7 @@ INCLUDES	:=
 ARCH	:=	-mthumb -mthumb-interwork
 
 CFLAGS	:=	-g -Wall -O3\
+		-Wno-multichar\
 		-mcpu=arm7tdmi -mtune=arm7tdmi\
  		-fomit-frame-pointer\
 		-ffast-math \
@@ -38,8 +43,8 @@ CFLAGS	+=	$(INCLUDE)
 
 CXXFLAGS	:=	$(CFLAGS) -fno-rtti -fno-exceptions
 
-ASFLAGS	:=	$(ARCH)
-LDFLAGS	=	-g $(ARCH) -Wl,-Map,$(notdir $@).map
+ASFLAGS	:=	-g $(ARCH)
+LDFLAGS	=	$(ARCH) -Wl,-Map,$(notdir $@).map
 
 #---------------------------------------------------------------------------------
 # any extra libraries we wish to link with the project
@@ -109,6 +114,7 @@ export LIBPATHS	:=	$(foreach dir,$(LIBDIRS),-L$(dir)/lib)
 #---------------------------------------------------------------------------------
 $(BUILD):
 	@[ -d $@ ] || mkdir -p $@
+	$(CC) -MMD -MP -MF start/pkjb_crt0.d -x assembler-with-cpp $(ASFLAGS) -c start/pkjb_crt0.s -o start/pkjb_crt0.o $(ERROR_FILTER)
 	@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
 
 all	: $(BUILD)
diff --git a/gba/gba_pkjb.ld b/gba/gba_pkjb.ld
new file mode 100644
index 0000000..102cb42
--- /dev/null
+++ b/gba/gba_pkjb.ld
@@ -0,0 +1,296 @@
+/* Linker Script Original v1.3 by Jeff Frohwein     */
+/*  v1.0 - Original release                         */
+/*  v1.1 - Added proper .data section support       */
+/*  v1.2 - Added support for c++ & iwram overlays   */
+/*       - Major contributions by Jason Wilkins.    */
+/*  v1.3 - .ewram section now can be used when      */
+/*         compiling for MULTIBOOT mode. This fixes */
+/*         malloc() in DevKitAdvance which depends  */
+/*         on __eheap_start instead of end to define*/
+/*         the starting location of heap space.     */
+/*         External global variable __gba_iwram_heap*/
+/*         support added to allow labels end, _end, */
+/*         & __end__ to point to end of iwram or    */
+/*         the end of ewram.                        */
+/*	Additions by WinterMute	               	*/
+/* v1.4 -	.sbss section added for unitialised		*/
+/*		    data in ewram 							*/
+/* v1.5 -	padding section added to stop EZF 		*/
+/*		    stripping important data				*/
+/* v1.6 -	added memory sections			 		*/
+
+/* This file is released into the public domain		*/
+/* for commercial or non-commercial use with no		*/
+/* restrictions placed upon it.						*/
+
+/* NOTE!!!: This linker script defines the RAM &  */
+/*   ROM start addresses. In order for it to work */
+/*   properly, remove -Ttext and -Tbss linker     */
+/*   options from your makefile if they are       */
+/*   present.                                     */
+
+/* You can use the following to view section      */
+/* addresses in your .elf file:                   */
+/*   objdump -h file.elf                          */
+/* Please note that empty sections may incorrectly*/
+/* list the lma address as the vma address for    */
+/* some versions of objdump.                      */
+
+OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+
+MEMORY {
+
+	rom	: ORIGIN = 0x08000000, LENGTH = 32M
+	iwram	: ORIGIN = 0x03000000, LENGTH = 32K
+	ewram	: ORIGIN = 0x02000000, LENGTH = 256K
+}
+
+
+
+__text_start	=	ORIGIN(ewram);
+__eheap_end	=	ORIGIN(ewram) + LENGTH(ewram);
+__iwram_start	=	ORIGIN(iwram);
+__iwram_top	=	ORIGIN(iwram) + LENGTH(iwram);;
+
+__sp_irq	=	__iwram_top - 0x060;
+__sp_usr	=	__sp_irq - 0x0a0;
+__irq_flags	=	0x03007ff8;
+
+SECTIONS
+{
+	. = __text_start;
+	.init :
+	{
+		KEEP (*(.init))
+		. = ALIGN(4);
+	} >ewram =0xff
+
+	.plt :
+ 	{
+		*(.plt)
+		. = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
+	} >ewram
+
+	.text  ALIGN (4):
+	{
+		*(EXCLUDE_FILE (*.iwram*) .text)
+		*(.text .stub .text.* .gnu.linkonce.t.*)
+		KEEP (*(.text.*personality*))
+		/* .gnu.warning sections are handled specially by elf32.em.  */
+		*(.gnu.warning)
+		*(.glue_7t) *(.glue_7) *(.vfp11_veneer)
+		. = ALIGN(4);  /* REQUIRED. LD is flaky without it. */
+	} >ewram = 0xff
+
+	__text_end = .;
+	.fini           :
+	{
+		KEEP (*(.fini))
+		. = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
+	} >ewram =0
+
+	.rodata :
+	{
+		*(.rodata)
+		*all.rodata*(*)
+		*(.roda)
+		*(.rodata.*)
+		*(.gnu.linkonce.r*)
+		SORT(CONSTRUCTORS)
+		. = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
+	} >ewram = 0xff
+
+  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >ewram
+   __exidx_start = .;
+  .ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >ewram
+   __exidx_end = .;
+  /* Ensure the __preinit_array_start label is properly aligned.  We
+     could instead move the label definition inside the section, but
+     the linker would then create the section even if it turns out to
+     be empty, which isn't pretty.  */
+  . = ALIGN(32 / 8);
+  PROVIDE (__preinit_array_start = .);
+  .preinit_array     : { KEEP (*(.preinit_array)) } >ewram = 0xff
+  PROVIDE (__preinit_array_end = .);
+  PROVIDE (__init_array_start = .);
+  .init_array     : { KEEP (*(.init_array)) } >ewram = 0xff
+  PROVIDE (__init_array_end = .);
+  PROVIDE (__fini_array_start = .);
+  .fini_array     : { KEEP (*(.fini_array)) } >ewram = 0xff
+  PROVIDE (__fini_array_end = .);
+	.ctors :
+	{
+		/*	gcc uses crtbegin.o to find the start of the constructors, so
+			we make sure it is first.  Because this is a wildcard, it
+			doesn't matter if the user does not actually link against
+			crtbegin.o; the linker won't look for a file to match a
+			wildcard.  The wildcard also means that it doesn't matter which
+			directory crtbegin.o is in.  */
+		KEEP (*crtbegin.o(.ctors))
+		KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+		KEEP (*(SORT(.ctors.*)))
+		KEEP (*(.ctors))
+		. = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
+	} >ewram = 0
+
+	.dtors :
+	{
+		KEEP (*crtbegin.o(.dtors))
+		KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+		KEEP (*(SORT(.dtors.*)))
+		KEEP (*(.dtors))
+		. = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
+	} >ewram = 0
+
+	.jcr            : { KEEP (*(.jcr)) } >ewram
+	.eh_frame :
+	{
+		KEEP (*(.eh_frame))
+	. = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
+	} >ewram = 0
+
+	.gcc_except_table :
+	{
+		*(.gcc_except_table)
+		. = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
+	} >ewram = 0
+
+	__iwram_lma = .;
+
+	.iwram __iwram_start : AT (__iwram_lma)
+	{
+		__iwram_start__ = ABSOLUTE(.) ;
+		*(.iwram)
+		*iwram.*(.text)
+		. = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
+		__iwram_end__ = ABSOLUTE(.) ;
+	} >iwram = 0xff
+
+	__data_lma = __iwram_lma + SIZEOF(.iwram) ;
+
+	.bss ALIGN(4) (NOLOAD):
+	{
+		__bss_start__ = ABSOLUTE(.);
+		*(.dynbss)
+		*(.gnu.linkonce.b*)
+		*(.bss*)
+		*(COMMON)
+		. = ALIGN(4);    /* REQUIRED. LD is flaky without it. */
+		__bss_end__ = ABSOLUTE(.) ;
+	}
+
+	.data ALIGN(4) : AT (__data_lma)
+	{
+		__data_start__ = ABSOLUTE(.);
+		*(.data)
+		*(.data.*)
+		*(.gnu.linkonce.d*)
+		CONSTRUCTORS
+		. = ALIGN(4);  /* REQUIRED. LD is flaky without it. */
+		__data_end__  =  ABSOLUTE(.);
+	} >iwram = 0xff
+
+	__iwram_overlay_lma = __data_lma + SIZEOF(.data);
+
+	PROVIDE (edata = .);
+	__iwram_overlay_start = . ;
+
+	OVERLAY ALIGN(4) : NOCROSSREFS AT (__iwram_overlay_lma)
+	{
+	   .iwram0 { *(.iwram0) . = ALIGN(4);}
+	   .iwram1 { *(.iwram1) . = ALIGN(4);}
+	   .iwram2 { *(.iwram2) . = ALIGN(4);}
+	   .iwram3 { *(.iwram3) . = ALIGN(4);}
+	   .iwram4 { *(.iwram4) . = ALIGN(4);}
+	   .iwram5 { *(.iwram5) . = ALIGN(4);}
+	   .iwram6 { *(.iwram6) . = ALIGN(4);}
+	   .iwram7 { *(.iwram7) . = ALIGN(4);}
+	   .iwram8 { *(.iwram8) . = ALIGN(4);}
+	   .iwram9 { *(.iwram9) . = ALIGN(4);}
+	} >iwram = 0xff
+
+	__ewram_lma = LOADADDR(.iwram0) + SIZEOF(.iwram0)+SIZEOF(.iwram1)+SIZEOF(.iwram2)+SIZEOF(.iwram3)+SIZEOF(.iwram4)+SIZEOF(.iwram5)+SIZEOF(.iwram6)+SIZEOF(.iwram7)+SIZEOF(.iwram8)+SIZEOF(.iwram9);
+
+	__iwram_overlay_end = __ewram_lma ;
+
+	/* v1.3 */
+	__ewram_start =  __ewram_lma ;
+
+	.ewram __ewram_start : AT (__ewram_lma)
+	{
+		*(.ewram)
+		. = ALIGN(4);  /* REQUIRED. LD is flaky without it. */
+		__ewram_end = ABSOLUTE(.);
+	} >ewram = 0xff
+
+	__ewram_overlay_lma = __ewram_lma + SIZEOF(.ewram);
+
+	.sbss ALIGN(4)(NOLOAD):
+ 	{
+		__sbss_start__ = ABSOLUTE(.);
+		*(.sbss)
+		. = ALIGN(4);
+		__sbss_end__  = ABSOLUTE(.);
+		__end__ = ABSOLUTE(.);
+		__eheap_start = ABSOLUTE(.);
+	}
+
+	OVERLAY ALIGN(4): NOCROSSREFS AT (__ewram_overlay_lma)
+	{
+		.ewram0 { *(.ewram0) . = ALIGN(4);}
+		.ewram1 { *(.ewram1) . = ALIGN(4);}
+		.ewram2 { *(.ewram2) . = ALIGN(4);}
+		.ewram3 { *(.ewram3) . = ALIGN(4);}
+		.ewram4 { *(.ewram4) . = ALIGN(4);}
+		.ewram5 { *(.ewram5) . = ALIGN(4);}
+		.ewram6 { *(.ewram6) . = ALIGN(4);}
+		.ewram7 { *(.ewram7) . = ALIGN(4);}
+		.ewram8 { *(.ewram8) . = ALIGN(4);}
+		.ewram9 { *(.ewram9) . = ALIGN(4);}
+	} >ewram = 0xff
+	__ewram_overlay_end  = ABSOLUTE(.);
+
+	__eheap_start = __ewram_overlay_end ;
+
+	_end = __ewram_overlay_end;
+	__end__ = __ewram_overlay_end;
+	__rom_end__ = __ewram_overlay_end;
+
+	/* Stabs debugging sections.  */
+	.stab 0 : { *(.stab) }
+	.stabstr 0 : { *(.stabstr) }
+	.stab.excl 0 : { *(.stab.excl) }
+	.stab.exclstr 0 : { *(.stab.exclstr) }
+	.stab.index 0 : { *(.stab.index) }
+	.stab.indexstr 0 : { *(.stab.indexstr) }
+	.comment 0 : { *(.comment) }
+	/* DWARF debug sections.
+	   Symbols in the DWARF debugging sections are relative to the beginning
+	   of the section so we begin them at 0.  */
+	/* DWARF 1 */
+	.debug          0 : { *(.debug) }
+	.line           0 : { *(.line) }
+	/* GNU DWARF 1 extensions */
+	.debug_srcinfo  0 : { *(.debug_srcinfo) }
+	.debug_sfnames  0 : { *(.debug_sfnames) }
+	/* DWARF 1.1 and DWARF 2 */
+	.debug_aranges  0 : { *(.debug_aranges) }
+	.debug_pubnames 0 : { *(.debug_pubnames) }
+	/* DWARF 2 */
+	.debug_info     0 : { *(.debug_info) }
+	.debug_abbrev   0 : { *(.debug_abbrev) }
+	.debug_line     0 : { *(.debug_line) }
+	.debug_frame    0 : { *(.debug_frame) }
+	.debug_str      0 : { *(.debug_str) }
+	.debug_loc      0 : { *(.debug_loc) }
+	.debug_macinfo  0 : { *(.debug_macinfo) }
+	/* SGI/MIPS DWARF 2 extensions */
+	.debug_weaknames 0 : { *(.debug_weaknames) }
+	.debug_funcnames 0 : { *(.debug_funcnames) }
+	.debug_typenames 0 : { *(.debug_typenames) }
+	.debug_varnames  0 : { *(.debug_varnames) }
+	.stack 0x80000 : { _stack = .; *(.stack) }
+	/* These must appear regardless of  .  */
+}
diff --git a/gba/gba_pkjb.specs b/gba/gba_pkjb.specs
new file mode 100644
index 0000000..7c9027b
--- /dev/null
+++ b/gba/gba_pkjb.specs
@@ -0,0 +1,8 @@
+%rename link                old_link
+
+*link:
+-T ../gba_pkjb.ld%s %(old_link) --gc-sections
+
+*startfile:
+../start/pkjb_crt0%O%s crti%O%s crtbegin%O%s
+
diff --git a/gba/source/gamedata.c b/gba/source/gamedata.c
new file mode 100644
index 0000000..19d5100
--- /dev/null
+++ b/gba/source/gamedata.c
@@ -0,0 +1,534 @@
+/*
+ * Pokemon Gen III Data Extractor by hatkirby 2017.
+ *
+ * This software may be modified and distributed under the terms
+ * of the MIT license.  See the LICENSE file for details.
+ *
+ */
+#include "gamedata.h"
+
+void decryptSaveStructures(
+    pSaveBlock1 SaveBlock1,
+    pSaveBlock2 SaveBlock2,
+    pSaveBlock3 SaveBlock3)
+{
+  if (GAME_RS)
+  {
+    // R/S doesn't have save crypto.
+    return;
+  }
+
+  u8* sb1raw = (u8*)SaveBlock1;
+  u8* sb2raw = (u8*)SaveBlock2;
+  //u8* sb3raw = (u8*)SaveBlock3; // unused
+
+  u32* xor_key_ptr = (u32*)(&sb2raw[( GAME_EM ? 0xA8 : 0xF20 )]);
+
+  u32 xor_key = *xor_key_ptr;
+  u16 xor_key16 = (u16)xor_key;
+  if (!xor_key)
+  {
+    // xor key is zero, nothing needs to be done.
+    return;
+  }
+
+  u32* ptr_to_xor;
+  u32 save_offset;
+  int i;
+  u32* bag_pocket_offsets;
+  u32* bag_pocket_counts;
+  if (GAME_FRLG)
+  {
+    // loop over and decrypt various things
+    save_offset = 0x3D38 + 4;
+    for (i = 3; i >= 0; i--)
+    {
+      ptr_to_xor = (u32*)(&sb1raw[save_offset]);
+      *ptr_to_xor ^= xor_key;
+      save_offset += 12;
+    }
+
+    for (i = 0; i <= 0x3f; i++)
+    {
+      save_offset = 0x1200 + (i*sizeof(u32));
+      ptr_to_xor = (u32*)(&sb1raw[save_offset]);
+      *ptr_to_xor ^= xor_key;
+    }
+
+    // loop over each of the bag pockets and decrypt decrypt decrypt
+    bag_pocket_offsets = (u32[5]) { 0x310, 0x388, 0x430, 0x464, 0x54C };
+    bag_pocket_counts = (u32[5]) { 42, 30, 13, 58, 43 };
+
+    for (i = 0; i < 5; i++)
+    {
+      for (int bag_i = 0; bag_i < bag_pocket_counts[i]; bag_i++)
+      {
+        save_offset = bag_pocket_offsets[i] + (sizeof(u32) * bag_i) + 2;
+        *(u16*)(&sb1raw[save_offset]) ^= xor_key16;
+      }
+    }
+
+    // decrypt some more stuff
+    save_offset = 0xaf8;
+    ptr_to_xor = (u32*)(&sb1raw[save_offset]);
+    *ptr_to_xor ^= xor_key;
+
+    save_offset = 0x290;
+    ptr_to_xor = (u32*)(&sb1raw[save_offset]);
+    *ptr_to_xor ^= xor_key;
+
+    save_offset = 0x294;
+    *(u16*)(&sb1raw[save_offset]) ^= xor_key16;
+  } else {
+    // Emerald
+
+    // loop over and decrypt various things
+    for (i = 0; i <= 0x3f; i++)
+    {
+      save_offset = 0x159c + (i*sizeof(u32));
+      ptr_to_xor = (u32*)(&sb1raw[save_offset]);
+      *ptr_to_xor ^= xor_key;
+    }
+
+    // loop over each of the bag pockets and decrypt decrypt decrypt
+    bag_pocket_offsets = (u32[5]) { 0x560, 0x5D8, 0x650, 0x690, 0x790 };
+    bag_pocket_counts = (u32[5]) { 30, 30, 16, 64, 46 };
+
+    for (i = 0; i < 5; i++)
+    {
+      for (int bag_i = 0; bag_i < bag_pocket_counts[i]; bag_i++)
+      {
+        save_offset = bag_pocket_offsets[i] + (sizeof(u32) * bag_i) + 2;
+        *(u16*)(&sb1raw[save_offset]) ^= xor_key16;
+      }
+    }
+
+    // decrypt some more stuff
+    save_offset = 0x1F4;
+    ptr_to_xor = (u32*)(&sb1raw[save_offset]);
+    *ptr_to_xor ^= xor_key;
+
+    save_offset = 0x490;
+    ptr_to_xor = (u32*)(&sb1raw[save_offset]);
+    *ptr_to_xor ^= xor_key;
+
+    save_offset = 0x494;
+    *(u16*)(&sb1raw[save_offset]) ^= xor_key16;
+  }
+
+  *xor_key_ptr = 0;
+}
+
+bool initSaveData(
+    pSaveBlock1* SaveBlock1,
+    pSaveBlock2* SaveBlock2,
+    pSaveBlock3* SaveBlock3)
+{
+  // check the ROM code, make sure this game is supported.
+  u8* ROM = (u8*) 0x8000000;
+
+  u32 gamecode = (*(u32*)(&ROM[0xAC]));
+
+  void(*loadsave)(char a1);
+  //void(*mainloop)();
+  //void(*load_pokemon)();
+  pSaveBlock1 gSaveBlock1;
+  pSaveBlock2 gSaveBlock2;
+  pSaveBlock3 gSaveBlock3;
+  //u32 titlemid = 0;
+
+  // get the address of the save loading function.
+  switch (gamecode)
+  {
+    // --- R/S ---
+    case 'DVXA': // Ruby German
+    case 'DPXA': // Sapphire German
+    {
+      // TODO: detect debug ROM?
+      gSaveBlock1 = (pSaveBlock1) 0x2025734;
+      gSaveBlock2 = (pSaveBlock2) 0x2024EA4;
+      gSaveBlock3 = (pSaveBlock3) 0x20300A0;
+      loadsave = (void(*)(char)) 0x8126249; // same for v1.0 + v1.1
+      //mainloop = (void(*)()) 0x80003D9;
+      //load_pokemon = (void(*)()) 0x8047da9;
+
+      break;
+    }
+
+    case 'FVXA': // Ruby French
+    case 'FPXA': // Sapphire French
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x2025734;
+      gSaveBlock2 = (pSaveBlock2) 0x2024EA4;
+      gSaveBlock3 = (pSaveBlock3) 0x20300A0;
+      loadsave = (void(*)(char)) 0x8126351; // same for v1.0 + v1.1
+      //mainloop = (void(*)()) 0x80003D9;
+      //load_pokemon = (void(*)()) 0x8047e95;
+
+      break;
+    }
+
+    case 'IVXA': // Ruby Italian
+    case 'IPXA': // Sapphire Italian
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x2025734;
+      gSaveBlock2 = (pSaveBlock2) 0x2024EA4;
+      gSaveBlock3 = (pSaveBlock3) 0x20300A0;
+      loadsave = (void(*)(char)) 0x8126249; // same for v1.0 + v1.1
+      //mainloop = (void(*)()) 0x80003D9;
+      //load_pokemon = (void(*)()) 0x8047dbd;
+
+      break;
+    }
+
+    case 'SVXA': // Ruby Spanish
+    case 'SPXA': // Sapphire Spanish
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x2025734;
+      gSaveBlock2 = (pSaveBlock2) 0x2024EA4;
+      gSaveBlock3 = (pSaveBlock3) 0x20300A0;
+      loadsave = (void(*)(char)) 0x8126349; // same for v1.0 + v1.1
+      //mainloop = (void(*)()) 0x80003D9;
+      //load_pokemon = (void(*)()) 0x8047ea5;
+
+      break;
+    }
+
+    case 'EVXA': // Ruby English
+    case 'EPXA': // Sapphire English
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x2025734;
+      gSaveBlock2 = (pSaveBlock2) 0x2024EA4;
+      gSaveBlock3 = (pSaveBlock3) 0x20300A0;
+      //mainloop = (void(*)()) 0x80002A5;
+
+      // version number
+      switch (ROM[0xBC])
+      {
+        case 0:
+        {
+          loadsave = (void(*)(char)) 0x8125EC9;
+          //load_pokemon = (void(*)()) 0x8047a85;
+
+          break;
+        }
+
+        case 1:
+        case 2:
+        {
+          loadsave = (void(*)(char)) 0x8125EE9;
+          //load_pokemon = (void(*)()) 0x8047aa5;
+
+          break;
+        }
+
+        default:
+        {
+          return false; // unsupported version
+        }
+      }
+
+      break;
+    }
+
+    case 'JVXA': // Ruby Japanese
+    case 'JPXA': // Sapphire Japanese
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x2025494;
+      gSaveBlock2 = (pSaveBlock2) 0x2024C04;
+      gSaveBlock3 = (pSaveBlock3) 0x202FDBC;
+      loadsave = (void(*)(char)) 0x8120d05; // same for v1.0 + v1.1
+      //mainloop = (void(*)()) 0x80002A9;
+      //load_pokemon = (void(*)()) 0x8044d55;
+
+      break;
+    }
+
+    /// --- FR/LG ---
+    // In FR/LG, the function that initialises the save-block pointers to default does not set up saveblock3.
+    // Which will need to be set up before loading the save if we want boxed Pokémon to not disappear.
+    // Oh, and loadsave() offset is different between FR and LG...
+
+    case 'DRPB': // FireRed German
+    case 'DGPB': // LeafGreen German
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x202552C;
+      gSaveBlock2 = (pSaveBlock2) 0x2024588;
+      gSaveBlock3 = (pSaveBlock3) 0x2029314;
+      *(pSaveBlock3*)(0x3004f60) = gSaveBlock3;
+      loadsave = (void(*)(char)) ( GAME_FR ? 0x80da721 : 0x80da6f5 );
+      //mainloop = (void(*)()) 0x8000425;
+      //titlemid = 0x80791df;
+      //load_pokemon = (void(*)()) 0x804c251;
+
+      break;
+    }
+
+    case 'FRPB': // FireRed French
+    case 'FGPB': // LeafGreen French
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x202552C;
+      gSaveBlock2 = (pSaveBlock2) 0x2024588;
+      gSaveBlock3 = (pSaveBlock3) 0x2029314;
+      *(pSaveBlock3*)(0x3004f60) = gSaveBlock3;
+      loadsave = (void(*)(char)) ( GAME_FR ? 0x80da7e1 : 0x80da7b5 );
+      //mainloop = (void(*)()) 0x8000417;
+      //titlemid = 0x807929f;
+      //load_pokemon = (void(*)()) 0x804c311;
+
+      break;
+    }
+
+    case 'IRPB': // FireRed Italian
+    case 'IGPB': // LeafGreen Italian
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x202552C;
+      gSaveBlock2 = (pSaveBlock2) 0x2024588;
+      gSaveBlock3 = (pSaveBlock3) 0x2029314;
+      *(pSaveBlock3*)(0x3004f60) = gSaveBlock3;
+      loadsave = (void(*)(char)) ( GAME_FR ? 0x80da721 : 0x80da6f5 );
+      //mainloop = (void(*)()) 0x8000425;
+      //titlemid = 0x80791cb;
+      //load_pokemon = (void(*)()) 0x804c23d;
+
+      break;
+    }
+
+    case 'SRPB': // FireRed Spanish
+    case 'SGPB': // LeafGreen Spanish
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x202552C;
+      gSaveBlock2 = (pSaveBlock2) 0x2024588;
+      gSaveBlock3 = (pSaveBlock3) 0x2029314;
+      *(pSaveBlock3*)(0x3004f60) = gSaveBlock3;
+      loadsave = (void(*)(char)) ( GAME_FR ? 0x80da809 : 0x80da7dd );
+      //mainloop = (void(*)()) 0x8000417;
+      //titlemid = 0x80792b3;
+      //load_pokemon = (void(*)()) 0x804c325;
+
+      break;
+    }
+
+    case 'ERPB': // FireRed English
+    case 'EGPB': // LeafGreen English
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x202552C;
+      gSaveBlock2 = (pSaveBlock2) 0x2024588;
+      gSaveBlock3 = (pSaveBlock3) 0x2029314;
+      *(pSaveBlock3*)(0x3005010) = gSaveBlock3;
+
+      // version number
+      switch (ROM[0xBC])
+      {
+        case 0:
+        {
+          loadsave = (void(*)(char)) ( GAME_FR ? 0x80da4fd : 0x80da4d1 );
+          //mainloop = (void(*)()) 0x800041b;
+          //titlemid = 0x807927b;
+          //load_pokemon = (void(*)()) 0x804c231;
+
+          break;
+        }
+
+        case 1:
+        {
+          loadsave = (void(*)(char)) ( GAME_FR ? 0x80da511 : 0x80da4e5 );
+          //mainloop = (void(*)()) 0x8000429;
+          //titlemid = 0x807928f;
+          //load_pokemon = (void(*)()) 0x804c245;
+
+          break;
+        }
+
+        default:
+        {
+          return false; // unsupported version
+        }
+      }
+
+      break;
+    }
+
+    case 'JRPB': // FireRed Japanese
+    case 'JGPB': // LeafGreen Japanese
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x202548C;
+      gSaveBlock2 = (pSaveBlock2) 0x20244E8;
+      gSaveBlock3 = (pSaveBlock3) 0x202924C;
+      *(pSaveBlock3*)(0x3005050) = gSaveBlock3;
+
+      // version number
+      switch (ROM[0xBC])
+      {
+        case 0:
+        {
+          loadsave = (void(*)(char)) ( GAME_FR ? 0x80db4e5 : 0x80db4b9 );
+          //mainloop = (void(*)()) 0x800041b;
+          //titlemid = 0x8078a0f;
+          //load_pokemon = (void(*)()) 0x804b9e9;
+
+          break;
+        }
+
+        case 1:
+        {
+          if ((gamecode << 8) == 'GPB\x00')
+          {
+            // LeafGreen v1.1 Japanese is undumped.
+            // Therefore, it is unsupported.
+            // I will make guesses at the offsets in the comments, but I will not actually implement them until LeafGreen v1.1 is dumped.
+
+            return false;
+          }
+
+          loadsave = (void(*)(char)) 0x80db529; // potential LG1.1 address: 0x80db4fd
+          //mainloop = (void(*)()) 0x8000417;
+          //titlemid = 0x8078987;
+          //load_pokemon = (void(*)()) 0x804b9c5;
+
+          break;
+        }
+
+        default:
+        {
+          return false; // unsupported version
+        }
+      }
+
+      break;
+    }
+
+    /// --- Emerald ---
+    // In Emerald, the saveblock pointer that isn't set up is saveblock1 (in FR/LG it was saveblock3).
+    // The initial save loading code after the copyright screen is also updated, now it sets up ASLR/crypto here before loading the save.
+
+    case 'DEPB': // Emerald German
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x2025A00;
+      gSaveBlock2 = (pSaveBlock2) 0x2024A54;
+      gSaveBlock3 = (pSaveBlock3) 0x2029808;
+      *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1;
+      loadsave = (void(*)(char)) 0x8153075;
+      //mainloop = (void(*)()) 0x800042b;
+      //titlemid = 0x816fdb5;
+      //load_pokemon = (void(*)()) 0x8076dd5;
+
+      break;
+    }
+
+    case 'FEPB': // Emerald French
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x2025A00;
+      gSaveBlock2 = (pSaveBlock2) 0x2024A54;
+      gSaveBlock3 = (pSaveBlock3) 0x2029808;
+      *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1;
+      loadsave = (void(*)(char)) 0x815319d;
+      //mainloop = (void(*)()) 0x800042b;
+      //titlemid = 0x816fedd;
+      //load_pokemon = (void(*)()) 0x8076dd1;
+
+      break;
+    }
+
+    case 'IEPB': // Emerald Italian
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x2025A00;
+      gSaveBlock2 = (pSaveBlock2) 0x2024A54;
+      gSaveBlock3 = (pSaveBlock3) 0x2029808;
+      *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1;
+      loadsave = (void(*)(char)) 0x8153065;
+      //mainloop = (void(*)()) 0x800042b;
+      //titlemid = 0x816fda5;
+      //load_pokemon = (void(*)()) 0x8076dd5;
+
+      break;
+    }
+
+    case 'SEPB': // Emerald Spanish
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x2025A00;
+      gSaveBlock2 = (pSaveBlock2) 0x2024A54;
+      gSaveBlock3 = (pSaveBlock3) 0x2029808;
+      *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1;
+      loadsave = (void(*)(char)) 0x8153175;
+      //mainloop = (void(*)()) 0x800042b;
+      //titlemid = 0x816feb5;
+      //load_pokemon = (void(*)()) 0x8076dd1;
+
+      break;
+    }
+
+    case 'EEPB': // Emerald English
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x2025A00;
+      gSaveBlock2 = (pSaveBlock2) 0x2024A54;
+      gSaveBlock3 = (pSaveBlock3) 0x2029808;
+      *(pSaveBlock1*)(0x3005d8c) = gSaveBlock1;
+      loadsave = (void(*)(char)) 0x81534d1;
+      //mainloop = (void(*)()) 0x800042b;
+      //titlemid = 0x817014d;
+      //load_pokemon = (void(*)()) 0x8076dd5;
+
+      break;
+    }
+
+    case 'JEPB': // Emerald Japanese
+    {
+      gSaveBlock1 = (pSaveBlock1) 0x20256A4;
+      gSaveBlock2 = (pSaveBlock2) 0x20246F8;
+      gSaveBlock3 = (pSaveBlock3) 0x20294AC;
+      *(pSaveBlock1*)(0x3005aec) = gSaveBlock1;
+      loadsave = (void(*)(char)) 0x815340d;
+      //mainloop = (void(*)()) 0x800042b;
+      //titlemid = 0x816ff45;
+      //load_pokemon = (void(*)()) 0x80767dd;
+
+      break;
+    }
+
+    default:
+    {
+      return false; // this game isn't supported
+    }
+  }
+
+  loadsave(0);
+
+  // now the save is loaded, we can do what we want with the loaded blocks.
+  // first, we're going to want to decrypt the parts that are crypted, if applicable.
+  decryptSaveStructures(gSaveBlock1,gSaveBlock2,gSaveBlock3);
+
+  *SaveBlock1 = gSaveBlock1;
+  *SaveBlock2 = gSaveBlock2;
+  *SaveBlock3 = gSaveBlock3;
+
+  /*
+  // time to call the payload.
+  payload(gSaveBlock1,gSaveBlock2,gSaveBlock3);
+  // Now, we better call the function that sets the pokemon-related stuff from the structure elements of the loaded save again.
+  // Just in case the payload did something with that.
+  load_pokemon();
+  // In FR/LG/Emerald, just returning to the game is unwise.
+  // The game reloads the savefile.
+  // In FR/LG, this is done at the title screen after setting ASLR/saveblock-crypto up. (probably because at initial save-load, SaveBlock3 ptr isn't set up lol)
+  // So, better bypass the title screen and get the game to return directly to the Continue/New Game screen.
+  // In Emerald, the save reload happens after the Continue option was chosen, so we have no choice but to bypass everything and get the game to go straight to the overworld.
+  // Easiest way to do this is to call into the middle of the function we want, using an ASM wrapper to set up the stack.
+  // Here goes...
+  if (titlemid) {
+    // Function reserves an extra 4 bytes of stack space in FireRed/LeafGreen, and none in Emerald.
+    call_into_middle_of_titlescreen_func(titlemid,(GAME_EM ? 0 : 4));
+  }
+  // Now we've done what we want, time to return to the game.
+  // Can't just return, the game will reload the save.
+  // So let's just call the main-loop directly ;)
+  // turn the sound back on before we head back to the game
+  *(vu16 *)(REG_BASE + 0x84) = 0x8f;
+  // re-enable interrupts
+  REG_IME = 1;
+  mainloop();
+  // Anything past here will not be executed.
+  return 0;
+  */
+  return true;
+}
diff --git a/gba/source/gamedata.h b/gba/source/gamedata.h
new file mode 100644
index 0000000..99dfa8e
--- /dev/null
+++ b/gba/source/gamedata.h
@@ -0,0 +1,22 @@
+#ifndef _GAMEDATA_H_
+#define _GAMEDATA_H_
+
+#include <gba.h>
+#include "saveblocks.h"
+
+#define GAME_RUBY (((*(u32*)(0x80000AC)) << 8) == 'VXA\x00')
+#define GAME_SAPP (((*(u32*)(0x80000AC)) << 8) == 'PXA\x00')
+#define GAME_RS   ((GAME_RUBY) || (GAME_SAPP))
+#define GAME_FR   (((*(u32*)(0x80000AC)) << 8) == 'RPB\x00')
+#define GAME_LG   (((*(u32*)(0x80000AC)) << 8) == 'GPB\x00')
+#define GAME_FRLG ((GAME_FR) || (GAME_LG))
+#define GAME_EM   (((*(u32*)(0x80000AC)) << 8) == 'EPB\x00')
+
+#define LANG_JAPAN ((*(u8*)(0x80000AF)) == 'J')
+
+bool initSaveData(
+    pSaveBlock1* SaveBlock1,
+    pSaveBlock2* SaveBlock2,
+    pSaveBlock3* SaveBlock3);
+
+#endif
diff --git a/gba/source/link.c b/gba/source/link.c
new file mode 100644
index 0000000..e695622
--- /dev/null
+++ b/gba/source/link.c
@@ -0,0 +1,41 @@
+#include "link.h"
+
+#define JOY_WRITE 2
+#define JOY_READ 4
+#define JOY_RW 6
+
+void waitForWriteAccess()
+{
+  //while ((REG_HS_CTRL & JOY_READ) == 0);
+	while ((REG_HS_CTRL & JOY_WRITE) == 0);
+  REG_HS_CTRL |= JOY_RW;
+}
+
+void waitForAck()
+{
+  while ((REG_HS_CTRL & JOY_WRITE) == 0);
+  REG_HS_CTRL |= JOY_RW;
+	REG_JOYTR = 0;
+  while ((REG_HS_CTRL & JOY_WRITE) == 0);
+  REG_HS_CTRL |= JOY_RW;
+}
+
+void sendS32(s32 val)
+{
+  REG_JOYTR = val;
+	//waitForWriteAccess();
+}
+
+void sendU32(u32 val)
+{
+  REG_JOYTR = val;
+	//waitForWriteAccess();
+}
+
+u32 recieveU32()
+{
+  while ((REG_HS_CTRL & JOY_WRITE) == 0);
+  REG_HS_CTRL |= JOY_RW;
+	return REG_JOYRE;
+}
+
diff --git a/gba/source/link.h b/gba/source/link.h
new file mode 100644
index 0000000..f18b38a
--- /dev/null
+++ b/gba/source/link.h
@@ -0,0 +1,12 @@
+#ifndef _LINK_H_
+#define _LINK_H_
+
+#include <gba.h>
+
+void waitForWriteAccess();
+void waitForAck();
+void sendS32(s32 val);
+void sendU32(u32 val);
+u32 recieveU32();
+
+#endif
diff --git a/gba/source/main.c b/gba/source/main.c
index ee94c35..104866a 100644
--- a/gba/source/main.c
+++ b/gba/source/main.c
@@ -8,6 +8,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "libSave.h"
+#include "gamedata.h"
+#include "link.h"
 
 #define	REG_WAITCNT *(vu16 *)(REG_BASE + 0x204)
 #define JOY_WRITE 2
@@ -18,224 +20,398 @@ u8 save_data[0x20000] __attribute__ ((section (".sbss")));
 
 s32 getGameSize(void)
 {
-	if(*(vu32*)(0x08000004) != 0x51AEFF24)
-		return -1;
-	s32 i;
-	for(i = (1<<20); i < (1<<25); i<<=1)
+  if(*(vu32*)(0x08000004) != 0x51AEFF24)
+    return -1;
+  s32 i;
+  for(i = (1<<20); i < (1<<25); i<<=1)
+  {
+    vu16 *rompos = (vu16*)(0x08000000+i);
+    int j;
+    bool romend = true;
+    for(j = 0; j < 0x1000; j++)
+    {
+      if(rompos[j] != j)
+      {
+        romend = false;
+        break;
+      }
+    }
+    if(romend) break;
+  }
+  return i;
+}
+
+
+// === (from tonc_memdef.h) ===========================================
+
+// --- REG_DISPCNT defines ---
+#define DCNT_MODE0     0x0000
+#define DCNT_MODE1      0x0001
+#define DCNT_MODE2      0x0002
+#define DCNT_MODE3      0x0003
+#define DCNT_MODE4      0x0004
+#define DCNT_MODE5      0x0005
+// layers
+#define DCNT_BG0        0x0100
+#define DCNT_BG1        0x0200
+#define DCNT_BG2        0x0400
+#define DCNT_BG3        0x0800
+#define DCNT_OBJ        0x1000
+typedef u16 COLOR;
+#define MEM_VRAM    0x06000000
+#define SCREEN_WIDTH   240
+#define vid_mem     ((u16*)MEM_VRAM)
+static inline void m3_plot(int x, int y, COLOR clr)
+{   vid_mem[y*SCREEN_WIDTH+x]= clr;    }
+static inline COLOR RGB15(u32 red, u32 green, u32 blue)
+{   return red | (green<<5) | (blue<<10);   }
+void plot_sqr(int x, int y, COLOR clr)
+{
+	/*for (int j=0;j<8; j++)
 	{
-		vu16 *rompos = (vu16*)(0x08000000+i);
-		int j;
-		bool romend = true;
-		for(j = 0; j < 0x1000; j++)
+		for (int i=0;i<8; i++)
 		{
-			if(rompos[j] != j)
-			{
-				romend = false;
-				break;
-			}
+			vid_mem[(y*8+j+32)*SCREEN_WIDTH+x*8+i+32] = clr;
 		}
-		if(romend) break;
 	}
-	return i;
+	vid_mem[(y*8+1+32)*SCREEN_WIDTH+x*8+1+32] = RGB15(31,31,31);*/
+}
+void m3_fill(COLOR clr) 
+{   
+    /*int ii;
+    u32 *dst= (u32*)vid_mem;
+    u32 wd= (clr<<16) | clr;
+
+    for(ii=0; ii<SCREEN_WIDTH/4; ii++)
+        *dst++= wd;*/
 }
 
 //---------------------------------------------------------------------------------
 // Program entry point
 //---------------------------------------------------------------------------------
-int main(void) {
-//---------------------------------------------------------------------------------
+int main(void)
+{
+	//REG_IME = 0;
+  //REG_DISPCNT= DCNT_MODE3 | DCNT_BG2;
+	m3_fill(RGB15(31,31,31));
+  plot_sqr( 4, 4, RGB15(31, 0, 0) );    // or CLR_RED
 
-	// the vblank interrupt must be enabled for VBlankIntrWait() to work
-	// since the default dispatcher handles the bios flags no vblank handler
-	// is required
-	irqInit();
-	irqEnable(IRQ_VBLANK);
-
-	consoleDemoInit();
-	REG_JOYTR = 0;
-	// ansi escape sequence to set print co-ordinates
-	// /x1b[line;columnH
-	u32 i;
-	iprintf("\x1b[9;2HGBA Link Cable Dumper v1.6\n");
-	iprintf("\x1b[10;4HPlease look at the TV\n");
-	// disable this, needs power
-	SNDSTAT = 0;
-	SNDBIAS = 0;
-	// Set up waitstates for EEPROM access etc. 
-	REG_WAITCNT = 0x0317;
-	//clear out previous messages
-	REG_HS_CTRL |= JOY_RW;
-	while (1) {
-		if(REG_HS_CTRL&JOY_READ)
-		{
-			REG_HS_CTRL |= JOY_RW;
-			s32 gamesize = getGameSize();
-			u32 savesize = SaveSize(save_data,gamesize);
-			REG_JOYTR = gamesize;
-			//wait for a cmd receive for safety
-			while((REG_HS_CTRL&JOY_WRITE) == 0) ;
-			REG_HS_CTRL |= JOY_RW;
-			REG_JOYTR = savesize;
-			//wait for a cmd receive for safety
-			while((REG_HS_CTRL&JOY_WRITE) == 0) ;
-			REG_HS_CTRL |= JOY_RW;
-			if(gamesize == -1)
-			{
-				REG_JOYTR = 0;
-				continue; //nothing to read
-			}
-			//game in, send header
-			for(i = 0; i < 0xC0; i+=4)
-			{
-				REG_JOYTR = *(vu32*)(0x08000000+i);
-				while((REG_HS_CTRL&JOY_READ) == 0) ;
-				REG_HS_CTRL |= JOY_RW;
-			}
-			REG_JOYTR = 0;
-			//wait for other side to choose
-			while((REG_HS_CTRL&JOY_WRITE) == 0) ;
-			REG_HS_CTRL |= JOY_RW;
-			u32 choseval = REG_JOYRE;
-			if(choseval == 0)
-			{
-				REG_JOYTR = 0;
-				continue; //nothing to read
-			}
-			else if(choseval == 1)
-			{
-				//disable interrupts
-				u32 prevIrqMask = REG_IME;
-				REG_IME = 0;
-				//dump the game
-				for(i = 0; i < gamesize; i+=4)
-				{
-					REG_JOYTR = *(vu32*)(0x08000000+i);
-					while((REG_HS_CTRL&JOY_READ) == 0) ;
-					REG_HS_CTRL |= JOY_RW;
-				}
-				//restore interrupts
-				REG_IME = prevIrqMask;
-			}
-			else if(choseval == 2)
-			{
-				//disable interrupts
-				u32 prevIrqMask = REG_IME;
-				REG_IME = 0;
-				//backup save
-				switch (savesize){
-				case 0x200:
-					GetSave_EEPROM_512B(save_data);
-					break;
-				case 0x2000:
-					GetSave_EEPROM_8KB(save_data);
-					break;
-				case 0x8000:
-					GetSave_SRAM_32KB(save_data);
-					break;
-				case 0x10000:
-					GetSave_FLASH_64KB(save_data);
-					break;
-				case 0x20000:
-					GetSave_FLASH_128KB(save_data);
-					break;
-				default:
-					break;
-				}
-				//restore interrupts
-				REG_IME = prevIrqMask;
-				//say gc side we read it
-				REG_JOYTR = savesize;
-				//wait for a cmd receive for safety
-				while((REG_HS_CTRL&JOY_WRITE) == 0) ;
-				REG_HS_CTRL |= JOY_RW;
-				//send the save
-				for(i = 0; i < savesize; i+=4)
-				{
-					REG_JOYTR = *(vu32*)(save_data+i);
-					while((REG_HS_CTRL&JOY_READ) == 0) ;
-					REG_HS_CTRL |= JOY_RW;
-				}
-			}
-			else if(choseval == 3 || choseval == 4)
-			{
-				REG_JOYTR = savesize;
-				if(choseval == 3)
-				{
-					//receive the save
-					for(i = 0; i < savesize; i+=4)
-					{
-						while((REG_HS_CTRL&JOY_WRITE) == 0) ;
-						REG_HS_CTRL |= JOY_RW;
-						*(vu32*)(save_data+i) = REG_JOYRE;
-					}
-				}
-				else
-				{
-					//clear the save
-					for(i = 0; i < savesize; i+=4)
-						*(vu32*)(save_data+i) = 0;
-				}
-				//disable interrupts
-				u32 prevIrqMask = REG_IME;
-				REG_IME = 0;
-				//write it
-				switch (savesize){
-				case 0x200:
-					PutSave_EEPROM_512B(save_data);
-					break;
-				case 0x2000:
-					PutSave_EEPROM_8KB(save_data);
-					break;
-				case 0x8000:
-					PutSave_SRAM_32KB(save_data);
-					break;
-				case 0x10000:
-					PutSave_FLASH_64KB(save_data);
-					break;
-				case 0x20000:
-					PutSave_FLASH_128KB(save_data);
-					break;
-				default:
-					break;
-				}
-				//restore interrupts
-				REG_IME = prevIrqMask;
-				//say gc side we're done
-				REG_JOYTR = 0;
-				//wait for a cmd receive for safety
-				while((REG_HS_CTRL&JOY_WRITE) == 0) ;
-				REG_HS_CTRL |= JOY_RW;
-			}
-			REG_JOYTR = 0;
-		}
-		else if(REG_HS_CTRL&JOY_WRITE)
-		{
-			REG_HS_CTRL |= JOY_RW;
-			u32 choseval = REG_JOYRE;
-			if(choseval == 5)
-			{
-				//disable interrupts
-				u32 prevIrqMask = REG_IME;
-				REG_IME = 0;
-				//dump BIOS
-				for (i = 0; i < 0x4000; i+=4)
-				{
-					// the lower bits are inaccurate, so just get it four times :)
-					u32 a = MidiKey2Freq((WaveData *)(i-4), 180-12, 0) * 2;
-					u32 b = MidiKey2Freq((WaveData *)(i-3), 180-12, 0) * 2;
-					u32 c = MidiKey2Freq((WaveData *)(i-2), 180-12, 0) * 2;
-					u32 d = MidiKey2Freq((WaveData *)(i-1), 180-12, 0) * 2;
-					REG_JOYTR = ((a>>24<<24) | (d>>24<<16) | (c>>24<<8) | (b>>24));
-					while((REG_HS_CTRL&JOY_READ) == 0) ;
-					REG_HS_CTRL |= JOY_RW;
-				}
-				//restore interrupts
-				REG_IME = prevIrqMask;
-			}
-			REG_JOYTR = 0;
-		}
-		Halt();
-	}
+
+	
+  //*(vu16 *)(REG_BASE + 0x84) = 0x8f;
+  //REG_IME = 1;
+  // the vblank interrupt must be enabled for VBlankIntrWait() to work
+  // since the default dispatcher handles the bios flags no vblank handler
+  // is required
+  //irqInit();
+  //irqEnable(IRQ_VBLANK);
+
+  //consoleDemoInit();
+  //REG_JOYTR = 0;
+
+  // ansi escape sequence to set print co-ordinates
+  // /x1b[line;columnH
+  //u32 i;
+  //iprintf("\x1b[9;2HPokemon Gen III Data Extractor\n");
+  //iprintf("\x1b[10;4HPlease look at the TV\n");
+
+  // disable this, needs power
+  //SNDSTAT = 0;
+  //SNDBIAS = 0;
+
+  // Set up waitstates for EEPROM access etc.
+  //REG_WAITCNT = 0x0317;
+
+  //clear out previous messages
+  REG_HS_CTRL |= JOY_RW;
+	sendU32(0);
+  plot_sqr( 4, 5, RGB15( 0,31, 0) );    // or CLR_LIME
+  while (1)
+  {
+		waitForWriteAccess();
+		//while (recieveU32() != 0);
+		//waitForAck();
+  plot_sqr( 4, 6, RGB15( 0, 0,31) );    // or CLR_BLUE
+    // Send game size to acknowledge that an actual cart is inserted.
+    //s32 gamesize = getGameSize();
+    //sendS32(gamesize);
+    //waitForAck();
+
+    // If the game size is illegal, start over.
+    //if (gamesize == -1)
+    //{
+    //  sendS32(0);
+		//
+    //  continue;
+    //}
+
+    // Identify the inserted game.
+    if (GAME_RUBY)
+    {
+      sendS32(1);
+    } else if (GAME_SAPP)
+    {
+      sendS32(2);
+    } else if (GAME_FR)
+    {
+      sendS32(3);
+    } else if (GAME_LG)
+    {
+      sendS32(4);
+    } else if (GAME_EM)
+    {
+      sendS32(5);
+    } else {
+      sendS32(-1);
+      waitForAck();
+
+      sendS32(0);
+
+      continue;
+    }
+  plot_sqr( 5, 4, RGB15( 31, 0,31) );
+    waitForAck();
+  plot_sqr( 5, 5, RGB15( 16, 16,16) );
+    // Get access to save data.
+    pSaveBlock1 SaveBlock1;
+    pSaveBlock2 SaveBlock2;
+    pSaveBlock3 SaveBlock3;
+    if (!initSaveData(&SaveBlock1, &SaveBlock2, &SaveBlock3))
+    {
+      // Unsupported game version.
+      //iprintf("Unsupported game version\n");
+      sendS32(-1);
+      waitForAck();
+
+      sendS32(0);
+
+      continue;
+    }
+  plot_sqr( 5, 6, RGB15( 0, 31,16) );
+    sendS32(1);
+    waitForAck();
+		/*
+    // Send trainer name.
+    u8* trainerName;
+
+    if (GAME_RS)
+    {
+      trainerName = SaveBlock2->rs.playerName;
+    } else if (GAME_FRLG)
+    {
+      trainerName = SaveBlock2->frlg.playerName;
+    } else if (GAME_EM)
+    {
+      trainerName = SaveBlock2->e.playerName;
+    }
+    iprintf("%d\n", trainerName[0]);
+    iprintf("%d\n", trainerName[1]);
+    iprintf("%d\n", trainerName[2]);
+    iprintf("%d\n", trainerName[3]);
+    iprintf("%d\n", trainerName[4]);
+    iprintf("%d\n", trainerName[5]);
+    iprintf("%d\n", trainerName[6]);
+    iprintf("%d\n", trainerName[7]);
+
+    u32 tn1 =
+        (trainerName[0] << 24)
+      | (trainerName[1] << 16)
+      | (trainerName[2] << 8)
+      | (trainerName[3]);
+
+    u32 tn2 =
+        (trainerName[4] << 24)
+      | (trainerName[5] << 16)
+      | (trainerName[6] << 8)
+      | (trainerName[7]);
+
+    sendU32(tn1);
+    waitForAck();
+
+    sendU32(tn2);
+    waitForAck();
+*/
+    // Send trainer ID.
+    u8* trainerId = 0;
+
+    if (GAME_RS)
+    {
+      trainerId = SaveBlock2->rs.playerTrainerId;
+    } else if (GAME_FRLG)
+    {
+      trainerId = SaveBlock2->frlg.playerTrainerId;
+    } else if (GAME_EM)
+    {
+      trainerId = SaveBlock2->e.playerTrainerId;
+    }
+
+    u32 tti =
+        (trainerId[2] << 8)
+      | (trainerId[3]);
+
+    sendU32(tti);
+    waitForAck();
+
+    // Restart, because we're just testing.
+    sendS32(0);
+    //continue;
+		break;
+
+/*
+
+
+      //game in, send header
+      for(i = 0; i < 0xC0; i+=4)
+      {
+        REG_JOYTR = *(vu32*)(0x08000000+i);
+        while((REG_HS_CTRL&JOY_READ) == 0) ;
+        REG_HS_CTRL |= JOY_RW;
+      }
+      REG_JOYTR = 0;
+      //wait for other side to choose
+      while((REG_HS_CTRL&JOY_WRITE) == 0) ;
+      REG_HS_CTRL |= JOY_RW;
+      u32 choseval = REG_JOYRE;
+      if(choseval == 0)
+      {
+        REG_JOYTR = 0;
+        continue; //nothing to read
+      }
+      else if(choseval == 1)
+      {
+        //disable interrupts
+        u32 prevIrqMask = REG_IME;
+        REG_IME = 0;
+        //dump the game
+        for(i = 0; i < gamesize; i+=4)
+        {
+          REG_JOYTR = *(vu32*)(0x08000000+i);
+          while((REG_HS_CTRL&JOY_READ) == 0) ;
+          REG_HS_CTRL |= JOY_RW;
+        }
+        //restore interrupts
+        REG_IME = prevIrqMask;
+      }
+      else if(choseval == 2)
+      {
+        //disable interrupts
+        u32 prevIrqMask = REG_IME;
+        REG_IME = 0;
+        //backup save
+        switch (savesize){
+          case 0x200:
+            GetSave_EEPROM_512B(save_data);
+            break;
+          case 0x2000:
+            GetSave_EEPROM_8KB(save_data);
+            break;
+          case 0x8000:
+            GetSave_SRAM_32KB(save_data);
+            break;
+          case 0x10000:
+            GetSave_FLASH_64KB(save_data);
+            break;
+          case 0x20000:
+            GetSave_FLASH_128KB(save_data);
+            break;
+          default:
+            break;
+        }
+        //restore interrupts
+        REG_IME = prevIrqMask;
+        //say gc side we read it
+        REG_JOYTR = savesize;
+        //wait for a cmd receive for safety
+        while((REG_HS_CTRL&JOY_WRITE) == 0) ;
+        REG_HS_CTRL |= JOY_RW;
+        //send the save
+        for(i = 0; i < savesize; i+=4)
+        {
+          REG_JOYTR = *(vu32*)(save_data+i);
+          while((REG_HS_CTRL&JOY_READ) == 0) ;
+          REG_HS_CTRL |= JOY_RW;
+        }
+      }
+      else if(choseval == 3 || choseval == 4)
+      {
+        REG_JOYTR = savesize;
+        if(choseval == 3)
+        {
+          //receive the save
+          for(i = 0; i < savesize; i+=4)
+          {
+            while((REG_HS_CTRL&JOY_WRITE) == 0) ;
+            REG_HS_CTRL |= JOY_RW;
+            *(vu32*)(save_data+i) = REG_JOYRE;
+          }
+        }
+        else
+        {
+          //clear the save
+          for(i = 0; i < savesize; i+=4)
+            *(vu32*)(save_data+i) = 0;
+        }
+        //disable interrupts
+        u32 prevIrqMask = REG_IME;
+        REG_IME = 0;
+        //write it
+        switch (savesize){
+          case 0x200:
+            PutSave_EEPROM_512B(save_data);
+            break;
+          case 0x2000:
+            PutSave_EEPROM_8KB(save_data);
+            break;
+          case 0x8000:
+            PutSave_SRAM_32KB(save_data);
+            break;
+          case 0x10000:
+            PutSave_FLASH_64KB(save_data);
+            break;
+          case 0x20000:
+            PutSave_FLASH_128KB(save_data);
+            break;
+          default:
+            break;
+        }
+        //restore interrupts
+        REG_IME = prevIrqMask;
+        //say gc side we're done
+        REG_JOYTR = 0;
+        //wait for a cmd receive for safety
+        while((REG_HS_CTRL&JOY_WRITE) == 0) ;
+        REG_HS_CTRL |= JOY_RW;
+      }
+      REG_JOYTR = 0;
+    }
+    } else if(REG_HS_CTRL&JOY_WRITE)
+    {
+      REG_HS_CTRL |= JOY_RW;
+      u32 choseval = REG_JOYRE;
+      if(choseval == 5)
+      {
+        //disable interrupts
+        u32 prevIrqMask = REG_IME;
+        REG_IME = 0;
+        //dump BIOS
+        for (i = 0; i < 0x4000; i+=4)
+        {
+          // the lower bits are inaccurate, so just get it four times :)
+          u32 a = MidiKey2Freq((WaveData *)(i-4), 180-12, 0) * 2;
+          u32 b = MidiKey2Freq((WaveData *)(i-3), 180-12, 0) * 2;
+          u32 c = MidiKey2Freq((WaveData *)(i-2), 180-12, 0) * 2;
+          u32 d = MidiKey2Freq((WaveData *)(i-1), 180-12, 0) * 2;
+          REG_JOYTR = ((a>>24<<24) | (d>>24<<16) | (c>>24<<8) | (b>>24));
+          while((REG_HS_CTRL&JOY_READ) == 0) ;
+          REG_HS_CTRL |= JOY_RW;
+        }
+        //restore interrupts
+        REG_IME = prevIrqMask;
+      }
+      REG_JOYTR = 0;
+    }*/
+    Halt();
+  }
 }
 
 
diff --git a/gba/source/saveblocks.h b/gba/source/saveblocks.h
new file mode 100644
index 0000000..cf0a5c3
--- /dev/null
+++ b/gba/source/saveblocks.h
@@ -0,0 +1,343 @@
+/*
+ * Example Gen3-multiboot payload by slipstream/RoL 2017.
+ *
+ * This software may be modified and distributed under the terms
+ * of the MIT license.  See the LICENSE file for details.
+ *
+ * saveblocks.h: describes saveblock structures for all of Gen 3 (yay!)
+ */
+
+// Most of the structures come from pokeruby, FR/LG changes come from my own research / the firered IDB on pokecommunity
+#include "savestructs.h"
+
+typedef struct
+{
+    /*0x00*/ struct Coords16 pos;
+    /*0x04*/ struct WarpData location;
+    /*0x0C*/ struct WarpData warp[4];
+    /*0x2C*/ u16 battleMusic;
+    /*0x2E*/ u8 weather;
+    /*0x2F*/ u8 filler_2F;
+    /*0x30*/ u8 flashUsed;
+    /*0x32*/ u16 mapDataId;
+    /*0x34*/ u16 mapView[0x100];
+    /*0x234*/ u8 playerPartyCount;
+    /*0x238*/ struct Pokemon playerParty[6];
+    /*0x490*/ u32 money;
+    /*0x494*/ u16 coins;
+    /*0x496*/ u16 registeredItem; // registered for use with SELECT button
+    /*0x498*/ struct ItemSlot pcItems[50];
+    /*0x560*/ struct ItemSlot bagPocket_Items[20];
+    /*0x5B0*/ struct ItemSlot bagPocket_KeyItems[20];
+    /*0x600*/ struct ItemSlot bagPocket_PokeBalls[16];
+    /*0x640*/ struct ItemSlot bagPocket_TMHM[64];
+    /*0x740*/ struct ItemSlot bagPocket_Berries[46];
+    /*0x7F8*/ struct Pokeblock pokeblocks[40];
+    /*0x938*/ u8 unk938[52];  // pokedex related
+    /*0x96C*/ u16 berryBlenderRecords[3];
+    /*0x972*/ u8 filler_972[0x6];
+    /*0x978*/ u16 trainerRematchStepCounter;
+    /*0x97A*/ u8 trainerRematches[100];
+    /*0x9E0*/ struct MapObject mapObjects[16];
+    /*0xC20*/ struct MapObjectTemplate mapObjectTemplates[64];
+    /*0x1220*/ u8 flags[0x120];
+    /*0x1340*/ u16 vars[0x100];
+    /*0x1540*/ u32 gameStats[50];
+    /*0x1608*/ struct BerryTree berryTrees[128];
+    /*0x1A08*/ struct SecretBaseRecord secretBases[20];
+    /*0x2688*/ u8 playerRoomDecor[12];
+    /*0x2694*/ u8 playerRoomDecorPos[12];
+    /*0x26A0*/ u8 decorDesk[10];
+    /*0x26AA*/ u8 decorChair[10];
+    /*0x26B4*/ u8 decorPlant[10];
+    /*0x26BE*/ u8 decorOrnament[30];
+    /*0x26DC*/ u8 decorMat[30];
+    /*0x26FA*/ u8 decorPoster[10];
+    /*0x2704*/ u8 decorDoll[40];
+    /*0x272C*/ u8 decorCushion[10];
+    /*0x2736*/ u8 padding_2736[2];
+    /*0x2738*/ TVShow tvShows[24];
+    /*0x2A98*/ u8 filler_2A98[0x64];
+    /*0x2AFC*/ u16 outbreakPokemonSpecies;
+    /*0x2AFE*/ u8 outbreakLocationMapNum;
+    /*0x2AFF*/ u8 outbreakLocationMapGroup;
+    /*0x2B00*/ u8 outbreakPokemonLevel;
+    /*0x2B01*/ u8 outbreakUnk1;
+    /*0x2B02*/ u16 outbreakUnk2;
+    /*0x2B04*/ u16 outbreakPokemonMoves[4];
+    /*0x2B0C*/ u8 outbreakUnk4;
+    /*0x2B0D*/ u8 outbreakPokemonProbability;
+    /*0x2B0E*/ u16 outbreakUnk5;
+    /*0x2B10*/ u8 filler_2B0E[0xC];
+    /*0x2B1C*/ u16 unk2B1C[4];
+    /*0x2B24*/ u8 filler_2B24[0x28];
+    /*0x2B4C*/ struct MailStruct mail[16];
+    /*0x2D8C*/ u8 filler_2D8C[0x8];
+    /*0x2D94*/ OldMan oldMan;
+    /*0x2DC0*/ u8 unk_2DC0[0x14];
+    /*0x2DD4*/ struct EasyChatPair easyChatPairs[5]; //Dewford trend [0] and some other stuff
+    /*0x2DFC*/ u8 filler_2DFC[0x100];
+    /*0x2EFC*/ struct SB1_2EFC_Struct sb1_2EFC_struct[5];
+    /*0x2F9C*/ u8 filler_2F9C[0xA0];
+    /*0x303C*/ u8 filler_303C[0x38];
+    /*0x3074*/ u8 filler_3074[0x42];
+    /*0x30B6*/ u8 filler_30B6;
+    /*0x30B7*/ u8 filler_30B7[0x59];
+    /*0x3110*/ u8 giftRibbons[7];
+    /*0x3117*/ u8 filler_311B[0x2D];
+    /*0x3144*/ struct Roamer roamer;
+    /*0x3158*/ u8 filler_3158[0x8];
+    /*0x3160*/ struct EnigmaBerry enigmaBerry; // this is actually offset by 0x98 ...
+    /*0x3690*/ struct RamScript ramScript;
+    /*0x3A7C*/ u8 filler_3A7C[0x10];
+    /*0x3A8C*/ u8 unk3A8C[52]; //pokedex related
+} SaveBlock1_RS;
+
+typedef struct // Don't rely on the commented offsets, they'll be wrong due to elements removed in FR/LG...
+{
+    /*0x00*/ struct Coords16 pos;
+    /*0x04*/ struct WarpData location;
+    /*0x0C*/ struct WarpData warp[4];
+    /*0x2C*/ u16 battleMusic;
+    /*0x2E*/ u8 weather;
+    /*0x2F*/ u8 filler_2F;
+    /*0x30*/ u8 flashUsed;
+    /*0x32*/ u16 mapDataId;
+//    /*0x34*/ u16 mapView[0x100]; // Not in fr/lg
+    /*0x234*/ u8 playerPartyCount;
+    /*0x238*/ struct Pokemon playerParty[6];
+    /*0x490*/ u32 money;
+    /*0x494*/ u16 coins;
+    /*0x496*/ u16 registeredItem; // registered for use with SELECT button
+    /*0x498*/ struct ItemSlot pcItems[30];
+    /*0x560*/ struct ItemSlot bagPocket_Items[42];
+    /*0x5B0*/ struct ItemSlot bagPocket_KeyItems[30];
+    /*0x600*/ struct ItemSlot bagPocket_PokeBalls[13];
+    /*0x640*/ struct ItemSlot bagPocket_TMHM[58];
+    /*0x740*/ struct ItemSlot bagPocket_Berries[43];
+//    /*0x7F8*/ struct Pokeblock pokeblocks[40]; // Not in fr/lg
+    /*0x938*/ u8 unk938[52];  // pokedex related
+    /*0x96C*/ u8 unk_62C[12];
+    /*0x972*/ u8 filler_972[0x6];
+    /*0x97A*/ u8 unk_63E[98];
+    /*0x9E0*/ struct MapObject mapObjects[16];
+    /*0xC20*/ struct MapObjectTemplate mapObjectTemplates[64];
+    /*0x1220*/ u8 flags[0x120];
+    /*0x1340*/ u16 vars[0x100];
+	/*0x1540*/ u32 gameStats[64]; // encrypted with saveblock2 xor-key
+    struct QuestStory questlog[4];
+    u8 messages[12][4];
+	struct NPCState npc_states[0x10];
+	u8 unk_2f10[112];
+	struct DaycarePokemon daycare[2];
+	u8 unk_3098[56];
+	struct Roamer roamer;
+	u8 unk_30e4[8];
+	/*0x3160*/ struct EnigmaBerryFRLGE enigmaBerry;
+	u8 unk_3120[0x1C0]; // 4 bytes of CRC16, then 444 bytes of unknown. Mystery Gift related.
+	u8 unk_32E0[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_1"
+	u8 unk_3430[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_2"
+	u8 unk_368C[0x9C]; // padding? doesn't seem to be actually used
+	struct RamScript ramScript;
+	u8 unk_3A07[17];
+	u8 pokemon_flags_2[52];
+	u8 rivalName[8];
+	u8 unk_3a54[128];
+	u8 words[21][10];
+	u8 unk_3ba6[570];
+} __attribute__((aligned(1))) SaveBlock1_FRLG;
+
+typedef struct // Don't rely on the commented offsets, they'll be wrong due to elements changed/added in Emerald...
+{
+    /*0x00*/ struct Coords16 pos;
+    /*0x04*/ struct WarpData location;
+    /*0x0C*/ struct WarpData warp[4];
+    /*0x2C*/ u16 battleMusic;
+    /*0x2E*/ u8 weather;
+    /*0x2F*/ u8 filler_2F;
+    /*0x30*/ u8 flashUsed;
+    /*0x32*/ u16 mapDataId;
+    /*0x34*/ u16 mapView[0x100];
+    /*0x234*/ u8 playerPartyCount;
+    /*0x238*/ struct Pokemon playerParty[6];
+    /*0x490*/ u32 money;
+    /*0x494*/ u16 coins;
+    /*0x496*/ u16 registeredItem; // registered for use with SELECT button
+    /*0x498*/ struct ItemSlot pcItems[50];
+    /*0x560*/ struct ItemSlot bagPocket_Items[30];
+    /*0x5D8*/ struct ItemSlot bagPocket_KeyItems[30];
+    /*0x650*/ struct ItemSlot bagPocket_PokeBalls[16];
+    /*0x690*/ struct ItemSlot bagPocket_TMHM[64];
+    /*0x790*/ struct ItemSlot bagPocket_Berries[46];
+    /*0x7F8*/ struct Pokeblock pokeblocks[40]; // every offset is shifted by 0x50 from here on thanks to changed bag-counts
+    /*0x938*/ u8 unk938[52];  // pokedex related
+    /*0x96C*/ u16 berryBlenderRecords[3];
+    /*0x972*/ u8 filler_972[0x6];
+    /*0x978*/ u16 trainerRematchStepCounter;
+    /*0x97A*/ u8 trainerRematches[100];
+    /*0x9E0*/ struct MapObject mapObjects[16];
+    /*0xC20*/ struct MapObjectTemplate mapObjectTemplates[64];
+    /*0x1220*/ u8 flags[0x12C];
+    /*0x1340*/ u16 vars[0x100]; // offsets shifted by 0x5C from here on thanks to added flags
+    /*0x1540*/ u32 gameStats[64]; // encrypted with saveblock2 xor-key
+    /*0x1608*/ struct BerryTree berryTrees[128]; // offsets shifted by 0x94 from here on thanks to added 14 gamestats
+    /*0x1A08*/ struct SecretBaseRecord secretBases[20];
+    /*0x2688*/ u8 playerRoomDecor[12];
+    /*0x2694*/ u8 playerRoomDecorPos[12];
+    /*0x26A0*/ u8 decorDesk[10];
+    /*0x26AA*/ u8 decorChair[10];
+    /*0x26B4*/ u8 decorPlant[10];
+    /*0x26BE*/ u8 decorOrnament[30];
+    /*0x26DC*/ u8 decorMat[30];
+    /*0x26FA*/ u8 decorPoster[10];
+    /*0x2704*/ u8 decorDoll[40];
+    /*0x272C*/ u8 decorCushion[10];
+    // /*0x2736*/ u8 padding_2736[2];
+    /*0x2738*/ TVShow tvShows[24];
+    /*0x2A98*/ u8 filler_2A98[0x64];
+    /*0x2AFC*/ u16 outbreakPokemonSpecies; // offset by 0x94
+    /*0x2AFE*/ u8 outbreakLocationMapNum;
+    /*0x2AFF*/ u8 outbreakLocationMapGroup;
+    /*0x2B00*/ u8 outbreakPokemonLevel;
+    /*0x2B01*/ u8 outbreakUnk1;
+    /*0x2B02*/ u16 outbreakUnk2;
+    /*0x2B04*/ u16 outbreakPokemonMoves[4];
+    /*0x2B0C*/ u8 outbreakUnk4;
+    /*0x2B0D*/ u8 outbreakPokemonProbability;
+    /*0x2B0E*/ u16 outbreakUnk5;
+    /*0x2B10*/ u8 filler_2B0E[0xC];
+    /*0x2B1C*/ u16 unk2B1C[4];
+    /*0x2B24*/ u8 filler_2B24[0x28];
+    /*0x2B4C*/ struct MailStruct mail[16]; // offset by 0x94
+    /*0x2D8C*/ u8 filler_2D8C[0x8];
+    /*0x2D94*/ OldMan oldMan;
+    /*0x2DC0*/ u8 unk_2DC0[0x14];
+    /*0x2DD4*/ struct EasyChatPair easyChatPairs[5]; //Dewford trend [0] and some other stuff
+    // /*0x2DFC*/ u8 filler_2DFC[0x100];
+    /*0x2EFC*/ struct SB1_2EFC_Struct sb1_2EFC_struct[12];
+	u8 unk_3010[0x198]; // no idea if any of this is actually used.
+    /*0x3110*/ u8 giftRibbons[7];
+    /*0x3117*/ u8 filler_311B[0x2D];
+    /*0x3144*/ struct Roamer roamer;
+    /*0x3158*/ u8 filler_3158[0x8];
+    /*0x3160*/ struct EnigmaBerryFRLGE enigmaBerry;
+	u8 unk_322C[0x1C0]; // 4 bytes of CRC16, then 444 bytes of unknown. Mystery Gift related.
+	u8 unk_33EC[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_1"
+	u8 unk_353C[0x150]; // 4 bytes of CRC16, then 332 bytes of unknown. Mystery Gift related. "mevent_buffer_2"
+	u8 unk_368C[0x9C]; // padding? doesn't seem to be actually used
+    /*0x3690*/ struct RamScript ramScript;
+    /*0x3A7C*/ u8 filler_3A7C[0x10];
+    /*0x3A8C*/ u8 unk3A8C[52]; //pokedex related
+} SaveBlock1_E;
+
+// ---
+
+struct SaveBlock2_Sub
+{
+    /*0x0000, 0x00A8*/ u8 filler_000[0x4AE];
+    /*0x04AE, 0x0556*/ u8 var_4AE;
+    /*0x04AF, 0x0557*/ u8 var_4AF;
+    /*0x04B0, 0x0558*/ u16 var_4B0;
+    /*0x04B2, 0x055A*/ u16 var_4B2;
+    /*0x04B4, 0x055C*/ u16 var_4B4;
+    /*0x04B6, 0x055E*/ u16 var_4B6;
+    /*0x04B8, 0x0560*/ u8 filler_4B8[0x10];
+    /*0x04C8, 0x0570*/ u16 var_4C8;
+    /*0x04CA, 0x0572*/ u16 var_4CA;
+    /*0x04CC, 0x0574*/ u8 filler_4CC[0x31C];
+};
+
+typedef struct
+{
+    /*0x00*/ u8 playerName[8];
+    /*0x08*/ u8 playerGender; // MALE, FEMALE
+    /*0x09*/ u8 specialSaveWarp;
+    /*0x0A*/ u8 playerTrainerId[4];
+    /*0x0E*/ u16 playTimeHours;
+    /*0x10*/ u8 playTimeMinutes;
+    /*0x11*/ u8 playTimeSeconds;
+    /*0x12*/ u8 playTimeVBlanks;
+    /*0x13*/ u8 optionsButtonMode;  // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A]
+    /*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST]
+             u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes
+             u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO]
+             u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET]
+             u16 optionsBattleSceneOff:1; // whether battle animations are disabled
+             u16 regionMapZoom:1; // whether the map is zoomed in
+    /*0x18*/ struct Pokedex pokedex;
+    /*0x90*/ u8 filler_90[0x8];
+    /*0x98*/ struct Time localTimeOffset;
+    /*0xA0*/ struct Time lastBerryTreeUpdate;
+    /*0xA8*/ struct SaveBlock2_Sub filler_A8;
+} SaveBlock2_RS;
+
+typedef struct
+{
+    /*0x00*/ u8 playerName[8];
+    /*0x08*/ u8 playerGender; // MALE, FEMALE
+    /*0x09*/ u8 specialSaveWarp;
+    /*0x0A*/ u8 playerTrainerId[4];
+    /*0x0E*/ u16 playTimeHours;
+    /*0x10*/ u8 playTimeMinutes;
+    /*0x11*/ u8 playTimeSeconds;
+    /*0x12*/ u8 playTimeVBlanks;
+    /*0x13*/ u8 optionsButtonMode;  // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A]
+    /*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST]
+             u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes
+             u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO]
+             u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET]
+             u16 optionsBattleSceneOff:1; // whether battle animations are disabled
+             u16 regionMapZoom:1; // whether the map is zoomed in
+    /*0x18*/ struct Pokedex pokedex;
+    /*0x90*/ u8 filler_90[0x8];
+    /*0x98*/ struct Time localTimeOffset;
+    /*0xA0*/ struct Time lastBerryTreeUpdate;
+    /*0xA8*/ struct SaveBlock2_Sub filler_A8;
+	/*0x890*/ u8 unk_890[8];
+	/*0x898*/ u8 mapdata[0x258];
+	/*0xaf0*/ u16 field_af0;
+	/*0xaf2*/ u16 field_af2;
+	/*0xaf4*/ u16 field_af4;
+	/*0xaf6*/ u16 field_af6;
+	/*0xaf8*/ u8 unk_af8[0x428];
+	/*0xf20*/ u32 xor_key;
+} SaveBlock2_FRLG;
+
+typedef struct
+{
+    /*0x00*/ u8 playerName[8];
+    /*0x08*/ u8 playerGender; // MALE, FEMALE
+    /*0x09*/ u8 specialSaveWarp;
+    /*0x0A*/ u8 playerTrainerId[4];
+    /*0x0E*/ u16 playTimeHours;
+    /*0x10*/ u8 playTimeMinutes;
+    /*0x11*/ u8 playTimeSeconds;
+    /*0x12*/ u8 playTimeVBlanks;
+    /*0x13*/ u8 optionsButtonMode;  // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A]
+    /*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST]
+             u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes
+             u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO]
+             u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET]
+             u16 optionsBattleSceneOff:1; // whether battle animations are disabled
+             u16 regionMapZoom:1; // whether the map is zoomed in
+    /*0x18*/ struct Pokedex pokedex;
+    /*0x90*/ u8 filler_90[0x8];
+    /*0x98*/ struct Time localTimeOffset;
+    /*0xA0*/ struct Time lastBerryTreeUpdate;
+	/*0xA8*/ u32 xor_key;
+    /*0xAC*/ struct SaveBlock2_Sub filler_A8;
+} SaveBlock2_E;
+
+typedef union {
+	SaveBlock1_RS rs;
+	SaveBlock1_FRLG frlg;
+	SaveBlock1_E e;
+} SaveBlock1, *pSaveBlock1;
+
+typedef union {
+	SaveBlock2_RS rs;
+	SaveBlock2_FRLG frlg;
+	SaveBlock2_E e;
+} SaveBlock2, *pSaveBlock2;
+
+typedef struct PokemonStorage SaveBlock3, *pSaveBlock3;
\ No newline at end of file
diff --git a/gba/source/savestructs.h b/gba/source/savestructs.h
new file mode 100644
index 0000000..2bf4d4d
--- /dev/null
+++ b/gba/source/savestructs.h
@@ -0,0 +1,793 @@
+/*
+ * Example Gen3-multiboot payload by slipstream/RoL 2017.
+ *
+ * This software may be modified and distributed under the terms
+ * of the MIT license.  See the LICENSE file for details.
+ *
+ * saveblocks.h: describes structures used by saveblocks for all of Gen 3
+ */
+
+// Most of the structures come from pokeruby, FR/LG changes come from my own research / the firered IDB on pokecommunity
+
+#define POKEMON_NAME_LENGTH 10
+#define OT_NAME_LENGTH 7
+#define TILE_SIZE_4BPP 32
+
+struct Coords16
+{
+    s16 x;
+    s16 y;
+};
+
+struct UCoords16
+{
+    u16 x;
+    u16 y;
+};
+
+struct SecretBaseRecord
+{
+    u8 sbr_field_0; // ID?
+    u8 sbr_field_1_0:4;
+    u8 gender:1;
+    u8 sbr_field_1_5:1;
+    u8 sbr_field_2[7]; // 0xFF bytes?
+    u8 trainerId[4]; // byte 0 is used for determining trainer class
+    u16 sbr_field_e;
+    u8 sbr_field_10;
+    u8 sbr_field_11;
+    u8 decorations[16];
+    u8 sbr_field_22[16];
+    u32 partyPersonality[6];
+    u16 partyMoves[6 * 4];
+    u16 partySpecies[6];
+    u16 partyHeldItems[6];
+    u8 partyLevels[6];
+    u8 partyEVs[6];
+};
+
+typedef void (*TilesetCB)(void);
+
+struct Tileset
+{
+    u8 isCompressed;
+    u8 isSecondary;
+    void *tiles;
+    void *palettes;
+    void *metatiles;
+    void *metatileAttributes;
+    TilesetCB callback;
+};
+
+struct MapData
+{
+    s32 width;
+    s32 height;
+    u16 *border;
+    u16 *map;
+    struct Tileset *primaryTileset;
+    struct Tileset *secondaryTileset;
+};
+
+struct MapObjectTemplate
+{
+    /*0x00*/ u8 localId;
+    /*0x01*/ u8 graphicsId;
+    /*0x02*/ u8 unk2;
+    /*0x04*/ s16 x;
+    /*0x06*/ s16 y;
+    /*0x08*/ u8 elevation;
+    /*0x09*/ u8 movementType;
+    /*0x0A*/ u8 unkA_0:4;
+             u8 unkA_4:4;
+    ///*0x0B*/ u8 fillerB[1];
+    /*0x0C*/ u16 unkC;
+    /*0x0E*/ u16 unkE;
+    /*0x10*/ u8 *script;
+    /*0x14*/ u16 flagId;
+    /*0x16*/ u8 filler_16[2];
+};  /*size = 0x18*/
+
+struct WarpEvent
+{
+    s16 x, y;
+    s8 warpId;
+    u8 mapGroup;
+    u8 mapNum;
+    u8 unk7;
+};
+
+struct CoordEvent
+{
+    s16 x, y;
+    u8 unk4;
+    u8 filler_5;
+    u16 trigger;
+    u16 index;
+    u8 filler_A[0x2];
+    u8 *script;
+};
+
+struct BgEvent
+{
+    s16 x, y;
+    u8 unk4;
+    u8 kind;
+    s16 filler_6;
+    u8 *script;
+};
+
+struct MapEvents
+{
+    u8 mapObjectCount;
+    u8 warpCount;
+    u8 coordEventCount;
+    u8 bgEventCount;
+
+    struct MapObjectTemplate *mapObjects;
+    struct WarpEvent *warps;
+    struct CoordEvent *coordEvents;
+    struct BgEvent *bgEvents;
+};
+
+struct MapConnection
+{
+    u8 direction;
+    u32 offset;
+    u8 mapGroup;
+    u8 mapNum;
+};
+
+struct MapConnections
+{
+    s32 count;
+    struct MapConnection *connections;
+};
+
+struct MapHeader
+{
+    struct MapData *mapData;
+    struct MapEvents *events;
+    u8 *mapScripts;
+    struct MapConnections *connections;
+    u16 music;
+    u16 mapDataId;
+    u8 name;
+    u8 cave;
+    u8 weather;
+    /* 0x17 */ u8 mapType;
+    u8 filler_18;
+    u8 escapeRope;
+    u8 flags;
+    u8 battleType;
+};
+
+struct MapObject
+{
+    /*0x00*/ u32 active:1;
+             u32 mapobj_bit_1:1;
+             u32 mapobj_bit_2:1;
+             u32 mapobj_bit_3:1;
+             u32 mapobj_bit_4:1;
+             u32 mapobj_bit_5:1;
+             u32 mapobj_bit_6:1;
+             u32 mapobj_bit_7:1;
+    /*0x01*/ u32 mapobj_bit_8:1;
+             u32 mapobj_bit_9:1;
+             u32 mapobj_bit_10:1;
+             u32 mapobj_bit_11:1;
+             u32 mapobj_bit_12:1;
+             u32 mapobj_bit_13:1;
+             u32 mapobj_bit_14:1;
+             u32 mapobj_bit_15:1;
+    /*0x02*/ u32 mapobj_bit_16:1;
+             u32 mapobj_bit_17:1;
+             u32 mapobj_bit_18:1;
+             u32 mapobj_bit_19:1;
+             u32 mapobj_bit_20:1;
+             u32 mapobj_bit_21:1;
+             u32 mapobj_bit_22:1;
+             u32 mapobj_bit_23:1;
+    /*0x03*/ u32 mapobj_bit_24:1;
+             u32 mapobj_bit_25:1;
+             u32 mapobj_bit_26:1;
+             u32 mapobj_bit_27:1;
+             u32 mapobj_bit_28:1;
+             u32 mapobj_bit_29:1;
+             u32 mapobj_bit_30:1;
+             u32 mapobj_bit_31:1;
+    /*0x04*/ u8 spriteId;
+    /*0x05*/ u8 graphicsId;
+    /*0x06*/ u8 animPattern;
+    /*0x07*/ u8 trainerType;
+    /*0x08*/ u8 localId;
+    /*0x09*/ u8 mapNum;
+    /*0x0A*/ u8 mapGroup;
+    /*0x0B*/ u8 mapobj_unk_0B_0:4;
+             u8 elevation:4;
+    /*0x0C*/ struct Coords16 coords1;
+    /*0x10*/ struct Coords16 coords2;
+    /*0x14*/ struct Coords16 coords3;
+    /*0x18*/ u8 mapobj_unk_18:4;  //current direction?
+    /*0x18*/ u8 placeholder18:4;
+    /*0x19*/ u8 mapobj_unk_19;
+    /*0x1A*/ u8 mapobj_unk_1A;
+    /*0x1B*/ u8 mapobj_unk_1B;
+    /*0x1C*/ u8 mapobj_unk_1C;
+    /*0x1D*/ u8 trainerRange_berryTreeId;
+    /*0x1E*/ u8 mapobj_unk_1E;
+    /*0x1F*/ u8 mapobj_unk_1F;
+    /*0x20*/ u8 mapobj_unk_20;
+    /*0x21*/ u8 mapobj_unk_21;
+    /*0x22*/ u8 animId;
+    /*size = 0x24*/
+};
+
+struct Berry
+{
+    const u8 name[7];
+    u8 firmness;
+    u16 size;
+    u8 maxYield;
+    u8 minYield;
+    const u8 *description1;
+    const u8 *description2;
+    u8 stageDuration;
+    u8 spicy;
+    u8 dry;
+    u8 sweet;
+    u8 bitter;
+    u8 sour;
+    u8 smoothness;
+};
+
+struct EnigmaBerry
+{
+    struct Berry berry;
+    u8 pic[(6 * 6) * TILE_SIZE_4BPP];
+    u16 palette[16];
+    u8 description1[45];
+    u8 description2[45];
+    u8 itemEffect[18];
+    u8 holdEffect;
+    u8 holdEffectParam;
+    u32 checksum;
+};
+
+struct BattleEnigmaBerry
+{
+    u8 name[7];
+    u8 holdEffect;
+    u8 itemEffect[18];
+    u8 holdEffectParam;
+};
+
+struct EnigmaBerryFRLGE {
+	struct Berry berry; // 0x00
+	u8 itemEffect[18]; // 0x1C
+	u8 holdEffect; // 0x2E
+	u8 holdEffectParam; // 0x2F
+	u32 checksum; // 0x30
+};
+
+struct __attribute__((aligned(4))) BerryTree
+{
+    u8 berry;
+    u8 stage:7;
+    u8 growthSparkle:1;
+    u16 secondsUntilNextStage;
+    u8 berryYield;
+    u8 regrowthCount:4;
+    u8 watered1:1;
+    u8 watered2:1;
+    u8 watered3:1;
+    u8 watered4:1;
+};
+
+struct PokemonSubstruct0
+{
+    u16 species;
+    u16 heldItem;
+    u32 experience;
+    u8 ppBonuses;
+    u8 friendship;
+};
+
+struct PokemonSubstruct1
+{
+    u16 moves[4];
+    u8 pp[4];
+};
+
+struct PokemonSubstruct2
+{
+    u8 hpEV;
+    u8 attackEV;
+    u8 defenseEV;
+    u8 speedEV;
+    u8 spAttackEV;
+    u8 spDefenseEV;
+    u8 cool;
+    u8 beauty;
+    u8 cute;
+    u8 smart;
+    u8 tough;
+    u8 sheen;
+};
+
+struct PokemonSubstruct3
+{
+ /* 0x00 */ u8 pokerus;
+ /* 0x01 */ u8 metLocation;
+
+ /* 0x02 */ u16 metLevel:7;
+ /* 0x02 */ u16 metGame:4;
+ /* 0x03 */ u16 pokeball:4;
+ /* 0x03 */ u16 otGender:1;
+
+ /* 0x04 */ u32 hpIV:5;
+ /* 0x04 */ u32 attackIV:5;
+ /* 0x05 */ u32 defenseIV:5;
+ /* 0x05 */ u32 speedIV:5;
+ /* 0x05 */ u32 spAttackIV:5;
+ /* 0x06 */ u32 spDefenseIV:5;
+ /* 0x07 */ u32 isEgg:1;
+ /* 0x07 */ u32 altAbility:1;
+
+ /* 0x08 */ u32 coolRibbon:3;
+ /* 0x08 */ u32 beautyRibbon:3;
+ /* 0x08 */ u32 cuteRibbon:3;
+ /* 0x09 */ u32 smartRibbon:3;
+ /* 0x09 */ u32 toughRibbon:3;
+ /* 0x09 */ u32 championRibbon:1;
+ /* 0x0A */ u32 winningRibbon:1;
+ /* 0x0A */ u32 victoryRibbon:1;
+ /* 0x0A */ u32 artistRibbon:1;
+ /* 0x0A */ u32 effortRibbon:1;
+ /* 0x0A */ u32 giftRibbon1:1;
+ /* 0x0A */ u32 giftRibbon2:1;
+ /* 0x0A */ u32 giftRibbon3:1;
+ /* 0x0A */ u32 giftRibbon4:1;
+ /* 0x0B */ u32 giftRibbon5:1;
+ /* 0x0B */ u32 giftRibbon6:1;
+ /* 0x0B */ u32 giftRibbon7:1;
+ /* 0x0B */ u32 fatefulEncounter:5; // unused in Ruby/Sapphire, but the high bit must be set for Mew/Deoxys to obey in FR/LG/Emerald
+};
+
+union PokemonSubstruct
+{
+    struct PokemonSubstruct0 type0;
+    struct PokemonSubstruct1 type1;
+    struct PokemonSubstruct2 type2;
+    struct PokemonSubstruct3 type3;
+    u16 raw[6];
+};
+
+struct BoxPokemon
+{
+    u32 personality;
+    u32 otId;
+    u8 nickname[POKEMON_NAME_LENGTH];
+    u8 language;
+    u8 isBadEgg:1;
+    u8 hasSpecies:1;
+    u8 isEgg:1;
+    u8 unused:5;
+    u8 otName[OT_NAME_LENGTH];
+    u8 markings;
+    u16 checksum;
+    u16 unknown;
+
+    union
+    {
+        u32 raw[12];
+        union PokemonSubstruct substructs[4];
+    } secure;
+};
+
+struct Pokemon
+{
+    struct BoxPokemon box;
+    u32 status;
+    u8 level;
+    u8 pokerus;
+    u16 hp;
+    u16 maxHP;
+    u16 attack;
+    u16 defense;
+    u16 speed;
+    u16 spAttack;
+    u16 spDefense;
+};
+
+struct UnknownPokemonStruct
+{
+    u16 species;
+    u16 heldItem;
+    u16 moves[4];
+    u8 level;
+    u8 ppBonuses;
+    u8 hpEV;
+    u8 attackEV;
+    u8 defenseEV;
+    u8 speedEV;
+    u8 spAttackEV;
+    u8 spDefenseEV;
+    u32 otId;
+    u32 hpIV:5;
+    u32 attackIV:5;
+    u32 defenseIV:5;
+    u32 speedIV:5;
+    u32 spAttackIV:5;
+    u32 spDefenseIV:5;
+    u32 gap:1;
+    u32 altAbility:1;
+    u32 personality;
+    u8 nickname[POKEMON_NAME_LENGTH + 1];
+    u8 friendship;
+};
+
+struct BattlePokemon
+{
+ /* 0x00 */ u16 species;
+ /* 0x02 */ u16 attack;
+ /* 0x04 */ u16 defense;
+ /* 0x06 */ u16 speed;
+ /* 0x08 */ u16 spAttack;
+ /* 0x0A */ u16 spDefense;
+ /* 0x0C */ u16 moves[4];
+ /* 0x14 */ u32 hpIV:5;
+ /* 0x14 */ u32 attackIV:5;
+ /* 0x15 */ u32 defenseIV:5;
+ /* 0x15 */ u32 speedIV:5;
+ /* 0x16 */ u32 spAttackIV:5;
+ /* 0x17 */ u32 spDefenseIV:5;
+ /* 0x17 */ u32 isEgg:1;
+ /* 0x17 */ u32 altAbility:1;
+ /* 0x18 */ s8 statStages[8];
+ /* 0x20 */ u8 ability;
+ /* 0x21 */ u8 type1;
+ /* 0x22 */ u8 type2;
+ /* 0x23 */ u8 unknown;
+ /* 0x24 */ u8 pp[4];
+ /* 0x28 */ u16 hp;
+ /* 0x2A */ u8 level;
+ /* 0x2B */ u8 friendship;
+ /* 0x2C */ u16 maxHP;
+ /* 0x2E */ u16 item;
+ /* 0x30 */ u8 nickname[POKEMON_NAME_LENGTH + 1];
+ /* 0x3B */ u8 ppBonuses;
+ /* 0x3C */ u8 otName[8];
+ /* 0x44 */ u32 experience;
+ /* 0x48 */ u32 personality;
+ /* 0x4C */ u32 status1;
+ /* 0x50 */ u32 status2;
+ /* 0x54 */ u32 otId;
+};
+
+struct BaseStats
+{
+ /* 0x00 */ u8 baseHP;
+ /* 0x01 */ u8 baseAttack;
+ /* 0x02 */ u8 baseDefense;
+ /* 0x03 */ u8 baseSpeed;
+ /* 0x04 */ u8 baseSpAttack;
+ /* 0x05 */ u8 baseSpDefense;
+ /* 0x06 */ u8 type1;
+ /* 0x07 */ u8 type2;
+ /* 0x08 */ u8 catchRate;
+ /* 0x09 */ u8 expYield;
+ /* 0x0A */ u16 evYield_HP:2;
+ /* 0x0A */ u16 evYield_Attack:2;
+ /* 0x0A */ u16 evYield_Defense:2;
+ /* 0x0A */ u16 evYield_Speed:2;
+ /* 0x0B */ u16 evYield_SpAttack:2;
+ /* 0x0B */ u16 evYield_SpDefense:2;
+ /* 0x0C */ u16 item1;
+ /* 0x0E */ u16 item2;
+ /* 0x10 */ u8 genderRatio;
+ /* 0x11 */ u8 eggCycles;
+ /* 0x12 */ u8 friendship;
+ /* 0x13 */ u8 growthRate;
+ /* 0x14 */ u8 eggGroup1;
+ /* 0x15 */ u8 eggGroup2;
+ /* 0x16 */ u8 ability1;
+ /* 0x17 */ u8 ability2;
+ /* 0x18 */ u8 safariZoneFleeRate;
+ /* 0x19 */ u8 bodyColor;
+};
+
+struct BattleMove
+{
+    u8 effect;
+    u8 power;
+    u8 type;
+    u8 accuracy;
+    u8 pp;
+    u8 secondaryEffectChance;
+    u8 target;
+    u8 priority;
+    u32 flags;
+};
+
+struct PokemonStorage
+{
+ /* 0x00 */ u8 currentBox;
+ /* 0x01 */ struct BoxPokemon boxes[14][30];
+    u8 boxNames[14][9];
+    u8 boxBackground[14];
+};
+
+struct WarpData
+{
+    s8 mapGroup;
+    s8 mapNum;
+    s8 warpId;
+    s16 x, y;
+};
+
+struct ItemSlot
+{
+    u16 itemId;
+    u16 quantity;
+};
+
+struct __attribute__((aligned(2))) Pokeblock
+{
+    u8 color;
+    u8 spicy;
+    u8 dry;
+    u8 sweet;
+    u8 bitter;
+    u8 sour;
+    u8 feel;
+};
+
+struct Roamer
+{
+    /*0x00*/ u32 ivs;
+    /*0x04*/ u32 personality;
+    /*0x08*/ u16 species;
+    /*0x0A*/ u16 hp;
+    /*0x0C*/ u8 level;
+    /*0x0D*/ u8 status;
+    /*0x0E*/ u8 cool;
+    /*0x0F*/ u8 beauty;
+    /*0x10*/ u8 cute;
+    /*0x11*/ u8 smart;
+    /*0x12*/ u8 tough;
+    /*0x13*/ u8 active;
+};
+
+struct RamScriptData
+{
+    u8 magic;
+    u8 mapGroup;
+    u8 mapNum;
+    u8 objectId;
+    u8 script[995];
+} __attribute__((aligned(1),packed));
+
+struct RamScript
+{
+    u32 checksum;
+    struct RamScriptData data;
+} __attribute__((aligned(1),packed));
+
+struct SB1_2EFC_Struct
+{
+    u8 unknown[0x20];
+};
+
+struct EasyChatPair
+{
+    u16 unk0_0:7;
+    u16 unk0_7:7;
+    u16 unk1_6:1;
+    u16 unk2;
+    u16 words[2];
+}; /*size = 0x8*/
+
+struct TVShowCommon {
+    /*0x00*/ u8 var00;
+    /*0x01*/ u8 var01;
+};
+
+struct TVShowFanClubLetter {
+    /*0x00*/ u8 var00;
+    /*0x01*/ u8 var01;
+    /*0x02*/ u16 species;
+    u8 pad04[12];
+    /*0x10*/ u8 playerName[8];
+    /*0x18*/ u8 var18;
+};
+
+struct TVShowRecentHappenings {
+    /*0x00*/ u8 var00;
+    /*0x01*/ u8 var01;
+    /*0x02*/ u16 var02;
+    u8 pad04[12];
+    /*0x10*/ u8 var10[8];
+    /*0x18*/ u8 var18;
+    u8 pad19[10];
+};
+
+struct TVShowFanclubOpinions {
+    /*0x00*/ u8 var00;
+    /*0x01*/ u8 var01;
+    /*0x02*/ u16 var02;
+    /*0x04*/ u8 var04A:4;
+    u8 var04B:4;
+    /*0x04*/ u8 var05[8];
+    /*0x0D*/ u8 var0D;
+    /*0x0E*/ u8 var0E;
+    /*0x0F*/ u8 var0F;
+    /*0x10*/ u8 var10[8];
+};
+
+struct TVShowNameRaterShow {
+    /*0x00*/ u8 var00;
+    /*0x01*/ u8 var01;
+    /*0x02*/ u16 species;
+    /*0x04*/ u8 pokemonName[11];
+    /*0x0F*/ u8 trainerName[11];
+    /*0x1A*/ u8 random;
+    /*0x1B*/ u8 random2;
+    /*0x1C*/ u16 var1C;
+    /*0x1E*/ u8 language;
+    /*0x1F*/ u8 var1F;
+};
+
+struct TVShowMassOutbreak {
+    /*0x00*/ u8 var00;
+    /*0x01*/ u8 var01;
+    /*0x02*/ u8 var02;
+    /*0x03*/ u8 var03;
+    /*0x04*/ u16 moves[4];
+    /*0x0C*/ u16 species;
+    /*0x0E*/ u16 var0E;
+    /*0x10*/ u8 locationMapNum;
+    /*0x11*/ u8 locationMapGroup;
+    /*0x12*/ u8 var12;
+    /*0x13*/ u8 probability;
+    /*0x14*/ u8 level;
+    /*0x15*/ u8 var15;
+    /*0x16*/ u16 var16;
+    /*0x18*/ u8 var18;
+    u8 pad19[11];
+};
+
+typedef union TVShow {
+    struct TVShowCommon common;
+    struct TVShowFanClubLetter fanclubLetter;
+    struct TVShowRecentHappenings recentHappenings;
+    struct TVShowFanclubOpinions fanclubOpinions;
+    struct TVShowNameRaterShow nameRaterShow;
+    struct TVShowMassOutbreak massOutbreak;
+} TVShow;
+
+struct __attribute__((aligned(4))) MailStruct
+{
+    /*0x00*/ u16 words[9];
+    /*0x12*/ u8 playerName[8];
+    /*0x1A*/ u8 trainerId[4];
+    /*0x1E*/ u16 species;
+    /*0x20*/ u16 itemId;
+};
+
+struct UnkMauvilleOldManStruct
+{
+    u8 unk_2D94;
+    u8 unk_2D95;
+    /*0x2D96*/ u16 mauvilleOldMan_ecArray[6];
+    /*0x2DA2*/ u16 mauvilleOldMan_ecArray2[6];
+    /*0x2DAE*/ u8 playerName[8];
+    /*0x2DB6*/ u8 filler_2DB6[0x3];
+    /*0x2DB9*/ u8 playerTrainerId[4];
+    u8 unk_2DBD;
+	/* size = 0x2C */
+};
+
+struct UnkMauvilleOldManStruct2
+{
+	u8 filler0;
+	u8 unk1;
+	u8 unk2;
+	u16 mauvilleOldMan_ecArray[10];
+	u16 mauvilleOldMan_ecArray2[6];
+    u8 fillerF[0x2];
+	/* size = 0x2C */
+};
+
+typedef union OldMan {
+	struct UnkMauvilleOldManStruct oldMan1;
+	struct UnkMauvilleOldManStruct2 oldMan2;
+} OldMan;
+
+struct QuestStoryNPC {
+	u16 bitfield;
+	u8 direction;
+	u8 height;
+	u8 type_id;
+	u8 running_behaviour_or_picture_id;
+	u8 is_trainer;
+	u8 local_id;
+	u8 local_mapnumber;
+	u8 local_mapbank;
+	u16 x;
+	u16 y;
+	u8 sight_distance;
+	u8 role_from;
+	u8 unknown_decrement_on_step;
+	u8 unk_11;
+	u16 padding_12;
+};
+
+struct QuestStory {
+	u8 active;
+	u8 bank;
+	u8 map;
+	u8 warpId;
+	u16 x;
+	u16 y;
+	struct QuestStoryNPC npc[0x10];
+	u8 unk_148[0x51f];
+};
+
+struct NPCState {
+	u8 bitfield;
+	u8 obj_anim_and_vis_control;
+	u8 unk_2;
+	u8 unk_3;
+	u8 oamid;
+	u8 type_id;
+	u8 running_behaviour_or_picture_id;
+	u8 is_trainer;
+	u8 local_id;
+	u8 local_mapnumber;
+	u8 local_mapbank;
+	u8 height;
+	struct Coords16 stay_around;
+	struct Coords16 to;
+	struct Coords16 from;
+	u8 direction;
+	u8 movement_area;
+	u8 objid_surfing;
+	u8 objid_1B;
+	u8 idx_movement_behaviour;
+	u8 sight_distance;
+	u8 role_to;
+	u8 role_from;
+	u8 unk_20;
+	u8 unknown_decrement_on_step;
+	u8 unk_22;
+	u8 unk_23;
+};
+
+struct DaycarePokemon {
+	struct BoxPokemon pokemon;
+	u8 unk_50[56];
+	u32 steps;
+};
+
+
+struct Time
+{
+    /*0x00*/ s16 days;
+    /*0x02*/ s8 hours;
+    /*0x03*/ s8 minutes;
+    /*0x04*/ s8 seconds;
+};
+
+struct Pokedex
+{
+    /*0x00*/ u8 order;
+    /*0x01*/ u8 unknown1;
+    /*0x02*/ u8 nationalMagic; // must equal 0xDA in order to have National mode
+    /*0x03*/ u8 unknown2;
+    /*0x04*/ u32 unownPersonality; // set when you first see Unown
+    /*0x08*/ u32 spindaPersonality; // set when you first see Spinda
+    /*0x0C*/ u32 unknown3;
+    /*0x10*/ u8 owned[52];
+    /*0x44*/ u8 seen[52];
+};
\ No newline at end of file
diff --git a/gba/start/pkjb_crt0.s b/gba/start/pkjb_crt0.s
new file mode 100644
index 0000000..0d9d657
--- /dev/null
+++ b/gba/start/pkjb_crt0.s
@@ -0,0 +1,102 @@
+	.section	".init"
+	.global     _start
+	.align
+	.arm
+@---------------------------------------------------------------------------------
+_start:
+@---------------------------------------------------------------------------------
+	b	rom_header_end
+
+	.fill   156,1,0			@ Nintendo Logo Character Data (8000004h)
+	.fill	16,1,0			@ Game Title
+	.byte   0x30,0x31		@ Maker Code (80000B0h)
+	.byte   0x96			@ Fixed Value (80000B2h)
+	.byte   0x00			@ Main Unit Code (80000B3h)
+	.byte   0x00			@ Device Type (80000B4h)
+	.fill	7,1,0			@ unused
+	.byte	0x00			@ Software Version No (80000BCh)
+	.byte	0xf0			@ Complement Check (80000BDh)
+	.byte	0x00,0x00    		@ Checksum (80000BEh)
+
+@---------------------------------------------------------------------------------
+rom_header_end:
+@---------------------------------------------------------------------------------
+	b	start_vector			@ This branch must be here for proper
+						@ positioning of the following header.
+
+	.GLOBAL	__boot_method, __slave_number
+@---------------------------------------------------------------------------------
+__boot_method:
+@---------------------------------------------------------------------------------
+	.byte   0				@ boot method (0=ROM boot, 3=Multiplay boot)
+@---------------------------------------------------------------------------------
+__slave_number:
+@---------------------------------------------------------------------------------
+	.byte   0				@ slave # (1=slave#1, 2=slave#2, 3=slave#3)
+
+	.byte   0 				@ reserved
+	.byte   0 				@ reserved
+	.word   0    				@ reserved
+	.word   0				@ reserved
+	.word   0    				@ reserved
+	.word   0    				@ reserved
+	.word   0    				@ reserved
+	.word   0    				@ reserved
+
+	.fill 4096,1,0 @ 4kb of filler so no useful code gets overwritten when flash bytes get copied over the top.
+    .global     start_vector
+    .align
+@---------------------------------------------------------------------------------
+start_vector:
+@---------------------------------------------------------------------------------
+
+@---------------------------------------------------------------------------------
+@ Enter Thumb mode
+@---------------------------------------------------------------------------------
+	add	r0, pc, #1
+	bx	r0
+
+	.thumb
+@ Turn off sound
+	ldr r1, =0x4000084
+	eor r0, r0, r0
+	strh r0, [r1]
+	
+@---------------------------------------------------------------------------------
+@ set heap end
+@---------------------------------------------------------------------------------
+	ldr	r1, =fake_heap_end
+	ldr	r0, =__eheap_end
+	str	r0, [r1]
+@---------------------------------------------------------------------------------
+@ global constructors
+@---------------------------------------------------------------------------------
+	ldr	r3, =__libc_init_array
+	push {lr}
+	bl	_blx_r3_stub
+@---------------------------------------------------------------------------------
+@ Jump to user code
+@---------------------------------------------------------------------------------
+	mov	r0, #0				@ int argc
+	mov	r1, #0				@ char	*argv[]
+	ldr	r3, =main
+	bl	_blx_r3_stub
+@; If we're here, turn the sound back on before we return
+	ldr r1, =0x4000084
+	mov r0, #0x8F
+	strh r0, [r1]
+@; Also turn interrupts back on
+	ldr r1, =0x4000208
+	mov r0, #1
+	str r0, [r1]
+	pop {pc}
+	
+@---------------------------------------------------------------------------------
+_blx_r3_stub:
+@---------------------------------------------------------------------------------
+	bx	r3
+
+	.align
+	.pool
+	.end
+
-- 
cgit 1.4.1