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; 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) { 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)?; 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
114fn 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(¶m, 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(¶m.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 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}