⬆️ ⬇️

Font Filing and Translation Quest 1996 - I Have no Mouth, and I Must Scream

All good!



Based on Harlan Ellison's eponymous novella (Harlan Ellison) , I Have No Mouth, and I Must Scream is one of the darkest quests of all time. The pressing atmosphere does not let go until the very end.



The near future. Three superpowers, the United States, Russia and China, each trying to outperform their rivals, created supercomputers for waging war. But they miscalculated. Having united in a single whole, which calls itself AM, three supercomputers, using the power given to them by people, wiped humanity from the face of the earth. In the living computer leaves only five who will serve him toys for endless torture.



Last time I described the 8-bit font , but this time I managed to make out the 1-bit font .

Both types of fonts are not encrypted or compressed, which greatly simplified the task.

')

Tools: IDA, dosbox + debugger , winhex, GBS .



KDPV

image



The problem is to find the font among the files.

Obviously, such as FONTS, there are no files and folders, there is a 75.5 MB resource file scream.res.

Using Process Monitor (ex FileMon) with a filter on dosbox, I compiled a table of file access orders, offsets and sizes.

First I started watching the .res file with GBS and spoiling the blocks in scream.res in the order of their call, I found a place to store font images:

image





I sketched out my fonts, taken from Arial, did not fit the width and alignment, but there was confidence that the game could be translated, the codes of letters corresponded to cp1251.

For drawing of characters, Excel will also fit:





image



Later, a great link was suggested, comrades from SCUMMVM picked up the game, there is a manual .



The font is 1-bit, that is, one pixel can be set to 8 pixels. Several bytes form a row. Several rows form a height, we get a block in memory (height * row length) bytes or a rectangle by resolution (height X (row * 8)).

Visually in memory and in the game look like this (green and red bars indicate bytes):

image

image



The game uses a small R3 font and an interactive R1.

The structure of each font
The header is 1286 bytes and consists of

ITE_FONTHEADER, 6 bytes

INT16, c_height is the maximum character height,

INT16 c_width is the maximum character width, the parameter is completely unnecessary,

INT16 row_length - the length of the font image row (in bytes),



Then I equated the 256 bytes, an array of indices, the indices themselves to cp1251 (AJay is the indices 192-255).

INT16 index [256] - the number of the byte from which the symbol image begins.



Then 256 bytes, array of widths of characters:

BYTE width [256] - the width of the character in pixels, can be any reason, from 0 to infinity. Well, up to 255 :)



Then 256 bytes flag. As I understand it from a series of experiments, it sets the left indent, in pixels.

BYTE flag [256] Unknown character flag (either 0 or 1)



Then 256 bytes - tracking, how many pixels of the character to draw. Countdown from left to right from 0 to infinity. Up to byte.

BYTE tracking [256] - tracking



Following the header, there is a block of data (row_length * c_height) bytes long.



The formula for accessing any beginning of a series

font_data + (row_length * y),

where font_data is the pointer to the beginning of the font data, y is the row number.



Each character occupies ((width [index] - 1) / 8) + 1 byte.





After these data, the forum artist issued a beautiful, similar to the original dialogue font. In the original it was reserved by any badges and umlauts enough bytes to fit the original and Russian font. As a result, there was even 1 byte left in the stock, which I annulled. I wrote a program for copying blocks from BMP, where a Russian font was drawn in a row, right in the game block.

At the output, I received a cp1251 set of symbols, icons, numbers, English and Russian characters:





I proceeded to the second font:





Here there happened a joint. In the original block there was very, very little space, that is, 14 stupid characters could be excluded from the proposed set, but this was clearly not enough for 66 Russian letters.



First I tried to play the number of bytes in the row, thinking that the game initializes the buffers in height and row bytes. I looked in winhex that the next block is the next font, but it is not used anywhere in the game. Zeroed the block, increased the length of the row, but received the same set of characters, but with a shift. It turned out that the size of the data buffer is registered somewhere else.

I tried to leave the length of the row unchanged, but play with indices.

I could not get anything out of all the ventures, climbed the garbage. But it turned out, I forgot that at the end of the resource file there is a table of offsets and block sizes, it was necessary to play with it and everything would work out without patching.ehe file)



Here I was confused as a dos header to throw out and remove the LE file, which can be inserted into IDA, that it happily recognizes as a 32-bit LE file. I opened the file in winhex and found the line "*** NULL assignment detected", went up to MZ and started cutting to MZ.

Thanks cracklab.



Then another problem came out, how to match the address in the dosbox (looks like 180: 200c61) and the address in IDA (looks like cseg01: 0001AC1F) to see where the code is being executed and analyze it. The site SCUMMVM offered a pretty stupid option, working, but extremely slow. I'm waiting for IDADOS, when it will be possible to look at the code in IDA and immediately trace it with the power of dosbox. But for now ... in IDA, we search for a sequence of about 10 bytes, what we see in the dosbox debuger.

For this game, the formula is:

the address in the dosbox is 1DFFFE h = the address in IDA.



At this point, I knew the exact displacements of the beginnings of the font blocks and their sizes. I began to look for these numbers in IDA with a simple search, but did not find anything.

Then, in dosbox, the debuger set an interrupt to position the read pointer (int 21h, ah = 42h, LSEEK). Before a call to CX: DX, there must be a value for how much to move the pointer: (CX * 65536) + DX.



After a few jumps I found 2 consecutive calls to read 2 fonts from the game, just the ones that are used.

The DX was the number I needed. I rewound a little back and found a font initialization block that looks like this:

mov eax, 6 (eventually patched on mov eax, 3)

call InitFont

mov eax, 8

call InitFont



just in the block - it is 5 and 7 font. After playing with different numbers, I looked at how unused fonts look in the game:

image

image

image

image



I looked at the sizes of the font blocks, chose the largest, transferred a small font block there, patched the executable file, made BMP import of Russian letters (for bmp 256 colors just one byte is equal to one pixel, casting a byte into bits is the second), corrected indices, tracking , width, row length, increased by one height, so that the letters above and below did not merge and finally got the final font.

Retained the original characters, numbers, letters, added a number of Russian letters, including .

image

Source: https://habr.com/ru/post/194988/



All Articles