Dragon „Hackst“ IV

Translation Embedding (First Proof of Concept, since 2021-12-19 deprecated)

In the class TranslationEmbedding the actual embedding happens. My idea is that a translator creates a folder and puts translated files (see above) in it.

A translation is entered in the first line of 006C.csv.

Only the corresponding textblocks will be patched in the HBD1PS1D file. However, it is also necessary to patch the SLPM_869.16 PS-EXE file, since as already mentioned we have to also change the loading of the dialog start point. While it seems to be rather simple and also stable to replace the textblocks‘ huffman codes and trees, the patching of the PS-EXE file is trickier.

When we debug the game at position 0x8008EBA4 (a nop) it seems to be the perfect position to correct the dialog pointer.

Breakpoint at 0x8008EBA4 before the dialog is loaded.

This breakpoint is right in the middle of two things: (a) it is after a dialog pointer is written to RAM (at 0x800F4E40 where now 0x5AD91815 is stored) and (b) before the huffman code is decoded to actual letters. The dialog pointer is used by the decoding routine to know where (byte and bit position) the dialog should be decoded. For example, the 0x5AD91815 in RAM is read to a register as 0x1518D95A. Using only the right 3 bytes (0x0018D95A) and adding 0x80000000 to it, we get the location of the dialog beginning of the huffman code byte in RAM: 0x8018D95A. The remaining 0x15 part, namely the left 4-bit 0x1 tells us the bit position in the byte (0 – 7). The 0x*5 I currently don’t know what it could mean.

I’m unsure if this is always the case, but it helps us to get all information to do the actual replacement of the dialog pointer with the correct one: In register r5 we find 0x8018D768 which is the start position of loaded text block. In register r9 is 0x800F4DE4 which is the start of dialog region in RAM. In register r16 is 0x800F4DE8 which is the start of dialog textbox (this „0x50710F80“). Using r5 we can get the textblock ID and using r9 or r16 we can get the dialog pointer.

I used the position 0x8008EBA4 (a nop) to replace it with a jump operation in order to do the pointer replacement in a separate code section. I decided to overwrite at the beginning of the PS-EXE file where debug messages are stored with the hope that overwriting those will not break the game. In PS RAM at 0x80017F00 program PS-EXE seems to start. I placed the extra code at 0x8001D4CC.

The idea is to use always relative (not absolute) byte distances to find out what dialog segment is loaded in order to replace it with the correct distance. First, the (deprecated) dialog pointer is stored in register r23 and the byte difference (distance) from the textblock begin to the pointer is calculated. In r22 the textblock ID is stored. With the textblock ID and the byte distance we can do a sequence of if-else operations to find the matching one. Pseudo-code is:

if(textblockID == 0x006C) { //first scene
  if(byteDist == 0x0018) { 
    byteDist = 0x0018;
    bitIndex = 0;
  } else if // etc ... 
  
  } else if(byteDist == 0x01F2) { //first dialog segment
    byteDist = 0x0069;
    bitIndex = 2;
  }
}

We replace the distance accordingly and also set the correct bit index. The last part of the routine is putting this together in the dialog pointer and stores it at the RAM position were the old pointer was. This way we make sure the huffman code decoding routing which is coming after this starts at the right bit and byte from the huffman code.

The first dialog successfully replaced with the translated text.

We made sure that huffman tree and huffman code is patched correctly in the textblock. The huffman tree contains japanese letters (Shift-JIS) but our familiar letters (a- z resp. A- Z). Because this is already part of the font, the dialog works out-of-the-box. Playername, system text (memory card loading text) and dialog options have to be patched by another way.

Download the assembler-analysis spreadsheet (open office document) for more details:

Translated Text from Dragon Quest IV Mobile Version

If you download the game’s data, you will also get a main.11029.com.square_enix.android_googleplay.dq4.obb file. The obb file is actually a zip file. It contains the game’s assets.

In the msg folder we find for each language a folder: en, ja, ko, sl, tl. All of them have 201 files that are named similarly. They are UTF-8 encoded.

Left the Japanese file and right the English file.

If you align them in a text editor based on these @ annotations, you get the translation of each dialog textblock. With some code I guess we can easily get all translations from Japanese to English. Be aware that the mobile version uses accents.

LZS Compression Scheme

Some subblock data is compressed with the LZS algorithm. Using this helpful documentation it seems I guessed the LZS scheme correctly.

The buffer has a size of 4096 bytes. The control byte has to be read bit-wise from right to left. A 1-bit indicates a literal byte. A 0-bit indicates a reference. The reference is 2-bytes in length, e.g. 1110101111110000 . The last four bits describe the length. To the length has to be added +3 to get the correct length. In our example 0000 is the length which is 0 in decimal value, plus 3 it is the correct length of 3. The offset is combined in the following way: The remaining three parts p1=1110 p2=1011 p3=1111 are combined to p3 p1 p2 which creates: 1111 1110 1011 . The decimal value of our example offset is 4093 (little-endian). To the offset has to be added a value of +18, so we get the correct offset of 4093. Since our buffer has a size of 4096 and our example length is 3 bytes, the last three bytes of the buffer are written to the output stream, in the given example. Maybe it can happen that the offset value overflows the buffer size which is why you should put % 4096 (modulo the buffer size). Read until the compressed data is read completely.

The algorithm is implemented in the DQLZS class.

Doing this for subblocks of type 8 we can decompress the TIM files without an error.

A decompressed TIM file of DQ4.

8 Gedanken zu „Dragon „Hackst“ IV“

  1. Hello, I think to have dragon quest 4 for ps1 would be the best way to play dragon quest 4 because the only way to play it is on ds and its s tiny and the nes version its so old, my dream is to play it on my ps1 on my crt that would be espectacular, I hope you finish the job one day it would be EPIC

  2. Don’t give up! I’m currently playing through Dragon Quest 1 and 2 on the SNES and hopefully the Dragon Quest 3’s HD-2D Remake will be out soon, and after that i really hope your translation on part 4 for the PSX is done!! 🙂

  3. Amazing work so far! It seems you sir are a genius, the way you are finding possibilities to translate it. Can’t wait to play the finished translation, hopefully it will also encourage more people to play this gem!

  4. I’m eagerly awating the day that a fan translation of DQ4 is released. The official translation of the DS version is simply awful, and the NES version’s graphics genuinely hurt my eyes to look at. I pray that, someday, I can play a version of Dragon Quest IV that is both pleasant to look at, and has a translation that is accurate and faithful to the original Japanese script and fully in English. No goofy accents, no non-English words peppered throughout the script, no puns. A true translation of DQ4! Take your time with this project, and make it the best it can be!

  5. Hi, I understand that people looking forward to a translation. However, the patching of the game is still in an early state. I hoped that there could be other technicians (like computer scientist students) who help with the technical issues. Since the sprint in March 2023 I did not work on the project (busy job and life). One has to debug the actual game’s assembler code to check if the patching does the correct thing. Any technical help from interested people is very much appreciated.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert