binarygcode/components/
deserialiser.rs

1use core::{array::TryFromSliceError, fmt};
2
3use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
4use base64::{prelude::BASE64_STANDARD, Engine};
5use embedded_heatshrink::{
6    HSDFinishRes, HSDPollRes, HSDSinkRes, HeatshrinkDecoder,
7};
8use meatpack::Unpacker;
9use miniz_oxide::inflate::decompress_to_vec_zlib;
10
11use crate::components::common::{
12    crc32, BinaryGcodeError, BlockKind, Checksum, CompressionAlgorithm,
13    Encoding, MAGIC,
14};
15
16/// A utility enum to keep track of the state of the deserialiser
17/// instance when digesting some bytes.
18enum DeserialiserState {
19    FileHeader,
20    Block,
21}
22
23/// The possible outputs from a call to deserialise().
24#[derive(Debug)]
25pub enum DeserialisedResult {
26    FileHeader(DeserialisedFileHeader),
27    MoreBytesRequired(usize),
28    Block(DeserialisedBlock),
29}
30
31/// A struct containing the header details of the bgcode.
32#[derive(Debug)]
33pub struct DeserialisedFileHeader {
34    pub magic: u32,
35    pub version: u32,
36    pub checksum: Checksum,
37}
38
39/// A utility function to take a generic slice and return a slice of a specific size.
40pub(crate) fn try_from_slice<const N: usize>(
41    buf: &[u8]
42) -> Result<[u8; N], BinaryGcodeError> {
43    let bytes: Result<[u8; N], TryFromSliceError> = buf.try_into();
44    match bytes {
45        Ok(bytes) => Ok(bytes),
46        Err(_) => Err(BinaryGcodeError::TryFromSliceError),
47    }
48}
49
50/// A binarygcode deserialiser that can parse a bgcode file. It can
51/// digest data in chunks and returns header and blocks when available.
52/// The block remain compressed so the user can decide which ones they
53/// which to decompress.
54pub struct Deserialiser {
55    pub inner: Vec<u8>,
56    state: DeserialiserState,
57    checksum: Checksum,
58}
59
60impl Default for Deserialiser {
61    fn default() -> Self {
62        Self {
63            inner: Vec::new(),
64            state: DeserialiserState::FileHeader,
65            checksum: Checksum::None,
66        }
67    }
68}
69
70impl Deserialiser {
71    /// Provide some more bytes for the deserialiser to process/
72    pub fn digest(
73        &mut self,
74        buf: &[u8],
75    ) {
76        self.inner.extend(buf);
77    }
78
79    /// Reset the deserialisor to its default state.
80    pub fn reset(&mut self) {
81        self.inner.clear();
82        self.state = DeserialiserState::FileHeader;
83    }
84
85    /// Try and deserialised either a file header or block element from the
86    /// current digest.
87    pub fn deserialise(
88        &mut self
89    ) -> Result<DeserialisedResult, BinaryGcodeError> {
90        match self.state {
91            DeserialiserState::FileHeader => self.deserialise_file_header(),
92            DeserialiserState::Block => self.deserialise_block(),
93        }
94    }
95
96    /// An internal function to deserialise the file header.
97    fn deserialise_file_header(
98        &mut self
99    ) -> Result<DeserialisedResult, BinaryGcodeError> {
100        if self.inner.len() < 10 {
101            return Ok(DeserialisedResult::MoreBytesRequired(
102                10 - self.inner.len(),
103            ));
104        }
105        // We have enough data to read the file header
106        let bytes = try_from_slice::<4>(&self.inner[0..=3])?;
107        let magic = u32::from_le_bytes(bytes);
108        if magic != MAGIC {
109            return Err(BinaryGcodeError::InvalidMagic(magic));
110        }
111
112        let bytes = try_from_slice::<4>(&self.inner[4..=7])?;
113        let version = u32::from_le_bytes(bytes);
114
115        let bytes = try_from_slice::<2>(&self.inner[8..=9])?;
116        let checksum_value = u16::from_le_bytes(bytes);
117
118        let checksum = match checksum_value {
119            1 => Checksum::Crc32,
120            0 => Checksum::None,
121            v => return Err(BinaryGcodeError::InvalidChecksumType(v)),
122        };
123
124        let fh = DeserialisedFileHeader {
125            magic,
126            version,
127            checksum: checksum.clone(),
128        };
129
130        self.checksum = checksum;
131        self.state = DeserialiserState::Block;
132        self.inner.drain(..10);
133
134        Ok(DeserialisedResult::FileHeader(fh))
135    }
136
137    /// An internal function to deserialise a block from the digest.
138    fn deserialise_block(
139        &mut self
140    ) -> Result<DeserialisedResult, BinaryGcodeError> {
141        // Check if we have enough data to read the block header
142        if self.inner.len() < 12 {
143            return Ok(DeserialisedResult::MoreBytesRequired(
144                12 - self.inner.len(),
145            ));
146        }
147
148        // Check the header
149        let bytes = try_from_slice::<2>(&self.inner[0..=1])?;
150        let kind = BlockKind::from_le_bytes(bytes)?;
151
152        let bytes = try_from_slice::<2>(&self.inner[2..=3])?;
153        let compression = CompressionAlgorithm::from_le_bytes(bytes)?;
154
155        let bytes = try_from_slice::<4>(&self.inner[4..=7])?;
156        let data_uncompressed_len = u32::from_le_bytes(bytes) as usize;
157
158        let data_compressed_len: Option<usize> = match compression {
159            CompressionAlgorithm::None => None,
160            _ => {
161                let bytes = try_from_slice::<4>(&self.inner[8..=11])?;
162                Some(u32::from_le_bytes(bytes) as usize)
163            }
164        };
165
166        let param_len = match kind {
167            BlockKind::Thumbnail => 6,
168            _ => 2,
169        };
170
171        // Have we collected all the data we need?
172        let block_len = match data_compressed_len {
173            Some(compressed_len) => {
174                // header + parameters + comrpessed_len
175                let mut block_len = 12 + param_len + compressed_len;
176                if self.checksum == Checksum::Crc32 {
177                    block_len += 4;
178                }
179                if self.inner.len() < block_len {
180                    return Ok(DeserialisedResult::MoreBytesRequired(
181                        block_len - self.inner.len(),
182                    ));
183                }
184                block_len
185            }
186            None => {
187                let mut block_len = 8 + param_len + data_uncompressed_len;
188                if self.checksum == Checksum::Crc32 {
189                    block_len += 4;
190                }
191                if self.inner.len() < block_len {
192                    return Ok(DeserialisedResult::MoreBytesRequired(
193                        block_len - self.inner.len(),
194                    ));
195                }
196                block_len
197            }
198        };
199
200        // Checksum check
201        match self.checksum {
202            Checksum::None => {}
203            Checksum::Crc32 => {
204                let bytes =
205                    try_from_slice::<4>(&self.inner[block_len - 4..block_len])?;
206                let c = u32::from_le_bytes(bytes);
207                let chk = crc32(&self.inner[..block_len - 4]);
208                if c != chk {
209                    return Err(BinaryGcodeError::InvalidChecksum(c, chk));
210                }
211            }
212        }
213
214        let param_start = match data_compressed_len {
215            Some(_) => 12,
216            None => 8,
217        };
218
219        let encoding = &self.inner[param_start..param_start + 2];
220        let encoding = try_from_slice::<2>(encoding)?;
221        let encoding = Encoding::from_le_bytes(encoding, &kind)?;
222
223        let parameters = self.inner[param_start..param_start + param_len]
224            .to_owned()
225            .into_boxed_slice();
226        let data = self.inner[param_start + param_len..block_len - 4]
227            .to_owned()
228            .into_boxed_slice();
229
230        // Pass out the block
231        let b = DeserialisedBlock {
232            kind,
233            data_compressed_len,
234            data_uncompressed_len,
235            compression,
236            encoding,
237            parameters,
238            data,
239        };
240
241        self.inner.drain(..block_len);
242
243        Ok(DeserialisedResult::Block(b))
244    }
245}
246
247#[derive(Debug)]
248pub enum BlockError {
249    DecodeError(&'static str),
250}
251
252/// A struct representing a deserialised binary gcode block.
253#[derive(Debug)]
254pub struct DeserialisedBlock {
255    pub kind: BlockKind,
256    pub data_compressed_len: Option<usize>,
257    pub data_uncompressed_len: usize,
258    pub compression: CompressionAlgorithm,
259    pub encoding: Encoding,
260    pub parameters: Box<[u8]>,
261    pub data: Box<[u8]>,
262}
263
264impl fmt::Display for DeserialisedBlock {
265    fn fmt(
266        &self,
267        f: &mut fmt::Formatter<'_>,
268    ) -> fmt::Result {
269        write!(
270			f,
271			"{:?}  {{ compressed_len: {:?}, uncompressed_len: {}, compression: {:?}, encoding: {:?} }}",
272			self.kind, self.data_compressed_len, self.data_uncompressed_len, self.compression, self.encoding
273		)
274    }
275}
276
277impl DeserialisedBlock {
278    /// Internal function to decompress the data given the compression algorithm.
279    pub fn decompress(&self) -> Result<Box<[u8]>, BlockError> {
280        match self.compression {
281            CompressionAlgorithm::None => Ok(self.data.clone()),
282            CompressionAlgorithm::Deflate => {
283                let output = decompress_to_vec_zlib(&self.data);
284                if let Ok(o) = output {
285                    Ok(o.into_boxed_slice())
286                } else {
287                    Err(BlockError::DecodeError("deflate"))
288                }
289            }
290            CompressionAlgorithm::Heatshrink11_4 => {
291                unshrink(&self.data, self.data_uncompressed_len, 11, 4)
292            }
293            CompressionAlgorithm::Heatshrink12_4 => {
294                unshrink(&self.data, self.data_uncompressed_len, 12, 4)
295            }
296        }
297    }
298
299    /// Pumps the decompressed ascii representation of the gcode block into a buffer. The user can define whether they want to include our block comments so they can see the decomposition of blocks in the gcode.
300    pub fn to_ascii(
301        &mut self,
302        buf: &mut Vec<u8>,
303        with_block_comments: bool,
304    ) -> Result<(), BinaryGcodeError> {
305        let data = self.decompress().unwrap();
306        match self.kind {
307            BlockKind::FileMetadata => {
308                if with_block_comments {
309                    buf.extend("; [FILE_METADATA_BLOCK_START]\n".as_bytes());
310                }
311                buf.extend(data);
312                if with_block_comments {
313                    // Add a new line.
314                    buf.extend("\n; [FILE_METADATA_BLOCK_END]\n".as_bytes());
315                }
316            }
317            BlockKind::PrinterMetadata => {
318                if with_block_comments {
319                    buf.extend("; [PRINTER_METADATA_BLOCK_START]\n".as_bytes());
320                }
321                buf.extend(data);
322                if with_block_comments {
323                    buf.extend("\n; [PRINTER_METADATA_BLOCK_END]\n".as_bytes());
324                }
325            }
326            BlockKind::PrintMetadata => {
327                if with_block_comments {
328                    buf.extend("; [PRINT_METADATA_BLOCK_START]\n".as_bytes());
329                }
330                buf.extend(data);
331                if with_block_comments {
332                    buf.extend("\n; [PRINT_METADATA_BLOCK_END]\n".as_bytes());
333                }
334            }
335            BlockKind::SlicerMetadata => {
336                if with_block_comments {
337                    buf.extend("; [SLICER_METADATA_BLOCK_START]\n".as_bytes());
338                }
339                buf.extend(data);
340                if with_block_comments {
341                    buf.extend("\n; [SLICER_METADATA_BLOCK_END]\n".as_bytes());
342                }
343            }
344            BlockKind::Thumbnail => {
345                //buf.resize_with(buf.len() + data.len(), Default::default);
346                let width = try_from_slice::<2>(&self.parameters[2..=3])?;
347                let width = u16::from_le_bytes(width);
348                let height = try_from_slice::<2>(&self.parameters[4..=5])?;
349                let height = u16::from_le_bytes(height);
350
351                if with_block_comments {
352                    buf.extend("; [THUMBNAIL_BLOCK_START]\n".as_bytes());
353                }
354                let r = BASE64_STANDARD.encode(&data).into_bytes();
355                match self.encoding {
356                    Encoding::Png => {
357                        let header = format!(
358                            "; thumbnail begin {}x{} {}\n",
359                            width,
360                            height,
361                            r.len()
362                        );
363                        buf.extend(header.as_bytes());
364                    }
365                    Encoding::Jpg => {
366                        let header = format!(
367                            "; thumbnail_JPG begin {}x{} {}\n",
368                            width,
369                            height,
370                            r.len()
371                        );
372                        buf.extend(header.as_bytes());
373                    }
374                    Encoding::Qoi => {
375                        let header = format!(
376                            "; thumbnail_QOI begin {}x{} {}\n",
377                            width,
378                            height,
379                            r.len()
380                        );
381                        buf.extend(header.as_bytes());
382                    }
383                    _ => {}
384                }
385                // Taking the max row length of 78 from libbgcode
386                for chunk in r.chunks(78) {
387                    buf.extend("; ".as_bytes());
388                    buf.extend(chunk);
389                    buf.push(b'\n');
390                }
391                match self.encoding {
392                    Encoding::Png => {
393                        buf.extend("; thumbnail end \n\n".as_bytes())
394                    }
395                    Encoding::Jpg => {
396                        buf.extend("; thumbnail_JPG end \n\n".as_bytes())
397                    }
398                    Encoding::Qoi => {
399                        buf.extend("; thumbnail_QOI end \n\n".as_bytes())
400                    }
401                    _ => {}
402                }
403                if with_block_comments {
404                    buf.extend("; [THUMBNAIL_BLOCK_END]\n".as_bytes());
405                }
406            }
407            BlockKind::GCode => {
408                if with_block_comments {
409                    buf.extend("; [GCODE_BLOCK_START]\n".as_bytes());
410                }
411                match self.encoding {
412                    Encoding::Ascii => buf.extend(data),
413                    Encoding::Meatpack => {
414                        // Use the Meatpack crate to re-encode back to ASCII Gcode.
415                        if let Some(e) =
416                            Unpacker::<64>::unpack_slice(&data, buf).err()
417                        {
418                            return Err(BinaryGcodeError::Meatpack(e));
419                        }
420                    }
421                    Encoding::MeatpackWithComments => {
422                        if let Some(e) =
423                            Unpacker::<64>::unpack_slice(&data, buf).err()
424                        {
425                            return Err(BinaryGcodeError::Meatpack(e));
426                        }
427                    }
428                    _ => {}
429                }
430                if with_block_comments {
431                    buf.extend("; [GCODE_BLOCK_END]\n".as_bytes());
432                }
433            }
434        }
435        Ok(())
436    }
437}
438
439/// An internal function wrapping around the heatshrink decoder.
440fn unshrink(
441    input: &[u8],
442    uncompressed_len: usize,
443    window: u8,
444    lookahead: u8,
445) -> Result<Box<[u8]>, BlockError> {
446    let input_buffer_size = input.len();
447    let mut decoder =
448        HeatshrinkDecoder::new(input_buffer_size as u16, window, lookahead)
449            .unwrap();
450    let mut uncompressed: Vec<u8> = vec![0; uncompressed_len];
451    let mut sunk: usize = 0;
452    let mut polled: usize = 0;
453
454    while sunk < input_buffer_size {
455        match decoder.sink(&input[sunk..]) {
456            HSDSinkRes::Ok(sz) => {
457                sunk += sz;
458            }
459            HSDSinkRes::Full => {
460                return Err(BlockError::DecodeError("HSDSinkRes::Full"))
461            }
462            HSDSinkRes::ErrorNull => {
463                return Err(BlockError::DecodeError("HSDSinkRes::ErrorNull"))
464            }
465        }
466        loop {
467            let res = decoder.poll(&mut uncompressed[polled..]);
468            match res {
469                HSDPollRes::Empty(sz) => {
470                    polled += sz;
471                    if sz == 0 {
472                        break;
473                    }
474                }
475                // Panics after looping for more. Is there a bug where
476                // More is Empty and Empty is More or my interpretation
477                // of what they mean?
478                HSDPollRes::More(sz) => {
479                    polled += sz;
480                    break;
481                }
482                HSDPollRes::ErrorNull => {
483                    return Err(BlockError::DecodeError(
484                        "HSDPollRes::ErrorNull",
485                    ))
486                }
487                HSDPollRes::ErrorUnknown => {
488                    return Err(BlockError::DecodeError(
489                        "HSDPollRes::ErrorUnknown",
490                    ))
491                }
492            }
493        }
494    }
495
496    loop {
497        match decoder.finish() {
498            HSDFinishRes::Done => break,
499            HSDFinishRes::More => {
500                match decoder.poll(&mut uncompressed[polled..]) {
501                    HSDPollRes::Empty(sz) => {
502                        polled += sz;
503                        if sz == 0 {
504                            break;
505                        }
506                    }
507                    HSDPollRes::More(sz) => {
508                        polled += sz;
509                    }
510                    HSDPollRes::ErrorUnknown => {
511                        return Err(BlockError::DecodeError(
512                            "HSDPollRes::ErrorUnknown",
513                        ))
514                    }
515                    HSDPollRes::ErrorNull => {
516                        return Err(BlockError::DecodeError(
517                            "HSDPollRes::ErrorNull",
518                        ))
519                    }
520                }
521            }
522            HSDFinishRes::ErrorNull => {
523                return Err(BlockError::DecodeError("HSDFinishRes::ErrorNull"))
524            }
525        }
526    }
527
528    Ok(uncompressed.into_boxed_slice())
529}