TL;DR. The end goal is unchanged: a 2D renderer inviewer.cthat draws (eventually) every character, costume, and stage, driven by the replay'saction_state+anim_frame. What changed is the front half: the first part of this plan is writing a from-scratch C extraction pipeline (tools/extract) that reads the ISO → FST → DAT container → HSD types, and produces cached assets for fighters, fighter colours, animations, stages, and stage animations. We do it in C, as original work: Zig (litewing) is a reference for behavior only — we study what it does and why, then write our own code. Understanding HSD deeply is a prerequisite, not a side quest.
action_state, anim_frame, facing, x/y, character_id, FOD/Whispy/Stadium events.
melee_c/src/viewer.c Our C HTTP + software-2D + PNG renderer (browser at melee.zamachnoi.com).
litewing (Zig) reference only dat-extractor + melee-viewer prove the pipeline works and reveal the hard parts. We read it for behavior and never copy code; it is not a build dependency.
Known-good numbers Validation target (Falco): bones=67, primitive_groups=26, textures=24, actions > 300. Our extractor must reproduce these independently.
Toolchain cc 13.3 + existing Makefile. No Zig needed to build melee_c.
docs/DAT_FILE_LAYOUT.md + USEFUL_LINKS.md — useful index of the above (read for pointers, not code).| Concern | Where it lives | Language | Note |
|---|---|---|---|
| ISO → cached assets (extraction) | tools/extract in melee_c | C | Original implementation. Offline, run on dev box. |
| Asset loading | src/asset.{c,h} | C | Reads the cache produced by extract. |
| Pose + 2D raster | src/viewer.c renderer | C | Same server, PNG pipeline unchanged. |
| Action-state → animation lookup | src/asset.c (tables) | C | Common states + Fox/Falco specials, name → prefix → raw-index fallback. |
git subtree, vendor, or transpile litewing. We implement the same formats with our own structure. Shared facts (offsets, byte layouts from the community thread) are fine to reproduce verbatim — that's data, not code.game.iso ──[tools/extract: C]──► /data/cache (versioned, ISO-fingerprinted)
│ ├─ meta.json fingerprint, schema ver
│ FST ──► DAT bytes ──► HSD ├─ char/<char>-<colour>.model bones, prims, verts, tex
│ ├─ char/<char>-<colour>.anims action table + figatree tracks
│ └─ stage/<stage>.stage sections, scale, camera, stage anims
master.slp ──[parser.c]──► action_state, anim_frame, facing, x, y
│
└─[viewer.c render]────────► 4x4 RGBA ──PNG──► browser
action_state ─lookup→ animation anim_frame ─sample→ bones
bones ─skin→ verts x,y,facing ─project→ 2D ─paint
Goal: a written, precise model of the formats before writing decoder code. This is the milestone that makes extraction possible — do it properly.
docs/DAT.md: GameCube disc structure, FST entries, locating *.dat, reading file bytes.0x20 + dataOffset), what root names mean (ftData*, *figatree*, *_joint, coll_data).docs/HSD.md: the scene graph — JOBJ (joints/bones + transforms), DOBJ (draw objects), POBJ (primitive objects: display lists), MOBJ (materials: color, alpha, TEV), TOBJ (textures: TPL image + wrap/scale), COBJ? (lights/cameras). Enumerate every HSD type we must handle for fighters and stages.docs/ANIM.md: figatree — AOBJ-based joint/material/texture tracks, keyframes (constant/step/linear/hermite), track interpolation math; and how the fighter action table (in ftData*, 0x18-byte entries) links to byte slices inside Pl*AJ.dat.docs/STAGES.md: stage DAT layout (GrNLa = FD …), sections, camera/scale/lights, and stage animations (platform movement, Whispy, Stadium transforms).Exit: reading any of these docs + the community DAT thread should let us explain any byte in a Pl*.dat without peeking at Zig sources.
tools/extract)Goal: original C code, module per format layer, each with a tiny self-check.
| Module | Parses / produces | Self-check |
|---|---|---|
iso.c | FST index; dat_entry(path) -> bytes | Known DAT paths resolve; sizes sane. |
dat.c | DAT container → sections, nodes, names | Root names match the thread's naming. |
hsd_jobj.c | JOBJ tree → bones, base/inv-bind transforms | Falco skeleton: 67 bones. |
hsd_dobj.c | DOBJ/POBJ/MOBJ → primitive groups, indices, vertices | Falco: 26 prim groups. |
texture.c | TOBJ/TLU + GX formats → RGBA | Falco: 24 textures, decodes to correct palette. |
colour.c | costume → mesh DAT mapping (e.g. PlFxOr/Bu/Gr/Nr) + material swap | Each costume yields a distinct model. |
anim.c | figatree → joint/material/texture keyframe tracks | Tracks interpolate identically at known frames. |
action.c | ftData* action table → linked Pl*AJ.dat slices | Falco: >300 actions, Wait1 resolves. |
stage.c | stage sections, scale, camera, lights, stage animations | FD: scale ~1.0, sections match. |
cache.c | serialize all of the above; meta.json | Re-read round-trips byte-identical. |
Exit: tools/extract reproduces every validation number above with zero Zig in the build. Cache never lands in git.
src/asset.{c,h}): mmap cache files into runtime structs; validate schema version + ISO fingerprint; load Fox/Falco on startup (ASSET_DIR=/data/cache).action_state → animation; sample at anim_frame (mind the +1 frame quirk); local → world transforms; mirror x when facing < 0.MOBJ colors first, affine texture mapping next./api/pose?char=falco&action=Wait1&frame=30&facing=1 debug endpoint — iterate on one skeleton before touching playback./api/frame: followers/Nana, 4 ports, same character twice, Zelda/Sheik (key models by internal char id).slp_fod_at / slp_whispy_at / slp_stadium_at events.| # | Decision | Resolved |
|---|---|---|
| 1 | Who writes the extraction? | Us, in C, original code. Zig is reference-only. No litewing in the build. |
| 2 | What counts as "copying"? | Facts/offsets from the community thread = fine verbatim. Structure, idioms, and code = ours. No copy-paste from litewing. |
| 3 | Cache location | Dev box /data/cache, versioned + ISO-fingerprinted, never in git. |
| 4 | First projection | Side view (matches replay x/y; facing → mirror). |
| 5 | Textures in renderer | Flat-shaded first (silhouettes > accuracy); texture decode still built in Phase B for correctness. |
| 6 | Costume order | Default (0) for Fox/Falco first, then all colours. |
game.iso is the version the known-good numbers were measured on before trusting them.pngCache already exists; optimize the triangle filler only if needed.docs/DAT.md + a C ISO/FST → DAT tool in tools/extract; dump a real PlFc.dat header, sections, roots, and names.PlFc.dat; print the bone hierarchy + base transforms; hit 67 bones.Wait1 / a blaster action; hit >300 actions./api/pose render of Wait1 in 2D.