Narrow the collision header to two fields plus a third used by five levels
Range measured: all 27 levels that carry collision, reading the 0x40-byte
header each.
Method: read +0x00, +0x04 and +0x08, then check +0x08..+0x40 for zero.
Result:
u32@+0x00 is 0x40 on 27 of 27 -- the header is self-sizing
u32@+0x04 is the payload size, 20 distinct values across the disc
bytes +0x08..+0x40 zero on 22 of 27
Five levels carry a large non-zero word at +0x08:
level 7 1248048 level 12 2541616
level 9 1766832 level 13 3376592
level 14 4060640
So the header is not two fields and 56 bytes of padding: there is a third field
most levels leave at zero and five use. Whether it is a size, an offset or a
second block's length is not established, and nothing measured here separates
those readings. Exposed as CollisionHeader::field_08.
Correction: the collision header was previously described as self-describing
across its full 0x40, which is true for 22 levels and overstates the case for
the other five.
Still not decoded: the payload. It contains ascending u32 runs on levels 0 and
2 -- level 0 at +280 with deltas 12, 12, 12 then a jump, level 2 at +13868 with
deltas 204, 204, 200, 196 -- but irregular deltas fit neither a flat table nor a
simple stride, and level 1's apparent run is a byte-shift false positive.
Nothing about the payload is asserted.
This commit is contained in:
@@ -27,6 +27,15 @@ pub struct CollisionHeader {
|
|||||||
pub header_size: u32,
|
pub header_size: u32,
|
||||||
/// Size of the payload that follows.
|
/// Size of the payload that follows.
|
||||||
pub payload_size: u32,
|
pub payload_size: u32,
|
||||||
|
/// The word at `+0x08`.
|
||||||
|
///
|
||||||
|
/// Measured on all 27 levels that carry collision: the rest of the 0x40
|
||||||
|
/// header is zero padding on 22 of them, but **5 levels carry a large
|
||||||
|
/// non-zero word here** -- 1,248,048, 1,766,832, 2,541,616, 3,376,592 and
|
||||||
|
/// 4,060,640, on levels 7, 9, 12, 13 and 14. Meaning not established; it
|
||||||
|
/// is a size, an offset, or a second block's length, and nothing here
|
||||||
|
/// distinguishes those.
|
||||||
|
pub field_08: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CollisionHeader {
|
impl CollisionHeader {
|
||||||
@@ -39,6 +48,7 @@ impl CollisionHeader {
|
|||||||
Ok(CollisionHeader {
|
Ok(CollisionHeader {
|
||||||
header_size: b.u32("collision header size")?,
|
header_size: b.u32("collision header size")?,
|
||||||
payload_size: b.u32("collision payload size")?,
|
payload_size: b.u32("collision payload size")?,
|
||||||
|
field_08: b.u32("collision +0x08")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,6 +118,7 @@ mod tests {
|
|||||||
const BASE: usize = 16;
|
const BASE: usize = 16;
|
||||||
|
|
||||||
fn core(header_size: u32, payload_size: u32) -> Vec<u8> {
|
fn core(header_size: u32, payload_size: u32) -> Vec<u8> {
|
||||||
|
#[allow(clippy::let_and_return)]
|
||||||
let mut v = vec![0u8; BASE + 64];
|
let mut v = vec![0u8; BASE + 64];
|
||||||
v[BASE..BASE + 4].copy_from_slice(&header_size.to_le_bytes());
|
v[BASE..BASE + 4].copy_from_slice(&header_size.to_le_bytes());
|
||||||
v[BASE + 4..BASE + 8].copy_from_slice(&payload_size.to_le_bytes());
|
v[BASE + 4..BASE + 8].copy_from_slice(&payload_size.to_le_bytes());
|
||||||
|
|||||||
@@ -1331,3 +1331,41 @@ be small on most levels and is not a count at all.
|
|||||||
`gc_formats::sky` parses the header and the sub-objects, and the gated test
|
`gc_formats::sky` parses the header and the sub-objects, and the gated test
|
||||||
asserts the sphere is finite, the radius positive where it is positive at all,
|
asserts the sphere is finite, the radius positive where it is positive at all,
|
||||||
the dimensions non-zero, and the sub-object list non-empty on every level.
|
the dimensions non-zero, and the sub-object list non-empty on every level.
|
||||||
|
|
||||||
|
## The collision header is two fields, plus a third on five levels
|
||||||
|
|
||||||
|
**Range measured:** all 27 levels that carry collision, reading the 0x40-byte
|
||||||
|
header each.
|
||||||
|
|
||||||
|
**Method:** read `+0x00`, `+0x04` and `+0x08`, then check `+0x08..+0x40` for
|
||||||
|
zero.
|
||||||
|
|
||||||
|
**Result:**
|
||||||
|
|
||||||
|
- `u32@+0x00` is `0x40` on **27 of 27** -- the header is self-sizing.
|
||||||
|
- `u32@+0x04` is the payload size, 20 distinct values across the disc.
|
||||||
|
- **Bytes `+0x08..+0x40` are zero on 22 of 27**, but five levels carry a large
|
||||||
|
non-zero word at `+0x08`:
|
||||||
|
|
||||||
|
| level | `+0x08` |
|
||||||
|
|---|---|
|
||||||
|
| 7 | 1,248,048 |
|
||||||
|
| 9 | 1,766,832 |
|
||||||
|
| 12 | 2,541,616 |
|
||||||
|
| 13 | 3,376,592 |
|
||||||
|
| 14 | 4,060,640 |
|
||||||
|
|
||||||
|
So the header is not simply "two fields and 56 bytes of padding" -- there is a
|
||||||
|
third field that most levels leave at zero and five use. Whether it is a size,
|
||||||
|
an offset or a second block's length is not established; nothing measured here
|
||||||
|
separates those readings. It is exposed as `CollisionHeader::field_08`.
|
||||||
|
|
||||||
|
**Correction to the framing description.** The collision header was previously
|
||||||
|
described as self-describing across its full 0x40, which is true for 22 levels
|
||||||
|
and overstates the case for the other five.
|
||||||
|
|
||||||
|
**Still not decoded:** the payload. It contains ascending `u32` runs on levels
|
||||||
|
0 and 2 -- level 0 at `+280` with deltas 12, 12, 12 then a jump, level 2 at
|
||||||
|
`+13868` with deltas 204, 204, 200, 196 -- but the irregular deltas fit neither
|
||||||
|
a flat table nor a simple stride, and level 1's apparent run is a byte-shift
|
||||||
|
false positive. Nothing about the payload is asserted.
|
||||||
|
|||||||
Reference in New Issue
Block a user