feat(dm2-graphics): apply the per-object cls3=0x0E remap LUTs #273

Merged
jqueuniet merged 0 commits from refs/pull/273/head into main 2026-07-18 07:42:53 +02:00
jqueuniet commented 2026-07-18 07:32:50 +02:00 (Migrated from codeberg.org)

Implements #265: wire the entry-492/496 selector chain into palette resolution and apply the resolved cls4 remap LUT to per-object image palettes.

Static-RE findings driving the design

Disassembling the Sega CD SKUL.FTL GDAT resolver (entry 313, 0x2abc0) and its record finder (0x2aafa) calibrated the argument ABI against records that exist in every archive, correcting the tuple notation used by the issue and the earlier notes:

  • The gate (entry 496) is word at (0x0B, 0x09, ·, object_type) == 1 — not GDAT(0x0B, 0, 0x0A, type). It opens exactly for object type 8, the wall trap shooter; the trailing #0x9 at the policy sites is cls2 = 9, not a dtype.
  • The LUT fetch is (0x0B, 0x09, 0x0E, cls4). The only such record in any staged fixture is cls4 = 0x67 — payload -----------b----, a functional identity remap. All 12 retail ports carry the full gate+LUT trio, PC family included; the demos ship none of it.
  • The entry-492 selector input is the actuator's documented ornate index (behaviour bits 15:12); real dungeons have 30–31 type-8 trap shooters with nonzero ornate indices. The selector's byte table remains runtime generation state (per #269) and is statically approximated by the documented unique-record rule.

Changes

  • Archive::object_lut_gate / object_lut / find_object_lut — the gate → ornate-nibble → LUT chain.
  • Lut0e::remap_palette + RemappedPalette — the application step (entry p of the result = entry lut[p] of the 16-entry sub-palette).
  • Archive::find_image_palette_objfind_image_palette_ctx with the per-object remap layered on top, returning Base/Remapped.
  • Docs: palette-lut.md gains the resolver-ABI calibration section plus inline corrections; graphics-dat.md's per-port 0x0E count table now reflects reality (PC retail ships 4 records, not 0); ROADMAP item moved to landed.

Acceptance (from the issue)

  • LUT application exercised by tests: 10 new tests, using the real identity LUT 0x67 on the live path and the genuine remap (0x09, 0x43, 0x0E, 0x66) for a non-identity application.
  • Byte-identical output where the gate is not taken: extraction path untouched; pinned by the gate-closed and PC-family wiring tests.
  • Fixture/object exercising the path: documented — the archive trio in all 12 retail ports plus the trap shooters; the engine path is live but visually a no-op (identity LUT), and only for the actively-targeted object at runtime.

Closes #265

Implements #265: wire the entry-492/496 selector chain into palette resolution and apply the resolved `cls4` remap LUT to per-object image palettes. ## Static-RE findings driving the design Disassembling the Sega CD `SKUL.FTL` GDAT resolver (entry 313, `0x2abc0`) and its record finder (`0x2aafa`) calibrated the argument ABI against records that exist in every archive, correcting the tuple notation used by the issue and the earlier notes: - The gate (entry 496) is `word at (0x0B, 0x09, ·, object_type) == 1` — not `GDAT(0x0B, 0, 0x0A, type)`. It opens exactly for **object type 8**, the wall trap shooter; the trailing `#0x9` at the policy sites is `cls2 = 9`, not a dtype. - The LUT fetch is `(0x0B, 0x09, 0x0E, cls4)`. The only such record in any staged fixture is `cls4 = 0x67` — payload `-----------b----`, a **functional identity remap**. All 12 retail ports carry the full gate+LUT trio, PC family included; the demos ship none of it. - The entry-492 selector input is the actuator's documented **ornate index** (`behaviour` bits 15:12); real dungeons have 30–31 type-8 trap shooters with nonzero ornate indices. The selector's byte table remains runtime generation state (per #269) and is statically approximated by the documented unique-record rule. ## Changes - `Archive::object_lut_gate` / `object_lut` / `find_object_lut` — the gate → ornate-nibble → LUT chain. - `Lut0e::remap_palette` + `RemappedPalette` — the application step (entry `p` of the result = entry `lut[p]` of the 16-entry sub-palette). - `Archive::find_image_palette_obj` — `find_image_palette_ctx` with the per-object remap layered on top, returning `Base`/`Remapped`. - Docs: `palette-lut.md` gains the resolver-ABI calibration section plus inline corrections; `graphics-dat.md`'s per-port 0x0E count table now reflects reality (PC retail ships 4 records, not 0); ROADMAP item moved to landed. ## Acceptance (from the issue) - LUT application exercised by tests: 10 new tests, using the real identity LUT `0x67` on the live path and the genuine remap `(0x09, 0x43, 0x0E, 0x66)` for a non-identity application. - Byte-identical output where the gate is not taken: extraction path untouched; pinned by the gate-closed and PC-family wiring tests. - Fixture/object exercising the path: documented — the archive trio in all 12 retail ports plus the trap shooters; the engine path is live but visually a no-op (identity LUT), and only for the actively-targeted object at runtime. Closes #265
Sign in to join this conversation.
No reviewers
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!273
No description provided.