1use crate::buffer::Buffer;
8use crate::crc::{crc16_xmodem, crc32_iso_hdlc};
9use crate::error::Error;
10use crate::io::{Read, Write};
11use crate::zdle;
12use crate::{XON, ZDLE, ZPAD};
13use bitflags::bitflags;
14
15const HEADER_SIZE: usize = 32;
17const HEADER_PAYLOAD_SIZE: usize = 5;
19
20pub(crate) const ZACK_HEADER: Header = Header::new(Encoding::ZHEX, Frame::ZACK, &[0; 4]);
21pub(crate) const ZDATA_HEADER: Header = Header::new(Encoding::ZBIN32, Frame::ZDATA, &[0; 4]);
22pub(crate) const ZEOF_HEADER: Header = Header::new(Encoding::ZBIN32, Frame::ZEOF, &[0; 4]);
23pub(crate) const ZFIN_HEADER: Header = Header::new(Encoding::ZHEX, Frame::ZFIN, &[0; 4]);
24pub(crate) const ZNAK_HEADER: Header = Header::new(Encoding::ZHEX, Frame::ZNAK, &[0; 4]);
25pub(crate) const ZRPOS_HEADER: Header = Header::new(Encoding::ZHEX, Frame::ZRPOS, &[0; 4]);
26pub(crate) const ZRQINIT_HEADER: Header = Header::new(Encoding::ZHEX, Frame::ZRQINIT, &[0; 4]);
27
28#[repr(C)]
31#[derive(PartialEq)]
32pub struct Header {
33 encoding: Encoding,
34 frame: Frame,
35 flags: [u8; 4],
36}
37
38impl Header {
39 #[must_use]
41 pub const fn new(encoding: Encoding, frame: Frame, flags: &[u8; 4]) -> Self {
42 Self {
43 encoding,
44 frame,
45 flags: *flags,
46 }
47 }
48
49 #[must_use]
51 pub const fn encoding(&self) -> Encoding {
52 self.encoding
53 }
54
55 #[must_use]
57 pub const fn frame(&self) -> Frame {
58 self.frame
59 }
60
61 #[must_use]
63 pub const fn count(&self) -> u32 {
64 u32::from_le_bytes(self.flags)
65 }
66
67 pub fn write<P>(&self, port: &mut P) -> Result<Option<()>, Error>
75 where
76 P: Write + ?Sized,
77 {
78 if write_header_start(port, self.encoding)?.is_none() {
79 return Ok(None);
80 }
81
82 let mut out: Buffer<HEADER_SIZE> = Buffer::new();
83 out.push(self.frame as u8).map_err(|_| Error::OutOfMemory)?;
84 out.extend_from_slice(&self.flags)
85 .map_err(|_| Error::OutOfMemory)?;
86
87 let mut crc = [0u8; 4];
88 let crc_len = make_crc(&out, &mut crc, self.encoding);
89 out.extend_from_slice(&crc[..crc_len])
90 .map_err(|_| Error::OutOfMemory)?;
91
92 if self.encoding == Encoding::ZHEX {
93 let mut hex_buf = [0u8; HEADER_SIZE];
94 let len = out.len() * 2;
95 let hex = &mut hex_buf.get_mut(..len).ok_or(Error::UnexpectedEof)?;
96 hex::encode_to_slice(&out, hex).map_err(|_| Error::OutOfMemory)?;
97 if write_slice_escaped(port, hex)?.is_none() {
98 return Ok(None);
99 }
100
101 if write_header_end_hex(port, self.frame)?.is_none() {
102 return Ok(None);
103 }
104 } else if write_slice_escaped(port, &out)?.is_none() {
105 return Ok(None);
106 }
107
108 Ok(Some(()))
109 }
110
111 pub fn read<P>(port: &mut P) -> Result<Option<Header>, Error>
120 where
121 P: Read + ?Sized,
122 {
123 let Some(encoding_byte) = port.read_byte()? else {
124 return Ok(None);
125 };
126 let encoding = Encoding::try_from(encoding_byte)?;
127
128 let mut out_hex: Buffer<HEADER_SIZE> = Buffer::new();
129 for _ in 0..Header::read_size(encoding) {
130 let Some(byte) = read_byte_unescaped(port)? else {
131 return Ok(None);
132 };
133 out_hex.push(byte).map_err(|_| Error::OutOfMemory)?;
134 }
135
136 let mut out: Buffer<HEADER_SIZE> = Buffer::new();
137 if encoding == Encoding::ZHEX {
138 let mut out_bytes = [0u8; HEADER_SIZE / 2];
139 let out_len = out_hex.len() / 2;
140 hex::decode_to_slice(&out_hex, &mut out_bytes[..out_len])
141 .map_err(|_| Error::MalformedHeader)?;
142 out.extend_from_slice(&out_bytes[..out_len])
143 .map_err(|_| Error::OutOfMemory)?;
144 } else {
145 out.extend_from_slice(&out_hex)
146 .map_err(|_| Error::OutOfMemory)?;
147 }
148 check_crc(&out[..5], &out[5..], encoding)?;
149 let frame = Frame::try_from(out[0])?;
150 let mut header = Header::new(encoding, frame, &[0; 4]);
151 header.flags.copy_from_slice(&out[1..=4]);
152 Ok(Some(header))
153 }
154
155 #[must_use]
158 pub const fn with_count(&self, count: u32) -> Self {
159 Header::new(self.encoding, self.frame, &count.to_le_bytes())
160 }
161
162 const fn read_size(encoding: Encoding) -> usize {
164 match encoding {
165 Encoding::ZBIN => HEADER_PAYLOAD_SIZE + 2,
166 Encoding::ZBIN32 => HEADER_PAYLOAD_SIZE + 4,
167 Encoding::ZHEX => (HEADER_PAYLOAD_SIZE + 2) * 2,
168 }
169 }
170}
171
172#[repr(u8)]
174#[allow(clippy::upper_case_acronyms)]
175#[derive(Clone, Copy, Debug, PartialEq)]
176pub enum Encoding {
177 ZBIN = 0x41,
178 ZHEX = 0x42,
179 ZBIN32 = 0x43,
180}
181
182impl TryFrom<u8> for Encoding {
183 type Error = Error;
184
185 fn try_from(value: u8) -> Result<Self, Self::Error> {
186 match value {
187 0x41 => Ok(Encoding::ZBIN),
188 0x42 => Ok(Encoding::ZHEX),
189 0x43 => Ok(Encoding::ZBIN32),
190 _ => Err(Error::MalformedEncoding(value)),
191 }
192 }
193}
194
195#[repr(u8)]
196#[allow(clippy::upper_case_acronyms)]
197#[derive(Clone, Copy, Debug, PartialEq)]
198pub enum Frame {
200 ZRQINIT = 0,
202 ZRINIT = 1,
204 ZSINIT = 2,
206 ZACK = 3,
208 ZFILE = 4,
210 ZSKIP = 5,
212 ZNAK = 6,
214 ZABORT = 7,
216 ZFIN = 8,
218 ZRPOS = 9,
220 ZDATA = 10,
222 ZEOF = 11,
224 ZFERR = 12,
226 ZCRC = 13,
228 ZCHALLENGE = 14,
230 ZCOMPL = 15,
232 ZCAN = 16,
234 ZFREECNT = 17,
236 ZCOMMAND = 18,
238 ZSTDERR = 19,
240}
241
242impl TryFrom<u8> for Frame {
243 type Error = Error;
244
245 fn try_from(value: u8) -> Result<Self, Self::Error> {
246 match value {
247 0 => Ok(Frame::ZRQINIT),
248 1 => Ok(Frame::ZRINIT),
249 2 => Ok(Frame::ZSINIT),
250 3 => Ok(Frame::ZACK),
251 4 => Ok(Frame::ZFILE),
252 5 => Ok(Frame::ZSKIP),
253 6 => Ok(Frame::ZNAK),
254 7 => Ok(Frame::ZABORT),
255 8 => Ok(Frame::ZFIN),
256 9 => Ok(Frame::ZRPOS),
257 10 => Ok(Frame::ZDATA),
258 11 => Ok(Frame::ZEOF),
259 12 => Ok(Frame::ZFERR),
260 13 => Ok(Frame::ZCRC),
261 14 => Ok(Frame::ZCHALLENGE),
262 15 => Ok(Frame::ZCOMPL),
263 16 => Ok(Frame::ZCAN),
264 17 => Ok(Frame::ZFREECNT),
265 18 => Ok(Frame::ZCOMMAND),
266 19 => Ok(Frame::ZSTDERR),
267 _ => Err(Error::MalformedFrame(value)),
268 }
269 }
270}
271
272bitflags! {
273 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
275 pub struct Zrinit: u8 {
276 const CANFDX = 0x01;
278 const CANOVIO = 0x02;
280 const CANBRK = 0x04;
282 const CANCRY = 0x08;
284 const CANLZW = 0x10;
286 const CANFC32 = 0x20;
288 const ESCCTL = 0x40;
290 const ESC8 = 0x80;
292 }
293}
294
295fn write_header_start<P>(port: &mut P, encoding: Encoding) -> Result<Option<()>, Error>
296where
297 P: Write + ?Sized,
298{
299 if port.write_byte(ZPAD)?.is_none() {
300 return Ok(None);
301 }
302 if encoding == Encoding::ZHEX && port.write_byte(ZPAD)?.is_none() {
303 return Ok(None);
304 }
305 if port.write_byte(ZDLE)?.is_none() {
306 return Ok(None);
307 }
308 port.write_byte(encoding as u8)
309}
310
311fn write_header_end_hex<P>(port: &mut P, frame: Frame) -> Result<Option<()>, Error>
312where
313 P: Write + ?Sized,
314{
315 if port.write_byte(b'\r')?.is_none() {
316 return Ok(None);
317 }
318 if port.write_byte(b'\n')?.is_none() {
319 return Ok(None);
320 }
321 if frame != Frame::ZACK && frame != Frame::ZFIN && port.write_byte(XON)?.is_none() {
322 return Ok(None);
323 }
324 Ok(Some(()))
325}
326
327fn make_crc(data: &[u8], out: &mut [u8], encoding: Encoding) -> usize {
328 if encoding == Encoding::ZBIN32 {
329 let crc = crc32_iso_hdlc(data).to_le_bytes();
330 out[..4].copy_from_slice(&crc[..4]);
331 4
332 } else {
333 let crc = crc16_xmodem(data).to_be_bytes();
334 out[..2].copy_from_slice(&crc[..2]);
335 2
336 }
337}
338
339fn check_crc(data: &[u8], crc: &[u8], encoding: Encoding) -> Result<(), Error> {
340 let mut crc2 = [0u8; 4];
341 let crc2_len = make_crc(data, &mut crc2, encoding);
342 if *crc == crc2[..crc2_len] {
343 Ok(())
344 } else if encoding == Encoding::ZBIN32 {
345 Err(Error::UnexpectedCrc32)
346 } else {
347 Err(Error::UnexpectedCrc16)
348 }
349}
350
351pub(crate) fn write_slice_escaped<P>(port: &mut P, buf: &[u8]) -> Result<Option<()>, Error>
352where
353 P: Write + ?Sized,
354{
355 for value in buf {
356 if write_byte_escaped(port, *value)?.is_none() {
357 return Ok(None);
358 }
359 }
360
361 Ok(Some(()))
362}
363
364pub(crate) fn write_byte_escaped<P>(port: &mut P, value: u8) -> Result<Option<()>, Error>
365where
366 P: Write + ?Sized,
367{
368 let escaped = zdle::ZDLE_TABLE[value as usize];
369 if escaped != value && port.write_byte(ZDLE)?.is_none() {
370 return Ok(None);
371 }
372 port.write_byte(escaped)
373}
374
375pub(crate) fn read_byte_unescaped<P>(port: &mut P) -> Result<Option<u8>, Error>
376where
377 P: Read + ?Sized,
378{
379 let Some(b) = port.read_byte()? else {
380 return Ok(None);
381 };
382 Ok(Some(if b == ZDLE {
383 let Some(b) = port.read_byte()? else {
384 return Ok(None);
385 };
386 zdle::UNZDLE_TABLE[b as usize]
387 } else {
388 b
389 }))
390}