1use std::fmt;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4pub enum ShaderKind {
5 Vertex,
6 Pixel,
7}
8
9impl ShaderKind {
10 pub fn profile_prefix(self) -> &'static str {
11 match self {
12 ShaderKind::Vertex => "vs",
13 ShaderKind::Pixel => "ps",
14 }
15 }
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum Opcode {
20 Nop,
21 Mov,
22 Add,
23 Sub,
24 Mad,
25 Mul,
26 Rcp,
27 Rsq,
28 Dp3,
29 Dp4,
30 Min,
31 Max,
32 Slt,
33 Sge,
34 Exp,
35 Log,
36 Lit,
37 Dst,
38 Lrp,
39 Frc,
40 M4x4,
41 M4x3,
42 M3x4,
43 M3x3,
44 M3x2,
45 Call,
46 CallNZ,
47 Loop,
48 Ret,
49 EndLoop,
50 Label,
51 Dcl,
52 Pow,
53 Crs,
54 Sgn,
55 Abs,
56 Nrm,
57 SinCos,
58 Rep,
59 EndRep,
60 If,
61 IfC,
62 Else,
63 EndIf,
64 Break,
65 BreakC,
66 MovA,
67 DefB,
68 DefI,
69 TexCoord,
70 TexKill,
71 Tex,
72 TexBem,
73 TexBeml,
74 TexReg2AR,
75 TexReg2GB,
76 TexM3x2Pad,
77 TexM3x2Tex,
78 TexM3x3Pad,
79 TexM3x3Tex,
80 TexM3x3Diff,
81 TexM3x3Spec,
82 TexM3x3VSpec,
83 ExpP,
84 LogP,
85 Cnd,
86 Def,
87 TexReg2RGB,
88 TexDP3Tex,
89 TexM3x2Depth,
90 TexDP3,
91 TexM3x3,
92 TexDepth,
93 Cmp,
94 Bem,
95 Dp2Add,
96 Dsx,
97 Dsy,
98 TexLdd,
99 SetP,
100 TexLdl,
101 BreakP,
102 Phase,
103 Comment,
104 End,
105 Unknown(u16),
106}
107
108impl Opcode {
109 pub fn from_u16(v: u16) -> Self {
110 match v {
111 0 => Self::Nop,
112 1 => Self::Mov,
113 2 => Self::Add,
114 3 => Self::Sub,
115 4 => Self::Mad,
116 5 => Self::Mul,
117 6 => Self::Rcp,
118 7 => Self::Rsq,
119 8 => Self::Dp3,
120 9 => Self::Dp4,
121 10 => Self::Min,
122 11 => Self::Max,
123 12 => Self::Slt,
124 13 => Self::Sge,
125 14 => Self::Exp,
126 15 => Self::Log,
127 16 => Self::Lit,
128 17 => Self::Dst,
129 18 => Self::Lrp,
130 19 => Self::Frc,
131 20 => Self::M4x4,
132 21 => Self::M4x3,
133 22 => Self::M3x4,
134 23 => Self::M3x3,
135 24 => Self::M3x2,
136 25 => Self::Call,
137 26 => Self::CallNZ,
138 27 => Self::Loop,
139 28 => Self::Ret,
140 29 => Self::EndLoop,
141 30 => Self::Label,
142 31 => Self::Dcl,
143 32 => Self::Pow,
144 33 => Self::Crs,
145 34 => Self::Sgn,
146 35 => Self::Abs,
147 36 => Self::Nrm,
148 37 => Self::SinCos,
149 38 => Self::Rep,
150 39 => Self::EndRep,
151 40 => Self::If,
152 41 => Self::IfC,
153 42 => Self::Else,
154 43 => Self::EndIf,
155 44 => Self::Break,
156 45 => Self::BreakC,
157 46 => Self::MovA,
158 47 => Self::DefB,
159 48 => Self::DefI,
160 64 => Self::TexCoord,
161 65 => Self::TexKill,
162 66 => Self::Tex,
163 67 => Self::TexBem,
164 68 => Self::TexBeml,
165 69 => Self::TexReg2AR,
166 70 => Self::TexReg2GB,
167 71 => Self::TexM3x2Pad,
168 72 => Self::TexM3x2Tex,
169 73 => Self::TexM3x3Pad,
170 74 => Self::TexM3x3Tex,
171 75 => Self::TexM3x3Diff,
172 76 => Self::TexM3x3Spec,
173 77 => Self::TexM3x3VSpec,
174 78 => Self::ExpP,
175 79 => Self::LogP,
176 80 => Self::Cnd,
177 81 => Self::Def,
178 82 => Self::TexReg2RGB,
179 83 => Self::TexDP3Tex,
180 84 => Self::TexM3x2Depth,
181 85 => Self::TexDP3,
182 86 => Self::TexM3x3,
183 87 => Self::TexDepth,
184 88 => Self::Cmp,
185 89 => Self::Bem,
186 90 => Self::Dp2Add,
187 91 => Self::Dsx,
188 92 => Self::Dsy,
189 93 => Self::TexLdd,
190 94 => Self::SetP,
191 95 => Self::TexLdl,
192 96 => Self::BreakP,
193 0xfffd => Self::Phase,
194 0xfffe => Self::Comment,
195 0xffff => Self::End,
196 x => Self::Unknown(x),
197 }
198 }
199
200 pub fn code(self) -> u16 {
201 match self {
202 Self::Nop => 0,
203 Self::Mov => 1,
204 Self::Add => 2,
205 Self::Sub => 3,
206 Self::Mad => 4,
207 Self::Mul => 5,
208 Self::Rcp => 6,
209 Self::Rsq => 7,
210 Self::Dp3 => 8,
211 Self::Dp4 => 9,
212 Self::Min => 10,
213 Self::Max => 11,
214 Self::Slt => 12,
215 Self::Sge => 13,
216 Self::Exp => 14,
217 Self::Log => 15,
218 Self::Lit => 16,
219 Self::Dst => 17,
220 Self::Lrp => 18,
221 Self::Frc => 19,
222 Self::M4x4 => 20,
223 Self::M4x3 => 21,
224 Self::M3x4 => 22,
225 Self::M3x3 => 23,
226 Self::M3x2 => 24,
227 Self::Call => 25,
228 Self::CallNZ => 26,
229 Self::Loop => 27,
230 Self::Ret => 28,
231 Self::EndLoop => 29,
232 Self::Label => 30,
233 Self::Dcl => 31,
234 Self::Pow => 32,
235 Self::Crs => 33,
236 Self::Sgn => 34,
237 Self::Abs => 35,
238 Self::Nrm => 36,
239 Self::SinCos => 37,
240 Self::Rep => 38,
241 Self::EndRep => 39,
242 Self::If => 40,
243 Self::IfC => 41,
244 Self::Else => 42,
245 Self::EndIf => 43,
246 Self::Break => 44,
247 Self::BreakC => 45,
248 Self::MovA => 46,
249 Self::DefB => 47,
250 Self::DefI => 48,
251 Self::TexCoord => 64,
252 Self::TexKill => 65,
253 Self::Tex => 66,
254 Self::TexBem => 67,
255 Self::TexBeml => 68,
256 Self::TexReg2AR => 69,
257 Self::TexReg2GB => 70,
258 Self::TexM3x2Pad => 71,
259 Self::TexM3x2Tex => 72,
260 Self::TexM3x3Pad => 73,
261 Self::TexM3x3Tex => 74,
262 Self::TexM3x3Diff => 75,
263 Self::TexM3x3Spec => 76,
264 Self::TexM3x3VSpec => 77,
265 Self::ExpP => 78,
266 Self::LogP => 79,
267 Self::Cnd => 80,
268 Self::Def => 81,
269 Self::TexReg2RGB => 82,
270 Self::TexDP3Tex => 83,
271 Self::TexM3x2Depth => 84,
272 Self::TexDP3 => 85,
273 Self::TexM3x3 => 86,
274 Self::TexDepth => 87,
275 Self::Cmp => 88,
276 Self::Bem => 89,
277 Self::Dp2Add => 90,
278 Self::Dsx => 91,
279 Self::Dsy => 92,
280 Self::TexLdd => 93,
281 Self::SetP => 94,
282 Self::TexLdl => 95,
283 Self::BreakP => 96,
284 Self::Phase => 0xfffd,
285 Self::Comment => 0xfffe,
286 Self::End => 0xffff,
287 Self::Unknown(x) => x,
288 }
289 }
290
291 pub fn mnemonic(self) -> &'static str {
292 match self {
293 Self::Nop => "nop",
294 Self::Mov => "mov",
295 Self::Add => "add",
296 Self::Sub => "sub",
297 Self::Mad => "mad",
298 Self::Mul => "mul",
299 Self::Rcp => "rcp",
300 Self::Rsq => "rsq",
301 Self::Dp3 => "dp3",
302 Self::Dp4 => "dp4",
303 Self::Min => "min",
304 Self::Max => "max",
305 Self::Slt => "slt",
306 Self::Sge => "sge",
307 Self::Exp => "exp",
308 Self::Log => "log",
309 Self::Lit => "lit",
310 Self::Dst => "dst",
311 Self::Lrp => "lrp",
312 Self::Frc => "frc",
313 Self::M4x4 => "m4x4",
314 Self::M4x3 => "m4x3",
315 Self::M3x4 => "m3x4",
316 Self::M3x3 => "m3x3",
317 Self::M3x2 => "m3x2",
318 Self::Call => "call",
319 Self::CallNZ => "callnz",
320 Self::Loop => "loop",
321 Self::Ret => "ret",
322 Self::EndLoop => "endloop",
323 Self::Label => "label",
324 Self::Dcl => "dcl",
325 Self::Pow => "pow",
326 Self::Crs => "crs",
327 Self::Sgn => "sgn",
328 Self::Abs => "abs",
329 Self::Nrm => "nrm",
330 Self::SinCos => "sincos",
331 Self::Rep => "rep",
332 Self::EndRep => "endrep",
333 Self::If => "if",
334 Self::IfC => "ifc",
335 Self::Else => "else",
336 Self::EndIf => "endif",
337 Self::Break => "break",
338 Self::BreakC => "breakc",
339 Self::MovA => "mova",
340 Self::DefB => "defb",
341 Self::DefI => "defi",
342 Self::TexCoord => "texcoord",
343 Self::TexKill => "texkill",
344 Self::Tex => "texld",
345 Self::TexBem => "texbem",
346 Self::TexBeml => "texbeml",
347 Self::TexReg2AR => "texreg2ar",
348 Self::TexReg2GB => "texreg2gb",
349 Self::TexM3x2Pad => "texm3x2pad",
350 Self::TexM3x2Tex => "texm3x2tex",
351 Self::TexM3x3Pad => "texm3x3pad",
352 Self::TexM3x3Tex => "texm3x3tex",
353 Self::TexM3x3Diff => "texm3x3diff",
354 Self::TexM3x3Spec => "texm3x3spec",
355 Self::TexM3x3VSpec => "texm3x3vspec",
356 Self::ExpP => "expp",
357 Self::LogP => "logp",
358 Self::Cnd => "cnd",
359 Self::Def => "def",
360 Self::TexReg2RGB => "texreg2rgb",
361 Self::TexDP3Tex => "texdp3tex",
362 Self::TexM3x2Depth => "texm3x2depth",
363 Self::TexDP3 => "texdp3",
364 Self::TexM3x3 => "texm3x3",
365 Self::TexDepth => "texdepth",
366 Self::Cmp => "cmp",
367 Self::Bem => "bem",
368 Self::Dp2Add => "dp2add",
369 Self::Dsx => "dsx",
370 Self::Dsy => "dsy",
371 Self::TexLdd => "texldd",
372 Self::SetP => "setp",
373 Self::TexLdl => "texldl",
374 Self::BreakP => "breakp",
375 Self::Phase => "phase",
376 Self::Comment => "comment",
377 Self::End => "end",
378 Self::Unknown(_) => "unknown",
379 }
380 }
381
382 pub fn has_destination(self) -> bool {
383 matches!(
384 self,
385 Self::Abs
386 | Self::Add
387 | Self::Bem
388 | Self::Cmp
389 | Self::Cnd
390 | Self::Crs
391 | Self::Dcl
392 | Self::Def
393 | Self::DefB
394 | Self::DefI
395 | Self::Dp2Add
396 | Self::Dp3
397 | Self::Dp4
398 | Self::Dst
399 | Self::Dsx
400 | Self::Dsy
401 | Self::Exp
402 | Self::ExpP
403 | Self::Frc
404 | Self::Lit
405 | Self::Log
406 | Self::LogP
407 | Self::Lrp
408 | Self::M3x2
409 | Self::M3x3
410 | Self::M3x4
411 | Self::M4x3
412 | Self::M4x4
413 | Self::Mad
414 | Self::Max
415 | Self::Min
416 | Self::Mov
417 | Self::MovA
418 | Self::Mul
419 | Self::Nrm
420 | Self::Pow
421 | Self::Rcp
422 | Self::Rsq
423 | Self::SetP
424 | Self::Sge
425 | Self::Sgn
426 | Self::SinCos
427 | Self::Slt
428 | Self::Sub
429 | Self::Tex
430 | Self::TexBem
431 | Self::TexBeml
432 | Self::TexCoord
433 | Self::TexDepth
434 | Self::TexDP3
435 | Self::TexDP3Tex
436 | Self::TexKill
437 | Self::TexLdd
438 | Self::TexLdl
439 | Self::TexM3x2Depth
440 | Self::TexM3x2Pad
441 | Self::TexM3x2Tex
442 | Self::TexM3x3
443 | Self::TexM3x3Diff
444 | Self::TexM3x3Pad
445 | Self::TexM3x3Spec
446 | Self::TexM3x3Tex
447 | Self::TexM3x3VSpec
448 | Self::TexReg2AR
449 | Self::TexReg2GB
450 | Self::TexReg2RGB
451 )
452 }
453}
454
455#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
456pub enum RegisterType {
457 Temp,
458 Input,
459 Const,
460 Texture,
461 RastOut,
462 AttrOut,
463 Output,
464 ConstInt,
465 ColorOut,
466 DepthOut,
467 Sampler,
468 Const2,
469 Const3,
470 Const4,
471 ConstBool,
472 Loop,
473 TempFloat16,
474 MiscType,
475 Label,
476 Predicate,
477 Unknown(u8),
478}
479
480impl RegisterType {
481 pub fn from_param_token(token: u32) -> Self {
482 let raw = (((token >> 28) & 0x7) | ((token >> 8) & 0x18)) as u8;
483 match raw {
484 0 => Self::Temp,
485 1 => Self::Input,
486 2 => Self::Const,
487 3 => Self::Texture,
488 4 => Self::RastOut,
489 5 => Self::AttrOut,
490 6 => Self::Output,
491 7 => Self::ConstInt,
492 8 => Self::ColorOut,
493 9 => Self::DepthOut,
494 10 => Self::Sampler,
495 11 => Self::Const2,
496 12 => Self::Const3,
497 13 => Self::Const4,
498 14 => Self::ConstBool,
499 15 => Self::Loop,
500 16 => Self::TempFloat16,
501 17 => Self::MiscType,
502 18 => Self::Label,
503 19 => Self::Predicate,
504 x => Self::Unknown(x),
505 }
506 }
507
508 pub fn asm_prefix(self) -> &'static str {
509 match self {
510 Self::Temp => "r",
511 Self::Input => "v",
512 Self::Const => "c",
513 Self::Texture => "t",
514 Self::RastOut => "o",
515 Self::AttrOut => "oD",
516 Self::Output => "o",
517 Self::ConstInt => "i",
518 Self::ColorOut => "oC",
519 Self::DepthOut => "oDepth",
520 Self::Sampler => "s",
521 Self::ConstBool => "b",
522 Self::Loop => "aL",
523 Self::TempFloat16 => "r",
524 Self::MiscType => "v",
525 Self::Label => "l",
526 Self::Predicate => "p",
527 Self::Const2 | Self::Const3 | Self::Const4 => "c",
528 Self::Unknown(_) => "u",
529 }
530 }
531}
532
533#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
534pub struct RegisterKey {
535 pub ty: RegisterType,
536 pub number: u16,
537}
538
539#[derive(Debug, Clone, Copy, PartialEq, Eq)]
540pub enum SourceModifier {
541 None,
542 Negate,
543 Bias,
544 BiasAndNegate,
545 Sign,
546 SignAndNegate,
547 Complement,
548 X2,
549 X2AndNegate,
550 DivideByZ,
551 DivideByW,
552 Abs,
553 AbsAndNegate,
554 Not,
555 Unknown(u8),
556}
557
558impl SourceModifier {
559 pub fn from_param_token(token: u32) -> Self {
560 match ((token >> 24) & 0xf) as u8 {
561 0 => Self::None,
562 1 => Self::Negate,
563 2 => Self::Bias,
564 3 => Self::BiasAndNegate,
565 4 => Self::Sign,
566 5 => Self::SignAndNegate,
567 6 => Self::Complement,
568 7 => Self::X2,
569 8 => Self::X2AndNegate,
570 9 => Self::DivideByZ,
571 10 => Self::DivideByW,
572 11 => Self::Abs,
573 12 => Self::AbsAndNegate,
574 13 => Self::Not,
575 x => Self::Unknown(x),
576 }
577 }
578}
579
580#[derive(Debug, Clone, Copy, PartialEq, Eq)]
581pub struct ResultModifier {
582 pub saturate: bool,
583 pub partial_precision: bool,
584 pub centroid: bool,
585 pub raw: u8,
586}
587
588impl ResultModifier {
589 pub fn from_dest_token(token: u32) -> Self {
590 let raw = ((token >> 20) & 0xf) as u8;
591 Self {
592 saturate: (raw & 1) != 0,
593 partial_precision: (raw & 2) != 0,
594 centroid: (raw & 4) != 0,
595 raw,
596 }
597 }
598}
599
600#[derive(Debug, Clone, Copy, PartialEq, Eq)]
601pub enum SamplerTextureType {
602 Unknown,
603 TwoD,
604 Cube,
605 Volume,
606}
607
608impl SamplerTextureType {
609 pub fn from_decl_token(token: u32) -> Self {
610 match ((token >> 27) & 0xf) as u8 {
611 2 => Self::TwoD,
612 3 => Self::Cube,
613 4 => Self::Volume,
614 _ => Self::Unknown,
615 }
616 }
617
618 pub fn asm_name(self) -> &'static str {
619 match self {
620 Self::TwoD => "2d",
621 Self::Cube => "cube",
622 Self::Volume => "volume",
623 Self::Unknown => "unknown",
624 }
625 }
626
627 pub fn hlsl_dim(self) -> usize {
628 match self {
629 Self::Cube | Self::Volume => 3,
630 Self::TwoD | Self::Unknown => 2,
631 }
632 }
633}
634
635#[derive(Debug, Clone, Copy, PartialEq, Eq)]
636pub enum DeclUsage {
637 Position,
638 BlendWeight,
639 BlendIndices,
640 Normal,
641 PSize,
642 TexCoord,
643 Tangent,
644 Binormal,
645 TessFactor,
646 PositionT,
647 Color,
648 Fog,
649 Depth,
650 Sample,
651 Unknown(u8),
652}
653
654impl DeclUsage {
655 pub fn from_decl_token(token: u32) -> Self {
656 match (token & 0x1f) as u8 {
657 0 => Self::Position,
658 1 => Self::BlendWeight,
659 2 => Self::BlendIndices,
660 3 => Self::Normal,
661 4 => Self::PSize,
662 5 => Self::TexCoord,
663 6 => Self::Tangent,
664 7 => Self::Binormal,
665 8 => Self::TessFactor,
666 9 => Self::PositionT,
667 10 => Self::Color,
668 11 => Self::Fog,
669 12 => Self::Depth,
670 13 => Self::Sample,
671 x => Self::Unknown(x),
672 }
673 }
674
675 pub fn semantic_prefix(self) -> &'static str {
676 match self {
677 Self::Position => "POSITION",
678 Self::BlendWeight => "BLENDWEIGHT",
679 Self::BlendIndices => "BLENDINDICES",
680 Self::Normal => "NORMAL",
681 Self::PSize => "PSIZE",
682 Self::TexCoord => "TEXCOORD",
683 Self::Tangent => "TANGENT",
684 Self::Binormal => "BINORMAL",
685 Self::TessFactor => "TESSFACTOR",
686 Self::PositionT => "POSITIONT",
687 Self::Color => "COLOR",
688 Self::Fog => "FOG",
689 Self::Depth => "DEPTH",
690 Self::Sample => "SAMPLE",
691 Self::Unknown(_) => "TEXCOORD",
692 }
693 }
694}
695
696#[derive(Debug, Clone)]
697pub struct Instruction {
698 pub offset: usize,
699 pub token: u32,
700 pub opcode: Opcode,
701 pub params: Vec<u32>,
702}
703
704impl Instruction {
705 pub fn comparison(&self) -> u8 {
706 ((self.token >> 16) & 7) as u8
707 }
708
709 pub fn texld_controls(&self) -> u8 {
710 ((self.token >> 16) & 3) as u8
711 }
712
713 pub fn dest_param_index(&self) -> usize {
714 if self.opcode == Opcode::Dcl { 1 } else { 0 }
715 }
716
717 pub fn dest_token(&self) -> Option<u32> {
718 self.params.get(self.dest_param_index()).copied()
719 }
720
721 pub fn dest_register(&self) -> Option<RegisterKey> {
722 let t = self.dest_token()?;
723 Some(RegisterKey {
724 ty: RegisterType::from_param_token(t),
725 number: (t & 0x7ff) as u16,
726 })
727 }
728
729 pub fn dest_write_mask(&self) -> u8 {
730 let Some(t) = self.dest_token() else {
731 return 0xf;
732 };
733 let mask = ((t >> 16) & 0xf) as u8;
734 if mask == 0 { 0xf } else { mask }
735 }
736
737 pub fn dest_modifier(&self) -> ResultModifier {
738 ResultModifier::from_dest_token(self.dest_token().unwrap_or(0))
739 }
740
741 pub fn source_register(&self, param_index: usize) -> Option<RegisterKey> {
742 let t = *self.params.get(param_index)?;
743 Some(RegisterKey {
744 ty: RegisterType::from_param_token(t),
745 number: (t & 0x7ff) as u16,
746 })
747 }
748
749 pub fn source_modifier(&self, param_index: usize) -> SourceModifier {
750 self.params
751 .get(param_index)
752 .copied()
753 .map(SourceModifier::from_param_token)
754 .unwrap_or(SourceModifier::None)
755 }
756
757 pub fn source_swizzle(&self, param_index: usize) -> [usize; 4] {
758 let t = self.params.get(param_index).copied().unwrap_or(0);
759 let swz = ((t >> 16) & 0xff) as usize;
760 [swz & 3, (swz >> 2) & 3, (swz >> 4) & 3, (swz >> 6) & 3]
761 }
762
763 pub fn decl_usage(&self) -> DeclUsage {
764 self.params
765 .first()
766 .copied()
767 .map(DeclUsage::from_decl_token)
768 .unwrap_or(DeclUsage::TexCoord)
769 }
770
771 pub fn decl_index(&self) -> u8 {
772 self.params
773 .first()
774 .map(|v| ((v >> 16) & 0x0f) as u8)
775 .unwrap_or(0)
776 }
777
778 pub fn decl_sampler_type(&self) -> SamplerTextureType {
779 self.params
780 .first()
781 .copied()
782 .map(SamplerTextureType::from_decl_token)
783 .unwrap_or(SamplerTextureType::Unknown)
784 }
785
786 pub fn get_float_param(&self, index: usize) -> f32 {
787 f32::from_bits(*self.params.get(index).unwrap_or(&0))
788 }
789
790 pub fn get_int_param(&self, index: usize) -> i32 {
791 *self.params.get(index).unwrap_or(&0) as i32
792 }
793}
794
795#[derive(Debug, Clone)]
796pub struct ShaderModel {
797 pub kind: ShaderKind,
798 pub major: u8,
799 pub minor: u8,
800 pub instructions: Vec<Instruction>,
801}
802
803#[derive(Debug, Clone)]
804pub enum ShaderParseError {
805 TooShort,
806 BadVersion(u32),
807 Truncated {
808 offset: usize,
809 need: usize,
810 len: usize,
811 },
812 MissingEnd,
813}
814
815impl fmt::Display for ShaderParseError {
816 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
817 match self {
818 Self::TooShort => write!(f, "shader bytecode is too short"),
819 Self::BadVersion(v) => write!(f, "unsupported shader version token 0x{v:08x}"),
820 Self::Truncated { offset, need, len } => write!(
821 f,
822 "truncated shader token stream at 0x{offset:x}: need {need} bytes, len {len}"
823 ),
824 Self::MissingEnd => write!(f, "shader token stream has no END token"),
825 }
826 }
827}
828
829impl std::error::Error for ShaderParseError {}
830
831fn read_u32(data: &[u8], off: usize) -> Result<u32, ShaderParseError> {
832 let b = data.get(off..off + 4).ok_or(ShaderParseError::Truncated {
833 offset: off,
834 need: 4,
835 len: data.len(),
836 })?;
837 Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
838}
839
840pub fn parse_shader(data: &[u8]) -> Result<ShaderModel, ShaderParseError> {
841 if data.len() < 4 {
842 return Err(ShaderParseError::TooShort);
843 }
844 let version = read_u32(data, 0)?;
845 let minor = (version & 0xff) as u8;
846 let major = ((version >> 8) & 0xff) as u8;
847 let kind = match version & 0xffff_0000 {
848 0xfffe_0000 => ShaderKind::Vertex,
849 0xffff_0000 => ShaderKind::Pixel,
850 _ => return Err(ShaderParseError::BadVersion(version)),
851 };
852
853 let mut instructions = Vec::new();
854 let mut off = 4usize;
855 while off + 4 <= data.len() {
856 let token = read_u32(data, off)?;
857 let opcode = Opcode::from_u16((token & 0xffff) as u16);
858 let param_count = if opcode == Opcode::Comment {
859 ((token >> 16) & 0x7fff) as usize
860 } else if major == 1 {
861 fixed_size_param_count(opcode)
862 } else {
863 ((token >> 24) & 0x0f) as usize
864 };
865 let params_start = off + 4;
866 let params_bytes = param_count.saturating_mul(4);
867 if params_start + params_bytes > data.len() {
868 return Err(ShaderParseError::Truncated {
869 offset: off,
870 need: 4 + params_bytes,
871 len: data.len(),
872 });
873 }
874 let mut params = Vec::with_capacity(param_count);
875 for p in 0..param_count {
876 params.push(read_u32(data, params_start + p * 4)?);
877 }
878 instructions.push(Instruction {
879 offset: off,
880 token,
881 opcode,
882 params,
883 });
884 off = params_start + params_bytes;
885 if opcode == Opcode::End {
886 return Ok(ShaderModel {
887 kind,
888 major,
889 minor,
890 instructions,
891 });
892 }
893 }
894
895 Err(ShaderParseError::MissingEnd)
896}
897
898fn fixed_size_param_count(opcode: Opcode) -> usize {
899 match opcode {
900 Opcode::Comment => 0,
901 Opcode::Def => 5,
902 Opcode::TexCoord
903 | Opcode::TexKill
904 | Opcode::Tex
905 | Opcode::TexBem
906 | Opcode::TexBeml
907 | Opcode::TexReg2AR
908 | Opcode::TexReg2GB
909 | Opcode::TexM3x2Pad
910 | Opcode::TexM3x2Tex
911 | Opcode::TexM3x3Pad
912 | Opcode::TexM3x3Tex
913 | Opcode::TexM3x3Diff
914 | Opcode::TexM3x3Spec
915 | Opcode::TexM3x3VSpec
916 | Opcode::TexReg2RGB
917 | Opcode::TexDP3Tex
918 | Opcode::TexM3x2Depth
919 | Opcode::TexDP3
920 | Opcode::TexM3x3
921 | Opcode::TexDepth => 2,
922 Opcode::Dcl => 2,
923 Opcode::End
924 | Opcode::Nop
925 | Opcode::Phase
926 | Opcode::Ret
927 | Opcode::Else
928 | Opcode::EndIf
929 | Opcode::EndLoop
930 | Opcode::EndRep
931 | Opcode::Break => 0,
932 _ if opcode.has_destination() => 1 + num_inputs(opcode),
933 _ => num_inputs(opcode),
934 }
935}
936
937pub fn num_inputs(opcode: Opcode) -> usize {
938 match opcode {
939 Opcode::Abs
940 | Opcode::CallNZ
941 | Opcode::Dsx
942 | Opcode::Dsy
943 | Opcode::Exp
944 | Opcode::ExpP
945 | Opcode::Frc
946 | Opcode::Lit
947 | Opcode::Log
948 | Opcode::LogP
949 | Opcode::Loop
950 | Opcode::Mov
951 | Opcode::MovA
952 | Opcode::Nrm
953 | Opcode::Rcp
954 | Opcode::Rsq
955 | Opcode::SinCos
956 | Opcode::TexKill
957 | Opcode::If
958 | Opcode::Rep => 1,
959 Opcode::Add
960 | Opcode::Bem
961 | Opcode::Crs
962 | Opcode::Dp3
963 | Opcode::Dp4
964 | Opcode::Dst
965 | Opcode::M3x2
966 | Opcode::M3x3
967 | Opcode::M3x4
968 | Opcode::M4x3
969 | Opcode::M4x4
970 | Opcode::Max
971 | Opcode::Min
972 | Opcode::Mul
973 | Opcode::Pow
974 | Opcode::SetP
975 | Opcode::Sge
976 | Opcode::Slt
977 | Opcode::Sub
978 | Opcode::Tex
979 | Opcode::TexLdd
980 | Opcode::TexLdl
981 | Opcode::BreakC
982 | Opcode::IfC => 2,
983 Opcode::Cmp | Opcode::Cnd | Opcode::Dp2Add | Opcode::Lrp | Opcode::Mad | Opcode::Sgn => 3,
984 _ => 0,
985 }
986}
987
988pub fn mask_len(mask: u8) -> usize {
989 (0..4).filter(|i| (mask & (1 << i)) != 0).count().max(1)
990}
991
992pub fn mask_suffix(mask: u8) -> String {
993 let m = if mask == 0 { 0xf } else { mask };
994 if m == 0xf {
995 String::new()
996 } else {
997 let mut s = String::from(".");
998 for (i, c) in ['x', 'y', 'z', 'w'].iter().enumerate() {
999 if (m & (1 << i)) != 0 {
1000 s.push(*c);
1001 }
1002 }
1003 s
1004 }
1005}
1006
1007pub fn swizzle_suffix(swizzle: [usize; 4], count: usize) -> String {
1008 let count = count.clamp(1, 4);
1009 let identity = [0usize, 1, 2, 3];
1010 if count == 4 && swizzle == identity {
1011 return String::new();
1012 }
1013 if count < 4 && swizzle[..count] == identity[..count] {
1014 return String::new();
1015 }
1016 let names = ['x', 'y', 'z', 'w'];
1017 let mut s = String::from(".");
1018 for i in 0..count {
1019 s.push(names[swizzle[i]]);
1020 }
1021 s
1022}
1023
1024pub fn asm_register_name(reg: RegisterKey) -> String {
1025 match reg.ty {
1026 RegisterType::RastOut => match reg.number {
1027 0 => "oPos".to_string(),
1028 1 => "oFog".to_string(),
1029 2 => "oPts".to_string(),
1030 _ => format!("o{}", reg.number),
1031 },
1032 RegisterType::DepthOut => "oDepth".to_string(),
1033 RegisterType::MiscType => match reg.number {
1034 0 => "vPos".to_string(),
1035 1 => "vFace".to_string(),
1036 _ => format!("vMisc{}", reg.number),
1037 },
1038 _ => format!("{}{}", reg.ty.asm_prefix(), reg.number),
1039 }
1040}
1041
1042fn format_source_param(inst: &Instruction, param_index: usize, count: usize) -> String {
1043 let Some(reg) = inst.source_register(param_index) else {
1044 return "<?>".to_string();
1045 };
1046 let base = asm_register_name(reg);
1047 let swz = swizzle_suffix(inst.source_swizzle(param_index), count);
1048 let value = format!("{base}{swz}");
1049 match inst.source_modifier(param_index) {
1050 SourceModifier::None => value,
1051 SourceModifier::Negate => format!("-{value}"),
1052 SourceModifier::Abs => format!("abs({value})"),
1053 SourceModifier::AbsAndNegate => format!("-abs({value})"),
1054 SourceModifier::Bias => format!("{value}_bias"),
1055 SourceModifier::BiasAndNegate => format!("-{value}_bias"),
1056 SourceModifier::Sign => format!("{value}_bx2"),
1057 SourceModifier::SignAndNegate => format!("-{value}_bx2"),
1058 SourceModifier::Complement => format!("1-{value}"),
1059 SourceModifier::X2 => format!("{value}_x2"),
1060 SourceModifier::X2AndNegate => format!("-{value}_x2"),
1061 SourceModifier::DivideByZ => format!("{value}_dz"),
1062 SourceModifier::DivideByW => format!("{value}_dw"),
1063 SourceModifier::Not => format!("!{value}"),
1064 SourceModifier::Unknown(_) => value,
1065 }
1066}
1067
1068fn format_dest_param(inst: &Instruction) -> String {
1069 let Some(reg) = inst.dest_register() else {
1070 return "<?>".to_string();
1071 };
1072 format!(
1073 "{}{}",
1074 asm_register_name(reg),
1075 mask_suffix(inst.dest_write_mask())
1076 )
1077}
1078
1079fn format_decl(inst: &Instruction) -> String {
1080 let Some(reg) = inst.dest_register() else {
1081 return "dcl".to_string();
1082 };
1083 if reg.ty == RegisterType::Sampler {
1084 return format!(
1085 "dcl_{} {}",
1086 inst.decl_sampler_type().asm_name(),
1087 asm_register_name(reg)
1088 );
1089 }
1090 let mut sem = inst.decl_usage().semantic_prefix().to_ascii_lowercase();
1091 let idx = inst.decl_index();
1092 if idx != 0 || inst.decl_usage() == DeclUsage::TexCoord || inst.decl_usage() == DeclUsage::Color
1093 {
1094 sem.push_str(&idx.to_string());
1095 }
1096 format!("dcl_{} {}", sem, asm_register_name(reg))
1097}
1098
1099fn format_def(inst: &Instruction) -> String {
1100 let Some(reg) = inst.dest_register() else {
1101 return "def".to_string();
1102 };
1103 match inst.opcode {
1104 Opcode::Def => format!(
1105 "def {}, {:.9}, {:.9}, {:.9}, {:.9}",
1106 asm_register_name(reg),
1107 inst.get_float_param(1),
1108 inst.get_float_param(2),
1109 inst.get_float_param(3),
1110 inst.get_float_param(4)
1111 ),
1112 Opcode::DefI => format!(
1113 "defi {}, {}, {}, {}, {}",
1114 asm_register_name(reg),
1115 inst.get_int_param(1),
1116 inst.get_int_param(2),
1117 inst.get_int_param(3),
1118 inst.get_int_param(4)
1119 ),
1120 Opcode::DefB => format!("defb {}, {}", asm_register_name(reg), inst.get_int_param(1)),
1121 _ => String::new(),
1122 }
1123}
1124
1125pub fn format_instruction_asm(inst: &Instruction) -> Option<String> {
1126 match inst.opcode {
1127 Opcode::Comment => None,
1128 Opcode::End => Some("end".to_string()),
1129 Opcode::Dcl => Some(format_decl(inst)),
1130 Opcode::Def | Opcode::DefI | Opcode::DefB => Some(format_def(inst)),
1131 Opcode::Nop => Some("nop".to_string()),
1132 Opcode::If | Opcode::Rep | Opcode::Loop => Some(format!(
1133 "{} {}",
1134 inst.opcode.mnemonic(),
1135 format_source_param(inst, 0, 1)
1136 )),
1137 Opcode::IfC | Opcode::BreakC => Some(format!(
1138 "{} {}, {}",
1139 inst.opcode.mnemonic(),
1140 format_source_param(inst, 0, 4),
1141 format_source_param(inst, 1, 4)
1142 )),
1143 Opcode::Else
1144 | Opcode::EndIf
1145 | Opcode::Break
1146 | Opcode::EndLoop
1147 | Opcode::EndRep
1148 | Opcode::Ret
1149 | Opcode::Phase => Some(inst.opcode.mnemonic().to_string()),
1150 _ if inst.opcode.has_destination() => {
1151 let dest = format_dest_param(inst);
1152 let mut parts = vec![dest];
1153 let first_src = if inst.opcode == Opcode::Dcl { 2 } else { 1 };
1154 let count = mask_len(inst.dest_write_mask());
1155 for i in first_src..inst.params.len() {
1156 parts.push(format_source_param(inst, i, count));
1157 }
1158 Some(format!("{} {}", inst.opcode.mnemonic(), parts.join(", ")))
1159 }
1160 _ => Some(inst.opcode.mnemonic().to_string()),
1161 }
1162}
1163
1164pub fn disassemble(data: &[u8]) -> String {
1165 match parse_shader(data) {
1166 Ok(shader) => disassemble_model(&shader),
1167 Err(_) => String::new(),
1168 }
1169}
1170
1171pub fn disassemble_model(shader: &ShaderModel) -> String {
1172 let mut out = String::new();
1173 out.push_str(&format!(
1174 "{}_{}_{}\n",
1175 shader.kind.profile_prefix(),
1176 shader.major,
1177 shader.minor
1178 ));
1179 for inst in &shader.instructions {
1180 if let Some(line) = format_instruction_asm(inst) {
1181 out.push_str(&line);
1182 out.push('\n');
1183 }
1184 }
1185 out
1186}