//! Marathon Wad format handling. use crate::durandal::{bin::*, err::*, image, text::mac_roman_conv}; use crate::marathon::{map, phy, pict, trm}; use std::collections::BTreeMap; /// Reads all chunks in an entry. pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS> { let mut chunks = Vec::new(); let mut p = 0; while p < b.len() { read_data! { p + siz_cnk, BE in b => iden = Ident[p]; size = u32[p + 8] as usize; } let beg = p + siz_cnk; let end = beg + size; let data = &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"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()}, }; chunks.push(chunk); p = end; } Ok(chunks) } /// 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> { 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 != siz_went { bail!("invalid entry size"); } if siz_cnk != siz_wcnk { 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, serde::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), Note(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, serde::Serialize)] pub struct Entry { pub chunks: Vec, pub appdata: Vec, } /// The header of a `Wad`. #[derive(Debug, serde::Serialize)] pub struct WadHeader { pub ver_wad: Ver, pub ver_dat: u16, pub name: String, pub siz_app: usize, } /// A Map file containing entries. #[derive(Debug, serde::Serialize)] pub struct Wad { pub head: WadHeader, 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