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


In the last couple of articles I’ve shown you how to mess around with the game file’s assembly code to do some simple text replacement and font parameters tuning. But there is, of course, much more to translate in the engine than just some text on the screen. These scenario files we’ve seen earlier are also capable of triggering complex visual effects. Take, for instance, Senmomo’s date display screen.

These show up sporadically in the game. Pictured above is the very first one, showing the date “March 4th” (Literally: month 3, day 4). It might look simple but there’s quite a lot going on here. This screen is generated by an algorithm that takes a date as an input, then displays in turn each of the digits as images (not text!) at the right spot, with a fancy little animation.

At first glance you might think that translating this screen would be straightforward: why don’t we just replace the images for these Japanese digits with our Arabic numerals? Unfortunately it won’t be as easy as that, otherwise I wouldn’t be here making a whole article about it. There are a couple of complications:

  • The Japanese counting system is fundamentally different from the western one, and you can’t just take each digit one by one, replace them with the right one in our decimal system and be done with it. For example, numbers starting from 21 use three digits in Japanese, whereas we would need only two.
  • The date format we want is different than what’s used here: we’re going for the name of the month followed by a number for the day (for instance, the date shown above would be translated to “March 4”). We could have gone with something more similar to what’s already there, but decided against a potentially ambiguous MM/DD format that might confuse our European audiences.
  • Remember, it’s all assembly code. That will severely limit our capabilities to comprehend, let alone modify the aforementioned algorithm that handles date formatting.

By performing some strategic changes to a couple of strings in the game’s scripts and cleverly constructing the translated images, we’ll be able to figure something out. Working out a solution for this was one of my favorite challenges in the entire project and possibly one of the most incredibly stupid hack I’ve ever worked on. But I live by the motto of “if it looks stupid but it works, then it’s not stupid”. So let me share with you the whole adventure that was translating Senmomo’s calendar screens…

Manipulating the displayed date in the scenario files

To understand a bit more how this date system works, let’s first try to find out what in the scenario files causes to show this screen with which specific dates. We’ll be looking through Senmomo’s first scenario file, hat010010. One good starting point would be to check out the list of strings used in the file and see if there’s anything interesting there. EthornellEditor is an excellent tool for this, it lists all the strings in a scenario file in the order that they appear. Scrolling down that list to the part where a calendar screen should appear, using the dialog lines as reference points, here’s what we find:

Now this is interesting… Some time after the line of dialog that should show up just before the first calendar screen, the game loads in the two strings “Cal” and “Date”. That’s all we know though. If we want to find out more, like how each date is specified in the script, we’ll need to look into the raw file. Searching for the string “Date” in hat010010 using a hex editor tells me it’s stored at address 4DF5F, so let’s do a reverse lookup of that to find where that string is used. Remember what we learned in the previous articles: strings are loaded through opcode 3, which is followed by the little-endian notation of the address of the string that is being fetched minus the size of the header, 40. 4DF5F – 40 is 4DF1F, or 1FDF04 in little-endian. So here, the sequence of bytes that loads our string is: 03000000 1FDF0400. Searching for that we get three hits in the file, which happens to also be the number of calendar screens that appear in hat010010 – that’s reassuring! Here’s the code around the instruction that loads the string “Date”, on the left: for the first calendar view in the file (which we know shows “March 4th”) and on the right for the third one (March 7th).

Calendar code comparison

The hexadecimal sequences shown here are somehow responsible for triggering a date display screen, but we don’t need to know in detail what they do and how they work. The only thing we’re interested in here is to find where the date is being encoded. The trick is to compare side by sides these two hexadecimal sections, which are supposed to do the exact same thing (showing a calendar screen) with the only difference that one shows the date “March 4th”, and the other one “March 7th”, so a difference of three days. The bytes that have changed are marked in red, and note that the ones I’ve highlighted in blue have a difference of three. Just like the number of days between March 4th and March 7th, so that might just be what we’re looking for! To confirm, let’s convert that to the decimal format. On the left we have 30 01 and on the right, 33 01. Remember that numbers should be read from right to left, so that’s 130 and 133. And then we convert to base 10 and get respectively 304 and 307. That’s our two dates written in mmdd format! For further confirmation, we can edit these numbers and launch the game. Lo and behold, our new date now displays in the calendar screen. To summarize, here’s how to change which date is displayed:

  • Write the date you want to be shown with the two digits for the month first, then the two digits for the day
  • Convert that to hexadecimal, then to little endian notation (add a zero to the beginning if needed, then move the last two digits to the beginning). For instance, 304 (March 4th) becomes 30 01
  • Find the operation that loads the string “Date” in the scenario file (here: 03000000 1FDF0400)
  • Four bytes before that, replace the value with the one you calculated at step 2

We’ve just completed step one of our master plan: we have acquired full control over which date are displayed.

Funnily enough, and that’s gonna be extremely useful for what’s coming next, the engine is absolutely fine with displaying a date that isn’t even real. The highest one it supports is 19/99:

 

The ninety-ninth of nineteenuary, a date that’s perfectly reasonable as far as the engine is concerned

Being able to display any date we want on the screen will help us a lot when we get to testing out our “translation”, but now that that’s handled let’s have a look at how to inject our translated images.

Breaking through the ten digits limit

Pictured above: all the images used for the calendar screen, as we dug them out of the game’s archives. One serious limitation that we’ll need to overcome here: there’s only ten images for the digits (The ones whose filename start with days_c0). Remember that we want the date to be displayed as “name of the month” followed by the day’s number. So we will need one extra “digit” for each of the month’s name, bringing our total to 22 digit images. Actually, we’ll only need 16 because some months are never shown in any calendar view throughout the game. But still, the ten (eleven if you count the zero) that we have here won’t be sufficient. We need to find a way to tell the engine to use more images. And to that end, it’s now time to go have a look at the script that loads those images to build the calendar screens.

If we search for the name of those files in the game’s scripts, we find only one that directly references them: it’s the script called effect, in the data01000 archive that also contain the scenario files. Let’s look at which strings appear in it using EthornellEditor.

The “effect” file viewed in EthornellEditor

Oof, that’s a lot of strings! Scrolling through it all, I counted 19 uses of days_c0. Thinking about it though, this is not too unreasonable: there are a lot of potential different conditions under which a digit image needs to be loaded in the calendar script. For instance, it would need to be shown at a different position depending on if it appears in the “month” number or the “day” number, and at which position in that number. That’s why it’s used so many times: to cover all the possible combinations.

For our translation though, we don’t want the engine to re-use the same set of ten images all the time. If the digit being loaded is for a month, we would want it to use a different set of images than the ones used for the days. Here’s what we’re going to try: instead of using the string days_c0 the first time, we’ll instead change that to days_ca. That will tell the engine to use a different set of ten digit files (numbered from days_ca0 to days_ca9), but only under the conditions where it would use  the first occurrence of days_c0. And then we keep going. Second time days_c0 appears, we change it to days_cb to force the engine to use yet another set of digits here. And so on, until we’ve replaced all 19 occurrences of days_c0.

We’ll illustrate this in a second if it sounds confusing. What’s happening after this change is that the engine now uses 19 sets of 10 digits images, rather than just one set of 10. So we need to generate 190 new images. And we want them to be slightly different, so we can identify which “variant” (a, b, c, etc up to s) each one belongs too. We’ll do that by adding a little letter in the corner of the image. Here’s a Python script that does all the work for us: it handles renaming days_c0 in the effect script, and generating the new images that we need.

import io
import os
from PIL import Image # If missing, run "pip install Pillow"
from PIL import ImageFont
from PIL import ImageDraw 

EFFECT_INPUT = "effect_orig" # Original effect file from the game's archive
EFFECT_OUTPUT = "effect" # Patched archive file


def find_all(data, substring):
    index = -1
    while True:
        index = data.find(substring, index + 1)
        if index == -1:
            break
        yield index


with open(EFFECT_OUTPUT, "wb+") as output_file:
    with open(EFFECT_INPUT, "rb") as input_file:
        input_data = input_file.read()
    # Copy the data from the original effect file into the new one
    output_file.write(input_data)

    # Address of the bytes that hold the string "days_c0"
    name_address = input_data.find(b"days_c0\x00")
    index = "a"

    # Iterate over all the times where "days_c0" is loaded through opcode 3
    for address in find_all(input_data, b"\x03\x00\x00\x00" + int.to_bytes(name_address - 0x120, 4, "little")):
        output_file.seek(0, io.SEEK_END)
        new_address = output_file.tell()
        # "days_c0" will be replaced by the following string, forcing the engine to use a new set of 10 images
        output_file.write(b"days_c" + index.encode() + b"\x00")

        output_file.seek(address + 4)
        output_file.write(int.to_bytes(new_address - 0x120, 4, "little")) # Writes the address of the new string

        # Generate the ten new images for our new digits variant
        for i in range(10):
            image = Image.open(os.path.join("Images", f"days_c0{i}.png"))
            draw = ImageDraw.Draw(image)
            font = ImageFont.truetype("arial", 32)
            # Adds a letter at the top left so we can identify the variant
            draw.text((0, 0), index, (255, 0, 0), font=font)
            image.save(os.path.join("AnnotatedImages", f"days_c{index}{i}.png"))

        index = chr(ord(index) + 1) # "index" becomes the next letter in the alphabet

Once we run this, here’s what the new effect script looks like:

Patched “effect” file viewed in EthornellEditor. We’ve replaced “days_c0” with “days_ca”, “days_cb”, and so on 19 times.

And the new images:

The new digit images are like the original ones, except there’s 190 of them now. There’s a little letter in the corner to indicate which of the 19 variants each belongs to.

Let’s patch the game with all these new files. The new effect can simple be drag-and-dropped in the game’s folder, but all the images will need to be converted with something like BgiImageEncoder first. Then let’s run the game and see what happens. When we check out the calendar screen for “March 3rd”, this is what we get:

Calendar screen for march 3rd, but with each digit using a different image variant (here, a and b)

Success! It’s now using two different images for the digit “3”, the first one from the “a” set of images and the second one from the “b” images. Whereas before applying our patch, the engine would have used the same image for these two digits. Which would have made it impossible for us to translate it to “March 3”, because changing the first image to say “March” would have changed the other one too, and we would have ended up with something stupid like “March March”. Here though, we have gained more freedom. We can change digit 3, variant “a” to say “March”, and digit 3, variant “b” to become the number 3 from our digits system.

This works fine for dates where the day and month numbers both have a single digit, but for the rest other combinations of variants are used. Thankfully, that seems to be consistent with the number of digits that show up. Trying out a bunch of different dates helped us determine under which conditions which variants are used:

Variant sets used by different dates

Notice some patterns?

  • When the month number is under 10, it always uses the “a” variant. We’ll use those for the month name images. We can encode only 9 months that way, but it’s enough for us, considering that only 6 unique months are used throughout the entire game.
  • When the “days” number is under 10, it always use the “b” variant. So we’ll simply use the numbers from 0 to 9 for the “b” images, and thanks to that we can support every date with a single-digit “day” number.
  • For dates with a “day” number above 20, it’s always the “g”, “e” and “h” variants for each of the three respective Japanese digits. We’ll use this to encode any translated date that needs two digits in its “day” number, having the first digit in the “g” variant and the second one in “h”. We’ll just replace the one in the middle with a transparent image, since it’s always the character “十” that doesn’t translate to the English numbering system. A downside is that we have to start from 20 (because below that it uses another set of variants), but it’s OK because remember that the days number can go above what’s reasonable for a date. We’ll just translate “20” to “10”, “21” to “11”, and so on. We will have to update the original dates in the scripts to reflect this, though.

Final product

Here’s the same calendar display from the previous section for the date “March 3rd”, except this time we’ve replaced the images with their translated ones: digit 3 in variant “a” (days_ca3.png) becomes the word “March”, and digit 3 in variant “b” (days_cb3.png) becomes the number 3. We’ve also overwritten the smaller characters for “month” and “day” with a transparent image, as they don’t need to be translated. Notice that the image for “March” is much wider than the original, but thankfully the engine is fine with that and will display it normally. This is really helpful for fine-tuning the positions of each digit, as we can add some empty space in front of them to effectively move them to the right, which we did for the “3” image so it leaves enough space for “march”.

Tadaaa!

That’s it for today! I realize that this might not have been the most useful of tutorials considering how these calendar screens are very specific to Senmomo, but I hope it gave you some insight above the kind of trickery you can do on the engine to translate more complex aspects of a game, without doing anything more complicated to the original code than just replacing a bunch of strings.

This is where I would usually say “see you next week and have fun!”, but due to some real life circumstances (nothing crazy, just a bunch of travel plans I need to catch up to now that the world is returning to normal), I’ll have to put the series on hiatus for a month or two. Which works out pretty well, considering that I’ve reached a good milestone regarding the content I have to share with you all. With this article and all the ones preceding it, I’ve shown you everything interesting to know about manipulating the game’s files to have it do your bidding. It is, however, far from all I had to share. There is another way to patch the engine, which involves sideloading custom C++ code using a clever trick called DLL proxies, and without touching any of the game’s original content. I won’t spoil it further, as I have a good 5-6 more articles in mind to cover all of this. So stay tuned for part two of Adventures in the Ethornell Engine, whenever that will come. See you then! And have fun!


My inbox is still open while I’m gone (I won’t be dead, just on Central European Standard Time), so if you have any question, comment or concern, 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.

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>