using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Diagnostics; using Switch_Toolbox.Library; using Switch_Toolbox.Library.Forms; using Switch_Toolbox.Library.IO; using System.Windows.Forms; using FirstPlugin.Forms; namespace FirstPlugin { public class BNSH : TreeNodeFile, IFileFormat { public FileType FileType { get; set; } = FileType.Shader; public bool CanSave { get; set; } public string[] Description { get; set; } = new string[] { "Binary Shader" }; public string[] Extension { get; set; } = new string[] { "*.bnsh" }; public string FileName { get; set; } public string FilePath { get; set; } public IFileInfo IFileInfo { get; set; } public bool Identify(System.IO.Stream stream) { using (var reader = new Switch_Toolbox.Library.IO.FileReader(stream, true)) { return reader.CheckSignature(4, "BNSH"); } } public Type[] Types { get { List types = new List(); return types.ToArray(); } } public void Load(System.IO.Stream stream) { Header header = new Header(); header.Read(new FileReader(stream), this); Text = header.FileName + ".bnsh"; } public void Unload() { } public byte[] Save() { return null; } public enum ShaderType { Vertex, Geometry, Fragment, Compute } public class Header { public List ShaderVariations = new List(); public uint VersionMajor { get; set; } public uint VersionMajor2 { get; set; } public uint VersionMinor { get; set; } public uint VersionMinor2 { get; set; } public string FileName { get; set; } private void SetVersionInfo(uint Version) { VersionMajor = Version >> 24; VersionMajor2 = Version >> 16 & 0xFF; VersionMinor = Version >> 8 & 0xFF; VersionMinor2 = Version & 0xFF; } public void Read(FileReader reader, TreeNodeCustom node) { reader.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian; reader.ReadSignature(4, "BNSH"); uint padding = reader.ReadUInt32(); uint Version = reader.ReadUInt32(); SetVersionInfo(Version); ushort ByteOrderMark = reader.ReadUInt16(); byte Alignment = reader.ReadByte(); byte Target = reader.ReadByte(); FileName = reader.LoadString(false, typeof(uint), null, 0); uint PathOffset = reader.ReadUInt32(); uint RelocationTableOffset = reader.ReadUInt32(); uint FileSize = reader.ReadUInt32(); reader.Seek(0x40); //padding reader.ReadSignature(4, "grsc"); uint BlockOffset = reader.ReadUInt32(); ulong BlockSize = reader.ReadUInt64(); reader.Seek(0x0C); uint VariationCount = reader.ReadUInt32(); long VariationOffset = reader.ReadUInt32(); reader.Seek(VariationOffset, SeekOrigin.Begin); for (int i = 0; i < VariationCount; i++) { ShaderVariation var = new ShaderVariation(); var.Text = "Shader Variation" + i; var.Read(reader); ShaderVariations.Add(var); node.Nodes.Add(var); } reader.Close(); reader.Dispose(); } } public class ShaderVariation : TreeNodeCustom { public ShaderProgram shaderProgram; public void Read(FileReader reader) { long SourceProgramOffset = reader.ReadInt64(); long unk2 = reader.ReadInt64(); long ShaderProgramOffset = reader.ReadInt64(); long grscOffset = reader.ReadInt64(); //Points back to grsc reader.Seek(0x20); //padding using (reader.TemporarySeek(ShaderProgramOffset + SourceProgramOffset, SeekOrigin.Begin)) { shaderProgram = new ShaderProgram(); shaderProgram.Read(reader); if (shaderProgram.VertexShader != null) { shaderProgram.VertexShader.Text = "Vertex Shader"; Nodes.Add(shaderProgram.VertexShader); } if (shaderProgram.GeometryShader != null) { shaderProgram.GeometryShader.Text = "Geometry Shader"; Nodes.Add(shaderProgram.GeometryShader); } if (shaderProgram.FragmentShader != null) { shaderProgram.FragmentShader.Text = "Fragment Shader"; Nodes.Add(shaderProgram.FragmentShader); } if (shaderProgram.UnkShader != null) { shaderProgram.UnkShader.Text = "Unk Shader"; Nodes.Add(shaderProgram.UnkShader); } if (shaderProgram.Unk2Shader != null) { shaderProgram.Unk2Shader.Text = "Unk2 Shader"; Nodes.Add(shaderProgram.Unk2Shader); } if (shaderProgram.ComputeShader != null) { shaderProgram.ComputeShader.Text = "Compute Shader"; Nodes.Add(shaderProgram.ComputeShader); } } } } public class ShaderProgram { public ShaderData VertexShader { get; set; } public ShaderData UnkShader { get; set; } public ShaderData Unk2Shader { get; set; } public ShaderData GeometryShader { get; set; } public ShaderData FragmentShader { get; set; } public ShaderData ComputeShader { get; set; } public void Read(FileReader reader) { byte ShaderType = reader.ReadByte(); byte Format = reader.ReadByte(); reader.Seek(6); long VertexShaderOffset = reader.ReadInt64(); long UnkShaderOffset = reader.ReadInt64(); //Might be TessControl long Unk2ShaderOffset = reader.ReadInt64(); //Might be TessEvaluation long GeometryShaderOffset = reader.ReadInt64(); long FragmentShaderOffset = reader.ReadInt64(); long ComputeShaderOffset = reader.ReadInt64(); long pos = reader.Position; if (Format == 3) { if (VertexShaderOffset != 0) { reader.Seek(VertexShaderOffset, SeekOrigin.Begin); VertexShader = new ShaderSourceData(); VertexShader.shaderType = BNSH.ShaderType.Vertex; VertexShader.Format = Format; VertexShader.Read(reader); } if (FragmentShaderOffset != 0) { reader.Seek(FragmentShaderOffset, SeekOrigin.Begin); FragmentShader = new ShaderSourceData(); FragmentShader.shaderType = BNSH.ShaderType.Fragment; FragmentShader.Format = Format; FragmentShader.Read(reader); } } else { if (VertexShaderOffset != 0) { reader.Seek(VertexShaderOffset, SeekOrigin.Begin); VertexShader = new ShaderData(); VertexShader.shaderType = BNSH.ShaderType.Vertex; VertexShader.Format = Format; VertexShader.Read(reader); } if (UnkShaderOffset != 0) { reader.Seek(UnkShaderOffset, SeekOrigin.Begin); UnkShader = new ShaderData(); UnkShader.Read(reader); } if (Unk2ShaderOffset != 0) { reader.Seek(Unk2ShaderOffset, SeekOrigin.Begin); Unk2Shader = new ShaderData(); Unk2Shader.Read(reader); } if (GeometryShaderOffset != 0) { reader.Seek(GeometryShaderOffset, SeekOrigin.Begin); GeometryShader = new ShaderData(); GeometryShader.shaderType = BNSH.ShaderType.Geometry; GeometryShader.Format = Format; GeometryShader.Read(reader); } if (FragmentShaderOffset != 0) { reader.Seek(FragmentShaderOffset, SeekOrigin.Begin); FragmentShader = new ShaderData(); FragmentShader.shaderType = BNSH.ShaderType.Fragment; FragmentShader.Format = Format; FragmentShader.Read(reader); } if (ComputeShaderOffset != 0) { reader.Seek(ComputeShaderOffset, SeekOrigin.Begin); ComputeShader = new ShaderData(); ComputeShader.shaderType = BNSH.ShaderType.Compute; ComputeShader.Format = Format; ComputeShader.Read(reader); } } reader.Seek(pos, SeekOrigin.Begin); } } public class ShaderSourceData : ShaderData { public string[] Code; public override void Read(FileReader reader) { ushort CodeCount = reader.ReadUInt16(); reader.Seek(6); long ShaderSizeArray = reader.ReadInt64(); long ShaderOffsetArray = reader.ReadInt64(); reader.Seek(8); Console.WriteLine(ShaderOffsetArray); Console.WriteLine(ShaderSizeArray); long[] Offsets = new long[CodeCount]; uint[] Sizes = new uint[CodeCount]; Code = new string[CodeCount]; using (reader.TemporarySeek(ShaderOffsetArray, SeekOrigin.Begin)) { for (int i = 0; i < CodeCount; i++) Offsets[i] = reader.ReadInt64(); } using (reader.TemporarySeek(ShaderSizeArray, SeekOrigin.Begin)) { for (int i = 0; i < CodeCount; i++) Sizes[i] = reader.ReadUInt32(); } for (int i = 0; i < CodeCount; i++) { using (reader.TemporarySeek(Offsets[i], SeekOrigin.Begin)) { Code[i] = reader.ReadString((int)Sizes[i]); } } } public override void OnClick(TreeView treeview) { TextEditor editor = (TextEditor)LibraryGUI.Instance.GetActiveContent(typeof(TextEditor)); if (editor == null) { editor = new TextEditor(); editor.Dock = DockStyle.Fill; LibraryGUI.Instance.LoadEditor(editor); } editor.Text = Text; editor.FillEditor(string.Join("", Code)); } } public class ShaderData : TreeNodeCustom { public ShaderType shaderType; public List data = new List(); public int Format; public virtual void Read(FileReader reader) { reader.Seek(8); long ShaderOffset = reader.ReadInt64(); long ShaderOffset2 = reader.ReadInt64(); int ShaderSize = reader.ReadInt32(); int ShaderSize2 = reader.ReadInt32(); reader.Seek(0x20); using (reader.TemporarySeek(ShaderOffset, SeekOrigin.Begin)) data.Add(reader.ReadBytes(ShaderSize2)); using (reader.TemporarySeek(ShaderOffset2, SeekOrigin.Begin)) data.Add(reader.ReadBytes(ShaderSize)); ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export Shader0"); ContextMenu.MenuItems.Add(export); export.Click += ExportShader0; MenuItem export2 = new MenuItem("Export Shader1"); ContextMenu.MenuItems.Add(export2); export2.Click += ExportShader1; } private void ExportShader0(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.DefaultExt = "bin"; sfd.FileName = "shader0"; sfd.Filter = "Supported Formats|*.bin;"; if (sfd.ShowDialog() == DialogResult.OK) { File.WriteAllBytes(sfd.FileName, data[0]); } } private void ExportShader1(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.DefaultExt = "bin"; sfd.FileName = "shader1"; sfd.Filter = "Supported Formats|*.bin;"; if (sfd.ShowDialog() == DialogResult.OK) { File.WriteAllBytes(sfd.FileName, data[1]); } } public override void OnClick(TreeView treeview) { ShaderBinaryDisplay editor = (ShaderBinaryDisplay)LibraryGUI.Instance.GetActiveContent(typeof(ShaderBinaryDisplay)); if (editor == null) { editor = new ShaderBinaryDisplay(); editor.Dock = DockStyle.Fill; LibraryGUI.Instance.LoadEditor(editor); } int ShaderIndex = 0; editor.Text = Text; editor.FillEditor(Utils.CombineByteArray(data.ToArray()), DecompileShader(ShaderIndex)); } private string DecompileShader(int ShaderIndex = 1) { string TypeArg = "v"; switch (shaderType) { case ShaderType.Vertex: TypeArg = "v"; break; case ShaderType.Fragment: TypeArg = "f"; break; case ShaderType.Geometry: TypeArg = "g"; break; } if (!Directory.Exists("temp")) Directory.CreateDirectory("temp"); if (!Directory.Exists("ShaderTools")) Directory.CreateDirectory("ShaderTools"); // File.WriteAllBytes("temp/shader1.bin", Utils.CombineByteArray(data.ToArray())); File.WriteAllBytes("temp/shader1.bin", data[1]); if (!File.Exists($"{Runtime.ExecutableDir}/ShaderTools/Ryujinx.ShaderTools.exe")) { MessageBox.Show("No shader decompiler found in ShaderTools. If you want to decompile a shader, you can use Ryujinx's ShaderTools.exe and put in the ShaderTools folder of the toolbox."); return ""; } ProcessStartInfo start = new ProcessStartInfo(); start.FileName = "ShaderTools/Ryujinx.ShaderTools.exe"; start.WorkingDirectory = Runtime.ExecutableDir; start.CreateNoWindow = false; start.Arguments = $"{TypeArg} {Utils.AddQuotesIfRequired("temp/shader1.bin")}"; start.UseShellExecute = false; start.RedirectStandardOutput = true; start.CreateNoWindow = true; start.WindowStyle = ProcessWindowStyle.Hidden; using (Process process = Process.Start(start)) { using (StreamReader reader = process.StandardOutput) { try { return reader.ReadToEnd(); } catch { return ""; } } } // File.WriteAllBytes("temp/shader1.bin", Utils.CombineByteArray(data.ToArray())); return ""; } } } }