예제 #1
0
        public void AsmMatchesFxc(string relPath)
        {
            string file = $"{ShaderDirectory}/{relPath}";
            // Arrange.
            var asmFileText = string.Join(Environment.NewLine,
                                          File.ReadAllLines(file + ".asm").Select(x => x.Trim()));

            asmFileText = TestUtils.StripDX9InstructionSlots(asmFileText);
            asmFileText = TestUtils.TrimLines(asmFileText);
            asmFileText = TestUtils.NormalizeAssembly(asmFileText);
            // Act.
            var bytecode = File.ReadAllBytes(file + ".o");
            var shader   = ShaderReader.ReadShader(bytecode);

            var decompiledAsm = AsmWriter.Disassemble(shader);

            File.WriteAllText($"{file}.d.asm", decompiledAsm);

            var decompiledAsmText = decompiledAsm;

            decompiledAsmText = TestUtils.StripDX9InstructionSlots(decompiledAsmText);
            decompiledAsmText = TestUtils.TrimLines(decompiledAsmText);
            decompiledAsmText = TestUtils.NormalizeAssembly(decompiledAsmText);

            File.WriteAllText($"{file}.d1.asm", asmFileText);
            File.WriteAllText($"{file}.d2.asm", decompiledAsmText);
            // Assert.
            Assert.That(decompiledAsmText, Is.EqualTo(asmFileText));
        }
예제 #2
0
        public void DecompileTest(string baseFilename)
        {
            string compiledShaderFilename = $"CompiledShaders{Path.DirectorySeparatorChar}{baseFilename}.fxc";
            string asmExpectedFilename    = $"ShaderAssembly{Path.DirectorySeparatorChar}{baseFilename}.asm";
            string hlslExpectedFilename   = $"ShaderSources{Path.DirectorySeparatorChar}{baseFilename}.fx";
            string asmOutputFilename      = $"{baseFilename}.asm";
            string hlslOutputFilename     = $"{baseFilename}.fx";

            ShaderModel shader;

            var inputStream = File.Open(compiledShaderFilename, FileMode.Open, FileAccess.Read);

            using (var input = new ShaderReader(inputStream, true))
            {
                shader = input.ReadShader();
            }

            var asmWriter = new AsmWriter(shader);

            asmWriter.Write(asmOutputFilename);

            var hlslWriter = new HlslWriter(shader, true);

            hlslWriter.Write(hlslOutputFilename);

            FileAssert.AreEqual(asmExpectedFilename, asmOutputFilename, "Assembly not equal");
            FileAssert.AreEqual(hlslExpectedFilename, hlslOutputFilename, "HLSL not equal");
        }
예제 #3
0
        public void Decompile(string relPath)
        {
            string file = $"{ShaderDirectory}/{relPath}";
            // Arrange.
            // Act.
            var bytecode = File.ReadAllBytes(file + ".o");
            var shader   = ShaderReader.ReadShader(bytecode);

            var    hlslWriter     = new HlslWriter(shader);
            string decompiledHlsl = "";

            using (var stream = new MemoryStream())
            {
                hlslWriter.Write(stream);
                stream.Position = 0;
                using (var reader = new StreamReader(stream, Encoding.UTF8))
                {
                    decompiledHlsl = reader.ReadToEnd();
                }
            }

            File.WriteAllText($"{file}.d.hlsl", decompiledHlsl);

            // Assert.
        }
예제 #4
0
        public void AsmMatchesFxc(string relPath)
        {
            string file = $"{ShaderDirectory}/{relPath}";
            // Arrange.
            var asmFileText = string.Join(Environment.NewLine,
                                          File.ReadAllLines(file + ".asm").Select(x => x.Trim()));

            asmFileText = TestUtils.NormalizeAssembly(asmFileText);
            // Act.
            var bytecode = File.ReadAllBytes(file + ".o");
            var shader   = ShaderReader.ReadShader(bytecode);

            var    asmWriter     = new AsmWriter(shader);
            string decompiledAsm = "";

            using (var stream = new MemoryStream())
            {
                asmWriter.Write(stream);
                stream.Position = 0;
                using (var reader = new StreamReader(stream, Encoding.UTF8))
                {
                    decompiledAsm = reader.ReadToEnd();
                }
            }
            var decompiledAsmText = string.Join(Environment.NewLine, decompiledAsm
                                                .Split(new[] { Environment.NewLine }, StringSplitOptions.None)
                                                .Select(x => x.Trim()));

            File.WriteAllText($"{file}.d.asm", decompiledAsm);

            // Assert.
            Assert.That(decompiledAsmText, Is.EqualTo(asmFileText));
        }
예제 #5
0
        public void RecompileShaders(string relPath)
        {
            string file = $"{ShaderDirectory}/{relPath}";
            // Arrange.
            var relDir = Path.GetDirectoryName(relPath);

            Directory.CreateDirectory($"{OutputDir}/{relDir}");
            var sourceName = GetSourceNameFromObject($"{ShaderDirectory}/{relPath}.o");

            if (ShaderDirectory != OutputDir)
            {
                File.Copy($"{ShaderDirectory}/{relDir}/{sourceName}", $"{OutputDir}/{relDir}/{sourceName}", true);
            }
            if (ShaderDirectory != OutputDir)
            {
                File.Copy($"{ShaderDirectory}/{relPath}.asm", $"{OutputDir}/{relPath}.asm", true);
            }

            var asmFileText = string.Join(Environment.NewLine,
                                          File.ReadAllLines(file + ".asm").Select(x => x.Trim()));

            // Act.
            var    binaryFileBytes = File.ReadAllBytes(file + ".o");
            var    shaderModel     = ShaderReader.ReadShader(binaryFileBytes);
            var    hlslWriter      = new HlslWriter(shaderModel);
            string decompiledHLSL  = "";

            using (var stream = new MemoryStream())
            {
                hlslWriter.Write(stream);
                stream.Position = 0;
                using (var reader = new StreamReader(stream, Encoding.UTF8))
                {
                    decompiledHLSL = reader.ReadToEnd();
                }
            }
            File.WriteAllText($"{OutputDir}/{relPath}.d.hlsl", decompiledHLSL);

            using (var shaderBytecode = ShaderBytecode.FromStream(new MemoryStream(binaryFileBytes)))
            {
                var profile = shaderModel.Type == DX9Shader.ShaderType.Pixel ?
                              $"ps_{shaderModel.MajorVersion}_{shaderModel.MinorVersion}" :
                              $"vs_{shaderModel.MajorVersion}_{shaderModel.MinorVersion}";
                var compiledShader = ShaderBytecode.Compile(decompiledHLSL, "main", profile);
                var disassembly    = shaderBytecode.Disassemble();
                var redisassembly  = compiledShader.Bytecode.Disassemble();
                File.WriteAllText($"{OutputDir}/{relPath}.d1.asm", disassembly);
                File.WriteAllText($"{OutputDir}/{relPath}.d2.asm", redisassembly);

                // Assert.
                Warn.If(disassembly, Is.EqualTo(redisassembly));
            }

            // Assert.
            Assert.Pass();
        }
예제 #6
0
        public static StateBlob Parse(BytecodeReader reader, BytecodeReader shaderReader)
        {
            var result = new StateBlob();

            result.TechniqueIndex    = shaderReader.ReadUInt32();
            result.PassIndex         = shaderReader.ReadUInt32();
            result.SamplerStateIndex = shaderReader.ReadUInt32();
            result.AssignmentIndex   = shaderReader.ReadUInt32();
            result.BlobType          = (StateBlobType)shaderReader.ReadUInt32();
            var dataReader = shaderReader.CopyAtCurrentPosition();
            var blobSize   = shaderReader.ReadUInt32();
            var paddedSize = blobSize + (blobSize % 4 == 0 ? 0 : 4 - blobSize % 4);
            //Seak ahead
            var data = shaderReader.ReadBytes((int)paddedSize);

            if (result.BlobType == StateBlobType.Shader)
            {
                result.Shader = ShaderReader.ReadShader(data);
            }
            else if (result.BlobType == StateBlobType.Variable)
            {
                result.VariableName = dataReader.TryReadString();
            }
            else if (result.BlobType == StateBlobType.IndexShader)
            {
                var _blobSize    = dataReader.ReadUInt32();
                var variableSize = dataReader.ReadUInt32();
                result.VariableName = dataReader.ReadString();
                if (variableSize > (result.VariableName.Length + 1))
                {
                    var paddingCount = variableSize - (result.VariableName.Length + 1);
                    var padding      = dataReader.ReadBytes((int)paddingCount);
                }
                result.Shader = result.Shader = ShaderModel.Parse(dataReader);
            }
            return(result);
        }
예제 #7
0
 private ShaderInfo DisassembleShader()
 {
     return(info = ShaderReader.DisassembleShader(ShaderByteCode, out dissassembly));
 }
예제 #8
0
        public void TestEffect9Constants()
        {
            var bytecode    = ShaderBytecode.Compile(@"
				struct T {
					float f;
					int i;
					int2 j;
					float4 v;
				};
				T t : register(vs, c5) : register(ps, c5) = {
					1.0f,
					2,
					int2(2, 3),
					float4(4, 5, 6, 7)
				};

				struct M {
					column_major float2x3 f;
					float g;
				} m : register(vs, c80) : register(ps, c80);

				struct U {
					row_major float2x3 f;
					T t[2];
					float g;
				};
				U u[3] : register(vs, c10) : register(ps, c10);

				float4 VS(float4 p : POSITION) : POSITION {
					// some random code which uses those constants
					// so they won't be optimized away.
					return p * 2 * t.i * u[2].g * m.g * u[0].t[1].v;
				}

				technique Tq {
					pass p0 {
						VertexShader = compile vs_2_0 VS();
					}
				}
			"            , "fx_2_0").Bytecode;
            var shaderModel = ShaderReader.ReadShader(bytecode);
            var constants   = from blob in shaderModel.EffectChunk.StateBlobLookup.Values
                              where blob.BlobType == DX9Shader.FX9.StateBlobType.Shader
                              from constdecl in blob.Shader.ConstantTable.ConstantDeclarations
                              select constdecl;
            // constant "t"
            var t = constants.First(c => c.Name == "t");

            Assert.AreEqual(5, t.RegisterIndex);

            var t_j = t.GetRegisterTypeByOffset(2);

            Assert.AreEqual(7, t_j.RegisterIndex);
            Assert.AreEqual(ParameterClass.Vector, t_j.Type.ParameterClass);
            Assert.AreEqual(ParameterType.Int, t_j.Type.ParameterType);
            var int2Name = t.GetMemberNameByOffset(2);

            Assert.AreEqual("t.j", int2Name);

            // constant "m"
            var m = constants.First(c => c.Name == "m");

            Assert.AreEqual(80, m.RegisterIndex);

            var m_f = m.GetRegisterTypeByOffset(2);

            Assert.AreEqual(80, m_f.RegisterIndex);
            Assert.AreEqual(ParameterClass.MatrixColumns, m_f.Type.ParameterClass);
            Assert.AreEqual("m.f", m.GetMemberNameByOffset(2));

            var m_g = m.GetRegisterTypeByOffset(3);

            Assert.AreEqual(83, m_g.RegisterIndex);
            Assert.AreEqual("m.g", m.GetMemberNameByOffset(3));

            // constant "u"
            var u = constants.First(c => c.Name == "u");

            Assert.AreEqual(10, u.RegisterIndex);

            var u_2_g = u.GetRegisterTypeByOffset(32);

            Assert.AreEqual(42, u_2_g.RegisterIndex);
            Assert.AreEqual(ParameterClass.Scalar, u_2_g.Type.ParameterClass);
            Assert.AreEqual("u[2].g", u.GetMemberNameByOffset(32));

            var u_2_t_1_v = u.GetRegisterTypeByOffset(31);

            Assert.AreEqual(41, u_2_t_1_v.RegisterIndex);
            Assert.AreEqual(ParameterClass.Vector, u_2_t_1_v.Type.ParameterClass);
            Assert.AreEqual(ParameterType.Float, u_2_t_1_v.Type.ParameterType);
            Assert.AreEqual(1, u_2_t_1_v.Type.Rows);
            Assert.AreEqual(4, u_2_t_1_v.Type.Columns);
            Assert.AreEqual("u[2].t[1].v", u.GetMemberNameByOffset(31));
        }
예제 #9
0
        public void TestSimpleVertexShaderWithConstants()
        {
            var bytecode    = ShaderBytecode.Compile(@"
				struct T {
					float f;
					float4 v;
				};

				struct U {
					row_major float2x3 f;
					T t[2];
					float g;
				};
				U u[3];

				float4 HakureiReimuAliceMargatroid;
				row_major float2x2 m;
				column_major float2x2 n;

				sampler Gensokyo[2];

				float4 VS(float4 p : POSITION) : POSITION {
					return p * HakureiReimuAliceMargatroid.w  * u[2].t[1].v * m[0].y * n[0].y;
				}

				float4 PS(float4 t : TEXCOORD) : COLOR {
					return tex2D(Gensokyo[0], t.xy) + tex2D(Gensokyo[1], t.zw);
				}

				technique Tq {
					pass p0 {
						VertexShader = compile vs_3_0 VS();
						PixelShader = compile ps_3_0 PS();
					}
				}
			"            , "fx_2_0").Bytecode;
            var shaderModel = ShaderReader.ReadShader(bytecode);
            var shaders     = from blob in shaderModel.EffectChunk.StateBlobLookup.Values
                              where blob.BlobType == DX9Shader.FX9.StateBlobType.Shader
                              select blob.Shader;

            string AstDecompile(ShaderModel shader) => new HlslWriter(shader, doAstAnalysis: true).Decompile();

            var testVertexShader       = shaders.First(s => s.Type == ShaderType.Vertex);
            var decompiledVertexShader = AstDecompile(testVertexShader);

            // here we use the `.w` component, to make sure `HakureiReimuAliceMargatroid.w`
            // is inside the actual decompiled shader too, not just inside constant declaration
            // (that is, `float4 HakureiReimuAliceMargatroid`).
            StringAssert.Contains("HakureiReimuAliceMargatroid.w", decompiledVertexShader);
            // assert that `u[2].t[1].v` appears inside the decompiled source code
            StringAssert.Contains("u[2].t[1].v", decompiledVertexShader);

            var testPixelShader       = shaders.First(s => s.Type == ShaderType.Pixel);
            var decompiledPixelShader = AstDecompile(testPixelShader);

            // assert that sampler array elements `Gensokyo[0]` and `[1]`
            // appears inside the decompile source code
            StringAssert.Contains("Gensokyo[0]", decompiledPixelShader);
            StringAssert.Contains("Gensokyo[1]", decompiledPixelShader);

            var fromNonAst = HlslWriter.Decompile(bytecode);

            StringAssert.Contains("HakureiReimuAliceMargatroid.w", fromNonAst);
            StringAssert.Contains("u[2].t[1].v", fromNonAst);
            StringAssert.Contains("Gensokyo[0]", fromNonAst);
            StringAssert.Contains("Gensokyo[1]", fromNonAst);
        }