use std::path::PathBuf; use clap::Parser; use colored::{ColoredString, Colorize}; use picori::{gcm, Gcm}; extern crate picori; /// Simple program to greet a person #[derive(Parser, Debug)] #[command( name = "gcm_dump", bin_name = "gcm_dump", author="Julgodis <self@julgodis.xyz>", version=env!("CARGO_PKG_VERSION"), about="Example program to dump .gcm/.iso files using picori", long_about = None)] struct Args { /// Path to the file to dump #[arg()] path: PathBuf, /// Dump boot.bin #[arg(short, long)] boot: bool, /// Dump bi2.bin #[arg(short = '2', long)] bi2: bool, /// Dump apploader.img #[arg(short = 'l', long)] apploader: bool, /// Dump fst.bin #[arg(short, long)] fst: bool, /// Dump data #[arg(short, long)] data: bool, /// Dump all #[arg(short, long)] all: bool, /// Column width #[arg(short, long, default_value = "32")] width: usize, } fn hex2(value: u8) -> ColoredString { format!("{:#04x}", value).cyan() } fn hex8(value: u32) -> ColoredString { format!("{:#010x}", value).cyan() } fn num(value: u32) -> ColoredString { format!("{}", value).cyan() } fn output_boot(boot: &gcm::Boot) { println!("boot.bin:"); println!(" console: {}", match boot.console { gcm::ConsoleType::GameCube => "GameCube", }); println!( " game code: {} {}", hex2(boot.game_code[0]), hex2(boot.game_code[1]) ); println!(" country code: {}", hex2(boot.country_code)); println!( " maker code: {} {}", hex2(boot.maker_code[0]), hex2(boot.maker_code[1]) ); println!(" disc id: {}", hex2(boot.disc_id)); println!(" version: {}", hex2(boot.version)); println!(" audio streaming: {}", hex2(boot.audio_streaming)); println!( " streaming buffer size: {}", hex2(boot.streaming_buffer_size) ); println!( " debug_monitor_offset: {}", hex8(boot.debug_monitor_offset) ); println!( " debug_monitor_address: {}", hex8(boot.debug_monitor_address) ); println!( " main_executable_offset: {}", hex8(boot.main_executable_offset) ); println!(" fst_offset: {}", hex8(boot.fst_offset)); println!(" fst_size: {}", hex8(boot.fst_size)); println!(" fst_max_size: {}", hex8(boot.fst_max_size)); println!(" user_position: {}", hex8(boot.user_position)); println!(" user_length: {}", hex8(boot.user_length)); } fn output_bi2(bi2: &gcm::Bi2) { println!("bi2.bin:"); let mut options = bi2 .options() .iter() .map(|x| (*x.0, *x.1)) .collect::<Vec<_>>(); options.sort_by(|a, b| a.0.cmp(&b.0)); for (i, value) in options { println!(" [{:04x}]: {} ({})", i.index(), hex8(value), num(value)); } } fn output_data(data: &Vec<u8>, width: usize) { for (j, line) in data.chunks(width).enumerate() { print!("{:06x}: ", j * 32); for byte in line { print!("{:02x} ", byte); } println!(); } } fn output_apploader(apploader: &gcm::Apploader, data: bool, width: usize) { println!("apploader.img:"); println!(" entry point: {}", hex8(apploader.entry_point)); println!(" size: {}", hex8(apploader.size)); println!(" trailer size: {}", hex8(apploader.trailer_size)); println!(" unknown: {}", hex8(apploader.unknown)); if data { println!(" data:"); output_data(&apploader.data, width); } } fn output_fst(fst: &gcm::Fst) { println!("fst.bin:"); for (path, entry) in fst.files() { let indent = path.ancestors().count() - 1; match entry { gcm::fst::Entry::Root => {}, gcm::fst::Entry::File { name, offset, size, .. } => { println!( "{:indent$}{} offset: {} size: {}", "", name.green(), hex8(offset), hex8(size), indent = indent * 2 ); }, gcm::fst::Entry::Directory { name, .. } => { println!("{:indent$}{}", "", name.red(), indent = indent * 2); }, } } } fn main() { let args = Args::parse(); let mut dump_boot = args.boot; let mut dump_bi2 = args.bi2; let mut dump_apploader = args.apploader; let mut dump_fst = args.fst; let data = args.data; let width = match args.width { 0 => 1, _ => args.width, }; if args.all { dump_boot = true; dump_bi2 = true; dump_apploader = true; dump_fst = true; } if !dump_boot && !dump_bi2 && !dump_apploader && !dump_fst { println!("nothing to dump :("); return; } let file = std::fs::File::open(args.path).unwrap(); let mut file = std::io::BufReader::new(file); let gcm = Gcm::from_binary(&mut file).unwrap(); if dump_boot { output_boot(&gcm.boot()); } if dump_bi2 { output_bi2(&gcm.bi2()); } if dump_apploader { output_apploader(&gcm.apploader(), data, width); } if dump_fst { output_fst(&gcm.fst()); } }