diff --git a/src/args.rs b/src/args.rs index 5dfcf83..b5b17bf 100644 --- a/src/args.rs +++ b/src/args.rs @@ -205,6 +205,10 @@ pub struct Arg { #[arg(long, global = true, action = ArgAction::SetTrue)] /// Whether to compress files in BGI archive when packing BGI archive. pub bgi_compress_file: bool, + #[cfg(feature = "bgi-arc")] + #[arg(long, global = true, default_value_t = 3, value_parser = crate::scripts::bgi::archive::dsc::parse_min_length)] + /// Minimum length of match size for DSC compression. Possible values are 2-256. + pub bgi_compress_min_len: usize, #[cfg(feature = "kirikiri-img")] #[arg(long, global = true)] /// Whether to overlay PIMG images. (By default, true if all layers are not group layers.) diff --git a/src/main.rs b/src/main.rs index 5b5df6a..4996ecc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1467,7 +1467,7 @@ pub fn create_file( input: &str, output: Option<&str>, arg: &args::Arg, - _config: &types::ExtraConfig, + config: &types::ExtraConfig, ) -> anyhow::Result<()> { let typ = match &arg.script_type { Some(t) => t, @@ -1502,7 +1502,7 @@ pub fn create_file( pb.to_string_lossy().into_owned() } }; - builder.create_image_file_filename(data, &output, _config)?; + builder.create_image_file_filename(data, &output, config)?; return Ok(()); } @@ -1531,6 +1531,7 @@ pub fn create_file( &output, get_encoding(arg, builder), get_output_encoding(arg), + config, )?; Ok(()) } @@ -1591,6 +1592,8 @@ fn main() { )), #[cfg(feature = "bgi-arc")] bgi_compress_file: arg.bgi_compress_file, + #[cfg(feature = "bgi-arc")] + bgi_compress_min_len: arg.bgi_compress_min_len, #[cfg(feature = "kirikiri-img")] kirikiri_pimg_overlay: arg.kirikiri_pimg_overlay, #[cfg(feature = "artemis-arc")] diff --git a/src/scripts/artemis/asb.rs b/src/scripts/artemis/asb.rs index ae540e2..af58286 100644 --- a/src/scripts/artemis/asb.rs +++ b/src/scripts/artemis/asb.rs @@ -61,6 +61,7 @@ impl ScriptBuilder for ArtemisAsbBuilder { writer: Box, encoding: Encoding, file_encoding: Encoding, + _config: &ExtraConfig, ) -> Result<()> { create_file(filename, writer, encoding, file_encoding) } diff --git a/src/scripts/base.rs b/src/scripts/base.rs index 83a0803..b0ea92e 100644 --- a/src/scripts/base.rs +++ b/src/scripts/base.rs @@ -94,6 +94,7 @@ pub trait ScriptBuilder: std::fmt::Debug { _writer: Box, _encoding: Encoding, _file_encoding: Encoding, + _config: &ExtraConfig, ) -> Result<()> { Err(anyhow::anyhow!( "This script type does not support creating directly." @@ -106,10 +107,11 @@ pub trait ScriptBuilder: std::fmt::Debug { output_filename: &str, encoding: Encoding, file_encoding: Encoding, + config: &ExtraConfig, ) -> Result<()> { let f = std::fs::File::create(output_filename)?; let f = std::io::BufWriter::new(f); - self.create_file(filename, Box::new(f), encoding, file_encoding) + self.create_file(filename, Box::new(f), encoding, file_encoding, config) } #[cfg(feature = "image")] diff --git a/src/scripts/bgi/archive/dsc.rs b/src/scripts/bgi/archive/dsc.rs index 58bb1c3..bcb7aa1 100644 --- a/src/scripts/bgi/archive/dsc.rs +++ b/src/scripts/bgi/archive/dsc.rs @@ -401,16 +401,18 @@ pub struct DscEncoder<'a, T: Write + Seek> { magic: u32, key: u32, dec_count: u32, + min_len: usize, } impl<'a, T: Write + Seek> DscEncoder<'a, T> { - pub fn new(writer: &'a mut T) -> Self { + pub fn new(writer: &'a mut T, min_len: usize) -> Self { let stream = MsbBitWriter::new(writer); DscEncoder { stream, magic: 0x5344 << 16, // "DS" key: rand::rng().random(), dec_count: 0, + min_len, } } @@ -419,7 +421,6 @@ impl<'a, T: Write + Seek> DscEncoder<'a, T> { let mut ops = vec![]; let mut pos = 0; - const MIN_LEN: usize = 2; const MAX_LEN: usize = 257; const WINDOW_SIZE: usize = 4097; @@ -431,7 +432,7 @@ impl<'a, T: Write + Seek> DscEncoder<'a, T> { let mut best_len = 0; let mut best_offset = 0; - if max_len >= MIN_LEN { + if max_len >= self.min_len { let limit = pos.saturating_sub(WINDOW_SIZE); let key = (data[pos] as u16) << 8 | data[pos + 1] as u16; let mut match_pos_i32 = head[key as usize]; @@ -463,7 +464,7 @@ impl<'a, T: Write + Seek> DscEncoder<'a, T> { } } - if best_len >= MIN_LEN && best_offset >= 2 { + if best_len >= self.min_len && best_offset >= 2 { ops.push(LzssOp::Match { len: best_len as u16, offset: best_offset as u16, @@ -574,10 +575,10 @@ impl ScriptBuilder for DscBuilder { _filename: &str, _encoding: Encoding, _archive_encoding: Encoding, - _config: &ExtraConfig, + config: &ExtraConfig, _archive: Option<&Box>, ) -> Result> { - Ok(Box::new(Dsc::new(buf)?)) + Ok(Box::new(Dsc::new(buf, config)?)) } fn extensions(&self) -> &'static [&'static str] { @@ -605,8 +606,9 @@ impl ScriptBuilder for DscBuilder { mut writer: Box, _encoding: Encoding, _file_encoding: Encoding, + config: &ExtraConfig, ) -> Result<()> { - let encoder = DscEncoder::new(&mut writer); + let encoder = DscEncoder::new(&mut writer, config.bgi_compress_min_len); let data = crate::utils::files::read_file(filename)?; encoder.pack(&data)?; Ok(()) @@ -616,16 +618,20 @@ impl ScriptBuilder for DscBuilder { #[derive(Debug)] pub struct Dsc { data: Vec, + min_len: usize, } impl Dsc { - pub fn new(buf: Vec) -> Result { + pub fn new(buf: Vec, config: &ExtraConfig) -> Result { if buf.len() < 16 || !buf.starts_with(b"DSC FORMAT 1.00\0") { return Err(anyhow::anyhow!("Invalid DSC format")); } let decoder = DscDecoder::new(&buf)?; let data = decoder.unpack()?; - Ok(Dsc { data }) + Ok(Dsc { + data, + min_len: config.bgi_compress_min_len, + }) } } @@ -659,9 +665,13 @@ impl Script for Dsc { _encoding: Encoding, _output_encoding: Encoding, ) -> Result<()> { - let encoder = DscEncoder::new(&mut file); + let encoder = DscEncoder::new(&mut file, self.min_len); let data = crate::utils::files::read_file(custom_filename)?; encoder.pack(&data)?; Ok(()) } } + +pub fn parse_min_length(len: &str) -> Result { + clap_num::number_range(len, 2, 256) +} diff --git a/src/scripts/bgi/archive/v1.rs b/src/scripts/bgi/archive/v1.rs index f8d393c..ad7fc34 100644 --- a/src/scripts/bgi/archive/v1.rs +++ b/src/scripts/bgi/archive/v1.rs @@ -512,6 +512,7 @@ pub struct BgiArchiveWriter { headers: HashMap, compress_file: bool, encoding: Encoding, + min_len: usize, } impl BgiArchiveWriter { @@ -540,6 +541,7 @@ impl BgiArchiveWriter { headers, compress_file: config.bgi_compress_file, encoding, + min_len: config.bgi_compress_min_len, }) } } @@ -561,7 +563,7 @@ impl Archive for BgiArchiveWriter { pos: 0, }; Ok(if self.compress_file { - Box::new(BgiArchiveFileWithDsc::new(file)) + Box::new(BgiArchiveFileWithDsc::new(file, self.min_len)) } else { Box::new(file) }) @@ -640,13 +642,15 @@ impl<'a, T: Write + Seek> Seek for BgiArchiveFile<'a, T> { pub struct BgiArchiveFileWithDsc<'a, T: Write + Seek> { writer: BgiArchiveFile<'a, T>, buf: MemWriter, + min_len: usize, } impl<'a, T: Write + Seek> BgiArchiveFileWithDsc<'a, T> { - pub fn new(writer: BgiArchiveFile<'a, T>) -> Self { + pub fn new(writer: BgiArchiveFile<'a, T>, min_len: usize) -> Self { BgiArchiveFileWithDsc { writer, buf: MemWriter::new(), + min_len, } } } @@ -678,7 +682,7 @@ impl<'a, T: Write + Seek> Seek for BgiArchiveFileWithDsc<'a, T> { impl<'a, T: Write + Seek> Drop for BgiArchiveFileWithDsc<'a, T> { fn drop(&mut self) { let buf = self.buf.as_slice(); - let encoder = DscEncoder::new(&mut self.writer); + let encoder = DscEncoder::new(&mut self.writer, self.min_len); if let Err(e) = encoder.pack(&buf) { eprintln!("Failed to write DSC data: {}", e); crate::COUNTER.inc_error(); diff --git a/src/scripts/bgi/archive/v2.rs b/src/scripts/bgi/archive/v2.rs index 4b8c12a..c26c041 100644 --- a/src/scripts/bgi/archive/v2.rs +++ b/src/scripts/bgi/archive/v2.rs @@ -514,6 +514,7 @@ pub struct BgiArchiveWriter { headers: HashMap, compress_file: bool, encoding: Encoding, + min_len: usize, } impl BgiArchiveWriter { @@ -543,6 +544,7 @@ impl BgiArchiveWriter { headers, compress_file: config.bgi_compress_file, encoding, + min_len: config.bgi_compress_min_len, }) } } @@ -564,7 +566,7 @@ impl Archive for BgiArchiveWriter { pos: 0, }; Ok(if self.compress_file { - Box::new(BgiArchiveFileWithDsc::new(file)) + Box::new(BgiArchiveFileWithDsc::new(file, self.min_len)) } else { Box::new(file) }) @@ -643,13 +645,15 @@ impl<'a, T: Write + Seek> Seek for BgiArchiveFile<'a, T> { pub struct BgiArchiveFileWithDsc<'a, T: Write + Seek> { writer: BgiArchiveFile<'a, T>, buf: MemWriter, + min_len: usize, } impl<'a, T: Write + Seek> BgiArchiveFileWithDsc<'a, T> { - pub fn new(writer: BgiArchiveFile<'a, T>) -> Self { + pub fn new(writer: BgiArchiveFile<'a, T>, min_len: usize) -> Self { BgiArchiveFileWithDsc { writer, buf: MemWriter::new(), + min_len, } } } @@ -681,7 +685,7 @@ impl<'a, T: Write + Seek> Seek for BgiArchiveFileWithDsc<'a, T> { impl<'a, T: Write + Seek> Drop for BgiArchiveFileWithDsc<'a, T> { fn drop(&mut self) { let buf = self.buf.as_slice(); - let encoder = DscEncoder::new(&mut self.writer); + let encoder = DscEncoder::new(&mut self.writer, self.min_len); if let Err(e) = encoder.pack(&buf) { eprintln!("Failed to write DSC data: {}", e); crate::COUNTER.inc_error(); diff --git a/src/scripts/bgi/bsi.rs b/src/scripts/bgi/bsi.rs index aec9be5..0d57ed4 100644 --- a/src/scripts/bgi/bsi.rs +++ b/src/scripts/bgi/bsi.rs @@ -50,6 +50,7 @@ impl ScriptBuilder for BGIBsiScriptBuilder { writer: Box, encoding: Encoding, file_encoding: Encoding, + _config: &ExtraConfig, ) -> Result<()> { create_file(filename, writer, encoding, file_encoding) } diff --git a/src/scripts/cat_system/cstl.rs b/src/scripts/cat_system/cstl.rs index 576333e..fe66e35 100644 --- a/src/scripts/cat_system/cstl.rs +++ b/src/scripts/cat_system/cstl.rs @@ -57,6 +57,7 @@ impl ScriptBuilder for CstlScriptBuilder { writer: Box, encoding: Encoding, file_encoding: Encoding, + _config: &ExtraConfig, ) -> Result<()> { create_file(filename, writer, encoding, file_encoding) } diff --git a/src/scripts/escude/list.rs b/src/scripts/escude/list.rs index 6d22d9e..b25e328 100644 --- a/src/scripts/escude/list.rs +++ b/src/scripts/escude/list.rs @@ -61,6 +61,7 @@ impl ScriptBuilder for EscudeBinListBuilder { writer: Box, encoding: Encoding, file_encoding: Encoding, + _config: &ExtraConfig, ) -> Result<()> { create_file(filename, writer, encoding, file_encoding) } diff --git a/src/types.rs b/src/types.rs index 41971ab..b3709b3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -221,6 +221,8 @@ pub struct ExtraConfig { pub kirikiri_message_commands: std::sync::Arc>, #[cfg(feature = "bgi-arc")] pub bgi_compress_file: bool, + #[cfg(feature = "bgi-arc")] + pub bgi_compress_min_len: usize, #[cfg(feature = "kirikiri-img")] pub kirikiri_pimg_overlay: Option, #[cfg(feature = "artemis-arc")]