1use crate::PacketGenerator;
6use crate::helpers::cpacket_paginator::paginate_cpackets;
7
8pub struct WristApp {
10 pub wrist_app_data: Vec<u8>,
12}
13
14impl WristApp {
15 pub fn from_zap_file(file_path: &str) -> std::io::Result<Self> {
19 let file_data = std::fs::read(file_path)?;
21
22 let wrist_app_data = Self::parse_zap_file(&file_data)?;
24
25 Ok(Self {
26 wrist_app_data,
27 })
28 }
29
30 pub fn parse_zap_file(zap_data: &[u8]) -> std::io::Result<Vec<u8>> {
37 const WRIST_APP_CODE_INDEX: usize = 18;
39
40 let mut sections = Vec::new();
42 let mut current_section = Vec::new();
43 let mut i = 0;
44
45 while i < zap_data.len() {
46 if zap_data[i] == 0xAC {
47 if !current_section.is_empty() {
49 sections.push(current_section);
50 current_section = Vec::new();
51 }
52
53 i += 1;
55 while i < zap_data.len() && !(zap_data[i] == b'\r' && i + 1 < zap_data.len() && zap_data[i + 1] == b'\n') {
56 i += 1;
57 }
58
59 if i < zap_data.len() && zap_data[i] == b'\r' && i + 1 < zap_data.len() && zap_data[i + 1] == b'\n' {
61 i += 2; }
63 } else {
64 current_section.push(zap_data[i]);
66 i += 1;
67 }
68 }
69
70 if !current_section.is_empty() {
72 sections.push(current_section);
73 }
74
75 if sections.len() <= WRIST_APP_CODE_INDEX {
77 return Err(std::io::Error::new(
78 std::io::ErrorKind::InvalidData,
79 format!("ZAP file does not contain enough sections, expected at least {}", WRIST_APP_CODE_INDEX + 1)
80 ));
81 }
82
83 let target_section = §ions[WRIST_APP_CODE_INDEX];
85
86 let mut result = Vec::new();
89
90 let mut i = 0;
92 while i + 1 < target_section.len() {
93 let high = Self::hex_digit_to_value(target_section[i]);
94 let low = Self::hex_digit_to_value(target_section[i + 1]);
95
96 if let (Some(high), Some(low)) = (high, low) {
97 result.push((high << 4) | low);
98 } else {
99 if high.is_none() {
101 i += 1;
102 }
103 }
105
106 i += 2;
107 }
108
109 Ok(result)
110 }
111
112 fn hex_digit_to_value(digit: u8) -> Option<u8> {
114 match digit {
115 b'0'..=b'9' => Some(digit - b'0'),
116 b'A'..=b'F' => Some(digit - b'A' + 10),
117 b'a'..=b'f' => Some(digit - b'a' + 10),
118 _ => None,
119 }
120 }
121}
122
123impl PacketGenerator for WristApp {
124 fn packets(&self) -> Vec<Vec<u8>> {
125 const CPACKET_CLEAR: [u8; 2] = [0x93, 0x02];
127 const CPACKET_SECT: [u8; 2] = [0x90, 0x02];
128 const CPACKET_DATA: [u8; 2] = [0x91, 0x02];
129 const CPACKET_END: [u8; 2] = [0x92, 0x02];
130 const CPACKET_DATA_LENGTH: usize = 32;
131
132 let wrist_app_data = &self.wrist_app_data;
135
136 let payloads = paginate_cpackets(&CPACKET_DATA, CPACKET_DATA_LENGTH, wrist_app_data);
138
139 let mut cpacket_sect = Vec::new();
141 cpacket_sect.extend_from_slice(&CPACKET_SECT);
142 cpacket_sect.push(payloads.len() as u8);
143 cpacket_sect.push(1); let mut all_packets = Vec::with_capacity(payloads.len() + 3);
147 all_packets.push(CPACKET_CLEAR.to_vec());
148 all_packets.push(cpacket_sect);
149 all_packets.extend(payloads);
150 all_packets.push(CPACKET_END.to_vec());
151
152 use crate::helpers::crc_packets_wrapper::wrap_packets_with_crc;
154 wrap_packets_with_crc(all_packets)
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 const EXAMPLE_ZAP: &[u8] = include_bytes!("../../fixtures/EXAMPLE.ZAP");
165
166 #[test]
167 fn test_wrist_app() {
168 let processed_data = WristApp::parse_zap_file(EXAMPLE_ZAP).unwrap();
170
171 let wrist_app = WristApp {
172 wrist_app_data: processed_data,
173 };
174
175 #[rustfmt::skip]
177 let expected = vec![
178 vec![
179 5,
180 147,
181 2,
182 48,
183 253
184 ],
185 vec![
186 7,
187 144,
188 2,
189 5,
190 1,
191 144,
192 251
193 ],
194 vec![
195 38,
196 145,
197 2,
198 1,
199 49,
200 53,
201 48,
202 115,
203 32,
204 100,
205 97,
206 116,
207 97,
208 58,
209 32,
210 76,
211 111,
212 114,
213 101,
214 109,
215 32,
216 105,
217 112,
218 115,
219 117,
220 109,
221 32,
222 100,
223 111,
224 108,
225 111,
226 114,
227 32,
228 115,
229 105,
230 116,
231 28,
232 52
233 ],
234 vec![
235 38,
236 145,
237 2,
238 2,
239 32,
240 97,
241 109,
242 101,
243 116,
244 44,
245 32,
246 99,
247 111,
248 110,
249 115,
250 101,
251 99,
252 116,
253 101,
254 116,
255 117,
256 114,
257 32,
258 97,
259 100,
260 105,
261 112,
262 105,
263 115,
264 99,
265 105,
266 110,
267 103,
268 32,
269 101,
270 108,
271 240,
272 169
273 ],
274 vec![
275 38,
276 145,
277 2,
278 3,
279 105,
280 116,
281 44,
282 32,
283 115,
284 101,
285 100,
286 32,
287 100,
288 111,
289 32,
290 101,
291 105,
292 117,
293 115,
294 109,
295 111,
296 100,
297 32,
298 116,
299 101,
300 109,
301 112,
302 111,
303 114,
304 32,
305 105,
306 110,
307 99,
308 105,
309 100,
310 105,
311 19,
312 82
313 ],
314 vec![
315 38,
316 145,
317 2,
318 4,
319 100,
320 117,
321 110,
322 116,
323 32,
324 117,
325 116,
326 32,
327 108,
328 97,
329 98,
330 111,
331 114,
332 101,
333 32,
334 101,
335 116,
336 32,
337 100,
338 111,
339 108,
340 111,
341 114,
342 101,
343 32,
344 109,
345 97,
346 103,
347 110,
348 97,
349 32,
350 97,
351 208,
352 63
353 ],
354 vec![
355 12,
356 145,
357 2,
358 5,
359 108,
360 105,
361 113,
362 117,
363 97,
364 46,
365 127,
366 67
367 ],
368 vec![
369 5,
370 146,
371 2,
372 160,
373 252
374 ]
375 ];
376
377 assert_eq!(wrist_app.packets(), expected);
378 }
379}