//! Map file header. use crate::{err::*, text::mac_roman_cstr}; use std::io::prelude::*; /// Reads a Map file. pub fn read(rd: &mut impl Read) -> ResultS { let mut data = Vec::new(); rd.read_to_end(&mut data)?; read_data! { endian: BIG, buf: &data, size: 128, start: 0, data { let ver_wad = u16[0] enum Ver; let ver_data = u16[2]; let name = mac_roman_cstr[4; 64] no_try; let dir_ofs = u32[72] usize; let num_ent = u16[76] usize; let size_appl = u16[78] usize; let size_wcnk = u16[80] usize; let size_went = u16[82] usize; } } let head = Header::new(size_appl, ver_data, ver_wad, Some(name)); if !head.old_wad() && head.size_entry_base() != size_went { bail!("invalid entry size"); } if !head.old_wad() && head.size_chunk() != size_wcnk { bail!("invalid chunk size"); } let map = Map{head, data, dir_ofs, num_ent}; if map.dir_end() > map.data.len() { bail!("not enough data in map file"); } Ok(map) } impl Header { /// Creates a new `Header`. pub fn new(size_appl: usize, ver_data: u16, ver_wad: Ver, name: Option) -> Self { let name = name.unwrap_or(String::new()); Header{name, size_appl, ver_data, ver_wad} } /// Returns `true` if the data is in Marathon 1 format. #[inline] pub fn old_data(&self) -> bool {self.ver_data() == 0} /// Returns `true` if the Map file is in Marathon 1 format. #[inline] pub fn old_wad(&self) -> bool {self.ver_wad() == Ver::Base} /// The data version of this file. #[inline] pub const fn ver_data(&self) -> u16 {self.ver_data} /// The format version of this file. #[inline] pub const fn ver_wad(&self) -> Ver {self.ver_wad} /// The size of each `Entry`'s `appdata` field. #[inline] pub const fn size_appl(&self) -> usize {self.size_appl} /// The size of each `Entry`'s data. #[inline] pub fn size_entry_base(&self) -> usize {if self.old_wad() {8} else {10}} /// The size of each `Entry`. #[inline] pub fn size_entry(&self) -> usize {self.size_entry_base() + self.size_appl()} /// The size of each chunk's header. #[inline] pub fn size_chunk(&self) -> usize {if self.old_wad() {12} else {16}} } impl Map { /// The header for this map. pub fn head(&self) -> &Header {&self.head} /// The data section of this map. pub fn data(&self) -> &[u8] {&self.data[..]} /// The directory section of this map. pub fn dir(&self) -> &[u8] {&self.data[self.dir_beg()..self.dir_end()]} /// The number of entries in the directory. pub fn num_ent(&self) -> usize {self.num_ent} fn dir_beg(&self) -> usize {self.dir_ofs} fn dir_end(&self) -> usize { self.dir_ofs + self.head.size_entry() * self.num_ent } } /// A Map header. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug, Eq, PartialEq)] pub struct Header { /// The original name of this file. pub name: String, size_appl: usize, ver_data: u16, ver_wad: Ver, } /// A Map file. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug, Eq, PartialEq)] pub struct Map { head: Header, data: Vec, dir_ofs: usize, num_ent: usize, } c_enum! { /// The version of a Map file. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] pub enum Ver: u16 { Base = 0, Dir = 1, Over = 2, Inf = 4, } } // EOF