//! 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> { read_data! { 128, BE in b => wadver = u16[0]; dataver = u16[2]; origname = mac_roman_conv[4..68] nt; dirofs = u32[72] as usize; numents = u16[76] as usize; appsize = u16[78] as usize; wcnksize = u16[80] as usize; wentsize = u16[82] as usize; } let wadver = Ver::from_repr(wadver)?; let is_old = match wadver { Ver::Base => true, _ => false, }; let entsize = if is_old {8 } else {10}; let cnksize = if is_old {12} else {16}; 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 { read_data! { p + entsize, 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 cnkdata = &b[offset..offset + size]; let chunks = get_chunks(cnkdata, cnksize)?; let appdata = &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() { read_data! { p + cnksize, BE in b => iden = iden[p]; size = u32[p + 8] as usize; } 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>; #[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