Refactor version handling to handle subversions and correctly parse v8a

This commit is contained in:
Truman Kilen 2023-01-20 12:09:44 -06:00
parent af3ef60f30
commit 529bdcac4e
5 changed files with 82 additions and 35 deletions

View file

@ -1,4 +1,4 @@
use super::{ext::ReadExt, Compression, Version};
use super::{ext::ReadExt, Compression, Version, VersionMajor};
use byteorder::{ReadBytesExt, LE};
use std::io;
@ -45,8 +45,11 @@ impl Entry {
size += 8; // offset
size += 8; // compressed
size += 8; // uncompressed
size += 4; // compression
size += match version == Version::Initial {
size += match version != Version::V8A {
true => 4, // 32 bit compression
false => 1, // 8 bit compression
};
size += match version.version_major() == VersionMajor::Initial {
true => 8, // timestamp
false => 0,
};
@ -56,7 +59,7 @@ impl Entry {
false => 0,
};
size += 1; // encrypted
size += match version >= Version::CompressionEncryption {
size += match version.version_major() >= VersionMajor::CompressionEncryption {
true => 4, // blocks uncompressed
false => 0,
};
@ -68,7 +71,7 @@ impl Entry {
let offset = reader.read_u64::<LE>()?;
let compressed = reader.read_u64::<LE>()?;
let uncompressed = reader.read_u64::<LE>()?;
let compression = match reader.read_u32::<LE>()? {
let compression = match if version == Version::V8A { reader.read_u8()? as u32 } else { reader.read_u32::<LE>()? } {
0x01 | 0x10 | 0x20 => Compression::Zlib,
_ => Compression::None,
};
@ -77,19 +80,19 @@ impl Entry {
compressed,
uncompressed,
compression,
timestamp: match version == Version::Initial {
timestamp: match version.version_major() == VersionMajor::Initial {
true => Some(reader.read_u64::<LE>()?),
false => None,
},
hash: Some(reader.read_guid()?),
blocks: match version >= Version::CompressionEncryption
blocks: match version.version_major() >= VersionMajor::CompressionEncryption
&& compression != Compression::None
{
true => Some(reader.read_array(Block::new)?),
false => None,
},
encrypted: version >= Version::CompressionEncryption && reader.read_bool()?,
block_uncompressed: match version >= Version::CompressionEncryption {
encrypted: version.version_major() >= VersionMajor::CompressionEncryption && reader.read_bool()?,
block_uncompressed: match version.version_major() >= VersionMajor::CompressionEncryption {
true => Some(reader.read_u32::<LE>()?),
false => None,
},
@ -140,7 +143,7 @@ impl Entry {
};
let offset_base =
match version >= super::Version::RelativeChunkOffsets {
match version.version_major() >= VersionMajor::RelativeChunkOffsets {
true => 0,
false => offset,
} + Entry::get_serialized_size(version, compression, compression_block_count);
@ -189,7 +192,7 @@ impl Entry {
pub fn read<R: io::Read + io::Seek, W: io::Write>(
&self,
reader: &mut R,
version: super::Version,
version: Version,
key: Option<&aes::Aes256Dec>,
buf: &mut W,
) -> Result<(), super::Error> {
@ -217,7 +220,7 @@ impl Entry {
for block in blocks {
io::copy(
&mut <$decompressor>::new(
&data[match version >= Version::RelativeChunkOffsets {
&data[match version.version_major() >= VersionMajor::RelativeChunkOffsets {
true => {
(block.start - (data_offset - self.offset)) as usize
..(block.end - (data_offset - self.offset)) as usize

View file

@ -1,4 +1,4 @@
use super::{ext::ReadExt, Compression, Version};
use super::{ext::ReadExt, Compression, Version, VersionMajor};
use byteorder::{ReadBytesExt, LE};
use std::str::FromStr;
@ -8,6 +8,7 @@ pub struct Footer {
pub encrypted: bool,
pub magic: u32,
pub version: Version,
pub version_major: VersionMajor,
pub index_offset: u64,
pub index_size: u64,
pub hash: [u8; 20],
@ -18,21 +19,22 @@ pub struct Footer {
impl Footer {
pub fn new<R: std::io::Read>(reader: &mut R, version: Version) -> Result<Self, super::Error> {
let footer = Self {
encryption_uuid: match version >= Version::EncryptionKeyGuid {
encryption_uuid: match version.version_major() >= VersionMajor::EncryptionKeyGuid {
true => Some(reader.read_u128::<LE>()?),
false => None,
},
encrypted: version >= Version::IndexEncryption && reader.read_bool()?,
encrypted: version.version_major() >= VersionMajor::IndexEncryption && reader.read_bool()?,
magic: reader.read_u32::<LE>()?,
version: Version::from_repr(reader.read_u32::<LE>()?).unwrap_or(version),
version,
version_major: VersionMajor::from_repr(reader.read_u32::<LE>()?).unwrap_or(version.version_major()),
index_offset: reader.read_u64::<LE>()?,
index_size: reader.read_u64::<LE>()?,
hash: reader.read_guid()?,
frozen: version == Version::FrozenIndex && reader.read_bool()?,
frozen: version.version_major() == VersionMajor::FrozenIndex && reader.read_bool()?,
compression: {
let mut compression = Vec::with_capacity(match version {
ver if ver < Version::FNameBasedCompression => 0,
Version::FNameBasedCompression => 4,
ver if ver < Version::V8A => 0,
ver if ver < Version::V8B => 4,
_ => 5,
});
for _ in 0..compression.capacity() {
@ -54,7 +56,7 @@ impl Footer {
if super::MAGIC != footer.magic {
return Err(super::Error::Magic(footer.magic));
}
if version != footer.version {
if version.version_major() != footer.version_major {
return Err(super::Error::Version {
used: version,
version: footer.version,

View file

@ -9,12 +9,31 @@ pub use {error::*, pak::*};
pub const MAGIC: u32 = 0x5A6F12E1;
#[derive(
Clone, Copy, PartialEq, Eq, PartialOrd, Debug, strum::Display, strum::FromRepr, strum::EnumIter,
)]
pub enum Version {
V0,
V1,
V2,
V3,
V4,
V5,
V6,
V7,
V8A,
V8B,
V9,
V10,
V11,
}
#[repr(u32)]
#[derive(
Clone, Copy, PartialEq, Eq, PartialOrd, Debug, strum::Display, strum::FromRepr, strum::EnumIter,
)]
pub enum Version {
/// Version actually written to the pak file
pub enum VersionMajor {
Unknown, // v0 unknown (mostly just for padding)
Initial, // v1 initial specification
NoTimestamps, // v2 timestamps removed
@ -38,24 +57,47 @@ impl Version {
pub fn size(self) -> i64 {
// (magic + version): u32 + (offset + size): u64 + hash: [u8; 20]
let mut size = 4 + 4 + 8 + 8 + 20;
if self >= Version::EncryptionKeyGuid {
if self.version_major() >= VersionMajor::EncryptionKeyGuid {
// encryption uuid: u128
size += 16;
}
if self >= Version::IndexEncryption {
if self.version_major() >= VersionMajor::IndexEncryption {
// encrypted: bool
size += 1;
}
if self == Version::FrozenIndex {
if self.version_major() == VersionMajor::FrozenIndex {
// frozen index: bool
size += 1;
}
if self >= Version::FNameBasedCompression {
if self >= Version::V8A {
// compression names: [[u8; 32]; 5]
size += 32 * 5;
size += 32 * 4;
}
if self >= Version::V8B {
// compression names: [[u8; 32]; 5]
size += 32 * 1;
}
size
}
/// Losslessly convert full version into major version
pub fn version_major(&self) -> VersionMajor {
match self {
Version::V0 => VersionMajor::Unknown,
Version::V1 => VersionMajor::Initial,
Version::V2 => VersionMajor::NoTimestamps,
Version::V3 => VersionMajor::CompressionEncryption,
Version::V4 => VersionMajor::IndexEncryption,
Version::V5 => VersionMajor::RelativeChunkOffsets,
Version::V6 => VersionMajor::DeleteRecords,
Version::V7 => VersionMajor::EncryptionKeyGuid,
Version::V8A => VersionMajor::FNameBasedCompression,
Version::V8B => VersionMajor::FNameBasedCompression,
Version::V9 => VersionMajor::FrozenIndex,
Version::V10 => VersionMajor::PathHashIndex,
Version::V11 => VersionMajor::Fnv64BugFix,
}
}
}
#[derive(Default, Clone, Copy, PartialEq, Eq, Debug, strum::Display, strum::EnumString)]

View file

@ -1,4 +1,4 @@
use super::Version;
use super::{Version, VersionMajor};
use hashbrown::HashMap;
use std::io::{self, Seek};
@ -102,7 +102,7 @@ impl<R: io::Read + io::Seek> PakReader<R> {
let mount_point = index.read_string()?;
let len = index.read_u32::<LE>()? as usize;
let index = if version >= Version::PathHashIndex {
let index = if version.version_major() >= VersionMajor::PathHashIndex {
let path_hash_seed = index.read_u64::<LE>()?;
let path_hash_index = if index.read_u32::<LE>()? != 0 {

View file

@ -65,12 +65,12 @@ macro_rules! encryptindex {
matrix_test!(
(
"v5" unpak::Version::RelativeChunkOffsets,
"v7" unpak::Version::EncryptionKeyGuid,
"v8a" unpak::Version::FNameBasedCompression,
"v8b" unpak::Version::FNameBasedCompression,
"v9" unpak::Version::FrozenIndex,
"v11" unpak::Version::Fnv64BugFix,
"v5" unpak::Version::V5,
"v7" unpak::Version::V7,
"v8a" unpak::Version::V8A,
"v8b" unpak::Version::V8B,
"v9" unpak::Version::V9,
"v11" unpak::Version::V11,
),
("", "_compress"),
("", "_encrypt"),