add archive tests

This commit is contained in:
2023-05-08 23:41:55 +02:00
parent 06b679ba30
commit 4d048e77ee
19 changed files with 837 additions and 101 deletions

View File

@@ -1,46 +0,0 @@
use binrw::prelude::*;
use binrw::{BinRead, Error};
use image;
use image::error::{DecodingError, ImageFormatHint};
use image::{ImageError, ImageResult, Rgb, RgbImage};
use std::io::Cursor;
#[binrw]
#[brw(little)]
pub struct LevelTile {
pub index: u8,
pub id: u8,
}
#[binrw]
#[brw(little)]
pub struct LevelLayer {
pub tile_count: u32,
pub width: u32,
pub height: u32,
pub unknown_2: u32,
#[br(count = width * height)]
pub tiles: Vec<LevelTile>,
}
pub fn level_tile_data_to_image(tile_data: &[u8]) -> ImageResult<RgbImage> {
let mut cursor = Cursor::new(tile_data);
let layer = LevelLayer::read(&mut cursor).map_err(to_decoding_err)?;
let mut image = RgbImage::new(layer.width, layer.height);
for y in 0..layer.height {
for x in 0..layer.width {
let tile = LevelTile::read(&mut cursor).map_err(to_decoding_err)?;
image.put_pixel(x, y, Rgb([tile.id, tile.index, 0]));
}
}
Ok(image)
}
fn to_decoding_err(err: Error) -> ImageError {
ImageError::Decoding(DecodingError::new(
ImageFormatHint::Name(String::from("mhjnr_layer")),
err,
))
}

View File

@@ -1,131 +0,0 @@
use binrw::prelude::*;
use binrw::Endian;
use image::error::{DecodingError, ImageFormatHint};
use image::{AnimationDecoder, Delay, Frame, Frames, ImageBuffer, ImageError};
use std::io::{Read, Seek};
use std::time::Duration;
#[binread]
#[br(little, magic = 0x67u32)]
pub struct RleImage {
pub hash: u64,
pub color_table: [[u8; 4]; 512],
pub width: u32,
pub height: u32,
pub numerator: u32,
pub denominator: u32,
#[br(temp)]
pub frame_count: u32,
#[br(count = frame_count)]
pub frames: Vec<RleLayer>,
}
#[binread]
#[br(little)]
pub struct RleLayer {
pub width: u32,
pub height: u32,
pub left: u32,
pub top: u32,
pub numerator: u32,
pub denominator: u32,
pub data_size: u32,
pub unknown3: u32,
#[br(args(width * height), parse_with = parse_rle)]
pub data: Vec<u8>,
}
pub fn parse_rle<R: Read + Seek>(
reader: &mut R,
endian: Endian,
(size,): (u32,),
) -> BinResult<Vec<u8>> {
let mut data = Vec::with_capacity(size as usize);
while data.len() != size as usize {
let count: i8 = reader.read_type(endian)?;
if count > 0 {
let value: u8 = reader.read_type(endian)?;
for _ in 0..count {
data.push(value);
}
} else {
for _ in 0..-count {
data.push(reader.read_type(endian)?);
}
}
}
Ok(data)
}
impl RleImage {
pub fn get_image_data(&self, layer: &RleLayer) -> Vec<u8> {
let mut data = Vec::<u8>::with_capacity(self.width as usize * self.height as usize * 4);
let mut i = 0;
for y in 0..self.height {
for x in 0..self.width {
if y < layer.top
|| y >= layer.top + layer.height
|| x < layer.left
|| x >= layer.left + layer.width
{
data.push(0);
data.push(0);
data.push(0);
data.push(0);
} else {
let color = self.color_table[layer.data[i] as usize];
i += 1;
data.push(color[2]);
data.push(color[1]);
data.push(color[0]);
data.push(if color[2] == 0 && color[1] == 0 && color[0] == 0 {
0
} else {
255
});
}
}
}
data
}
}
impl<'a> AnimationDecoder<'a> for RleImage {
fn into_frames(self) -> Frames<'a> {
Frames::new(Box::new(self.frames.into_iter().map(move |frame| {
let buffer = ImageBuffer::from_raw(
frame.width,
frame.height,
frame
.data
.into_iter()
.flat_map(|it| bgra_to_rgba(self.color_table[it as usize]))
.collect(),
)
.ok_or(to_rle_image_err(std::fmt::Error::default()))?;
Ok(Frame::from_parts(
buffer,
frame.left,
frame.top,
Delay::from_saturating_duration(Duration::from_millis(80)),
))
})))
}
}
pub fn bgra_to_rgba(pixel: [u8; 4]) -> [u8; 4] {
[pixel[2], pixel[1], pixel[0], pixel[3]]
}
fn to_rle_image_err<T>(err: T) -> ImageError
where
T: Into<Box<dyn std::error::Error + Send + Sync>>,
{
ImageError::Decoding(DecodingError::new(
ImageFormatHint::Name(String::from("mhjnr_rle")),
err,
))
}

View File

@@ -1,75 +0,0 @@
#[derive(Debug)]
pub struct Sprites {
pub name: String,
pub sprite_type: SpriteType,
pub file_name: String,
pub render_mode: RenderMode,
pub frames: Option<CropMode>,
}
#[derive(Debug)]
pub enum Error {
InvalidData,
UnknownEnum(String),
}
impl Sprites {
pub fn parse(string: &str) -> Result<Vec<Self>, Error> {
string
.split('\n')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(Sprites::parse_single)
.collect()
}
pub fn parse_single(string: &str) -> Result<Self, Error> {
let mut components = string.split_whitespace();
Ok(Sprites {
file_name: components.next().ok_or(Error::InvalidData)?.to_string(),
sprite_type: match components.next().ok_or(Error::InvalidData)? {
"anim_rle" => SpriteType::AnimRle,
"anim" => SpriteType::Anim,
"static" => SpriteType::Static,
e => return Err(Error::UnknownEnum(e.to_string())),
},
name: components.next().ok_or(Error::InvalidData)?.to_string(),
render_mode: match components.next().ok_or(Error::InvalidData)? {
"normx" => RenderMode::NormX,
"flipx" => RenderMode::FlipX,
e => return Err(Error::UnknownEnum(e.to_string())),
},
frames: if let Some(c) = components.next() {
Some(match c {
"nocrop" => CropMode::NoCrop,
x => x
.parse::<i32>()
.map(CropMode::FrameCount)
.map_err(|e| Error::UnknownEnum(e.to_string()))?,
})
} else {
None
},
})
}
}
#[derive(Debug)]
pub enum CropMode {
FrameCount(i32),
NoCrop,
}
#[derive(Debug)]
pub enum RenderMode {
NormX,
FlipX,
}
#[derive(Debug)]
pub enum SpriteType {
Static,
Anim,
AnimRle,
}

View File

@@ -1,68 +0,0 @@
use std::num::ParseIntError;
use std::string::FromUtf8Error;
#[derive(Debug)]
pub enum DecryptError {
FromUtf8Error(FromUtf8Error),
ParseIntError(ParseIntError),
}
impl From<FromUtf8Error> for DecryptError {
fn from(e: FromUtf8Error) -> DecryptError {
DecryptError::FromUtf8Error(e)
}
}
impl From<ParseIntError> for DecryptError {
fn from(e: ParseIntError) -> DecryptError {
DecryptError::ParseIntError(e)
}
}
/// Decrypts txt files contained inside the dat file
pub fn decrypt_txt<I>(buffer: I) -> Result<String, DecryptError>
where
I: Iterator<Item = u8>,
{
let mut key = 0x1234u16;
String::from_utf8(
buffer
.map(|char| {
let decr = char ^ key as u8;
key = key.wrapping_mul(3).wrapping_add(2);
decr
})
.map(|char| (((char >> 1) ^ (char << 1)) & 0x55) ^ (char << 1))
.collect(),
)
.map_err(DecryptError::from)
}
/// Parses a hex string to a Vec<u8>
fn from_hex(line: &str) -> Result<Vec<u8>, ParseIntError> {
(0..line.len())
.step_by(2)
.map(|i| u8::from_str_radix(line.get(i..=i + 1).unwrap_or(""), 16))
.collect()
}
/// This function is applied to *exposed* txt files,
/// such as the player profile or high scores
///
/// If the file is contained in the datafile, it has
/// to first be decrypted normally and then again
/// with this function.
pub fn decrypt_exposed_txt(contents: String) -> Result<String, DecryptError> {
contents
.split_terminator("\r\n")
.map(|line| line.trim())
.filter(|line| !line.is_empty())
.map(from_hex)
.map(|line| decrypt_txt(line.map_err(DecryptError::from)?.into_iter()))
.collect::<Result<Vec<String>, _>>()
.map(|l| l.join("\r\n"))
}
#[cfg(test)]
mod tests {}

View File

@@ -1 +0,0 @@
ABCDEFGHIJKLMNOPQRSTUVWXYZ<EFBFBD><EFBFBD><EFBFBD>abcdefghijklmnopqrstuvwxyz<EFBFBD><EFBFBD><EFBFBD><EFBFBD>0123456789,;.:!?+-*/=<>()[]{}\"$%&#~_<>^@|<7C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>I<EFBFBD>G<EFBFBD><47><EFBFBD><EFBFBD>Ռ<EFBFBD>S<EFBFBD><53><EFBFBD><EFBFBD><EFBFBD>ޟ<EFBFBD><DE9F><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>i<EFBFBD>g<EFBFBD><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>s<EFBFBD><73><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ע<EFBFBD><D7A2><EFBFBD><EFBFBD><EFBFBD>