use crate::{
core::{
result::Result,
sa::{self, Position, SymbolArt, SymbolArtLayer},
symbol,
},
parser::decode,
};
pub fn parse(bytes: impl Into<Box<[u8]>>) -> Result<impl SymbolArt + std::fmt::Debug> {
let body = get_body(bytes.into())?;
Payload::parse(&body)
}
fn get_body(mut bytes: Box<[u8]>) -> Result<Box<[u8]>> {
let compression = decode::validate_format(&bytes)?;
let (_, body) = bytes.split_at_mut(4);
decode::decrypt(body);
match compression {
decode::Compression::None => Ok(Box::from(body)),
decode::Compression::Compressed => decode::decompress(body),
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Payload {
header: Header,
layers: Vec<Layer>,
name: Vec<u16>,
}
impl Payload {
pub fn parse(bytes: &[u8]) -> Result<Self> {
let header = Header::parse(&bytes[0..std::mem::size_of::<Header>()])?;
let layers = Layers::parse(&bytes[std::mem::size_of::<Header>()..])?.into();
let name = Self::parse_name(bytes, &header)?;
Ok(Self {
header,
layers,
name,
})
}
fn parse_name(bytes: &[u8], header: &Header) -> Result<Vec<u16>> {
let size_of_header = std::mem::size_of::<Header>();
let size_of_layer = std::mem::size_of::<Layer>();
let start = size_of_header + size_of_layer * header.layers() as usize;
let name_bytes = bytes[usize::min(start, bytes.len())..]
.chunks_exact(2)
.take(13) .map(|b| u16::from_le_bytes(b.try_into().unwrap()))
.collect::<Vec<_>>();
Ok(name_bytes)
}
}
const HEADER_SIZE_TEAM_FLAG: u8 = 0x40;
const HEADER_SIZE_NORMAL: u8 = 0x80;
impl SymbolArt for Payload {
type Layer = Layer;
fn author_id(&self) -> u32 {
self.header.author_id
}
fn height(&self) -> u8 {
match self.header.height {
HEADER_SIZE_NORMAL => 96,
HEADER_SIZE_TEAM_FLAG => 32,
_ => panic!("Invalid height having: {}", self.header.height),
}
}
fn width(&self) -> u8 {
match self.header.height {
HEADER_SIZE_NORMAL => 193,
HEADER_SIZE_TEAM_FLAG => 32,
_ => panic!("Invalid width having: {}", self.header.height),
}
}
fn layers(&self) -> Vec<Layer> {
self.layers.clone()
}
fn name(&self) -> String {
String::from_utf16_lossy(&self.name)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Header {
pub(super) author_id: u32,
pub(super) layers: u8,
pub(super) height: u8,
pub(super) width: u8,
pub(super) sound_effect: u8,
}
impl Header {
pub(super) fn parse(bytes: &[u8]) -> Result<Self> {
Ok(Header {
author_id: u32::from_be_bytes(bytes[0..4].try_into().unwrap()),
layers: bytes[4],
height: bytes[5],
width: bytes[6],
sound_effect: bytes[7],
})
}
pub(super) fn layers(&self) -> u8 {
self.layers
}
}
pub struct Layers {
layers: Vec<Layer>,
}
impl Layers {
pub(super) fn parse(bytes: &[u8]) -> Result<Self> {
let layers = bytes
.chunks_exact(std::mem::size_of::<Layer>())
.map(Layer::parse)
.collect::<Result<Vec<_>>>()?;
Ok(Self { layers })
}
}
impl From<Layers> for Vec<Layer> {
fn from(layers: Layers) -> Self {
layers.layers
}
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub struct Layer {
pub(super) top_left: Position,
pub(super) bottom_left: Position,
pub(super) top_right: Position,
pub(super) bottom_right: Position,
pub(super) is_hidden: bool,
pub(super) symbol_id: u16,
pub(super) alpha: u8,
pub(super) color_r: u8,
pub(super) color_g: u8,
pub(super) color_b: u8,
}
const LAYER_IS_HIDDEN: u32 = 0b10000000000000000000000000000000;
const MASK_SYMBOL_ID: u32 = 0b01111111111000000000000000000000;
const MASK_ALPHA: u32 = 0b00000000000111000000000000000000;
const MASK_COLOR_R: u32 = 0b00000000000000000000000000111111;
const MASK_COLOR_G: u32 = 0b00000000000000000000111111000000;
const MASK_COLOR_B: u32 = 0b00000000000000111111000000000000;
impl Layer {
fn parse(bytes: &[u8]) -> Result<Self> {
let top_left = Position::parse(&bytes[0..2])?;
let bottom_left = Position::parse(&bytes[2..4])?;
let top_right = Position::parse(&bytes[4..6])?;
let bottom_right = Position::parse(&bytes[6..8])?;
let layer_data = u32::from_le_bytes(bytes[8..12].try_into().unwrap());
Ok(Self {
top_left,
bottom_left,
top_right,
bottom_right,
is_hidden: Self::extract_is_hidden(layer_data),
symbol_id: Self::extract_symbol_id(layer_data),
alpha: Self::extract_alpha(layer_data),
color_r: Self::extract_color_r(layer_data),
color_g: Self::extract_color_g(layer_data),
color_b: Self::extract_color_b(layer_data),
})
}
fn extract_is_hidden(layer_data: u32) -> bool {
(layer_data & LAYER_IS_HIDDEN) != 0
}
fn extract_symbol_id(layer_data: u32) -> u16 {
((layer_data & MASK_SYMBOL_ID) >> 21) as u16
}
fn extract_alpha(layer_data: u32) -> u8 {
((layer_data & MASK_ALPHA) >> 18) as u8
}
fn extract_color_r(layer_data: u32) -> u8 {
(layer_data & MASK_COLOR_R) as u8
}
fn extract_color_g(layer_data: u32) -> u8 {
((layer_data & MASK_COLOR_G) >> 6) as u8
}
fn extract_color_b(layer_data: u32) -> u8 {
((layer_data & MASK_COLOR_B) >> 12) as u8
}
}
const ALPHA_FACTOR: u8 = 37;
const COLOR_FACTOR: u8 = 4;
impl SymbolArtLayer for Layer {
fn top_left(&self) -> Position {
self.top_left
}
fn bottom_left(&self) -> Position {
self.bottom_left
}
fn top_right(&self) -> Position {
self.top_right
}
fn bottom_right(&self) -> Position {
self.bottom_right
}
fn symbol(&self) -> symbol::Symbol {
symbol::Symbol::new(self.symbol_id.into())
}
fn color(&self) -> sa::Color {
let a = self.alpha.saturating_mul(ALPHA_FACTOR);
let r = self.color_r.saturating_mul(COLOR_FACTOR);
let g = self.color_g.saturating_mul(COLOR_FACTOR);
let b = self.color_b.saturating_mul(COLOR_FACTOR);
sa::Color::new(a, r, g, b)
}
fn is_hidden(&self) -> bool {
self.is_hidden
}
}
impl Position {
fn parse(bytes: &[u8]) -> Result<Self> {
Ok(Self {
x: bytes[0],
y: bytes[1],
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
core::sa::Position,
test::{RAW_FILE, RAW_FILE_UNCOMPRESSED},
};
#[test]
fn test_get_body() {
let bytes = Box::from(RAW_FILE);
let body = get_body(bytes).unwrap();
assert_eq!(body.len(), 1682);
}
#[test]
fn test_parse() {
let bytes = Box::from(RAW_FILE);
let body = get_body(bytes).unwrap();
let payload = Payload::parse(&body).unwrap();
let expected_name = &[12394, 12363, 12383, 12373, 12435]; let expected = Payload {
header: Header {
author_id: 881302016,
layers: 104,
height: 128,
width: 193,
sound_effect: 3,
},
layers: vec![
Layer {
top_left: Position { x: 0, y: 0 },
bottom_left: Position { x: 0, y: 0 },
top_right: Position { x: 0, y: 0 },
bottom_right: Position { x: 0, y: 0 },
is_hidden: false,
symbol_id: 0,
alpha: 0,
color_r: 0,
color_g: 0,
color_b: 0,
};
104
],
name: expected_name.to_vec(),
};
assert_eq!(payload.header, expected.header);
assert_eq!(payload.layers.len(), expected.layers.len());
assert_eq!(payload.name, expected.name);
}
#[test]
fn test_parse_uncompressed() {
let bytes = Box::from(RAW_FILE_UNCOMPRESSED);
let body = get_body(bytes).unwrap();
let payload = Payload::parse(&body).unwrap();
assert_eq!(
payload.name,
vec![84, 104, 97, 110, 107, 32, 121, 111, 117, 32, 33, 33]
);
}
}