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
18pub(crate) struct View<'a>(pub(crate) &'a [u8]);
19impl View<'_> {
20 pub(crate) 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 pub(crate) 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 pub(crate) fn u32(&self, at: usize) -> Result<u32> {
43 Ok(u32::from_le_bytes(self.array(at)?))
44 }
45 pub(crate) fn u16(&self, at: usize) -> Result<u16> {
46 Ok(u16::from_le_bytes(self.array(at)?))
47 }
48 pub(crate) 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 pub(crate) fn field(&self, at: usize, delta: usize) -> Result<u32> {
59 self.u32(self.plus(at, delta)?)
60 }
61 pub(crate) fn field16(&self, at: usize, delta: usize) -> Result<u16> {
63 self.u16(self.plus(at, delta)?)
64 }
65 pub(crate) 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 pub(crate) 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 pub(crate) 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 let name = if layout.ty >= 36 {
147 match self.field(at, 32)? as usize {
148 0 => None,
149 offset => Some(self.string(offset)?),
150 }
151 } else {
152 None
153 };
154 Ok(RdefType {
155 word: at / 4,
156 name,
157 class: self.u16(at)?,
158 scalar: self.field16(at, 2)?,
159 rows: self.field16(at, 4)?,
160 columns: self.field16(at, 6)?,
161 elements: self.field16(at, 8)?,
162 members,
163 })
164 }
165}
166
167#[derive(Clone, Copy)]
171struct Layout {
172 binding: usize,
174 variable: usize,
176 ty: usize,
178}
179
180const SM4: Layout = Layout {
182 binding: 32,
183 variable: 24,
184 ty: 16,
185};
186const SM50: Layout = Layout {
188 binding: 32,
189 variable: 40,
190 ty: 36,
191};
192const SM51: Layout = Layout {
194 binding: 40,
195 variable: 40,
196 ty: 36,
197};
198
199const SM51_TAG: &[u8; 4] = &[0x13, 0x13, 0x44, 0x25];
201
202#[derive(Clone, Debug, PartialEq, Eq)]
204pub struct SignatureElement {
205 pub name: String,
207 pub index: u32,
209 pub system_value: u32,
211 pub component_type: u32,
213 pub register: u32,
215 pub mask: u8,
217 pub read_write_mask: u8,
219 pub at: usize,
221 pub stream: u32,
223 pub min_precision: u32,
225}
226
227impl SignatureElement {
228 pub fn system_value_for(&self, output: bool, kind: ProgramType) -> u32 {
231 if self.system_value != 0 || !output || kind != ProgramType::Pixel {
232 return self.system_value;
233 }
234 match self.name.to_ascii_lowercase().as_str() {
235 "sv_target" => 64,
236 "sv_depth" => 65,
237 "sv_coverage" => 66,
238 "sv_depthgreaterequal" => 67,
239 "sv_depthlessequal" => 68,
240 "sv_stencilref" => 69,
241 "sv_innercoverage" => 70,
242 _ => 0,
243 }
244 }
245
246 pub fn system_value_name(&self, output: bool, kind: ProgramType) -> String {
249 match self.system_value_for(output, kind) {
250 0 => "NONE",
251 1 => "POS",
252 2 => "CLIPDST",
253 3 => "CULLDST",
254 4 => "RTINDEX",
255 5 => "VPINDEX",
256 6 => "VERTID",
257 7 => "PRIMID",
258 8 => "INSTID",
259 9 => "FFACE",
260 10 => "SAMPLE",
261 11 => "QUADEDGE",
262 12 => "QUADINT",
263 13 => "TRIEDGE",
264 14 => "TRIINT",
265 15 => "LINEDET",
266 16 => "LINEDEN",
267 64 => "TARGET",
268 65 => "DEPTH",
269 66 => "COVERAGE",
270 67 => "DEPTHGE",
271 68 => "DEPTHLE",
272 69 => "STENCILREF",
273 70 => "INNERCOV",
274 other => return format!("SV{other}"),
275 }
276 .to_owned()
277 }
278
279 pub fn format_name(&self) -> &'static str {
282 match (self.min_precision, self.component_type) {
283 (1, _) => "min16f",
284 (2, _) => "min2_8f",
285 (4, _) => "min16i",
286 (5, _) => "min16u",
287 (_, 1) => "uint",
288 (_, 2) => "int",
289 (_, 3) => "float",
290 _ => "unknown",
291 }
292 }
293}
294
295pub fn signature(bytes: &[u8], kind: &[u8; 4]) -> Result<Vec<SignatureElement>> {
299 let v = View(bytes);
300 let count = v.u32(0)? as usize;
301 let directory = v.u32(4)?;
302 if directory != 8 {
303 return Err(error(
304 1,
305 format!("expected the record directory at byte 8, found byte {directory}"),
306 ));
307 }
308 let (skip, record) = match kind {
310 b"ISG1" | b"OSG1" | b"PSG1" => (4, 32),
311 b"OSG5" => (4, 28),
312 _ => (0, 24),
313 };
314 let mut result = Vec::new();
315 for i in v.table(8, count, record)? {
316 let p = v.record(8, i, record)?;
317 let q = v.plus(p, skip)?;
318 let [mask, read_write_mask, _, _] = v.array::<4>(v.plus(q, 20)?)?;
319 result.push(SignatureElement {
320 name: v.string(v.u32(q)? as usize)?,
321 index: v.field(q, 4)?,
322 system_value: v.field(q, 8)?,
323 component_type: v.field(q, 12)?,
324 register: v.field(q, 16)?,
325 mask,
326 read_write_mask,
327 at: p / 4,
328 stream: if skip == 4 { v.u32(p)? } else { 0 },
329 min_precision: if record == 32 { v.field(q, 24)? } else { 0 },
330 });
331 }
332 Ok(result)
333}
334
335pub fn patch_signature_elements(bytes: &[u8]) -> Result<Vec<SignatureElement>> {
338 let directory = parts(bytes)?;
339 let (data, tag) = signature_part(&directory, &[b"PCSG", b"PSG1"])?;
340 signature(data, &tag)
341}
342
343fn signature_part<'a>(
345 directory: &[dxcontainer::Chunk<'a>],
346 tags: &[&[u8; 4]],
347) -> Result<(&'a [u8], [u8; 4])> {
348 let tag = tags
349 .iter()
350 .find(|t| directory.iter().any(|c| &&c.kind == *t))
351 .or(tags.first())
352 .ok_or_else(|| error(0, "no signature part kinds to look for"))?;
353 Ok((chunk(directory, tag)?, **tag))
354}
355
356pub fn signature_elements(
358 bytes: &[u8],
359) -> Result<(ProgramType, Vec<SignatureElement>, Vec<SignatureElement>)> {
360 let directory = parts(bytes)?;
361 let kind = program_type(&directory)?;
362 let read = |tags: &[&[u8; 4]]| -> Result<Vec<SignatureElement>> {
363 let (data, tag) = signature_part(&directory, tags)?;
364 signature(data, &tag)
365 };
366 Ok((
367 kind,
368 read(&[b"ISGN", b"ISG1"])?,
369 read(&[b"OSGN", b"OSG1", b"OSG5"])?,
370 ))
371}
372
373pub fn program_type(directory: &[dxcontainer::Chunk<'_>]) -> Result<ProgramType> {
375 let code = crate::code_part(directory)?;
376 let token = View(code.data).u32(0)?;
377 match token >> 16 {
378 0 => Ok(ProgramType::Pixel),
379 1 => Ok(ProgramType::Vertex),
380 2 => Ok(ProgramType::Geometry),
381 3 => Ok(ProgramType::Hull),
382 4 => Ok(ProgramType::Domain),
383 5 => Ok(ProgramType::Compute),
384 kind => Err(error(
385 0,
386 format!("expected a program type from 0 (pixel) to 5 (compute), found {kind}"),
387 )),
388 }
389}
390
391#[derive(Clone, Debug, PartialEq, Eq)]
393pub struct Rdef {
394 pub target: u32,
396 pub flags: u32,
398 pub creator: String,
400 pub bindings: Vec<RdefBinding>,
402 pub buffers: Vec<RdefBuffer>,
404}
405
406#[derive(Clone, Debug, PartialEq, Eq)]
408pub struct RdefBinding {
409 pub word: usize,
411 pub name: String,
413 pub kind: u32,
415 pub returns: u32,
417 pub dimension: u32,
419 pub samples: u32,
421 pub register: u32,
423 pub count: u32,
425 pub flags: u32,
427 pub space: u32,
429 pub id: Option<u32>,
432}
433
434impl RdefBinding {
435 pub fn type_name(&self) -> &'static str {
438 match self.kind {
439 0 => "cbuffer",
440 1 => "tbuffer",
441 3 if self.flags & 2 != 0 => "sampler_c",
443 3 => "sampler",
444 4 | 6 | 8..=11 | 13 => "UAV",
445 _ => "texture",
446 }
447 }
448
449 pub fn format_name(&self) -> String {
452 match self.kind {
453 0 | 1 | 3 => "NA".to_owned(),
454 5 | 6 | 9..=11 => "struct".to_owned(),
455 7 | 8 => "byte".to_owned(),
456 _ => {
457 let name = match self.returns {
458 1 => "unorm",
459 2 => "snorm",
460 3 => "sint",
461 4 => "uint",
462 5 => "float",
463 6 => "mixed",
464 7 => "double",
465 8 => "continued",
466 _ => "NA",
467 };
468 let components = ((self.flags >> 2) & 3).saturating_add(1);
470 if components > 1 {
471 format!("{name}{components}")
472 } else {
473 name.to_owned()
474 }
475 }
476 }
477 }
478
479 pub fn dimension_name(&self) -> String {
481 match self.kind {
482 0 | 1 | 3 => return "NA".to_owned(),
483 5 | 7 => return "r/o".to_owned(),
484 6 | 8 => return "r/w".to_owned(),
485 9 => return "append".to_owned(),
486 10 => return "consume".to_owned(),
487 11 => return "r/w+cnt".to_owned(),
488 _ => {}
489 }
490 let name = match self.dimension {
491 1 | 11 => "buf",
492 2 => "1d",
493 3 => "1darray",
494 4 => "2d",
495 5 => "2darray",
496 6 => "2dMS",
497 7 => "2darrayMS",
498 8 => "3d",
499 9 => "cube",
500 10 => "cubearray",
501 _ => "NA",
502 };
503 if matches!(self.dimension, 6 | 7) && self.samples != 0 && self.samples != u32::MAX {
504 format!("{name}{}", self.samples)
505 } else {
506 name.to_owned()
507 }
508 }
509
510 pub fn register_prefix(&self) -> &'static str {
512 match self.type_name() {
513 "cbuffer" => "cb",
514 "sampler" | "sampler_c" => "s",
515 "UAV" => "u",
516 _ => "t",
517 }
518 }
519}
520
521#[derive(Clone, Debug, PartialEq, Eq)]
523pub struct RdefBuffer {
524 pub word: usize,
526 pub name: String,
528 pub kind: u32,
530 pub size: u32,
532 pub variables: Vec<RdefVariable>,
534}
535
536#[derive(Clone, Debug, PartialEq, Eq)]
538pub struct RdefVariable {
539 pub word: usize,
541 pub name: String,
543 pub offset: u32,
545 pub size: u32,
547 pub flags: u32,
549 pub ty: RdefType,
551 pub default: Option<Vec<u8>>,
553 pub textures: [u32; 2],
556 pub samplers: [u32; 2],
558}
559
560#[derive(Clone, Debug, PartialEq, Eq)]
562pub struct RdefType {
563 pub word: usize,
565 pub class: u16,
567 pub scalar: u16,
569 pub rows: u16,
571 pub columns: u16,
573 pub elements: u16,
575 pub members: Vec<RdefMember>,
577 pub name: Option<String>,
579}
580
581impl RdefType {
582 pub fn scalar_name(&self) -> String {
585 match self.scalar {
586 0 => "void",
587 1 => "bool",
588 2 => "int",
589 3 => "float",
590 4 => "string",
591 5 => "texture",
592 6 => "Texture1D",
593 7 => "Texture2D",
594 8 => "Texture3D",
595 9 => "TextureCube",
596 10 => "SamplerState",
597 19 => "uint",
598 20 => "uint8",
599 25 => "Buffer",
600 26 => "cbuffer",
601 27 => "tbuffer",
602 28 => "Texture1DArray",
603 29 => "Texture2DArray",
604 32 => "Texture2DMS",
605 33 => "Texture2DMSArray",
606 34 => "TextureCubeArray",
607 39 => "double",
608 40 => "RWTexture1D",
609 41 => "RWTexture1DArray",
610 42 => "RWTexture2D",
611 43 => "RWTexture2DArray",
612 44 => "RWTexture3D",
613 45 => "RWBuffer",
614 46 => "ByteAddressBuffer",
615 47 => "RWByteAddressBuffer",
616 48 => "StructuredBuffer",
617 49 => "RWStructuredBuffer",
618 50 => "AppendStructuredBuffer",
619 51 => "ConsumeStructuredBuffer",
620 52 => "min8float",
621 53 => "min10float",
622 54 => "min16float",
623 55 => "min12int",
624 56 => "min16int",
625 57 => "min16uint",
626 other => return format!("type{other}"),
627 }
628 .to_owned()
629 }
630
631 pub fn type_name(&self) -> String {
636 let scalar = self.scalar_name();
637 match self.class {
638 1 => format!("{scalar}{}", self.columns),
639 2 => format!("row_major {scalar}{}x{}", self.rows, self.columns),
640 3 => format!("{scalar}{}x{}", self.rows, self.columns),
641 5 => "struct".to_owned(),
642 6 | 7 => format!("interface {}", self.name.as_deref().unwrap_or("")),
644 _ => scalar,
645 }
646 }
647}
648
649#[derive(Clone, Debug, PartialEq, Eq)]
651pub struct RdefMember {
652 pub name: String,
654 pub offset: u32,
656 pub ty: RdefType,
658}
659
660pub fn rdef_target(bytes: &[u8]) -> Result<Option<u32>> {
663 let directory = parts(bytes)?;
664 directory
665 .iter()
666 .find(|c| &c.kind == b"RDEF")
667 .map(|rdef| View(rdef.data).u32(16))
668 .transpose()
669}
670
671fn read_directory(v: &View<'_>) -> Result<Layout> {
674 let tag = v.bytes(28, 4)?;
675 let (layout, name) = if tag == b"RD11" {
676 (SM50, "RD11")
677 } else if tag == SM51_TAG {
678 (SM51, "5.1")
679 } else {
680 return Err(error(
681 7,
682 format!(
683 "expected the `RD11` tag of the Shader Model 5.0 RDEF layout or the bytes 13 13 44 25 of the 5.1 one, found {:02x?} ({:?})",
684 tag,
685 String::from_utf8_lossy(tag)
686 ),
687 ));
688 };
689 for (i, size) in [60, 24, layout.binding, layout.variable, layout.ty, 12]
690 .into_iter()
691 .enumerate()
692 {
693 let at = v.record(32, i, 4)?;
694 let found = v.u32(at)? as usize;
695 if found != size {
696 return Err(error(
697 at / 4,
698 format!(
699 "expected record size {size} (entry {i} of the {name} size directory), found {found}"
700 ),
701 ));
702 }
703 }
704 Ok(layout)
705}
706
707pub fn rdef(bytes: &[u8]) -> Result<Option<Rdef>> {
711 let directory = parts(bytes)?;
712 if !directory.iter().any(|c| &c.kind == b"RDEF") {
713 return Ok(None);
714 }
715 let v = View(chunk(&directory, b"RDEF")?);
716 let target = v.u32(16)?;
717 let layout = match (target >> 8) & 0xff {
718 4 => SM4,
719 _ => read_directory(&v)?,
720 };
721 let mut bindings = Vec::new();
722 let offset = v.field(0, 12)? as usize;
723 for i in v.table(offset, v.field(0, 8)? as usize, layout.binding)? {
724 let p = v.record(offset, i, layout.binding)?;
725 let spaced = layout.binding == SM51.binding;
726 bindings.push(RdefBinding {
727 word: p / 4,
728 name: v.string(v.u32(p)? as usize)?,
729 kind: v.field(p, 4)?,
730 returns: v.field(p, 8)?,
731 dimension: v.field(p, 12)?,
732 samples: v.field(p, 16)?,
733 register: v.field(p, 20)?,
734 count: v.field(p, 24)?,
735 flags: v.field(p, 28)?,
736 space: if spaced { v.field(p, 32)? } else { 0 },
737 id: if spaced { Some(v.field(p, 36)?) } else { None },
738 });
739 }
740 let mut buffers = Vec::new();
741 let offset = v.field(0, 4)? as usize;
742 let mut budget = MAX_RECORDS;
743 for i in v.table(offset, v.u32(0)? as usize, 24)? {
744 let p = v.record(offset, i, 24)?;
745 let name = v.string(v.u32(p)? as usize)?;
746 let kind = v.field(p, 20)?;
747 let size = v.field(p, 12)?;
748 if size as usize > MAX_BUFFER_SIZE {
749 return Err(error(
750 v.plus(p, 12)? / 4,
751 format!("constant buffer of {size} bytes, more than the {MAX_BUFFER_SIZE} allowed"),
752 ));
753 }
754 let mut variables = Vec::new();
755 let entries = v.field(p, 8)? as usize;
756 for j in v.table(entries, v.field(p, 4)? as usize, layout.variable)? {
757 let q = v.record(entries, j, layout.variable)?;
758 let start = v.field(q, 4)?;
759 let len = v.field(q, 8)?;
760 if kind != 2 && start.checked_add(len).is_none_or(|end| end > size) {
763 return Err(error(
764 v.plus(q, 4)? / 4,
765 format!(
766 "variable at byte {start} of {len} bytes lies outside its {size} byte constant buffer"
767 ),
768 ));
769 }
770 let default = v.field(q, 20)? as usize;
771 let ty = v.ty(v.field(q, 16)? as usize, layout, &mut Vec::new(), &mut budget)?;
772 variables.push(RdefVariable {
773 word: q / 4,
774 name: v.string(v.u32(q)? as usize)?,
775 offset: start,
776 size: len,
777 flags: v.field(q, 12)?,
778 ty,
779 default: if default == 0 {
780 None
781 } else {
782 Some(v.bytes(default, len as usize)?.to_vec())
783 },
784 textures: if layout.variable >= 40 {
785 [v.field(q, 24)?, v.field(q, 28)?]
786 } else {
787 [u32::MAX, 0]
788 },
789 samplers: if layout.variable >= 40 {
790 [v.field(q, 32)?, v.field(q, 36)?]
791 } else {
792 [u32::MAX, 0]
793 },
794 });
795 }
796 buffers.push(RdefBuffer {
797 word: p / 4,
798 name,
799 kind,
800 size,
801 variables,
802 });
803 }
804 Ok(Some(Rdef {
805 target,
806 flags: v.u32(20)?,
807 creator: v.string(v.u32(24)? as usize)?,
808 bindings,
809 buffers,
810 }))
811}