use sylpheed_formats::{idxd::IdxdObject, PakArchive}; fn main() { let disc = std::env::var("SYLPHEED_DISC").unwrap(); let arc = PakArchive::open(format!("{disc}/dat/GP_MAIN_GAME_E.pak")).unwrap(); // 1. All stages: BackGroundID + phases + stage tag (from EnumUnit_SNN) println!("=== STAGES (StageResource 3c9ae32e) ==="); let mut rows = vec![]; for e in arc.entries() { let Ok(b) = arc.read(e) else { continue }; let Ok(o) = IdxdObject::parse(&b) else { continue; }; if o.schema_hash != 0x3c9ae32e { continue; } let t = o.tokens(); let bg = o.get_raw("BackGroundID").unwrap_or("?").to_string(); let stage = t .iter() .find_map(|s| { s.strip_prefix("EnumUnit_") .map(|x| x.trim_end_matches(".tbl").to_string()) }) .unwrap_or("?".into()); let phases = t.iter().filter(|s| s.starts_with("Phase_")).count(); let unitgrp = t.iter().any(|s| s.starts_with("UnitGroup_")); let route = t.iter().any(|s| s.starts_with("Route_")); let formation = t.iter().any(|s| s.starts_with("FormationSet_")); rows.push((stage, bg, phases, unitgrp, route, formation)); } rows.sort(); for (s, bg, p, ug, rt, fm) in &rows { println!( " {s:8} location={bg:14} phases={p} [squadrons:{} route:{} formations:{}]", if *ug { "Y" } else { "·" }, if *rt { "Y" } else { "·" }, if *fm { "Y" } else { "·" } ); } // 2. Parse objectives table into (stage,phase) -> key counts println!("\n=== OBJECTIVES (033b5b7e) — per stage/phase key groups ==="); for e in arc.entries() { let Ok(b) = arc.read(e) else { continue }; let Ok(o) = IdxdObject::parse(&b) else { continue; }; if o.schema_hash != 0x033b5b7e { continue; } let t = o.tokens(); let mut groups: std::collections::BTreeMap = Default::default(); for tok in t { if let Some(p) = tok .find("_Objective_") .or_else(|| tok.find("_Lose_")) .or_else(|| tok.find("_Hint_")) { let key = &tok[..p]; *groups .entry( key.trim_start_matches(|c: char| !c.is_ascii_alphabetic()) .into(), ) .or_default() += 1; } } let stages: std::collections::BTreeSet = groups .keys() .filter_map(|k| k.get(..3).map(|s| s.to_string())) .collect(); println!(" covers {} stages: {:?}", stages.len(), stages); println!( " sample S01_P1 group counts: {:?}", groups .iter() .filter(|(k, _)| k.starts_with("S01_P1")) .collect::>() ); break; } // 3. Resolve objective TEXT: search all pak entries for the key "S01_P1_Objective_00" as an ID with English text println!("\n=== OBJECTIVE TEXT resolution probe ==="); for e in arc.entries() { let Ok(b) = arc.read(e) else { continue }; let Ok(o) = IdxdObject::parse(&b) else { continue; }; let t = o.tokens(); if let Some(i) = t.iter().position(|s| s == "S01_P1_Objective_00") { let lo = i.saturating_sub(1); let hi = (i + 3).min(t.len()); println!(" schema {:08x}: ...{:?}...", o.schema_hash, &t[lo..hi]); } } }