Two class-header words are base-relative offsets pointing past the tie packets

Chasing where the AD-GIF is, since f43a1c2 established it is not in the packet.
Two of the undecoded class-header words are offsets.

base + 0x1c and base + 0x34 land inside core_data on 1,864 of 1,864 classes, so
both are relative to the class base rather than absolute, and both are non-zero
on every class. Both also land **past** that class's own tie packet blocks on
every class, so they address per-class data held elsewhere in core_data rather
than the tie geometry.

That is consistent with f43a1c2: if the packet's own control region is not a tag
stream, the per-class draw state is referenced from somewhere else.

WHAT IS THERE, AND WHAT IS NOT CLAIMED

The bytes are structured, not random -- class 3 shows a fixed-stride table with
`80 01 00` recurring, class 2's field is a monotone byte table, class 5's has
`38 38 38` and `ff 00 ff ff`. They read like compiled shader programs with
small-integer parameters, or lookup tables.

**Not identified, and deliberately not named.** None of those three fragments has
been run against a control, and all three were picked because they looked like
something. That is precisely the finding rule 4 refuses, and having spent this
session on four retractions I would rather record an honest "structured,
unidentified" than a plausible format that has not earned it. If any of these
turns out to be a shader program the next step is a control, not a better-looking
hex dump.

ALSO SETTLED

0x24 is demonstrably not an offset: only 17 of 1,864 of its values land in range
for core_data. Recorded as packed data. 0x28, 0x2c, 0x38 remain high-entropy and
unexplained.

281 passing with the disc present (23 + 19 + 61 + 156 + 22), clippy clean.
This commit is contained in:
2026-10-14 05:03:34 +02:00
parent ff9699c3c7
commit 2ff2abbd32
3 changed files with 128 additions and 2 deletions
+82
View File
@@ -6539,3 +6539,85 @@ fn tie_class_header_tail_words() {
);
eprintln!("0x3c histogram: {w3c:?}");
}
/// `0x1c` and `0x34` of the tie class header are base-relative offsets, and they
/// land past the class's own tie packets.
#[test]
fn tie_class_tail_offsets_point_past_the_tie_packets() {
require_disc!();
let data = std::env::var_os("GC_EXTRACTED")
.map(PathBuf::from)
.unwrap_or_else(|| {
let home = std::env::var("HOME").unwrap_or_else(|_| ".".into());
PathBuf::from(home).join("Documents/gc-extracted")
});
if !data.exists() {
eprintln!("skipped: unpack the disc first");
return;
}
use gc_formats::level_core::{ClassEntry, LevelCoreHeader};
use gc_formats::tie::TieClassHeader;
let (mut classes, mut in_range, mut past_packets, mut both_nonzero) =
(0usize, 0usize, 0usize, 0usize);
for lv in 0..27usize {
let (Ok(idx), Ok(core)) = (
std::fs::read(data.join(format!("level{lv}/core_index.bin"))),
std::fs::read(data.join(format!("level{lv}/core_data.bin"))),
) else {
continue;
};
let t = LevelCoreHeader::parse(&idx).expect("hdr").tie_classes;
for c in 0..t.count as usize {
let o = t.offset as usize + c * 0x20;
let Ok(e) = ClassEntry::parse(&idx[o..o + 0x20]) else {
continue;
};
let base = e.offset_in_asset_wad as usize;
if base == 0 || base + 0x48 > core.len() {
continue;
}
let Ok(hdr) = TieClassHeader::parse(&core[base..base + 0x48]) else {
continue;
};
classes += 1;
let word = |k: usize| -> u32 {
let at = base + 0x1c + k * 4;
u32::from_le_bytes(core[at..at + 4].try_into().unwrap())
};
let (o1c, o34) = (word(0) as usize, word(6) as usize);
if usize::from(o1c) + 32 <= core.len() && o34 + 32 <= core.len() {
in_range += 1;
}
if word(0) != 0 && word(6) != 0 {
both_nonzero += 1;
}
// Past the class's own tie packet blocks.
let last_packet = hdr
.packets
.iter()
.filter(|p| **p > 0)
.max()
.copied()
.unwrap_or(0);
if o1c as i32 > last_packet && o34 as i32 > last_packet {
past_packets += 1;
}
}
}
assert!(classes > 1_800, "only {classes} tie classes read");
eprintln!("0x1c/0x34 in range {in_range}/{classes}, past own packets {past_packets}/{classes}");
// Both are relative to the class base, not absolute: base + offset lands in
// core_data, and absolute offsets would not.
assert!(
in_range * 100 >= classes * 99,
"only {in_range}/{classes} offsets in range"
);
assert_eq!(both_nonzero, classes, "a class has a zero offset");
// They are never inside the class's own tie data.
assert_eq!(
past_packets, classes,
"{classes} offsets fall inside the tie packets"
);
}
+8 -2
View File
@@ -111,8 +111,14 @@ impl TieClassHeader {
// bfc_distance is flat across every bucket.
// 0x40 a float -- 0.00208 to 10.84, no negatives. The bounding
// sphere radius or the class scale; not distinguished.
// 0x1c, 0x34 look like offsets (0x1e0..0x3cd60, 0x190..0x31e20) and
// 0x24, 0x28, 0x2c, 0x38 are high-entropy. Unidentified.
// 0x1c and 0x34 are base-relative offsets: base + offset lands inside
// core_data on 1,864 of 1,864 classes, and always *past* the class's own
// tie packet blocks, so they address per-class data stored elsewhere.
// The bytes there are structured -- repeating small-integer records,
// occasional `ff 00 ff ff` -- but are not VIF or GIF DMA tags and are
// not identified. See tie_class_tail_offsets_point_past_the_tie_packets.
// 0x24, 0x28, 0x2c, 0x38 are high-entropy and unidentified; 0x24 is
// demonstrably not an offset (17 of 1,864 land in range).
b.skip(0x44 - 0x1c, "tie class fields not decoded yet")?;
let o_class = b.i32("tie class number")?;
Ok(TieClassHeader {