repak/src/entry.rs

130 lines
4.6 KiB
Rust
Raw Normal View History

2023-01-14 18:32:06 +00:00
use super::{ext::ReadExt, Compression, Version};
use byteorder::{ReadBytesExt, LE};
2023-01-11 13:52:40 +00:00
use std::io;
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
})
}
}
#[derive(Debug)]
pub struct Entry {
pub offset: u64,
pub compressed: u64,
pub uncompressed: u64,
pub compression: Compression,
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>,
}
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
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>()? {
2023-01-10 22:29:20 +00:00
0x01 | 0x10 | 0x20 => Compression::Zlib,
_ => Compression::None,
};
Ok(Self {
offset,
compressed,
uncompressed,
compression,
2023-01-10 22:29:20 +00:00
timestamp: match version == Version::Initial {
true => Some(reader.read_u64::<LE>()?),
false => None,
},
hash: reader.read_guid()?,
2023-01-13 18:12:47 +00:00
blocks: match version >= Version::CompressionEncryption
&& 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-15 16:26:23 +00:00
pub fn read<R: io::Read + io::Seek, W: io::Write>(
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-15 16:26:23 +00:00
buf: &mut W,
) -> Result<(), super::Error> {
2023-01-13 15:23:51 +00:00
reader.seek(io::SeekFrom::Start(self.offset))?;
Entry::new(reader, version)?;
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
2023-01-20 03:13:03 +00:00
true => (self.compressed + 15) & !15,
2023-01-13 15:23:51 +00:00
false => self.compressed,
} as usize)?;
if self.encrypted {
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
}
data.truncate(self.compressed as usize);
2023-01-13 15:23:51 +00:00
}
2023-01-14 15:17:24 +00:00
macro_rules! decompress {
($decompressor: ty) => {
match &self.blocks {
Some(blocks) => {
for block in blocks {
io::copy(
&mut <$decompressor>::new(
&data[match version >= Version::RelativeChunkOffsets {
true => {
(block.start - (data_offset - self.offset)) as usize
..(block.end - (data_offset - self.offset)) as usize
}
2023-01-14 15:17:24 +00:00
false => {
(block.start - data_offset) as usize
..(block.end - data_offset) as usize
}
}],
),
2023-01-15 16:26:23 +00:00
buf,
2023-01-14 15:17:24 +00:00
)?;
}
}
None => {
2023-01-15 16:26:23 +00:00
io::copy(&mut flate2::read::ZlibDecoder::new(data.as_slice()), buf)?;
2023-01-13 18:12:47 +00:00
}
}
2023-01-14 15:17:24 +00:00
};
}
match self.compression {
Compression::None => buf.write_all(&data)?,
Compression::Zlib => decompress!(flate2::read::ZlibDecoder<&[u8]>),
Compression::Gzip => decompress!(flate2::read::GzDecoder<&[u8]>),
2023-01-13 15:23:51 +00:00
Compression::Oodle => todo!(),
}
buf.flush()?;
2023-01-15 16:26:23 +00:00
Ok(())
}
}