Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
5401200add
|
|||
312c3bc90e
|
|||
7222ee1790
|
|||
d628400f6a
|
133
Cargo.lock
generated
133
Cargo.lock
generated
@@ -28,15 +28,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.98"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.1"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
|
||||
[[package]]
|
||||
name = "arg_enum_proc_macro"
|
||||
@@ -46,7 +46,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -77,9 +77,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "avif-serialize"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42"
|
||||
checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
@@ -113,7 +113,7 @@ checksum = "7b9a5040dce49a7642c97ccb1ae59567098967b5d52c29773f1299a42d23bb39"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -148,22 +148,22 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.23.1"
|
||||
version = "1.23.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
|
||||
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
|
||||
dependencies = [
|
||||
"bytemuck_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck_derive"
|
||||
version = "1.10.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4"
|
||||
checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -180,9 +180,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.30"
|
||||
version = "1.2.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
|
||||
checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -372,7 +372,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -496,9 +496,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.4"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -550,9 +550,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
@@ -594,7 +594,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -650,9 +650,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
version = "0.2.175"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
@@ -713,7 +713,7 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "mhws-tex-decompressor"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"color-eyre",
|
||||
"colored",
|
||||
@@ -819,7 +819,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -964,9 +964,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
version = "1.0.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -987,7 +987,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1093,9 +1093,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
@@ -1103,9 +1103,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
@@ -1124,14 +1124,14 @@ dependencies = [
|
||||
"image_dds",
|
||||
"num-traits",
|
||||
"strum 0.27.2",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.15"
|
||||
version = "0.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec"
|
||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
@@ -1139,7 +1139,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ree-pak-core"
|
||||
version = "0.4.1"
|
||||
source = "git+https://github.com/eigeen/ree-pak-rs.git?branch=main#bf38404e0c3c1777a9bc351087264d7bea7ae1f8"
|
||||
source = "git+https://github.com/eigeen/ree-pak-rs.git?branch=main#d06ed6995207d289b86f091efaaccb9937490d54"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"byteorder",
|
||||
@@ -1152,7 +1152,7 @@ dependencies = [
|
||||
"rayon",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
@@ -1164,9 +1164,9 @@ checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.25"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
@@ -1189,9 +1189,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.21"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
@@ -1222,14 +1222,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.141"
|
||||
version = "1.0.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -1316,7 +1316,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1328,7 +1328,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1344,9 +1344,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
version = "2.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1396,11 +1396,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
version = "2.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.12",
|
||||
"thiserror-impl 2.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1411,18 +1411,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
version = "2.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1517,9 +1517,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
|
||||
[[package]]
|
||||
name = "unit-prefix"
|
||||
@@ -1587,7 +1587,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -1609,7 +1609,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -1633,6 +1633,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
@@ -1648,7 +1654,7 @@ version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
"windows-targets 0.53.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1669,10 +1675,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
@@ -1814,7 +1821,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mhws-tex-decompressor"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
@@ -16,7 +16,7 @@ dialoguer = "0.11"
|
||||
indicatif = "0.18"
|
||||
|
||||
color-eyre = "0.6"
|
||||
rayon = "1.10"
|
||||
rayon = "1.11"
|
||||
parking_lot = "0.12"
|
||||
colored = "3.0"
|
||||
fs-err = "3.1"
|
||||
|
289
src/app.rs
289
src/app.rs
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
path::{Path, PathBuf},
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
@@ -48,9 +48,11 @@ impl Mode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ChunkSelection {
|
||||
chunk_name: ChunkName,
|
||||
file_size: u64,
|
||||
full_path: PathBuf,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ChunkSelection {
|
||||
@@ -68,7 +70,7 @@ pub struct App {
|
||||
impl App {
|
||||
pub fn run(&mut self) -> color_eyre::Result<()> {
|
||||
println!("Version v{} - Tool by @Eigeen", env!("CARGO_PKG_VERSION"));
|
||||
println!("Get updates at https://github.com/eigeen/mhws-tex-decompressor");
|
||||
println!("Get updates from https://github.com/eigeen/mhws-tex-decompressor");
|
||||
println!();
|
||||
|
||||
println!("Loading embedded file name table...");
|
||||
@@ -94,6 +96,89 @@ impl App {
|
||||
self.filename_table.as_ref().unwrap()
|
||||
}
|
||||
|
||||
/// Scan for all pak files in the game directory, including DLC directory
|
||||
fn scan_all_pak_files(&self, game_dir: &Path) -> color_eyre::Result<Vec<ChunkSelection>> {
|
||||
let mut main_chunks = Vec::new();
|
||||
let mut dlc_chunks = Vec::new();
|
||||
|
||||
// Scan main game directory
|
||||
self.scan_pak_files_in_dir(game_dir, &mut main_chunks)?;
|
||||
|
||||
// Scan DLC directory if it exists
|
||||
let dlc_dir = game_dir.join("dlc");
|
||||
if dlc_dir.is_dir() {
|
||||
self.scan_pak_files_in_dir(&dlc_dir, &mut dlc_chunks)?;
|
||||
}
|
||||
|
||||
// If both main and DLC have files, ask user which locations to process
|
||||
let selected_locations = if !main_chunks.is_empty() && !dlc_chunks.is_empty() {
|
||||
let locations = vec!["Main game directory", "DLC directory"];
|
||||
|
||||
MultiSelect::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Select locations to process (Space to select, Enter to confirm)")
|
||||
.items(&locations)
|
||||
.defaults(&[true, true])
|
||||
.interact()?
|
||||
} else if !main_chunks.is_empty() {
|
||||
vec![0]
|
||||
} else if !dlc_chunks.is_empty() {
|
||||
vec![1]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let mut all_chunks = Vec::new();
|
||||
for &location_idx in &selected_locations {
|
||||
match location_idx {
|
||||
0 => all_chunks.extend(main_chunks.iter().cloned()),
|
||||
1 => all_chunks.extend(dlc_chunks.iter().cloned()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
all_chunks.sort_by(|a, b| a.chunk_name.cmp(&b.chunk_name));
|
||||
|
||||
Ok(all_chunks)
|
||||
}
|
||||
|
||||
/// Scan pak files in a specific directory
|
||||
fn scan_pak_files_in_dir(
|
||||
&self,
|
||||
dir: &Path,
|
||||
all_chunks: &mut Vec<ChunkSelection>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let entries = fs::read_dir(dir)?;
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
if !entry.file_type()?.is_file() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_name = entry.file_name().to_string_lossy().to_string();
|
||||
let file_path = entry.path();
|
||||
|
||||
if !file_name.ends_with(".pak") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let chunk_name = match ChunkName::try_from_str(&file_name) {
|
||||
Ok(chunk_name) => chunk_name,
|
||||
Err(e) => {
|
||||
println!("Invalid chunk name, skipped: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let file_size = fs::metadata(&file_path)?.len();
|
||||
all_chunks.push(ChunkSelection {
|
||||
chunk_name,
|
||||
file_size,
|
||||
full_path: file_path,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_chunk(
|
||||
&self,
|
||||
filename_table: &FileNameTable,
|
||||
@@ -242,51 +327,15 @@ I'm sure I've checked the list, press Enter to continue"#,
|
||||
bail!("game directory not exists.");
|
||||
}
|
||||
|
||||
// scan for pak files
|
||||
let dir = fs::read_dir(game_dir)?;
|
||||
let mut all_chunks: Vec<ChunkName> = vec![];
|
||||
for entry in dir {
|
||||
let entry = entry?;
|
||||
if !entry.file_type()?.is_file() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_name = entry.file_name().to_string_lossy().to_string();
|
||||
if !file_name.ends_with(".pak") || !file_name.starts_with("re_chunk_") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let chunk_name = match ChunkName::try_from_str(&file_name) {
|
||||
Ok(chunk_name) => chunk_name,
|
||||
Err(e) => {
|
||||
println!("Invalid chunk name, skipped: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
all_chunks.push(chunk_name);
|
||||
}
|
||||
all_chunks.sort();
|
||||
// scan for pak files in main game directory and DLC directory
|
||||
let all_chunk_selections = self.scan_all_pak_files(game_dir)?;
|
||||
|
||||
// show chunks for selection
|
||||
// only show sub chunks
|
||||
let chunk_selections = all_chunks
|
||||
let chunk_selections: Vec<&ChunkSelection> = all_chunk_selections
|
||||
.iter()
|
||||
.filter_map(|chunk| {
|
||||
if chunk.sub_id.is_some() {
|
||||
Some(chunk.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|file_name| {
|
||||
let file_path = game_dir.join(&file_name);
|
||||
let file_size = fs::metadata(file_path)?.len();
|
||||
Ok(ChunkSelection {
|
||||
chunk_name: ChunkName::try_from_str(&file_name)?,
|
||||
file_size,
|
||||
})
|
||||
})
|
||||
.collect::<color_eyre::Result<Vec<_>>>()?;
|
||||
.filter(|chunk_selection| chunk_selection.chunk_name.sub_id().is_some())
|
||||
.collect();
|
||||
if chunk_selections.is_empty() {
|
||||
bail!("No available pak files found.");
|
||||
}
|
||||
@@ -294,9 +343,9 @@ I'm sure I've checked the list, press Enter to continue"#,
|
||||
let selected_chunks: Vec<bool> = chunk_selections
|
||||
.iter()
|
||||
.map(|chunk_selection| {
|
||||
Ok(chunk_selection.file_size >= AUTO_CHUNK_SELECTION_SIZE_THRESHOLD as u64)
|
||||
chunk_selection.file_size >= AUTO_CHUNK_SELECTION_SIZE_THRESHOLD as u64
|
||||
})
|
||||
.collect::<color_eyre::Result<Vec<_>>>()?;
|
||||
.collect();
|
||||
|
||||
let selected_chunks: Option<Vec<usize>> =
|
||||
MultiSelect::with_theme(&ColorfulTheme::default())
|
||||
@@ -308,10 +357,10 @@ I'm sure I've checked the list, press Enter to continue"#,
|
||||
bail!("No chunks selected.");
|
||||
};
|
||||
|
||||
let selected_chunks = selected_chunks
|
||||
let selected_chunk_selections: Vec<&ChunkSelection> = selected_chunks
|
||||
.iter()
|
||||
.map(|i| chunk_selections[*i].chunk_name.clone())
|
||||
.collect::<Vec<_>>();
|
||||
.map(|i| chunk_selections[*i])
|
||||
.collect();
|
||||
|
||||
// replace mode: replace original files with uncompressed files
|
||||
// patch mode: generate patch files after original patch files
|
||||
@@ -325,42 +374,51 @@ I'm sure I've checked the list, press Enter to continue"#,
|
||||
.unwrap();
|
||||
let use_replace_mode = use_replace_mode == 1;
|
||||
|
||||
// all chunk names for patch ID tracking
|
||||
let mut all_chunk_names: Vec<ChunkName> = all_chunk_selections
|
||||
.iter()
|
||||
.map(|cs| cs.chunk_name.clone())
|
||||
.collect();
|
||||
|
||||
// start processing
|
||||
for chunk_name in selected_chunks {
|
||||
let chunk_path = game_dir.join(chunk_name.to_string());
|
||||
for chunk_selection in selected_chunk_selections {
|
||||
let chunk_path = &chunk_selection.full_path;
|
||||
let chunk_name = &chunk_selection.chunk_name;
|
||||
|
||||
let output_path = if use_replace_mode {
|
||||
// In replace mode, first generate a temporary decompressed file
|
||||
chunk_path.with_extension("pak.temp")
|
||||
} else {
|
||||
// In patch mode
|
||||
// Find the max patch id for the current chunk series
|
||||
let max_patch_id = all_chunks
|
||||
let max_patch_id = all_chunk_names
|
||||
.iter()
|
||||
.filter(|c| {
|
||||
c.major_id == chunk_name.major_id
|
||||
&& c.patch_id == chunk_name.patch_id
|
||||
&& c.sub_id == chunk_name.sub_id
|
||||
c.major_id() == chunk_name.major_id()
|
||||
&& c.patch_id() == chunk_name.patch_id()
|
||||
&& c.sub_id() == chunk_name.sub_id()
|
||||
})
|
||||
.filter_map(|c| c.sub_patch_id)
|
||||
.filter_map(|c| c.sub_patch_id())
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
let new_patch_id = max_patch_id + 1;
|
||||
|
||||
// Create a new chunk name
|
||||
let mut output_chunk_name = chunk_name.clone();
|
||||
output_chunk_name.sub_patch_id = Some(new_patch_id);
|
||||
let output_chunk_name = chunk_name.with_sub_patch(new_patch_id);
|
||||
|
||||
// Add the new patch to the chunk list so it can be found in subsequent processing
|
||||
all_chunks.push(output_chunk_name.clone());
|
||||
all_chunk_names.push(output_chunk_name.clone());
|
||||
|
||||
game_dir.join(output_chunk_name.to_string())
|
||||
// Determine output directory based on original chunk location
|
||||
let output_dir = chunk_path.parent().unwrap();
|
||||
output_dir.join(output_chunk_name.to_string())
|
||||
};
|
||||
|
||||
println!("Output patch file: {}", output_path.display());
|
||||
self.process_chunk(
|
||||
self.filename_table(),
|
||||
&chunk_path,
|
||||
chunk_path,
|
||||
&output_path,
|
||||
use_replace_mode,
|
||||
true,
|
||||
@@ -374,9 +432,9 @@ I'm sure I've checked the list, press Enter to continue"#,
|
||||
if backup_path.exists() {
|
||||
fs::remove_file(&backup_path)?;
|
||||
}
|
||||
fs::rename(&chunk_path, &backup_path)?;
|
||||
fs::rename(chunk_path, &backup_path)?;
|
||||
// Rename the temporary file to the original file name
|
||||
fs::rename(&output_path, &chunk_path)?;
|
||||
fs::rename(&output_path, chunk_path)?;
|
||||
}
|
||||
println!();
|
||||
}
|
||||
@@ -447,40 +505,27 @@ I'm sure I've checked the list, press Enter to continue"#,
|
||||
|
||||
// scan all pak files, find files generated by this tool
|
||||
println!("Scanning tool generated files...");
|
||||
let dir = fs::read_dir(game_dir)?;
|
||||
let mut tool_generated_files = Vec::new();
|
||||
let mut backup_files = Vec::new();
|
||||
let mut all_chunks = Vec::new();
|
||||
|
||||
for entry in dir {
|
||||
let entry = entry?;
|
||||
if !entry.file_type()?.is_file() {
|
||||
continue;
|
||||
}
|
||||
// Scan main directory
|
||||
self.scan_tool_files_in_directory(
|
||||
game_dir,
|
||||
&mut tool_generated_files,
|
||||
&mut backup_files,
|
||||
&mut all_chunks,
|
||||
)?;
|
||||
|
||||
let file_name = entry.file_name().to_string_lossy().to_string();
|
||||
let file_path = entry.path();
|
||||
|
||||
// check backup files
|
||||
if file_name.ends_with(".pak.backup") {
|
||||
backup_files.push(file_path);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check pak files
|
||||
if !file_name.ends_with(".pak") || !file_name.starts_with("re_chunk_") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// collect chunk info
|
||||
if let Ok(chunk_name) = ChunkName::try_from_str(&file_name) {
|
||||
all_chunks.push(chunk_name.clone());
|
||||
}
|
||||
|
||||
// check if the file is generated by this tool
|
||||
if let Ok(Some(metadata)) = self.check_tool_generated_file(&file_path) {
|
||||
tool_generated_files.push((file_path, metadata));
|
||||
}
|
||||
// Scan DLC directory if exists
|
||||
let dlc_dir = game_dir.join("dlc");
|
||||
if dlc_dir.is_dir() {
|
||||
self.scan_tool_files_in_directory(
|
||||
&dlc_dir,
|
||||
&mut tool_generated_files,
|
||||
&mut backup_files,
|
||||
&mut all_chunks,
|
||||
)?;
|
||||
}
|
||||
|
||||
if tool_generated_files.is_empty() && backup_files.is_empty() {
|
||||
@@ -532,13 +577,13 @@ I'm sure I've checked the list, press Enter to continue"#,
|
||||
|
||||
// Check if there are any patches with higher numbers
|
||||
let has_higher_patches = all_chunks.iter().any(|c| {
|
||||
c.major_id == chunk_name.major_id
|
||||
&& c.sub_id == chunk_name.sub_id
|
||||
&& match (c.sub_id, c.sub_patch_id) {
|
||||
c.major_id() == chunk_name.major_id()
|
||||
&& c.sub_id() == chunk_name.sub_id()
|
||||
&& match (c.sub_id(), c.sub_patch_id()) {
|
||||
(Some(_), Some(patch_id)) => {
|
||||
patch_id > chunk_name.sub_patch_id.unwrap()
|
||||
patch_id > chunk_name.sub_patch_id().unwrap()
|
||||
}
|
||||
(None, Some(patch_id)) => patch_id > chunk_name.patch_id.unwrap(),
|
||||
(None, Some(patch_id)) => patch_id > chunk_name.patch_id().unwrap(),
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
@@ -550,6 +595,8 @@ I'm sure I've checked the list, press Enter to continue"#,
|
||||
} else {
|
||||
// no higher patches exist, safe to delete
|
||||
fs::remove_file(file_path)?;
|
||||
// remove from all_chunks
|
||||
all_chunks.retain(|c| c != chunk_name);
|
||||
println!(" Removed patch file");
|
||||
}
|
||||
}
|
||||
@@ -559,6 +606,56 @@ I'm sure I've checked the list, press Enter to continue"#,
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Scan tool generated files in a specific directory
|
||||
fn scan_tool_files_in_directory(
|
||||
&self,
|
||||
dir: &Path,
|
||||
tool_generated_files: &mut Vec<(std::path::PathBuf, PakMetadata)>,
|
||||
backup_files: &mut Vec<std::path::PathBuf>,
|
||||
all_chunks: &mut Vec<ChunkName>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let entries = fs::read_dir(dir)?;
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
if !entry.file_type()?.is_file() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_name = entry.file_name().to_string_lossy().to_string();
|
||||
let file_path = entry.path();
|
||||
|
||||
// check backup files
|
||||
if file_name.ends_with(".pak.backup") {
|
||||
backup_files.push(file_path);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check pak files
|
||||
if !file_name.ends_with(".pak") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if it's a chunk or DLC file
|
||||
let is_chunk = file_name.starts_with("re_chunk_");
|
||||
let is_dlc = file_name.starts_with("re_dlc_");
|
||||
|
||||
if !is_chunk && !is_dlc {
|
||||
continue;
|
||||
}
|
||||
|
||||
// collect chunk info
|
||||
if let Ok(chunk_name) = ChunkName::try_from_str(&file_name) {
|
||||
all_chunks.push(chunk_name.clone());
|
||||
}
|
||||
|
||||
// check if the file is generated by this tool
|
||||
if let Ok(Some(metadata)) = self.check_tool_generated_file(&file_path) {
|
||||
tool_generated_files.push((file_path, metadata));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// check if the file is generated by this tool, return metadata
|
||||
fn check_tool_generated_file(
|
||||
&self,
|
||||
|
204
src/chunk.rs
204
src/chunk.rs
@@ -5,29 +5,36 @@
|
||||
//! - Patch: re_chunk_XXX.pak.patch_XXX.pak
|
||||
//! - Sub: re_chunk_XXX.pak.sub_XXX.pak
|
||||
//! - Sub Patch: re_chunk_XXX.pak.sub_XXX.pak.patch_XXX.pak
|
||||
//! - DLC: re_dlc_stm_3308900.pak (and more)
|
||||
|
||||
use color_eyre::eyre;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ChunkComponent {
|
||||
/// Base chunk with major ID (re_chunk_XXX.pak)
|
||||
Base(u32),
|
||||
/// DLC chunk with DLC ID (re_dlc_stm_3308900.pak)
|
||||
Dlc(String),
|
||||
/// Patch chunk with patch ID (XXX in .patch_XXX.pak)
|
||||
Patch(u32),
|
||||
/// Sub chunk with sub ID (XXX in .sub_XXX.pak)
|
||||
Sub(u32),
|
||||
/// Sub patch chunk with sub patch ID (YYY in .sub_XXX.pak.patch_YYY.pak)
|
||||
SubPatch(u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ChunkName {
|
||||
/// Major chunk ID (XXX in re_chunk_XXX.pak)
|
||||
pub major_id: u32,
|
||||
/// Patch number (XXX in .patch_XXX.pak)
|
||||
pub patch_id: Option<u32>,
|
||||
/// Sub chunk ID (XXX in .sub_XXX.pak)
|
||||
pub sub_id: Option<u32>,
|
||||
/// Patch number for sub chunk (YYY in .sub_XXX.pak.patch_YYY.pak)
|
||||
pub sub_patch_id: Option<u32>,
|
||||
/// Chunk components
|
||||
pub components: Vec<ChunkComponent>,
|
||||
}
|
||||
|
||||
impl ChunkName {
|
||||
#[allow(dead_code)]
|
||||
/// Create a new base chunk name (re_chunk_XXX.pak)
|
||||
pub fn new(major_id: u32) -> Self {
|
||||
Self {
|
||||
major_id,
|
||||
patch_id: None,
|
||||
sub_id: None,
|
||||
sub_patch_id: None,
|
||||
components: vec![ChunkComponent::Base(major_id)],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,36 +49,87 @@ impl ChunkName {
|
||||
}
|
||||
|
||||
// every 2 parts is a component
|
||||
let components = dot_parts
|
||||
let part_pairs = dot_parts
|
||||
.chunks_exact(2)
|
||||
.map(|c| (c[0], c[1]))
|
||||
.collect::<Vec<(&str, &str)>>();
|
||||
|
||||
// check if all parts have the correct extension
|
||||
if !components.iter().all(|(_, ext)| *ext == "pak") {
|
||||
if !part_pairs.iter().all(|(_, ext)| *ext == "pak") {
|
||||
return Err(eyre::eyre!(
|
||||
"Invalid chunk name with invalid extension: {}",
|
||||
name
|
||||
));
|
||||
}
|
||||
|
||||
let mut this = Self::new(0);
|
||||
let mut components = Vec::new();
|
||||
let mut has_sub = false;
|
||||
|
||||
for (name, _) in components.iter() {
|
||||
let component = Self::parse_component(name)?;
|
||||
for (part_name, _) in part_pairs.iter() {
|
||||
let component = Self::parse_component(part_name)?;
|
||||
match component {
|
||||
Component::Major(id) => this.major_id = id,
|
||||
Component::Sub(id) => this.sub_id = Some(id),
|
||||
Component::Major(id) => {
|
||||
components.push(ChunkComponent::Base(id));
|
||||
}
|
||||
Component::Dlc(id) => {
|
||||
components.push(ChunkComponent::Dlc(id));
|
||||
}
|
||||
Component::Sub(id) => {
|
||||
components.push(ChunkComponent::Sub(id));
|
||||
has_sub = true;
|
||||
}
|
||||
Component::Patch(id) => {
|
||||
if this.sub_id.is_some() {
|
||||
this.sub_patch_id = Some(id);
|
||||
if has_sub {
|
||||
components.push(ChunkComponent::SubPatch(id));
|
||||
} else {
|
||||
this.patch_id = Some(id);
|
||||
components.push(ChunkComponent::Patch(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(this)
|
||||
Ok(Self { components })
|
||||
}
|
||||
|
||||
/// Get the major ID (base chunk ID)
|
||||
pub fn major_id(&self) -> Option<u32> {
|
||||
self.components.iter().find_map(|c| match c {
|
||||
ChunkComponent::Base(id) => Some(*id),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the patch ID
|
||||
pub fn patch_id(&self) -> Option<u32> {
|
||||
self.components.iter().find_map(|c| match c {
|
||||
ChunkComponent::Patch(id) => Some(*id),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the sub ID
|
||||
pub fn sub_id(&self) -> Option<u32> {
|
||||
self.components.iter().find_map(|c| match c {
|
||||
ChunkComponent::Sub(id) => Some(*id),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the sub patch ID
|
||||
pub fn sub_patch_id(&self) -> Option<u32> {
|
||||
self.components.iter().find_map(|c| match c {
|
||||
ChunkComponent::SubPatch(id) => Some(*id),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a sub patch component with the given ID
|
||||
pub fn with_sub_patch(&self, patch_id: u32) -> Self {
|
||||
let mut new_components = self.components.clone();
|
||||
new_components.push(ChunkComponent::SubPatch(patch_id));
|
||||
Self {
|
||||
components: new_components,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_component(name: &str) -> color_eyre::Result<Component> {
|
||||
@@ -82,6 +140,9 @@ impl ChunkName {
|
||||
.parse::<u32>()
|
||||
.map_err(|e| eyre::eyre!("Chunk name with invalid major ID: {}", e))?;
|
||||
Ok(Component::Major(major_id))
|
||||
} else if name.starts_with("re_dlc_") {
|
||||
let dlc_id = name.strip_prefix("re_dlc_").unwrap().to_string();
|
||||
Ok(Component::Dlc(dlc_id))
|
||||
} else if name.starts_with("patch_") {
|
||||
let patch_id = name
|
||||
.strip_prefix("patch_")
|
||||
@@ -107,18 +168,18 @@ impl ChunkName {
|
||||
|
||||
impl std::fmt::Display for ChunkName {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "re_chunk_{:03}.pak", self.major_id)?;
|
||||
if let Some(patch_id) = self.patch_id {
|
||||
write!(f, ".patch_{:03}.pak", patch_id)?;
|
||||
return Ok(());
|
||||
for (i, component) in self.components.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ".")?;
|
||||
}
|
||||
if let Some(sub_id) = self.sub_id {
|
||||
write!(f, ".sub_{:03}.pak", sub_id)?;
|
||||
match component {
|
||||
ChunkComponent::Base(id) => write!(f, "re_chunk_{:03}.pak", id)?,
|
||||
ChunkComponent::Dlc(id) => write!(f, "re_dlc_{}.pak", id)?,
|
||||
ChunkComponent::Patch(id) => write!(f, "patch_{:03}.pak", id)?,
|
||||
ChunkComponent::Sub(id) => write!(f, "sub_{:03}.pak", id)?,
|
||||
ChunkComponent::SubPatch(id) => write!(f, "patch_{:03}.pak", id)?,
|
||||
}
|
||||
if let Some(sub_patch_id) = self.sub_patch_id {
|
||||
write!(f, ".patch_{:03}.pak", sub_patch_id)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -131,16 +192,45 @@ impl PartialOrd for ChunkName {
|
||||
|
||||
impl Ord for ChunkName {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.major_id
|
||||
.cmp(&other.major_id)
|
||||
.then(self.sub_id.cmp(&other.sub_id))
|
||||
.then(self.patch_id.cmp(&other.patch_id))
|
||||
.then(self.sub_patch_id.cmp(&other.sub_patch_id))
|
||||
// compare by component count first
|
||||
self.components
|
||||
.len()
|
||||
.cmp(&other.components.len())
|
||||
.then_with(|| {
|
||||
// compare each component
|
||||
for (a, b) in self.components.iter().zip(other.components.iter()) {
|
||||
let cmp = match (a, b) {
|
||||
(ChunkComponent::Base(a), ChunkComponent::Base(b)) => a.cmp(b),
|
||||
(ChunkComponent::Dlc(a), ChunkComponent::Dlc(b)) => a.cmp(b),
|
||||
(ChunkComponent::Patch(a), ChunkComponent::Patch(b)) => a.cmp(b),
|
||||
(ChunkComponent::Sub(a), ChunkComponent::Sub(b)) => a.cmp(b),
|
||||
(ChunkComponent::SubPatch(a), ChunkComponent::SubPatch(b)) => a.cmp(b),
|
||||
// compare by component type priority
|
||||
(ChunkComponent::Base(_), _) => std::cmp::Ordering::Less,
|
||||
(_, ChunkComponent::Base(_)) => std::cmp::Ordering::Greater,
|
||||
(ChunkComponent::Dlc(_), _) => std::cmp::Ordering::Less,
|
||||
(_, ChunkComponent::Dlc(_)) => std::cmp::Ordering::Greater,
|
||||
(ChunkComponent::Sub(_), _) => std::cmp::Ordering::Less,
|
||||
(_, ChunkComponent::Sub(_)) => std::cmp::Ordering::Greater,
|
||||
(ChunkComponent::Patch(_), ChunkComponent::SubPatch(_)) => {
|
||||
std::cmp::Ordering::Less
|
||||
}
|
||||
(ChunkComponent::SubPatch(_), ChunkComponent::Patch(_)) => {
|
||||
std::cmp::Ordering::Greater
|
||||
}
|
||||
};
|
||||
if cmp != std::cmp::Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
std::cmp::Ordering::Equal
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
enum Component {
|
||||
Major(u32),
|
||||
Dlc(String),
|
||||
Patch(u32),
|
||||
Sub(u32),
|
||||
}
|
||||
@@ -170,5 +260,45 @@ mod tests {
|
||||
sub_patch.to_string(),
|
||||
"re_chunk_000.pak.sub_000.pak.patch_001.pak"
|
||||
);
|
||||
|
||||
// Test DLC chunk
|
||||
let dlc = ChunkName::try_from_str("re_dlc_stm_3308900.pak").unwrap();
|
||||
assert_eq!(dlc.to_string(), "re_dlc_stm_3308900.pak");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chunk_helper_methods() {
|
||||
// Test base chunk helper methods
|
||||
let base = ChunkName::new(123);
|
||||
assert_eq!(base.major_id(), Some(123));
|
||||
assert_eq!(base.patch_id(), None);
|
||||
assert_eq!(base.sub_id(), None);
|
||||
assert_eq!(base.sub_patch_id(), None);
|
||||
|
||||
// Test complex chunk helper methods
|
||||
let complex =
|
||||
ChunkName::try_from_str("re_chunk_456.pak.sub_789.pak.patch_012.pak").unwrap();
|
||||
assert_eq!(complex.major_id(), Some(456));
|
||||
assert_eq!(complex.patch_id(), None);
|
||||
assert_eq!(complex.sub_id(), Some(789));
|
||||
assert_eq!(complex.sub_patch_id(), Some(12));
|
||||
|
||||
// Test DLC chunk helper methods
|
||||
let dlc = ChunkName::try_from_str("re_dlc_stm_3308900.pak").unwrap();
|
||||
assert_eq!(dlc.major_id(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_sub_patch() {
|
||||
let base = ChunkName::try_from_str("re_chunk_000.pak.sub_001.pak").unwrap();
|
||||
let with_patch = base.with_sub_patch(99);
|
||||
|
||||
assert_eq!(with_patch.major_id(), Some(0));
|
||||
assert_eq!(with_patch.sub_id(), Some(1));
|
||||
assert_eq!(with_patch.sub_patch_id(), Some(99));
|
||||
assert_eq!(
|
||||
with_patch.to_string(),
|
||||
"re_chunk_000.pak.sub_001.pak.patch_099.pak"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user