Comparing the engine revisions
The reconstruction follows the 14 January 1998 Windows software engine. The collection also contains three earlier Windows engines, several DOS revisions and two 3dfx builds. Disc contents, dates and executable hashes are listed in the media comparison.
Finding routines without symbols
The 1998 executables retain Watcom symbols; the 1996 Windows engines do not. We first locate instruction bodies whose bytes agree after masking PE relocations and external relative call destinations. Every unmasked byte must match, and the location must be unique. Matching callers then identify additional callees at the corresponding instruction positions.
| Earlier Windows build | Unique matching bodies | Routines located after following calls | Matching instruction shapes |
|---|---|---|---|
| 5 September 1996, European V2 | 184 | 317 | 209 |
| 18 September 1996, US | 190 | 325 | 215 |
| 2 October 1996, Japanese | 194 | 330 | 220 |
The initial search considers 2,193 named bodies. The resulting maps include mission loading, actor construction, weapons, radar symbols, sound, menus, drawing, mathematics and platform services. They are partial address maps, not a count of every function in these engines. The instruction-shape column ignores relocated addresses; data and call targets still need their own comparison.
Reports: byte matches, call maps and instruction differences. The tools are compare_earlier_engines.py and compare_earlier_calls.py.
Code can look different and do the same thing
The later compiler moves uncommon branches behind the main return, duplicates short return sequences and shares tails between functions. For example, the 1998 set_drone_position_ at 00427548 writes Y and Z, then falls through into set_drone_position_x_ at 00427550 to write X and return. The European 1996 version at 00435e7c writes X, Y and Z in order. Both update exactly the same fields.
The attitude setter shares its heading setter in the same way. Treating every symbol as the end of the previous function would truncate both routines. Our control-flow reader follows the fall-through.
get_approx_2d_range_ also changes register allocation and branch layout. Its calculation remains max(abs(x), abs(y)) + floor(min(abs(x), abs(y)) / 4) within the non-overflowing domain. The Q31 transformation-matrix calculation changes scheduling and temporary storage, but preserves its products, rounding and table lookup.
We executed these routines from each earlier binary, rather than judging them from disassembly alone. For each 1996 engine, the comparison covers:
- 2,000 approximate-distance inputs;
- 2,000 matrices, comparing all 18,000 coefficients;
- 6,000 actor/waypoint setter calls, comparing 2,784,000 output bytes;
- 20 full crater-pool updates, comparing 15,360 common fields.
All agree with the 1998 software engine. The three result streams have the same SHA-256, c566e604b1b3d5124be7c1cbafe0a0fc085b466dd0375835351422aedeb17f76. The matrix check supplies the original SINCOS.TBL to each executable. Original-execution report.
Crater records grew
The 1996 short- and long-life crater entries occupy 24 bytes: position, model, heading and animation age. In 1998 each occupies 36 bytes, adding the three scale words used by scalable craters. This changes the pool stride and the addresses of its end and next-entry pointers.
The older update routine at 0049f7c8 and the 1998 routine at 0048e1d0 still treat splash model 384 the same way: increment its age once per visual update and remove it when it reaches five. Both pools still hold 128 entries. The execution check compares the common fields using each build's actual record size.
The effects notes describe the later scale calculations and slot reuse. These are already implemented in C++ because they belong to the selected software reference.
Some shorter routines are just less inlining
The 1996 release_target_sanely_ includes the target-pool decrement and 48-byte clear. The 1998 version calls the separate release_target_ for that work. Both retain the same diagnostic cases for attempts to release weapon rounds or objects with an invalid user type. A shorter body here does not mean that target cleanup disappeared.
The mission allocator still allocates and clears an 88-byte mission record in both builds. Its linked-list insertion is laid out differently, but the record offsets and insertion order remain the same.
Software and 3dfx
The 1998 symbol-bearing builds can be compared by source-unit and function name. Their normalized instruction report records the software/3dfx and January/June 3dfx comparisons. The hardware path adds Glide texture setup, fog, filtered textured drawing and different framebuffer access. The June update changes framebuffer locking, cockpit/instrument drawing, pause rendering, texture setup and thread shutdown.
The shared software renderer remains our reconstruction target. The later Voodoo2 executable does not replace the software executable on the French disc; both are supplied. The software executable is byte-identical to the selected reference.
The DOS revisions are inventoried and retained, and the selected DOS game runs in the reference capture environment. The address maps and cross-version execution checks on this page cover the Windows engines.