diff --git a/src/leela/main.rs b/src/leela/main.rs index 5e1e86f..cdfd42f 100644 --- a/src/leela/main.rs +++ b/src/leela/main.rs @@ -1,21 +1,18 @@ -use maraiah::{durandal::{bin::*, err::*, file::*, image::*, sound::*, text::*}, - marathon::{machdr, map, phy, pict, shp, snd, trm, wad}}; -use std::{collections::HashSet, - fs, +use maraiah::{durandal::{err::*, file::*, image::*, sound::*, text::*}, + marathon::{machdr, shp, snd, wad}}; +use std::{fs, io::{self, Write}}; -fn make_tga(fname: &str, im: &impl Image) -> ResultS<()> +fn make_tga(_opt: &Options, 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<()> +fn make_data(_opt: &Options, fname: &str, data: &[u8]) -> 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)?; + let mut out = io::BufWriter::new(fs::File::create(fname)?); + out.write_all(data)?; Ok(()) } @@ -37,42 +34,23 @@ fn make_wav(fname: &str, snd: &impl Sound) -> ResultS<()> write_wav(&mut out, snd) } -fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()> +fn dump_chunk(opt: &Options, cnk: &wad::Chunk, eid: u16) -> ResultS<()> { - if opt.wad_all { - make_chunk(opt, cid, cnk, eid)?; - } - - if opt.wad_wrt_all || 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)?; + if opt.wad_wrt_all { + match cnk { + wad::Chunk::Pict(im) => { + let fname = format!("{}/pict_{}.tga", opt.out_dir, eid); + make_tga(opt, &fname, 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"LITE" => make_yaml(opt, &rd_array(cnk, map::read_lite)?)?, - b"OBJS" => make_yaml(opt, &rd_array(cnk, map::read_objs)?)?, - b"plac" => make_yaml(opt, &rd_array(cnk, map::read_plac)?)?, - b"ambi" => make_yaml(opt, &rd_array(cnk, map::read_ambi)?)?, - b"bonk" => make_yaml(opt, &rd_array(cnk, map::read_bonk)?)?, - b"medi" => make_yaml(opt, &rd_array(cnk, map::read_medi)?)?, - b"plat" => make_yaml(opt, &rd_array(cnk, map::read_plat)?)?, - b"term" => make_yaml(opt, &rd_array(cnk, trm::read_term)?)?, - b"FXpx" => make_yaml(opt, &rd_array(cnk, phy::read_fxpx)?)?, - b"MNpx" => make_yaml(opt, &rd_array(cnk, phy::read_mnpx)?)?, - b"PRpx" => make_yaml(opt, &rd_array(cnk, phy::read_prpx)?)?, - b"PXpx" => make_yaml(opt, &rd_array(cnk, phy::read_pxpx)?)?, - b"WPpx" => make_yaml(opt, &rd_array(cnk, phy::read_wppx)?)?, - _ => { - if opt.wad_unknown && !opt.wad_all { - make_chunk(opt, cid, cnk, eid)?; + wad::Chunk::Data{iden, data} => { + if opt.wad_unknown { + let iden = mac_roman_conv(iden); + let fname = format!("{}/{:04}{}.bin", opt.out_dir, eid, iden); + make_data(opt, &fname, data)?; } + make_yaml(opt, cnk)?; } + _ => make_yaml(opt, cnk)?, } } @@ -81,15 +59,15 @@ fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()> fn process_wad(opt: &Options, b: &[u8]) -> ResultS<()> { - let wad = wad::Wad::new(b)?; + let wad = wad::read_wad(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)?; + for cnk in ent.chunks { + dump_chunk(opt, &cnk, eid)?; } } @@ -106,11 +84,11 @@ fn dump_bitmaps(opt: &Options, c: &shp::Collection, i: usize) -> ResultS<()> 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))?; + make_tga(opt, &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]))?; + make_tga(opt, &fname, &shp::ImageShp::new(bmp, &c.tabs[0]))?; } } @@ -245,11 +223,6 @@ fn main() -> ResultS<()> 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-write-all", opt.wad_wrt_all, StoreTrue, @@ -265,11 +238,6 @@ fn main() -> ResultS<()> 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, @@ -291,12 +259,6 @@ fn main() -> ResultS<()> 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 { @@ -334,12 +296,9 @@ struct Options shp_seq: bool, snd_dump: bool, snd_write: bool, - wad_all: bool, wad_unknown: bool, wad_wrt_all: bool, wad_header: bool, - wad_chunks: HashSet, - wad_c_temp: String, } // EOF diff --git a/src/marathon/wad.rs b/src/marathon/wad.rs index 486142f..e7b5717 100644 --- a/src/marathon/wad.rs +++ b/src/marathon/wad.rs @@ -1,85 +1,52 @@ //! Marathon Wad format handling. -use crate::durandal::{bin::*, err::*, text::mac_roman_conv}; +use crate::durandal::{bin::*, err::*, image, text::mac_roman_conv}; +use crate::marathon::{map, phy, pict, trm}; use serde::Serialize; -use std::{collections::BTreeMap, fmt}; +use std::collections::BTreeMap; -impl Wad<'_> +/// Reads all chunks in an entry. +pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS> { - pub fn new(b: &[u8]) -> ResultS> - { - read_data! { - 128, BE in b => - wadver = u16[0]; - dataver = u16[2]; - origname = mac_roman_conv[4..68] nt; - dirofs = u32[72] as usize; - numents = u16[76] as usize; - appsize = u16[78] as usize; - wcnksize = u16[80] as usize; - wentsize = u16[82] as usize; - } - - let wadver = Ver::from_repr(wadver)?; - - let is_old = match wadver { - Ver::Base => true, - _ => false, - }; - - let entsize = if is_old {8 } else {10}; - let cnksize = if is_old {12} else {16}; - - if entsize != wentsize { - bail!("invalid entry size"); - } - - if cnksize != wcnksize { - bail!("invalid chunk size"); - } - - let mut entries = EntryMap::new(); - let mut p = dirofs; - - for i in 0..numents { - read_data! { - p + entsize, BE in b => - offset = u32[p] as usize; - size = u32[p + 4] as usize; - index = u16[p + 8]; - } - - let index = if is_old {i as u16} else {index}; - - let cnkdata = &b[offset..offset + size]; - let chunks = get_chunks(cnkdata, cnksize)?; - let appdata = &b[p..p + appsize]; - - entries.insert(index, Entry{chunks, appdata}); - - p += entsize + appsize; - } - - Ok(Wad{head: WadHeader{wadver, dataver, appsize, origname}, entries}) - } -} - -fn get_chunks(b: &[u8], cnksize: usize) -> ResultS> -{ - let mut chunks = ChunkMap::new(); + let mut chunks = Vec::new(); let mut p = 0; while p < b.len() { read_data! { - p + cnksize, BE in b => - iden = iden[p]; + p + siz_cnk, BE in b => + iden = Ident[p]; size = u32[p + 8] as usize; } - let beg = p + cnksize; - let end = beg + size; + let beg = p + siz_cnk; + let end = beg + size; + let data = &b[beg..end]; - chunks.insert(iden, &b[beg..end]); + let chunk = match &iden { + b"PICT" => Chunk::Pict(pict::load_pict(data)?), + b"Minf" => Chunk::Minf(map::read_minf(data)?), + b"EPNT" => Chunk::Pnts(rd_array(data, map::read_epnt)?), + b"PNTS" => Chunk::Pnts(rd_array(data, map::read_pnts)?), + b"LINS" => Chunk::Lins(rd_array(data, map::read_lins)?), + b"SIDS" => Chunk::Sids(rd_array(data, map::read_sids)?), + b"POLY" => Chunk::Poly(rd_array(data, map::read_poly)?), + b"LITE" => Chunk::Lite(rd_array(data, map::read_lite)?), + b"OBJS" => Chunk::Objs(rd_array(data, map::read_objs)?), + b"plac" => Chunk::Plac(rd_array(data, map::read_plac)?), + b"ambi" => Chunk::Ambi(rd_array(data, map::read_ambi)?), + b"bonk" => Chunk::Bonk(rd_array(data, map::read_bonk)?), + b"medi" => Chunk::Medi(rd_array(data, map::read_medi)?), + b"plat" => Chunk::Plat(rd_array(data, map::read_plat)?), + b"term" => Chunk::Term(rd_array(data, trm::read_term)?), + b"FXpx" => Chunk::Fxpx(rd_array(data, phy::read_fxpx)?), + b"MNpx" => Chunk::Mnpx(rd_array(data, phy::read_mnpx)?), + b"PRpx" => Chunk::Prpx(rd_array(data, phy::read_prpx)?), + b"PXpx" => Chunk::Pxpx(rd_array(data, phy::read_pxpx)?), + b"WPpx" => Chunk::Wppx(rd_array(data, phy::read_wppx)?), + _ => Chunk::Data{iden, data: data.to_vec()}, + }; + + chunks.push(chunk); p = end; } @@ -87,34 +54,134 @@ fn get_chunks(b: &[u8], cnksize: usize) -> ResultS> Ok(chunks) } -type Chunk<'a> = &'a [u8]; -type ChunkMap<'a> = BTreeMap>; -type EntryMap<'a> = BTreeMap>; - -#[derive(Serialize)] -pub struct Entry<'a> +/// Reads all entries in a `Wad`. +pub fn read_entries(b: &[u8], + is_old: bool, + siz_app: usize, + siz_ent: usize, + siz_cnk: usize) + -> ResultS> { - pub chunks: ChunkMap<'a>, - pub appdata: &'a [u8], + read_data! { + 128, BE in b => + dirofs = u32[72] as usize; + numents = u16[76] as usize; + } + + let mut entries = BTreeMap::new(); + let mut p = dirofs; + + for i in 0..numents { + read_data! { + p + siz_ent, BE in b => + offset = u32[p] as usize; + size = u32[p + 4] as usize; + index = u16[p + 8]; + } + + let index = if is_old {i as u16} else {index}; + + let chunks = read_chunks(&b[offset..offset + size], siz_cnk)?; + let appdata = b[p..p + siz_app].to_vec(); + + entries.insert(index, Entry{chunks, appdata}); + + p += siz_ent + siz_app; + } + + Ok(entries) } +/// Reads a Map file. +pub fn read_wad(b: &[u8]) -> ResultS +{ + read_data! { + 128, BE in b => + ver_wad = u16[0]; + ver_dat = u16[2]; + name = mac_roman_conv[4..68] nt; + siz_app = u16[78] as usize; + siz_wcnk = u16[80] as usize; + siz_went = u16[82] as usize; + } + + let ver_wad = Ver::from_repr(ver_wad)?; + + let is_old = match ver_wad { + Ver::Base => true, + _ => false, + }; + + let siz_ent = if is_old {8 } else {10}; + let siz_cnk = if is_old {12} else {16}; + + if siz_ent != wentsize { + bail!("invalid entry size"); + } + + if siz_cnk != wcnksize { + bail!("invalid chunk size"); + } + + let entries = read_entries(b, is_old, siz_app, siz_ent, siz_cnk)?; + + Ok(Wad{head: WadHeader{ver_wad, ver_dat, siz_app, name}, entries}) +} + +/// Any kind of chunk in an `Entry`. +#[derive(Debug, Serialize)] +pub enum Chunk +{ + Pict(image::Image8), + Minf(map::Minf), + Pnts(Vec), + Lins(Vec), + Sids(Vec), + Poly(Vec), + Lite(Vec), + Objs(Vec), + Plac(Vec), + Ambi(Vec), + Bonk(Vec), + Medi(Vec), + Plat(Vec), + Term(Vec), + Fxpx(Vec), + Mnpx(Vec), + Prpx(Vec), + Pxpx(Vec), + Wppx(Vec), + Data{iden: Ident, data: Vec}, +} + +/// An entry containing chunks and application-specific data. +#[derive(Debug, Serialize)] +pub struct Entry +{ + pub chunks: Vec, + pub appdata: Vec, +} + +/// The header of a `Wad`. #[derive(Debug, Serialize)] pub struct WadHeader { - pub wadver: Ver, - pub dataver: u16, - pub origname: String, - pub appsize: usize, + pub ver_wad: Ver, + pub ver_dat: u16, + pub name: String, + pub siz_app: usize, } +/// A Map file containing entries. #[derive(Debug, Serialize)] -pub struct Wad<'a> +pub struct Wad { pub head: WadHeader, - pub entries: EntryMap<'a>, + pub entries: BTreeMap, } c_enum! { + /// The version of a `Wad`. #[derive(Debug, Serialize)] pub enum Ver: u16 { @@ -125,19 +192,4 @@ c_enum! { } } -impl fmt::Debug for Entry<'_> -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - write!(f, "Entry{{ ")?; - for iden in self.chunks.keys() { - write!(f, "{} ", mac_roman_conv(iden))?; - } - if !self.appdata.is_empty() { - write!(f, "\nappdata: {:?} ", self.appdata)?; - } - write!(f, "}}") - } -} - // EOF