Use PCSX2 RAM to recover the chunk loader's transformation
This is what RAM was needed for: nothing about the loader's transformation is recoverable from the disc. Anchor: a 32-byte probe from inside a chunk record in level 0's core_data, searched in the level-0 savestate. It hits at 0x18538a8 and the record's first bytes at 0x185387e -- exactly chunk_base + record_offset, no rescaling. So the chunk payload is copied VERBATIM; unlike collision and the shrub packet tables it is not expanded or repacked. Result 1: the offset table is rewritten to absolute EE addresses. disc: f000057c f00006b0 f00007e4 f0000914 RAM: 0xf185387c 0xf18539b0 0xf1853ae4 0xf1853c14 The 0xf0000000|low24 tag becomes 0xf1850000|low24 -- tag replaced by the chunk's load base, low 24 bits untouched. Consecutive deltas agree exactly (0x134, 0x134, 0x130), which is why the disc-side stride is approximate rather than constant. Result 2: every record gains a constant 2-byte prefix 02 00, and the rewritten table points at THAT rather than at the record data. Walking all 90 records from the RAM dump: prefix 02 00 on 90 of 90; the disc's index bytes preserved unchanged at +2/+3 on 90 of 90. Result 3, the useful one: the byte at +2 takes exactly 0, 16, 32, ... 240 -- the 16 values index_rule predicts for step = 16, phase = 0. The mixed-radix record index derived from the disc is corroborated on the running game's own copy of the chunk. That is a disc-derived claim validated against the game, which is the strongest form of check available here. On disc a chunk is [16-byte header][10 zero bytes][tagged offset table][records]. In RAM it is [16-byte header][10 zero bytes][absolute-pointer table][records each prefixed with 02 00]. The 02 00 is unexplained -- possibly a record type or an alignment the loader needs -- but it is constant, so like the two index bytes it carries no per-record information.
This commit is contained in:
@@ -1427,3 +1427,27 @@ So 9.4%, and no chunk is a template. Six records looked like a majority because
|
|||||||
that chunk's tail happens to carry shared constants -- the per-type figures range
|
that chunk's tail happens to carry shared constants -- the per-type figures range
|
||||||
from 9.4% overall to 55.6% on one type. A pooled figure over all types would have
|
from 9.4% overall to 55.6% on one type. A pooled figure over all types would have
|
||||||
hidden that spread again, so it is reported per type as well.
|
hidden that spread again, so it is reported per type as well.
|
||||||
|
|
||||||
|
## What the loader does to a chunk (from RAM)
|
||||||
|
|
||||||
|
Verified against PCSX2 level-0 RAM; method and full dump in
|
||||||
|
`plan/pcsx2-verification.md`.
|
||||||
|
|
||||||
|
The chunk payload is copied **verbatim** -- a probe from inside a record hits at
|
||||||
|
`chunk_base + record_offset` with no rescaling. Two things change around it:
|
||||||
|
|
||||||
|
1. **The offset table is rewritten to absolute EE addresses.** Disc offsets
|
||||||
|
`0xf000057c, 0xf00006b0, 0xf00007e4, 0xf0000914` become
|
||||||
|
`0xf185387c, 0xf18539b0, 0xf1853ae4, 0xf1853c14` in RAM. The
|
||||||
|
`0xf0000000 | low24` tag becomes `0xf1850000 | low24` -- tag replaced by the
|
||||||
|
load base, low 24 bits untouched.
|
||||||
|
|
||||||
|
2. **Every record gains a constant 2-byte prefix `02 00`,** and the rewritten
|
||||||
|
table points at *that* rather than at the record data. Measured on all
|
||||||
|
**90 of 90** records: prefix `02 00` on every one, and the disc's index bytes
|
||||||
|
preserved unchanged at `+2`/`+3` on every one.
|
||||||
|
|
||||||
|
The third result is the useful one: the byte at `+2` takes exactly
|
||||||
|
`0, 16, 32, ... 240` -- the 16 values `index_rule` predicts for `step = 16,
|
||||||
|
phase = 0`. The mixed-radix record index, derived from the disc, is corroborated
|
||||||
|
on the running game's own copy.
|
||||||
|
|||||||
@@ -726,3 +726,55 @@ coincidence inside a short run.
|
|||||||
**The collision lead in this file is disproved, not merely unproven.** Recorded
|
**The collision lead in this file is disproved, not merely unproven.** Recorded
|
||||||
here so it is not picked up again. Level 1's grid is 384 cells and nothing found
|
here so it is not picked up again. Level 1's grid is 384 cells and nothing found
|
||||||
so far counts to it.
|
so far counts to it.
|
||||||
|
|
||||||
|
## The chunk container: what the loader does to it
|
||||||
|
|
||||||
|
This is what RAM was needed for. Nothing about the loader's transformation is
|
||||||
|
recoverable from the disc.
|
||||||
|
|
||||||
|
**Anchor.** A 32-byte probe from inside a chunk record in level 0's `core_data`
|
||||||
|
was searched in the level-0 savestate. It hits at `0x18538a8`, and the record's
|
||||||
|
first bytes hit at `0x185387e` -- exactly `chunk_base + record_offset`, with no
|
||||||
|
rescaling. So the chunk payload is copied **verbatim**; it is not expanded or
|
||||||
|
repacked the way the collision payload and the shrub packet tables are.
|
||||||
|
|
||||||
|
**Result 1 -- the offset table is rewritten to absolute EE addresses.**
|
||||||
|
Disc, at chunk `0x100b302`:
|
||||||
|
|
||||||
|
offsets[0..3] = f000057c f00006b0 f00007e4 f0000914
|
||||||
|
|
||||||
|
RAM, in a table at `0x185331c`:
|
||||||
|
|
||||||
|
0xf185387c 0xf18539b0 0xf1853ae4 0xf1853c14
|
||||||
|
|
||||||
|
The tagged form `0xf0000000 | low24` becomes `0xf1850000 | low24`: the tag is
|
||||||
|
replaced by the chunk's load base and the low 24 bits are untouched. The
|
||||||
|
consecutive deltas agree exactly (`0x134, 0x134, 0x130`, which is why the
|
||||||
|
disc-side `stride` is approximate rather than constant).
|
||||||
|
|
||||||
|
**Result 2 -- every record gains a constant 2-byte prefix, and the table points
|
||||||
|
at the prefix.** RAM at each record:
|
||||||
|
|
||||||
|
0x185387c 02 00 00 00 38 d3 4c 38 ...
|
||||||
|
0x18539b0 02 00 10 00 2f bb 4c 38 ...
|
||||||
|
0x1853ae4 02 00 20 00 20 8f 4c 38 ...
|
||||||
|
0x1853c14 02 00 30 00 14 6f 4c 38 ...
|
||||||
|
|
||||||
|
Walked all **90** records of the chunk from the RAM dump:
|
||||||
|
|
||||||
|
2-byte prefix : 02 00 on 90 of 90
|
||||||
|
disc index bytes preserved at +2/+3 : 90 of 90
|
||||||
|
byte at +2, distinct values : 0, 16, 32, ... 240 (16 values)
|
||||||
|
|
||||||
|
**Result 3 -- the disc-side index finding is confirmed against live data.** The
|
||||||
|
bytes at `+2` take exactly the 16 values `ResolvedChunk::index_rule` predicts for
|
||||||
|
`step = 16, phase = 0`, stepping 0x00, 0x10, 0x20, ... That is the mixed-radix
|
||||||
|
record index, measured on 2,430 records from the disc and now corroborated on the
|
||||||
|
running game's own copy of one chunk.
|
||||||
|
|
||||||
|
**What this means for the format.** On disc, a chunk is
|
||||||
|
`[16-byte header][10 zero bytes][tagged offset table][records]`. In RAM it is
|
||||||
|
`[16-byte header][10 zero bytes][absolute-pointer table][records each prefixed
|
||||||
|
with 02 00]`. The `02 00` is unexplained -- it may be a record type or an
|
||||||
|
alignment the loader needs -- but it is constant, so it carries no per-record
|
||||||
|
information either.
|
||||||
|
|||||||
Reference in New Issue
Block a user