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> { fn main() -> Result<(), un_pak::Error> {
for version in un_pak::Version::iter().rev() { 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(_) => { Ok(_) => {
println!("parsed successfully!"); println!("parsed successfully!");
return Ok(()); 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), IoError(#[from] std::io::Error),
#[error("error converting to enum: {0}")] #[error("error converting to enum: {0}")]
StrumError(#[from] strum::ParseError), 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 { pub trait ReadExt {
fn read_bool(&mut self) -> Result<bool, super::Error>; fn read_bool(&mut self) -> Result<bool, super::Error>;
fn read_guid(&mut self) -> Result<[u8; 20], 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>; 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) 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> { fn read_len(&mut self, len: usize) -> Result<Vec<u8>, super::Error> {
let mut buf = Vec::with_capacity(len); let mut buf = Vec::with_capacity(len);
self.read_exact(&mut buf)?; self.read_exact(&mut buf)?;

View file

@ -1,4 +1,4 @@
use std::{io, str::FromStr}; use std::str::FromStr;
use byteorder::{ReadBytesExt, LE}; use byteorder::{ReadBytesExt, LE};
@ -17,7 +17,7 @@ pub struct Footer {
} }
impl 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 { let footer = Footer {
encryption_guid: (version >= &Version::EncryptionKeyGuid) encryption_guid: (version >= &Version::EncryptionKeyGuid)
.then_some(reader.read_guid()?), .then_some(reader.read_guid()?),
@ -36,14 +36,17 @@ impl Footer {
5 5
}); });
for _ in 0..compression.capacity() { for _ in 0..compression.capacity() {
compression.push(Compression::from_str( compression.push(
&reader Compression::from_str(
.read_len(32)? &reader
.iter() .read_len(32)?
// filter out whitespace and convert to char .iter()
.filter_map(|&ch| (ch != 0).then_some(ch as char)) // filter out whitespace and convert to char
.collect::<String>(), .filter_map(|&ch| (ch != 0).then_some(ch as char))
)?) .collect::<String>(),
)
.unwrap_or_default(),
)
} }
compression 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)] #![allow(dead_code)]
mod entry;
mod error; mod error;
mod ext; mod ext;
mod footer; mod footer;
mod pakentry; mod index;
mod pakfile; mod pak;
pub use {error::*, ext::*, footer::*, pakentry::*, pakfile::*}; pub use {entry::*, error::*, ext::*, footer::*, index::*, pak::*};
pub const MAGIC: u32 = 0x5A6F12E1; pub const MAGIC: u32 = 0x5A6F12E1;
@ -37,16 +38,20 @@ pub enum Version {
PathHashIndex, // more compression methods PathHashIndex, // more compression methods
} }
// i don't want people to need to install strum // strum shouldn't need to be installed
impl Version { impl Version {
pub fn iter() -> VersionIter { pub fn iter() -> VersionIter {
<Version as strum::IntoEnumIterator>::iter() <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 { pub enum Compression {
#[default]
None,
Zlib, Zlib,
ZlibBiasMemory,
ZlibBiasSpeed,
Gzip, Gzip,
Oodle, Oodle,
} }

View file

@ -2,13 +2,13 @@ use std::io;
use super::Version; use super::Version;
pub struct PakFile { pub struct Pak {
pub version: Version, pub version: Version,
pub footer: super::Footer, 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>( pub fn new<R: io::Read + io::Seek>(
version: super::Version, version: super::Version,
mut reader: R, 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,
}