diff --git a/src/args.rs b/src/args.rs index f77eb65..fb2f66d 100644 --- a/src/args.rs +++ b/src/args.rs @@ -742,6 +742,11 @@ pub struct Arg { /// Disable diff handle when exporting Emote PIMG images to PSD files. /// If enabled, no group layers will be crated if both layer don't have diff_id and group_layer_id attribute. pub emote_pimg_psd_no_diff: bool, + #[cfg(feature = "kirikiri-arc")] + #[arg(long, global = true)] + /// The path to the file list for Kirikiri XP3 archive. This is used to recover file names from hashed values. + /// Only works with some encyrption methods. + pub xp3_file_list_path: Option, #[command(subcommand)] /// Command pub command: Command, diff --git a/src/main.rs b/src/main.rs index 4f0bb73..a2ee639 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3401,6 +3401,8 @@ fn main() { xp3_force_decrypt: arg.xp3_force_decrypt, #[cfg(feature = "emote-img")] emote_pimg_psd_no_diff: arg.emote_pimg_psd_no_diff, + #[cfg(feature = "kirikiri-arc")] + xp3_file_list_path: arg.xp3_file_list_path.clone(), }); match &arg.command { args::Command::Export { input, output } => { diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index 691fbfe..ffbee91 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -256,7 +256,7 @@ pub struct Schema { } impl Schema { - pub fn create_crypt(&self, filename: &str) -> Result> { + pub fn create_crypt(&self, filename: &str, config: &ExtraConfig) -> Result> { Ok(match &self.crypt { CryptType::NoCrypt => Box::new(NoCrypt::new()), CryptType::FateCrypt => Box::new(FateCrypt::new(self.base.clone())), @@ -354,9 +354,11 @@ impl Schema { hash_table.clone(), key_table.bytes.clone(), )?), - CryptType::RhapsodyCrypt { file_list_name } => { - Box::new(RhapsodyCrypt::new(self.base.clone(), &file_list_name)?) - } + CryptType::RhapsodyCrypt { file_list_name } => Box::new(RhapsodyCrypt::new( + self.base.clone(), + &file_list_name, + config.xp3_file_list_path.as_ref().map(|s| s.as_str()), + )?), }) } } @@ -1543,8 +1545,16 @@ pub struct RhapsodyCrypt { } impl RhapsodyCrypt { - pub fn new(base: BaseSchema, file_list_name: &str) -> Result { - let file_list = query_filename_list(file_list_name)?; + pub fn new( + base: BaseSchema, + file_list_name: &str, + file_list_path: Option<&str>, + ) -> Result { + let file_list = if let Some(path) = file_list_path { + std::fs::read_to_string(path)? + } else { + query_filename_list(file_list_name)? + }; let mut names = HashMap::new(); for name in file_list.lines() { let name = name.trim(); diff --git a/src/scripts/kirikiri/archive/xp3/read.rs b/src/scripts/kirikiri/archive/xp3/read.rs index 94e7e05..a80f5cd 100644 --- a/src/scripts/kirikiri/archive/xp3/read.rs +++ b/src/scripts/kirikiri/archive/xp3/read.rs @@ -10,15 +10,15 @@ use std::sync::{Arc, Mutex}; impl Xp3Archive { pub fn new( stream: T, - _config: &ExtraConfig, + config: &ExtraConfig, filename: &str, ) -> Result { - let crypt: Box = if let Some(game_title) = &_config.xp3_game_title { + let crypt: Box = if let Some(game_title) = &config.xp3_game_title { query_crypt_schema(game_title) .ok_or_else(|| { anyhow::anyhow!("Unsupported game title for XP3 archive: {}", game_title) })? - .create_crypt(filename)? + .create_crypt(filename, config)? } else { Box::new(NoCrypt::new()) }; diff --git a/src/types.rs b/src/types.rs index a053323..b7867e8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -653,6 +653,10 @@ pub struct ExtraConfig { /// Disable diff handle when exporting Emote PIMG images to PSD files. /// If enabled, no group layers will be crated if both layer don't have diff_id and group_layer_id attribute. pub emote_pimg_psd_no_diff: bool, + #[cfg(feature = "kirikiri-arc")] + /// The path to the file list for Kirikiri XP3 archive. This is used to recover file names from hashed values. + /// Only works with some encyrption methods. + pub xp3_file_list_path: Option, } #[cfg(feature = "artemis")]