5 Commits
v0.2.1 ... main

Author SHA1 Message Date
96acb36767 update TU3 list file 2025-10-02 21:15:02 +08:00
6e401c86cf update deps, add reqwest and clippy 2025-10-02 20:37:04 +08:00
1686114181 fix: error setting new sub patch id
Some checks failed
Release Build / build (push) Has been cancelled
2025-08-17 21:14:07 +08:00
4ca65979c9 workflows 2025-08-15 15:04:03 +08:00
6e493b3fdc update to TU2.5 list file 2025-08-15 14:59:10 +08:00
7 changed files with 1279 additions and 261 deletions

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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.

View File

@@ -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>

View File

@@ -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();
// 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)); 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));

View File

@@ -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