diff --git a/docs/plan/pcsx2-verification.md b/docs/plan/pcsx2-verification.md index a2abd57..ad1238c 100644 --- a/docs/plan/pcsx2-verification.md +++ b/docs/plan/pcsx2-verification.md @@ -823,3 +823,42 @@ Two lessons, both already written down and both re-learned: - the control for this one is trivial and was not run: read the predicted RAM address and compare it to the disc, which takes one command and would have shown the verbatim copy immediately. + +## tfrag: two relocated pointer fields per table record, and morphed vertices behind them + +Checking the tfrag record framing against RAM. The tfrags block header is +`table_offset 0x40, tfrag_count 833, lod_distance 32.0`, and that 8-byte prefix +hits once in the level-0 savestate: **RAM `0x848000`**. So the block is at a +known place, and disc offset 0 maps to RAM `0x848000`. + +**Result 1 -- exactly two fields per record are rewritten.** Comparing all 833 +64-byte table records, field by field: + + records compared 833 + u32 fields differing 1,665 = 833 x 2, and no others + their offsets +0x10 and +0x38, on every record + relocation delta 8,683,584 = 0x848000, on 833 of them + +So `+0x10` and `+0x38` are **block-relative pointers**, and the loader adds the +block's RAM base to each. Every other byte of all 833 records is verbatim. The +remaining six fields differ by 0xF80000 and 0x1F00000 rather than 0x848000 -- +three records pointing into a different buffer, recorded rather than averaged. + +This names two fields the disc-side work had left as `data` and an unnamed word. + +**Result 2 -- behind the pointers, about 8.6% of bytes are rewritten.** Following +record 0's `+0x10` to disc `0xd040` / RAM `0x855040` and comparing 16 KiB: + + identical bytes 14,977 of 16,384 + differing 1,407 = 8.6% + +The differences are spread across many field positions rather than concentrated, +which is what morphed **positions** look like: the topology, UVs, counts and +command lists stay verbatim while a fraction of the position bytes move. 8.6% is +about half of a 16-byte vertex's 6 position bytes, i.e. consistent with roughly +half the vertices in this patch being mid-morph at the saved camera distance. + +**Not established:** that the morphed bytes match `resolve_vertex`'s formula. That +needs mapping RAM vertices to disc vertex indices and solving for the weight, which +is the next step -- but the framing is now known well enough to attempt it, and +the two pointer fields are confirmed rather than inferred. diff --git a/docs/roadmap.md b/docs/roadmap.md index f530b9e..3175d75 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -245,11 +245,20 @@ Phase 1 hands over the extents; this phase works out what each one contains. vertices'** midpoint, not toward separate morph targets — there are no morph targets in this format, which is what the old "morph targets unrecovered" note wrongly implied. -- [ ] What tfrag is **not** yet verified against is the original: the LOD switch - distances, morph weight and collapse edge are derived from the data (tiers - classified from the four V4_8 parent arrays, weight running zero to one - across each band), but have not been compared against PCSX2 the way the - lighting pass was. That needs a frame at a known view distance. +- [x] The tfrag **table record has exactly two relocated pointer fields**, at + `+0x10` and `+0x38`, verified against PCSX2 across all **833** records: the + loader adds the block's RAM base (`0x848000`) to each, every other byte of + every record is verbatim, and 1,665 fields differ where 1,666 were possible. + This names two fields previously left as `data` and an unnamed word. +- [~] Behind those pointers, **8.6% of bytes are rewritten** (14,977 of 16,384 + identical), spread across many field positions rather than concentrated -- + consistent with morphed positions with topology and UVs left verbatim. +- [ ] What tfrag is **not** yet verified is that the morphed bytes match + `resolve_vertex`'s formula. The LOD switch distances, morph weight and + collapse edge are derived from the data (tiers classified from the four V4_8 + parent arrays, weight running zero to one across each band); the framing is + now known well enough to map RAM vertices to disc indices and solve for the + weight, which is the next step. - [x] `tie` — **fully chained.** `resolve_tie_class` walks both indirections: the class table is in `core_index.bin`, and the entry's `offset_in_asset_wad` addresses `core_data.bin` (not `core_data.wad`). All **1,864** tie classes