129 lines
2.9 KiB
Rust
129 lines
2.9 KiB
Rust
//! Marathon Wad format handling.
|
|
|
|
use std::collections::BTreeMap;
|
|
use std::fmt;
|
|
|
|
use crate::durandal::bin::*;
|
|
use crate::durandal::machead::try_mac_header;
|
|
use crate::durandal::text::mac_roman_conv;
|
|
|
|
type Chunk <'a> = &'a[u8];
|
|
type ChunkMap<'a> = BTreeMap<Ident, Chunk<'a>>;
|
|
type EntryMap<'a> = BTreeMap<u16 , Entry<'a>>;
|
|
|
|
pub struct Entry<'a>
|
|
{
|
|
pub map: ChunkMap<'a>,
|
|
pub ext: &'a[u8],
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Wad<'a>
|
|
{
|
|
ver: Ver,
|
|
dvr: u16,
|
|
ext: usize,
|
|
nam: String,
|
|
pub ent: EntryMap<'a>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Ver
|
|
{
|
|
MI,
|
|
M2,
|
|
M1Dir,
|
|
M1
|
|
}
|
|
|
|
impl<'a> fmt::Debug for Entry<'a>
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
|
|
{
|
|
write!(f, "Entry{{ ")?;
|
|
for (k, _) in &self.map {write!(f, "{} ", mac_roman_conv(&k[..]))?}
|
|
write!(f, "}}")
|
|
}
|
|
}
|
|
|
|
impl<'a> Wad<'a>
|
|
{
|
|
pub fn new(b: &[u8]) -> ResultS<Wad>
|
|
{
|
|
const SIZE_ENTRY_NEW: usize = 10;
|
|
const SIZE_ENTRY_OLD: usize = 8;
|
|
|
|
let b = &b[try_mac_header(b)..];
|
|
if b.len() < 128 {return Err("not enough data for header")}
|
|
|
|
let ver = b.c_u16b( 0)?;
|
|
let dvr = b.c_u16b( 2)?;
|
|
let nam = &b[4..68];
|
|
// crc = b.c_u32b(68)?;
|
|
let dir = b.c_u32b(72)? as usize;
|
|
let num = b.c_u16b(76)? as usize;
|
|
let ext = b.c_u16b(78)? as usize;
|
|
// hdr = b.c_u16b(80)?;
|
|
// bsz = b.c_u16b(82)?;
|
|
// pck = b.c_u32b(84)?;
|
|
|
|
let ver = match ver {
|
|
4 => Ver::MI,
|
|
2 => Ver::M2,
|
|
1 => Ver::M1Dir,
|
|
0 => Ver::M1,
|
|
_ => return Err("invalid wad version"),
|
|
};
|
|
|
|
let mut map = EntryMap::new();
|
|
let mut p = dir;
|
|
let o = match ver {Ver::M1 | Ver::M1Dir => true, _ => false};
|
|
let h = if o {SIZE_ENTRY_OLD} else {SIZE_ENTRY_NEW};
|
|
|
|
for i in 0..num
|
|
{
|
|
let ofs = b.c_u32b(p )? as usize;
|
|
let len = b.c_u32b(p+4)? as usize;
|
|
let ind = if o {i as u16}
|
|
else {b.c_u16b(p+8)?};
|
|
|
|
if ofs + len > b.len() {return Err("not enough data for entry")}
|
|
|
|
let ent = Entry{map: get_chunks(&b[ofs..ofs+len], o)?,
|
|
ext: &b[p+h..p+h+ext]};
|
|
|
|
map.insert(ind, ent);
|
|
|
|
p += h + ext;
|
|
}
|
|
|
|
Ok(Wad{ver, dvr, ext,
|
|
nam: mac_roman_conv(nam),
|
|
ent: map})
|
|
}
|
|
}
|
|
|
|
fn get_chunks(b: &[u8], o: bool) -> ResultS<ChunkMap>
|
|
{
|
|
const SIZE_CHUNK_NEW: usize = 16;
|
|
const SIZE_CHUNK_OLD: usize = 12;
|
|
|
|
let mut map = ChunkMap::new();
|
|
let mut p = 0;
|
|
let h = if o {SIZE_CHUNK_OLD} else {SIZE_CHUNK_NEW};
|
|
|
|
while p < b.len()
|
|
{
|
|
let k = b.c_iden(p )?;
|
|
// nx = b.c_u32b(p+ 4)?;
|
|
let l = b.c_u32b(p+ 8)? as usize;
|
|
// o = b.c_u32b(p+12)?;
|
|
map.insert(k, &b[p+h ..p+h+l]);
|
|
p += l + h;
|
|
}
|
|
|
|
Ok(map)
|
|
}
|
|
|
|
// EOF
|