DARKER Research archive

Sprite atlas and remaining HUD extraction

Sprite browser · Indexed pixels and masks · Native selector checks · Procedural HUD

Extracted assets

965 PNG previews (including the empty weapon panel) preserve source-sheet associations rather than claiming 965 unique artworks:

JSON preserves palette indices, source rectangles, coverage masks, source palettes, drawing references and RGBA pixel hashes. Particle transparency follows the native scanline masks, not colour-key guessing. Weapon/digit rectangles retain their copied background pixels. Existing 364 main-directory masked HUD PNGs remain in analysis/interface/hud/.

Particle size and phase selection

Native 6AA6–6B32 selects these tables by projected depth:

Pixel size Table Depth interval, upper bound excluded
16×16 6B9C 0–1752
12×12 6B79 1752–2454
8×8 6B5E 2454–3510
6×6 6B47 3510–4914
4×4 6B34 4914–8190

Depth below 32 is rejected by the ordinary projected-object entry at 6A6F; the table's first interval describes selector arithmetic, not visibility. At depth 8190 and above, 6A59 selects a single-pixel colour from the 22-byte table at 6A43, subject to bounds checks. That colour table is retained in JSON.

Each size table has two five-byte source records (phase parity), a radius byte and 2*radius skip/width row pairs. Source X additionally uses a phase-pair lookup at 6BC7 and a variant selected by destination X bit 1. This is why atlas extraction must retain the two alignment variants. These are precomputed scaled sprites, not a single bitmap resized during ordinary rendering.

The source rows are normalised to the physical cockpit cache. 6B19 subtracts the viewport top before accessing a row table whose origin includes that same top; the offsets cancel. Sources therefore remain stable between normal cockpit and external views.

Effect aging at 6C01–6C28 and 6C65–6C8E derives a phase from remaining lifetime: remaining = (flags & 127)*64 - elapsed_ticks; negative remaining expires the effect. For ordinary bounded lifetimes, the phase is remaining >> 6. If flag bit 7 is set and that phase is at least ten, six is added to it. This establishes a reversed aging progression and an alternative bright phase range, not a single universal 0→21 animation. The browser now offers the two native-order sequences: fire 21→16, then 9→0, and smoke 15→0, plus a separate ascending atlas inspection order. Fire/smoke names come from visual identification; the splice at phase 9 is confirmed by the native bright-flag branch. Both aging implementations pass 68 synthetic lifetime/expiry checks; see sequence verification. Preview steps use approximately 128 ms (64 timer ticks), without claiming the exact first-frame duration or scheduling of each spawned effect. Follow-up: effect recipes now exports all 21 referenced recipe entry points, emitter timing/motion, attached sound records, building bindings and the separate damage-trail constructor. Composite spatial drawing and full scheduling remain separate. Another drawing path at 689F supplies the selector through the self-modified immediate at 6AA7.

Additional HUD selectors

The existing descriptor directories do not contain every mask:

The Skimma field 454B is now identified more precisely: producer 579C–57B6 emits value 2 if the player's height high byte is below 4, otherwise zero. A nonzero 7E61 additionally gates it with timer bit 0100h to blink. This is a low-altitude indication, superseding the tentative missile-lock label. The threshold corresponds to native height 1024; it is not converted to metres. Field 454E remains unresolved.

Caero source alignment: native cause established

The successful +8 correction is explained by the original view setup:

  1. 7919–7959 selects CX=08A8h for the normal Caero cockpit: top 8, height 168.
  2. 7974–7978 moves that top into the starting row-address high byte.
  3. 23B1–2406 preserves the view height and enters AF57.
  4. AF57–AF60 rebuilds the first 168 row addresses starting at physical row 8; entries 168–239 retain their original unshifted addresses.

HUD source artwork lies in the shifted range while instrument destinations/restoration rows lie below it. Consequently only source artwork needed the +8 correction. Skimma cockpit and ordinary external modes use top zero. Four original-code setup probes verify these complete row tables. The exporter metadata now records a native viewport offset rather than an unexplained visual calibration.

Procedural HUD coverage

The Caero field 4542 is derived from heading at 56C7–56CF. Callback 5384–53D2 clears five previous marker pixels and draws five new ones around (54,215). The original coordinate-generation code has been executed for all 136 heading states; JSON preserves erase/draw coordinates and colour tokens. The five explicit drawing colour indices at 53D8 are 9E,9C,9C,9C,9E; their sheet-palette RGB values exactly match the five red-highlight pixels at heading state zero. The five erase indices at 53D3 are zero. The browser now clears that initial highlight and draws the selected heading using these colours. This reproduces the marker rather than leaving a fixed red mark plus a white inspection overlay. It does not simulate radar contacts.

The saved DOSBox memory capture identifies the patched drawing call at 53CC as targeting DBC1. That routine reads the colour cache using AH and writes a pixel; it does not sample the five initial compass pixels. Thus the proposed sampled-highlight interpretation predicts the correct colours, but the actual implementation uses a hardcoded five-colour sequence.

Remaining procedural work is explicitly separate from missing sheet sprites: live radar contacts and projection, target/lock markers, the flashing missile-view indicator, complete instrument-update timing and full effect recipes. The current exports do not constitute a live cockpit renderer.

Verification and reproduction

PYTHONPATH=/tmp/darker-python python3 tools/extract_hud_procedural.py
python3 tools/extract_interface_sprites.py
PYTHONPATH=/tmp/darker-python python3 tools/verify_particle_sequences.py
PYTHONPATH=/tmp/darker-python python3 tools/verify_interface_sprites.py
python3 tools/extract_hud.py
python3 tools/interface_browser.py

Verification executes 440 native particle selections (all size/phase/alignment choices at both viewport origins), 293 native HUD selections and four complete viewport row-table initialisations. The particle blitter and HUD copy calls are intercepted at their argument boundaries; these checks do not emulate final VGA pixel transfers. Existing masked-blitter verification remains separate. Browser checks covered all four sprite categories, five particle sizes, both alignments, phase sweep, image loading and the compass slider with no JavaScript errors.

Selection-zero correction: visual inspection identified the earlier X=140 crop as part of the altitude indicator. Directly supplying zero to the isolated coordinate setup did not establish a valid icon. The export now explicitly represents no sprite for zero; exact native clear/skip handling remains to be traced.

Follow-up: dynamic cockpit inspection combines the assets, verifies radar projection and strip transitions, exports native target outlines, identifies Skimma 454E as the selected weapon reserve/reload counter, and recovers the engine’s separate dim-state source patch.

Incoming-missile markers and flight quit confirmation

The Caero's central HUD includes nearby hostile-projectile markers, independently of the four aircraft-pursuit lamps. Native 5CE0 → 5E6A → 5A75 invokes this in the cockpit and forward full-screen view. 5AC9 scans the hostile projectile list, rejects hidden records and uncovered radio cells, then rotates their horizontal positions by player heading. 5B76 magnifies these radar coordinates eightfold and clips to squared radius 0x0c30. The preceding coordinate window extends seven city cells either side of the player, with an exclusive positive edge. This is a local overhead warning display, not a perspective marker on the missile mesh: shots behind the player appear below the sight centre.

The six-pixel navigation mask at 6B52 uses the alignment-specific sheet columns at 5CDC and row 8. 5B92 overlays a twelve-pixel centre whose colour reflects signed height difference from the camera. Radio outages suppress these warnings; tunnels have full coverage. The scan includes missiles aimed at scenery as well as the player. The Skimma and external/missile-follow views do not call this path.

Escape's native handler B7CE toggles the in-flight quit prompt and records the low byte of the game clock. 3E9F displays it for 170 ticks of every wrapping 256-tick period, while flight continues. Q's handler B7DA only requests exit when confirmation is active; a second Escape cancels. The prompt is drawn at physical row 120, with the original compact font and colour pair 165F. Its English, French and German text and authored horizontal positions come from E3F6, E5E7 and E822 respectively.