use crate::lump::QuakeLump;
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::{Result, Error};
#[derive(Clone, Debug)]
pub struct QuakeWad {
pub lumps: Vec<QuakeLump>
}
impl QuakeWad {
pub fn blank() -> Self {
Self { lumps: vec!() }
}
pub fn load(f: &mut File) -> Result<Self> {
f.seek(std::io::SeekFrom::Start(0))?;
let mut lumps: Vec<QuakeLump> = vec!();
let mut wad_sig = [0u8; 4];
f.read_exact(&mut wad_sig)?;
if wad_sig != *b"WAD2" {
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 + 32 * n))?;
let filepos = f.read_u32::<LittleEndian>()? as u64;
let filesize = f.read_u32::<LittleEndian>()? as usize;
let size_in_memory = f.read_u32::<LittleEndian>()?;
let data_type = f.read_u8()? as char;
let compression = f.read_u8()? as char;
let _unused = f.read_u16::<LittleEndian>()?;
let mut lumpname = "".to_owned();
let mut null_seen = false;
for _ in 0..16 {
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(QuakeLump {
name: lumpname,
data: filebuf.into_boxed_slice(),
memsize: size_in_memory,
datatype: data_type,
compression: compression,
})
}
Ok(Self { lumps })
}
pub fn save(&self, f: &mut File) -> Result<()> {
f.seek(std::io::SeekFrom::Start(0))?;
f.write(b"WAD2")?;
f.write_u32::<LittleEndian>(self.lumps.len() as u32)?;
f.seek(std::io::SeekFrom::Start(12))?;
struct DictionaryEntry {
name: String,
size: u32,
loc: u32,
memsize: u32,
dtype: char,
comprs: char
}
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,
memsize: l.memsize,
dtype: l.datatype,
comprs: l.compression
});
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)?;
f.write_u32::<LittleEndian>(d.memsize)?;
f.write(&[d.dtype as u8])?;
f.write(&[d.comprs as u8])?;
f.write_u16::<LittleEndian>(0)?;
let mut lname = d.name.clone();
lname.truncate(16);
while lname.len() < 16 { 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<QuakeLump> for QuakeWad {
fn clump_amt(&self) -> usize {
self.lumps.len()
}
fn nth_clump(&self, n: usize) -> &QuakeLump {
&self.lumps[n]
}
fn a_sub_nth_clumps(&self, a: impl Iterator<Item = usize>) -> Vec<&QuakeLump> {
let a: Vec<usize> = a.collect();
(&self.lumps[..])
.iter()
.enumerate()
.filter(|(k, _)| a.contains(k))
.map(|(_, l)| l)
.collect()
}
}
impl FlatClodMut<QuakeLump> for QuakeWad {
fn nth_clump_mut(&mut self, n: usize) -> &mut QuakeLump {
&mut self.lumps[n]
}
fn a_sub_nth_clumps_mut(&mut self, a: impl Iterator<Item = usize>) -> Vec<&mut QuakeLump> {
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: QuakeLump, n: usize) {
self.lumps.insert(n, l);
}
fn remove_clump(&mut self, n: usize) -> QuakeLump {
self.lumps.remove(n)
}
}