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, layout: Layout, 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, layout.ty)?;
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, layout, 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, Copy)]
161struct Layout {
162 variable: usize,
164 ty: usize,
166}
167
168const SM4: Layout = Layout { variable: 24, ty: 16 };
170const SM5: Layout = Layout { variable: 40, ty: 36 };
172
173#[derive(Clone, Debug, PartialEq, Eq)]
175pub struct SignatureElement {
176 pub name: String,
178 pub index: u32,
180 pub system_value: u32,
182 pub component_type: u32,
184 pub register: u32,
186 pub mask: u8,
188 pub read_write_mask: u8,
190 pub at: usize,
192}
193
194pub fn signature(bytes: &[u8], kind: &[u8; 4]) -> Result<Vec<SignatureElement>> {
198 let v = View(bytes);
199 let count = v.u32(0)? as usize;
200 let directory = v.u32(4)?;
201 if directory != 8 {
202 return Err(error(
203 1,
204 format!("expected the record directory at byte 8, found byte {directory}"),
205 ));
206 }
207 let (skip, record) = match kind {
209 b"ISG1" | b"OSG1" | b"PSG1" => (4, 32),
210 b"OSG5" => (4, 28),
211 _ => (0, 24),
212 };
213 let mut result = Vec::new();
214 for i in v.table(8, count, record)? {
215 let p = v.record(8, i, record)?;
216 let q = v.plus(p, skip)?;
217 let [mask, read_write_mask, _, _] = v.array::<4>(v.plus(q, 20)?)?;
218 result.push(SignatureElement {
219 name: v.string(v.u32(q)? as usize)?,
220 index: v.field(q, 4)?,
221 system_value: v.field(q, 8)?,
222 component_type: v.field(q, 12)?,
223 register: v.field(q, 16)?,
224 mask,
225 read_write_mask,
226 at: p / 4,
227 });
228 }
229 Ok(result)
230}
231
232pub fn patch_signature_elements(bytes: &[u8]) -> Result<Vec<SignatureElement>> {
235 let directory = parts(bytes)?;
236 let (data, tag) = signature_part(&directory, &[b"PCSG", b"PSG1"])?;
237 signature(data, &tag)
238}
239
240fn signature_part<'a>(
242 directory: &[dxcontainer::Chunk<'a>],
243 tags: &[&[u8; 4]],
244) -> Result<(&'a [u8], [u8; 4])> {
245 let tag = tags
246 .iter()
247 .find(|t| directory.iter().any(|c| &&c.kind == *t))
248 .or(tags.first())
249 .ok_or_else(|| error(0, "no signature part kinds to look for"))?;
250 Ok((chunk(directory, tag)?, **tag))
251}
252
253pub fn signature_elements(
255 bytes: &[u8],
256) -> Result<(ProgramType, Vec<SignatureElement>, Vec<SignatureElement>)> {
257 let directory = parts(bytes)?;
258 let kind = program_type(&directory)?;
259 let read = |tags: &[&[u8; 4]]| -> Result<Vec<SignatureElement>> {
260 let (data, tag) = signature_part(&directory, tags)?;
261 signature(data, &tag)
262 };
263 Ok((
264 kind,
265 read(&[b"ISGN", b"ISG1"])?,
266 read(&[b"OSGN", b"OSG1", b"OSG5"])?,
267 ))
268}
269
270pub fn program_type(directory: &[dxcontainer::Chunk<'_>]) -> Result<ProgramType> {
272 let code = directory
273 .iter()
274 .find(|c| matches!(&c.kind, b"SHDR" | b"SHEX"))
275 .ok_or_else(|| error(0, "the container has no SHDR or SHEX part"))?;
276 let token = View(code.data).u32(0)?;
277 match token >> 16 {
278 0 => Ok(ProgramType::Pixel),
279 1 => Ok(ProgramType::Vertex),
280 2 => Ok(ProgramType::Geometry),
281 3 => Ok(ProgramType::Hull),
282 4 => Ok(ProgramType::Domain),
283 5 => Ok(ProgramType::Compute),
284 kind => Err(error(
285 0,
286 format!("expected a program type from 0 (pixel) to 5 (compute), found {kind}"),
287 )),
288 }
289}
290
291#[derive(Clone, Debug, PartialEq, Eq)]
293pub struct Rdef {
294 pub target: u32,
296 pub bindings: Vec<RdefBinding>,
298 pub buffers: Vec<RdefBuffer>,
300}
301
302#[derive(Clone, Debug, PartialEq, Eq)]
304pub struct RdefBinding {
305 pub word: usize,
307 pub name: String,
309 pub kind: u32,
311 pub returns: u32,
313 pub dimension: u32,
315 pub samples: u32,
317 pub register: u32,
319 pub count: u32,
321 pub flags: u32,
323}
324
325#[derive(Clone, Debug, PartialEq, Eq)]
327pub struct RdefBuffer {
328 pub word: usize,
330 pub name: String,
332 pub kind: u32,
334 pub size: u32,
336 pub variables: Vec<RdefVariable>,
338}
339
340#[derive(Clone, Debug, PartialEq, Eq)]
342pub struct RdefVariable {
343 pub word: usize,
345 pub name: String,
347 pub offset: u32,
349 pub size: u32,
351 pub ty: RdefType,
353 pub default: Option<Vec<u8>>,
355}
356
357#[derive(Clone, Debug, PartialEq, Eq)]
359pub struct RdefType {
360 pub word: usize,
362 pub class: u16,
364 pub scalar: u16,
366 pub rows: u16,
368 pub columns: u16,
370 pub elements: u16,
372 pub members: Vec<RdefMember>,
374}
375
376#[derive(Clone, Debug, PartialEq, Eq)]
378pub struct RdefMember {
379 pub name: String,
381 pub offset: u32,
383 pub ty: RdefType,
385}
386
387pub fn rdef_target(bytes: &[u8]) -> Result<Option<u32>> {
390 let directory = parts(bytes)?;
391 directory
392 .iter()
393 .find(|c| &c.kind == b"RDEF")
394 .map(|rdef| View(rdef.data).u32(16))
395 .transpose()
396}
397
398fn read_directory(v: &View<'_>) -> Result<()> {
400 let tag = v.bytes(28, 4)?;
401 if tag != b"RD11" {
402 return Err(error(
403 7,
404 format!(
405 "expected the `RD11` tag of the Shader Model 5 RDEF layout, found {:?}",
406 String::from_utf8_lossy(tag)
407 ),
408 ));
409 }
410 for (i, size) in [60, 24, 32, 40, 36, 12].into_iter().enumerate() {
411 let at = v.record(32, i, 4)?;
412 let found = v.u32(at)?;
413 if found != size {
414 return Err(error(
415 at / 4,
416 format!("expected record size {size} (entry {i} of the RD11 size directory), found {found}"),
417 ));
418 }
419 }
420 Ok(())
421}
422
423pub fn rdef(bytes: &[u8]) -> Result<Option<Rdef>> {
426 let directory = parts(bytes)?;
427 if !directory.iter().any(|c| &c.kind == b"RDEF") {
428 return Ok(None);
429 }
430 let v = View(chunk(&directory, b"RDEF")?);
431 let target = v.u32(16)?;
432 let layout = match (target >> 8) & 0xff {
433 4 => SM4,
434 _ => {
435 read_directory(&v)?;
436 SM5
437 }
438 };
439 let mut bindings = Vec::new();
440 let offset = v.field(0, 12)? as usize;
441 for i in v.table(offset, v.field(0, 8)? as usize, 32)? {
442 let p = v.record(offset, i, 32)?;
443 bindings.push(RdefBinding {
444 word: p / 4,
445 name: v.string(v.u32(p)? as usize)?,
446 kind: v.field(p, 4)?,
447 returns: v.field(p, 8)?,
448 dimension: v.field(p, 12)?,
449 samples: v.field(p, 16)?,
450 register: v.field(p, 20)?,
451 count: v.field(p, 24)?,
452 flags: v.field(p, 28)?,
453 });
454 }
455 let mut buffers = Vec::new();
456 let offset = v.field(0, 4)? as usize;
457 let mut budget = MAX_RECORDS;
458 for i in v.table(offset, v.u32(0)? as usize, 24)? {
459 let p = v.record(offset, i, 24)?;
460 let name = v.string(v.u32(p)? as usize)?;
461 let kind = v.field(p, 20)?;
462 let size = v.field(p, 12)?;
463 if size as usize > MAX_BUFFER_SIZE {
464 return Err(error(
465 v.plus(p, 12)? / 4,
466 format!("constant buffer of {size} bytes, more than the {MAX_BUFFER_SIZE} allowed"),
467 ));
468 }
469 let mut variables = Vec::new();
470 let entries = v.field(p, 8)? as usize;
471 for j in v.table(entries, v.field(p, 4)? as usize, layout.variable)? {
472 let q = v.record(entries, j, layout.variable)?;
473 let start = v.field(q, 4)?;
474 let len = v.field(q, 8)?;
475 if start.checked_add(len).is_none_or(|end| end > size) {
476 return Err(error(
477 v.plus(q, 4)? / 4,
478 format!(
479 "variable at byte {start} of {len} bytes lies outside its {size} byte constant buffer"
480 ),
481 ));
482 }
483 let default = v.field(q, 20)? as usize;
484 let ty = v.ty(v.field(q, 16)? as usize, layout, &mut Vec::new(), &mut budget)?;
485 variables.push(RdefVariable {
486 word: q / 4,
487 name: v.string(v.u32(q)? as usize)?,
488 offset: start,
489 size: len,
490 ty,
491 default: if default == 0 {
492 None
493 } else {
494 Some(v.bytes(default, len as usize)?.to_vec())
495 },
496 });
497 }
498 buffers.push(RdefBuffer {
499 word: p / 4,
500 name,
501 kind,
502 size,
503 variables,
504 });
505 }
506 Ok(Some(Rdef {
507 target,
508 bindings,
509 buffers,
510 }))
511}