Commit Graph

3 Commits

Author SHA1 Message Date
d9c69c566c lsta: the count was never unreliable -- it counts sprites AND PRMD primitives
The module said "a few entries disagree with the actual frame count, so we walk
by the T8aD magic instead". They do not disagree. An LSTA is a display list of
inline elements that are either T8aD sprites or PRMD primitives (the flat
coloured quad the UI bundles use to dim a scene), and the count at 0x04 counts
both: across all 64 lists on the disc, count == T8aD + PRMD, with no exceptions.

The six lists that looked wrong (GP_DEBRIEFING_PILOTLOG, GP_MISSION_SELECT, two
language builds each) each hold exactly one primitive, which is the whole of the
off-by-one.

Also measured after the T8aD rectangle-list fix: all 1281 sprite frames decode,
100%.

parse() still returns sprites and skips primitives -- that is the useful
behaviour -- but the docs now say so instead of blaming the header.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 22:52:02 +00:00
ac10d3bd19 t8ad: a surface is a list of sub-rectangles, not a 256 grid -- disc decode 96% -> 100%
The "~15% deferred variants" were not variants. Auditing every T8aD on the disc
gave 19216 surfaces, 18442 decoding (96.0%) and 774 failing in two clusters:
GP_DIALOG strips declaring 524x63 with a "tile count" of 1 or 2 instead of 3, and
small textures in the six *2D language paks whose pixels ran past the end of the
file.

Both fall out of the per-tile header, which is not opaque flags: it is four BE
u32 -- dst X, dst Y, width, height. A 15x18 icon stores a 13x18 rectangle at
(1,0); pdmes010 stores (59,6,256,54) and (315,6,149,54), the second beginning
exactly 16 + 256*54*4 bytes after the first. So 0x1c is a RECTANGLE COUNT and the
256-grid reading was an accident of most surfaces being stored as full-width
bands.

Parser rewritten to that model, still refusing to guess: a rectangle must fit the
declared surface and its pixels must fit the file, else None. Disc decode is now
19216/19216 = 100.00%.

Two test fixtures were built to the old model and are corrected rather than
worked around. lsta's t8ad_frame wrote NO offset-table entry, so the decoder read
"pixels" from inside the header -- the test passed only because it checked
dimensions alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 22:37:21 +00:00
MechaCat02
e1ca95c0af feat(formats,viewer): decode T8aD textures, RATC bundles, LSTA sprites
Add three static PAK-format decoders in the Bevy-free formats crate and
present them in the explorer:

- t8ad.rs: linear 32bpp A8R8G8B8 2D texture (UI/HUD art). Dims at
  0x14/0x18, header size keyed by the type field @0x1c (container-safe).
  Decodes the ~85% RGBA variants; defers the rest (likely DXT) rather
  than misdecoding.
- ratc.rs: nested resource bundle — lists named children by magic scan.
- lsta.rs: sprite list — walks the inline T8aD frames.

Viewer: PakContent gains T8ad/Lsta/Ratc, decoded off-thread and bounded
by an 8M-texel per-entry cap; detail views show the texture, a sprite
grid, and a child list with thumbnails. Decoded images carry a
"colours unverified" note — channel-order/sRGB stays on the dynamic-RE
backlog.

Tests: 12 new unit + 3 real-disc (T8aD >=70% decode over the hangar
pack, LSTA frames, RATC named children incl. a decodable T8aD).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 16:49:50 +02:00