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> {
|
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
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),
|
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),
|
||||||
}
|
}
|
||||||
|
|
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 {
|
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)?;
|
||||||
|
|
|
@ -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
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)]
|
#![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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
|
@ -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