2019-02-08 21:08:53 -08:00
|
|
|
//! Marathon Wad format handling.
|
|
|
|
|
|
|
|
use crate::durandal::{bin::*, err::*, text::mac_roman_conv};
|
2019-02-12 14:32:16 -08:00
|
|
|
use serde::Serialize;
|
2019-02-08 21:08:53 -08:00
|
|
|
use std::{collections::BTreeMap, fmt};
|
|
|
|
|
|
|
|
impl Wad<'_>
|
|
|
|
{
|
2019-02-24 20:34:59 -08:00
|
|
|
pub fn new(b: &[u8]) -> ResultS<Wad<'_>>
|
2019-02-08 21:08:53 -08:00
|
|
|
{
|
2019-02-18 20:06:34 -08:00
|
|
|
read_data! {
|
|
|
|
128, BE in b =>
|
|
|
|
wadver = u16[0];
|
|
|
|
dataver = u16[2];
|
2019-02-21 11:24:20 -08:00
|
|
|
origname = mac_roman_conv[4..68] nt;
|
2019-02-18 20:06:34 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-02-21 11:24:20 -08:00
|
|
|
let wadver = Ver::from_repr(wadver)?;
|
2019-02-08 21:08:53 -08:00
|
|
|
|
2019-02-08 21:53:27 -08:00
|
|
|
let is_old = match wadver {
|
|
|
|
Ver::Base => true,
|
|
|
|
_ => false,
|
|
|
|
};
|
2019-02-20 15:39:29 -08:00
|
|
|
|
2019-02-24 20:34:59 -08:00
|
|
|
let entsize = if is_old {8 } else {10};
|
|
|
|
let cnksize = if is_old {12} else {16};
|
2019-02-08 21:08:53 -08:00
|
|
|
|
2019-02-08 21:53:27 -08:00
|
|
|
if entsize != wentsize {
|
2019-02-10 02:31:57 -08:00
|
|
|
bail!("invalid entry size");
|
2019-02-08 21:53:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if cnksize != wcnksize {
|
2019-02-10 02:31:57 -08:00
|
|
|
bail!("invalid chunk size");
|
2019-02-08 21:53:27 -08:00
|
|
|
}
|
2019-02-08 21:08:53 -08:00
|
|
|
|
|
|
|
let mut entries = EntryMap::new();
|
|
|
|
let mut p = dirofs;
|
|
|
|
|
|
|
|
for i in 0..numents {
|
2019-02-18 20:06:34 -08:00
|
|
|
read_data! {
|
|
|
|
p + entsize, BE in b =>
|
|
|
|
offset = u32[p] as usize;
|
|
|
|
size = u32[p + 4] as usize;
|
|
|
|
index = u16[p + 8];
|
|
|
|
}
|
|
|
|
|
2019-02-24 20:34:59 -08:00
|
|
|
let index = if is_old {i as u16} else {index};
|
2019-02-08 21:08:53 -08:00
|
|
|
|
2019-02-18 20:06:34 -08:00
|
|
|
let cnkdata = &b[offset..offset + size];
|
2019-02-10 02:31:57 -08:00
|
|
|
let chunks = get_chunks(cnkdata, cnksize)?;
|
2019-02-18 20:06:34 -08:00
|
|
|
let appdata = &b[p..p + appsize];
|
2019-02-08 21:08:53 -08:00
|
|
|
|
|
|
|
entries.insert(index, Entry{chunks, appdata});
|
|
|
|
|
|
|
|
p += entsize + appsize;
|
|
|
|
}
|
|
|
|
|
2019-02-12 15:03:18 -08:00
|
|
|
Ok(Wad{head: WadHeader{wadver, dataver, appsize, origname}, entries})
|
2019-02-08 21:08:53 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-24 20:34:59 -08:00
|
|
|
fn get_chunks(b: &[u8], cnksize: usize) -> ResultS<ChunkMap<'_>>
|
2019-02-08 21:08:53 -08:00
|
|
|
{
|
|
|
|
let mut chunks = ChunkMap::new();
|
|
|
|
let mut p = 0;
|
|
|
|
|
|
|
|
while p < b.len() {
|
2019-02-18 20:06:34 -08:00
|
|
|
read_data! {
|
|
|
|
p + cnksize, BE in b =>
|
|
|
|
iden = iden[p];
|
|
|
|
size = u32[p + 8] as usize;
|
|
|
|
}
|
|
|
|
|
2019-02-20 18:33:57 -08:00
|
|
|
let beg = p + cnksize;
|
|
|
|
let end = beg + size;
|
2019-02-18 20:06:34 -08:00
|
|
|
|
|
|
|
chunks.insert(iden, &b[beg..end]);
|
|
|
|
|
2019-02-08 21:08:53 -08:00
|
|
|
p = end;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(chunks)
|
|
|
|
}
|
|
|
|
|
2019-02-08 21:53:27 -08:00
|
|
|
type Chunk<'a> = &'a [u8];
|
2019-02-08 21:08:53 -08:00
|
|
|
type ChunkMap<'a> = BTreeMap<Ident, Chunk<'a>>;
|
2019-02-08 21:53:27 -08:00
|
|
|
type EntryMap<'a> = BTreeMap<u16, Entry<'a>>;
|
2019-02-08 21:08:53 -08:00
|
|
|
|
2019-02-12 15:03:18 -08:00
|
|
|
#[derive(Serialize)]
|
2019-02-08 21:08:53 -08:00
|
|
|
pub struct Entry<'a>
|
|
|
|
{
|
2019-02-08 21:53:27 -08:00
|
|
|
pub chunks: ChunkMap<'a>,
|
|
|
|
pub appdata: &'a [u8],
|
2019-02-08 21:08:53 -08:00
|
|
|
}
|
|
|
|
|
2019-02-12 15:03:18 -08:00
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
pub struct WadHeader
|
|
|
|
{
|
|
|
|
pub wadver: Ver,
|
|
|
|
pub dataver: u16,
|
|
|
|
pub origname: String,
|
|
|
|
pub appsize: usize,
|
|
|
|
}
|
|
|
|
|
2019-02-12 14:32:16 -08:00
|
|
|
#[derive(Debug, Serialize)]
|
2019-02-08 21:08:53 -08:00
|
|
|
pub struct Wad<'a>
|
|
|
|
{
|
2019-02-12 15:03:18 -08:00
|
|
|
pub head: WadHeader,
|
2019-02-08 21:53:27 -08:00
|
|
|
pub entries: EntryMap<'a>,
|
2019-02-08 21:08:53 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
c_enum! {
|
2019-02-12 14:32:16 -08:00
|
|
|
#[derive(Debug, Serialize)]
|
2019-02-08 21:08:53 -08:00
|
|
|
pub enum Ver: u16
|
|
|
|
{
|
|
|
|
0 => Base,
|
|
|
|
1 => Dir,
|
|
|
|
2 => Over,
|
|
|
|
4 => Inf,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for Entry<'_>
|
|
|
|
{
|
2019-02-24 20:34:59 -08:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
2019-02-08 21:08:53 -08:00
|
|
|
{
|
|
|
|
write!(f, "Entry{{ ")?;
|
2019-02-13 21:19:36 -08:00
|
|
|
for iden in self.chunks.keys() {
|
2019-02-08 21:53:27 -08:00
|
|
|
write!(f, "{} ", mac_roman_conv(iden))?;
|
|
|
|
}
|
2019-02-13 21:19:36 -08:00
|
|
|
if !self.appdata.is_empty() {
|
2019-02-08 21:53:27 -08:00
|
|
|
write!(f, "\nappdata: {:?} ", self.appdata)?;
|
|
|
|
}
|
2019-02-08 21:08:53 -08:00
|
|
|
write!(f, "}}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// EOF
|