Toy Project: Fortune Telling from Super Mario Bros. DX
I recently moved out of my country and I’ve been staying at a hotel and turns out I got bored so I started just trying random stuff to try to have fun while I get my own place.
So I remembered… back in the day, I don’t remember how, because I never actually had a gameboy of my own, but I remember I managed to play Super Mario Bros. DX for the Gameboy Color. I remember that port, a pretty good one at that, because of the fact that not only does it recreate Super Mario Bros. on the Gameboy Color really well, no matter the small screen size of the system itself which leaves a lot to desire to the NES’s resolution, but because there’s a massive amount of extra content added to the original SMB trip.
There’s time trials, You vs. Boo mode, multiplayer modes, hidden red coins, hidden yoshi eggs, a new map screen and a lot of other random smaller stuff, such as Gameboy printer stickers (in case you have one of those laying around lmao), a day planner in case you’re still rocking your gameboy color as a PDA 20 years later, and even a fortune telling minigame. It’s awesome!
The fortune telling minigame
You see, for some strange reason I have this weird kinship for the fortune telling minigame… I always liked the fact that the little messages you get are so satisfying to read, the little jingle that sounds before the message is displayed sounds so exciting, and the fact that you can even get extra lives for the main platforming game if you have good luck and you get a nice fortune :)
So here’s the challenge:
I want to recreate, for fun, the fortune telling minigame in a web application.
Right off from the bat, this is what I’m gonna need:
- A dump of all the fortune messages that Nintendo put in the game.
- A dump of all the graphics and sound used in the fortune telling minigame.
- A database schema to store traffic data and fortune content data, so people can login, see and share fortunes, ideally with facebook.
- An admin interface to add or edit the fortunes on my database easily (nice to have, and it involves making a User model too. I can do with SQL only though)
- A web server that displays the actual frontend for the fortune telling minigame, does the “random shuffling” process, handles user traffic, then shows the users’ fortune as an actual document that they can see on a browser (Ideally I’d expose it as a RESTful API too, but I don’t want to do that at the moment, and for the sake of having a controllable scope for this project, I’ll focus on doing everything as a rails web application)
TL;DR: Here’s is the finished product: Have fun
This is gonna be fun :)
Dumping the fortunes
First of all, unfortunately I didn’t manage to find a complete text dump of all the available text data in SMBDX online. Bummer! I was sorta anticipating this though. I guess I have to dump the text myself.
How to dump the text off a gameboy color game though? I was puzzled by this… however, I did notice something, the font used in the fortune telling minigame is similar to the font used in other gameboy color games, like the Legend of Zeldas available for it.
For example, compare both uppercase “W"s in the words "Words and "What”, respectively. It’s the same typography.
So I figured out that there ought to be a parallel there. Maybe if somebody has already dumped the text off of, say, The Legend of Zelda: Link’s Awakening DX, and shared their techniques online, I could use something similar to dump the text off of SMBDX into a text file.
Turns out that there’s many text dumps of all the zelda games on Gameboy Color, and off of the notes in those, I found out that there’s many games that use ASCII encoding for their script. ZeldaDX is one of them. You can theoretically just open a game’s files on a text editor with ASCII encoding and copy the script off of that
What does this mean? It means that the ROM image of the game, which is the actual collection of all the data that composes the game and that got shipped in carts in the original release of ZeldaDX (or any game on cart, ever, for that matter), has the actual text content itself encoded with the exact same hexadecimal values that the American Standard Code for Information Interchange uses:
From here onwards things are about to get more and more technical so buckle up and enjoy the ride. If you don’t yet understand terms such as “Encoding”, “VRAM”, why RAM is different than ROM in the GBC or any computer’s architecture, or what “Hexadecimal” means, feel free to stop here and just look at the results, or shoot me a line and we can chat about it over beers :)
So let’s fire up good old trusty XVI32 and look at the ZeldaDX ROM image in it using ASCII and lo and behold, what happens when we try to search for a string of text which we already know is in the game, for example, “Marin”
There it is :)
You can also use a decent text editor to open the ROM and force “Western” (or Windows-1252) encoding when opening the ROM.
So, I procured a ROM image of SMBDX off from the actual retail copy I have and that I purchased with my own money, like you also should. Then, after dumping the ROM image, from here it should be a cakewalk, right? Open the SMBDX ROM on a hex-editor with ASCII encoding and presto, right?
Well, not quite.
Unfortunately, Mario DX, for some bizarre reason, doesn’t use ASCII encoding for its text. Strange.
My hypotesis at this point is that, well the fortunes data must be there in some kind of sequential order still, because the fortune lines are data that must necessarily be persisted in order somewhere in the ROM image to be presented to the users.
Let’s pretend that we have a false encoding for a second, where the letters of the alphabet are represented by the following key:
A => A0
B => A1
C => A2
D => A3
E => A4
F => A5
G => A6
H => A7
I => A8
J => A9
K => AA
L => AB
M => AC
N => AD
O => AE
P => AF
Q => B0
R => B1
S => B2
T => B3
U => B4
V => B5
W => B6
X => B7
Y => B8
Z => B9
a => BA
b => BB
c => BC
d => BD
e => BE
f => BF
g => C0
h => C1
i => C2
j => C3
k => C4
l => C5
m => C6
n => C7
o => C8
p => C9
q => CA
r => CB
s => CC
t => CD
u => CE
v => CF
w => D0
x => D1
y => D2
z => D3
' ' => D4
Based on that assumption, and unless SMBDX is doing some shifty on-the-fly uncompression thing to the ROM image (!!!), based on the fact that we already know that there’s a message in the game that reads: “With a Fire Flower”, and that the data has to necessarily be in that order somewhere in the rom, we should expect to find a sequence similar to the following data somewhere in the ROM:
B7 C2 CD C1 D4 BA D4 A5 C2 CB BE D4 A5 C5 C8 D0 BE CB
W i t h a F i r e F l o w e r
The problem is, though, that we don’t know for certain that the game stores its alphabet starting from uppercase A, with value A0… it could be any other value for all we know.
So to try to get a bit more clarity at this point we can use a hearty good old emulation debugger. If we fire up the ROM on a good-ass old emulator (in this case, I’m using nocashgmb), you can see the state of the Gameboy Color’s RAM on runtime. Nice. We can trigger a fortune inside the fortune telling game, then inspect the memory to find out what data is actually getting read and from where:
If we take a look at the first uppercase W when we triggered a fortune it shows that the data comes from address 99C1 in the Gameboy Color RAM (look under Map Address)
So let’s use a hex-editor to Goto address 99C1
and we find from offset 99C0
8D D6 E2 ED E1 1D DA 1D C5 E2 EB DE 1D C5 E5 E8 F0 DE EB
Look at the following screenshot:
Notice something interesting here? Probably we got the jackpot ;)
What are the giveaways?:
- Notice how there’s a lot of instances on this string of hex values of the value
1D
. Why could that be? Could it be some kind of separator? Or delineator? What about… if it was a representation for typographical space? - Notice how the value
C5
appears two times. The characterF
also appears twice on the phrase, on “Fire” and “Flower”. Coincidence? I think not.
It seems to me that the string of hex values we found translates very well to:
8D D6 E2 ED E1 1D DA 1D C5 E2 EB DE 1D C5 E5 E8 F0 DE EB
W i t h a F i r e F l o w e r
Some other thing that I noticed is that the lowercase values seem to be incremental and alphabetically ordered (notice at the values representing ‘l’ and 'o’ and how they seem to be in sequence when you interpolate ’m’ and 'n’ between them, exactly in order going from E5
to E8
), yet the uppercase values don’t seem to follow the same order. I may be mistaken though.
So perhaps we can build a translation table from Mario-encoding to ASCII by deducing it from the debugger, then input it into a hex editor that can do custom translation tables, and hopefully try to deduct the rest of the code from there. We could throw out some ruby for this:
#!/bin/ruby
LOWERCASE = { }
ASCII_LETTER_a_OFFSET = 97
offset = 0xDA
(0..25).each do |i|
LOWERCASE[($ASCII_LETTER_a_OFFSET + i).ord.chr] = offset+i
end
$LOWERCASE.each do |k, v|
puts "#{k} => #{v.to_s(16).upcase}"
end
Which produces this table:
a => DA
b => DB
c => DC
d => DD
e => DE
f => DF
g => E0
h => E1
i => E2
j => E3
k => E4
l => E5
m => E6
n => E7
o => E8
p => E9
q => EA
r => EB
s => EC
t => ED
u => EE
v => EF
w => F0
x => F1
y => F2
z => F3
So far so good. Notice how it perfectly syncs with the example fortune.
Let’s try to see how this plays out in a hex editor before we move onto the uppercase characters. Let’s open the Rom on XVI32.
A quick tangent: of particular interest, notice how there’s a string of perfectly good ASCII text at the beginning of the ROM file, showing the game’s title and key. Longtime experience with Wii hacking and Nintendo DS and 3DS fidgetry has made me understand that since a forever, Nintendo has always liked to assign a unique alphanumeric code to each and every title that releases on their console. They call that code the TITLEID
. I can see that SMBDX is AHYE
. A quick look at the game’s product ID on a site like GameFAQs seems to confirm that:
So there’s a feature on XVI32 called character conversion that one can use to make XVI32 follow a custom encoding instead of ASCII when trying to visualize the data inside a file. This fits perfectly with our use case that we want to see the text in Mario-Encoding, which we have already deduced. Let’s try to input our conversion table there:
Turns out XVI32 has a format that it uses to exchange conversion table data called XCT. This is a simple format which can be created from plaintext consisting of n different lines denoting a semicolon-separated pair of translation values, one per each line. We can adapt our ruby code to produce an xct table very easily, associating our values on the reverse order, so to speak.
For example, we have determined that 'a’ (ASCII hex 61) corresponds to Mario encoding 'DA’, our first translation pair in the xct table would read: DA;61
Let’s use the code that we used before and modify it a bit so it dumps an xct table for us:
#!/bin/ruby
LOWERCASE = { }
ASCII_LETTER_a_OFFSET = 0x61
offset = 0xDA
(0..25).each do |i|
LOWERCASE[($ASCII_LETTER_a_OFFSET + i).ord] = offset+i
end
$LOWERCASE.each do |k, v|
puts "#{v.to_s(16).upcase};#{k.to_s(16).upcase}"
end
The resulting xct table, so you don’t have to run this script, is here.
Which we can then load into XVI32 for conversion… and once we do that, lo-and-behold:
And from here it should be pretty easy to decode the uppercase alphabet that Nintendo used for this game:
#!/bin/ruby
UPPERCASE = { }
ASCII_LETTER_A_OFFSET = 0x41
offset = 0xC0
(0..25).each do |i|
UPPERCASE[($ASCII_LETTER_A_OFFSET + i).ord] = offset+i
end
$UPPERCASE.each do |k, v|
puts "#{v.to_s(16).upcase};#{k.to_s(16).upcase}"
end
Yes, I know, I didn’t bother to modify the original code to integrate both cases and just ran one for uppercase alphabet, and another one for lowercase. I was lazy xD.
The final fortunes content, with a bit of formatting and retouching, is here.
Getting the GFX and the Sound
Fortunately it was really easy to find the graphics for the fortune telling online, since sprite ripping is fairly easy to do and has been commonplace since the dawn of the commercial internet and emulation (credits to spriters-resource.com).
The sounds weren’t as easy to find since the Result, option select and option opening sounds weren’t as interesting as to warrant somebody posting them online in wav format for other people to download. I didn’t manage to find them, but not to worry, this is a simple sound capture job. VBA-m, a popular gameboy emulator, has screen and sound recording features. After sound capture, it´s a simple edition job. Audacity is a great tool for this.
But anyway, I had to rip the sound effects that come after you get your fortune, because those weren’t available anywhere. That was a simple audio capture and then editing with audacity. My audio collection is available here.
Developing the app
Now that we have this, we can jump into development of the actual app! Oh joy!
I decided to architecture the application as a standard server request-and-response cycle. I kind of had to take a bit of time to think about this. Do I develop this in the traditional way, using a server and a database with a request-response loop, or do I do it on the clientside? After all, doing everything clientside is pretty hot these days… But the argument that having a relational database available to add extra fortune messages is too compelling to ignore, and backend languages are still in my opinion more amicable to that purpose than just doing everything on frontend. I will eventually make this app more robust clientside in a version 2.0.
These are the design tenets:
A web server displays the fortune selection page, which should have the necessary design to play audio and display animated content (without plugins, mind you! Modern Web is awesome!).
On click, the frontend performs an animation, then the user gets redirected. The user’s traffic is intercepted by the server, which makes a dice throw and shows the user’s result, with the corresponding audio and graphics according to the result.
The pages in the site should be responsive so it works well on different viewports, even smaller screens. The web server should be able to connect to a relational database to retrieve the fortunes.
The server should be able to retrieve the fortune text off of a relational database.
So I decided to go with a simple Ruby stack for this, with the Sinatra framework for coding the server, PostgreSQL on the database, heroku as the platform (I like PaaS and heroku has amazing, free support for PostgreSQL out of the box).
On the frontend I use some small parts of CSS for webkit animations, transitions to animate all the assets I got off the ROM, and rotations/translations to position the clickable elements of the page in a circle formation, and the Javascript audio API to play sound.
A snag that I ran into quickly is that I need to add another media query to order the elements in another way on very small screens (the responsiveness is achieved purely through media-queries). Plus the animation is fidgety and not compatible in all browsers because I’m tweening the content
property of an img
element in the page for animation and this doesn’t play well with browsers, or rather, doesn’t produce smooth-enough animation. I think that I will look into canvas
in version 2.0 of the app to handle all the gfx.
And finally, I needed a way to populate my database quickly with all the text I dumped from the ROM. I made a quick sql script for that, which I placed on the project’s repository. Vim proved very good for this, parting from the previous txt file with the raw Fortune strings:
CREATE TABLE Fortunes(type_id integer NOT NULL, content varchar(40), created_at timestamp DEFAULT current_timestamp, updated_at timestamp DEFAULT current_timestamp);
INSERT INTO Fortunes
VALUES (1, 'With a Fire Flower you''ll beat Bowser.'),
(1, 'Fortune awaits you in the clouds.'),
(1, 'Fortune will lead you to Yoshi.'),
(1, 'Look below to find what you seek.'),
(1, 'Good friends bear good news.'),
(1, 'Southerly winds bring sunny skies.'),
(1, 'Kindness given is returned tenfold.'),
(1, 'Good news is due from a loved one.'),
(1, 'Good things come from hard work.'),
(1, 'Soothing music soothes the soul.'),
(1, 'Keep a good grasp Fortune will last.'),
(1, 'Worries naturally melt away.'),
(2, 'Stomp a shell and scores will swell.'),
(2, 'Pipe cleaning reaps rewards.'),
(2, 'An eye to the sky reveals Red Coins.'),
(2, 'Keep your head up Find what you seek.'),
(2, 'A party of three brings good luck.'),
(2, 'Luck arrives with the easterly wind.'),
(2, 'Express yourself with written word.'),
(2, 'Seek out the one who thinks of you.'),
(2, 'Active bodies breed active minds.'),
(2, 'To create music is to create joy.'),
(2, 'You shall achieve great victories.'),
(3, 'Enemies lurk in watery depths.'),
(3, 'Fortune is hidden in bricks unbroken.'),
(3, 'Today is your day to win the race.'),
(3, 'Boxes may contain clues to the quest.'),
(3, 'Victory is yours in the coming race.'),
(3, 'Beware of winds from the west.'),
(3, 'Feelings shared will be understood.'),
(3, 'He who thinks of you is beside you.'),
(3, 'Imagination is a wonderful toy.'),
(3, 'Old tunes bring new fortune.'),
(3, 'Trade high scores to set new goals.'),
(4, 'Stomping on spikes leads to sore feet.'),
(4, 'Careless footing causes one to fall.'),
(4, 'Kicked shells may bounce back.'),
(4, 'What you seek is right beside you.'),
(4, 'Sincere apologies renew friendships.'),
(4, 'Storms ride in on northerly winds.'),
(4, 'You will not find true love this day.'),
(4, 'A VS Mode victory is not your fate.'),
(4, 'Not all pain leads to gain.'),
(4, 'Happy songs lift sad moods.'),
(4, 'The Warp Zone speeds success.'),
(4, 'Only a Challenge clears the mind.'),
(5, 'Bowser''s breath engulfs the future.'),
(5, 'Flying Koopas cut short success.'),
(5, 'Dont count Yoshis before they hatch.'),
(5, 'Leave impossible dreams to dreamers.'),
(5, 'Seek answers in a friends advice.'),
(5, 'Beware of pointed enemies.'),
(5, 'Change old habits Yield new success.'),
(5, 'Victory in a race may wash pain away.'),
(5, 'Excess is a powerful enemy.'),
(5, 'Favorite tunes never fade.'),
(5, 'Now is the time for patience.'),
(5, 'Help all in need, not just a friend.');
If you’re using heroku to mount the app, it’s very easy to populate heroku’s postgres database through the command line by using unix redirection from the file to heroku pg:sql
with the following command:
heroku pg:psql --app "the_name_of_your_app_here" < scripts/create_databases.sql
A quick note: a fast look at the main executable for the server, fortune.rb
, you can notice how I’m handling connections to the database. It is a good practice to store connection credentials for your database on an Environment Variable in the server, from where I read at the moment of connection in runtime, else I default to development-credentials, which you should replace by your own if you try to run this on your machine.
Final Words
So this is it! Hope you enjoyed the ride and remember that you can check out my project and deploy it for yourself from my github [profile] (https://github.com/nullset2/smbdxfortunes).
Have fun and may you have great luck in your future! :)