use crate::error::Error;
use crate::types::{ResolvedDesign, StitchCommand};
const HEADER_SIZE: usize = 256;
pub fn parse(data: &[u8]) -> Result<(Vec<StitchCommand>, Vec<(u8, u8, u8)>), Error> {
if data.len() < HEADER_SIZE + 2 {
return Err(Error::TooShort {
expected: HEADER_SIZE + 2,
actual: data.len(),
});
}
let color_count = u16::from_le_bytes([data[0x27], data[0x28]]) as usize;
if color_count == 0 {
return Err(Error::InvalidHeader("zero color count".into()));
}
let mut commands = Vec::new();
let mut i = HEADER_SIZE;
let mut color_table_start = data.len();
while i < data.len() {
let b1 = data[i];
i += 1;
if b1 == 0x7D || b1 == 0x7E {
if i + 4 > data.len() {
break;
}
let x = i16::from_le_bytes([data[i], data[i + 1]]);
let y = -i16::from_le_bytes([data[i + 2], data[i + 3]]);
i += 4;
commands.push(StitchCommand::Jump { dx: x, dy: y });
continue;
}
if i >= data.len() {
break;
}
let b2 = data[i];
i += 1;
if b1 != 0x7F {
let dx = b1 as i8 as i16;
let dy = -(b2 as i8 as i16);
commands.push(StitchCommand::Stitch { dx, dy });
continue;
}
if i + 2 > data.len() {
break;
}
let b3 = data[i];
let b4 = data[i + 1];
i += 2;
if b2 == 0x01 {
let dx = b3 as i8 as i16;
let dy = -(b4 as i8 as i16);
commands.push(StitchCommand::Jump { dx, dy });
} else if b2 == 0x03 {
commands.push(StitchCommand::Trim);
let dx = b3 as i8 as i16;
let dy = -(b4 as i8 as i16);
if dx != 0 || dy != 0 {
commands.push(StitchCommand::Jump { dx, dy });
}
} else if b2 == 0x08 || (0x0A..=0x17).contains(&b2) {
commands.push(StitchCommand::ColorChange);
} else if b2 == 0x7F || b2 == 0x18 {
color_table_start = i + 2;
break;
}
}
if commands.is_empty() {
return Err(Error::NoStitchData);
}
commands.push(StitchCommand::End);
let colors = if color_table_start + color_count * 4 <= data.len() {
(0..color_count)
.map(|c| {
let base = color_table_start + c * 4;
let rgb = u32::from_be_bytes([
data[base],
data[base + 1],
data[base + 2],
data[base + 3],
]);
(
((rgb >> 16) & 0xFF) as u8,
((rgb >> 8) & 0xFF) as u8,
(rgb & 0xFF) as u8,
)
})
.collect()
} else {
crate::palette::default_colors(color_count)
};
Ok((commands, colors))
}
pub fn parse_and_resolve(data: &[u8]) -> Result<ResolvedDesign, Error> {
let (commands, colors) = parse(data)?;
crate::resolve::resolve(&commands, colors)
}