From 28be9fc25002fb1c0aeeac3202f1e250d4ad2855 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 1 May 2026 23:44:50 +0800 Subject: [PATCH] Add PinPointCrypt (untested) --- msg_tool_xp3data/crypt.json | 12 ++++++++ src/scripts/kirikiri/archive/xp3/crypt/mod.rs | 28 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/msg_tool_xp3data/crypt.json b/msg_tool_xp3data/crypt.json index c6be55b..b1d22cf 100644 --- a/msg_tool_xp3data/crypt.json +++ b/msg_tool_xp3data/crypt.json @@ -416,6 +416,10 @@ "$type": "HashCrypt", "Title": "ドM妹おねだり個人レッスン~私のSEX家庭教師お兄ちゃん~ | 义妹私教辅导~哥哥我是家庭教师" }, + "Dosukebe Bitch na Enkou...": { + "$type": "PinPointCrypt", + "Title": "ドスケベビッチな援交○学生(高学年)のセックス三昧夏休み ~あかり&雪乃~" + }, "Dracu-Riot!": { "$type": "CxEncryption", "Mask": 752, @@ -2141,6 +2145,10 @@ "$type": "FlyingShineCrypt", "Title": "相姦恥療病棟 ~ナースはママで、女医は叔母" }, + "Soukou Seiki Ysphere": { + "$type": "PinPointCrypt", + "Title": "装煌聖姫イースフィア~淫虐の洗脳改造~" + }, "Sousaku Kanojo no Ren'ai Koushiki": { "$type": "AkabeiCrypt", "Seed": 798088789, @@ -2333,6 +2341,10 @@ "$type": "PoringSoftCrypt", "Title": "つばさの丘の姫王 A red and blue moon -finite loop-" }, + "Tsuma ga Kakushiteita Video...": { + "$type": "PinPointCrypt", + "Title": "妻が隠していたビデオ… ~元カレ寝取らせ観察記~" + }, "Tsuma Kai -Tsuma Gai-": { "$type": "FlyingShineCrypt", "Title": "貸し出し妻、満里奈の“ネトラセ”報告 外伝1 ―女子校生、満里奈の性感開発―" diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index 6df397a..ab9c14b 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -244,6 +244,7 @@ enum CryptType { key_seq: Base64Bytes, }, FestivalCrypt, + PinPointCrypt, } #[derive(Clone, Debug, Deserialize)] @@ -381,6 +382,7 @@ impl Schema { Box::new(SmxCrypt::new(self.base.clone(), *mask, &key_seq.bytes)?) } CryptType::FestivalCrypt => Box::new(FestivalCrypt::new(self.base.clone())), + CryptType::PinPointCrypt => Box::new(PinPointCrypt::new(self.base.clone())), }) } } @@ -504,6 +506,7 @@ macro_rules! seek_reader_impl { struct $reader<$t: Read> { #[skip_fmt] inner: $t, + #[allow(unused)] /// Start offset of the current xp3 entry. seg_start: u64, seg_size: u64, @@ -1880,6 +1883,31 @@ impl Read for FestivalCryptReader { } } +seek_crypt_impl!(PinPointCrypt, PinPointCryptReader); + +impl PinPointCryptReader { + #[inline(always)] + fn count_set_bits(x: u8) -> u32 { + let mut bit_count = ((x & 0x55) + ((x >> 1) & 0x55)) as u32; + bit_count = (bit_count & 0x33) + ((bit_count >> 2) & 0x33); + ((bit_count & 0xF) + ((bit_count >> 4) & 0xF)) & 0xF + } +} + +impl Read for PinPointCryptReader { + 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 bit_count = Self::count_set_bits(*t); + if bit_count > 0 { + *t = (*t).rotate_left(bit_count); + } + } + self.pos += readed as u64; + Ok(readed) + } +} + #[test] fn test_deserialize_crypt() { for (key, schema) in CRYPT_SCHEMA.iter() {