Saved campaigns, pilots, demos and generated training
All extraction is read-only. Original files remain in their installations. The resource browser exposes saved theatre maps, decoded named pilots and scrub/play controls for recorded flight paths.
How the save files fit together
A .THT file saves one ongoing theatre. It is a single structured file:
a header containing theatre information and an embedded pilot/statistics record,
followed by the complete saved world. It is not an archive of named files.
One pilot can own several saved theatres, each in a separate numbered .THT.
| File | Role |
|---|---|
PILOT.SCR |
Pilot roster, career information and references to each pilot's saved theatres. It does not contain the world maps. |
THT/<number>.THT |
Persistent save for one theatre, including its world and associated statistics. |
3UNIV.DAT |
Shared working copy of the current world, passed between the separate DOS programs. It contains the world stream without the .THT campaign header. |
During ordinary saved-campaign play:
- The menu loads the selected
.THT's world into3UNIV.DAT. - The flight engine reads that working world, runs the sortie and writes its
changed world back to
3UNIV.DAT. - The menu incorporates the updated world into the selected
.THTand updates the appropriate pilot statistics.
The single 3UNIV.DAT is reused when another saved theatre is selected or a
fresh scenario is opened. Changes to it can survive in the corresponding saved
campaign; they do not alter the original DEF/CCT scenario for every pilot.
Persistence rules
describe which changes are saved and the direct-engine-launch qualifications.
Saved theatres
All 22 distinct supplied .THT files have a 671-byte header, followed by a complete 64-tile world stream. Both plain and XOR-encoded type words are accepted only after the whole world validates. Every byte boundary and object type is checked. The stored world is the live saved campaign, so its objects can differ from a pristine DEF/CCT.
| Header offset | Bytes | Interpretation |
|---|---|---|
| 0 | 28 | NUL-terminated theatre title |
| 28 | 12 | Original definition basename |
| 40 | 8 | Four campaign words, retained without speculative labels |
| 48 | 623 | Embedded pilot/statistics record |
| 671 | remainder | World stream, including its twelve-byte trailer |
The embedded record uses the same 623-byte structure found in PILOT.SCR: a 22-byte name field, flags at offset 22, then statistics and other state. It is plaintext in the theatre file. Source definition basenames reconnect saved maps to their TWR briefings and mission starts. The compact-record expansion now verifies 898 objects across the normal reference's original and saved worlds against 3.EXE.
Named pilots
The supplied PILOT.SCR files are not corrupt or compressed. They use a random-byte mask:
- Plain little-endian
uint16format version: 24. - Plain
uint16seed. - Remaining bytes XORed with the original library PRNG output.
The 32-bit PRNG is state = state * 0x015a4e35 + 1, wrapping at 32 bits. Each mask byte is ((state >> 16) & 0x7fff) / 128, using integer division. The native reader's masking wrapper is at MOAG file offset 0x17ca5; srand/rand are at 0x3c71/0x3c82 in the normal 3.42 front end. These are Borland runtime routines, separate from the flight engine's random generator.
Most decoded streams contain a 24-byte header with the pilot count at +22, then 623-byte pilot records. The played 2.00 floppy instead has a 23-byte header with the count at +21, despite also declaring format version 24. Both layouts have one extra check byte after the first pilot, or after the header if there are no pilots. Their on-disk sizes are 29 + 623 * count and 28 + 623 * count respectively. The decoder selects a layout only when the count matches the complete file length exactly; it does not silently discard a byte.
Pilot names occupy the first 22 bytes of each record. Flags follow; bit 0x8000 identifies the selected record in the front end. The rest of the record and all header/check bytes are exported intact. The precise labels of every statistics word and the check-byte calculation are not inferred from sample values.
The normal reference currently has no named pilots; the expansion has two, your original installation has one, and the played 2.00 floppy has eleven. All four unique pilot files are in the browser. Their decoded masks have been checked against the original executable for every supplied byte; unlabelled statistics remain intact in JSON.
Rainmaker: surviving historical date
The old installation's PILOT.SCR contains a last-use/clock-check dword at
pilot-record offset 594 (0x252). Rainmaker stores 0x3d1ca82f
(1025288239), whose Unix interpretation is 2002-06-28 18:17:19 UTC.
Running this copy's original MOAG.EXE time conversion in isolated emulation
reproduces that exact value from a DOS system clock of 2002-06-28 14:17:19;
the unmodified runtime applies a four-hour summer offset. The stored value does
not establish the machine's actual geographical timezone or clock accuracy.
In this old install's MOAG, the last-use field is refreshed by time() at file
ranges 0x115f4–0x11600 (before launching the flight executable) and
0x18ddb–0x18de7 (configuration writing). The name-display helper compares it
with the current time at 0x19c44–0x19c68 and 0x19cbf–0x19cdd, detecting a
clock set earlier than the stored value. This establishes historical use/update
evidence, rather than a pilot-creation or mission-completion date. The runtime
time() implementation is at file range 0x470d–0x475a; this MOAG has a
0x3200-byte MZ header and initial data segment 0x187f.
All three Rainmaker theatre saves (Dusk, Atkinsville and Wasteland) have zero in the corresponding embedded-pilot field. They cannot individually be dated from it. The old install's current filesystem modification times are all from its copy here on 15 October 2026, including both pilot files and the three THTs; these do not preserve their historical modification dates.
Recorded demonstrations
The three unique recordings are shared by shareware 1.20 and the later expansion package. Their accompanying DEMO.NDX gives the titles Basic Mission, Junky Corridor and Vertical Mission, along with readable descriptions.
| File | Motion records |
|---|---|
| DEMO1.DMO | 2,456 |
| DEMO5.DMO | 1,111 |
| DEMO6.DMO | 1,013 |
A tag starts every record. Bit 0x08 must be set. With bit 0x80 clear, the record is 37 bytes: tag, three signed 32-bit positions, three 16-bit angles, three 32-bit velocity fields and three signed 16-bit acceleration fields. With bit 0x80 set, it is eight bytes: tag, three signed acceleration bytes, three angle high bytes and one packed byte containing the next two bits of each angle.
The compact reader updates the low velocity word, then sign-extends it to advance each 32-bit position. It preserves the stored high velocity word. The packed angle byte assigns bits 4–5 to pitch, 2–3 to yaw and 0–1 to roll. Full records with bit 0x20 set do not replace the stored aircraft state; the browser preserves this distinction. Other low tag bits are retained rather than given invented names.
A 0xff tag terminates playback. The writer also emits a final 36-byte payload, which the reader does not consume. It is preserved as unused terminal state. Files validate exactly to that final record.
All 4,580 decoded position/angle states match execution of the original 1.20 reader at 0x1d15. The viewer plots the route and allows scrubbing and playback. It does not resimulate aircraft physics or supply an invented world; the recordings do not embed their training scenery. Preview frame rate is selectable because elapsed wall-clock timing is not encoded in these motion records.
Built-in training world
The flight executable constructs training scenery procedurally at 0x904f–0x960a. extract_training.py runs that original routine in Unicorn. The harness supplies isolated object allocation; native code constructs the records, applies quality flags and chooses positions. No DOS filesystem or existing save is used.
Three examples are in the map browser:
| Example | Objects | Defence counts: KLA / vehicle sites / AAA |
|---|---|---|
| Default | 67 | 8 / 8 / 6 |
| No defences | 45 | 0 / 0 / 0 |
| One of each | 48 | 1 / 1 / 1 |
“No defences” still contains the airfield, fuel targets, forcefields and other training scenery. Inputs are the native count words at DS:0e6c, 0e6e, 0e70. The extracted examples use the original initial PRNG state; they are not an exhaustive catalogue of random training layouts.
Reproduction
python3 tools/extract_training.py
python3 tools/extract_resources.py
python3 tools/validate_extended.py
Native execution requires Unicorn. Verification results record demo and pilot checks; the generated world JSON retains every full 74-byte object record and generator input.
High-score tables
Four distinct PILOT.HSC files are now decoded. Each is 5,040 bytes and contains:
| List | File offset | Entries | Bytes per entry |
|---|---|---|---|
| Best missions | 0 | 10 | 180 |
| Top careers | 1,800 | 10 | 162 |
| Fastest careers | 3,420 | 10 | 162 |
All entries begin with a 22-byte pilot name. Mission entries then have a 22-byte theatre name and 130 statistics bytes. Career entries instead have 16-bit mission/resurrection counts followed by the same 130-byte statistics block. A signed 32-bit score and 16-bit retired/deleted flag finish each entry. Names, score, objectives, primary/other kills, mission and resurrection counts are exposed. The fastest-career list displays integer points per mission. Empty slots and the complete original record bytes remain in JSON.
Retail MOAG's display routines at file offsets 0xe9bd–0xef6a establish the
three table offsets, record sizes and display fields. Its two kill-summing
helpers at 0xf897–0xf8fc verify all 240 decoded primary/other totals across
the four files. The manual's high-score help confirms the meaning of the lists,
kill categories, retirement marker and three-mission fastest-career eligibility.
See remaining-verification.json.