diff --git a/src/args.rs b/src/args.rs index fb2f66d..9118c08 100644 --- a/src/args.rs +++ b/src/args.rs @@ -747,6 +747,14 @@ pub struct Arg { /// 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 = "kirikiri-arc")] + #[arg(long, global = true, value_enum, default_value_t = crate::scripts::kirikiri::archive::xp3::FileHashOption::Both)] + /// Control the behavior to how to extract files from Cxdec3/4(Hxv4) protected archives. + pub xp3_cxdec_file_hash: crate::scripts::kirikiri::archive::xp3::FileHashOption, + #[cfg(feature = "kirikiri-arc")] + #[arg(long, global = true, value_enum, default_value_t = crate::scripts::kirikiri::archive::xp3::PathHashOption::Both)] + /// Control the behavior to how to append path name to files from Cxdec3/4(Hxv4) protected archives. + pub xp3_cxdec_path_hash: crate::scripts::kirikiri::archive::xp3::PathHashOption, #[command(subcommand)] /// Command pub command: Command, diff --git a/src/main.rs b/src/main.rs index 6a96117..c188d8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3403,6 +3403,10 @@ fn main() { 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(), + #[cfg(feature = "kirikiri-arc")] + xp3_cxdec_file_hash: arg.xp3_cxdec_file_hash, + #[cfg(feature = "kirikiri-arc")] + xp3_cxdec_path_hash: arg.xp3_cxdec_path_hash, }); match &arg.command { args::Command::Export { input, output } => { diff --git a/src/scripts/kirikiri/archive/xp3/crypt/cx.rs b/src/scripts/kirikiri/archive/xp3/crypt/cx.rs index 247a8f1..019da68 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/cx.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/cx.rs @@ -1,3 +1,4 @@ +use super::super::{FileHashOption, PathHashOption}; use super::*; use crate::ext::atomic::AtomicQuick; use crate::ext::mutex::MutexExt; @@ -2041,6 +2042,8 @@ pub struct HxCrypt { file_mapping: HashMap, path_mapping: HashMap, info_map: Mutex>, + file_hash: FileHashOption, + path_hash: PathHashOption, } #[derive(Clone)] @@ -2145,6 +2148,7 @@ impl HxCrypt { file_list_name: Option<&str>, file_list_path: Option<&str>, filename: &str, + config: &ExtraConfig, ) -> Result { let p = std::path::Path::new(filename); let b = p @@ -2185,6 +2189,8 @@ impl HxCrypt { file_mapping: file_map, path_mapping: path_map, info_map: Mutex::new(HashMap::new()), + file_hash: config.xp3_cxdec_file_hash, + path_hash: config.xp3_cxdec_path_hash, }) } @@ -2476,7 +2482,11 @@ impl Crypt for HxCrypt { if info.is_garbage { continue; } - entry.name = format!("{}{}", info.path, info.name); + if self.path_hash == PathHashOption::Both || !info.path_is_hash { + entry.name = format!("{}{}", info.path, info.name); + } else { + entry.name = info.name.clone(); + } let info = info.clone(); entry.extra = Some(Arc::new(Box::new(info))) } @@ -2484,6 +2494,23 @@ impl Crypt for HxCrypt { archive.entries.retain(|x| { x.extra.is_some() || !info_map.get(&x.name).is_some_and(|x| x.is_garbage) }); + if self.file_hash == FileHashOption::WithName { + archive.entries.retain(|x| { + !(x.extra.as_ref().is_some_and(|x| { + x.as_any() + .downcast_ref::() + .is_some_and(|x| x.name_is_hash) + })) + }); + } else if self.file_hash == FileHashOption::WithoutName { + archive.entries.retain(|x| { + x.extra.as_ref().is_some_and(|x| { + x.as_any() + .downcast_ref::() + .is_some_and(|x| x.name_is_hash) + }) + }); + } } archive.extras.retain(|x| x.tag != "Hxv4"); Ok(()) diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index 0842197..db40bdf 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -503,6 +503,7 @@ impl Schema { file_list_name.as_ref().map(|s| s.as_str()), config.xp3_file_list_path.as_ref().map(|s| s.as_str()), filename, + config, )?), }) } diff --git a/src/scripts/kirikiri/archive/xp3/mod.rs b/src/scripts/kirikiri/archive/xp3/mod.rs index f30daf9..cdd7534 100644 --- a/src/scripts/kirikiri/archive/xp3/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/mod.rs @@ -11,6 +11,7 @@ use crate::ext::io::*; use crate::scripts::base::*; use crate::types::*; use anyhow::Result; +use clap::ValueEnum; use consts::ZSTD_SIGNATURE; use crypt::Crypt; pub use crypt::get_supported_games; @@ -73,6 +74,37 @@ pub fn parse_segmenter_config(str: &str) -> Result { } } +#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)] +/// Control file extract behavior +pub enum FileHashOption { + /// Extract all files + Both, + /// Extract files that have name + WithName, + /// Extract files that don't have name + WithoutName, +} + +impl Default for FileHashOption { + fn default() -> Self { + Self::Both + } +} + +#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)] +pub enum PathHashOption { + /// Append both hash and normal path name to file name + Both, + /// Append only normal path name to file name + NameOnly, +} + +impl Default for PathHashOption { + fn default() -> Self { + Self::Both + } +} + #[derive(Debug)] /// Builder for Kirikiri XP3 Archive pub struct Xp3ArchiveBuilder {} diff --git a/src/types.rs b/src/types.rs index 6c2e16f..b4ea60a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -657,6 +657,12 @@ pub struct ExtraConfig { /// 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 = "kirikiri-arc")] + /// Control the behavior to how to extract files from Cxdec3/4(Hxv4) protected archives. + pub xp3_cxdec_file_hash: crate::scripts::kirikiri::archive::xp3::FileHashOption, + #[cfg(feature = "kirikiri-arc")] + /// Control the behavior to how to append path name to files from Cxdec3/4(Hxv4) protected archives. + pub xp3_cxdec_path_hash: crate::scripts::kirikiri::archive::xp3::PathHashOption, } #[cfg(feature = "artemis")]