mirror of
https://github.com/xavo95/repak.git
synced 2025-01-18 19:04:07 +00:00
Refactor version handling to handle subversions and correctly parse v8a
This commit is contained in:
parent
af3ef60f30
commit
529bdcac4e
5 changed files with 82 additions and 35 deletions
27
src/entry.rs
27
src/entry.rs
|
@ -1,4 +1,4 @@
|
||||||
use super::{ext::ReadExt, Compression, Version};
|
use super::{ext::ReadExt, Compression, Version, VersionMajor};
|
||||||
use byteorder::{ReadBytesExt, LE};
|
use byteorder::{ReadBytesExt, LE};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
@ -45,8 +45,11 @@ impl Entry {
|
||||||
size += 8; // offset
|
size += 8; // offset
|
||||||
size += 8; // compressed
|
size += 8; // compressed
|
||||||
size += 8; // uncompressed
|
size += 8; // uncompressed
|
||||||
size += 4; // compression
|
size += match version != Version::V8A {
|
||||||
size += match version == Version::Initial {
|
true => 4, // 32 bit compression
|
||||||
|
false => 1, // 8 bit compression
|
||||||
|
};
|
||||||
|
size += match version.version_major() == VersionMajor::Initial {
|
||||||
true => 8, // timestamp
|
true => 8, // timestamp
|
||||||
false => 0,
|
false => 0,
|
||||||
};
|
};
|
||||||
|
@ -56,7 +59,7 @@ impl Entry {
|
||||||
false => 0,
|
false => 0,
|
||||||
};
|
};
|
||||||
size += 1; // encrypted
|
size += 1; // encrypted
|
||||||
size += match version >= Version::CompressionEncryption {
|
size += match version.version_major() >= VersionMajor::CompressionEncryption {
|
||||||
true => 4, // blocks uncompressed
|
true => 4, // blocks uncompressed
|
||||||
false => 0,
|
false => 0,
|
||||||
};
|
};
|
||||||
|
@ -68,7 +71,7 @@ impl Entry {
|
||||||
let offset = reader.read_u64::<LE>()?;
|
let offset = reader.read_u64::<LE>()?;
|
||||||
let compressed = reader.read_u64::<LE>()?;
|
let compressed = reader.read_u64::<LE>()?;
|
||||||
let uncompressed = 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,
|
0x01 | 0x10 | 0x20 => Compression::Zlib,
|
||||||
_ => Compression::None,
|
_ => Compression::None,
|
||||||
};
|
};
|
||||||
|
@ -77,19 +80,19 @@ impl Entry {
|
||||||
compressed,
|
compressed,
|
||||||
uncompressed,
|
uncompressed,
|
||||||
compression,
|
compression,
|
||||||
timestamp: match version == Version::Initial {
|
timestamp: match version.version_major() == VersionMajor::Initial {
|
||||||
true => Some(reader.read_u64::<LE>()?),
|
true => Some(reader.read_u64::<LE>()?),
|
||||||
false => None,
|
false => None,
|
||||||
},
|
},
|
||||||
hash: Some(reader.read_guid()?),
|
hash: Some(reader.read_guid()?),
|
||||||
blocks: match version >= Version::CompressionEncryption
|
blocks: match version.version_major() >= VersionMajor::CompressionEncryption
|
||||||
&& compression != Compression::None
|
&& compression != Compression::None
|
||||||
{
|
{
|
||||||
true => Some(reader.read_array(Block::new)?),
|
true => Some(reader.read_array(Block::new)?),
|
||||||
false => None,
|
false => None,
|
||||||
},
|
},
|
||||||
encrypted: version >= Version::CompressionEncryption && reader.read_bool()?,
|
encrypted: version.version_major() >= VersionMajor::CompressionEncryption && reader.read_bool()?,
|
||||||
block_uncompressed: match version >= Version::CompressionEncryption {
|
block_uncompressed: match version.version_major() >= VersionMajor::CompressionEncryption {
|
||||||
true => Some(reader.read_u32::<LE>()?),
|
true => Some(reader.read_u32::<LE>()?),
|
||||||
false => None,
|
false => None,
|
||||||
},
|
},
|
||||||
|
@ -140,7 +143,7 @@ impl Entry {
|
||||||
};
|
};
|
||||||
|
|
||||||
let offset_base =
|
let offset_base =
|
||||||
match version >= super::Version::RelativeChunkOffsets {
|
match version.version_major() >= VersionMajor::RelativeChunkOffsets {
|
||||||
true => 0,
|
true => 0,
|
||||||
false => offset,
|
false => offset,
|
||||||
} + Entry::get_serialized_size(version, compression, compression_block_count);
|
} + 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>(
|
pub fn read<R: io::Read + io::Seek, W: io::Write>(
|
||||||
&self,
|
&self,
|
||||||
reader: &mut R,
|
reader: &mut R,
|
||||||
version: super::Version,
|
version: Version,
|
||||||
key: Option<&aes::Aes256Dec>,
|
key: Option<&aes::Aes256Dec>,
|
||||||
buf: &mut W,
|
buf: &mut W,
|
||||||
) -> Result<(), super::Error> {
|
) -> Result<(), super::Error> {
|
||||||
|
@ -217,7 +220,7 @@ impl Entry {
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
io::copy(
|
io::copy(
|
||||||
&mut <$decompressor>::new(
|
&mut <$decompressor>::new(
|
||||||
&data[match version >= Version::RelativeChunkOffsets {
|
&data[match version.version_major() >= VersionMajor::RelativeChunkOffsets {
|
||||||
true => {
|
true => {
|
||||||
(block.start - (data_offset - self.offset)) as usize
|
(block.start - (data_offset - self.offset)) as usize
|
||||||
..(block.end - (data_offset - self.offset)) as usize
|
..(block.end - (data_offset - self.offset)) as usize
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{ext::ReadExt, Compression, Version};
|
use super::{ext::ReadExt, Compression, Version, VersionMajor};
|
||||||
use byteorder::{ReadBytesExt, LE};
|
use byteorder::{ReadBytesExt, LE};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ pub struct Footer {
|
||||||
pub encrypted: bool,
|
pub encrypted: bool,
|
||||||
pub magic: u32,
|
pub magic: u32,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
|
pub version_major: VersionMajor,
|
||||||
pub index_offset: u64,
|
pub index_offset: u64,
|
||||||
pub index_size: u64,
|
pub index_size: u64,
|
||||||
pub hash: [u8; 20],
|
pub hash: [u8; 20],
|
||||||
|
@ -18,21 +19,22 @@ pub struct Footer {
|
||||||
impl Footer {
|
impl Footer {
|
||||||
pub fn new<R: std::io::Read>(reader: &mut R, version: Version) -> Result<Self, super::Error> {
|
pub fn new<R: std::io::Read>(reader: &mut R, version: Version) -> Result<Self, super::Error> {
|
||||||
let footer = Self {
|
let footer = Self {
|
||||||
encryption_uuid: match version >= Version::EncryptionKeyGuid {
|
encryption_uuid: match version.version_major() >= VersionMajor::EncryptionKeyGuid {
|
||||||
true => Some(reader.read_u128::<LE>()?),
|
true => Some(reader.read_u128::<LE>()?),
|
||||||
false => None,
|
false => None,
|
||||||
},
|
},
|
||||||
encrypted: version >= Version::IndexEncryption && reader.read_bool()?,
|
encrypted: version.version_major() >= VersionMajor::IndexEncryption && reader.read_bool()?,
|
||||||
magic: reader.read_u32::<LE>()?,
|
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_offset: reader.read_u64::<LE>()?,
|
||||||
index_size: reader.read_u64::<LE>()?,
|
index_size: reader.read_u64::<LE>()?,
|
||||||
hash: reader.read_guid()?,
|
hash: reader.read_guid()?,
|
||||||
frozen: version == Version::FrozenIndex && reader.read_bool()?,
|
frozen: version.version_major() == VersionMajor::FrozenIndex && reader.read_bool()?,
|
||||||
compression: {
|
compression: {
|
||||||
let mut compression = Vec::with_capacity(match version {
|
let mut compression = Vec::with_capacity(match version {
|
||||||
ver if ver < Version::FNameBasedCompression => 0,
|
ver if ver < Version::V8A => 0,
|
||||||
Version::FNameBasedCompression => 4,
|
ver if ver < Version::V8B => 4,
|
||||||
_ => 5,
|
_ => 5,
|
||||||
});
|
});
|
||||||
for _ in 0..compression.capacity() {
|
for _ in 0..compression.capacity() {
|
||||||
|
@ -54,7 +56,7 @@ impl Footer {
|
||||||
if super::MAGIC != footer.magic {
|
if super::MAGIC != footer.magic {
|
||||||
return Err(super::Error::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 {
|
return Err(super::Error::Version {
|
||||||
used: version,
|
used: version,
|
||||||
version: footer.version,
|
version: footer.version,
|
||||||
|
|
56
src/lib.rs
56
src/lib.rs
|
@ -9,12 +9,31 @@ pub use {error::*, pak::*};
|
||||||
|
|
||||||
pub const MAGIC: u32 = 0x5A6F12E1;
|
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)]
|
#[repr(u32)]
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Copy, PartialEq, Eq, PartialOrd, Debug, strum::Display, strum::FromRepr, strum::EnumIter,
|
Clone, Copy, PartialEq, Eq, PartialOrd, Debug, strum::Display, strum::FromRepr, strum::EnumIter,
|
||||||
)]
|
)]
|
||||||
|
/// Version actually written to the pak file
|
||||||
pub enum Version {
|
pub enum VersionMajor {
|
||||||
Unknown, // v0 unknown (mostly just for padding)
|
Unknown, // v0 unknown (mostly just for padding)
|
||||||
Initial, // v1 initial specification
|
Initial, // v1 initial specification
|
||||||
NoTimestamps, // v2 timestamps removed
|
NoTimestamps, // v2 timestamps removed
|
||||||
|
@ -38,24 +57,47 @@ impl Version {
|
||||||
pub fn size(self) -> i64 {
|
pub fn size(self) -> i64 {
|
||||||
// (magic + version): u32 + (offset + size): u64 + hash: [u8; 20]
|
// (magic + version): u32 + (offset + size): u64 + hash: [u8; 20]
|
||||||
let mut size = 4 + 4 + 8 + 8 + 20;
|
let mut size = 4 + 4 + 8 + 8 + 20;
|
||||||
if self >= Version::EncryptionKeyGuid {
|
if self.version_major() >= VersionMajor::EncryptionKeyGuid {
|
||||||
// encryption uuid: u128
|
// encryption uuid: u128
|
||||||
size += 16;
|
size += 16;
|
||||||
}
|
}
|
||||||
if self >= Version::IndexEncryption {
|
if self.version_major() >= VersionMajor::IndexEncryption {
|
||||||
// encrypted: bool
|
// encrypted: bool
|
||||||
size += 1;
|
size += 1;
|
||||||
}
|
}
|
||||||
if self == Version::FrozenIndex {
|
if self.version_major() == VersionMajor::FrozenIndex {
|
||||||
// frozen index: bool
|
// frozen index: bool
|
||||||
size += 1;
|
size += 1;
|
||||||
}
|
}
|
||||||
if self >= Version::FNameBasedCompression {
|
if self >= Version::V8A {
|
||||||
// compression names: [[u8; 32]; 5]
|
// compression names: [[u8; 32]; 5]
|
||||||
size += 32 * 5;
|
size += 32 * 4;
|
||||||
|
}
|
||||||
|
if self >= Version::V8B {
|
||||||
|
// compression names: [[u8; 32]; 5]
|
||||||
|
size += 32 * 1;
|
||||||
}
|
}
|
||||||
size
|
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)]
|
#[derive(Default, Clone, Copy, PartialEq, Eq, Debug, strum::Display, strum::EnumString)]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::Version;
|
use super::{Version, VersionMajor};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use std::io::{self, Seek};
|
use std::io::{self, Seek};
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ impl<R: io::Read + io::Seek> PakReader<R> {
|
||||||
let mount_point = index.read_string()?;
|
let mount_point = index.read_string()?;
|
||||||
let len = index.read_u32::<LE>()? as usize;
|
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_seed = index.read_u64::<LE>()?;
|
||||||
|
|
||||||
let path_hash_index = if index.read_u32::<LE>()? != 0 {
|
let path_hash_index = if index.read_u32::<LE>()? != 0 {
|
||||||
|
|
|
@ -65,12 +65,12 @@ macro_rules! encryptindex {
|
||||||
|
|
||||||
matrix_test!(
|
matrix_test!(
|
||||||
(
|
(
|
||||||
"v5" unpak::Version::RelativeChunkOffsets,
|
"v5" unpak::Version::V5,
|
||||||
"v7" unpak::Version::EncryptionKeyGuid,
|
"v7" unpak::Version::V7,
|
||||||
"v8a" unpak::Version::FNameBasedCompression,
|
"v8a" unpak::Version::V8A,
|
||||||
"v8b" unpak::Version::FNameBasedCompression,
|
"v8b" unpak::Version::V8B,
|
||||||
"v9" unpak::Version::FrozenIndex,
|
"v9" unpak::Version::V9,
|
||||||
"v11" unpak::Version::Fnv64BugFix,
|
"v11" unpak::Version::V11,
|
||||||
),
|
),
|
||||||
("", "_compress"),
|
("", "_compress"),
|
||||||
("", "_encrypt"),
|
("", "_encrypt"),
|
||||||
|
|
Loading…
Reference in a new issue