For the past two months, I’ve been streaming a variety of games every weekday to gather screenshots, info, and more for all the things I write about. It’s hard to analyze a game’s localization without playing the original version and its localized version, though, and playing the same game twice takes twice as much time and work. So, to save time, I developed some “stream magic” that displays a game’s Japanese text while I play the English version on stream.
Stream Magic Examples
Here’s an example from our Breath of Fire II streams:
And an example from our A Link to the Past streams:
In all, it’s super-useful for comparing lines of text side-by-side, in context, and live for others to discuss!
How It Works
I regularly get questions on how this all works, but it’s pretty complicated to explain quickly. I’ll cover the details in a bit, but the simple answer is this: it’s a bunch of custom, experimental programming built on a shaky foundation and is barely held together by a bunch of virtual string and tape. It also has to be reprogrammed to work with each game, and that reprogramming is tough.
There’s been some interest from language students – a tool like this would be a really nice and handy learning tool. As it is right now, though, the program isn’t ready for public use. There are potential security concerns, it crashes a lot, and it could possibly ruin your OS. I’ll explain more below, but this is why I haven’t released a version for others yet.
The Technical Concept
Let’s take a look at how the Breath of Fire II text displayer came about. The core idea here is actually pretty simple:
- When an emulator is running a game, detect when a line of text is being displayed on the screen
- Identify the current line of text being displayed
- Take that identifier, load up the original, untranslated game, and then locate the equivalent line in the game’s data
- Extract the untranslated line’s data, convert whatever needs to be converted, then display it on screen
In all, it’s not a complicated process – Steps #1, #3, and #4 are pretty much straight out of ROM Hacking 101. Step #2 is the trouble spot, though – we need a way to identify the line and then communicate that info to our text displayer program. Ideally, whenever an English line is being displayed, its ID number would be sitting in RAM somewhere. Then we’d just need to find it and check it when needed. To accomplish this, we need a way to read the game’s RAM and CPU status from outside the game.
There are a couple options for reading an emulator’s info live. First, I looked into Super NES emulators that include LUA scripting capabilities, as that would make things infinitely easier and more accurate. I checked all kinds of emulators, including different versions of the same emulator, but was surprised by how poor the documentation was and/or how poor the support was. I even looked at non-English emulators I’d never heard of before. Sadly, this turned out to be a dead-end for me, at least for Super NES stuff. NES support looks much more promising, though.
Another option I investigated was taking an open source emulator and modifying it as I needed. This would’ve been great, but I’ve never, ever had a good experience with this sort of thing. This would’ve been a nice solution too, but in the end I dropped the idea. I did use some of what I learned for later work, however.
Eventually I decided to go with a far less accurate solution, based on things I made a while back for my old personal streams. Some examples:
Ninja Gaiden Data Overlay
I once made some stream overlay stuff that read an NES emulator’s memory and used it to create an automatic death counter, game play timer, and death taunter. I also added some bells and whistles, like showing what items are in each item container:
Final Fantasy VI T-Edition Data Overlay
I once created a stream overlay that showed enemy stats. Since I was playing a Japanese ROM hack, I also had the overlay display simple terms in English, like spell and enemy names.
These are very simple things, though; I never intended for my old program to handle more complex data. But I decided to see how far I could take it.
Messing with other programs’ memory is obviously a security and stability concern, which is one reason why I’m hesitant to release these tools just yet.
Identifying a Line
The key to everything was finding the current line’s unique ID. Ideally, the game might have a line ID number stored in RAM somewhere. Unfortunately, after a lot of reverse engineering, I learned that Breath of Fire II’s text system isn’t so generous. As a result, I spent a day or so poking at the game’s code to get as close to a line ID as I could. I wasn’t familiar with SNES programming code, but my time with MOTHER 3’s translation helped a lot. I also needed to learn the English ROM’s layout, particularly the script’s location. And then I needed to do all of this over again, but with the Japanese game.
There’s a major flaw with my approach, though: the game’s RAM changes thousands of times a second, but my program can only check it a few times a second. That means things in RAM can change between checks, which makes things even tougher and more unreliable. If you’ve ever heard of ROM hacking before, this is like that, but imagine that the ROM is changing thousands of times a second. I call it “RAM hacking”.
|Watching RAM change as a game runs|
Eventually, I managed to locate a “quasi ID” number that I could depend on some of the time, but not all of the time. Then I had to figure out what to do when I couldn’t trust the number. And then I had to repeat that a few more times.
|A look at some disassembled assembly code with some of my notes added|
After enough of this, I could tell which line of text was being printed most of the time. And sometimes, even when I would get an ID number I could rely on, the data would change before I could use it. My quick and dirty fix to this was to have the program “refresh” everything and try it again when a certain button was pressed. It’s basically the equivalent of kicking the computer to make it work right when it messes up.
|When the program messes up, it can be pretty funny|
In all, this was a very messy process and solution for Breath of Fire II. Luckily, it’s been a bit cleaner with A Link to the Past.
With the line ID in hand, the next step is to load the original, untranslated game and locate the equivalent line in it. This is standard ROM hacking, so it wasn’t too tough with Breath of Fire II. Once the line is located in the game, it’s time to read that data.
Older games rarely used a standard text format, so I needed to convert all of the Japanese characters into a modern, standard format that computers can use. This is also a standard part of ROM hacking, so I looked online to see if Breath of Fire II’s Japanese text format had already been documented somewhere. Luckily, I found the needed info on a Japanese site, so I plopped it into my program. But I soon learned that you can’t 100% rely on outside sources – the info on the Japanese site had many mistakes. So I then had to go back and decipher the game’s Japanese text format myself from scratch. I’ve posted my documentation here.
|A look at the data-to-text conversion table|
There’s another hurdle in the process, though: game scripts are filled with text data, but they’re also filled with non-text information known as “control codes“. I found rough documentation online about Breath of Fire II’s Japanese control codes too, but quickly learned that it was incomplete and wrong in places, so I had to document them all from scratch as well. Basically, by the end of all this, I had re-created Breath of Fire II’s text engine in my own program.
Now that I could locate an equivalent Japanese line, read it, and convert it, all that remained was to display it on the screen. I used to program many games in XNA/MonoGame years ago, so I was most comfortable with using that to draw things to a window. But here was another problem: I had no experience with displaying Japanese text using XNA/MonoGame. My stream overlay design only has room for a small window pane, so formatting/wrapping the text as needed was also important. I wasn’t sure what to do at this point, but then I realized I could let the stream software handle that for me.
OBS (the streaming software I use) can display text files, and it will even refresh what’s shown whenever a text file gets updated. So I decided I’d have my program write the Japanese text to a text file, and then I could have it display the text file “on top” of my program’s window pane. This saved me a lot of time and worked pretty well. Of course, this meant that the program itself doesn’t do any of the text display, which made for a weird, user-unfriendly setup.
|Placing the text file on top like this was a quick shortcut|
But now there was a new, unexpected problem: I personally couldn’t see the Japanese text very well when streaming – it appeared very tiny in the streaming preview window. Eventually, I realized I could use a plugin for Notepad++ that would refresh a text file whenever it got updated elsewhere, and then just have Notepad++ off to the side while playing a game. This worked okay, but the plugin only checked for text file changes every 3 seconds, which was too long for my purposes. I had to figure out how to hack the plugin to drop it from 3 seconds to 1 second or something less.
With all of these pieces in place, I could display the Japanese line of text on a stream whenever an English line appeared. It didn’t work all of the time, but I could kick it to make it work properly whenever it messed up. My Frankenstein experiment worked!
A few days later, I realized it would also be nice to see the Japanese enemy names in battle somehow. Enemy name localizations can be fascinating, so I got to work. I essentially had to do most of the above steps all over again, but things were even worse this time: the enemies’ IDs never stayed in the SNES’ RAM for more than a millisecond – too short for my program to reliably read them. And once the enemies were loaded into RAM, there was no way to identify who was what. They just became a blob of stats.
It felt like I had hit a dead-end – if I couldn’t even identify the enemies in the English game, there’d be no way to find their names in the Japanese game. Then a weird solution hit me: I could “create” my own IDs for every enemy! Although the enemies’ IDs don’t stay in RAM, the enemies’ stats do. So I could glue a lot of these numbers together to create a long string of numbers, sort of like “MaxHP-MaxMP-Exp-Defense-Offense”, but with even more stats. Under ideal conditions, this would give every enemy a unique identifier… assuming no enemies shared the exact same stats.
I whipped up a quick tool to read an enemy’s stats from the Japanese ROM, write its Japanese name to a text file, and then name the text file whatever that enemy’s “stat ID” was. This resulted in several hundred individual text files that I put in a dedicated folder.
|Each file contains a Japanese enemy name|
I then updated my stream overlay program to detect which enemy is currently selected during battle. It would read that enemy’s stats from RAM, combine them all into the crazy “stat ID”, and then try to load a text file by that name. If it succeeded, it would then load the Japanese name stored inside the text file. If it didn’t succeed, then there was no other way to figure out the Japanese name, so the program would simply state that the enemy “doesn’t exist” in the files.
|This filename trick wasn't 100% reliable|
As always, a new problem appeared: I had already hacked the English game to increase the money and experience that enemies dropped. I had done this to get through the game more quickly, since my main goal was to get screenshots and info as efficiently as possible. I had to take this into account and redump all the enemy files.
After this, everything worked pretty well! This solution for giving enemies a unique ID had several risks though:
- It assumed that absolutely no enemy stats were changed during localization, which is a big assumption to make
- It also assumed that the stats in RAM were static and reliable – but that’s a big assumption to make when it’s common for enemies to have multiple forms and have stat buffs/debuffs at unexpected times
- If any enemies happened to share the exact same stats, then they’d end up sharing the same text file… and thus at least one of them would be misidentified
Luckily, things worked out ~95% of the time:
|Here, we learn that RoadSlug should've been Lord Slug|
The remaining issues involved a bunch of enemies that I later realized went unused in the game. I also learned how to display Japanese text with XNA/MonoGame by this point, and since the short, single-line names didn’t require much text formatting, I used that knowledge here.
Bring It All Together
All of these programs and files are strewn about, so to make it all look seamless, I loaded up the streaming software and aligned everything just right. It’s a bit difficult to explain in words, so here it is in action:
|The final setup is a messy pile of separate programs and files working together|
Everything that’s magenta gets turned transparent, kind of like a green screen. This means I could also display stuff right on top of the game if I wanted, although I haven’t used that functionality for Legends of Localization streams yet.
As mentioned, I also had to fiddle with text file stuff to display Breath of Fire II’s Japanese script. By the time I moved on to A Link to the Past, I knew how to directly display Japanese text with XNA/MonoGame, which has simplified things and made it look nicer.
|I can also now scroll text if it's too long to fit in the vertical window|
This was a general look at how my on-stream Japanese text displayer works, and the basic process for developing it. The setup is still very messy, complicated, dangerous, and unreliable, and my implementation is scream-worthy, but I’m hoping that as I continue to polish it I’ll eventually be able to simplify the process and maybe even release some of it for others to enjoy. It’s already proven to be an extremely handy tool for me, and I bet it’d super-helpful for studying languages, playing old games in new ways, and other things I haven’t even thought of yet.
Also, I didn’t realize it until now, but this is a really good chronicle of how designing a program is often a bunch of problem-solving, which gives rise to new problems, which require more solutions, which brings even more unexpected obstacles. Programming is a grueling back-and-forth battle of problems and solutions.
Anyway, this turned out to be pretty long, but hopefully all this technical mumbo jumbo was enlightening and interesting. If you want to check out this stuff live, you can catch our streams here. See you there!