diff --git a/msg_tool_xp3data/crypt.json b/msg_tool_xp3data/crypt.json index dfda4e1..d1e5259 100644 --- a/msg_tool_xp3data/crypt.json +++ b/msg_tool_xp3data/crypt.json @@ -2204,6 +2204,11 @@ "ControlBlockName": "shugaten.bin", "Title": "しゅがてん!-sugarfull tempering- | 糖调! -sugarfull tempering- | 糖调! | 甜糖热恋" }, + "Sis x Miko": { + "$type": "SisMikoCrypt", + "StartupTjsNotEncrypted": true, + "Title": "シス×みこ" + }, "Sis Koi ~Shisukoi~": { "$type": "CxEncryption", "Mask": 286, diff --git a/src/scripts/kirikiri/archive/xp3/crypt/chain_reaction.rs b/src/scripts/kirikiri/archive/xp3/crypt/chain_reaction.rs index 876f8de..f64bf96 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/chain_reaction.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/chain_reaction.rs @@ -444,3 +444,78 @@ impl Read for XanaduCryptReader { Ok(readed) } } + +#[derive(Debug)] +pub struct SisMikoCrypt { + base: XanaduCrypt, +} + +impl SisMikoCrypt { + pub fn new(base: BaseSchema) -> Self { + Self { + base: XanaduCrypt::new(base), + } + } +} + +impl AsRef for SisMikoCrypt { + fn as_ref(&self) -> &BaseSchema { + self.base.as_ref() + } +} + +impl Crypt for SisMikoCrypt { + base_schema_impl!(); + fn init(&self, archive: &mut Xp3Archive) -> Result<()> { + IChainReactionCrypt::init(&self.base, archive) + } + 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(SisMikoCryptReader::new( + stream, + cur_seg, + (self.base.get_encryption_limit(entry), entry.file_hash), + ))) + } + fn decrypt_with_seek<'a>( + &self, + entry: &Xp3Entry, + cur_seg: &Segment, + stream: Box, + ) -> Result> { + Ok(Box::new(SisMikoCryptReader::new( + stream, + cur_seg, + (self.base.get_encryption_limit(entry), entry.file_hash), + ))) + } +} + +impl Read for SisMikoCryptReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let readed = self.inner.read(buf)?; + let (limit, hash) = self.key; + let limit = limit as u64; + let mut offset = self.seg_start + self.pos; + if offset < limit { + let key = !(hash.rotate_right(16)); + let count = (limit - offset).min(readed as u64); + for t in buf[..count as usize].iter_mut() { + *t ^= (key >> ((offset & 3) << 3)) as u8; + offset += 1; + } + } + self.pos += readed as u64; + Ok(readed) + } +} diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index 3a3cdac..9df6cd1 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -270,6 +270,7 @@ enum CryptType { HachukanoCrypt, ChocolatCrypt, XanaduCrypt, + SisMikoCrypt, } #[derive(Clone, Debug, Deserialize)] @@ -442,6 +443,9 @@ impl Schema { Box::new(chain_reaction::ChocolatCrypt::new(self.base.clone())) } CryptType::XanaduCrypt => Box::new(chain_reaction::XanaduCrypt::new(self.base.clone())), + CryptType::SisMikoCrypt => { + Box::new(chain_reaction::SisMikoCrypt::new(self.base.clone())) + } }) } } @@ -2480,6 +2484,7 @@ impl Crypt for PureMoreCrypt { seek_reader_key_impl!(ChainReactionCryptReader, (u32, u32)); seek_reader_key_impl!(XanaduCryptReader, (u32, u32)); +seek_reader_key_impl!(SisMikoCryptReader, (u32, u32)); #[test] fn test_deserialize_crypt() {