implementation of v1 complete (I think)

This commit is contained in:
spuds 2023-01-03 14:20:35 +00:00
parent b2188fc6f8
commit c0a1c5154c
No known key found for this signature in database
GPG key ID: 0B6CA6068E827C8F
9 changed files with 159 additions and 37 deletions

View file

@ -1,6 +1,6 @@
fn main() -> Result<(), un_pak::Error> {
for version in un_pak::Version::iter().rev() {
match un_pak::PakFile::new(version, std::io::Cursor::new(include_bytes!("rando_p.pak"))) {
match un_pak::Pak::new(version, std::io::Cursor::new(include_bytes!("rando_p.pak"))) {
Ok(_) => {
println!("parsed successfully!");
return Ok(());

60
src/entry.rs Normal file
View file

@ -0,0 +1,60 @@
use byteorder::{ReadBytesExt, LE};
use super::{Compression, ReadExt, Version};
pub struct Entry {
pub name: String,
pub offset: u64,
pub compressed: u64,
pub uncompressed: u64,
pub compression_method: Compression,
pub timestamp: Option<u64>,
pub hash: [u8; 20],
pub compression_blocks: Option<Vec<Block>>,
}
impl Entry {
pub fn new<R: std::io::Read>(
reader: &mut R,
version: &super::Version,
) -> Result<Self, super::Error> {
let name = reader.read_string()?;
let offset = reader.read_u64::<LE>()?;
let compressed = reader.read_u64::<LE>()?;
let uncompressed = reader.read_u64::<LE>()?;
let compression_method = match reader.read_u32::<LE>()? {
0x01 => Compression::Zlib,
0x10 => Compression::ZlibBiasMemory,
0x20 => Compression::ZlibBiasMemory,
_ => Compression::None,
};
Ok(Self {
name,
offset,
compressed,
uncompressed,
compression_method,
timestamp: (version == &Version::Initial).then_some(reader.read_u64::<LE>()?),
hash: reader.read_guid()?,
compression_blocks: (version >= &Version::CompressionEncryption
&& compression_method != Compression::None)
.then_some(reader.read_array(Block::new)?),
})
}
}
pub struct Block {
/// start offset relative to the start of the entry header
pub offset: u64,
/// size of the compressed block
pub size: u64,
}
impl Block {
pub fn new<R: std::io::Read>(reader: &mut R) -> Result<Self, super::Error> {
Ok(Self {
offset: reader.read_u64::<LE>()?,
size: reader.read_u64::<LE>()?,
})
}
}

View file

@ -6,4 +6,8 @@ pub enum Error {
IoError(#[from] std::io::Error),
#[error("error converting to enum: {0}")]
StrumError(#[from] strum::ParseError),
#[error("error converting to utf8: {0}")]
Utf8Error(#[from] std::string::FromUtf8Error),
#[error("error converting to utf16: {0}")]
Utf16Error(#[from] std::string::FromUtf16Error),
}

View file

@ -1,8 +1,15 @@
use byteorder::ReadBytesExt;
use byteorder::{ReadBytesExt, LE};
type R = dyn std::io::Read;
pub trait ReadExt {
fn read_bool(&mut self) -> Result<bool, super::Error>;
fn read_guid(&mut self) -> Result<[u8; 20], super::Error>;
fn read_array<T>(
&mut self,
func: impl FnMut(&mut Self) -> Result<T, super::Error>,
) -> Result<Vec<T>, super::Error>;
fn read_string(&mut self) -> Result<String, super::Error>;
fn read_len(&mut self, len: usize) -> Result<Vec<u8>, super::Error>;
}
@ -17,6 +24,32 @@ impl<R: std::io::Read> ReadExt for R {
Ok(guid)
}
fn read_array<T>(
&mut self,
mut func: impl FnMut(&mut Self) -> Result<T, super::Error>,
) -> Result<Vec<T>, super::Error> {
let mut buf = Vec::with_capacity(self.read_u32::<LE>()? as usize);
for _ in 0..buf.capacity() {
buf.push(func(self)?);
}
Ok(buf)
}
fn read_string(&mut self) -> Result<String, crate::Error> {
Ok(match self.read_i32::<LE>()? {
size if size.is_positive() => String::from_utf8(self.read_len(size as usize)?)?,
size if size.is_negative() => {
let size = 2 * -size;
let mut buf = Vec::with_capacity(size as usize / 2);
for _ in 0..buf.capacity() {
buf.push(self.read_u16::<LE>()?);
}
String::from_utf16(&buf)?
}
_ => String::new(),
})
}
fn read_len(&mut self, len: usize) -> Result<Vec<u8>, super::Error> {
let mut buf = Vec::with_capacity(len);
self.read_exact(&mut buf)?;

View file

@ -1,4 +1,4 @@
use std::{io, str::FromStr};
use std::str::FromStr;
use byteorder::{ReadBytesExt, LE};
@ -17,7 +17,7 @@ pub struct Footer {
}
impl Footer {
pub fn new<R: 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 = Footer {
encryption_guid: (version >= &Version::EncryptionKeyGuid)
.then_some(reader.read_guid()?),
@ -36,14 +36,17 @@ impl Footer {
5
});
for _ in 0..compression.capacity() {
compression.push(Compression::from_str(
&reader
.read_len(32)?
.iter()
// filter out whitespace and convert to char
.filter_map(|&ch| (ch != 0).then_some(ch as char))
.collect::<String>(),
)?)
compression.push(
Compression::from_str(
&reader
.read_len(32)?
.iter()
// filter out whitespace and convert to char
.filter_map(|&ch| (ch != 0).then_some(ch as char))
.collect::<String>(),
)
.unwrap_or_default(),
)
}
compression
}),

34
src/index.rs Normal file
View file

@ -0,0 +1,34 @@
use super::{ReadExt, Version};
pub enum Index {
WithoutPathHash(IndexV1),
WithPathHash(IndexV2),
}
impl Index {
pub fn new<R: std::io::Read>(reader: &mut R, version: &Version) -> Result<Self, super::Error> {
Ok(match version < &Version::PathHashIndex {
true => Index::WithoutPathHash(IndexV1::new(reader, version)?),
false => Index::WithPathHash(todo!()),
})
}
}
pub struct IndexV1 {
pub mount_point: String,
pub entries: Vec<super::Entry>,
}
impl IndexV1 {
pub fn new<R: std::io::Read>(
reader: &mut R,
version: &super::Version,
) -> Result<Self, super::Error> {
Ok(Self {
mount_point: reader.read_string()?,
entries: reader.read_array(|reader| super::Entry::new(reader, version))?,
})
}
}
pub struct IndexV2 {}

View file

@ -1,11 +1,12 @@
#![allow(dead_code)]
mod entry;
mod error;
mod ext;
mod footer;
mod pakentry;
mod pakfile;
mod index;
mod pak;
pub use {error::*, ext::*, footer::*, pakentry::*, pakfile::*};
pub use {entry::*, error::*, ext::*, footer::*, index::*, pak::*};
pub const MAGIC: u32 = 0x5A6F12E1;
@ -37,16 +38,20 @@ pub enum Version {
PathHashIndex, // more compression methods
}
// i don't want people to need to install strum
// strum shouldn't need to be installed
impl Version {
pub fn iter() -> VersionIter {
<Version as strum::IntoEnumIterator>::iter()
}
}
#[derive(Copy, Clone, Debug, strum::Display, strum::EnumString)]
#[derive(Default, Copy, Clone, PartialEq, Eq, Debug, strum::Display, strum::EnumString)]
pub enum Compression {
#[default]
None,
Zlib,
ZlibBiasMemory,
ZlibBiasSpeed,
Gzip,
Oodle,
}

View file

@ -2,13 +2,13 @@ use std::io;
use super::Version;
pub struct PakFile {
pub struct Pak {
pub version: Version,
pub footer: super::Footer,
pub entries: hashbrown::HashMap<String, super::PakEntry>,
pub entries: hashbrown::HashMap<String, super::Entry>,
}
impl PakFile {
impl Pak {
pub fn new<R: io::Read + io::Seek>(
version: super::Version,
mut reader: R,

View file

@ -1,17 +0,0 @@
pub struct PakEntry {
pub offset: u64,
pub compressed: u64,
pub decompressed: u64,
pub compression_method: super::Compression,
pub hash: [u8; 20],
pub compression_blocks: Vec<Block>,
pub flags: Vec<u8>,
pub block_size: u32,
}
pub struct Block {
/// start offset relative to the start of the entry header
pub offset: u64,
/// size of the compressed block
pub size: u64,
}