//! Where does the `DIFFICULTY` screen live? //! //! `boot-config-and-gamepart-registry.md` records a count-match — "Ⓑ = event 0, //! four menu items load an external archive, EXTRAS stays inside GP_TITLE" — //! explicitly as an observation, not a decode. The disc can test half of it: //! OPTIONS, LOAD GAME and TUTORIAL have their own paks, and EXTRAS' two items //! have GP_MISSION_SELECT / GP_MOVIE_THEATER while EXTRAS itself is GP_TITLE //! entries 6/9. NEW GAME is the fourth, and there is no GP_DIFFICULTY.pak. //! //! So: which archive holds a build with EASY / NORMAL / HARD buttons? //! //! CONTROL: the same scan must find the EXTRAS build in GP_TITLE, whose location //! is independently known (entries 6/9, buttons ptbtn11/12/13). //! //! cargo run -p sylpheed-formats --example find_difficulty_build use std::path::PathBuf; use sylpheed_formats::{pak::PakArchive, ui_layout}; fn main() { let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let dat = root.join("dat"); let mut paks: Vec<_> = std::fs::read_dir(&dat) .expect("dat") .filter_map(|e| e.ok().map(|e| e.path())) .filter(|p| p.extension().map(|x| x == "pak").unwrap_or(false)) .collect(); paks.sort(); let mut found_extras = false; for p in &paks { let Ok(ar) = PakArchive::open(p) else { continue; }; for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; let Some(b) = ui_layout::parse_build(&by) else { continue; }; let btns: Vec<&String> = b .records .keys() .filter(|n| n.starts_with("ptbtn") || n.contains("btn")) .collect(); if btns.len() != 8 { continue; } let name = p.file_name().unwrap().to_string_lossy(); // CONTROL: the known MAIN MENU build (11 records, so this control is now vacuous) must show up. if name == "GP_TITLE.pak" && (i == 5 || i == 8) { found_extras = true; println!("CONTROL {name} entry {i}: {} button records — the known MAIN MENU build (11 records, so this control is now vacuous)", btns.len()); } // any build outside GP_TITLE with a small button set is a candidate if name != "GP_TITLE.pak" { let mut names: Vec = btns.iter().map(|s| (*s).clone()).collect(); names.sort(); println!( " {name:28} entry {i:3} {} buttons {:?}", btns.len(), &names[..names.len().min(8)] ); } } } println!("\ncontrol {} — the known MAIN MENU build (11 records, so this control is now vacuous) was {}found", if found_extras { "PASSED" } else { "FAILED" }, if found_extras { "" } else { "NOT " }); }