diff --git a/msg_tool_xp3data/crypt.json b/msg_tool_xp3data/crypt.json index 0c534b2..6c3a5d0 100644 --- a/msg_tool_xp3data/crypt.json +++ b/msg_tool_xp3data/crypt.json @@ -228,6 +228,10 @@ "Berry's": { "$type": "SourireCrypt" }, + "Bi'an Huazang": { + "$type": "NatsupochiCrypt", + "Title": "彼岸花葬" + }, "Bishuu ~Chigyaku no Mesu Dorei~": { "$type": "CxEncryption", "Mask": 531, @@ -677,6 +681,10 @@ "$type": "FateCrypt", "HashAfterCrypt": true }, + "Feicui Yue": { + "$type": "NatsupochiCrypt", + "Title": "翡翠月" + }, "Fukashi na Aijou": { "$type": "HashCrypt", "Title": "不可視な愛情~透明なボクに感じるママ~" @@ -1280,6 +1288,10 @@ "$type": "HashCrypt", "Title": "地味めな侍女さんのお仕事セックス~お嬢様に代わってお相手します~" }, + "Jixiang Ling": { + "$type": "NatsupochiCrypt", + "Title": "吉祥铃" + }, "JK Trouble Main\"s": { "$type": "CxEncryption", "Mask": 266, @@ -2921,6 +2933,10 @@ "ControlBlockName": "shugaten.bin", "Title": "しゅがてん!-sugarfull tempering- | 糖调! -sugarfull tempering- | 糖调! | 甜糖热恋" }, + "Shuidi Li de Xiatian": { + "$type": "NatsupochiCrypt", + "Title": "水滴里的夏天" + }, "Sis x Miko": { "$type": "SisMikoCrypt", "StartupTjsNotEncrypted": true, @@ -3326,9 +3342,35 @@ "ControlBlockName": "walpurgis.bin", "Title": "ヴァルプルギス" }, + "Wanruo Zhaoyang": { + "$type": "NatsupochiCrypt", + "Title": "宛若朝阳" + }, "With Ribbon": { "$type": "SourireCrypt" }, + "Wuyue Moli Episode 01 - Hewei Shen?": { + "$type": "PoringSoftCrypt", + "Title": "五月茉莉 首部曲-何謂神? | MayJasmine Episode01 What is God? | 五月茉莉 首部曲-何谓神?" + }, + "Wu zhi Benjing": { + "$type": "Xor2Crypt", + "Key1": 91, + "Key2": 57, + "Title": "雾之本境" + }, + "Xue zhi Benjing 2010": { + "$type": "HashCrypt", + "Title": "雪之本境 2010" + }, + "Xue zhi Benjing ~Jie Jing Pian~": { + "$type": "NatsupochiCrypt", + "Title": "雪之本境~解境篇~" + }, + "Xue zhi Benjing Ex": { + "$type": "NatsupochiCrypt", + "Title": "雪之本境Ex" + }, "xx na Kanojo no Tsukurikata 2": { "$type": "Kano2Crypt", "Title": "××な彼女のつくりかた2" @@ -3349,6 +3391,16 @@ "KeySeq": "AQMDAgQB", "Title": "やや置き場がない!" }, + "Ye zhi Libie: Ruoye Guichen Demo": { + "$type": "LeaveSLeaveCrypt", + "Title": "叶之离别:若叶归尘 Demo" + }, + "Yiwang Huayuan": { + "$type": "Xor2Crypt", + "Key1": 83, + "Key2": 58, + "Title": "遗忘花园" + }, "Yome no Ane ga Kyonyuu Sugite": { "$type": "HashCrypt", "Title": "嫁の姉が巨乳過ぎて我慢できない ~義姉さんがこんなにエロかったなんて!" @@ -3386,6 +3438,18 @@ "your diary + H": { "$type": "SourireCrypt" }, + "Yu Gang Jilong": { + "$type": "PoringSoftCrypt", + "Title": "雨港基隆" + }, + "Yuezhuo: Jinghuashuiyue": { + "$type": "PoringSoftCrypt", + "Title": "鸑鷟:镜花水月" + }, + "Yuezhuo: Juzi Zhuan": { + "$type": "PoringSoftCrypt", + "Title": "鸑鷟:橘子传" + }, "Yurameku Kokoro ni Michita Sekai de": { "$type": "XorCrypt", "Key": 205, @@ -3426,6 +3490,10 @@ "Key": 205, "Title": "勇者と魔王と、魔女のカフェ" }, + "Yuye": { + "$type": "NatsupochiCrypt", + "Title": "雨夜" + }, "Zecchou Spiral!!": { "$type": "CxEncryption", "Mask": 492, @@ -3451,6 +3519,12 @@ "$type": "HashCrypt", "Title": "絶対搾精! サキュバスちゃん" }, + "Ziluolan: Li - Season 2 -Shui Zhong Daoying-": { + "$type": "Xor2Crypt", + "Key1": 61, + "Key2": 229, + "Title": "紫罗兰·里 第二季 夏 -水中倒影-" + }, "Zutto Sukishite Takusan Sukishite": { "$type": "CxEncryption", "Mask": 555, diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index 01f481f..07e5059 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -302,6 +302,12 @@ enum CryptType { Hxv4Crypt { key_packages: Vec, }, + #[serde(rename_all = "PascalCase")] + Xor2Crypt { + key1: u8, + key2: u8, + }, + LeaveSLeaveCrypt, } #[derive(Clone, Debug, Deserialize)] @@ -517,6 +523,10 @@ impl Schema { filename, config, )?), + CryptType::Xor2Crypt { key1, key2 } => { + Box::new(Xor2Crypt::new(self.base.clone(), *key1, *key2)) + } + CryptType::LeaveSLeaveCrypt => Box::new(LeaveSLeaveCrypt::new(self.base.clone())), }) } } @@ -2556,6 +2566,86 @@ impl Crypt for PureMoreCrypt { } } +#[derive(Debug)] +pub struct Xor2Crypt { + base: BaseSchema, + key1: u8, + key2: u8, +} + +impl Xor2Crypt { + pub fn new(base: BaseSchema, key1: u8, key2: u8) -> Self { + Self { base, key1, key2 } + } +} + +impl Crypt for Xor2Crypt { + 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(Xor2CryptReader::new( + stream, + cur_seg, + (self.key1, self.key2), + ))) + } + fn decrypt_with_seek<'a>( + &self, + _entry: &Xp3Entry, + cur_seg: &Segment, + stream: Box, + ) -> Result> { + Ok(Box::new(Xor2CryptReader::new( + stream, + cur_seg, + (self.key1, self.key2), + ))) + } +} + +seek_reader_key_impl!(Xor2CryptReader, (u8, u8)); + +impl Read for Xor2CryptReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let readed = self.inner.read(buf)?; + let mut offset = self.seg_start + self.pos; + for t in (&mut buf[..readed]).iter_mut() { + *t ^= if offset % 2 == 0 { + self.key.0 + } else { + self.key.1 + }; + offset += 1; + } + self.pos += readed as u64; + Ok(readed) + } +} + +seek_crypt_filehash_key_u8_impl!(LeaveSLeaveCrypt, LeaveSLeaveCryptReader); + +impl Read for LeaveSLeaveCryptReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let readed = self.inner.read(buf)?; + for t in (&mut buf[..readed]).iter_mut() { + let b = *t ^ self.key; + *t = (b << 4) | (b >> 4); + } + self.pos += readed as u64; + Ok(readed) + } +} + seek_reader_key_impl!(ChainReactionCryptReader, (u32, u32)); seek_reader_key_impl!(XanaduCryptReader, (u32, u32)); seek_reader_key_impl!(SisMikoCryptReader, (u32, u32));