torque_tracker_engine/file/impulse_format/
sample.rs1use std::num::NonZeroU32;
4
5use crate::file::{
6 err::{LoadDefect, LoadErr},
7 InFilePtr,
8};
9
10use super::header;
11
12#[derive(Debug, Default, Clone, Copy)]
13pub enum VibratoWave {
14 #[default]
15 Sine = 0,
16 RampDown = 1,
17 Square = 2,
18 Random = 3,
19}
20
21impl TryFrom<u8> for VibratoWave {
22 type Error = u8;
23
24 fn try_from(value: u8) -> Result<Self, Self::Error> {
25 match value {
26 0 => Ok(Self::Sine),
27 1 => Ok(Self::RampDown),
28 2 => Ok(Self::Square),
29 3 => Ok(Self::Random),
30 _ => Err(value),
31 }
32 }
33}
34
35enum ImpulseSampleBitWidth {
36 Seven,
37 Eight,
38 Sixteen,
39 TwentyFour,
40 ThirtyTwo,
41}
42
43impl From<ImpulseSampleBitWidth> for usize {
44 fn from(value: ImpulseSampleBitWidth) -> Self {
45 match value {
46 ImpulseSampleBitWidth::Seven => 7,
47 ImpulseSampleBitWidth::Eight => 8,
48 ImpulseSampleBitWidth::Sixteen => 16,
49 ImpulseSampleBitWidth::TwentyFour => 24,
50 ImpulseSampleBitWidth::ThirtyTwo => 32,
51 }
52 }
53}
54
55enum ImpulseSampleChannels {
56 Mono,
57 StereoInterleaved,
58 StereoSplit,
59}
60
61enum ImpulseSampleEndianness {
62 Little,
63 Big,
64}
65
66enum ImpulseSampleEncoding {
68 PCMSigned,
69 PCMUnsigned,
70 PCMDelta,
71 IT2_14Comp,
72 IT2_15Comp,
73 Ams,
74 Dmf,
75 Mdl,
76 Ptm,
77 PCM16,
78}
79
80#[derive(Debug, Copy, Clone)]
82pub struct SampleFormatConvert(u8);
83
84impl SampleFormatConvert {
85 pub fn is_signed(self) -> bool {
87 (self.0 & 0x1) != 0
88 }
89 pub fn is_big_endian(self) -> bool {
91 (self.0 & 0x2) != 0
92 }
93 pub fn delta_samples(self) -> bool {
95 (self.0 & 0x4) != 0
96 }
97 pub fn byte_delta(self) -> bool {
99 (self.0 & 0x8) != 0
100 }
101 pub fn tx_wave_12bit(self) -> bool {
103 (self.0 & 0x10) != 0
104 }
105 pub fn should_show_stereo_prompt(self) -> bool {
107 (self.0 & 0x20) != 0
108 }
109}
110
111#[derive(Debug, Copy, Clone)]
112pub struct SampleFormatFlags(u8);
113
114impl SampleFormatFlags {
115 pub fn has_sample(self) -> bool {
116 (self.0 & 0x01) != 0
117 }
118 pub fn is_16bit(self) -> bool {
119 (self.0 & 0x02) != 0
120 }
121 pub fn is_8bit(self) -> bool {
122 !self.is_16bit()
123 }
124 pub fn is_steroe(self) -> bool {
125 (self.0 & 0x04) != 0
126 }
127 pub fn is_compressed(self) -> bool {
128 (self.0 & 0x08) != 0
129 }
130 pub fn uses_loop(self) -> bool {
131 (self.0 & 0x10) != 0
132 }
133 pub fn uses_sustain_loop(self) -> bool {
134 (self.0 & 0x20) != 0
135 }
136 pub fn ping_pong_loop(self) -> bool {
137 (self.0 & 0x40) != 0
138 }
139 pub fn forward_loop(self) -> bool {
140 !self.ping_pong_loop()
141 }
142 pub fn ping_pong_sustain_loop(self) -> bool {
143 (self.0 & 0x80) != 0
144 }
145 pub fn forward_sustain_loop(self) -> bool {
146 !self.ping_pong_sustain_loop()
147 }
148}
149
150#[derive(Debug)]
151pub struct ImpulseSampleHeader {
152 pub dos_filename: Box<[u8]>,
153 pub sample_name: String,
154 pub global_volume: u8,
155 pub flags: SampleFormatFlags,
156 pub default_volume: u8,
157 pub convert: SampleFormatConvert,
158 pub default_pan: u8,
159 pub length: u32,
161 pub loop_start: u32,
162 pub loop_end: u32,
163 pub c5_speed: u32,
164 pub sustain_start: u32,
165 pub sustain_end: u32,
166 pub data_ptr: InFilePtr,
167 pub vibrato_speed: u8,
168 pub vibrato_depth: u8,
169 pub vibrato_type: VibratoWave,
170 pub vibrato_rate: u8,
171}
172
173impl ImpulseSampleHeader {
174 const SIZE: usize = 80;
175
176 pub fn parse<H: FnMut(LoadDefect)>(
177 buf: &[u8; Self::SIZE],
178 defect_handler: &mut H,
179 ) -> Result<Self, LoadErr> {
180 if !buf.starts_with(b"IMPS") {
181 return Err(LoadErr::Invalid);
182 }
183
184 let dos_filename = buf[0x4..=0xF].to_vec().into_boxed_slice();
185 if buf[0x10] != 0 {
186 return Err(LoadErr::Invalid);
187 }
188
189 let global_volume = if buf[0x11] > 64 {
190 defect_handler(LoadDefect::OutOfBoundsValue);
191 64
192 } else {
193 buf[0x11]
194 };
195
196 let flags = SampleFormatFlags(buf[0x12]);
197 let default_volume = buf[0x13];
198 let sample_name = {
199 let str = buf[0x14..=0x2D].split(|b| *b == 0).next().unwrap().to_vec();
200 let str = String::from_utf8(str);
201 if str.is_err() {
202 defect_handler(LoadDefect::InvalidText);
203 }
204 str.unwrap_or_default()
205 };
206
207 let convert = SampleFormatConvert(buf[0x2E]);
208
209 let default_pan = buf[0x2F];
210
211 let length = u32::from_le_bytes([buf[0x30], buf[0x31], buf[0x32], buf[0x33]]);
213 let loop_start = u32::from_le_bytes([buf[0x34], buf[0x35], buf[0x36], buf[0x37]]);
214 let loop_end = u32::from_le_bytes([buf[0x38], buf[0x39], buf[0x3A], buf[0x3B]]);
215
216 let c5_speed = {
218 let speed = u32::from_le_bytes([buf[0x3C], buf[0x3D], buf[0x3E], buf[0x3F]]);
219 if speed > 9999999 {
220 defect_handler(LoadDefect::OutOfBoundsValue);
221 9999999 / 2
223 } else {
224 speed
225 }
226 };
227
228 let sustain_start = u32::from_le_bytes([buf[0x40], buf[0x41], buf[0x42], buf[0x43]]);
230 let sustain_end = u32::from_le_bytes([buf[0x44], buf[0x45], buf[0x46], buf[0x47]]);
231
232 let data_ptr = {
233 let value = u32::from_le_bytes([buf[0x48], buf[0x49], buf[0x4A], buf[0x4B]]);
234 if value < header::ImpulseHeader::BASE_SIZE as u32 {
235 return Err(LoadErr::Invalid);
236 }
237 InFilePtr(NonZeroU32::new(value).unwrap())
238 };
239
240 let vibrato_speed = if buf[0x4C] > 64 {
241 defect_handler(LoadDefect::OutOfBoundsValue);
242 32
243 } else {
244 buf[0x4C]
245 };
246
247 let vibrato_depth = if buf[0x4D] > 64 {
248 defect_handler(LoadDefect::OutOfBoundsValue);
249 32
250 } else {
251 buf[0x4D]
252 };
253
254 let vibrato_rate = if buf[0x4E] > 64 {
255 defect_handler(LoadDefect::OutOfBoundsValue);
256 32
257 } else {
258 buf[0x4E]
259 };
260
261 let vibrato_type = {
262 let wave = VibratoWave::try_from(buf[0x4F]);
263 if wave.is_err() {
264 defect_handler(LoadDefect::OutOfBoundsValue);
265 }
266 wave.unwrap_or_default()
267 };
268
269 Ok(Self {
270 dos_filename,
271 sample_name,
272 global_volume,
273 flags,
274 default_volume,
275 convert,
276 default_pan,
277 length,
278 loop_start,
279 loop_end,
280 c5_speed,
281 sustain_start,
282 sustain_end,
283 data_ptr,
284 vibrato_speed,
285 vibrato_depth,
286 vibrato_type,
287 vibrato_rate,
288 })
289 }
290}