Two class-header words are base-relative offsets pointing past the tie packets
Chasing where the AD-GIF is, sincef43a1c2established 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 withf43a1c2: 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:
@@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user