1use crate::config::ParseOptions;
2use crate::error::Result;
3use crate::id3::v2::tag::Id3v2Tag;
4use crate::macros::{err, try_vec};
5use crate::util::text::utf8_decode;
6
7use std::io::{Read, Seek, SeekFrom};
8use std::marker::PhantomData;
9
10use byteorder::{ByteOrder, ReadBytesExt};
11
12const RIFF_CHUNK_HEADER_SIZE: u64 = 8;
13
14pub(crate) struct Chunks<B>
15where
16 B: ByteOrder,
17{
18 pub fourcc: [u8; 4],
19 pub size: u32,
20 remaining_size: u64,
21 _phantom: PhantomData<B>,
22}
23
24impl<B: ByteOrder> Chunks<B> {
25 #[must_use]
26 pub const fn new(file_size: u64) -> Self {
27 Self {
28 fourcc: [0; 4],
29 size: 0,
30 remaining_size: file_size,
31 _phantom: PhantomData,
32 }
33 }
34
35 pub fn next<R>(&mut self, data: &mut R) -> Result<bool>
36 where
37 R: Read,
38 {
39 if self.remaining_size < RIFF_CHUNK_HEADER_SIZE {
40 return Ok(false);
41 }
42
43 data.read_exact(&mut self.fourcc)?;
44 self.size = data.read_u32::<B>()?;
45
46 self.remaining_size = self.remaining_size.saturating_sub(8);
47
48 Ok(true)
49 }
50
51 pub fn read_cstring<R>(&mut self, data: &mut R) -> Result<String>
52 where
53 R: Read + Seek,
54 {
55 let cont = self.content(data)?;
56 self.correct_position(data)?;
57
58 utf8_decode(cont)
59 }
60
61 pub fn read_pstring<R>(&mut self, data: &mut R, size: Option<u32>) -> Result<String>
62 where
63 R: Read + Seek,
64 {
65 let cont = if let Some(size) = size {
66 self.read(data, u64::from(size))?
67 } else {
68 self.content(data)?
69 };
70
71 if cont.len() % 2 != 0 {
72 data.seek(SeekFrom::Current(1))?;
73 }
74
75 utf8_decode(cont)
76 }
77
78 pub fn content<R>(&mut self, data: &mut R) -> Result<Vec<u8>>
79 where
80 R: Read,
81 {
82 self.read(data, u64::from(self.size))
83 }
84
85 fn read<R>(&mut self, data: &mut R, size: u64) -> Result<Vec<u8>>
86 where
87 R: Read,
88 {
89 if size > self.remaining_size {
90 err!(SizeMismatch);
91 }
92
93 let mut content = try_vec![0; size as usize];
94 data.read_exact(&mut content)?;
95
96 self.remaining_size = self.remaining_size.saturating_sub(size);
97 Ok(content)
98 }
99
100 pub fn id3_chunk<R>(&mut self, data: &mut R, parse_options: ParseOptions) -> Result<Id3v2Tag>
101 where
102 R: Read + Seek,
103 {
104 use crate::id3::v2::header::Id3v2Header;
105 use crate::id3::v2::read::parse_id3v2;
106
107 let content = self.content(data)?;
108
109 let reader = &mut &*content;
110
111 let header = Id3v2Header::parse(reader)?;
112 let id3v2 = parse_id3v2(reader, header, parse_options)?;
113
114 if id3v2.flags().footer {
116 data.seek(SeekFrom::Current(10))?;
117 }
118
119 self.correct_position(data)?;
120
121 Ok(id3v2)
122 }
123
124 pub fn skip<R>(&mut self, data: &mut R) -> Result<()>
125 where
126 R: Read + Seek,
127 {
128 data.seek(SeekFrom::Current(i64::from(self.size)))?;
129 self.correct_position(data)?;
130
131 self.remaining_size = self.remaining_size.saturating_sub(u64::from(self.size));
132
133 Ok(())
134 }
135
136 pub fn correct_position<R>(&mut self, data: &mut R) -> Result<()>
137 where
138 R: Read + Seek,
139 {
140 if self.size % 2 != 0 {
144 data.seek(SeekFrom::Current(1))?;
145 self.remaining_size = self.remaining_size.saturating_sub(1);
146 }
147
148 Ok(())
149 }
150}