Maraiah/src/marathon/wad.rs

144 lines
2.9 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<'_>>
{
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<ChunkMap<'_>>
{
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<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