mirror of
https://github.com/xavo95/repak.git
synced 2025-01-18 19:04:07 +00:00
Improved compression and flags handling
This commit is contained in:
parent
829acdfeeb
commit
8d46fde79e
3 changed files with 56 additions and 40 deletions
|
@ -1,4 +1,4 @@
|
||||||
use super::{ext::ReadExt, ext::WriteExt, Compression, Version, VersionMajor};
|
use super::{ext::ReadExt, Compression, Version, VersionMajor};
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
|
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
@ -39,18 +39,24 @@ pub struct Entry {
|
||||||
pub offset: u64,
|
pub offset: u64,
|
||||||
pub compressed: u64,
|
pub compressed: u64,
|
||||||
pub uncompressed: u64,
|
pub uncompressed: u64,
|
||||||
pub compression: Compression,
|
pub compression: Option<u32>,
|
||||||
pub timestamp: Option<u64>,
|
pub timestamp: Option<u64>,
|
||||||
pub hash: Option<[u8; 20]>,
|
pub hash: Option<[u8; 20]>,
|
||||||
pub blocks: Option<Vec<Block>>,
|
pub blocks: Option<Vec<Block>>,
|
||||||
pub encrypted: bool,
|
pub flags: u8,
|
||||||
pub compression_block_size: u32,
|
pub compression_block_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entry {
|
impl Entry {
|
||||||
|
pub fn is_encrypted(&self) -> bool {
|
||||||
|
0 != (self.flags & 1)
|
||||||
|
}
|
||||||
|
pub fn is_deleted(&self) -> bool {
|
||||||
|
0 != (self.flags >> 1) & 1
|
||||||
|
}
|
||||||
pub fn get_serialized_size(
|
pub fn get_serialized_size(
|
||||||
version: super::Version,
|
version: super::Version,
|
||||||
compression: Compression,
|
compression: Option<u32>,
|
||||||
block_count: u32,
|
block_count: u32,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
|
@ -66,9 +72,9 @@ impl Entry {
|
||||||
false => 0,
|
false => 0,
|
||||||
};
|
};
|
||||||
size += 20; // hash
|
size += 20; // hash
|
||||||
size += match compression != Compression::None {
|
size += match compression {
|
||||||
true => 4 + (8 + 8) * block_count as u64, // blocks
|
Some(_) => 4 + (8 + 8) * block_count as u64, // blocks
|
||||||
false => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
size += 1; // encrypted
|
size += 1; // encrypted
|
||||||
size += match version.version_major() >= VersionMajor::CompressionEncryption {
|
size += match version.version_major() >= VersionMajor::CompressionEncryption {
|
||||||
|
@ -91,8 +97,8 @@ impl Entry {
|
||||||
} else {
|
} else {
|
||||||
reader.read_u32::<LE>()?
|
reader.read_u32::<LE>()?
|
||||||
} {
|
} {
|
||||||
0x01 | 0x10 | 0x20 => Compression::Zlib,
|
0 => None,
|
||||||
_ => Compression::None,
|
n => Some(n - 1),
|
||||||
};
|
};
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
offset,
|
offset,
|
||||||
|
@ -105,13 +111,15 @@ impl Entry {
|
||||||
},
|
},
|
||||||
hash: Some(reader.read_guid()?),
|
hash: Some(reader.read_guid()?),
|
||||||
blocks: match version.version_major() >= VersionMajor::CompressionEncryption
|
blocks: match version.version_major() >= VersionMajor::CompressionEncryption
|
||||||
&& compression != Compression::None
|
&& compression.is_some()
|
||||||
{
|
{
|
||||||
true => Some(reader.read_array(Block::read)?),
|
true => Some(reader.read_array(Block::read)?),
|
||||||
false => None,
|
false => None,
|
||||||
},
|
},
|
||||||
encrypted: version.version_major() >= VersionMajor::CompressionEncryption
|
flags: match version.version_major() >= VersionMajor::CompressionEncryption {
|
||||||
&& reader.read_bool()?,
|
true => reader.read_u8()?,
|
||||||
|
false => 0,
|
||||||
|
},
|
||||||
compression_block_size: match version.version_major()
|
compression_block_size: match version.version_major()
|
||||||
>= VersionMajor::CompressionEncryption
|
>= VersionMajor::CompressionEncryption
|
||||||
{
|
{
|
||||||
|
@ -132,7 +140,7 @@ impl Entry {
|
||||||
if (compression_block_size << 11) != self.compression_block_size {
|
if (compression_block_size << 11) != self.compression_block_size {
|
||||||
compression_block_size = 0x3f;
|
compression_block_size = 0x3f;
|
||||||
}
|
}
|
||||||
let compression_blocks_count = if self.compression != Compression::None {
|
let compression_blocks_count = if self.compression.is_some() {
|
||||||
self.blocks.as_ref().unwrap().len() as u32
|
self.blocks.as_ref().unwrap().len() as u32
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
@ -143,8 +151,8 @@ impl Entry {
|
||||||
|
|
||||||
let flags = (compression_block_size)
|
let flags = (compression_block_size)
|
||||||
| (compression_blocks_count << 6)
|
| (compression_blocks_count << 6)
|
||||||
| ((self.encrypted as u32) << 22)
|
| ((self.is_encrypted() as u32) << 22)
|
||||||
| ((self.compression as u32) << 23)
|
| (self.compression.map_or(0, |n| n + 1) << 23)
|
||||||
| ((is_size_32_bit_safe as u32) << 29)
|
| ((is_size_32_bit_safe as u32) << 29)
|
||||||
| ((is_uncompressed_size_32_bit_safe as u32) << 30)
|
| ((is_uncompressed_size_32_bit_safe as u32) << 30)
|
||||||
| ((is_offset_32_bit_safe as u32) << 31);
|
| ((is_offset_32_bit_safe as u32) << 31);
|
||||||
|
@ -167,7 +175,7 @@ impl Entry {
|
||||||
writer.write_u64::<LE>(self.uncompressed)?
|
writer.write_u64::<LE>(self.uncompressed)?
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.compression != Compression::None {
|
if self.compression.is_some() {
|
||||||
if is_size_32_bit_safe {
|
if is_size_32_bit_safe {
|
||||||
writer.write_u32::<LE>(self.compressed as u32)?;
|
writer.write_u32::<LE>(self.compressed as u32)?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -176,7 +184,7 @@ impl Entry {
|
||||||
|
|
||||||
assert!(self.blocks.is_some());
|
assert!(self.blocks.is_some());
|
||||||
let blocks = self.blocks.as_ref().unwrap();
|
let blocks = self.blocks.as_ref().unwrap();
|
||||||
if blocks.len() > 1 || self.encrypted {
|
if blocks.len() > 1 || self.is_encrypted() {
|
||||||
for b in blocks {
|
for b in blocks {
|
||||||
let block_size = b.end - b.start;
|
let block_size = b.end - b.start;
|
||||||
writer.write_u32::<LE>(block_size.try_into().unwrap())?;
|
writer.write_u32::<LE>(block_size.try_into().unwrap())?;
|
||||||
|
@ -192,15 +200,10 @@ impl Entry {
|
||||||
})?;
|
})?;
|
||||||
writer.write_u64::<LE>(self.compressed)?;
|
writer.write_u64::<LE>(self.compressed)?;
|
||||||
writer.write_u64::<LE>(self.uncompressed)?;
|
writer.write_u64::<LE>(self.uncompressed)?;
|
||||||
let compression: u8 = match self.compression {
|
let compression = self.compression.map_or(0, |n| n + 1);
|
||||||
Compression::None => 0,
|
|
||||||
Compression::Zlib => 1,
|
|
||||||
Compression::Gzip => todo!(),
|
|
||||||
Compression::Oodle => todo!(),
|
|
||||||
};
|
|
||||||
match version {
|
match version {
|
||||||
Version::V8A => writer.write_u8(compression)?,
|
Version::V8A => writer.write_u8(compression.try_into().unwrap())?,
|
||||||
_ => writer.write_u32::<LE>(compression.into())?,
|
_ => writer.write_u32::<LE>(compression)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
if version.version_major() == VersionMajor::Initial {
|
if version.version_major() == VersionMajor::Initial {
|
||||||
|
@ -218,7 +221,7 @@ impl Entry {
|
||||||
block.write(writer)?;
|
block.write(writer)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writer.write_bool(self.encrypted)?;
|
writer.write_u8(self.flags)?;
|
||||||
writer.write_u32::<LE>(self.compression_block_size)?;
|
writer.write_u32::<LE>(self.compression_block_size)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,8 +235,8 @@ impl Entry {
|
||||||
) -> Result<Self, super::Error> {
|
) -> Result<Self, super::Error> {
|
||||||
let bits = reader.read_u32::<LE>()?;
|
let bits = reader.read_u32::<LE>()?;
|
||||||
let compression = match (bits >> 23) & 0x3f {
|
let compression = match (bits >> 23) & 0x3f {
|
||||||
0x01 | 0x10 | 0x20 => Compression::Zlib,
|
0 => None,
|
||||||
_ => Compression::None,
|
n => Some(n - 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
let encrypted = (bits & (1 << 22)) != 0;
|
let encrypted = (bits & (1 << 22)) != 0;
|
||||||
|
@ -257,7 +260,7 @@ impl Entry {
|
||||||
let offset = var_int(31)?;
|
let offset = var_int(31)?;
|
||||||
let uncompressed = var_int(30)?;
|
let uncompressed = var_int(30)?;
|
||||||
let compressed = match compression {
|
let compressed = match compression {
|
||||||
Compression::None => uncompressed,
|
None => uncompressed,
|
||||||
_ => var_int(29)?,
|
_ => var_int(29)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -302,7 +305,7 @@ impl Entry {
|
||||||
compression,
|
compression,
|
||||||
hash: None,
|
hash: None,
|
||||||
blocks,
|
blocks,
|
||||||
encrypted,
|
flags: encrypted as u8,
|
||||||
compression_block_size,
|
compression_block_size,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -311,17 +314,18 @@ impl Entry {
|
||||||
&self,
|
&self,
|
||||||
reader: &mut R,
|
reader: &mut R,
|
||||||
version: Version,
|
version: Version,
|
||||||
|
compression: &[Compression],
|
||||||
key: Option<&aes::Aes256>,
|
key: Option<&aes::Aes256>,
|
||||||
buf: &mut W,
|
buf: &mut W,
|
||||||
) -> Result<(), super::Error> {
|
) -> Result<(), super::Error> {
|
||||||
reader.seek(io::SeekFrom::Start(self.offset))?;
|
reader.seek(io::SeekFrom::Start(self.offset))?;
|
||||||
Entry::read(reader, version)?;
|
Entry::read(reader, version)?;
|
||||||
let data_offset = reader.stream_position()?;
|
let data_offset = reader.stream_position()?;
|
||||||
let mut data = reader.read_len(match self.encrypted {
|
let mut data = reader.read_len(match self.is_encrypted() {
|
||||||
true => align(self.compressed),
|
true => align(self.compressed),
|
||||||
false => self.compressed,
|
false => self.compressed,
|
||||||
} as usize)?;
|
} as usize)?;
|
||||||
if self.encrypted {
|
if self.is_encrypted() {
|
||||||
let Some(key) = key else {
|
let Some(key) = key else {
|
||||||
return Err(super::Error::Encrypted);
|
return Err(super::Error::Encrypted);
|
||||||
};
|
};
|
||||||
|
@ -361,11 +365,12 @@ impl Entry {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
match self.compression {
|
match self.compression.map(|c| compression[c as usize]) {
|
||||||
Compression::None => buf.write_all(&data)?,
|
None => buf.write_all(&data)?,
|
||||||
Compression::Zlib => decompress!(flate2::read::ZlibDecoder<&[u8]>),
|
Some(Compression::Zlib) => decompress!(flate2::read::ZlibDecoder<&[u8]>),
|
||||||
Compression::Gzip => decompress!(flate2::read::GzDecoder<&[u8]>),
|
Some(Compression::Gzip) => decompress!(flate2::read::GzDecoder<&[u8]>),
|
||||||
Compression::Oodle => todo!(),
|
Some(Compression::Oodle) => todo!("Oodle compression"),
|
||||||
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
buf.flush()?;
|
buf.flush()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -54,6 +54,11 @@ impl Footer {
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if version < Version::V8A {
|
||||||
|
compression.push(Compression::Zlib);
|
||||||
|
compression.push(Compression::Gzip);
|
||||||
|
compression.push(Compression::Oodle);
|
||||||
|
}
|
||||||
compression
|
compression
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -115,7 +115,13 @@ impl PakReader {
|
||||||
writer: &mut W,
|
writer: &mut W,
|
||||||
) -> Result<(), super::Error> {
|
) -> Result<(), super::Error> {
|
||||||
match self.pak.index.entries().get(path) {
|
match self.pak.index.entries().get(path) {
|
||||||
Some(entry) => entry.read_file(reader, self.pak.version, self.key.as_ref(), writer),
|
Some(entry) => entry.read_file(
|
||||||
|
reader,
|
||||||
|
self.pak.version,
|
||||||
|
&self.pak.compression,
|
||||||
|
self.key.as_ref(),
|
||||||
|
writer,
|
||||||
|
),
|
||||||
None => Err(super::Error::Other("no file found at given path")),
|
None => Err(super::Error::Other("no file found at given path")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,11 +177,11 @@ impl<W: Write + Seek> PakWriter<W> {
|
||||||
offset,
|
offset,
|
||||||
compressed: len,
|
compressed: len,
|
||||||
uncompressed: len,
|
uncompressed: len,
|
||||||
compression: super::Compression::None,
|
compression: None,
|
||||||
timestamp: None,
|
timestamp: None,
|
||||||
hash: Some(hasher.finalize().into()),
|
hash: Some(hasher.finalize().into()),
|
||||||
blocks: None,
|
blocks: None,
|
||||||
encrypted: false,
|
flags: 0,
|
||||||
compression_block_size: 0,
|
compression_block_size: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue