Add gc-extract probe to dump structures through the real parser
Reading these formats by unpacking raw offsets out of core_index.bin has cost real time. The index interleaves single-word region offsets with count/offset pairs, so miscounting one shifts every field after it: that produced a page of implausible collision headers on all 27 levels, sent me chasing a tie-breakoff that turned out not to exist, and nearly had me "fix" a stride bug in code that was already right. probe dumps a level's core header and all three class tables through LevelCoreHeader, so a field name printed is the field name the parser uses. It also reports how many entries carry geometry inside core_data, which is what pins the class table strides rather than guessing at them: shrub reads 7/7 sane at 0x30 against 4/7 read at 0x20. Reading a structure by hand in a shell still works; this just stops it being the default, and stops the offset arithmetic being re-derived every time a new region is investigated.
This commit is contained in:
@@ -26,6 +26,7 @@ fn main() -> ExitCode {
|
||||
// byte range of an already-unpacked file with the WAD decoder.
|
||||
Some("wad") => run_wad(&args[1..]),
|
||||
Some("dump-texture") => run_dump_texture(&args[1..]),
|
||||
Some("probe") => run_probe(&args[1..]),
|
||||
Some("--help") | Some("-h") | Some("help") | None => {
|
||||
usage();
|
||||
ExitCode::SUCCESS
|
||||
@@ -66,6 +67,12 @@ fn usage() {
|
||||
verify <out-dir> <table.tsv>
|
||||
Check <out-dir> against a previously written table.
|
||||
|
||||
probe <unpacked-dir> <level> [max-rows]
|
||||
Dump a level's core header and class tables through the same parser the
|
||||
rest of the project uses. Reach for this before reading core_index.bin
|
||||
by hand: the index interleaves single-word region offsets with
|
||||
count/offset pairs, and miscounting one shifts every field after it.
|
||||
|
||||
dump-texture <unpacked-dir> <level> <index> [mip] <out.ppm>
|
||||
Decode one tfrag texture to a binary PPM, so a decode can be eyeballed
|
||||
against the disc rather than trusted. Textures are 8-bit indexed; a wrong
|
||||
@@ -314,6 +321,73 @@ fn run_dump_texture(args: &[String]) -> ExitCode {
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
|
||||
fn run_probe(args: &[String]) -> ExitCode {
|
||||
if args.len() < 2 {
|
||||
eprintln!("usage: gc-extract probe <unpacked-dir> <level> [max-rows]");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
let dir = Path::new(&args[0]);
|
||||
let level: u32 = match args[1].parse() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
eprintln!("bad level: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
let max_rows: usize = args.get(2).and_then(|s| s.parse().ok()).unwrap_or(8);
|
||||
let p = match gc_extract::probe_level(dir, level) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("error: {e:#}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
println!(
|
||||
"level{level}: core_data {} bytes, core_index {} bytes",
|
||||
p.core_len, p.index_len
|
||||
);
|
||||
println!("core header");
|
||||
println!("{}", "-".repeat(64));
|
||||
for (k, v) in &p.header {
|
||||
println!(" {k:<22} {v:>14}");
|
||||
}
|
||||
for t in &p.classes {
|
||||
let sane = t.entries.iter().filter(|e| e.in_core(p.core_len)).count();
|
||||
println!();
|
||||
println!(
|
||||
"{} classes: {} entries at stride 0x{:x}, {sane} with geometry in core_data",
|
||||
t.name,
|
||||
t.entries.len(),
|
||||
t.stride
|
||||
);
|
||||
println!("{}", "-".repeat(64));
|
||||
println!(
|
||||
" {:>4} {:>12} {:>8} {:>10} {:>10}",
|
||||
"idx", "geom_offset", "o_class", "unknown_8", "unknown_c"
|
||||
);
|
||||
for e in t.entries.iter().take(max_rows) {
|
||||
println!(
|
||||
" {:>4} {:>12} {:>8} {:>10} {:>10}{}",
|
||||
e.index,
|
||||
e.geometry_offset,
|
||||
e.o_class,
|
||||
e.unknown_8,
|
||||
e.unknown_c,
|
||||
if e.in_core(p.core_len) {
|
||||
""
|
||||
} else {
|
||||
" <- outside core_data"
|
||||
}
|
||||
);
|
||||
}
|
||||
if t.entries.len() > max_rows {
|
||||
println!(" ... {} more", t.entries.len() - max_rows);
|
||||
}
|
||||
}
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
|
||||
fn run_wad(args: &[String]) -> ExitCode {
|
||||
if args.len() < 4 {
|
||||
eprintln!("usage: gc-extract wad <file> <offset> <size> <out>");
|
||||
|
||||
Reference in New Issue
Block a user