BT410 5.3.54: skeleton-walk API inventory complete; staged plan with pixel-free checks

Confirmed every call the .SKL walk needs -- the NotationFile accessors and the
MakeEntryList/GetFirstEntry/GetNextEntry idiom (copy DPLReadINIPage's
objectpath loop), and the dPL side (NewDCS/SetDCSMatrix/AddDCSToDCS/
AddDCSToScene/SetDCSZone/FlushDCS, LoadObject/NewInstance/SetInstanceObject/
AddInstanceToDCS/FlushInstance).

On the matrix: a DCS flush body is [remote][type_check][node][64 bytes] = a
4x4 of float32.  Decoding a real BT capture shows a near-identity with a
single 10.0 term in the last row, suggesting row-major with translation in
row 3 -- but the rows print shifted by one word against a true identity, so
the decoder offset is suspect and the convention is recorded as NOT yet
proven.  Flagged rather than guessed.

Staged the work so each half is verifiable without looking at pixels: build
the tree with identity matrices first and prove the structure by wire counts
(26 DCS + 22 instance flushes, which MAD.SKL's JointCount=25/DZoneCount=22 and
the dpl3-revive reference capture agree on), then add real transforms and fix
the convention by watching which slot moves.

Also noted the one API still to check first: the skeleton reaches us as a
non-Object L4VideoObject::ResourceType, so branch on that enumerator rather
than re-deriving the filename.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-28 17:07:00 -05:00
co-authored by Claude Fable 5
parent a6612d5f75
commit fac559bc31
@@ -680,3 +680,49 @@ real pod decodes as "Transforms: flush bodies type 5 (dcs) x26" and
= 26) and DZoneCount=22. So a correct walk of this one file should put 26 DCS = 26) and DZoneCount=22. So a correct walk of this one file should put 26 DCS
flushes and 22 instance flushes on the wire -- countable in the fifodump flushes and 22 instance flushes on the wire -- countable in the fifodump
without looking at a single pixel. without looking at a single pixel.
--------------------------------------------------------------------------------
API INVENTORY for the skeleton walk -- all confirmed, nothing left to discover
--------------------------------------------------------------------------------
NotationFile (CODE/RP/MUNGA/NOTATION.HPP) reads every field the .SKL needs:
GetEntry(page, entry, Scalar*) tranx/trany/tranz, pitch/yaw/roll
GetEntry(page, entry, const char**) Object= (the .bgf)
MakeEntryList(page, "joint") the child pages
MakeEntryList(page, "dzone") the damage-zone tags
NameList iteration is entry = list->GetFirstEntry(); entry;
entry = entry->GetNextEntry() -- exactly how DPLReadINIPage walks
objectpath= at L4VIDEO.CPP:1858, so copy that idiom.
dPL side, all in libDPL/dpl/DPL.H:
dpl_NewDCS / dpl_SetDCSMatrix / dpl_AddDCSToDCS / dpl_AddDCSToScene
dpl_SetDCSZone / dpl_FlushDCS
dpl_LoadObject(name, dpl_load_normal) / dpl_NewInstance /
dpl_SetInstanceObject / dpl_AddInstanceToDCS / dpl_FlushInstance /
dpl_FlushObject
THE MATRIX, as far as the evidence goes. A DCS flush body decodes as
[remote:4][type_check:4][node:4][matrix:64] -- 16 float32, a 4x4 (confirmed
by dpl3-revive/patha/analyze_scene.py, which reads rest[4:68] as 16 floats).
Decoding a real BT capture (patha/bt1.raw.bin) prints a near-identity DCS with
a single 10.0 term in the LAST ROW, which points at row-major with the
translation in row 3 -- but the printed rows look shifted by one word against
a true identity, so that decoder's offset is suspect by exactly 4 bytes.
DO NOT trust this without re-deriving it; the cheap way is to build the tree
with IDENTITY matrices first (below), then set one known node's translation
and watch which slot moves in the fifodump.
RECOMMENDED ORDER OF WORK -- two stages, each independently verifiable:
STAGE 1: walk the .SKL and build the tree with IDENTITY matrices. Every
node collapses onto the origin so it looks wrong, but the STRUCTURE is
provable without pixels: the wire must carry 26 DCS flushes and 22 instance
flushes (MAD.SKL: JointCount=25 +root, DZoneCount=22 -- and the dpl3-revive
reference capture of a real pod shows exactly 26 and 22). Getting those
counts proves the recursion, the object loads and the parenting.
STAGE 2: put the real transforms in and fix the convention by observation.
ONE API STILL TO CHECK BEFORE STAGE 1: how the .SKL filename reaches us. The
engine builds a chain via L4VideoObjectWrapper::BuildVideoObjectChainFromResource
and rejects our mech with "wrong video resource type for object mad.skl"
(L4VIDEO.CPP:4265) because it only accepts L4VideoObject::Object / Rubble. So
the skeleton arrives as a different L4VideoObject::ResourceType -- find that
enumerator and branch on it in our override rather than re-deriving the name.