mirror of
https://github.com/KillzXGaming/Switch-Toolbox
synced 2024-11-27 15:01:06 +00:00
449 lines
17 KiB
C#
449 lines
17 KiB
C#
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<Type> types = new List<Type>();
|
|
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<ShaderVariation> ShaderVariations = new List<ShaderVariation>();
|
|
|
|
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<byte[]> data = new List<byte[]>();
|
|
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 "";
|
|
}
|
|
}
|
|
}
|
|
}
|