diff --git a/docs/plan/pcsx2-verification.md b/docs/plan/pcsx2-verification.md index ad1238c..0ae7454 100644 --- a/docs/plan/pcsx2-verification.md +++ b/docs/plan/pcsx2-verification.md @@ -844,7 +844,11 @@ 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. +**Correction:** both fields were already named. `+0x10` is +`TfragHeader::data` -- the byte offset of the tfrag's data block, relative to the +table -- and `+0x38` is `TfragHeader::cube_ofs`. The RAM comparison does not name +them; what it adds is that both are **pointers**, relocated by adding the block's +RAM base, rather than plain offsets. **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: @@ -858,7 +862,34 @@ 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. +### How much of a patch actually changes, and where + +Patch 1 (13 morphing entries of 83) compared over 4 KiB, disc `0xdf60` against RAM +`0x855f60`: + + bytes differing 354 of 4,096 = 8.6% + contiguous runs 120 + longest run 8 bytes + extent +0xc1 through the end of the window + +83 positions at 6 bytes each is 498 bytes, and only 13 of 83 vertices morph, so +**if morphing rewrote position fields alone** the differing bytes would be a +13/83 slice of a 498-byte array -- a couple of dozen bytes in one contiguous +region. Instead 354 bytes differ, spread over 120 short runs across the whole +window. + +So the loader is not patching positions in place. It **regenerates the VU packet +stream** for the current morph state, which on this hardware is what morph +actually is -- VU1 GIF packets, not a CPU-side vertex array. + +**Consequence for verification.** The plan of "map RAM vertices to disc indices and +solve for the weight" does not work against this memory image: the positions are +inside regenerated packets, so there is no stable per-vertex correspondence to +read a weight out of. Verifying the morph weight against the original needs the +**camera distance at the savestate** so the weight can be computed from the +derivation and compared with the rendered result, rather than read back out of +RAM. + +Along the way, two population figures worth having: **833 of 833 patches parse**, +carrying **43,613** vertex-info entries, and **499 of 833 patches have at least +one morphing entry** -- patch 0, the first record, is base-only with none. diff --git a/docs/roadmap.md b/docs/roadmap.md index 3175d75..fcb8fe1 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -249,16 +249,23 @@ Phase 1 hands over the extents; this phase works out what each one contains. `+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. + They are the existing `TfragHeader::data` and `::cube_ofs` -- what RAM adds + is that both are **pointers**, not plain offsets. All **833** patches parse, + carrying **43,613** vertex-info entries; **499 of 833** have at least one + morphing entry. +- [~] Behind those pointers, **8.6% of bytes are rewritten** (354 of 4,096 on + patch 1), spread over 120 short runs across the whole window rather than + concentrated in a position array. Morphing on this hardware is VU1 GIF + packets, and the loader **regenerates the packet stream** rather than + patching position fields in place. +- [ ] What tfrag is **not** yet verified is that the morph matches + `resolve_vertex`'s formula. Reading a weight back out of RAM does **not** + work -- the positions live inside regenerated VU packets, so there is no + stable per-vertex correspondence. The LOD switch distances, morph weight and + collapse edge remain derived from the data (tiers classified from the four + V4_8 parent arrays, weight running zero to one across each band); verifying + them needs the **camera distance at the savestate** so the weight can be + computed and compared against the rendered result. - [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