//! `cargo run -p benilla-formats --example chain_shadow` — every file a named archive carries that //! some *higher-priority* archive also carries, i.e. the part of that archive the client will never //! read. //! //! The question a realm update raises: the reference mounts `patch-?.MPQ` by case-folded name, so a //! letter patch outranks a digit patch (`chain::mount_order`). When an update ships as //! `patch-4`/`patch-5` and the install also holds `patch-H`, `patch-M`, `patch-O`… , every file the //! two sets share resolves to the *letter* one. `chain::demote_stale_snapshots` only catches an //! archive whose `FrameXML.toc` is a strict subset of another's, and a letter patch carrying no toc //! at all is not a candidate — so this is the measurement that says whether the update landed. //! //! Argument: one or more archive file names as they sit in `Data` (default `patch-4.mpq`, //! `patch-5.mpq`). fn main() { let Some(data) = benilla_formats::wow_data() else { eprintln!("no install"); std::process::exit(1); }; let args: Vec = std::env::args().skip(1).collect(); let wanted: Vec = if args.is_empty() { vec!["patch-4.mpq".into(), "patch-5.mpq".into()] } else { args }; let chain = benilla_formats::Chain::open(&data).expect("chain opens"); let all = chain.list().expect("chain lists"); println!("chain holds {} distinct paths", all.len()); for want in &wanted { // Every path this archive carries, from the chain's own per-file copy list. let mut carried = 0usize; let mut shadowed: Vec<(String, String)> = Vec::new(); for e in &all { let copies = chain.copies(&e.name); let mine = copies .iter() .any(|(p, _)| p.file_name().and_then(|n| n.to_str()) == Some(want.as_str())); if !mine { continue; } carried += 1; let winner = chain .find_file_archive(&e.name) .and_then(|p| p.file_name()) .and_then(|n| n.to_str()) .unwrap_or("?"); if winner != want { shadowed.push((e.name.clone(), winner.to_string())); } } println!( "\n== {want}: {carried} paths, {} shadowed ==", shadowed.len() ); let mut by_winner: std::collections::BTreeMap> = Default::default(); for (path, winner) in &shadowed { by_winner.entry(winner.clone()).or_default().push(path); } for (winner, paths) in &by_winner { println!(" lost to {winner}: {} path(s)", paths.len()); for p in paths.iter().take(12) { println!(" {p}"); } if paths.len() > 12 { println!(" … {} more", paths.len() - 12); } } } }