//! Marathon Wad format handling. use crate::durandal::{bin::*, err::*, text::mac_roman_conv}; use serde::Serialize; use std::{collections::BTreeMap, fmt}; impl Wad<'_> { pub fn new(b: &[u8]) -> ResultS { let wadver = c_u16b(b, 0)?; let dataver = c_u16b(b, 2)?; let origname = c_data(b, 4..68)?; let dirofs = c_u32b(b, 72)? as usize; let numents = c_u16b(b, 76)? as usize; let appsize = c_u16b(b, 78)? as usize; let wcnksize = c_u16b(b, 80)? as usize; let wentsize = c_u16b(b, 82)? as usize; let wadver = Ver::from_repr(wadver)?; let origname = mac_roman_conv(origname); let is_old = match wadver { Ver::Base => true, _ => false, }; let entsize = if !is_old {10} else {8 }; let cnksize = if !is_old {16} else {12}; 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 { let offset = c_u32b(b, p)? as usize; let size = c_u32b(b, p + 4)? as usize; let index = if !is_old {c_u16b(b, p + 8)?} else {i as u16}; if offset + size > b.len() { bail!("not enough data for entry"); } let cnkdata = c_data(b, offset..offset + size)?; let chunks = get_chunks(cnkdata, cnksize)?; let appdata = c_data(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 p = 0; while p < b.len() { let iden = c_iden(b, p)?; let size = c_u32b(b, p + 8)? as usize; let beg = p + cnksize; let end = beg + size; chunks.insert(iden, c_data(b, beg..end)?); p = end; } Ok(chunks) } type Chunk<'a> = &'a [u8]; type ChunkMap<'a> = BTreeMap>; type EntryMap<'a> = BTreeMap>; #[derive(Serialize)] pub struct Entry<'a> { pub chunks: ChunkMap<'a>, pub appdata: &'a [u8], } #[derive(Debug, Serialize)] pub struct WadHeader { pub wadver: Ver, pub dataver: u16, pub origname: String, pub appsize: usize, } #[derive(Debug, Serialize)] pub struct Wad<'a> { pub head: WadHeader, pub entries: EntryMap<'a>, } c_enum! { #[derive(Debug, Serialize)] pub enum Ver: u16 { 0 => Base, 1 => Dir, 2 => Over, 4 => Inf, } } 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