1use crate::parameters::{Parameter, ParameterData, Parameters};
3use crate::processor::Processor;
4use crate::{C3dWriteError, C3dParseError};
5use grid::Grid;
6use std::collections::HashMap;
7use std::ops::{Deref, DerefMut};
19
20#[derive(Debug, Clone, PartialEq, Default)]
21pub struct EventContext {
22 pub used: Option<i16>,
23 pub icon_ids: Option<Vec<u16>>,
24 pub labels: Option<Vec<String>>,
25 pub descriptions: Option<Vec<String>>,
26 pub colours: Option<Vec<[u8; 3]>>,
27}
28
29impl EventContext {
30 pub fn new() -> Self {
31 EventContext::default()
32 }
33
34 pub(crate) fn from_parameters(parameters: &mut Parameters) -> Result<Self, C3dParseError> {
35 let used = parameters.remove("EVENT_CONTEXT", "USED");
36 let used = match used {
37 Some(parameter) => Some(parameter.as_ref().try_into()?),
38 _ => None,
39 };
40 let icon_ids = parameters.remove("EVENT_CONTEXT", "ICON_IDS");
41 let icon_ids = match icon_ids {
42 Some(parameter) => Some(parameter.as_ref().try_into()?),
43 _ => None,
44 };
45 let labels = parameters.remove("EVENT_CONTEXT", "LABELS");
46 let labels = match labels {
47 Some(parameter) => Some(parameter.as_ref().try_into()?),
48 _ => None,
49 };
50 let descriptions = parameters.remove("EVENT_CONTEXT", "DESCRIPTIONS");
51 let descriptions = match descriptions {
52 Some(parameter) => Some(parameter.as_ref().try_into()?),
53 _ => None,
54 };
55 Ok(EventContext {
56 used,
57 icon_ids,
58 labels,
59 descriptions,
60 colours: get_colour_array(parameters, "EVENT_CONTEXT", "COLOURS"),
61 })
62 }
63}
64
65fn get_colour_array(
66 parameters: &mut Parameters,
67 group_name: &str,
68 parameter_name: &str,
69) -> Option<Vec<[u8; 3]>> {
70 let parameter = parameters.remove(group_name, parameter_name)?;
71 match ¶meter.data {
72 ParameterData::Byte(data) => {
73 if parameter.dimensions.len() == 2 {
74 let mut colours = Vec::new();
75 for row in 0..data.len() % 3 {
76 let mut colour = [0; 3];
77 colour[0] = data[row * 3];
78 colour[1] = data[row * 3 + 1];
79 colour[2] = data[row * 3 + 2];
80 colours.push(colour);
81 }
82 Some(colours)
83 } else {
84 None
85 }
86 }
87 _ => None,
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Default)]
93pub struct Events {
94 pub supports_events_labels: bool,
95 events: Vec<Event>,
96 event_context: EventContext,
97}
98
99impl ToString for Events {
100 fn to_string(&self) -> String {
101 let mut string = String::new();
102 string.push_str("Events:\n");
103 string.push_str(&format!(
104 " Supports events labels: {}\n",
105 self.supports_events_labels
106 ));
107 string.push_str(&format!(" Number of events: {}\n", self.events.len()));
108 for event in &self.events {
109 string.push_str(&format!(
110 " Event: {}\n",
111 event.id.iter().collect::<String>()
112 ));
113 string.push_str(&format!(" Label: {}\n", event.label));
114 string.push_str(&format!(" Display flag: {}\n", event.display_flag));
115 string.push_str(&format!(" Time: {}\n", event.time));
116 string.push_str(&format!(" Context: {}\n", event.context));
117 string.push_str(&format!(" Description: {}\n", event.description));
118 string.push_str(&format!(" Subject: {}\n", event.subject));
119 string.push_str(&format!(" Icon ID: {}\n", event.icon_id));
120 string.push_str(&format!(" Generic flag: {}\n", event.generic_flag));
121 }
122 string
123 }
124}
125
126impl Deref for Events {
127 type Target = Vec<Event>;
128
129 fn deref(&self) -> &Self::Target {
130 &self.events
131 }
132}
133
134impl DerefMut for Events {
135 fn deref_mut(&mut self) -> &mut Self::Target {
136 &mut self.events
137 }
138}
139
140#[derive(Debug, Clone, Default, PartialEq)]
142pub struct Event {
143 pub id: [char; 4], pub label: String, pub display_flag: bool,
146 pub time: f32,
147 pub context: String,
148 pub description: String,
149 pub subject: String,
150 pub icon_id: i16,
151 pub generic_flag: i16,
152}
153
154#[allow(dead_code)]
155impl Event {
156 pub fn new() -> Event {
157 Event::default()
158 }
159}
160
161impl Events {
162 #[allow(dead_code)]
163 pub(crate) fn new() -> Events {
164 Events::default()
165 }
166
167 pub fn num_events(&self) -> usize {
170 self.events.len()
171 }
172
173 pub fn event(&self, index: usize) -> Option<&Event> {
177 if index < self.events.len() {
178 Some(&self.events[index])
179 } else {
180 None
181 }
182 }
183
184 pub(crate) fn from_header_and_parameters(
185 header_block: &[u8; 512],
186 parameters: &mut Parameters,
187 processor: &Processor,
188 ) -> Result<Events, C3dParseError> {
189 let supports_events_labels =
190 processor.u16([header_block[298], header_block[299]]) == 0x3039;
191 let num_time_events =
192 get_num_time_events(header_block, parameters, &processor, supports_events_labels)?;
193
194 let mut events = Vec::<Event>::with_capacity(num_time_events);
195
196 let times = get_times_array(parameters)?;
197 let labels = get_labels_array(parameters)?;
198 let contexts = get_contexts_array(parameters);
199 let descriptions = get_descriptions_array(parameters);
200 let subjects = get_subjects_array(parameters);
201 let icon_ids = get_icon_ids_array(parameters);
202 let generic_flags = get_generic_flags_array(parameters);
203
204 for event_num in 0..num_time_events {
206 let id = get_event_id(event_num, header_block)?;
207 let label = match supports_events_labels && labels.len() > event_num {
208 true => labels[event_num].clone(),
209 false => "".to_string(),
210 };
211 let display_flag = match supports_events_labels {
212 true => get_display_flag(event_num, header_block),
213 false => true,
214 };
215 let time = verify_time(event_num, header_block, ×, processor)?;
216 let context = get_event_context(event_num, &contexts);
217 let description = get_event_description(event_num, &descriptions);
218 let subject = get_event_subject(event_num, &subjects);
219 let icon_id = get_event_icon_id(event_num, &icon_ids);
220 let generic_flag = get_event_generic_flag(event_num, &generic_flags);
221 events.push(Event {
222 id,
223 label,
224 display_flag,
225 time,
226 context,
227 description,
228 subject,
229 icon_id,
230 generic_flag,
231 });
232 }
233 Ok(Events {
234 supports_events_labels,
235 events,
236 event_context: EventContext::from_parameters(parameters)?,
237 })
238 }
239
240 pub(crate) fn write(
241 &self,
242 processor: &Processor,
243 group_names_to_ids: &HashMap<String, usize>,
244 ) -> Result<Vec<u8>, C3dWriteError> {
245 let mut bytes = Vec::new();
246 bytes.extend(Parameter::integer(self.events.len() as i16).write(
247 processor,
248 "USED".to_string(),
249 group_names_to_ids["EVENT"],
250 false,
251 )?);
252 let times = self
253 .events
254 .iter()
255 .map(|event| event.time)
256 .collect::<Vec<f32>>();
257 if times.len() > 0 {
258 bytes.extend(Parameter::floats(times)?.write(
259 processor,
260 "TIMES".to_string(),
261 group_names_to_ids["EVENT"],
262 false,
263 )?);
264 }
265 let labels = self
266 .events
267 .iter()
268 .map(|event| event.label.clone())
269 .collect::<Vec<String>>();
270 if labels.len() > 0 {
271 bytes.extend(Parameter::strings(labels).write(
272 processor,
273 "LABELS".to_string(),
274 group_names_to_ids["EVENT"],
275 false,
276 )?);
277 }
278 let contexts = self
279 .events
280 .iter()
281 .map(|event| event.context.clone())
282 .collect::<Vec<String>>();
283 if contexts.len() > 0 {
284 bytes.extend(Parameter::strings(contexts).write(
285 processor,
286 "CONTEXTS".to_string(),
287 group_names_to_ids["EVENT"],
288 false,
289 )?);
290 }
291 let descriptions = self
292 .events
293 .iter()
294 .map(|event| event.description.clone())
295 .collect::<Vec<String>>();
296 if descriptions.len() > 0 {
297 bytes.extend(Parameter::strings(descriptions).write(
298 processor,
299 "DESCRIPTIONS".to_string(),
300 group_names_to_ids["EVENT"],
301 false,
302 )?);
303 }
304 let subjects = self
305 .events
306 .iter()
307 .map(|event| event.subject.clone())
308 .collect::<Vec<String>>();
309 if subjects.len() > 0 {
310 bytes.extend(Parameter::strings(subjects).write(
311 processor,
312 "SUBJECTS".to_string(),
313 group_names_to_ids["EVENT"],
314 false,
315 )?);
316 }
317 let icon_ids = self
318 .events
319 .iter()
320 .map(|event| event.icon_id)
321 .collect::<Vec<i16>>();
322 if icon_ids.len() > 0 {
323 bytes.extend(Parameter::integers(icon_ids)?.write(
324 processor,
325 "ICON_IDS".to_string(),
326 group_names_to_ids["EVENT"],
327 false,
328 )?);
329 }
330 let generic_flags = self
331 .events
332 .iter()
333 .map(|event| event.generic_flag)
334 .collect::<Vec<i16>>();
335 if generic_flags.len() > 0 {
336 bytes.extend(Parameter::integers(generic_flags)?.write(
337 processor,
338 "GENERIC_FLAGS".to_string(),
339 group_names_to_ids["EVENT"],
340 false,
341 )?);
342 }
343 let event_context_used = match &self.event_context.used {
344 Some(used) => *used,
345 _ => 0,
346 };
347 if event_context_used > 0 {
348 bytes.extend(Parameter::integer(event_context_used).write(
349 processor,
350 "USED".to_string(),
351 group_names_to_ids["EVENT_CONTEXT"],
352 false,
353 )?);
354 }
355 let event_context_icon_ids = match &self.event_context.icon_ids {
356 Some(icon_ids) => icon_ids.iter().map(|id| *id as i16).collect::<Vec<i16>>(),
357 _ => Vec::new(),
358 };
359 if event_context_icon_ids.len() > 0 {
360 bytes.extend(Parameter::integers(event_context_icon_ids)?.write(
361 processor,
362 "ICON_IDS".to_string(),
363 group_names_to_ids["EVENT_CONTEXT"],
364 false,
365 )?);
366 }
367 let event_context_labels = match &self.event_context.labels {
368 Some(labels) => labels.clone(),
369 _ => Vec::new(),
370 };
371 if event_context_labels.len() > 0 {
372 bytes.extend(Parameter::strings(event_context_labels).write(
373 processor,
374 "LABELS".to_string(),
375 group_names_to_ids["EVENT_CONTEXT"],
376 false,
377 )?);
378 }
379 let event_context_descriptions = match &self.event_context.descriptions {
380 Some(descriptions) => descriptions.clone(),
381 _ => Vec::new(),
382 };
383 if event_context_descriptions.len() > 0 {
384 bytes.extend(Parameter::strings(event_context_descriptions).write(
385 processor,
386 "DESCRIPTIONS".to_string(),
387 group_names_to_ids["EVENT_CONTEXT"],
388 false,
389 )?);
390 }
391 let event_context_colours = match &self.event_context.colours {
392 Some(colours) => colours.clone(),
393 _ => Vec::new(),
394 };
395 if event_context_colours.len() > 0 {
396 let mut colours_grid = Grid::new(0, 3);
397 for colour in event_context_colours {
398 colours_grid.push_row(colour.to_vec());
399 }
400 bytes.extend(Parameter::byte_grid(colours_grid).write(
401 processor,
402 "COLOURS".to_string(),
403 group_names_to_ids["EVENT_CONTEXT"],
404 false,
405 )?);
406 }
407 Ok(bytes)
408 }
409}
410
411fn get_num_time_events(
412 header_block: &[u8; 512],
413 parameters: &mut Parameters,
414 processor: &Processor,
415 supports_events_labels: bool,
416) -> Result<usize, C3dParseError> {
417 match supports_events_labels {
418 true => {
419 let num_time_events = processor.i16([header_block[300], header_block[301]]);
420
421 if num_time_events > 18 {
422 return Err(C3dParseError::TooManyEvents(num_time_events));
423 }
424 let parameter_num_time_events = parameters.remove("EVENT", "USED");
425 if parameter_num_time_events.is_some() {
426 let parameter_num_time_events: i16 =
427 parameter_num_time_events.unwrap().as_ref().try_into()?;
428 if parameter_num_time_events != num_time_events as i16 {
429 return Ok(parameter_num_time_events as usize);
430 } else {
431 return Ok(num_time_events as usize);
432 }
433 }
434 }
435 false => {
436 let parameter_num_time_events = parameters.remove("EVENT", "USED");
437 if parameter_num_time_events.is_some() {
438 let parameter_num_time_events: i16 =
439 parameter_num_time_events.unwrap().as_ref().try_into()?;
440 return Ok(parameter_num_time_events as usize);
441 }
442 }
443 }
444 Ok(0)
445}
446
447fn get_times_array(parameters: &mut Parameters) -> Result<Vec<[f32; 2]>, C3dParseError> {
448 let parameter = parameters.remove("EVENT", "TIMES");
449 if parameter.is_none() {
450 Ok(Vec::new())
451 } else {
452 let parameter = parameter.unwrap();
453 match ¶meter.data {
454 ParameterData::Float(data) => {
455 if parameter.dimensions.len() == 2 && data.len() > 1 {
456 let mut times = Vec::new();
457 for row in 0..data.len() % 2 {
458 let mut time = [0.0; 2];
459 time[0] = data[row * 2];
460 time[1] = data[row * 2 + 1];
461 times.push(time);
462 }
463 Ok(times)
464 } else {
465 Ok(Vec::new())
466 }
467 }
468 _ => Err(C3dParseError::InvalidParameterType(
469 "EVENT ".to_string() + "TIMES",
470 )),
471 }
472 }
473}
474
475fn get_labels_array(parameters: &mut Parameters) -> Result<Vec<String>, C3dParseError> {
476 let labels = parameters.remove("EVENT", "LABELS");
477 if labels.is_none() {
478 Ok(Vec::new())
479 } else {
480 let labels = labels.unwrap();
481 Ok(labels.as_ref().try_into()?)
482 }
483}
484
485fn get_contexts_array(parameters: &mut Parameters) -> Vec<String> {
486 let contexts = parameters.remove("EVENT", "CONTEXTS");
487 if contexts.is_none() {
488 Vec::new()
489 } else {
490 let contexts = contexts.unwrap();
491 contexts.as_ref().try_into().unwrap_or(Vec::new())
492 }
493}
494
495fn get_descriptions_array(parameters: &mut Parameters) -> Vec<String> {
496 let descriptions = parameters.remove("EVENT", "DESCRIPTIONS");
497 if descriptions.is_none() {
498 Vec::new()
499 } else {
500 let descriptions = descriptions.unwrap();
501 descriptions.as_ref().try_into().unwrap_or(Vec::new())
502 }
503}
504
505fn get_subjects_array(parameters: &mut Parameters) -> Vec<String> {
506 let subjects = parameters.remove("EVENT", "SUBJECTS");
507 if subjects.is_none() {
508 Vec::new()
509 } else {
510 let subjects = subjects.unwrap();
511 subjects.as_ref().try_into().unwrap_or(Vec::new())
512 }
513}
514
515fn get_icon_ids_array(parameters: &mut Parameters) -> Vec<i16> {
516 let icon_ids = parameters.remove("EVENT", "ICON_IDS");
517 if icon_ids.is_none() {
518 Vec::new()
519 } else {
520 let icon_ids = icon_ids.unwrap();
521 icon_ids.as_ref().try_into().unwrap_or(Vec::new())
522 }
523}
524
525fn get_generic_flags_array(parameters: &mut Parameters) -> Vec<i16> {
526 let generic_flags = parameters.remove("EVENT", "GENERIC_FLAGS");
527 if generic_flags.is_none() {
528 Vec::new()
529 } else {
530 let generic_flags = generic_flags.unwrap();
531 generic_flags.as_ref().try_into().unwrap_or(Vec::new())
532 }
533}
534
535fn verify_time(
536 event_num: usize,
537 header_block: &[u8; 512],
538 _time: &Vec<[f32; 2]>,
539 processor: &Processor,
540) -> Result<f32, C3dParseError> {
541 let time_start = 304 + (event_num * 4);
542 Ok(processor.f32(header_block[time_start..time_start + 4].try_into().unwrap()))
543 }
545
546fn get_event_id(event_num: usize, header_block: &[u8; 512]) -> Result<[char; 4], C3dParseError> {
547 if event_num > 18 {
548 return Ok([0x00 as char; 4]);
549 }
550 let label_start = 396 + (event_num * 4);
551 let label_bytes: [u8; 4] = header_block[label_start..label_start + 4]
552 .try_into()
553 .unwrap();
554 let label_chars = label_bytes
555 .iter()
556 .map(|b| *b as char)
557 .collect::<Vec<char>>();
558 Ok(label_chars.try_into().unwrap())
559}
560
561fn get_display_flag(event_num: usize, header_block: &[u8; 512]) -> bool {
562 let display_flag_start = 376 + event_num;
563 header_block[display_flag_start] == 0
564}
565
566fn get_event_context(event_num: usize, contexts: &Vec<String>) -> String {
567 if contexts.len() <= event_num {
568 return "".to_string();
569 }
570 contexts[event_num].clone()
571}
572
573fn get_event_description(event_num: usize, descriptions: &Vec<String>) -> String {
574 if descriptions.len() <= event_num {
575 return "".to_string();
576 }
577 descriptions[event_num].clone()
578}
579
580fn get_event_subject(event_num: usize, subjects: &Vec<String>) -> String {
581 if subjects.len() <= event_num {
582 return "".to_string();
583 }
584 subjects[event_num].clone()
585}
586
587fn get_event_icon_id(event_num: usize, icon_ids: &Vec<i16>) -> i16 {
588 if icon_ids.len() <= event_num {
589 return 0;
590 }
591 icon_ids[event_num]
592}
593
594fn get_event_generic_flag(event_num: usize, generic_flags: &Vec<i16>) -> i16 {
595 if generic_flags.len() <= event_num {
596 return 0;
597 }
598 generic_flags[event_num]
599}