feat(formats,cli): recover entry TOC paths from IDXD identity
Add hash::recover_toc_name + TOC_NAME_SCHEMES (confirmed path schemes: unit\<ID>.tbl, weapon\<ID>.tbl, message\<ID>.tbl, effect\<ID>.tbl, <name>.tbl) that reproduce an entry's original backslash path from its internal identity string via the recovered name-hash. `pak list` now extracts identifier candidates from each IDXD entry (ID/Name/Model + pool tokens) and prints the resolved path when a scheme matches — e.g. unit\UN_f001_TCAF_DeltaSaber_T.tbl, weapon\Weapon_…_Missile.tbl, message\CharacterCARL.tbl — plus a name-resolved count. 308/1004 resolved on GP_MAIN_GAME_E; the remainder use deeper cross-referenced paths (deferred). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -398,6 +398,24 @@ fn idxd_identity(obj: &IdxdObject) -> String {
|
||||
format!("schema {:08x}", obj.schema_hash)
|
||||
}
|
||||
|
||||
/// Try to recover an IDXD entry's original TOC path from its identity tokens.
|
||||
/// Uses the entry's ID/Name/Model fields plus identifier-like pool tokens as
|
||||
/// candidates for [`sylpheed_formats::hash::recover_toc_name`].
|
||||
fn idxd_toc_name(obj: &IdxdObject, name_hash: u32) -> Option<String> {
|
||||
let mut cands: Vec<&str> = Vec::new();
|
||||
for key in ["ID", "Name", "Model"] {
|
||||
if let Some(v) = obj.get_raw(key) {
|
||||
cands.push(v);
|
||||
}
|
||||
}
|
||||
for t in obj.tokens() {
|
||||
if t.contains('_') || t.len() >= 5 {
|
||||
cands.push(t.as_str());
|
||||
}
|
||||
}
|
||||
sylpheed_formats::hash::recover_toc_name(name_hash, &cands)
|
||||
}
|
||||
|
||||
fn cmd_pak_list(pak: &Path, idxd_only: bool) -> Result<()> {
|
||||
let arc = PakArchive::open(pak).with_context(|| format!("opening {}", pak.display()))?;
|
||||
println!(
|
||||
@@ -409,6 +427,7 @@ fn cmd_pak_list(pak: &Path, idxd_only: bool) -> Result<()> {
|
||||
);
|
||||
|
||||
let mut shown = 0usize;
|
||||
let mut named = 0usize;
|
||||
for e in arc.entries() {
|
||||
let payload = match arc.read(e) {
|
||||
Ok(p) => p,
|
||||
@@ -422,23 +441,36 @@ fn cmd_pak_list(pak: &Path, idxd_only: bool) -> Result<()> {
|
||||
if idxd_only && !is_idxd {
|
||||
continue;
|
||||
}
|
||||
let detail = if is_idxd {
|
||||
IdxdObject::parse(&payload)
|
||||
.map(|o| idxd_identity(&o))
|
||||
.unwrap_or_default()
|
||||
let (detail, name) = if is_idxd {
|
||||
match IdxdObject::parse(&payload) {
|
||||
Ok(o) => (idxd_identity(&o), idxd_toc_name(&o, e.name_hash)),
|
||||
Err(_) => (String::new(), None),
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
(String::new(), None)
|
||||
};
|
||||
let name_col = match &name {
|
||||
Some(p) => p.clone(),
|
||||
None => "?".into(),
|
||||
};
|
||||
println!(
|
||||
" {:08x} {:>9} B {:<6} {}",
|
||||
" {:08x} {:<28} {:>9} B {:<6} {}",
|
||||
e.name_hash,
|
||||
name_col.green(),
|
||||
payload.len(),
|
||||
label.yellow(),
|
||||
detail.dimmed(),
|
||||
);
|
||||
if name.is_some() {
|
||||
named += 1;
|
||||
}
|
||||
shown += 1;
|
||||
}
|
||||
println!("\n {} entries shown", shown.to_string().yellow());
|
||||
println!(
|
||||
"\n {} entries shown ({} name-resolved)",
|
||||
shown.to_string().yellow(),
|
||||
named.to_string().green(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user