Maraiah/src/marathon/wad.rs

133 lines
3.0 KiB
Rust

//! 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<Wad>
{
let wadver = c_u16b(b, 0)?;
let dataver = c_u16b(b, 2)?;
let origname = c_data(b, 4..68)?;
let dirofs = c_u32b(b, 72)? as usize;
let numents = c_u16b(b, 76)? as usize;
let appsize = c_u16b(b, 78)? as usize;
let wcnksize = c_u16b(b, 80)? as usize;
let wentsize = c_u16b(b, 82)? as usize;
let wadver = Ver::from_repr(wadver)?;
let origname = mac_roman_conv(origname);
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 {
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 {
let offset = c_u32b(b, p)? as usize;
let size = c_u32b(b, p + 4)? as usize;
let index = if !is_old {c_u16b(b, p + 8)?} else {i as u16};
if offset + size > b.len() {
bail!("not enough data for entry");
}
let cnkdata = c_data(b, offset..offset + size)?;
let chunks = get_chunks(cnkdata, cnksize)?;
let appdata = c_data(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<ChunkMap>
{
let mut chunks = ChunkMap::new();
let mut p = 0;
while p < b.len() {
let iden = c_iden(b, p)?;
let size = c_u32b(b, p + 8)? as usize;
let beg = p + cnksize;
let end = beg + size;
chunks.insert(iden, c_data(b, beg..end)?);
p = end;
}
Ok(chunks)
}
type Chunk<'a> = &'a [u8];
type ChunkMap<'a> = BTreeMap<Ident, Chunk<'a>>;
type EntryMap<'a> = BTreeMap<u16, Entry<'a>>;
#[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