1use core::iter;
2
3use bit_field::BitField as _;
4use bitflags::bitflags;
5use ironrdp_core::{
6 cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult,
7 ReadCursor, WriteCursor,
8};
9use num_derive::FromPrimitive;
10use num_traits::FromPrimitive as _;
11
12use crate::codecs::rfx::Block;
13
14const CONTEXT_ID: u8 = 0;
15const TILE_SIZE: u16 = 0x0040;
16const COLOR_CONVERSION_ICT: u16 = 1;
17const CLW_XFORM_DWT_53_A: u16 = 1;
18const SCALAR_QUANTIZATION: u16 = 1;
19const LRF: bool = true;
20const CBT_REGION: u16 = 0xcac1;
21const NUMBER_OF_TILESETS: u16 = 1;
22const CBT_TILESET: u16 = 0xcac2;
23const IDX: u16 = 0;
24const IS_LAST_TILESET_FLAG: bool = true;
25const RECTANGLE_SIZE: usize = 8;
26
27#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct ContextPdu {
32 pub flags: OperatingMode,
33 pub entropy_algorithm: EntropyAlgorithm,
34}
35
36impl ContextPdu {
37 const NAME: &'static str = "RfxContext";
38
39 const FIXED_PART_SIZE: usize = 1 + 2 + 2 ;
40}
41
42impl Encode for ContextPdu {
43 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
44 ensure_fixed_part_size!(in: dst);
45
46 dst.write_u8(CONTEXT_ID);
47 dst.write_u16(TILE_SIZE);
48
49 let mut properties: u16 = 0;
50 properties.set_bits(0..3, self.flags.bits());
51 properties.set_bits(3..5, COLOR_CONVERSION_ICT);
52 properties.set_bits(5..9, CLW_XFORM_DWT_53_A);
53 properties.set_bits(9..13, self.entropy_algorithm.as_u16());
54 properties.set_bits(13..15, SCALAR_QUANTIZATION);
55 properties.set_bit(15, false); dst.write_u16(properties);
57
58 Ok(())
59 }
60
61 fn name(&self) -> &'static str {
62 Self::NAME
63 }
64
65 fn size(&self) -> usize {
66 Self::FIXED_PART_SIZE
67 }
68}
69
70impl<'de> Decode<'de> for ContextPdu {
71 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
72 ensure_fixed_part_size!(in: src);
73
74 let id = src.read_u8();
75 if id != CONTEXT_ID {
76 return Err(invalid_field_err!("ctxId", "Invalid context ID"));
77 }
78
79 let tile_size = src.read_u16();
80 if tile_size != TILE_SIZE {
81 return Err(invalid_field_err!("tileSize", "Invalid tile size"));
82 }
83
84 let properties = src.read_u16();
85 let flags = OperatingMode::from_bits_truncate(properties.get_bits(0..3));
86 let color_conversion_transform = properties.get_bits(3..5);
87 if color_conversion_transform != COLOR_CONVERSION_ICT {
88 return Err(invalid_field_err!("cct", "Invalid color conversion transform"));
89 }
90
91 let dwt = properties.get_bits(5..9);
92 if dwt != CLW_XFORM_DWT_53_A {
93 return Err(invalid_field_err!("dwt", "Invalid DWT"));
94 }
95
96 let entropy_algorithm_bits = properties.get_bits(9..13);
97 let entropy_algorithm = EntropyAlgorithm::from_u16(entropy_algorithm_bits)
98 .ok_or_else(|| invalid_field_err!("entropy_algorithm", "Invalid entropy algorithm"))?;
99
100 let quantization_type = properties.get_bits(13..15);
101 if quantization_type != SCALAR_QUANTIZATION {
102 return Err(invalid_field_err!("qt", "Invalid quantization type"));
103 }
104
105 let _reserved = properties.get_bit(15);
106
107 Ok(Self {
108 flags,
109 entropy_algorithm,
110 })
111 }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq)]
118pub struct FrameBeginPdu {
119 pub index: u32,
120 pub number_of_regions: i16,
121}
122
123impl FrameBeginPdu {
124 const NAME: &'static str = "RfxFrameBegin";
125
126 const FIXED_PART_SIZE: usize = 4 + 2 ;
127}
128
129impl Encode for FrameBeginPdu {
130 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
131 ensure_fixed_part_size!(in: dst);
132
133 dst.write_u32(self.index);
134 dst.write_i16(self.number_of_regions);
135
136 Ok(())
137 }
138
139 fn name(&self) -> &'static str {
140 Self::NAME
141 }
142
143 fn size(&self) -> usize {
144 Self::FIXED_PART_SIZE
145 }
146}
147
148impl<'de> Decode<'de> for FrameBeginPdu {
149 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
150 ensure_fixed_part_size!(in: src);
151
152 let index = src.read_u32();
153 let number_of_regions = src.read_i16();
154
155 Ok(Self {
156 index,
157 number_of_regions,
158 })
159 }
160}
161
162#[derive(Debug, Clone, PartialEq, Eq)]
166pub struct FrameEndPdu;
167
168impl FrameEndPdu {
169 const NAME: &'static str = "RfxFrameEnd";
170
171 const FIXED_PART_SIZE: usize = 0;
172}
173
174impl Encode for FrameEndPdu {
175 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
176 ensure_fixed_part_size!(in: dst);
177
178 Ok(())
179 }
180
181 fn name(&self) -> &'static str {
182 Self::NAME
183 }
184
185 fn size(&self) -> usize {
186 Self::FIXED_PART_SIZE
187 }
188}
189
190impl<'de> Decode<'de> for FrameEndPdu {
191 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
192 ensure_fixed_part_size!(in: src);
193
194 Ok(Self)
195 }
196}
197
198#[derive(Debug, Clone, PartialEq, Eq)]
202pub struct RegionPdu {
203 pub rectangles: Vec<RfxRectangle>,
204}
205
206impl RegionPdu {
207 const NAME: &'static str = "RfxRegion";
208
209 const FIXED_PART_SIZE: usize = 1 + 2 + 2 + 2 ;
210}
211
212impl Encode for RegionPdu {
213 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
214 ensure_fixed_part_size!(in: dst);
215
216 let mut region_flags = 0;
217 region_flags.set_bit(0, LRF);
218 dst.write_u8(region_flags);
219
220 dst.write_u16(cast_length!("numRectangles", self.rectangles.len())?);
221 for rectangle in self.rectangles.iter() {
222 rectangle.encode(dst)?;
223 }
224
225 dst.write_u16(CBT_REGION);
226 dst.write_u16(NUMBER_OF_TILESETS);
227
228 Ok(())
229 }
230
231 fn name(&self) -> &'static str {
232 Self::NAME
233 }
234
235 fn size(&self) -> usize {
236 Self::FIXED_PART_SIZE + self.rectangles.len() * RECTANGLE_SIZE
237 }
238}
239
240impl<'de> Decode<'de> for RegionPdu {
241 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
242 ensure_fixed_part_size!(in: src);
243
244 let region_flags = src.read_u8();
245 let lrf = region_flags.get_bit(0);
246 if lrf != LRF {
247 return Err(invalid_field_err!("lrf", "Invalid lrf"));
248 }
249
250 let number_of_rectangles = usize::from(src.read_u16());
251
252 ensure_size!(in: src, size: number_of_rectangles * RECTANGLE_SIZE);
253
254 let rectangles = iter::repeat_with(|| RfxRectangle::decode(src))
255 .take(number_of_rectangles)
256 .collect::<Result<Vec<_>, _>>()?;
257
258 ensure_size!(in: src, size: 4);
259
260 let region_type = src.read_u16();
261 if region_type != CBT_REGION {
262 return Err(invalid_field_err!("regionType", "Invalid region type"));
263 }
264
265 let number_of_tilesets = src.read_u16();
266 if number_of_tilesets != NUMBER_OF_TILESETS {
267 return Err(invalid_field_err!("numTilesets", "Invalid number of tilesets"));
268 }
269
270 Ok(Self { rectangles })
271 }
272}
273
274#[derive(Debug, Clone, PartialEq, Eq)]
278pub struct TileSetPdu<'a> {
279 pub entropy_algorithm: EntropyAlgorithm,
280 pub quants: Vec<Quant>,
281 pub tiles: Vec<Tile<'a>>,
282}
283
284impl TileSetPdu<'_> {
285 const NAME: &'static str = "RfxTileSet";
286
287 const FIXED_PART_SIZE: usize = 2 + 2 + 2 + 1 + 1 + 2 + 4 ;
288}
289
290impl Encode for TileSetPdu<'_> {
291 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
292 ensure_fixed_part_size!(in: dst);
293
294 dst.write_u16(CBT_TILESET);
295 dst.write_u16(IDX);
296
297 let mut properties: u16 = 0;
298 properties.set_bit(0, IS_LAST_TILESET_FLAG);
299 properties.set_bits(1..4, OperatingMode::empty().bits()); properties.set_bits(4..6, COLOR_CONVERSION_ICT);
301 properties.set_bits(6..10, CLW_XFORM_DWT_53_A);
302 properties.set_bits(10..14, self.entropy_algorithm.as_u16());
303 properties.set_bits(14..16, SCALAR_QUANTIZATION);
304 dst.write_u16(properties);
305
306 dst.write_u8(cast_length!("numQuant", self.quants.len())?);
307 dst.write_u8(u8::try_from(TILE_SIZE).expect("TILE_SIZE value fits into u8"));
308 dst.write_u16(cast_length!("numTiles", self.tiles.len())?);
309
310 let tiles_data_size = self.tiles.iter().map(|t| Block::Tile(t.clone()).size()).sum::<usize>();
311 dst.write_u32(cast_length!("tilesDataSize", tiles_data_size)?);
312
313 for quant in &self.quants {
314 quant.encode(dst)?;
315 }
316
317 for tile in &self.tiles {
318 Block::Tile(tile.clone()).encode(dst)?;
319 }
320
321 Ok(())
322 }
323
324 fn name(&self) -> &'static str {
325 Self::NAME
326 }
327
328 fn size(&self) -> usize {
329 Self::FIXED_PART_SIZE
330 + self.quants.iter().map(Encode::size).sum::<usize>()
331 + self.tiles.iter().map(|t| Block::Tile(t.clone()).size()).sum::<usize>()
332 }
333}
334
335impl<'de> Decode<'de> for TileSetPdu<'de> {
336 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
337 ensure_fixed_part_size!(in: src);
338
339 let subtype = src.read_u16();
340 if subtype != CBT_TILESET {
341 return Err(invalid_field_err!("subtype", "Invalid message type"));
342 }
343
344 let id_of_context = src.read_u16();
345 if id_of_context != IDX {
346 return Err(invalid_field_err!("id_of_context", "Invalid RFX context"));
347 }
348
349 let properties = src.read_u16();
350 let is_last = properties.get_bit(0);
351 if is_last != IS_LAST_TILESET_FLAG {
352 return Err(invalid_field_err!("last", "Invalid last flag"));
353 }
354
355 let color_conversion_transform = properties.get_bits(4..6);
361 if color_conversion_transform != COLOR_CONVERSION_ICT {
362 return Err(invalid_field_err!("cct", "Invalid color conversion"));
363 }
364
365 let dwt = properties.get_bits(6..10);
366 if dwt != CLW_XFORM_DWT_53_A {
367 return Err(invalid_field_err!("xft", "Invalid DWT"));
368 }
369
370 let entropy_algorithm_bits = properties.get_bits(10..14);
371 let entropy_algorithm = EntropyAlgorithm::from_u16(entropy_algorithm_bits)
372 .ok_or_else(|| invalid_field_err!("entropy", "Invalid entropy algorithm"))?;
373
374 let quantization_type = properties.get_bits(14..16);
375 if quantization_type != SCALAR_QUANTIZATION {
376 return Err(invalid_field_err!("scalar", "Invalid quantization type"));
377 }
378
379 let number_of_quants = usize::from(src.read_u8());
380
381 let tile_size = u16::from(src.read_u8());
382 if tile_size != TILE_SIZE {
383 return Err(invalid_field_err!("tile_size", "Invalid tile size"));
384 }
385
386 let number_of_tiles = usize::from(src.read_u16());
387 let _tiles_data_size = src.read_u32();
388
389 let quants = iter::repeat_with(|| Quant::decode(src))
390 .take(number_of_quants)
391 .collect::<Result<Vec<_>, _>>()?;
392
393 let tiles = iter::repeat_with(|| Block::decode(src))
394 .take(number_of_tiles)
395 .collect::<Result<Vec<_>, _>>()?;
396
397 let tiles = tiles
398 .into_iter()
399 .map(|b| match b {
400 Block::Tile(tile) => Ok(tile),
401 _ => Err(invalid_field_err!("tile", "Invalid block type, expected Tile")),
402 })
403 .collect::<Result<Vec<_>, _>>()?;
404
405 Ok(Self {
406 entropy_algorithm,
407 quants,
408 tiles,
409 })
410 }
411}
412#[derive(Debug, Clone, PartialEq, Eq)]
416pub struct RfxRectangle {
417 pub x: u16,
418 pub y: u16,
419 pub width: u16,
420 pub height: u16,
421}
422
423impl RfxRectangle {
424 const NAME: &'static str = "RfxRectangle";
425
426 const FIXED_PART_SIZE: usize = 4 * 2 ;
427}
428
429impl Encode for RfxRectangle {
430 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
431 ensure_fixed_part_size!(in: dst);
432
433 dst.write_u16(self.x);
434 dst.write_u16(self.y);
435 dst.write_u16(self.width);
436 dst.write_u16(self.height);
437
438 Ok(())
439 }
440
441 fn name(&self) -> &'static str {
442 Self::NAME
443 }
444
445 fn size(&self) -> usize {
446 Self::FIXED_PART_SIZE
447 }
448}
449
450impl<'de> Decode<'de> for RfxRectangle {
451 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
452 ensure_fixed_part_size!(in: src);
453
454 let x = src.read_u16();
455 let y = src.read_u16();
456 let width = src.read_u16();
457 let height = src.read_u16();
458
459 Ok(Self { x, y, width, height })
460 }
461}
462
463#[derive(Debug, Clone, PartialEq, Eq)]
467pub struct Quant {
468 pub ll3: u8,
469 pub lh3: u8,
470 pub hl3: u8,
471 pub hh3: u8,
472 pub lh2: u8,
473 pub hl2: u8,
474 pub hh2: u8,
475 pub lh1: u8,
476 pub hl1: u8,
477 pub hh1: u8,
478}
479
480impl Default for Quant {
487 fn default() -> Self {
488 Self {
489 ll3: 6,
490 lh3: 6,
491 hl3: 6,
492 hh3: 6,
493 lh2: 7,
494 hl2: 7,
495 hh2: 8,
496 lh1: 8,
497 hl1: 8,
498 hh1: 9,
499 }
500 }
501}
502
503impl Quant {
504 const NAME: &'static str = "RfxFrameEnd";
505
506 const FIXED_PART_SIZE: usize = 5 ;
507}
508
509impl Encode for Quant {
510 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
511 ensure_fixed_part_size!(in: dst);
512
513 let mut level3 = 0;
514 level3.set_bits(0..4, u16::from(self.ll3));
515 level3.set_bits(4..8, u16::from(self.lh3));
516 level3.set_bits(8..12, u16::from(self.hl3));
517 level3.set_bits(12..16, u16::from(self.hh3));
518
519 let mut level2_with_lh1 = 0;
520 level2_with_lh1.set_bits(0..4, u16::from(self.lh2));
521 level2_with_lh1.set_bits(4..8, u16::from(self.hl2));
522 level2_with_lh1.set_bits(8..12, u16::from(self.hh2));
523 level2_with_lh1.set_bits(12..16, u16::from(self.lh1));
524
525 let mut level1 = 0;
526 level1.set_bits(0..4, self.hl1);
527 level1.set_bits(4..8, self.hh1);
528
529 dst.write_u16(level3);
530 dst.write_u16(level2_with_lh1);
531 dst.write_u8(level1);
532
533 Ok(())
534 }
535
536 fn name(&self) -> &'static str {
537 Self::NAME
538 }
539
540 fn size(&self) -> usize {
541 Self::FIXED_PART_SIZE
542 }
543}
544
545impl<'de> Decode<'de> for Quant {
546 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
547 #![allow(
548 clippy::similar_names,
549 reason = "it’s hard to do better than ll3, lh3, etc without going overly verbose"
550 )]
551
552 ensure_fixed_part_size!(in: src);
553
554 let ll3lh3 = src.read_u8();
555 let ll3 = ll3lh3.get_bits(0..4);
556 let lh3 = ll3lh3.get_bits(4..8);
557
558 let hl3hh3 = src.read_u8();
559 let hl3 = hl3hh3.get_bits(0..4);
560 let hh3 = hl3hh3.get_bits(4..8);
561
562 let lh2hl2 = src.read_u8();
563 let lh2 = lh2hl2.get_bits(0..4);
564 let hl2 = lh2hl2.get_bits(4..8);
565
566 let hh2lh1 = src.read_u8();
567 let hh2 = hh2lh1.get_bits(0..4);
568 let lh1 = hh2lh1.get_bits(4..8);
569
570 let hl1hh1 = src.read_u8();
571 let hl1 = hl1hh1.get_bits(0..4);
572 let hh1 = hl1hh1.get_bits(4..8);
573
574 Ok(Self {
575 ll3,
576 lh3,
577 hl3,
578 hh3,
579 lh2,
580 hl2,
581 hh2,
582 lh1,
583 hl1,
584 hh1,
585 })
586 }
587}
588#[derive(Debug, Clone, PartialEq, Eq)]
592pub struct Tile<'a> {
593 pub y_quant_index: u8,
594 pub cb_quant_index: u8,
595 pub cr_quant_index: u8,
596
597 pub x: u16,
598 pub y: u16,
599
600 pub y_data: &'a [u8],
601 pub cb_data: &'a [u8],
602 pub cr_data: &'a [u8],
603}
604
605impl Tile<'_> {
606 const NAME: &'static str = "RfxTile";
607
608 const FIXED_PART_SIZE: usize = 1 + 1 + 1 + 2 + 2 + 2 + 2 + 2 ;
609}
610
611impl Encode for Tile<'_> {
612 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
613 ensure_size!(in: dst, size: self.size());
614
615 dst.write_u8(self.y_quant_index);
616 dst.write_u8(self.cb_quant_index);
617 dst.write_u8(self.cr_quant_index);
618
619 dst.write_u16(self.x);
620 dst.write_u16(self.y);
621
622 dst.write_u16(cast_length!("YLen", self.y_data.len())?);
623 dst.write_u16(cast_length!("CbLen", self.cb_data.len())?);
624 dst.write_u16(cast_length!("CrLen", self.cr_data.len())?);
625
626 dst.write_slice(self.y_data);
627 dst.write_slice(self.cb_data);
628 dst.write_slice(self.cr_data);
629
630 Ok(())
631 }
632
633 fn name(&self) -> &'static str {
634 Self::NAME
635 }
636
637 fn size(&self) -> usize {
638 Self::FIXED_PART_SIZE + self.y_data.len() + self.cb_data.len() + self.cr_data.len()
639 }
640}
641
642impl<'de> Decode<'de> for Tile<'de> {
643 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
644 #![allow(clippy::similar_names)] ensure_fixed_part_size!(in: src);
646
647 let y_quant_index = src.read_u8();
648 let cb_quant_index = src.read_u8();
649 let cr_quant_index = src.read_u8();
650
651 let x = src.read_u16();
652 let y = src.read_u16();
653
654 let y_component_length = usize::from(src.read_u16());
655 let cb_component_length = usize::from(src.read_u16());
656 let cr_component_length = usize::from(src.read_u16());
657
658 ensure_size!(in: src, size: y_component_length + cb_component_length + cr_component_length);
659
660 let y_data = src.read_slice(y_component_length);
661 let cb_data = src.read_slice(cb_component_length);
662 let cr_data = src.read_slice(cr_component_length);
663
664 Ok(Self {
665 y_quant_index,
666 cb_quant_index,
667 cr_quant_index,
668
669 x,
670 y,
671
672 y_data,
673 cb_data,
674 cr_data,
675 })
676 }
677}
678
679#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive)]
680#[repr(u16)]
681pub enum EntropyAlgorithm {
682 Rlgr1 = 0x01,
683 Rlgr3 = 0x04,
684}
685
686impl EntropyAlgorithm {
687 #[expect(
688 clippy::as_conversions,
689 reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
690 )]
691 fn as_u16(self) -> u16 {
692 self as u16
693 }
694}
695
696bitflags! {
697 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
698 pub struct OperatingMode: u16 {
699 const IMAGE_MODE = 0x02; }
701}