diff --git a/msg_tool_xp3data/crypt.json b/msg_tool_xp3data/crypt.json index 1af4cd1..dec461a 100644 --- a/msg_tool_xp3data/crypt.json +++ b/msg_tool_xp3data/crypt.json @@ -230,6 +230,20 @@ "Key": 49, "Title": "僕の未来は、恋と課金と。~Charge To The Future~ [体験版] | 我的未来是恋爱与氪金 [试用版]" }, + "Boku to Koi Suru Ponkotsu Akuma.": { + "$type": "SmileCrypt", + "KeyXor": 3505437307, + "FirstXor": 123, + "ZeroXor": 94, + "Title": "僕と恋するポンコツアクマ。 | 与我恋爱的废柴小恶魔 | The Ditzy Demons Are in Love With Me | 與我戀愛的廢柴小惡魔" + }, + "Boku to Koi Suru Ponkotsu Akuma. Suggoi Ecchi!": { + "$type": "SmileCrypt", + "KeyXor": 3505437307, + "FirstXor": 123, + "ZeroXor": 94, + "Title": "僕と恋するポンコツアクマ。 すっごいえっち! | 与我恋爱的废柴恶魔。 ~甜腻后日谈~ | The Ditzy Demons Are in Love With Me - Fandisc" + }, "Bosei Kanojo -Shikyuu Kikan Hen-": { "$type": "AkabeiCrypt", "Seed": 3598228156, @@ -814,6 +828,13 @@ "$type": "SourireCrypt", "Title": "妹のおかげでモテすぎてヤバい。 | 因为妹妹让我太受欢迎了糟糕了" }, + "Imouto no Seiiki": { + "$type": "SmileCrypt", + "KeyXor": 552518022, + "FirstXor": 134, + "ZeroXor": 60, + "Title": "妹のセイイキ | 妹的圣域 | My Little Sister's Special Place" + }, "Imouto Shokushu Rape": { "$type": "HashCrypt", "Title": "妹触手レイプ ~復讐の処女貫通~" @@ -1399,6 +1420,13 @@ "ControlBlockName": "nekogami.bin", "Title": "ネコ神さまと、ななつぼし -妹の姉- | 猫神大人与七颗星星" }, + "Neko Para Extra": { + "$type": "SmileCrypt", + "KeyXor": 1146260359, + "FirstXor": 135, + "ZeroXor": 35, + "Title": "ネコぱらExtra 仔ネコの日の約束 | 猫娘乐园Extra 小猫之日的约定 | 貓娘樂園Extra 貓咪之日的約定" + }, "Netorare Osananajimi ~Haruka to Chika~": { "$type": "NatsupochiCrypt", "Title": "寝取られ幼馴染~春花と千夏~" @@ -1720,6 +1748,13 @@ "Seed": 798088789, "Title": "Role player:小粥姉妹の粘膜ポトレ ぐりぐちゃLIVE! | ROLEPLAYER:小粥姐妹的黏膜游戏!" }, + "Royal Garden": { + "$type": "SmileCrypt", + "KeyXor": 1651056185, + "FirstXor": 57, + "ZeroXor": 106, + "Title": "ロイヤルガーデン~乙女に恋する皇子の戯曲~" + }, "Rui wa Tomo o Yobu": { "$type": "CxEncryption", "Mask": 740, diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index 797e27e..7417305 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -181,6 +181,12 @@ enum CryptType { key: u8, }, ExaCrypt, + #[serde(rename_all = "PascalCase")] + SmileCrypt { + key_xor: u32, + first_xor: u8, + zero_xor: u8, + }, } #[derive(Clone, Debug, Deserialize)] @@ -283,6 +289,16 @@ impl Schema { CryptType::HaikuoCrypt => Box::new(HaikuoCrypt::new(self.base.clone())), CryptType::StripeCrypt { key } => Box::new(StripeCrypt::new(self.base.clone(), *key)), CryptType::ExaCrypt => Box::new(ExaCrypt::new(self.base.clone())), + CryptType::SmileCrypt { + key_xor, + first_xor, + zero_xor, + } => Box::new(SmileCrypt::new( + self.base.clone(), + *key_xor, + *first_xor, + *zero_xor, + )), }) } } @@ -1209,6 +1225,85 @@ impl Read for ExaCryptReader { } } +#[derive(Debug)] +pub struct SmileCrypt { + base: BaseSchema, + key_xor: u32, + first_xor: u8, + zero_xor: u8, +} + +impl SmileCrypt { + pub fn new(base: BaseSchema, key_xor: u32, first_xor: u8, zero_xor: u8) -> Self { + Self { + base, + key_xor, + first_xor, + zero_xor, + } + } +} + +impl Crypt for SmileCrypt { + 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> { + let key = entry.file_hash ^ self.key_xor; + Ok(Box::new(SmileCryptReader::new( + stream, + cur_seg, + (key, self.first_xor, self.zero_xor), + ))) + } + fn decrypt_with_seek<'a>( + &self, + entry: &Xp3Entry, + cur_seg: &Segment, + stream: Box, + ) -> Result> { + let key = entry.file_hash ^ self.key_xor; + Ok(Box::new(SmileCryptReader::new( + stream, + cur_seg, + (key, self.first_xor, self.zero_xor), + ))) + } +} + +seek_reader_key_impl!(SmileCryptReader, (u32, u8, u8)); + +impl Read for SmileCryptReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let readed = self.inner.read(buf)?; + let (mut hash, first_xor, zero_xor) = self.key; + let mut key = (hash ^ (hash >> 8) ^ (hash >> 16) ^ (hash >> 24)) as u8; + if key == 0 { + key = zero_xor; + } + if self.pos == 0 && self.seg_start == 0 && readed > 0 { + if hash & 0xFF == 0 { + hash = first_xor as u32; + } + buf[0] ^= hash as u8; + } + for t in (&mut buf[..readed]).iter_mut() { + *t ^= key; + } + self.pos += readed as u64; + Ok(readed) + } +} + #[test] fn test_deserialize_crypt() { for (key, schema) in CRYPT_SCHEMA.iter() {