1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
pub mod archive;
mod pakbuilder;
mod pakentry;
mod pakfile;
mod pakindex;
mod pakindexv1;
mod pakindexv2;
mod pakinfo;
use std::{fmt, io};
use aes::cipher::generic_array::GenericArray;
use aes::{Aes256, BlockCipher, NewBlockCipher};
use block_modes::block_padding::NoPadding;
use block_modes::{BlockMode, Ecb};
pub use pakbuilder::{AssetWriter, PakFileBuilder};
pub use pakentry::{PakCompressedBlock, PakEntry};
pub use pakfile::PakFile;
pub use pakindex::PakIndex;
pub use pakindexv1::PakIndexV1;
pub use pakindexv2::{PakEntryLocation, PakIndexV2};
pub use pakinfo::PakInfo;
use sha1::digest::generic_array::typenum::Unsigned;
type Aes256KeySize = <Aes256 as NewBlockCipher>::KeySize;
type Aes256BlockSize = <Aes256 as BlockCipher>::BlockSize;
type Aes256Key = GenericArray<u8, Aes256KeySize>;
type Aes256Cipher = Ecb<Aes256, NoPadding>;
fn aes256_base64_key(key: &str) -> io::Result<Aes256Key> {
let key = base64::decode(key).map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
if key.len() != Aes256KeySize::USIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"invalid base64 key size, found a {} bytes key, expecting a {} bytes key",
key.len(),
Aes256KeySize::USIZE
),
));
}
Ok(*Aes256Key::from_slice(&key))
}
fn aes256_ecb_cipher(key: &Aes256Key) -> Aes256Cipher {
Ecb::<Aes256, NoPadding>::new_fix(key, &Default::default())
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum PakVersion {
Initial,
NoTimestamps,
CompressionEncryption,
IndexEncryption,
RelativeChunkOffsets,
DeleteRecords,
EncryptionKeyGuid,
FNameBasedCompressionMethod422,
FNameBasedCompressionMethod,
FrozenIndex,
PathHashIndex,
Fnv64BugFix,
}
impl fmt::Display for PakVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{}",
self.raw(),
if *self == PakVersion::FNameBasedCompressionMethod422 {
" with 4.22 workaround"
} else {
""
}
)
}
}
impl PakVersion {
pub fn list() -> &'static [Self] {
&[
PakVersion::Initial,
PakVersion::NoTimestamps,
PakVersion::CompressionEncryption,
PakVersion::IndexEncryption,
PakVersion::RelativeChunkOffsets,
PakVersion::DeleteRecords,
PakVersion::EncryptionKeyGuid,
PakVersion::FNameBasedCompressionMethod422,
PakVersion::FNameBasedCompressionMethod,
PakVersion::FrozenIndex,
PakVersion::PathHashIndex,
PakVersion::Fnv64BugFix,
]
}
pub fn raw(self) -> i32 {
match self {
PakVersion::Initial => 1,
PakVersion::NoTimestamps => 2,
PakVersion::CompressionEncryption => 3,
PakVersion::IndexEncryption => 4,
PakVersion::RelativeChunkOffsets => 5,
PakVersion::DeleteRecords => 6,
PakVersion::EncryptionKeyGuid => 7,
PakVersion::FNameBasedCompressionMethod422 => 8,
PakVersion::FNameBasedCompressionMethod => 8,
PakVersion::FrozenIndex => 9,
PakVersion::PathHashIndex => 10,
PakVersion::Fnv64BugFix => 11,
}
}
}
pub mod constants {
pub const PAK_FILE_MAGIC: u32 = 0x5A6F12E1;
pub const MAX_CHUNK_DATA_SIZE: usize = 64 * 1024;
pub const COMPRESSION_METHOD_NAME_LEN: usize = 32;
pub const MAX_NUM_COMPRESSION_METHODS: usize = 5; pub const COMPRESS_NONE: i32 = 0x00;
pub const COMPRESS_ZLIB: i32 = 0x01;
pub const COMPRESS_GZIP: i32 = 0x02;
pub const COMPRESS_CUSTOM: i32 = 0x04;
pub const COMPRESS_DEPRECATED_FORMAT_FLAGS_MASK: i32 = 0xF;
pub const COMPRESS_NO_FLAGS: i32 = 0x00;
pub const COMPRESS_BIAS_MEMORY: i32 = 0x10;
pub const COMPRESS_BIAS_SPEED: i32 = 0x20;
pub const COMPRESS_SOURCE_IS_PADDED: i32 = 0x80;
pub const COMPRESS_OPTIONS_FLAGS_MASK: i32 = 0xF0;
}