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:
@@ -1664,6 +1664,7 @@ fn shrub_headers_name_their_own_class_on_every_level() {
|
||||
let mut billboards = 0usize;
|
||||
let mut billboard_entries = 0usize;
|
||||
let mut packets_total = 0usize;
|
||||
let mut packet_headers_total = 0usize;
|
||||
|
||||
for n in 0..27 {
|
||||
let dir = data.join(format!("level{n}"));
|
||||
@@ -1803,6 +1804,30 @@ fn shrub_headers_name_their_own_class_on_every_level() {
|
||||
);
|
||||
packets_total += packets.len();
|
||||
|
||||
// The packet headers themselves. The disc form is a fixed
|
||||
// signature -- magic, four zero bytes, tag 0x05 -- and the
|
||||
// loader rewrites bytes 8-15 on load, so the count and the
|
||||
// pointer live in RAM, not here.
|
||||
let hdrs: Vec<_> = s.packet_headers().collect();
|
||||
assert_eq!(
|
||||
hdrs.len(),
|
||||
packets.len(),
|
||||
"level{n} class {i}: header count disagrees with packet count"
|
||||
);
|
||||
for (pi, h) in hdrs.iter().enumerate() {
|
||||
assert!(
|
||||
h.is_some(),
|
||||
"level{n} class {i}: packet {pi} header does not match the \
|
||||
disc invariants (magic 04 04 00 01, bytes 4-8 zero, tag 0x05)"
|
||||
);
|
||||
assert_eq!(
|
||||
packets[pi].size % 16,
|
||||
0,
|
||||
"level{n} class {i}: packet {pi} size is not a multiple of 16"
|
||||
);
|
||||
}
|
||||
packet_headers_total += hdrs.len();
|
||||
|
||||
// The billboard: present exactly when the class entry
|
||||
// carries billboard texture fields.
|
||||
let entry_has_billboard = e.billboard_width != 0 || e.billboard_height != 0;
|
||||
@@ -1856,7 +1881,7 @@ fn shrub_headers_name_their_own_class_on_every_level() {
|
||||
all {bounded_bodies} bodies bounded, {bounded_records} records, \
|
||||
{palette_records} of them palette normals and {padding_records} zero padding; \
|
||||
{billboards} billboards, all with texture fields; \
|
||||
{packets_total} packets across all blobs; \
|
||||
{packets_total} packets across all blobs, {packet_headers_total} headers; \
|
||||
mode_bits {mode_hist:?}, radius {radius_range:?}, scale {scale_range:?}, \
|
||||
largest palette deviation from unit length {norm2_deviation} of a possible 65535"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user