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 ;
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#[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)?; 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); 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#[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 result.insert(codec_str, true);
705 }
706 }
707
708 Ok(result)
709}
710
711pub 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
781pub 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}