Maraiah/maraiah/map/head.rs

148 lines
3.5 KiB
Rust

//! 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<Map>
{
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<String>) -> 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<u8>,
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