1use std::fmt::Display;
2
3use binrw::{BinRead, BinReaderExt, binread};
4use resource_fork::Resource;
5
6#[binread]
7#[derive(Debug)]
8struct LongArray<T: BinRead + 'static>
9where
10 for<'a> <T as BinRead>::Args<'a>: Default + Clone,
11{
12 #[br(temp)]
13 count: u16,
14
15 #[br(count(count))]
16 pub(crate) items: Vec<T>,
17}
18
19fn long_list<T: BinRead>(input: LongArray<T>) -> Vec<T>
20where
21 for<'a> <T as BinRead>::Args<'a>: Default + Clone,
22{
23 input.items
24}
25
26#[derive(Debug, Resource)]
27#[resource(code = "snd ")]
28pub struct Sound {
29 pub channels: u16,
30 pub sample_rate: SampleRate,
31 pub bits_per_sample: u16,
32 pub format: AudioFormat,
33 pub samples: Vec<i8>,
34}
35
36#[derive(Debug, BinRead)]
37#[br(repr=u16, big)]
38pub enum Format {
39 Standard = 1,
40 Hypercard = 2,
41}
42
43#[derive(Debug, BinRead)]
44#[br(repr=u16, big)]
45pub enum ModifierType {
46 SampledSynth = 5,
47}
48
49#[derive(Debug, BinRead)]
50#[br(big)]
51pub struct Modifier {
52 pub modifier_type: ModifierType,
53 pub init: i32,
54}
55
56#[derive(Debug)]
57pub enum Op {
58 Null = 0,
59 Quiet = 3,
60 Flush = 4,
61 ReInit = 5,
62 Wait = 10,
63 Pause = 11,
64 Resume = 12,
65 CallBack = 13,
66 Sync = 14,
67 Available = 24,
68 Version = 25,
69 Volume = 46, GetVolume = 47, ClockComponent = 50, GetClockComponent = 51, ScheduledSound = 52, LinkSoundComponents = 53, Sound = 80,
76 Buffer = 81,
77 RateMultiplier = 86,
78 GetRateMultiplier = 87,
79}
80
81#[derive(Debug, thiserror::Error)]
82pub enum ConversionError {
83 #[error("Unknown opcode {0} encountered")]
84 UnknownOpcode(u16),
85 #[error("Unknown sample rate {0} encountered")]
86 UnknownSampleRate(u32),
87 #[error("Unknown format")]
88 UnsupportedFormat,
89 #[error("Channel count {0} is not supported")]
90 UnsupportedChannelCount(u16),
91 #[error("Sample format {0} is not supported")]
92 UnsupportedSampleFormat(AudioFormat),
93 #[error("Header format {0} is not supported yet")]
94 UnsupportedHeaderEncoding(HeaderEncoding),
95 #[error("Missing functionality")]
96 NotImplementedYet,
97}
98
99impl TryFrom<u16> for Op {
100 type Error = ConversionError;
101
102 fn try_from(value: u16) -> Result<Self, Self::Error> {
103 Ok(match value {
104 0 => Self::Null,
105 3 => Self::Quiet,
106 4 => Self::Flush,
107 5 => Self::ReInit,
108 10 => Self::Wait,
109 11 => Self::Pause,
110 12 => Self::Resume,
111 13 => Self::CallBack,
112 14 => Self::Sync,
113 24 => Self::Available,
114 25 => Self::Version,
115 46 => Self::Volume, 47 => Self::GetVolume, 50 => Self::ClockComponent, 51 => Self::GetClockComponent, 52 => Self::ScheduledSound, 53 => Self::LinkSoundComponents, 80 => Self::Sound,
122 81 => Self::Buffer,
123 86 => Self::RateMultiplier,
124 87 => Self::GetRateMultiplier,
125 value => return Err(ConversionError::UnknownOpcode(value)),
126 })
127 }
128}
129
130impl From<ConversionError> for binrw::Error {
131 fn from(val: ConversionError) -> Self {
132 binrw::Error::Custom {
133 pos: 0,
134 err: Box::new(val),
135 }
136 }
137}
138
139const DATA_OFFSET_FLAG: u16 = 0x8000;
140#[derive(Debug)]
141pub struct OpCode {
142 has_data_offset_flag: bool,
143 op: Op,
144}
145
146impl BinRead for OpCode {
147 type Args<'a> = ();
148
149 fn read_options<R: std::io::Read + std::io::Seek>(
150 reader: &mut R,
151 _endian: binrw::Endian,
152 _args: Self::Args<'_>,
153 ) -> binrw::BinResult<Self> {
154 let data: u16 = reader.read_be()?;
155 let has_flag = (data & DATA_OFFSET_FLAG) != 0;
156 let cmd = Op::try_from(data & !DATA_OFFSET_FLAG).unwrap();
157
158 Ok(OpCode {
159 has_data_offset_flag: has_flag,
160 op: cmd,
161 })
162 }
163}
164
165#[derive(Debug, BinRead)]
166#[br(big)]
167pub struct Command {
168 pub opcode: OpCode,
169 pub param1: i16,
170 pub param2: i32,
171}
172
173#[derive(Debug, BinRead)]
174#[br(big)]
175#[repr(u32)]
176pub enum SampleRate {
177 Rate48khz = 0xbb800000,
179 Rate44khz = 0xac440000,
181 Rate32khz = 0x7d000000,
183 Rate22050hz = 0x56220000,
185 Rate22khz = 0x56ee8ba3,
187 Rate16khz = 0x3e800000,
189 Rate11khz = 0x2b7745d1,
191 Rate11025hz = 0x2b110000,
193 Rate8khz = 0x1f400000,
195
196 Other(u32),
197}
198
199impl TryFrom<SampleRate> for u32 {
200 type Error = ConversionError;
201
202 fn try_from(val: SampleRate) -> Result<Self, Self::Error> {
203 Ok(match val {
204 SampleRate::Rate44khz => 44100,
205 SampleRate::Rate48khz => 48000,
206 SampleRate::Rate32khz => 32000,
207 SampleRate::Rate22050hz => 22050,
208 SampleRate::Rate22khz => 22254,
209 SampleRate::Rate16khz => 16000,
210 SampleRate::Rate11khz => 11127,
211 SampleRate::Rate11025hz => 11025,
212 SampleRate::Rate8khz => 8000,
213 SampleRate::Other(value) => {
214 return Err(ConversionError::UnknownSampleRate(value));
215 }
216 })
217 }
218}
219
220#[derive(Debug, BinRead)]
221#[br(big)]
222pub struct Header {
223 pub sample_ptr: u32,
225 pub length: u32,
227 pub sample_rate: SampleRate,
229 pub loop_start: u32,
231 pub loop_end: u32,
233 pub encoding: HeaderEncoding,
235 pub base_frequency: u8,
237}
238
239#[derive(Debug, BinRead)]
240#[br(repr=u8)]
241pub enum HeaderEncoding {
242 Standard = 0,
243 Compressed = 0xFE,
244 Extended = 0xFF,
245}
246impl Display for HeaderEncoding {
247 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248 match self {
249 HeaderEncoding::Standard => f.write_str("Standard"),
250 HeaderEncoding::Compressed => f.write_str("Compressed"),
251 HeaderEncoding::Extended => f.write_str("Extended"),
252 }
253 }
254}
255
256#[derive(Debug, Clone, Copy)]
257pub enum AudioFormat {
258 Uncompressed,
259 EightBitOffsetBinary,
260 SixteenBitBigEndian,
261 SixteenBitLittleEndian,
262}
263
264impl Display for AudioFormat {
265 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266 match self {
267 AudioFormat::Uncompressed => f.write_str("Uncompressed"),
268 AudioFormat::EightBitOffsetBinary => f.write_str("8bit Offset Binary"),
269 AudioFormat::SixteenBitBigEndian => f.write_str("16-bit Big Endian"),
270 AudioFormat::SixteenBitLittleEndian => f.write_str("16-bit Little Endian"),
271 }
272 }
273}
274
275impl BinRead for Sound {
276 type Args<'a> = ();
277
278 fn read_options<R: std::io::Read + std::io::Seek>(
279 reader: &mut R,
280 _endian: binrw::Endian,
281 _args: Self::Args<'_>,
282 ) -> binrw::BinResult<Self> {
283 match reader.read_be()? {
284 Format::Standard => {
285 let modifiers: Vec<Modifier> = long_list(reader.read_be()?);
286 let commands: Vec<Command> = long_list(reader.read_be()?);
287 if modifiers.len() != 1
288 || commands.len() != 1
289 || !commands.first().unwrap().opcode.has_data_offset_flag
290 || !matches!(commands.first().unwrap().opcode.op, Op::Buffer | Op::Sound)
291 {
292 return Err(ConversionError::UnsupportedFormat.into());
293 }
294
295 let header: Header = reader.read_be().unwrap();
296 let (channel_count, packet_count, format) = match header.encoding {
297 HeaderEncoding::Extended => {
298 return Err(
299 ConversionError::UnsupportedHeaderEncoding(header.encoding).into()
300 );
301 }
302 HeaderEncoding::Compressed => {
303 return Err(
304 ConversionError::UnsupportedHeaderEncoding(header.encoding).into()
305 );
306 }
307 HeaderEncoding::Standard => {
308 (1, header.length, AudioFormat::EightBitOffsetBinary)
309 }
310 };
311
312 if channel_count != 1 {
313 return Err(ConversionError::UnsupportedChannelCount(channel_count).into());
314 }
315
316 if !matches!(format, AudioFormat::EightBitOffsetBinary) {
317 return Err(ConversionError::UnsupportedSampleFormat(format).into());
318 }
319
320 let mut samples = Vec::new();
321 for _ in 0..packet_count {
322 let sample: i8 = reader.read_be().unwrap();
323 samples.push(sample)
324 }
325
326 Ok(Self {
327 format,
328 samples,
329 channels: channel_count,
330 sample_rate: header.sample_rate,
331 bits_per_sample: if matches!(format, AudioFormat::EightBitOffsetBinary) {
332 8
333 } else {
334 16
335 },
336 })
337 }
338 Format::Hypercard => {
339 let _ref_count: i16 = reader.read_be()?;
340 let commands: Vec<Command> = long_list(reader.read_be()?);
341 if commands.len() != 1
342 || !commands.first().unwrap().opcode.has_data_offset_flag
343 || !matches!(commands.first().unwrap().opcode.op, Op::Buffer | Op::Sound)
344 {
345 return Err(ConversionError::NotImplementedYet.into());
346 }
347
348 let header: Header = reader.read_be().unwrap();
349 let (channel_count, packet_count, format) = match header.encoding {
350 HeaderEncoding::Extended => {
351 return Err(ConversionError::NotImplementedYet.into());
352 }
353 HeaderEncoding::Compressed => {
354 return Err(ConversionError::NotImplementedYet.into());
355 }
356 HeaderEncoding::Standard => {
357 (1, header.length, AudioFormat::EightBitOffsetBinary)
358 }
359 };
360
361 if channel_count != 1 {
362 return Err(ConversionError::NotImplementedYet.into());
363 }
364
365 if !matches!(format, AudioFormat::EightBitOffsetBinary) {
366 return Err(ConversionError::NotImplementedYet.into());
367 }
368
369 let mut samples = Vec::new();
370 for _ in 0..packet_count {
371 let sample: i8 = reader.read_be().unwrap();
372 samples.push(sample)
373 }
374
375 Ok(Self {
376 format,
377 samples,
378 channels: channel_count,
379 sample_rate: header.sample_rate,
380 bits_per_sample: if matches!(format, AudioFormat::EightBitOffsetBinary) {
381 8
382 } else {
383 16
384 },
385 })
386 }
387 }
388 }
389}