From 527354a3aff7bc8fc2b5617021735aff38cef47d Mon Sep 17 00:00:00 2001 From: Eigeen Date: Thu, 20 Mar 2025 11:06:22 +0800 Subject: [PATCH] full package and feature clone --- Cargo.lock | 4 +- Cargo.toml | 10 ++--- src/main.rs | 120 +++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 97 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bb40bb..094aa5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -494,7 +494,6 @@ dependencies = [ [[package]] name = "re-tex" version = "0.1.0" -source = "git+https://github.com/eigeen/re-tex.git?branch=main#8d0278fa1064791e2e58049c55f9c9b784b53d62" dependencies = [ "better_default", "byteorder", @@ -514,8 +513,7 @@ dependencies = [ [[package]] name = "ree-pak-core" -version = "0.3.1" -source = "git+https://github.com/eigeen/ree-pak-rs.git?branch=main#0f8e027f7dc60d3be950d403c8ae8c74c87b9798" +version = "0.4.0" dependencies = [ "bitflags", "byteorder", diff --git a/Cargo.toml b/Cargo.toml index e973325..69c9745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,12 @@ version = "0.1.2" edition = "2024" [dependencies] -# # local development -# re-tex = { path = "../re-tex" } -# ree-pak-core = { path = "../../ree-pak-rs/ree-pak-core" } +# local development +re-tex = { path = "../re-tex" } +ree-pak-core = { path = "../../ree-pak-rs/ree-pak-core" } -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" } +# 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" } dialoguer = "0.11" eyre = "0.6" diff --git a/src/main.rs b/src/main.rs index 09d16f1..ffdd2aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,17 +9,22 @@ use std::{ time::Duration, }; -use dialoguer::{Input, theme::ColorfulTheme}; +use dialoguer::{Input, Select, theme::ColorfulTheme}; use indicatif::{HumanBytes, ProgressBar, ProgressStyle}; use parking_lot::Mutex; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use re_tex::tex::Tex; -use ree_pak_core::{filename::FileNameTable, read::archive::PakArchiveReader, write::FileOptions}; +use ree_pak_core::{ + filename::{FileNameExt, FileNameTable}, + pak::PakEntry, + read::archive::PakArchiveReader, + write::FileOptions, +}; const FILE_NAME_LIST: &[u8] = include_bytes!("../assets/MHWs_STM_Release.list.zst"); fn main() { - println!("Version {}. Tool by @Eigeen", env!("CARGO_PKG_VERSION")); + println!("Version v{} - Tool by @Eigeen", env!("CARGO_PKG_VERSION")); if let Err(e) = main_entry() { eprintln!("Error: {e}"); @@ -36,12 +41,31 @@ fn main_entry() -> eyre::Result<()> { .interact_text() .unwrap(); - println!("Input file: {}", input); let input_path = Path::new(&input); if !input_path.is_file() { eyre::bail!("input file not exists."); } + const FALSE_TRUE_SELECTION: [&str; 2] = ["False", "True"]; + + let use_full_package_mode = Select::with_theme(&ColorfulTheme::default()) + .with_prompt( + "Package all files, including non-tex parts (suitable for replacing original files)", + ) + .default(0) + .items(&FALSE_TRUE_SELECTION) + .interact() + .unwrap(); + let use_full_package_mode = use_full_package_mode == 1; + + let use_feature_clone = Select::with_theme(&ColorfulTheme::default()) + .with_prompt("Clone feature flags from original file? (experimental)") + .default(0) + .items(&FALSE_TRUE_SELECTION) + .interact() + .unwrap(); + let use_feature_clone = use_feature_clone == 1; + println!("Loading embedded file name table..."); let filename_table = FileNameTable::from_bytes(FILE_NAME_LIST)?; @@ -54,17 +78,16 @@ fn main_entry() -> eyre::Result<()> { let archive_reader_mtx = Mutex::new(archive_reader); // filtered entries - println!("Filtering entries..."); - let entries = pak_archive - .entries() - .iter() - .filter(|entry| { - let Some(file_name) = filename_table.get_file_name(entry.hash()) else { - return false; - }; - file_name.get_name().ends_with(".tex.241106027") - }) - .collect::>(); + let entries = if use_full_package_mode { + pak_archive.entries().iter().collect::>() + } else { + println!("Filtering entries..."); + pak_archive + .entries() + .iter() + .filter(|entry| is_tex_file(entry.hash(), &filename_table)) + .collect::>() + }; // new pak archive let output_path = input_path.with_extension("uncompressed.pak"); @@ -97,24 +120,37 @@ fn main_entry() -> eyre::Result<()> { let mut archive_reader = archive_reader_mtx.lock(); archive_reader.owned_entry_reader(entry.clone())? }; - let mut tex = Tex::from_reader(&mut entry_reader)?; - // decompress mipmaps - tex.batch_decompress()?; - let tex_bytes = tex.as_bytes()?; - bytes_written.fetch_add(tex_bytes.len() as usize, Ordering::SeqCst); - - // save file - let file_name = filename_table.get_file_name(entry.hash()).unwrap().clone(); - { + if !is_tex_file(entry.hash(), &filename_table) { + // plain file, just copy + let mut buf = vec![]; + std::io::copy(&mut entry_reader, &mut buf)?; let mut pak_writer = pak_writer_mtx.lock(); - // clone attributes from original file - pak_writer.start_file( - file_name, - FileOptions::default().with_unk_attr(*entry.unk_attr()), + let write_bytes = write_to_pak( + &mut pak_writer, + entry, + entry.hash(), + &buf, + use_feature_clone, )?; - pak_writer.write_all(&tex_bytes)?; + bytes_written.fetch_add(write_bytes, Ordering::SeqCst); + } else { + let mut tex = Tex::from_reader(&mut entry_reader)?; + // decompress mipmaps + tex.batch_decompress()?; + + let tex_bytes = tex.as_bytes()?; + let mut pak_writer = pak_writer_mtx.lock(); + let write_bytes = write_to_pak( + &mut pak_writer, + entry, + entry.hash(), + &tex_bytes, + use_feature_clone, + )?; + bytes_written.fetch_add(write_bytes, Ordering::SeqCst); } + bar.inc(1); if bar.position() % 100 == 0 { bar.set_message( @@ -145,6 +181,32 @@ fn main_entry() -> eyre::Result<()> { Ok(()) } +fn is_tex_file(hash: u64, file_name_table: &FileNameTable) -> bool { + let Some(file_name) = file_name_table.get_file_name(hash) else { + return false; + }; + file_name.get_name().ends_with(".tex.241106027") +} + +fn write_to_pak( + writer: &mut ree_pak_core::write::PakWriter, + entry: &PakEntry, + file_name: impl FileNameExt, + data: &[u8], + use_feature_clone: bool, +) -> eyre::Result +where + W: io::Write + io::Seek, +{ + let mut file_options = FileOptions::default(); + if use_feature_clone { + file_options = file_options.with_unk_attr(*entry.unk_attr()) + } + writer.start_file(file_name, file_options)?; + writer.write_all(data)?; + Ok(data.len()) +} + fn wait_for_exit() { let _: String = Input::with_theme(&ColorfulTheme::default()) .allow_empty(true)