From b376a9ab22b3cc4de38cd38a1ce20d51ebab5ae4 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Mon, 4 May 2026 13:21:31 +0800 Subject: [PATCH] Add XanaduCrypt --- msg_tool_xp3data/crypt.json | 5 + .../archive/xp3/crypt/chain_reaction.rs | 99 +++++++++++++++++++ src/scripts/kirikiri/archive/xp3/crypt/mod.rs | 3 + 3 files changed, 107 insertions(+) diff --git a/msg_tool_xp3data/crypt.json b/msg_tool_xp3data/crypt.json index a33c961..dfda4e1 100644 --- a/msg_tool_xp3data/crypt.json +++ b/msg_tool_xp3data/crypt.json @@ -790,6 +790,11 @@ "$type": "HashCrypt", "Title": "ヒメと魔神と恋するたましぃ" }, + "Himesho!": { + "$type": "XanaduCrypt", + "StartupTjsNotEncrypted": true, + "Title": "ひめしょ!" + }, "Hinekuremono no Gakuen Seishun Monogatari": { "$type": "CxEncryption", "Mask": 559, diff --git a/src/scripts/kirikiri/archive/xp3/crypt/chain_reaction.rs b/src/scripts/kirikiri/archive/xp3/crypt/chain_reaction.rs index b9a1d5a..876f8de 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/chain_reaction.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/chain_reaction.rs @@ -345,3 +345,102 @@ impl IChainReactionCrypt for ChocolatCrypt { self.base.init(archive) } } + +#[derive(Debug)] +pub struct XanaduCrypt { + base: BaseSchema, + inner: ChainReactionCryptBase, +} + +impl XanaduCrypt { + pub fn new(base: BaseSchema) -> Self { + Self { + base, + inner: ChainReactionCryptBase::new("plugins/list.txt".into()), + } + } +} + +impl AsRef for XanaduCrypt { + fn as_ref(&self) -> &BaseSchema { + &self.base + } +} + +impl IChainReactionCrypt for XanaduCrypt { + fn get_encryption_limit(&self, entry: &Xp3Entry) -> u32 { + let limit = self.inner.get_encryption_limit(entry); + match limit { + 0 => 0, + 2 => entry.original_size as u32, + _ => 0x100, + } + } + fn init(&self, archive: &mut Xp3Archive) -> Result<()> { + let mut bin = ChainReactionCryptBase::read_list_bin(archive, "list2.txt")?; + if bin.is_none() { + bin = ChainReactionCryptBase::read_list_bin(archive, "plugins/list.txt")?; + } + if let Some(bin) = bin { + self.inner.init2(bin)?; + } + Ok(()) + } +} + +impl Crypt for XanaduCrypt { + base_schema_impl!(); + fn init(&self, archive: &mut Xp3Archive) -> Result<()> { + IChainReactionCrypt::init(self, archive) + } + fn decrypt_supported(&self) -> bool { + true + } + fn decrypt_seek_supported(&self) -> bool { + true + } + fn decrypt<'a>( + &self, + entry: &Xp3Entry, + cur_seg: &Segment, + stream: Box, + ) -> Result> { + Ok(Box::new(XanaduCryptReader::new( + stream, + cur_seg, + (self.get_encryption_limit(entry), entry.file_hash), + ))) + } + fn decrypt_with_seek<'a>( + &self, + entry: &Xp3Entry, + cur_seg: &Segment, + stream: Box, + ) -> Result> { + Ok(Box::new(XanaduCryptReader::new( + stream, + cur_seg, + (self.get_encryption_limit(entry), entry.file_hash), + ))) + } +} + +impl Read for XanaduCryptReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let readed = self.inner.read(buf)?; + let (limit, hash) = self.key; + let limit = limit as u64; + let mut offset = self.seg_start + self.pos; + if offset < limit { + let key = hash ^ (!0x03020100); + let count = (limit - offset).min(readed as u64); + for t in buf[..count as usize].iter_mut() { + let extra = (((offset & 0xFF) >> 2) << 2) as u8; + *t ^= (key >> (((offset & 3) << 3) as u32)) as u8 ^ extra; + offset += 1; + } + } + self.pos += readed as u64; + Ok(readed) + } +} diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index 14f274a..3a3cdac 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -269,6 +269,7 @@ enum CryptType { ChainReactionCrypt, HachukanoCrypt, ChocolatCrypt, + XanaduCrypt, } #[derive(Clone, Debug, Deserialize)] @@ -440,6 +441,7 @@ impl Schema { CryptType::ChocolatCrypt => { Box::new(chain_reaction::ChocolatCrypt::new(self.base.clone())) } + CryptType::XanaduCrypt => Box::new(chain_reaction::XanaduCrypt::new(self.base.clone())), }) } } @@ -2477,6 +2479,7 @@ impl Crypt for PureMoreCrypt { } seek_reader_key_impl!(ChainReactionCryptReader, (u32, u32)); +seek_reader_key_impl!(XanaduCryptReader, (u32, u32)); #[test] fn test_deserialize_crypt() {