const LITERALS: usize = 60;
const FUTURE_CODE: &str = "if(_update60)_update=function()_update60()_update60()end";
const FUTURE_CODE2: &str =
"if(_update60)_update=function()_update60()_update_buttons()_update60()end";
const LITERAL: &str = "^\n 0123456789abcdefghijklmnopqrstuvwxyz!#%(){}[]<>+=/*:;.,~_";
#[derive(thiserror::Error, Debug)]
pub enum P8Error {
#[error("Invalid block reference")]
InvalidBlock,
#[error("Decompressed length exceeds output buffer")]
OutputExceeded,
#[error("Unexpected end of input")]
EndOfInput,
}
pub fn decompress(input: &[u8], output: &mut [u8]) -> Result<usize, P8Error> {
let mut in_pos = 0;
macro_rules! read_val {
() => {{
if in_pos >= input.len() {
return Err(P8Error::EndOfInput);
}
let v = input[in_pos];
in_pos += 1;
v
}};
}
for _ in 0..4 {
read_val!();
}
let mut len = (read_val!() as usize) << 8;
len += read_val!() as usize;
read_val!();
read_val!();
if len > output.len() {
return Err(P8Error::OutputExceeded);
}
let mut out_pos = 0;
while out_pos < len {
let val = read_val!();
if (val as usize) < LITERALS {
if val == 0 {
output[out_pos] = read_val!();
} else {
output[out_pos] = LITERAL
.as_bytes()
.get(val as usize)
.copied()
.unwrap_or(b'?'); }
out_pos += 1;
} else {
let mut block_offset = (val as usize - LITERALS) * 16;
let val2 = read_val!();
block_offset += (val2 % 16) as usize;
let block_length = (val2 / 16) as usize + 2;
if block_offset == 0 || out_pos < block_offset || out_pos + block_length > output.len()
{
return Err(P8Error::InvalidBlock);
}
let (src, dst) = output.split_at_mut(out_pos);
let (from, _to) = dst.split_at_mut(block_length);
from.copy_from_slice(
&src[out_pos - block_offset..out_pos - block_offset + block_length],
);
out_pos += block_length;
}
}
let mut final_len = out_pos;
let as_str = std::str::from_utf8(&output[..final_len]).unwrap_or("");
if let Some(pos) = as_str.find(FUTURE_CODE) {
if pos + FUTURE_CODE.len() == final_len {
final_len = pos;
}
}
if let Some(pos) = as_str.find(FUTURE_CODE2) {
if pos + FUTURE_CODE2.len() == final_len {
final_len = pos;
}
}
output[final_len] = 0;
Ok(final_len)
}