1use bon::Builder;
2use core::fmt;
3
4use crate::{atom::FourCC, parser::ParseAtomData, writer::SerializeAtom, ParseError};
5
6pub const HDLR: FourCC = FourCC::new(b"hdlr");
7
8pub const HANDLER_VIDEO: FourCC = FourCC::new(b"vide");
10pub const HANDLER_AUDIO: FourCC = FourCC::new(b"soun");
11pub const HANDLER_HINT: FourCC = FourCC::new(b"hint");
12pub const HANDLER_META: FourCC = FourCC::new(b"meta");
13pub const HANDLER_TEXT: FourCC = FourCC::new(b"text");
14pub const HANDLER_MDIR: FourCC = FourCC::new(b"mdir");
15pub const HANDLER_SUBTITLE: FourCC = FourCC::new(b"subt");
16pub const HANDLER_TIMECODE: FourCC = FourCC::new(b"tmcd");
17
18#[derive(Debug, Clone, PartialEq)]
19pub enum HandlerType {
20 Video,
21 Audio,
22 Hint,
23 Meta,
24 Text,
25 Mdir,
26 Subtitle,
27 Timecode,
28 Unknown(FourCC),
29}
30
31impl Default for HandlerType {
32 fn default() -> Self {
33 Self::Unknown(FourCC([0u8; 4]))
34 }
35}
36
37impl HandlerType {
38 pub fn from_bytes(bytes: &[u8; 4]) -> Self {
39 match FourCC::new(bytes) {
40 HANDLER_VIDEO => HandlerType::Video,
41 HANDLER_AUDIO => HandlerType::Audio,
42 HANDLER_HINT => HandlerType::Hint,
43 HANDLER_META => HandlerType::Meta,
44 HANDLER_TEXT => HandlerType::Text,
45 HANDLER_MDIR => HandlerType::Mdir,
46 HANDLER_SUBTITLE => HandlerType::Subtitle,
47 HANDLER_TIMECODE => HandlerType::Timecode,
48 _ => HandlerType::Unknown(FourCC::new(bytes)),
49 }
50 }
51
52 pub fn to_bytes(&self) -> [u8; 4] {
53 match self {
54 HandlerType::Video => *HANDLER_VIDEO,
55 HandlerType::Audio => *HANDLER_AUDIO,
56 HandlerType::Hint => *HANDLER_HINT,
57 HandlerType::Meta => *HANDLER_META,
58 HandlerType::Text => *HANDLER_TEXT,
59 HandlerType::Mdir => *HANDLER_MDIR,
60 HandlerType::Subtitle => *HANDLER_SUBTITLE,
61 HandlerType::Timecode => *HANDLER_TIMECODE,
62 HandlerType::Unknown(fourcc) => fourcc.into_bytes(),
63 }
64 }
65
66 pub fn as_str(&self) -> &str {
67 match self {
68 HandlerType::Video => "Video",
69 HandlerType::Audio => "Audio",
70 HandlerType::Hint => "Hint",
71 HandlerType::Meta => "Metadata",
72 HandlerType::Text => "Text",
73 HandlerType::Mdir => "Mdir",
74 HandlerType::Subtitle => "Subtitle",
75 HandlerType::Timecode => "Timecode",
76 HandlerType::Unknown(_) => "Unknown",
77 }
78 }
79
80 pub fn is_media_handler(&self) -> bool {
81 matches!(
82 self,
83 HandlerType::Video | HandlerType::Audio | HandlerType::Text | HandlerType::Subtitle
84 )
85 }
86}
87
88#[derive(Default, Debug, Clone, Builder)]
89pub struct HandlerReferenceAtom {
90 #[builder(default = 0)]
92 pub version: u8,
93 #[builder(default = [0u8; 3])]
95 pub flags: [u8; 3],
96 #[builder(default = FourCC([0u8; 4]))]
98 pub component_type: FourCC,
99 pub handler_type: HandlerType,
101 #[builder(default = FourCC([0u8; 4]))]
103 pub component_manufacturer: FourCC,
104 #[builder(default = 0)]
106 pub component_flags: u32,
107 #[builder(default = 0)]
109 pub component_flags_mask: u32,
110 #[builder(into)]
112 pub name: Option<HandlerName>,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
116pub enum HandlerName {
117 Raw(String),
119 Pascal(String),
121 CString(String),
123 CString2(String),
125}
126
127impl Default for HandlerName {
128 fn default() -> Self {
129 Self::Raw(String::new())
130 }
131}
132
133impl fmt::Display for HandlerName {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 fmt::Display::fmt(self.as_string(), f)
136 }
137}
138
139impl From<String> for HandlerName {
140 fn from(value: String) -> Self {
141 Self::CString(value)
142 }
143}
144
145impl From<&String> for HandlerName {
146 fn from(value: &String) -> Self {
147 Self::CString(value.to_owned())
148 }
149}
150
151impl From<&str> for HandlerName {
152 fn from(value: &str) -> Self {
153 Self::CString(value.to_owned())
154 }
155}
156
157impl HandlerName {
158 fn as_string(&self) -> &String {
159 match self {
160 Self::Raw(str) => str,
161 Self::Pascal(str) => str,
162 Self::CString(str) => str,
163 Self::CString2(str) => str,
164 }
165 }
166
167 pub fn as_str(&self) -> &str {
168 self.as_string().as_str()
169 }
170}
171
172impl ParseAtomData for HandlerReferenceAtom {
173 fn parse_atom_data(atom_type: FourCC, input: &[u8]) -> Result<Self, ParseError> {
174 crate::atom::util::parser::assert_atom_type!(atom_type, HDLR);
175 use crate::atom::util::parser::stream;
176 use winnow::Parser;
177 Ok(parser::parse_hdlr_data.parse(stream(input))?)
178 }
179}
180
181impl SerializeAtom for HandlerReferenceAtom {
182 fn atom_type(&self) -> FourCC {
183 HDLR
184 }
185
186 fn into_body_bytes(self) -> Vec<u8> {
187 serializer::serialize_hdlr_atom(self)
188 }
189}
190
191mod serializer {
192 use crate::FourCC;
193
194 use super::{HandlerName, HandlerReferenceAtom, HandlerType};
195
196 pub fn serialize_hdlr_atom(atom: HandlerReferenceAtom) -> Vec<u8> {
197 vec![
198 version(atom.version),
199 flags(atom.flags),
200 component_type(atom.component_type),
201 handler_type(atom.handler_type),
202 component_manufacturer(atom.component_manufacturer),
203 component_flags(atom.component_flags),
204 component_flags_mask(atom.component_flags_mask),
205 atom.name.map(name).unwrap_or_default(),
206 ]
207 .into_iter()
208 .flatten()
209 .collect()
210 }
211
212 fn version(version: u8) -> Vec<u8> {
213 vec![version]
214 }
215
216 fn flags(flags: [u8; 3]) -> Vec<u8> {
217 flags.to_vec()
218 }
219
220 fn component_type(component_type: FourCC) -> Vec<u8> {
221 component_type.to_vec()
222 }
223
224 fn handler_type(handler_type: HandlerType) -> Vec<u8> {
225 handler_type.to_bytes().to_vec()
226 }
227
228 fn component_manufacturer(manufacturer: FourCC) -> Vec<u8> {
229 manufacturer.to_vec()
230 }
231
232 fn component_flags(flags: u32) -> Vec<u8> {
233 flags.to_be_bytes().to_vec()
234 }
235
236 fn component_flags_mask(flags_mask: u32) -> Vec<u8> {
237 flags_mask.to_be_bytes().to_vec()
238 }
239
240 fn name(name: HandlerName) -> Vec<u8> {
241 let mut data = Vec::new();
242 match name {
243 HandlerName::Pascal(name) => {
244 let name_bytes = name.as_bytes();
245 let len = u8::try_from(name_bytes.len())
246 .expect("HandlerName::Pascal length must not exceed u8::MAX");
247 data.push(len);
248 data.extend_from_slice(name_bytes);
249 }
250 HandlerName::CString(name) => {
251 data.extend_from_slice(name.as_bytes());
252 data.push(0); }
254 HandlerName::CString2(name) => {
255 data.extend_from_slice(name.as_bytes());
256 data.push(0); data.push(0); }
259 HandlerName::Raw(name) => {
260 data.extend_from_slice(name.as_bytes());
261 }
262 }
263 data
264 }
265}
266
267mod parser {
268 use winnow::{
269 binary::{be_u32, length_take, u8},
270 combinator::{alt, opt, repeat_till, seq, trace},
271 error::StrContext,
272 token::{literal, rest},
273 ModalResult, Parser,
274 };
275
276 use super::{HandlerName, HandlerReferenceAtom, HandlerType};
277 use crate::{
278 atom::util::parser::{byte_array, flags3, fourcc, version, Stream},
279 FourCC,
280 };
281
282 pub fn parse_hdlr_data(input: &mut Stream<'_>) -> ModalResult<HandlerReferenceAtom> {
283 trace(
284 "hdlr",
285 seq!(HandlerReferenceAtom {
286 version: version,
287 flags: flags3,
288 component_type: component_type,
289 handler_type: handler_type,
290 component_manufacturer: component_manufacturer,
291 component_flags: component_flags,
292 component_flags_mask: component_flags_mask,
293 name: opt(name),
294 })
295 .context(StrContext::Label("hdlr")),
296 )
297 .parse_next(input)
298 }
299
300 fn component_type(input: &mut Stream<'_>) -> ModalResult<FourCC> {
301 trace(
302 "component_type",
303 fourcc.context(StrContext::Label("component_type")),
304 )
305 .parse_next(input)
306 }
307
308 fn handler_type(input: &mut Stream<'_>) -> ModalResult<HandlerType> {
309 trace(
310 "handler_type",
311 byte_array
312 .map(|fourcc| HandlerType::from_bytes(&fourcc))
313 .context(StrContext::Label("handler_type")),
314 )
315 .parse_next(input)
316 }
317
318 fn component_manufacturer(input: &mut Stream<'_>) -> ModalResult<FourCC> {
319 trace(
320 "component_manufacturer",
321 fourcc.context(StrContext::Label("component_manufacturer")),
322 )
323 .parse_next(input)
324 }
325
326 fn component_flags(input: &mut Stream<'_>) -> ModalResult<u32> {
327 trace(
328 "component_flags",
329 be_u32.context(StrContext::Label("component_flags")),
330 )
331 .parse_next(input)
332 }
333
334 fn component_flags_mask(input: &mut Stream<'_>) -> ModalResult<u32> {
335 trace(
336 "component_flags_mask",
337 be_u32.context(StrContext::Label("component_flags_mask")),
338 )
339 .parse_next(input)
340 }
341
342 fn name(input: &mut Stream<'_>) -> ModalResult<HandlerName> {
343 trace(
344 "name",
345 alt((name_cstr, name_pascal, name_raw)).context(StrContext::Label("name")),
346 )
347 .parse_next(input)
348 }
349
350 fn name_cstr(input: &mut Stream<'_>) -> ModalResult<HandlerName> {
351 trace(
352 "name_cstr",
353 repeat_till(1.., u8, null_term).map(|(data, null2): (Vec<u8>, Option<()>)| {
354 let str = String::from_utf8_lossy(&data).to_string();
355 match null2 {
356 Some(_) => HandlerName::CString2(str),
357 None => HandlerName::CString(str),
358 }
359 }),
360 )
361 .parse_next(input)
362 }
363
364 fn null_term(input: &mut Stream<'_>) -> ModalResult<Option<()>> {
365 trace(
366 "null_term",
367 (literal(0x00), opt(literal(0x00))).map(|(_, null2)| null2.map(|_| ())),
368 )
369 .parse_next(input)
370 }
371
372 fn name_pascal(input: &mut Stream<'_>) -> ModalResult<HandlerName> {
373 trace(
374 "name_pascal",
375 length_take(u8)
376 .map(|data| HandlerName::Pascal(String::from_utf8_lossy(data).to_string())),
377 )
378 .parse_next(input)
379 }
380
381 fn name_raw(input: &mut Stream<'_>) -> ModalResult<HandlerName> {
382 trace(
383 "name_raw",
384 rest.map(|data| HandlerName::Raw(String::from_utf8_lossy(data).to_string())),
385 )
386 .parse_next(input)
387 }
388
389 #[cfg(test)]
390 mod tests {
391
392 use super::*;
393 use crate::{atom::util::parser::stream, FourCC};
394
395 #[test]
396 fn test_handler_type_from_bytes() {
397 assert_eq!(HandlerType::from_bytes(b"vide"), HandlerType::Video);
398 assert_eq!(HandlerType::from_bytes(b"soun"), HandlerType::Audio);
399 assert_eq!(HandlerType::from_bytes(b"text"), HandlerType::Text);
400 assert_eq!(HandlerType::from_bytes(b"meta"), HandlerType::Meta);
401 assert_eq!(
402 HandlerType::from_bytes(b"abcd"),
403 HandlerType::Unknown(FourCC::new(b"abcd"))
404 );
405 }
406
407 #[test]
408 fn test_handler_type_methods() {
409 let video_handler = HandlerType::Video;
410 assert!(video_handler.is_media_handler());
411 assert_eq!(video_handler.as_str(), "Video");
412 assert_eq!(video_handler.to_bytes(), *b"vide");
413
414 let unknown_handler = HandlerType::Unknown(FourCC::new(b"test"));
415 assert!(!unknown_handler.is_media_handler());
416 assert_eq!(unknown_handler.as_str(), "Unknown");
417 assert_eq!(unknown_handler.to_bytes(), *b"test");
418 }
419
420 #[test]
421 fn test_parse_handler_name_pascal() {
422 let pascal_name = b"\x0CHello World!";
424 let result = name.parse(stream(pascal_name)).unwrap();
425 assert_eq!(result, HandlerName::Pascal("Hello World!".to_owned()));
426 }
427
428 #[test]
429 fn test_parse_handler_name_null_terminated() {
430 let c_name = b"Hello World!\0";
432 let result = name.parse(stream(c_name)).unwrap();
433 assert_eq!(result, HandlerName::CString("Hello World!".to_owned()));
434 }
435
436 #[test]
437 fn test_parse_handler_name_double_null_terminated() {
438 let c_name = b"Hello World!\0\0";
440 let result = name.parse(stream(c_name)).unwrap();
441 assert_eq!(result, HandlerName::CString2("Hello World!".to_owned()));
442 }
443
444 #[test]
445 fn test_parse_handler_name_raw() {
446 let raw_name = b"Hello World!";
448 let result = name.parse(stream(raw_name)).unwrap();
449 assert_eq!(result, HandlerName::Raw("Hello World!".to_owned()));
450 }
451
452 #[test]
453 fn test_parse_handler_name_empty() {
454 let empty_name = b"";
455 let result = name.parse(stream(empty_name)).unwrap();
456 assert_eq!(result, HandlerName::Raw(String::new()));
457 }
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464 use crate::atom::test_utils::test_atom_roundtrip;
465
466 #[test]
468 fn test_hdlr_roundtrip() {
469 test_atom_roundtrip::<HandlerReferenceAtom>(HDLR);
470 }
471}