1use crate::slices::{Slice, SliceError, SLICE_LOOP_POINT_DEFAULT};
22use crate::traits::SwapBytes;
23use crate::{
24 HasChecksumField, HasFileVersionField, HasHeaderField, OctatrackFileIO, OtToolsIoError,
25};
26use ot_tools_io_derive::{IntegrityChecks, IsDefaultCheck};
27use serde::{Deserialize, Serialize};
28use serde_big_array::{Array, BigArray};
29use std::array::from_fn;
30use thiserror::Error;
31#[derive(Debug, Error)]
42pub enum MarkersError {
43 #[error("invalid loop point: {value}")]
44 Loop { value: u32 },
45 #[error("invalid trim: start={start} end={end}")]
46 Trim { start: u32, end: u32 },
47 #[error("invalid slice count: {value}")]
48 SliceCount { value: u32 },
49 #[error("invalid slice")]
50 Slice(#[from] SliceError),
51}
52
53pub const MARKERS_HEADER: [u8; 21] = [
55 0x46, 0x4f, 0x52, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x44, 0x50, 0x53, 0x31, 0x53, 0x41, 0x4d, 0x50,
56 0x00, 0x00, 0x00, 0x00, 0x00,
57];
58
59pub const MARKERS_FILE_VERSION: u8 = 4;
61
62#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Hash)]
73pub struct SlotMarkers {
74 pub trim_offset: u32,
76
77 pub trim_end: u32,
79
80 pub loop_point: u32,
82
83 #[serde(with = "BigArray")]
85 pub slices: [Slice; 64],
86
87 pub slice_count: u32,
89}
90
91impl SlotMarkers {
92 fn validate(&self) -> Result<bool, MarkersError> {
93 for slice in self.slices.iter() {
94 slice.validate()?;
95 }
96 if self.trim_offset >= self.trim_end {
97 return Err(MarkersError::Trim {
98 start: self.trim_offset,
99 end: self.trim_end,
100 });
101 }
102 if self.slice_count != self.slices.len() as u32 {
103 return Err(MarkersError::SliceCount {
104 value: self.slice_count,
105 });
106 }
107 if !(self.loop_point >= self.trim_offset
108 || self.loop_point <= self.trim_end
109 || self.loop_point == SLICE_LOOP_POINT_DEFAULT)
110 {
111 return Err(MarkersError::Loop {
112 value: self.loop_point,
113 });
114 }
115
116 Ok(true)
117 }
118}
119
120impl SwapBytes for SlotMarkers {
121 fn swap_bytes(self) -> Self {
122 let mut slices: [Slice; 64] = self.slices;
123
124 for (i, slice) in self.slices.iter().enumerate() {
125 slices[i] = slice.swap_bytes();
126 }
127
128 Self {
129 trim_offset: self.trim_offset.swap_bytes(),
130 trim_end: self.trim_end.swap_bytes(),
131 loop_point: self.loop_point.swap_bytes(),
132 slices,
133 slice_count: self.slice_count.swap_bytes(),
134 }
135 }
136}
137
138impl Default for SlotMarkers {
139 fn default() -> Self {
140 Self {
141 trim_offset: 0,
142 trim_end: 0,
143 loop_point: 0,
144 slices: from_fn(|_| Slice::default()),
145 slice_count: 0,
146 }
147 }
148}
149
150#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, IntegrityChecks, IsDefaultCheck)]
152pub struct MarkersFile {
153 #[serde(with = "BigArray")]
154 pub header: [u8; 21],
155
156 pub datatype_version: u8,
175
176 pub flex_slots: Box<Array<SlotMarkers, 136>>,
178
179 pub static_slots: Box<Array<SlotMarkers, 128>>,
181
182 pub checksum: u16,
184}
185
186impl OctatrackFileIO for MarkersFile {
187 fn encode(&self) -> Result<Vec<u8>, OtToolsIoError>
188 where
189 Self: Serialize,
190 {
191 let mut chkd = self.clone();
192 chkd.checksum = self.calculate_checksum()?;
193 let encoded = if cfg!(target_endian = "little") {
194 bincode::serialize(&chkd.swap_bytes())?
195 } else {
196 bincode::serialize(&chkd)?
197 };
198 Ok(encoded)
199 }
200
201 fn decode(bytes: &[u8]) -> Result<Self, OtToolsIoError>
202 where
203 Self: Sized,
204 Self: for<'a> Deserialize<'a>,
205 {
206 let mut x: Self = bincode::deserialize(bytes)?;
207 #[cfg(target_endian = "little")]
208 {
209 x = x.swap_bytes();
210 }
211
212 Ok(x)
213 }
214}
215
216impl MarkersFile {
217 #[allow(dead_code)]
218 fn validate(&self) -> Result<bool, MarkersError> {
219 for slot in self.flex_slots.iter() {
220 slot.validate()?;
221 }
222 for slot in self.static_slots.iter() {
223 slot.validate()?;
224 }
225
226 Ok(true)
227 }
228}
229
230impl SwapBytes for MarkersFile {
231 fn swap_bytes(self) -> Self {
232 let mut flex_slots = self.flex_slots.clone();
233 for (i, slot) in self.flex_slots.iter().enumerate() {
234 flex_slots[i] = slot.clone().swap_bytes();
235 }
236
237 let mut static_slots = self.static_slots.clone();
238 for (i, slot) in self.static_slots.iter().enumerate() {
239 static_slots[i] = slot.clone().swap_bytes();
240 }
241
242 Self {
243 header: self.header,
244 datatype_version: self.datatype_version,
245 flex_slots,
246 static_slots,
247 checksum: self.checksum.swap_bytes(),
248 }
249 }
250}
251
252impl MarkersFile {
253 fn new(
254 flex_slots: [SlotMarkers; 136],
255 static_slots: [SlotMarkers; 128],
256 ) -> Result<Self, OtToolsIoError> {
257 let mut init = Self {
258 header: MARKERS_HEADER,
259 datatype_version: MARKERS_FILE_VERSION,
260 flex_slots: Array(flex_slots).into(),
261 static_slots: Array(static_slots).into(),
262 checksum: 0,
263 };
264
265 init.checksum = init.calculate_checksum()?;
266 Ok(init)
267 }
268}
269
270impl Default for MarkersFile {
271 fn default() -> Self {
272 Self::new(
273 from_fn(|_| SlotMarkers::default()),
274 from_fn(|_| SlotMarkers::default()),
275 )
276 .unwrap()
277 }
278}
279
280impl HasChecksumField for MarkersFile {
281 fn calculate_checksum(&self) -> Result<u16, OtToolsIoError> {
282 let bytes = bincode::serialize(self)?;
283 let mut chk: u16 = 0;
284 for byte in &bytes[16..bytes.len() - 2] {
285 chk = chk.wrapping_add(*byte as u16);
286 }
287 Ok(chk)
288 }
289 fn check_checksum(&self) -> Result<bool, OtToolsIoError> {
290 Ok(self.checksum == self.calculate_checksum()?)
291 }
292}
293
294impl HasHeaderField for MarkersFile {
295 fn check_header(&self) -> Result<bool, OtToolsIoError> {
296 Ok(self.header == MARKERS_HEADER)
297 }
298}
299
300impl HasFileVersionField for MarkersFile {
301 fn check_file_version(&self) -> Result<bool, OtToolsIoError> {
302 Ok(self.datatype_version == MARKERS_FILE_VERSION)
303 }
304}