msg_tool\scripts\kirikiri\archive\xp3\crypt/
chain_reaction.rs1use super::*;
2use crate::ext::mutex::*;
3use crate::utils::lzss::*;
4use std::sync::Mutex;
5
6macro_rules! base_schema_impl {
7 () => {
8 fn hash_after_crypt(&self) -> bool {
9 AsRef::<BaseSchema>::as_ref(self).hash_after_crypt
10 }
11 fn startup_tjs_not_encrypted(&self) -> bool {
12 AsRef::<BaseSchema>::as_ref(self).startup_tjs_not_encrypted
13 }
14 fn obfuscated_index(&self) -> bool {
15 AsRef::<BaseSchema>::as_ref(self).obfuscated_index
16 }
17 };
18}
19
20fn convert_u32_from_string(input: &str) -> Result<u32> {
21 let s = input.trim();
22 if s.is_empty() {
23 anyhow::bail!("String is empty");
24 }
25 Ok(if s.starts_with("0x") || s.starts_with("0X") {
26 u32::from_str_radix(&s[2..], 16)?
27 } else if s.starts_with('#') {
28 u32::from_str_radix(&s[1..], 16)?
29 } else if s.to_lowercase().starts_with("&h") {
30 u32::from_str_radix(&s[2..], 16)?
31 } else {
32 s.parse::<u32>()?
33 })
34}
35
36trait IChainReactionCrypt: std::fmt::Debug {
37 fn get_encryption_limit(&self, entry: &Xp3Entry) -> u32;
38 fn init(&self, archive: &mut Xp3Archive) -> Result<()>;
39}
40
41#[derive(Debug)]
42struct ChainReactionCryptBase {
43 encryption_threshold_map: Mutex<HashMap<u32, u32>>,
44 list_bin: String,
45}
46
47impl ChainReactionCryptBase {
48 fn new(list_bin: String) -> Self {
49 Self {
50 encryption_threshold_map: Mutex::new(HashMap::new()),
51 list_bin,
52 }
53 }
54
55 fn init2(&self, mut bin: Vec<u8>) -> Result<()> {
56 if !bin.starts_with(b"\"\r\n") {
57 for _ in 0..3 {
58 bin = Self::decode_list_bin(bin)?;
59 }
60 }
62 self.encryption_threshold_map.lock_blocking().clear();
63 self.parse_list_bin(bin)
64 }
65
66 fn read_list_bin(archive: &mut Xp3Archive, list_name: &str) -> Result<Option<Vec<u8>>> {
67 let bin = match archive.entries.iter().find(|x| x.name == list_name) {
68 Some(index) => index.clone(),
69 None => return Ok(None),
70 };
71 let mut entry = Entry::new2(
72 archive.inner.clone(),
73 bin,
74 archive.base_offset,
75 archive.crypt.clone(),
76 );
77 let mut data = Vec::new();
78 entry.read_to_end(&mut data)?;
79 Ok(Some(data))
80 }
81
82 fn parse_list_bin(&self, data: Vec<u8>) -> Result<()> {
83 let mut map = self.encryption_threshold_map.lock_blocking();
84 let decoded = decode_to_string(Encoding::Utf8, &data, true)?;
85 for line in decoded.lines() {
86 let line = line.trim();
87 if line.is_empty() || !line.starts_with("0") {
88 continue;
89 }
90 let pair: Vec<_> = line.split(',').collect();
91 if pair.len() > 1 {
92 let hash = convert_u32_from_string(pair[0])?;
93 let threshold = convert_u32_from_string(pair[1])?;
94 map.insert(hash, threshold);
95 }
96 }
97 Ok(())
98 }
99
100 fn decode_list_bin(data: Vec<u8>) -> Result<Vec<u8>> {
101 let mut header = [0; 0x30];
102 Self::decode_dpd(&data[..0x30], &mut header)?;
103 let hread = MemReaderRef::new(&header);
104 let packed_size = hread.cpeek_u32_at(0x0c)? as usize;
105 let unpacked_size = hread.cpeek_u32_at(0x10)? as usize;
106 if packed_size > data.len() - 0x30 {
107 anyhow::bail!("Data is too smail.");
108 }
109 let sig = &header[0..4];
110 if sig == b"DPDC" {
111 let mut decrypted = Vec::with_capacity(packed_size);
112 decrypted.resize(packed_size, 0);
113 Self::decode_dpd(&data[0x30..packed_size + 0x30], &mut decrypted)?;
114 Ok(decrypted)
115 } else if sig == b"SZLC" {
116 let reader = MemReaderRef::new(&data[0x30..packed_size + 0x30]);
117 let mut lzss = LzssReader::new(reader);
118 let mut result = Vec::with_capacity(unpacked_size);
119 lzss.read_to_end(&mut result)?;
120 if result.len() > unpacked_size {
121 result.truncate(unpacked_size);
122 }
123 Ok(result)
124 } else if sig == b"ELRC" {
125 let min_repeat = hread.cpeek_u32_at(0x1C)?;
126 let mut decoded = Vec::with_capacity(unpacked_size);
127 decoded.resize(unpacked_size, 0);
128 Self::decode_rle(&data[0x30..packed_size + 0x30], &mut decoded, min_repeat)?;
129 Ok(decoded)
130 } else {
131 anyhow::bail!("Unknown signature: {:?}", sig);
132 }
133 }
134
135 fn decode_dpd(src: &[u8], dst: &mut [u8]) -> Result<()> {
136 let length = src.len();
137 if length != dst.len() {
138 anyhow::bail!("Length no matched.");
139 }
140 if length < 8 {
141 dst.copy_from_slice(src);
142 return Ok(());
143 }
144 let tail = length & 3;
145 if tail > 0 {
146 dst[length - tail..].copy_from_slice(&src[length - tail..]);
147 }
148 let length = length / 4;
149 let mut reader = MemReaderRef::new(src);
150 let mut writer = MemWriterRef::new(dst);
151 let mut val = reader.read_u32()?;
152 for _ in 0..length - 1 {
153 let nval = reader.read_u32()?;
154 writer.write_u32(val ^ nval)?;
155 val = nval;
156 }
157 let fdst = writer.peek_u32_at(0)?;
158 writer.write_u32(fdst ^ val)?;
159 Ok(())
160 }
161
162 fn decode_rle(src: &[u8], dst: &mut [u8], min_repeat: u32) -> Result<()> {
163 let mut reader = MemReaderRef::new(src);
164 let mut writer = MemWriterRef::new(dst);
165 while !reader.is_eof() {
166 let b = reader.read_u8()?;
167 let mut repeat = 1;
168 while repeat < min_repeat && !reader.is_eof() && reader.cpeek_u8()? == b {
169 repeat += 1;
170 reader.pos += 1;
171 }
172 if repeat == min_repeat {
173 let ctl = reader.read_u8()?;
174 if ctl > 0x7F {
175 repeat += (reader.read_u8()? as u32) + (((ctl & 0x7F) as u32) << 8) + 0x80;
176 } else {
177 repeat += ctl as u32;
178 }
179 }
180 for _ in 0..repeat {
181 writer.write_u8(b)?;
182 }
183 }
184 Ok(())
185 }
186}
187
188impl IChainReactionCrypt for ChainReactionCryptBase {
189 fn get_encryption_limit(&self, entry: &Xp3Entry) -> u32 {
190 self.encryption_threshold_map
191 .lock_blocking()
192 .get(&entry.file_hash)
193 .map(|s| *s)
194 .unwrap_or(0x200)
195 }
196 fn init(&self, archive: &mut Xp3Archive) -> Result<()> {
197 let bin = Self::read_list_bin(archive, &self.list_bin)?;
198 if let Some(bin) = bin {
199 if bin.len() >= 0x30 {
200 self.init2(bin)?;
201 }
202 }
203 Ok(())
204 }
205}
206
207#[derive(Debug)]
208pub struct ChainReactionCrypt {
209 base: BaseSchema,
210 inner: Box<dyn IChainReactionCrypt + Send + Sync>,
211}
212
213impl ChainReactionCrypt {
214 pub fn new(base: BaseSchema) -> Self {
215 Self {
216 base,
217 inner: Box::new(ChainReactionCryptBase::new("plugin/list.bin".into())),
218 }
219 }
220
221 fn new_inner(base: BaseSchema, inner: Box<dyn IChainReactionCrypt + Send + Sync>) -> Self {
222 Self { base, inner }
223 }
224}
225
226impl AsRef<BaseSchema> for ChainReactionCrypt {
227 fn as_ref(&self) -> &BaseSchema {
228 &self.base
229 }
230}
231
232impl Crypt for ChainReactionCrypt {
233 base_schema_impl!();
234 fn init(&self, archive: &mut Xp3Archive) -> Result<()> {
235 self.inner.init(archive)
236 }
237 fn decrypt_supported(&self) -> bool {
238 true
239 }
240 fn decrypt_seek_supported(&self) -> bool {
241 true
242 }
243 fn decrypt<'a>(
244 &self,
245 entry: &Xp3Entry,
246 cur_seg: &Segment,
247 stream: Box<dyn Read + Send + Sync + 'a>,
248 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
249 Ok(Box::new(ChainReactionCryptReader::new(
250 stream,
251 cur_seg,
252 (self.inner.get_encryption_limit(entry), entry.file_hash),
253 )))
254 }
255 fn decrypt_with_seek<'a>(
256 &self,
257 entry: &Xp3Entry,
258 cur_seg: &Segment,
259 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
260 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
261 Ok(Box::new(ChainReactionCryptReader::new(
262 stream,
263 cur_seg,
264 (self.inner.get_encryption_limit(entry), entry.file_hash),
265 )))
266 }
267}
268
269impl<R: Read> Read for ChainReactionCryptReader<R> {
270 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
271 let readed = self.inner.read(buf)?;
272 let (limit, hash) = self.key;
273 let limit = limit as u64;
274 let mut offset = self.seg_start + self.pos;
275 if offset < limit {
276 let count = (limit - offset).min(readed as u64);
277 for t in buf[..count as usize].iter_mut() {
278 *t ^= (offset ^ ((hash >> ((offset & 3) << 3)) as u8) as u64) as u8;
279 offset += 1;
280 }
281 }
282 self.pos += readed as u64;
283 Ok(readed)
284 }
285}
286
287#[derive(Debug)]
288pub struct HachukanoCrypt {
289 base: ChainReactionCryptBase,
290}
291
292impl HachukanoCrypt {
293 pub fn new(base: BaseSchema) -> ChainReactionCrypt {
294 ChainReactionCrypt::new_inner(
295 base,
296 Box::new(Self {
297 base: ChainReactionCryptBase::new("plugins/list.txt".into()),
298 }),
299 )
300 }
301}
302
303impl IChainReactionCrypt for HachukanoCrypt {
304 fn get_encryption_limit(&self, entry: &Xp3Entry) -> u32 {
305 let limit = self.base.get_encryption_limit(entry);
306 match limit {
307 0 => 0,
308 1 => 0x100,
309 2 => 0x200,
310 3 => entry.original_size as u32,
311 _ => limit,
312 }
313 }
314 fn init(&self, archive: &mut Xp3Archive) -> Result<()> {
315 self.base.init(archive)
316 }
317}
318
319#[derive(Debug)]
320pub struct ChocolatCrypt {
321 base: ChainReactionCryptBase,
322}
323
324impl ChocolatCrypt {
325 pub fn new(base: BaseSchema) -> ChainReactionCrypt {
326 ChainReactionCrypt::new_inner(
327 base,
328 Box::new(Self {
329 base: ChainReactionCryptBase::new("plugins/list.txt".into()),
330 }),
331 )
332 }
333}
334
335impl IChainReactionCrypt for ChocolatCrypt {
336 fn get_encryption_limit(&self, entry: &Xp3Entry) -> u32 {
337 let limit = self.base.get_encryption_limit(entry);
338 match limit {
339 0 => 0,
340 2 => entry.original_size as u32,
341 _ => 0x100,
342 }
343 }
344 fn init(&self, archive: &mut Xp3Archive) -> Result<()> {
345 self.base.init(archive)
346 }
347}
348
349#[derive(Debug)]
350pub struct XanaduCrypt {
351 base: BaseSchema,
352 inner: ChainReactionCryptBase,
353}
354
355impl XanaduCrypt {
356 pub fn new(base: BaseSchema) -> Self {
357 Self {
358 base,
359 inner: ChainReactionCryptBase::new("plugins/list.txt".into()),
360 }
361 }
362}
363
364impl AsRef<BaseSchema> for XanaduCrypt {
365 fn as_ref(&self) -> &BaseSchema {
366 &self.base
367 }
368}
369
370impl IChainReactionCrypt for XanaduCrypt {
371 fn get_encryption_limit(&self, entry: &Xp3Entry) -> u32 {
372 let limit = self.inner.get_encryption_limit(entry);
373 match limit {
374 0 => 0,
375 2 => entry.original_size as u32,
376 _ => 0x100,
377 }
378 }
379 fn init(&self, archive: &mut Xp3Archive) -> Result<()> {
380 let mut bin = ChainReactionCryptBase::read_list_bin(archive, "list2.txt")?;
381 if bin.is_none() {
382 bin = ChainReactionCryptBase::read_list_bin(archive, "plugins/list.txt")?;
383 }
384 if let Some(bin) = bin {
385 self.inner.init2(bin)?;
386 }
387 Ok(())
388 }
389}
390
391impl Crypt for XanaduCrypt {
392 base_schema_impl!();
393 fn init(&self, archive: &mut Xp3Archive) -> Result<()> {
394 IChainReactionCrypt::init(self, archive)
395 }
396 fn decrypt_supported(&self) -> bool {
397 true
398 }
399 fn decrypt_seek_supported(&self) -> bool {
400 true
401 }
402 fn decrypt<'a>(
403 &self,
404 entry: &Xp3Entry,
405 cur_seg: &Segment,
406 stream: Box<dyn Read + Send + Sync + 'a>,
407 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
408 Ok(Box::new(XanaduCryptReader::new(
409 stream,
410 cur_seg,
411 (self.get_encryption_limit(entry), entry.file_hash),
412 )))
413 }
414 fn decrypt_with_seek<'a>(
415 &self,
416 entry: &Xp3Entry,
417 cur_seg: &Segment,
418 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
419 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
420 Ok(Box::new(XanaduCryptReader::new(
421 stream,
422 cur_seg,
423 (self.get_encryption_limit(entry), entry.file_hash),
424 )))
425 }
426}
427
428impl<R: Read> Read for XanaduCryptReader<R> {
429 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
430 let readed = self.inner.read(buf)?;
431 let (limit, hash) = self.key;
432 let limit = limit as u64;
433 let mut offset = self.seg_start + self.pos;
434 if offset < limit {
435 let key = hash ^ (!0x03020100);
436 let count = (limit - offset).min(readed as u64);
437 for t in buf[..count as usize].iter_mut() {
438 let extra = (((offset & 0xFF) >> 2) << 2) as u8;
439 *t ^= (key >> (((offset & 3) << 3) as u32)) as u8 ^ extra;
440 offset += 1;
441 }
442 }
443 self.pos += readed as u64;
444 Ok(readed)
445 }
446}
447
448#[derive(Debug)]
449pub struct SisMikoCrypt {
450 base: XanaduCrypt,
451}
452
453impl SisMikoCrypt {
454 pub fn new(base: BaseSchema) -> Self {
455 Self {
456 base: XanaduCrypt::new(base),
457 }
458 }
459}
460
461impl AsRef<BaseSchema> for SisMikoCrypt {
462 fn as_ref(&self) -> &BaseSchema {
463 self.base.as_ref()
464 }
465}
466
467impl Crypt for SisMikoCrypt {
468 base_schema_impl!();
469 fn init(&self, archive: &mut Xp3Archive) -> Result<()> {
470 IChainReactionCrypt::init(&self.base, archive)
471 }
472 fn decrypt_supported(&self) -> bool {
473 true
474 }
475 fn decrypt_seek_supported(&self) -> bool {
476 true
477 }
478 fn decrypt<'a>(
479 &self,
480 entry: &Xp3Entry,
481 cur_seg: &Segment,
482 stream: Box<dyn Read + Send + Sync + 'a>,
483 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
484 Ok(Box::new(SisMikoCryptReader::new(
485 stream,
486 cur_seg,
487 (self.base.get_encryption_limit(entry), entry.file_hash),
488 )))
489 }
490 fn decrypt_with_seek<'a>(
491 &self,
492 entry: &Xp3Entry,
493 cur_seg: &Segment,
494 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
495 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
496 Ok(Box::new(SisMikoCryptReader::new(
497 stream,
498 cur_seg,
499 (self.base.get_encryption_limit(entry), entry.file_hash),
500 )))
501 }
502}
503
504impl<R: Read> Read for SisMikoCryptReader<R> {
505 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
506 let readed = self.inner.read(buf)?;
507 let (limit, hash) = self.key;
508 let limit = limit as u64;
509 let mut offset = self.seg_start + self.pos;
510 if offset < limit {
511 let key = !(hash.rotate_right(16));
512 let count = (limit - offset).min(readed as u64);
513 for t in buf[..count as usize].iter_mut() {
514 *t ^= (key >> ((offset & 3) << 3)) as u8;
515 offset += 1;
516 }
517 }
518 self.pos += readed as u64;
519 Ok(readed)
520 }
521}