131 lines
3.0 KiB
Rust
131 lines
3.0 KiB
Rust
//! Marathon Wad format handling.
|
|
|
|
use crate::durandal::{bin::*, err::*, text::mac_roman_conv};
|
|
use std::{collections::BTreeMap, fmt};
|
|
|
|
impl Wad<'_>
|
|
{
|
|
pub fn new(b: &[u8]) -> ResultS<Wad>
|
|
{
|
|
if b.len() < 128 {
|
|
return Err(err_msg("not enough data for Wad header"));
|
|
}
|
|
|
|
let wadver = b.c_u16b(0)?;
|
|
let dataver = b.c_u16b(2)?;
|
|
let origname = mac_roman_conv(&b[4..68]);
|
|
// crc = b.c_u32b(68)?;
|
|
let dirofs = b.c_u32b(72)? as usize;
|
|
let numents = b.c_u16b(76)? as usize;
|
|
let appsize = b.c_u16b(78)? as usize;
|
|
let wcnksize = b.c_u16b(80)? as usize;
|
|
let wentsize = b.c_u16b(82)? as usize;
|
|
// parent = b.c_u32b(84)?;
|
|
let wadver = Ver::from_repr(wadver)?;
|
|
|
|
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 {
|
|
return Err(err_msg("invalid entry size"));
|
|
}
|
|
|
|
if cnksize != wcnksize {
|
|
return Err(err_msg("invalid chunk size"));
|
|
}
|
|
|
|
let mut entries = EntryMap::new();
|
|
let mut p = dirofs;
|
|
|
|
for i in 0..numents {
|
|
let offset = b.c_u32b(p)? as usize;
|
|
let size = b.c_u32b(p + 4)? as usize;
|
|
let index = if !is_old {b.c_u16b(p + 8)?} else {i as u16};
|
|
|
|
if offset + size > b.len() {
|
|
return Err(err_msg("not enough data for entry"));
|
|
}
|
|
|
|
let chunks = get_chunks(&b[offset..offset + size], cnksize)?;
|
|
let appdata = &b[p..p + appsize];
|
|
|
|
entries.insert(index, Entry{chunks, appdata});
|
|
|
|
p += entsize + appsize;
|
|
}
|
|
|
|
Ok(Wad{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 = b.c_iden(p)?;
|
|
// ofs = b.c_u32b(p+ 4)?;
|
|
let size = b.c_u32b(p + 8)? as usize;
|
|
// pofs = b.c_u32b(p+12)?;
|
|
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>>;
|
|
|
|
pub struct Entry<'a>
|
|
{
|
|
pub chunks: ChunkMap<'a>,
|
|
pub appdata: &'a [u8],
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Wad<'a>
|
|
{
|
|
wadver: Ver,
|
|
dataver: u16,
|
|
origname: String,
|
|
appsize: usize,
|
|
pub entries: EntryMap<'a>,
|
|
}
|
|
|
|
c_enum! {
|
|
#[derive(Debug)]
|
|
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 {
|
|
write!(f, "{} ", mac_roman_conv(iden))?;
|
|
}
|
|
if self.appdata.len() != 0 {
|
|
write!(f, "\nappdata: {:?} ", self.appdata)?;
|
|
}
|
|
write!(f, "}}")
|
|
}
|
|
}
|
|
|
|
// EOF
|