diff --git a/src/args.rs b/src/args.rs index cf71c55..fc24107 100644 --- a/src/args.rs +++ b/src/args.rs @@ -484,6 +484,14 @@ pub struct Arg { #[arg(long, global = true, visible_alias = "softpal-idx")] /// Whether to add message index to Softpal src script when exporting. pub softpal_add_message_index: bool, + #[cfg(feature = "kirikiri-arc")] + #[arg(long, global = true)] + /// Disable decrypt SimpleCrypt files in Kirikiri XP3 archive when extracting. + pub xp3_no_simple_crypt: bool, + #[cfg(feature = "kirikiri-arc")] + #[arg(long, global = true)] + /// Disable decompressing mdf files in Kirikiri XP3 archive when extracting. + pub xp3_no_mdf_decompress: bool, #[command(subcommand)] /// Command pub command: Command, diff --git a/src/main.rs b/src/main.rs index 9d4ece3..680355c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2744,6 +2744,10 @@ fn main() { softpal_add_message_index: arg.softpal_add_message_index, #[cfg(feature = "kirikiri")] kirikiri_chat_multilang: !arg.kirikiri_chat_no_multilang, + #[cfg(feature = "kirikiri-arc")] + xp3_simple_crypt: !arg.xp3_no_simple_crypt, + #[cfg(feature = "kirikiri-arc")] + xp3_mdf_decompress: !arg.xp3_no_mdf_decompress, }); match &arg.command { args::Command::Export { input, output } => { diff --git a/src/scripts/kirikiri/archive/xp3.rs b/src/scripts/kirikiri/archive/xp3.rs index 7537a89..0f24a90 100644 --- a/src/scripts/kirikiri/archive/xp3.rs +++ b/src/scripts/kirikiri/archive/xp3.rs @@ -83,11 +83,13 @@ impl ScriptBuilder for Xp3ArchiveBuilder { pub struct Xp3Archive { reader: Arc>, entries: Vec<(String, XP3FileIndex)>, + decrypt_simple_crypt: bool, + decompress_mdf: bool, } impl Xp3Archive { /// Create a new Kirikiri XP3 Archive - pub fn new(reader: T, _config: &ExtraConfig) -> Result { + pub fn new(reader: T, config: &ExtraConfig) -> Result { let xp3_reader = XP3Reader::open_archive(reader) .map_err(|e| anyhow::anyhow!("Failed to open XP3 archive: {:?}", e))?; let entries = xp3_reader @@ -106,6 +108,8 @@ impl Xp3Archive { Ok(Self { reader: Arc::new(Mutex::new(xp3_reader.close().1)), entries, + decrypt_simple_crypt: config.xp3_simple_crypt, + decompress_mdf: config.xp3_mdf_decompress, }) } } @@ -143,7 +147,8 @@ impl Script for Xp3Archive { let mut header = [0u8; 16]; let header_len = entry.read(&mut header)?; entry.rewind()?; - if header_len >= 5 + if self.decrypt_simple_crypt + && header_len >= 5 && header[0] == 0xFE && header[1] == 0xFE && header[3] == 0xFF @@ -159,6 +164,14 @@ impl Script for Xp3Archive { return Ok(Box::new(SimpleCrypt::new(entry, index, crypt)?)); } } + if self.decompress_mdf + && header_len >= 4 + && &header[0..4] == b"mdf\0" + && entry.index.info().file_size() > 8 + { + let index = entry.index.clone(); + return Ok(Box::new(MdfEntry::new(entry, index)?)); + } Ok(Box::new(entry)) } } @@ -461,3 +474,30 @@ impl Seek for SimpleCrypt { self.inner.stream_position() } } + +#[derive(Debug)] +struct MdfEntry { + inner: ZlibDecoder>>, + index: XP3FileIndex, +} + +impl MdfEntry { + fn new(mut entry: Entry, index: XP3FileIndex) -> Result { + entry.seek(SeekFrom::Start(8))?; + let entry = StreamRegion::new(entry, 8, index.info().file_size())?; + let inner = ZlibDecoder::new(entry); + Ok(Self { inner, index }) + } +} + +impl ArchiveContent for MdfEntry { + fn name(&self) -> &str { + &self.index.info().name() + } +} + +impl Read for MdfEntry { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} diff --git a/src/types.rs b/src/types.rs index 6084f67..90a6b46 100644 --- a/src/types.rs +++ b/src/types.rs @@ -472,6 +472,14 @@ pub struct ExtraConfig { /// Enable multi-language support for Kirikiri chat messages. Default is true. /// Note: This requires [Self::kirikiri_language_index] and [Self::kirikiri_languages] to be set correctly. pub kirikiri_chat_multilang: bool, + #[cfg(feature = "kirikiri-arc")] + #[default(true)] + /// Decrypt SimpleCrypt files in Kirikiri XP3 archive when extracting. Default is true. + pub xp3_simple_crypt: bool, + #[cfg(feature = "kirikiri-arc")] + #[default(true)] + /// Decompress mdf files in Kirikiri XP3 archive when extracting. Default is true. + pub xp3_mdf_decompress: bool, } #[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]