Doom2 Longtics Hack
The Problem
When recording demos, doom2.exe
only records 4 bytes of
data per tic. One for forward/backward movement, one for sidestepping,
one for turning, and one for actions (firing, door opening, etc). The
angle data in Doom is normally stored in 2 bytes — this is what is
transmitted in network games — for demos, presumably to reduce the
space used, the low byte is thrown away.
This reduces the resolution of the player turning data. This is very noticable in play: the player's turning steps through about 1.5 degrees at a time, instead of turning smoothly. Not only has Doom halved the available data, but because it keeps the high byte, the player can still turn 180 degrees in an instant, but loses all the accuracy of small turns. So they made the strange choice to continue to allow fast turning, and to make accurate small turns, needed for aiming, impossible.
Clearly modern computers are not short of disk space, so it becomes desirable to regain the greater accuracy in demos which is already available in network games and normal play. This is easy for the ports, but plenty of players still want to use doom2.exe. And since we don't have the source code, compilers and libraries to build a new doom2.exe, we have to work on the old one.
Any hack has to be minimal: there has to be no incompatibility with the original EXE, and we mustn't affect the gameplay other than allowing better demos. The people still using doom2.exe are purists :-)
The Hack
Okay, G_WriteDemoTiccmd
is at offset 0x00638e4 in
doom2.exe v1.9. Don't ask how I know this — let's just say it took a
lot of work. We want to record one extra byte, so we insert an extra inc
of the pointer, modify the old store instruction to store the low byte,
and add a store instruction for the high byte. Fortunately the existing
code is quite long-winded, as it has to round the angleturn to the
nearest value when it truncates it — we are replacing it by simpler
code in fact.
63912: 43 inc %ebx 63913: 88 03 90 90 mov %al,(%ebx); nop; nopExtra pointer advance.
Store low byte of angleturn.… Existing pointer advance left in place. 63918: 90 90 90 nop; nop; nop 6391b: 88 23 mov %ah,(%ebx)Padding where old rounding/truncation was.
Store high byte of angleturn. 63928: 83 eb 04 sub $0x4,%ebx…Finally, we have to move the pointer back one more than before ready for the G_ReadDemoTiccmd.
Now for the reading end. G_ReadDemoTiccmd
is at
0x0063894. The code is much simpler here; we just have to read an extra
byte and advance the pointer, instead of inserting a 0 byte.
638bb: 8a 33 mov (%ebx),%dh 638bd: 43 inc %ebx
But this part of the hack doesn't work — G_ReadDemoTiccmd does not
get called. The code has been inlined by the Watcom compiler in the
calling functions. So we have also to patch G_WriteDemoTiccmd
(which calls the read function in order to read back in what it wrote,
to ensure that the game being played and recorded will match the
playback), and G_Ticker
. These are at 0x00638e4 and 0x0062464 respectively. The corresponding changes are:
63965: 8a 23 mov (%ebx),%ah 63967: 43 inc %ebx
and
62653: 8a 22 mov (%edx),%ah 62655: 42 inc %edx
Finally, to change the demo version number, we change the version number check in G_DoPlayDemo
at 0x0063ae4:
63b17: 80 fa 6f cmp $0x6f,%dl
and the version number recorded in G_BeginRecording
at 0x0063a44:
63a4b: c6 02 6f movb $0x6f,(%edx)
Author
Colin Phipps. Written 2004/09/23.