1use crate::samples::slices::{Slice, SLICE_LOOP_POINT_DEFAULT};
21use crate::samples::SwapBytes;
22use crate::{
23 CalculateChecksum, CheckChecksum, CheckHeader, CheckIntegrity, Decode, Encode, IsDefault,
24 OtToolsIoErrors, RBoxErr,
25};
26use ot_tools_io_derive::Decodeable;
27use serde::{Deserialize, Serialize};
28use serde_big_array::{Array, BigArray};
29use std::array::from_fn;
30
31pub const MARKERS_HEADER: [u8; 21] = [
43 0x46, 0x4f, 0x52, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x44, 0x50, 0x53, 0x31, 0x53, 0x41, 0x4d, 0x50,
44 0x00, 0x00, 0x00, 0x00, 0x00,
45];
46
47pub const MARKERS_FILE_VERSION: u8 = 4;
49
50#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Decodeable)]
53pub struct SlotPlayback {
54 pub trim_offset: u32,
56
57 pub trim_end: u32,
59
60 pub loop_point: u32,
62
63 #[serde(with = "BigArray")]
65 pub slices: [Slice; 64],
66
67 pub slice_count: u32,
69}
70
71impl SlotPlayback {
72 fn validate(&self) -> RBoxErr<bool> {
73 for slice in self.slices.iter() {
74 slice.validate()?;
75 }
76 let trim_ok = self.trim_offset < self.trim_end;
77 let slice_count_ok = self.slice_count == self.slices.len() as u32;
78 let loop_start_ok = self.loop_point >= self.trim_offset;
79 let loop_end_ok =
80 self.loop_point <= self.trim_end || self.loop_point == SLICE_LOOP_POINT_DEFAULT;
81
82 if !(trim_ok && slice_count_ok && loop_start_ok && loop_end_ok) {
83 return Err(OtToolsIoErrors::TodoError.into());
84 }
85
86 Ok(true)
87 }
88}
89
90impl SwapBytes for SlotPlayback {
91 type T = SlotPlayback;
92 fn swap_bytes(self) -> RBoxErr<Self::T> {
93 let mut slices: [Slice; 64] = self.slices;
94
95 for (i, slice) in self.slices.iter().enumerate() {
96 slices[i] = slice.swap_bytes()?;
97 }
98
99 let bswapped = Self {
100 trim_offset: self.trim_offset.swap_bytes(),
101 trim_end: self.trim_end.swap_bytes(),
102 loop_point: self.loop_point.swap_bytes(),
103 slices,
104 slice_count: self.slice_count.swap_bytes(),
105 };
106
107 Ok(bswapped)
108 }
109}
110
111impl Default for SlotPlayback {
112 fn default() -> Self {
113 Self {
114 trim_offset: 0,
115 trim_end: 0,
116 loop_point: 0,
117 slices: from_fn(|_| Slice::default()),
118 slice_count: 0,
119 }
120 }
121}
122
123#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
125pub struct MarkersFile {
126 #[serde(with = "BigArray")]
127 pub header: [u8; 21],
128
129 pub datatype_version: u8,
148
149 pub flex_slots: Box<Array<SlotPlayback, 128>>,
151
152 pub recorder_slots: Box<Array<SlotPlayback, 8>>,
154
155 pub static_slots: Box<Array<SlotPlayback, 128>>,
157
158 pub checksum: u16,
160}
161
162impl MarkersFile {
163 #[allow(dead_code)]
164 fn validate(&self) -> RBoxErr<bool> {
165 for slot in self.flex_slots.iter() {
166 slot.validate()?;
167 }
168 for slot in self.recorder_slots.iter() {
169 slot.validate()?;
170 }
171 for slot in self.static_slots.iter() {
172 slot.validate()?;
173 }
174
175 Ok(true)
176 }
177}
178
179impl SwapBytes for MarkersFile {
180 type T = MarkersFile;
181 fn swap_bytes(self) -> RBoxErr<Self::T> {
182 let mut flex_slots = self.flex_slots.clone();
183 for (i, slot) in self.flex_slots.iter().enumerate() {
184 flex_slots[i] = slot.clone().swap_bytes()?;
185 }
186
187 let mut recorder_slots = self.recorder_slots.clone();
188 for (i, slot) in self.recorder_slots.iter().enumerate() {
189 recorder_slots[i] = slot.clone().swap_bytes()?;
190 }
191
192 let mut static_slots = self.static_slots.clone();
193 for (i, slot) in self.static_slots.iter().enumerate() {
194 static_slots[i] = slot.clone().swap_bytes()?;
195 }
196
197 let bswapped = Self {
198 header: self.header,
199 datatype_version: self.datatype_version,
200 flex_slots,
201 recorder_slots,
202 static_slots,
203 checksum: self.checksum.swap_bytes(),
204 };
205
206 Ok(bswapped)
207 }
208}
209
210impl Decode for MarkersFile {
211 fn decode(bytes: &[u8]) -> RBoxErr<Self>
212 where
213 Self: Sized,
214 Self: for<'a> Deserialize<'a>,
215 {
216 let mut x: Self = bincode::deserialize(bytes)?;
217 #[cfg(target_endian = "little")]
218 {
219 x = x.swap_bytes()?;
220 }
221
222 Ok(x)
223 }
224}
225
226impl Encode for MarkersFile {
227 fn encode(&self) -> RBoxErr<Vec<u8>> {
228 let bytes = bincode::serialize(self)?;
229 Ok(bytes)
230 }
231}
232
233impl MarkersFile {
234 fn new(
235 flex_slots: [SlotPlayback; 128],
236 recorder_slots: [SlotPlayback; 8],
237 static_slots: [SlotPlayback; 128],
238 ) -> RBoxErr<Self> {
239 let mut init = Self {
240 header: MARKERS_HEADER,
241 datatype_version: MARKERS_FILE_VERSION,
242 flex_slots: Array(flex_slots).into(),
243 recorder_slots: Array(recorder_slots).into(),
244 static_slots: Array(static_slots).into(),
245 checksum: 0,
246 };
247
248 init.checksum = init.calculate_checksum()?;
249 Ok(init)
250 }
251}
252
253impl Default for MarkersFile {
254 fn default() -> Self {
255 Self::new(
256 from_fn(|_| SlotPlayback::default()),
257 from_fn(|_| SlotPlayback::default()),
258 from_fn(|_| SlotPlayback::default()),
259 )
260 .unwrap()
261 }
262}
263
264impl IsDefault for MarkersFile {
265 fn is_default(&self) -> bool {
266 let default = &mut MarkersFile::default();
267 default == self
268 }
269}
270
271impl CalculateChecksum for MarkersFile {
272 fn calculate_checksum(&self) -> RBoxErr<u16> {
273 let bytes = self.encode()?;
274 let mut chk: u16 = 0;
275 for byte in &bytes[16..bytes.len() - 2] {
276 chk = chk.wrapping_add(*byte as u16);
277 }
278 Ok(chk)
279 }
280}
281
282impl CheckHeader for MarkersFile {
283 fn check_header(&self) -> bool {
284 self.header == MARKERS_HEADER
285 }
286}
287
288impl CheckChecksum for MarkersFile {
289 fn check_checksum(&self) -> RBoxErr<bool> {
290 let calculated = self.calculate_checksum()?;
291 if self.checksum != calculated {
292 eprintln!(
293 "Markers file: Invalid checksum: current={} calcuated={}",
294 self.checksum, calculated,
295 );
296 Err(OtToolsIoErrors::TodoError.into())
297 } else {
298 Ok(true)
299 }
300 }
301}
302
303impl CheckIntegrity for MarkersFile {}
304
305#[derive(Debug, Serialize, Deserialize, Decodeable)]
306pub struct MarkersRawBytes {
307 pub data: Box<Array<u8, 0x00032898>>,
308}
309
310#[cfg(test)]
311mod tests {
312 mod can_read_generated_files {
313 use crate::markers::MarkersFile;
314 use crate::samples::slices::Slice;
315 use crate::test_utils::get_markers_dirpath;
316 use crate::{read_type_from_bin_file, CalculateChecksum};
317
318 const SAMPLE_LEN: u32 = 2679;
319
320 fn test_helper(fname: &str) -> MarkersFile {
321 let fpath = get_markers_dirpath().join(fname);
322 let r = read_type_from_bin_file::<MarkersFile>(&fpath);
323
324 println!("result: {r:?}");
325 assert!(r.is_ok());
326 r.unwrap()
327 }
328
329 fn assert_helper(m: MarkersFile, mut v: MarkersFile) {
330 v.checksum = v.calculate_checksum().unwrap();
331 println!("header");
332 assert_eq!(m.header, v.header);
333 println!("datatype version");
334 assert_eq!(m.datatype_version, v.datatype_version);
335 println!("recorder slots");
336 assert_eq!(m.recorder_slots, v.recorder_slots);
337 println!("flex slots");
338 assert_eq!(m.flex_slots, v.flex_slots);
339 println!("static slots");
340 assert_eq!(m.static_slots, v.static_slots);
341 println!("checksum slots");
342 assert_eq!(m.checksum, v.checksum);
343 }
344
345 #[test]
346 fn flex_slot_1_noedit() {
347 let m = test_helper("flex-slot-1-noedit.work");
348 let mut valid = MarkersFile::default();
349 valid.flex_slots[0].trim_end = SAMPLE_LEN;
350 assert_helper(m, valid);
351 }
352
353 #[test]
354 fn flex_slot_128_noedit() {
355 let m = test_helper("flex-slot-128-noedit.work");
356 let mut valid = MarkersFile::default();
357 valid.flex_slots[127].trim_end = SAMPLE_LEN;
358 assert_helper(m, valid);
359 }
360
361 #[test]
362 fn static_slot_1_noedit() {
363 let m = test_helper("static-slot-1-noedit.work");
364 let mut valid = MarkersFile::default();
365 valid.recorder_slots[0].trim_end = SAMPLE_LEN;
369 valid.static_slots[0].trim_end = SAMPLE_LEN;
370 assert_helper(m, valid);
371 }
372
373 #[test]
374 fn static_slot_128_noedit() {
375 let m = test_helper("static-slot-128-noedit.work");
376 let mut valid = MarkersFile::default();
377 valid.recorder_slots[0].trim_end = SAMPLE_LEN;
381 valid.static_slots[127].trim_end = SAMPLE_LEN;
382 assert_helper(m, valid);
383 }
384
385 #[test]
386 fn recorder_slot_1_noedit() {
387 let m = test_helper("recorder-slot-1-noedit.work");
388 let mut valid = MarkersFile::default();
389 valid.recorder_slots[0].trim_end = SAMPLE_LEN;
390 assert_helper(m, valid);
391 }
392
393 #[test]
394 fn flex_slot_1_loop_edit() {
395 let m = test_helper("flex-slot-1-loop-edit.work");
396 let mut valid = MarkersFile::default();
397 valid.flex_slots[0].trim_end = SAMPLE_LEN;
398 valid.flex_slots[0].loop_point = 1456;
399 assert_helper(m, valid);
400 }
401
402 #[test]
403 fn flex_slot_1_start_edit() {
404 let m = test_helper("flex-slot-1-start-edit.work");
405 let mut valid = MarkersFile::default();
406 valid.flex_slots[0].trim_end = SAMPLE_LEN;
407 valid.flex_slots[0].trim_offset = 1456;
408 valid.flex_slots[0].loop_point = 1456;
410 assert_helper(m, valid);
411 }
412
413 #[ignore]
415 #[test]
416 fn flex_slot_1_end_edit() {
417 let m = test_helper("flex-slot-1-start-edit.work");
418 let mut valid = MarkersFile::default();
419 valid.flex_slots[0].trim_end = SAMPLE_LEN;
420 assert_helper(m, valid);
421 }
422
423 #[test]
424 fn flex_slot_1_slice_1_noloop() {
425 let m = test_helper("flex-slot-1-slice-1-noloop.work");
426 let mut valid = MarkersFile::default();
427 valid.flex_slots[0].trim_end = SAMPLE_LEN;
428 valid.flex_slots[0].slice_count = 1;
429 valid.flex_slots[0].slices[0] = Slice {
430 trim_start: 0,
431 trim_end: 416,
432 loop_start: 0xFFFFFFFF,
433 };
434 assert_helper(m, valid);
435 }
436
437 #[test]
438 fn flex_slot_1_slice_1_looped() {
439 let m = test_helper("flex-slot-1-slice-1-looped.work");
440 let mut valid = MarkersFile::default();
441 valid.flex_slots[0].trim_end = SAMPLE_LEN;
442 valid.flex_slots[0].slice_count = 1;
443 valid.flex_slots[0].slices[0] = Slice {
444 trim_start: 0,
445 trim_end: 416,
446 loop_start: 202,
447 };
448 assert_helper(m, valid);
449 }
450
451 #[test]
453 fn flex_slot_1_slice_4_noloop() {
454 let m = test_helper("flex-slot-1-slice-4-noloop.work");
455 let mut valid = MarkersFile::default();
456 valid.flex_slots[0].trim_end = SAMPLE_LEN;
457 valid.flex_slots[0].slice_count = 4;
458 valid.flex_slots[0].slices[0] = Slice {
459 trim_start: 0,
460 trim_end: 668,
461 loop_start: 0xFFFFFFFF,
462 };
463 valid.flex_slots[0].slices[1] = Slice {
464 trim_start: 668,
465 trim_end: 1338,
466 loop_start: 0xFFFFFFFF,
467 };
468 valid.flex_slots[0].slices[2] = Slice {
469 trim_start: 1338,
470 trim_end: 2008,
471 loop_start: 0xFFFFFFFF,
472 };
473 valid.flex_slots[0].slices[3] = Slice {
474 trim_start: 2008,
475 trim_end: SAMPLE_LEN,
476 loop_start: 0xFFFFFFFF,
477 };
478 assert_helper(m, valid);
479 }
480 }
481}