Parse the shrub packet header, a fixed signature on all 2056 disc packets

The packets found by the +0x40 table have a header the whole disc agrees on.

Range measured: every packet on the disc -- 2056 of them, across the 334
successor-bounded spans -- read at each packet's span-relative offset.

  bytes 0-3   04 04 00 01   2056 / 2056
  bytes 4-7   zero           2056 / 2056
  byte  8     zero           2056 / 2056
  byte  11    0x05           2056 / 2056
  size % 16   zero           2056 / 2056

Zero exceptions on any of the five.

And the loader fills the rest in. Read back out of the running game the same
records carry byte 8 as a count (0x0b to 0x29 observed), byte 11 as one of four
tag values, and bytes 12-15 as an absolute EE RAM pointer where the disc has a
span-relative offset. Bytes 8-15 are rewritten wholesale on load.

That settles what the disc header is for. It carries no count of its own, and
the count the RAM copy has is the +0x40 table's count arriving by another
route. The disc header is a signature; the RAM header is populated state.

ShrubPacketHeader::parse and Shrub::packet_headers() expose this. parse returns
None unless all the invariants hold, rather than a default, so a packet that
does not match is reported as not-understood instead of half-interpreted.

The gated test asserts all four header invariants and the size alignment on
every packet of every bounded span.

This is the first piece of the mesh packet format represented in gc-formats;
everything known about the packet payloads so far is RAM measurement, since the
disc form of bytes 8-15 is rewritten at load.
This commit is contained in:
2026-10-12 02:35:00 +02:00
parent af0b1aa949
commit 5ade83cf0a
3 changed files with 141 additions and 1 deletions
+33
View File
@@ -1263,3 +1263,36 @@ PS2 primitive lists, which is the obvious next reference to bring in.
VIF stream: the 16-byte tiling is uniform across all 29 packets with no
command-shaped tail, and a VIF packet would show a changing command byte in its
last position rather than the constant `6c` at header byte 15.
## Shrub packet headers: a fixed disc signature, rewritten on load
The packets found by the `+0x40` table have a header the whole disc agrees on.
**Range measured:** every packet on the disc -- **2,056 of them**, across the
334 successor-bounded spans -- read at each packet's span-relative offset.
**Result:**
| bytes | value | packets |
|---|---|---|
| 0-3 | `04 04 00 01` | **2056 / 2056** |
| 4-7 | zero | **2056 / 2056** |
| 8 | zero | **2056 / 2056** |
| 11 | `0x05` | **2056 / 2056** |
Every packet's size is also a multiple of 16 (2056 / 2056). Zero exceptions on
any of the four.
**And the loader fills the rest in.** Read back out of the running game, the
same records carry byte 8 as a **count** (0x0b to 0x29 observed), byte 11 as
one of four tag values, and bytes 12-15 as an absolute EE RAM pointer where the
disc has a span-relative offset.
So **bytes 8-15 are rewritten wholesale on load.** What that means for reading
the disc: the header carries no count of its own, and the one the RAM copy has
is the `+0x40` table's `count` arriving by another route. The disc header is a
signature; the RAM header is populated state.
`ShrubPacketHeader::parse` and `Shrub::packet_headers()` expose this, and the
gated test asserts all four invariants plus the size alignment on every packet
of every bounded span.