1use crate::bytes::{Reader, Writer, checked_i32, vec_for_count};
18use crate::error::{Error, Result};
19use crate::message::{self, NAT_MODELDEF};
20use crate::version::{Layout, ParsePolicy, Version};
21
22pub const TYPE_MARKER_SET: i32 = 0;
24pub const TYPE_RIGID_BODY: i32 = 1;
26pub const TYPE_SKELETON: i32 = 2;
28pub const TYPE_FORCE_PLATE: i32 = 3;
30pub const TYPE_DEVICE: i32 = 4;
32pub const TYPE_CAMERA: i32 = 5;
34pub const TYPE_ASSET: i32 = 6;
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40pub struct MarkerSetDef {
41 pub name: String,
43 pub marker_names: Vec<String>,
45}
46
47#[derive(Debug, Clone, PartialEq)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59pub struct RigidBodyDef {
60 pub name: String,
62 pub id: i32,
64 pub parent_id: i32,
66 pub offset: [f32; 3],
68 pub tail: Vec<u8>,
70}
71
72#[derive(Debug, Clone, PartialEq)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80pub struct SkeletonDef {
81 pub name: String,
83 pub id: i32,
85 pub rigid_bodies: Vec<RigidBodyDef>,
88}
89
90#[derive(Debug, Clone, PartialEq)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96pub struct ForcePlateDef {
97 pub id: i32,
99 pub serial_number: String,
101 pub width: f32,
103 pub length: f32,
105 pub origin: [f32; 3],
107 pub calibration_matrix: [[f32; 12]; 12],
109 pub corners: [[f32; 3]; 4],
111 pub plate_type: i32,
113 pub channel_data_type: i32,
116 pub channel_names: Vec<String>,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq)]
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126pub struct DeviceDef {
127 pub id: i32,
129 pub name: String,
131 pub serial_number: String,
133 pub device_type: i32,
135 pub channel_data_type: i32,
137 pub channel_names: Vec<String>,
139}
140
141#[derive(Debug, Clone, PartialEq)]
147#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
148pub struct CameraDef {
149 pub name: String,
151 pub position: [f32; 3],
153 pub orientation: [f32; 4],
155}
156
157#[derive(Debug, Clone, PartialEq)]
162#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
163pub struct MarkerDef {
164 pub name: String,
166 pub id: i32,
168 pub position: [f32; 3],
170 pub size: f32,
172 pub params: u16,
174}
175
176#[derive(Debug, Clone, PartialEq)]
182#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
183pub struct AssetDef {
184 pub name: String,
186 pub asset_type: i32,
188 pub id: i32,
190 pub rigid_bodies: Vec<RigidBodyDef>,
192 pub markers: Vec<MarkerDef>,
194}
195
196#[derive(Debug, Clone, PartialEq)]
198#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
199#[non_exhaustive]
200pub enum DataSet {
201 MarkerSet(MarkerSetDef),
203 RigidBody(RigidBodyDef),
205 Skeleton(SkeletonDef),
207 ForcePlate(Box<ForcePlateDef>),
209 Device(DeviceDef),
211 Camera(CameraDef),
213 Asset(AssetDef),
215 Other {
218 type_id: i32,
220 body: Vec<u8>,
222 },
223}
224
225impl DataSet {
226 #[must_use]
228 pub const fn type_id(&self) -> i32 {
229 match self {
230 Self::MarkerSet(_) => TYPE_MARKER_SET,
231 Self::RigidBody(_) => TYPE_RIGID_BODY,
232 Self::Skeleton(_) => TYPE_SKELETON,
233 Self::ForcePlate(_) => TYPE_FORCE_PLATE,
234 Self::Device(_) => TYPE_DEVICE,
235 Self::Camera(_) => TYPE_CAMERA,
236 Self::Asset(_) => TYPE_ASSET,
237 Self::Other { type_id, .. } => *type_id,
238 }
239 }
240}
241
242#[derive(Debug, Clone, PartialEq)]
244#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
245pub struct ModelDef {
246 pub datasets: Vec<DataSet>,
248}
249
250fn invalid_count(count: i32, offset: usize) -> Error {
251 Error::InvalidCount { count, offset }
252}
253
254fn count(r: &mut Reader<'_>, min_entry_bytes: usize) -> Result<usize> {
257 let offset = r.position();
258 let raw = r.i32()?;
259 if raw < 0 {
260 return Err(invalid_count(raw, offset));
261 }
262 r.guard_count(raw, min_entry_bytes)
263}
264
265fn size(r: &mut Reader<'_>) -> Result<usize> {
266 let offset = r.position();
267 let raw = r.i32()?;
268 if raw < 0 {
269 return Err(invalid_count(raw, offset));
270 }
271 let size = usize::try_from(raw).map_err(|_| invalid_count(raw, offset))?;
272 if size > r.remaining() {
273 return Err(Error::UnexpectedEof {
274 offset: r.position(),
275 needed: size,
276 available: r.remaining(),
277 });
278 }
279 Ok(size)
280}
281
282fn read_marker_set_def(r: &mut Reader<'_>) -> Result<MarkerSetDef> {
283 let name = r.cstr()?;
284 let n = count(r, 1)?;
285 let mut marker_names = vec_for_count(n);
286 for _ in 0..n {
287 marker_names.push(r.cstr()?);
288 }
289 Ok(MarkerSetDef { name, marker_names })
290}
291
292fn rigid_marker_min(layout: Layout) -> usize {
293 if layout.rigid_body_marker_names() {
294 17
295 } else {
296 16
297 }
298}
299
300fn walk_rigid_body_tail(r: &mut Reader<'_>, layout: Layout) -> Result<()> {
301 let n = count(r, rigid_marker_min(layout))?;
302 for _ in 0..n {
303 r.vec3()?;
304 }
305 for _ in 0..n {
306 r.i32()?;
307 }
308 if layout.rigid_body_marker_names() {
309 for _ in 0..n {
310 r.cstr()?;
311 }
312 }
313 Ok(())
314}
315
316fn read_rigid_body_def(r: &mut Reader<'_>, layout: Layout) -> Result<RigidBodyDef> {
317 let name = r.cstr()?;
318 let id = r.i32()?;
319 let parent_id = r.i32()?;
320 let offset = r.vec3()?;
321 let tail_start = r.position();
322 walk_rigid_body_tail(r, layout)?;
323 let tail = r.span(tail_start, r.position())?.to_vec();
324 Ok(RigidBodyDef {
325 name,
326 id,
327 parent_id,
328 offset,
329 tail,
330 })
331}
332
333const MIN_RIGID_BODY_DEF: usize = 25;
334const MIN_MARKER_DEF: usize = 23;
335
336fn read_rigid_body_defs(r: &mut Reader<'_>, layout: Layout) -> Result<Vec<RigidBodyDef>> {
337 let n = count(r, MIN_RIGID_BODY_DEF)?;
338 let mut out = vec_for_count(n);
339 for _ in 0..n {
340 out.push(read_rigid_body_def(r, layout)?);
341 }
342 Ok(out)
343}
344
345fn read_names(r: &mut Reader<'_>) -> Result<Vec<String>> {
346 let n = count(r, 1)?;
347 let mut out = vec_for_count(n);
348 for _ in 0..n {
349 out.push(r.cstr()?);
350 }
351 Ok(out)
352}
353
354fn read_skeleton_def(r: &mut Reader<'_>, layout: Layout) -> Result<SkeletonDef> {
355 Ok(SkeletonDef {
356 name: r.cstr()?,
357 id: r.i32()?,
358 rigid_bodies: read_rigid_body_defs(r, layout)?,
359 })
360}
361
362fn read_force_plate_def(r: &mut Reader<'_>) -> Result<ForcePlateDef> {
363 let id = r.i32()?;
364 let serial_number = r.cstr()?;
365 let width = r.f32()?;
366 let length = r.f32()?;
367 let origin = r.vec3()?;
368 let mut calibration_matrix = [[0.0; 12]; 12];
369 for row in &mut calibration_matrix {
370 for value in row {
371 *value = r.f32()?;
372 }
373 }
374 let mut corners = [[0.0; 3]; 4];
375 for corner in &mut corners {
376 *corner = r.vec3()?;
377 }
378 Ok(ForcePlateDef {
379 id,
380 serial_number,
381 width,
382 length,
383 origin,
384 calibration_matrix,
385 corners,
386 plate_type: r.i32()?,
387 channel_data_type: r.i32()?,
388 channel_names: read_names(r)?,
389 })
390}
391
392fn read_device_def(r: &mut Reader<'_>) -> Result<DeviceDef> {
393 Ok(DeviceDef {
394 id: r.i32()?,
395 name: r.cstr()?,
396 serial_number: r.cstr()?,
397 device_type: r.i32()?,
398 channel_data_type: r.i32()?,
399 channel_names: read_names(r)?,
400 })
401}
402
403fn read_camera_def(r: &mut Reader<'_>) -> Result<CameraDef> {
404 Ok(CameraDef {
405 name: r.cstr()?,
406 position: r.vec3()?,
407 orientation: r.quat()?,
408 })
409}
410
411fn read_marker_def(r: &mut Reader<'_>) -> Result<MarkerDef> {
412 Ok(MarkerDef {
413 name: r.cstr()?,
414 id: r.i32()?,
415 position: r.vec3()?,
416 size: r.f32()?,
417 params: r.u16()?,
418 })
419}
420
421fn read_asset_def(r: &mut Reader<'_>, layout: Layout) -> Result<AssetDef> {
422 let name = r.cstr()?;
423 let asset_type = r.i32()?;
424 let id = r.i32()?;
425 let rigid_bodies = read_rigid_body_defs(r, layout)?;
426 let n = count(r, MIN_MARKER_DEF)?;
427 let mut markers = vec_for_count(n);
428 for _ in 0..n {
429 markers.push(read_marker_def(r)?);
430 }
431 Ok(AssetDef {
432 name,
433 asset_type,
434 id,
435 rigid_bodies,
436 markers,
437 })
438}
439
440fn read_described(type_id: i32, r: &mut Reader<'_>, layout: Layout) -> Result<Option<DataSet>> {
441 Ok(Some(match type_id {
442 TYPE_SKELETON => DataSet::Skeleton(read_skeleton_def(r, layout)?),
443 TYPE_FORCE_PLATE => DataSet::ForcePlate(Box::new(read_force_plate_def(r)?)),
444 TYPE_DEVICE => DataSet::Device(read_device_def(r)?),
445 TYPE_CAMERA => DataSet::Camera(read_camera_def(r)?),
446 TYPE_ASSET => DataSet::Asset(read_asset_def(r, layout)?),
447 _ => return Ok(None),
448 }))
449}
450
451fn walk_dataset(r: &mut Reader<'_>, type_id: i32, layout: Layout, version: Version) -> Result<()> {
452 match type_id {
453 TYPE_MARKER_SET => {
454 read_marker_set_def(r)?;
455 }
456 TYPE_RIGID_BODY => {
457 read_rigid_body_def(r, layout)?;
458 }
459 _ => {
460 if read_described(type_id, r, layout)?.is_none() {
461 return Err(Error::UnsupportedDataSet {
462 type_id,
463 major: version.major,
464 minor: version.minor,
465 });
466 }
467 }
468 }
469 Ok(())
470}
471
472fn validate_rigid_tail(tail: &[u8], layout: Layout) -> Result<()> {
473 let mut r = Reader::new(tail);
474 walk_rigid_body_tail(&mut r, layout)?;
475 if !layout.has_section_sizes() {
476 r.finish()?;
477 }
478 Ok(())
479}
480
481fn validate_nested_rigid_tail(tail: &[u8], layout: Layout) -> Result<()> {
482 let mut r = Reader::new(tail);
483 walk_rigid_body_tail(&mut r, layout)?;
484 r.finish()
485}
486
487fn parse_marker_set_reader(r: &mut Reader<'_>, policy: ParsePolicy) -> Result<DataSet> {
488 let marker_set = read_marker_set_def(r)?;
489 if policy == ParsePolicy::Strict {
490 r.finish()?;
491 } else {
492 r.take(r.remaining())?;
493 }
494 Ok(DataSet::MarkerSet(marker_set))
495}
496
497fn parse_rigid_body_reader(
498 r: &mut Reader<'_>,
499 layout: Layout,
500 policy: ParsePolicy,
501) -> Result<DataSet> {
502 let name = r.cstr()?;
503 let id = r.i32()?;
504 let parent_id = r.i32()?;
505 let offset = r.vec3()?;
506 let tail_start = r.position();
507 let tail_len = r.remaining();
508 let mut tail_reader = r.subreader(tail_len)?;
509 if policy == ParsePolicy::Strict {
510 walk_rigid_body_tail(&mut tail_reader, layout)?;
511 }
512 let tail = r.span(tail_start, tail_start + tail_len)?.to_vec();
513 Ok(DataSet::RigidBody(RigidBodyDef {
514 name,
515 id,
516 parent_id,
517 offset,
518 tail,
519 }))
520}
521
522fn parse_described_reader(
523 type_id: i32,
524 r: &mut Reader<'_>,
525 layout: Layout,
526 version: Version,
527 policy: ParsePolicy,
528) -> Result<DataSet> {
529 let start = r.position();
530 let len = r.remaining();
531 let mut body = r.subreader(len)?;
532 let parsed = read_described(type_id, &mut body, layout).and_then(|dataset| {
533 body.finish()?;
534 Ok(dataset)
535 });
536 let bytes = r.span(start, start + len)?;
537 match (policy, parsed) {
538 (ParsePolicy::Strict, Ok(Some(dataset))) => Ok(dataset),
539 (ParsePolicy::Strict, Err(error)) => Err(error),
540 (ParsePolicy::Compatible, Ok(Some(dataset)))
541 if encode_dataset(&dataset, layout, version).is_ok_and(|encoded| encoded == bytes) =>
542 {
543 Ok(dataset)
544 }
545 _ => Ok(DataSet::Other {
546 type_id,
547 body: bytes.to_vec(),
548 }),
549 }
550}
551
552fn parse_dataset_reader(
553 type_id: i32,
554 r: &mut Reader<'_>,
555 layout: Layout,
556 version: Version,
557 policy: ParsePolicy,
558) -> Result<DataSet> {
559 match type_id {
560 TYPE_MARKER_SET => parse_marker_set_reader(r, policy),
561 TYPE_RIGID_BODY => parse_rigid_body_reader(r, layout, policy),
562 TYPE_SKELETON..=TYPE_ASSET => parse_described_reader(type_id, r, layout, version, policy),
563 _ => Ok(DataSet::Other {
564 type_id,
565 body: r.take(r.remaining())?.to_vec(),
566 }),
567 }
568}
569
570fn parse_dataset(
571 type_id: i32,
572 body: &[u8],
573 layout: Layout,
574 version: Version,
575 policy: ParsePolicy,
576) -> Result<DataSet> {
577 let mut r = Reader::new(body);
578 let dataset = parse_dataset_reader(type_id, &mut r, layout, version, policy)?;
579 r.finish()?;
580 Ok(dataset)
581}
582
583impl ModelDef {
584 pub fn parse(datagram: &[u8], version: Version) -> Result<Self> {
590 Self::parse_with_policy(datagram, version, ParsePolicy::Compatible)
591 }
592
593 pub fn parse_payload(payload: &[u8], version: Version) -> Result<Self> {
599 Self::parse_payload_with_policy(payload, version, ParsePolicy::Compatible)
600 }
601
602 pub fn parse_with_policy(
606 datagram: &[u8],
607 version: Version,
608 policy: ParsePolicy,
609 ) -> Result<Self> {
610 Self::parse_payload_with_policy(message::expect(datagram, NAT_MODELDEF)?, version, policy)
611 }
612
613 pub fn parse_payload_with_policy(
617 payload: &[u8],
618 version: Version,
619 policy: ParsePolicy,
620 ) -> Result<Self> {
621 let layout = version.layout()?;
622 let mut r = Reader::new(payload);
623 let dataset_count = count(&mut r, if layout.has_section_sizes() { 8 } else { 4 })?;
624 let mut datasets = vec_for_count(dataset_count);
625
626 for _ in 0..dataset_count {
627 let type_id = r.i32()?;
628 if layout.has_section_sizes() {
629 let body_size = size(&mut r)?;
630 let mut body_reader = r.subreader(body_size)?;
631 let dataset =
632 parse_dataset_reader(type_id, &mut body_reader, layout, version, policy)?;
633 body_reader.finish()?;
634 datasets.push(dataset);
635 } else {
636 let start = r.position();
637 walk_dataset(&mut r, type_id, layout, version)?;
638 let end = r.position();
639 let body = r.span(start, end)?;
640 datasets.push(parse_dataset(type_id, body, layout, version, policy)?);
641 }
642 }
643
644 if policy == ParsePolicy::Strict {
645 r.finish()?;
646 }
647 Ok(Self { datasets })
648 }
649
650 pub fn to_payload(&self, version: Version) -> Result<Vec<u8>> {
656 self.encode_payload(version.layout()?, version)
657 }
658
659 pub fn to_datagram(&self, version: Version) -> Result<Vec<u8>> {
665 let payload = self.to_payload(version)?;
666 message::frame_checked(NAT_MODELDEF, &payload)
667 }
668
669 pub fn to_payload_lossy(&self, version: Version) -> Result<Vec<u8>> {
675 Ok(self.encode_payload_lossy(version.layout()?))
676 }
677
678 pub fn to_datagram_lossy(&self, version: Version) -> Result<Vec<u8>> {
684 let payload = self.to_payload_lossy(version)?;
685 Ok(message::frame(NAT_MODELDEF, &payload))
686 }
687
688 fn encode_payload(&self, layout: Layout, version: Version) -> Result<Vec<u8>> {
689 let mut w = Writer::new();
690 w.i32(checked_i32(self.datasets.len(), "dataset count")?);
691 for dataset in &self.datasets {
692 let type_id = dataset.type_id();
693 let body = encode_dataset(dataset, layout, version)?;
694 w.i32(type_id);
695 if layout.has_section_sizes() {
696 w.i32(checked_i32(body.len(), "dataset body")?);
697 }
698 w.bytes(&body);
699 }
700 Ok(w.into_vec())
701 }
702
703 fn encode_payload_lossy(&self, layout: Layout) -> Vec<u8> {
704 let mut w = Writer::new();
705 w.i32(i32::try_from(self.datasets.len()).unwrap_or(i32::MAX));
706 for dataset in &self.datasets {
707 let type_id = dataset.type_id();
708 let body = encode_dataset_lossy(dataset);
709 w.i32(type_id);
710 if layout.has_section_sizes() {
711 w.i32(i32::try_from(body.len()).unwrap_or(i32::MAX));
712 }
713 w.bytes(&body);
714 }
715 w.into_vec()
716 }
717
718 #[must_use]
721 pub fn resolve_rigid_body(&self, name: &str) -> Option<i32> {
722 self.datasets.iter().find_map(|ds| match ds {
723 DataSet::RigidBody(rb) if rb.name == name => Some(rb.id),
724 _ => None,
725 })
726 }
727
728 pub fn rigid_bodies(&self) -> impl Iterator<Item = (&str, i32)> {
730 self.datasets.iter().filter_map(|ds| match ds {
731 DataSet::RigidBody(rb) => Some((rb.name.as_str(), rb.id)),
732 _ => None,
733 })
734 }
735
736 #[must_use]
738 pub fn count_of_type(&self, type_id: i32) -> usize {
739 self.datasets
740 .iter()
741 .filter(|d| d.type_id() == type_id)
742 .count()
743 }
744}
745
746fn write_rigid_body_head(w: &mut Writer, rigid_body: &RigidBodyDef) -> Result<()> {
747 w.try_cstr(&rigid_body.name, "rigid-body name")?;
748 w.i32(rigid_body.id);
749 w.i32(rigid_body.parent_id);
750 w.vec3(rigid_body.offset);
751 Ok(())
752}
753
754fn write_nested_rigid_bodies(
755 w: &mut Writer,
756 rigid_bodies: &[RigidBodyDef],
757 layout: Layout,
758 count_field: &'static str,
759) -> Result<()> {
760 w.i32(checked_i32(rigid_bodies.len(), count_field)?);
761 for rigid_body in rigid_bodies {
762 write_rigid_body_head(w, rigid_body)?;
763 validate_nested_rigid_tail(&rigid_body.tail, layout)?;
764 w.bytes(&rigid_body.tail);
765 }
766 Ok(())
767}
768
769fn write_names(
770 w: &mut Writer,
771 names: &[String],
772 count_field: &'static str,
773 name_field: &'static str,
774) -> Result<()> {
775 w.i32(checked_i32(names.len(), count_field)?);
776 for name in names {
777 w.try_cstr(name, name_field)?;
778 }
779 Ok(())
780}
781
782fn write_force_plate_fixed(w: &mut Writer, plate: &ForcePlateDef) {
783 w.f32(plate.width);
784 w.f32(plate.length);
785 w.vec3(plate.origin);
786 for row in &plate.calibration_matrix {
787 for value in row {
788 w.f32(*value);
789 }
790 }
791 for corner in &plate.corners {
792 w.vec3(*corner);
793 }
794 w.i32(plate.plate_type);
795 w.i32(plate.channel_data_type);
796}
797
798fn write_marker_def_fixed(w: &mut Writer, marker: &MarkerDef) {
799 w.i32(marker.id);
800 w.vec3(marker.position);
801 w.f32(marker.size);
802 w.u16(marker.params);
803}
804
805fn other_has_typed_roundtrip(type_id: i32, body: &[u8], layout: Layout, version: Version) -> bool {
806 let mut reader = Reader::new(body);
807 let Ok(Some(dataset)) = read_described(type_id, &mut reader, layout) else {
808 return false;
809 };
810 if reader.finish().is_err() {
811 return false;
812 }
813 encode_dataset(&dataset, layout, version).is_ok_and(|encoded| encoded == body)
814}
815
816fn encode_dataset(dataset: &DataSet, layout: Layout, version: Version) -> Result<Vec<u8>> {
817 let mut w = Writer::new();
818 match dataset {
819 DataSet::MarkerSet(marker_set) => {
820 w.try_cstr(&marker_set.name, "marker-set name")?;
821 write_names(
822 &mut w,
823 &marker_set.marker_names,
824 "marker count",
825 "marker name",
826 )?;
827 }
828 DataSet::RigidBody(rigid_body) => {
829 write_rigid_body_head(&mut w, rigid_body)?;
830 validate_rigid_tail(&rigid_body.tail, layout)?;
831 w.bytes(&rigid_body.tail);
832 }
833 DataSet::Skeleton(skeleton) => {
834 w.try_cstr(&skeleton.name, "skeleton name")?;
835 w.i32(skeleton.id);
836 write_nested_rigid_bodies(
837 &mut w,
838 &skeleton.rigid_bodies,
839 layout,
840 "skeleton rigid body count",
841 )?;
842 }
843 DataSet::ForcePlate(plate) => {
844 w.i32(plate.id);
845 w.try_cstr(&plate.serial_number, "force plate serial number")?;
846 write_force_plate_fixed(&mut w, plate);
847 write_names(
848 &mut w,
849 &plate.channel_names,
850 "force plate channel count",
851 "force plate channel name",
852 )?;
853 }
854 DataSet::Device(device) => {
855 w.i32(device.id);
856 w.try_cstr(&device.name, "device name")?;
857 w.try_cstr(&device.serial_number, "device serial number")?;
858 w.i32(device.device_type);
859 w.i32(device.channel_data_type);
860 write_names(
861 &mut w,
862 &device.channel_names,
863 "device channel count",
864 "device channel name",
865 )?;
866 }
867 DataSet::Camera(camera) => {
868 w.try_cstr(&camera.name, "camera name")?;
869 w.vec3(camera.position);
870 w.quat(camera.orientation);
871 }
872 DataSet::Asset(asset) => {
873 w.try_cstr(&asset.name, "asset name")?;
874 w.i32(asset.asset_type);
875 w.i32(asset.id);
876 write_nested_rigid_bodies(
877 &mut w,
878 &asset.rigid_bodies,
879 layout,
880 "asset rigid body count",
881 )?;
882 w.i32(checked_i32(asset.markers.len(), "asset marker count")?);
883 for marker in &asset.markers {
884 w.try_cstr(&marker.name, "asset marker name")?;
885 write_marker_def_fixed(&mut w, marker);
886 }
887 }
888 DataSet::Other { type_id, body } => {
889 if *type_id == TYPE_MARKER_SET || *type_id == TYPE_RIGID_BODY {
890 return Err(Error::InvalidValue {
891 field: "dataset type",
892 reason: "type 0 and type 1 require their typed representation",
893 });
894 }
895 if (TYPE_SKELETON..=TYPE_ASSET).contains(type_id)
896 && other_has_typed_roundtrip(*type_id, body, layout, version)
897 {
898 return Err(Error::InvalidValue {
899 field: "dataset type",
900 reason: "known dataset body has a typed representation",
901 });
902 }
903 if !layout.has_section_sizes() {
904 let mut r = Reader::new(body);
905 walk_dataset(&mut r, *type_id, layout, version)?;
906 r.finish()?;
907 }
908 w.bytes(body);
909 }
910 }
911 Ok(w.into_vec())
912}
913
914fn lossy_count(len: usize) -> i32 {
915 i32::try_from(len).unwrap_or(i32::MAX)
916}
917
918fn lossy_rigid_bodies(w: &mut Writer, rigid_bodies: &[RigidBodyDef]) {
919 w.i32(lossy_count(rigid_bodies.len()));
920 for rigid_body in rigid_bodies {
921 lossy_rigid_body(w, rigid_body);
922 }
923}
924
925fn lossy_rigid_body(w: &mut Writer, rigid_body: &RigidBodyDef) {
926 w.cstr(&rigid_body.name);
927 w.i32(rigid_body.id);
928 w.i32(rigid_body.parent_id);
929 w.vec3(rigid_body.offset);
930 w.bytes(&rigid_body.tail);
931}
932
933fn lossy_names(w: &mut Writer, names: &[String]) {
934 w.i32(lossy_count(names.len()));
935 for name in names {
936 w.cstr(name);
937 }
938}
939
940fn encode_dataset_lossy(dataset: &DataSet) -> Vec<u8> {
941 let mut w = Writer::new();
942 match dataset {
943 DataSet::MarkerSet(marker_set) => {
944 w.cstr(&marker_set.name);
945 lossy_names(&mut w, &marker_set.marker_names);
946 }
947 DataSet::RigidBody(rigid_body) => lossy_rigid_body(&mut w, rigid_body),
948 DataSet::Skeleton(skeleton) => {
949 w.cstr(&skeleton.name);
950 w.i32(skeleton.id);
951 lossy_rigid_bodies(&mut w, &skeleton.rigid_bodies);
952 }
953 DataSet::ForcePlate(plate) => {
954 w.i32(plate.id);
955 w.cstr(&plate.serial_number);
956 write_force_plate_fixed(&mut w, plate);
957 lossy_names(&mut w, &plate.channel_names);
958 }
959 DataSet::Device(device) => {
960 w.i32(device.id);
961 w.cstr(&device.name);
962 w.cstr(&device.serial_number);
963 w.i32(device.device_type);
964 w.i32(device.channel_data_type);
965 lossy_names(&mut w, &device.channel_names);
966 }
967 DataSet::Camera(camera) => {
968 w.cstr(&camera.name);
969 w.vec3(camera.position);
970 w.quat(camera.orientation);
971 }
972 DataSet::Asset(asset) => {
973 w.cstr(&asset.name);
974 w.i32(asset.asset_type);
975 w.i32(asset.id);
976 lossy_rigid_bodies(&mut w, &asset.rigid_bodies);
977 w.i32(lossy_count(asset.markers.len()));
978 for marker in &asset.markers {
979 w.cstr(&marker.name);
980 write_marker_def_fixed(&mut w, marker);
981 }
982 }
983 DataSet::Other { body, .. } => w.bytes(body),
984 }
985 w.into_vec()
986}