ironrdp_pdu/rdp/capability_sets/
bitmap_codecs.rs

1#[cfg(test)]
2mod tests;
3
4use core::fmt::{self, Debug};
5use std::collections::HashMap;
6
7use bitflags::bitflags;
8use ironrdp_core::{
9    cast_length, decode, ensure_fixed_part_size, ensure_size, invalid_field_err, other_err, Decode, DecodeResult,
10    Encode, EncodeResult, ReadCursor, WriteCursor,
11};
12use num_derive::{FromPrimitive, ToPrimitive};
13use num_traits::{FromPrimitive as _, ToPrimitive as _};
14
15const RFX_ICAP_VERSION: u16 = 0x0100;
16const RFX_ICAP_TILE_SIZE: u16 = 0x40;
17const RFX_ICAP_COLOR_CONVERSION: u8 = 1;
18const RFX_ICAP_TRANSFORM_BITS: u8 = 1;
19const RFX_ICAP_LENGTH: usize = 8;
20
21const RFX_CAPSET_BLOCK_TYPE: u16 = 0xcbc1;
22const RFX_CAPSET_TYPE: u16 = 0xcfc0;
23const RFX_CAPSET_STATIC_DATA_LENGTH: usize = 13;
24
25const RFX_CAPS_BLOCK_TYPE: u16 = 0xcbc0;
26const RFX_CAPS_BLOCK_LENGTH: u32 = 8;
27const RFX_CAPS_NUM_CAPSETS: u16 = 1;
28const RFX_CAPS_STATIC_DATA_LENGTH: usize = 8;
29
30const RFX_CLIENT_CAPS_CONTAINER_STATIC_DATA_LENGTH: usize = 12;
31
32const NSCODEC_LENGTH: usize = 3;
33const CODEC_STATIC_DATA_LENGTH: usize = 19;
34
35#[rustfmt::skip]
36const GUID_NSCODEC: Guid = Guid(0xca8d_1bb9, 0x000f, 0x154f, 0x58, 0x9f, 0xae, 0x2d, 0x1a, 0x87, 0xe2, 0xd6);
37#[rustfmt::skip]
38const GUID_REMOTEFX: Guid = Guid(0x7677_2f12, 0xbd72, 0x4463, 0xaf, 0xb3, 0xb7, 0x3c, 0x9c, 0x6f, 0x78, 0x86);
39#[rustfmt::skip]
40const GUID_IMAGE_REMOTEFX: Guid = Guid(0x2744_ccd4, 0x9d8a, 0x4e74, 0x80, 0x3c, 0x0e, 0xcb, 0xee, 0xa1, 0x9c, 0x54);
41#[rustfmt::skip]
42const GUID_IGNORE: Guid = Guid(0x9c43_51a6, 0x3535, 0x42ae, 0x91, 0x0c, 0xcd, 0xfc, 0xe5, 0x76, 0x0b, 0x58);
43#[rustfmt::skip]
44#[cfg(feature="qoi")]
45const GUID_QOI: Guid = Guid(0x4dae_9af8, 0xb399, 0x4df6, 0xb4, 0x3a, 0x66, 0x2f, 0xd9, 0xc0, 0xf5, 0xd6);
46#[rustfmt::skip]
47#[cfg(feature="qoiz")]
48const GUID_QOIZ: Guid = Guid(0x229c_c6dc, 0xa860, 0x4b52, 0xb4, 0xd8, 0x05, 0x3a, 0x22, 0xb3, 0x89, 0x2b);
49
50#[derive(Debug, PartialEq, Eq)]
51pub struct Guid(u32, u16, u16, u8, u8, u8, u8, u8, u8, u8, u8);
52
53impl Guid {
54    const NAME: &'static str = "Guid";
55
56    const FIXED_PART_SIZE: usize = 16;
57}
58
59impl Encode for Guid {
60    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
61        ensure_fixed_part_size!(in: dst);
62
63        dst.write_u32(self.0);
64        dst.write_u16(self.1);
65        dst.write_u16(self.2);
66        dst.write_u8(self.3);
67        dst.write_u8(self.4);
68        dst.write_u8(self.5);
69        dst.write_u8(self.6);
70        dst.write_u8(self.7);
71        dst.write_u8(self.8);
72        dst.write_u8(self.9);
73        dst.write_u8(self.10);
74
75        Ok(())
76    }
77
78    fn name(&self) -> &'static str {
79        Self::NAME
80    }
81
82    fn size(&self) -> usize {
83        Self::FIXED_PART_SIZE
84    }
85}
86
87impl<'de> Decode<'de> for Guid {
88    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
89        ensure_fixed_part_size!(in: src);
90
91        let guid1 = src.read_u32();
92        let guid2 = src.read_u16();
93        let guid3 = src.read_u16();
94        let guid4 = src.read_u8();
95        let guid5 = src.read_u8();
96        let guid6 = src.read_u8();
97        let guid7 = src.read_u8();
98        let guid8 = src.read_u8();
99        let guid9 = src.read_u8();
100        let guid10 = src.read_u8();
101        let guid11 = src.read_u8();
102
103        Ok(Guid(
104            guid1, guid2, guid3, guid4, guid5, guid6, guid7, guid8, guid9, guid10, guid11,
105        ))
106    }
107}
108
109#[derive(Debug, PartialEq, Eq, Clone, Default)]
110pub struct BitmapCodecs(pub Vec<Codec>);
111
112impl BitmapCodecs {
113    const NAME: &'static str = "BitmapCodecs";
114
115    const FIXED_PART_SIZE: usize = 1 /* len */;
116}
117
118impl Encode for BitmapCodecs {
119    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
120        ensure_size!(in: dst, size: self.size());
121
122        dst.write_u8(cast_length!("len", self.0.len())?);
123
124        for codec in self.0.iter() {
125            codec.encode(dst)?;
126        }
127
128        Ok(())
129    }
130
131    fn name(&self) -> &'static str {
132        Self::NAME
133    }
134
135    fn size(&self) -> usize {
136        Self::FIXED_PART_SIZE + self.0.iter().map(Encode::size).sum::<usize>()
137    }
138}
139
140impl<'de> Decode<'de> for BitmapCodecs {
141    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
142        ensure_fixed_part_size!(in: src);
143
144        let codecs_count = src.read_u8();
145
146        let mut codecs = Vec::with_capacity(codecs_count as usize);
147        for _ in 0..codecs_count {
148            codecs.push(Codec::decode(src)?);
149        }
150
151        Ok(BitmapCodecs(codecs))
152    }
153}
154
155#[derive(Debug, PartialEq, Eq, Clone)]
156pub struct Codec {
157    pub id: u8,
158    pub property: CodecProperty,
159}
160
161impl Codec {
162    const NAME: &'static str = "Codec";
163
164    const FIXED_PART_SIZE: usize = CODEC_STATIC_DATA_LENGTH;
165}
166
167impl Encode for Codec {
168    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
169        ensure_size!(in: dst, size: self.size());
170
171        let guid = match &self.property {
172            CodecProperty::NsCodec(_) => GUID_NSCODEC,
173            CodecProperty::RemoteFx(_) => GUID_REMOTEFX,
174            CodecProperty::ImageRemoteFx(_) => GUID_IMAGE_REMOTEFX,
175            CodecProperty::Ignore => GUID_IGNORE,
176            #[cfg(feature = "qoi")]
177            CodecProperty::Qoi => GUID_QOI,
178            #[cfg(feature = "qoiz")]
179            CodecProperty::QoiZ => GUID_QOIZ,
180            _ => return Err(other_err!("invalid codec")),
181        };
182        guid.encode(dst)?;
183
184        dst.write_u8(self.id);
185
186        match &self.property {
187            CodecProperty::NsCodec(p) => {
188                dst.write_u16(cast_length!("len", p.size())?);
189                p.encode(dst)?;
190            }
191            CodecProperty::RemoteFx(p) => {
192                match p {
193                    RemoteFxContainer::ClientContainer(container) => {
194                        dst.write_u16(cast_length!("len", container.size())?);
195                        container.encode(dst)?;
196                    }
197                    RemoteFxContainer::ServerContainer(size) => {
198                        dst.write_u16(cast_length!("len", *size)?);
199                        let buff = vec![0u8; *size];
200                        dst.write_slice(&buff);
201                    }
202                };
203            }
204            CodecProperty::ImageRemoteFx(p) => {
205                match p {
206                    RemoteFxContainer::ClientContainer(container) => {
207                        dst.write_u16(cast_length!("len", container.size())?);
208                        container.encode(dst)?;
209                    }
210                    RemoteFxContainer::ServerContainer(size) => {
211                        dst.write_u16(cast_length!("len", *size)?);
212                        let buff = vec![0u8; *size];
213                        dst.write_slice(&buff);
214                    }
215                };
216            }
217            #[cfg(feature = "qoi")]
218            CodecProperty::Qoi => dst.write_u16(0),
219            #[cfg(feature = "qoiz")]
220            CodecProperty::QoiZ => dst.write_u16(0),
221            CodecProperty::Ignore => dst.write_u16(0),
222            CodecProperty::None => dst.write_u16(0),
223        };
224
225        Ok(())
226    }
227
228    fn name(&self) -> &'static str {
229        Self::NAME
230    }
231
232    fn size(&self) -> usize {
233        Self::FIXED_PART_SIZE
234            + match &self.property {
235                CodecProperty::NsCodec(p) => p.size(),
236                CodecProperty::RemoteFx(p) => match p {
237                    RemoteFxContainer::ClientContainer(container) => container.size(),
238                    RemoteFxContainer::ServerContainer(size) => *size,
239                },
240                CodecProperty::ImageRemoteFx(p) => match p {
241                    RemoteFxContainer::ClientContainer(container) => container.size(),
242                    RemoteFxContainer::ServerContainer(size) => *size,
243                },
244                #[cfg(feature = "qoi")]
245                CodecProperty::Qoi => 0,
246                #[cfg(feature = "qoiz")]
247                CodecProperty::QoiZ => 0,
248                CodecProperty::Ignore => 0,
249                CodecProperty::None => 0,
250            }
251    }
252}
253
254impl<'de> Decode<'de> for Codec {
255    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
256        ensure_fixed_part_size!(in: src);
257
258        let guid = Guid::decode(src)?;
259
260        let id = src.read_u8();
261        let codec_properties_len = usize::from(src.read_u16());
262
263        ensure_size!(in: src, size: codec_properties_len);
264        let property_buffer = src.read_slice(codec_properties_len);
265
266        let property = match guid {
267            GUID_NSCODEC => CodecProperty::NsCodec(decode(property_buffer)?),
268            GUID_REMOTEFX | GUID_IMAGE_REMOTEFX => {
269                let byte = property_buffer
270                    .first()
271                    .ok_or_else(|| invalid_field_err!("remotefx property", "must not be empty"))?;
272                let property = if *byte == 0 {
273                    RemoteFxContainer::ServerContainer(codec_properties_len)
274                } else {
275                    RemoteFxContainer::ClientContainer(decode(property_buffer)?)
276                };
277
278                match guid {
279                    GUID_REMOTEFX => CodecProperty::RemoteFx(property),
280                    GUID_IMAGE_REMOTEFX => CodecProperty::ImageRemoteFx(property),
281                    _ => unreachable!(),
282                }
283            }
284            GUID_IGNORE => CodecProperty::Ignore,
285            #[cfg(feature = "qoi")]
286            GUID_QOI => {
287                if !property_buffer.is_empty() {
288                    return Err(invalid_field_err!("qoi property", "must be empty"));
289                }
290                CodecProperty::Qoi
291            }
292            #[cfg(feature = "qoiz")]
293            GUID_QOIZ => {
294                if !property_buffer.is_empty() {
295                    return Err(invalid_field_err!("qoi property", "must be empty"));
296                }
297                CodecProperty::QoiZ
298            }
299            _ => CodecProperty::None,
300        };
301
302        Ok(Self { id, property })
303    }
304}
305
306#[derive(Debug, PartialEq, Eq, Clone)]
307pub enum RemoteFxContainer {
308    ClientContainer(RfxClientCapsContainer),
309    ServerContainer(usize),
310}
311
312#[derive(Debug, PartialEq, Eq, Clone)]
313pub enum CodecProperty {
314    NsCodec(NsCodec),
315    RemoteFx(RemoteFxContainer),
316    ImageRemoteFx(RemoteFxContainer),
317    Ignore,
318    #[cfg(feature = "qoi")]
319    Qoi,
320    #[cfg(feature = "qoiz")]
321    QoiZ,
322    None,
323}
324
325/// The NsCodec structure advertises properties of the NSCodec Bitmap Codec.
326///
327/// # Fields
328///
329/// * `is_dynamic_fidelity_allowed` - indicates support for lossy bitmap compression by reducing color fidelity
330/// * `is_subsampling_allowed` - indicates support for chroma subsampling
331/// * `color_loss_level` - indicates the maximum supported Color Loss Level
332///
333/// If received Color Loss Level value is lesser than 1 or greater than 7, it assigns to 1 or 7 respectively. This was made for compatibility with FreeRDP server.
334///
335/// # MSDN
336///
337/// * [NSCodec Capability Set](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpnsc/0eac0ba8-7bdd-4300-ab8d-9bc784c0a669)
338#[derive(Debug, PartialEq, Eq, Clone)]
339pub struct NsCodec {
340    pub is_dynamic_fidelity_allowed: bool,
341    pub is_subsampling_allowed: bool,
342    pub color_loss_level: u8,
343}
344
345impl NsCodec {
346    const NAME: &'static str = "NsCodec";
347
348    const FIXED_PART_SIZE: usize = NSCODEC_LENGTH;
349}
350
351impl Encode for NsCodec {
352    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
353        ensure_fixed_part_size!(in: dst);
354
355        dst.write_u8(u8::from(self.is_dynamic_fidelity_allowed));
356        dst.write_u8(u8::from(self.is_subsampling_allowed));
357        dst.write_u8(self.color_loss_level);
358
359        Ok(())
360    }
361
362    fn name(&self) -> &'static str {
363        Self::NAME
364    }
365
366    fn size(&self) -> usize {
367        Self::FIXED_PART_SIZE
368    }
369}
370
371impl<'de> Decode<'de> for NsCodec {
372    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
373        ensure_fixed_part_size!(in: src);
374
375        let is_dynamic_fidelity_allowed = src.read_u8() != 0;
376        let is_subsampling_allowed = src.read_u8() != 0;
377
378        let color_loss_level = src.read_u8().clamp(1, 7);
379
380        Ok(Self {
381            is_dynamic_fidelity_allowed,
382            is_subsampling_allowed,
383            color_loss_level,
384        })
385    }
386}
387
388#[derive(Debug, PartialEq, Eq, Clone)]
389pub struct RfxClientCapsContainer {
390    pub capture_flags: CaptureFlags,
391    pub caps_data: RfxCaps,
392}
393
394impl RfxClientCapsContainer {
395    const NAME: &'static str = "RfxClientCapsContainer";
396
397    const FIXED_PART_SIZE: usize = RFX_CLIENT_CAPS_CONTAINER_STATIC_DATA_LENGTH;
398}
399
400impl Encode for RfxClientCapsContainer {
401    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
402        ensure_size!(in: dst, size: self.size());
403
404        dst.write_u32(cast_length!("len", self.size())?);
405        dst.write_u32(self.capture_flags.bits());
406        dst.write_u32(cast_length!("capsLen", self.caps_data.size())?);
407        self.caps_data.encode(dst)?;
408
409        Ok(())
410    }
411
412    fn name(&self) -> &'static str {
413        Self::NAME
414    }
415
416    fn size(&self) -> usize {
417        Self::FIXED_PART_SIZE + self.caps_data.size()
418    }
419}
420
421impl<'de> Decode<'de> for RfxClientCapsContainer {
422    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
423        ensure_fixed_part_size!(in: src);
424
425        let _length = src.read_u32();
426        let capture_flags = CaptureFlags::from_bits_truncate(src.read_u32());
427        let _caps_length = src.read_u32();
428        let caps_data = RfxCaps::decode(src)?;
429
430        Ok(Self {
431            capture_flags,
432            caps_data,
433        })
434    }
435}
436
437#[derive(Debug, PartialEq, Eq, Clone)]
438pub struct RfxCaps(pub RfxCapset);
439
440impl RfxCaps {
441    const NAME: &'static str = "RfxCaps";
442
443    const FIXED_PART_SIZE: usize = RFX_CAPS_STATIC_DATA_LENGTH;
444}
445
446impl Encode for RfxCaps {
447    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
448        ensure_size!(in: dst, size: self.size());
449
450        dst.write_u16(RFX_CAPS_BLOCK_TYPE);
451        dst.write_u32(RFX_CAPS_BLOCK_LENGTH);
452        dst.write_u16(RFX_CAPS_NUM_CAPSETS);
453        self.0.encode(dst)?; // capsets data
454
455        Ok(())
456    }
457
458    fn name(&self) -> &'static str {
459        Self::NAME
460    }
461
462    fn size(&self) -> usize {
463        Self::FIXED_PART_SIZE + self.0.size()
464    }
465}
466
467impl<'de> Decode<'de> for RfxCaps {
468    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
469        ensure_fixed_part_size!(in: src);
470
471        let block_type = src.read_u16();
472        if block_type != RFX_CAPS_BLOCK_TYPE {
473            return Err(invalid_field_err!("blockType", "invalid rfx caps block type"));
474        }
475
476        let block_len = src.read_u32();
477        if block_len != RFX_CAPS_BLOCK_LENGTH {
478            return Err(invalid_field_err!("blockLen", "invalid rfx caps block length"));
479        }
480
481        let num_capsets = src.read_u16();
482        if num_capsets != RFX_CAPS_NUM_CAPSETS {
483            return Err(invalid_field_err!("numCapsets", "invalid rfx caps num capsets"));
484        }
485
486        let capsets_data = RfxCapset::decode(src)?;
487
488        Ok(RfxCaps(capsets_data))
489    }
490}
491
492#[derive(Debug, PartialEq, Eq, Clone)]
493pub struct RfxCapset(pub Vec<RfxICap>);
494
495impl RfxCapset {
496    const NAME: &'static str = "RfxCapset";
497
498    const FIXED_PART_SIZE: usize = RFX_CAPSET_STATIC_DATA_LENGTH;
499}
500
501impl Encode for RfxCapset {
502    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
503        ensure_size!(in: dst, size: self.size());
504
505        dst.write_u16(RFX_CAPSET_BLOCK_TYPE);
506        dst.write_u32(cast_length!(
507            "len",
508            RFX_CAPSET_STATIC_DATA_LENGTH + self.0.len() * RFX_ICAP_LENGTH
509        )?);
510        dst.write_u8(1); // codec id
511        dst.write_u16(RFX_CAPSET_TYPE);
512        dst.write_u16(cast_length!("len", self.0.len())?);
513        dst.write_u16(cast_length!("len", RFX_ICAP_LENGTH)?);
514
515        for rfx in self.0.iter() {
516            rfx.encode(dst)?;
517        }
518
519        Ok(())
520    }
521
522    fn name(&self) -> &'static str {
523        Self::NAME
524    }
525
526    fn size(&self) -> usize {
527        Self::FIXED_PART_SIZE + self.0.len() * RFX_ICAP_LENGTH
528    }
529}
530
531impl<'de> Decode<'de> for RfxCapset {
532    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
533        ensure_fixed_part_size!(in: src);
534
535        let block_type = src.read_u16();
536        if block_type != RFX_CAPSET_BLOCK_TYPE {
537            return Err(invalid_field_err!("blockType", "invalid rfx capset block type"));
538        }
539
540        let _block_len = src.read_u32();
541
542        let codec_id = src.read_u8();
543        if codec_id != 1 {
544            return Err(invalid_field_err!("codecId", "invalid rfx codec ID"));
545        }
546
547        let capset_type = src.read_u16();
548        if capset_type != RFX_CAPSET_TYPE {
549            return Err(invalid_field_err!("capsetType", "invalid rfx capset type"));
550        }
551
552        let num_icaps = src.read_u16();
553        let _icaps_len = src.read_u16();
554
555        let mut icaps_data = Vec::with_capacity(num_icaps as usize);
556        for _ in 0..num_icaps {
557            icaps_data.push(RfxICap::decode(src)?);
558        }
559
560        Ok(RfxCapset(icaps_data))
561    }
562}
563
564#[derive(Debug, PartialEq, Eq, Clone)]
565pub struct RfxICap {
566    pub flags: RfxICapFlags,
567    pub entropy_bits: EntropyBits,
568}
569
570impl RfxICap {
571    const NAME: &'static str = "RfxICap";
572
573    const FIXED_PART_SIZE: usize = RFX_ICAP_LENGTH;
574}
575
576impl Encode for RfxICap {
577    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
578        ensure_fixed_part_size!(in: dst);
579
580        dst.write_u16(RFX_ICAP_VERSION);
581        dst.write_u16(RFX_ICAP_TILE_SIZE);
582        dst.write_u8(self.flags.bits());
583        dst.write_u8(RFX_ICAP_COLOR_CONVERSION);
584        dst.write_u8(RFX_ICAP_TRANSFORM_BITS);
585        dst.write_u8(self.entropy_bits.to_u8().unwrap());
586
587        Ok(())
588    }
589
590    fn name(&self) -> &'static str {
591        Self::NAME
592    }
593
594    fn size(&self) -> usize {
595        Self::FIXED_PART_SIZE
596    }
597}
598
599impl<'de> Decode<'de> for RfxICap {
600    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
601        ensure_fixed_part_size!(in: src);
602
603        let version = src.read_u16();
604        if version != RFX_ICAP_VERSION {
605            return Err(invalid_field_err!("version", "invalid rfx icap version"));
606        }
607
608        let tile_size = src.read_u16();
609        if tile_size != RFX_ICAP_TILE_SIZE {
610            return Err(invalid_field_err!("tileSize", "invalid rfx icap tile size"));
611        }
612
613        let flags = RfxICapFlags::from_bits_truncate(src.read_u8());
614
615        let color_conversion = src.read_u8();
616        if color_conversion != RFX_ICAP_COLOR_CONVERSION {
617            return Err(invalid_field_err!("colorConv", "invalid rfx color conversion bits"));
618        }
619
620        let transform_bits = src.read_u8();
621        if transform_bits != RFX_ICAP_TRANSFORM_BITS {
622            return Err(invalid_field_err!("transformBits", "invalid rfx transform bits"));
623        }
624
625        let entropy_bits = EntropyBits::from_u8(src.read_u8())
626            .ok_or_else(|| invalid_field_err!("entropyBits", "invalid rfx entropy bits"))?;
627
628        Ok(RfxICap { flags, entropy_bits })
629    }
630}
631
632#[derive(PartialEq, Eq, Debug, FromPrimitive, ToPrimitive, Copy, Clone)]
633pub enum EntropyBits {
634    Rlgr1 = 1,
635    Rlgr3 = 4,
636}
637
638bitflags! {
639    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
640    pub struct CaptureFlags: u32 {
641        const CARDP_CAPS_CAPTURE_NON_CAC = 1;
642    }
643}
644
645bitflags! {
646    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
647    pub struct RfxICapFlags: u8 {
648        const CODEC_MODE = 2;
649    }
650}
651
652// Those IDs are hard-coded for practical reasons, they are implementation
653// details of the IronRDP client. The server should respect the client IDs.
654#[derive(Copy, Clone, PartialEq, Eq)]
655pub struct CodecId(u8);
656
657pub const CODEC_ID_NONE: CodecId = CodecId(0);
658pub const CODEC_ID_REMOTEFX: CodecId = CodecId(3);
659pub const CODEC_ID_QOI: CodecId = CodecId(0x0A);
660pub const CODEC_ID_QOIZ: CodecId = CodecId(0x0B);
661
662impl Debug for CodecId {
663    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
664        let name = match self.0 {
665            0 => "None",
666            3 => "RemoteFx",
667            0x0A => "QOI",
668            0x0B => "QOIZ",
669            _ => "unknown",
670        };
671        write!(f, "CodecId({name})")
672    }
673}
674
675impl CodecId {
676    pub const fn from_u8(value: u8) -> Option<Self> {
677        match value {
678            0 => Some(CODEC_ID_NONE),
679            3 => Some(CODEC_ID_REMOTEFX),
680            0x0A => Some(CODEC_ID_QOI),
681            0x0B => Some(CODEC_ID_QOIZ),
682            _ => None,
683        }
684    }
685}
686
687fn parse_codecs_config<'a>(codecs: &'a [&'a str]) -> Result<HashMap<&'a str, bool>, String> {
688    let mut result = HashMap::new();
689
690    for &codec_str in codecs {
691        if let Some(colon_index) = codec_str.find(':') {
692            let codec_name = &codec_str[0..colon_index];
693            let state_str = &codec_str[colon_index + 1..];
694
695            let state = match state_str {
696                "on" => true,
697                "off" => false,
698                _ => return Err(format!("Unhandled configuration: {state_str}")),
699            };
700
701            result.insert(codec_name, state);
702        } else {
703            // No colon found, assume it's "on"
704            result.insert(codec_str, true);
705        }
706    }
707
708    Ok(result)
709}
710
711/// This function generates a list of client codec capabilities based on the
712/// provided configuration.
713///
714/// # Arguments
715///
716/// * `config` - A slice of string slices that specifies which codecs to include
717///   in the capabilities. Codecs can be explicitly turned on ("codec:on") or
718///   off ("codec:off").
719///
720/// # List of codecs
721///
722/// * `remotefx` (on by default)
723/// * `qoi` (on by default, when feature "qoi")
724/// * `qoiz` (on by default, when feature "qoiz")
725///
726/// # Returns
727///
728/// A vector of `Codec` structs representing the codec capabilities, or an error
729/// suitable for CLI.
730pub fn client_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, String> {
731    if config.contains(&"help") {
732        return Err(r#"
733List of codecs:
734- `remotefx` (on by default)
735- `qoi` (on by default, when feature "qoi")
736- `qoiz` (on by default, when feature "qoiz")
737"#
738        .to_owned());
739    }
740
741    let mut config = parse_codecs_config(config)?;
742    let mut codecs = vec![];
743
744    if config.remove("remotefx").unwrap_or(true) {
745        codecs.push(Codec {
746            id: CODEC_ID_REMOTEFX.0,
747            property: CodecProperty::RemoteFx(RemoteFxContainer::ClientContainer(RfxClientCapsContainer {
748                capture_flags: CaptureFlags::empty(),
749                caps_data: RfxCaps(RfxCapset(vec![RfxICap {
750                    flags: RfxICapFlags::empty(),
751                    entropy_bits: EntropyBits::Rlgr3,
752                }])),
753            })),
754        });
755    }
756
757    #[cfg(feature = "qoi")]
758    if config.remove("qoi").unwrap_or(true) {
759        codecs.push(Codec {
760            id: CODEC_ID_QOI.0,
761            property: CodecProperty::Qoi,
762        });
763    }
764
765    #[cfg(feature = "qoiz")]
766    if config.remove("qoiz").unwrap_or(true) {
767        codecs.push(Codec {
768            id: CODEC_ID_QOIZ.0,
769            property: CodecProperty::QoiZ,
770        });
771    }
772
773    let codec_names = config.keys().copied().collect::<Vec<_>>().join(", ");
774    if !codec_names.is_empty() {
775        return Err(format!("Unknown codecs: {codec_names}"));
776    }
777
778    Ok(BitmapCodecs(codecs))
779}
780
781/// This function generates a list of server codec capabilities based on the
782/// provided configuration.
783///
784/// # Arguments
785///
786/// * `config` - A slice of string slices that specifies which codecs to include
787///   in the capabilities. Codecs can be explicitly turned on ("codec:on") or
788///   off ("codec:off").
789///
790/// # List of codecs
791///
792/// * `remotefx` (on by default)
793/// * `qoi` (on by default, when feature "qoi")
794/// * `qoiz` (on by default, when feature "qoiz")
795///
796/// # Returns
797///
798/// A vector of `Codec` structs representing the codec capabilities, or an help message suitable
799/// for CLI errors.
800pub fn server_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, String> {
801    if config.contains(&"help") {
802        return Err(r#"
803List of codecs:
804- `remotefx` (on by default)
805- `qoi` (on by default, when feature "qoi")
806- `qoiz` (on by default, when feature "qoiz")
807"#
808        .to_owned());
809    }
810
811    let mut config = parse_codecs_config(config)?;
812    let mut codecs = vec![];
813
814    if config.remove("remotefx").unwrap_or(true) {
815        codecs.push(Codec {
816            id: 0,
817            property: CodecProperty::RemoteFx(RemoteFxContainer::ServerContainer(1)),
818        });
819        codecs.push(Codec {
820            id: 0,
821            property: CodecProperty::ImageRemoteFx(RemoteFxContainer::ServerContainer(1)),
822        });
823    }
824
825    #[cfg(feature = "qoi")]
826    if config.remove("qoi").unwrap_or(true) {
827        codecs.push(Codec {
828            id: 0,
829            property: CodecProperty::Qoi,
830        });
831    }
832
833    #[cfg(feature = "qoiz")]
834    if config.remove("qoiz").unwrap_or(true) {
835        codecs.push(Codec {
836            id: 0,
837            property: CodecProperty::QoiZ,
838        });
839    }
840
841    let codec_names = config.keys().copied().collect::<Vec<_>>().join(", ");
842    if !codec_names.is_empty() {
843        return Err(format!("Unknown codecs: {codec_names}"));
844    }
845
846    Ok(BitmapCodecs(codecs))
847}