ittech/
parser.rs

1//! Parsing functions
2
3use 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
36/// Parse Impulse Tracker module file (.it)
37pub 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    // Offsets are relative to the start of the file, use the whole input every time.
44    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            // Pattern parsing is inlined from `offset_list` because we need to handle the special
50            // case of offset 0 here.
51            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
104/// Parse Impulse Tracker instrument file (.iti)
105pub 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
117/// Parse Impulse Tracker sample file (.its)
118pub 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    // Parse static parts.
132    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    // Parse dynamic parts of the header.
157    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    // Check ranged values and canonicalize out-of-range values.
166    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            // Invalid values get skipped.
230            _ => {
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                    // Explicit map to None, but don't override previous mapping.
333                }
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        // TODO canonicalize/skip invalid values
437        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        // TODO canonicalize/skip invalid values
450        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        // TODO add support for more sample formats, do not panic
494
495        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}