dm2-graphics: low-severity batch (feature-flags word, LZW mode 1, odd-width remainder quirk, dropped-record superset, Sfx byte-order footgun) #194

Closed
opened 2026-07-05 07:48:42 +02:00 by jqueuniet · 1 comment
jqueuniet commented 2026-07-05 07:48:42 +02:00 (Migrated from codeberg.org)

Low-severity batch from the dm2-graphics fidelity review. None of these change fixture-decoding behaviour today; they are latent divergences vs the reference engine ([SKPROJECT] SKWINSPX/src/v5) or API/doc polish.

  • The (0, 0, 0x0b, 0) inline word is a per-archive feature-flags word — decoded bits so far: 0x01 = 4-bit records carry a trailing per-image Palette16 (see the trailing-palette issue), 0x20 = dtSnd payloads have 6-byte rate headers (see the console-SFX issue), 0x40 = 8-bit image dialect + is_pc_family (already used, v5/bgdat.cpp:1118-1132). Observed values: DOS 0x7b, Mac US 0x7b, Amiga 0x2b, Mac JP / PC-98x1 / IBM PS/V 0x0b, Sega CD / FM-Towns 0x0a. Worth a typed accessor (e.g. Archive::feature_flags()) instead of three ad-hoc bit reads, and a table in docs/formats/graphics-dat.md. Note Amiga sets 0x20 but not 0x40 — "PC-family" is per-subsystem, and the current docs use the term for both axes.
  • 8-bit mode 1 (LZW) is implemented in the engine (v5/gfxdec.cpp::dec9_1, line 547) and rejected by the crate as ModeUnsupported(1). Fine while tests/lzw_mode_1_unused.rs holds across fixtures; the engine source is the port target if a mode-1 record ever surfaces.
  • Engine odd-width remainder quirk: in the odd-width decoders the trailing partial-row SPILL/TRANSPARENT run copies pixels1 (rest-of-row) pixels while advancing by pixels2 (v5/gfxdec.cpp:216-221, :422-427, :450-455); the crate fills exactly the run count. Identical output for well-formed streams (the overshoot is overwritten); differs only when a stream ends immediately after such a run, where the crate zero-fills and the engine leaves spilled pixels. Comment-level note in fill_run/spill_pixels so a future bit-exactness comparison isn't surprised.
  • Ent1::parse accepts records the engine drops: entries with cls1 > 0x1c or cls3 > 0x0e are excluded from the engine's runtime table (v5/bgdat.cpp:700). The crate keeps them (superset — fine), worth one docstring line on Ent1::entries.
  • Sfx::parse byte-order footgun is pinned but easy to hit: the flag quartet is byte-order-invariant so a wrong ByteOrder silently swaps the rate (test parse_does_not_validate_byte_order). Once SFX keys on ENT1 records (console-SFX issue), iter_sfx covers all callers and the raw parse could be de-emphasised.
Low-severity batch from the dm2-graphics fidelity review. None of these change fixture-decoding behaviour today; they are latent divergences vs the reference engine (`[SKPROJECT] SKWINSPX/src/v5`) or API/doc polish. - [ ] **The `(0, 0, 0x0b, 0)` inline word is a per-archive feature-flags word** — decoded bits so far: `0x01` = 4-bit records carry a trailing per-image Palette16 (see the trailing-palette issue), `0x20` = dtSnd payloads have 6-byte rate headers (see the console-SFX issue), `0x40` = 8-bit image dialect + `is_pc_family` (already used, `v5/bgdat.cpp:1118-1132`). Observed values: DOS `0x7b`, Mac US `0x7b`, Amiga `0x2b`, Mac JP / PC-98x1 / IBM PS/V `0x0b`, Sega CD / FM-Towns `0x0a`. Worth a typed accessor (e.g. `Archive::feature_flags()`) instead of three ad-hoc bit reads, and a table in `docs/formats/graphics-dat.md`. Note Amiga sets 0x20 but not 0x40 — "PC-family" is per-subsystem, and the current docs use the term for both axes. - [ ] **8-bit mode 1 (LZW) is implemented in the engine** (`v5/gfxdec.cpp::dec9_1`, line 547) and rejected by the crate as `ModeUnsupported(1)`. Fine while `tests/lzw_mode_1_unused.rs` holds across fixtures; the engine source is the port target if a mode-1 record ever surfaces. - [ ] **Engine odd-width remainder quirk**: in the odd-width decoders the trailing partial-row SPILL/TRANSPARENT run copies `pixels1` (rest-of-row) pixels while advancing by `pixels2` (`v5/gfxdec.cpp:216-221`, `:422-427`, `:450-455`); the crate fills exactly the run count. Identical output for well-formed streams (the overshoot is overwritten); differs only when a stream ends immediately after such a run, where the crate zero-fills and the engine leaves spilled pixels. Comment-level note in `fill_run`/`spill_pixels` so a future bit-exactness comparison isn't surprised. - [ ] **`Ent1::parse` accepts records the engine drops**: entries with `cls1 > 0x1c` or `cls3 > 0x0e` are excluded from the engine's runtime table (`v5/bgdat.cpp:700`). The crate keeps them (superset — fine), worth one docstring line on `Ent1::entries`. - [ ] **`Sfx::parse` byte-order footgun is pinned but easy to hit**: the flag quartet is byte-order-invariant so a wrong `ByteOrder` silently swaps the rate (test `parse_does_not_validate_byte_order`). Once SFX keys on ENT1 records (console-SFX issue), `iter_sfx` covers all callers and the raw `parse` could be de-emphasised.
jqueuniet commented 2026-07-06 06:22:46 +02:00 (Migrated from codeberg.org)

Fixed by #208 (merged into main).

Fixed by #208 (merged into `main`).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jqueuniet/dm2-tools#194
No description provided.