use nom::{
Compare, IResult, Input, Parser,
bytes::streaming::{tag, take},
combinator::cond,
number::streaming::{le_u16, le_u32},
};
use crate::parse::compact::binary::file::ChecksumType;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlockHeader {
pub compression: Compression,
pub uncompressed_size: u32,
pub compressed_size: Option<u32>,
}
impl BlockHeader {
pub fn parse<I>(ty: BlockType, input: I) -> IResult<I, BlockHeader>
where
I: Input<Item = u8> + for<'a> Compare<&'a [u8]>,
{
let bytes = (ty as u16).to_le_bytes();
let (input, ((_ty, compression), uncompressed_size)) = tag(bytes.as_slice())
.and(Compression::parser())
.and(le_u32)
.parse(input)?;
cond(compression != Compression::None, le_u32)
.map(move |compressed_size| Self {
compression,
uncompressed_size,
compressed_size,
})
.parse(input)
}
}
pub fn parse_block<I, P, T>(
ty: BlockType,
checksum_type: ChecksumType,
parameters_parser: P,
input: I,
) -> IResult<I, (BlockHeader, T, I, Option<u32>)>
where
I: Input<Item = u8> + for<'a> Compare<&'a [u8]>,
P: Parser<I, Output = T, Error = nom::error::Error<I>>,
{
let (input, block_header) = BlockHeader::parse(ty, input)?;
let size = block_header
.compressed_size
.unwrap_or(block_header.uncompressed_size);
parameters_parser
.and(take(size))
.and(cond(checksum_type == ChecksumType::CRC32, le_u32))
.map(move |((block_parameters, block_bytes), block_checksum)| {
(block_header, block_parameters, block_bytes, block_checksum)
})
.parse(input)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum BlockType {
FileMetadata = 0,
GCode = 1,
SlicerMetadata = 2,
PrinterMetadata = 3,
PrintMetadata = 4,
Thumbnail = 5,
}
impl BlockType {
pub fn parser<I>() -> impl Parser<I, Output = Self, Error = nom::error::Error<I>>
where
I: Input<Item = u8> + for<'a> Compare<&'a [u8]>,
{
le_u16.map_res(|value| match value {
0 => Ok(Self::FileMetadata),
1 => Ok(Self::GCode),
2 => Ok(Self::SlicerMetadata),
3 => Ok(Self::PrinterMetadata),
4 => Ok(Self::PrintMetadata),
5 => Ok(Self::Thumbnail),
_other => Err(()),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum Compression {
None = 0,
Deflate = 1,
Heatshrink11_4 = 2,
Heatshrink12_4 = 3,
}
impl Compression {
pub fn parser<I>() -> impl Parser<I, Output = Self, Error = nom::error::Error<I>>
where
I: Input<Item = u8> + for<'a> Compare<&'a [u8]>,
{
le_u16.map_res(|value| match value {
0 => Ok(Self::None),
1 => Ok(Self::Deflate),
2 => Ok(Self::Heatshrink11_4),
3 => Ok(Self::Heatshrink12_4),
_other => Err(()),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum EncodingType {
Ini = 0,
}
impl EncodingType {
pub fn parser<I>() -> impl Parser<I, Output = Self, Error = nom::error::Error<I>>
where
I: Input<Item = u8> + for<'a> Compare<&'a [u8]>,
{
le_u16.map_res(|value| match value {
0 => Ok(Self::Ini),
_other => Err(()),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ThumbnailParameters {
pub format: ThumbnailFormat,
pub width: u16,
pub height: u16,
}
impl ThumbnailParameters {
pub fn parser<I>() -> impl Parser<I, Output = Self, Error = nom::error::Error<I>>
where
I: Input<Item = u8> + for<'a> Compare<&'a [u8]>,
{
(ThumbnailFormat::parser(), le_u16, le_u16).map(|(format, width, height)| Self {
format,
width,
height,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum ThumbnailFormat {
Png = 0,
Jpg = 1,
Qoi = 2,
}
impl ThumbnailFormat {
fn parser<I>() -> impl Parser<I, Output = Self, Error = nom::error::Error<I>>
where
I: Input<Item = u8> + for<'a> Compare<&'a [u8]>,
{
le_u16.map_res(|value| match value {
0 => Ok(Self::Png),
1 => Ok(Self::Jpg),
2 => Ok(Self::Qoi),
_other => Err(()),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum GCodeEncodingType {
None = 0,
Meatpack = 1,
MeatpackWithComments = 2,
}
impl GCodeEncodingType {
pub fn parser<I>() -> impl Parser<I, Output = Self, Error = nom::error::Error<I>>
where
I: Input<Item = u8> + for<'a> Compare<&'a [u8]>,
{
le_u16.map_res(|value| match value {
0 => Ok(Self::None),
1 => Ok(Self::Meatpack),
2 => Ok(Self::MeatpackWithComments),
_other => Err(()),
})
}
}