I recently bought a Mega Everdrive flash cartridge. This little piece of hardware allows you to play any ROM image on a real Mega Drive. I got it to start testing my own code on the real hardware.
Using emulators, I got to the point where I can render a sprite and move it around with the DPad:
Unfortunately, my code does not work on the real thing, and there are a myriad possibilities why not. So, it’s going to take quite some time until I fix it.
Nevertheless, this was a good opportunity to play some Mega Drive games I didn’t have as a child.
We didn’t buy Probotector because we had the Super NES version, which was equally awesome (maybe a bit better in terms of graphics), but a completely different game. So, during last week, we’ve been playing Probotector with my brother and it was fantastic, but unbelievably hard!
So maybe, just maybe, instead of fixing my own bugs, I’ll try to fix Konami’s! Today we’re going to ROM hack!
And first of all, I would like infinite continues. Those days are over… you know what days….
So, I fired up Regen (Mega Drive emulator), loaded the ROM and…. lost some “continues”. I dumped the RAM to “3.dmp”, after having 3 continues remaining. Then to “2.dmp”, and finally, just in case, to “1.dmp”.
I then wrote a C program to compare the RAM dumps, and, by filtering the valid addresses, only 0xFA44 remained! We didn’t even have to dump 1.dmp!
So, I went to this magic address and wrote 9 there, in Regen’s RAM editor, so we’d remain in the single digits, as the GUI might only print a single character for the continues.
Of course, rewriting a byte in RAM while playing a game is not that practical, so the next step was to somehow alter the code that manipulates this variable.
There are about 35 occurrences of “FA44” in the ROM, I can’t think of a better way but examine them all. Regen claims to have memory access breakpoints but they don’t seem to work.
So, here is the first occurrence:
This is clearly a variable initialization. Our continue counter gets set to ‘5’ when a new game is about to start! Let’s set it to 10 as a little test!
And of course it works!
And I might just be lucky, but the second occurrence subtracts 1 from the continue counter! Which means it is the code that gets executed when you lose a continue, so your counter gets decremented:
So I put a couple of ‘nop’ instructions there (4E71). I now had ‘A’ continues! It never decremented them from the ’10’ that I initialized the variable!
It is noteworthy that the game prints single numeric characters in hexadecimal. I tried to make it print 16, which is after ‘F’, so I expected a ‘G’, but it gave me a ‘0’ instead. So the routine that prints the continue counter in the GUI pays attention to just the 4 low bits in the counter variable. Maybe they tested the game with more continues internally but they wouldn’t waste a two-digit renderer for this. Everyone in Konami must have known what ‘A’ continues means!
So that was a quick win, but I wasn’t satisfied just yet! I then wanted to change some text in the game so that I could convince my brother that I was the one who did it. So I decided to replace the “1P SCORE” and “2P SCORE” labels in the rackup screen with our names.
Which was not as easy as I thought! There were no ASCII occurrences of GAME OVER 1P SCORE or 2P SCORE anywhere in the ROM.
By now, we know that characters from 0 to 9 are represented by their integer value. Also that ‘A’ is 10 (its hex value), so maybe the rest of the letters would follow in sequence.
I did some searches in the hex editor, but with no luck.
The next thing I tried was to write a C program that would try and guess the encoding, under the assumption that the encoding has the letters in sequence. So I searched for sequential bytes in the ROM that had the differences the letters ‘S’, ‘C’, ‘O’, ‘R’ had, in sequence.
Which yielded some occurrences:
The first 3 occurrences are identical and must be the actual encoding. Furthermore, the first two are close enough together. They must live in the same text buffer, so they must be the labels I’m after.
After knowing the offset from ASCII, it was easy to change the text with my own:
So, in short…
Here’s how to get yourself infinite continues in Probotector:
- FA44 is the address in RAM where the continue counter variable lives.
- C3AA is the ROM offset that stores a 16 bit word with the default continue count (5) you get for a new game).
- C81E is the offset in ROM containing the decrement command (4 bytes), which you deactivate by putting 2 nop instructions of 2 bytes each: 4E714E71.
- ROM offset 9821C contains the string “1P SCORE”.
- ROM offset 98227 contains the string “2P SCORE”.
- You add 0x39 to the ASCII value to produce a character in the game’s encoding.
- Mostly, because I noticed whitespace is represented by zero. There may be other exceptions.
- You also need to recalculate the ROM’s checksum so that it can run on a real console. There may be other tools out there but I did it with my own (“romtool”). You can find it in the downloads below.
Here you can download C source code for the tools I used:
- Analyzer 1 (for finding valid addresses for the variable).
- Analyzer 2 (for finding where the text might be)
- Romtool (for signing/recalculating the ROM’s checksum).