From ed56ef1e391d163e1fecb37c4ae28d37f66c07a5 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sat, 2 May 2026 16:12:23 +0800 Subject: [PATCH] Add SyangrilaSmartCrypt --- msg_tool_xp3data/crypt.json | 4 + src/scripts/kirikiri/archive/xp3/crypt/mod.rs | 76 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/msg_tool_xp3data/crypt.json b/msg_tool_xp3data/crypt.json index cfc0188..bd8c91d 100644 --- a/msg_tool_xp3data/crypt.json +++ b/msg_tool_xp3data/crypt.json @@ -1152,6 +1152,10 @@ "Key": 10, "Title": "こあくまちゃんの誘惑っ! | 小恶魔的诱惑!" }, + "Koi de wa Naku - It's not love, but so where near.": { + "$type": "SyangrilaSmartCrypt", + "Title": "恋ではなく ―― It’s not love,but so where near." + }, "Koi ga Saku Koro Sakura Doki": { "$type": "CxEncryption", "Mask": 393, diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index 04d1c7c..98023c6 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -256,6 +256,7 @@ enum CryptType { key2: u64, key3: u64, }, + SyangrilaSmartCrypt, } #[derive(Clone, Debug, Deserialize)] @@ -404,6 +405,7 @@ impl Schema { *key2, *key3, )), + CryptType::SyangrilaSmartCrypt => Box::new(SyangrilaSmartCrypt::new(self.base.clone())), }) } } @@ -2127,6 +2129,80 @@ impl Read for NinkiSeiyuuCryptReader { } } +#[derive(Debug)] +pub struct SyangrilaSmartCrypt { + base: BaseSchema, +} + +impl SyangrilaSmartCrypt { + pub fn new(base: BaseSchema) -> Self { + Self { base } + } + + fn get_key(hash: u32) -> [u8; 5] { + [ + (hash >> 5) as u8, + (hash >> 5) as u8, + (hash >> 7) as u8, + (hash >> 1) as u8, + (hash >> 4) as u8, + ] + } +} + +impl Crypt for SyangrilaSmartCrypt { + base_schema_impl!(); + 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(SyangrilaSmartCryptReader::new( + stream, + cur_seg, + Self::get_key(entry.file_hash), + ))) + } + fn decrypt_with_seek<'a>( + &self, + entry: &Xp3Entry, + cur_seg: &Segment, + stream: Box, + ) -> Result> { + Ok(Box::new(SyangrilaSmartCryptReader::new( + stream, + cur_seg, + Self::get_key(entry.file_hash), + ))) + } +} + +seek_reader_key_impl!(SyangrilaSmartCryptReader, [u8; 5]); + +impl Read for SyangrilaSmartCryptReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let readed = self.inner.read(buf)?; + let offset = self.seg_start + self.pos; + for (i, t) in buf[..readed].iter_mut().enumerate() { + let tpos = offset + i as u64; + if tpos <= 0x64 { + *t ^= self.key[4]; + } else { + *t ^= self.key[(tpos & 3) as usize]; + } + } + self.pos += readed as u64; + Ok(readed) + } +} + #[test] fn test_deserialize_crypt() { for (key, schema) in CRYPT_SCHEMA.iter() {