l6t_file/
encoder.rs

1use std::io;
2use std::io::{Cursor, Write};
3
4use crate::iff::Chunk;
5use crate::model::*;
6use crate::types;
7use crate::types::TypeID;
8
9pub struct Writer<W: Write> {
10    writer: W,
11    pub little_endian: bool
12}
13
14impl <W: Write> Writer<W> {
15    pub fn new (writer: W, little_endian: bool) -> Self {
16        Self { writer, little_endian }
17    }
18
19    pub fn write_u8(&mut self, value: u8) -> Result<(), io::Error> {
20        let v = [value];
21        self.writer.write_all(&v)
22    }
23
24    pub fn write_u16(&mut self, value: u16) -> Result<(), io::Error> {
25        let v = match self.little_endian {
26            true => value.to_le_bytes(),
27            false => value.to_be_bytes(),
28        };
29        self.writer.write_all(&v)
30    }
31
32    pub fn write_u32(&mut self, value: u32) -> Result<(), io::Error> {
33        let v = match self.little_endian {
34            true => value.to_le_bytes(),
35            false => value.to_be_bytes()
36        };
37        self.writer.write_all(&v)
38    }
39
40    fn write_utf_pad(&mut self, len: usize, pad: usize, value: &str) -> Result<usize, io::Error> {
41        let mut n = 0;
42
43        for c in value.chars() {
44            let l = c.len_utf16() * 2; // utf-16 string in bytes
45            if n + l > len - pad {
46                break;
47            }
48
49            let mut b = [0; 2];
50            c.encode_utf16(&mut b);
51            for c in b.iter().take(l / 2) { // l is in bytes!
52                self.write_u16(*c)?;
53            }
54            n += l;
55        }
56        while n < len {
57            self.write_u16(0u16)?;
58            n += 2;
59        }
60
61        Ok(n)
62    }
63
64    pub fn write_utf(&mut self, len: usize, value: &str) -> Result<usize, io::Error> {
65        self.write_utf_pad(len, 0, value)
66    }
67
68    pub fn write_utf_z(&mut self, len: usize, value: &str) -> Result<usize, io::Error> {
69        self.write_utf_pad(len, 2, value)
70    }
71}
72
73fn writer_for_slice(slice: &mut [u8], little_endian: bool) -> Writer<Cursor<&mut [u8]>> {
74    Writer::new( Cursor::new(slice), little_endian)
75}
76
77fn writer_for_vec(vec: &mut Vec<u8>, little_endian: bool) -> Writer<Cursor<&mut Vec<u8>>> {
78    Writer::new( Cursor::new(vec), little_endian)
79}
80
81pub struct Encoder {}
82
83impl Encoder {
84
85    pub fn write(patch: &L6Patch) -> Result<Vec<u8>, io::Error> {
86        Self::write_with_endian(patch, false)
87    }
88
89    pub fn write_with_endian(patch: &L6Patch, little_endian: bool) -> Result<Vec<u8>, io::Error> {
90        let mut envelope = Chunk::create(types::FORM, types::L6PA, little_endian);
91
92        envelope.append_chunk(write_target_device(&patch.target_device, little_endian)?);
93        envelope.append_chunk(write_models(&patch.models, little_endian)?);
94        envelope.append_chunk(write_meta_tags(&patch.meta, little_endian)?);
95
96        let mut vec = Vec::new();
97        envelope.write(&mut vec)?;
98        Ok(vec)
99    }
100}
101
102fn write_target_device(dev: &TargetDevice, little_endian: bool) -> Result<Chunk, io::Error> {
103    let mut data = [0u8; 76];
104    let mut w = writer_for_slice(&mut data, little_endian);
105
106    w.write_u32(1)?; // PINF version
107    w.write_u32(dev.midi_id)?;
108    w.write_utf_z(32, &dev.name)?;
109    w.write_u32(dev.version)?;
110
111    Ok(Chunk::Data { id: types::PINF, data: Vec::from(data), little_endian })
112}
113
114/// Encode a string into a UTF-16 chunk
115fn encode_utf(str: &String, type_id: TypeID, little_endian: bool) -> Option<Chunk> {
116    if str.is_empty() { return None }
117
118    let size = str.encode_utf16().count() * 2;
119    let mut data: Vec<u8> = Vec::with_capacity(size);
120    let mut w = writer_for_vec(&mut data, little_endian);
121    w.write_utf(size, str).unwrap();
122
123    Some(Chunk::Data { id: type_id, data, little_endian })
124}
125
126fn encode_date(date: &usize, type_id: TypeID, little_endian: bool) -> Option<Chunk> {
127    if *date == 0 { return None }
128
129    let str = (*date / 1000).to_string();
130    encode_utf(&str, type_id, little_endian)
131}
132
133fn encode_value(value: &Value) -> Result<[u32;2], io::Error> {
134    match value {
135        Value::Int(v) => {
136            Ok([0, *v])
137        }
138        Value::Float(v) => {
139            Ok([1, v.to_bits()])
140        }
141    }
142}
143
144fn write_meta_tags(tags: &MetaTags, little_endian: bool) -> Result<Chunk, io::Error> {
145    let mut envelope = Chunk::create(types::LIST, types::UNFO, little_endian);
146    let chunks = [
147        encode_utf(&tags.author, types::IAUT, little_endian),
148        encode_utf(&tags.guitarist, types::IGTR, little_endian),
149        encode_utf(&tags.band, types::IBND, little_endian),
150        encode_utf(&tags.song, types::ISNG, little_endian),
151        encode_utf(&tags.style, types::ISTL, little_endian),
152        encode_utf(&tags.pickup_style, types::IPUS, little_endian),
153        encode_utf(&tags.pickup_position, types::IPUP, little_endian),
154        encode_date(&tags.date, types::IDAT, little_endian),
155        encode_utf(&tags.amp_name, types::IAMP, little_endian),
156        encode_utf(&tags.creator_app, types::IAPP, little_endian),
157        encode_utf(&tags.creator_app_version, types::IAPV, little_endian),
158        encode_utf(&tags.comments, types::ICMT, little_endian),
159    ];
160
161    for chunk in chunks {
162        let Some(chunk) = chunk else { continue };
163        envelope.append_chunk(chunk);
164    }
165
166    Ok(envelope)
167}
168
169fn write_models(models: &[Model], little_endian: bool) -> Result<Chunk, io::Error> {
170    let mut envelope = Chunk::create(types::LIST, types::PATC, little_endian);
171    for model in models {
172        envelope.append_chunk(write_model(model, little_endian)?);
173    }
174
175    Ok(envelope)
176}
177
178fn write_model(model: &Model, little_endian: bool) -> Result<Chunk, io::Error> {
179    let mut envelope = Chunk::create(types::LIST, types::MODL, little_endian);
180
181    envelope.append_chunk(write_model_info(&model, little_endian)?);
182    for param in &model.params {
183        envelope.append_chunk(write_model_param(&param, little_endian)?)
184    }
185
186    Ok(envelope)
187}
188
189fn write_model_info(model: &Model, little_endian: bool) -> Result<Chunk, io::Error> {
190    let mut data = [0u8; 12];
191    let mut w = writer_for_slice(&mut data, little_endian);
192
193    w.write_u32(model.model_id)?;
194    w.write_u32(model.slot_id)?;
195    w.write_u8(model.ordinal)?;
196    w.write_u8(0)?;
197    w.write_u8(0)?;
198    w.write_u8(if model.enabled { 1 } else { 0 })?;
199
200    Ok(Chunk::Data { id: types::MINF, data: Vec::from(data), little_endian })
201}
202
203fn write_model_param(param: &ModelParam, little_endian: bool) -> Result<Chunk, io::Error> {
204    let mut data = [0u8; 12];
205    let mut w = writer_for_slice(&mut data, little_endian);
206
207    w.write_u32(0x3f000000 | (param.param_id & 0x00ffffff))?;
208
209    let value = encode_value(&param.value)?;
210    w.write_u32(value[0])?;
211    w.write_u32(value[1])?;
212
213    Ok(Chunk::Data { id: types::PARM, data: Vec::from(data), little_endian })
214}
215
216#[cfg(test)]
217mod test {
218    use crate::encoder::{writer_for_vec};
219
220    #[test]
221    fn test_write_utf() {
222        let str = "Hello";
223        let expected = &[0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f];
224
225        let mut vec = Vec::with_capacity(10);
226        let mut w = writer_for_vec(&mut vec, false);
227
228        w.write_utf(10, str).unwrap();
229
230        assert_eq!(&vec, expected);
231
232        // NUL-terminated
233
234        let expected = &[0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x00];
235
236        let mut vec = Vec::with_capacity(10);
237        let mut w = writer_for_vec(&mut vec, false);
238
239        w.write_utf_z(10, str).unwrap();
240
241        assert_eq!(&vec, expected);
242    }
243}