1use crate::data::*;
4use crate::error::ContextError;
5use bitflags::bitflags;
6use nom::bytes::complete::{tag, take};
7use nom::combinator::{all_consuming, map};
8use nom::error::{ErrorKind, ParseError};
9use nom::multi::{count, many_till};
10use nom::number::complete::{be_i16, le_i16, le_i8, le_u16, le_u32, le_u8};
11use nom::sequence::tuple;
12use nom::{Err, IResult};
13use pattern::pattern;
14use std::convert::{TryFrom, TryInto};
15use std::ops::RangeInclusive;
16
17
18macro_rules! info {
19 ( $($tt:tt)* ) => {
20 #[cfg(feature = "tracing")]
21 ::tracing::info!($($tt)*);
22 };
23}
24
25
26mod pattern;
27pub(crate) mod scan;
28mod util;
29
30pub use pattern::parse_effect as effect;
31
32use util::*;
33pub use scan::scan;
34
35
36pub fn module_file<'i, E>(input: &'i [u8]) -> Result<Module, Err<E>>
38where
39 E: ParseError<&'i [u8]> + ContextError<&'i [u8]> + 'i,
40{
41 let (_, header) = module_header(input)?;
42
43 let (_, instruments) = offset_list(instrument, header.instrument_offsets)(input)?;
45 let (_, sample_headers) = offset_list(sample_header, header.sample_offsets)(input)?;
46 let patterns = {
47 let mut patterns = Vec::with_capacity(header.pattern_offsets.len());
48 for offset in header.pattern_offsets.into_iter().map(<_>::cast) {
49 if offset == 0 {
52 patterns.push(Pattern {
53 active_channels: ActiveChannels::empty(),
54 rows: vec![Row::empty(); 64]
55 });
56 continue
57 }
58 if offset >= input.len() {
59 return Err(Err::Error(E::from_error_kind(input, ErrorKind::Eof)));
60 }
61 let (_, pat) = pattern(&input[offset..])?;
62 patterns.push(pat);
63 }
64 patterns
65 };
66
67 let samples = sample_headers.into_iter()
68 .map(|header| sample_data(header, input))
69 .collect::<Result<Vec<_>, _>>()?;
70
71 let message = {
72 let offset = header.message_offset.cast::<usize>();
73 if offset == 0 || offset >= input.len() {
74 String::new()
75 } else {
76 let (_, bytes) = take(header.message_length.cast::<usize>())(input)?;
77 String::from_utf8_lossy(bytes)
78 .to_string()
79 }
80 };
81
82 Ok(Module {
83 name: header.name,
84 highlight: header.highlight,
85 made_with_version: header.made_with_version,
86 compatible_with_version: header.compatible_with_version,
87 flags: header.flags,
88 global_volume: header.global_volume,
89 sample_volume: header.sample_volume,
90 speed: header.speed,
91 tempo: header.tempo,
92 pan_separation: header.pan_separation,
93 pitch_wheel_depth: header.pitch_wheel_depth,
94 message,
95 orders: header.orders,
96 init_channel_panning: header.init_channel_panning,
97 init_channel_volume: header.init_channel_volume,
98 instruments,
99 samples,
100 patterns,
101 })
102}
103
104pub fn instrument_file<'i, E>(input: &'i [u8]) -> Result<InstrumentFile, Err<E>>
106where
107 E: ParseError<&'i [u8]> + ContextError<&'i [u8]>,
108{
109 let (input2, instrument) = instrument(input)?;
110 let (_, sample_headers) = count(sample_header, instrument.number_of_samples.into())(input2)?;
111 let samples = sample_headers.into_iter()
112 .map(|header| sample_data(header, input))
113 .collect::<Result<Vec<_>, _>>()?;
114 Ok(InstrumentFile { instrument, samples })
115}
116
117pub fn sample_file<'i, E>(input: &'i [u8]) -> Result<Sample, Err<E>>
119where
120 E: ParseError<&'i [u8]> + ContextError<&'i [u8]>,
121{
122 let (_, header) = sample_header(input)?;
123 sample_data(header, input)
124}
125
126
127fn module_header<'i, E>(input: &'i [u8]) -> IResult<&'i [u8], ModuleHeader, E>
128where
129 E: ParseError<&'i [u8]> + ContextError<&'i [u8]>,
130{
131 let (input, _) = tag(b"IMPM")(input)?;
133 let (input, songname) = name(input)?;
134 let (input, highlight_minor) = le_u8(input)?;
135 let (input, highlight_major) = le_u8(input)?;
136 let (input, ordnum) = le_u16(input)?;
137 let (input, insnum) = le_u16(input)?;
138 let (input, smpnum) = le_u16(input)?;
139 let (input, patnum) = le_u16(input)?;
140 let (input, cwtv) = le_u16(input)?;
141 let (input, cmwt) = le_u16(input)?;
142 let (input, flags) = le_u16(input)?;
143 let (input, special) = le_u16(input)?;
144 let (input, globalvol) = le_u8(input)?;
145 let (input, mv) = le_u8(input)?;
146 let (input, speed) = le_u8(input)?;
147 let (input, tempo) = le_u8(input)?;
148 let (input, sep) = le_u8(input)?;
149 let (input, pwd) = le_u8(input)?;
150 let (input, msglength) = le_u16(input)?;
151 let (input, msgoffset) = le_u32(input)?;
152 let (input, _reserved) = le_u32(input)?;
153 let (input, chnpan) = byte_array(input)?;
154 let (input, chnvol) = byte_array(input)?;
155
156 let (input, orders) = count(order, ordnum.into())(input)?;
158 let orders = orders.into_iter().flatten().collect();
159 let (input, ins_offsets) = count(le_u32, insnum.into())(input)?;
160 let (input, sam_offsets) = count(le_u32, smpnum.into())(input)?;
161 let (_rest, pat_offsets) = count(le_u32, patnum.into())(input)?;
162
163 let flags = ModuleFlags::from_parts(flags, special);
164
165 fn ranged(value: u8, range: RangeInclusive<u8>, or_else: impl FnOnce(u8) -> u8) -> u8 {
167 if range.contains(&value) {
168 value
169 } else {
170 let value = or_else(value);
171 assert!(range.contains(&value), "BUG: fallback value is also out of range");
172 value
173 }
174 }
175 let globalvol = ranged(globalvol, 0..=128, |_| {
176 info!(globalvol, "global_volume cannot be more than 128, clipping");
177 128
178 });
179 let mv = ranged(mv, 0..=128, |_| {
180 info!(mv, "sample_volume cannot be more than 128, clipping");
181 128
182 });
183 let speed = ranged(speed, 1..=255, |_| {
184 info!("speed must be at least 1, using default of 6");
185 6
186 });
187 let tempo = ranged(tempo, 31..=255, |_| {
188 info!("tempo must be at least 31, using default of 120");
189 120
190 });
191 let sep = ranged(sep, 0..=128, |_| {
192 info!("pan_separation cannot be more than 128, clipping");
193 128
194 });
195
196 Ok((
197 input,
198 ModuleHeader {
199 name: songname,
200 highlight: (highlight_major, highlight_minor),
201 made_with_version: cwtv,
202 compatible_with_version: cmwt,
203 flags,
204 global_volume: globalvol.cast(),
205 sample_volume: mv.cast(),
206 speed: speed.cast(),
207 tempo: tempo.cast(),
208 pan_separation: sep.cast(),
209 pitch_wheel_depth: pwd,
210 message_length: msglength,
211 message_offset: msgoffset,
212 init_channel_panning: chnpan,
213 init_channel_volume: chnvol,
214 orders,
215 instrument_offsets: ins_offsets,
216 sample_offsets: sam_offsets,
217 pattern_offsets: pat_offsets,
218 },
219 ))
220}
221
222fn order<'i, E: ParseError<&'i [u8]> + ContextError<&'i [u8]>>(input: &'i [u8]) -> IResult<&'i [u8], Option<Order>, E> {
223 map(
224 le_u8,
225 |value| match value {
226 0 ..= 199 => Some(Order::Index(value.cast())),
227 254 => Some(Order::Separator),
228 255 => Some(Order::EndOfSong),
229 _ => {
231 info!(value, "order value is out of range 0..=199,254,255, skipping");
232 None
233 }
234 },
235 )(input)
236}
237
238fn name<'i, E: ParseError<&'i [u8]> + ContextError<&'i [u8]>>(input: &'i [u8]) -> IResult<&'i [u8], Name, E> {
239 let (input, bytes) = byte_array(input)?;
240 Ok((input, Name { bytes }))
241}
242
243fn dosfilename<'i, E: ParseError<&'i [u8]> + ContextError<&'i [u8]>>(input: &'i [u8]) -> IResult<&'i [u8], DosFilename, E> {
244 let (input, bytes) = byte_array(input)?;
245 Ok((input, DosFilename { bytes }))
246}
247
248fn instrument<'i, E: ParseError<&'i [u8]> + ContextError<&'i [u8]>>(input: &'i [u8]) -> IResult<&'i [u8], Instrument, E> {
249 let (input, _) = tag(b"IMPI")(input)?;
250 let (input, filename) = dosfilename(input)?;
251 let (input, nna) = le_u8(input)?;
252 let (input, dct) = le_u8(input)?;
253 let (input, dca) = le_u8(input)?;
254 let (input, fadeout) = le_u16(input)?;
255 let (input, pps) = le_i8(input)?;
256 let (input, ppc) = le_u8(input)?;
257 let (input, gbv) = le_u8(input)?;
258 let (input, dfp) = le_u8(input)?;
259 let (input, rv) = le_u8(input)?;
260 let (input, rp) = le_u8(input)?;
261 let (input, trkver) = le_u16(input)?;
262 let (input, nos) = le_u8(input)?;
263 let (input, _reserved) = le_u8(input)?;
264 let (input, name) = name(input)?;
265 let (input, ifc) = le_u8(input)?;
266 let (input, ifr) = le_u8(input)?;
267 let (input, mch) = le_u8(input)?;
268 let (input, mpr) = le_u8(input)?;
269 let (input, mbank) = byte_array(input)?;
270 let (input, sample_map) = sample_map(input)?;
271 let (input, volenv) = envelope(input)?;
272 let (input, panenv) = envelope(input)?;
273 let (input, pitchenv) = envelope(input)?;
274 let (input, _dummy) = byte_array::<_, 4>(input)?;
275
276 let mut flags = InstrumentFlags::default();
277
278 if dfp & Instrument::dfp_ignorePanning == 0 {
279 flags |= InstrumentFlags::ENABLE_PANNING;
280 }
281 let dfp = dfp & !Instrument::dfp_ignorePanning;
282
283 if ifc & Instrument::ifc_enableCutoff != 0 {
284 flags |= InstrumentFlags::ENABLE_FILTER_CUTOFF;
285 }
286 let ifc = ifc & !Instrument::ifc_enableCutoff;
287
288 if ifr & Instrument::ifr_enableResonance != 0 {
289 flags |= InstrumentFlags::ENABLE_FILTER_RESONANCE;
290 }
291 let ifr = ifr & !Instrument::ifr_enableResonance;
292
293 Ok((
294 input,
295 Instrument {
296 name,
297 filename,
298 flags,
299 new_note_action: nna,
300 duplicate_check_type: dct,
301 duplicate_check_action: dca,
302 instrument_fadeout: fadeout.try_into().unwrap(),
303 pitch_pan_separation: pps,
304 pitch_pan_centre: ppc,
305 global_volume: gbv,
306 default_panning: dfp.try_into().unwrap(),
307 random_volume_variation: rv.try_into().unwrap(),
308 random_panning_variation: rp.try_into().unwrap(),
309 trkver,
310 number_of_samples: nos,
311 initial_filter_cutoff: ifc.try_into().unwrap(),
312 initial_filter_resonance: ifr.try_into().unwrap(),
313 mch,
314 mpr,
315 mbank,
316 sample_map,
317 volume_envelope: volenv,
318 panning_envelope: panenv,
319 pitch_filter_envelope: pitchenv,
320 },
321 ))
322}
323
324fn sample_map<'i, E: ParseError<&'i [u8]> + ContextError<&'i [u8]>>(input: &'i [u8]) -> IResult<&'i [u8], SampleMap, E> {
325 scan_count(
326 120,
327 tuple((le_u8, le_u8)),
328 SampleMap::default(),
329 |sm: &mut SampleMap, (note, sample)| {
330 match (note, sample) {
331 (0..=119, 0) => {
332 }
334 (0..=119, 1..=99) => {
335 let sample = SampleId::try_from(sample - 1).unwrap();
336 sm.map[usize::from(note)] = Some(sample);
337 }
338 _ => {
339 info!(
340 note, sample,
341 "note or sample out of range 0..=119 and 0..=99 respectively"
342 );
343 }
344 }
345 },
346 )(input)
347}
348
349fn envelope<'i, E: ParseError<&'i [u8]> + ContextError<&'i [u8]>>(input: &'i [u8]) -> IResult<&'i [u8], Envelope, E> {
350 let (input, flags) = le_u8(input)?;
351 let (input, num) = le_u8(input)?;
352 let (input, lpb) = le_u8(input)?;
353 let (input, lpe) = le_u8(input)?;
354 let (input, slb) = le_u8(input)?;
355 let (input, sle) = le_u8(input)?;
356 let (input, data): (_, [_; 25]) = array(node)(input)?;
357 let (input, _reserved) = le_u8(input)?;
358
359 let envelope_loop;
360 let sustain_loop;
361 let num = if num > 25 {
362 info!(len = num, "envelope size out of range 0..=25, using 0");
363 envelope_loop = None;
364 sustain_loop = None;
365 0
366 } else {
367 envelope_loop = if lpb <= lpe && lpe < num {
368 Some(EnvelopeLoop { start: lpb, end: lpe })
369 } else {
370 info!(
371 start = lpb, end = lpe, len = num,
372 "invalid loop points, ignoring envelope loop",
373 );
374 None
375 };
376 sustain_loop = if slb <= sle && sle < num {
377 Some(EnvelopeLoop { start: slb, end: sle })
378 } else {
379 info!(
380 start = lpb, end = lpe, len = num,
381 "invalid loop points, ignoring sustain loop",
382 );
383 None
384 };
385 num
386 };
387
388 let flags = EnvelopeFlags::from_bits_truncate(flags);
389 let nodes = Vec::from(&data[..usize::from(num)]);
390
391 Ok((
392 input,
393 Envelope {
394 flags,
395 envelope_loop,
396 sustain_loop,
397 nodes,
398 },
399 ))
400}
401
402fn node<'i, E: ParseError<&'i [u8]> + ContextError<&'i [u8]>>(input: &'i [u8]) -> IResult<&'i [u8], Node, E> {
403 let (input, value) = le_i8(input)?;
404 let (input, tick) = le_u16(input)?;
405 Ok((input, Node { value, tick }))
406}
407
408fn sample_header<'i, E>(input: &'i [u8]) -> IResult<&'i [u8], SampleHeader, E>
409where
410 E: ParseError<&'i [u8]> + ContextError<&'i [u8]>,
411{
412 let (input, _) = tag(b"IMPS")(input)?;
413 let (input, filename) = dosfilename(input)?;
414 let (input, gvl) = le_u8(input)?;
415 let (input, flags) = le_u8(input)?;
416 let (input, vol) = le_u8(input)?;
417 let (input, name) = name(input)?;
418 let (input, cvt) = le_u8(input)?;
419
420 let flags = SampleFlags::from_parts(flags, cvt);
421
422 let (input, dfp) = le_u8(input)?;
423 let (input, length) = le_u32(input)?;
424 let (input, loopbegin) = le_u32(input)?;
425 let (input, loopend) = le_u32(input)?;
426 let (input, c5speed) = le_u32(input)?;
427 let (input, susloopbegin) = le_u32(input)?;
428 let (input, susloopend) = le_u32(input)?;
429 let (input, samplepointer) = le_u32(input)?;
430 let (input, vis) = le_u8(input)?;
431 let (input, vid) = le_u8(input)?;
432 let (input, vir) = le_u8(input)?;
433 let (input, vit) = le_u8(input)?;
434
435 let loop_ = if flags.contains(SampleFlags::LOOP) {
436 assert!(loopbegin < loopend);
438 assert!(loopend <= length);
439 Some(SampleLoop {
440 start: loopbegin,
441 end: loopend,
442 bidi: flags.contains(SampleFlags::BIDI_LOOP),
443 })
444 } else {
445 None
446 };
447
448 let sustain_loop = if flags.contains(SampleFlags::SUSTAIN) {
449 assert!(susloopbegin < susloopend);
451 assert!(susloopend <= length);
452 Some(SampleLoop {
453 start: susloopbegin,
454 end: susloopend,
455 bidi: flags.contains(SampleFlags::BIDI_SUSTAIN),
456 })
457 } else {
458 None
459 };
460
461 Ok((
462 input,
463 SampleHeader {
464 name,
465 filename,
466 global_volume: gvl,
467 default_volume: vol,
468 default_panning: dfp,
469 loop_,
470 sustain_loop,
471 samplerate_c5: c5speed,
472 vibrato_speed: vis,
473 vibrato_depth: vid,
474 vibrato_rate: vir,
475 vibrato_type: vit,
476
477 flags,
478 data_offset: samplepointer,
479 data_length: length,
480 },
481 ))
482}
483
484fn sample_data<'i, E>(header: SampleHeader, input: &'i [u8]) -> Result<Sample, Err<E>>
485where
486 E: ParseError<&'i [u8]> + ContextError<&'i [u8]>,
487{
488 let flags = header.flags;
489
490 let data = if !flags.contains(SampleFlags::DATA_PRESENT) {
491 None
492 } else {
493 assert!(flags.contains(SampleFlags::DATA_SIGNED), "only signed samples are supported");
496 assert!(!flags.contains(SampleFlags::STEREO), "only mono samples supported");
497
498 assert!(!flags.contains(SampleFlags::COMPRESSED), "sample compression is not supported");
499 assert!(!flags.contains(SampleFlags::OPL_INSTRUMENT), "OPL instrument is not supported");
500 assert!(!flags.contains(SampleFlags::EXTERNAL_SAMPLE), "external samples are not supported");
501 assert!(!flags.contains(SampleFlags::ADPCM_SAMPLE), "MODPlugin :(");
502 assert!(!flags.contains(SampleFlags::DELTA), "delta samples are not supported");
503 assert!(!flags.contains(SampleFlags::PTM8_TO_16), "PTM loader is not supported");
504
505 let offset = header.data_offset.cast();
506 let length = header.data_length.cast();
507 let input = &input[offset..];
508
509 let (_, data) = match (
510 flags.contains(SampleFlags::DATA_16BIT),
511 flags.contains(SampleFlags::DATA_BIG_ENDIAN),
512 ) {
513 (true, true) => count(map(be_i16, |s| f32::from(s) / f32::from(i16::MAX)), length)(input)?,
514 (true, false) => count(map(le_i16, |s| f32::from(s) / f32::from(i16::MAX)), length)(input)?,
515 (false, _) => count(map(le_i8, |s| f32::from(s) / f32::from(i8::MAX)), length)(input)?,
516 };
517
518 Some(data)
519 };
520
521 Ok(Sample {
522 name: header.name,
523 filename: header.filename,
524 global_volume: header.global_volume,
525 default_volume: header.default_volume,
526 default_panning: header.default_panning,
527 loop_: header.loop_,
528 sustain_loop: header.sustain_loop,
529 samplerate_c5: header.samplerate_c5,
530 vibrato_speed: header.vibrato_speed,
531 vibrato_depth: header.vibrato_depth,
532 vibrato_rate: header.vibrato_rate,
533 vibrato_type: header.vibrato_type,
534 data,
535 })
536}