Show the loader rewrites shrub packet headers into RAM pointers

Answering the "what is the tag byte" question needed an anchor. Searching live
RAM from scratch was the wrong way to get one: 04 04 00 01 matches unrelated
data at 0x1ac988 and there is no way to tell a real hit from a coincidental
one. The savestates are the anchor -- slot 1 is level 0, and searching one
gives candidates that can be checked rather than guessed at.

Range measured: savestate slot 1,
~/.config/PCSX2/sstates/SCUS-97268 (38996035).01.p2s, records at 0x22a6b8
onward, read with gc-trace read --state.

Result: 40+ records beginning 04 04 00 01 00 00 00 00 at 0x30 spacing -- the
packet framing identified on disc -- with the second half of each header
rewritten:

  disc:  04 04 00 01 | 00 00 00 00 | 00 00 00 05 | 00 80 0e 6c
  RAM:   04 04 00 01 | 00 00 00 00 | 13 00 00 30 | 70 6a 93 00

Bytes 0-7 unchanged; bytes 8-15 are not. On disc bytes 12-13 are the constant
00 80 and byte 15 the constant 6c; in RAM bytes 12-15 hold a pointer --
0x00936a70, 0x009376f0, 0x00937850, 0x00939c50 in consecutive records, all
inside EE RAM and ascending across the table as a walk of consecutive objects
would be. Byte 11 becomes 0x30 in every record seen, against a constant 0x05 on
disc, and bytes 8-9 hold a varying small value (0x13, 0x16, 0x24).

So the packet table at +0x40 is what the loader walks: span-relative on disc,
absolute in RAM. This was inferred from one class before; these 40+ records
show it directly.

Consequence for the tag bytes: the vocabularies at record bytes 7 and 15 are
real, but byte 15 sits in a field the loader overwrites with a pointer. Its
disc values are read as data by something rather than stored as a tag.

Not settled: whether byte 7's vocabulary is also rewritten, and whether 0x6d
marks payload the loader expands. That needs these records compared one by one
against their disc spans, which is cheap now that the anchor exists.
This commit is contained in:
2026-10-12 02:15:30 +02:00
parent d1aec9f866
commit 3854e52019
+48
View File
@@ -163,3 +163,51 @@ Advanced); the socket is `$XDG_RUNTIME_DIR/pcsx2.sock` on Linux.
**Nothing here redistributes game data.** The disc, the BIOS and the
savestates are the user's own; the repository holds the tool and the numbers.
## Shrub packet headers are rewritten in RAM: the loader relocates them
Answering the "what is the tag byte" question needed an anchor, and searching
RAM from scratch was the wrong way to get one -- it finds coincidental matches
(`04 04 00 01` matches unrelated data at `0x1ac988`) and no way to tell them
apart. The savestates are the anchor: slot `.01` is level 0, and searching one
gives candidates that can be *checked* rather than guessed at.
**Range measured:** savestate slot 1,
`~/.config/PCSX2/sstates/SCUS-97268 (38996035).01.p2s`, records at `0x22a6b8`
onward, read with `gc-trace read --state`.
**Result: 40+ records beginning `04 04 00 01 00 00 00 00` at `0x30` spacing**,
which is the packet framing identified on disc, and the loader has rewritten
the second half of each header:
disc: 04 04 00 01 | 00 00 00 00 | 00 00 00 05 | 00 80 0e 6c
RAM: 04 04 00 01 | 00 00 00 00 | 13 00 00 30 | 70 6a 93 00
Bytes 0-7 are unchanged. Bytes 8-11 and 12-15 are not:
- on disc, bytes 12-13 are the constant `00 80` and byte 15 the constant `6c`;
- in RAM, bytes 12-15 hold what is plainly a pointer -- `0x00936a70`,
`0x009376f0`, `0x00937850`, `0x00939c50` in consecutive records, all inside
EE RAM, and ascending across the table exactly as a walk of consecutive
objects would be.
Byte 11 becomes `0x30` in every record seen, against a constant `0x05` on
disc, and bytes 8-9 hold a varying small value (`0x13`, `0x16`, `0x24`).
**What this means.** The packet table at `+0x40` is the thing the loader walks:
it holds span-relative offsets on disc and absolute RAM addresses after
loading, which is what the previous pass found and what these 40+ records show
directly. Any header field read past byte 8 on disc is describing something the
loader replaces.
**Consequence for the tag bytes.** The values at record bytes 7 and 15 measured
on disc are *not* stable identifiers -- at least byte 15 sits in a field the
loader overwrites with a pointer. The four-value and two-value vocabularies are
real (0x30, 0x6d, 0x80 and 0 appear against 1636 records of otherwise-uniform
bytes), but byte 15 being a pointer in RAM means its disc values are being read
as data by something, not stored as a tag.
**Not settled:** whether byte 7's vocabulary is also rewritten, and whether
`0x6d` marks a payload that the loader expands. That needs the same records
compared one by one against their disc spans, which is the next step and is
now cheap because the anchor exists.