Skip to main content

car/model/
rendition.rs

1use std::io::{Read, Seek, SeekFrom};
2
3use bom::{BOMBlock, BOMEror, BOMResult};
4use deku::{ctx::Order, prelude::*, reader::Reader};
5
6use super::ColorSpace;
7use crate::PayloadBytes;
8
9#[derive(Debug, DekuRead)]
10#[deku(magic = b"tmfk", endian = "little")]
11pub struct KeyFmt {
12    pub version: u32,
13    pub max_count: u32,
14    #[deku(reader = "KeyFmt::read_attr_types(deku::reader, *max_count)")]
15    pub attribute_types: Vec<AttributeType>,
16}
17
18impl KeyFmt {
19    fn read_attr_types<R: Read + Seek>(
20        reader: &mut Reader<R>,
21        count: u32,
22    ) -> Result<Vec<AttributeType>, DekuError> {
23        let mut attr_types: Vec<AttributeType> = vec![];
24        for _ in 0..count {
25            attr_types.push(AttributeType::from_reader_with_ctx(
26                reader,
27                deku::ctx::Endian::Little,
28            )?);
29            reader.seek(SeekFrom::Current(2))?;
30        }
31        Ok(attr_types)
32    }
33}
34
35#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, DekuRead)]
36#[deku(endian = "little")]
37pub struct KeyToken {
38    pub x: u16,
39    pub y: u16,
40    count: u16,
41    #[deku(count = "count")]
42    pub attrs: Vec<Attribute>,
43}
44
45#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, DekuRead)]
46#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
47pub struct Attribute {
48    pub name: AttributeType,
49    pub val: u16,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, DekuRead)]
53#[deku(id_type = "u16", endian = "little", ctx = "endian: deku::ctx::Endian")]
54pub enum AttributeType {
55    #[deku(id = 0)]
56    ThemeLook,
57    #[deku(id = 1)]
58    Element,
59    #[deku(id = 2)]
60    Part,
61    #[deku(id = 3)]
62    Size,
63    #[deku(id = 4)]
64    Direction,
65    #[deku(id = 5)]
66    Placeholder,
67    #[deku(id = 6)]
68    Value,
69    #[deku(id = 7)]
70    ThemeAppearance,
71    #[deku(id = 8)]
72    Dimension1,
73    #[deku(id = 9)]
74    Dimension2,
75    #[deku(id = 10)]
76    State,
77    #[deku(id = 11)]
78    Layer,
79    #[deku(id = 12)]
80    Scale,
81    #[deku(id = 13)]
82    Localization,
83    #[deku(id = 14)]
84    PresentationState,
85    #[deku(id = 15)]
86    Idiom,
87    #[deku(id = 16)]
88    Subtype,
89    #[deku(id = 17)]
90    Identifier,
91    #[deku(id = 18)]
92    PreviousValue,
93    #[deku(id = 19)]
94    PreviousState,
95    #[deku(id = 20)]
96    HorizontalSizeClass,
97    #[deku(id = 21)]
98    VerticalSizeClass,
99    #[deku(id = 22)]
100    MemoryLevelClass,
101    #[deku(id = 23)]
102    GraphicsFeatureSetClass,
103    #[deku(id = 24)]
104    DisplayGamut,
105    #[deku(id = 25)]
106    DeploymentTarget,
107    #[deku(id = 26)]
108    GlyphWeight,
109    #[deku(id = 27)]
110    GlyphSize,
111
112    #[deku(id_pat = "_")]
113    Unknown { tag: u16 },
114}
115
116#[derive(Debug, Clone)]
117pub enum Rendition {
118    Color(RenditionColor),
119    RawData(RenditionRawData),
120    ThemeCBCK(RenditionThemeCBCK),
121    MultisizeImageSet(RenditionMultisizeImageSet),
122    Unknown {
123        tag: [u8; 4],
124        data: RenditionUnknown,
125    },
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub enum RenditionKind {
130    None,
131    Color,
132    RawData,
133    ThemeCBCK,
134    MultisizeImageSet,
135    Unknown,
136}
137
138#[derive(Debug, Clone, DekuRead)]
139#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
140pub struct RenditionColor {
141    pub version: u32,
142    pub color_space: ColorSpace,
143    _component_count: u32,
144    #[deku(count = "_component_count")]
145    pub components: Vec<f64>,
146}
147
148#[derive(Debug, Clone)]
149pub struct RenditionRawData {
150    pub version: u32,
151    _raw_data_length: u32,
152    pub raw_data: PayloadBytes,
153}
154
155#[derive(Debug, Clone)]
156pub struct RenditionThemeCBCK {
157    pub version: u32,
158    pub compression_type: CompressionType,
159    _raw_data_length: u32,
160    pub chunks: Vec<ThemeChunk>,
161}
162
163impl RenditionThemeCBCK {
164    pub fn raw_datas(&self) -> impl Iterator<Item = &[u8]> {
165        self.chunks.iter().map(|chunk| chunk.raw_data.as_slice())
166    }
167}
168
169#[derive(Debug, Clone, DekuRead)]
170#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
171pub struct RenditionMultisizeImageSet {
172    pub version: u32,
173    _count: u32,
174    #[deku(count = "_count")]
175    pub entries: Vec<MultisizeImageSetEntry>,
176}
177
178#[derive(Debug, Clone)]
179pub struct RenditionUnknown {
180    // pub tag: [u8; 4],
181    pub version: u32,
182    _raw_data_length: u32,
183    pub raw_data: PayloadBytes,
184}
185
186#[derive(Debug, Clone, DekuRead)]
187#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
188pub struct CBCK {
189    pub idk: u32,
190    pub tag: u32,
191    pub a: u32,
192    pub b: u32,
193}
194
195#[derive(Debug, Clone, DekuRead)]
196#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
197pub struct MultisizeImageSetEntry {
198    pub width: u32,
199    pub height: u32,
200    pub index: u16,
201    pub idiom: Idiom,
202}
203
204#[derive(Debug, Clone, DekuRead)]
205#[deku(endian = "little")]
206pub struct ThemePartHeader {
207    pub tag: [u8; 4],
208    pub arg2: u32,
209    pub arg3: u32,
210    pub height: u32,
211    pub raw_len: u32,
212}
213
214#[derive(Debug, Clone)]
215pub struct ThemeChunk {
216    pub part_header: Option<ThemePartHeader>,
217    pub raw_data: PayloadBytes,
218}
219
220pub(crate) fn parse_rendition_payload(payload: PayloadBytes) -> BOMResult<Rendition> {
221    let mut reader = Reader::new(BOMBlock::new(payload));
222    let mut tag = [0_u8; 4];
223    reader.read_bytes(4, &mut tag, Order::default())?;
224
225    match &tag {
226        b"RLOC" => {
227            let data =
228                RenditionColor::from_reader_with_ctx(&mut reader, deku::ctx::Endian::Little)?;
229            Ok(Rendition::Color(data))
230        }
231        b"DWAR" => {
232            let version = read_u32(&mut reader)?;
233            let raw_data_length = read_u32(&mut reader)?;
234            let raw_data = payload_slice(&mut reader, raw_data_length)?;
235            Ok(Rendition::RawData(RenditionRawData {
236                version,
237                _raw_data_length: raw_data_length,
238                raw_data,
239            }))
240        }
241        b"MLEC" => {
242            let version = read_u32(&mut reader)?;
243            let compression_type =
244                CompressionType::from_reader_with_ctx(&mut reader, deku::ctx::Endian::Little)?;
245            let raw_data_length = read_u32(&mut reader)?;
246            let chunks = read_theme_chunks(&mut reader, version, raw_data_length)?;
247            Ok(Rendition::ThemeCBCK(RenditionThemeCBCK {
248                version,
249                compression_type,
250                _raw_data_length: raw_data_length,
251                chunks,
252            }))
253        }
254        b"SISM" => {
255            let data = RenditionMultisizeImageSet::from_reader_with_ctx(
256                &mut reader,
257                deku::ctx::Endian::Little,
258            )?;
259            Ok(Rendition::MultisizeImageSet(data))
260        }
261        _ => {
262            let version = read_u32(&mut reader)?;
263            let raw_data_length = read_u32(&mut reader)?;
264            let raw_data = payload_slice(&mut reader, raw_data_length)?;
265            Ok(Rendition::Unknown {
266                tag,
267                data: RenditionUnknown {
268                    version,
269                    _raw_data_length: raw_data_length,
270                    raw_data,
271                },
272            })
273        }
274    }
275}
276
277fn read_u32(reader: &mut Reader<BOMBlock>) -> Result<u32, DekuError> {
278    u32::from_reader_with_ctx(reader, deku::ctx::Endian::Little)
279}
280
281fn payload_slice(reader: &mut Reader<BOMBlock>, len: u32) -> BOMResult<PayloadBytes> {
282    let len = len as usize;
283    let slice = reader.as_mut().slice_at_current(len)?;
284    reader.bits_read = reader.bits_read.saturating_add(len.saturating_mul(8));
285    reader.leftover = None;
286    Ok(slice)
287}
288
289fn read_theme_chunks(
290    reader: &mut Reader<BOMBlock>,
291    version: u32,
292    raw_len: u32,
293) -> BOMResult<Vec<ThemeChunk>> {
294    match version {
295        0 | 2 => {
296            let raw_data = payload_slice(reader, raw_len)?;
297            Ok(vec![ThemeChunk {
298                part_header: None,
299                raw_data,
300            }])
301        }
302        1 | 3 => {
303            let mut result = vec![];
304            for _ in 0..raw_len {
305                let part_header = ThemePartHeader::from_reader_with_ctx(reader, ())?;
306                let raw_data = payload_slice(reader, part_header.raw_len)?;
307                result.push(ThemeChunk {
308                    part_header: Some(part_header),
309                    raw_data,
310                });
311            }
312            Ok(result)
313        }
314        _ => Err(BOMEror::ParseStruct(DekuError::Parse(
315            format!("Not support version {}", version).into(),
316        ))),
317    }
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead)]
321#[deku(id_type = "u32", endian = "little", ctx = "endian: deku::ctx::Endian")]
322pub enum CompressionType {
323    #[deku(id = 0)]
324    Uncompressed,
325    #[deku(id = 1)]
326    Rle,
327    #[deku(id = 2)]
328    Zip,
329    #[deku(id = 3)]
330    Lzvn,
331    #[deku(id = 4)]
332    Lzfse,
333    #[deku(id = 5)]
334    JpegLzfse,
335    #[deku(id = 6)]
336    Blurred,
337    #[deku(id = 7)]
338    Astc,
339    #[deku(id = 8)]
340    PaletteImg,
341    #[deku(id = 9)]
342    HEVC, // ?
343    #[deku(id = 10)]
344    DeepmapLzfse,
345    // Unknow,
346    #[deku(id = 11)]
347    Deepmap2,
348
349    #[deku(id_pat = "_")]
350    Unknown { tag: u32 },
351}
352
353#[derive(Debug, Clone, DekuRead)]
354#[deku(id_type = "u16", endian = "little", ctx = "endian: deku::ctx::Endian")]
355pub enum Idiom {
356    #[deku(id = 0)]
357    Universal,
358    #[deku(id = 1)]
359    Phone,
360    #[deku(id = 2)]
361    Pad,
362    #[deku(id = 3)]
363    TV,
364    #[deku(id = 4)]
365    Car,
366    #[deku(id = 5)]
367    Watch,
368    #[deku(id = 6)]
369    Marketing,
370
371    #[deku(id_pat = "_")]
372    Unknown { tag: u16 },
373}
374
375#[derive(Debug, DekuRead)]
376#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
377pub struct BitmapList {
378    pub bitmap_count: u32, // usually 1?
379    pub zero: u32,         // usually 0?
380    pub rendition_length: u32,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead)]
384#[deku(id_type = "u32", endian = "little", ctx = "endian: deku::ctx::Endian")]
385pub enum LayoutType {
386    #[deku(id = 6)]
387    Gradient,
388    #[deku(id = 7)]
389    Effect,
390    #[deku(id = 9)]
391    Vector,
392    #[deku(id = 10)]
393    OnePartFixedSize,
394    #[deku(id = 11)]
395    OnePartTile,
396    #[deku(id = 12)]
397    OnePartScale,
398    #[deku(id = 20)]
399    ThreePartHorizontalTile,
400    #[deku(id = 21)]
401    ThreePartHorizontalScale,
402    #[deku(id = 22)]
403    ThreePartHorizontalUniform,
404    #[deku(id = 23)]
405    ThreePartVerticalTile,
406    #[deku(id = 24)]
407    ThreePartVerticalScale,
408    #[deku(id = 25)]
409    ThreePartVerticalUniform,
410    #[deku(id = 30)]
411    NinePartTile,
412    #[deku(id = 31)]
413    NinePartScale,
414    #[deku(id = 32)]
415    NinePartHorizontalUniformVerticalScale,
416    #[deku(id = 33)]
417    NinePartHorizontalScaleVerticalUniform,
418    #[deku(id = 34)]
419    NinePartEdgesOnly,
420    #[deku(id = 40)]
421    SixPart,
422    #[deku(id = 50)]
423    AnimationFilmstrip,
424
425    #[deku(id = 1000)]
426    Data,
427    #[deku(id = 1001)]
428    ExternalLink,
429    #[deku(id = 1002)]
430    LayerStack,
431    #[deku(id = 1003)]
432    InternalReference,
433    #[deku(id = 1004)]
434    PackedImage,
435    #[deku(id = 1005)]
436    NameList,
437    #[deku(id = 1006)]
438    UnknownAddObject,
439    #[deku(id = 1007)]
440    Texture,
441    #[deku(id = 1008)]
442    TextureImage,
443    #[deku(id = 1009)]
444    Color,
445    #[deku(id = 1010)]
446    MultisizeImage,
447    #[deku(id = 1012)]
448    LayerReference,
449    #[deku(id = 1013)]
450    ContentRendition,
451    #[deku(id = 1014)]
452    RecognitionObject,
453
454    #[deku(id_pat = "_")]
455    Unknown { tag: u32 },
456}
457
458#[derive(Debug, DekuRead)]
459#[deku(
460    endian = "little",
461    bit_order = "lsb",
462    ctx = "endian: deku::ctx::Endian"
463)]
464pub struct Flags {
465    #[deku(bits = 1)]
466    pub is_header_flagged_fpo: bool,
467    #[deku(bits = 1)]
468    pub is_excluded_from_contrast_filter: bool,
469    #[deku(bits = 1)]
470    pub is_vector_based: bool,
471    #[deku(bits = 1)]
472    pub is_opaque: bool,
473    #[deku(bits = 4)]
474    pub bitmap_encoding: u8,
475    #[deku(bits = 1)]
476    pub opt_out_of_thinning: bool,
477    #[deku(bits = 1)]
478    pub is_flippable: bool,
479    #[deku(bits = 1)]
480    pub is_tintable: bool,
481    #[deku(bits = 1)]
482    pub preserved_vector_representation: bool,
483    #[deku(bits = 20)]
484    _reserved: u32,
485}
486
487#[derive(Debug, DekuRead)]
488#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
489pub struct Slice {
490    pub x: u32,
491    pub y: u32,
492    pub width: u32,
493    pub height: u32,
494}
495
496#[derive(Debug, DekuRead)]
497#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
498pub struct Metric {
499    pub width: u32,
500    pub height: u32,
501}
502
503#[derive(Debug, DekuRead)]
504#[deku(id_type = "u32", endian = "little", ctx = "endian: deku::ctx::Endian")]
505pub enum RenditionType {
506    #[deku(id = 1001)]
507    Slices(RenditionTypeSlice),
508    #[deku(id = 1003)]
509    Metrics(RenditionTypeMetric),
510    #[deku(id = 1004)]
511    BlendModeAndOpacity(RenditionTypeBlendModeAndOpacity),
512    #[deku(id = 1005)]
513    UTI(RenditionTypeUTI),
514    #[deku(id = 1006)]
515    EXIFOrientation(RenditionTypeEXIFOrientation),
516    #[deku(id = 1007)]
517    BytesPerRow { length: u32, bytes_per_row: u32 },
518    #[deku(id = 1010)]
519    Reference(RenditionTypeReference),
520    #[deku(id_pat = "_")]
521    Unknown {
522        tag: u32,
523        data: RenditionTypeUnknown,
524    },
525}
526
527#[derive(Debug, DekuRead)]
528#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
529pub struct RenditionTypeSlice {
530    _length: u32,
531    _count: u32,
532    #[deku(count = "_count")]
533    pub data: Vec<Slice>,
534}
535
536#[derive(Debug, DekuRead)]
537#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
538pub struct RenditionTypeMetric {
539    _length: u32,
540    _count: u32,
541    pub top_right_inset: Metric,
542    pub bottom_left_inset: Metric,
543    pub image_size: Metric,
544}
545
546#[derive(Debug, DekuRead)]
547#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
548pub struct RenditionTypeBlendModeAndOpacity {
549    _length: u32,
550    pub blend_mode: u32,
551    pub opacity: f32,
552}
553
554#[derive(Debug, DekuRead)]
555#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
556pub struct RenditionTypeUTI {
557    _length: u32,
558    pub string_length: u32,
559    pub padding: u32,
560    #[deku(count = "string_length")]
561    pub string: Vec<u8>,
562}
563
564#[derive(Debug, DekuRead)]
565#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
566pub struct RenditionTypeEXIFOrientation {
567    _length: u32,
568    pub orientation: EXIFOrientationValue,
569}
570
571#[derive(Debug, DekuRead)]
572#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
573pub struct RenditionTypeBytesPerRow {
574    _length: u32,
575    pub bytes_per_row: u32,
576}
577
578#[derive(Debug, DekuRead)]
579#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
580pub struct RenditionTypeReference {
581    _length: u32,
582    _magic: [u8; 4], // INLK
583    _padding: u32,   // always 0?
584    pub x: u32,
585    pub y: u32,
586    pub width: u32,
587    pub height: u32,
588    pub layout: u16, // since rendition header says internal link
589    _key_length: u32,
590    #[deku(count = "_key_length")]
591    pub keys: Vec<u8>, // rendition containing data
592}
593#[derive(Debug, DekuRead)]
594#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
595pub struct RenditionTypeUnknown {
596    _length: u32,
597    #[deku(count = "_length")]
598    pub data: Vec<u8>,
599}
600
601#[derive(Debug, DekuRead)]
602#[deku(id_type = "u32", endian = "little", ctx = "endian: deku::ctx::Endian")]
603pub enum EXIFOrientationValue {
604    #[deku(id = 0)]
605    None,
606    #[deku(id = 1)]
607    Normal,
608    #[deku(id = 2)]
609    Mirrored,
610    #[deku(id = 3)]
611    Rotated180,
612    #[deku(id = 4)]
613    Rotated180Mirrored,
614    #[deku(id = 5)]
615    Rotated90,
616    #[deku(id = 6)]
617    Rotated90Mirrored,
618    #[deku(id = 7)]
619    Rotated270,
620    #[deku(id = 8)]
621    Rotated270Mirrored,
622
623    #[deku(id_pat = "_")]
624    Unknown { tag: u32 },
625}
626
627#[cfg(test)]
628mod tests {
629    use std::io::Cursor;
630
631    use super::*;
632
633    fn parse_flags(raw: u32) -> Flags {
634        let mut reader = Reader::new(Cursor::new(raw.to_le_bytes()));
635        Flags::from_reader_with_ctx(&mut reader, deku::ctx::Endian::Little)
636            .expect("parse CSI flags")
637    }
638
639    #[test]
640    fn flags_decode_each_bit_at_its_little_endian_position() {
641        let patterns = (0..32)
642            .map(|bit| 1u32 << bit)
643            .chain([0, u32::MAX, 0x1234_5678]);
644
645        for raw in patterns {
646            let flags = parse_flags(raw);
647            assert_eq!(
648                [
649                    flags.is_header_flagged_fpo,
650                    flags.is_excluded_from_contrast_filter,
651                    flags.is_vector_based,
652                    flags.is_opaque,
653                    flags.opt_out_of_thinning,
654                    flags.is_flippable,
655                    flags.is_tintable,
656                    flags.preserved_vector_representation,
657                ],
658                [0, 1, 2, 3, 8, 9, 10, 11].map(|bit| raw & (1 << bit) != 0),
659                "boolean flags for {raw:#010x}"
660            );
661            assert_eq!(flags.bitmap_encoding, ((raw >> 4) & 0xf) as u8);
662            assert_eq!(flags._reserved, raw >> 12);
663        }
664    }
665
666    #[test]
667    fn flags_preserve_every_bitmap_encoding_value() {
668        for encoding in 0u8..16 {
669            let flags = parse_flags(0x0f0f | (u32::from(encoding) << 4));
670            assert_eq!(flags.bitmap_encoding, encoding);
671        }
672    }
673
674    #[test]
675    fn flags_consume_exactly_four_bytes() {
676        let mut bytes = 0x1234_5678u32.to_le_bytes().to_vec();
677        bytes.extend_from_slice(&0xa1b2_c3d4u32.to_le_bytes());
678        let mut reader = Reader::new(Cursor::new(bytes));
679
680        Flags::from_reader_with_ctx(&mut reader, deku::ctx::Endian::Little).unwrap();
681        let next = u32::from_reader_with_ctx(&mut reader, deku::ctx::Endian::Little).unwrap();
682
683        assert_eq!(next, 0xa1b2_c3d4);
684    }
685}