# Mission text, named places, stills and animation frames

Update 2026-09-21: [script-linked presentation clips, three recovered fonts and cockpit layouts](presentations-and-interface.md) are now available. Full movie composition and multilingual text-command playback remain incomplete.

2026-09-18. Initial inspection artifacts:

- [Recovered imagery gallery](../analysis/media/index.html): 48 palette-bearing
  images/graphics sheets and the first frame from each of 45 animation resources.
- [Full palette browser](../analysis/media/palettes.html): 32 distinct recovered
  definitions/composites, grouping 54 labelled source/bank entries. Each grid has
  all 256 indices, stored RGB values and explicit undefined/retained entries.
- [English text runs](../analysis/text/english-runs.txt) and
  [multilingual text index](../analysis/text/mission-text.json).
- [Animation frame directory](../analysis/media/animations.json),
  [still-image inventory](../analysis/media/stills.json), and
  [original-code verification](../analysis/media/frame-verification.json).

## Text and district names

`DARKER.04` contains readable mission briefings, in-flight messages and narrative
text. Its 16 decoded resources contain **124 stored records**; these are not a
one-to-one numbering of the game's playable missions.

The loader `BAC1–BAF5` navigates records with a little-endian length word:
`next = current + 2 + length`. Three further words partition a shared block and
English, French and German sections. Language-section offsets are relative to
the address immediately after the record length. The last language ends at that
record's length boundary. The export validates all boundaries to resource EOF.
Text is embedded in scripts, with formatting, timing and control bytes. It is
not an ordinary null-separated string table; printable runs can contain a stray
printable command operand at either end. The text export marks this limitation
and preserves exact source offsets. All printable runs are now retained, including single-word lines, single characters and digits. The earlier space/length filter incorrectly omitted lines such as “area.” and “disappear.” in `04_000`, record 7. Retaining runs also exposes isolated printable control operands; a full text-command decoder is still needed to distinguish those from displayed text.

For example, resource `04_001`, stored record 0, includes:

> Tolly, locate Burdekin Market on your map.
> Residents of Mardle Court have reported at least two Skimma Supas buzzing that area.

It also mentions a Skimma south of Janstead. Other resources name Zahner Storage,
Sperwent Power, Kismet Square, Aspel Arcade, Romilday and Garth. These are direct
links between the narrative and the supplied paper map's named locations.

No model-name or district-name field has been identified in the decoded **8-byte
building directory** or **11-byte model header**. Geometry identity and narrative
place identity are separate layers so far. A briefing naming a district does not
by itself prove which grid cell or building type bears that name. Mission-script
coordinates and a registration of the paper map against the 128×128 grid are
still needed for a reliable named-location overlay.

The paper map explicitly labels Storage HQ, Comms HQ, Research HQ and Admin HQ;
it calls the glowing towers “Energy Beacon”. The landing logic independently
supports the gate/approach identification for types 17–24; see
[model components](model-components-and-aliases.md). It does not yet associate
every gate instance with a particular named HQ.

## Palette-bearing images

Known sources are `00/14–19`, `00/21–27`, and `03/7–44`. A resource begins with
the same palette-update stream used by the model palettes. Some contain only a
palette. Otherwise the following bytes are indexed pixels. The loading path at
`4149–4176` converts these sequential bytes into four display planes.

The gallery reveals cockpit graphics/sprite sheets, backgrounds, character
portraits and illustrated scenes. Examples include `00/16` (Caero cockpit
sheet), `03/20` and `03/35` (320×160 illustrated scenes). Stored image dimensions
are not included in the palette stream: the gallery currently uses dimensions
inferred from row continuity and visual inspection. Scene-script confirmation
of every width/height and composition remains outstanding. Some resources are
component sheets rather than standalone finished screens.

Palette skips preserve earlier runtime entries. The palette browser hatches
these slots instead of fabricating RGB values. PNG previews use magenta if a
pixel needs an unresolved entry. Identical RGB/skip definitions share a browser
entry, with all sources listed. The underground gameplay palette is the known
composition of `00/16` followed by `00/19`; the standalone `00/19` update is also
available. Source RGB values precede fades and VGA DAC quantisation.

## Animation format

The following resources parse completely as positioned frame sequences:

| Resources | Sequence count |
| --- | ---: |
| `00/0–13` | 14 |
| `01/0–9` | 10 |
| `02/0–13` | 14 |
| `03/0–6` | 7 |

Together these contain **1,144 stored frames**, not 45 confirmed complete movies.
The scene scripting can load and play multiple sequences and overlay frames.

Each frame has a five-byte header:

```
u16le x_start
u8    y_start
u16le payload_length
```

Payload commands, decoded from `DF36–DFA7`:

- `00–7F`: copy the next `command+1` literal palette indices; advance X.
- `81–FF`: leave `command & 7F` pixels untouched; advance X.
- `80`: end scanline, reset X to `x_start`, increment Y.

The frame ends at the header-defined payload boundary. These streams have
already passed through the outer archive LZ decompressor. The inner skip/literal
format explains why their original bytes do not resemble a standard AVI/Flic
header or an uncompressed rectangular image. Transparent skips permit overlays;
calling every frame a delta frame would require checking its scene's composition.

`C0FB–C13F` loads these resources and records frame pointers by advancing
`5 + word[frame+3]`. `DAFD–DB19` retrieves the positioned header; `DAA7–DAD9`
selects frame indices in animated channels and calls `DF36`.

`tools/verify_animation_frames.py` runs the original decoder in Unicorn and
captures VGA plane-selected pixel writes. The first frame from **all 45
resources** agrees exactly with the Python decoder: **254,008 pixel writes**.
All 1,144 frames pass strict length, command and scanline-boundary parsing;
original-code comparison currently covers the 45 first frames, not every frame.

The preview PNGs encode original palette indices as grayscale with transparency.
They are deliberately not presented as correctly coloured movie exports yet:
scene palette assignment, background composition, timing and script playback
order remain to be reconstructed. The coloured stills are already viewable.

## Reproduction

```
python3 tools/extract_mission_text.py
python3 tools/inspect_visual_assets.py
PYTHONPATH=/tmp/darker-python python3 tools/verify_animation_frames.py
python3 tools/model_viewer.py
```

Image export requires Pillow; x86 verification requires Unicorn. Browser checks
exercise all 32 palette selections, all 256 cells per selection, colour
inspection, image loading and the model-atlas link. No JavaScript errors or
broken images were observed; see `analysis/media/browser-check.json`.

Follow-up: the [mission-script and scenario investigation](mission-scripts.md) now exports setup for all records, object groups, objective lists, main bytecode and compact routes, with an [interactive scenario inspector](../analysis/missions/index.html).

Update: [masked HUD components and formatted multilingual text](hud-and-formatted-text.md) now provide descriptor-level cockpit assets and decoded text commands/message headers, with one flagged French source-data inconsistency.
