public static void DecompileShader(string InputFile, string OutputFile, string MetadataFile = null) { // Create a copy of the file since it will be trashed if (!InputFile.Equals(OutputFile)) { File.Copy(InputFile, OutputFile, true); } var p = new System.Diagnostics.Process(); p.StartInfo.WorkingDirectory = "C:\\Users\\Administrator\\Desktop\\cmd_Decompiler-1.3.2\\"; p.StartInfo.FileName = "C:\\Users\\Administrator\\Desktop\\cmd_Decompiler-1.3.2\\cmd_Decompiler.exe"; p.StartInfo.Arguments = "-D " + OutputFile; p.StartInfo.RedirectStandardOutput = false; p.StartInfo.UseShellExecute = false; p.StartInfo.CreateNoWindow = true; p.Start(); // Gather metadata while it's being decompiled ShaderMetadata metadata = null; ShaderVariableMetadata variables = null; if (!string.IsNullOrEmpty(MetadataFile)) { metadata = new ShaderMetadata(MetadataFile); variables = new ShaderVariableMetadata(MetadataFile); } p.WaitForExit(); // Fix stupid naming quirk with 3dm (swap the files) string ext = Path.GetExtension(OutputFile); if (!ext.ToLower().Equals(".hlsl")) { string target = OutputFile.Replace(Path.GetExtension(OutputFile), ".hlsl"); File.Delete(OutputFile); File.Move(target, OutputFile); } if (metadata != null) { // Rip out 3dmigoto's header - it ends right before the "void main()" line List <string> fileLines = System.IO.File.ReadAllLines(OutputFile).ToList(); for (int i = 0; i < fileLines.Count; i++) { if (fileLines[i].ToLower().Contains("void main")) { break; } fileLines.Remove(fileLines[i]); i--; } // Now paste our header back into it List <string> replacedLines = new List <string>(metadata.GetTextData()); // Regenerate samplers and texture slots foreach (Tuple <int, string> sampler in metadata.GetSamplers()) { ReplaceInAllLines(fileLines, $"s{sampler.Item1}_s", $"{sampler.Item2}"); ReplaceInAllLines(fileLines, $"t{sampler.Item1}.", $"Tex{sampler.Item2}."); replacedLines.Add($"SamplerState {sampler.Item2} : register(s{sampler.Item1});"); replacedLines.Add($"Texture2D<float4> Tex{sampler.Item2} : register(t{sampler.Item1});"); } // Regenerate constant buffer variable accesses (TODO: inefficient as hell) for (int i = 0; i < fileLines.Count; i++) { } // Add back HLSL instructions replacedLines.Add(Environment.NewLine); replacedLines.AddRange(fileLines); // Done. Dump it back to disk. File.WriteAllLines(OutputFile, replacedLines); } }
public static bool ValidateShaderOfType(string Type, string HlslType, string OriginalFile, string SourcePath) { var metadata = new ShaderMetadata(OriginalFile.Replace(".bin", ".txt")); // Grab the technique along with each #define used var techniqueId = metadata.GetTechnique(); var macros = GetCompilationMacros(Type, HlslType, metadata.GetDefines().ToList()); //Program.LogLine("Validating shader [Technique: {0:X8}]: {1}...", techniqueId, OriginalFile); // Read from disk, compile, then disassemble to text ShaderBytecode originalBytecode = null; ShaderBytecode newBytecode = null; try { originalBytecode = RecompileShader3DMigoto(OriginalFile, HlslType).Strip(m_StripFlags); //originalBytecode = ShaderBytecode.FromFile(OriginalFile).Strip(m_StripFlags); newBytecode = CompileShaderOfType(SourcePath, HlslType, macros).Strip(m_StripFlags); } catch (InvalidProgramException e) { Program.Log($"{OriginalFile}\n{e.Message}"); return(false); } string[] originalDisasm = originalBytecode.Disassemble(DisassemblyFlags.None).Split('\n'); string[] newDisasm = newBytecode.Disassemble(DisassemblyFlags.None).Split('\n'); // Sometimes the newly generated output will be shorter than the original code. Add some padding // to prevent out-of-bounds array access. if (originalDisasm.Length > newDisasm.Length) { string[] newArray = new string[originalDisasm.Length]; for (int i = 0; i < newArray.Length; i++) { newArray[i] = "\n"; } newDisasm.CopyTo(newArray, 0); newDisasm = newArray; } try { ValidateShaderHeader(originalDisasm, newDisasm); ValidateShaderCode(originalDisasm, newDisasm); } catch (Exception) { //Program.LogLine("Validation failed."); // Dump raw disassembly to file File.WriteAllLines($"{Program.ShaderDiffDirectory}\\{Type}-{techniqueId:X}-{HlslType}-old.txt", originalDisasm); File.WriteAllLines($"{Program.ShaderDiffDirectory}\\{Type}-{techniqueId:X}-{HlslType}-new.txt", newDisasm); // // Generate the "symbolic" diff by: // // - Replacing all temporary registers with rX.xxxx // - Sorting all lines // - Eliminating all empty lines // var tempRegisterExpr = new Regex(@"r\d\.[xXyYzZwW]{1,4}", RegexOptions.Compiled); for (int i = 0; i < originalDisasm.Length; i++) { originalDisasm[i] = tempRegisterExpr.Replace(originalDisasm[i], "rX.xxxx"); newDisasm[i] = tempRegisterExpr.Replace(newDisasm[i], "rX.xxxx"); } File.WriteAllLines($"{Program.ShaderDiffDirectory}\\{Type}-{techniqueId:X}-{HlslType}-symbolic-old.txt", originalDisasm.Where(x => !string.IsNullOrWhiteSpace(x)).OrderBy(x => x)); File.WriteAllLines($"{Program.ShaderDiffDirectory}\\{Type}-{techniqueId:X}-{HlslType}-symbolic-new.txt", newDisasm.Where(x => !string.IsNullOrWhiteSpace(x)).OrderBy(x => x)); return(false); } return(true); }