1use std::io::{self, Read, Write};
2
3use crate::{Error, Result};
4
5const DSD_CHUNK_LEN: u64 = 28;
6const FMT_CHUNK_LEN: u64 = 52;
7pub const DATA_CHUNK_HEADER_LEN: u64 = 12;
9
10pub const AUDIO_OFFSET: u64 = DSD_CHUNK_LEN + FMT_CHUNK_LEN + DATA_CHUNK_HEADER_LEN;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct DsfHeader {
16 pub total_file_size: u64,
18 pub metadata_offset: u64,
20 pub channel_type: u32,
22 pub channel_count: u32,
24 pub sample_rate: u32,
26 pub bits_per_sample: u32,
28 pub sample_count: u64,
30 pub block_size: u32,
32 pub data_chunk_size: u64,
34}
35
36impl DsfHeader {
37 pub fn read<R: Read>(mut r: R) -> Result<Self> {
39 let r = &mut r;
40 magic(r, b"DSD ", "DSD")?;
41 if u64_le(r)? != DSD_CHUNK_LEN {
42 return Err(Error::Malformed("unexpected DSD chunk size"));
43 }
44 let total_file_size = u64_le(r)?;
45 let metadata_offset = u64_le(r)?;
46
47 magic(r, b"fmt ", "fmt")?;
48 if u64_le(r)? != FMT_CHUNK_LEN {
49 return Err(Error::Malformed("unexpected fmt chunk size"));
50 }
51 if u32_le(r)? != 1 {
52 return Err(Error::Malformed("unsupported DSF version"));
53 }
54 if u32_le(r)? != 0 {
55 return Err(Error::Malformed("unsupported format id (only raw DSD)"));
56 }
57 let channel_type = u32_le(r)?;
58 let channel_count = u32_le(r)?;
59 let sample_rate = u32_le(r)?;
60 let bits_per_sample = u32_le(r)?;
61 let sample_count = u64_le(r)?;
62 let block_size = u32_le(r)?;
63 let _reserved = u32_le(r)?;
64
65 magic(r, b"data", "data")?;
66 let data_chunk_size = u64_le(r)?;
67
68 let header = Self {
69 total_file_size,
70 metadata_offset,
71 channel_type,
72 channel_count,
73 sample_rate,
74 bits_per_sample,
75 sample_count,
76 block_size,
77 data_chunk_size,
78 };
79 header.validate()?;
80 Ok(header)
81 }
82
83 pub fn write<W: Write>(&self, mut w: W) -> Result<()> {
85 let mut b = Vec::with_capacity(92);
86 b.extend_from_slice(b"DSD ");
87 b.extend_from_slice(&DSD_CHUNK_LEN.to_le_bytes());
88 b.extend_from_slice(&self.total_file_size.to_le_bytes());
89 b.extend_from_slice(&self.metadata_offset.to_le_bytes());
90 b.extend_from_slice(b"fmt ");
91 b.extend_from_slice(&FMT_CHUNK_LEN.to_le_bytes());
92 b.extend_from_slice(&1u32.to_le_bytes()); b.extend_from_slice(&0u32.to_le_bytes()); b.extend_from_slice(&self.channel_type.to_le_bytes());
95 b.extend_from_slice(&self.channel_count.to_le_bytes());
96 b.extend_from_slice(&self.sample_rate.to_le_bytes());
97 b.extend_from_slice(&self.bits_per_sample.to_le_bytes());
98 b.extend_from_slice(&self.sample_count.to_le_bytes());
99 b.extend_from_slice(&self.block_size.to_le_bytes());
100 b.extend_from_slice(&0u32.to_le_bytes()); b.extend_from_slice(b"data");
102 b.extend_from_slice(&self.data_chunk_size.to_le_bytes());
103 w.write_all(&b)?;
104 Ok(())
105 }
106
107 pub fn audio_end(&self) -> u64 {
109 (DSD_CHUNK_LEN + FMT_CHUNK_LEN).saturating_add(self.data_chunk_size)
110 }
111
112 fn validate(&self) -> Result<()> {
113 if self.channel_count == 0 {
114 return Err(Error::Malformed("zero channels"));
115 }
116 if self.block_size == 0 {
117 return Err(Error::Malformed("zero block size"));
118 }
119 if !matches!(self.bits_per_sample, 1 | 8) {
120 return Err(Error::Malformed("bits per sample must be 1 or 8"));
121 }
122 if self.data_chunk_size < DATA_CHUNK_HEADER_LEN
123 || (DSD_CHUNK_LEN + FMT_CHUNK_LEN)
124 .checked_add(self.data_chunk_size)
125 .is_none()
126 {
127 return Err(Error::Malformed("invalid data chunk size"));
128 }
129 Ok(())
130 }
131}
132
133fn fill<R: Read, const N: usize>(r: &mut R) -> Result<[u8; N]> {
134 let mut buf = [0u8; N];
135 r.read_exact(&mut buf).map_err(|e| match e.kind() {
136 io::ErrorKind::UnexpectedEof => Error::Malformed("truncated header"),
137 _ => Error::Io(e),
138 })?;
139 Ok(buf)
140}
141
142fn u32_le<R: Read>(r: &mut R) -> Result<u32> {
143 Ok(u32::from_le_bytes(fill(r)?))
144}
145
146fn u64_le<R: Read>(r: &mut R) -> Result<u64> {
147 Ok(u64::from_le_bytes(fill(r)?))
148}
149
150fn magic<R: Read>(r: &mut R, expected: &[u8; 4], name: &'static str) -> Result<()> {
151 if &fill::<R, 4>(r)? == expected {
152 Ok(())
153 } else {
154 Err(Error::BadSignature(name))
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 fn sample() -> DsfHeader {
163 DsfHeader {
164 total_file_size: AUDIO_OFFSET + 8192,
165 metadata_offset: 0,
166 channel_type: 2,
167 channel_count: 2,
168 sample_rate: 2_822_400,
169 bits_per_sample: 1,
170 sample_count: 32_768,
171 block_size: 4096,
172 data_chunk_size: DATA_CHUNK_HEADER_LEN + 8192,
173 }
174 }
175
176 fn encoded() -> Vec<u8> {
177 let mut bytes = Vec::new();
178 assert!(sample().write(&mut bytes).is_ok());
179 bytes
180 }
181
182 #[test]
183 fn round_trips() {
184 assert_eq!(DsfHeader::read(encoded().as_slice()).ok(), Some(sample()));
185 }
186
187 #[test]
188 fn every_truncation_is_an_error_not_a_panic() {
189 let bytes = encoded();
190 for len in 0..bytes.len() {
191 let prefix = bytes.get(..len).unwrap_or_default();
192 assert!(DsfHeader::read(prefix).is_err(), "prefix of {len} bytes");
193 }
194 }
195
196 #[test]
197 fn every_single_byte_corruption_is_handled() {
198 let bytes = encoded();
199 for index in 0..bytes.len() {
200 for flip in [0x01u8, 0x80, 0xFF] {
201 let mut copy = bytes.clone();
202 if let Some(byte) = copy.get_mut(index) {
203 *byte ^= flip;
204 }
205 let _ = DsfHeader::read(copy.as_slice());
207 }
208 }
209 }
210
211 #[test]
212 fn rejects_zero_channels_and_block_size() {
213 for mutate in [
214 |h: &mut DsfHeader| h.channel_count = 0,
215 |h: &mut DsfHeader| h.block_size = 0,
216 |h: &mut DsfHeader| h.bits_per_sample = 4,
217 |h: &mut DsfHeader| h.data_chunk_size = 3,
218 ] {
219 let mut header = sample();
220 mutate(&mut header);
221 let mut bytes = Vec::new();
222 assert!(header.write(&mut bytes).is_ok());
223 assert!(DsfHeader::read(bytes.as_slice()).is_err());
224 }
225 }
226}