use crate::error::Error;
use crate::palette::default_colors;
use crate::types::{ResolvedDesign, StitchCommand};
pub fn parse(data: &[u8]) -> Result<Vec<StitchCommand>, Error> {
if data.len() < 3 {
return Err(Error::TooShort {
expected: 3,
actual: data.len(),
});
}
let offset = if data.len() > 512 && &data[0..3] == b"LA:" {
512
} else {
0
};
let stitch_data = &data[offset..];
if stitch_data.len() < 3 {
return Err(Error::NoStitchData);
}
let mut commands = Vec::new();
let mut i = 0;
while i + 2 < stitch_data.len() {
let b0 = stitch_data[i];
let b1 = stitch_data[i + 1];
let b2 = stitch_data[i + 2];
i += 3;
if b0 == 0x00 && b1 == 0x00 && b2 == 0xF3 {
commands.push(StitchCommand::End);
break;
}
let dx = decode_dx(b0, b1, b2);
let dy = decode_dy(b0, b1, b2);
let flags = b2 & 0xFC;
if flags & 0x80 != 0 {
commands.push(StitchCommand::ColorChange);
continue;
}
if flags & 0x40 != 0 {
commands.push(StitchCommand::Jump { dx, dy });
continue;
}
commands.push(StitchCommand::Stitch { dx, dy });
}
if commands.is_empty() || (commands.len() == 1 && matches!(commands[0], StitchCommand::End)) {
return Err(Error::NoStitchData);
}
if !matches!(commands.last(), Some(StitchCommand::End)) {
commands.push(StitchCommand::End);
}
Ok(commands)
}
fn decode_dx(b0: u8, b1: u8, b2: u8) -> i16 {
let mut x: i16 = 0;
if b0 & 0x01 != 0 {
x += 1;
}
if b0 & 0x02 != 0 {
x -= 1;
}
if b0 & 0x04 != 0 {
x += 9;
}
if b0 & 0x08 != 0 {
x -= 9;
}
if b1 & 0x01 != 0 {
x += 3;
}
if b1 & 0x02 != 0 {
x -= 3;
}
if b1 & 0x04 != 0 {
x += 27;
}
if b1 & 0x08 != 0 {
x -= 27;
}
if b2 & 0x04 != 0 {
x += 81;
}
if b2 & 0x08 != 0 {
x -= 81;
}
x
}
fn decode_dy(b0: u8, b1: u8, b2: u8) -> i16 {
let mut y: i16 = 0;
if b0 & 0x80 != 0 {
y += 1;
}
if b0 & 0x40 != 0 {
y -= 1;
}
if b0 & 0x20 != 0 {
y += 9;
}
if b0 & 0x10 != 0 {
y -= 9;
}
if b1 & 0x80 != 0 {
y += 3;
}
if b1 & 0x40 != 0 {
y -= 3;
}
if b1 & 0x20 != 0 {
y += 27;
}
if b1 & 0x10 != 0 {
y -= 27;
}
if b2 & 0x20 != 0 {
y += 81;
}
if b2 & 0x10 != 0 {
y -= 81;
}
-y
}
pub fn parse_and_resolve(data: &[u8]) -> Result<ResolvedDesign, Error> {
let commands = parse(data)?;
let color_count = commands
.iter()
.filter(|c| matches!(c, StitchCommand::ColorChange))
.count()
+ 1;
let colors = default_colors(color_count);
crate::resolve::resolve(&commands, colors)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_end_marker() {
let data = [0x00, 0x00, 0xF3];
let cmds = parse(&data).unwrap_err();
assert!(matches!(cmds, Error::NoStitchData));
}
#[test]
fn decode_simple_stitch() {
let data = [0x81, 0x00, 0x03, 0x00, 0x00, 0xF3];
let cmds = parse(&data).unwrap();
assert!(matches!(cmds[0], StitchCommand::Stitch { dx: 1, dy: -1 }));
assert!(matches!(cmds[1], StitchCommand::End));
}
#[test]
fn decode_jump() {
let data = [0x01, 0x00, 0x43, 0x00, 0x00, 0xF3];
let cmds = parse(&data).unwrap();
assert!(matches!(cmds[0], StitchCommand::Jump { dx: 1, dy: 0 }));
}
#[test]
fn decode_color_change() {
let data = [0x00, 0x00, 0x83, 0x01, 0x00, 0x03, 0x00, 0x00, 0xF3];
let cmds = parse(&data).unwrap();
assert!(matches!(cmds[0], StitchCommand::ColorChange));
}
#[test]
fn decode_dx_values() {
assert_eq!(decode_dx(0x01, 0x00, 0x00), 1);
assert_eq!(decode_dx(0x02, 0x00, 0x00), -1);
assert_eq!(decode_dx(0x04, 0x00, 0x00), 9);
assert_eq!(decode_dx(0x00, 0x04, 0x00), 27);
assert_eq!(decode_dx(0x00, 0x00, 0x04), 81);
assert_eq!(decode_dx(0x05, 0x05, 0x04), 1 + 9 + 3 + 27 + 81); }
#[test]
fn decode_dy_values() {
assert_eq!(decode_dy(0x80, 0x00, 0x00), -1);
assert_eq!(decode_dy(0x40, 0x00, 0x00), 1);
assert_eq!(decode_dy(0x20, 0x00, 0x00), -9);
assert_eq!(decode_dy(0x00, 0x20, 0x00), -27);
assert_eq!(decode_dy(0x00, 0x00, 0x20), -81);
}
}