1use std::{
2 borrow::Cow,
3 error::Error,
4 fmt::Display,
5 io::{self, Cursor},
6 str::{self, Utf8Error},
7};
8
9use byteorder::{LittleEndian, ReadBytesExt};
10
11use crate::SampleId;
12
13#[derive(Debug, Clone)]
14pub struct Decoder<'a> {
15 samples_count: u32,
16 comments: Comments<'a>,
17 samples: Vec<Sample<'a>>,
18}
19
20#[derive(Debug)]
21pub enum DecodeError {
22 InvalidData(io::Error),
23 InvalidSignature,
24 SampleCountMismatch {
25 expected_count: u32,
26 found_count: u32,
27 },
28}
29
30impl Display for DecodeError {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 DecodeError::InvalidData(err) => err.fmt(f),
34 DecodeError::InvalidSignature => write!(f, "invalid signature"),
35 DecodeError::SampleCountMismatch {
36 expected_count,
37 found_count,
38 } => write!(f, "expected {expected_count} samples, found {found_count}"),
39 }
40 }
41}
42
43impl Error for DecodeError {
44 fn source(&self) -> Option<&(dyn Error + 'static)> {
45 match self {
46 DecodeError::InvalidData(ref err) => Some(err),
47 _ => None,
48 }
49 }
50}
51
52impl From<io::Error> for DecodeError {
53 fn from(error: io::Error) -> Self {
54 DecodeError::InvalidData(error)
55 }
56}
57
58impl<'a> Decoder<'a> {
59 pub fn decode(data: &'a [u8]) -> Result<Self, DecodeError> {
60 if &data[0..12] != b"ElecbyteSnd\0" {
61 return Err(DecodeError::InvalidSignature);
62 }
63
64 let mut bytes = Cursor::new(&data);
65 bytes.set_position(16);
66
67 let samples_count = bytes.read_u32::<LittleEndian>()?;
68 let first_subfile_offset = bytes.read_u32::<LittleEndian>()?;
69 let subfile_header_size = 15;
70 let comments = Comments(&data[24..511]);
71
72 let mut samples = Vec::with_capacity(samples_count as usize);
73 let mut next_subfile_offset = Some(first_subfile_offset);
74 let total_size = data.len() as u32;
75 while let Some(offset) = next_subfile_offset.take() {
76 bytes.set_position(offset.into());
77
78 next_subfile_offset = bytes.read_u32::<LittleEndian>().ok().and_then(|n| match n {
79 n if n > 0 && n + subfile_header_size <= total_size => Some(n),
80 _ => None,
81 });
82
83 let size = bytes.read_u32::<LittleEndian>()? as usize;
84 let group = bytes.read_u32::<LittleEndian>()?;
85 let number = bytes.read_u32::<LittleEndian>()?;
86
87 let data_offset = bytes.position() as usize;
88
89 samples.push(Sample {
90 id: SampleId { group, number },
91 data: Cow::Borrowed(&data[data_offset..data_offset + size]),
92 });
93 }
94
95 if samples.len() != samples_count as usize {
96 return Err(DecodeError::SampleCountMismatch {
97 expected_count: samples_count,
98 found_count: samples.len() as u32,
99 });
100 }
101
102 Ok(Decoder {
103 samples_count,
104 comments,
105 samples,
106 })
107 }
108
109 pub fn comments(&self) -> &Comments {
110 &self.comments
111 }
112
113 pub fn samples_count(&self) -> u32 {
114 self.samples_count
115 }
116
117 pub fn samples(&self) -> impl Iterator<Item = &Sample> {
118 self.samples.iter()
119 }
120}
121
122impl<'a> IntoIterator for Decoder<'a> {
123 type Item = Sample<'a>;
124 type IntoIter = std::vec::IntoIter<Self::Item>;
125
126 fn into_iter(self) -> Self::IntoIter {
127 self.samples.into_iter()
128 }
129}
130
131#[derive(Debug, Clone)]
132pub struct Sample<'a> {
133 id: SampleId,
134 data: Cow<'a, [u8]>,
135}
136
137impl<'a> Sample<'a> {
138 pub fn id(&self) -> SampleId {
139 self.id
140 }
141
142 pub fn raw_data(&self) -> &[u8] {
143 &self.data
144 }
145
146 pub fn to_owned(&self) -> Sample<'static> {
147 Sample {
148 id: self.id,
149 data: Cow::Owned(self.raw_data().to_owned()),
150 }
151 }
152}
153
154#[derive(Debug, Clone, PartialEq, Eq)]
155pub struct Comments<'a>(&'a [u8]);
156
157impl<'a> Comments<'a> {
158 pub fn as_str(&self) -> Result<&str, Utf8Error> {
159 Ok(str::from_utf8(self.0)?.trim_end_matches('\u{0}'))
160 }
161}