mod3d_gltf/
glb.rs

1/// A GLB file contains a 12-byte header; a chunk 0 (JSON); an optional
2/// chunk 1 (binary data)
3///
4/// The 12-byte header is 0x46546C67_u32 ; 0x00000002_u32; byte_length_u32
5///
6/// byte_length_u32 must equal the byte size of the GLB file content
7///
8/// Chunk 0 has an 8-byte header that is byte_length_u32; 0x4E4F534A_u32,
9/// followed by data
10///
11/// byte_length_u32 must equal the byte size of the data - must be a
12/// multiple of 4 bytes
13///
14/// Chunk 1 (optional) has an 8-byte header that is byte_length_u32;
15/// 0x004E4942_32, followed by data
16///
17/// byte_length_u32 must equal the byte size of the data - must be a
18/// multiple of 4 bytes
19///
20///
21/// A function is required that takes a GLB file and returns a Gltf Json
22/// Value and invokes a callback on the chunk 1 file data to get an
23/// Option<Vec<u8>> of chunk 1
24///
25/// A function is required that takes a Json file and returns a Gltf Json
26/// Value
27///
28/// A method on the Gltf Json Value is required that takes invokes
29/// callbacks for each buffer data in the file
30///
31/// A method on the Gltf Json Value is required that turns it into a Gltf
32/// descriptor
33///
34/// Methods are required on the Gltf descriptor that access the
35/// scenes; the default scene; the nodes by name; the skeletons; etc.
36use serde_json::json;
37use serde_json::Value as JsonValue;
38
39use crate::{Error, Gltf, Result};
40
41struct GlbLoader<'file, F: std::io::Read, B> {
42    file: &'file mut F,
43    max_json_length: usize,
44    byte_length: u32,
45    json_value: JsonValue,
46    buffer_0: Option<B>,
47}
48impl<'file, F: std::io::Read, B> GlbLoader<'file, F, B> {
49    //cp new
50    /// Create a new GlbLoader given a file and maximum length
51    /// expected of the Json within it
52    fn new(file: &'file mut F, max_json_length: usize) -> Self {
53        let byte_length = 0;
54        let json_value = json!(null);
55        let buffer_0 = None;
56        Self {
57            file,
58            max_json_length,
59            byte_length,
60            json_value,
61            buffer_0,
62        }
63    }
64
65    //mp read_glb_hdr
66    /// Read the GLB header from the file for the loader and validate
67    /// it
68    fn read_glb_hdr(&mut self) -> Result<()> {
69        let mut hdr = [0; 12];
70        self.file.read_exact(&mut hdr)?;
71        if hdr[0..8] != [0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00] {
72            return Err(Error::GlbHdr);
73        }
74        let byte_length = (hdr[8] as u32)
75            | ((hdr[9] as u32) << 8)
76            | ((hdr[10] as u32) << 16)
77            | ((hdr[11] as u32) << 24);
78        self.byte_length = byte_length;
79        Ok(())
80    }
81
82    //mp read_json
83    /// Read the Json chunk of the GLB file (assuming the header has
84    /// been read), and validate the lengtht of it, and parse the Json
85    ///
86    /// Do not at this point attempt to validate the Json to be Gltf Json
87    fn read_json(&mut self) -> Result<()> {
88        let mut hdr = [0; 8];
89        self.file.read_exact(&mut hdr).map_err(Error::GlbJsonIo)?;
90        if hdr[4..8] != [0x4a, 0x53, 0x4f, 0x4e] {
91            return Err(Error::GlbJsonHdr);
92        }
93        let json_byte_length = (hdr[0] as u32)
94            | ((hdr[1] as u32) << 8)
95            | ((hdr[2] as u32) << 16)
96            | ((hdr[3] as u32) << 24);
97        let json_byte_length = json_byte_length as usize;
98        if json_byte_length > self.max_json_length {
99            return Err(Error::GlbJsonHdr);
100        }
101        let mut buffer = vec![0; json_byte_length];
102        let bytes_read = self.file.read(&mut buffer)?;
103        if bytes_read < json_byte_length {
104            return Err(Error::GlbJsonLength);
105        }
106        self.json_value = serde_json::from_str(std::str::from_utf8(&buffer)?)?;
107        Ok(())
108    }
109
110    //mp read_buffer
111    /// Read the 'binary' buffer  chunk of the GLB if it is present
112    ///
113    /// This does *NOT* support GLB files with a Json chunk and an
114    /// extension chunk without a binary chunk. It consumes the header
115    /// of the next Glb chunk and checks that it is a binary chunk; if
116    /// no header is there (no binary) then all is okay, but if there
117    /// is no binary chunk but there is an extension chunk then its
118    /// header will have been consumed
119    fn read_buffer<BR>(&mut self, buf_reader: &BR) -> Result<()>
120    where
121        BR: Fn(&mut F, usize) -> std::result::Result<Option<B>, std::io::Error>,
122    {
123        let mut hdr = [0; 8];
124        let n = self.file.read(&mut hdr).map_err(Error::GlbJsonIo)?;
125        if n == 0 {
126            return Ok(());
127        }
128        if hdr[4..8] != [0x42, 0x49, 0x4e, 0x00] {
129            return Err(Error::GlbBinHdr);
130        }
131        let bin_byte_length = (hdr[0] as u32)
132            | ((hdr[1] as u32) << 8)
133            | ((hdr[2] as u32) << 16)
134            | ((hdr[3] as u32) << 24);
135        let bin_byte_length = bin_byte_length as usize;
136        self.buffer_0 = buf_reader(self.file, bin_byte_length).map_err(Error::GlbBinIo)?;
137        Ok(())
138    }
139
140    //dp into_gltf
141    /// Drop the borrow of the file and return a GltfJsonValue and the
142    /// binary buffer loaded from the Glb file (if any)
143    fn into_gltf(self) -> Result<(Gltf, Option<B>)> {
144        let gltf_json_value = Gltf::of_json_value(self.json_value)?;
145        Ok((gltf_json_value, self.buffer_0))
146    }
147}
148
149pub fn glb_load<F, B, BR>(file: &mut F, b: &BR, max_json_length: usize) -> Result<(Gltf, Option<B>)>
150where
151    F: std::io::Read,
152    BR: Fn(&mut F, usize) -> std::result::Result<Option<B>, std::io::Error>,
153{
154    let mut loader = GlbLoader::new(file, max_json_length);
155    loader.read_glb_hdr()?;
156    loader.read_json()?;
157    loader.read_buffer(b)?;
158    loader.into_gltf()
159}