mirror of
https://github.com/xavo95/repak.git
synced 2025-01-18 19:04:07 +00:00
download in repak-cli
This commit is contained in:
parent
1a79469a11
commit
6c0ef1ce7b
5 changed files with 75 additions and 12 deletions
|
@ -12,8 +12,8 @@ edition = "2021"
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
aes = "0.8.2"
|
aes = "0.8.2"
|
||||||
base64 = "0.21.0"
|
base64 = "0.21.0"
|
||||||
hex = "0.4.3"
|
|
||||||
strum = { version = "0.24", features = ["derive"] }
|
strum = { version = "0.24", features = ["derive"] }
|
||||||
|
sha1 = "0.10"
|
||||||
|
|
||||||
# generated by 'cargo dist init'
|
# generated by 'cargo dist init'
|
||||||
[profile.dist]
|
[profile.dist]
|
||||||
|
|
|
@ -14,16 +14,13 @@ encryption = ["dep:aes"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.4"
|
byteorder = "1.4"
|
||||||
aes = { version = "0.8", optional = true }
|
aes = { workspace = true, optional = true }
|
||||||
flate2 = { version = "1.0", optional = true }
|
flate2 = { version = "1.0", optional = true }
|
||||||
zstd = { version = "0.12", optional = true }
|
zstd = { version = "0.12", optional = true }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
sha1 = "0.10.5"
|
sha1 = { workspace = true }
|
||||||
strum = { workspace = true }
|
strum = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
paste = "1.0.11"
|
paste = "1.0.11"
|
||||||
|
|
||||||
[package.metadata.cargo-all-features]
|
|
||||||
denylist = ["oodle"]
|
|
||||||
|
|
|
@ -9,7 +9,8 @@ pub use {error::*, pak::*};
|
||||||
|
|
||||||
pub const MAGIC: u32 = 0x5A6F12E1;
|
pub const MAGIC: u32 = 0x5A6F12E1;
|
||||||
|
|
||||||
type DECOMPRESS = fn(
|
#[cfg(feature = "oodle")]
|
||||||
|
pub type DECOMPRESS = fn(
|
||||||
compBuf: *mut u8,
|
compBuf: *mut u8,
|
||||||
compBufSize: usize,
|
compBufSize: usize,
|
||||||
rawBuf: *mut u8,
|
rawBuf: *mut u8,
|
||||||
|
|
|
@ -12,6 +12,12 @@ path = "src/main.rs"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
repak = { path = "../repak", features = ["oodle"] }
|
repak = { path = "../repak", features = ["oodle"] }
|
||||||
|
sha1 = { workspace = true }
|
||||||
|
ureq = "2.6"
|
||||||
|
once_cell = "1.17"
|
||||||
|
hex-literal = "0.4"
|
||||||
|
libloading = "0.7"
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
repak = { path = "../repak" }
|
repak = { path = "../repak" }
|
||||||
|
|
||||||
|
@ -19,7 +25,7 @@ repak = { path = "../repak" }
|
||||||
aes = { workspace = true }
|
aes = { workspace = true }
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
clap = { version = "4.1.4", features = ["derive"] }
|
clap = { version = "4.1.4", features = ["derive"] }
|
||||||
hex = { workspace = true }
|
hex = "0.4.3"
|
||||||
indicatif = { version = "0.17.3", features = ["rayon"] }
|
indicatif = { version = "0.17.3", features = ["rayon"] }
|
||||||
path-clean = "0.1.0"
|
path-clean = "0.1.0"
|
||||||
path-slash = "0.2.1"
|
path-slash = "0.2.1"
|
||||||
|
|
|
@ -118,7 +118,7 @@ enum Action {
|
||||||
Info(ActionInfo),
|
Info(ActionInfo),
|
||||||
/// List .pak files
|
/// List .pak files
|
||||||
List(ActionList),
|
List(ActionList),
|
||||||
/// List .pka files and the SHA256 of their contents. Useful for finding differences between paks
|
/// List .pak files and the SHA256 of their contents. Useful for finding differences between paks
|
||||||
HashList(ActionHashList),
|
HashList(ActionHashList),
|
||||||
/// Unpack .pak file
|
/// Unpack .pak file
|
||||||
Unpack(ActionUnpack),
|
Unpack(ActionUnpack),
|
||||||
|
@ -248,6 +248,12 @@ fn hash_list(aes_key: Option<aes::Aes256>, action: ActionHashList) -> Result<(),
|
||||||
|
|
||||||
let hashes: std::sync::Arc<std::sync::Mutex<BTreeMap<std::borrow::Cow<'_, str>, Vec<u8>>>> =
|
let hashes: std::sync::Arc<std::sync::Mutex<BTreeMap<std::borrow::Cow<'_, str>, Vec<u8>>>> =
|
||||||
Default::default();
|
Default::default();
|
||||||
|
use std::ops::Deref;
|
||||||
|
let lib = match OODLE.deref() {
|
||||||
|
Ok(lib) => lib,
|
||||||
|
Err(e) => return Err(repak::Error::Other(e.clone())),
|
||||||
|
};
|
||||||
|
let oodle_decompress = unsafe { lib.get(b"OodleLZ_Decompress") }.unwrap();
|
||||||
|
|
||||||
full_paths.par_iter().zip(stripped).try_for_each_init(
|
full_paths.par_iter().zip(stripped).try_for_each_init(
|
||||||
|| (hashes.clone(), File::open(&action.input)),
|
|| (hashes.clone(), File::open(&action.input)),
|
||||||
|
@ -255,10 +261,11 @@ fn hash_list(aes_key: Option<aes::Aes256>, action: ActionHashList) -> Result<(),
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
|
|
||||||
let mut hasher = sha2::Sha256::new();
|
let mut hasher = sha2::Sha256::new();
|
||||||
pak.read_file(
|
pak.read_file_with_oodle(
|
||||||
path,
|
path,
|
||||||
&mut BufReader::new(file.as_ref().unwrap()),
|
&mut BufReader::new(file.as_ref().unwrap()),
|
||||||
&mut hasher,
|
&mut hasher,
|
||||||
|
&oodle_decompress,
|
||||||
)?;
|
)?;
|
||||||
let hash = hasher.finalize();
|
let hash = hasher.finalize();
|
||||||
hashes
|
hashes
|
||||||
|
@ -354,6 +361,12 @@ fn unpack(aes_key: Option<aes::Aes256>, action: ActionUnpack) -> Result<(), repa
|
||||||
|
|
||||||
let progress = indicatif::ProgressBar::new(entries.len() as u64)
|
let progress = indicatif::ProgressBar::new(entries.len() as u64)
|
||||||
.with_style(indicatif::ProgressStyle::with_template(STYLE).unwrap());
|
.with_style(indicatif::ProgressStyle::with_template(STYLE).unwrap());
|
||||||
|
use std::ops::Deref;
|
||||||
|
let lib = match OODLE.deref() {
|
||||||
|
Ok(lib) => lib,
|
||||||
|
Err(e) => return Err(repak::Error::Other(e.clone())),
|
||||||
|
};
|
||||||
|
let decompress = unsafe { lib.get(b"OodleLZ_Decompress") }.unwrap();
|
||||||
entries.par_iter().try_for_each_init(
|
entries.par_iter().try_for_each_init(
|
||||||
|| (progress.clone(), File::open(input)),
|
|| (progress.clone(), File::open(input)),
|
||||||
|(progress, file), entry| -> Result<(), repak::Error> {
|
|(progress, file), entry| -> Result<(), repak::Error> {
|
||||||
|
@ -361,13 +374,14 @@ fn unpack(aes_key: Option<aes::Aes256>, action: ActionUnpack) -> Result<(), repa
|
||||||
progress.println(format!("unpacking {}", entry.entry_path));
|
progress.println(format!("unpacking {}", entry.entry_path));
|
||||||
}
|
}
|
||||||
fs::create_dir_all(&entry.out_dir)?;
|
fs::create_dir_all(&entry.out_dir)?;
|
||||||
pak.read_file(
|
pak.read_file_with_oodle(
|
||||||
&entry.entry_path,
|
&entry.entry_path,
|
||||||
&mut BufReader::new(
|
&mut BufReader::new(
|
||||||
file.as_ref()
|
file.as_ref()
|
||||||
.map_err(|e| repak::Error::Other(format!("error reading pak: {e}")))?,
|
.map_err(|e| repak::Error::Other(format!("error reading pak: {e}")))?,
|
||||||
),
|
),
|
||||||
&mut fs::File::create(&entry.out_path)?,
|
&mut fs::File::create(&entry.out_path)?,
|
||||||
|
&decompress,
|
||||||
)?;
|
)?;
|
||||||
progress.inc(1);
|
progress.inc(1);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -463,6 +477,51 @@ fn get(aes_key: Option<aes::Aes256>, args: ActionGet) -> Result<(), repak::Error
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
std::io::stdout().write_all(&pak.get(&file.to_slash_lossy(), &mut reader)?)?;
|
use std::ops::Deref;
|
||||||
|
let lib = match OODLE.deref() {
|
||||||
|
Ok(lib) => lib,
|
||||||
|
Err(e) => return Err(repak::Error::Other(e.clone())),
|
||||||
|
};
|
||||||
|
std::io::stdout().write_all(&pak.get_with_oodle(
|
||||||
|
&file.to_slash_lossy(),
|
||||||
|
&mut reader,
|
||||||
|
&unsafe { lib.get(b"OodleLZ_Decompress") }.unwrap(),
|
||||||
|
)?)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
#[cfg(windows)]
|
||||||
|
static OODLE: Lazy<Result<libloading::Library, String>> =
|
||||||
|
Lazy::new(|| get_oodle().map_err(|e| e.to_string()));
|
||||||
|
#[cfg(windows)]
|
||||||
|
static OODLE_HASH: [u8; 20] = hex_literal::hex!("4bcc73614cb8fd2b0bce8d0f91ee5f3202d9d624");
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn get_oodle() -> Result<libloading::Library, repak::Error> {
|
||||||
|
use sha1::{Digest, Sha1};
|
||||||
|
|
||||||
|
let oodle = std::env::current_exe()?.with_file_name("oo2core_9_win64.dll");
|
||||||
|
if !oodle.exists() {
|
||||||
|
let mut data = vec![];
|
||||||
|
ureq::get("https://cdn.discordapp.com/attachments/817251677086285848/992648087371792404/oo2core_9_win64.dll")
|
||||||
|
.call().map_err(|e| repak::Error::Other(e.to_string()))?
|
||||||
|
.into_reader().read_to_end(&mut data)?;
|
||||||
|
|
||||||
|
std::fs::write(&oodle, data)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut hasher = Sha1::new();
|
||||||
|
hasher.update(std::fs::read(&oodle)?);
|
||||||
|
let hash = hasher.finalize();
|
||||||
|
(hash[..] == OODLE_HASH).then_some(()).ok_or_else(|| {
|
||||||
|
repak::Error::Other(format!(
|
||||||
|
"oodle hash mismatch expected: {} got: {} ",
|
||||||
|
hex::encode(OODLE_HASH),
|
||||||
|
hex::encode(hash)
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
unsafe { libloading::Library::new(oodle) }.map_err(|_| repak::Error::OodleFailed)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue