Maraiah/src/main.rs

298 lines
7.6 KiB
Rust

use maraiah::{durandal::{bin::*, err::*, file::*, image::*, sound::*, text::*},
marathon::{machdr, map, pict, shp, snd, trm, wad}};
use std::{collections::HashSet,
fs,
io::{self, Write}};
fn make_tga(fname: &str, im: &impl Image) -> ResultS<()>
{
let mut out = io::BufWriter::new(fs::File::create(fname)?);
write_tga(&mut out, im)
}
fn make_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
{
let cid = mac_roman_conv(&cid);
let fname = format!("{}/{:04}{}.bin", opt.out_dir, eid, cid);
let mut out = io::BufWriter::new(fs::File::create(&fname)?);
out.write_all(cnk)?;
Ok(())
}
fn make_yaml<T>(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)?);
write_wav(&mut out, snd)
}
fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
{
if opt.wad_all {
make_chunk(opt, cid, cnk, eid)?;
} else if opt.wad_chunks.contains(&cid) {
match &cid {
b"PICT" => {
let im = pict::load_pict(cnk)?;
make_tga(&format!("{}/pict_{}.tga", opt.out_dir, eid), &im)?;
}
b"Minf" => make_yaml(opt, &map::read_minf(cnk)?)?,
b"EPNT" => make_yaml(opt, &rd_array(cnk, map::read_epnt)?)?,
b"PNTS" => make_yaml(opt, &rd_array(cnk, map::read_pnts)?)?,
b"LINS" => make_yaml(opt, &rd_array(cnk, map::read_lins)?)?,
b"SIDS" => make_yaml(opt, &rd_array(cnk, map::read_sids)?)?,
b"POLY" => make_yaml(opt, &rd_array(cnk, map::read_poly)?)?,
b"term" => make_yaml(opt, &rd_array(cnk, trm::read_term)?)?,
_ => (),
}
} else if opt.wad_unknown {
make_chunk(opt, cid, cnk, eid)?;
}
Ok(())
}
fn process_wad(opt: &Options, b: &[u8]) -> ResultS<()>
{
let wad = wad::Wad::new(b)?;
if opt.wad_header {
make_yaml(opt, &wad.head)?;
}
for (eid, ent) in wad.entries {
for (cid, cnk) in ent.chunks {
dump_chunk(opt, cid, cnk, eid)?;
}
}
Ok(())
}
fn dump_bitmaps(opt: &Options, c: &shp::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(&fname, &shp::ImageShp::new(bmp, &tab))?;
}
} else {
let fname = format!("{}/shape{}_{}.tga", opt.out_dir, i, j);
make_tga(&fname, &shp::ImageShp::new(bmp, &c.tabs[0]))?;
}
}
Ok(())
}
fn write_shp_objs(opt: &Options, cl: &shp::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_shapes(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 write_sounds(opt: &Options, st: &snd::SoundTable) -> ResultS<()>
{
if opt.snd_write {
for sd in st.values() {
make_yaml(opt, &sd.header)?;
}
}
Ok(())
}
fn process_snd(opt: &Options, b: &[u8]) -> ResultS<()>
{
for (c, st) in snd::read_sounds(b)?.iter().enumerate() {
dump_sounds(opt, st, c)?;
write_sounds(opt, st)?;
}
Ok(())
}
fn main() -> ResultS<()>
{
use argparse::*;
use memmap::Mmap;
let mut opt: Options = Default::default();
{
let mut ap = ArgumentParser::new();
macro_rules! arg {
($name:expr, $ref:expr, $type:expr, $desc:expr) => {
ap.refer(&mut $ref).add_option(&[$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-write", opt.snd_write, StoreTrue,
"snd: Dump all sound headers as YAML to standard output");
arg!("--snd-dump", opt.snd_dump, StoreTrue,
"snd: Dump all sounds to WAVE files");
arg!("--wad-dump-all", opt.wad_all, StoreTrue,
"wad: Dump all chunks into a folder");
arg!("--wad-dump-unknown", opt.wad_unknown, StoreTrue,
"wad: Dump all unknown chunks into a folder");
arg!("--wad-write-header", opt.wad_header, StoreTrue,
"wad: Dump header info as YAML to standard output");
arg!("--wad-write-chunks", opt.wad_c_temp, Store,
"wad: Dump specified chunks in various formats");
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();
}
if !opt.wad_c_temp.is_empty() {
for ctyp in opt.wad_c_temp.split(',') {
opt.wad_chunks.insert(ident(ctyp.as_bytes()));
}
}
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 {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),
_ => Err(err_msg("invalid file type specified on commandline")),
}?;
}
Ok(())
}
#[derive(Default)]
struct Options
{
inputs: Vec<String>,
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,
snd_write: bool,
wad_all: bool,
wad_unknown: bool,
wad_header: bool,
wad_chunks: HashSet<Ident>,
wad_c_temp: String,
}
// EOF