# 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](../media-comparison.md).

## 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](earlier-code-map.json), [call maps and instruction differences](earlier-call-comparison.json). 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](earlier-native-check.json).

## 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](../effects/format.md) 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](function-comparisons.json) 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.
