bunny_codec/
compressed.rs1mod error;
2mod read;
3mod view;
4
5use read::{checked_add, checked_payload_len, parse_header, take, validate_triangles};
6use view::CompressedMeshParts;
7
8pub use error::CompressedMeshError;
9pub use view::{CompressedIndexWidth, CompressedMesh, CompressedTriangle};
10
11const HEADER_LEN: usize = 76;
12const VERTEX_STRIDE: usize = 6;
13const TRIANGLE16_STRIDE: usize = 6;
14const TRIANGLE32_STRIDE: usize = 12;
15const MAX_VERTICES: usize = 1_000_000;
16const MAX_TRIANGLES: usize = 1_000_000;
17
18pub fn decode_compressed_mesh(input: &[u8]) -> Result<CompressedMesh<'_>, CompressedMeshError> {
28 let header = parse_header(input)?;
29 let layout = payload_layout(&header)?;
30 validate_total_len(input, layout.payload_end)?;
31 let sections = payload_sections(input, &layout)?;
32 validate_triangles(
33 sections.triangle_bytes,
34 header.triangle_count,
35 header.vertex_count,
36 header.index_width,
37 )?;
38 Ok(CompressedMesh::new(CompressedMeshParts {
39 bounds: header.bounds,
40 vertex_bytes: sections.vertex_bytes,
41 triangle_bytes: sections.triangle_bytes,
42 vertex_count: header.vertex_count,
43 triangle_count: header.triangle_count,
44 index_width: header.index_width,
45 }))
46}
47
48struct PayloadLayout {
49 vertex_len: usize,
50 triangle_len: usize,
51 triangle_start: usize,
52 payload_end: usize,
53}
54
55struct PayloadSections<'a> {
56 vertex_bytes: &'a [u8],
57 triangle_bytes: &'a [u8],
58}
59
60fn payload_layout(header: &read::Header) -> Result<PayloadLayout, CompressedMeshError> {
61 let vertex_len = checked_payload_len(header.vertex_count, VERTEX_STRIDE)?;
62 let triangle_len = checked_payload_len(header.triangle_count, header.index_width.stride())?;
63 let expected_len = checked_add(vertex_len, triangle_len)?;
64 let expected_len_u64 =
65 u64::try_from(expected_len).map_err(|_| CompressedMeshError::IntegerOverflow)?;
66 if header.payload_len != expected_len_u64 {
67 return Err(CompressedMeshError::InvalidPayloadLength);
68 }
69 let triangle_start = checked_add(HEADER_LEN, vertex_len)?;
70 let payload_end = checked_add(HEADER_LEN, expected_len)?;
71
72 Ok(PayloadLayout { vertex_len, triangle_len, triangle_start, payload_end })
73}
74
75const fn validate_total_len(input: &[u8], payload_end: usize) -> Result<(), CompressedMeshError> {
76 if input.len() < payload_end {
77 return Err(CompressedMeshError::PayloadTooShort);
78 }
79 if input.len() != payload_end {
80 return Err(CompressedMeshError::TrailingData);
81 }
82 Ok(())
83}
84
85fn payload_sections<'a>(
86 input: &'a [u8],
87 layout: &PayloadLayout,
88) -> Result<PayloadSections<'a>, CompressedMeshError> {
89 let vertex_bytes = take(input, HEADER_LEN, layout.vertex_len)?;
90 let triangle_bytes = take(input, layout.triangle_start, layout.triangle_len)?;
91 Ok(PayloadSections { vertex_bytes, triangle_bytes })
92}