diff --git a/msg_tool_xp3data/crypt.json b/msg_tool_xp3data/crypt.json index ff82224..c8f278d 100644 --- a/msg_tool_xp3data/crypt.json +++ b/msg_tool_xp3data/crypt.json @@ -1207,6 +1207,10 @@ "$type": "HashCrypt", "Title": "お嬢様は学園の精液便所 ~寝取らせ・ぶっかけ・乱交生活日誌~" }, + "Okiba ga Nai!": { + "$type": "OkibaCrypt", + "Title": "置き場がない!" + }, "Omae no Tsuma wa, Ore no Inran Niku Yome": { "$type": "HashCrypt", "Title": "お前の妻は、オレの淫乱肉嫁 ~お隣さんは欲求不満でした~" diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index db33ef1..2b95cd6 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -159,6 +159,7 @@ enum CryptType { key2: u32, }, SeitenCrypt, + OkibaCrypt, } #[derive(Clone, Debug, Deserialize)] @@ -245,6 +246,7 @@ impl Schema { *key2, )?), CryptType::SeitenCrypt => Box::new(SeitenCrypt::new(self.base.clone())), + CryptType::OkibaCrypt => Box::new(OkibaCrypt::new(self.base.clone())), }) } } @@ -687,52 +689,53 @@ impl Read for FlyingShineCryptReader { } } -#[derive(Debug)] -pub struct SeitenCrypt { - base: BaseSchema, +macro_rules! seek_crypt_filehash_key_base_impl { + ($crypt:ident, $reader:ident) => { + #[derive(Debug)] + pub struct $crypt { + base: BaseSchema, + } + impl $crypt { + pub fn new(base: BaseSchema) -> Self { + Self { base } + } + } + impl Crypt for $crypt { + 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($reader::new(stream, cur_seg, entry.file_hash))) + } + fn decrypt_with_seek<'a>( + &self, + entry: &Xp3Entry, + cur_seg: &Segment, + stream: Box, + ) -> Result> { + Ok(Box::new($reader::new(stream, cur_seg, entry.file_hash))) + } + } + }; } -impl SeitenCrypt { - pub fn new(base: BaseSchema) -> Self { - Self { base } - } +macro_rules! seek_crypt_filehash_key_impl { + ($crypt:ident,$reader:ident<$t:ident>,$key:ty) => { + seek_crypt_filehash_key_base_impl!($crypt, $reader); + seek_reader_key_impl!($reader<$t>, $key); + }; } -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); +seek_crypt_filehash_key_impl!(SeitenCrypt, SeitenCryptReader, u32); impl Read for SeitenCryptReader { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { @@ -761,6 +764,43 @@ impl Read for SeitenCryptReader { } } +seek_crypt_filehash_key_impl!(OkibaCrypt, OkibaCryptReader, u32); + +impl Read for OkibaCryptReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let readed = self.inner.read(buf)?; + let mut offset = self.seg_start + self.pos; + let mut i = 0; + if offset < 0x65 { + let key = self.key >> 4; + let limit = readed.min(0x65 - offset as usize); + for _ in 0..limit { + buf[i] ^= key as u8; + i += 1; + offset += 1; + } + } + if i < readed { + offset -= 0x65; + let mut key = self.key; + key = ((key & 0xff0000) << 8) + | ((key & 0xff000000) >> 8) + | ((key & 0xff00) >> 8) + | ((key & 0xff) << 8); + loop { + buf[i] ^= (key >> (8 * (offset as u32 & 3))) as u8; + offset += 1; + i += 1; + if i >= readed { + break; + } + } + } + self.pos += readed as u64; + Ok(readed) + } +} + #[test] fn test_deserialize_crypt() { for (key, schema) in CRYPT_SCHEMA.iter() {