use std::{ error::Error, fs::File, io::{BufWriter, Write}, path::Path, }; /// A single file entry in a PAK file #[derive(Debug, Clone, PartialEq, Eq)] pub struct Entry { pub(super) index: usize, /// The location within the PAK file, this number is multiplied by the /// block size pub(super) offset: u32, /// The size of the entry in bytes pub(super) length: u32, /// ??? pub(super) unknown1: Option<[u8; 12]>, /// The name of the entry as stored in the PAK pub(super) name: Option, /// The ID of the entry, effectively an index pub(super) id: u32, /// The actual data which makes up the entry pub(super) data: Vec, } impl Entry { /// Get the name of the [`Entry`] pub fn name(&self) -> &Option { &self.name } pub fn index(&self) -> usize { self.index } pub fn id(&self) -> u32 { self.id } /// Save an [`Entry`] as its underlying data to a file pub fn save>(&self, path: &P) -> Result<(), Box> { let mut out_file = BufWriter::new(File::create(path)?); out_file.write_all(&self.data)?; out_file.flush()?; Ok(()) } pub fn len(&self) -> usize { self.length as usize } pub fn is_empty(&self) -> bool { self.len() == 0 } /// Get the raw byte data of an [`Entry`] pub fn as_bytes(&self) -> &Vec { &self.data } /// Get the byte data of an entry, but fixed to be compatible with normal things pub fn cloned_bytes_fixed(&self) -> Vec { match self.file_type() { EntryType::OGGPAK => { dbg!(self.data[15]); self.data[15..].to_vec() }, _ => self.data.clone() } } pub fn display_name(&self) -> String { let mut name = self.name().clone().unwrap_or(self.id().to_string()); let entry_type = self.file_type(); name.push_str(entry_type.extension()); name } pub fn file_type(&self) -> EntryType { if self.data[0..2] == [b'C', b'Z'] { match self.data[2] { b'0' => EntryType::CZ0, b'1' => EntryType::CZ1, b'2' => EntryType::CZ2, b'3' => EntryType::CZ3, b'4' => EntryType::CZ4, b'5' => EntryType::CZ5, _ => EntryType::Unknown, } } else if self.data[0..3] == [b'M', b'V', b'T'] { EntryType::MVT } else if self.data[0..4] == [b'R', b'I', b'F', b'F'] { EntryType::WAV } else if self.data[0..4] == [b'O', b'g', b'g', b'S'] { EntryType::OGG } else if self.data[0..6] == [b'O', b'G', b'G', b'P', b'A', b'K'] { EntryType::OGGPAK } else { EntryType::Unknown } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EntryType { // CZ image files CZ0, CZ1, CZ2, CZ3, CZ4, CZ5, /// An MVT video file MVT, /// OGG Audio file OGG, /// OGGPAK Audio file OGGPAK, /// Wav Audio file WAV, /// Who knows! Unknown, } impl EntryType { /// Get the file extension for the file pub fn extension(&self) -> &'static str { match self { Self::CZ0 => ".cz0", Self::CZ1 => ".cz1", Self::CZ2 => ".cz2", Self::CZ3 => ".cz3", Self::CZ4 => ".cz4", Self::CZ5 => ".cz5", Self::MVT => ".mvt", Self::OGG => ".ogg", Self::OGGPAK => ".oggpak", Self::WAV => ".wav", Self::Unknown => "", } } }