use maraiah::{durandal::{err::*, file::*, image::*, sound::*}, marathon::{machdr, map, ppm, shp, snd, tga, wav}}; use std::{fs, io, slice::from_ref}; fn make_tga(_opt: &Options, fname: &str, im: &impl Image) -> ResultS<()> { let mut out = io::BufWriter::new(fs::File::create(fname)?); tga::write_tga(&mut out, im) } fn make_ppm(_opt: &Options, fname: &str, im: &impl Image) -> ResultS<()> { let mut out = io::BufWriter::new(fs::File::create(fname)?); ppm::write_ppm(&mut out, im) } fn make_yaml(opt: &Options, data: &T) -> ResultS<()> where T: serde::Serialize + std::fmt::Debug { if opt.out_debug { println!("{:#?}", data); } else { serde_yaml::to_writer(io::stdout(), &data)?; println!(); } Ok(()) } fn make_wav(fname: &str, snd: &impl Sound) -> ResultS<()> { let mut out = io::BufWriter::new(fs::File::create(fname)?); wav::write_wav(&mut out, snd) } fn process_wad(opt: &Options, b: &[u8]) -> ResultS<()> { let wad = map::read(b)?; if opt.wad_wrt_all { make_yaml(opt, &wad)?; } Ok(()) } fn dump_bitmaps(opt: &Options, c: &shp::coll::Collection, i: usize) -> ResultS<()> { if !opt.shp_bmp { return Ok(()); } for (j, bmp) in c.bmps.iter().enumerate() { if opt.shp_bmp_all { for (k, tab) in c.tabs.iter().enumerate() { let fname = format!("{}/shape{}_{}_{}.tga", opt.out_dir, i, j, k); make_tga(opt, &fname, &shp::bmap::ImageShp::new(bmp, &tab))?; } } else { let fname = format!("{}/shape{}_{}.tga", opt.out_dir, i, j); make_tga(opt, &fname, &shp::bmap::ImageShp::new(bmp, &c.tabs[0]))?; } } Ok(()) } fn write_shp_objs(opt: &Options, cl: &shp::coll::Collection) -> ResultS<()> { if opt.shp_tab {make_yaml(opt, &cl.tabs)?;} if opt.shp_frm {make_yaml(opt, &cl.frms)?;} if opt.shp_seq {make_yaml(opt, &cl.seqs)?;} Ok(()) } fn process_shp(opt: &Options, b: &[u8]) -> ResultS<()> { for (i, cl) in shp::read(b)?.iter().enumerate() { if let Some(cl) = &cl.0 { dump_bitmaps(opt, cl, i)?; write_shp_objs(opt, cl)?; } if let Some(cl) = &cl.1 { dump_bitmaps(opt, cl, i + 100)?; write_shp_objs(opt, cl)?; } } Ok(()) } fn dump_sounds(opt: &Options, st: &snd::SoundTable, c: usize) -> ResultS<()> { if !opt.snd_dump { return Ok(()); } for (k, sd) in st { for (i, snd) in sd.sounds.iter().enumerate() { let fname = format!("{}/snd{}_{}_{}.wav", opt.out_dir, c, k, i); make_wav(&fname, snd)?; } } Ok(()) } fn process_snd(opt: &Options, b: &[u8]) -> ResultS<()> { for (c, st) in snd::read(b)?.iter().enumerate() { dump_sounds(opt, st, c)?; } Ok(()) } fn process_ppm(opt: &Options, b: &[u8]) -> ResultS<()> { let im = ppm::read_ppm(b)?; let fname = format!("{}/out.tga", opt.out_dir); make_tga(opt, &fname, &im)?; let fname = format!("{}/out.ppm", opt.out_dir); make_ppm(opt, &fname, &im) } fn main() -> ResultS<()> { use argparse::*; let mut opt = Options::default(); { let mut ap = ArgumentParser::new(); macro_rules! arg { ($name:expr, $ref:expr, $type:expr, $desc:expr) => { ap.refer(&mut $ref).add_option(from_ref(&$name), $type, $desc); }; } ap.set_description(env!("CARGO_PKG_DESCRIPTION")); ap.add_option(&["-v", "--version"], Print(concat!(env!("CARGO_PKG_NAME"), " ", env!("CARGO_PKG_VERSION")).to_string()), "Show the version"); ap.refer(&mut opt.inputs) .add_argument("inputs", Collect, "Input files"); arg!("--shp-write-tab", opt.shp_tab, StoreTrue, "shp: Dump all CLUTs as YAML to standard output"); arg!("--shp-dump-bitmaps", opt.shp_bmp, StoreTrue, "shp: Dump bitmaps into a folder"); arg!("--shp-dump-more-bitmaps", opt.shp_bmp_all, StoreTrue, "shp: Dump all color variations of each bitmap"); arg!("--shp-write-frm", opt.shp_frm, StoreTrue, "shp: Dump all frames as YAML to standard output"); arg!("--shp-write-seq", opt.shp_seq, StoreTrue, "shp: Dump all sequences as YAML to standard output"); arg!("--snd-dump", opt.snd_dump, StoreTrue, "snd: Dump all sounds to WAVE files"); arg!("--wad-write-all", opt.wad_wrt_all, StoreTrue, "wad: Dump all known chunks"); arg!("--out-dir", opt.out_dir, Store, "Sets output directory for dump options"); arg!("--out-debug", opt.out_debug, StoreTrue, "Writes debugging output rather than YAML"); ap.parse_args_or_exit(); } if opt.shp_bmp_all { opt.shp_bmp = true; } if opt.out_dir.is_empty() { opt.out_dir = ".".to_string(); } validate_folder_path(&opt.out_dir)?; for arg in &opt.inputs { let (typ, fna) = if let Some(st) = arg.find(':') { arg.split_at(st + 1) } else { ("wad:", arg.as_str()) }; let fp = fs::File::open(fna)?; let mm = unsafe {memmap::Mmap::map(&fp)?}; let b = &mm[machdr::try_mac_header(&mm)..]; match typ { "wad:" => process_wad(&opt, b), "shp:" => process_shp(&opt, b), "snd:" => process_snd(&opt, b), "ppm:" => process_ppm(&opt, b), _ => Err(err_msg("invalid file type specified on commandline")), }?; } Ok(()) } #[derive(Default)] struct Options { inputs: Vec, out_dir: String, out_debug: bool, shp_tab: bool, shp_bmp: bool, shp_bmp_all: bool, shp_frm: bool, shp_seq: bool, snd_dump: bool, wad_wrt_all: bool, } // EOF