//! 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_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_minf(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 ver_wad = Ver::from_repr(ver_wad)?; let old_wad = match ver_wad { Ver::Base => true, _ => false, }; let old_dat = ver_dat == 0; 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{head: WadHeader{ver_wad, old_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), Iidx(Vec), 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 old_dat: bool, 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