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;
#[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 {
#[arg()]
path: PathBuf,
#[arg(short = 't', long)]
header: bool,
#[arg(short, long)]
sections: bool,
#[arg(short, long)]
imports: bool,
#[arg(short, long)]
relocations: bool,
#[arg(short, long)]
data: bool,
#[arg(short, long)]
all: bool,
#[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,
);
}