use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek};
use crate::error::Result;
use crate::binary::ReadBytes;
static INDEX_STRING_KEY: [u8; 64] = *b"#:AhppdV-!PEfz&}[]Nv?6w4guU%dF5.fq:n*-qGuhBJJBm&?2tPy!geW/+k#pG?";
static INDEX_U32_KEY: u32 = 0xE10B_73F4;
static DATA_KEY: u64 = 0x8FEB_2A67_40A6_920E;
pub trait Decryptable: ReadBytes + Read + Seek {
fn decrypt(&mut self) -> Result<Vec<u8>> {
let ciphertext_len = self.len()? as usize;
let mut ciphertext = self.read_slice(ciphertext_len, false)?;
let size = ciphertext.len();
let padding = 8 - (size % 8);
if padding < 8 {
ciphertext.resize(size + padding, 0);
}
let mut plaintext = Vec::with_capacity(ciphertext.len());
let mut edi: u64 = 0;
let chunks = ciphertext.len() / 8;
for i in 0..chunks {
let esi = edi as usize;
if i == chunks - 1 {
plaintext.extend_from_slice(&ciphertext[esi..esi + 8]);
} else {
let mut prod = DATA_KEY.wrapping_mul(!edi);
prod ^= (&ciphertext[esi..esi + 8]).read_u64::<LittleEndian>().unwrap();
plaintext.write_u64::<LittleEndian>(prod).unwrap();
}
edi += 8
}
plaintext.truncate(size);
Ok(plaintext)
}
fn decrypt_u32(&mut self, second_key: u32) -> Result<u32> {
let bytes = self.read_u32()?;
Ok(bytes ^ INDEX_U32_KEY ^ !second_key)
}
fn decrypt_string(&mut self, second_key: u8) -> Result<String> {
let mut path: String = String::new();
let mut index = 0;
loop {
let character = self.read_u8()? ^ INDEX_STRING_KEY[index % INDEX_STRING_KEY.len()] ^ !second_key;
index += 1;
if character == 0 { break; }
path.push(character as char);
}
Ok(path)
}
}
impl<R: ReadBytes + Read + Seek> Decryptable for R {}