Translating YU-NO - The Girl that Chants Love at the Edge of the World

I decided to move further discussion and progress information into a new thread after announcing my efforts on porting the existing fan translation for the Windows version of Yu-No here.

I finally managed to inject text into the game. This is still just for testing, so ignore the full-width font, remaining Japanese characters and wrong line breaks here. This will all be addressed.

YU-NO_Message.png



YU-NO_Option.png


Getting here proved to be a lot more work than I originally thought because in my last post I thought I had understood the structure of the scenario files. Turns out there was more to it.

A scenario file for the Saturn version of Yu-No consists of five parts:
  • A 16-byte header containing a magic byte, the number of characters in the character/string dictionary (discussed below) and the offsets of the next four parts. Padded with 0s.
  • The script, which is a concatenated sequence of byte commands. Part of these are message display and menu option commands. We'll come back to these later.
  • A table of two-byte section or entry offsets into the script. The offsets are from the beginning of the script. This also imposes a soft length limit on the script: If it is longer than 64 KiB, any offset will wrap.
  • A table of two-byte string offsets. These strings form a dictionary from which the messages displayed in the current scenario are formed. As there are quite some repeated characters, this allows for some reduction in size of the whole, uncompressed scenario file. As with the section offsets, the format of these offsets impose a soft 64 KiB limit on the character/string dictionary.
  • A character/string dictionary for the messages. The first entries are single characters followed by 0-terminated strings. The very first string after the characters is pointed to by the first offset in the table before. All characters and strings consist strictly of two-byte, SHIFT-JIS-encoded characters.
As I wrote in my last post, messages in Yu-No are byte sequences pointing into the character/string dictionary. Bytes between 0x01 and 0xFA are direct indices, bytes 0xFB, 0xFC and 0xFD add 250, 500 and 750 respectively, so they sort of act as bank switches. Byte 0x00 terminates a message byte string. 0xFE and 0xFF never occur in the game, but I guess that they function similar to codes 0xFB - 0xFD.

Menu option are much easier, thankfully. They each are just a SHIFT-JIS, 0-terminated string 🙂

So what does this all entail? To inject custom text, one has to first extract the message and menu option commands from the script without misinterpreting any of the other commands. I managed to write a parser that does exactly that.

The much harder part is to compose a scenario file:

First, all new messages and menu options have to be encoded to bytes. This must respect custom control characters such as end-of-message, newline etc. There are quite a few of these, and I am still not sure what most of them do. However, most of them occur as the last character in a message, so it will be enough to just put them there in the translated text as well. This step is also where the half-width-font-inside-full-width-font magic will happen that I hinted at in my last post.

Then, from all messages to inject, a new character/string dictionary has to be generated. I skipped this step as of now, encoding each message as its own dictionary entry. This unfortunately results in later messages being garbled, probably due to some memory overflow or the like. So this must be implemented later on.

From the original script bytecode and list of section offsets, the script must be rebuilt while inserting the new message and menu option bytes, all while keeping track of how the section offsets shift by doing so.

Then, the section offset table must be built, and after that, the string offset table and the character/string dictionary.

Finally, from all the lengths of the individual parts, the header can be generated and with this, the whole file can be put together.

The file then of course has to be compressed with Yu-No's custom LZSS compression and then embedded in SENARIO.ABL, the main scenario archive. As of now, I simply overwrite the existing file at the correct offset without any regard for its length, but in the end, I will have to implement actually rebuilding SENARIO.ABL, or any ABL file for that matter.

Rebuilding ABL files is also important for GRAPH.ABL, which holds nearly all graphics of the game. Some of the translated images I created are larger than their original counterparts. However, the encoder I wrote is a little bit more efficient than what the game's developers used. Reencoding all images in the game frees up around 1.5 MiB of space, more than enough to fit all edited images.

There is still a long way to go, but as of now I think that there are no more potential roadblocks to finish this translation. So it is just a matter of time and effort 🙂
 
Last edited:
Back
Top