diff --git a/src/args.rs b/src/args.rs
index f711935..2dcaf66 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -728,6 +728,10 @@ pub struct Arg {
/// Print/write debug information for Kirikiri XP3 archive when extracting archive to specifiy location.
/// This is used to find correct configuration for unknown XP3 archives.
pub xp3_debug_archive: bool,
+ #[cfg(feature = "kirikiri-arc")]
+ #[arg(long, global = true)]
+ /// Force extract encrypted files in Kirikiri XP3 archive without decryption.
+ pub xp3_force_extract: bool,
#[command(subcommand)]
/// Command
pub command: Command,
diff --git a/src/main.rs b/src/main.rs
index 7b94b1e..c478a25 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3395,6 +3395,8 @@ fn main() {
xp3_game_title: arg.xp3_game_title.clone(),
#[cfg(feature = "kirikiri-arc")]
xp3_debug_archive: arg.xp3_debug_archive,
+ #[cfg(feature = "kirikiri-arc")]
+ xp3_force_extract: arg.xp3_force_extract,
});
match &arg.command {
args::Command::Export { input, output } => {
diff --git a/src/scripts/kirikiri/archive/xp3/mod.rs b/src/scripts/kirikiri/archive/xp3/mod.rs
index b5179db..374053d 100644
--- a/src/scripts/kirikiri/archive/xp3/mod.rs
+++ b/src/scripts/kirikiri/archive/xp3/mod.rs
@@ -162,6 +162,7 @@ pub struct Xp3Archive {
archive: archive::Xp3Archive,
decrypt_simple_crypt: bool,
decompress_mdf: bool,
+ force_extract: bool,
}
impl Xp3Archive {
@@ -185,6 +186,7 @@ impl Xp3Archive {
archive,
decrypt_simple_crypt: config.xp3_simple_crypt,
decompress_mdf: config.xp3_mdf_decompress,
+ force_extract: config.xp3_force_extract,
})
}
}
@@ -222,16 +224,20 @@ impl Script for Xp3Archive {
.ok_or(anyhow::anyhow!("Index out of bounds: {}", index))?
.clone();
let crypt = self.archive.crypt.clone();
- if index.is_encrypted() && !crypt.decrypt_supported() {
- return Err(anyhow::anyhow!(
- "The archive is encrypted with a method that is not supported by the current crypt implementation. You may need to specify a game title by using --xp3-game-title
."
- ));
+ let skip_decrypt = index.is_encrypted() && !crypt.decrypt_supported();
+ if skip_decrypt {
+ if !self.force_extract {
+ return Err(anyhow::anyhow!(
+ "The archive is encrypted with a method that is not supported by the current crypt implementation. You may need to specify a game title by using --xp3-game-title ."
+ ));
+ }
}
let mut entry = Entry::new(
self.archive.inner.clone(),
index,
self.archive.base_offset,
crypt,
+ skip_decrypt,
);
let mut header = [0u8; 16];
let header_len = entry.read(&mut header)?;
@@ -305,6 +311,7 @@ struct Entry {
base_offset: u64,
entries_pos: Vec,
script_type: Option,
+ skip_decrypt: bool,
}
#[automatically_derived]
@@ -320,6 +327,7 @@ impl std::fmt::Debug for Entry {
.field("base_offset", &self.base_offset)
.field("entries_pos", &self.entries_pos)
.field("script_type", &self.script_type)
+ .field("skip_decrypt", &self.skip_decrypt)
.finish()
}
}
@@ -330,6 +338,7 @@ impl Entry {
index: archive::Xp3Entry,
base_offset: u64,
crypt: Arc>,
+ skip_decrypt: bool,
) -> Self {
let mut pos = 0;
let entries_pos = index
@@ -351,6 +360,7 @@ impl Entry {
base_offset,
crypt,
crypt_stream: None,
+ skip_decrypt,
}
}
}
@@ -407,7 +417,7 @@ impl Read for Entry {
let seg_pos = self.entries_pos[seg_index];
let skip_pos = self.pos - seg_pos;
let read_size = seg.archived_size;
- if self.index.is_encrypted() {
+ if !self.skip_decrypt && self.index.is_encrypted() {
if seg.is_compressed || !self.crypt.decrypt_seek_supported() {
let mut cache: Box = if seg.is_compressed {
let mut inner =
diff --git a/src/types.rs b/src/types.rs
index 7c5aef2..f97ebbe 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -642,6 +642,9 @@ pub struct ExtraConfig {
/// Print debug information for Kirikiri XP3 archive when extracting archive to stdout.
/// This is used to find correct configuration for unknown XP3 archives.
pub xp3_debug_archive: bool,
+ #[cfg(feature = "kirikiri-arc")]
+ /// Force extract encrypted files in Kirikiri XP3 archive without decryption.
+ pub xp3_force_extract: bool,
}
#[cfg(feature = "artemis")]