Identify the container the class tables point into: core_data itself

assets_compressed_size and assets_decompressed_size at index offsets
0x88 and 0x8c are 9,738,678 and 16,969,296 on level 0 -- exactly the
sizes of core_data before and after decompression. There is no separate
asset container. The "asset WAD" is core_data, and offset_in_asset_wad
indexes the decompressed form directly.

Checked rather than assumed, on all 27 levels: the decompressed size
equals core_data's exact length, every class offset is either 0 or inside
it, and the moby, tie and shrub spans do not overlap each other.

An offset of 0 means the slot has no geometry, not the start of the
lump; level 0's ninth moby class is 4909 at offset 0. Two assumptions
worth discarding while here -- the tables are not sorted by class number
(level 0's run 0, 1, 2, 14 ... 4909) and the offsets within a table are
not monotonic. Neither is needed, but both look true from the first few
entries.

The chunk descriptors after the named extents are not it: they exist on
only 3 of 27 levels, because level 0's four named extents tile the
container exactly. An asset blob there would be missing almost
everywhere.

What sits at the offsets is raw GS/VIF packet data rather than a further
container -- moby[0] begins 00 00 00 00 3f 16 02 55 6f 6f and tie[0]
00 01 00 00 70 50 00 00, which read as packet headers rather than a
magic number.

None of the three is decoded yet and this does not start that. Moby is
skeletal and is the largest single piece left before the rest of a level
can draw.

Also: the pipeline now writes each level's chunk descriptors where they
exist, and LevelCoreHeader decodes the asset lump's two sizes.
This commit is contained in:
2026-10-11 02:03:31 +02:00
parent e1105543e4
commit 4c4a78d402
6 changed files with 213 additions and 6 deletions
+20
View File
@@ -137,6 +137,26 @@ pub fn unpack(iso_path: &Path, out_dir: &Path) -> Result<Vec<Extracted>, String>
}
parts += 1;
}
// Chunk descriptors follow the named extents. One of them
// is the level's asset WAD, which the moby/tie/shrub class
// tables point into by byte offset.
for (i, range) in header.present_chunks() {
let Some(offset) = range.byte_offset() else {
continue;
};
let len = range.byte_len() as usize;
if offset + len as u64 > raw.len() as u64 {
continue;
}
write_file(
out_dir,
&format!("{stem}/chunk{i}.bin"),
&raw[offset as usize..offset as usize + len],
&mut written,
);
parts += 1;
}
// The `data` extent is itself a container of byte ranges.
// Split out the geometry directory and the geometry, so the
// latter is usable without re-deriving the layout later.