1use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
2use std::borrow::Cow;
3use std::{fmt, io, mem};
4
5#[derive(Debug)]
7pub enum Error {
8 Io(::std::io::Error),
10 Version(u32),
12 Magic([u8; 4]),
14 Length {
16 length: u32,
18 length_read: usize,
20 },
21 ChunkLength {
23 ty: ChunkType,
25 length: u32,
27 length_read: usize,
29 },
30 ChunkType(ChunkType),
32 UnknownChunkType([u8; 4]),
34}
35
36#[derive(Clone, Debug)]
38pub struct Glb<'a> {
39 pub header: Header,
41 pub json: Cow<'a, [u8]>,
43 pub bin: Option<Cow<'a, [u8]>>,
45}
46
47#[derive(Copy, Clone, Debug)]
49#[repr(C)]
50pub struct Header {
51 pub magic: [u8; 4],
53 pub version: u32,
55 pub length: u32,
57}
58
59#[derive(Copy, Clone, Debug)]
61pub enum ChunkType {
62 Json,
64 Bin,
66}
67
68#[derive(Copy, Clone, Debug)]
70#[repr(C)]
71struct ChunkHeader {
72 length: u32,
74 ty: ChunkType,
76}
77
78impl Header {
79 fn from_reader<R: io::Read>(mut reader: R) -> Result<Self, Error> {
80 use self::Error::Io;
81 let mut magic = [0; 4];
82 reader.read_exact(&mut magic).map_err(Io)?;
83 if &magic == b"glTF" {
87 Ok(Self {
88 magic,
89 version: reader.read_u32::<LittleEndian>().map_err(Io)?,
90 length: reader.read_u32::<LittleEndian>().map_err(Io)?,
91 })
92 } else {
93 Err(Error::Magic(magic))
94 }
95 }
96
97 fn size_of() -> usize {
98 12
99 }
100}
101
102impl ChunkHeader {
103 fn from_reader<R: io::Read>(mut reader: R) -> Result<Self, Error> {
104 use self::Error::Io;
105 let length = reader.read_u32::<LittleEndian>().map_err(Io)?;
106 let mut ty = [0; 4];
107 reader.read_exact(&mut ty).map_err(Io)?;
108 let ty = match &ty {
109 b"JSON" => Ok(ChunkType::Json),
110 b"BIN\0" => Ok(ChunkType::Bin),
111 _ => Err(Error::UnknownChunkType(ty)),
112 }?;
113 Ok(Self { length, ty })
114 }
115}
116
117fn align_to_multiple_of_four(n: &mut usize) {
118 *n = (*n + 3) & !3;
119}
120
121fn split_binary_gltf(mut data: &[u8]) -> Result<(&[u8], Option<&[u8]>), Error> {
122 let (json, mut data) = ChunkHeader::from_reader(&mut data)
123 .and_then(|json_h| {
124 if let ChunkType::Json = json_h.ty {
125 Ok(json_h)
126 } else {
127 Err(Error::ChunkType(json_h.ty))
128 }
129 })
130 .and_then(|json_h| {
131 if json_h.length as usize <= data.len() {
132 Ok(json_h)
133 } else {
134 Err(Error::ChunkLength {
135 ty: json_h.ty,
136 length: json_h.length,
137 length_read: data.len(),
138 })
139 }
140 })
141 .map(|json_h| data.split_at(json_h.length as usize))?;
144
145 let bin = if !data.is_empty() {
146 ChunkHeader::from_reader(&mut data)
147 .and_then(|bin_h| {
148 if let ChunkType::Bin = bin_h.ty {
149 Ok(bin_h)
150 } else {
151 Err(Error::ChunkType(bin_h.ty))
152 }
153 })
154 .and_then(|bin_h| {
155 if bin_h.length as usize <= data.len() {
156 Ok(bin_h)
157 } else {
158 Err(Error::ChunkLength {
159 ty: bin_h.ty,
160 length: bin_h.length,
161 length_read: data.len(),
162 })
163 }
164 })
165 .map(|bin_h| data.split_at(bin_h.length as usize))
168 .map(|(x, _)| Some(x))?
169 } else {
170 None
171 };
172 Ok((json, bin))
173}
174
175impl<'a> Glb<'a> {
176 pub fn to_writer<W>(&self, mut writer: W) -> Result<(), crate::Error>
178 where
179 W: io::Write,
180 {
181 {
183 let magic = b"glTF";
184 let version = 2;
185 let mut length =
186 mem::size_of::<Header>() + mem::size_of::<ChunkHeader>() + self.json.len();
187 align_to_multiple_of_four(&mut length);
188 if let Some(bin) = self.bin.as_ref() {
189 length += mem::size_of::<ChunkHeader>() + bin.len();
190 align_to_multiple_of_four(&mut length);
191 }
192
193 writer.write_all(&magic[..])?;
194 writer.write_u32::<LittleEndian>(version)?;
195 writer.write_u32::<LittleEndian>(length as u32)?;
196 }
197
198 {
200 let magic = b"JSON";
201 let mut length = self.json.len();
202 align_to_multiple_of_four(&mut length);
203 let padding = length - self.json.len();
204
205 writer.write_u32::<LittleEndian>(length as u32)?;
206 writer.write_all(&magic[..])?;
207 writer.write_all(&self.json)?;
208 for _ in 0..padding {
209 writer.write_u8(0x20)?;
210 }
211 }
212
213 if let Some(bin) = self.bin.as_ref() {
214 let magic = b"BIN\0";
215 let mut length = bin.len();
216 align_to_multiple_of_four(&mut length);
217 let padding = length - bin.len();
218
219 writer.write_u32::<LittleEndian>(length as u32)?;
220 writer.write_all(&magic[..])?;
221 writer.write_all(bin)?;
222 for _ in 0..padding {
223 writer.write_u8(0)?;
224 }
225 }
226
227 Ok(())
228 }
229
230 pub fn to_vec(&self) -> Result<Vec<u8>, crate::Error> {
232 let mut length = mem::size_of::<Header>() + mem::size_of::<ChunkHeader>() + self.json.len();
233 align_to_multiple_of_four(&mut length);
234 if let Some(bin) = self.bin.as_ref() {
235 length += mem::size_of::<ChunkHeader>() + bin.len();
236 align_to_multiple_of_four(&mut length);
237 }
238
239 let mut vec = Vec::with_capacity(length);
240 self.to_writer(&mut vec as &mut dyn io::Write)?;
241 Ok(vec)
242 }
243
244 pub fn from_slice(mut data: &'a [u8]) -> Result<Self, crate::Error> {
250 let header = Header::from_reader(&mut data)
251 .and_then(|header| {
252 let contents_length = header.length as usize - Header::size_of();
253 if contents_length <= data.len() {
254 Ok(header)
255 } else {
256 Err(Error::Length {
257 length: contents_length as u32,
258 length_read: data.len(),
259 })
260 }
261 })
262 .map_err(crate::Error::Binary)?;
263 match header.version {
264 2 => split_binary_gltf(data)
265 .map(|(json, bin)| Glb {
266 header,
267 json: json.into(),
268 bin: bin.map(Into::into),
269 })
270 .map_err(crate::Error::Binary),
271 x => Err(crate::Error::Binary(Error::Version(x))),
272 }
273 }
274
275 pub fn from_reader<R: io::Read>(mut reader: R) -> Result<Self, crate::Error> {
282 let header = Header::from_reader(&mut reader).map_err(crate::Error::Binary)?;
283 match header.version {
284 2 => {
285 let glb_len = header.length - Header::size_of() as u32;
286 let mut buf = vec![0; glb_len as usize];
287 if let Err(e) = reader.read_exact(&mut buf).map_err(Error::Io) {
288 Err(crate::Error::Binary(e))
289 } else {
290 split_binary_gltf(&buf)
291 .map(|(json, bin)| Glb {
292 header,
293 json: json.to_vec().into(),
294 bin: bin.map(<[u8]>::to_vec).map(Into::into),
295 })
296 .map_err(crate::Error::Binary)
297 }
298 }
299 x => Err(crate::Error::Binary(Error::Version(x))),
300 }
301 }
302}
303
304impl fmt::Display for Error {
305 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306 write!(
307 f,
308 "{}",
309 match *self {
310 Error::Io(ref e) => return e.fmt(f),
311 Error::Version(_) => "unsupported version",
312 Error::Magic(_) => "not glTF magic",
313 Error::Length { .. } => "could not completely read the object",
314 Error::ChunkLength { ty, .. } => match ty {
315 ChunkType::Json => "JSON chunk length exceeds that of slice",
316 ChunkType::Bin => "BIN\\0 chunk length exceeds that of slice",
317 },
318 Error::ChunkType(ty) => match ty {
319 ChunkType::Json => "was not expecting JSON chunk",
320 ChunkType::Bin => "was not expecting BIN\\0 chunk",
321 },
322 Error::UnknownChunkType(_) => "unknown chunk type",
323 }
324 )
325 }
326}
327
328impl ::std::error::Error for Error {}