151 lines
3.3 KiB
Rust
151 lines
3.3 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_default();
|
|
Self{name, size_appl, ver_data, ver_wad}
|
|
}
|
|
|
|
/// Returns `true` if the data is in Marathon 1 format.
|
|
#[inline]
|
|
pub const 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.
|
|
#[inline]
|
|
pub const fn head(&self) -> &Header {&self.head}
|
|
|
|
/// The data section of this map.
|
|
#[inline]
|
|
pub fn data(&self) -> &[u8] {&self.data[..]}
|
|
|
|
/// The directory section of this map.
|
|
#[inline]
|
|
pub fn dir(&self) -> &[u8] {&self.data[self.dir_beg()..self.dir_end()]}
|
|
|
|
/// The number of entries in the directory.
|
|
#[inline]
|
|
pub const fn num_ent(&self) -> usize {self.num_ent}
|
|
|
|
#[inline]
|
|
const fn dir_beg(&self) -> usize {self.dir_ofs}
|
|
|
|
#[inline]
|
|
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
|