diff --git a/Cargo.toml b/Cargo.toml index 7f2fbdb..45b1aa6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ hexen-haus = ["memchr", "utils-str"] hexen-haus-arc = ["hexen-haus"] hexen-haus-img = ["hexen-haus", "image"] kirikiri = ["emote-psb", "fancy-regex", "flate2", "json", "lz4", "utils-escape"] -kirikiri-arc = ["kirikiri", "adler", "fastcdc", "flate2", "parse-size", "sha2", "xp3", "utils-threadpool"] +kirikiri-arc = ["kirikiri", "adler", "fastcdc", "flate2", "parse-size", "sha2", "xp3", "zstd", "utils-threadpool"] kirikiri-img = ["kirikiri", "image", "libtlg-rs"] silky = [] softpal = ["int-enum"] diff --git a/src/args.rs b/src/args.rs index 935e76f..f7e6a41 100644 --- a/src/args.rs +++ b/src/args.rs @@ -508,9 +508,13 @@ pub struct Arg { /// Disable compressing index in Kirikiri XP3 archive when creating XP3 archive. pub xp3_no_compress_index: bool, #[cfg(feature = "kirikiri-arc")] - #[arg(long, global = true, default_value_t = num_cpus::get())] + #[arg(long, global = true, default_value_t = num_cpus::get(), visible_alias = "xp3-compress-jobs")] /// Workers count for compress files in Kirikiri XP3 archive when creating in parallel. pub xp3_compress_workers: usize, + #[cfg(feature = "kirikiri-arc")] + #[arg(long, global = true)] + /// Use zstd compression for files in Kirikiri XP3 archive when creating. (Warning: Kirikiri engine don't support this. Hook is required.) + pub xp3_zstd: bool, #[command(subcommand)] /// Command pub command: Command, diff --git a/src/main.rs b/src/main.rs index 15d3b08..406bbc2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2757,6 +2757,8 @@ fn main() { xp3_compress_index: !arg.xp3_no_compress_index, #[cfg(feature = "kirikiri-arc")] xp3_compress_workers: arg.xp3_compress_workers, + #[cfg(feature = "kirikiri-arc")] + xp3_zstd: arg.xp3_zstd, }); match &arg.command { args::Command::Export { input, output } => { diff --git a/src/scripts/kirikiri/archive/xp3pack/writer.rs b/src/scripts/kirikiri/archive/xp3pack/writer.rs index 218d3ac..c182a17 100644 --- a/src/scripts/kirikiri/archive/xp3pack/writer.rs +++ b/src/scripts/kirikiri/archive/xp3pack/writer.rs @@ -73,6 +73,8 @@ pub struct Xp3ArchiveWriter { stats: Arc, compress_workers: Option>>>, processing_segments: Arc>>, + use_zstd: bool, + zstd_compression_level: i32, } impl Xp3ArchiveWriter> { @@ -113,6 +115,8 @@ impl Xp3ArchiveWriter> { None }, processing_segments: Arc::new(Mutex::new(HashSet::new())), + use_zstd: config.xp3_zstd, + zstd_compression_level: config.zstd_compression_level, }) } } @@ -194,6 +198,8 @@ impl Archive for Xp3ArchiveWriter { let zlib_compression_level = self.zlib_compression_level; let workers = self.compress_workers.clone(); let processiong_segments = self.processing_segments.clone(); + let use_zstd = self.use_zstd; + let zstd_compression_level = self.zstd_compression_level; self.runner.execute( move |_| { let mut reader = reader; @@ -234,14 +240,23 @@ impl Archive for Xp3ArchiveWriter { workers.execute( move |_| { let data = { - let mut e = flate2::write::ZlibEncoder::new( - Vec::new(), - flate2::Compression::new( - zlib_compression_level, - ), - ); - e.write_all(&seg)?; - e.finish()? + if use_zstd { + let mut e = zstd::stream::Encoder::new( + Vec::new(), + zstd_compression_level, + )?; + e.write_all(&seg)?; + e.finish()? + } else { + let mut e = flate2::write::ZlibEncoder::new( + Vec::new(), + flate2::Compression::new( + zlib_compression_level, + ), + ); + e.write_all(&seg)?; + e.finish()? + } }; let mut file = file.lock_blocking(); let start = file.seek(std::io::SeekFrom::End(0))?; @@ -375,11 +390,19 @@ impl Archive for Xp3ArchiveWriter { let start = file.seek(std::io::SeekFrom::End(0))?; let size = { let mut writer = if is_compressed { - let e = flate2::write::ZlibEncoder::new( - &mut *file, - flate2::Compression::new(zlib_compression_level), - ); - Box::new(e) as Box + if use_zstd { + let e = zstd::stream::Encoder::new( + &mut *file, + zstd_compression_level, + )?; + Box::new(e) as Box + } else { + let e = flate2::write::ZlibEncoder::new( + &mut *file, + flate2::Compression::new(zlib_compression_level), + ); + Box::new(e) as Box + } } else { Box::new(&mut *file) as Box }; @@ -474,12 +497,18 @@ impl Archive for Xp3ArchiveWriter { } let index_data = index_data.into_inner(); if self.compress_index { - let mut e = flate2::write::ZlibEncoder::new( - Vec::new(), - flate2::Compression::new(self.zlib_compression_level), - ); - e.write_all(&index_data)?; - let compressed_index = e.finish()?; + let compressed_index = if self.use_zstd { + let mut e = zstd::stream::Encoder::new(Vec::new(), self.zstd_compression_level)?; + e.write_all(&index_data)?; + e.finish()? + } else { + let mut e = flate2::write::ZlibEncoder::new( + Vec::new(), + flate2::Compression::new(self.zlib_compression_level), + ); + e.write_all(&index_data)?; + e.finish()? + }; file.write_u8(TVP_XP3_INDEX_ENCODE_ZLIB)?; file.write_u64(compressed_index.len() as u64)?; file.write_u64(index_data.len() as u64)?; diff --git a/src/types.rs b/src/types.rs index 7389ba2..0de026d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -495,6 +495,9 @@ pub struct ExtraConfig { #[default(num_cpus::get())] /// Workers count for compress files in Kirikiri XP3 archive when creating in parallel. Default is CPU cores count. pub xp3_compress_workers: usize, + #[cfg(feature = "kirikiri-arc")] + /// Use zstd compression for files in Kirikiri XP3 archive when creating. (Warning: Kirikiri engine don't support this. Hook is required.) + pub xp3_zstd: bool, } #[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]