2023-01-13 15:23:51 +00:00
|
|
|
use super::{Compression, ReadExt, Version};
|
2023-01-03 14:20:35 +00:00
|
|
|
use byteorder::{ReadBytesExt, LE};
|
2023-01-11 13:52:40 +00:00
|
|
|
use std::io;
|
2023-01-03 14:20:35 +00:00
|
|
|
|
2023-01-11 13:52:40 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Block {
|
2023-01-13 18:12:47 +00:00
|
|
|
pub start: u64,
|
|
|
|
pub end: u64,
|
2023-01-11 13:52:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Block {
|
|
|
|
pub fn new<R: io::Read>(reader: &mut R) -> Result<Self, super::Error> {
|
|
|
|
Ok(Self {
|
2023-01-13 18:12:47 +00:00
|
|
|
start: reader.read_u64::<LE>()?,
|
|
|
|
end: reader.read_u64::<LE>()?,
|
2023-01-11 13:52:40 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-06 19:54:48 +00:00
|
|
|
#[derive(Debug)]
|
2023-01-03 14:20:35 +00:00
|
|
|
pub struct Entry {
|
|
|
|
pub offset: u64,
|
|
|
|
pub compressed: u64,
|
|
|
|
pub uncompressed: u64,
|
2023-01-11 07:54:09 +00:00
|
|
|
pub compression: Compression,
|
2023-01-03 14:20:35 +00:00
|
|
|
pub timestamp: Option<u64>,
|
|
|
|
pub hash: [u8; 20],
|
2023-01-13 18:12:47 +00:00
|
|
|
pub blocks: Option<Vec<Block>>,
|
2023-01-10 22:29:20 +00:00
|
|
|
pub encrypted: bool,
|
|
|
|
pub block_uncompressed: Option<u32>,
|
2023-01-03 14:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Entry {
|
2023-01-11 13:52:40 +00:00
|
|
|
pub fn new<R: io::Read>(reader: &mut R, version: super::Version) -> Result<Self, super::Error> {
|
2023-01-13 12:38:30 +00:00
|
|
|
// since i need the compression flags, i have to store these as variables which is mildly annoying
|
2023-01-03 14:20:35 +00:00
|
|
|
let offset = reader.read_u64::<LE>()?;
|
|
|
|
let compressed = reader.read_u64::<LE>()?;
|
|
|
|
let uncompressed = reader.read_u64::<LE>()?;
|
2023-01-11 07:54:09 +00:00
|
|
|
let compression = match reader.read_u32::<LE>()? {
|
2023-01-10 22:29:20 +00:00
|
|
|
0x01 | 0x10 | 0x20 => Compression::Zlib,
|
2023-01-03 14:20:35 +00:00
|
|
|
_ => Compression::None,
|
|
|
|
};
|
|
|
|
Ok(Self {
|
|
|
|
offset,
|
|
|
|
compressed,
|
|
|
|
uncompressed,
|
2023-01-11 07:54:09 +00:00
|
|
|
compression,
|
2023-01-10 22:29:20 +00:00
|
|
|
timestamp: match version == Version::Initial {
|
|
|
|
true => Some(reader.read_u64::<LE>()?),
|
|
|
|
false => None,
|
|
|
|
},
|
2023-01-03 14:20:35 +00:00
|
|
|
hash: reader.read_guid()?,
|
2023-01-13 18:12:47 +00:00
|
|
|
blocks: match version >= Version::CompressionEncryption
|
2023-01-11 07:54:09 +00:00
|
|
|
&& compression != Compression::None
|
2023-01-10 22:29:20 +00:00
|
|
|
{
|
|
|
|
true => Some(reader.read_array(Block::new)?),
|
|
|
|
false => None,
|
|
|
|
},
|
|
|
|
encrypted: version >= Version::CompressionEncryption && reader.read_bool()?,
|
|
|
|
block_uncompressed: match version >= Version::CompressionEncryption {
|
|
|
|
true => Some(reader.read_u32::<LE>()?),
|
|
|
|
false => None,
|
|
|
|
},
|
2023-01-03 14:20:35 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-01-11 13:52:40 +00:00
|
|
|
pub fn read<R: io::Read + io::Seek>(
|
2023-01-13 12:38:30 +00:00
|
|
|
&self,
|
2023-01-11 13:52:40 +00:00
|
|
|
reader: &mut R,
|
|
|
|
version: super::Version,
|
2023-01-13 12:38:30 +00:00
|
|
|
key: Option<&aes::Aes256Dec>,
|
2023-01-11 13:52:40 +00:00
|
|
|
) -> Result<Vec<u8>, super::Error> {
|
2023-01-13 15:23:51 +00:00
|
|
|
let mut buf = io::BufWriter::new(Vec::with_capacity(self.uncompressed as usize));
|
|
|
|
reader.seek(io::SeekFrom::Start(self.offset))?;
|
2023-01-14 14:20:00 +00:00
|
|
|
Entry::new(reader, version)?;
|
2023-01-14 14:41:57 +00:00
|
|
|
let data_offset = reader.stream_position()?;
|
2023-01-13 15:23:51 +00:00
|
|
|
let mut data = reader.read_len(match self.encrypted {
|
|
|
|
// add alignment (aes block size: 16) then zero out alignment bits
|
|
|
|
true => (self.compressed + 15) & !17,
|
|
|
|
false => self.compressed,
|
|
|
|
} as usize)?;
|
|
|
|
if self.encrypted {
|
2023-01-14 14:20:00 +00:00
|
|
|
let Some(key) = key else {
|
|
|
|
return Err(super::Error::Encrypted);
|
|
|
|
};
|
|
|
|
use aes::cipher::BlockDecrypt;
|
|
|
|
for block in data.chunks_mut(16) {
|
|
|
|
key.decrypt_block(aes::Block::from_mut_slice(block))
|
2023-01-13 15:23:51 +00:00
|
|
|
}
|
2023-01-14 14:20:00 +00:00
|
|
|
data.truncate(self.compressed as usize);
|
2023-01-13 15:23:51 +00:00
|
|
|
}
|
|
|
|
use io::Write;
|
|
|
|
match self.compression {
|
|
|
|
Compression::None => buf.write_all(&data)?,
|
2023-01-13 18:12:47 +00:00
|
|
|
Compression::Zlib => {
|
|
|
|
let mut decoder = flate2::write::ZlibDecoder::new(buf);
|
|
|
|
match &self.blocks {
|
|
|
|
Some(blocks) => {
|
|
|
|
for block in blocks {
|
|
|
|
decoder.write(
|
|
|
|
&data[match version >= Version::RelativeChunkOffsets {
|
2023-01-14 14:41:57 +00:00
|
|
|
true => block.start as usize..block.end as usize,
|
|
|
|
false => {
|
|
|
|
(block.start - data_offset) as usize
|
|
|
|
..(block.end - data_offset) as usize
|
2023-01-13 18:12:47 +00:00
|
|
|
}
|
|
|
|
}],
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => decoder.write_all(&data)?,
|
|
|
|
}
|
|
|
|
buf = decoder.finish()?;
|
|
|
|
}
|
2023-01-13 15:23:51 +00:00
|
|
|
Compression::Gzip => todo!(),
|
|
|
|
Compression::Oodle => todo!(),
|
|
|
|
}
|
|
|
|
buf.flush()?;
|
2023-01-11 13:52:40 +00:00
|
|
|
Ok(buf.into_inner()?)
|
2023-01-03 14:20:35 +00:00
|
|
|
}
|
|
|
|
}
|