Add ZSTD compress support when pack xp3 archive

This commit is contained in:
2025-10-11 22:48:18 +08:00
parent 6a5550e4c3
commit 0e01dcc194
5 changed files with 59 additions and 21 deletions

View File

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

View File

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

View File

@@ -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 } => {

View File

@@ -73,6 +73,8 @@ pub struct Xp3ArchiveWriter<T: Write + Seek> {
stats: Arc<Stats>,
compress_workers: Option<Arc<ThreadPool<Result<()>>>>,
processing_segments: Arc<Mutex<HashSet<[u8; 32]>>>,
use_zstd: bool,
zstd_compression_level: i32,
}
impl Xp3ArchiveWriter<std::io::BufWriter<std::fs::File>> {
@@ -113,6 +115,8 @@ impl Xp3ArchiveWriter<std::io::BufWriter<std::fs::File>> {
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<T: Write + Seek + Sync + Send + 'static> Archive for Xp3ArchiveWriter<T> {
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<T: Write + Seek + Sync + Send + 'static> Archive for Xp3ArchiveWriter<T> {
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<T: Write + Seek + Sync + Send + 'static> Archive for Xp3ArchiveWriter<T> {
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<dyn Write>
if use_zstd {
let e = zstd::stream::Encoder::new(
&mut *file,
zstd_compression_level,
)?;
Box::new(e) as Box<dyn Write>
} else {
let e = flate2::write::ZlibEncoder::new(
&mut *file,
flate2::Compression::new(zlib_compression_level),
);
Box::new(e) as Box<dyn Write>
}
} else {
Box::new(&mut *file) as Box<dyn Write>
};
@@ -474,12 +497,18 @@ impl<T: Write + Seek + Sync + Send + 'static> Archive for Xp3ArchiveWriter<T> {
}
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)?;

View File

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