This patch changes the save file read and write routines of the game to only write to 512Kb of Flash memory instead of the 1Mb Flash memory a real cartridge would have. This is useful for emulators, flash cards, or devices (eg. the 3DS' AGB_FIRM) that cannot handle 1Mb Flash memory. This works because the majority of the original game's save file is used as a backup in case of corruption. By removing this and changing a few other small things, we can cleanly save to and load from a 512Kb save file.
In particular, this avoids the save corruption message after the title screen, allows the game to save correctly after the Elite 4, and allows the game to save data outside of the main save file (Hall of Fame and Battle Frontier Battle Recording) correctly.
It is not recommended to use this patch if the device or emulator you plan on using already supports 1Mb Flash memory correctly.
If you want to use an existing save file from the original game with this patch, or want to use a save file from this patch with the original game, run the included save conversion tool on the save file. It can convert saves both ways, though obviously the 1Mb to 512Kb direction is slightly lossy.
Pokémon Emerald Real 512Kb Flash Memory Patch v1.1 by AdmiralCurtiss
This patch changes the save file read and write routines of the game to only write to 512Kb of Flash memory instead of the 1Mb Flash memory a real cartridge would have. This is useful for emulators, flash cards, or devices (eg. the 3DS' AGB_FIRM) that cannot handle 1Mb Flash memory. This works because the majority of the original game's save file is used as a backup in case of corruption. By removing this and changing a few other small things, we can cleanly save to and load from a 512Kb save file.
If your game hangs while using this patch, please report where and how it hung.
It is not recommended to use this patch if the device or emulator you plan on using already supports 1Mb Flash memory correctly.
If you want to use an existing save file from the original game with this patch, or want to use a save file from this patch with the original game, run the included save conversion tool on the save file. It can convert saves both ways, though obviously the 1Mb -> 512Kb direction is slightly lossy.
Version 1.1 of this patch just improves compatibility. If the previous version worked for you already, there is no need to update.
This patch should be applied to "Pokemon - Emerald Version (USA, Europe)", filesize of 16777216 bytes, with the following checksums:
CRC32: 1F1C08FB
MD5: 605b89b67018abcea91e693a4dd25be3
SHA1: f3ae088181bf583e55daf962a92bb46f4f1d07b7
SHA256: a9dec84dfe7f62ab2220bafaef7479da0929d066ece16a6885f6226db19085af
SHA512: df06013ec9b09400bf233e110ee55b328fe8e39d28dbe9a671910397c99766e192ed38bfbd3d82fd7d9306b4f848f4265a86af1c073af0bb80b7f32bc0efae8e
It will create a patched version with the same filesize and following checksums:
CRC32: E7B0C2AC
MD5: 37b82fea4b90616203b59fbe33e36acd
SHA1: 2c49b5ea3ae018da7831e86ab053f2a1bd894ef6
SHA256: e65bcfaa63fee006dd8ab785c6d52de9a7681b89777f7d19d08b58979e7856eb
SHA512: 9ba097996e89016e06c7c92f4de0ea5d4cab22b1d9bd2d2f0cdf4d861e076368148800fc962b8832448cf5b6121e650a37d2b242754eb0e90cb619dac0abf16d
Technical details:
Pokémon Emerald's save file is split into 0x20 sectors of 0x1000 bytes each. They are laid out as follows:
Sectors 0x00-0x0D -> First save slot.
Sectors 0x0E-0x1B -> Second save slot.
Sectors 0x1C-0x1D -> Hall of Fame.
Sector 0x1E -> Japanese version seems to use this for the e-Card data for Trainer Hill. Unused in International versions?
Sector 0x1F -> Recorded battle from the Battle Frontier.
To fit that into a 512Kb save file, we need to get rid of 0x10 of those sectors. (or disable some features, but that's boring!) The obvious candidates are the second save slot and the unused Trainer Hill sector, but that leaves us one sector short! So what I did is reduce the Hall of Fame to a single sector, which means it can now only store 32 teams instead of 50.
The patched 512Kb sector map looks like this:
Sectors 0x00-0x0D -> Single save slot.
Sector 0x0E -> Hall of Fame.
Sector 0x0F -> Recorded battle from the Battle Frontier.
Getting this to work required several ASM hacks across various functions. The details can be found below. Additionally, I've changed the flash ID in the ROM from FLASH1M_V103 to FLASH512_V131, which is a late gen 512Kb GBA flash memory type, and modified a few constants to report the now-correct amount of memory and sectors.
I've also replaced the save bank switching function to do nothing if bank 0 (that is, one of the lower 0x10 sectors) is selected, but hang the game if bank 1 (that is, one of the higher 0x10 sectors that can only be found in 1Mb Flash memory). This is both a safety measure to reduce the chance of accidental save corruption, and a detection mechanism to find places that haven't been modified correctly for the 512Kb save size.
I've put a detailed list of my changes below. If you want to know the exact instruction changes, I recommend running a comparison between the original game and the patched one and looking at the changed bytes in a debugger such as no$gba.
The game stores the current number of saves in 0x03006200 and does a mod 2 on it to figure out if the current save is in slot 0 or 1. I modified those calculations to always select slot 0, even though I'm not entirely sure what some of the functions are used for.
1x @ 0x1527CC -> Always select the first save slot when determining the slot to write to.
1x @ 0x152AAC -> Always select the first save slot in this undetermined function.
1x @ 0x152C42 -> Always select the first save slot in this undetermined function.
1x @ 0x152CD0 -> Always select the first save slot in this undetermined function.
1x @ 0x152D68 -> Always select the first save slot in this undetermined function.
1x @ 0x152E1E -> Always select the first save slot in this undetermined function.
On game init, the game reads both save slots, determines which of them are valid and which one is newer then the other (via the save counter).
5x @ 0x152F7C -> The read of the second save slot has been replaced to always report that the second slot is the same validity as the first slot (so you still get correct system messages for corruption and stuff), and that the second slot is always one save older than the first slot, so the game will never pick it when deciding which slot to load.
2 * 48 bytes @ 0x152F88 -> The second save slot read code that is now skipped over has been repurposed as storage for two extra flash chip identification structures.
After the Elite 4, the game calls a special function to save the game and record the current team in the Hall of Fame. Normally, the Hall of Fame occupies two sectors of flash memory, 0x1C and 0x1D, and stores a maximum of 50 teams. With a 512K flash, we only have two spare sectors for extra data, and we still need a place to hold the recorded battles from the Battle Frontier, so I've modified the Hall of Fame reads and writes to only store 32 teams and only use one flash sector, 0x0E.
1x @ 0x153244, 2x @ 0x153256 -> When Hall of Fame data has to be erased for whatever reason, only erase sector 0xE.
1x @ 0x15327E -> Save the first half of the Hall of Fame to sector 0xE instead of 0x1C.
2x @ 0x153290 -> Remove the write to sector 0x1D. By limiting the Hall of Fame to 32 teams, the second half is always empty and doesn't need to be written.
1x @ 0x1532F8, 2x @ 0x15330A -> Again, when Hall of Fame data has to be erased for whatever reason, only erase sector 0xE.
1x @ 0x15352E -> Load the first half of the Hall of Fame from sector 0xE instead of 0x1C.
7x @ 0x153540 -> memset the second half of the Hall of Fame to 0 instead of reading it from sector 0x1D.
1x @ 0x15359A -> Always select the first save slot in this undetermined function.
The Battle Frontier battle recording usually saves to sector 0x1F. I remapped it to sector 0xF.
2x @ 0x1535EC -> This seems like a sanity check that determines if the requested sector is either 0x1E or 0x1F and returns from the function with failure if it's not. Since 0xF is outside that range, I modified the check to just check for sector 0xF.
2x @ 0x153644 -> Same sanity check.
1x @ 0x173922 -> Change the maximum amount of saved hall of fame records from 50 to 32.
1x @ 0x173930 -> Same as above.
1x @ 0x17438C -> Change the maximum amount of loaded hall of fame records from 50 to 32.
1x @ 0x17439A -> Same as above.
1x @ 0x1743C4 -> Same as above.
1x @ 0x1795D2 -> Modify function that (re?)initializes the entire save file to only write to 0x10 sectors we now have instead of the 0x20 sectors we had.
1x @ 0x18531C -> Write to sector 0xF instead of 0x1F for the Battle Frontier's battle recording.
1x @ 0x185A58 -> Read from sector 0xF instead of 0x1F for the Battle Frontier's battle recording.
In the Japanese version of the game, the Trainer Hill on Route 111 worked by scanning e-Cards that modified the trainer setups and floor layouts of the tower. The data for the currently scanned cards was stored in Flash sector 0x1E. In the International versions of the game, the Trainer Hill was modified to just contain a few pre-set layouts, so Flash sector 0x1E seems to be unused in those versions. Just in case, I've dummied out the leftover reads and writes to that sector.
2x @ 0x1D3A92 -> If, somehow, the Trainer Hill save write function is ever called, don't call the flash write function and just claim the write failed.
2x @ 0x1D3AE0 -> Likewise, if the Trainer Hill save read function is called, don't call the flash read and just claim the read failed. This function is actually still called when Trainer Hill starts in the English version, but doesn't seem to do anything.
Functions that directly access Flash memory:
11x @ 0x2E1860 -> Completely rewrite SwitchFlashBank. It now does nothing if bank 0 is selected, but hangs the game when a higher bank is selected, so we can figure out if we missed a place where the save file is accessed. This also serves as a protection for writes that would hit flash bank 1, since on devices/emulators that don't understand flash banks they would hit bank 0 and corrupt the data in it.
1x @ 0x2E1AF8, 1x @ 0x2E1AFC -> ReadFlash checks for a 1M Flash size before calling SwitchFlashBank. This check has been modified so that SwitchFlashBank is called for effectively all flash sizes.
1x @ 0x2E1BC0, 1x @ 0x2E1BC4 -> Same as above for VerifyFlashSector.
1x @ 0x2E1C4C, 1x @ 0x2E1C50 -> Same as above for VerifyFlashSectorNBytes.
Note that the Program and Erase functions always call SwitchFlashBank regardless of Flash size, probably because they're supposed to be flash type specific.
32 bit pointer @ 0x2E1D84 -> Change pointer to the location of the array of flash chip identification structures in IdentifyFlash to a new, bigger one.
5x @ 0x2E1D8C -> Change the loop that identifies the flash chip the hardware reported so it reports failure once it encounters a nullptr, rather than reporting a failure once it finds a flash chip structure with an ID of zero.
String @ 0x9A30C0 -> Change the Flash identification string to claim a 512K flash (I suspect these are unused on actual hardware, but some emulators use them to detect save type).
32 bit pointer @ 0x9A30DC -> Change pointer to the location of the array of flash chip identification structures in IdentifyFlash to a new, bigger one.
1 byte @ 0x9A3166, 1 byte @ 0x9A316E, 2 bytes @ 0x9A3178 -> Modify the fallback flash chip identification structure to another valid one.
24 bytes @ 0x9A31A0 -> This space is now used as a bigger array of pointers to flash chip identification structures, as the original was just 3 entries large. The original array at 0x9A30D0 is still there unmodified, but now unused, and could be repurposed if the need arises for a little bit of extra memory.
The six entries are, in order:
- Pointer to 0x8152F88, data for the SST 512K flash chip. (was instructions for read of second save slot)
- Pointer to 0x8152FB8, data for the Macronix 512K flash chip. (was instructions for read of second save slot)
- Pointer to 0x89A314C, data for the Panasonic 512K flash chip. (was fallback structure)
- Pointer to 0x89A311C, data for the Macronix 1M flash chip. (unchanged from original game)
- Pointer to 0x89A31C8, data for the Sanyo 1M flash chip. (effectively unchanged from original game)
- Null pointer to indicate end of array.
Reference the table at http://problemkaputt.de/gbatek.htm#gbacartbackupflashrom for the types of flash chips that exist.
Scholars of GBA hardware may notice that I did not list the Atmel 512K flash chip. Due to the odd properties of that chip compared to the other five, I have decided to not support it. Hopefully this is not a problem.
1 byte @ 0x9A31CC -> Modify pointer in flash chip identification structure at 0x89A31C8 from pointing at 0x9A31A0 to point an identical copy of that data at 0x9A3104, so we can reuse the memory at 0x9A31A0.
Feel free to port these modifications to other Pokémon GBA games and language versions. I suspect that FireRed and LeafGreen should be relatively simple by just finding the same functions I've modified here, but Ruby and Sapphire could be tricky due to the International e-Card support and Berry Patch. Also, the Japanese version of Emerald probably doesn't have enough storage for all save data in a 512Kb save, so you either have to get really creative or remove support for one of the Hall of Fame, Trainer Hill, or Battle Recordings.
No-Intro Name: Pokemon - Emerald Version (USA, Europe)
(No-Intro version 20130720-015858)
ROM/File SHA-1: F3AE088181BF583E55DAF962A92BB46F4F1D07B7
Patched ROM/File SHA-1: 2C49B5EA3AE018DA7831E86AB053F2A1BD894EF6