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: &Self) -> u8 {
706 ((self.token >> 16) & 7) as u8
707 }
708
709 pub fn texld_controls(self: &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 { ty: RegisterType::from_param_token(t), number: (t & 0x7ff) as u16 })
724 }
725
726 pub fn dest_write_mask(&self) -> u8 {
727 let Some(t) = self.dest_token() else { return 0xf; };
728 let mask = ((t >> 16) & 0xf) as u8;
729 if mask == 0 { 0xf } else { mask }
730 }
731
732 pub fn dest_modifier(&self) -> ResultModifier {
733 ResultModifier::from_dest_token(self.dest_token().unwrap_or(0))
734 }
735
736 pub fn source_register(&self, param_index: usize) -> Option<RegisterKey> {
737 let t = *self.params.get(param_index)?;
738 Some(RegisterKey { ty: RegisterType::from_param_token(t), number: (t & 0x7ff) as u16 })
739 }
740
741 pub fn source_modifier(&self, param_index: usize) -> SourceModifier {
742 self.params.get(param_index).copied().map(SourceModifier::from_param_token).unwrap_or(SourceModifier::None)
743 }
744
745 pub fn source_swizzle(&self, param_index: usize) -> [usize; 4] {
746 let t = self.params.get(param_index).copied().unwrap_or(0);
747 let swz = ((t >> 16) & 0xff) as usize;
748 [swz & 3, (swz >> 2) & 3, (swz >> 4) & 3, (swz >> 6) & 3]
749 }
750
751 pub fn decl_usage(&self) -> DeclUsage {
752 self.params.first().copied().map(DeclUsage::from_decl_token).unwrap_or(DeclUsage::TexCoord)
753 }
754
755 pub fn decl_index(&self) -> u8 {
756 self.params.first().map(|v| ((v >> 16) & 0x0f) as u8).unwrap_or(0)
757 }
758
759 pub fn decl_sampler_type(&self) -> SamplerTextureType {
760 self.params.first().copied().map(SamplerTextureType::from_decl_token).unwrap_or(SamplerTextureType::Unknown)
761 }
762
763 pub fn get_float_param(&self, index: usize) -> f32 {
764 f32::from_bits(*self.params.get(index).unwrap_or(&0))
765 }
766
767 pub fn get_int_param(&self, index: usize) -> i32 {
768 *self.params.get(index).unwrap_or(&0) as i32
769 }
770}
771
772#[derive(Debug, Clone)]
773pub struct ShaderModel {
774 pub kind: ShaderKind,
775 pub major: u8,
776 pub minor: u8,
777 pub instructions: Vec<Instruction>,
778}
779
780#[derive(Debug, Clone)]
781pub enum ShaderParseError {
782 TooShort,
783 BadVersion(u32),
784 Truncated { offset: usize, need: usize, len: usize },
785 MissingEnd,
786}
787
788impl fmt::Display for ShaderParseError {
789 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
790 match self {
791 Self::TooShort => write!(f, "shader bytecode is too short"),
792 Self::BadVersion(v) => write!(f, "unsupported shader version token 0x{v:08x}"),
793 Self::Truncated { offset, need, len } => write!(f, "truncated shader token stream at 0x{offset:x}: need {need} bytes, len {len}"),
794 Self::MissingEnd => write!(f, "shader token stream has no END token"),
795 }
796 }
797}
798
799impl std::error::Error for ShaderParseError {}
800
801fn read_u32(data: &[u8], off: usize) -> Result<u32, ShaderParseError> {
802 let b = data.get(off..off + 4).ok_or(ShaderParseError::Truncated { offset: off, need: 4, len: data.len() })?;
803 Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
804}
805
806pub fn parse_shader(data: &[u8]) -> Result<ShaderModel, ShaderParseError> {
807 if data.len() < 4 {
808 return Err(ShaderParseError::TooShort);
809 }
810 let version = read_u32(data, 0)?;
811 let minor = (version & 0xff) as u8;
812 let major = ((version >> 8) & 0xff) as u8;
813 let kind = match version & 0xffff_0000 {
814 0xfffe_0000 => ShaderKind::Vertex,
815 0xffff_0000 => ShaderKind::Pixel,
816 _ => return Err(ShaderParseError::BadVersion(version)),
817 };
818
819 let mut instructions = Vec::new();
820 let mut off = 4usize;
821 while off + 4 <= data.len() {
822 let token = read_u32(data, off)?;
823 let opcode = Opcode::from_u16((token & 0xffff) as u16);
824 let param_count = if opcode == Opcode::Comment {
825 ((token >> 16) & 0x7fff) as usize
826 } else if major == 1 {
827 fixed_size_param_count(opcode)
828 } else {
829 ((token >> 24) & 0x0f) as usize
830 };
831 let params_start = off + 4;
832 let params_bytes = param_count.saturating_mul(4);
833 if params_start + params_bytes > data.len() {
834 return Err(ShaderParseError::Truncated { offset: off, need: 4 + params_bytes, len: data.len() });
835 }
836 let mut params = Vec::with_capacity(param_count);
837 for p in 0..param_count {
838 params.push(read_u32(data, params_start + p * 4)?);
839 }
840 instructions.push(Instruction { offset: off, token, opcode, params });
841 off = params_start + params_bytes;
842 if opcode == Opcode::End {
843 return Ok(ShaderModel { kind, major, minor, instructions });
844 }
845 }
846
847 Err(ShaderParseError::MissingEnd)
848}
849
850fn fixed_size_param_count(opcode: Opcode) -> usize {
851 match opcode {
852 Opcode::Comment => 0,
853 Opcode::Def => 5,
854 Opcode::TexCoord | Opcode::TexKill | Opcode::Tex | Opcode::TexBem | Opcode::TexBeml | Opcode::TexReg2AR | Opcode::TexReg2GB | Opcode::TexM3x2Pad | Opcode::TexM3x2Tex | Opcode::TexM3x3Pad | Opcode::TexM3x3Tex | Opcode::TexM3x3Diff | Opcode::TexM3x3Spec | Opcode::TexM3x3VSpec | Opcode::TexReg2RGB | Opcode::TexDP3Tex | Opcode::TexM3x2Depth | Opcode::TexDP3 | Opcode::TexM3x3 | Opcode::TexDepth => 2,
855 Opcode::Dcl => 2,
856 Opcode::End | Opcode::Nop | Opcode::Phase | Opcode::Ret | Opcode::Else | Opcode::EndIf | Opcode::EndLoop | Opcode::EndRep | Opcode::Break => 0,
857 _ if opcode.has_destination() => 1 + num_inputs(opcode),
858 _ => num_inputs(opcode),
859 }
860}
861
862pub fn num_inputs(opcode: Opcode) -> usize {
863 match opcode {
864 Opcode::Abs | Opcode::CallNZ | Opcode::Dsx | Opcode::Dsy | Opcode::Exp | Opcode::ExpP | Opcode::Frc | Opcode::Lit | Opcode::Log | Opcode::LogP | Opcode::Loop | Opcode::Mov | Opcode::MovA | Opcode::Nrm | Opcode::Rcp | Opcode::Rsq | Opcode::SinCos | Opcode::TexKill | Opcode::If | Opcode::Rep => 1,
865 Opcode::Add | Opcode::Bem | Opcode::Crs | Opcode::Dp3 | Opcode::Dp4 | Opcode::Dst | Opcode::M3x2 | Opcode::M3x3 | Opcode::M3x4 | Opcode::M4x3 | Opcode::M4x4 | Opcode::Max | Opcode::Min | Opcode::Mul | Opcode::Pow | Opcode::SetP | Opcode::Sge | Opcode::Slt | Opcode::Sub | Opcode::Tex | Opcode::TexLdd | Opcode::TexLdl | Opcode::BreakC | Opcode::IfC => 2,
866 Opcode::Cmp | Opcode::Cnd | Opcode::Dp2Add | Opcode::Lrp | Opcode::Mad | Opcode::Sgn => 3,
867 _ => 0,
868 }
869}
870
871pub fn mask_len(mask: u8) -> usize {
872 (0..4).filter(|i| (mask & (1 << i)) != 0).count().max(1)
873}
874
875pub fn mask_suffix(mask: u8) -> String {
876 let m = if mask == 0 { 0xf } else { mask };
877 if m == 0xf {
878 String::new()
879 } else {
880 let mut s = String::from(".");
881 for (i, c) in ['x', 'y', 'z', 'w'].iter().enumerate() {
882 if (m & (1 << i)) != 0 {
883 s.push(*c);
884 }
885 }
886 s
887 }
888}
889
890pub fn swizzle_suffix(swizzle: [usize; 4], count: usize) -> String {
891 let count = count.clamp(1, 4);
892 let identity = [0usize, 1, 2, 3];
893 if count == 4 && swizzle == identity {
894 return String::new();
895 }
896 if count < 4 && swizzle[..count] == identity[..count] {
897 return String::new();
898 }
899 let names = ['x', 'y', 'z', 'w'];
900 let mut s = String::from(".");
901 for i in 0..count {
902 s.push(names[swizzle[i]]);
903 }
904 s
905}
906
907pub fn asm_register_name(reg: RegisterKey) -> String {
908 match reg.ty {
909 RegisterType::RastOut => match reg.number {
910 0 => "oPos".to_string(),
911 1 => "oFog".to_string(),
912 2 => "oPts".to_string(),
913 _ => format!("o{}", reg.number),
914 },
915 RegisterType::DepthOut => "oDepth".to_string(),
916 RegisterType::MiscType => match reg.number {
917 0 => "vPos".to_string(),
918 1 => "vFace".to_string(),
919 _ => format!("vMisc{}", reg.number),
920 },
921 _ => format!("{}{}", reg.ty.asm_prefix(), reg.number),
922 }
923}
924
925fn format_source_param(inst: &Instruction, param_index: usize, count: usize) -> String {
926 let Some(reg) = inst.source_register(param_index) else { return "<?>".to_string(); };
927 let base = asm_register_name(reg);
928 let swz = swizzle_suffix(inst.source_swizzle(param_index), count);
929 let value = format!("{base}{swz}");
930 match inst.source_modifier(param_index) {
931 SourceModifier::None => value,
932 SourceModifier::Negate => format!("-{value}"),
933 SourceModifier::Abs => format!("abs({value})"),
934 SourceModifier::AbsAndNegate => format!("-abs({value})"),
935 SourceModifier::Bias => format!("{value}_bias"),
936 SourceModifier::BiasAndNegate => format!("-{value}_bias"),
937 SourceModifier::Sign => format!("{value}_bx2"),
938 SourceModifier::SignAndNegate => format!("-{value}_bx2"),
939 SourceModifier::Complement => format!("1-{value}"),
940 SourceModifier::X2 => format!("{value}_x2"),
941 SourceModifier::X2AndNegate => format!("-{value}_x2"),
942 SourceModifier::DivideByZ => format!("{value}_dz"),
943 SourceModifier::DivideByW => format!("{value}_dw"),
944 SourceModifier::Not => format!("!{value}"),
945 SourceModifier::Unknown(_) => value,
946 }
947}
948
949fn format_dest_param(inst: &Instruction) -> String {
950 let Some(reg) = inst.dest_register() else { return "<?>".to_string(); };
951 format!("{}{}", asm_register_name(reg), mask_suffix(inst.dest_write_mask()))
952}
953
954fn format_decl(inst: &Instruction) -> String {
955 let Some(reg) = inst.dest_register() else { return "dcl".to_string(); };
956 if reg.ty == RegisterType::Sampler {
957 return format!("dcl_{} {}", inst.decl_sampler_type().asm_name(), asm_register_name(reg));
958 }
959 let mut sem = inst.decl_usage().semantic_prefix().to_ascii_lowercase();
960 let idx = inst.decl_index();
961 if idx != 0 || inst.decl_usage() == DeclUsage::TexCoord || inst.decl_usage() == DeclUsage::Color {
962 sem.push_str(&idx.to_string());
963 }
964 format!("dcl_{} {}", sem, asm_register_name(reg))
965}
966
967fn format_def(inst: &Instruction) -> String {
968 let Some(reg) = inst.dest_register() else { return "def".to_string(); };
969 match inst.opcode {
970 Opcode::Def => format!(
971 "def {}, {:.9}, {:.9}, {:.9}, {:.9}",
972 asm_register_name(reg),
973 inst.get_float_param(1),
974 inst.get_float_param(2),
975 inst.get_float_param(3),
976 inst.get_float_param(4)
977 ),
978 Opcode::DefI => format!(
979 "defi {}, {}, {}, {}, {}",
980 asm_register_name(reg),
981 inst.get_int_param(1),
982 inst.get_int_param(2),
983 inst.get_int_param(3),
984 inst.get_int_param(4)
985 ),
986 Opcode::DefB => format!("defb {}, {}", asm_register_name(reg), inst.get_int_param(1)),
987 _ => String::new(),
988 }
989}
990
991pub fn format_instruction_asm(inst: &Instruction) -> Option<String> {
992 match inst.opcode {
993 Opcode::Comment => None,
994 Opcode::End => Some("end".to_string()),
995 Opcode::Dcl => Some(format_decl(inst)),
996 Opcode::Def | Opcode::DefI | Opcode::DefB => Some(format_def(inst)),
997 Opcode::Nop => Some("nop".to_string()),
998 Opcode::If | Opcode::Rep | Opcode::Loop => Some(format!("{} {}", inst.opcode.mnemonic(), format_source_param(inst, 0, 1))),
999 Opcode::IfC | Opcode::BreakC => Some(format!("{} {}, {}", inst.opcode.mnemonic(), format_source_param(inst, 0, 4), format_source_param(inst, 1, 4))),
1000 Opcode::Else | Opcode::EndIf | Opcode::Break | Opcode::EndLoop | Opcode::EndRep | Opcode::Ret | Opcode::Phase => Some(inst.opcode.mnemonic().to_string()),
1001 _ if inst.opcode.has_destination() => {
1002 let dest = format_dest_param(inst);
1003 let mut parts = vec![dest];
1004 let first_src = if inst.opcode == Opcode::Dcl { 2 } else { 1 };
1005 let count = mask_len(inst.dest_write_mask());
1006 for i in first_src..inst.params.len() {
1007 parts.push(format_source_param(inst, i, count));
1008 }
1009 Some(format!("{} {}", inst.opcode.mnemonic(), parts.join(", ")))
1010 }
1011 _ => Some(inst.opcode.mnemonic().to_string()),
1012 }
1013}
1014
1015pub fn disassemble(data: &[u8]) -> String {
1016 match parse_shader(data) {
1017 Ok(shader) => disassemble_model(&shader),
1018 Err(_) => String::new(),
1019 }
1020}
1021
1022pub fn disassemble_model(shader: &ShaderModel) -> String {
1023 let mut out = String::new();
1024 out.push_str(&format!("{}_{}_{}\n", shader.kind.profile_prefix(), shader.major, shader.minor));
1025 for inst in &shader.instructions {
1026 if let Some(line) = format_instruction_asm(inst) {
1027 out.push_str(&line);
1028 out.push('\n');
1029 }
1030 }
1031 out
1032}