1use crate::decode::ProgramType;
9use crate::{Result, chunk, error, parts};
10
11const MAX_RECORDS: usize = 65536;
13const MAX_NAME: usize = 4096;
15const MAX_BUFFER_SIZE: usize = 65536;
17
18struct View<'a>(&'a [u8]);
19impl View<'_> {
20 fn bytes(&self, at: usize, len: usize) -> Result<&[u8]> {
21 let end = at.checked_add(len).ok_or_else(|| {
22 error(
23 at / 4,
24 format!("{len} bytes at byte {at} overflow the address space"),
25 )
26 })?;
27 self.0.get(at..end).ok_or_else(|| {
28 error(
29 at / 4,
30 format!(
31 "{len} bytes at byte {at} exceed the part's {} bytes",
32 self.0.len()
33 ),
34 )
35 })
36 }
37 fn array<const N: usize>(&self, at: usize) -> Result<[u8; N]> {
38 self.bytes(at, N)?
39 .try_into()
40 .map_err(|_| error(at / 4, format!("{N} bytes at byte {at}")))
41 }
42 fn u32(&self, at: usize) -> Result<u32> {
43 Ok(u32::from_le_bytes(self.array(at)?))
44 }
45 fn u16(&self, at: usize) -> Result<u16> {
46 Ok(u16::from_le_bytes(self.array(at)?))
47 }
48 fn plus(&self, at: usize, delta: usize) -> Result<usize> {
50 at.checked_add(delta).ok_or_else(|| {
51 error(
52 at / 4,
53 format!("byte {at} plus {delta} overflows the address space"),
54 )
55 })
56 }
57 fn field(&self, at: usize, delta: usize) -> Result<u32> {
59 self.u32(self.plus(at, delta)?)
60 }
61 fn field16(&self, at: usize, delta: usize) -> Result<u16> {
63 self.u16(self.plus(at, delta)?)
64 }
65 fn record(&self, at: usize, i: usize, stride: usize) -> Result<usize> {
67 i.checked_mul(stride)
68 .and_then(|n| at.checked_add(n))
69 .ok_or_else(|| {
70 error(
71 at / 4,
72 format!("record {i} of {stride} bytes at byte {at} overflows"),
73 )
74 })
75 }
76 fn string(&self, at: usize) -> Result<String> {
78 let tail = self.0.get(at..).ok_or_else(|| {
79 error(
80 at / 4,
81 format!("a string at byte {at}, past the part's {} bytes", self.0.len()),
82 )
83 })?;
84 let end = tail.iter().position(|&b| b == 0).ok_or_else(|| {
85 error(
86 at / 4,
87 format!("the string at byte {at} has no terminating zero in the part"),
88 )
89 })?;
90 if end > MAX_NAME {
91 return Err(error(
92 at / 4,
93 format!("the string at byte {at} is {end} bytes, more than the {MAX_NAME} allowed"),
94 ));
95 }
96 let text = tail
97 .get(..end)
98 .ok_or_else(|| error(at / 4, format!("the string at byte {at}")))?;
99 std::str::from_utf8(text)
100 .map(str::to_owned)
101 .map_err(|_| error(at / 4, format!("the string at byte {at} is not UTF-8")))
102 }
103 fn table(&self, at: usize, count: usize, stride: usize) -> Result<std::ops::Range<usize>> {
105 if count > MAX_RECORDS {
106 return Err(error(
107 at / 4,
108 format!("a table of {count} records at byte {at}, more than the {MAX_RECORDS} allowed"),
109 ));
110 }
111 self.bytes(
112 at,
113 count.checked_mul(stride).ok_or_else(|| {
114 error(
115 at / 4,
116 format!("a table of {count} records of {stride} bytes overflows"),
117 )
118 })?,
119 )?;
120 Ok(0..count)
121 }
122 fn ty(&self, at: usize, stack: &mut Vec<usize>, budget: &mut usize) -> Result<RdefType> {
124 if stack.len() >= 32 || stack.contains(&at) || *budget == 0 {
125 return Err(error(
126 at / 4,
127 "the type at this word is part of a cycle, nests deeper than 32 or is one of more than 65536 types",
128 ));
129 }
130 *budget = budget.saturating_sub(1);
131 self.bytes(at, 36)?;
132 stack.push(at);
133 let count = usize::from(self.field16(at, 10)?);
134 let offset = self.field(at, 12)? as usize;
135 let mut members = Vec::new();
136 for i in self.table(offset, count, 12)? {
137 let p = self.record(offset, i, 12)?;
138 members.push(RdefMember {
139 name: self.string(self.u32(p)? as usize)?,
140 offset: self.field(p, 8)?,
141 ty: self.ty(self.field(p, 4)? as usize, stack, budget)?,
142 });
143 }
144 stack.pop();
145 Ok(RdefType {
146 word: at / 4,
147 class: self.u16(at)?,
148 scalar: self.field16(at, 2)?,
149 rows: self.field16(at, 4)?,
150 columns: self.field16(at, 6)?,
151 elements: self.field16(at, 8)?,
152 members,
153 })
154 }
155}
156
157#[derive(Clone, Debug, PartialEq, Eq)]
159pub struct SignatureElement {
160 pub name: String,
162 pub index: u32,
164 pub system_value: u32,
166 pub component_type: u32,
168 pub register: u32,
170 pub mask: u8,
172 pub read_write_mask: u8,
174 pub at: usize,
176}
177
178pub fn signature(bytes: &[u8], kind: &[u8; 4]) -> Result<Vec<SignatureElement>> {
182 let v = View(bytes);
183 let count = v.u32(0)? as usize;
184 let directory = v.u32(4)?;
185 if directory != 8 {
186 return Err(error(
187 1,
188 format!("expected the record directory at byte 8, found byte {directory}"),
189 ));
190 }
191 let (skip, record) = match kind {
193 b"ISG1" | b"OSG1" => (4, 32),
194 b"OSG5" => (4, 28),
195 _ => (0, 24),
196 };
197 let mut result = Vec::new();
198 for i in v.table(8, count, record)? {
199 let p = v.record(8, i, record)?;
200 let q = v.plus(p, skip)?;
201 let [mask, read_write_mask, _, _] = v.array::<4>(v.plus(q, 20)?)?;
202 result.push(SignatureElement {
203 name: v.string(v.u32(q)? as usize)?,
204 index: v.field(q, 4)?,
205 system_value: v.field(q, 8)?,
206 component_type: v.field(q, 12)?,
207 register: v.field(q, 16)?,
208 mask,
209 read_write_mask,
210 at: p / 4,
211 });
212 }
213 Ok(result)
214}
215
216pub fn patch_signature_elements(bytes: &[u8]) -> Result<Vec<SignatureElement>> {
218 let directory = parts(bytes)?;
219 signature(chunk(&directory, b"PCSG")?, b"PCSG")
220}
221
222fn signature_part<'a>(
224 directory: &[dxcontainer::Chunk<'a>],
225 tags: &[&[u8; 4]],
226) -> Result<(&'a [u8], [u8; 4])> {
227 let tag = tags
228 .iter()
229 .find(|t| directory.iter().any(|c| &&c.kind == *t))
230 .or(tags.first())
231 .ok_or_else(|| error(0, "no signature part kinds to look for"))?;
232 Ok((chunk(directory, tag)?, **tag))
233}
234
235pub fn signature_elements(
237 bytes: &[u8],
238) -> Result<(ProgramType, Vec<SignatureElement>, Vec<SignatureElement>)> {
239 let directory = parts(bytes)?;
240 let kind = program_type(&directory)?;
241 let read = |tags: &[&[u8; 4]]| -> Result<Vec<SignatureElement>> {
242 let (data, tag) = signature_part(&directory, tags)?;
243 signature(data, &tag)
244 };
245 Ok((
246 kind,
247 read(&[b"ISGN", b"ISG1"])?,
248 read(&[b"OSGN", b"OSG1", b"OSG5"])?,
249 ))
250}
251
252pub fn program_type(directory: &[dxcontainer::Chunk<'_>]) -> Result<ProgramType> {
254 let code = directory
255 .iter()
256 .find(|c| matches!(&c.kind, b"SHDR" | b"SHEX"))
257 .ok_or_else(|| error(0, "the container has no SHDR or SHEX part"))?;
258 let token = View(code.data).u32(0)?;
259 match token >> 16 {
260 0 => Ok(ProgramType::Pixel),
261 1 => Ok(ProgramType::Vertex),
262 2 => Ok(ProgramType::Geometry),
263 3 => Ok(ProgramType::Hull),
264 4 => Ok(ProgramType::Domain),
265 5 => Ok(ProgramType::Compute),
266 kind => Err(error(
267 0,
268 format!("expected a program type from 0 (pixel) to 5 (compute), found {kind}"),
269 )),
270 }
271}
272
273#[derive(Clone, Debug, PartialEq, Eq)]
275pub struct Rdef {
276 pub target: u32,
278 pub bindings: Vec<RdefBinding>,
280 pub buffers: Vec<RdefBuffer>,
282}
283
284#[derive(Clone, Debug, PartialEq, Eq)]
286pub struct RdefBinding {
287 pub word: usize,
289 pub name: String,
291 pub kind: u32,
293 pub returns: u32,
295 pub dimension: u32,
297 pub samples: u32,
299 pub register: u32,
301 pub count: u32,
303 pub flags: u32,
305}
306
307#[derive(Clone, Debug, PartialEq, Eq)]
309pub struct RdefBuffer {
310 pub word: usize,
312 pub name: String,
314 pub kind: u32,
316 pub size: u32,
318 pub variables: Vec<RdefVariable>,
320}
321
322#[derive(Clone, Debug, PartialEq, Eq)]
324pub struct RdefVariable {
325 pub word: usize,
327 pub name: String,
329 pub offset: u32,
331 pub size: u32,
333 pub ty: RdefType,
335 pub default: Option<Vec<u8>>,
337}
338
339#[derive(Clone, Debug, PartialEq, Eq)]
341pub struct RdefType {
342 pub word: usize,
344 pub class: u16,
346 pub scalar: u16,
348 pub rows: u16,
350 pub columns: u16,
352 pub elements: u16,
354 pub members: Vec<RdefMember>,
356}
357
358#[derive(Clone, Debug, PartialEq, Eq)]
360pub struct RdefMember {
361 pub name: String,
363 pub offset: u32,
365 pub ty: RdefType,
367}
368
369pub fn rdef_target(bytes: &[u8]) -> Result<Option<u32>> {
372 let directory = parts(bytes)?;
373 directory
374 .iter()
375 .find(|c| &c.kind == b"RDEF")
376 .map(|rdef| View(rdef.data).u32(16))
377 .transpose()
378}
379
380pub fn rdef(bytes: &[u8]) -> Result<Option<Rdef>> {
382 let directory = parts(bytes)?;
383 if !directory.iter().any(|c| &c.kind == b"RDEF") {
384 return Ok(None);
385 }
386 let v = View(chunk(&directory, b"RDEF")?);
387 let target = v.u32(16)?;
388 let tag = v.bytes(28, 4)?;
389 if tag != b"RD11" {
390 return Err(error(
391 7,
392 format!(
393 "expected the `RD11` tag of the Shader Model 5 RDEF layout, found {:?}",
394 String::from_utf8_lossy(tag)
395 ),
396 ));
397 }
398 for (i, size) in [60, 24, 32, 40, 36, 12].into_iter().enumerate() {
399 let at = v.record(32, i, 4)?;
400 let found = v.u32(at)?;
401 if found != size {
402 return Err(error(
403 at / 4,
404 format!("expected record size {size} (entry {i} of the RD11 size directory), found {found}"),
405 ));
406 }
407 }
408 let mut bindings = Vec::new();
409 let offset = v.field(0, 12)? as usize;
410 for i in v.table(offset, v.field(0, 8)? as usize, 32)? {
411 let p = v.record(offset, i, 32)?;
412 bindings.push(RdefBinding {
413 word: p / 4,
414 name: v.string(v.u32(p)? as usize)?,
415 kind: v.field(p, 4)?,
416 returns: v.field(p, 8)?,
417 dimension: v.field(p, 12)?,
418 samples: v.field(p, 16)?,
419 register: v.field(p, 20)?,
420 count: v.field(p, 24)?,
421 flags: v.field(p, 28)?,
422 });
423 }
424 let mut buffers = Vec::new();
425 let offset = v.field(0, 4)? as usize;
426 let mut budget = MAX_RECORDS;
427 for i in v.table(offset, v.u32(0)? as usize, 24)? {
428 let p = v.record(offset, i, 24)?;
429 let name = v.string(v.u32(p)? as usize)?;
430 let kind = v.field(p, 20)?;
431 let size = v.field(p, 12)?;
432 if size as usize > MAX_BUFFER_SIZE {
433 return Err(error(
434 v.plus(p, 12)? / 4,
435 format!("constant buffer of {size} bytes, more than the {MAX_BUFFER_SIZE} allowed"),
436 ));
437 }
438 let mut variables = Vec::new();
439 let entries = v.field(p, 8)? as usize;
440 for j in v.table(entries, v.field(p, 4)? as usize, 40)? {
441 let q = v.record(entries, j, 40)?;
442 let start = v.field(q, 4)?;
443 let len = v.field(q, 8)?;
444 if start.checked_add(len).is_none_or(|end| end > size) {
445 return Err(error(
446 v.plus(q, 4)? / 4,
447 format!(
448 "variable at byte {start} of {len} bytes lies outside its {size} byte constant buffer"
449 ),
450 ));
451 }
452 let default = v.field(q, 20)? as usize;
453 let ty = v.ty(v.field(q, 16)? as usize, &mut Vec::new(), &mut budget)?;
454 variables.push(RdefVariable {
455 word: q / 4,
456 name: v.string(v.u32(q)? as usize)?,
457 offset: start,
458 size: len,
459 ty,
460 default: if default == 0 {
461 None
462 } else {
463 Some(v.bytes(default, len as usize)?.to_vec())
464 },
465 });
466 }
467 buffers.push(RdefBuffer {
468 word: p / 4,
469 name,
470 kind,
471 size,
472 variables,
473 });
474 }
475 Ok(Some(Rdef {
476 target,
477 bindings,
478 buffers,
479 }))
480}