diff --git a/docs/plans/2026-08-30-campaign-fw-frame-walk.md b/docs/plans/2026-08-30-campaign-fw-frame-walk.md index 2eaf5624..5ff39160 100644 --- a/docs/plans/2026-08-30-campaign-fw-frame-walk.md +++ b/docs/plans/2026-08-30-campaign-fw-frame-walk.md @@ -152,6 +152,23 @@ preserved length. TS-56 records the non-retail adjustment scalars ### FW0 — the walk oracle (tooling + fixtures) +**STATUS: COMPLETE 2026-08-30.** Ten trace fixtures + README +(`docs/research/2026-08-30-fw-walk-oracle/`, commit `71b11817`), the +replay helper + 19 fixture goldens (`9f4c0f95`), and the port-ready +pseudocode model + full decomp appendix +(`docs/research/2026-08-30-fw-walk-pseudocode.md`, `a6885aa2`). Findings +that reshaped the campaign: the frame roots at the CAMERA's cell; +outdoor frames draw the #456 far building every frame and bury it by +depth (no hiding mechanism exists to port); retail DOES draw depth-only +invisible portal-polygon panels (`DrawPortalPolyInternal` — punch far-Z +/ seal own-depth) — the AD-117 stamps re-invented a real mechanism at +the wrong site. **Known gap:** the first capture round did not record +the camera pose; exact FW1 replay needs pose-stamped traces. A prepared +template (`tools/walk-oracle/fw0-capture-pose.cdb.template`) makes the +re-capture a short owner-assisted session; scheduled for when FW1's walk +is ready to gate (structural conformance proceeds meanwhile on the +existing traces). + **Goal:** retail's actual per-frame draw walk, captured and replayable, BEFORE any port is judged. diff --git a/tools/walk-oracle/fw0-capture-pose.cdb.template b/tools/walk-oracle/fw0-capture-pose.cdb.template new file mode 100644 index 00000000..14a13404 --- /dev/null +++ b/tools/walk-oracle/fw0-capture-pose.cdb.template @@ -0,0 +1,32 @@ +* FW0 pose-stamped capture TEMPLATE (2026-08-30). +* The first capture round (fw0-capture.cdb) did not record the camera pose, +* which exact FW1 replay needs. This template adds a per-frame pose dump. +* +* BEFORE USE (one recon attach, no breakpoints): +* x acclient!*viewer_pos* +* x acclient!SmartBox::viewer* +* dt acclient!Position +* to pin the viewer Position global's address and confirm the field offsets +* assumed below (Position: +4 objcell_id, Frame at +8: quat +8..+0x17, +* cached 3x3 at +0x18.., m_fOrigin at +0x3C..+0x47 -- VERIFY with dt; the +* update_viewer read said Frame origin writes land at Frame+0x34, i.e. +* Position+0x3C). +* +* Replace with the resolved address (or symbol acclient!Render::viewer_pos +* if it resolves), with the output path, then run as usual: +* cdb -pn acclient.exe -cf +* +* The P line prints raw hex dwords; decode to floats offline +* (BitConverter.Int32BitsToSingle). Order: objcell_id, origin xyz, quat wxyz. + +.logopen +.sympath C:\Users\erikn\source\repos\acdream\refs +.symopt+ 0x40 +.reload /f acclient.exe +r $t0 = 0 +bp 00453aa0 "r $t0 = @$t0 + 1; .printf \"F %d\\n\", @$t0; .printf \"P %08x %08x %08x %08x %08x %08x %08x %08x\\n\", poi(+4), poi(+0x3c), poi(+0x40), poi(+0x44), poi(+8), poi(+0xc), poi(+0x10), poi(+0x14); .if (@$t0 >= 0n40) { qd } .else { gc }" +bp 005a4840 "r $t2 = poi(@ecx+0x60); .printf \"DC pv=%08x ov=%d n=%d:\", @ecx, poi(@ecx+0x38), @$t2; r $t3 = poi(@ecx+0x50); r $t4 = 0; .while (@$t4 < @$t2) { r $t5 = poi(@$t3 + @$t4*4); .printf \" %08x\", poi(@$t5+0x28); r $t4 = @$t4 + 1 }; .printf \"\\n\"; gc" +bp 005a5860 ".printf \"DI %08x\\n\", poi(poi(@esp+4)+0x28); gc" +bp 00506330 ".printf \"LS\\n\"; gc" +bp 0059f2a0 ".printf \"BLD %08x\\n\", poi(poi(@esp+4)+0x4c); gc" +g