//! Marathon Wad format handling. use crate::durandal::{bin::*, err::*, text::mac_roman_conv}; use std::{collections::BTreeMap, fmt}; impl Wad<'_> { pub fn new(b: &[u8]) -> ResultS { if b.len() < 128 { return Err(err_msg("not enough data for Wad header")); } let wadver = b.c_u16b(0)?; let dataver = b.c_u16b(2)?; let origname = mac_roman_conv(&b[4..68]); // crc = b.c_u32b(68)?; let dirofs = b.c_u32b(72)? as usize; let numents = b.c_u16b(76)? as usize; let appsize = b.c_u16b(78)? as usize; let wcnksize = b.c_u16b(80)? as usize; let wentsize = b.c_u16b(82)? as usize; // parent = b.c_u32b(84)?; let wadver = Ver::from_repr(wadver)?; 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 { return Err(err_msg("invalid entry size")); } if cnksize != wcnksize { return Err(err_msg("invalid chunk size")); } let mut entries = EntryMap::new(); let mut p = dirofs; for i in 0..numents { let offset = b.c_u32b(p)? as usize; let size = b.c_u32b(p + 4)? as usize; let index = if !is_old {b.c_u16b(p + 8)?} else {i as u16}; if offset + size > b.len() { return Err(err_msg("not enough data for entry")); } let chunks = get_chunks(&b[offset..offset + size], cnksize)?; let appdata = &b[p..p + appsize]; entries.insert(index, Entry{chunks, appdata}); p += entsize + appsize; } Ok(Wad{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 = b.c_iden(p)?; // ofs = b.c_u32b(p+ 4)?; let size = b.c_u32b(p + 8)? as usize; // pofs = b.c_u32b(p+12)?; let beg = p + cnksize; let end = beg + size; chunks.insert(iden, &b[beg..end]); p = end; } Ok(chunks) } type Chunk<'a> = &'a [u8]; type ChunkMap<'a> = BTreeMap>; type EntryMap<'a> = BTreeMap>; pub struct Entry<'a> { pub chunks: ChunkMap<'a>, pub appdata: &'a [u8], } #[derive(Debug)] pub struct Wad<'a> { wadver: Ver, dataver: u16, origname: String, appsize: usize, pub entries: EntryMap<'a>, } c_enum! { #[derive(Debug)] 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 { write!(f, "{} ", mac_roman_conv(iden))?; } if self.appdata.len() != 0 { write!(f, "\nappdata: {:?} ", self.appdata)?; } write!(f, "}}") } } // EOF