gltf_v1/
binary.rs

1use std::{
2    borrow::Cow,
3    fmt,
4    io::{self, Read},
5    mem,
6};
7
8use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
9
10#[derive(Debug)]
11pub enum Error {
12    /// Io error occured.
13    Io(::std::io::Error),
14    /// Unsupported version.
15    Version(u32),
16    /// Magic says that file is not glTF.
17    Magic([u8; 4]),
18    Length {
19        /// length specified in GLB header.
20        length: u32,
21        /// Actual length of data read.
22        length_read: usize,
23    },
24}
25
26/// Binary glTF contents.
27#[derive(Clone, Debug)]
28pub struct Glb<'a> {
29    /// The header section of the `.glb` file.
30    pub header: Header,
31    /// The Content section of the `.glb` file.
32    pub content: Cow<'a, [u8]>,
33    /// The optional body(BIN) section of the `.glb` file.
34    pub body: Option<Cow<'a, [u8]>>,
35}
36
37/// The header section of a .glb file.
38#[derive(Copy, Clone, Debug, Default)]
39#[repr(C)]
40pub struct Header {
41    /// Must be `b"glTF"`.
42    pub magic: [u8; 4],
43    /// Must be `1`.
44    pub version: u32,
45    /// Must match the length of the parent .glb file.
46    pub length: u32,
47    /// Must match the length of the content section of the GLB. Must be greater than 0
48    pub content_length: u32,
49    /// Must be `0` for JSON
50    pub content_format: u32,
51}
52
53impl Header {
54    fn from_reader<R: io::Read>(mut reader: R) -> Result<Self, Error> {
55        use self::Error::Io;
56        let mut magic = [0; 4];
57        reader.read_exact(&mut magic).map_err(Io)?;
58        // We only validate magic as we don't care for version and length of
59        // contents, the caller does.  Let them decide what to do next with
60        // regard to version and length.
61        if &magic == b"glTF" {
62            Ok(Self {
63                magic,
64                version: reader.read_u32::<LittleEndian>().map_err(Io)?,
65                length: reader.read_u32::<LittleEndian>().map_err(Io)?,
66                content_length: reader.read_u32::<LittleEndian>().map_err(Io)?,
67                content_format: reader.read_u32::<LittleEndian>().map_err(Io)?,
68            })
69        } else {
70            Err(Error::Magic(magic))
71        }
72    }
73
74    fn size_of() -> usize {
75        20
76    }
77}
78
79impl<'a> Glb<'a> {
80    pub fn from_slice(mut data: &'a [u8]) -> Result<Self, crate::GLTF_Error> {
81        let header = Header::from_reader(&mut data)
82            .and_then(|header| {
83                let contents_length = header.length as usize - Header::size_of();
84                if contents_length <= data.len() {
85                    Ok(header)
86                } else {
87                    Err(Error::Length {
88                        length: contents_length as u32,
89                        length_read: data.len(),
90                    })
91                }
92            })
93            .map_err(crate::GLTF_Error::Binary)?;
94        match header.version {
95            1 => {
96                let content_len = header.content_length;
97                let mut content_buf = vec![0; content_len as usize];
98                if let Err(e) = (&mut data).read_exact(&mut content_buf).map_err(Error::Io) {
99                    return Err(crate::GLTF_Error::Binary(e));
100                }
101
102                let body_len = header.length - Header::size_of() as u32 - content_len;
103                let body = if body_len != 0 {
104                    let mut body_buf = vec![0; body_len as usize];
105                    if let Err(e) = (&mut data).read_exact(&mut body_buf).map_err(Error::Io) {
106                        return Err(crate::GLTF_Error::Binary(e));
107                    }
108                    Some(body_buf)
109                } else {
110                    None
111                };
112
113                Ok(Glb {
114                    header,
115                    content: content_buf.into(),
116                    body: body.map(|x| x.into()),
117                })
118            }
119            x => Err(crate::GLTF_Error::Binary(Error::Version(x))),
120        }
121    }
122    pub fn from_reader<R: io::Read>(mut reader: R) -> Result<Self, crate::GLTF_Error> {
123        let header = Header::from_reader(&mut reader).map_err(crate::GLTF_Error::Binary)?;
124        match header.version {
125            1 => {
126                let content_len = header.content_length;
127                let mut content_buf = vec![0; content_len as usize];
128                if let Err(e) = reader.read_exact(&mut content_buf).map_err(Error::Io) {
129                    return Err(crate::GLTF_Error::Binary(e));
130                }
131
132                let body_len = header.length - Header::size_of() as u32 - content_len;
133                let body = if body_len != 0 {
134                    let mut body_buf = vec![0; body_len as usize];
135                    if let Err(e) = reader.read_exact(&mut body_buf).map_err(Error::Io) {
136                        return Err(crate::GLTF_Error::Binary(e));
137                    }
138                    Some(body_buf)
139                } else {
140                    None
141                };
142
143                Ok(Glb {
144                    header,
145                    content: content_buf.to_vec().into(),
146                    body: body.map(|x| x.to_vec()).map(|x| x.into()),
147                })
148            }
149            x => Err(crate::GLTF_Error::Binary(Error::Version(x))),
150        }
151    }
152    pub fn to_writer<W: io::Write>(&self, mut writer: W) -> Result<(), crate::GLTF_Error> {
153        // Write GLB header
154        {
155            let magic = b"glTF";
156            let version: u32 = 1;
157            let mut length = mem::size_of::<Header>() + self.content.len();
158            align_to_multiple_of_four(&mut length);
159            if let Some(body) = self.body.as_ref() {
160                length += body.len();
161                align_to_multiple_of_four(&mut length);
162            }
163
164            writer.write_all(&magic[..])?;
165            writer.write_u32::<LittleEndian>(version)?;
166            writer.write_u32::<LittleEndian>(length as u32)?;
167        }
168
169        // Write JSON chunk header
170        {
171            let mut length = self.content.len();
172            let format: u32 = 0;
173            align_to_multiple_of_four(&mut length);
174            let padding = length - self.content.len();
175
176            writer.write_u32::<LittleEndian>(length as u32)?;
177            writer.write_u32::<LittleEndian>(format)?;
178            writer.write_all(&self.content)?;
179            for _ in 0..padding {
180                writer.write_u8(0x20)?;
181            }
182        }
183
184        if let Some(body) = self.body.as_ref() {
185            let mut length = body.len();
186            align_to_multiple_of_four(&mut length);
187            let padding = length - body.len();
188            writer.write_all(body)?;
189            for _ in 0..padding {
190                writer.write_u8(0)?;
191            }
192        }
193
194        Ok(())
195    }
196}
197
198impl fmt::Display for Error {
199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200        write!(
201            f,
202            "{}",
203            match *self {
204                Error::Version(_) => "unsupported version",
205                Error::Magic(_) => "not glTF magic",
206                Error::Length { .. } => "could not completely read the object",
207                Error::Io(ref e) => return e.fmt(f),
208            }
209        )
210    }
211}
212
213impl ::std::error::Error for Error {}
214
215fn align_to_multiple_of_four(n: &mut usize) {
216    *n = (*n + 3) & !3;
217}