1use crate::arrangements::{ArrangeRow, ArrangementBlock};
21use itertools::Itertools;
22
23use serde::ser::{Error as SerializeErr, SerializeMap, SerializeStruct, Serializer};
24use serde::Serialize;
25
26impl Serialize for ArrangementBlock {
29 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
30 where
31 S: Serializer,
32 {
33 let first_empty_row = self
46 .rows
47 .iter()
48 .find_position(|x| **x == ArrangeRow::EmptyRow());
49 if first_empty_row.is_none() && self.n_rows < 255_u8 {
50 return Err(S::Error::custom(format![
51 "No Empty Rows, but n_rows is less than 255: firstEmptyIdx={:?} nRows={:?}",
52 first_empty_row.unwrap().0,
53 self.n_rows,
54 ]));
55 }
56
57 let first_empty_row = first_empty_row.unwrap_or((0, &ArrangeRow::EmptyRow())).0;
58 if first_empty_row != self.n_rows as usize {
59 return Err(S::Error::custom(format![
60 "Index of first Empty Row does not match value for n_rows: idx={:?} nRows={:?}",
61 first_empty_row, self.n_rows,
62 ]));
63 }
64
65 let mut state = serializer.serialize_struct("ArrangementBlock", 4)?;
66 state.serialize_field("name", &self.name)?;
67 state.serialize_field("unknown_1", &self.unknown_1)?;
68 state.serialize_field("n_rows", &self.n_rows)?;
69 state.serialize_field("rows", &self.rows)?;
70 state.end()
71 }
72}
73
74impl Serialize for ArrangeRow {
79 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
80 where
81 S: Serializer,
82 {
83 match self {
85 ArrangeRow::PatternRow {
86 pattern_id,
87 repetitions,
88 mute_mask,
89 tempo_1,
90 tempo_2,
91 scene_a,
92 scene_b,
93 offset,
94 length,
95 midi_transpose,
96 } => {
97 if repetitions > &63_u8 {
99 return Err(S::Error::custom(
100 "ArrangeRow::PatternRow: Repetitions cannot exceed 63 (64x)",
101 ));
102 }
103
104 if *scene_a != 255_u8 && scene_a > &15_u8 {
105 return Err(S::Error::custom("ArrangeRow::PatternRow: Scene A index cannot be greater than 15 (zero index; 16 length)"));
106 }
107 if *scene_b != 255_u8 && scene_b > &15_u8 {
108 return Err(S::Error::custom("ArrangeRow::PatternRow: Scene B index cannot be greater than 15 (zero index; 16 length)"));
109 }
110
111 if serializer.is_human_readable() {
112 let mut state = serializer.serialize_struct("PatternRow", 10)?;
113 state.serialize_field("pattern_id", pattern_id)?;
114 state.serialize_field("repetitions", repetitions)?;
115 state.serialize_field("mute_mask", mute_mask)?;
116 state.serialize_field("tempo_1", tempo_1)?;
117 state.serialize_field("tempo_2", tempo_2)?;
118 state.serialize_field("scene_a", scene_a)?;
119 state.serialize_field("scene_b", scene_b)?;
120 state.serialize_field("offset", offset)?;
121 state.serialize_field("length", length)?;
122 state.serialize_field("midi_transpose", midi_transpose)?;
123 state.end()
124 } else {
125 let mut state = serializer.serialize_struct("PatternRow", 22)?;
126 state.serialize_field("row_type", &0_u8)?;
127 state.serialize_field("pattern_id", pattern_id)?;
128 state.serialize_field("repetitions", repetitions)?;
129 state.serialize_field("unused_1", &0_u8)?;
130 state.serialize_field("mute_mask", mute_mask)?;
131 state.serialize_field("unused_2", &0_u8)?;
132 state.serialize_field("tempo_1", tempo_1)?;
133 state.serialize_field("tempo_2", tempo_2)?;
134 state.serialize_field("scene_a", scene_a)?;
135 state.serialize_field("scene_b", scene_b)?;
136 state.serialize_field("unused_3", &0_u8)?;
137 state.serialize_field("offset", offset)?;
138 state.serialize_field("unused_4", &0_u8)?;
139 state.serialize_field("length", length)?;
140 state.serialize_field("midi_transpose", midi_transpose)?;
141 state.end()
142 }
143 }
144 ArrangeRow::LoopOrJumpOrHaltRow {
145 loop_count,
146 row_target,
147 } => {
148 if loop_count > &100_u8 {
149 return Err(S::Error::custom(
150 "ArrangeRow::LoopOrJumpOrHaltRow: Loop count cannot exceed 100 (99x)",
151 ));
152 }
153
154 if serializer.is_human_readable() {
155 let mut state = serializer.serialize_struct("LoopOrJumpOrHaltRow", 2)?;
156 state.serialize_field("loop_count", loop_count)?;
157 state.serialize_field("row_target", row_target)?;
158 state.end()
159 } else {
160 let mut state = serializer.serialize_struct("LoopOrJumpOrHaltRow", 22)?;
161 state.serialize_field("row_type", &1_u8)?;
162 state.serialize_field("loop_count", loop_count)?;
163 state.serialize_field("row_target", row_target)?;
164 state.serialize_field("unused_1", &0_u8)?;
165 state.serialize_field("unused_2", &0_u8)?;
166 state.serialize_field("unused_3", &0_u8)?;
167 state.serialize_field("unused_4", &0_u8)?;
168 state.serialize_field("unused_5", &0_u8)?;
169 state.serialize_field("unused_6", &0_u8)?;
170 state.serialize_field("unused_7", &0_u8)?;
171 state.serialize_field("unused_8", &0_u8)?;
172 state.serialize_field("unused_9", &0_u8)?;
173 state.serialize_field("unused_10", &0_u8)?;
174 state.serialize_field("unused_11", &0_u8)?;
175 state.serialize_field("unused_12", &0_u8)?;
176 state.serialize_field("unused_13", &0_u8)?;
177 state.serialize_field("unused_14", &0_u8)?;
178 state.serialize_field("unused_15", &0_u8)?;
179 state.serialize_field("unused_16", &0_u8)?;
180 state.serialize_field("unused_17", &0_u8)?;
181 state.serialize_field("unused_18", &0_u8)?;
182 state.serialize_field("unused_19", &0_u8)?;
183 state.end()
184 }
185 }
186 ArrangeRow::ReminderRow(x) => {
187 if x.len() > 15 {
188 return Err(S::Error::custom(format![
189 "ArrangeRow::ReminderRow: string length exceeds 15: str={x:?}",
190 ]));
191 };
192 if serializer.is_human_readable() {
193 let mut state = serializer.serialize_map(Some(1))?;
194 state.serialize_entry("reminder", x)?;
195 state.end()
196 } else {
197 let mut state = serializer.serialize_struct("ReminderRow", 22)?;
198 state.serialize_field("row_type", &2_u8)?;
199 for c in x.as_bytes() {
200 state.serialize_field("char", &c)?;
201 }
202 for _ in x.len()..15 {
203 state.serialize_field("char", &0_u8)?;
204 }
205 state.serialize_field("unused_1", &0_u8)?;
206 state.serialize_field("unused_2", &0_u8)?;
207 state.serialize_field("unused_3", &0_u8)?;
208 state.serialize_field("unused_4", &0_u8)?;
209 state.serialize_field("unused_5", &0_u8)?;
210 state.serialize_field("unused_6", &0_u8)?;
211 state.end()
212 }
213 }
214 ArrangeRow::EmptyRow() => {
215 if serializer.is_human_readable() {
216 let mut state = serializer.serialize_map(Some(1))?;
217 state.serialize_entry("empty", "")?;
218 state.end()
219 } else {
220 let mut state = serializer.serialize_struct("EmptyRow", 0)?;
221 state.serialize_field("unused_1", &0_u8)?;
222 state.serialize_field("unused_2", &0_u8)?;
223 state.serialize_field("unused_3", &0_u8)?;
224 state.serialize_field("unused_4", &0_u8)?;
225 state.serialize_field("unused_5", &0_u8)?;
226 state.serialize_field("unused_6", &0_u8)?;
227 state.serialize_field("unused_7", &0_u8)?;
228 state.serialize_field("unused_8", &0_u8)?;
229 state.serialize_field("unused_9", &0_u8)?;
230 state.serialize_field("unused_10", &0_u8)?;
231 state.serialize_field("unused_11", &0_u8)?;
232 state.serialize_field("unused_12", &0_u8)?;
233 state.serialize_field("unused_13", &0_u8)?;
234 state.serialize_field("unused_14", &0_u8)?;
235 state.serialize_field("unused_15", &0_u8)?;
236 state.serialize_field("unused_16", &0_u8)?;
237 state.serialize_field("unused_17", &0_u8)?;
238 state.serialize_field("unused_18", &0_u8)?;
239 state.serialize_field("unused_19", &0_u8)?;
240 state.serialize_field("unused_20", &0_u8)?;
241 state.serialize_field("unused_21", &0_u8)?;
242 state.serialize_field("unused_22", &0_u8)?;
243 state.end()
244 }
245 }
246 }
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 mod arrangement_file {
255 use crate::arrangements::ArrangementFile;
256 use crate::test_utils::get_arrange_dirpath;
257 use crate::{OctatrackFileIO, OtToolsIoError};
258
259 #[test]
260 fn test_serialize_to_json() -> Result<(), OtToolsIoError> {
262 let path = get_arrange_dirpath().join("blank.work");
263 assert!(ArrangementFile::from_data_file(&path)?
264 .to_json_string()
265 .is_ok());
266 Ok(())
267 }
268
269 #[test]
270 #[cfg(not(target_os = "windows"))]
272 fn test_serialize_to_yaml() -> Result<(), OtToolsIoError> {
274 let valid_yaml_path = get_arrange_dirpath().join("blank.yaml");
275 let valid_yaml = crate::read_str_file(&valid_yaml_path);
276
277 let bin_file_path = get_arrange_dirpath().join("blank.work");
278 let yaml = ArrangementFile::from_data_file(&bin_file_path)?.to_yaml_string();
279
280 assert!(yaml.is_ok());
281 assert_eq!(valid_yaml?, yaml?);
282 Ok(())
283 }
284 }
285
286 mod arrangement_block {
287 #[test]
288 fn test_ok() {
289 let expected_rows: [super::ArrangeRow; 256] = std::array::from_fn(|i| {
290 if i < 10 {
291 super::ArrangeRow::PatternRow {
292 pattern_id: 1,
293 repetitions: 1,
294 mute_mask: 1,
295 tempo_1: 1,
296 tempo_2: 1,
297 scene_a: 1,
298 scene_b: 1,
299 offset: 1,
300 length: 1,
301 midi_transpose: [8, 1, 1, 1, 1, 1, 1, 8],
302 }
303 } else {
304 super::ArrangeRow::EmptyRow()
305 }
306 });
307
308 let rows = Box::new(serde_big_array::Array(expected_rows));
309
310 let expected = super::ArrangementBlock {
311 name: [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10],
312 unknown_1: [10, 9],
313 n_rows: 10,
314 rows,
315 };
316
317 let _: [u8; 5652] = std::array::from_fn(|x| {
319 match x {
320 0 => 10,
322 13 => 10,
324 14 => 10,
326 15 => 9,
328 16 => 10,
330 5650 => 10,
332 5651 => 9,
334 _ => 0,
335 }
336 });
337 let r = bincode::serialize(&expected);
338 println!("{r:?}");
339 assert!(r.is_ok());
340 let v = r.unwrap();
341 assert_eq!(5650, v.len());
342 }
343 }
344
345 mod arrangement_row {
346 use super::*;
347
348 mod pattern_row {
349 #[test]
350 fn valid() {
351 let x = super::ArrangeRow::PatternRow {
352 pattern_id: 0,
353 repetitions: 0,
354 mute_mask: 0,
355 tempo_1: 0,
356 tempo_2: 0,
357 scene_a: 0,
358 scene_b: 0,
359 offset: 0,
360 length: 0,
361 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
362 };
363 let r = bincode::serialize(&x);
364 println!("{r:?}");
365 assert!(r.is_ok());
366 assert_eq!(r.unwrap().len(), 22);
367 }
368
369 #[test]
370 fn valid_yaml() {
371 let x = super::ArrangeRow::PatternRow {
372 pattern_id: 0,
373 repetitions: 0,
374 mute_mask: 0,
375 tempo_1: 0,
376 tempo_2: 0,
377 scene_a: 0,
378 scene_b: 0,
379 offset: 0,
380 length: 0,
381 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
382 };
383 let r = serde_yml::to_string(&x);
384 println!("{r:?}");
385 assert!(r.is_ok());
386 assert_eq!(r.unwrap(), "pattern_id: 0\nrepetitions: 0\nmute_mask: 0\ntempo_1: 0\ntempo_2: 0\nscene_a: 0\nscene_b: 0\noffset: 0\nlength: 0\nmidi_transpose:\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n");
387 }
388
389 #[test]
390 fn valid_json() {
391 let x = super::ArrangeRow::PatternRow {
392 pattern_id: 0,
393 repetitions: 0,
394 mute_mask: 0,
395 tempo_1: 0,
396 tempo_2: 0,
397 scene_a: 0,
398 scene_b: 0,
399 offset: 0,
400 length: 0,
401 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
402 };
403 let r = serde_json::to_string(&x);
404 println!("{r:?}");
405 assert!(r.is_ok());
406 assert_eq!(r.unwrap(), "{\"pattern_id\":0,\"repetitions\":0,\"mute_mask\":0,\"tempo_1\":0,\"tempo_2\":0,\"scene_a\":0,\"scene_b\":0,\"offset\":0,\"length\":0,\"midi_transpose\":[0,0,0,0,0,0,0,0]}");
407 }
408
409 #[test]
410 fn invalid_repetitions() {
411 let x = super::ArrangeRow::PatternRow {
412 pattern_id: 0,
413 repetitions: 64,
414 mute_mask: 0,
415 tempo_1: 0,
416 tempo_2: 0,
417 scene_a: 0,
418 scene_b: 0,
419 offset: 0,
420 length: 0,
421 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
422 };
423 let r = bincode::serialize(&x);
424 assert!(r.is_err());
425 }
426
427 #[test]
428 fn valid_scene_a_off() {
429 let x = super::ArrangeRow::PatternRow {
430 pattern_id: 0,
431 repetitions: 0,
432 mute_mask: 0,
433 tempo_1: 0,
434 tempo_2: 0,
435 scene_a: 255,
436 scene_b: 0,
437 offset: 0,
438 length: 0,
439 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
440 };
441 let r = bincode::serialize(&x);
442 println!("{r:?}");
443 assert!(r.is_ok());
444 }
445
446 #[test]
447 fn valid_scene_b_off() {
448 let x = super::ArrangeRow::PatternRow {
449 pattern_id: 0,
450 repetitions: 0,
451 mute_mask: 0,
452 tempo_1: 0,
453 tempo_2: 0,
454 scene_a: 0,
455 scene_b: 255,
456 offset: 0,
457 length: 0,
458 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
459 };
460 let r = bincode::serialize(&x);
461 println!("{r:?}");
462 assert!(r.is_ok());
463 }
464
465 #[test]
466 fn invalid_scene_a() {
467 let x = super::ArrangeRow::PatternRow {
468 pattern_id: 0,
469 repetitions: 0,
470 mute_mask: 0,
471 tempo_1: 0,
472 tempo_2: 0,
473 scene_a: 16,
474 scene_b: 0,
475 offset: 0,
476 length: 0,
477 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
478 };
479 let r = bincode::serialize(&x);
480 println!("{r:#?}");
481 assert!(r.is_err());
482 }
483
484 #[test]
485 fn invalid_scene_b() {
486 let x = super::ArrangeRow::PatternRow {
487 pattern_id: 0,
488 repetitions: 0,
489 mute_mask: 0,
490 tempo_1: 0,
491 tempo_2: 0,
492 scene_a: 16,
493 scene_b: 16,
494 offset: 0,
495 length: 0,
496 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
497 };
498 let r = bincode::serialize(&x);
499 assert!(r.is_err());
500 }
501 }
502
503 mod loop_or_jump_or_halt {
504
505 #[test]
506 fn valid() {
507 let x = super::ArrangeRow::LoopOrJumpOrHaltRow {
508 loop_count: 1,
509 row_target: 1,
510 };
511 let r = bincode::serialize(&x);
512 println!("{r:?}");
513 assert!(r.is_ok());
514 assert_eq!(r.unwrap().len(), 22);
515 }
516
517 #[test]
518 fn valid_yaml() {
519 let x = super::ArrangeRow::LoopOrJumpOrHaltRow {
520 loop_count: 1,
521 row_target: 1,
522 };
523 let r = serde_yml::to_string(&x);
524 println!("{r:?}");
525 assert!(r.is_ok());
526 assert_eq!(r.unwrap(), "loop_count: 1\nrow_target: 1\n");
527 }
528
529 #[test]
530 fn valid_json() {
531 let x = super::ArrangeRow::LoopOrJumpOrHaltRow {
532 loop_count: 1,
533 row_target: 1,
534 };
535 let r = serde_json::to_string(&x);
536 println!("{r:?}");
537 assert!(r.is_ok());
538 assert_eq!(r.unwrap(), "{\"loop_count\":1,\"row_target\":1}");
539 }
540
541 #[test]
542 fn invalid_loop_count() {
543 let x = super::ArrangeRow::LoopOrJumpOrHaltRow {
544 loop_count: 101,
545 row_target: 1,
546 };
547 let r = bincode::serialize(&x);
548 assert!(r.is_err());
549 }
550 }
551
552 mod reminder_row {
553 #[test]
554 fn valid_string() {
555 let x = super::ArrangeRow::ReminderRow(String::from("HELLO WORLD"));
556 let r = bincode::serialize(&x);
557 println!("{r:?}");
558 assert!(r.is_ok());
559 println!("{r:?}");
560 assert_eq!(r.unwrap().len(), 22);
561 }
562
563 #[test]
564 fn valid_string_yaml() {
565 let x = super::ArrangeRow::ReminderRow(String::from("HELLO WORLD"));
566 let r = serde_yml::to_string(&x);
567 println!("{r:?}");
568 assert!(r.is_ok());
569 assert_eq!(r.unwrap(), "reminder: HELLO WORLD\n");
570 }
571
572 #[test]
573 fn valid_string_json() {
574 let x = super::ArrangeRow::ReminderRow(String::from("HELLO WORLD"));
575 let r = serde_json::to_string(&x);
576 println!("{r:?}");
577 assert!(r.is_ok());
578 assert_eq!(r.unwrap(), "{\"reminder\":\"HELLO WORLD\"}");
579 }
580
581 #[test]
582 fn empty_string() {
583 let x = super::ArrangeRow::ReminderRow(String::new());
584 let r = bincode::serialize(&x);
585 println!("{r:?}");
586 assert!(r.is_ok());
587 assert_eq!(r.unwrap().len(), 22);
588 }
589
590 #[test]
591 fn empty_string_yaml() {
592 let x = super::ArrangeRow::ReminderRow(String::new());
593 let r = serde_yml::to_string(&x);
594 println!("{r:?}");
595 assert!(r.is_ok());
596 assert_eq!(r.unwrap(), "reminder: ''\n");
597 }
598
599 #[test]
600 fn empty_string_json() {
601 let x = super::ArrangeRow::ReminderRow(String::new());
602 let r = serde_json::to_string(&x);
603 println!("{r:?}");
604 assert!(r.is_ok());
605 assert_eq!(r.unwrap(), "{\"reminder\":\"\"}");
606 }
607
608 #[test]
609 fn invalid() {
610 let x = super::ArrangeRow::ReminderRow(String::from("1111111111111111"));
612 let r = bincode::serialize(&x);
613 assert!(r.is_err());
614 }
615 }
616
617 mod empty_row {
618 #[test]
619 fn valid() {
620 let x = super::ArrangeRow::EmptyRow();
621 let r = bincode::serialize(&x);
622 println!("{r:?}");
623 assert!(r.is_ok());
624 assert_eq!(r.unwrap().len(), 22);
625 }
626
627 #[test]
628 fn valid_yaml() {
629 let x = super::ArrangeRow::EmptyRow();
630 let r = serde_yml::to_string(&x);
631 println!("{r:?}");
632 assert!(r.is_ok());
633 assert_eq!(r.unwrap(), "empty: ''\n");
634 }
635
636 #[test]
637 fn valid_json() {
638 let x = super::ArrangeRow::EmptyRow();
639 let r = serde_json::to_string(&x);
640 println!("{r:?}");
641 assert!(r.is_ok());
642 assert_eq!(r.unwrap(), "{\"empty\":\"\"}");
643 }
644 }
645 }
646}