mirror of
https://github.com/xavo95/repak.git
synced 2025-01-18 19:04:07 +00:00
implementation of v1 complete (I think)
This commit is contained in:
parent
b2188fc6f8
commit
c0a1c5154c
9 changed files with 159 additions and 37 deletions
|
@ -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
60
src/entry.rs
Normal 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>()?,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
|
|
35
src/ext.rs
35
src/ext.rs
|
@ -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)?;
|
||||
|
|
|
@ -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(
|
||||
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
34
src/index.rs
Normal 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 {}
|
15
src/lib.rs
15
src/lib.rs
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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,
|
|
@ -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,
|
||||
}
|
Loading…
Reference in a new issue