Legitimizing your counterfeit Zelda save

I've been working on filling the gaps in my collection of the Legend of Zelda franchise, my favorite video game series. One of the gaps I had was The Legend of Zelda: The Minish Cap for the Game Boy Advance. Since the game came out in 2005, I was stuck with getting a used copy if I wanted to play it on the GBA. Unfortunately, little did I know, Minish Cap is one of the more frequently counterfeited GBA games. Also unfortunately for me, I didn't even realize I had purchased a counterfeit until I had gotten around 90% of the way through the game...whoops.

As soon as I realized my mistake, I read up on avoiding fake GBA carts and ordered a new copy. Unfortunately, the damage was done. I was already mostly done with the game, and some parts of the game were not completable due to either a bad version of the game or some sort of copy protection.

Cart comparison - front It is surprisingly obvious seeing them side-by-side. Especially the quality of the printing on the label. Additionally, the ESRB and Nintendo logos are wrong. It is also missing the imprinted numbers. Fake on the left, in case you couldn't tell.

I knew when I ordered the new cart that I would try to transfer the save (using homebrew on a Nintendo DS.) The most popular homebrew for doing this is Rudolph's GBA Backup Tool. While it worked fine for pulling the ROM images (for analysis) and the legit cart's safe, when I pulled the save from the pirate cart: I got 8 KB of 0xFF. This was disappointing, but I wasn't going to give up there.

Impossible to reach heart piece I mean, the pirated cart had an impossible-to-reach heart piece in it! You can't just leave those behind!

Diagnosing the undownloadable save

The truth lies within the cart:

Cart comparison - inside

As before, the pirate cart is on the left. A battery hints at an SRAM save backup, and this is confirmed by reading the chip model information below. If you look at the real cart, it uses an EEPROM. Unfortunately, the only (reliable) way to detect cart backup media is to look for magic strings placed by Nintendo's build tools. My guess is Rudolph's tool uses this technique, and incorrectly identifies the cart as an EEPROM cart.

Pirate chip info

  • The bigger chip in the pirate cart is a "FUJ1TSU DEVICES INC MSP55LV128T" (Seriously, it's a knockoff of a Fujitsu chip.)
    • I had trouble getting info on this chip, but it is most definitely the ROM chip.
    • Here are some Russian dudes talking about it: http://www.ezoflash.com/forum/viewtopic.php?f=4&t=9673
    • Google Translate suggests they know it is 16 MB but can't figure out the pinout.
    • 16 MB adds up since the 128 in the model number probably means 128 MBit. (Minish Cap is 16 MB too.)
  • Smaller chip under battery is CY62128EV30
    • 1 MBit static ram (128 KB)
    • Minish cap's save is only 8 KB, so this is a bit of a waste.

EEPROM magic string in ROM dump

Those with sharp eyes will also notice in the above screenshot that the pirate cart is GBAZELDA_MC_BZMP, which is the PAL region (European) version of the game. I live in the US though and should (as the cart has on its label) GBAZELDA_MC_BZME.

When doing some analysis on the ROM dumps, it appears that it definitely the PAL version of the game. Additionally, the entire ROM is doubled. I imagine this is either an error in Rudolph's tool or the pirate cart's design (the real cart didn't do this.) Other than that, there are two other small changes:

ROM dump differences

I didn't have the patience to disassemble this (don't blame me, blame THUMB mode), but I imagine these are save file routines. (Patching from EEPROM to SRAM is actually very automated from the little I know. There are tools out there from when the reloadable GBA carts didn't have EEPROM chips or support emulating it.)

So at this point I am starting to worry. I tried a handful of other save dumpers with similar results. Additionally, even if I got the save down, there's no telling if it is going to work because of the separate regions. (I could've tried loading a pirate save from an emulator on the legit cart or with the legit rom in an emulator, but I decided against this since the following was more fun.)

The Solution

So what would any sensible* person do in this situation? Give up. Make their own save file dumper from scratch. (There is actually an open source save downloader on GitHub, but for various reasons I ignored it.)
*For various definitions of sensible.

So with reading my trusty hardware reference and reinstalling the ever-reliable devkitPro I set out on my quest.

I started with a simple Nintendo DS tool that could print out the contents of a GBA save file. Basically, a simple hex viewer. I'm lucky that the pirate cart was SDRAM instead of EEPROM rather than the other way around, because reading from SRAM is dead simple while the EEPROM...not so much (from what I've gathered.)

All you have to do is clear bit 7 of the EXMEMCNT register to give access to the ARM9 processor to the GBA slot. (The Nintendo DS has two processors, an ARM9 and an ARM7.) Then, the SRAM can be accessed using 8 bit reads in the address range 0x0A000000 - 0x0A010000, which is the "GBA Slot RAM" (previously known as the Game Pak SRAM at 0x0E000000 in the GBA.)

Clearing bit 7 in EXMEMCNT is pretty important, because if you forget you will get all 0x00. Which is not awesome. (This is why the app has the EXMEMCNT displayed and a message stating whether the entire save region is 0x00 or 0xFF...oops.)

PathogenBackup picture

As you can see, the app properly detects the game's name, and can read its save data. It is backwards, but you can also see that the save data begins with the text "ZELDA". I'll talk a little bit more about this later.

PathogenBackup picture 2

You can also see my nickname "Path" in there (also backwards.)

At this point we know we can definitely read the save data, so now we just have to save it to the flash cart. I was actually pretty surprised I didn't need to mess with waitstates or anything like that.

I did end up copying the data "by hand" 1 byte at a time into a temporary buffer since GBATEK states that the SRAM only has an 8 bit bus, and I figured memcpy and fprintf might not use 8-bit reads in devkitPro. (If I was feeling hard core, I would've done it in assembly to be extra sure.)

Shuffled save data

I actually ended up finding this wasn't completely necessary, but I thought I'd cover it anyway.

For reasons not completely clear to me, the save data in EEPROM games is partially backwards. To be more exact, each chunk of 64 bits is reversed. The 64 bits makes sense since you access the EEPROM 64 bits at a time, but why the reverse? I am thinking it might have to do with how the generic Nintendo EEPROM code works. (It also turns out that Rudolph's GBA Backup Tool ships with a VBS script to fix it for you.)

If you compare the two raw save files from NO$GBA, you'll see that the real version (bottom) is linear, but the pirated cart is reversed.

Shuffled save files screenshot.

I imagine this is because the Nintendo save routine is still writing everything backwards, but NO$GBA isn't reversing it for the raw save because its SRAM and not EEPROM. Or maybe EEPROM is naturally recorded backwards. Either way, I wanted to be able to test my dump save in an emulator before loading it on the real legitimate cart. So as a part of this project, I also wrote a small C# tool to fix the orderings of the dumped save files.

Small side notes about the saves

This is a bit out of place, but I found it interesting so I wanted to mention it. From a very light look at the dumped saves, it looks like Nintendo/Capcom are storing each save file twice. I imagine this is to ensure one version of the save file is still available if one gets corrupted by the player removing the cart or old hardware. You can corrupt one, but not the other, and still get a proper save in game.

Of course, if you mess with both of them, you get a dreaded error that I imagine most players will never see.

The red message box of sadness.

So there's definitely some crazy CRC action going on. Also interesting, the game takes a noticeably longer period of time at the start screen when the save file is corrupted. I guess it is either a pretty thorough integrity check, or maybe there's some error correction going on.

Loading the pirate save into a legit game

At this point, if I loaded the pirate save into the legit game in an emulator, I only got a blank save screen. (Interestingly, no error on the screen.) So I had to look at what was different between the two games' saves (and hope it wasn't everything.)

Diff between two saves

There's only a few bytes different here and there, and they are all in that little header section. One difference in particular stands out, the difference in the game title string at the start of the save: "ZELDA 3" vs "ZELDA 5". I imagine this is some sort of internal version number. All I did was change the 3 to a 5, and load it in the English version of the game and...

It works!

We have savefiles! After this, I tried to load it on the real cart. Since I knew it worked with the real cart, I just used Rudolph's GBA Backup Tool again. Since the save it dumped from my real cart was shuffled, I took this as a hint to give it a shuffled version (but still with the corrected version number.)

It works in real life too!

Source code

Even though it isn't much, I've open sourced the code for both my little NDS SRAM save viewer/dumper as well as my little C# tool for un/reshuffling save files. You can find the source code for both on GitHub under PathogenDavid/PathogenBackup. Both projects are contained in a Visual Studio Solution. The NDS project is an NMake project, so if you don't have Visual Studio you can just run the makefile as you normally would (provided that you have devkitPro installed.) I imagine both projects should build/run fine on Linux and Mac, but I have not tested this.

This is the longest I've ever spent on a piece of heart side quest ever.

Post cover is a parody of the Nintendo Anti-Piracy website header. Nintendo and Mario are trademarks of Nintendo Ltd.