This is the fifth article in Operation Bellflower’s weekly tech posts series. Check out the previous ones here!

Welcome to another Adventure in the Ethornell Engine! Last week we’ve learned how to replace the game’s original script with our translated text. But as you may have noticed, there are a couple of issues with how the text is displayed. It’s not too surprising, considering that the game was never meant to display characters from our Latin alphabet. And so we’ll have to fiddle a bit with the scripts that handle fonts. There are two kinds of scripts packaged with the game: “scenario files”, like the one we dealt with last week, and system script files. Today I’ll show you a couple of tricks to adjust the font from both system scripts and scenario files.

Credit where it’s due, all the content from today’s article was made possible thanks to previous research made by ArcusMaximus. This tutorial illustrates the method they described in here to perform various font changes. Huge kudos!

Enabling “proportional” mode

Taken from last week’s article, this is what our main text box looks like when it tries to display English text:

The most glaring issue, and the one we’ll try to fix first, is that strange inconsistent spacing between each letters. To explain exactly what went wrong here I’ll need to go over a little bit of font terminology first. Fonts can be separated into two groups: monospaced fonts and proportional fonts. As the name implies, each different character in a monospaced font takes the same amount of space, whereas in a proportional font the width of a letter can vary. Here, the engine is treating the font as a monospaced one. This explains why the wider letters seem to crash into each other, and why the slimmer ones like the l’s have all this unnecessary blank space between each other. It’s not surprising that the engine is set up this way, if you think about it: all Japanese characters have the same width, so it would make sense to display it as monospaced. But it’s something we need to change here, as English characters have varying widths. Thankfully the engine is able to support proportional text, we just need to find where fonts are configured in the game’s system scripts and toggle the right parameter.

First step is to extract those system scripts from the game’s archives. You can recognize them from their ._bp extension. In Senmomo, they’re in the sysprg.arc archive. Then you’ll want to grab a copy of ArcusMaximus’s BGI Disassembler, which is a command line tool that will convert that ugly assembly code in the ._bp files into something slightly easier to digest. Run it on the folder with your extracted system scripts, and it should generate a text file for each of them. Basically, each of these disassembled files lists all the assembly instructions included in their respective ._bp file and formats them in a way that’s much, much nicer for us than looking at the raw bytes of the original assembly code.

With that we’re ready to get started on the hacking, now open up the disassembled version of the system script that manages the text box: scrmsg.txt. If you remember last week’s tutorial, scenario files have an instruction (opcode 140) to display text on the screen. It’s the same for system scripts, though it’s a wholly different set of opcodes since we’re dealing with a different kind of assembly language here. The instruction that renders text is graphcall 91:88, try searching for it in the file. Just like how it worked for opcode 140 in scenario files, graphcall 91:88 takes its parameters from the stack, and so right before it you will see a set of instructions that write data on the stack. These parameters include the text that needs to be displayed or various font settings, like the one that we want to modify: it’s the second argument counting from the bottom.

Text rendering in scrmsg._bp

It takes these six instructions to load the value for “Proportional mode” onto the stack, because it’s being loaded indirectly from another address. There’s quite a bit to unpack here, so let’s go over this line by line:

  • First: push the two constants “276E8” and “1D74C”
  • “add” is an operation that removes the two values at the top of the stack, and push their sum to it. The top of the stack becomes 276E8 + 1D74C
  • Next two lines, we add 6 to that value
  • And finally, “load byte” removes the value as the top of the stack, treat it as an address, and put whatever byte is at that location at the top of the stack.

We now know that the value for “Proportional mode” is stored at memory address 276E8 + 1D74C + 6. All there’s left to do is find who writes at that address, and change that. We’re looking for something that accesses this address in a similar way, but writes a byte there instead of loading it. Using VScode’s “search everywhere” in regex mode or the grep command for the linux fans out there, you’ll find something interesting in userdata.tx:

Storing the value later used for “Proportional mode” in userdata._bp

What that section does is it stores a “0” at 276E8 + 1D74C + 6. So if we change that “0” to something else, that’s what will be used for “Proportional mode” when we render text in scrmsg._bp. “0” means “disabled”, so we want it to become a “1” instead. Don’t change it directly in the disassembled script, as there is no tool that can regenerate a ._bp file from its disassembled format. Instead you’ll need to open the original userdata._bp in a hex editor and change to a “1” the byte right after the push instruction, in this case located at address 125C. Make sure not to modify the byte at 125C (since that’s the push instruction itself), but the one just after it. Re-disassemble your modified file to ensure that there is now a “push 1” where it used to say “push 0”. If all went well, dropping it in your game’s folder will make the text box have its content in proportional mode like so:

Aaaah, now that’s better!

We’ve done some good progress but we’re not quite done yet. The text box isn’t the only place where text is displayed, for instance the speaker name or the backlog text are rendered elsewhere, and these need to be patched as well. Their font is being modified through a different opcode, graphcall 92:9C, which is a function specifically for configuring text format. It needs to be changed in a similar way to what we did with graphcall 91:88, except this time it’s the 9th argument we need to modify (counting from the bottom), and it will be easier as well since there’s no indirect access here, just a push instruction to modify.

An example of the “configure text format” function in scrmsg._bp

Make sure to look in all the scripts, as this function appears multiple time. In the Senmomo patch, we’ve performed this modification in all these files:

  • cnfgmsgwnd._bp: for the text in the “preview” box in the setting’s menu “text” section
  • logwnd._bp: backlog text
  • scnbckwnd._bp: “Scene rewind” text
  • scrmsg._bp: main text box and speaker name
  • usdtwnd._bp: text in the comment box of a save file

Changing the font’s size

The English language tends to be much more verbose than Japanese, and so you will often find that an original line that fits just fine in the text box will have its translation be too long for the text box to handle. To fix this we can look into adjusting the text’s size. This can be done in two different ways.

The first approach is by modifying the system files, exactly like we just did for enabling proportional mode. Since graphcall 92:9C is the function that configure all the aspects of the text’s appearance, there is also a parameter for the font’s size. It’s the twelfth one from the bottom, or the one three instructions above the value for proportional mode. In our patch, we noticed that the text area in the backlog is noticeably smaller than the game’s main text box, so we had to edit logwnd._bp and change graphcall 92:9C ‘s font size parameter from “1D” to “16” in order to make all the text fit.

The downside of this is that it changes the font’s size across the board, whereas you might want to do it only for some specific lines, namely the one that don’t fit in the text box. Hence the second approach: modifying the scenario files. Just like graphcall 92:9C does it for system files, scenario files have their own instruction for changing the text box’s font, which is opcode 14C. It takes three arguments, and the middle one is the one that defines the text’s size. As opposed to how it works in the system files, opcode 14C only changes the font from where it’s encountered in the script. Here’s an example where we included it right before an opcode 140 that displays a line too long to fit in the box, to change the text’s size to 16:

Changing the font size of one line in scenario file hat010010

Injecting a new instruction like this requires overwriting some bytes, namely the ones the correspond to opcodes 7B and 7F that always precede a message instruction. I have not been able to determine what these do, and removing them doesn’t seem to have any impact in the game whatsoever so I’m just going to assume that they’re there for debug purpose and that it’s perfectly fine to stomp over them like this. If you or someone you know is a BGI professional that knows what these instructions are for please do enlighten me, there’s my contact info at the bottom. 

That about covers it for today’s article. Combined with last week’s tutorial, you now know all there is to know about messing with the text that the BGI engine displays on the screen. Next week we’ll go back to our scenario files to show how we “translated” the game’s date display screen. Look forward to that, I promise it’s going to be an entertaining one. See you next time, and have fun!

Any question, comment, concern, or know of a resource that describe all of BGI/Ethornell’s opcodes (please I’m begging you), hit me up on Reddit or Discord at Perturbed_pangolin#3792, or shoot me an email.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>