//! Marathon Wad format handling. use crate::{durandal::{bin::*, err::*, image, text::mac_roman_cstr}, marathon::{map, phy, pict, trm}}; use std::collections::BTreeMap; /// Reads all chunks in an entry. pub fn read_chunks(b: &[u8], old_dat: bool, siz_cnk: usize) -> ResultS> { let mut chunks = Vec::new(); let mut p = 0; let map_read_minfo = if old_dat {map::read_old_minf} else {map::read_minf}; let map_read_sides = if old_dat {map::read_old_sids} else {map::read_sids}; let map_read_polys = if old_dat {map::read_old_poly} else {map::read_poly}; let map_read_light = if old_dat {map::read_old_lite} else {map::read_lite}; while p < b.len() { read_data! { p + siz_cnk, BE in b => iden = Ident[p]; size = u32[p + 8] usize; } let beg = p + siz_cnk; let end = beg + size; let data = &b[beg..end]; chunks.push(match &iden.0 { b"PICT" => Chunk::Pict(pict::load_pict(data)?), b"Minf" => Chunk::Minf(map_read_minfo(data)?), b"iidx" => Chunk::Iidx(rd_array(data, map::read_iidx)?), 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_sides)?), b"POLY" => Chunk::Poly(rd_array(data, map_read_polys)?), b"OBJS" => Chunk::Objs(rd_array(data, map::read_objs)?), b"LITE" => Chunk::Lite(rd_array(data, map_read_light)?), 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"NOTE" => Chunk::Note(rd_array(data, map::read_note)?), 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()}, }); p = end; } Ok(chunks) } /// Reads all entries in a `Wad`. pub fn read_entries(b: &[u8], old_wad: bool, old_dat: bool, siz_app: usize, siz_ent: usize, siz_cnk: usize) -> ResultS> { read_data! { 128, BE in b => dirofs = u32[72] usize; numents = u16[76] 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] usize; size = u32[p + 4] usize; index = u16[p + 8]; } let index = if old_wad {i as u16} else {index}; let chunks = read_chunks(&b[offset..offset + size], old_dat, 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_cstr[4..68]; siz_app = u16[78] usize; siz_wcnk = u16[80] usize; siz_went = u16[82] usize; } let old_dat = ver_dat == 0; let old_wad = match Ver::from_repr(ver_wad)? { Ver::Base => true, _ => false, }; let siz_ent = if old_wad {8 } else {10}; let siz_cnk = if old_wad {12} else {16}; if !old_wad && siz_ent != siz_went { bail!("invalid entry size"); } if !old_wad && siz_cnk != siz_wcnk { bail!("invalid chunk size"); } let entries = read_entries(b, old_wad, old_dat, siz_app, siz_ent, siz_cnk)?; Ok(Wad{name, siz_app, entries}) } /// Any kind of chunk in an `Entry`. #[derive(Debug, serde::Serialize)] pub enum Chunk { /// A `PICT` chunk. Pict(image::Image8), /// A `Minf` chunk. Minf(map::Minf), /// An `iidx` chunk. Iidx(Vec), /// A `PNTS` chunk. Pnts(Vec), /// A `LINS` chunk. Lins(Vec), /// A `SIDS` chunk. Sids(Vec), /// A `POLY` chunk. Poly(Vec), /// A `LITE` chunk. Lite(Vec), /// An `OBJS` chunk. Objs(Vec), /// A `plac` chunk. Plac(Vec), /// An `ambi` chunk. Ambi(Vec), /// A `bonk` chunk. Bonk(Vec), /// A `medi` chunk. Medi(Vec), /// A `plat` chunk. Plat(Vec), /// A `NOTE` chunk. Note(Vec), /// A `term` chunk. Term(Vec), /// A `FXpx` chunk. Fxpx(Vec), /// A `MNpx` chunk. Mnpx(Vec), /// A `PRpx` chunk. Prpx(Vec), /// A `PXpx` chunk. Pxpx(Vec), /// A `WPpx` chunk. Wppx(Vec), /// Any other type of chunk, which may have arbitrary data in it. Data{/** The name of the chunk. */ iden: Ident, /** The data. */ data: Vec}, } /// An entry containing chunks and application-specific data. #[derive(Debug, serde::Serialize)] pub struct Entry { /// All of the chunks in this `Entry`. pub chunks: Vec, /// The application specific data for this Entry. pub appdata: Vec, } /// A Map file containing entries. #[derive(Debug, serde::Serialize)] pub struct Wad { /// The original name of this file. pub name: String, /// The size of each `Entry`'s `appdata` field. pub siz_app: usize, /// All of the entries in this `Wad`. pub entries: BTreeMap, } c_enum! { /// The version of a `Wad`. #[derive(Debug, serde::Serialize)] pub enum Ver: u16 { 0 => Base, 1 => Dir, 2 => Over, 4 => Inf, } } // EOF