Decode the tie LOD block header; establish the payload is variable-length

TieBlockHeader parses the block's own 16 bytes, measured over the 922 blocks
where "the next packets offset ends this block" holds, across all 27 levels. A
real-disc test asserts every figure.

  +0x00  count        multiple of 16 on 922/922; 16..2096; 72 distinct disc-wide
  +0x02               zero on 922/922
  +0x04               ONE OF FOUR VALUES DISC-WIDE: 0x501, 0xa02, 0xf03, 0x1404
  +0x06               15 distinct, 0x0206..0x0512
  +0x08               135 distinct, 0x0b05..0x4ee4
  +0x0a               45 distinct
  +0x0c               94 distinct, 0x010e..0x0553
  +0x0e               zero on 914/922

+0x04 is packed rather than a magnitude: four values across the whole disc with
the two bytes moving independently, low 1-4 and high 5/10/15/20 in lockstep. That
it is a texture index paired with a scale is a guess; that it is two fields is
not.

The count is not a record count -- block_size minus the header is divisible by it
on 0 of 922, for any record size and any header up to 512 bytes.

On the payload, a control was needed and changed the answer. Reading blocks as
3 x f32 triples, 53-64% of reads land within +/-5000 of origin, against 16% for
a uniform-random control of identical length. So the content is float-shaped and
genuinely not random.

But the rate is flat across every start offset and every stride tried, 53-65%
throughout. A statistic that cannot separate the right stride from the wrong one
cannot identify a stride -- rule 4's exact case. Flatness plus a count that does
not tile means the records are variable length.

That killed two readings. A VIF1 command walk consumes 0 of 88 blocks exactly,
so it is not a VIF1 CPU-format stream. wad::decompress rejects the block at every
start offset, so it is not WAD LZ77.

Scanning every (start, stride) pair from 0x10 to 0x7f for byte positions with a
small distinct-value vocabulary finds none: every position takes all 256 values
at every alignment. For float payload that is expected, so the scan found nothing
rather than confirming anything -- worth saying, because "no fields found" here is
uninformative, not evidence against fields.

Still open: what the payload holds. Established: variable-length, float-shaped,
non-random, not LZ77, not VIF1. Not established: any record boundary.
This commit is contained in:
2026-10-12 04:09:08 +02:00
parent 881fd48e9a
commit 02342de31b
3 changed files with 204 additions and 0 deletions
+55
View File
@@ -1166,3 +1166,58 @@ the kind of field that gets mistaken for a pointer. It is now
**Still not decoded:** what the 16 bytes hold. `i16[0]` at `+4` takes 8,969
distinct values on level 0 and is not the two-valued field a 12-unit sample
suggested; the word at `+0` is not a run offset.
## tie: the LOD block header, decoded
Over the **922** blocks where "the next `packets` offset ends this block" holds,
the block's own 16-byte header is measured. `gc_formats::tie::TieBlockHeader`
parses it and a real-disc test asserts every line below.
+0x00 u16 count multiple of 16 on 922/922; range 16..2096;
72 distinct values disc-wide
+0x02 u16 zero on 922/922
+0x04 u16 ONE OF FOUR VALUES DISC-WIDE: 0x501, 0xa02, 0xf03,
0x1404 -- low byte 1..4, high byte 5/10/15/20
+0x06 u16 15 distinct, 0x0206..0x0512
+0x08 u16 135 distinct, 0x0b05..0x4ee4
+0x0a u16 45 distinct
+0x0c u16 94 distinct, 0x010e..0x0553
+0x0e u16 zero on 914/922
**`+0x04` is packed, not a magnitude.** Four values across the whole disc, with
the two bytes moving independently (low 1-4, high 5/10/15/20 in lockstep), is two
small fields sharing a word. That it is a texture index paired with a scale is a
guess; that it is two fields is not.
**The count is not a record count.** `block_size - header` is divisible by it on
**0 of 922** blocks, for *any* record size and *any* header up to 512 bytes.
Combined with block sizes always being multiples of 16, that rules out a uniform
stride without a header.
### What the payload is, and what retired the coordinate reading
The payload is **not random**, and that took a control to establish. Reading each
block as 3 x `f32` triples, 53-64% of reads land within +/-5000 of origin; a
uniform-random control of identical length gives **16%**. So the content really
is float-shaped.
But the rate is **flat across every start offset and every stride tried**
(0x10..0x40 start, 8/12/16/20/24 stride: 53-65% throughout). A statistic that
cannot distinguish the right stride from the wrong one cannot identify a stride,
which is rule 4's exact case. The flatness is the informative part: combined
with the count not tiling, it says the records are **variable length**.
A VIF1 command walk was implemented and tested against the blocks: it consumes
**0 of 88** exactly. So the payload is variable-length and not a VIF1 CPU-format
stream. Not WAD LZ77 either -- `wad::decompress` rejects the block at every
start offset tried.
Scanning every (start, stride) pair from 0x10 to 0x7f for byte positions with a
small distinct-value vocabulary -- the signature of integer index fields -- finds
**none**: every byte position takes all 256 values at every alignment. For float
payload that is expected, and it is why the field scan found nothing rather than
confirming anything.
**Still open: what the payload holds.** Established is that it is variable-length,
float-shaped, non-random, not LZ77, and not VIF1. Not established is any record
boundary.