gcode_nom/binary/thumbnail_block/
mod.rs

1use core::fmt::Display;
2use std::fmt::Write;
3
4use super::{
5    block_header::{block_header_parser, BlockHeader},
6    BlockError,
7};
8
9use nom::{
10    bytes::streaming::take,
11    combinator::verify,
12    error::Error,
13    number::streaming::{le_u16, le_u32},
14    sequence::preceded,
15    IResult, Parser,
16};
17
18mod param;
19use param::param_parser;
20use param::Param;
21
22use crate::binary::{CompressionType, Markdown};
23
24#[derive(Clone, Debug, PartialEq, Eq)]
25pub struct ThumbnailBlock<'a> {
26    header: BlockHeader,
27    pub param: Param,
28    pub data: &'a [u8],
29    checksum: Option<u32>,
30}
31impl Display for ThumbnailBlock<'_> {
32    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33        writeln!(
34            f,
35            "-------------------------- ThumbnailBlock --------------------------"
36        )?;
37        writeln!(f)?;
38        writeln!(f, "Params")?;
39        writeln!(f, "{}", self.param)?;
40        writeln!(f, "DataBlock omitted")?;
41        writeln!(f)?;
42        write!(f, "-------------------------- ThumbnailBlock ")?;
43        match self.checksum {
44            Some(checksum) => writeln!(f, "Checksum Ox{checksum:X} ---------")?,
45            None => writeln!(f, "No checksum")?,
46        }
47        Ok(())
48    }
49}
50
51impl Markdown for Vec<ThumbnailBlock<'_>> {
52    /// Write to formatter a markdown block.
53    fn markdown<W>(&self, f: &mut W) -> core::fmt::Result
54    where
55        W: Write,
56    {
57        writeln!(f)?;
58        writeln!(f, "## ThumbnailBlocks")?;
59        for (i, thumbnail) in self.iter().enumerate() {
60            // All titles (for a given level), must be unique
61            // otherwise, as per spec,  table of content block cannot be constructed.
62            writeln!(f)?;
63            writeln!(f, "### ThumbnailBlock {i}")?;
64            writeln!(f)?;
65            thumbnail.headless_markdown(f)?;
66        }
67        Ok(())
68    }
69}
70
71impl ThumbnailBlock<'_> {
72    /// Write to formatter a markdown block.
73    pub(super) fn headless_markdown<W>(&self, f: &mut W) -> core::fmt::Result
74    where
75        W: Write,
76    {
77        writeln!(f, "### Params")?;
78        writeln!(f)?;
79        writeln!(f, "{}", self.param)?;
80        writeln!(f, "DataBlock omitted")?;
81        writeln!(f)?;
82        match self.checksum {
83            Some(checksum) => writeln!(f, "Checksum Ox{checksum:X}")?,
84            None => writeln!(f, "No checksum")?,
85        }
86        Ok(())
87    }
88}
89
90static THUMBNAIL_BLOCK_ID: u16 = 5u16;
91pub fn thumbnail_parser(input: &[u8]) -> IResult<&[u8], ThumbnailBlock<'_>, BlockError> {
92    let (after_block_header, header) = preceded(
93        verify(le_u16, |block_type| {
94            log::debug!(
95                "Looking for THUMBNAIL_BLOCK_ID {THUMBNAIL_BLOCK_ID} found {block_type} cond {}",
96                *block_type == THUMBNAIL_BLOCK_ID
97            );
98            *block_type == THUMBNAIL_BLOCK_ID
99        }),
100        block_header_parser,
101    )
102    .parse(input)
103    .map_err(|e| e.map(|_e| BlockError::Thumbnail))?;
104
105    log::info!("Found thumbnail block id");
106
107    let (after_param, param) =
108        param_parser(after_block_header).map_err(|e| e.map(|_e| BlockError::Thumbnail))?;
109
110    // Decompress data block
111    let (after_data, data) = match header.compressed_size {
112        Some(size) => take(size)(after_param)?,
113        None => take(header.uncompressed_size)(after_param)?,
114    };
115
116    let (after_checksum, checksum) =
117        le_u32(after_data).map_err(|e| e.map(|_e: Error<_>| BlockError::Thumbnail))?;
118
119    Ok((
120        after_checksum,
121        ThumbnailBlock {
122            header,
123            param,
124            data,
125            checksum: Some(checksum),
126        },
127    ))
128}
129
130pub fn thumbnail_parser_with_checksum(
131    input: &[u8],
132) -> IResult<&[u8], ThumbnailBlock<'_>, BlockError> {
133    let (remain, thumbnail) = thumbnail_parser(input)?;
134
135    if let Some(checksum) = thumbnail.checksum {
136        let param_size = 6;
137        let payload_size = match thumbnail.header.compression_type {
138            CompressionType::None => thumbnail.header.uncompressed_size as usize,
139            _ => thumbnail.header.compressed_size.unwrap() as usize,
140        };
141        let block_size = thumbnail.header.size_in_bytes() + param_size + payload_size;
142        let crc_input = &input[..block_size];
143        let computed_checksum = crc32fast::hash(crc_input);
144
145        log::debug!(
146            "thumbnail checksum 0x{checksum:04x} computed checksum 0x{computed_checksum:04x} "
147        );
148        if checksum == computed_checksum {
149            log::debug!("checksum match");
150        } else {
151            log::error!("fail checksum");
152            return Err(nom::Err::Error(BlockError::Thumbnail));
153        }
154    }
155
156    Ok((remain, thumbnail))
157}