use binrw::BinRead;
use std::borrow::Cow;
use std::fs::File;
use std::io::{Error, Read, Seek, SeekFrom};
use std::path::PathBuf;
use std::sync::Arc;
#[derive(Debug)]
pub struct VPKEntry {
pub dir_entry: VPKDirectoryEntry,
pub archive_path: Option<Arc<PathBuf>>,
pub preload_data: Vec<u8>,
}
impl VPKEntry {
pub fn get(&self) -> Result<Cow<[u8]>, Error> {
let mut reader = self.reader()?;
let mut buf = Vec::new();
reader.read_to_end(&mut buf)?;
Ok(Cow::from(buf))
}
pub fn reader(&self) -> Result<VPKEntryReader<'_>, Error> {
let file = self
.archive_path
.as_ref()
.map(|archive_path| {
let mut file = File::open(archive_path.as_path())?;
file.seek(SeekFrom::Start(self.dir_entry.archive_offset as u64))?;
Ok::<_, Error>(file.take(self.dir_entry.file_length as u64))
})
.transpose()?;
Ok(VPKEntryReader::new(&self.preload_data, file))
}
}
pub enum VPKEntryReader<'a> {
PreloadedOnly {
preloaded_data: std::io::Cursor<&'a [u8]>,
},
PreloadAndFile {
preloaded_data_len: usize,
preloaded_bytes_read: usize,
preloaded_data: std::io::Cursor<&'a [u8]>,
file: std::io::Take<File>,
},
FileOnly { file: std::io::Take<File> },
}
impl Read for VPKEntryReader<'_> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match self {
VPKEntryReader::PreloadedOnly { preloaded_data } => preloaded_data.read(buf),
VPKEntryReader::PreloadAndFile {
preloaded_data_len,
preloaded_bytes_read,
preloaded_data,
file,
} => {
if preloaded_bytes_read >= preloaded_data_len {
file.read(buf)
} else {
let bytes_read = preloaded_data.read(buf)?;
let bytes_read = if bytes_read < buf.len() {
let file_bytes_read = file.read(&mut buf[bytes_read..])?;
bytes_read + file_bytes_read
} else {
bytes_read
};
*preloaded_bytes_read += bytes_read;
Ok(bytes_read)
}
}
VPKEntryReader::FileOnly { file } => file.read(buf),
}
}
}
impl<'a> VPKEntryReader<'a> {
pub fn new(preloaded_data: &'a [u8], file: Option<std::io::Take<File>>) -> Self {
match file {
Some(file) => {
if preloaded_data.is_empty() {
Self::FileOnly { file }
} else {
Self::PreloadAndFile {
preloaded_data_len: preloaded_data.len(),
preloaded_bytes_read: 0,
preloaded_data: std::io::Cursor::new(preloaded_data),
file,
}
}
}
None => Self::PreloadedOnly {
preloaded_data: std::io::Cursor::new(preloaded_data),
},
}
}
}
#[derive(Debug, BinRead)]
pub struct VPKDirectoryEntry {
pub crc32: u32,
pub preload_length: u16,
pub archive_index: u16,
pub archive_offset: u32,
pub file_length: u32,
pub suffix: u16,
}