Compare commits
5 Commits
v0.2.1
...
96acb36767
Author | SHA1 | Date | |
---|---|---|---|
96acb36767
|
|||
6e401c86cf
|
|||
1686114181
|
|||
4ca65979c9
|
|||
6e493b3fdc
|
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -3,6 +3,7 @@ name: Release Build
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ["v*"]
|
tags: ["v*"]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -30,8 +31,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir release
|
mkdir release
|
||||||
copy target/release/${{ env.BINARY_NAME }}.exe release/
|
copy target/release/${{ env.BINARY_NAME }}.exe release/
|
||||||
copy README.md release/
|
|
||||||
copy LICENSE release/
|
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
1479
Cargo.lock
generated
1479
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mhws-tex-decompressor"
|
name = "mhws-tex-decompressor"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -12,13 +12,15 @@ re-tex = { git = "https://github.com/eigeen/re-tex.git", branch = "main" }
|
|||||||
ree-pak-core = { git = "https://github.com/eigeen/ree-pak-rs.git", branch = "main" }
|
ree-pak-core = { git = "https://github.com/eigeen/ree-pak-rs.git", branch = "main" }
|
||||||
|
|
||||||
# UI
|
# UI
|
||||||
dialoguer = "0.11"
|
dialoguer = "0.12"
|
||||||
indicatif = "0.18"
|
indicatif = "0.18"
|
||||||
|
colored = "3.0"
|
||||||
|
|
||||||
color-eyre = "0.6"
|
color-eyre = "0.6"
|
||||||
rayon = "1.11"
|
rayon = "1.11"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
colored = "3.0"
|
|
||||||
fs-err = "3.1"
|
fs-err = "3.1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
tokio = { version = "1.47", features = ["parking_lot", "rt-multi-thread", "macros"] }
|
||||||
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
|
Binary file not shown.
25
src/app.rs
25
src/app.rs
@@ -18,15 +18,12 @@ use parking_lot::Mutex;
|
|||||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
use re_tex::tex::Tex;
|
use re_tex::tex::Tex;
|
||||||
use ree_pak_core::{
|
use ree_pak_core::{
|
||||||
filename::{FileNameExt, FileNameTable},
|
filename::FileNameTable, pak::PakEntry, read::archive::PakArchiveReader,
|
||||||
pak::PakEntry,
|
utf16_hash::Utf16HashExt, write::FileOptions,
|
||||||
read::archive::PakArchiveReader,
|
|
||||||
write::FileOptions,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{chunk::ChunkName, metadata::PakMetadata, util::human_bytes};
|
use crate::{chunk::ChunkName, metadata::PakMetadata, util::human_bytes};
|
||||||
|
|
||||||
const FILE_NAME_LIST: &[u8] = include_bytes!("../assets/MHWs_STM_Release.list.zst");
|
|
||||||
const AUTO_CHUNK_SELECTION_SIZE_THRESHOLD: usize = 50 * 1024 * 1024; // 50MB
|
const AUTO_CHUNK_SELECTION_SIZE_THRESHOLD: usize = 50 * 1024 * 1024; // 50MB
|
||||||
const FALSE_TRUE_SELECTION: [&str; 2] = ["False", "True"];
|
const FALSE_TRUE_SELECTION: [&str; 2] = ["False", "True"];
|
||||||
|
|
||||||
@@ -74,13 +71,11 @@ impl App {
|
|||||||
println!();
|
println!();
|
||||||
|
|
||||||
println!("Loading embedded file name table...");
|
println!("Loading embedded file name table...");
|
||||||
let filename_table = FileNameTable::from_bytes(FILE_NAME_LIST)?;
|
|
||||||
self.filename_table = Some(filename_table);
|
|
||||||
|
|
||||||
// Mode selection
|
// Mode selection
|
||||||
let mode = Select::with_theme(&ColorfulTheme::default())
|
let mode = Select::with_theme(&ColorfulTheme::default())
|
||||||
.with_prompt("Select mode")
|
.with_prompt("Select mode")
|
||||||
.items(&["Automatic", "Manual", "Restore"])
|
.items(["Automatic", "Manual", "Restore"])
|
||||||
.default(0)
|
.default(0)
|
||||||
.interact()?;
|
.interact()?;
|
||||||
let mode = Mode::from_index(mode)?;
|
let mode = Mode::from_index(mode)?;
|
||||||
@@ -277,7 +272,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bar.inc(1);
|
bar.inc(1);
|
||||||
if bar.position() % 100 == 0 {
|
if bar.position().is_multiple_of(100) {
|
||||||
bar.set_message(
|
bar.set_message(
|
||||||
HumanBytes(bytes_written.load(Ordering::SeqCst) as u64).to_string(),
|
HumanBytes(bytes_written.load(Ordering::SeqCst) as u64).to_string(),
|
||||||
);
|
);
|
||||||
@@ -369,7 +364,7 @@ I'm sure I've checked the list, press Enter to continue"#,
|
|||||||
"Replace original files with uncompressed files? (Will automatically backup original files)",
|
"Replace original files with uncompressed files? (Will automatically backup original files)",
|
||||||
)
|
)
|
||||||
.default(0)
|
.default(0)
|
||||||
.items(&FALSE_TRUE_SELECTION)
|
.items(FALSE_TRUE_SELECTION)
|
||||||
.interact()
|
.interact()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let use_replace_mode = use_replace_mode == 1;
|
let use_replace_mode = use_replace_mode == 1;
|
||||||
@@ -405,7 +400,7 @@ I'm sure I've checked the list, press Enter to continue"#,
|
|||||||
let new_patch_id = max_patch_id + 1;
|
let new_patch_id = max_patch_id + 1;
|
||||||
|
|
||||||
// Create a new chunk name
|
// Create a new chunk name
|
||||||
let output_chunk_name = chunk_name.with_sub_patch(new_patch_id);
|
let output_chunk_name = chunk_name.set_sub_patch(new_patch_id);
|
||||||
|
|
||||||
// Add the new patch to the chunk list so it can be found in subsequent processing
|
// Add the new patch to the chunk list so it can be found in subsequent processing
|
||||||
all_chunk_names.push(output_chunk_name.clone());
|
all_chunk_names.push(output_chunk_name.clone());
|
||||||
@@ -462,7 +457,7 @@ I'm sure I've checked the list, press Enter to continue"#,
|
|||||||
"Package all files, including non-tex files (for replacing original files)",
|
"Package all files, including non-tex files (for replacing original files)",
|
||||||
)
|
)
|
||||||
.default(0)
|
.default(0)
|
||||||
.items(&FALSE_TRUE_SELECTION)
|
.items(FALSE_TRUE_SELECTION)
|
||||||
.interact()
|
.interact()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let use_full_package_mode = use_full_package_mode == 1;
|
let use_full_package_mode = use_full_package_mode == 1;
|
||||||
@@ -470,7 +465,7 @@ I'm sure I've checked the list, press Enter to continue"#,
|
|||||||
let use_feature_clone = Select::with_theme(&ColorfulTheme::default())
|
let use_feature_clone = Select::with_theme(&ColorfulTheme::default())
|
||||||
.with_prompt("Clone feature flags from original file?")
|
.with_prompt("Clone feature flags from original file?")
|
||||||
.default(1)
|
.default(1)
|
||||||
.items(&FALSE_TRUE_SELECTION)
|
.items(FALSE_TRUE_SELECTION)
|
||||||
.interact()
|
.interact()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let use_feature_clone = use_feature_clone == 1;
|
let use_feature_clone = use_feature_clone == 1;
|
||||||
@@ -698,13 +693,13 @@ fn is_tex_file(hash: u64, file_name_table: &FileNameTable) -> bool {
|
|||||||
let Some(file_name) = file_name_table.get_file_name(hash) else {
|
let Some(file_name) = file_name_table.get_file_name(hash) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
file_name.get_name().ends_with(".tex.241106027")
|
file_name.to_string().unwrap().ends_with(".tex.241106027")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_to_pak<W>(
|
fn write_to_pak<W>(
|
||||||
writer: &mut ree_pak_core::write::PakWriter<W>,
|
writer: &mut ree_pak_core::write::PakWriter<W>,
|
||||||
entry: &PakEntry,
|
entry: &PakEntry,
|
||||||
file_name: impl FileNameExt,
|
file_name: impl Utf16HashExt,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
use_feature_clone: bool,
|
use_feature_clone: bool,
|
||||||
) -> color_eyre::Result<usize>
|
) -> color_eyre::Result<usize>
|
||||||
|
20
src/chunk.rs
20
src/chunk.rs
@@ -123,10 +123,20 @@ impl ChunkName {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a sub patch component with the given ID
|
/// Add or replace a sub patch component with the given ID
|
||||||
pub fn with_sub_patch(&self, patch_id: u32) -> Self {
|
pub fn set_sub_patch(&self, patch_id: u32) -> Self {
|
||||||
let mut new_components = self.components.clone();
|
let mut new_components = self.components.clone();
|
||||||
new_components.push(ChunkComponent::SubPatch(patch_id));
|
|
||||||
|
// Check if SubPatch already exists and replace it
|
||||||
|
if let Some(pos) = new_components
|
||||||
|
.iter()
|
||||||
|
.position(|c| matches!(c, ChunkComponent::SubPatch(_)))
|
||||||
|
{
|
||||||
|
new_components[pos] = ChunkComponent::SubPatch(patch_id);
|
||||||
|
} else {
|
||||||
|
new_components.push(ChunkComponent::SubPatch(patch_id));
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
components: new_components,
|
components: new_components,
|
||||||
}
|
}
|
||||||
@@ -289,9 +299,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_with_sub_patch() {
|
fn test_set_sub_patch() {
|
||||||
let base = ChunkName::try_from_str("re_chunk_000.pak.sub_001.pak").unwrap();
|
let base = ChunkName::try_from_str("re_chunk_000.pak.sub_001.pak").unwrap();
|
||||||
let with_patch = base.with_sub_patch(99);
|
let with_patch = base.set_sub_patch(99);
|
||||||
|
|
||||||
assert_eq!(with_patch.major_id(), Some(0));
|
assert_eq!(with_patch.major_id(), Some(0));
|
||||||
assert_eq!(with_patch.sub_id(), Some(1));
|
assert_eq!(with_patch.sub_id(), Some(1));
|
||||||
|
@@ -3,9 +3,9 @@
|
|||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
use ree_pak_core::{
|
use ree_pak_core::{
|
||||||
filename::{FileNameExt, FileNameFull},
|
|
||||||
pak::PakArchive,
|
pak::PakArchive,
|
||||||
read::archive::PakArchiveReader,
|
read::archive::PakArchiveReader,
|
||||||
|
utf16_hash::Utf16HashExt,
|
||||||
write::{FileOptions, PakWriter},
|
write::{FileOptions, PakWriter},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -37,11 +37,10 @@ impl PakMetadata {
|
|||||||
where
|
where
|
||||||
R: io::Read + io::Seek,
|
R: io::Read + io::Seek,
|
||||||
{
|
{
|
||||||
let key_name = FileNameFull::new(METADATA_KEY);
|
|
||||||
let entry = pak_archive
|
let entry = pak_archive
|
||||||
.entries()
|
.entries()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|entry| entry.hash() == key_name.hash_mixed());
|
.find(|entry| entry.hash() == METADATA_KEY.hash_mixed());
|
||||||
|
|
||||||
if let Some(entry) = entry {
|
if let Some(entry) = entry {
|
||||||
// read file
|
// read file
|
||||||
|
Reference in New Issue
Block a user