Skip to content

Displaying ingame text

robotanarchy edited this page Aug 6, 2015 · 6 revisions

Shorturl: http://git.io/g2hr-esc-text

Escape-Text

When playing GTA2, the user can press ESC to leave the game. The following confirmation text gets displayed:

Is that it?
Press the ENTER key to quit play.
Press the ESC key to resume play.

Always show the text

The exe file can be modified to always show this quit text. At 0xD8725 (Vike's GTA2.exe that we require for G2HR, 0xC8725 in the original GTA2.exe) is a JMP instruction, that gets executed when the ESC-key has not been pressed yet. We can NOP this instruction by writing 6x0x90 at this address and the text will always get displayed. In the post-alpha branch, the function rpc_apply_always_show_esc_dialog_hack() takes care of this (in rpc/rpc.c).

Change the displayed text (good method)

Since the bad method below worked with an offset, that only worked on 2 computers in wine, a new method was necessary. This time, a pointer for the start of the string table index and the offset, where the actual strings begin, was found. The string table is like in the GXT-files, except that they use absolute pointers. So we iterate through the string table and replace the pointers from quit1, quit2, quit3 with space that we have allocated in the dll. This way we can use larger text than in the translation, and it works with every language file (although the new menu will be in english for now).

To make it safer, while switching the pointer addresses, the main thread gets stopped and resumed. All this happens so fast, that is not noticable by the user.

Change the displayed text (old, bad method)

GTA2 shows the quit1, quit2 and quit3 messages from the loaded GXT file in these three lines. The obvious way to change it would be modifying the GXT file. However, we want to display text dynamically!

When the injected DLL gets loaded, the text has not been loaded yet. We try to access the text in a loop (checking with IsBadReadPtr(), then comparing the text), until we can read it.

The address isn't clear either. But at least for the english text it seems to be aligned at: 0x0000abb2 + n * 0x00010000

We simply try 0 ... 10000 for n and hope that we find it there. It works on at least two machines tested and the scanning delay isn't really noticeable.

Note: MinGW likes to 'optimize' when filling the address with zeros. This happens, when compiling on one of two machines tested.