1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use Error;
#[derive(Clone, Debug)]
pub struct Glb<'a> {
pub header: Header,
pub json: &'a [u8],
pub bin: Option<&'a [u8]>,
}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct Header {
pub magic: [u8; 4],
pub version: u32,
pub length: u32,
}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
struct ChunkInfo {
length: u32,
kind: [u8; 4],
}
impl<'a> Glb<'a> {
pub fn from_slice(data: &'a [u8]) -> Result<Self, Error> {
use std::mem::{size_of, transmute};
let err = |reason: &str| Err(Error::Glb(reason.to_string()));
let ptr = data.as_ptr();
if data.len() < size_of::<Header>() + size_of::<ChunkInfo>() {
return err("GLB missing header");
}
let mut offset = 0isize;
let header = unsafe {
let x: *const Header = transmute(ptr.offset(offset));
if &(*x).magic != b"glTF" {
return err("GLB does not start with `glTF`");
}
if (*x).length as usize != data.len() {
return err("length does not match length of data");
}
if (*x).version != 2 {
return err("Not GLB version 2.0");
}
*x
};
offset += size_of::<Header>() as isize;
let (json_chunk, json_chunk_length) = unsafe {
let x: *const ChunkInfo = transmute(ptr.offset(offset));
offset += size_of::<ChunkInfo>() as isize;
if (*x).length as usize > (data.len() as isize - offset) as usize {
return err("JSON chunkLength exceeds length of data");
}
if &(*x).kind != b"JSON" {
return err("JSON chunkType is not `JSON`");
}
let begin = offset as usize;
let end = begin + (*x).length as usize;
(begin..end, (*x).length as usize)
};
offset += json_chunk_length as isize;
let bin_chunk = if data.len() as isize - offset == 0 {
None
} else {
unsafe {
let x: *const ChunkInfo = transmute(ptr.offset(offset));
offset += size_of::<ChunkInfo>() as isize;
if (*x).length as usize > (data.len() as isize - offset) as usize {
return err("BIN chunkLength exceeds length of data");
}
if &(*x).kind != b"BIN\0" {
return err("BIN chunkType is not `BIN\0`");
}
let begin = offset as usize;
let end = begin + (*x).length as usize;
Some(begin..end)
}
};
let json = &data[json_chunk];
let bin = bin_chunk.map(|range| &data[range]);
Ok(Glb {
header,
json,
bin,
})
}
}