WIP pak init refactor

This commit is contained in:
Truman Kilen 2023-01-19 21:53:13 -06:00
parent 0b5bded4b4
commit 461bc66c30
4 changed files with 54 additions and 60 deletions

View file

@ -6,7 +6,7 @@ pub use {list::list, unpack::unpack, version::version};
fn load_pak( fn load_pak(
path: String, path: String,
key: Option<String>, key: Option<String>,
) -> Result<unpak::Pak<std::io::BufReader<std::fs::File>>, unpak::Error> { ) -> Result<unpak::PakReader<std::io::BufReader<std::fs::File>>, unpak::Error> {
use aes::cipher::KeyInit; use aes::cipher::KeyInit;
use base64::{engine::general_purpose, Engine as _}; use base64::{engine::general_purpose, Engine as _};
let key = key let key = key
@ -21,17 +21,5 @@ fn load_pak(
}) })
.transpose()?; .transpose()?;
for ver in unpak::Version::iter() { unpak::PakReader::new_any( std::io::BufReader::new(std::fs::OpenOptions::new().read(true).open(&path)?), key)
match unpak::Pak::new(
std::io::BufReader::new(std::fs::OpenOptions::new().read(true).open(&path)?),
ver,
key.clone(),
) {
Ok(pak) => {
return Ok(pak);
}
_ => continue,
}
}
Err(unpak::Error::Other("version unsupported"))
} }

View file

@ -31,8 +31,8 @@ pub enum Version {
// strum shouldn't need to be installed by users // strum shouldn't need to be installed by users
impl Version { impl Version {
pub fn iter() -> VersionIter { pub fn iter() -> std::iter::Rev<VersionIter> {
<Version as strum::IntoEnumIterator>::iter() <Version as strum::IntoEnumIterator>::iter().rev()
} }
pub fn size(self) -> i64 { pub fn size(self) -> i64 {

View file

@ -3,12 +3,16 @@ use hashbrown::HashMap;
use std::io::{self, Seek}; use std::io::{self, Seek};
#[derive(Debug)] #[derive(Debug)]
pub struct Pak<R: io::Read + io::Seek> { pub struct PakReader<R: io::Read + io::Seek> {
pak: Pak,
reader: R,
}
#[derive(Debug)]
pub struct Pak {
version: Version, version: Version,
mount_point: String, mount_point: String,
key: Option<aes::Aes256Dec>, key: Option<aes::Aes256Dec>,
index: Index, index: Index,
reader: R,
} }
#[derive(Debug)] #[derive(Debug)]
@ -52,12 +56,34 @@ fn decrypt(key: &Option<aes::Aes256Dec>, bytes: &mut [u8]) -> Result<(), super::
} }
} }
impl<R: io::Read + io::Seek> Pak<R> { impl<R: io::Read + io::Seek> PakReader<R> {
pub fn new_any(
mut reader: R,
key: Option<aes::Aes256Dec>,
) -> Result<Self, super::Error> {
for ver in Version::iter() {
match PakReader::new(
&mut reader,
ver,
key.clone(),
) {
Ok(pak) => {
return Ok(PakReader {
pak,
reader,
});
}
_ => continue,
}
}
Err(super::Error::Other("version unsupported"))
}
pub fn new( pub fn new(
mut reader: R, mut reader: R,
version: super::Version, version: super::Version,
key: Option<aes::Aes256Dec>, key: Option<aes::Aes256Dec>,
) -> Result<Self, super::Error> { ) -> Result<Pak, super::Error> {
use super::ext::ReadExt; use super::ext::ReadExt;
use byteorder::{ReadBytesExt, LE}; use byteorder::{ReadBytesExt, LE};
// read footer to get index, encryption & compression info // read footer to get index, encryption & compression info
@ -174,21 +200,20 @@ impl<R: io::Read + io::Seek> Pak<R> {
Index::V1(IndexV1 { entries }) Index::V1(IndexV1 { entries })
}; };
Ok(Self { Ok(Pak {
version, version,
mount_point, mount_point,
key, key,
index, index,
reader,
}) })
} }
pub fn version(&self) -> super::Version { pub fn version(&self) -> super::Version {
self.version self.pak.version
} }
pub fn mount_point(&self) -> &str { pub fn mount_point(&self) -> &str {
&self.mount_point &self.pak.mount_point
} }
pub fn get(&mut self, path: &str) -> Result<Vec<u8>, super::Error> { pub fn get(&mut self, path: &str) -> Result<Vec<u8>, super::Error> {
@ -198,14 +223,14 @@ impl<R: io::Read + io::Seek> Pak<R> {
} }
pub fn read<W: io::Write>(&mut self, path: &str, writer: &mut W) -> Result<(), super::Error> { pub fn read<W: io::Write>(&mut self, path: &str, writer: &mut W) -> Result<(), super::Error> {
match self.index.entries().get(path) { match self.pak.index.entries().get(path) {
Some(entry) => entry.read(&mut self.reader, self.version, self.key.as_ref(), writer), Some(entry) => entry.read(&mut self.reader, self.pak.version, self.pak.key.as_ref(), writer),
None => Err(super::Error::Other("no file found at given path")), None => Err(super::Error::Other("no file found at given path")),
} }
} }
pub fn files(&self) -> std::vec::IntoIter<String> { pub fn files(&self) -> std::vec::IntoIter<String> {
self.index self.pak.index
.entries() .entries()
.keys() .keys()
.cloned() .cloned()

View file

@ -1,38 +1,7 @@
fn load_pak( use paste::paste;
bytes: &[u8],
key: Option<String>,
) -> Result<unpak::Pak<std::io::Cursor<&[u8]>>, unpak::Error> {
use aes::cipher::KeyInit;
use base64::{engine::general_purpose, Engine as _};
let key = key
.map(|k| {
general_purpose::STANDARD
.decode(k)
.as_ref()
.map_err(|_| unpak::Error::Base64)
.and_then(|bytes| {
aes::Aes256Dec::new_from_slice(bytes).map_err(|_| unpak::Error::Aes)
})
})
.transpose()?;
for ver in unpak::Version::iter() {
match unpak::Pak::new(std::io::Cursor::new(bytes), ver, key.clone()) {
Ok(pak) => {
return Ok(pak);
}
_ => {
continue;
}
}
}
Err(unpak::Error::Other("version unsupported"))
}
static AES_KEY: &str = "lNJbw660IOC+kU7cnVQ1oeqrXyhk4J6UAZrCBbcnp94="; static AES_KEY: &str = "lNJbw660IOC+kU7cnVQ1oeqrXyhk4J6UAZrCBbcnp94=";
use paste::paste;
macro_rules! matrix_test { macro_rules! matrix_test {
( ($($version:literal $exp_version:expr),* $(,)?), $compress:tt, $encrypt:tt, $encryptindex:tt ) => { ( ($($version:literal $exp_version:expr),* $(,)?), $compress:tt, $encrypt:tt, $encryptindex:tt ) => {
$( compress!($version, $exp_version, $compress, $encrypt, $encryptindex); )* $( compress!($version, $exp_version, $compress, $encrypt, $encryptindex); )*
@ -57,7 +26,19 @@ macro_rules! encryptindex {
paste! { paste! {
#[test] #[test]
fn [< test_version_ $version $compress $encrypt $encryptindex >]() { fn [< test_version_ $version $compress $encrypt $encryptindex >]() {
let mut pak = load_pak(include_bytes!(concat!("packs/pack_", $version, $compress, $encrypt, $encryptindex, ".pak")), Some(AES_KEY.to_string())).unwrap(); use aes::cipher::KeyInit;
use base64::{engine::general_purpose, Engine as _};
let key = general_purpose::STANDARD
.decode(AES_KEY)
.as_ref()
.map_err(|_| unpak::Error::Base64)
.and_then(|bytes| {
aes::Aes256Dec::new_from_slice(bytes).map_err(|_| unpak::Error::Aes)
}).unwrap();
let mut pak = unpak::PakReader::new_any(
std::io::Cursor::new(include_bytes!(concat!("packs/pack_", $version, $compress, $encrypt, $encryptindex, ".pak"))),
Some(key),
).unwrap();
assert_eq!(pak.mount_point(), "../mount/point/root/"); assert_eq!(pak.mount_point(), "../mount/point/root/");
assert_eq!(pak.version(), $exp_version); assert_eq!(pak.version(), $exp_version);
use std::collections::HashSet; use std::collections::HashSet;