ot_tools_io/arrangements/
deserialize.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6//! Custom deserialization of arrangement data files.
7
8use crate::arrangements::{ArrangeRow, ArrangementBlock, ArrangementFile, ARRANGEMENT_FILE_HEADER};
9
10use bincode;
11use itertools::Itertools;
12use serde::de::{
13    self, Deserializer, Error as DeserializeErr, IgnoredAny, MapAccess, SeqAccess, Visitor,
14};
15use serde::Deserialize;
16use serde_big_array::Array;
17use std::array::from_fn;
18use std::{fmt, str};
19
20/// Helper function to convert a Serde Deserializer sequence argument into a `u8` vector.
21fn get_bytes_from_seq<'de, A>(mut seq: A) -> Result<Vec<u8>, A::Error>
22where
23    A: SeqAccess<'de>,
24{
25    let mut v: Vec<u8> = vec![];
26    while seq.size_hint().unwrap_or(0) > 0 {
27        v.push(
28            seq.next_element()?
29                .ok_or_else(|| A::Error::custom("Could not access byte array sequence element"))?,
30        );
31    }
32
33    Ok(v)
34}
35
36/// Custom Deserialize trait for `ArrangementFile` struct data.
37/// Needs to be customised so we can force the correct number of bytes be passed to the Deserializer
38/// with `deserialize_tuple`, otherwise we end up with an `io::UnexpectedEof` error whenever we try
39/// to deserialize.
40impl<'de> Deserialize<'de> for ArrangementFile {
41    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
42    where
43        D: Deserializer<'de>,
44    {
45        enum Field {
46            Header,
47            DatatypeVersion,
48            Unk1,
49            ArrangementStateCurrent,
50            Unk2,
51            /// whether this arrangement has been saved
52            SavedFlag,
53            ArrangementStatePrevious,
54            /// all arrangements saved state
55            ArrangementsSavedState,
56            CheckSum,
57        }
58
59        impl<'de> Deserialize<'de> for Field {
60            fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
61            where
62                D: Deserializer<'de>,
63            {
64                struct FieldVisitor;
65
66                impl Visitor<'_> for FieldVisitor {
67                    type Value = Field;
68
69                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
70                        formatter.write_str("ArrangementFile fields")
71                    }
72
73                    fn visit_str<E>(self, value: &str) -> Result<Field, E>
74                    where
75                        E: de::Error,
76                    {
77                        match value {
78                            "header" => Ok(Field::Header),
79                            "datatype_version" => Ok(Field::DatatypeVersion),
80                            "unk1" => Ok(Field::Unk1),
81                            "arrangement_state_current" => Ok(Field::ArrangementStateCurrent),
82                            "unk2" => Ok(Field::Unk2),
83                            "saved_flag" => Ok(Field::SavedFlag),
84                            "arrangement_state_previous" => Ok(Field::ArrangementStatePrevious),
85                            "arrangements_saved_state" => Ok(Field::ArrangementsSavedState),
86                            "checksum" => Ok(Field::CheckSum),
87                            _ => Err(de::Error::unknown_field(value, FIELDS)),
88                        }
89                    }
90                }
91
92                deserializer.deserialize_identifier(FieldVisitor)
93            }
94        }
95
96        struct ArrangementFileVisitor;
97
98        impl<'de> Visitor<'de> for ArrangementFileVisitor {
99            type Value = ArrangementFile;
100
101            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
102                formatter.write_str("struct ArrangementFile")
103            }
104
105            /// Raw binary deserialization is done via `deserializer.deserialize_tuple()`, which
106            /// calls `deserializer.deserialize_seq()` under the hood.
107            /// We have to use `deserializer.deserialize_tuple()` to ensure the number of bytes
108            /// passed from parent deserializers is correct.
109            fn visit_seq<V>(self, seq: V) -> Result<Self::Value, V::Error>
110            where
111                V: SeqAccess<'de>,
112            {
113                let v: Vec<u8> = get_bytes_from_seq::<V>(seq)?;
114
115                let mut shift = 0;
116
117                let header: [u8; 21] = from_fn(|i| v[i + shift]);
118                shift += 21;
119
120                let datatype_version: u8 = v[shift];
121                shift += 1;
122
123                let unk1: [u8; 2] = from_fn(|i| v[i + shift]);
124                shift += 2;
125
126                let curr_arr_bytes: [u8; 5652] = from_fn(|i| v[i + shift]);
127                shift += 5650; // TODO: Is this a bug?
128
129                // TODO: Error instead of Default
130                let arrangement_state_current =
131                    bincode::deserialize::<ArrangementBlock>(&curr_arr_bytes).unwrap_or_default();
132
133                let unk2: u8 = v[shift];
134                shift += 1;
135
136                let saved_flag: u8 = v[shift];
137                shift += 1;
138
139                let prev_arr_bytes: [u8; 5652] = from_fn(|i| v[i + shift]);
140                shift += 5650; // TODO: Is this a bug?
141
142                // TODO: Error instead of Default
143                let arrangement_state_previous =
144                    bincode::deserialize::<ArrangementBlock>(&prev_arr_bytes).unwrap_or_default();
145
146                let arrangements_saved_state: [u8; 8] = from_fn(|i| v[i + shift]);
147                shift += 8;
148
149                // TODO: little versus big endian?!
150                let check_sum_arr: [u8; 2] = from_fn(|i| v[i + shift]);
151                // shift += 2;
152
153                let checksum = crate::u8_bytes_to_u16(&check_sum_arr);
154
155                Ok(Self::Value {
156                    header,
157                    datatype_version,
158                    unk1,
159                    arrangement_state_current,
160                    unk2,
161                    saved_flag,
162                    arrangement_state_previous,
163                    arrangements_saved_state,
164                    checksum,
165                })
166            }
167
168            /// Human-readable deserialization for JSON/YAML etc. for `ArrangementFile`.
169            /// Uses the standard Serde custom struct deserialization pattern with Field enum
170            /// deserialization.
171            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
172            where
173                V: MapAccess<'de>,
174            {
175                let mut header = None;
176                let mut datatype_version = None;
177                let mut unk1 = None;
178                let mut arrangement_state_current = None;
179                let mut unk2 = None;
180                let mut saved_flag = None;
181                let mut arrangement_state_previous = None;
182                let mut arrangements_saved_state = None;
183                let mut checksum = None;
184
185                while let Some(key) = map.next_key()? {
186                    match key {
187                        Field::Header => {
188                            if header.is_some() {
189                                return Err(de::Error::duplicate_field("header"));
190                            }
191                            // type argument required here, otherwise Serde gets confused about the
192                            // header field being a sequence in yaml (likely the same for JSON, but
193                            // not tested yet):
194                            // ```
195                            // Error("header: invalid type: sequence, expected unit", line: 2, column: 1)
196                            // ```
197                            header = Some(map.next_value::<[u8; 21]>()?);
198                        }
199                        Field::DatatypeVersion => {
200                            if datatype_version.is_some() {
201                                return Err(de::Error::duplicate_field("datatype_version"));
202                            }
203                            datatype_version = Some(map.next_value::<u8>()?);
204                        }
205                        Field::Unk1 => {
206                            if unk1.is_some() {
207                                return Err(de::Error::duplicate_field("unk1"));
208                            }
209                            unk1 = Some(map.next_value::<[u8; 2]>()?);
210                        }
211                        Field::ArrangementStateCurrent => {
212                            if arrangement_state_current.is_some() {
213                                return Err(de::Error::duplicate_field(
214                                    "arrangement_state_current",
215                                ));
216                            }
217                            arrangement_state_current = Some(map.next_value()?);
218                        }
219                        Field::Unk2 => {
220                            if unk2.is_some() {
221                                return Err(de::Error::duplicate_field("unk2"));
222                            }
223                            unk2 = Some(map.next_value()?);
224                        }
225                        Field::SavedFlag => {
226                            if saved_flag.is_some() {
227                                return Err(de::Error::duplicate_field("saved_flag"));
228                            }
229                            saved_flag = Some(map.next_value()?);
230                        }
231                        Field::ArrangementStatePrevious => {
232                            if arrangement_state_previous.is_some() {
233                                return Err(de::Error::duplicate_field(
234                                    "arrangement_state_previous",
235                                ));
236                            }
237                            arrangement_state_previous = Some(map.next_value()?);
238                        }
239                        Field::ArrangementsSavedState => {
240                            if arrangements_saved_state.is_some() {
241                                return Err(de::Error::duplicate_field("arrangements_saved_state"));
242                            }
243                            arrangements_saved_state = Some(map.next_value()?);
244                        }
245                        Field::CheckSum => {
246                            if checksum.is_some() {
247                                return Err(de::Error::duplicate_field("checksum"));
248                            }
249                            checksum = Some(map.next_value()?);
250                        }
251                    }
252                }
253
254                // run the check for header data for now... but can possibly allow the field to be
255                // ignored in future as we just use the const value below.
256                let _header = header.ok_or_else(|| de::Error::missing_field("header"))?;
257                let datatype_version =
258                    datatype_version.ok_or_else(|| de::Error::missing_field("datatype_version"))?;
259                let unk1 = unk1.ok_or_else(|| de::Error::missing_field("unk1"))?;
260                let arrangement_state_current = arrangement_state_current
261                    .ok_or_else(|| de::Error::missing_field("arrangement_state_current"))?;
262                let unk2 = unk2.ok_or_else(|| de::Error::missing_field("unk2"))?;
263                let saved_flag =
264                    saved_flag.ok_or_else(|| de::Error::missing_field("saved_flag"))?;
265                let arrangement_state_previous = arrangement_state_previous
266                    .ok_or_else(|| de::Error::missing_field("arrangement_state_previous"))?;
267                let arrangements_saved_state = arrangements_saved_state
268                    .ok_or_else(|| de::Error::missing_field("arrangements_saved_state"))?;
269                let checksum = checksum.ok_or_else(|| de::Error::missing_field("checksum"))?;
270
271                Ok(Self::Value {
272                    header: ARRANGEMENT_FILE_HEADER,
273                    datatype_version,
274                    unk1,
275                    arrangement_state_current,
276                    unk2,
277                    saved_flag,
278                    arrangement_state_previous,
279                    arrangements_saved_state,
280                    checksum,
281                })
282            }
283        }
284
285        const FIELDS: &[&str] = &[
286            "header",
287            "datatype_version",
288            "unk1",
289            "arrangement_state_current",
290            "unk2",
291            "arrangement_state_previous",
292            "arrangements_saved_state",
293            "checksum",
294        ];
295
296        match deserializer.is_human_readable() {
297            true => deserializer.deserialize_map(ArrangementFileVisitor),
298            false => deserializer.deserialize_tuple(11336, ArrangementFileVisitor),
299        }
300    }
301}
302
303/// Custom Deserialize trait for `ArrangementBlock` struct data.
304/// Needs to be customised so we can handle any rows after `n_rows` being deserialized into
305/// `ArrangeRow::EmptyRow` variants.
306impl<'de> Deserialize<'de> for ArrangementBlock {
307    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
308    where
309        D: Deserializer<'de>,
310    {
311        struct ArrangementBlockVisitor;
312
313        impl<'de> Visitor<'de> for ArrangementBlockVisitor {
314            type Value = ArrangementBlock;
315
316            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
317                formatter.write_str("struct ArrangementBlock")
318            }
319
320            /// Raw binary deserialization is done via `deserializer.deserialize_tuple()`, which
321            /// calls `deserializer.deserialize_seq()` under the hood.
322            /// We have to use `deserializer.deserialize_tuple()` to ensure the number of bytes
323            /// passed from parent deserializers is correct.
324            fn visit_seq<V>(self, seq: V) -> Result<ArrangementBlock, V::Error>
325            where
326                V: SeqAccess<'de>,
327            {
328                let v: Vec<u8> = get_bytes_from_seq::<V>(seq)?;
329
330                let name: [u8; 15] = from_fn(|x| v[x]);
331                let unknown_1: [u8; 2] = from_fn(|x| v[x + 15]);
332                let n_rows = v[17];
333
334                let rows: [ArrangeRow; 256] = from_fn(|i| {
335                    /*
336                    IMPORTANT: DONTFIX: @dijksterhuis: It's not possible to work out if a row should
337                    be an EmptyRow  exclusively from the bytes for that row.
338
339                    An EmptyRow variant is only used when the current row's index is greater than
340                    the number of total rows in an ArrangementBlock.
341                    */
342                    match i >= n_rows as usize {
343                        true => ArrangeRow::EmptyRow(),
344                        false => {
345                            let offset = 18;
346                            let idx_start = offset + (i * 22);
347
348                            let row_bytes: [u8; 22] = from_fn(|j| v[j + idx_start]);
349                            // TODO: Default instead of an error
350                            bincode::deserialize::<ArrangeRow>(&row_bytes).unwrap_or_default()
351                        }
352                    }
353                });
354
355                let rows = Box::new(Array(rows));
356
357                Ok(ArrangementBlock {
358                    name,
359                    unknown_1,
360                    n_rows,
361                    rows,
362                })
363            }
364
365            /// Human-readable deserialization for JSON/YAML etc. for `ArrangementBlock`.
366            /// *Does not* uses the standard Serde custom struct deserialization pattern with Field
367            /// enum deserialization. Field names are matched manually for now.
368            fn visit_map<V>(self, mut map: V) -> Result<ArrangementBlock, V::Error>
369            where
370                V: MapAccess<'de>,
371            {
372                let mut name = None;
373                let mut unknown_1 = None;
374                let mut n_rows = None;
375                let mut rows = None;
376
377                while let Some(key) = map.next_key()? {
378                    match key {
379                        Some("name") => {
380                            if name.is_some() {
381                                return Err(de::Error::duplicate_field("name"));
382                            }
383                            name = Some(map.next_value()?);
384                        }
385                        Some("unknown_1") => {
386                            if unknown_1.is_some() {
387                                return Err(de::Error::duplicate_field("unknown_1"));
388                            }
389                            unknown_1 = Some(map.next_value()?);
390                        }
391                        Some("n_rows") => {
392                            if n_rows.is_some() {
393                                return Err(de::Error::duplicate_field("n_rows"));
394                            }
395                            n_rows = Some(map.next_value()?);
396                        }
397                        Some("rows") => {
398                            if rows.is_some() {
399                                return Err(de::Error::duplicate_field("rows"));
400                            }
401                            rows = Some(map.next_value()?);
402                        }
403                        _ => {
404                            return Err(de::Error::custom(format![
405                                "Did not recognise ArrangementBlock key: {:?}",
406                                key.unwrap(),
407                            ]));
408                        }
409                    }
410                }
411                let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
412                let unknown_1 = unknown_1.ok_or_else(|| de::Error::missing_field("unknown_1"))?;
413                let n_rows = n_rows.ok_or_else(|| de::Error::missing_field("n_rows"))?;
414                let rows = rows.ok_or_else(|| de::Error::missing_field("rows"))?;
415
416                Ok(ArrangementBlock {
417                    name,
418                    unknown_1,
419                    n_rows,
420                    rows,
421                })
422            }
423        }
424
425        match deserializer.is_human_readable() {
426            true => deserializer.deserialize_map(ArrangementBlockVisitor),
427            false => deserializer.deserialize_tuple(5650, ArrangementBlockVisitor),
428        }
429    }
430}
431
432/// Custom Deserialize trait for `ArrangeRow` variants.
433/// Ensures we can do
434/// - conditional/dynamic deserialization for binary data based on the row type byte
435/// - deserialize from both human-readable and raw binary formats
436///
437/// The variant of an `ArrangeRow` is determined by
438/// - the row's index in the `ArrangementBlock.rows` array versus number of rows in the arrangement `ArrangementBlock.n_rows`
439/// - The value of the first byte for an `ArrangeRow`. See the table below:
440///
441/// | `ArrangeRow` Variant   | First Byte |
442/// | ---------------------- | ---------- |
443/// | `PatternRow`           | 0          |
444/// | `ReminderRow`          | 0          |
445/// | `LoopOrJumpOrHaltRow`  | 0          |
446/// | `PatternRow`           | 0          |
447/// | `EmptyRow`             | n/a        |
448impl<'de> Deserialize<'de> for ArrangeRow {
449    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
450    where
451        D: Deserializer<'de>,
452    {
453        struct ArrangeRowVisitor;
454
455        impl<'de> Visitor<'de> for ArrangeRowVisitor {
456            type Value = ArrangeRow;
457
458            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
459                formatter.write_str("enum ArrangeRow")
460            }
461
462            /// Raw binary deserialization is done via `deserializer.deserialize_tuple()`, which
463            /// calls `deserializer.deserialize_seq()` under the hood.
464            /// We have to use `deserializer.deserialize_tuple()` to ensure the number of bytes
465            /// passed from parent deserializers is correct.
466            fn visit_seq<V>(self, seq: V) -> Result<ArrangeRow, V::Error>
467            where
468                V: SeqAccess<'de>,
469            {
470                let v: Vec<u8> = get_bytes_from_seq::<V>(seq)?;
471
472                if v.len() != 22 {
473                    return Err(de::Error::invalid_length(v.len(), &self));
474                }
475
476                let row_type = v[0];
477                let row_data = v[1..].to_vec();
478
479                if row_data.len() != 21 {
480                    return Err(de::Error::custom(format![
481                        "ArrangeRow: Invalid row data length, must be 21 bytes, received: {:?}",
482                        row_data.len()
483                    ]));
484                }
485
486                // TODO: Enum Variant with values to match `row_type`?
487                //       (can't remember its official name)
488                match row_type {
489                    0 => {
490                        let pattern_id = row_data[0];
491                        let repetitions = row_data[1];
492                        // TODO: mute mask across Audio and MIDI tracks (again)
493                        let mute_mask = row_data[3];
494                        // TODO: tempo mask (again)
495                        let tempo_1 = row_data[5];
496                        let tempo_2 = row_data[6];
497                        let scene_a = row_data[7];
498                        let scene_b = row_data[8];
499                        // TODO: what's the max for this? 64?
500                        let offset = row_data[10];
501                        // TODO: this has a max of something like 512 somehow?
502                        // TODO: The value for length changes on device depending on what the offset
503                        //       value is. With a length of 256, adding a 64 offset changes the
504                        //       length to 192.
505                        let length = row_data[12];
506
507                        let midi_transpose: [u8; 8] = from_fn(|x| row_data[x + 13]);
508
509                        if repetitions > 63 {
510                            return Err(de::Error::custom(format![
511                                "ArrangeRow::PatternRow: Too many repetitions, must be <= 63: rep={repetitions:?}",
512                            ]));
513                        }
514
515                        let x = ArrangeRow::PatternRow {
516                            pattern_id,
517                            repetitions,
518                            mute_mask,
519                            tempo_1,
520                            tempo_2,
521                            scene_a,
522                            scene_b,
523                            offset,
524                            length,
525                            midi_transpose,
526                        };
527                        Ok(x)
528                    }
529                    1 => {
530
531                        let loop_count = row_data[0];
532                        let row_target = row_data[1];
533
534                        if loop_count > 100_u8 {
535                            return Err(de::Error::custom(format![
536                                "ArrangeRow::LoopOrJumpOrHaltRow: Loop count cannot exceed 100 (99x). loops={loop_count:?}",
537                            ]));
538                        }
539                        let x = ArrangeRow::LoopOrJumpOrHaltRow {
540                            loop_count,
541                            row_target,
542                        };
543                        Ok(x)
544                    }
545                    2 => {
546                        let mut row_data = row_data[0..=14].to_vec();
547                        // ignore invalid ascii characters: https://www.asciitable.com
548                        let first_invalid: Option<(usize, &u8)> = row_data.iter().find_position(|x| { **x < 32 || **x > 126 });
549                        if let Some((x, _)) = first_invalid {
550                            row_data = row_data[..x].to_vec();
551                        }
552                        let s = String::from_utf8(row_data)
553                            .unwrap_or("ERROR".to_string())
554                            .to_ascii_uppercase();
555
556                        Ok(ArrangeRow::ReminderRow(s))
557                    }
558                    _ => {
559                        Err(de::Error::custom(
560                            format!["Invalid row type: {row_type:?} -- must be 0 (PatternRow/EmptyRow) / 1 (LoopOrHaltOrJump) / 2 (Reminder)"]
561                        ))
562                    }
563                }
564            }
565
566            /// Human-readable deserialization for JSON/YAML etc. for ArrangeRow data.
567            /// *Does not* uses the standard Serde custom struct deserialization pattern with Field
568            /// enum deserialization. Field names are matched manually for now.
569            fn visit_map<V>(self, mut map: V) -> Result<ArrangeRow, V::Error>
570            where
571                V: MapAccess<'de>,
572            {
573                // first key tells us which type of field this is
574                let k = map.next_key()?;
575                match k {
576                    Some("empty") => {
577                        let _ = map.next_value::<IgnoredAny>()?;
578                        Ok(ArrangeRow::EmptyRow())
579                    }
580                    Some("reminder") => Ok(ArrangeRow::ReminderRow(map.next_value::<String>()?)),
581                    Some("pattern_id") => {
582                        let pattern_id = map.next_value::<u8>()?;
583                        let repetitions = map.next_entry::<&str, u8>()?.unwrap_or(("", 0)).1;
584                        let mute_mask = map.next_entry::<&str, u8>()?.unwrap_or(("", 0)).1;
585                        let tempo_1 = map.next_entry::<&str, u8>()?.unwrap_or(("", 0)).1;
586                        let tempo_2 = map.next_entry::<&str, u8>()?.unwrap_or(("", 0)).1;
587                        let scene_a = map.next_entry::<&str, u8>()?.unwrap_or(("", 0)).1;
588                        let scene_b = map.next_entry::<&str, u8>()?.unwrap_or(("", 0)).1;
589                        let offset = map.next_entry::<&str, u8>()?.unwrap_or(("", 0)).1;
590                        let length = map.next_entry::<&str, u8>()?.unwrap_or(("", 0)).1;
591                        let midi_transpose = map
592                            .next_entry()?
593                            .unwrap_or(("", [0, 0, 0, 0, 0, 0, 0, 0]))
594                            .1;
595
596                        Ok(ArrangeRow::PatternRow {
597                            pattern_id,
598                            repetitions,
599                            mute_mask,
600                            tempo_1,
601                            tempo_2,
602                            scene_a,
603                            scene_b,
604                            offset,
605                            length,
606                            midi_transpose,
607                        })
608                    }
609                    Some("loop_count") => {
610                        let loop_count = map.next_value::<u8>()?;
611                        let _ = map.next_key::<IgnoredAny>()?;
612                        let row_target = map.next_value::<u8>()?;
613
614                        Ok(ArrangeRow::LoopOrJumpOrHaltRow {
615                            loop_count,
616                            row_target,
617                        })
618                    }
619                    _ => Err(de::Error::custom(format![
620                        "Did not recognise ArrangeRow type based on first key: {:?}",
621                        k.unwrap()
622                    ])),
623                }
624            }
625        }
626
627        match deserializer.is_human_readable() {
628            true => deserializer.deserialize_map(ArrangeRowVisitor),
629            false => deserializer.deserialize_tuple(22, ArrangeRowVisitor),
630        }
631    }
632}
633
634#[cfg(test)]
635#[allow(unused_imports)]
636mod tests {
637    use super::*;
638
639    mod arrangement_file {
640        use crate::test_utils::get_arrange_dirpath;
641        use crate::OctatrackFileIO;
642
643        #[test]
644        fn read_binfile_blank() {
645            let path = get_arrange_dirpath().join("blank.work");
646            let r = super::ArrangementFile::from_data_file(&path);
647            println!("{r:?}");
648            assert!(r.is_ok());
649        }
650
651        #[test]
652        fn read_binfile_full_options() {
653            let path = get_arrange_dirpath().join("full-options.work");
654            let r = super::ArrangementFile::from_data_file(&path);
655            println!("{r:?}");
656            assert!(r.is_ok());
657        }
658
659        #[test]
660        fn read_binfile_one_reminder_row() {
661            let path = get_arrange_dirpath().join("1-rem-blank-txt.work");
662            let r = super::ArrangementFile::from_data_file(&path);
663            println!("{r:?}");
664            assert!(r.is_ok());
665        }
666    }
667
668    mod arrangement_block {
669        #[test]
670        fn empty_rows() {
671            use crate::arrangements::ArrangeRow;
672
673            let expected_rows: [ArrangeRow; 256] = std::array::from_fn(|i| {
674                if i < 10 {
675                    ArrangeRow::PatternRow {
676                        pattern_id: 0,
677                        repetitions: 0,
678                        mute_mask: 0,
679                        tempo_1: 0,
680                        tempo_2: 0,
681                        scene_a: 0,
682                        scene_b: 0,
683                        offset: 0,
684                        length: 0,
685                        midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
686                    }
687                } else {
688                    ArrangeRow::EmptyRow()
689                }
690            });
691
692            let rows = Box::new(serde_big_array::Array(expected_rows));
693
694            let expected = super::ArrangementBlock {
695                name: [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10],
696                unknown_1: [10, 9],
697                n_rows: 10,
698                rows,
699            };
700
701            let b: [u8; 5652] = std::array::from_fn(|x| {
702                match x {
703                    // start name
704                    0 => 10,
705                    // end name
706                    14 => 10,
707                    // unk1 start
708                    15 => 10,
709                    // unk1 end
710                    16 => 9,
711                    // n rows
712                    17 => 10,
713                    _ => 0,
714                }
715            });
716
717            let r = bincode::deserialize::<super::ArrangementBlock>(&b);
718            assert_eq!(expected, r.unwrap());
719        }
720    }
721
722    mod arrange_row {
723        use super::*;
724
725        #[test]
726        fn invalid_row_type() {
727            let b = [
728                3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
729            ];
730            let r = bincode::deserialize::<ArrangeRow>(&b);
731            assert!(r.is_err());
732        }
733
734        mod empty_row {
735            /*
736            IMPORTANT: DONTFIX: @dijksterhuis: It's not possible to work out if a row should be an EmptyRow
737            exclusively from the bytes for that row.
738
739            An EmptyRow variant is only used when the current row's index is greater than the number of
740            total rows in an ArrangementBlock.
741
742            This test is to make sure this is true and no-one tries to fix it.
743            */
744            #[test]
745            fn row_index_matters() {
746                let not_expected = super::ArrangeRow::EmptyRow();
747                let expected = super::ArrangeRow::PatternRow {
748                    pattern_id: 0,
749                    repetitions: 0,
750                    mute_mask: 0,
751                    tempo_1: 0,
752                    tempo_2: 0,
753                    scene_a: 0,
754                    scene_b: 0,
755                    offset: 0,
756                    length: 0,
757                    midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
758                };
759
760                let b = [
761                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
762                ];
763                let r = bincode::deserialize::<super::ArrangeRow>(&b);
764                println!("{r:?}");
765                assert!(r.is_ok());
766                let x = r.unwrap();
767                assert_ne!(not_expected, x);
768                assert_eq!(expected, x);
769            }
770
771            #[test]
772            fn valid_yaml() {
773                let expected = super::ArrangeRow::EmptyRow();
774                let s = "empty: ''\n";
775                let r = serde_yml::from_str::<super::ArrangeRow>(s);
776                println!("{r:?}");
777                assert!(r.is_ok());
778                assert_eq!(expected, r.unwrap());
779            }
780
781            #[test]
782            fn valid_json() {
783                let expected = super::ArrangeRow::EmptyRow();
784                let s = "{\"empty\":\"\"}";
785                let r = serde_json::from_str::<super::ArrangeRow>(s);
786                println!("{r:?}");
787                assert!(r.is_ok());
788                assert_eq!(expected, r.unwrap());
789            }
790        }
791
792        mod reminder_row {
793            #[test]
794            fn valid() {
795                let expected = super::ArrangeRow::ReminderRow("CCCCCCCCCCCCCCC".to_string());
796                let b = [
797                    2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 0, 0, 0, 0, 0, 0,
798                ];
799                let r = bincode::deserialize::<super::ArrangeRow>(&b);
800                assert_eq!(expected, r.unwrap());
801            }
802
803            #[test]
804            fn valid_yaml() {
805                let expected = super::ArrangeRow::ReminderRow("CCCCCCCCCCCCCCC".to_string());
806
807                let s = "reminder: CCCCCCCCCCCCCCC\n";
808                let r = serde_yml::from_str::<super::ArrangeRow>(s);
809                assert_eq!(expected, r.unwrap());
810            }
811
812            #[test]
813            fn valid_drop_excess_characters() {
814                let expected = super::ArrangeRow::ReminderRow("CCCCCCCCCCCCCCC".to_string());
815                let b = [
816                    2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
817                    99, 99,
818                ];
819                let r = bincode::deserialize::<super::ArrangeRow>(&b);
820                assert_eq!(expected, r.unwrap());
821            }
822        }
823        mod loop_jump_or_halt_row {
824            #[test]
825            fn valid() {
826                let expected = super::ArrangeRow::LoopOrJumpOrHaltRow {
827                    loop_count: 1,
828                    row_target: 1,
829                };
830                let b = [
831                    1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
832                ];
833                let r = bincode::deserialize::<super::ArrangeRow>(&b);
834                assert_eq!(expected, r.unwrap());
835            }
836
837            #[test]
838            #[ignore]
839            fn valid_yaml() {
840                let expected = super::ArrangeRow::LoopOrJumpOrHaltRow {
841                    loop_count: 1,
842                    row_target: 1,
843                };
844
845                let s = "loop_count: 1\nrow_target: 1\n";
846                let r = serde_yml::from_str::<super::ArrangeRow>(s);
847                assert_eq!(expected, r.unwrap());
848            }
849
850            #[test]
851            fn invalid_loop_count() {
852                let b = [
853                    1, 101, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
854                ];
855                let r = bincode::deserialize::<super::ArrangeRow>(&b);
856                assert!(r.is_err());
857            }
858        }
859
860        mod pattern_row {
861
862            #[test]
863            fn valid_pattern_id_only() {
864                let expected = super::ArrangeRow::PatternRow {
865                    pattern_id: 1,
866                    repetitions: 0,
867                    mute_mask: 0,
868                    tempo_1: 0,
869                    tempo_2: 0,
870                    scene_a: 0,
871                    scene_b: 0,
872                    offset: 0,
873                    length: 0,
874                    midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
875                };
876                let b = [
877                    0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
878                ];
879                let r = bincode::deserialize::<super::ArrangeRow>(&b);
880                assert_eq!(expected, r.unwrap());
881            }
882
883            #[test]
884            fn valid_last_midi_transpose_only() {
885                let expected = super::ArrangeRow::PatternRow {
886                    pattern_id: 0,
887                    repetitions: 0,
888                    mute_mask: 0,
889                    tempo_1: 0,
890                    tempo_2: 0,
891                    scene_a: 0,
892                    scene_b: 0,
893                    offset: 0,
894                    length: 0,
895                    midi_transpose: [0, 0, 0, 0, 0, 0, 0, 8],
896                };
897                let b = [
898                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
899                ];
900                let r = bincode::deserialize::<super::ArrangeRow>(&b);
901                assert_eq!(expected, r.unwrap());
902            }
903
904            #[test]
905            fn invalid_too_many_repetitions() {
906                let b = [
907                    0, 1, 64, 1, 1, 1, 1, 1, 15, 15, 1, 1, 1, 64, 3, 3, 3, 3, 3, 3, 3, 3,
908                ];
909                let r = bincode::deserialize::<super::ArrangeRow>(&b);
910                println!("{r:?}");
911                assert!(r.is_err());
912            }
913
914            #[test]
915            fn valid_all() {
916                let expected = super::ArrangeRow::PatternRow {
917                    pattern_id: 1,
918                    repetitions: 10,
919                    mute_mask: 1,
920                    tempo_1: 1,
921                    tempo_2: 1,
922                    scene_a: 15,
923                    scene_b: 15,
924                    offset: 1,
925                    length: 64,
926                    midi_transpose: [3, 3, 3, 3, 3, 3, 3, 3],
927                };
928                let b = [
929                    0, 1, 10, 1, 1, 1, 1, 1, 15, 15, 1, 1, 1, 64, 3, 3, 3, 3, 3, 3, 3, 3,
930                ];
931                let r = bincode::deserialize::<super::ArrangeRow>(&b);
932                assert_eq!(expected, r.unwrap());
933            }
934
935            #[test]
936            fn zero_valued_is_still_pattern_row() {
937                let expected = super::ArrangeRow::PatternRow {
938                    pattern_id: 0,
939                    repetitions: 0,
940                    mute_mask: 0,
941                    tempo_1: 0,
942                    tempo_2: 0,
943                    scene_a: 0,
944                    scene_b: 0,
945                    offset: 0,
946                    length: 0,
947                    midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
948                };
949                let b = [
950                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
951                ];
952                let r = bincode::deserialize::<super::ArrangeRow>(&b);
953                assert_eq!(expected, r.unwrap());
954            }
955        }
956    }
957}