Maraiah/src/main.rs

255 lines
6.8 KiB
Rust

use maraiah::{durandal::{bin::*, chunk::*, err::*, file::*, image::*, text::*},
marathon::{machdr, map, pict, shp, term, 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(data: &impl serde::Serialize) -> ResultS<()>
{
serde_yaml::to_writer(io::stdout(), &data)?;
println!();
Ok(())
}
fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
{
if opt.wad_all {
make_chunk(opt, cid, cnk, eid)?;
return Ok(());
}
match &cid {
b"PICT" => {
if opt.wad_chunks.contains(&cid) {
let im = pict::load_pict(cnk)?;
make_tga(&format!("{}/pict_{}.tga", opt.out_dir, eid), &im)?;
}
}
b"Minf" => {
if opt.wad_chunks.contains(&cid) {
make_yaml(&map::Minf::chunk(cnk)?)?;
}
}
b"EPNT" => {
if opt.wad_chunks.contains(&cid) {
make_yaml(&map::Endpoint::chunk(cnk)?)?;
}
}
b"PNTS" => {
if opt.wad_chunks.contains(&cid) {
make_yaml(&map::Point::chunk(cnk)?)?;
}
}
b"LINS" => {
if opt.wad_chunks.contains(&cid) {
make_yaml(&map::Line::chunk(cnk)?)?;
}
}
b"SIDS" => {
if opt.wad_chunks.contains(&cid) {
make_yaml(&map::Side::chunk(cnk)?)?;
}
}
b"term" => {
if opt.wad_chunks.contains(&cid) {
make_yaml(&term::Terminal::chunk(cnk)?)?;
}
}
&cid => {
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(&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(&cl.tabs)?;
}
if opt.shp_frm {
make_yaml(&cl.frms)?;
}
if opt.shp_seq {
make_yaml(&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 main() -> ResultS<()>
{
use argparse::*;
use memmap::Mmap;
let mut opt: Options = Default::default();
{
let mut ap = ArgumentParser::new();
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.shp_tab)
.add_option(&["--shp-write-tab"], StoreTrue,
"shp: Dump all CLUTs as YAML to standard output");
ap.refer(&mut opt.shp_bmp)
.add_option(&["--shp-dump-bitmaps"], StoreTrue,
"shp: Dump bitmaps into a folder");
ap.refer(&mut opt.shp_bmp_all)
.add_option(&["--shp-dump-more-bitmaps"], StoreTrue,
"shp: Dump all color variations of each bitmap");
ap.refer(&mut opt.shp_frm)
.add_option(&["--shp-write-frm"], StoreTrue,
"shp: Dump all frames as YAML to standard output");
ap.refer(&mut opt.shp_seq)
.add_option(&["--shp-write-seq"], StoreTrue,
"shp: Dump all sequences as YAML to standard output");
ap.refer(&mut opt.wad_all)
.add_option(&["--wad-dump-all"], StoreTrue,
"wad: Dump all chunks into a folder");
ap.refer(&mut opt.wad_unknown)
.add_option(&["--wad-dump-unknown"], StoreTrue,
"wad: Dump all unknown chunks into a folder");
ap.refer(&mut opt.wad_header)
.add_option(&["--wad-write-header"], StoreTrue,
"wad: Dump header info as YAML to standard output");
ap.refer(&mut opt.wad_c_temp)
.add_option(&["--wad-write-chunks"], Store,
"wad: Dump specified chunks in various formats");
ap.refer(&mut opt.out_dir)
.add_option(&["--out-dir"], Store,
"Sets output directory for dump options");
ap.refer(&mut opt.inputs)
.add_argument("inputs", Collect, "Input files");
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(c_iden(ctyp.as_bytes(), 0)?);
}
}
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 = c_data(&mm, machdr::try_mac_header(&mm)..)?;
match typ {
"wad:" => process_wad(&opt, b),
"shp:" => process_shp(&opt, b),
_ => Err(err_msg("invalid file type specified on commandline")),
}?;
}
Ok(())
}
#[derive(Default)]
struct Options
{
inputs: Vec<String>,
out_dir: String,
shp_tab: bool,
shp_bmp: bool,
shp_bmp_all: bool,
shp_frm: bool,
shp_seq: bool,
wad_all: bool,
wad_unknown: bool,
wad_header: bool,
wad_chunks: HashSet<Ident>,
wad_c_temp: String,
}
// EOF