mirror of
https://github.com/xavo95/repak.git
synced 2025-01-18 19:04:07 +00:00
Merge pull request #6 from bananaturtlesandwich/features-redo
Move functionality behind `compression`, `encryption`, and `oodle` features
This commit is contained in:
commit
a749449f8c
8 changed files with 239 additions and 54 deletions
|
@ -6,20 +6,29 @@ license.workspace = true
|
|||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["compression", "encryption"]
|
||||
compression = ["dep:flate2", "dep:zstd"]
|
||||
oodle = ["dep:libloading", "dep:ureq", "dep:once_cell", "dep:hex-literal", "dep:hex"]
|
||||
encryption = ["dep:aes"]
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4"
|
||||
aes = "0.8"
|
||||
flate2 = "1.0"
|
||||
aes = { version = "0.8", optional = true }
|
||||
flate2 = { version = "1.0", optional = true }
|
||||
zstd = { version = "0.12", optional = true }
|
||||
thiserror = "1.0"
|
||||
sha1 = "0.10.5"
|
||||
strum = { workspace = true }
|
||||
libloading = "0.7.4"
|
||||
ureq = "2.6.2"
|
||||
hex-literal = "0.4.1"
|
||||
hex = { workspace = true }
|
||||
once_cell = "1.17.1"
|
||||
zstd = "0.12.3"
|
||||
libloading = { version = "0.7", optional = true }
|
||||
ureq = { version = "2.6", optional = true }
|
||||
once_cell = { version = "1.17", optional = true}
|
||||
hex-literal = { version = "0.4", optional = true }
|
||||
hex = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
base64 = { workspace = true }
|
||||
paste = "1.0.11"
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["oodle"]
|
||||
|
|
|
@ -3,13 +3,13 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LE};
|
|||
use std::io;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum EntryLocation {
|
||||
pub(crate) enum EntryLocation {
|
||||
Data,
|
||||
Index,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Block {
|
||||
pub(crate) struct Block {
|
||||
pub start: u64,
|
||||
pub end: u64,
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ fn align(offset: u64) -> u64 {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Entry {
|
||||
pub(crate) struct Entry {
|
||||
pub offset: u64,
|
||||
pub compressed: u64,
|
||||
pub uncompressed: u64,
|
||||
|
@ -311,27 +311,35 @@ impl Entry {
|
|||
reader: &mut R,
|
||||
version: Version,
|
||||
compression: &[Compression],
|
||||
key: Option<&aes::Aes256>,
|
||||
#[allow(unused)] key: &super::Key,
|
||||
buf: &mut W,
|
||||
) -> Result<(), super::Error> {
|
||||
reader.seek(io::SeekFrom::Start(self.offset))?;
|
||||
Entry::read(reader, version)?;
|
||||
#[cfg(any(feature = "compression", feature = "oodle"))]
|
||||
let data_offset = reader.stream_position()?;
|
||||
#[allow(unused_mut)]
|
||||
let mut data = reader.read_len(match self.is_encrypted() {
|
||||
true => align(self.compressed),
|
||||
false => self.compressed,
|
||||
} as usize)?;
|
||||
if self.is_encrypted() {
|
||||
let Some(key) = key else {
|
||||
return Err(super::Error::Encrypted);
|
||||
};
|
||||
use aes::cipher::BlockDecrypt;
|
||||
for block in data.chunks_mut(16) {
|
||||
key.decrypt_block(aes::Block::from_mut_slice(block))
|
||||
#[cfg(not(feature = "encryption"))]
|
||||
return Err(super::Error::Encryption);
|
||||
#[cfg(feature = "encryption")]
|
||||
{
|
||||
let super::Key::Some(key) = key else {
|
||||
return Err(super::Error::Encrypted);
|
||||
};
|
||||
use aes::cipher::BlockDecrypt;
|
||||
for block in data.chunks_mut(16) {
|
||||
key.decrypt_block(aes::Block::from_mut_slice(block))
|
||||
}
|
||||
data.truncate(self.compressed as usize);
|
||||
}
|
||||
data.truncate(self.compressed as usize);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "compression", feature = "oodle"))]
|
||||
let ranges = match &self.blocks {
|
||||
Some(blocks) => blocks
|
||||
.iter()
|
||||
|
@ -347,9 +355,11 @@ impl Entry {
|
|||
},
|
||||
)
|
||||
.collect::<Vec<_>>(),
|
||||
#[allow(clippy::single_range_in_vec_init)]
|
||||
None => vec![0..data.len()],
|
||||
};
|
||||
|
||||
#[cfg(feature = "compression")]
|
||||
macro_rules! decompress {
|
||||
($decompressor: ty) => {
|
||||
for range in ranges {
|
||||
|
@ -359,14 +369,18 @@ impl Entry {
|
|||
}
|
||||
|
||||
match self.compression.map(|c| compression[c as usize]) {
|
||||
None => buf.write_all(&data)?,
|
||||
None | Some(Compression::None) => buf.write_all(&data)?,
|
||||
#[cfg(feature = "compression")]
|
||||
Some(Compression::Zlib) => decompress!(flate2::read::ZlibDecoder<&[u8]>),
|
||||
#[cfg(feature = "compression")]
|
||||
Some(Compression::Gzip) => decompress!(flate2::read::GzDecoder<&[u8]>),
|
||||
#[cfg(feature = "compression")]
|
||||
Some(Compression::Zstd) => {
|
||||
for range in ranges {
|
||||
io::copy(&mut zstd::stream::read::Decoder::new(&data[range])?, buf)?;
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "oodle")]
|
||||
Some(Compression::Oodle) => {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
return Err(super::Error::Oodle);
|
||||
|
@ -410,6 +424,7 @@ impl Entry {
|
|||
*/
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
let OodleLZ_Decompress: libloading::Symbol<
|
||||
extern "C" fn(
|
||||
compBuf: *mut u8,
|
||||
|
@ -472,18 +487,25 @@ impl Entry {
|
|||
buf.write_all(&decompressed)?;
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
#[cfg(not(feature = "oodle"))]
|
||||
Some(Compression::Oodle) => return Err(super::Error::Oodle),
|
||||
#[cfg(not(feature = "compression"))]
|
||||
_ => return Err(super::Error::Compression),
|
||||
}
|
||||
buf.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "oodle")]
|
||||
use once_cell::sync::Lazy;
|
||||
#[cfg(feature = "oodle")]
|
||||
static OODLE: Lazy<Result<libloading::Library, String>> =
|
||||
Lazy::new(|| get_oodle().map_err(|e| e.to_string()));
|
||||
#[cfg(feature = "oodle")]
|
||||
static OODLE_HASH: [u8; 20] = hex_literal::hex!("4bcc73614cb8fd2b0bce8d0f91ee5f3202d9d624");
|
||||
|
||||
#[cfg(feature = "oodle")]
|
||||
fn get_oodle() -> Result<libloading::Library, super::Error> {
|
||||
use sha1::{Digest, Sha1};
|
||||
|
||||
|
|
|
@ -9,6 +9,23 @@ pub enum Error {
|
|||
#[error("expect 256 bit AES key as base64 or hex string")]
|
||||
Aes,
|
||||
|
||||
// feature errors
|
||||
#[error("enable the compression feature to read compressed paks")]
|
||||
Compression,
|
||||
|
||||
#[error("enable the encryption feature to read encrypted paks")]
|
||||
Encryption,
|
||||
|
||||
#[cfg_attr(
|
||||
windows,
|
||||
error("enable the oodle feature to read Oodle compressed paks")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(windows),
|
||||
error("Oodle compression only supported on Windows (or WINE)")
|
||||
)]
|
||||
Oodle,
|
||||
|
||||
// std errors
|
||||
#[error("io error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
@ -22,6 +39,7 @@ pub enum Error {
|
|||
#[error("utf16 conversion: {0}")]
|
||||
Utf16(#[from] std::string::FromUtf16Error),
|
||||
|
||||
#[cfg(feature = "oodle")]
|
||||
#[error("ureq error: {0}")]
|
||||
Ureq(#[from] Box<ureq::Error>), // boxed because ureq::Error is quite large
|
||||
|
||||
|
@ -35,9 +53,6 @@ pub enum Error {
|
|||
#[error("found magic of {0:#x} instead of {:#x}", super::MAGIC)]
|
||||
Magic(u32),
|
||||
|
||||
#[error("Oodle compression only supported on Windows (or WINE)")]
|
||||
Oodle,
|
||||
|
||||
#[error("Could not load oo2core_9_win64.dll")]
|
||||
OodleFailed,
|
||||
|
||||
|
@ -72,7 +87,7 @@ pub enum Error {
|
|||
OsString(std::ffi::OsString),
|
||||
|
||||
#[error("{0}version unsupported or is encrypted (possibly missing --aes-key?)")]
|
||||
UnsuportedOrEncrypted(String),
|
||||
UnsupportedOrEncrypted(String),
|
||||
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
|
|
|
@ -7,6 +7,9 @@ mod pak;
|
|||
|
||||
pub use {error::*, pak::*};
|
||||
|
||||
#[cfg(all(feature = "oodle", not(target_os = "windows")))]
|
||||
compile_error!("Oodle compression only supported on Windows (or WINE)");
|
||||
|
||||
pub const MAGIC: u32 = 0x5A6F12E1;
|
||||
|
||||
#[derive(
|
||||
|
@ -119,3 +122,18 @@ pub enum Compression {
|
|||
Oodle,
|
||||
Zstd,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Key {
|
||||
#[cfg(feature = "encryption")]
|
||||
Some(aes::Aes256),
|
||||
None,
|
||||
}
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
impl From<aes::Aes256> for Key {
|
||||
fn from(value: aes::Aes256) -> Self {
|
||||
Self::Some(value)
|
||||
}
|
||||
}
|
||||
|
|
141
repak/src/pak.rs
141
repak/src/pak.rs
|
@ -1,6 +1,5 @@
|
|||
use super::ext::{ReadExt, WriteExt};
|
||||
use super::{Version, VersionMajor};
|
||||
use aes::Aes256;
|
||||
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{self, Read, Seek, Write};
|
||||
|
@ -8,18 +7,18 @@ use std::io::{self, Read, Seek, Write};
|
|||
#[derive(Debug)]
|
||||
pub struct PakReader {
|
||||
pak: Pak,
|
||||
key: Option<aes::Aes256>,
|
||||
key: super::Key,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PakWriter<W: Write + Seek> {
|
||||
pak: Pak,
|
||||
writer: W,
|
||||
key: Option<aes::Aes256>,
|
||||
key: super::Key,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pak {
|
||||
pub(crate) struct Pak {
|
||||
version: Version,
|
||||
mount_point: String,
|
||||
index_offset: Option<u64>,
|
||||
|
@ -44,7 +43,7 @@ impl Pak {
|
|||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Index {
|
||||
pub(crate) struct Index {
|
||||
path_hash_seed: Option<u64>,
|
||||
entries: BTreeMap<String, super::entry::Entry>,
|
||||
}
|
||||
|
@ -70,8 +69,9 @@ impl Index {
|
|||
}
|
||||
}
|
||||
|
||||
fn decrypt(key: Option<&aes::Aes256>, bytes: &mut [u8]) -> Result<(), super::Error> {
|
||||
if let Some(key) = key {
|
||||
#[cfg(feature = "encryption")]
|
||||
fn decrypt(key: &super::Key, bytes: &mut [u8]) -> Result<(), super::Error> {
|
||||
if let super::Key::Some(key) = key {
|
||||
use aes::cipher::BlockDecrypt;
|
||||
for chunk in bytes.chunks_mut(16) {
|
||||
key.decrypt_block(aes::Block::from_mut_slice(chunk))
|
||||
|
@ -83,28 +83,79 @@ fn decrypt(key: Option<&aes::Aes256>, bytes: &mut [u8]) -> Result<(), super::Err
|
|||
}
|
||||
|
||||
impl PakReader {
|
||||
pub fn new_any<R: Read + Seek>(
|
||||
pub fn new_any<R: Read + Seek>(reader: &mut R) -> Result<Self, super::Error> {
|
||||
Self::new_any_inner(reader, super::Key::None)
|
||||
}
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
pub fn new_any_with_key<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
key: aes::Aes256,
|
||||
) -> Result<Self, super::Error> {
|
||||
Self::new_any_inner(reader, key.into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
pub fn new_any_with_optional_key<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
key: Option<aes::Aes256>,
|
||||
) -> Result<Self, super::Error> {
|
||||
match key {
|
||||
Some(key) => Self::new_any_with_key(reader, key),
|
||||
None => Self::new_any(reader),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_any_inner<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
key: super::Key,
|
||||
) -> Result<Self, super::Error> {
|
||||
use std::fmt::Write;
|
||||
let mut log = "\n".to_owned();
|
||||
|
||||
for ver in Version::iter() {
|
||||
match Pak::read(&mut *reader, ver, key.as_ref()) {
|
||||
match Pak::read(&mut *reader, ver, &key) {
|
||||
Ok(pak) => return Ok(Self { pak, key }),
|
||||
Err(err) => writeln!(log, "trying version {} failed: {}", ver, err)?,
|
||||
}
|
||||
}
|
||||
Err(super::Error::UnsuportedOrEncrypted(log))
|
||||
Err(super::Error::UnsupportedOrEncrypted(log))
|
||||
}
|
||||
|
||||
pub fn new<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
version: super::Version,
|
||||
) -> Result<Self, super::Error> {
|
||||
Self::new_inner(reader, version, super::Key::None)
|
||||
}
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
pub fn new_with_key<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
version: super::Version,
|
||||
key: aes::Aes256,
|
||||
) -> Result<Self, super::Error> {
|
||||
Self::new_inner(reader, version, key.into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
pub fn new_with_optional_key<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
version: super::Version,
|
||||
key: Option<aes::Aes256>,
|
||||
) -> Result<Self, super::Error> {
|
||||
Pak::read(reader, version, key.as_ref()).map(|pak| Self { pak, key })
|
||||
match key {
|
||||
Some(key) => Self::new_with_key(reader, version, key),
|
||||
None => Self::new(reader, version),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_inner<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
version: super::Version,
|
||||
key: super::Key,
|
||||
) -> Result<Self, super::Error> {
|
||||
Pak::read(reader, version, &key).map(|pak| Self { pak, key })
|
||||
}
|
||||
|
||||
pub fn version(&self) -> super::Version {
|
||||
|
@ -140,7 +191,7 @@ impl PakReader {
|
|||
reader,
|
||||
self.pak.version,
|
||||
&self.pak.compression,
|
||||
self.key.as_ref(),
|
||||
&self.key,
|
||||
writer,
|
||||
),
|
||||
None => Err(super::Error::MissingEntry(path.to_owned())),
|
||||
|
@ -166,11 +217,53 @@ impl PakReader {
|
|||
|
||||
impl<W: Write + Seek> PakWriter<W> {
|
||||
pub fn new(
|
||||
writer: W,
|
||||
version: Version,
|
||||
mount_point: String,
|
||||
path_hash_seed: Option<u64>,
|
||||
) -> Self {
|
||||
PakWriter {
|
||||
pak: Pak::new(version, mount_point, path_hash_seed),
|
||||
writer,
|
||||
key: super::Key::None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
pub fn new_with_key(
|
||||
writer: W,
|
||||
key: aes::Aes256,
|
||||
version: Version,
|
||||
mount_point: String,
|
||||
path_hash_seed: Option<u64>,
|
||||
) -> Self {
|
||||
PakWriter {
|
||||
pak: Pak::new(version, mount_point, path_hash_seed),
|
||||
writer,
|
||||
key: key.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
pub fn new_with_optional_key(
|
||||
writer: W,
|
||||
key: Option<aes::Aes256>,
|
||||
version: Version,
|
||||
mount_point: String,
|
||||
path_hash_seed: Option<u64>,
|
||||
) -> Self {
|
||||
match key {
|
||||
Some(key) => Self::new_with_key(writer, key, version, mount_point, path_hash_seed),
|
||||
None => Self::new(writer, version, mount_point, path_hash_seed),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
writer: W,
|
||||
key: super::Key,
|
||||
version: Version,
|
||||
mount_point: String,
|
||||
path_hash_seed: Option<u64>,
|
||||
) -> Self {
|
||||
PakWriter {
|
||||
pak: Pak::new(version, mount_point, path_hash_seed),
|
||||
|
@ -219,26 +312,30 @@ impl<W: Write + Seek> PakWriter<W> {
|
|||
}
|
||||
|
||||
pub fn write_index(mut self) -> Result<W, super::Error> {
|
||||
self.pak.write(&mut self.writer, self.key)?;
|
||||
self.pak.write(&mut self.writer, &self.key)?;
|
||||
Ok(self.writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pak {
|
||||
fn read<R: Read + Seek>(
|
||||
mut reader: R,
|
||||
reader: &mut R,
|
||||
version: super::Version,
|
||||
key: Option<&aes::Aes256>,
|
||||
#[allow(unused)] key: &super::Key,
|
||||
) -> Result<Self, super::Error> {
|
||||
// read footer to get index, encryption & compression info
|
||||
reader.seek(io::SeekFrom::End(-version.size()))?;
|
||||
let footer = super::footer::Footer::read(&mut reader, version)?;
|
||||
let footer = super::footer::Footer::read(reader, version)?;
|
||||
// read index to get all the entry info
|
||||
reader.seek(io::SeekFrom::Start(footer.index_offset))?;
|
||||
#[allow(unused_mut)]
|
||||
let mut index = reader.read_len(footer.index_size as usize)?;
|
||||
|
||||
// decrypt index if needed
|
||||
if footer.encrypted {
|
||||
#[cfg(not(feature = "encryption"))]
|
||||
return Err(super::Error::Encryption);
|
||||
#[cfg(feature = "encryption")]
|
||||
decrypt(key, &mut index)?;
|
||||
}
|
||||
|
||||
|
@ -260,6 +357,9 @@ impl Pak {
|
|||
// TODO verify hash
|
||||
|
||||
if footer.encrypted {
|
||||
#[cfg(not(feature = "encryption"))]
|
||||
return Err(super::Error::Encryption);
|
||||
#[cfg(feature = "encryption")]
|
||||
decrypt(key, &mut path_hash_index_buf)?;
|
||||
}
|
||||
|
||||
|
@ -283,11 +383,15 @@ impl Pak {
|
|||
let _full_directory_index_hash = index.read_len(20)?;
|
||||
|
||||
reader.seek(io::SeekFrom::Start(full_directory_index_offset))?;
|
||||
#[allow(unused_mut)]
|
||||
let mut full_directory_index =
|
||||
reader.read_len(full_directory_index_size as usize)?;
|
||||
// TODO verify hash
|
||||
|
||||
if footer.encrypted {
|
||||
#[cfg(not(feature = "encryption"))]
|
||||
return Err(super::Error::Encryption);
|
||||
#[cfg(feature = "encryption")]
|
||||
decrypt(key, &mut full_directory_index)?;
|
||||
}
|
||||
let mut fdi = io::Cursor::new(full_directory_index);
|
||||
|
@ -367,7 +471,7 @@ impl Pak {
|
|||
fn write<W: Write + Seek>(
|
||||
&self,
|
||||
writer: &mut W,
|
||||
_key: Option<aes::Aes256>,
|
||||
_key: &super::Key,
|
||||
) -> Result<(), super::Error> {
|
||||
let index_offset = writer.stream_position()?;
|
||||
|
||||
|
@ -594,7 +698,8 @@ fn pad_zeros_to_alignment(v: &mut Vec<u8>, alignment: usize) {
|
|||
assert!(v.len() % alignment == 0);
|
||||
}
|
||||
|
||||
fn encrypt(key: Aes256, bytes: &mut [u8]) {
|
||||
#[cfg(feature = "encryption")]
|
||||
fn encrypt(key: aes::Aes256, bytes: &mut [u8]) {
|
||||
use aes::cipher::BlockEncrypt;
|
||||
for chunk in bytes.chunks_mut(16) {
|
||||
key.encrypt_block(aes::Block::from_mut_slice(chunk))
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(feature = "default")]
|
||||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||
use paste::paste;
|
||||
use std::io::{self, Cursor, Read, Seek, SeekFrom};
|
||||
|
@ -96,7 +97,7 @@ fn test_read(version: repak::Version, _file_name: &str, bytes: &[u8]) {
|
|||
let len = inner_reader.seek(SeekFrom::End(0)).unwrap();
|
||||
let mut reader = ReadCounter::new_size(inner_reader, len as usize);
|
||||
|
||||
let pak = repak::PakReader::new_any(&mut reader, Some(key)).unwrap();
|
||||
let pak = repak::PakReader::new_any_with_key(&mut reader, key).unwrap();
|
||||
|
||||
assert_eq!(pak.mount_point(), "../mount/point/root/");
|
||||
assert_eq!(pak.version(), version);
|
||||
|
@ -159,12 +160,11 @@ fn test_write(_version: repak::Version, _file_name: &str, bytes: &[u8]) {
|
|||
.unwrap();
|
||||
|
||||
let mut reader = std::io::Cursor::new(bytes);
|
||||
let pak_reader = repak::PakReader::new_any(&mut reader, Some(key)).unwrap();
|
||||
let pak_reader = repak::PakReader::new_any_with_key(&mut reader, key).unwrap();
|
||||
|
||||
let writer = Cursor::new(vec![]);
|
||||
let mut pak_writer = repak::PakWriter::new(
|
||||
writer,
|
||||
None,
|
||||
pak_reader.version(),
|
||||
pak_reader.mount_point().to_owned(),
|
||||
Some(0x205C5A7D),
|
||||
|
@ -191,7 +191,7 @@ fn test_rewrite_index(_version: repak::Version, _file_name: &str, bytes: &[u8])
|
|||
.unwrap();
|
||||
|
||||
let mut buf = std::io::Cursor::new(bytes.to_vec());
|
||||
let pak_reader = repak::PakReader::new_any(&mut buf, Some(key)).unwrap();
|
||||
let pak_reader = repak::PakReader::new_any_with_key(&mut buf, key).unwrap();
|
||||
|
||||
let rewrite = pak_reader
|
||||
.into_pakwriter(buf)
|
||||
|
|
|
@ -10,6 +10,11 @@ edition.workspace = true
|
|||
name = "repak"
|
||||
path = "src/main.rs"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
repak = { path = "../repak", features = ["oodle"] }
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
repak = { path = "../repak" }
|
||||
|
||||
[dependencies]
|
||||
aes = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
|
@ -19,6 +24,5 @@ indicatif = { version = "0.17.3", features = ["rayon"] }
|
|||
path-clean = "0.1.0"
|
||||
path-slash = "0.2.1"
|
||||
rayon = "1.6.1"
|
||||
repak = { version = "0.1.7", path = "../repak" }
|
||||
sha2 = "0.10.7"
|
||||
strum = { workspace = true }
|
||||
|
|
|
@ -175,7 +175,10 @@ fn main() -> Result<(), repak::Error> {
|
|||
}
|
||||
|
||||
fn info(aes_key: Option<aes::Aes256>, action: ActionInfo) -> Result<(), repak::Error> {
|
||||
let pak = repak::PakReader::new_any(&mut BufReader::new(File::open(action.input)?), aes_key)?;
|
||||
let pak = repak::PakReader::new_any_with_optional_key(
|
||||
&mut BufReader::new(File::open(action.input)?),
|
||||
aes_key,
|
||||
)?;
|
||||
println!("mount point: {}", pak.mount_point());
|
||||
println!("version: {}", pak.version());
|
||||
println!("version major: {}", pak.version().version_major());
|
||||
|
@ -186,7 +189,10 @@ fn info(aes_key: Option<aes::Aes256>, action: ActionInfo) -> Result<(), repak::E
|
|||
}
|
||||
|
||||
fn list(aes_key: Option<aes::Aes256>, action: ActionList) -> Result<(), repak::Error> {
|
||||
let pak = repak::PakReader::new_any(&mut BufReader::new(File::open(action.input)?), aes_key)?;
|
||||
let pak = repak::PakReader::new_any_with_optional_key(
|
||||
&mut BufReader::new(File::open(action.input)?),
|
||||
aes_key,
|
||||
)?;
|
||||
|
||||
let mount_point = PathBuf::from(pak.mount_point());
|
||||
let prefix = Path::new(&action.strip_prefix);
|
||||
|
@ -215,7 +221,10 @@ fn list(aes_key: Option<aes::Aes256>, action: ActionList) -> Result<(), repak::E
|
|||
}
|
||||
|
||||
fn hash_list(aes_key: Option<aes::Aes256>, action: ActionHashList) -> Result<(), repak::Error> {
|
||||
let pak = repak::PakReader::new_any(&mut BufReader::new(File::open(&action.input)?), aes_key)?;
|
||||
let pak = repak::PakReader::new_any_with_optional_key(
|
||||
&mut BufReader::new(File::open(&action.input)?),
|
||||
aes_key,
|
||||
)?;
|
||||
|
||||
let mount_point = PathBuf::from(pak.mount_point());
|
||||
let prefix = Path::new(&action.strip_prefix);
|
||||
|
@ -270,7 +279,10 @@ fn hash_list(aes_key: Option<aes::Aes256>, action: ActionHashList) -> Result<(),
|
|||
const STYLE: &str = "[{elapsed_precise}] [{wide_bar}] {pos}/{len} ({eta})";
|
||||
|
||||
fn unpack(aes_key: Option<aes::Aes256>, action: ActionUnpack) -> Result<(), repak::Error> {
|
||||
let pak = repak::PakReader::new_any(&mut BufReader::new(File::open(&action.input)?), aes_key)?;
|
||||
let pak = repak::PakReader::new_any_with_optional_key(
|
||||
&mut BufReader::new(File::open(&action.input)?),
|
||||
aes_key,
|
||||
)?;
|
||||
let output = action
|
||||
.output
|
||||
.map(PathBuf::from)
|
||||
|
@ -388,7 +400,7 @@ fn pack(args: ActionPack) -> Result<(), repak::Error> {
|
|||
collect_files(&mut paths, input_path)?;
|
||||
paths.sort();
|
||||
|
||||
let mut pak = repak::PakWriter::new(
|
||||
let mut pak = repak::PakWriter::new_with_optional_key(
|
||||
BufWriter::new(File::create(&output)?),
|
||||
None,
|
||||
args.version,
|
||||
|
@ -423,7 +435,7 @@ fn pack(args: ActionPack) -> Result<(), repak::Error> {
|
|||
|
||||
fn get(aes_key: Option<aes::Aes256>, args: ActionGet) -> Result<(), repak::Error> {
|
||||
let mut reader = BufReader::new(File::open(&args.input)?);
|
||||
let pak = repak::PakReader::new_any(&mut reader, aes_key)?;
|
||||
let pak = repak::PakReader::new_any_with_optional_key(&mut reader, aes_key)?;
|
||||
let mount_point = PathBuf::from(pak.mount_point());
|
||||
let prefix = Path::new(&args.strip_prefix);
|
||||
|
||||
|
|
Loading…
Reference in a new issue