1pub mod rendition;
2
3use std::io::{Cursor, Read, Seek};
4
5use bom::{BOMBlock, BOMEror, BOMResult};
6use deku::{ctx::Order, prelude::*, reader::Reader};
7
8#[derive(Debug, DekuRead)]
10#[deku(endian = "big", magic = b"META")]
11pub struct ExtendedMetadata {
12 #[deku(bytes_read = "256", map = "crate::deku_read_str")]
13 pub thinning_args: String,
14 #[deku(bytes_read = "256", map = "crate::deku_read_str")]
15 pub deployment_platform_version: String,
16 #[deku(bytes_read = "256", map = "crate::deku_read_str")]
17 pub deployment_platform: String,
18 #[deku(bytes_read = "256", map = "crate::deku_read_str")]
19 pub authoring_tool: String,
20}
21
22#[derive(Debug, DekuRead)]
23#[deku(magic = b"RATC", endian = "little")]
24pub struct Header {
25 pub coreui_version: u32,
26 pub storage_version: u32,
27 pub storage_timestamp: u32,
28 pub rendition_count: u32,
29 #[deku(bytes_read = "128", map = "crate::deku_read_str")]
30 pub main_version_string: String,
31 #[deku(bytes_read = "256", map = "crate::deku_read_str")]
32 pub version_string: String,
33 pub uuid: [u8; 16],
34 pub associated_checksumag: u32,
35 pub schema_version: u32,
36 pub color_space: ColorSpace,
37 pub key_semantics: u32,
38}
39
40#[derive(Debug)]
41pub struct CSIHeader {
42 pub version: u32,
43 pub flags: rendition::Flags,
44 pub width: u32,
45 pub height: u32,
46 pub scale_factor: u32,
48 pub encoding: Encoding,
49 pub color_model: ColorModel,
50 pub metadata: CSIMetadata,
51 pub tlv_length: u32,
52 pub bitmap_list: rendition::BitmapList,
53 pub tlv_data: Vec<rendition::RenditionType>,
54 pub rendition: Option<rendition::Rendition>,
55}
56
57impl CSIHeader {
58 pub(crate) fn from_bom_block(block: BOMBlock) -> BOMResult<Self> {
59 let mut reader = Reader::new(block);
60 let parts = read_csi_header_parts(&mut reader)?;
61 let tlv_data = read_tlv_data_from_bom(&mut reader, parts.tlv_length)?;
62 let rendition = if parts.bitmap_list.rendition_length > 0 {
63 let payload = payload_slice(&mut reader, parts.bitmap_list.rendition_length)?;
64 Some(rendition::parse_rendition_payload(payload)?)
65 } else {
66 None
67 };
68
69 Ok(Self::from_parts(parts, tlv_data, rendition))
70 }
71
72 fn from_parts(
73 parts: CSIHeaderParts,
74 tlv_data: Vec<rendition::RenditionType>,
75 rendition: Option<rendition::Rendition>,
76 ) -> Self {
77 Self {
78 version: parts.version,
79 flags: parts.flags,
80 width: parts.width,
81 height: parts.height,
82 scale_factor: parts.scale_factor,
83 encoding: parts.encoding,
84 color_model: parts.color_model,
85 metadata: parts.metadata,
86 tlv_length: parts.tlv_length,
87 bitmap_list: parts.bitmap_list,
88 tlv_data,
89 rendition,
90 }
91 }
92
93 pub fn size_on_disk(&self) -> usize {
94 14 * 4 + 128 + self.tlv_length as usize + self.bitmap_list.rendition_length as usize
95 }
96}
97
98impl<'a> DekuReader<'a> for CSIHeader {
99 fn from_reader_with_ctx<R: Read + Seek>(
100 reader: &mut Reader<R>,
101 _ctx: (),
102 ) -> Result<Self, DekuError>
103 where
104 Self: Sized,
105 {
106 let parts = read_csi_header_parts(reader)?;
107 let tlv_data = read_tlv_data(reader, parts.tlv_length)?;
108 let rendition = if parts.bitmap_list.rendition_length > 0 {
109 let payload = read_payload_bytes(reader, parts.bitmap_list.rendition_length)?;
110 Some(
111 rendition::parse_rendition_payload(crate::PayloadBytes::from_vec(payload))
112 .map_err(bom_error_to_deku)?,
113 )
114 } else {
115 None
116 };
117
118 Ok(Self::from_parts(parts, tlv_data, rendition))
119 }
120}
121
122struct CSIHeaderParts {
123 version: u32,
124 flags: rendition::Flags,
125 width: u32,
126 height: u32,
127 scale_factor: u32,
128 encoding: Encoding,
129 color_model: ColorModel,
130 metadata: CSIMetadata,
131 tlv_length: u32,
132 bitmap_list: rendition::BitmapList,
133}
134
135fn read_csi_header_parts<R: Read + Seek>(
136 reader: &mut Reader<R>,
137) -> Result<CSIHeaderParts, DekuError> {
138 read_magic(reader, b"ISTC")?;
139 Ok(CSIHeaderParts {
140 version: read_u32(reader)?,
141 flags: rendition::Flags::from_reader_with_ctx(reader, deku::ctx::Endian::Little)?,
142 width: read_u32(reader)?,
143 height: read_u32(reader)?,
144 scale_factor: read_u32(reader)?,
145 encoding: Encoding::from_reader_with_ctx(reader, deku::ctx::Endian::Little)?,
146 color_model: ColorModel::from_reader_with_ctx(reader, deku::ctx::Endian::Little)?,
147 metadata: CSIMetadata::from_reader_with_ctx(reader, deku::ctx::Endian::Little)?,
148 tlv_length: read_u32(reader)?,
149 bitmap_list: rendition::BitmapList::from_reader_with_ctx(
150 reader,
151 deku::ctx::Endian::Little,
152 )?,
153 })
154}
155
156fn read_magic<R: Read + Seek>(reader: &mut Reader<R>, expected: &[u8; 4]) -> Result<(), DekuError> {
157 let mut actual = [0_u8; 4];
158 reader.read_bytes(4, &mut actual, Order::default())?;
159 if &actual != expected {
160 return Err(DekuError::Parse(
161 format!(
162 "invalid CSI header magic: expected {:?}, got {:?}",
163 expected, actual
164 )
165 .into(),
166 ));
167 }
168 Ok(())
169}
170
171fn read_u32<R: Read + Seek>(reader: &mut Reader<R>) -> Result<u32, DekuError> {
172 u32::from_reader_with_ctx(reader, deku::ctx::Endian::Little)
173}
174
175fn read_payload_bytes<R: Read + Seek>(
176 reader: &mut Reader<R>,
177 len: u32,
178) -> Result<Vec<u8>, DekuError> {
179 let len = len as usize;
180 let mut bytes = vec![0_u8; len];
181 reader.read_bytes(len, &mut bytes, Order::default())?;
182 Ok(bytes)
183}
184
185fn payload_slice(reader: &mut Reader<BOMBlock>, len: u32) -> BOMResult<crate::PayloadBytes> {
186 let len = len as usize;
187 let slice = reader.as_mut().slice_at_current(len)?;
188 reader.bits_read = reader.bits_read.saturating_add(len.saturating_mul(8));
189 reader.leftover = None;
190 Ok(slice)
191}
192
193fn read_tlv_data<R: Read + Seek>(
194 reader: &mut Reader<R>,
195 len: u32,
196) -> Result<Vec<rendition::RenditionType>, DekuError> {
197 let bytes = read_payload_bytes(reader, len)?;
198 parse_tlv_data(&bytes)
199}
200
201fn read_tlv_data_from_bom(
202 reader: &mut Reader<BOMBlock>,
203 len: u32,
204) -> BOMResult<Vec<rendition::RenditionType>> {
205 let bytes = payload_slice(reader, len)?;
206 parse_tlv_data(bytes.as_slice()).map_err(BOMEror::from)
207}
208
209fn parse_tlv_data(bytes: &[u8]) -> Result<Vec<rendition::RenditionType>, DekuError> {
210 let mut cursor = Cursor::new(bytes);
211 let mut reader = Reader::new(&mut cursor);
212 let mut result = Vec::new();
213 while (reader.as_mut().position() as usize) < bytes.len() {
214 result.push(rendition::RenditionType::from_reader_with_ctx(
215 &mut reader,
216 deku::ctx::Endian::Little,
217 )?);
218 }
219 Ok(result)
220}
221
222fn bom_error_to_deku(err: BOMEror) -> DekuError {
223 match err {
224 BOMEror::ParseStruct(err) => err,
225 err => DekuError::Parse(err.to_string().into()),
226 }
227}
228
229#[derive(Debug, DekuRead)]
230#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
231pub struct CSIMetadata {
232 pub modification_time: u32,
233 pub layout_type: rendition::LayoutType,
234 #[deku(bytes_read = "128", map = "crate::deku_read_str")]
235 pub name: String,
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, DekuRead)]
239#[deku(id_type = "u32", endian = "little", ctx = "endian: deku::ctx::Endian")]
240pub enum ColorSpace {
241 #[deku(id = 1)]
242 SRGB,
243 #[deku(id = 2)]
244 GrayGamma2_2,
245 #[deku(id = 3)]
246 DisplayP3,
247 #[deku(id = 4)]
248 ExtendedRangeSRGB,
249 #[deku(id = 5)]
250 ExtendedLinearSRGB,
251 #[deku(id = 6)]
252 ExtendedGray,
253
254 #[deku(id = 257)]
255 SystemSRGB,
256
257 #[deku(id_pat = "_")]
258 Unknown { tag: u32 },
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead)]
262#[deku(id_type = "u32", endian = "little", ctx = "endian: deku::ctx::Endian")]
263pub enum ColorModel {
264 #[deku(id = 0)]
265 None,
266 #[deku(id = 1)]
267 RGB,
268 #[deku(id = 2)]
269 Monochrome,
270 #[deku(id = 3)]
271 RGB0, #[deku(id = 4)]
273 RGBP3,
274
275 #[deku(id_pat = "_")]
276 Unknown { tag: u32 },
277}
278
279#[derive(Debug, PartialEq, Eq, Clone, Copy, DekuRead)]
280#[deku(
281 id_type = "[u8; 4]",
282 endian = "little",
283 ctx = "endian: deku::ctx::Endian"
284)]
285pub enum Encoding {
286 #[deku(id = "[0, 0, 0, 0]")]
287 None,
288 #[deku(id = b"BGRA")]
289 ARGB,
290 #[deku(id = b"ATAD")]
291 Data,
292 #[deku(id = b"YARG")]
293 GRAY,
294 #[deku(id = b"GEPJ")]
295 JPEG,
296 #[deku(id = b" FDP")]
297 PDF,
298 #[deku(id = b"PBEW")]
299 WEBP,
300 #[deku(id = b"WBGR")]
301 ARGB16,
302 #[deku(id = b"61AG")]
303 GA16,
304 #[deku(id = b" 8AG")]
305 GA8,
306 #[deku(id = b"5BGR")]
307 RGB5,
308 #[deku(id = b" GVS")]
309 SVG,
310 #[deku(id = b"FIEH")]
311 HEIF,
312
313 #[deku(id_pat = "_")]
314 Unknown { tag: [u8; 4] },
315}