TFX Research archive

TFX 2.05 executable and disassembly

The native code browser provides searchable x86 instructions, direct calls, possible callback targets, relocated references, strings and interpreter dispatch tables. It opens locally. Each routine can be downloaded as an annotated .asm listing.

The reference is installs/cd_1994_09/TFX/TFX.DAT, SHA-256 2cc41a39fa95a2c9fd883c4220d7bdd9cca00c543c89b019eb270763f59d1d9a. This is the protected-mode game executable, despite its .DAT extension; it is separate from the DID resource archive.

Compiler and build clues

The retained binaries identify two toolchains. These offsets are file offsets within each named file, not all within TFX.DAT:

Component Embedded evidence Offset
TFX.DAT WATCOM C/C++32 Run-Time system 0xb454a, immediately after the entry-point jump
CONFIG.EXE The same Watcom runtime identification; setup also explicitly names Watcom C/C++ 32 0x2c626; trademark text at 0x4296a
START.EXE Borland C++ - Copyright 1991 Borland Intl. 0x8634
Q2.COM The same Borland runtime banner 0x1574
GUS.COM The same Borland runtime banner 0x4c14

The main image and CONFIG also contain DOS/4GW Professional loader banners (TFX.DAT at 0x3162e, CONFIG at 0x218de). START is a 16-bit MZ executable; its music/timer/driver work is separate from the game's 32-bit protected-mode LE image. GUS.COM is also MZ despite its suffix, while Q2.COM has a flat code header. A filename suffix alone does not establish the executable layout.

The ordinary game interfaces repeatedly pass arguments in EAX, EDX, EBX and ECX, matching Watcom's documented register convention. For example, name entry at 0xa1ee0 receives four arguments in those registers, and the game entry at 0x70ad0 receives its argument count and vector in EAX/EDX. Variadic formatting uses stack arguments. Standard C-runtime behaviour, conventional procedure frames and x87 floating-point calculations are also present. Together with the runtime identifications, this strongly supports a mixed compiled C/C++ and specialised x86 build. It is not evidence that every routine was handwritten assembly.

The runtime banners identify the compiler families, not the exact compiler versions or source language of every module. Their copyright years are not build dates. Watcom's combined C/C++ banner does not distinguish C from C++; the Borland banners likewise establish linked runtime provenance rather than prove that each helper's own source used classes. No original makefiles, linker response files or complete game source tree have been recovered.

The software renderer includes self-modifying paths and compiled drawing-code resources, so specialised assembly and code generation remain important parts of the pipeline. The flight model instead contains extensive x87 double-precision calculation. The separate cockpit programs are executable VGA drawing assets, not a consequence of every other resource being machine code.

The audio component identifies itself as OSL: START contains track and device-driver banners for LMEDIT (0x3e99), ADLIB (0x5800), SCC1 (0x68e0) and LAPC1/MT32 (0x7170), each with an OSL copyright identification. See the native audio captures. These are identifiable runtime components; an expansion of OSL or a particular modern audio middleware lineage is not established by those banners alone.

The engine's surrounding asset tooling is custom: model/scenery programs have their own interpreters, missions retain readable authoring commands, and presentations are saved parser graphs. The 2,773-byte ssinfo.h retains C-style enum declarations for scenery entry/custom-state indices, but includes incomplete lines and identifiers beginning with digits; it is an authoring artefact, not a usable original C header as shipped. Inferno-named rendering modes, resources and diagnostics suggest shared internal DID technology, without identifying a third-party 3D framework or establishing identical behaviour in the released Inferno engine. See the cockpit provenance notes.

C library use and C++ evidence

There is positive identification of standard C runtime functions, not just a shared compiler banner. The string-copy loop at 0xc9602 follows the 386 small-data assembly pragma in Watcom's strcpy source, with register preservation added around it. The routines are present in the executable itself rather than visible only as imported names.

Identified routine File offset Evidence
atoi 0xc70b1 Character-class table, whitespace/sign handling and decimal accumulation; five native cases
strcmp 0xc7104 NUL-terminated comparison with word-at-a-time zero detection; five native cases
strcat 0xc7286 Destination scan, source append and destination return; three native cases
sprintf 0xc92a9 Variadic stack arguments, shared formatter and memory-output callback; native %s/%03d yields scene/007, return 9
strcpy 0xc9602 Upstream instruction-pattern match; six native cases include empty/unaligned strings and a write canary
strlen 0xc96a3 REPNE SCASB, excluding the terminator; six native cases
abs 0xc96bc Test/negate; three native non-overflow cases
Near malloc / free 0xd3820, 0xd36ef Runtime heap allocation/release helpers and heap-growth retries; static identification
x87 sine helper 0xd22e7 Hardware FSIN path plus reduction fallback; seven native hardware-path cases

The 28 string/integer cases, formatter case and seven sine cases execute isolated native routines. Flat segment selectors are initialised for instructions that reload ES, such as strlen; this setup is normally supplied by the DOS extender. Heap and filesystem operations are not exercised by these captures. The file-handle wrappers at 0xcc3e4, 0xcc410 and 0xcc41c forward to lower-level open/close/read routines; the thin wrappers impose the engine's 16-bit handle interface and should not be confused with the underlying library entries.

TFX also uses its own allocation layer: 0xc90ec reserves space from an arena, 0xc91a0 records a temporary allocation with a size prefix, and 0xc91f0 releases tracked temporary storage. Scenery handles and linked actor/projectile structures are further engine-specific machinery. The generator at 0xc98a3 is the game's own captured random sequence; it should not be renamed standard rand just because it supplies random values.

No C++-specific language machinery is positively identified yet. The survey distinguishes four questions:

The LE debug offset and length are both zero, and the file ends exactly with the initialised LE image; no appended debug-symbol block is present. Runtime function names in the browser are research annotations, not recovered linker symbols. Upstream Open Watcom is a later descendant of the compiler runtime and is useful for comparison, not proof that this exact revision was used in 1994. The pointer/call-pattern survey covers the decoded instructions, not every executable byte or runtime state. Absence of these markers cannot rule out C++ classes without virtual methods, disabled RTTI/exceptions, or inlined templates. The defensible description is therefore a Watcom C/C++ toolchain with clearly identified C library use; predominantly C game source remains an inference, and the combined runtime banner alone does not prove C++.

Development leftovers and embedded records

The development survey traces the terrain-buffer joke diagnostic, default high-score roster and encoding, retained interactive graph debugger, parser defaults and animation-fix arguments. Isolated native checks distinguish those working paths from unreferenced strings and ordinary documented editor features.

Embedded executable

DOS/4G precedes an embedded DOS MZ header at file 0x352a4. Its extended-header pointer is 0x60, locating the LE header at 0x35304. This distinction matters: LE table offsets use the LE origin, while this file's data-page offset uses the embedded MZ origin. Treating both as LE-relative shifts the image by 96 bytes and produces plausible-looking but incorrect addresses.

The object/page tables describe 140 physical pages of 4,096 bytes. The final page contains 1,683 bytes. The three objects are:

Object File start Preferred address Virtual bytes Initialised bytes
1: code, tables and renderer working data 0x70aa4 0x10000 505,042 505,042
2: small executable object 0xecaa4 0x90000 612 612
3: strings, globals and uninitialised data 0xedaa4 0xa0000 166,208 59,027

Object 3 therefore has 107,181 bytes of uninitialised storage, which must be zero-filled when reconstructing an image. It is incorrect to read that region from subsequent file bytes. The LE entry point is object 1 offset 0x43aa4, or file 0xb4548.

The decoder consumes 28,960 fixup records and retains 28,933 distinct relocation sources after collapsing duplicate cross-page records. All observed source types are 32-bit offset fixups. Signed page-relative source offsets, 16/32-bit target offsets and object references are decoded explicitly. A few linker-biased target expressions lie outside the nominal object's range: their raw values are retained, rather than given a fabricated file address.

The record layout is checked against IBM's linear executable format specification, especially its fixup-record section. That reference documents the general encoding; the addresses and counts above come from the reference executable itself.

Earlier DID executable layout

The earlier TFX builds and the Inferno cinematic preview do not expose the whole program through their small LE images. That LE is a bootstrap which loads three uncompressed sections from the same file, using a 52-byte footer and a separate page-grouped relocation stream. No decompression is needed. The data allocation exceeds its stored extent; the bootstrap's BSS-only LE object also has no file page.

The engine-build comparison documents this layout and validates all 175,476 fixup sites across six early builds against their original relocation helpers. inspect_did_executable.py supplies file-offset-preserving section/fixup records for those images, alongside ordinary LE handling for September 2.05. Analysis-selected allocation bases are explicitly distinguished from genuine LE preferred addresses. The comparison captures twenty-four native flight-preset calls across the eight engines and retains their exact data writes.

Address conventions

Research notes and browser links use file offsets. Three other address forms occur in the code:

The relocation identifies the object. Numeric magnitude alone cannot reliably distinguish code, globals, strings or pointers. The browser keeps the original operand spelling and annotates its object/offset/file target; it does not pretend that an unrelocated operand is already a linear runtime address.

Control-flow coverage

The current pass follows 95,061 instructions, totalling 336,889 distinct decoded bytes, from 2,161 entry roots. Roots include the LE entry, direct call destinations, known interpreter tables and plausible relocated callback pointers. These are analysis roots, not an original source-function count: assembly handlers can share tails and enter the middle of another routine.

Fourteen interpreter/switch tables are exposed: shape conversion, shape drawing, scenery events, part animation, presentation loading/execution/sub-flow loading, two player-flight mode switches, aircraft waypoint commands, three presentation-font dispatch tables and synth-bank preselection. Ordinary direct branches are followed; indirect sites remain visible. Absolute callback assignments can supply possible destinations, which are listed separately from direct calls. They are static evidence, not a runtime trace proving that a particular destination was selected.

Six undecodable traversal sites are retained in the report, including shared renderer paths. The executable contains self-modifying renderer code and embedded tables; treating the whole code object as one continuous instruction stream would create misleading disassembly. Handler names and responsibilities are recorded in annotations.json, with their evidence.

Dynamic cockpit/HUD resources also contain x86 code. The catalogue identifies six such overlay resources; the executable browser currently analyses the main LE image. Their drawing contracts are now captured separately in the cockpit notes: five unique programs execute with emulated VGA masks, including a real-mode 16-bit prototype, retaining colour indices and coverage. Full live HUD composition remains separate work.

Launch and configuration

The native command-line entry accepts a presentation-program name, with an optional leading environment assignment. START tfx selects the game graph; START DID=D: tfx selects resources from DOS drive D:. The configuration notes map the launcher/game keyword tables, HUD intensity controls, REALISM field order, retained memory flags and fixed VGA display setup.

The launcher comparison traces the original floppy hardware-stamp check, install-time archive assembly, the INT 60h/IRQBASE sound-service interface and actual cross-version startup tests. March and September share START.EXE; earlier launchers require the documented compatibility settings.

Reproduction

inspect_executable.py needs Python and Capstone. Run it from the research root:

python3 tools/inspect_executable.py

The isolated player-preset capture additionally needs Unicorn. capture_flight_presets.py loads the complete LE objects at their preferred addresses, zero-fills their virtual storage, applies relocations and executes each preset initialiser to a checked return. Its output covers native floating fields, with source offsets and exact bytes; field units and gameplay names require further tracing.

Continue with game flow and simulation, model programs, scenery definitions and the analysis index.