l6t_file/
decoder.rs

1use std::io;
2use std::io::{Read, Cursor};
3
4use crate::types;
5use crate::iff::Chunk;
6use crate::model::*;
7use crate::bytecast;
8use crate::hex::PrintHex;
9
10pub struct Reader<R: Read> {
11    reader: R,
12    pub little_endian: bool
13}
14
15impl<R: Read> Reader<R> {
16    pub fn new(reader: R, little_endian: bool) -> Self {
17        Reader { reader, little_endian }
18    }
19
20    pub fn read_u8(&mut self) -> Result<u8, io::Error> {
21        let mut v = [0u8; 1];
22        self.reader.read_exact(&mut v)?;
23        Ok(v[0])
24    }
25
26    pub fn read_u8_into(&mut self, buffer: &mut [u8]) -> Result<(), io::Error> {
27       self.reader.read_exact(buffer)?;
28        Ok(())
29    }
30
31    pub fn read_u32(&mut self) -> Result<u32, io::Error> {
32        let mut v = [0u8; 4];
33        self.reader.read_exact(&mut v)?;
34        Ok(match self.little_endian {
35            true => u32::from_le_bytes(v),
36            false => u32::from_be_bytes(v)
37        })
38    }
39
40    pub fn read_utf(&mut self, len: usize) -> Result<String, io::Error> {
41        let mut vec: Vec<u16> = vec![0; len / 2];
42        let mut arr = bytecast::u16_as_ne_mut_bytes(vec.as_mut_slice());
43        self.reader.read_exact(&mut arr)?;
44
45        match self.little_endian {
46            true => {
47                for v in vec.as_mut_slice() {
48                    *v = u16::from_le(*v);
49                }
50            }
51            false => {
52                for v in vec.as_mut_slice() {
53                    *v = u16::from_be(*v);
54                }
55            }
56        }
57        Ok(String::from_utf16_lossy(&vec))
58    }
59
60    pub fn read_utf_z(&mut self, len: usize) -> Result<String, io::Error> {
61        let str = self.read_utf(len)?;
62        Ok(str.split('\0').next().unwrap().to_string())
63    }
64
65    pub fn read_ascii_n(&mut self) -> Result<String, io::Error> {
66        let len = self.read_u8()? as usize;
67        let mut buffer = vec![0u8; len];
68        self.read_u8_into(&mut buffer)?;
69        // it's okay, we're dealing with ascii here
70        let str = unsafe { String::from_utf8_unchecked(buffer) };
71        Ok(str)
72    }
73}
74
75fn reader_for_slice(slice: &[u8], little_endian: bool) -> Reader<Cursor<&[u8]>> {
76    Reader::new(Cursor::new(slice), little_endian)
77}
78
79pub struct Decoder {}
80
81impl Decoder {
82    pub fn read(data: &[u8]) -> Result<L6Patch, io::Error> {
83        let mut chunk = Chunk::from_data(data, None)?;
84
85        if chunk.has_envelope_type(types::FORM, types::L6PA) {
86            // L6T patch file
87            let mut patch: L6Patch = Default::default();
88            let little_endian = false;
89
90            for (type_id, chunk) in chunk.all_chunks() {
91                match type_id {
92                    types::PATC => { patch.models = read_models(chunk)?; },
93                    types::UNFO => { patch.meta = read_meta_tags(chunk)?; },
94                    types::PINF => { patch.target_device = read_target_device(chunk, little_endian)?; },
95                    _ => {}
96                }
97            }
98            return Ok(patch);
99        } else if chunk.has_envelope_type(types::FORM, types::SSLB) {
100            // SoundDiver lib
101
102            // sounddiver sometimes places data outsize the FORM/SSLB container
103            if chunk.all_chunks().is_empty() && data.len() > 12 {
104                chunk = Chunk::from_data_with_size(data, data.len() - 8, None).unwrap()
105            }
106
107            for (type_id, chunk) in chunk.all_chunks() {
108                match type_id {
109                    types::LENT => { read_sslb_entry(chunk)?; },
110                    //types::LHDR | types::WSEQ => {}, // ignore
111                    _ => {}
112                }
113            }
114            return Err(io::Error::new(io::ErrorKind::InvalidInput, "ok"))
115        }
116
117        Err(io::Error::new(io::ErrorKind::InvalidInput, "cannot parse file"))
118    }
119}
120
121
122fn decode_date(str: &str) -> usize {
123    match str.parse::<usize>() {
124        Ok(v) => v * 1000,
125        Err(_) => 0
126    }
127}
128
129fn decode_value(data: &[u32;2]) -> Result<Value, io::Error> {
130    match data[0] {
131        0 => Ok(Value::Int(data[1])),
132        1 => {
133            let f: f32 = unsafe { std::mem::transmute_copy(&data[1]) };
134            Ok(Value::Float(f))
135        }
136        _ => Err(io::Error::new(
137            io::ErrorKind::InvalidData,
138            format!("Unsupported value type {:#x}", data[0]))
139        )
140    }
141}
142
143fn read_meta_tags(chunk: &Chunk) -> Result<MetaTags, io::Error> {
144    let mut tags: MetaTags = Default::default();
145    for (type_id, data) in chunk.data_chunks() {
146        let mut r = reader_for_slice(data, chunk.is_little_endian());
147        match type_id {
148            types::IAUT => tags.author = r.read_utf(data.len())?,
149            types::IGTR => tags.guitarist = r.read_utf(data.len())?,
150            types::IBND => tags.band = r.read_utf(data.len())?,
151            types::ISNG => tags.song = r.read_utf(data.len())?,
152            types::ISTL => tags.style = r.read_utf(data.len())?,
153            types::IPUS => tags.pickup_style = r.read_utf(data.len())?,
154            types::IPUP => tags.pickup_position = r.read_utf(data.len())?,
155            types::IDAT => tags.date = decode_date(&r.read_utf(data.len())?),
156            types::IAMP => tags.amp_name = r.read_utf(data.len())?,
157            types::IAPP => tags.creator_app = r.read_utf(data.len())?,
158            types::IAPV => tags.creator_app_version = r.read_utf(data.len())?,
159            types::ICMT => tags.comments = r.read_utf(data.len())?,
160            _ => {}
161        }
162    }
163
164    Ok(tags)
165}
166
167fn read_target_device(chunk: &Chunk, little_endian: bool) -> Result<TargetDevice, io::Error> {
168    let data = match chunk {
169        Chunk::Data { data, .. } => data,
170        _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Data chunk expected"))
171    };
172
173    if data.len() != 76 { return Err(io::Error::new(io::ErrorKind::InvalidInput, "Incorrect chunk length")); }
174
175    let mut r = reader_for_slice(data, little_endian);
176    if r.read_u32()? != 1 {
177        return Err(io::Error::new(io::ErrorKind::InvalidInput, "Incorrect PINF version"))
178    }
179    let midi_id = r.read_u32()?;
180    let name = r.read_utf_z(32)?;
181    let version = r.read_u32()?;
182    Ok(TargetDevice { midi_id, name, version })
183}
184
185fn read_models(chunk: &Chunk) -> Result<Vec<Model>, io::Error> {
186    let chunks = match chunk {
187        Chunk::Envelope { ref chunks, .. } => chunks,
188        Chunk::Data { .. } => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Container chunk expected"))
189    };
190
191    chunks.iter().map(|chunk| {
192        if chunk.has_envelope_type(types::LIST, types::MODL) {
193            read_model(chunk)
194        } else {
195            Err(io::Error::new(io::ErrorKind::InvalidInput, "Incorrect envelope type"))
196        }
197    }).collect()
198}
199
200
201fn read_model(chunk: &Chunk) -> Result<Model, io::Error> {
202    let mut model: Model = Default::default();
203    let mut params: Vec<ModelParam> = vec![];
204    let little_endian= chunk.is_little_endian();
205
206    for (type_id, chunk) in chunk.data_chunks() {
207        match type_id {
208            types::MINF => model = read_model_info(chunk, little_endian)?,
209            types::PARM => params.push(read_model_param(chunk, little_endian)?),
210            _ => {}
211        }
212    }
213    model.params.extend(params);
214
215    Ok(model)
216}
217
218fn read_model_info(data: &[u8], little_endian: bool) -> Result<Model, io::Error> {
219    let mut r = reader_for_slice(data, little_endian);
220    let mut model: Model = Default::default();
221
222    model.model_id = r.read_u32()?;
223    model.slot_id = r.read_u32()?;
224    model.ordinal = r.read_u8()?;
225    r.read_u8()?;
226    r.read_u8()?;
227    model.enabled = r.read_u8()? > 0;
228
229    Ok(model)
230}
231fn read_model_param(data: &[u8], little_endian: bool) -> Result<ModelParam, io::Error> {
232    let mut r = reader_for_slice(data, little_endian);
233    let mut param: ModelParam = Default::default();
234
235    param.param_id = r.read_u32()? & 0x00ffffff;
236    let data = [r.read_u32()?, r.read_u32()?];
237    param.value = decode_value(&data)?;
238
239    Ok(param)
240}
241
242fn read_sslb_entry(chunk: &Chunk) -> Result<(), io::Error> {
243    let little_endian = chunk.is_little_endian();
244    let data = match chunk {
245        Chunk::Data { data, .. } => data,
246        _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Data chunk expected"))
247    };
248    if data.len() < 13 {
249        // Empty entry?
250        return Ok(());
251    }
252    let mut r = reader_for_slice(data, little_endian);
253    let mut header = [0u8; 13];
254    r.read_u8_into(&mut header)?;
255
256    // 2a = POD, ff = universal
257    let can_process = header[1] == 0x2a || (header[0..2] == [0x80, 0xff]);
258    let name = r.read_ascii_n()?;
259    if !can_process {
260        // Not a POD program, stop here
261        return Ok(());
262    }
263    r.read_u8()?; // this should be 0x03
264
265    // This must be the POD model name if using UNI module
266    // Skip everything except the last 55 bytes of actual data
267    let n = r.read_u8()? - 55;
268    if n > 0 {
269        let mut bytes = vec![0u8; n as usize];
270        r.read_u8_into(&mut bytes)?;
271    }
272    let mut bytes = vec![0u8; 55];
273    r.read_u8_into(&mut bytes)?;
274
275    loop {
276        let id = r.read_u8()?;
277        match id {
278            0x02 => {
279                let str = r.read_ascii_n()?;
280                println!("comment: {}", str);
281            },
282            0x06 => {
283                let str = r.read_ascii_n()?;
284                println!("position: {}", str);
285            },
286            0x00 => {
287                let mut skip = vec![0u8; 2];
288                r.read_u8_into(&mut skip)?;
289                println!("end:");
290                skip.print_hex();
291                break;
292            }
293            _ => {
294                println!("unknown: {:#02x}", id);
295                break;
296            }
297        }
298    }
299
300    Ok(())
301}
302