torque_tracker_engine/file/impulse_format/
pattern.rs1use crate::file::err;
2use crate::file::err::LoadDefect;
3use crate::project::event_command::NoteCommand;
4use crate::project::note_event::{Note, NoteEvent, VolumeEffect};
5use crate::project::pattern::{InPatternPosition, Pattern};
6
7pub fn parse_pattern<R: std::io::Read + std::io::Seek, H: FnMut(LoadDefect)>(
11 reader: &mut R,
12 defect_handler: &mut H,
13) -> Result<Pattern, err::LoadErr> {
14 const PATTERN_HEADER_SIZE: usize = 8;
15
16 let read_start = reader.stream_position()?;
17
18 let (length, num_rows) = {
19 let mut header = [0; PATTERN_HEADER_SIZE];
20 reader.read_exact(&mut header)?;
21 (
22 u64::from(u16::from_le_bytes([header[0], header[1]])) + PATTERN_HEADER_SIZE as u64,
23 u16::from_le_bytes([header[2], header[3]]),
24 )
25 };
26
27 if length >= 64_000 {
29 return Err(err::LoadErr::Invalid);
30 }
31
32 if !(32..=200).contains(&num_rows) {
33 return Err(err::LoadErr::Invalid);
34 }
35
36 let mut pattern = Pattern::new(num_rows);
37
38 let mut row_num: u16 = 0;
39
40 let mut last_mask = [0; 64];
41 let mut last_event = [NoteEvent::default(); 64];
42
43 let mut scratch = [0; 1];
44
45 while row_num < num_rows && reader.stream_position()? - read_start < length {
46 let channel_variable = scratch[0];
47
48 if channel_variable == 0 {
49 row_num += 1;
50 continue;
51 }
52
53 let channel = (channel_variable - 1) & 63; let channel_id = usize::from(channel);
55
56 let maskvar = if (channel_variable & 0b10000000) != 0 {
57 reader.read_exact(&mut scratch)?;
58 let val = scratch[0];
59 last_mask[channel_id] = val;
60 val
61 } else {
62 last_mask[channel_id]
63 };
64
65 let mut event = NoteEvent::default();
66
67 if (maskvar & 0b00000001) != 0 {
69 reader.read_exact(&mut scratch)?;
70 let note = match Note::new(scratch[0]) {
71 Ok(n) => n,
72 Err(_) => {
73 defect_handler(LoadDefect::OutOfBoundsValue);
74 Note::default()
75 }
76 };
77
78 event.note = note;
79 last_event[channel_id].note = note;
80 }
81
82 if (maskvar & 0b00000010) != 0 {
84 reader.read_exact(&mut scratch)?;
85 let instrument = scratch[0];
86
87 event.sample_instr = instrument;
88 last_event[channel_id].sample_instr = instrument;
89 }
90
91 if (maskvar & 0b00000100) != 0 {
93 reader.read_exact(&mut scratch)?;
94 let vol_pan_raw = scratch[0];
95 let vol_pan = match vol_pan_raw.try_into() {
96 Ok(v) => v,
97 Err(_) => {
98 defect_handler(LoadDefect::OutOfBoundsValue);
99 VolumeEffect::default()
100 }
101 };
102
103 last_event[channel_id].vol = vol_pan;
104 event.vol = vol_pan;
105 }
106
107 if (maskvar & 0b00001000) != 0 {
109 reader.read_exact(&mut scratch)?;
110 let command = scratch[0];
111 reader.read_exact(&mut scratch)?;
112 let cmd_val = scratch[0];
113
114 let cmd = match NoteCommand::try_from((command, cmd_val)) {
115 Ok(cmd) => cmd,
116 Err(_) => {
117 defect_handler(LoadDefect::OutOfBoundsValue);
118 NoteCommand::default()
119 }
120 };
121
122 last_event[channel_id].command = cmd;
123 event.command = cmd;
124 }
125
126 if (maskvar & 0b00010000) != 0 {
128 event.note = last_event[channel_id].note;
129 }
130
131 if (maskvar & 0b00100000) != 0 {
133 event.sample_instr = last_event[channel_id].sample_instr;
134 }
135
136 if (maskvar & 0b01000000) != 0 {
138 event.vol = last_event[channel_id].vol;
139 }
140
141 if (maskvar & 0b10000000) != 0 {
143 event.command = last_event[channel_id].command;
144 }
145
146 pattern.set_event(
147 InPatternPosition {
148 row: row_num,
149 channel,
150 },
151 event,
152 );
153 }
154
155 if pattern.row_count() == row_num {
156 Ok(pattern)
157 } else {
158 Err(err::LoadErr::BufferTooShort)
159 }
160}