DARKER Research archive

Script-linked presentations, proportional fonts and cockpit layouts

New browsers: presentation clips and fonts / interface.

Presentation extraction

tools/extract_presentations.py associates 263 explicitly selected animation ranges with their images, palette history, layout, music selection and script offsets. It also exports 47 still pages, displayed in the same scene order. All frame references resolve. The browser groups these ranges under 124 record entries plus a synthetic title scene and exposes the ordered page chains, including entries with text but no animation. It embeds the original compressed streams for offline playback and frame stepping. Next page advances still pages, which remain on screen until requested. The first mission starts with both quotations over 00_022, then the welcome page and animated briefing. Play scene runs all extracted ranges and still pages once in script order, updating the background, palette and music links at each transition; it can be paused and resumed. Loop clip repeats one selected range. Sequence playback stops on the final frame and can be restarted. For scenes with counted film captions, playback instead uses script-paced intervals with delays, animation waits, concurrent channels and subtitle expiry. Other scenes retain the convenience clip sequence.

The scene and music catalogue maps fresh presentation entries to music groups and links each group to its complete intro/loop FLACs across the six exported playback modes. Gallery groups are one-based; script selectors are zero-based. For example, level 1’s opening quotations use group 6, before the welcome and main briefing select group 2. Startup and the menu use group 1; the committal ceremony and final presentation select group 6. Normal flight has no background music.

tools/presentation_music.py follows explicit next-page pointers after page stops, retaining music and presentation state within a record. tools/build_presentation_music.py generates the readable catalogue and JSON. A missing selection means inherited caller state, not necessarily silence; seamless continuation may bypass a briefing entirely.

Animation frame X coordinates are absolute screen positions; Y coordinates are relative to the scene image origin (BFD3 patches the blitter at DABA). The web viewer applies that vertical offset, matching the game renderer: the first briefing portrait needs 24 additional source pixels downward, with no horizontal offset.

The browser is not a complete presentation virtual machine. Palette fades, user input and persistent state across records are not simulated. Captioned scenes follow delays and animation waits, including the single delayed pass of conditional-repeat commands; their animation channels advance together. Other scenes retain isolated-channel timing. Formatted briefing text is overlaid using the original bitmap masks and palette colours, with English/French/German selection and a visibility checkbox. Raw language cursors follow page commands and fresh-entry displacement words; margins, line steps, positioning, tabs and centring follow the game formatter. Pages are retained or cleared as instructed, and full-screen backgrounds remain beneath portrait image rectangles. Malformed or unsupported source text is labelled in the viewer rather than guessed. The browser hides the 18 direct-entry text records bypassed during seamless continuation (including levels 17 and 18) by default; a checkbox exposes them for script inspection. They remain explicitly labelled in the music catalogue. The page chain is an ordered script inspection, not a timed recording. Level 101’s film dialogue uses the three counted-message channels: original placement, nine-row backing strips, colour pairs and expiry are preserved. Empty message entries neither replace a caption nor add a delay. All three languages share the script timing. Music links are listening references rather than synchronised playback. Palette skips retain prior known entries within the record; unknown retained colours are magenta.

Recovered consumers:

Consumer Interpretation
BFC0–BFDE Layout operands: text Y, height, width word, image Y, image X word. Height zero shortens the instruction to two operands.
C07A–C09D Image resource is archive 03, slot (2Fh + operand) & FFh. Load also applies palette updates.
C09E–C0C7, C0C8–C0D8 Show/load background using the layout and image blitter. Palette-only resources do not replace image pixels.
C0FB–C13F Animation resource uses operand high two bits as archive and low six as slot. Frame pointers append to a descriptor's table.
DADA–DAFC Reset animation-load descriptors at SS:9D00 and SS:9EE3. The second table begins at global frame index 161.
BFA5, C140–C16C Select one of twelve current/target animation pairs, set a range, wait for it, or disable it with A0h.
DAA7–DAD9 Draw active pairs in descending order, then move each current frame by one toward its target.
D938–D9CC Presentation iterations use the record tick multiplier; clip preview interval is multiplier / 500 seconds.

The frame decoder writes into the presentation page at Y+128; the browser uses the frame's stored display coordinates, removing that page offset. Backgrounds use script layout coordinates. Complete VGA page composition remains to be verified as a whole.

Thirty image-show events have exported layout associations. Eleven distinct nonempty image resources in these events have dimensions whose products exactly match their decoded pixel counts. These promote specific script-used dimensions beyond earlier visual guesses; they do not settle every image-sheet layout.

analysis/presentations/clips.json preserves script associations and layouts. verification.json records native decoding checks of the distinct first/last frames used by the clips, extending the earlier first-frame-only checks. This verifies frame pixels, not the complete compositor or timeline.

Three fonts in resource 00/29

The startup loader at 03CA–03D2 loads resource 00/29 at SS:2978. Font setup E29C receives its base, glyph count and a byte that patches the per-glyph advance instruction.

Inspection name Runtime base Resource offset Glyph count Setup examples Advance
interface 2978h 0000h 107 0A67–0A75 width
compact 4BE2h 226Ah 97 3CCA–3CD2, 9CDF–9CE7, C041 width + 1
wide 60D0h 3758h 96 C017–C020 width + 1

Each font has N vertical-offset bytes, N width/height pairs, then four N-entry relative-pointer tables for X modulo 4. Each glyph row contains ceil((width + alignment)/4) bytes. Low nibbles are VGA plane-write masks; high nibbles supply the per-plane pattern used by the runtime colour lookup. Both are exported, along with the original bytes. The atlas uses inspection colours to distinguish the two patterns; it does not claim to reproduce runtime palette/latch state.

Characters start at code 33. Space advances four pixels. Extra glyphs retain numeric codes rather than assuming a Unicode mapping. Individual PNGs, atlases, exact metrics, source offsets and all alignment variants are in analysis/interface/. The font browser accepts sample text and exposes each glyph's dimensions and advance.

tools/verify_fonts.py executes E29C and E1FE in Unicorn for all 300 glyphs × four alignments = 1,200 cases. All VGA write coordinates and final X advances match the exports. Runtime colour mapping is outside that assertion. All four decoded masks also agree after accounting for horizontal alignment.

Cockpit image transfers

BC50–BC82 selects the cockpit sheet from craft definition +0C. The five configuration values select resources 18, 16, 17, 18, 16 respectively; resource 15 is the sign-bit fallback.

DB71 writes a relative CALL displacement into DB9F, so its BP parameter is not an absolute function address. For cockpit setup, DBA1 + E3D1 wraps to BF72. That wrapper draws:

  1. Source (0,0,320,136) to destination (0,0).
  2. Source (0,96,320,104) to destination (0,136).

The overlap explains why a 320×200 graphics sheet should not simply be stretched to a 320×240 cockpit. The new static PNGs reproduce the transfers. The 3D viewport and instruments overwrite portions afterwards. Individual gauge frames, indicator selection rules, clipping and live HUD drawing remain to be reconstructed; these PNGs are not a finished cockpit renderer.

A further useful discovery is the text formatter at B292: controls 0–7 return, set position, change colour, newline, tab, set spacing, centre a line, and draw through an alternate path. Printable-run extraction alone does not recover these operations. Full multilingual message-command decoding and association with presentation events remain open.

Reproduce

python3 tools/extract_interface.py
PYTHONPATH=/tmp/darker-python python3 tools/verify_fonts.py
python3 tools/interface_browser.py
python3 tools/extract_presentations.py
PYTHONPATH=/tmp/darker-python python3 tools/verify_presentation_assets.py

Pillow is required for font/cockpit PNG exports; native verification uses Unicorn. Original DOS files are untouched.

Portable font exports (2026-09-23)

Download and preview the portable fonts. tools/export_portable_fonts.py produces OpenType/TrueType TTF and WOFF2 versions of all three fonts, plus coverage-layer companions. The outlines preserve source pixel boundaries and spacing; all 300 original glyphs have stable U+E000+byte addresses, with conservative ordinary Unicode aliases. Font raster checks cover all 600 glyph/layer combinations and their advances at 16 px. All six webfonts load in Chromium. Usage and encoding details.

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

Repeated background selection retains the portrait

The background selector (3B) clears the page text, but native BFE4–C007 compares the low six bits of the requested type against the retained type and skips loading/drawing the base image when they match. A clear-screen command sets that type to 3E. In level 98, D3A repeats type 1 without another portrait draw: the 03_010 portrait loaded at D2C therefore remains behind the animation ranges at D32 and D3F. Reloading 00_023 at that boundary incorrectly replaces the portrait with its black placeholder. Both the web preview and C++ renderer now retain it.

Startup order and synthetic title

The browser lists the startup logo animation (04_015 / 1) first, then the synthetic DARKER title, then level 1’s quotations and briefing. The title is executable-driven rather than a resource-04 script: native 9CFE uses image 00_014, placed at (16,65), with the compact-font TM overlay at (256,84). The preview reproduces the 1,024-tick fade (2.048 seconds at 500 Hz), using the original sine table at 944A and the AFA2/AFAD brightness/DAC arithmetic, followed by a 4,000-tick hold (eight seconds). It retains music group 1. This scene is explicitly labelled synthetic; the music catalogue still describes 124 actual script records. Browser animation consumes elapsed time rather than advancing only once per refresh.

Message-only records and fixed-frame victory page

The Supa supply-pad record (04_015 / 6) and rolling-blackout record (04_015 / 7) have no presentation graphics. Their scene entries display ordered transcripts (21 and nine messages respectively), selectable in English, French or German. The canvas is hidden for these entries. They remain gameplay scripts, not timed movies: in particular, blackout messages are interleaved with world-state changes.

Nightmare victory (04_015 / 4) intentionally selects one fixed frame: 49 04 at 09BD, followed by formatted text at 09BF and a script stop. Its completed page combines 03_007, frame 4 from 01_000 and the victory text. A fixed-frame selection before the formatter must use the completed page’s text, rather than an empty snapshot taken at the selection command. The viewer now displays the full page and waits for input.

Language selection and credits

Retail credits are translated, rather than shared English artwork. The language byte at BAEB selects a pointer from table 9D7A: English at 9D80, French at 9EA6, German at 9FE1. The original B292 formatter draws these pages over the shared credits background and 00/28 logo. Names remain as authored; headings and acknowledgements vary between languages. The French text even omits Joan from the English acknowledgement, which is preserved rather than silently corrected.

The original menu labels and scenario text also have three language variants. NIGHTMARE, STAR THREE and the hidden menu's “What do you want?” are shared English literals in the executable, not missing translations.

Confirmation shortcuts remain Y/N in every language: native code at 06FF–0707 compares the same ASCII letters regardless of the translated Yes/No labels.

German level 76 briefing corruption

The first German briefing page of level 76 (04_009 / 3) contains character code 153 at resource offset 27EB, outside the selected wide font's declared range (33–128). Retail testing in DOSBox confirms garbled, overlapping text in the second line, with the corrupt area flickering while the page remains on screen. This is an original-game defect, not merely a missing web font glyph. The flickering is consistent with the unchecked glyph lookup reading changing memory; the precise memory contents have not been traced.

The presentation viewer continues decoding the surrounding text and marks the undefined character with an amber outline box. It uses an explicit eight-pixel replacement advance to keep the preview readable, with an on-page explanation. This is an editorial annotation, not a corrected translation or an attempt to simulate the original's unstable bitmap and text overlap. The original source bytes remain unchanged. Later pages are decoded normally.

The playable reconstruction uses the selected font's question mark when an undefined code would read bitmap data beyond the font resource. This prevents the German level 76 briefing from throwing an exception. Its replacement width is the question mark's original advance; the viewer's amber annotation remains a separate inspection aid. Neither attempts to reproduce changing memory.