From f4e4a48aa331a3b489066949bc52d3e2ccfba49b Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sat, 2 May 2026 22:08:07 +0800 Subject: [PATCH] Add MiburoCrypt --- msg_tool_xp3data/crypt.json | 4 + src/scripts/kirikiri/archive/xp3/crypt/mod.rs | 73 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/msg_tool_xp3data/crypt.json b/msg_tool_xp3data/crypt.json index 6405c5c..297ec5c 100644 --- a/msg_tool_xp3data/crypt.json +++ b/msg_tool_xp3data/crypt.json @@ -177,6 +177,10 @@ "$type": "HashCrypt", "Title": "ばぁばとママとの超熟母娘丼 ~3世代での家庭内エッチ~ | 爱你深至夏海蔚蓝" }, + "Bakumatsu Jinchuu Houkoku Resshiden Miburo": { + "$type": "MiburoCrypt", + "Title": "幕末尽忠報国烈士伝 MIBURO" + }, "Bakunyuu Oyakodon": { "$type": "DieselmineCrypt", "Title": "爆乳母娘丼 ~仕事先の親子が寝取りやすそうな件~" diff --git a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs index 8ec67b0..eda5a52 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/mod.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/mod.rs @@ -258,6 +258,7 @@ enum CryptType { }, SyangrilaSmartCrypt, Kano2Crypt, + MiburoCrypt, } #[derive(Clone, Debug, Deserialize)] @@ -408,6 +409,7 @@ impl Schema { )), CryptType::SyangrilaSmartCrypt => Box::new(SyangrilaSmartCrypt::new(self.base.clone())), CryptType::Kano2Crypt => Box::new(Kano2Crypt::new(self.base.clone())), + CryptType::MiburoCrypt => Box::new(MiburoCrypt::new(self.base.clone())), }) } } @@ -2301,6 +2303,77 @@ impl Read for Kano2CryptReader { } } +#[derive(Debug)] +pub struct MiburoCrypt { + base: BaseSchema, +} + +impl MiburoCrypt { + pub fn new(base: BaseSchema) -> Self { + Self { base } + } + + fn init_key(mut hash: u32) -> [u8; 29] { + hash &= 0x1FFFFFFF; + hash |= (hash & 1) << 29; + let mut key = [0; 29]; + for i in 0..29 { + key[i] = hash as u8; + hash = (hash >> 8) | ((hash << 0x15) & 0xFF000000); + } + key + } +} + +impl Crypt for MiburoCrypt { + 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(MiburoCryptReader::new( + stream, + cur_seg, + Self::init_key(entry.file_hash), + ))) + } + fn decrypt_with_seek<'a>( + &self, + entry: &Xp3Entry, + cur_seg: &Segment, + stream: Box, + ) -> Result> { + Ok(Box::new(MiburoCryptReader::new( + stream, + cur_seg, + Self::init_key(entry.file_hash), + ))) + } +} + +seek_reader_key_impl!(MiburoCryptReader, [u8; 29]); + +impl Read for MiburoCryptReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let readed = self.inner.read(buf)?; + let mut offset = ((self.seg_start + self.pos) % 29) as usize; + for t in buf[..readed].iter_mut() { + *t ^= self.key[offset]; + offset = (offset + 1) % 29; + } + self.pos += readed as u64; + Ok(readed) + } +} + #[test] fn test_deserialize_crypt() { for (key, schema) in CRYPT_SCHEMA.iter() {