# Saved campaigns, pilots, demos and generated training

All extraction is read-only. Original files remain in their installations. The [resource browser](resources/index.html) 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:

1. The menu loads the selected `.THT`'s world into `3UNIV.DAT`.
2. The flight engine reads that working world, runs the sortie and writes its
   changed world back to `3UNIV.DAT`.
3. The menu incorporates the updated world into the selected `.THT` and 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](mission-rules.md#persistent-worlds-aircraft-and-recovery)
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:

1. Plain little-endian `uint16` format version: **24**.
2. Plain `uint16` seed.
3. 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](../tools/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

```sh
python3 tools/extract_training.py
python3 tools/extract_resources.py
python3 tools/validate_extended.py
```

Native execution requires Unicorn. [Verification results](resources/extended-verification.json) 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](resources/remaining-verification.json).
