diff --git a/Cargo.lock b/Cargo.lock index e3c9836..d4d7ed7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -616,6 +616,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "jobserver" version = "0.1.33" @@ -718,6 +724,8 @@ dependencies = [ "rayon", "re-tex", "ree-pak-core", + "serde", + "serde_json", ] [[package]] @@ -1183,6 +1191,12 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "scopeguard" version = "1.2.0" @@ -1209,6 +1223,18 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "serde_json" +version = "1.0.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.9" diff --git a/Cargo.toml b/Cargo.toml index e501faa..1fc9173 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,5 @@ rayon = "1.10" parking_lot = "0.12" colored = "3.0" fs-err = "3.1.1" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.141" diff --git a/src/app.rs b/src/app.rs index a82dd93..c26badd 100644 --- a/src/app.rs +++ b/src/app.rs @@ -24,7 +24,7 @@ use ree_pak_core::{ write::FileOptions, }; -use crate::{chunk::ChunkName, 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 @@ -111,7 +111,15 @@ impl App { .truncate(true) .write(true) .open(output_path)?; - let pak_writer = ree_pak_core::write::PakWriter::new(out_file, entries.len() as u64); + let mut pak_writer = ree_pak_core::write::PakWriter::new(out_file, entries.len() as u64); + + // write metadata + let metadata = PakMetadata { + version: 1, + is_uncompressed_patch: true, + }; + metadata.write_to_pak(&mut pak_writer)?; + let pak_writer_mtx = Arc::new(Mutex::new(pak_writer)); let bar = ProgressBar::new(entries.len() as u64); diff --git a/src/main.rs b/src/main.rs index 66622a2..ba24cff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod app; mod chunk; +mod metadata; mod util; use colored::Colorize; diff --git a/src/metadata.rs b/src/metadata.rs new file mode 100644 index 0000000..290a6c9 --- /dev/null +++ b/src/metadata.rs @@ -0,0 +1,61 @@ +//! Extended metadata for generated pak files. + +use std::io::{self, Read, Write}; + +use ree_pak_core::{ + filename::{FileNameExt, FileNameFull}, + pak::PakArchive, + read::archive::PakArchiveReader, + write::{FileOptions, PakWriter}, +}; +use serde::{Deserialize, Serialize}; + +const METADATA_KEY: &str = "__TEX_DECOMPRESSOR_METADATA__"; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PakMetadata { + pub version: u32, + pub is_uncompressed_patch: bool, +} + +impl PakMetadata { + pub fn from_pak_archive( + reader: R, + pak_archive: &PakArchive, + ) -> color_eyre::Result> + where + R: io::Read + io::Seek, + { + let key_name = FileNameFull::new(METADATA_KEY); + let entry = pak_archive + .entries() + .iter() + .find(|entry| entry.hash() == key_name.hash_mixed()); + + if let Some(entry) = entry { + // read file + let mut archive_reader = PakArchiveReader::new(reader, pak_archive); + let mut entry_reader = archive_reader.owned_entry_reader(entry.clone())?; + let mut buf = Vec::new(); + entry_reader.read_to_end(&mut buf)?; + + let metadata = serde_json::from_slice(&buf)?; + Ok(Some(metadata)) + } else { + Ok(None) + } + } + + pub fn write_to_pak(&self, pak_writer: &mut PakWriter) -> color_eyre::Result<()> + where + W: io::Write + io::Seek, + { + let json_str = serde_json::to_string(self)?; + let json_bytes = json_str.as_bytes(); + + pak_writer.start_file(METADATA_KEY, FileOptions::default())?; + pak_writer.write_all(json_bytes)?; + + Ok(()) + } +}