use super::{Compression, Version};
use std::io;
#[derive(Debug)]
pub struct Pak {
version: Version,
path: std::path::PathBuf,
mount_point: String,
compression: Vec<Compression>,
#[cfg(feature = "encryption")]
key: Option<aes::Aes256Dec>,
entries: hashbrown::HashMap<String, super::entry::Entry>,
}
impl Pak {
pub fn new(
path: impl AsRef<std::path::Path>,
version: super::Version,
#[cfg(feature = "encryption")] key_hash: Option<&[u8]>,
) -> Result<Self, super::Error> {
use super::ext::ReadExt;
use byteorder::{ReadBytesExt, LE};
use io::Seek;
let mut reader = std::fs::File::open(&path)?;
reader.seek(io::SeekFrom::End(-version.footer_size()))?;
let footer = super::footer::Footer::new(&mut reader, version)?;
reader.seek(io::SeekFrom::Start(footer.index_offset))?;
#[allow(unused_mut)]
let mut index = reader.read_len(footer.index_size as usize)?;
#[cfg(feature = "encryption")]
let mut key = None;
if footer.encrypted {
#[cfg(feature = "encryption")]
{
let Some(hash) = key_hash else {
return Err(super::Error::Encrypted);
};
use aes::cipher::KeyInit;
let Ok(dec) = aes::Aes256Dec::new_from_slice(hash) else {
return Err(super::Error::Aes)
};
key = Some(dec);
super::decrypt(key.as_ref(), &mut index)?;
}
#[cfg(not(feature = "encryption"))]
return Err(super::Error::Encryption);
}
let mut index = io::Cursor::new(index);
let mount_point = index.read_string()?;
let mut entries = hashbrown::HashMap::new();
if version >= Version::PathHashIndex {
index.read_u32::<LE>()?;
index.read_u64::<LE>()?;
if index.read_u32::<LE>()? != 0 {
index.read_u64::<LE>()?;
index.read_u64::<LE>()?;
index.read_guid()?;
}
let mut files = Vec::new();
if index.read_u32::<LE>()? != 0 {
let offset = index.read_u64::<LE>()?;
let size = index.read_u64::<LE>()?;
index.read_guid()?;
reader.seek(io::SeekFrom::Start(offset))?;
#[allow(unused_mut)]
let mut full_dir = reader.read_len(size as usize)?;
if footer.encrypted {
#[cfg(feature = "encryption")]
super::decrypt(key.as_ref(), &mut full_dir)?;
#[cfg(not(feature = "encryption"))]
return Err(super::Error::Encryption);
}
let mut full_dir = io::Cursor::new(full_dir);
for _ in 0..full_dir.read_u32::<LE>()? {
let dir = full_dir.read_name()?;
for _ in 0..full_dir.read_u32::<LE>()? {
files.push((
dir.clone() + &full_dir.read_string()?,
full_dir.read_u32::<LE>()?,
));
}
}
}
let size = index.read_u32::<LE>()? as usize;
let mut encoded = io::Cursor::new(index.read_len(size)?);
for (file, offset) in files {
encoded.seek(io::SeekFrom::Start(offset as u64))?;
entries.insert(file, super::entry::Entry::from_encoded(&mut encoded)?);
}
}
for _ in 0..index.read_u32::<LE>()? as usize {
entries.insert(
index.read_name()?,
super::entry::Entry::new(&mut index, version)?,
);
}
Ok(Self {
version,
path: path.as_ref().to_path_buf(),
mount_point,
compression: footer.compression,
#[cfg(feature = "encryption")]
key,
entries,
})
}
pub fn new_any(
path: impl AsRef<std::path::Path>,
#[cfg(feature = "encryption")] key: Option<&[u8]>,
) -> Result<Pak, super::Error> {
for ver in Version::iter().rev() {
match Pak::new(
&path,
ver,
#[cfg(feature = "encryption")]
key,
) {
Ok(pak) => return Ok(pak),
Err(e) => match e {
crate::Error::Io(io) => match io.kind() {
io::ErrorKind::NotFound
| io::ErrorKind::PermissionDenied
| io::ErrorKind::AlreadyExists
| io::ErrorKind::WouldBlock
| io::ErrorKind::InvalidInput
| io::ErrorKind::InvalidData
| io::ErrorKind::TimedOut
| io::ErrorKind::WriteZero
| io::ErrorKind::Interrupted
| io::ErrorKind::Unsupported => return Err(io.into()),
_ => continue,
},
crate::Error::Aes
| crate::Error::IntoInner(_)
| crate::Error::Encryption
| crate::Error::Compression
| crate::Error::Encrypted => return Err(e),
_ => continue,
},
}
}
Err(super::Error::Parse)
}
pub fn version(&self) -> super::Version {
self.version
}
pub fn mount_point(&self) -> &str {
&self.mount_point
}
pub fn read<W: io::Write>(&self, entry: &str, writer: &mut W) -> Result<(), super::Error> {
match self.entries.get(entry) {
Some(entry) => entry.read(
&self.path,
self.version,
&self.compression,
#[cfg(feature = "encryption")]
self.key.as_ref(),
writer,
),
None => Err(super::Error::Missing(entry.to_string())),
}
}
pub fn read_to_file(
&self,
entry: &str,
path: impl AsRef<std::path::Path>,
) -> Result<(), super::Error> {
match self.entries.get(entry) {
Some(entry) => entry.read(
&self.path,
self.version,
&self.compression,
#[cfg(feature = "encryption")]
self.key.as_ref(),
&mut std::fs::File::create(path)?,
),
None => Err(super::Error::Missing(entry.to_string())),
}
}
pub fn get(&self, entry: &str) -> Result<Vec<u8>, super::Error> {
let mut data = Vec::new();
self.read(entry, &mut data)?;
Ok(data)
}
pub fn entries(&self) -> Vec<String> {
self.entries.keys().cloned().collect::<Vec<String>>()
}
}