diff --git a/msg_tool_xp3data/crypt.json b/msg_tool_xp3data/crypt.json index eb1d0bb..ff82224 100644 --- a/msg_tool_xp3data/crypt.json +++ b/msg_tool_xp3data/crypt.json @@ -1508,6 +1508,10 @@ "ControlBlockName": "seiin.bin", "Title": "聖淫の迷宮" }, + "Seirei Tenshou": { + "$type": "SeitenCrypt", + "Title": "聖なるかな外伝・精霊天翔 ~壊れゆく世界の少女たち~ | 精灵天翔・迈向毁灭世界的少女们" + }, "Seiso na Ano Ko wa, Kakure Bitch": { "$type": "HashCrypt", "Title": "清楚なあの娘は、隠れビッチ ~いつでもどこでも欲情SEX~" diff --git a/src/scripts/kirikiri/archive/xp3/crypt/cx.rs b/src/scripts/kirikiri/archive/xp3/crypt/cx.rs index 6cddd0d..bbe6f35 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/cx.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/cx.rs @@ -1,6 +1,5 @@ use super::*; use anyhow::Result; -use overf::wrapping as w; use std::path::PathBuf; use std::sync::Weak; diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index 6e268b3..db33ef1 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -9,6 +9,7 @@ use crate::utils::serde_base64bytes::*; use crate::utils::simple_pack::*; use anyhow::Result; use msg_tool_xp3data::*; +use overf::wrapping as w; use serde::Deserialize; use std::collections::{BTreeMap, HashMap}; use std::io::{Read, Seek, SeekFrom}; @@ -157,6 +158,7 @@ enum CryptType { #[serde(default)] key2: u32, }, + SeitenCrypt, } #[derive(Clone, Debug, Deserialize)] @@ -242,6 +244,7 @@ impl Schema { *key1, *key2, )?), + CryptType::SeitenCrypt => Box::new(SeitenCrypt::new(self.base.clone())), }) } } @@ -684,6 +687,80 @@ impl Read for FlyingShineCryptReader { } } +#[derive(Debug)] +pub struct SeitenCrypt { + base: BaseSchema, +} + +impl SeitenCrypt { + pub fn new(base: BaseSchema) -> Self { + Self { base } + } +} + +impl Crypt for SeitenCrypt { + 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(SeitenCryptReader::new( + stream, + cur_seg, + entry.file_hash, + ))) + } + fn decrypt_with_seek<'a>( + &self, + entry: &Xp3Entry, + cur_seg: &Segment, + stream: Box, + ) -> Result> { + Ok(Box::new(SeitenCryptReader::new( + stream, + cur_seg, + entry.file_hash, + ))) + } +} + +seek_reader_key_impl!(SeitenCryptReader, u32); + +impl Read for SeitenCryptReader { + 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() { + let mut shift; + let key = self.key ^ (offset as u32); + if key & 2 != 0 { + shift = key & 0x18; + let ebx = key >> shift; + shift &= 8; + *t ^= (ebx | (key >> shift)) as u8; + } + if key & 4 != 0 { + w!(*t += key as u8); + } + if key & 8 != 0 { + shift = key & 0x10; + w!(*t -= (key >> shift) as u8); + } + offset += 1; + } + self.pos += readed as u64; + Ok(readed) + } +} + #[test] fn test_deserialize_crypt() { for (key, schema) in CRYPT_SCHEMA.iter() {