use crate::lump::DoomLump;
use crate::agnostic::{FlatClod, FlatClodMut};
use crate::error::Error as WadError;
use std::{fs::File, io::{Read, Seek, Write}};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use anyhow::{Error, Result};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DoomWadFlavor {
InternalWad,
PatchWad,
}
#[derive(Clone, Debug)]
pub struct DoomWad {
pub flavor: DoomWadFlavor,
pub lumps: Vec<DoomLump>,
}
impl DoomWad {
pub fn blank_i() -> Self {
Self { flavor: DoomWadFlavor::InternalWad, lumps: vec!() }
}
pub fn blank_p() -> Self {
Self { flavor: DoomWadFlavor::PatchWad, lumps: vec!() }
}
pub fn blank(flavor: DoomWadFlavor) -> Self {
Self { flavor, lumps: vec!() }
}
pub fn load(f: &mut File) -> Result<DoomWad> {
f.seek(std::io::SeekFrom::Start(0))?;
let mut lumps = vec!();
let mut wad_sig = [0u8; 4];
f.read_exact(&mut wad_sig)?;
let flavor = if wad_sig == *b"IWAD" {
DoomWadFlavor::InternalWad
} else if wad_sig == *b"PWAD" {
DoomWadFlavor::PatchWad
} else { return Err(Error::new(WadError::IncorrectMagicInHeader)); };
let lump_count = f.read_u32::<LittleEndian>()? as u64;
let directory_location = f.read_u32::<LittleEndian>()? as u64;
for n in 0..lump_count {
f.seek(std::io::SeekFrom::Start(directory_location + 16 * n))?;
let filepos = f.read_u32::<LittleEndian>()? as u64;
let filesize = f.read_u32::<LittleEndian>()? as usize;
let mut lumpname = "".to_owned();
let mut null_seen = false;
for _ in 0..8 {
let nextchar = f.read_u8()? as char;
if nextchar == '\0' { null_seen = true; }
if !null_seen { lumpname.push(nextchar); }
}
f.seek(std::io::SeekFrom::Start(filepos))?;
let mut filebuf = vec![0; filesize];
f.read_exact(&mut filebuf)?;
lumps.push(DoomLump {
name: lumpname,
data: filebuf.into_boxed_slice(),
})
}
Ok(Self { flavor, lumps })
}
pub fn save(&self, f: &mut File) -> Result<()> {
f.seek(std::io::SeekFrom::Start(0))?;
f.write(if self.flavor == DoomWadFlavor::InternalWad { b"IWAD" } else { b"PWAD" })?;
f.write_u32::<LittleEndian>(self.lumps.len() as u32)?;
f.seek(std::io::SeekFrom::Start(12))?;
struct DictionaryEntry {
name: String,
size: u32,
loc: u32,
}
let mut dict = vec!();
for l in &self.lumps {
dict.push(DictionaryEntry {
name: l.name.clone(),
size: l.data.len() as u32,
loc: f.stream_position()? as u32,
});
f.write(&l.data)?;
}
let dictpos = f.stream_position()?;
for d in dict {
f.write_u32::<LittleEndian>(d.loc)?;
f.write_u32::<LittleEndian>(d.size)?;
let mut lname = d.name.clone();
lname.truncate(8);
while lname.len() < 8 { lname.push('\0'); }
f.write(lname.as_bytes())?;
}
f.seek(std::io::SeekFrom::Start(8))?;
f.write_u32::<LittleEndian>(dictpos as u32)?;
Ok(())
}
}
impl FlatClod<DoomLump> for DoomWad {
fn clump_amt(&self) -> usize {
self.lumps.len()
}
fn nth_clump(&self, n: usize) -> &DoomLump {
&self.lumps[n]
}
fn a_sub_nth_clumps(&self, a: impl Iterator<Item = usize>) -> Vec<&DoomLump> {
let a: Vec<usize> = a.collect();
(&self.lumps[..])
.iter()
.enumerate()
.filter(|(k, _)| a.contains(k))
.map(|(_, l)| l)
.collect()
}
}
impl FlatClodMut<DoomLump> for DoomWad {
fn nth_clump_mut(&mut self, n: usize) -> &mut DoomLump {
&mut self.lumps[n]
}
fn a_sub_nth_clumps_mut(&mut self, a: impl Iterator<Item = usize>) -> Vec<&mut DoomLump> {
let a: Vec<usize> = a.collect();
(&mut self.lumps[..])
.iter_mut()
.enumerate()
.filter(|(k, _)| a.contains(k))
.map(|(_, l)| l)
.collect()
}
fn insert_clump(&mut self, l: DoomLump, n: usize) {
self.lumps.insert(n, l);
}
fn remove_clump(&mut self, n: usize) -> DoomLump {
self.lumps.remove(n)
}
}