use crate::lump::QuakePackLump;
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(Clone, Debug)]
pub struct QuakePack {
pub lumps: Vec<QuakePackLump>,
}
impl QuakePack {
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<QuakePackLump> = vec!();
let mut wad_sig = [0u8; 4];
f.read_exact(&mut wad_sig)?;
if wad_sig != *b"PACK" {
return Err(Error::new(WadError::IncorrectMagicInHeader));
}
let directory_location = f.read_u32::<LittleEndian>()? as u64;
let directory_size = f.read_u32::<LittleEndian>()? as u64;
let lump_count = directory_size / 64;
for n in 0..lump_count {
f.seek(std::io::SeekFrom::Start(directory_location + 64 * n))?;
let mut lumpname = "".to_owned();
let mut null_seen = false;
for _ in 0..56 {
let nextchar = f.read_u8()? as char;
if nextchar == '\0' { null_seen = true; }
if !null_seen { lumpname.push(nextchar); }
}
let filepos = f.read_u32::<LittleEndian>()? as u64;
let filesize = f.read_u32::<LittleEndian>()? as usize;
f.seek(std::io::SeekFrom::Start(filepos))?;
let mut filebuf = vec![0; filesize as usize];
f.read_exact(&mut filebuf)?;
lumps.push(QuakePackLump {
name: lumpname,
data: filebuf.into_boxed_slice(),
});
}
Ok(Self { lumps })
}
pub fn save(&self, f: &mut File) -> Result<()> {
f.seek(std::io::SeekFrom::Start(0))?;
f.write(b"PACK")?;
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 dictstart = f.stream_position()? as u32;
for d in dict {
let mut lname = d.name.clone();
lname.truncate(56);
while lname.len() < 56 { lname.push('\0'); }
f.write(lname.as_bytes())?;
f.write_u32::<LittleEndian>(d.loc)?;
f.write_u32::<LittleEndian>(d.size)?;
}
let dictend = f.stream_position()? as u32;
let dictsize = dictend - dictstart;
f.seek(std::io::SeekFrom::Start(4))?;
f.write_u32::<LittleEndian>(dictstart)?;
f.write_u32::<LittleEndian>(dictsize)?;
Ok(())
}
}
impl FlatClod<QuakePackLump> for QuakePack {
fn clump_amt(&self) -> usize {
self.lumps.len()
}
fn nth_clump(&self, n: usize) -> &QuakePackLump {
&self.lumps[n]
}
fn a_sub_nth_clumps(&self, a: impl Iterator<Item = usize>) -> Vec<&QuakePackLump> {
let a: Vec<usize> = a.collect();
(&self.lumps[..])
.iter()
.enumerate()
.filter(|(k, _)| a.contains(k))
.map(|(_, l)| l)
.collect()
}
}
impl FlatClodMut<QuakePackLump> for QuakePack {
fn nth_clump_mut(&mut self, n: usize) -> &mut QuakePackLump {
&mut self.lumps[n]
}
fn a_sub_nth_clumps_mut(&mut self, a: impl Iterator<Item = usize>) -> Vec<&mut QuakePackLump> {
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: QuakePackLump, n: usize) {
self.lumps.insert(n, l);
}
fn remove_clump(&mut self, n: usize) -> QuakePackLump {
self.lumps.remove(n)
}
}