Skip to main content

siglus_cfx_decompiler/
disasm.rs

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}