1use crate::{Result, error};
4
5#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct Type {
8 pub class: u16,
11 pub kind: u16,
13 pub rows: u16,
15 pub columns: u16,
17 pub elements: u16,
19 pub members: Vec<TypeMember>,
22}
23
24#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct TypeMember {
27 pub name: String,
29 pub ty: Type,
31}
32
33#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct Constant {
36 pub word: usize,
38 pub name: String,
40 pub register_set: u16,
42 pub register: u16,
44 pub count: u16,
47 pub reserved: u16,
50 pub ty: Type,
52 pub default: Option<Vec<u8>>,
54}
55
56#[derive(Clone, Debug, PartialEq, Eq)]
58pub struct ConstantTable {
59 pub creator: String,
61 pub target: String,
63 pub version: u32,
66 pub flags: u32,
68 pub constants: Vec<Constant>,
71}
72
73pub(crate) struct Reader<'a> {
76 pub(crate) data: &'a [u8],
77 pub(crate) word: usize,
79 pub(crate) tag: &'static str,
81}
82impl Reader<'_> {
83 pub(crate) fn word_of(&self, at: usize) -> usize {
85 self.word.saturating_add(at / 4)
86 }
87 pub(crate) fn plus(&self, at: usize, by: usize) -> Result<usize> {
89 at.checked_add(by).ok_or_else(|| {
90 error(
91 self.word_of(at),
92 format!("{} offset {at}+{by} is past the largest offset", self.tag),
93 )
94 })
95 }
96 pub(crate) fn bytes(&self, at: usize, count: usize, size: usize) -> Result<usize> {
98 count.checked_mul(size).ok_or_else(|| {
99 error(
100 self.word_of(at),
101 format!("{} {count} entries of {size} bytes do not fit", self.tag),
102 )
103 })
104 }
105 pub(crate) fn range(&self, at: usize, len: usize) -> Result<&[u8]> {
106 at.checked_add(len)
107 .and_then(|end| self.data.get(at..end))
108 .ok_or_else(|| {
109 error(
110 self.word_of(at),
111 format!(
112 "{} range {at}+{len} outside the comment's {} bytes",
113 self.tag,
114 self.data.len()
115 ),
116 )
117 })
118 }
119 pub(crate) fn u16(&self, at: usize) -> Result<u16> {
120 match self.range(at, 2)? {
121 &[a, b] => Ok(u16::from_le_bytes([a, b])),
122 _ => Err(error(
123 self.word_of(at),
124 format!("{} u16 outside comment", self.tag),
125 )),
126 }
127 }
128 pub(crate) fn u32(&self, at: usize) -> Result<u32> {
129 match self.range(at, 4)? {
130 &[a, b, c, d] => Ok(u32::from_le_bytes([a, b, c, d])),
131 _ => Err(error(
132 self.word_of(at),
133 format!("{} u32 outside comment", self.tag),
134 )),
135 }
136 }
137 pub(crate) fn offset(&self, at: usize) -> Result<usize> {
138 let offset = self.u32(at)?;
139 usize::try_from(offset).map_err(|_| {
140 error(
141 self.word_of(at),
142 format!("{} offset {offset} does not fit an address", self.tag),
143 )
144 })
145 }
146 pub(crate) fn string(&self, at: usize) -> Result<String> {
147 let bytes = self.range(at, self.data.len().saturating_sub(at))?;
148 let end = bytes
149 .iter()
150 .position(|&b| b == 0)
151 .ok_or_else(|| error(self.word_of(at), format!("{} string has no NUL", self.tag)))?;
152 let text = bytes.get(..end).unwrap_or_default();
153 std::str::from_utf8(text)
154 .map(str::to_owned)
155 .map_err(|_| error(self.word_of(at), format!("{} string is not UTF-8", self.tag)))
156 }
157 pub(crate) fn ty(&self, at: usize, depth: usize, budget: &mut usize) -> Result<Type> {
158 if depth > 32 || *budget == 0 {
159 return Err(error(
160 self.word_of(at),
161 format!("{} type nesting/cycle or 4096-node limit", self.tag),
162 ));
163 }
164 *budget = budget.saturating_sub(1);
165 self.range(at, 16)?;
166 let count = usize::from(self.u16(self.plus(at, 10)?)?);
167 let members_at = self.offset(self.plus(at, 12)?)?;
168 self.range(members_at, self.bytes(members_at, count, 8)?)?;
169 let mut members = Vec::new();
170 for n in 0..count {
171 let member = self.plus(members_at, self.bytes(members_at, n, 8)?)?;
172 members.push(TypeMember {
173 name: self.string(self.offset(member)?)?,
174 ty: self.ty(
175 self.offset(self.plus(member, 4)?)?,
176 depth.saturating_add(1),
177 budget,
178 )?,
179 });
180 }
181 Ok(Type {
182 class: self.u16(at)?,
183 kind: self.u16(self.plus(at, 2)?)?,
184 rows: self.u16(self.plus(at, 4)?)?,
185 columns: self.u16(self.plus(at, 6)?)?,
186 elements: self.u16(self.plus(at, 8)?)?,
187 members,
188 })
189 }
190}
191
192pub(crate) fn read(data: &[u8], word: usize, expected_version: u32) -> Result<ConstantTable> {
193 let r = Reader {
194 data,
195 word,
196 tag: "CTAB",
197 };
198 if r.u32(0)? != 28 {
199 return Err(error(word, "CTAB header size is not 28"));
200 }
201 let version = r.u32(8)?;
202 if version != expected_version {
203 return Err(error(
204 r.word_of(8),
205 format!(
206 "CTAB version disagrees with the program's: {version:#010x}, where the program's version token is {expected_version:#010x}"
207 ),
208 ));
209 }
210 let count = r.offset(12)?;
211 if count > 1024 {
212 return Err(error(
213 r.word_of(12),
214 format!("CTAB has {count} constants, where it has at most 1024"),
215 ));
216 }
217 let base = r.offset(16)?;
218 r.range(base, r.bytes(base, count, 20)?)?;
219 let mut constants: Vec<Constant> = Vec::new();
220 let mut budget = 4096;
221 for n in 0..count {
222 let at = r.plus(base, r.bytes(base, n, 20)?)?;
223 let name = r.string(r.offset(at)?)?;
224 let register_set = r.u16(r.plus(at, 4)?)?;
225 let register = r.u16(r.plus(at, 6)?)?;
226 let count = r.u16(r.plus(at, 8)?)?;
227 if register_set > 3 {
228 return Err(error(
229 r.word_of(at),
230 format!(
231 "CTAB register set {register_set}, where it is 0 (bool), 1 (int4), 2 (float4) or 3 (sampler)"
232 ),
233 ));
234 }
235 if count == 0 {
236 return Err(error(
237 r.word_of(at),
238 format!("CTAB register range c{register}+{count} is empty"),
239 ));
240 }
241 if let Some(c) = constants.iter().find(|c| {
244 c.register_set == register_set
245 && (c.name == name
246 || (u32::from(c.register) < u32::from(register).saturating_add(u32::from(count))
247 && register < c.register.saturating_add(c.count)))
248 }) {
249 let detail = if c.name == name {
250 format!("CTAB constant {name} appears twice in register set {register_set}")
251 } else {
252 format!(
253 "CTAB constant {name} overlaps {} in register set {register_set}",
254 c.name
255 )
256 };
257 return Err(error(r.word_of(at), detail));
258 }
259 let default_at = r.offset(r.plus(at, 16)?)?;
260 let default = if default_at == 0 {
261 None
262 } else {
263 if register_set == 3 {
264 return Err(error(r.word_of(at), "CTAB sampler default"));
265 }
266 let row = if register_set == 0 { 4 } else { 16 };
267 Some(
268 r.range(default_at, r.bytes(default_at, usize::from(count), row)?)?
269 .to_vec(),
270 )
271 };
272 constants.push(Constant {
273 word: r.word_of(at),
274 name,
275 register_set,
276 register,
277 count,
278 reserved: r.u16(r.plus(at, 10)?)?,
279 ty: r.ty(r.offset(r.plus(at, 12)?)?, 0, &mut budget)?,
280 default,
281 });
282 }
283 Ok(ConstantTable {
284 creator: r.string(r.offset(4)?)?,
285 version,
286 flags: r.u32(20)?,
287 target: r.string(r.offset(24)?)?,
288 constants,
289 })
290}