Parse the per-level overlay: header, constant block, level data
level_overlay was the least-characterised file in the level set -- no parser, no located structure. It now has gc_formats::overlay and a real-disc test over all 27 levels: overlay: 27 levels; field_00 1735808, field_08 1; field_04 37540..=45308, field_0c 2763912..=3027336 (17 below the file length, 10 above); +0x10..0x1220 identical on all The header is four words. +0x0c is not the file length: it was recorded as one on the strength of the first levels measured, where it sat near the file end, but across all 27 it falls on both sides -- below on 17, above on 10 -- so it is an ordinary level-specific word. The test asserts this, being the claim most worth pinning. The common prefix is 4 bytes, not the whole header: +0x00 is constant at 1735808 while +0x04 and +0x0c vary. Everything from +0x10 to +0x1220 -- 4624 bytes -- is byte-identical on all 27 levels. Still not named: what any of the four header words means, and how the level-specific tail is structured. The module reports them as fields. Also records in docs/plan/pcsx2-verification.md that the 64-byte RAM stride from the previous commit is disproved. A 415 KB dump of level 1 finds the longest such run at 27 records, not 384, and those are identity matrices -- 27 of them, one per level. It is a per-level transform table, not the collision grid, and the 20.0 reported there was a coincidence.
This commit is contained in:
@@ -2466,3 +2466,121 @@ fn sky_sub_objects_parse_on_every_level() {
|
||||
counts.0, counts.1, dims.0, dims.1, radii.0, radii.1
|
||||
);
|
||||
}
|
||||
|
||||
/// The per-level overlay: the header is stable, `+0x10..+0x1220` is identical
|
||||
/// on every level, and `+0x0c` is not the file length.
|
||||
///
|
||||
/// See `gc_formats::overlay`. This pins the header and the constant block's
|
||||
/// extent; it does not name any field.
|
||||
#[test]
|
||||
fn overlay_header_and_constant_block_are_stable() {
|
||||
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(|_| ".".to_string());
|
||||
PathBuf::from(home).join("Documents/gc-extracted")
|
||||
});
|
||||
if !data.exists() {
|
||||
eprintln!("skipped: unpack the disc first");
|
||||
return;
|
||||
}
|
||||
|
||||
use gc_formats::overlay::{parse_overlay, OVERLAY_CONSTANT_BLOCK_END, OVERLAY_HEADER_SIZE};
|
||||
|
||||
let mut levels = 0usize;
|
||||
let mut constant_block: Option<Vec<u8>> = None;
|
||||
let mut f00 = 0u32;
|
||||
let mut f08 = 0u32;
|
||||
let (mut min_f04, mut max_f04) = (u32::MAX, 0u32);
|
||||
let (mut min_f0c, mut max_f0c) = (u32::MAX, 0u32);
|
||||
let mut f0c_below = 0usize;
|
||||
let mut f0c_above = 0usize;
|
||||
let mut levels_seen = Vec::new();
|
||||
|
||||
for n in 0..27 {
|
||||
let p = data.join(format!("level{n}/overlay.bin"));
|
||||
let Ok(bytes) = std::fs::read(&p) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let (h, constant, level) = parse_overlay(&bytes).expect("overlay should parse");
|
||||
|
||||
assert!(
|
||||
bytes.len() > OVERLAY_CONSTANT_BLOCK_END,
|
||||
"level{n}: overlay is {} bytes, too short for the constant block",
|
||||
bytes.len()
|
||||
);
|
||||
assert!(
|
||||
!level.bytes.is_empty(),
|
||||
"level{n}: overlay has no level data"
|
||||
);
|
||||
assert_eq!(
|
||||
constant.bytes.len(),
|
||||
OVERLAY_CONSTANT_BLOCK_END - OVERLAY_HEADER_SIZE
|
||||
);
|
||||
|
||||
if f00 == 0 {
|
||||
f00 = h.field_00;
|
||||
}
|
||||
assert_eq!(h.field_00, f00, "level{n}: field_00 differs");
|
||||
if f08 == 0 {
|
||||
f08 = h.field_08;
|
||||
}
|
||||
assert_eq!(h.field_08, f08, "level{n}: field_08 differs");
|
||||
|
||||
assert_eq!(
|
||||
h.field_04 % 4,
|
||||
0,
|
||||
"level{n}: field_04 {} is not a multiple of 4",
|
||||
h.field_04
|
||||
);
|
||||
min_f04 = min_f04.min(h.field_04);
|
||||
max_f04 = max_f04.max(h.field_04);
|
||||
|
||||
min_f0c = min_f0c.min(h.field_0c);
|
||||
max_f0c = max_f0c.max(h.field_0c);
|
||||
// The claim being pinned: field_0c is not the file length.
|
||||
if (h.field_0c as usize) < bytes.len() {
|
||||
f0c_below += 1
|
||||
} else {
|
||||
f0c_above += 1
|
||||
}
|
||||
|
||||
match &constant_block {
|
||||
None => constant_block = Some(constant.bytes.to_vec()),
|
||||
Some(prev) => assert_eq!(
|
||||
prev.as_slice(),
|
||||
constant.bytes,
|
||||
"level{n}: the constant block +0x10..0x{OVERLAY_CONSTANT_BLOCK_END:x} differs"
|
||||
),
|
||||
}
|
||||
|
||||
levels += 1;
|
||||
levels_seen.push(n);
|
||||
}
|
||||
|
||||
assert!(
|
||||
levels >= 27,
|
||||
"only {levels} levels had an overlay.bin, saw {levels_seen:?}"
|
||||
);
|
||||
assert!(f0c_below > 0 && f0c_above > 0,
|
||||
"field_0c must fall on both sides of the file length to show it is not one: below {f0c_below}, above {f0c_above}");
|
||||
assert_eq!(levels, 27, "expected all levels, got {levels_seen:?}");
|
||||
assert!(
|
||||
max_f04 - min_f04 > 1000,
|
||||
"field_04 range collapsed to {min_f04}..={max_f04}"
|
||||
);
|
||||
assert!(
|
||||
max_f0c - min_f0c > 100000,
|
||||
"field_0c range collapsed to {min_f0c}..={max_f0c}"
|
||||
);
|
||||
|
||||
eprintln!(
|
||||
"overlay: {levels} levels; field_00 {f00}, field_08 {f08}; \
|
||||
field_04 {min_f04}..={max_f04}, field_0c {min_f0c}..={max_f0c} \
|
||||
({f0c_below} below the file length, {f0c_above} above); \
|
||||
+0x10..0x{OVERLAY_CONSTANT_BLOCK_END:x} identical on all"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user