bunny_codec/
compressed.rs1mod error;
2mod read;
3mod view;
4
5use read::{checked_add, checked_payload_len, parse_header, take, validate_triangles};
6
7pub use error::CompressedMeshError;
8pub use view::{CompressedIndexWidth, CompressedMesh, CompressedTriangle};
9
10const HEADER_LEN: usize = 76;
11const VERTEX_STRIDE: usize = 6;
12const TRIANGLE16_STRIDE: usize = 6;
13const TRIANGLE32_STRIDE: usize = 12;
14const MAX_VERTICES: usize = 1_000_000;
15const MAX_TRIANGLES: usize = 1_000_000;
16
17pub fn decode_compressed_mesh(input: &[u8]) -> Result<CompressedMesh<'_>, CompressedMeshError> {
27 let header = parse_header(input)?;
28 let vertex_len = checked_payload_len(header.vertex_count, VERTEX_STRIDE)?;
29 let triangle_len = checked_payload_len(header.triangle_count, header.index_width.stride())?;
30 let expected_len = checked_add(vertex_len, triangle_len)?;
31 let expected_len_u64 =
32 u64::try_from(expected_len).map_err(|_| CompressedMeshError::IntegerOverflow)?;
33 let _declared_end = u64::try_from(HEADER_LEN)
34 .map_err(|_| CompressedMeshError::IntegerOverflow)?
35 .checked_add(header.payload_len)
36 .ok_or(CompressedMeshError::IntegerOverflow)?;
37 if header.payload_len != expected_len_u64 {
38 return Err(CompressedMeshError::InvalidPayloadLength);
39 }
40 let payload_end = checked_add(HEADER_LEN, expected_len)?;
41 if input.len() < payload_end {
42 return Err(CompressedMeshError::PayloadTooShort);
43 }
44 if input.len() != payload_end {
45 return Err(CompressedMeshError::TrailingData);
46 }
47
48 let vertex_bytes = take(input, HEADER_LEN, vertex_len)?;
49 let triangle_start = checked_add(HEADER_LEN, vertex_len)?;
50 let triangle_bytes = take(input, triangle_start, triangle_len)?;
51 validate_triangles(
52 triangle_bytes,
53 header.triangle_count,
54 header.vertex_count,
55 header.index_width,
56 )?;
57 Ok(CompressedMesh::new(
58 header.bounds,
59 vertex_bytes,
60 triangle_bytes,
61 header.vertex_count,
62 header.triangle_count,
63 header.index_width,
64 ))
65}