use clap::Parser;
use kerbalobjects::ko::KOFile;
use kerbalobjects::ksm::KSMFile;
use kerbalobjects::BufferIterator;
use lazy_static::lazy_static;
use std::io::Write;
use std::path::PathBuf;
use std::{error::Error, fs};
use termcolor::{Color, ColorSpec, StandardStream};
mod fio;
use fio::{determine_file_type, FileType};
mod output;
use output::KOFileDebug;
use output::KSMFileDebug;
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
pub static WHITE_COLOR: Color = Color::Rgb(255, 255, 255);
pub static ORANGE_COLOR: Color = Color::Rgb(201, 155, 87);
pub static PURPLE_COLOR: Color = Color::Rgb(133, 80, 179);
pub static DARK_RED_COLOR: Color = Color::Rgb(201, 87, 87);
pub static LIGHT_RED_COLOR: Color = Color::Rgb(255, 147, 147);
pub static GREEN_COLOR: Color = Color::Rgb(129, 181, 154);
lazy_static! {
static ref ORANGE: ColorSpec = {
let mut spec = ColorSpec::new();
spec.set_fg(Some(ORANGE_COLOR));
spec
};
static ref PURPLE: ColorSpec = {
let mut spec = ColorSpec::new();
spec.set_fg(Some(PURPLE_COLOR));
spec
};
static ref DARK_RED: ColorSpec = {
let mut spec = ColorSpec::new();
spec.set_fg(Some(DARK_RED_COLOR));
spec
};
static ref LIGHT_RED: ColorSpec = {
let mut spec = ColorSpec::new();
spec.set_fg(Some(LIGHT_RED_COLOR));
spec
};
static ref GREEN: ColorSpec = {
let mut spec = ColorSpec::new();
spec.set_fg(Some(GREEN_COLOR));
spec
};
static ref WHITE: ColorSpec = {
let mut spec = ColorSpec::new();
spec.set_fg(Some(WHITE_COLOR));
spec
};
static ref NO_COLOR: ColorSpec = ColorSpec::default();
}
pub fn run(config: &CLIConfig) -> Result<(), Box<dyn Error>> {
let color_choice = if atty::is(atty::Stream::Stdout) {
termcolor::ColorChoice::Auto
} else {
termcolor::ColorChoice::Never
};
let mut stream = StandardStream::stdout(color_choice);
writeln!(stream, "kDump version {}", VERSION)?;
let raw_contents = fs::read(&config.file_path)?;
let mut raw_contents_iter = BufferIterator::new(&raw_contents);
let file_type = determine_file_type(&raw_contents)?;
match file_type {
FileType::KerbalMachineCode => {
let ksm = KSMFile::parse(&mut raw_contents_iter)?;
let ksm_debug = KSMFileDebug::new(ksm);
ksm_debug.dump(&mut stream, config)?;
Ok(())
}
FileType::KerbalObject => {
let kofile = KOFile::parse(&mut raw_contents_iter)?;
let ko_debug = KOFileDebug::new(kofile);
ko_debug.dump(&mut stream, config)?;
Ok(())
}
FileType::Unknown => Err("File type not recognized.".into()),
}
}
#[derive(Debug, Parser)]
#[command(name = "kDump Utility", author, version, about, long_about = None)]
pub struct CLIConfig {
#[arg(value_name = "FILE", help = "Sets the input file to use")]
pub file_path: PathBuf,
#[arg(
short = 'D',
long,
help = "Disassembles the contents of the entire object file",
conflicts_with("disassemble_symbol"),
conflicts_with("full_contents")
)]
pub disassemble: bool,
#[arg(
short = 'd',
long = "disassemble-symbol",
help = "Disassembles at the symbol provided until the end of the section",
require_equals = true,
value_name = "SYMBOL",
conflicts_with("disassemble"),
conflicts_with("full_contents")
)]
pub disassemble_symbol: Option<String>,
#[arg(
short = 'f',
long = "file-headers",
help = "Displays summary information of the KO file header"
)]
pub file_headers: bool,
#[arg(
short = 'a',
long = "argument-section",
help = "Displays the contents of the argument section of a KSM file"
)]
pub argument_section: bool,
#[arg(
short = 'l',
long = "line-numbers",
help = "Displays the line numbers of disassembled instructions when disassembling"
)]
pub line_numbers: bool,
#[arg(
long = "section-headers",
help = "Displays the section header table of a KO file"
)]
pub section_headers: bool,
#[arg(long = "data", help = "Displays each data section of a KO file")]
pub data: bool,
#[arg(
short = 's',
long = "full-contents",
help = "Displays the contents of every section in the object file"
)]
pub full_contents: bool,
#[arg(
short = 'S',
long = "stabs",
help = "Displays each string table of a KO file"
)]
pub stabs: bool,
#[arg(
short = 't',
long = "syms",
help = "Displays each symbol table of a KO file"
)]
pub syms: bool,
#[arg(
short = 'r',
long = "reloc",
help = "Displays each relocation data table of a KO file"
)]
pub reloc: bool,
#[arg(
short = 'x',
long = "all-headers",
help = "Displays all available KO file header information including the symbol table"
)]
pub all_headers: bool,
#[arg(
short = 'i',
long = "info",
help = "Displays all available meta info of the object file including compiler comments and version information"
)]
pub info: bool,
#[arg(
short = 'C',
long = "demangle",
help = "Tries to demangle disassembled function and variable names"
)]
pub demangle: bool,
#[arg(
long = "show-no-raw-instr",
help = "When disassembling, disables showing the raw bytes that make up each instruction"
)]
pub show_no_raw_instr: bool,
#[arg(
long = "show-no-labels",
help = "When disassembling, disables showing the label of each instruction"
)]
pub show_no_labels: bool,
}