Use universal packed resource resolution

This commit is contained in:
gamer147
2026-07-21 20:23:32 -04:00
parent 625c95a2d4
commit 88f2f65bda
19 changed files with 161 additions and 154 deletions

View File

@@ -54,8 +54,9 @@ highest-risk area of the port. This doc is the steering state; it feeds the A2b
all 13206 `offset+size` fit inside their real `.ALF`; 837 name-matched files → 0 size mismatches.
`files[]` preserves directory order (feeds step 2's order-correlation). Re-run:
`py -3.11 -X utf8 tools/parse_sys4ini.py --check`. (Ref: asmodean's `exs4alf` / GARbro Eushully `ArcALF.cs`.)
2. **Resolve `resource_id → asset file`.** **NATIVE RULE CONFIRMED IN GHIDRA (2026-07-21): resource
operands are universal packed SYS4INI/AAI ids. There is no scene-relative path or fallback.**
2. **Resolve `resource_id → asset file`.** **NATIVE RULE CONFIRMED IN GHIDRA AND IMPLEMENTED
(2026-07-21): resource operands are universal packed SYS4INI/AAI ids. There is no scene-relative path
or fallback.**
`asset_catalog_parse_base_tables@0x44e7e0` creates one flat base entry array in serialized SYS4INI
order. `asset_open_indexed_entry@0x44f390` receives the operand unchanged. If its high byte is zero,
@@ -80,7 +81,7 @@ highest-risk area of the port. This doc is the steering state; it feeds the A2b
correlation and the strong `file_number == position - group_start` pattern remain useful evidence about
catalog construction/order, but they do not describe runtime resolution. SC0000 starting at zero hid
the mistake, while later large ids often fell outside the invented scene range and happened to reach the
port's raw fallback. Low raw ids used from later scripts can instead be silently misresolved today.
port's former raw fallback. Low raw ids used from later scripts could instead be silently misresolved.
`tools/resolve_asset.py` and `build/asset-sections.json` are therefore correlation/manifest-inventory
diagnostics only; they must not drive runtime lookup.
@@ -144,9 +145,10 @@ rendering what the executed bytecode + the map produce (never a hardcoded image)
## Status
A2b-background: **steps 13 landed, but step 2's resolver must be corrected.** Step 1 =
`build/asset-index.json`. Step 2 originally normalized through inferred per-scene sections; native RE now
proves runtime operands are already universal packed ids. `tools/resolve_asset.py` and
A2b-background: **steps 13 landed; step 2's packed resolver was corrected 2026-07-21.** Step 1 =
`build/asset-index.json`. Step 2 originally normalized through inferred per-scene sections; native RE proved
runtime operands are already universal packed ids, and every typed runtime facade now uses that contract.
`tools/resolve_asset.py` and
`build/asset-sections.json` remain grouping/correlation diagnostics, not runtime inputs. Step 3 = **first-pass
render** (ResourceMap + GodotAdvHost texture ops → TextureRect compositing): the full-screen event-CG
layer renders end-to-end from the bytecode. Remaining (next chunk): the **graphics geometry/blend
@@ -214,12 +216,14 @@ scene-local numeric addressing mode.
on-disk `BinExtractALF.exe` are validation references; the Kelebek repository exposes no clear license,
so its code should not be copied without clarification. The focused `LzssDecoder` is shared with
`Sys4AssetCatalog`; raw and compressed information/pixel/ACIF sections use the same bounded primitive.
4. **Runtime consumers (packed-id correction pending).** Script loading and SFX already use
`ResolvePacked`. Texture/voice/non-modal movie facades still contain the disproven scene-first/raw-fallback
compatibility layer and must be switched to the same typed packed lookup. Godot caches decoded RGBA
surfaces by catalog identity and supplies synchronous dimensions to opcode `0x208`; it no longer reads
`build/textures/*.BMP`. BGM remains direct-name. Extraction, grouping, and conversion tools remain
diagnostics.
4. **Runtime consumers (packed-id correction complete 2026-07-21).** Script loading, textures, voice,
SFX, cursors, and modal/non-modal movies all select through `ResolvePacked` before type filtering. The VM
retains bytecode operands unchanged instead of asking the host for scene normalization. Godot caches
decoded RGBA and movie identities by the full packed id, preserving AAI selectors rather than colliding
with equal low indexes in the base catalog. It supplies synchronous dimensions to opcode `0x208` and no
longer reads `build/textures/*.BMP`. BGM remains direct-name. Focused regressions cover SC0010's low
`0x21` texture and `0x120..0x122` voices plus append-pack identity. Extraction, grouping, and conversion
tools remain diagnostics.
### Acceptance gates
@@ -352,7 +356,7 @@ corpus is `LOGO.BIN (0x335f,42,4)`, `OP.BIN (0x3364,42,4)`, and
Native `0x20f` also arms modal run-state `0x2000`; unlike `0x236`, these six-instruction wrapper scripts
depend on the movie service itself to park until EOF/input cancellation before they release surface 42.
`ResourceMap.ResolveRawMovie` currently supplies that typed universal lookup, while `ReadMovie` remains the MPEG
`ResourceMap.ResolveMovie` supplies that typed universal lookup, while `ReadMovie` remains the MPEG
signature gate. `IHost.PlayModalMovieToSurface` is distinct from the non-modal call for lifecycle only: Godot
reuses the asynchronous DirectShow frame decoder and retained compositor but parks the VM thread until EOF
or mouse/Accept/Cancel input. The wrapper's following release then tears down the completed/cancelled movie.

View File

@@ -2119,9 +2119,11 @@ open raw entry `0x21` (`SO013A.AGF`); adding SC0010's catalog position `0x11e` i
`COL0023.OGG`. Its `play-voice 0x120/0x121/0x122` operands directly select
`LILA1414/LILB0053/LILC0054.OGG`, whose SC0010-local file numbers are 2/3/4. The catalog grouping relation
is therefore `absolute id = group start + file_number`; the compiler has already performed that addition.
The port's scene-first compatibility resolver inverted this relationship and must be replaced by typed
`ResolvePacked` lookup for texture, voice, and movie consumers. Ghidra `/v2` renames the catalog parser and
entry-name helper, corrects the opener/caller comments, and is saved.
The port's former scene-first compatibility resolver inverted this relationship. On 2026-07-21 it was
replaced by typed `ResolvePacked` lookup for texture, voice, and both movie consumers; the VM now preserves
the operand unchanged and Godot cache/movie identities retain the complete packed selector. SC0010 low-id
and installed append-pack regressions cover the two failure modes. Ghidra `/v2` renames the catalog parser
and entry-name helper, corrects the opener/caller comments, and is saved.
---

View File

@@ -619,7 +619,7 @@ This is a target-pixel operation, not retained-object teardown. It invokes IDire
- **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2: op_0x20f_play_modal_movie_to_surface@0x422e50 shares the movie-object allocation, DirectShow graph open, audio-route, and volume setup used by 0x236, then calls movie_start_modal_playback@0x463280, ORs EngineCtx+0xa0ce4 with 0x2000, and marks movie presentation dirty. The main loop and window procedure special-case run-state 0x2000. Corpus has exactly three sites: LOGO (0x335f,42,4), OP (0x3364,42,4), and ED (0x3324,42,dynamic flags). Those ids are universal raw SYS4INI indexes for MPEG-pack LOGO.AGF, OP.AGF, and ED.AGF; each script releases its surface only after 0x20f resumes.
Implemented through IHost.PlayModalMovieToSurface. Its operand uses the same native universal packed-id catalog contract as 0x236; the separate host call exists for modal wait/cancel lifecycle, not a different resolver. ResourceMap must retain MPEG signature validation in ReadMovie. Godot reuses the asynchronous decoder/retained-surface compositor, parks only the VM thread until EOF, and treats mouse click or Accept/Cancel input as completion before wrapper cleanup releases the decoder. MPEG audio remains a separate backend/audio-clock contract.
Implemented through IHost.PlayModalMovieToSurface. Its operand uses the same native universal packed-id catalog contract as 0x236; the separate host call exists for modal wait/cancel lifecycle, not a different resolver. ResourceMap.ResolveMovie selects through ResolvePacked and retains MPEG signature validation in ReadMovie. Godot reuses the asynchronous decoder/retained-surface compositor, parks only the VM thread until EOF, and treats mouse click or Accept/Cancel input as completion before wrapper cleanup releases the decoder. MPEG audio remains a separate backend/audio-clock contract.
### 0x212 `set-gfx-field64` (set-gfx-field64, argc 2)
- **summary:** 0x212 (obj_idx)(val) — handler gfx_op_0x212_set_field64 @0x4230c0: obj=[ctx+0x14d54 + obj_idx*4]; if obj: *(obj+0x64)=val. The generic instruction length is 5 dwords. See docs/engine-re.md gfx op-contract table.
@@ -749,7 +749,7 @@ Implemented through IHost.PlayModalMovieToSurface. Its operand uses the same nat
- **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2 op_0x236_play_movie_to_surface@0x423ee0 fetches operand 1 and passes it unchanged to movie_to_texture_open_asset_graph@0x463e20, which passes it unchanged to asset_open_indexed_entry@0x44f390. The opener directly indexes the flat base table or selected AAI table and has no scene input. SC0000 native operand capture and exact 0x13c8->0x13d1 trace prove nonblocking behavior. BTL's live 0x2b21 site supplies 0x2af1/0x2af5/0x2bca/0x2bd8/0x2bde, the exact base entries MVB001/MVB004/MVB914/MVB958/MVB955.
The handler requires an existing destination texture, allocates/reuses a 0x478-byte movie-to-texture object for the surface, opens operand 1 through the native universal packed-id reader, builds FilterGraph/IGraphBuilder/IMediaControl/IMediaPosition/IMediaEvent/IBasicAudio, and presents bottom-up RGB samples through the movie texture renderer. Operand 3 selects movie/sound routing policy: bits 0x10000/0x20000/0x40000/0x80000 force sound route 0/1/2/3, otherwise set:DependMovieSound is used; SC0000's low value 2 is retained as native movie mode state. Operand 4 is the movie sync/device mask. Static layer preparation after 0x236 does not terminate the retained movie; 0x21c services it through EOF and subsequent surface cleanup stops/detaches it. The port should type-check the selected packed record as MPEG but must not add a scene base or scene-first fallback.
The handler requires an existing destination texture, allocates/reuses a 0x478-byte movie-to-texture object for the surface, opens operand 1 through the native universal packed-id reader, builds FilterGraph/IGraphBuilder/IMediaControl/IMediaPosition/IMediaEvent/IBasicAudio, and presents bottom-up RGB samples through the movie texture renderer. Operand 3 selects movie/sound routing policy: bits 0x10000/0x20000/0x40000/0x80000 force sound route 0/1/2/3, otherwise set:DependMovieSound is used; SC0000's low value 2 is retained as native movie mode state. Operand 4 is the movie sync/device mask. Static layer preparation after 0x236 does not terminate the retained movie; 0x21c services it through EOF and subsequent surface cleanup stops/detaches it. The port type-checks the selected ResolvePacked record as MPEG without adding a scene base or fallback.
### 0x238 `set-anim-clock` (set-anim-clock, argc 1)
- **summary:** (duration) — set the GLOBAL animation clock: native ctx+0x51b78=0 (elapsed), +0x51b7c=duration. The generic instruction length is 3 dwords. NON-BLOCKING: only configures; the render loop advances it and interpolates all animating objects. SC0000 opening @0x123bd/@0x13858. Handler 0x4240e0; Kelebek VA 0x422390 is drift.

View File

@@ -561,20 +561,23 @@ Focused regressions cover exact dispatch, signed edge cases, string aliasing, cl
movie polling, animation reset suppression, delayed voice operands, paired clipping, overlap, and colorkey
transparency. Manual DEBUGMAP acceptance reached player combat and exposed the next concrete frontier:
combat-effect movies do not play. The resolver/decoder diagnosis is canonical in
`docs/asset-resolution-re.md`; fix that bounded movie path before proceeding to enemy-turn/end-turn breadth.
`docs/asset-resolution-re.md`; packed resolution is now corrected, with manual BTL validation and the
separate decoder decision remaining before enemy-turn/end-turn breadth.
**Combat-effect movie gap diagnosed; native resolver generalized; implementation pending.** BTL's
**Universal packed resource resolver implemented; combat movie validation pending.** BTL's
`0x236@0x2b21` consumes universal packed MVB ids, exactly as texture, voice, script-load, and modal-movie
consumers do. Native Ghidra analysis shows that none of those paths applies an SC section base or fallback;
the port's scene-first compatibility resolver is therefore generally wrong, not merely incomplete for BTL.
the port's former scene-first compatibility resolver was therefore generally wrong, not merely incomplete for BTL.
That explains each `movie unresolved BTL:...` warning and the secondary attempt to decode MPEG-backed
`MVB914.AGF` as a still image. A packed-catalog decoder probe also found a separate backend wall: the current
DirectShow graph handles `MVB914` (400x400) but rejects the reached 280x352 MVB001/MVB004/MVB955/MVB958
assets with `0x80040217`. Broader samples tie current compatibility to 16-aligned display widths, while 125
installed MVB assets use 280x352. The next bounded slice is to replace typed texture, voice, and movie
consumers with universal packed resolution and regress SC0010's low texture/voice ids plus BTL effects.
The existing portable-decoder seam and a software fallback for 280x352 effects remain the following,
separate step, with destination dimensions and failed-movie identity preserved correctly.
installed MVB assets use 280x352. Texture, voice, and both movie consumers now use universal packed
resolution; VM surface state and Godot caches retain the full selector. Focused tests prove SC0010's low
texture/voice ids and append-pack identity; 302 engine tests, a zero-warning Godot build, and threaded
selftest pass. **NEXT:** manually re-enter combat and confirm the BTL ids now resolve into the movie backend.
The expected remaining failures are the known 280x352 DirectShow graphs; defer decoder replacement until
that resolver-only acceptance check, preserving destination dimensions and failed-movie identity afterward.
**Mutable-surface fill/blend regression corrected.** The first visual recheck exposed BUNKI's menu interior
as transparent. SYSTEM4 creates 800x600 surface 3 and fills it opaque white through `0x20b`; the metadata-only