Decode Project Sylpheed's IDXD data model into plain, cloneable structs (from GP_MAIN_GAME_E.pak; the D/F/I/J/S paks are localized dups): - Weapon (121), CraftUnit (89, with the AI flight model in .fields), Vessel (23, structural component counts), PlayerConfig (24) — the full combat balance. - Character (68 — faction + portrait set), Stage (29 missions: location, phases, and the per-stage resource tables it wires up). - Generic Record / load_records(schema) reads any of the 105 IDXD schemas without a bespoke struct. Each blob = one entity; token[0] names the table, fields are value-before-key. Named Option<> fields for the common stats + a full `fields` map; defaulted fields stay None (they're code-resident, not on disc — honest blanks, never guessed). 8 tests against the real disc. Examples: iso_map / deep_map census the ISO's formats and IDXD schemas; dm_extract / dm_rows / dm_roster / mission_map dump the decoded data. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
30 lines
1.7 KiB
Rust
30 lines
1.7 KiB
Rust
use sylpheed_formats::{idxd::IdxdObject, PakArchive};
|
|
use std::collections::BTreeMap;
|
|
fn main(){
|
|
let disc=std::env::var("SYLPHEED_DISC").unwrap();
|
|
let arc=PakArchive::open(format!("{disc}/dat/GP_MAIN_GAME_E.pak")).unwrap();
|
|
let tables:[(u32,&str);4]=[(0x0426e81d,"Player / physics+scoring"),(0x6ab4825a,"Weapon"),(0x43faa517,"Unit / craft"),(0x3c5b0549,"Vessel / capital ship")];
|
|
for (want,label) in tables{
|
|
// collect all records of this schema
|
|
let mut recs:Vec<IdxdObject>=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==want{recs.push(o);} }
|
|
// field-union (explicit-valued keys), with occurrence count
|
|
let mut cols:BTreeMap<String,u32>=BTreeMap::new();
|
|
for o in &recs{ for (k,_) in o.resolved_fields(){ *cols.entry(k.into()).or_default()+=1; } }
|
|
println!("\n╔══ {label} [{want:08x}] {} records ══", recs.len());
|
|
let mut cv:Vec<_>=cols.into_iter().collect(); cv.sort_by_key(|x|std::cmp::Reverse(x.1));
|
|
let colstr:String=cv.iter().take(28).map(|(k,c)|format!("{k}({c})")).collect::<Vec<_>>().join(" ");
|
|
println!("║ numeric/enum fields: {colstr}");
|
|
// dump 2 sample records: identity + explicit fields
|
|
for o in recs.iter().take(2){
|
|
let id=o.get_raw("ID").unwrap_or("?");
|
|
let name=o.get_raw("Name").unwrap_or("");
|
|
print!("║ • {id}");
|
|
if !name.is_empty(){print!(" «{name}»");}
|
|
println!();
|
|
let fields:Vec<String>=o.resolved_fields().iter().map(|(k,v)|format!("{k}={v}")).collect();
|
|
for chunk in fields.chunks(5){ println!("║ {}", chunk.join(" ")); }
|
|
}
|
|
}
|
|
}
|