use std::io::Cursor; use std::path::PathBuf; use clap::Parser; use colored::{ColoredString, Colorize}; use picori::rel::ImportKind; use picori::Rel; extern crate picori; /// Simple program to greet a person #[derive(Parser, Debug)] #[command( name = "rel_dump", bin_name = "rel_dump", author="Julgodis <self@julgodis.xyz>", version=env!("CARGO_PKG_VERSION"), about="Example program to dump .rel files using picori", long_about = None)] struct Args { /// Path to the file to dump #[arg()] path: PathBuf, /// Dump header #[arg(short = 't', long)] header: bool, /// Dump sections #[arg(short, long)] sections: bool, /// Dump imports #[arg(short, long)] imports: bool, /// Dump relocations #[arg(short, long)] relocations: bool, /// Dump section data #[arg(short, long)] data: bool, /// Dump all #[arg(short, long)] all: bool, /// Column width #[arg(short, long, default_value = "32")] width: usize, } fn hex4(value: u16) -> ColoredString { format!("{:#06x}", value).cyan() } fn hex8(value: u32) -> ColoredString { format!("{:#010x}", value).cyan() } fn num(value: u32) -> ColoredString { format!("{}", value).cyan() } fn output_header(rel: &Rel) { println!("header:"); println!(" module: {}", num(rel.module)); println!(" version: {}", num(rel.version)); println!(" name offset: {}", hex8(rel.name_offset)); println!(" name size: {}", hex8(rel.name_size)); println!(" alignment: {}", hex8(rel.alignment)); println!(" bss alignment: {}", hex8(rel.bss_alignment)); println!(" fix size: {}", hex8(rel.fix_size)); println!( " relocation offset: {}", hex8(rel.relocation_offset.unwrap_or(0)) ); println!( " import offset: {}", hex8(rel.import_offset.unwrap_or(0)) ); println!( " import size: {}", hex8(rel.import_size.unwrap_or(0)) ); println!( " prolog: {}", if let Some(prolog) = &rel.prolog { format!("{} (section: {})", hex8(prolog.offset), num(prolog.section)) } else { "".to_string() } ); println!( " epilog: {}", if let Some(epilog) = &rel.epilog { format!("{} (section: {})", hex8(epilog.offset), num(epilog.section)) } else { "".to_string() } ); println!( " unresolved: {}", if let Some(unresolved) = &rel.unresolved { format!( "{} (section: {})", hex8(unresolved.offset), num(unresolved.section) ) } else { "".to_string() } ); } fn output_sections(rel: &Rel) { println!("sections:"); for (i, section) in rel.sections.iter().enumerate() { println!( " #{:<2} offset: {}, size: {}{}{}{}{}", num(i as u32), hex8(section.offset), hex8(section.size), if section.executable { " [executable]".red() } else { ColoredString::default() }, if section.unknown { " [unknown]".red() } else { ColoredString::default() }, if i == 0 { " [null section]".green() } else { ColoredString::default() }, if i != 0 && section.offset == 0 && section.size == 0 { " [unused]".green() } else { ColoredString::default() } ); } } fn import_kind_to_string(kind: ImportKind) -> &'static str { match kind { ImportKind::None => "None", ImportKind::Addr32 => "Addr32", ImportKind::Addr24 => "Addr24", ImportKind::Addr16 => "Addr16", ImportKind::Addr16Lo => "Addr16Lo", ImportKind::Addr16Hi => "Addr16Hi", ImportKind::Addr16Ha => "Addr16Ha", ImportKind::Addr14 => "Addr14", ImportKind::Rel24 => "Rel24", ImportKind::Rel14 => "Rel14", ImportKind::DolphinNop => "DolphinNop", ImportKind::DolphinSection => "DolphinSection", ImportKind::DolphinEnd => "DolphinEnd", ImportKind::DolphinMRKREF => "DolphinMRKREF", } } fn output_imports(rel: &Rel) { println!("import tables:"); for (i, table) in rel.import_tables.iter().enumerate() { println!( " #{:<2} module: {:>4}, size: {}", num(i as u32), num(table.module), hex8(table.offset) ); } println!("import:"); for table in rel.import_tables.iter() { println!(" [ module: {:>4} ]", num(table.module)); for (j, import) in table.imports.iter().enumerate() { println!( " #{:<4} {:<20} section: {:>2}, offset: {}, addend: {}", num(j as u32), import_kind_to_string(import.kind), num(import.section as u32), hex4(import.offset), hex8(import.addend) ); } } } fn output_relocations(rel: &Rel) { println!("relocation:"); for (i, relocation) in rel.relocations().enumerate() { println!( " #{:<4} {:<20} target: [section: {:>2}, offset: {}], reference: [module: {:>4}, \ section: {:>2}, offset: {}]", num(i as u32), import_kind_to_string(relocation.kind), num(relocation.target.section), hex8(relocation.target.offset), num(relocation.module), num(relocation.reference.section), hex8(relocation.reference.offset) ); } } 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_sections_data(rel: &Rel, width: usize) { println!("sections:"); for (i, section) in rel.sections.iter().enumerate() { println!(" [ section: {:>2} ]", num(i as u32)); output_data(§ion.data, width); } } fn output( rel: &Rel, dump_header: bool, dump_sections: bool, dump_imports: bool, dump_relocations: bool, dump_data: bool, width: usize, ) { if dump_header { output_header(rel); } if dump_sections { output_sections(rel); } if dump_imports { output_imports(rel); } if dump_relocations { output_relocations(rel); } if dump_data { output_sections_data(rel, width); } } fn main() { let args = Args::parse(); let mut dump_header = args.header; let mut dump_sections = args.sections; let mut dump_imports = args.imports; let mut dump_relocations = args.relocations; let mut dump_data = args.data; let width = match args.width { 0 => 1, _ => args.width, }; if args.all { dump_header = true; dump_sections = true; dump_imports = true; dump_relocations = true; dump_data = true; } if !dump_header && !dump_sections && !dump_imports && !dump_relocations && !dump_data { println!("nothing to dump :("); return; } let file = std::fs::File::open(args.path).unwrap(); let mut file = std::io::BufReader::new(file); let rel = if picori::yaz0::is_yaz0(&mut file) { let mut reader = picori::Yaz0Reader::new(file).unwrap(); let decompressed = reader.decompress().unwrap(); let mut cursor = Cursor::new(decompressed); Rel::from_binary(&mut cursor).unwrap() } else { Rel::from_binary(file).unwrap() }; output( &rel, dump_header, dump_sections, dump_imports, dump_relocations, dump_data, width, ); }