1use crate::error::{Error, Result};
18use crate::tag::{self, ApduTag};
19use crate::traits::ApduDef;
20use alloc::vec::Vec;
21use dvb_common::{Parse, Serialize};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize))]
26#[non_exhaustive]
27pub enum DisplayControlCmd {
28 SetMmiMode,
30 GetDisplayCharacterTableList,
32 GetInputCharacterTableList,
34 GetOverlayGraphicsCharacteristics,
36 GetFullScreenGraphicsCharacteristics,
38 Reserved(u8),
40}
41
42impl DisplayControlCmd {
43 #[must_use]
45 pub fn from_u8(v: u8) -> Self {
46 match v {
47 0x01 => Self::SetMmiMode,
48 0x02 => Self::GetDisplayCharacterTableList,
49 0x03 => Self::GetInputCharacterTableList,
50 0x04 => Self::GetOverlayGraphicsCharacteristics,
51 0x05 => Self::GetFullScreenGraphicsCharacteristics,
52 other => Self::Reserved(other),
53 }
54 }
55 #[must_use]
57 pub fn to_u8(self) -> u8 {
58 match self {
59 Self::SetMmiMode => 0x01,
60 Self::GetDisplayCharacterTableList => 0x02,
61 Self::GetInputCharacterTableList => 0x03,
62 Self::GetOverlayGraphicsCharacteristics => 0x04,
63 Self::GetFullScreenGraphicsCharacteristics => 0x05,
64 Self::Reserved(v) => v,
65 }
66 }
67 #[must_use]
69 pub fn name(&self) -> &'static str {
70 match self {
71 Self::SetMmiMode => "set_mmi_mode",
72 Self::GetDisplayCharacterTableList => "get_display_character_table_list",
73 Self::GetInputCharacterTableList => "get_input_character_table_list",
74 Self::GetOverlayGraphicsCharacteristics => "get_overlay_graphics_characteristics",
75 Self::GetFullScreenGraphicsCharacteristics => {
76 "get_full-screen_graphics_characteristics"
77 }
78 Self::Reserved(_) => "reserved",
79 }
80 }
81}
82dvb_common::impl_spec_display!(DisplayControlCmd, Reserved);
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86#[cfg_attr(feature = "serde", derive(serde::Serialize))]
87#[non_exhaustive]
88pub enum MmiMode {
89 HighLevel,
91 LowLevelOverlayGraphics,
93 LowLevelFullScreenGraphics,
95 Reserved(u8),
97}
98
99impl MmiMode {
100 #[must_use]
102 pub fn from_u8(v: u8) -> Self {
103 match v {
104 0x01 => Self::HighLevel,
105 0x02 => Self::LowLevelOverlayGraphics,
106 0x03 => Self::LowLevelFullScreenGraphics,
107 other => Self::Reserved(other),
108 }
109 }
110 #[must_use]
112 pub fn to_u8(self) -> u8 {
113 match self {
114 Self::HighLevel => 0x01,
115 Self::LowLevelOverlayGraphics => 0x02,
116 Self::LowLevelFullScreenGraphics => 0x03,
117 Self::Reserved(v) => v,
118 }
119 }
120 #[must_use]
122 pub fn name(&self) -> &'static str {
123 match self {
124 Self::HighLevel => "high_level",
125 Self::LowLevelOverlayGraphics => "low_level_overlay_graphics",
126 Self::LowLevelFullScreenGraphics => "low_level_full_screen_graphics",
127 Self::Reserved(_) => "reserved",
128 }
129 }
130}
131dvb_common::impl_spec_display!(MmiMode, Reserved);
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize))]
136pub struct DisplayControl {
137 pub cmd: DisplayControlCmd,
139 pub mmi_mode: Option<MmiMode>,
141}
142
143impl<'a> Parse<'a> for DisplayControl {
144 type Error = Error;
145 fn parse(bytes: &'a [u8]) -> Result<Self> {
146 let body = super::parse_apdu_header(bytes, tag::DISPLAY_CONTROL, "display_control")?;
147 let cmd_byte = *body.first().ok_or(Error::BufferTooShort {
148 need: 1,
149 have: 0,
150 what: "display_control cmd",
151 })?;
152 let cmd = DisplayControlCmd::from_u8(cmd_byte);
153 let mmi_mode = if cmd == DisplayControlCmd::SetMmiMode {
154 if body.len() < 2 {
155 return Err(Error::BufferTooShort {
156 need: 2,
157 have: body.len(),
158 what: "display_control mmi_mode",
159 });
160 }
161 Some(MmiMode::from_u8(body[1]))
162 } else {
163 None
164 };
165 Ok(Self { cmd, mmi_mode })
166 }
167}
168
169impl Serialize for DisplayControl {
170 type Error = Error;
171 fn serialized_len(&self) -> usize {
172 super::apdu_len(1 + usize::from(self.mmi_mode.is_some()))
173 }
174 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
175 let body_len = 1 + usize::from(self.mmi_mode.is_some());
176 let mut pos = super::write_apdu_header(tag::DISPLAY_CONTROL, body_len, buf)?;
177 buf[pos] = self.cmd.to_u8();
178 pos += 1;
179 if let Some(m) = self.mmi_mode {
180 buf[pos] = m.to_u8();
181 pos += 1;
182 }
183 Ok(pos)
184 }
185}
186
187impl<'a> ApduDef<'a> for DisplayControl {
188 const TAG: ApduTag = tag::DISPLAY_CONTROL;
189 const NAME: &'static str = "DISPLAY_CONTROL";
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194#[cfg_attr(feature = "serde", derive(serde::Serialize))]
195#[non_exhaustive]
196pub enum DisplayReplyId {
197 MmiModeAck,
199 ListDisplayCharacterTables,
201 ListInputCharacterTables,
203 ListGraphicOverlayCharacteristics,
205 ListFullScreenGraphicCharacteristics,
207 UnknownDisplayControlCmd,
209 UnknownMmiMode,
211 UnknownCharacterTable,
213 Reserved(u8),
215}
216
217impl DisplayReplyId {
218 #[must_use]
220 pub fn from_u8(v: u8) -> Self {
221 match v {
222 0x01 => Self::MmiModeAck,
223 0x02 => Self::ListDisplayCharacterTables,
224 0x03 => Self::ListInputCharacterTables,
225 0x04 => Self::ListGraphicOverlayCharacteristics,
226 0x05 => Self::ListFullScreenGraphicCharacteristics,
227 0xF0 => Self::UnknownDisplayControlCmd,
228 0xF1 => Self::UnknownMmiMode,
229 0xF2 => Self::UnknownCharacterTable,
230 other => Self::Reserved(other),
231 }
232 }
233 #[must_use]
235 pub fn to_u8(self) -> u8 {
236 match self {
237 Self::MmiModeAck => 0x01,
238 Self::ListDisplayCharacterTables => 0x02,
239 Self::ListInputCharacterTables => 0x03,
240 Self::ListGraphicOverlayCharacteristics => 0x04,
241 Self::ListFullScreenGraphicCharacteristics => 0x05,
242 Self::UnknownDisplayControlCmd => 0xF0,
243 Self::UnknownMmiMode => 0xF1,
244 Self::UnknownCharacterTable => 0xF2,
245 Self::Reserved(v) => v,
246 }
247 }
248 #[must_use]
250 pub fn name(&self) -> &'static str {
251 match self {
252 Self::MmiModeAck => "mmi_mode_ack",
253 Self::ListDisplayCharacterTables => "list_display_character_tables",
254 Self::ListInputCharacterTables => "list_input_character_tables",
255 Self::ListGraphicOverlayCharacteristics => "list_graphic_overlay_characteristics",
256 Self::ListFullScreenGraphicCharacteristics => {
257 "list_full_screen_graphic_characteristics"
258 }
259 Self::UnknownDisplayControlCmd => "unknown display_control_cmd",
260 Self::UnknownMmiMode => "unknown_mmi_mode",
261 Self::UnknownCharacterTable => "unknown_character_table",
262 Self::Reserved(_) => "reserved",
263 }
264 }
265}
266dvb_common::impl_spec_display!(DisplayReplyId, Reserved);
267
268#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270#[cfg_attr(feature = "serde", derive(serde::Serialize))]
271pub struct PixelDepth {
272 pub display_depth: u8,
274 pub pixels_per_byte: u8,
276 pub region_overhead: u8,
278}
279
280#[derive(Debug, Clone, PartialEq, Eq)]
283#[cfg_attr(feature = "serde", derive(serde::Serialize))]
284pub struct GraphicsCharacteristics {
285 pub display_horizontal_size: u16,
287 pub display_vertical_size: u16,
289 pub aspect_ratio_information: u8,
291 pub graphics_relation_to_video: u8,
293 pub multiple_depths: bool,
295 pub display_bytes: u16,
297 pub composition_buffer_bytes: u8,
299 pub object_cache_bytes: u8,
301 pub depths: Vec<PixelDepth>,
303}
304
305const GFX_FIXED: usize = 9;
307
308#[derive(Debug, Clone, PartialEq, Eq)]
310#[cfg_attr(feature = "serde", derive(serde::Serialize))]
311#[non_exhaustive]
312pub enum DisplayReplyBody {
313 Graphics(GraphicsCharacteristics),
315 CharacterTables(Vec<u8>),
318 MmiModeAck(MmiMode),
320 None,
322}
323
324#[derive(Debug, Clone, PartialEq, Eq)]
326#[cfg_attr(feature = "serde", derive(serde::Serialize))]
327pub struct DisplayReply {
328 pub reply_id: DisplayReplyId,
330 pub body: DisplayReplyBody,
332}
333
334impl DisplayReply {
335 fn parse_graphics(b: &[u8]) -> Result<GraphicsCharacteristics> {
336 if b.len() < GFX_FIXED {
337 return Err(Error::BufferTooShort {
338 need: GFX_FIXED,
339 have: b.len(),
340 what: "display_reply graphics",
341 });
342 }
343 let display_horizontal_size = u16::from_be_bytes([b[0], b[1]]);
344 let display_vertical_size = u16::from_be_bytes([b[2], b[3]]);
345 let aspect_ratio_information = (b[4] >> 4) & 0x0F;
346 let graphics_relation_to_video = (b[4] >> 1) & 0x07;
347 let multiple_depths = (b[4] & 0x01) != 0;
348 let display_bytes = (((b[5] as u16) << 4) | ((b[6] >> 4) as u16)) & 0x0FFF;
351 let composition_buffer_bytes = ((b[6] & 0x0F) << 4) | (b[7] >> 4);
352 let object_cache_bytes = ((b[7] & 0x0F) << 4) | (b[8] >> 4);
353 let number_pixel_depths = (b[8] & 0x0F) as usize;
354 let mut depths = Vec::with_capacity(number_pixel_depths);
355 let mut pos = GFX_FIXED;
356 for _ in 0..number_pixel_depths {
357 if pos + 2 > b.len() {
358 return Err(Error::BufferTooShort {
359 need: pos + 2,
360 have: b.len(),
361 what: "display_reply pixel_depth",
362 });
363 }
364 depths.push(PixelDepth {
365 display_depth: (b[pos] >> 5) & 0x07,
366 pixels_per_byte: (b[pos] >> 2) & 0x07,
367 region_overhead: b[pos + 1],
368 });
369 pos += 2;
370 }
371 Ok(GraphicsCharacteristics {
372 display_horizontal_size,
373 display_vertical_size,
374 aspect_ratio_information,
375 graphics_relation_to_video,
376 multiple_depths,
377 display_bytes,
378 composition_buffer_bytes,
379 object_cache_bytes,
380 depths,
381 })
382 }
383
384 fn write_graphics(g: &GraphicsCharacteristics, buf: &mut [u8]) -> usize {
385 buf[0..2].copy_from_slice(&g.display_horizontal_size.to_be_bytes());
386 buf[2..4].copy_from_slice(&g.display_vertical_size.to_be_bytes());
387 buf[4] = ((g.aspect_ratio_information & 0x0F) << 4)
388 | ((g.graphics_relation_to_video & 0x07) << 1)
389 | u8::from(g.multiple_depths);
390 let n = g.depths.len() as u8 & 0x0F;
391 buf[5] = (g.display_bytes >> 4) as u8;
392 buf[6] = (((g.display_bytes & 0x0F) as u8) << 4) | (g.composition_buffer_bytes >> 4);
393 buf[7] = ((g.composition_buffer_bytes & 0x0F) << 4) | (g.object_cache_bytes >> 4);
394 buf[8] = ((g.object_cache_bytes & 0x0F) << 4) | n;
395 let mut pos = GFX_FIXED;
396 for d in &g.depths {
397 buf[pos] = ((d.display_depth & 0x07) << 5) | ((d.pixels_per_byte & 0x07) << 2);
398 buf[pos + 1] = d.region_overhead;
399 pos += 2;
400 }
401 pos
402 }
403
404 fn body_len(&self) -> usize {
405 1 + match &self.body {
406 DisplayReplyBody::Graphics(g) => GFX_FIXED + g.depths.len() * 2,
407 DisplayReplyBody::CharacterTables(t) => t.len(),
408 DisplayReplyBody::MmiModeAck(_) => 1,
409 DisplayReplyBody::None => 0,
410 }
411 }
412}
413
414impl<'a> Parse<'a> for DisplayReply {
415 type Error = Error;
416 fn parse(bytes: &'a [u8]) -> Result<Self> {
417 let body = super::parse_apdu_header(bytes, tag::DISPLAY_REPLY, "display_reply")?;
418 let id_byte = *body.first().ok_or(Error::BufferTooShort {
419 need: 1,
420 have: 0,
421 what: "display_reply id",
422 })?;
423 let reply_id = DisplayReplyId::from_u8(id_byte);
424 let rest = &body[1..];
425 let body = match reply_id {
426 DisplayReplyId::ListGraphicOverlayCharacteristics
427 | DisplayReplyId::ListFullScreenGraphicCharacteristics => {
428 DisplayReplyBody::Graphics(Self::parse_graphics(rest)?)
429 }
430 DisplayReplyId::ListDisplayCharacterTables
431 | DisplayReplyId::ListInputCharacterTables => {
432 DisplayReplyBody::CharacterTables(rest.to_vec())
433 }
434 DisplayReplyId::MmiModeAck => {
435 let m = *rest.first().ok_or(Error::BufferTooShort {
436 need: 1,
437 have: 0,
438 what: "display_reply mmi_mode_ack",
439 })?;
440 DisplayReplyBody::MmiModeAck(MmiMode::from_u8(m))
441 }
442 _ => DisplayReplyBody::None,
443 };
444 Ok(Self { reply_id, body })
445 }
446}
447
448impl Serialize for DisplayReply {
449 type Error = Error;
450 fn serialized_len(&self) -> usize {
451 super::apdu_len(self.body_len())
452 }
453 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
454 let body_len = self.body_len();
455 let mut pos = super::write_apdu_header(tag::DISPLAY_REPLY, body_len, buf)?;
456 buf[pos] = self.reply_id.to_u8();
457 pos += 1;
458 match &self.body {
459 DisplayReplyBody::Graphics(g) => {
460 pos += Self::write_graphics(g, &mut buf[pos..]);
461 }
462 DisplayReplyBody::CharacterTables(t) => {
463 buf[pos..pos + t.len()].copy_from_slice(t);
464 pos += t.len();
465 }
466 DisplayReplyBody::MmiModeAck(m) => {
467 buf[pos] = m.to_u8();
468 pos += 1;
469 }
470 DisplayReplyBody::None => {}
471 }
472 Ok(pos)
473 }
474}
475
476impl<'a> ApduDef<'a> for DisplayReply {
477 const TAG: ApduTag = tag::DISPLAY_REPLY;
478 const NAME: &'static str = "DISPLAY_REPLY";
479}
480
481#[derive(Debug, Clone, Copy, PartialEq, Eq)]
483#[cfg_attr(feature = "serde", derive(serde::Serialize))]
484#[non_exhaustive]
485pub enum KeypadControlCmd {
486 InterceptAllKeypresses,
488 IgnoreAllKeypresses,
490 InterceptSelectedKeypress,
492 IgnoreSelectedKeypress,
494 RejectKeypress,
496 Reserved(u8),
498}
499
500impl KeypadControlCmd {
501 #[must_use]
503 pub fn from_u8(v: u8) -> Self {
504 match v {
505 0x01 => Self::InterceptAllKeypresses,
506 0x02 => Self::IgnoreAllKeypresses,
507 0x03 => Self::InterceptSelectedKeypress,
508 0x04 => Self::IgnoreSelectedKeypress,
509 0x05 => Self::RejectKeypress,
510 other => Self::Reserved(other),
511 }
512 }
513 #[must_use]
515 pub fn to_u8(self) -> u8 {
516 match self {
517 Self::InterceptAllKeypresses => 0x01,
518 Self::IgnoreAllKeypresses => 0x02,
519 Self::InterceptSelectedKeypress => 0x03,
520 Self::IgnoreSelectedKeypress => 0x04,
521 Self::RejectKeypress => 0x05,
522 Self::Reserved(v) => v,
523 }
524 }
525 #[must_use]
527 pub fn name(&self) -> &'static str {
528 match self {
529 Self::InterceptAllKeypresses => "intercept_all_keypresses",
530 Self::IgnoreAllKeypresses => "ignore_all_keypresses",
531 Self::InterceptSelectedKeypress => "intercept_selected_keypress",
532 Self::IgnoreSelectedKeypress => "ignore_selected_keypress",
533 Self::RejectKeypress => "reject_keypress",
534 Self::Reserved(_) => "reserved",
535 }
536 }
537 fn carries_key_codes(self) -> bool {
539 matches!(
540 self,
541 Self::InterceptSelectedKeypress | Self::IgnoreSelectedKeypress | Self::RejectKeypress
542 )
543 }
544}
545dvb_common::impl_spec_display!(KeypadControlCmd, Reserved);
546
547#[derive(Debug, Clone, PartialEq, Eq)]
549#[cfg_attr(feature = "serde", derive(serde::Serialize))]
550pub struct KeypadControl {
551 pub cmd: KeypadControlCmd,
553 pub key_codes: Vec<u8>,
556}
557
558impl<'a> Parse<'a> for KeypadControl {
559 type Error = Error;
560 fn parse(bytes: &'a [u8]) -> Result<Self> {
561 let body = super::parse_apdu_header(bytes, tag::KEYPAD_CONTROL, "keypad_control")?;
562 let cmd_byte = *body.first().ok_or(Error::BufferTooShort {
563 need: 1,
564 have: 0,
565 what: "keypad_control cmd",
566 })?;
567 let cmd = KeypadControlCmd::from_u8(cmd_byte);
568 let key_codes = if cmd.carries_key_codes() {
569 body[1..].to_vec()
570 } else {
571 Vec::new()
572 };
573 Ok(Self { cmd, key_codes })
574 }
575}
576
577impl Serialize for KeypadControl {
578 type Error = Error;
579 fn serialized_len(&self) -> usize {
580 let codes = if self.cmd.carries_key_codes() {
581 self.key_codes.len()
582 } else {
583 0
584 };
585 super::apdu_len(1 + codes)
586 }
587 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
588 let with_codes = self.cmd.carries_key_codes();
589 let codes = if with_codes { self.key_codes.len() } else { 0 };
590 let mut pos = super::write_apdu_header(tag::KEYPAD_CONTROL, 1 + codes, buf)?;
591 buf[pos] = self.cmd.to_u8();
592 pos += 1;
593 if with_codes {
594 buf[pos..pos + self.key_codes.len()].copy_from_slice(&self.key_codes);
595 pos += self.key_codes.len();
596 }
597 Ok(pos)
598 }
599}
600
601impl<'a> ApduDef<'a> for KeypadControl {
602 const TAG: ApduTag = tag::KEYPAD_CONTROL;
603 const NAME: &'static str = "KEYPAD_CONTROL";
604}
605
606#[derive(Debug, Clone, Copy, PartialEq, Eq)]
608#[cfg_attr(feature = "serde", derive(serde::Serialize))]
609pub struct Keypress {
610 pub key_code: u8,
612}
613
614impl<'a> Parse<'a> for Keypress {
615 type Error = Error;
616 fn parse(bytes: &'a [u8]) -> Result<Self> {
617 let body = super::parse_apdu_header(bytes, tag::KEYPRESS, "keypress")?;
618 let key_code = *body.first().ok_or(Error::BufferTooShort {
619 need: 1,
620 have: 0,
621 what: "keypress key_code",
622 })?;
623 Ok(Self { key_code })
624 }
625}
626
627impl Serialize for Keypress {
628 type Error = Error;
629 fn serialized_len(&self) -> usize {
630 super::apdu_len(1)
631 }
632 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
633 let mut pos = super::write_apdu_header(tag::KEYPRESS, 1, buf)?;
634 buf[pos] = self.key_code;
635 pos += 1;
636 Ok(pos)
637 }
638}
639
640impl<'a> ApduDef<'a> for Keypress {
641 const TAG: ApduTag = tag::KEYPRESS;
642 const NAME: &'static str = "KEYPRESS";
643}
644
645#[derive(Debug, Clone, PartialEq, Eq)]
650#[cfg_attr(feature = "serde", derive(serde::Serialize))]
651pub struct SubtitleSegment<'a> {
652 pub more: bool,
654 #[cfg_attr(feature = "serde", serde(borrow, with = "super::bytes_serde"))]
656 pub segment: &'a [u8],
657}
658
659impl<'a> SubtitleSegment<'a> {
660 #[must_use]
662 pub fn tag(&self) -> ApduTag {
663 if self.more {
664 tag::SUBTITLE_SEGMENT_MORE
665 } else {
666 tag::SUBTITLE_SEGMENT_LAST
667 }
668 }
669}
670
671impl<'a> Parse<'a> for SubtitleSegment<'a> {
672 type Error = Error;
673 fn parse(bytes: &'a [u8]) -> Result<Self> {
674 if bytes.len() < 3 {
675 return Err(Error::BufferTooShort {
676 need: 3,
677 have: bytes.len(),
678 what: "subtitle_segment tag",
679 });
680 }
681 let t = ApduTag::from_bytes(bytes[0], bytes[1], bytes[2]);
682 let (expected, more) = match t {
683 tag::SUBTITLE_SEGMENT_MORE => (tag::SUBTITLE_SEGMENT_MORE, true),
684 _ => (tag::SUBTITLE_SEGMENT_LAST, false),
685 };
686 let body = super::parse_apdu_header(bytes, expected, "subtitle_segment")?;
687 Ok(Self {
688 more,
689 segment: body,
690 })
691 }
692}
693
694impl Serialize for SubtitleSegment<'_> {
695 type Error = Error;
696 fn serialized_len(&self) -> usize {
697 super::apdu_len(self.segment.len())
698 }
699 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
700 let mut pos = super::write_apdu_header(self.tag(), self.segment.len(), buf)?;
701 buf[pos..pos + self.segment.len()].copy_from_slice(self.segment);
702 pos += self.segment.len();
703 Ok(pos)
704 }
705}
706
707impl<'a> ApduDef<'a> for SubtitleSegment<'a> {
708 const TAG: ApduTag = tag::SUBTITLE_SEGMENT_LAST;
709 const NAME: &'static str = "SUBTITLE_SEGMENT";
710}
711
712#[derive(Debug, Clone, Copy, PartialEq, Eq)]
714#[cfg_attr(feature = "serde", derive(serde::Serialize))]
715#[non_exhaustive]
716pub enum DisplayMessageId {
717 DisplayOk,
719 DisplayError,
721 DisplayOutOfMemory,
723 DvbSubtitlingSyntaxError,
725 UndefinedRegionReferenced,
727 UndefinedClutReferenced,
729 UndefinedObjectReferenced,
731 ObjectIncompatibleWithRegion,
733 UnknownCharacterReferenced,
735 DisplayCharacteristicsChanged,
737 Reserved(u8),
739}
740
741impl DisplayMessageId {
742 #[must_use]
744 pub fn from_u8(v: u8) -> Self {
745 match v {
746 0x00 => Self::DisplayOk,
747 0x01 => Self::DisplayError,
748 0x02 => Self::DisplayOutOfMemory,
749 0x03 => Self::DvbSubtitlingSyntaxError,
750 0x04 => Self::UndefinedRegionReferenced,
751 0x05 => Self::UndefinedClutReferenced,
752 0x06 => Self::UndefinedObjectReferenced,
753 0x07 => Self::ObjectIncompatibleWithRegion,
754 0x08 => Self::UnknownCharacterReferenced,
755 0x09 => Self::DisplayCharacteristicsChanged,
756 other => Self::Reserved(other),
757 }
758 }
759 #[must_use]
761 pub fn to_u8(self) -> u8 {
762 match self {
763 Self::DisplayOk => 0x00,
764 Self::DisplayError => 0x01,
765 Self::DisplayOutOfMemory => 0x02,
766 Self::DvbSubtitlingSyntaxError => 0x03,
767 Self::UndefinedRegionReferenced => 0x04,
768 Self::UndefinedClutReferenced => 0x05,
769 Self::UndefinedObjectReferenced => 0x06,
770 Self::ObjectIncompatibleWithRegion => 0x07,
771 Self::UnknownCharacterReferenced => 0x08,
772 Self::DisplayCharacteristicsChanged => 0x09,
773 Self::Reserved(v) => v,
774 }
775 }
776 #[must_use]
778 pub fn name(&self) -> &'static str {
779 match self {
780 Self::DisplayOk => "Display OK",
781 Self::DisplayError => "Display Error",
782 Self::DisplayOutOfMemory => "Display out of memory",
783 Self::DvbSubtitlingSyntaxError => "DVB Subtitling syntax error",
784 Self::UndefinedRegionReferenced => "Undefined region referenced",
785 Self::UndefinedClutReferenced => "Undefined CLUT referenced",
786 Self::UndefinedObjectReferenced => "Undefined object referenced",
787 Self::ObjectIncompatibleWithRegion => "Object incompatible with region",
788 Self::UnknownCharacterReferenced => "Unknown character referenced",
789 Self::DisplayCharacteristicsChanged => "Display characteristics changed",
790 Self::Reserved(_) => "reserved",
791 }
792 }
793}
794dvb_common::impl_spec_display!(DisplayMessageId, Reserved);
795
796#[derive(Debug, Clone, Copy, PartialEq, Eq)]
798#[cfg_attr(feature = "serde", derive(serde::Serialize))]
799pub struct DisplayMessage {
800 pub message_id: DisplayMessageId,
802}
803
804impl<'a> Parse<'a> for DisplayMessage {
805 type Error = Error;
806 fn parse(bytes: &'a [u8]) -> Result<Self> {
807 let body = super::parse_apdu_header(bytes, tag::DISPLAY_MESSAGE, "display_message")?;
808 let message_id = DisplayMessageId::from_u8(*body.first().ok_or(Error::BufferTooShort {
809 need: 1,
810 have: 0,
811 what: "display_message id",
812 })?);
813 Ok(Self { message_id })
814 }
815}
816
817impl Serialize for DisplayMessage {
818 type Error = Error;
819 fn serialized_len(&self) -> usize {
820 super::apdu_len(1)
821 }
822 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
823 let mut pos = super::write_apdu_header(tag::DISPLAY_MESSAGE, 1, buf)?;
824 buf[pos] = self.message_id.to_u8();
825 pos += 1;
826 Ok(pos)
827 }
828}
829
830impl<'a> ApduDef<'a> for DisplayMessage {
831 const TAG: ApduTag = tag::DISPLAY_MESSAGE;
832 const NAME: &'static str = "DISPLAY_MESSAGE";
833}
834
835#[derive(Debug, Clone, Copy, PartialEq, Eq)]
837#[cfg_attr(feature = "serde", derive(serde::Serialize))]
838pub struct SceneEndMark {
839 pub decoder_continue_flag: bool,
841 pub scene_reveal_flag: bool,
843 pub send_scene_done: bool,
845 pub scene_tag: u8,
847}
848
849impl<'a> Parse<'a> for SceneEndMark {
850 type Error = Error;
851 fn parse(bytes: &'a [u8]) -> Result<Self> {
852 let body = super::parse_apdu_header(bytes, tag::SCENE_END_MARK, "scene_end_mark")?;
853 let b = *body.first().ok_or(Error::BufferTooShort {
854 need: 1,
855 have: 0,
856 what: "scene_end_mark",
857 })?;
858 Ok(Self {
859 decoder_continue_flag: (b & 0x80) != 0,
860 scene_reveal_flag: (b & 0x40) != 0,
861 send_scene_done: (b & 0x20) != 0,
862 scene_tag: b & 0x0F,
864 })
865 }
866}
867
868impl Serialize for SceneEndMark {
869 type Error = Error;
870 fn serialized_len(&self) -> usize {
871 super::apdu_len(1)
872 }
873 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
874 let mut pos = super::write_apdu_header(tag::SCENE_END_MARK, 1, buf)?;
875 buf[pos] = (u8::from(self.decoder_continue_flag) << 7)
877 | (u8::from(self.scene_reveal_flag) << 6)
878 | (u8::from(self.send_scene_done) << 5)
879 | 0x10
880 | (self.scene_tag & 0x0F);
881 pos += 1;
882 Ok(pos)
883 }
884}
885
886impl<'a> ApduDef<'a> for SceneEndMark {
887 const TAG: ApduTag = tag::SCENE_END_MARK;
888 const NAME: &'static str = "SCENE_END_MARK";
889}
890
891#[derive(Debug, Clone, Copy, PartialEq, Eq)]
896#[cfg_attr(feature = "serde", derive(serde::Serialize))]
897pub struct SceneDoneMessage {
898 pub decoder_continue_flag: bool,
900 pub scene_reveal_flag: bool,
902 pub scene_tag: u8,
904}
905
906impl<'a> Parse<'a> for SceneDoneMessage {
907 type Error = Error;
908 fn parse(bytes: &'a [u8]) -> Result<Self> {
909 let body = super::parse_apdu_header(bytes, tag::SCENE_DONE, "scene_done_message")?;
910 let b = *body.first().ok_or(Error::BufferTooShort {
911 need: 1,
912 have: 0,
913 what: "scene_done_message",
914 })?;
915 Ok(Self {
916 decoder_continue_flag: (b & 0x80) != 0,
917 scene_reveal_flag: (b & 0x40) != 0,
918 scene_tag: b & 0x0F,
920 })
921 }
922}
923
924impl Serialize for SceneDoneMessage {
925 type Error = Error;
926 fn serialized_len(&self) -> usize {
927 super::apdu_len(1)
928 }
929 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
930 let mut pos = super::write_apdu_header(tag::SCENE_DONE, 1, buf)?;
931 buf[pos] = (u8::from(self.decoder_continue_flag) << 7)
933 | (u8::from(self.scene_reveal_flag) << 6)
934 | 0x30
935 | (self.scene_tag & 0x0F);
936 pos += 1;
937 Ok(pos)
938 }
939}
940
941impl<'a> ApduDef<'a> for SceneDoneMessage {
942 const TAG: ApduTag = tag::SCENE_DONE;
943 const NAME: &'static str = "SCENE_DONE";
944}
945
946#[derive(Debug, Clone, Copy, PartialEq, Eq)]
948#[cfg_attr(feature = "serde", derive(serde::Serialize))]
949pub struct SceneControl {
950 pub decoder_continue_flag: bool,
952 pub scene_reveal_flag: bool,
954 pub scene_tag: u8,
956}
957
958impl<'a> Parse<'a> for SceneControl {
959 type Error = Error;
960 fn parse(bytes: &'a [u8]) -> Result<Self> {
961 let body = super::parse_apdu_header(bytes, tag::SCENE_CONTROL, "scene_control")?;
962 let b = *body.first().ok_or(Error::BufferTooShort {
963 need: 1,
964 have: 0,
965 what: "scene_control",
966 })?;
967 Ok(Self {
968 decoder_continue_flag: (b & 0x80) != 0,
969 scene_reveal_flag: (b & 0x40) != 0,
970 scene_tag: b & 0x0F,
971 })
972 }
973}
974
975impl Serialize for SceneControl {
976 type Error = Error;
977 fn serialized_len(&self) -> usize {
978 super::apdu_len(1)
979 }
980 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
981 let mut pos = super::write_apdu_header(tag::SCENE_CONTROL, 1, buf)?;
982 buf[pos] = (u8::from(self.decoder_continue_flag) << 7)
984 | (u8::from(self.scene_reveal_flag) << 6)
985 | 0x30
986 | (self.scene_tag & 0x0F);
987 pos += 1;
988 Ok(pos)
989 }
990}
991
992impl<'a> ApduDef<'a> for SceneControl {
993 const TAG: ApduTag = tag::SCENE_CONTROL;
994 const NAME: &'static str = "SCENE_CONTROL";
995}
996
997#[derive(Debug, Clone, PartialEq, Eq)]
1003#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1004pub struct SubtitleDownload<'a> {
1005 pub more: bool,
1007 #[cfg_attr(feature = "serde", serde(borrow, with = "super::bytes_serde"))]
1009 pub segment: &'a [u8],
1010}
1011
1012impl<'a> SubtitleDownload<'a> {
1013 #[must_use]
1015 pub fn tag(&self) -> ApduTag {
1016 if self.more {
1017 tag::SUBTITLE_DOWNLOAD_MORE
1018 } else {
1019 tag::SUBTITLE_DOWNLOAD_LAST
1020 }
1021 }
1022}
1023
1024impl<'a> Parse<'a> for SubtitleDownload<'a> {
1025 type Error = Error;
1026 fn parse(bytes: &'a [u8]) -> Result<Self> {
1027 if bytes.len() < 3 {
1028 return Err(Error::BufferTooShort {
1029 need: 3,
1030 have: bytes.len(),
1031 what: "subtitle_download tag",
1032 });
1033 }
1034 let t = ApduTag::from_bytes(bytes[0], bytes[1], bytes[2]);
1035 let (expected, more) = match t {
1036 tag::SUBTITLE_DOWNLOAD_MORE => (tag::SUBTITLE_DOWNLOAD_MORE, true),
1037 _ => (tag::SUBTITLE_DOWNLOAD_LAST, false),
1038 };
1039 let body = super::parse_apdu_header(bytes, expected, "subtitle_download")?;
1040 Ok(Self {
1041 more,
1042 segment: body,
1043 })
1044 }
1045}
1046
1047impl Serialize for SubtitleDownload<'_> {
1048 type Error = Error;
1049 fn serialized_len(&self) -> usize {
1050 super::apdu_len(self.segment.len())
1051 }
1052 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1053 let mut pos = super::write_apdu_header(self.tag(), self.segment.len(), buf)?;
1054 buf[pos..pos + self.segment.len()].copy_from_slice(self.segment);
1055 pos += self.segment.len();
1056 Ok(pos)
1057 }
1058}
1059
1060impl<'a> ApduDef<'a> for SubtitleDownload<'a> {
1061 const TAG: ApduTag = tag::SUBTITLE_DOWNLOAD_LAST;
1062 const NAME: &'static str = "SUBTITLE_DOWNLOAD";
1063}
1064
1065#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1067#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1068pub struct FlushDownload;
1069
1070impl<'a> Parse<'a> for FlushDownload {
1071 type Error = Error;
1072 fn parse(bytes: &'a [u8]) -> Result<Self> {
1073 super::parse_empty_apdu(bytes, tag::FLUSH_DOWNLOAD, "flush_download")?;
1074 Ok(Self)
1075 }
1076}
1077
1078impl Serialize for FlushDownload {
1079 type Error = Error;
1080 fn serialized_len(&self) -> usize {
1081 super::empty_apdu_len()
1082 }
1083 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1084 super::serialize_empty_apdu(tag::FLUSH_DOWNLOAD, buf)
1085 }
1086}
1087
1088impl<'a> ApduDef<'a> for FlushDownload {
1089 const TAG: ApduTag = tag::FLUSH_DOWNLOAD;
1090 const NAME: &'static str = "FLUSH_DOWNLOAD";
1091}
1092
1093#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1095#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1096#[non_exhaustive]
1097pub enum DownloadReplyId {
1098 DownloadOk,
1100 NotAnObjectDataSegment,
1102 MemoryExhausted,
1104 Reserved(u8),
1106}
1107
1108impl DownloadReplyId {
1109 #[must_use]
1111 pub fn from_u8(v: u8) -> Self {
1112 match v {
1113 0x00 => Self::DownloadOk,
1114 0x01 => Self::NotAnObjectDataSegment,
1115 0x02 => Self::MemoryExhausted,
1116 other => Self::Reserved(other),
1117 }
1118 }
1119 #[must_use]
1121 pub fn to_u8(self) -> u8 {
1122 match self {
1123 Self::DownloadOk => 0x00,
1124 Self::NotAnObjectDataSegment => 0x01,
1125 Self::MemoryExhausted => 0x02,
1126 Self::Reserved(v) => v,
1127 }
1128 }
1129 #[must_use]
1131 pub fn name(&self) -> &'static str {
1132 match self {
1133 Self::DownloadOk => "Download OK",
1134 Self::NotAnObjectDataSegment => "Not an object data segment",
1135 Self::MemoryExhausted => "Memory exhausted",
1136 Self::Reserved(_) => "reserved",
1137 }
1138 }
1139}
1140dvb_common::impl_spec_display!(DownloadReplyId, Reserved);
1141
1142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1144#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1145pub struct DownloadReply {
1146 pub object_id: u16,
1148 pub reply_id: DownloadReplyId,
1150}
1151
1152const DOWNLOAD_REPLY_BODY: usize = 3;
1154
1155impl<'a> Parse<'a> for DownloadReply {
1156 type Error = Error;
1157 fn parse(bytes: &'a [u8]) -> Result<Self> {
1158 let body = super::parse_apdu_header(bytes, tag::DOWNLOAD_REPLY, "download_reply")?;
1159 if body.len() < DOWNLOAD_REPLY_BODY {
1160 return Err(Error::BufferTooShort {
1161 need: DOWNLOAD_REPLY_BODY,
1162 have: body.len(),
1163 what: "download_reply",
1164 });
1165 }
1166 Ok(Self {
1167 object_id: u16::from_be_bytes([body[0], body[1]]),
1168 reply_id: DownloadReplyId::from_u8(body[2]),
1169 })
1170 }
1171}
1172
1173impl Serialize for DownloadReply {
1174 type Error = Error;
1175 fn serialized_len(&self) -> usize {
1176 super::apdu_len(DOWNLOAD_REPLY_BODY)
1177 }
1178 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1179 let mut pos = super::write_apdu_header(tag::DOWNLOAD_REPLY, DOWNLOAD_REPLY_BODY, buf)?;
1180 buf[pos..pos + 2].copy_from_slice(&self.object_id.to_be_bytes());
1181 buf[pos + 2] = self.reply_id.to_u8();
1182 pos += DOWNLOAD_REPLY_BODY;
1183 Ok(pos)
1184 }
1185}
1186
1187impl<'a> ApduDef<'a> for DownloadReply {
1188 const TAG: ApduTag = tag::DOWNLOAD_REPLY;
1189 const NAME: &'static str = "DOWNLOAD_REPLY";
1190}
1191
1192#[cfg(test)]
1193mod tests {
1194 use super::*;
1195
1196 #[test]
1197 fn display_control_set_mmi_mode_and_query() {
1198 let dc = DisplayControl {
1199 cmd: DisplayControlCmd::SetMmiMode,
1200 mmi_mode: Some(MmiMode::HighLevel),
1201 };
1202 let bytes = dc.to_bytes();
1203 assert_eq!(bytes, [0x9F, 0x88, 0x01, 0x02, 0x01, 0x01]);
1204 assert_eq!(DisplayControl::parse(&bytes).unwrap(), dc);
1205
1206 let q = DisplayControl {
1207 cmd: DisplayControlCmd::GetDisplayCharacterTableList,
1208 mmi_mode: None,
1209 };
1210 let qb = q.to_bytes();
1211 assert_eq!(qb, [0x9F, 0x88, 0x01, 0x01, 0x02]);
1212 assert_eq!(DisplayControl::parse(&qb).unwrap(), q);
1213
1214 let mut other = dc;
1215 other.mmi_mode = Some(MmiMode::LowLevelFullScreenGraphics);
1216 assert_ne!(bytes, other.to_bytes());
1217 }
1218
1219 #[test]
1220 fn display_reply_graphics_round_trips_and_bites() {
1221 let g = GraphicsCharacteristics {
1222 display_horizontal_size: 720,
1223 display_vertical_size: 576,
1224 aspect_ratio_information: 3,
1225 graphics_relation_to_video: 0b111,
1226 multiple_depths: true,
1227 display_bytes: 0x0ABC,
1228 composition_buffer_bytes: 0x12,
1229 object_cache_bytes: 0x34,
1230 depths: alloc::vec![
1231 PixelDepth {
1232 display_depth: 2,
1233 pixels_per_byte: 4,
1234 region_overhead: 0x10,
1235 },
1236 PixelDepth {
1237 display_depth: 7,
1238 pixels_per_byte: 1,
1239 region_overhead: 0x20,
1240 },
1241 ],
1242 };
1243 let dr = DisplayReply {
1244 reply_id: DisplayReplyId::ListGraphicOverlayCharacteristics,
1245 body: DisplayReplyBody::Graphics(g),
1246 };
1247 let bytes = dr.to_bytes();
1248 let parsed = DisplayReply::parse(&bytes).unwrap();
1249 assert_eq!(parsed, dr);
1250 if let DisplayReplyBody::Graphics(gg) = &parsed.body {
1251 assert_eq!(gg.depths.len(), 2);
1252 assert_eq!(gg.display_bytes, 0x0ABC);
1253 assert_eq!(gg.composition_buffer_bytes, 0x12);
1254 assert_eq!(gg.object_cache_bytes, 0x34);
1255 } else {
1256 panic!("expected graphics");
1257 }
1258
1259 let mut other = dr.clone();
1261 if let DisplayReplyBody::Graphics(gg) = &mut other.body {
1262 gg.depths[0].region_overhead = 0xFF;
1263 }
1264 assert_ne!(bytes, other.to_bytes());
1265 }
1266
1267 #[test]
1268 fn display_reply_char_tables_and_ack() {
1269 let ct = DisplayReply {
1270 reply_id: DisplayReplyId::ListDisplayCharacterTables,
1271 body: DisplayReplyBody::CharacterTables(alloc::vec![0x00, 0x01, 0x02]),
1272 };
1273 let bytes = ct.to_bytes();
1274 assert_eq!(bytes, [0x9F, 0x88, 0x02, 0x04, 0x02, 0x00, 0x01, 0x02]);
1275 assert_eq!(DisplayReply::parse(&bytes).unwrap(), ct);
1276
1277 let ack = DisplayReply {
1278 reply_id: DisplayReplyId::MmiModeAck,
1279 body: DisplayReplyBody::MmiModeAck(MmiMode::HighLevel),
1280 };
1281 let ab = ack.to_bytes();
1282 assert_eq!(ab, [0x9F, 0x88, 0x02, 0x02, 0x01, 0x01]);
1283 assert_eq!(DisplayReply::parse(&ab).unwrap(), ack);
1284
1285 let unknown = DisplayReply {
1286 reply_id: DisplayReplyId::UnknownMmiMode,
1287 body: DisplayReplyBody::None,
1288 };
1289 let ub = unknown.to_bytes();
1290 assert_eq!(ub, [0x9F, 0x88, 0x02, 0x01, 0xF1]);
1291 assert_eq!(DisplayReply::parse(&ub).unwrap(), unknown);
1292 }
1293
1294 #[test]
1295 fn keypad_control_multi_key_round_trips_and_bites() {
1296 let kc = KeypadControl {
1297 cmd: KeypadControlCmd::InterceptSelectedKeypress,
1298 key_codes: alloc::vec![0x00, 0x01, 0x0A], };
1300 let bytes = kc.to_bytes();
1301 assert_eq!(bytes, [0x9F, 0x88, 0x05, 0x04, 0x03, 0x00, 0x01, 0x0A]);
1302 let parsed = KeypadControl::parse(&bytes).unwrap();
1303 assert_eq!(parsed, kc);
1304 assert_eq!(parsed.key_codes.len(), 3);
1305
1306 let all = KeypadControl {
1308 cmd: KeypadControlCmd::InterceptAllKeypresses,
1309 key_codes: Vec::new(),
1310 };
1311 let ab = all.to_bytes();
1312 assert_eq!(ab, [0x9F, 0x88, 0x05, 0x01, 0x01]);
1313 assert_eq!(KeypadControl::parse(&ab).unwrap(), all);
1314
1315 let mut other = kc.clone();
1317 other.key_codes[1] = 0x09;
1318 assert_ne!(bytes, other.to_bytes());
1319 }
1320
1321 #[test]
1322 fn keypress_round_trips_and_bites() {
1323 let k = Keypress { key_code: 0x0A };
1324 let bytes = k.to_bytes();
1325 assert_eq!(bytes, [0x9F, 0x88, 0x06, 0x01, 0x0A]);
1326 assert_eq!(Keypress::parse(&bytes).unwrap(), k);
1327 let other = Keypress { key_code: 0x0B };
1328 assert_ne!(bytes, other.to_bytes());
1329 }
1330
1331 #[test]
1332 fn subtitle_segment_more_bites() {
1333 let s = SubtitleSegment {
1334 more: false,
1335 segment: &[0x0F, 0x10, 0x00, 0x01, 0x00, 0x05],
1336 };
1337 let bytes = s.to_bytes();
1338 assert_eq!(bytes[2], 0x0E);
1339 assert_eq!(SubtitleSegment::parse(&bytes).unwrap(), s);
1340 let mut more = s.clone();
1341 more.more = true;
1342 let mb = more.to_bytes();
1343 assert_eq!(mb[2], 0x0F);
1344 assert_ne!(bytes, mb);
1345 assert_eq!(SubtitleSegment::parse(&mb).unwrap(), more);
1346 }
1347
1348 #[test]
1349 fn display_message_round_trips_and_bites() {
1350 let m = DisplayMessage {
1351 message_id: DisplayMessageId::DvbSubtitlingSyntaxError,
1352 };
1353 let bytes = m.to_bytes();
1354 assert_eq!(bytes, [0x9F, 0x88, 0x10, 0x01, 0x03]);
1355 assert_eq!(DisplayMessage::parse(&bytes).unwrap(), m);
1356 assert_eq!(m.message_id.name(), "DVB Subtitling syntax error");
1357 let other = DisplayMessage {
1358 message_id: DisplayMessageId::DisplayOk,
1359 };
1360 assert_ne!(bytes, other.to_bytes());
1361 }
1362
1363 #[test]
1364 fn scene_end_mark_round_trips_and_bites() {
1365 let s = SceneEndMark {
1366 decoder_continue_flag: true,
1367 scene_reveal_flag: false,
1368 send_scene_done: true,
1369 scene_tag: 0x05,
1370 };
1371 let bytes = s.to_bytes();
1372 assert_eq!(bytes, [0x9F, 0x88, 0x11, 0x01, 0xB5]);
1374 assert_eq!(SceneEndMark::parse(&bytes).unwrap(), s);
1375 let mut other = s;
1376 other.scene_tag = 0x06;
1377 assert_ne!(bytes, other.to_bytes());
1378 }
1379
1380 #[test]
1381 fn scene_done_message_two_bit_reserved() {
1382 let s = SceneDoneMessage {
1383 decoder_continue_flag: false,
1384 scene_reveal_flag: true,
1385 scene_tag: 0x0A,
1386 };
1387 let bytes = s.to_bytes();
1388 assert_eq!(bytes, [0x9F, 0x88, 0x12, 0x01, 0x7A]);
1390 assert_eq!(SceneDoneMessage::parse(&bytes).unwrap(), s);
1391 let mut other = s;
1392 other.decoder_continue_flag = true;
1393 assert_ne!(bytes, other.to_bytes());
1394 }
1395
1396 #[test]
1397 fn scene_control_round_trips() {
1398 let s = SceneControl {
1399 decoder_continue_flag: true,
1400 scene_reveal_flag: true,
1401 scene_tag: 0x03,
1402 };
1403 let bytes = s.to_bytes();
1404 assert_eq!(bytes, [0x9F, 0x88, 0x13, 0x01, 0xF3]);
1406 assert_eq!(SceneControl::parse(&bytes).unwrap(), s);
1407 }
1408
1409 #[test]
1410 fn subtitle_download_more_bites() {
1411 let s = SubtitleDownload {
1412 more: true,
1413 segment: &[0x0F, 0x13, 0x00, 0x01],
1414 };
1415 let bytes = s.to_bytes();
1416 assert_eq!(bytes[2], 0x15);
1417 assert_eq!(SubtitleDownload::parse(&bytes).unwrap(), s);
1418 let mut last = s.clone();
1419 last.more = false;
1420 let lb = last.to_bytes();
1421 assert_eq!(lb[2], 0x14);
1422 assert_ne!(bytes, lb);
1423 }
1424
1425 #[test]
1426 fn flush_download_round_trips() {
1427 let bytes = FlushDownload.to_bytes();
1428 assert_eq!(bytes, [0x9F, 0x88, 0x16, 0x00]);
1429 assert_eq!(FlushDownload::parse(&bytes).unwrap(), FlushDownload);
1430 }
1431
1432 #[test]
1433 fn download_reply_round_trips_and_bites() {
1434 let d = DownloadReply {
1435 object_id: 0xFFFF,
1436 reply_id: DownloadReplyId::NotAnObjectDataSegment,
1437 };
1438 let bytes = d.to_bytes();
1439 assert_eq!(bytes, [0x9F, 0x88, 0x17, 0x03, 0xFF, 0xFF, 0x01]);
1440 assert_eq!(DownloadReply::parse(&bytes).unwrap(), d);
1441 let mut other = d;
1442 other.object_id = 0x0001;
1443 assert_ne!(bytes, other.to_bytes());
1444 }
1445}