diff --git a/Switch_FileFormatsMain/FileFormats/BFRES.cs b/Switch_FileFormatsMain/FileFormats/BFRES.cs new file mode 100644 index 00000000..918ef34f --- /dev/null +++ b/Switch_FileFormatsMain/FileFormats/BFRES.cs @@ -0,0 +1,334 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Threading.Tasks; +using Switch_Toolbox; +using System.Windows.Forms; +using Switch_Toolbox.Library; +using Switch_Toolbox.Library.Forms; +using Switch_Toolbox.Library.IO; +using Bfres.Structs; + +namespace FirstPlugin +{ + public class BFRES : IFileFormat + { + public bool CanSave { get; set; } = false; + public bool FileIsEdited { get; set; } = false; + public bool FileIsCompressed { get; set; } = false; + public string[] Description { get; set; } = new string[] { "BFRES", "*BFRES", "*BFRES" }; + public string[] Extension { get; set; } = new string[] { "*.bfres", "*.sbfres" }; + public string Magic { get; set; } = "FRES"; + public CompressionType CompressionType { get; set; } = CompressionType.None; + public byte[] Data { get; set; } + public string FileName { get; set; } + public IFileInfo IFileInfo { get; set; } + + public Type[] Types + { + get + { + List types = new List(); + return types.ToArray(); + } + } + + private TreeNodeFile eitorRoot; + public TreeNodeFile EditorRoot + { + get + { + return eitorRoot; + } + set + { + this.eitorRoot = value; + } + } + private void SaveFile() + { + List formats = new List(); + formats.Add(this); + + SaveFileDialog sfd = new SaveFileDialog(); + sfd.Filter = Utils.GetAllFilters(formats); + sfd.FileName = FileName; + + if (sfd.ShowDialog() == DialogResult.OK) + { + Cursor.Current = Cursors.WaitCursor; + SaveCompressFile(Save(), sfd.FileName, Alignment); + } + } + private void SaveCompressFile(byte[] data, string FileName, int Alignment = 0, bool EnableDialog = true) + { + if (EnableDialog && CompressionType != CompressionType.None) + { + DialogResult save = MessageBox.Show($"Compress file as {CompressionType}?", "File Save", MessageBoxButtons.YesNo); + + if (save == DialogResult.Yes) + { + switch (CompressionType) + { + case CompressionType.Yaz0: + data = EveryFileExplorer.YAZ0.Compress(data, Runtime.Yaz0CompressionLevel, (uint)Alignment); + break; + case CompressionType.Lz4f: + data = STLibraryCompression.Type_LZ4F.Compress(data); + break; + case CompressionType.Lz4: + break; + } + } + } + File.WriteAllBytes(FileName, data); + MessageBox.Show($"File has been saved to {FileName}"); + Cursor.Current = Cursors.Default; + } + + public bool IsActive { get; set; } = false; + public bool UseEditMenu { get; set; } = false; + public int Alignment { get; set; } = 0; + public string FilePath { get; set; } + public static bool IsWiiU = false; + + BFRESRender bfres; + public void Load() + { + IsActive = true; + CanSave = true; + + using (FileReader reader = new FileReader(new MemoryStream(Data))) + { + reader.Seek(4); + if (reader.ReadInt32() != 0x20202020) + { + IsWiiU = true; + } + reader.Close(); + } + + bfres = new BFRESRender(); + bfres.ResFileNode = new ResourceFile(this); + bfres.ResFileNode.BFRESRender = bfres; + bfres.SaveFile = SaveFile; + + EditorRoot = bfres.ResFileNode; + + if (IsWiiU) + { + bfres.LoadFile(new Syroot.NintenTools.Bfres.ResFile(new System.IO.MemoryStream(Data))); + } + else + { + bfres.LoadFile(new Syroot.NintenTools.NSW.Bfres.ResFile(new System.IO.MemoryStream(Data))); + } + + Runtime.abstractGlDrawables.Add(bfres); + } + public void Unload() + { + bfres.Destroy(); + bfres.ResFileNode.Nodes.Clear(); + } + + public byte[] Save() + { + MemoryStream mem = new MemoryStream(); + + if (IsWiiU) + SaveWiiU(mem); + else + SaveSwitch(mem); + + return mem.ToArray(); + } + private void SaveSwitch(MemoryStream mem) + { + int CurMdl = 0; + foreach (FMDL model in bfres.models) + { + bfres.resFile.Models[CurMdl].Shapes.Clear(); + bfres.resFile.Models[CurMdl].VertexBuffers.Clear(); + bfres.resFile.Models[CurMdl].Materials.Clear(); + bfres.resFile.Models[CurMdl].MaterialDict.Clear(); + + int i = 0; + var duplicates = model.shapes.GroupBy(c => c.Text).Where(g => g.Skip(1).Any()).SelectMany(c => c); + foreach (var shape in duplicates) + shape.Text += i++; + + foreach (FSHP shape in model.shapes) + { + CheckMissingTextures(shape); + BfresSwitch.SetShape(shape, shape.Shape); + + bfres.resFile.Models[CurMdl].Shapes.Add(shape.Shape); + bfres.resFile.Models[CurMdl].VertexBuffers.Add(shape.VertexBuffer); + + SetShaderAssignAttributes(shape.GetMaterial().shaderassign, shape); + } + foreach (FMAT mat in model.materials.Values) + { + BfresSwitch.SetMaterial(mat, mat.Material); + bfres.resFile.Models[CurMdl].Materials.Add(mat.Material); + } + CurMdl++; + } + bfres.resFile.SkeletalAnims.Clear(); + if (EditorRoot.Nodes.ContainsKey("FSKA")) + { + foreach (BfresSkeletonAnim ska in EditorRoot.Nodes["FSKA"].Nodes) + { + bfres.resFile.SkeletalAnims.Add(ska.SkeletalAnim); + } + } + + ErrorCheck(); + + BfresSwitch.WriteExternalFiles(bfres.resFile, EditorRoot); + bfres.resFile.Save(mem); + } + private void SaveWiiU(MemoryStream mem) + { + bfres.resFileU.Save(mem); + + int CurMdl = 0; + foreach (FMDL model in bfres.models) + { + bfres.resFileU.Models[CurMdl].Shapes.Clear(); + bfres.resFileU.Models[CurMdl].VertexBuffers.Clear(); + bfres.resFileU.Models[CurMdl].Materials.Clear(); + + int i = 0; + var duplicates = model.shapes.GroupBy(c => c.Text).Where(g => g.Skip(1).Any()).SelectMany(c => c); + foreach (var shape in duplicates) + shape.Text += i++; + + foreach (FSHP shape in model.shapes) + { + CheckMissingTextures(shape); + BfresWiiU.SetShape(shape, shape.ShapeU); + + bfres.resFileU.Models[CurMdl].Shapes.Add(shape.Text, shape.ShapeU); + bfres.resFileU.Models[CurMdl].VertexBuffers.Add(shape.VertexBufferU); + + SetShaderAssignAttributes(shape.GetMaterial().shaderassign, shape); + } + foreach (FMAT mat in model.materials.Values) + { + BfresWiiU.SetMaterial(mat, mat.MaterialU); + bfres.resFileU.Models[CurMdl].Materials.Add(mat.Text, mat.MaterialU); + } + CurMdl++; + } + bfres.resFile.SkeletalAnims.Clear(); + if (EditorRoot.Nodes.ContainsKey("FSKA")) + { + foreach (BfresSkeletonAnim ska in EditorRoot.Nodes["FSKA"].Nodes) + { + bfres.resFile.SkeletalAnims.Add(ska.SkeletalAnim); + } + } + } + + private void SetShaderAssignAttributes(FMAT.ShaderAssign shd, FSHP shape) + { + foreach (var att in shape.vertexAttributes) + { + if (!shd.attributes.ContainsValue(att.Name) && !shd.attributes.ContainsKey(att.Name)) + shd.attributes.Add(att.Name, att.Name); + } + foreach (var tex in shape.GetMaterial().textures) + { + if (!shd.samplers.ContainsValue(tex.SamplerName)) + shd.attributes.Add(tex.SamplerName, tex.SamplerName); + } + } + + + private void SetDuplicateShapeName(FSHP shape) + { + DialogResult dialogResult = MessageBox.Show($"A shape {shape.Text} already exists with that name", "", MessageBoxButtons.OK); + + if (dialogResult == DialogResult.OK) + { + RenameDialog renameDialog = new RenameDialog(); + renameDialog.Text = "Rename Texture"; + if (renameDialog.ShowDialog() == DialogResult.OK) + { + shape.Text = renameDialog.textBox1.Text; + } + } + } + + bool ImportMissingTextures = false; + private void CheckMissingTextures(FSHP shape) + { + foreach (BinaryTextureContainer bntx in PluginRuntime.bntxContainers) + { + foreach (MatTexture tex in shape.GetMaterial().textures) + { + if (!bntx.Textures.ContainsKey(tex.Name)) + { + if (!ImportMissingTextures) + { + DialogResult result = MessageBox.Show("Missing textures found! Would you like to use placeholders?", "", MessageBoxButtons.YesNo, MessageBoxIcon.Question); + + if (result == DialogResult.Yes) + { + ImportMissingTextures = true; + } + else + { + return; + } + } + + if (ImportMissingTextures) + bntx.ImportPlaceholderTexture(tex.Name); + } + } + } + } + + public void ErrorCheck() + { + if (bfres != null) + { + List Errors = new List(); + foreach (FMDL model in bfres.models) + { + foreach (FSHP shp in model.shapes) + { + Syroot.NintenTools.NSW.Bfres.VertexBuffer vtx = shp.VertexBuffer; + Syroot.NintenTools.NSW.Bfres.Material mat = shp.GetMaterial().Material; + Syroot.NintenTools.NSW.Bfres.ShaderAssign shdr = mat.ShaderAssign; + + for (int att = 0; att < vtx.Attributes.Count; att++) + { + if (!shdr.AttribAssigns.Contains(vtx.Attributes[att].Name)) + MessageBox.Show($"Error! Attribute {vtx.Attributes[att].Name} is unlinked!"); + } + for (int att = 0; att < mat.TextureRefs.Count; att++) + { + if (!shdr.SamplerAssigns.Contains(mat.SamplerDict.GetKey(att))) //mat.SamplerDict[att] + MessageBox.Show($"Error! Sampler {mat.SamplerDict.GetKey(att)} is unlinked!"); + } + } + } + // ErrorList errorList = new ErrorList(); + // errorList.LoadList(Errors); + // errorList.Show(); + } + } + public class Errors + { + public string Section = "None"; + public string Section2 = "None"; + public string Message = ""; + public string Type = "Unkown"; + } + } +} diff --git a/Switch_FileFormatsMain/FileFormats/BYAML.cs b/Switch_FileFormatsMain/FileFormats/BYAML.cs new file mode 100644 index 00000000..e2fda8d1 --- /dev/null +++ b/Switch_FileFormatsMain/FileFormats/BYAML.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Switch_Toolbox; +using System.Windows.Forms; +using Switch_Toolbox.Library; +using ByamlExt.Byaml; +using ByamlExt; +using WeifenLuo.WinFormsUI.Docking; + +namespace FirstPlugin +{ + public class BYAML : IFileFormat + { + public bool CanSave { get; set; } = false; + public bool FileIsEdited { get; set; } = false; + public bool FileIsCompressed { get; set; } = false; + public string[] Description { get; set; } = new string[] { "BYAML" }; + public string[] Extension { get; set; } = new string[] { "*.byaml", "*.byml", "*.bprm", "*.sbyml" }; + public string Magic { get; set; } = "YB"; + public CompressionType CompressionType { get; set; } = CompressionType.None; + public byte[] Data { get; set; } + public string FileName { get; set; } + public TreeNodeFile EditorRoot { get; set; } + public bool IsActive { get; set; } = false; + public bool UseEditMenu { get; set; } = false; + public string FilePath { get; set; } + public IFileInfo IFileInfo { get; set; } + public Type[] Types + { + get + { + List types = new List(); + return types.ToArray(); + } + } + + class EditableNode + { + public Type type { get => Node[Index].GetType(); } + dynamic Node; + dynamic Index; + + public dynamic Get() => Node[Index]; + public void Set(dynamic value) => Node[Index] = value; + public string GetTreeViewString() + { + if (Index is int) + return Node[Index].ToString(); + else + return Index + " : " + Node[Index].ToString(); + } + + public EditableNode(dynamic _node, dynamic _index) + { + Node = _node; + Index = _index; + } + } + + public void Load() + { + IsActive = false; + CanSave = false; + + // ByamlViewer.OpenByml(new System.IO.MemoryStream(Data), FileName); + + + // BymlFileData byamlFile = ByamlFile.LoadN(new System.IO.MemoryStream(Data), false, Syroot.BinaryData.ByteOrder.LittleEndian); + // EditorRoot = LoadByamlNodes(byamlFile.RootNode); + + // LoadDockedEditor(byamlFile); + } + public void Unload() + { + + } + + ByamlEditor ByamlEditor; + + public void LoadDockedEditor(BymlFileData byamlFile) + { + foreach (Control control in FirstPlugin.MainF.Controls) + { + if (control is DockPanel) + { + ByamlEditor = new ByamlEditor(); + ByamlEditor.Dock = DockStyle.Fill; + ByamlEditor.Show(((DockPanel)control), DockState.Document); + ByamlEditor.LoadByaml(byamlFile); + } + } + } + + public TreeNode LoadByamlNodes(dynamic root) + { + TreeNode node = new TreeNode(); + + if (root == null) + return node; + if (root is Dictionary) + { + parseDictNode(root, node.Nodes); + } + else if (root is List) + { + if (((List)root).Count == 0) + { + MessageBox.Show("This byml is empty"); + } + parseArrayNode(root, node.Nodes); + } + else if (root is List) + { + + } + + return node; + } + + void parseArrayNode(IList list, TreeNodeCollection addto) + { + int index = 0; + foreach (dynamic k in list) + { + if (k is IDictionary) + { + TreeNode current = addto.Add(""); + current.Tag = ((IDictionary)k); + current.Nodes.Add("✯✯dummy✯✯"); + } + else if (k is IList) + { + TreeNode current = addto.Add(""); + current.Tag = ((IList)k); + current.Nodes.Add("✯✯dummy✯✯"); + } + else if (k is IList) + { + TreeNode current = addto.Add(""); + current.Tag = ((IList)k); + parsePathPointArray(k, current.Nodes); + } + else + { + var n = addto.Add(k == null ? "" : k.ToString()); + if (k != null) n.Tag = new EditableNode(list, index); + } + index++; + } + } + + void parseDictNode(IDictionary node, TreeNodeCollection addto) + { + foreach (string k in node.Keys) + { + TreeNode current = addto.Add(k); + if (node[k] is IDictionary) + { + current.Text += " : "; + current.Tag = node[k]; + current.Nodes.Add("✯✯dummy✯✯"); //a text that can't be in a byml + } + else if (node[k] is IList) + { + current.Text += " : "; + current.Tag = ((IList)node[k]); + current.Nodes.Add("✯✯dummy✯✯"); + } + else if (node[k] is IList) + { + current.Text += " : "; + current.Tag = ((IList)node[k]); + parsePathPointArray(node[k], current.Nodes); + } + else + { + current.Text = current.Text + " : " + (node[k] == null ? "" : node[k].ToString()); + if (node[k] != null) current.Tag = new EditableNode(node, k); + } + } + } + void parsePathPointArray(IList list, TreeNodeCollection addto) + { + int index = 0; + foreach (var k in list) + { + index++; + var n = addto.Add(k == null ? "" : k.ToString()); + if (k != null) n.Tag = new EditableNode(list, index); + } + } + + public byte[] Save() + { + return null; + } + } +} diff --git a/Switch_FileFormatsMain/FileFormats/KCL.cs b/Switch_FileFormatsMain/FileFormats/KCL.cs index 132b6a82..907d560b 100644 --- a/Switch_FileFormatsMain/FileFormats/KCL.cs +++ b/Switch_FileFormatsMain/FileFormats/KCL.cs @@ -1,12 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Switch_Toolbox; +using System.IO; using System.Windows.Forms; using Switch_Toolbox.Library; -using KCLExt; using SFGraphics.GLObjects.Shaders; using Smash_Forge.Rendering; using GL_Core.Interfaces; @@ -15,6 +11,8 @@ using OpenTK; using Switch_Toolbox.Library.Rendering; using WeifenLuo.WinFormsUI.Docking; using GL_Core; +using System.Drawing; +using Switch_Toolbox.Library.IO; namespace FirstPlugin { @@ -29,11 +27,12 @@ namespace FirstPlugin public CompressionType CompressionType { get; set; } = CompressionType.None; public byte[] Data { get; set; } public string FileName { get; set; } - public TreeNode EditorRoot { get; set; } + public TreeNodeFile EditorRoot { get; set; } public bool IsActive { get; set; } = false; public bool UseEditMenu { get; set; } = false; - public int Alignment { get; set; } = 0; public string FilePath { get; set; } + public IFileInfo IFileInfo { get; set; } + public Type[] Types { get @@ -46,7 +45,8 @@ namespace FirstPlugin public void Load() { IsActive = true; - EditorRoot = new KCLRoot(FileName); + EditorRoot = new KCLRoot(FileName, this); + IFileInfo = new IFileInfo(); } public void Unload() { @@ -54,7 +54,34 @@ namespace FirstPlugin } public byte[] Save() { - return null; + KCLRoot root = (KCLRoot)EditorRoot; + return root.kcl.Write(Syroot.BinaryData.ByteOrder.LittleEndian); + } + + private static void SaveCompressFile(byte[] data, string FileName, CompressionType CompressionType, int Alignment = 0, bool EnableDialog = true) + { + if (EnableDialog && CompressionType != CompressionType.None) + { + DialogResult save = MessageBox.Show($"Compress file as {CompressionType}?", "File Save", MessageBoxButtons.YesNo); + + if (save == DialogResult.Yes) + { + switch (CompressionType) + { + case CompressionType.Yaz0: + data = EveryFileExplorer.YAZ0.Compress(data, Runtime.Yaz0CompressionLevel, (uint)Alignment); + break; + case CompressionType.Lz4f: + data = STLibraryCompression.Type_LZ4F.Compress(data); + break; + case CompressionType.Lz4: + break; + } + } + } + File.WriteAllBytes(FileName, data); + MessageBox.Show($"File has been saved to {FileName}"); + Cursor.Current = Cursors.Default; } public enum GameSet : ushort @@ -117,23 +144,152 @@ namespace FirstPlugin BoostTrick = 8202, } - public static Shader shader = null; - - public class KCLRoot : TreeNodeCustom + public class KCLRoot : TreeNodeFile { - public KCLRoot(string Name) + public KCLRoot(string Name, IFileFormat handler) { Text = Name; + FileHandler = handler; + Renderer = new KCLRendering(); + Read(handler.Data); + + ContextMenu = new ContextMenu(); + MenuItem save = new MenuItem("Save"); + ContextMenu.MenuItems.Add(save); + save.Click += Save; + MenuItem export = new MenuItem("Export"); + ContextMenu.MenuItems.Add(export); + export.Click += Export; + MenuItem replace = new MenuItem("Replace"); + ContextMenu.MenuItems.Add(replace); + replace.Click += Replace; + } + public void Save(object sender, EventArgs args) + { + SaveFileDialog sfd = new SaveFileDialog(); + sfd.Filter = "Supported Formats|*.kcl"; + sfd.FileName = Text; + sfd.DefaultExt = ".kcl"; + + if (sfd.ShowDialog() == DialogResult.OK) + { + + int Alignment = FileHandler.IFileInfo.Alignment; + SaveCompressFile(FileHandler.Save(), sfd.FileName, FileHandler.CompressionType, Alignment); + } + } + public void Export(object sender, EventArgs args) + { + if (kcl == null) + return; + + SaveFileDialog sfd = new SaveFileDialog(); + sfd.Filter = "Supported Formats|*.obj"; + sfd.FileName = Text; + sfd.DefaultExt = ".obj"; + + if (sfd.ShowDialog() == DialogResult.OK) + { + kcl.ToOBJ().toWritableObj().WriteObj(sfd.FileName + ".obj"); + } + } + public void Replace(object sender, EventArgs args) + { + OpenFileDialog ofd = new OpenFileDialog(); + ofd.Filter = "Supported Formats|*.obj"; + + if (ofd.ShowDialog() == DialogResult.OK) + { + var mod = EditorCore.Common.OBJ.Read(new MemoryStream(File.ReadAllBytes(ofd.FileName)), null); + if (mod.Faces.Count > 65535) + { + MessageBox.Show("this model has too many faces, only models with less than 65535 triangles can be converted"); + return; + } + kcl = MarioKart.MK7.KCL.FromOBJ(mod); + Read(kcl.Write(Syroot.BinaryData.ByteOrder.LittleEndian)); + } } KCLRendering Renderer; public override void OnClick(TreeView treeView) { - //If has models - if (Nodes[0].Nodes.Count > 0) + Renderer.LoadViewport(); + Renderer.UpdateVertexData(); + } + + public MarioKart.MK7.KCL kcl = null; + public void Read(byte[] file_data) + { + try { - Renderer.LoadViewport(); - Renderer.UpdateVertexData(); + kcl = new MarioKart.MK7.KCL(file_data, Syroot.BinaryData.ByteOrder.LittleEndian); + } + catch + { + kcl = new MarioKart.MK7.KCL(file_data, Syroot.BinaryData.ByteOrder.BigEndian); + } + Read(kcl); + Renderer.UpdateVertexData(); + } + public void Read(MarioKart.MK7.KCL kcl) + { + Nodes.Clear(); + Renderer.models.Clear(); + + int CurModelIndx = 0; + foreach (MarioKart.MK7.KCL.KCLModel mdl in kcl.Models) + { + KCLModel kclmodel = new KCLModel(); + + kclmodel.Text = "Model " + CurModelIndx; + + int ft = 0; + foreach (var plane in mdl.Planes) + { + var triangle = mdl.GetTriangle(plane); + var normal = triangle.Normal; + var pointA = triangle.PointA; + var pointB = triangle.PointB; + var pointC = triangle.PointC; + + Vertex vtx = new Vertex(); + Vertex vtx2 = new Vertex(); + Vertex vtx3 = new Vertex(); + + vtx.pos = new Vector3(Vec3D_To_Vec3(pointA)); + vtx2.pos = new Vector3(Vec3D_To_Vec3(pointB)); + vtx3.pos = new Vector3(Vec3D_To_Vec3(pointC)); + vtx.nrm = new Vector3(Vec3D_To_Vec3(normal)); + vtx2.nrm = new Vector3(Vec3D_To_Vec3(normal)); + vtx3.nrm = new Vector3(Vec3D_To_Vec3(normal)); + + KCLModel.Face face = new KCLModel.Face(); + face.Text = triangle.Collision.ToString(); + face.MaterialFlag = triangle.Collision; + + var col = MarioKart.MK7.KCLColors.GetMaterialColor(plane.CollisionType); + Vector3 ColorSet = new Vector3(col.R, col.G, col.B); + + vtx.col = new Vector4(ColorSet, 1); + vtx2.col = new Vector4(ColorSet, 1); + vtx3.col = new Vector4(ColorSet, 1); + + kclmodel.faces.Add(ft); + kclmodel.faces.Add(ft + 1); + kclmodel.faces.Add(ft + 2); + + ft += 3; + + kclmodel.vertices.Add(vtx); + kclmodel.vertices.Add(vtx2); + kclmodel.vertices.Add(vtx3); + } + + Renderer.models.Add(kclmodel); + Nodes.Add(kclmodel); + + CurModelIndx++; } } } @@ -147,15 +303,25 @@ namespace FirstPlugin //Set the game's material list public GameSet GameMaterialSet = GameSet.MarioKart8D; public List models = new List(); + public Shader shader = null; - public KCLRendering() + private void GenerateBuffers() { GL.GenBuffers(1, out vbo_position); GL.GenBuffers(1, out ibo_elements); } + public void Destroy() + { + GL.DeleteBuffer(vbo_position); + GL.DeleteBuffer(ibo_elements); + } + public void UpdateVertexData() { + if (OpenTKSharedResources.SetupStatus == OpenTKSharedResources.SharedResourceStatus.Unitialized) + return; + DisplayVertex[] Vertices; int[] Faces; @@ -169,8 +335,6 @@ namespace FirstPlugin List pv = m.CreateDisplayVertices(); Vs.AddRange(pv); - Console.WriteLine(m.displayFaceSize); - for (int i = 0; i < m.displayFaceSize; i++) { Ds.Add(m.display[i] + voffset); @@ -189,6 +353,8 @@ namespace FirstPlugin GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo_elements); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(Faces.Length * sizeof(int)), Faces, BufferUsageHint.StaticDraw); + + Viewport.Instance.UpdateViewport(); } string FileName; @@ -226,9 +392,16 @@ namespace FirstPlugin } public override void Draw(GL_ControlModern control) { - shader = OpenTKSharedResources.shaders["KCL"]; + bool buffersWereInitialized = ibo_elements != 0 && vbo_position != 0; + if (!buffersWereInitialized) + GenerateBuffers(); + if (OpenTKSharedResources.SetupStatus == OpenTKSharedResources.SharedResourceStatus.Unitialized) + return; + + shader = OpenTKSharedResources.shaders["KCL"]; shader.UseProgram(); + shader.EnableVertexAttributes(); SetRenderSettings(shader); @@ -237,10 +410,13 @@ namespace FirstPlugin Matrix4 camMat = previewScale * control.mtxCam * control.mtxProj; shader.SetVector3("difLightDirection", Vector3.TransformNormal(new Vector3(0f, 0f, -1f), camMat.Inverted()).Normalized()); + shader.SetVector3("difLightColor", new Vector3(1)); + shader.SetVector3("ambLightColor", new Vector3(1)); + shader.EnableVertexAttributes(); SetRenderSettings(shader); - shader.SetMatrix4x4("modelview", ref camMat); + shader.SetMatrix4x4("mvpMatrix", ref camMat); foreach (KCLModel mdl in models) { @@ -318,138 +494,44 @@ namespace FirstPlugin GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); shader.SetInt("colorOverride", 0); } - - public MarioKart.MK7.KCL kcl = null; - public void Read(byte[] file_data) - { - try - { - kcl = new MarioKart.MK7.KCL(file_data, Syroot.BinaryData.ByteOrder.LittleEndian); - } - catch - { - kcl = new MarioKart.MK7.KCL(file_data, Syroot.BinaryData.ByteOrder.BigEndian); - } - - /* - int CurModelIndx = 0; - foreach (MarioKart.MK7.KCL.KCLModel mdl in kcl.Models) - { - KCLModel kclmodel = new KCLModel(); - - kclmodel.Text = "Model " + CurModelIndx; - - KclFace[] indicesArray = mdl.Faces; - - int ft = 0; - foreach (KclFace f in mdl.Faces) - { - Vertex vtx = new Vertex(); - Vertex vtx2 = new Vertex(); - Vertex vtx3 = new Vertex(); - - - Vector3 CrossA = Vector3.Cross(Vec3F_To_Vec3(mdl.Normals[f.Normal1Index]), Vec3F_To_Vec3(mdl.Normals[f.DirectionIndex])); - Vector3 CrossB = Vector3.Cross(Vec3F_To_Vec3(mdl.Normals[f.Normal2Index]), Vec3F_To_Vec3(mdl.Normals[f.DirectionIndex])); - Vector3 CrossC = Vector3.Cross(Vec3F_To_Vec3(mdl.Normals[f.Normal3Index]), Vec3F_To_Vec3(mdl.Normals[f.DirectionIndex])); - Vector3 normal_a = Vec3F_To_Vec3(mdl.Normals[f.Normal1Index]); - Vector3 normal_b = Vec3F_To_Vec3(mdl.Normals[f.Normal2Index]); - Vector3 normal_c = Vec3F_To_Vec3(mdl.Normals[f.Normal3Index]); - - - float result1 = Vector3.Dot(new Vector3(CrossB.X, CrossB.Y, CrossB.Z), (new Vector3(normal_c.X, normal_c.Y, normal_c.Z))); - float result2 = Vector3.Dot(new Vector3(CrossA.X, CrossA.Y, CrossA.Z), (new Vector3(normal_c.X, normal_c.Y, normal_c.Z))); - - Vector3 pos = Vec3F_To_Vec3(mdl.Positions[f.PositionIndex]); - Vector3 nrm = Vec3F_To_Vec3(mdl.Normals[f.Normal1Index]); - - Vector3 Vertex1 = pos; - Vector3 Vertex2 = pos + CrossB * (f.Length / result1); - Vector3 Vertex3 = pos + CrossA * (f.Length / result2); - - vtx.pos = new Vector3(Vertex1.X, Vertex1.Y, Vertex1.Z); - vtx2.pos = new Vector3(Vertex2.X, Vertex2.Y, Vertex2.Z); - vtx3.pos = new Vector3(Vertex3.X, Vertex3.Y, Vertex3.Z); - - var dir = Vector3.Cross(Vertex2 - Vertex1, Vertex3 - Vertex1); - var norm = Vector3.Normalize(dir); - - vtx.nrm = norm; - vtx2.nrm = norm; - vtx3.nrm = norm; - - KCLModel.Face face = new KCLModel.Face(); - - face.Text = f.CollisionFlags.ToString(); - - face.MaterialFlag = f.CollisionFlags; - - Color color = SetMaterialColor(face); - - - AllFlags.Add(face.MaterialFlag); - - Vector3 ColorSet = new Vector3(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f); - - vtx.col = new Vector3(ColorSet); - vtx2.col = new Vector3(ColorSet); - vtx3.col = new Vector3(ColorSet); - - kclmodel.faces.Add(ft); - kclmodel.faces.Add(ft + 1); - kclmodel.faces.Add(ft + 2); - - ft += 3; - - kclmodel.vertices.Add(vtx); - kclmodel.vertices.Add(vtx2); - kclmodel.vertices.Add(vtx3); - - - } - - - models.Add(kclmodel); - - - - Nodes.Add(kclmodel); - - CurModelIndx++; - }*/ - } - //Convert KCL lib vec3 to opentk one so i can use the cross and dot methods - public static Vector3 Vec3F_To_Vec3(Syroot.Maths.Vector3F v) - { - return new Vector3(v.X, v.Y, v.Z); - } + } + + //Convert KCL lib vec3 to opentk one so i can use the cross and dot methods + public static Vector3 Vec3D_To_Vec3(System.Windows.Media.Media3D.Vector3D v) + { + return new Vector3((float)v.X, (float)v.Y, (float)v.Z); } public struct DisplayVertex { // Used for rendering. public Vector3 pos; public Vector3 nrm; - public Vector3 tan; - public Vector3 bit; - public Vector2 uv; - public Vector4 col; - public Vector4 node; - public Vector4 weight; - public Vector2 uv2; - public Vector2 uv3; - public Vector3 pos1; - public Vector3 pos2; + public Vector3 col; - public static int Size = 4 * (3 + 3 + 3 + 3 + 2 + 4 + 4 + 4 + 2 + 2 + 3 + 3); + public static int Size = 4 * (3 + 3 + 3); } public class KCLModel : STGenericObject { + public KCLModel() + { + ImageKey = "mesh"; + SelectedImageKey = "mesh"; + + Checked = true; + } + public int[] display; public int Offset; // For Rendering public int strip = 0x40; public int displayFaceSize = 0; + public class Face : TreeNode + { + public int MaterialFlag = 0; + + } + public List CreateDisplayVertices() { // rearrange faces @@ -466,7 +548,7 @@ namespace FirstPlugin { pos = v.pos, nrm = v.nrm, - col = v.col, + col = v.col.Xyz, }; displayVertList.Add(displayVert); diff --git a/Switch_FileFormatsMain/FileFormats/Texture/BNTX.cs b/Switch_FileFormatsMain/FileFormats/Texture/BNTX.cs index 55508d1f..578db50f 100644 --- a/Switch_FileFormatsMain/FileFormats/Texture/BNTX.cs +++ b/Switch_FileFormatsMain/FileFormats/Texture/BNTX.cs @@ -127,12 +127,12 @@ namespace FirstPlugin public CompressionType CompressionType { get; set; } = CompressionType.None; public byte[] Data { get; set; } public string FileName { get; set; } - public TreeNode EditorRoot { get; set; } + public TreeNodeFile EditorRoot { get; set; } public bool IsActive { get; set; } = false; public bool UseEditMenu { get; set; } = false; - public IFileFormat ArchiveFile { get; set; } public int Alignment { get; set; } = 0; public string FilePath { get; set; } + public IFileInfo IFileInfo { get; set; } public Type[] Types { get @@ -144,7 +144,7 @@ namespace FirstPlugin } class MenuExt : IFileMenuExtension { - public ToolStripItemDark[] NewFileMenuExtensions => newFileExt; + public ToolStripItemDark[] NewFileMenuExtensions => null; public ToolStripItemDark[] ToolsMenuExtensions => null; public ToolStripItemDark[] TitleBarExtensions => null; public ToolStripItemDark[] CompressionMenuExtensions => null; @@ -165,7 +165,7 @@ namespace FirstPlugin UseEditMenu = true; CanSave = true; - bntx = new BinaryTextureContainer(Data, FileName); + bntx = new BinaryTextureContainer(Data, FileName, "", this); EditorRoot = bntx; } public void Unload() @@ -186,7 +186,7 @@ namespace FirstPlugin } } - public class BinaryTextureContainer : TreeNodeCustom + public class BinaryTextureContainer : TreeNodeFile { public Dictionary Textures; @@ -201,7 +201,7 @@ namespace FirstPlugin SelectedImageKey = "bntx"; } - public BinaryTextureContainer(byte[] data, string Name = "", string FileName = "") + public BinaryTextureContainer(byte[] data, string Name = "", string FileName = "", IFileFormat handler = null) { if (data.Length == 0) data = CreateNewBNTX(Name); @@ -260,7 +260,6 @@ namespace FirstPlugin Textures = new Dictionary(); Data = data; - BinaryTexFile = new BntxFile(new MemoryStream(Data)); Text = BinaryTexFile.Name; @@ -305,15 +304,19 @@ namespace FirstPlugin public void ImportTexture() { OpenFileDialog ofd = new OpenFileDialog(); - // ofd.Filter = "Supported Formats|*.bftex;*.dds; *.png;*.tga;*.jpg;*.tiff|" + - ofd.Filter = "Supported Formats|*.bftex;*.dds;|" + - "Binary Texture |*.bftex|" + - "Microsoft DDS |*.dds|" + - /* "Portable Network Graphics |*.png|" + - "Joint Photographic Experts Group |*.jpg|" + - "Bitmap Image |*.bmp|" + - "Tagged Image File Format |*.tiff|" */ - "All files(*.*)|*.*"; + /* ofd.Filter = "Supported Formats|*.bftex;*.dds; *.png;*.tga;*.jpg;*.tiff|" + + "Binary Texture |*.bftex|" + + "Microsoft DDS |*.dds|" + + "Portable Network Graphics |*.png|" + + "Joint Photographic Experts Group |*.jpg|" + + "Bitmap Image |*.bmp|" + + "Tagged Image File Format |*.tiff|" + + "All files(*.*)|*.*";*/ + ofd.Filter = "Supported Formats|*.bftex;*.dds|" + + "Binary Texture |*.bftex|" + + "Microsoft DDS |*.dds|" + + "All files(*.*)|*.*"; + ofd.DefaultExt = "bftex"; ofd.Multiselect = true; @@ -325,6 +328,8 @@ namespace FirstPlugin foreach (string name in ofd.FileNames) { string ext = Path.GetExtension(name); + ext = ext.ToLower(); + if (ext == ".dds" || ext == ".bftex") { AddTexture(name); @@ -445,6 +450,8 @@ namespace FirstPlugin var importer = new TextureImporterSettings(); string ext = Path.GetExtension(name); + ext = ext.ToLower(); + switch (ext) { case ".bftex": @@ -467,6 +474,8 @@ namespace FirstPlugin TextureData texData = null; string ext = Path.GetExtension(name); + ext = ext.ToLower(); + switch (ext) { case ".bftex": @@ -627,7 +636,7 @@ namespace FirstPlugin { public Texture Texture; public BntxFile bntxFile; - public List mipmaps = new List(); + public List> mipmaps = new List>(); public BRTI_Texture renderedGLTex = new BRTI_Texture(); BNTXEditor BNTXEditor; @@ -723,7 +732,7 @@ namespace FirstPlugin throw new Exception("No texture data found"); } - renderedGLTex.data = mipmaps[0]; + renderedGLTex.data = mipmaps[0][0]; renderedGLTex.width = (int)Texture.Width; renderedGLTex.height = (int)Texture.Height; @@ -757,7 +766,7 @@ namespace FirstPlugin renderedGLTex.type = PixelInternalFormat.CompressedRgRgtc2; break; case SurfaceFormat.BC5_SNORM: - renderedGLTex.data = DDS_Decompress.DecompressBC5(mipmaps[0], (int)Texture.Width, (int)Texture.Height, true, true); + renderedGLTex.data = DDS_Decompress.DecompressBC5(mipmaps[0][0], (int)Texture.Width, (int)Texture.Height, true, true); renderedGLTex.type = PixelInternalFormat.Rgba; renderedGLTex.utype = OpenTK.Graphics.OpenGL.PixelFormat.Rgba; break; @@ -912,15 +921,19 @@ namespace FirstPlugin private void Replace(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); - // ofd.Filter = "Supported Formats|*.bftex;*.dds; *.png;*.tga;*.jpg;*.tiff|" + - ofd.Filter = "Supported Formats|*.bftex;*.dds;|" + - "Binary Texture |*.bftex|" + + /* ofd.Filter = "Supported Formats|*.bftex;*.dds; *.png;*.tga;*.jpg;*.tiff|" + + "Binary Texture |*.bftex|" + "Microsoft DDS |*.dds|" + - /* "Portable Network Graphics |*.png|" + - "Joint Photographic Experts Group |*.jpg|" + - "Bitmap Image |*.bmp|" + - "Tagged Image File Format |*.tiff|" */ - "All files(*.*)|*.*"; + "Portable Network Graphics |*.png|" + + "Joint Photographic Experts Group |*.jpg|" + + "Bitmap Image |*.bmp|" + + "Tagged Image File Format |*.tiff|" + + "All files(*.*)|*.*";*/ + + ofd.Filter = "Supported Formats|*.bftex;*.dds|" + + "Binary Texture |*.bftex|" + + "Microsoft DDS |*.dds|" + + "All files(*.*)|*.*"; ofd.Multiselect = false; if (ofd.ShowDialog() == DialogResult.OK) @@ -931,6 +944,8 @@ namespace FirstPlugin public void Replace(string FileName) { string ext = Path.GetExtension(FileName); + ext = ext.ToLower(); + switch (ext) { case ".bftex": @@ -982,9 +997,12 @@ namespace FirstPlugin Export(sfd.FileName); } } - public void Export(string FileName) + public void Export(string FileName, bool ExportSurfaceLevel = false, + bool ExportMipMapLevel = false, int SurfaceLevel = 0, int MipLevel = 0) { string ext = Path.GetExtension(FileName); + ext = ext.ToLower(); + switch (ext) { case ".bftex": @@ -998,9 +1016,9 @@ namespace FirstPlugin break; } } - internal void SaveBitMap(string FileName) + internal void SaveBitMap(string FileName, int SurfaceLevel = 0, int MipLevel = 0) { - Bitmap bitMap = DisplayTexture(); + Bitmap bitMap = DisplayTexture(MipLevel, SurfaceLevel); bitMap.Save(FileName); } @@ -1091,31 +1109,39 @@ namespace FirstPlugin uint blkHeight = blk_dim & 0xF; int linesPerBlockHeight = (1 << (int)tex.BlockHeightLog2) * 8; - int blockHeightShift = 0; uint bpp = Formats.bpps((uint)((int)tex.Format >> 8)); - for (int mipLevel = 0; mipLevel < tex.TextureData.Length; mipLevel++) + for (int arrayLevel = 0; arrayLevel < tex.ArrayLength; arrayLevel++) { - uint width = (uint)Math.Max(1, tex.Width >> mipLevel); - uint height = (uint)Math.Max(1, tex.Height >> mipLevel); + int blockHeightShift = 0; - uint size = TegraX1Swizzle.DIV_ROUND_UP(width, blkWidth) * TegraX1Swizzle.DIV_ROUND_UP(height, blkHeight) * bpp; + List mips = new List(); + for (int mipLevel = 0; mipLevel < tex.TextureData[arrayLevel].Count; mipLevel++) + { + uint width = (uint)Math.Max(1, tex.Width >> mipLevel); + uint height = (uint)Math.Max(1, tex.Height >> mipLevel); - if (TegraX1Swizzle.pow2_round_up(TegraX1Swizzle.DIV_ROUND_UP(height, blkWidth)) < linesPerBlockHeight) - blockHeightShift += 1; + uint size = TegraX1Swizzle.DIV_ROUND_UP(width, blkWidth) * TegraX1Swizzle.DIV_ROUND_UP(height, blkHeight) * bpp; - byte[] result = TegraX1Swizzle.deswizzle(width, height, blkWidth, blkHeight, target, bpp, (uint)tex.TileMode, (int)Math.Max(0, tex.BlockHeightLog2 - blockHeightShift), tex.TextureData[mipLevel]); - //Create a copy and use that to remove uneeded data - byte[] result_ = new byte[size]; - Array.Copy(result, 0, result_, 0, size); + if (TegraX1Swizzle.pow2_round_up(TegraX1Swizzle.DIV_ROUND_UP(height, blkWidth)) < linesPerBlockHeight) + blockHeightShift += 1; - mipmaps.Add(result_); + byte[] result = TegraX1Swizzle.deswizzle(width, height, blkWidth, blkHeight, target, bpp, (uint)tex.TileMode, (int)Math.Max(0, tex.BlockHeightLog2 - blockHeightShift), tex.TextureData[arrayLevel][mipLevel]); + //Create a copy and use that to remove uneeded data + byte[] result_ = new byte[size]; + Array.Copy(result, 0, result_, 0, size); + + mips.Add(result_); + + } + mipmaps.Add(mips); } + Texture = tex; } - public Bitmap DisplayTexture(int DisplayMipIndex = 0) + public Bitmap DisplayTexture(int DisplayMipIndex = 0, int ArrayIndex = 0) { LoadTexture(Texture); @@ -1127,7 +1153,7 @@ namespace FirstPlugin uint width = (uint)Math.Max(1, Texture.Width >> DisplayMipIndex); uint height = (uint)Math.Max(1, Texture.Height >> DisplayMipIndex); - byte[] data = mipmaps[DisplayMipIndex]; + byte[] data = mipmaps[ArrayIndex][DisplayMipIndex]; return DecodeBlock(data, width, height, Texture.Format); } diff --git a/Switch_FileFormatsMain/FileFormats/Texture/FTEX.cs b/Switch_FileFormatsMain/FileFormats/Texture/FTEX.cs new file mode 100644 index 00000000..14acba96 --- /dev/null +++ b/Switch_FileFormatsMain/FileFormats/Texture/FTEX.cs @@ -0,0 +1,546 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Drawing; +using System.Threading.Tasks; +using System.Windows.Forms; +using Switch_Toolbox.Library; +using Switch_Toolbox.Library.Forms; +using Syroot.NintenTools.Bfres; +using Syroot.NintenTools.Bfres.GX2; +using OpenTK; +using OpenTK.Graphics.OpenGL; +using Smash_Forge.Rendering; +using WeifenLuo.WinFormsUI.Docking; + +namespace FirstPlugin +{ + public class FTEXContainer : TreeNodeCustom + { + public Dictionary Textures = new Dictionary(); //To get instance of classes + + public FTEXContainer() + { + Text = "Textures"; + Name = "FTEXCONT"; + + ContextMenu = new ContextMenu(); + MenuItem exportAll = new MenuItem("Export All Textures"); + ContextMenu.MenuItems.Add(exportAll); + exportAll.Click += ExportAll; + MenuItem clear = new MenuItem("Clear"); + ContextMenu.MenuItems.Add(clear); + clear.Click += Clear; + } + private void Clear(object sender, EventArgs args) + { + Nodes.Clear(); + Textures.Clear(); + } + public void RefreshGlTexturesByName() + { + } + + public override void OnClick(TreeView treeview) + { + } + public void RemoveTexture(FTEX textureData) + { + Nodes.Remove(textureData); + Textures.Remove(textureData.Text); + Viewport.Instance.UpdateViewport(); + } + + private void ExportAll(object sender, EventArgs args) + { + List Formats = new List(); + Formats.Add("Cafe Binary Textures (.bftex)"); + Formats.Add("Microsoft DDS (.dds)"); + Formats.Add("Portable Graphics Network (.png)"); + Formats.Add("Joint Photographic Experts Group (.jpg)"); + Formats.Add("Bitmap Image (.bmp)"); + Formats.Add("Tagged Image File Format (.tiff)"); + + FolderSelectDialog sfd = new FolderSelectDialog(); + + if (sfd.ShowDialog() == DialogResult.OK) + { + string folderPath = sfd.SelectedPath; + + TextureFormatExport form = new TextureFormatExport(Formats); + if (form.ShowDialog() == DialogResult.OK) + { + foreach (FTEX tex in Nodes) + { + if (form.Index == 0) + tex.SaveBinaryTexture(folderPath + '\\' + tex.Text + ".bftex"); + else if (form.Index == 1) + tex.SaveDDS(folderPath + '\\' + tex.Text + ".dds"); + else if (form.Index == 2) + tex.SaveBitMap(folderPath + '\\' + tex.Text + ".png"); + else if (form.Index == 3) + tex.SaveBitMap(folderPath + '\\' + tex.Text + ".jpg"); + else if (form.Index == 4) + tex.SaveBitMap(folderPath + '\\' + tex.Text + ".bmp"); + else if (form.Index == 5) + tex.SaveBitMap(folderPath + '\\' + tex.Text + ".tiff"); + } + } + } + } + } + + public class FTEX : TreeNodeCustom + { + public int format; + public RenderableTex renderedTex = new RenderableTex(); + GX2CompSel ChannelRed; + GX2CompSel ChannelBlue; + GX2CompSel ChannelGreen; + GX2CompSel ChannelAlpha; + + public FTEX() + { + ContextMenu = new ContextMenu(); + MenuItem export = new MenuItem("Export"); + ContextMenu.MenuItems.Add(export); + export.Click += Export; + MenuItem remove = new MenuItem("Remove"); + ContextMenu.MenuItems.Add(remove); + remove.Click += Remove; + MenuItem rename = new MenuItem("Rename"); + ContextMenu.MenuItems.Add(rename); + rename.Click += Rename; + } + + private void Replace(object sender, EventArgs args) + { + + } + private void Rename(object sender, EventArgs args) + { + RenameDialog dialog = new RenameDialog(); + dialog.SetString(Text); + + if (dialog.ShowDialog() == DialogResult.OK) + { + ((FTEXContainer)Parent).Textures.Remove(Text); + Text = dialog.textBox1.Text; + + ((FTEXContainer)Parent).Textures.Add(Text, this); + } + } + private void Remove(object sender, EventArgs args) + { + ((FTEXContainer)Parent).RemoveTexture(this); + } + private void Export(object sender, EventArgs args) + { + SaveFileDialog sfd = new SaveFileDialog(); + sfd.FileName = Text; + sfd.DefaultExt = "bftex"; + sfd.Filter = "Supported Formats|*.bftex;*.dds; *.png;*.tga;*.jpg;*.tiff|" + + "Binary Texture |*.bftex|" + + "Microsoft DDS |*.dds|" + + "Portable Network Graphics |*.png|" + + "Joint Photographic Experts Group |*.jpg|" + + "Bitmap Image |*.bmp|" + + "Tagged Image File Format |*.tiff|" + + "All files(*.*)|*.*"; + + if (sfd.ShowDialog() == DialogResult.OK) + { + Export(sfd.FileName); + } + } + + public void Read(Texture tex) + { + ImageKey = "Texture"; + SelectedImageKey = "Texture"; + Text = tex.Name; + + ChannelRed = tex.CompSelR; + ChannelGreen = tex.CompSelG; + ChannelBlue = tex.CompSelB; + ChannelAlpha = tex.CompSelA; + + renderedTex.width = (int)tex.Width; + renderedTex.height = (int)tex.Height; + format = (int)tex.Format; + int swizzle = (int)tex.Swizzle; + int pitch = (int)tex.Pitch; + renderedTex.data = GTX.swizzleBC(tex.Data, renderedTex.width, renderedTex.height, format, (int)tex.TileMode, pitch, swizzle); + } + + public void Export(string FileName, bool ExportSurfaceLevel = false, + bool ExportMipMapLevel = false, int SurfaceLevel = 0, int MipLevel = 0) + { + string ext = System.IO.Path.GetExtension(FileName); + ext = ext.ToLower(); + + switch (ext) + { + case ".bftex": + SaveBinaryTexture(FileName); + break; + case ".dds": + SaveDDS(FileName); + break; + default: + SaveBitMap(FileName); + break; + } + } + internal void SaveBitMap(string FileName, int SurfaceLevel = 0, int MipLevel = 0) + { + Bitmap bitMap = DisplayTexture(MipLevel, SurfaceLevel); + + bitMap.Save(FileName); + } + internal void SaveBinaryTexture(string FileName) + { + Console.WriteLine("Test"); + // Texture.Export(FileName, bntxFile); + } + internal void SaveDDS(string FileName) + { + DDS dds = new DDS(); + dds.header = new DDS.Header(); + dds.header.width = (uint)renderedTex.width; + dds.header.height = (uint)renderedTex.width; + dds.header.mipmapCount = (uint)renderedTex.mipmaps[0].Count; + + bool IsDX10 = false; + + switch (format) + { + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC1_UNORM): + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC1_SRGB): + dds.header.ddspf.fourCC = "DXT1"; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC2_UNORM): + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC2_SRGB): + dds.header.ddspf.fourCC = "DXT3"; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC3_UNORM): + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC3_SRGB): + dds.header.ddspf.fourCC = "DXT5"; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC4_UNORM): + IsDX10 = true; + dds.DX10header = new DDS.DX10Header(); + dds.DX10header.DXGI_Format = DDS.DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC4_SNORM): + IsDX10 = true; + dds.DX10header = new DDS.DX10Header(); + dds.DX10header.DXGI_Format = DDS.DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC5_UNORM): + IsDX10 = true; + dds.DX10header = new DDS.DX10Header(); + dds.DX10header.DXGI_Format = DDS.DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC5_SNORM): + IsDX10 = true; + dds.DX10header = new DDS.DX10Header(); + dds.DX10header.DXGI_Format = DDS.DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM; + break; + default: + throw new Exception($"Format {(GTX.GX2SurfaceFormat)format} not supported!"); + } + + if (IsDX10) + dds.header.ddspf.fourCC = "DX10"; + + dds.Save(dds, FileName, renderedTex.mipmaps, IsDX10); + } + + + public void LoadOpenGLTexture() + { + if (OpenTKSharedResources.SetupStatus == OpenTKSharedResources.SharedResourceStatus.Unitialized) + return; + + switch (format) + { + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC1_UNORM): + renderedTex.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt1Ext; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC1_SRGB): + renderedTex.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt1Ext; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC2_UNORM): + renderedTex.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt3Ext; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC2_SRGB): + renderedTex.pixelInternalFormat = PixelInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC3_UNORM): + renderedTex.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt5Ext; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC3_SRGB): + renderedTex.pixelInternalFormat = PixelInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC4_UNORM): + renderedTex.pixelInternalFormat = PixelInternalFormat.CompressedRedRgtc1; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC4_SNORM): + renderedTex.pixelInternalFormat = PixelInternalFormat.CompressedSignedRedRgtc1; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC5_UNORM): + renderedTex.pixelInternalFormat = PixelInternalFormat.CompressedRgRgtc2; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC5_SNORM): + //OpenTK doesn't load BC5 SNORM textures right so I'll use the same decompress method bntx has + byte[] fixBC5 = DDS_Decompress.DecompressBC5(renderedTex.data, renderedTex.width, renderedTex.height, true, true); + renderedTex.data = fixBC5; + renderedTex.pixelInternalFormat = PixelInternalFormat.Rgba; + renderedTex.pixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Rgba; + break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM): + renderedTex.pixelInternalFormat = PixelInternalFormat.Rgba; + renderedTex.pixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Rgba; + break; + } + renderedTex.display = loadImage(renderedTex); + } + public static int loadImage(RenderableTex t) + { + int texID = GL.GenTexture(); + + GL.BindTexture(TextureTarget.Texture2D, texID); + + if (t.pixelInternalFormat != PixelInternalFormat.Rgba) + { + GL.CompressedTexImage2D(TextureTarget.Texture2D, 0, (InternalFormat)t.pixelInternalFormat, + t.width, t.height, 0, getImageSize(t), t.data); + //Debug.WriteLine(GL.GetError()); + } + else + { + GL.TexImage2D(TextureTarget.Texture2D, 0, t.pixelInternalFormat, t.width, t.height, 0, + t.pixelFormat, PixelType.UnsignedByte, t.data); + } + + GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); + + return texID; + } + public static int getImageSize(RenderableTex t) + { + switch (t.pixelInternalFormat) + { + case PixelInternalFormat.CompressedRgbaS3tcDxt1Ext: + case PixelInternalFormat.CompressedSrgbAlphaS3tcDxt1Ext: + case PixelInternalFormat.CompressedRedRgtc1: + case PixelInternalFormat.CompressedSignedRedRgtc1: + return (t.width * t.height / 2); + case PixelInternalFormat.CompressedRgbaS3tcDxt3Ext: + case PixelInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext: + case PixelInternalFormat.CompressedRgbaS3tcDxt5Ext: + case PixelInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext: + case PixelInternalFormat.CompressedSignedRgRgtc2: + case PixelInternalFormat.CompressedRgRgtc2: + return (t.width * t.height); + case PixelInternalFormat.Rgba: + return t.data.Length; + default: + return t.data.Length; + } + } + public unsafe Bitmap GLTextureToBitmap(RenderableTex t, int id) + { + Bitmap bitmap = new Bitmap(t.width, t.height); + System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, t.width, t.height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + GL.BindTexture(TextureTarget.Texture2D, id); + GL.GetTexImage(TextureTarget.Texture2D, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bitmapData.Scan0); + + bitmap.UnlockBits(bitmapData); + return bitmap; + } + + public override void OnClick(TreeView treeView) + { + foreach (Control control in FirstPlugin.MainF.Controls) + { + if (control is DockPanel) + { + if (FirstPlugin.DockedEditorS == null) + { + FirstPlugin.DockedEditorS = new DockContent(); + FirstPlugin.DockedEditorS.Show((DockPanel)control, PluginRuntime.FSHPDockState); + } + } + } + + if (!EditorIsActive(FirstPlugin.DockedEditorS)) + { + FirstPlugin.DockedEditorS.Controls.Clear(); + + FTEXEditor FTEXEditor = new FTEXEditor(); + FTEXEditor.Text = Text; + FTEXEditor.Dock = DockStyle.Fill; + FTEXEditor.LoadPicture(DisplayTexture()); + FTEXEditor.LoadProperty(this); + FirstPlugin.DockedEditorS.Controls.Add(FTEXEditor); + } + } + public bool EditorIsActive(DockContent dock) + { + foreach (Control ctrl in dock.Controls) + { + if (ctrl is FTEXEditor) + { + dock.Text = Text; + ((FTEXEditor)ctrl).LoadPicture(DisplayTexture()); + ((FTEXEditor)ctrl).LoadProperty(this); + return true; + } + } + + return false; + } + + public class RenderableTex + { + public int width, height; + public int display; + public PixelInternalFormat pixelInternalFormat; + public PixelFormat pixelFormat; + public PixelType pixelType = PixelType.UnsignedByte; + public int mipMapCount; + public List> mipmaps = new List>(); + + public byte[] data + { + get + { + return mipmaps[0][0]; + } + set + { + List mips = new List(); + mips.Add(value); + mipmaps.Add(mips); + } + } + public class Surface + { + + } + } + + public Bitmap DisplayTexture(int DisplayMipIndex = 0, int ArrayIndex = 0) + { + if (renderedTex.mipmaps.Count <= 0) + { + throw new Exception("No texture data found"); + } + + uint width = (uint)Math.Max(1, renderedTex.width >> DisplayMipIndex); + uint height = (uint)Math.Max(1, renderedTex.height >> DisplayMipIndex); + + byte[] data = renderedTex.mipmaps[ArrayIndex][DisplayMipIndex]; + + return DecodeBlock(data, width, height, format); + } + + public static Bitmap DecodeBlock(byte[] data, uint Width, uint Height, int Format) + { + Bitmap decomp; + + switch (Format) + { + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC1_UNORM): + decomp = DDS_Decompress.DecompressBC1(data, (int)Width, (int)Height, false); break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC1_SRGB): + decomp = DDS_Decompress.DecompressBC1(data, (int)Width, (int)Height, true); break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC3_UNORM): + decomp = DDS_Decompress.DecompressBC3(data, (int)Width, (int)Height, false); break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC3_SRGB): + decomp = DDS_Decompress.DecompressBC3(data, (int)Width, (int)Height, true); break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC4_UNORM): + decomp = DDS_Decompress.DecompressBC4(data, (int)Width, (int)Height, false); break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC4_SNORM): + decomp = DDS_Decompress.DecompressBC4(data, (int)Width, (int)Height, true); break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC5_UNORM): + decomp = DDS_Decompress.DecompressBC5(data, (int)Width, (int)Height, false); break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC5_SNORM): + decomp = DDS_Decompress.DecompressBC5(data, (int)Width, (int)Height, true); break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_SNORM): + decomp = DDS_PixelDecode.DecodeR8G8B8A8(data, (int)Width, (int)Height); break; + case ((int)GTX.GX2SurfaceFormat.GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_UINT): + decomp = DDS_PixelDecode.DecodeR8G8B8A8(data, (int)Width, (int)Height); break; + default: + decomp = Properties.Resources.TextureError; + Console.WriteLine($"Format {Format} not supported!"); + break; + } + return decomp; + } + + public Bitmap UpdateBitmap(Bitmap image) + { + return ColorComponentSelector(image, ChannelRed, ChannelGreen, ChannelBlue, ChannelAlpha); + } + public static Bitmap ColorComponentSelector(Bitmap image, GX2CompSel R, GX2CompSel G, GX2CompSel B, GX2CompSel A) + { + BitmapExtension.ColorSwapFilter color = new BitmapExtension.ColorSwapFilter(); + if (R == GX2CompSel.ChannelR) + color.CompRed = BitmapExtension.ColorSwapFilter.Red.Red; + if (R == GX2CompSel.ChannelG) + color.CompRed = BitmapExtension.ColorSwapFilter.Red.Green; + if (R == GX2CompSel.ChannelB) + color.CompRed = BitmapExtension.ColorSwapFilter.Red.Blue; + if (R == GX2CompSel.ChannelA) + color.CompRed = BitmapExtension.ColorSwapFilter.Red.Alpha; + if (R == GX2CompSel.Always0) + color.CompRed = BitmapExtension.ColorSwapFilter.Red.One; + if (R == GX2CompSel.Always1) + color.CompRed = BitmapExtension.ColorSwapFilter.Red.Zero; + + if (G == GX2CompSel.ChannelR) + color.CompGreen = BitmapExtension.ColorSwapFilter.Green.Red; + if (G == GX2CompSel.ChannelG) + color.CompGreen = BitmapExtension.ColorSwapFilter.Green.Green; + if (G == GX2CompSel.ChannelB) + color.CompGreen = BitmapExtension.ColorSwapFilter.Green.Blue; + if (G == GX2CompSel.ChannelA) + color.CompGreen = BitmapExtension.ColorSwapFilter.Green.Alpha; + if (G == GX2CompSel.Always0) + color.CompGreen = BitmapExtension.ColorSwapFilter.Green.One; + if (G == GX2CompSel.Always1) + color.CompGreen = BitmapExtension.ColorSwapFilter.Green.Zero; + + if (B == GX2CompSel.ChannelR) + color.CompBlue = BitmapExtension.ColorSwapFilter.Blue.Red; + if (B == GX2CompSel.ChannelG) + color.CompBlue = BitmapExtension.ColorSwapFilter.Blue.Green; + if (B == GX2CompSel.ChannelB) + color.CompBlue = BitmapExtension.ColorSwapFilter.Blue.Blue; + if (B == GX2CompSel.ChannelA) + color.CompBlue = BitmapExtension.ColorSwapFilter.Blue.Alpha; + if (B == GX2CompSel.Always0) + color.CompBlue = BitmapExtension.ColorSwapFilter.Blue.One; + if (B == GX2CompSel.Always1) + color.CompBlue = BitmapExtension.ColorSwapFilter.Blue.Zero; + + if (A == GX2CompSel.ChannelR) + color.CompAlpha = BitmapExtension.ColorSwapFilter.Alpha.Red; + if (A == GX2CompSel.ChannelG) + color.CompAlpha = BitmapExtension.ColorSwapFilter.Alpha.Green; + if (A == GX2CompSel.ChannelB) + color.CompAlpha = BitmapExtension.ColorSwapFilter.Alpha.Blue; + if (A == GX2CompSel.ChannelA) + color.CompAlpha = BitmapExtension.ColorSwapFilter.Alpha.Alpha; + if (A == GX2CompSel.Always0) + color.CompAlpha = BitmapExtension.ColorSwapFilter.Alpha.One; + if (A == GX2CompSel.Always1) + color.CompAlpha = BitmapExtension.ColorSwapFilter.Alpha.Zero; + + return BitmapExtension.SwapRGB(image, color); + } + } +} diff --git a/Switch_FileFormatsMain/FileFormats/Texture/GTX.cs b/Switch_FileFormatsMain/FileFormats/Texture/GTX.cs new file mode 100644 index 00000000..20c3f340 --- /dev/null +++ b/Switch_FileFormatsMain/FileFormats/Texture/GTX.cs @@ -0,0 +1,1138 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Switch_Toolbox.Library.IO; +using System.Text; + +namespace FirstPlugin +{ + public enum BlockType : uint + { + Invalid = 0x00, + EndOfFile = 0x01, + AlignData = 0x02, + VertexShaderHeader = 0x03, + VertexShaderProgram = 0x05, + PixelShaderHeader = 0x06, + PixelShaderProgram = 0x07, + GeometryShaderHeader = 0x08, + GeometryShaderProgram = 0x09, + } + + public class GTXHeader + { + public uint MajorVersion; + public uint MinorVersion; + public uint GpuVersion; + public uint AlignMode; + + public void Read(FileReader reader) + { + string Signature = reader.ReadString(4, Encoding.ASCII); + if (Signature != "Gfx2") + throw new Exception($"Invalid signature {Signature}! Expected Gfx2."); + + uint HeaderSize = reader.ReadUInt32(); + MajorVersion = reader.ReadUInt32(); + MinorVersion = reader.ReadUInt32(); + GpuVersion = reader.ReadUInt32(); + AlignMode = reader.ReadUInt32(); + uint Reserved = reader.ReadUInt32(); + uint Reserved2 = reader.ReadUInt32(); + } + public void Write(FileWriter reader) + { + } + } + public class GTXDataBlock + { + public uint MajorVersion; + public uint MinorVersion; + public BlockType BlockType; + public uint Identifier; + public uint index; + + public void Read(FileReader reader, GTXHeader header) + { + string Signature = reader.ReadString(4, Encoding.ASCII); + if (Signature != "BLK") + throw new Exception($"Invalid signature {Signature}! Expected BLK."); + uint HeaderSize = reader.ReadUInt32(); + + MajorVersion = reader.ReadUInt32(); //Must be 0x01 for 6.x.x + MinorVersion = reader.ReadUInt32(); //Must be 0x00 for 6.x.x + BlockType = reader.ReadEnum(false); + uint DataSize = reader.ReadUInt32(); + Identifier = reader.ReadUInt32(); + index = reader.ReadUInt32(); + } + } + public class GTX + { + + + + //From https://github.com/jam1garner/Smash-Forge/blob/master/Smash%20Forge/Filetypes/Textures/GTX.cs + //Todo. Add swizzling back + + public struct GX2Surface + { + public int dim; + public int width; + public int height; + public int depth; + public int numMips; + public int format; + public int aa; + public int use; + public int resourceFlags; + public int imageSize; + public int imagePtr; + public int pMem; + public int mipSize; + public int mipPtr; + public int tileMode; + public int swizzle; + public int alignment; + public int pitch; + + public byte[] data; + + public int[] mipOffset; + }; + + public enum GX2SurfaceDimension + { + GX2_SURFACE_DIM_1D = 0x0, + GX2_SURFACE_DIM_2D = 0x1, + GX2_SURFACE_DIM_3D = 0x2, + GX2_SURFACE_DIM_CUBE = 0x3, + GX2_SURFACE_DIM_1D_ARRAY = 0x4, + GX2_SURFACE_DIM_2D_ARRAY = 0x5, + GX2_SURFACE_DIM_2D_MSAA = 0x6, + GX2_SURFACE_DIM_2D_MSAA_ARRAY = 0x7, + GX2_SURFACE_DIM_FIRST = 0x0, + GX2_SURFACE_DIM_LAST = 0x7, + }; + public enum GX2SurfaceFormat + { + GX2_SURFACE_FORMAT_INVALID = 0x0, + GX2_SURFACE_FORMAT_TC_R8_UNORM = 0x1, + GX2_SURFACE_FORMAT_TC_R8_UINT = 0x101, + GX2_SURFACE_FORMAT_TC_R8_SNORM = 0x201, + GX2_SURFACE_FORMAT_TC_R8_SINT = 0x301, + GX2_SURFACE_FORMAT_T_R4_G4_UNORM = 0x2, + GX2_SURFACE_FORMAT_TCD_R16_UNORM = 0x5, + GX2_SURFACE_FORMAT_TC_R16_UINT = 0x105, + GX2_SURFACE_FORMAT_TC_R16_SNORM = 0x205, + GX2_SURFACE_FORMAT_TC_R16_SINT = 0x305, + GX2_SURFACE_FORMAT_TC_R16_FLOAT = 0x806, + GX2_SURFACE_FORMAT_TC_R8_G8_UNORM = 0x7, + GX2_SURFACE_FORMAT_TC_R8_G8_UINT = 0x107, + GX2_SURFACE_FORMAT_TC_R8_G8_SNORM = 0x207, + GX2_SURFACE_FORMAT_TC_R8_G8_SINT = 0x307, + GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM = 0x8, + GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM = 0xA, + GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM = 0xB, + GX2_SURFACE_FORMAT_TC_A1_B5_G5_R5_UNORM = 0xC, + GX2_SURFACE_FORMAT_TC_R32_UINT = 0x10D, + GX2_SURFACE_FORMAT_TC_R32_SINT = 0x30D, + GX2_SURFACE_FORMAT_TCD_R32_FLOAT = 0x80E, + GX2_SURFACE_FORMAT_TC_R16_G16_UNORM = 0xF, + GX2_SURFACE_FORMAT_TC_R16_G16_UINT = 0x10F, + GX2_SURFACE_FORMAT_TC_R16_G16_SNORM = 0x20F, + GX2_SURFACE_FORMAT_TC_R16_G16_SINT = 0x30F, + GX2_SURFACE_FORMAT_TC_R16_G16_FLOAT = 0x810, + GX2_SURFACE_FORMAT_D_D24_S8_UNORM = 0x11, + GX2_SURFACE_FORMAT_T_R24_UNORM_X8 = 0x11, + GX2_SURFACE_FORMAT_T_X24_G8_UINT = 0x111, + GX2_SURFACE_FORMAT_D_D24_S8_FLOAT = 0x811, + GX2_SURFACE_FORMAT_TC_R11_G11_B10_FLOAT = 0x816, + GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM = 0x19, + GX2_SURFACE_FORMAT_TC_R10_G10_B10_A2_UINT = 0x119, + GX2_SURFACE_FORMAT_T_R10_G10_B10_A2_SNORM = 0x219, + GX2_SURFACE_FORMAT_TC_R10_G10_B10_A2_SNORM = 0x219, + GX2_SURFACE_FORMAT_TC_R10_G10_B10_A2_SINT = 0x319, + GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM = 0x1A, + GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_UINT = 0x11A, + GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_SNORM = 0x21A, + GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_SINT = 0x31A, + GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB = 0x41A, + GX2_SURFACE_FORMAT_TCS_A2_B10_G10_R10_UNORM = 0x1B, + GX2_SURFACE_FORMAT_TC_A2_B10_G10_R10_UINT = 0x11B, + GX2_SURFACE_FORMAT_D_D32_FLOAT_S8_UINT_X24 = 0x81C, + GX2_SURFACE_FORMAT_T_R32_FLOAT_X8_X24 = 0x81C, + GX2_SURFACE_FORMAT_T_X32_G8_UINT_X24 = 0x11C, + GX2_SURFACE_FORMAT_TC_R32_G32_UINT = 0x11D, + GX2_SURFACE_FORMAT_TC_R32_G32_SINT = 0x31D, + GX2_SURFACE_FORMAT_TC_R32_G32_FLOAT = 0x81E, + GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_UNORM = 0x1F, + GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_UINT = 0x11F, + GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_SNORM = 0x21F, + GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_SINT = 0x31F, + GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_FLOAT = 0x820, + GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_UINT = 0x122, + GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_SINT = 0x322, + GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_FLOAT = 0x823, + GX2_SURFACE_FORMAT_T_BC1_UNORM = 0x31, + GX2_SURFACE_FORMAT_T_BC1_SRGB = 0x431, + GX2_SURFACE_FORMAT_T_BC2_UNORM = 0x32, + GX2_SURFACE_FORMAT_T_BC2_SRGB = 0x432, + GX2_SURFACE_FORMAT_T_BC3_UNORM = 0x33, + GX2_SURFACE_FORMAT_T_BC3_SRGB = 0x433, + GX2_SURFACE_FORMAT_T_BC4_UNORM = 0x34, + GX2_SURFACE_FORMAT_T_BC4_SNORM = 0x234, + GX2_SURFACE_FORMAT_T_BC5_UNORM = 0x35, + GX2_SURFACE_FORMAT_T_BC5_SNORM = 0x235, + GX2_SURFACE_FORMAT_T_NV12_UNORM = 0x81, + GX2_SURFACE_FORMAT_FIRST = 0x1, + GX2_SURFACE_FORMAT_LAST = 0x83F, + }; + public enum GX2AAMode + { + GX2_AA_MODE_1X = 0x0, + GX2_AA_MODE_2X = 0x1, + GX2_AA_MODE_4X = 0x2, + GX2_AA_MODE_8X = 0x3, + GX2_AA_MODE_FIRST = 0x0, + GX2_AA_MODE_LAST = 0x3, + }; + public enum GX2SurfaceUse : uint + { + GX2_SURFACE_USE_TEXTURE = 0x1, + GX2_SURFACE_USE_COLOR_BUFFER = 0x2, + GX2_SURFACE_USE_DEPTH_BUFFER = 0x4, + GX2_SURFACE_USE_SCAN_BUFFER = 0x8, + GX2_SURFACE_USE_FTV = 0x80000000, + GX2_SURFACE_USE_COLOR_BUFFER_TEXTURE = 0x3, + GX2_SURFACE_USE_DEPTH_BUFFER_TEXTURE = 0x5, + GX2_SURFACE_USE_COLOR_BUFFER_FTV = 0x80000002, + GX2_SURFACE_USE_COLOR_BUFFER_TEXTURE_FTV = 0x80000003, + GX2_SURFACE_USE_FIRST = 0x1, + GX2_SURFACE_USE_LAST = 0x8, + }; + public enum GX2RResourceFlags + { + GX2R_RESOURCE_FLAGS_NONE = 0x0, + GX2R_BIND_NONE = 0x0, + GX2R_BIND_TEXTURE = 0x1, + GX2R_BIND_COLOR_BUFFER = 0x2, + GX2R_BIND_DEPTH_BUFFER = 0x4, + GX2R_BIND_SCAN_BUFFER = 0x8, + GX2R_BIND_VERTEX_BUFFER = 0x10, + GX2R_BIND_INDEX_BUFFER = 0x20, + GX2R_BIND_UNIFORM_BLOCK = 0x40, + GX2R_BIND_SHADER_PROGRAM = 0x80, + GX2R_BIND_STREAM_OUTPUT = 0x100, + GX2R_BIND_DISPLAY_LIST = 0x200, + GX2R_BIND_GS_RING = 0x400, + GX2R_USAGE_NONE = 0x0, + GX2R_USAGE_CPU_READ = 0x800, + GX2R_USAGE_CPU_WRITE = 0x1000, + GX2R_USAGE_GPU_READ = 0x2000, + GX2R_USAGE_GPU_WRITE = 0x4000, + GX2R_USAGE_DMA_READ = 0x8000, + GX2R_USAGE_DMA_WRITE = 0x10000, + GX2R_USAGE_FORCE_MEM1 = 0x20000, + GX2R_USAGE_FORCE_MEM2 = 0x40000, + GX2R_USAGE_MEM_DEFAULT = 0x0, + GX2R_USAGE_CPU_READWRITE = 0x1800, + GX2R_USAGE_GPU_READWRITE = 0x6000, + GX2R_USAGE_NON_CPU_WRITE = 0x14000, + GX2R_OPTION_NONE = 0x0, + GX2R_OPTION_IGNORE_IN_USE = 0x80000, + GX2R_OPTION_FIRST = 0x80000, + GX2R_OPTION_NO_CPU_INVALIDATE = 0x100000, + GX2R_OPTION_NO_GPU_INVALIDATE = 0x200000, + GX2R_OPTION_LOCK_READONLY = 0x400000, + GX2R_OPTION_NO_TOUCH_DESTROY = 0x800000, + GX2R_OPTION_LAST = 0x800000, + GX2R_OPTION_NO_INVALIDATE = 0x300000, + GX2R_OPTION_MASK = 0xF80000, + GX2R_RESOURCE_FLAG_RESERVED2 = 0x10000000, + GX2R_RESOURCE_FLAG_RESERVED1 = 0x20000000, + GX2R_RESOURCE_FLAG_RESERVED0 = 0x40000000, + }; + public enum GX2TileMode + { + GX2_TILE_MODE_DEFAULT = 0x0, + GX2_TILE_MODE_LINEAR_SPECIAL = 0x10, + GX2_TILE_MODE_DEFAULT_FIX2197 = 0x20, + GX2_TILE_MODE_LINEAR_ALIGNED = 0x1, + GX2_TILE_MODE_1D_TILED_THIN1 = 0x2, + GX2_TILE_MODE_1D_TILED_THICK = 0x3, + GX2_TILE_MODE_2D_TILED_THIN1 = 0x4, + GX2_TILE_MODE_2D_TILED_THIN2 = 0x5, + GX2_TILE_MODE_2D_TILED_THIN4 = 0x6, + GX2_TILE_MODE_2D_TILED_THICK = 0x7, + GX2_TILE_MODE_2B_TILED_THIN1 = 0x8, + GX2_TILE_MODE_2B_TILED_THIN2 = 0x9, + GX2_TILE_MODE_2B_TILED_THIN4 = 0xA, + GX2_TILE_MODE_2B_TILED_THICK = 0xB, + GX2_TILE_MODE_3D_TILED_THIN1 = 0xC, + GX2_TILE_MODE_3D_TILED_THICK = 0xD, + GX2_TILE_MODE_3B_TILED_THIN1 = 0xE, + GX2_TILE_MODE_3B_TILED_THICK = 0xF, + GX2_TILE_MODE_FIRST = 0x0, + GX2_TILE_MODE_LAST = 0x20, + }; + + public enum AddrTileMode + { + ADDR_TM_LINEAR_GENERAL = 0x0, + ADDR_TM_LINEAR_ALIGNED = 0x1, + ADDR_TM_1D_TILED_THIN1 = 0x2, + ADDR_TM_1D_TILED_THICK = 0x3, + ADDR_TM_2D_TILED_THIN1 = 0x4, + ADDR_TM_2D_TILED_THIN2 = 0x5, + ADDR_TM_2D_TILED_THIN4 = 0x6, + ADDR_TM_2D_TILED_THICK = 0x7, + ADDR_TM_2B_TILED_THIN1 = 0x8, + ADDR_TM_2B_TILED_THIN2 = 0x9, + ADDR_TM_2B_TILED_THIN4 = 0x0A, + ADDR_TM_2B_TILED_THICK = 0x0B, + ADDR_TM_3D_TILED_THIN1 = 0x0C, + ADDR_TM_3D_TILED_THICK = 0x0D, + ADDR_TM_3B_TILED_THIN1 = 0x0E, + ADDR_TM_3B_TILED_THICK = 0x0F, + ADDR_TM_2D_TILED_XTHICK = 0x10, + ADDR_TM_3D_TILED_XTHICK = 0x11, + ADDR_TM_POWER_SAVE = 0x12, + ADDR_TM_COUNT = 0x13, + } + public enum AddrTileType + { + ADDR_DISPLAYABLE = 0, + ADDR_NON_DISPLAYABLE = 1, + ADDR_DEPTH_SAMPLE_ORDER = 2, + ADDR_THICK_TILING = 3, + } + public enum AddrPipeCfg + { + ADDR_PIPECFG_INVALID = 0x0, + ADDR_PIPECFG_P2 = 0x1, + ADDR_PIPECFG_P4_8x16 = 0x5, + ADDR_PIPECFG_P4_16x16 = 0x6, + ADDR_PIPECFG_P4_16x32 = 0x7, + ADDR_PIPECFG_P4_32x32 = 0x8, + ADDR_PIPECFG_P8_16x16_8x16 = 0x9, + ADDR_PIPECFG_P8_16x32_8x16 = 0xA, + ADDR_PIPECFG_P8_32x32_8x16 = 0xB, + ADDR_PIPECFG_P8_16x32_16x16 = 0xC, + ADDR_PIPECFG_P8_32x32_16x16 = 0xD, + ADDR_PIPECFG_P8_32x32_16x32 = 0xE, + ADDR_PIPECFG_P8_32x64_32x32 = 0xF, + ADDR_PIPECFG_MAX = 0x10, + } + public enum AddrFormat + { + ADDR_FMT_INVALID = 0x0, + ADDR_FMT_8 = 0x1, + ADDR_FMT_4_4 = 0x2, + ADDR_FMT_3_3_2 = 0x3, + ADDR_FMT_RESERVED_4 = 0x4, + ADDR_FMT_16 = 0x5, + ADDR_FMT_16_FLOAT = 0x6, + ADDR_FMT_8_8 = 0x7, + ADDR_FMT_5_6_5 = 0x8, + ADDR_FMT_6_5_5 = 0x9, + ADDR_FMT_1_5_5_5 = 0xA, + ADDR_FMT_4_4_4_4 = 0xB, + ADDR_FMT_5_5_5_1 = 0xC, + ADDR_FMT_32 = 0xD, + ADDR_FMT_32_FLOAT = 0xE, + ADDR_FMT_16_16 = 0xF, + ADDR_FMT_16_16_FLOAT = 0x10, + ADDR_FMT_8_24 = 0x11, + ADDR_FMT_8_24_FLOAT = 0x12, + ADDR_FMT_24_8 = 0x13, + ADDR_FMT_24_8_FLOAT = 0x14, + ADDR_FMT_10_11_11 = 0x15, + ADDR_FMT_10_11_11_FLOAT = 0x16, + ADDR_FMT_11_11_10 = 0x17, + ADDR_FMT_11_11_10_FLOAT = 0x18, + ADDR_FMT_2_10_10_10 = 0x19, + ADDR_FMT_8_8_8_8 = 0x1A, + ADDR_FMT_10_10_10_2 = 0x1B, + ADDR_FMT_X24_8_32_FLOAT = 0x1C, + ADDR_FMT_32_32 = 0x1D, + ADDR_FMT_32_32_FLOAT = 0x1E, + ADDR_FMT_16_16_16_16 = 0x1F, + ADDR_FMT_16_16_16_16_FLOAT = 0x20, + ADDR_FMT_RESERVED_33 = 0x21, + ADDR_FMT_32_32_32_32 = 0x22, + ADDR_FMT_32_32_32_32_FLOAT = 0x23, + ADDR_FMT_RESERVED_36 = 0x24, + ADDR_FMT_1 = 0x25, + ADDR_FMT_1_REVERSED = 0x26, + ADDR_FMT_GB_GR = 0x27, + ADDR_FMT_BG_RG = 0x28, + ADDR_FMT_32_AS_8 = 0x29, + ADDR_FMT_32_AS_8_8 = 0x2A, + ADDR_FMT_5_9_9_9_SHAREDEXP = 0x2B, + ADDR_FMT_8_8_8 = 0x2C, + ADDR_FMT_16_16_16 = 0x2D, + ADDR_FMT_16_16_16_FLOAT = 0x2E, + ADDR_FMT_32_32_32 = 0x2F, + ADDR_FMT_32_32_32_FLOAT = 0x30, + ADDR_FMT_BC1 = 0x31, + ADDR_FMT_BC2 = 0x32, + ADDR_FMT_BC3 = 0x33, + ADDR_FMT_BC4 = 0x34, + ADDR_FMT_BC5 = 0x35, + ADDR_FMT_BC6 = 0x36, + ADDR_FMT_BC7 = 0x37, + ADDR_FMT_32_AS_32_32_32_32 = 0x38, + ADDR_FMT_APC3 = 0x39, + ADDR_FMT_APC4 = 0x3A, + ADDR_FMT_APC5 = 0x3B, + ADDR_FMT_APC6 = 0x3C, + ADDR_FMT_APC7 = 0x3D, + ADDR_FMT_CTX1 = 0x3E, + ADDR_FMT_RESERVED_63 = 0x3F, + }; + + private static byte[] formatHwInfo = { + 0x00, 0x00, 0x00, 0x01, 0x08, 0x03, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00, 0x10, 0x03, 0x00, 0x01, 0x10, 0x03, 0x00, 0x01, + 0x10, 0x0B, 0x00, 0x01, 0x10, 0x01, 0x00, 0x01, 0x10, 0x03, 0x00, 0x01, 0x10, 0x03, 0x00, 0x01, + 0x10, 0x03, 0x00, 0x01, 0x20, 0x03, 0x00, 0x00, 0x20, 0x07, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, + 0x20, 0x03, 0x00, 0x01, 0x20, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x20, 0x0B, 0x00, 0x01, 0x20, 0x0B, 0x00, 0x01, 0x20, 0x0B, 0x00, 0x01, + 0x40, 0x05, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, + 0x40, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x00, 0x00, + 0x10, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, + 0x60, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x01, 0x80, 0x01, 0x00, 0x01, 0x80, 0x01, 0x00, 0x01, + 0x40, 0x01, 0x00, 0x01, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + /*--------------------------------------- + * + * Code ported from AboodXD's GTX Extractor https://github.com/aboood40091/GTX-Extractor/blob/master/gtx_extract.py + * + * With help by Aelan! + * + *---------------------------------------*/ + + /*var s_textureFormats[] = { + // internalFormat, gxFormat, glFormat, fourCC, nutFormat, name, bpp, compressed + { FORMAT_RGBA_8888, GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM, GL_RGBA8, 0x00000000, 0x11, "RGBA_8888", 0x20, 0 }, + { FORMAT_ABGR_8888, GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM, GL_RGBA8, 0x00000000, 0x0E, "ABGR_8888 (WIP)", 0x20, 0 }, + { FORMAT_DXT1, GX2_SURFACE_FORMAT_T_BC1_UNORM, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0x31545844, 0x00, "DXT1", 0x40, 1 }, + { FORMAT_DXT3, GX2_SURFACE_FORMAT_T_BC2_UNORM, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0x33545844, 0x01, "DXT3", 0x80, 1 }, + { FORMAT_DXT5, GX2_SURFACE_FORMAT_T_BC3_UNORM, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0x35545844, 0x02, "DXT5", 0x80, 1 }, + { FORMAT_ATI1, GX2_SURFACE_FORMAT_T_BC4_UNORM, GL_COMPRESSED_RED_RGTC1, 0x31495441, 0x15, "ATI1", 0x40, 1 }, + { FORMAT_ATI2, GX2_SURFACE_FORMAT_T_BC5_UNORM, GL_COMPRESSED_RG_RGTC2, 0x32495441, 0x16, "ATI2", 0x80, 1 }, + { FORMAT_INVALID, GX2_SURFACE_FORMAT_INVALID, 0, 0xFFFFFFFF, 0x00, nullptr, 0x00, 0 } + };*/ + + + public static byte[] swizzleBC(byte[] data, int width, int height, int format, int tileMode, int pitch, int swizzle) + { + GX2Surface sur = new GX2Surface(); + sur.width = width; + sur.height = height; + sur.tileMode = tileMode; + sur.format = format; + sur.swizzle = swizzle; + sur.pitch = pitch; + sur.data = data; + sur.imageSize = data.Length; + //return swizzleBC(sur); + return swizzleSurface(sur, (GX2SurfaceFormat)sur.format != GX2SurfaceFormat.GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM & + (GX2SurfaceFormat)sur.format != GX2SurfaceFormat.GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB); + } + + public static int getBPP(int i) + { + switch ((GX2SurfaceFormat)i) + { + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM: + return 0x10; + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM: + return 0x20; + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC1_UNORM: + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC4_UNORM: + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC1_SRGB: + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC4_SNORM: + return 0x40; + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC2_UNORM: + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC3_UNORM: + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC5_UNORM: + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC2_SRGB: + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC3_SRGB: + case GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC5_SNORM: + return 0x80; + } + return -1; + } + + public static byte[] swizzleSurface(GX2Surface surface, bool isCompressed) + { + byte[] original = new byte[surface.data.Length]; + + surface.data.CopyTo(original, 0); + + int swizzle = ((surface.swizzle >> 8) & 1) + (((surface.swizzle >> 9) & 3) << 1); + int blockSize; + int width = surface.width; + int height = surface.height; + + int format = getBPP(surface.format); + Console.WriteLine(((GX2SurfaceFormat)surface.format).ToString()); + + if (isCompressed) + { + width /= 4; + height /= 4; + + if ((GX2SurfaceFormat)surface.format == GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC1_UNORM || + (GX2SurfaceFormat)surface.format == GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC1_SRGB || + (GX2SurfaceFormat)surface.format == GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC4_UNORM || + (GX2SurfaceFormat)surface.format == GX2SurfaceFormat.GX2_SURFACE_FORMAT_T_BC4_SNORM) + { + blockSize = 8; + } + else + { + blockSize = 16; + } + } + else + { + /*if ((GX2SurfaceFormat)surface.format == GX2SurfaceFormat.GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM) + { + blockSize = format / 4; + } + else*/ + blockSize = format / 8; + } + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int pos = surfaceAddrFromCoordMacroTiled(x, y, format, surface.pitch, swizzle); + int pos_ = (y * width + x) * blockSize; + + for (int k = 0; k < blockSize; k++) + { + if (pos + k >= original.Length || pos_ + k >= surface.data.Length) + { + Console.WriteLine("Break Point " + pos_ + " " + pos); + break; + } + surface.data[pos_ + k] = original[pos + k]; + } + } + } + return surface.data; + } + + public static int surfaceAddrFromCoordMacroTiled(int x, int y, int bpp, int pitch, int swizzle) + { + int pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp); + int elemOffset = (bpp * pixelIndex) >> 3; + + int pipe = computePipeFromCoordWoRotation(x, y); + int bank = computeBankFromCoordWoRotation(x, y); + int bankPipe = ((pipe + 2 * bank) ^ swizzle) % 9; + + pipe = bankPipe % 2; + bank = bankPipe / 2; + + int macroTileBytes = (bpp * 512 + 7) >> 3; + int macroTileOffset = (x / 32 + pitch / 32 * (y / 16)) * macroTileBytes; + + int unk1 = elemOffset + (macroTileOffset >> 3); + int unk2 = unk1 & ~0xFF; + + return (unk2 << 3) | (0xFF & unk1) | (pipe << 8) | (bank << 9); + } + + public static int computePixelIndexWithinMicroTile(int x, int y, int bpp) + { + int bits = ((x & 4) << 1) | ((y & 2) << 3) | ((y & 4) << 3); + + if (bpp == 0x20 || bpp == 0x60) + { + bits |= (x & 1) | (x & 2) | ((y & 1) << 2); + } + else if (bpp == 0x40) + { + bits |= (x & 1) | ((y & 1) << 1) | ((x & 2) << 1); + } + else if (bpp == 0x80) + { + bits |= (y & 1) | ((x & 1) << 1) | ((x & 2) << 1); + } + + return bits; + } + + public static int getFormatBpp(int format) + { + int hwFormat = format & 0x3F; + return formatHwInfo[hwFormat * 4]; + } + + public static int computeSurfaceThickness(AddrTileMode tileMode) + { + switch (tileMode) + { + case AddrTileMode.ADDR_TM_1D_TILED_THICK: + case AddrTileMode.ADDR_TM_2D_TILED_THICK: + case AddrTileMode.ADDR_TM_2B_TILED_THICK: + case AddrTileMode.ADDR_TM_3D_TILED_THICK: + case AddrTileMode.ADDR_TM_3B_TILED_THICK: + { + return 4; + } + case AddrTileMode.ADDR_TM_2D_TILED_XTHICK: + case AddrTileMode.ADDR_TM_3D_TILED_XTHICK: + { + return 8; + } + } + + return 1; + } + + public static int isThickMacroTiled(AddrTileMode tileMode) + { + switch (tileMode) + { + case AddrTileMode.ADDR_TM_2D_TILED_THICK: + case AddrTileMode.ADDR_TM_2B_TILED_THICK: + case AddrTileMode.ADDR_TM_3D_TILED_THICK: + case AddrTileMode.ADDR_TM_3B_TILED_THICK: + { + return 1; + } + } + + return 0; + } + + public static int isBankSwappedTileMode(AddrTileMode tileMode) + { + switch (tileMode) + { + case AddrTileMode.ADDR_TM_2B_TILED_THIN1: + case AddrTileMode.ADDR_TM_2B_TILED_THIN2: + case AddrTileMode.ADDR_TM_2B_TILED_THIN4: + case AddrTileMode.ADDR_TM_2B_TILED_THICK: + case AddrTileMode.ADDR_TM_3B_TILED_THIN1: + case AddrTileMode.ADDR_TM_3B_TILED_THICK: + { + return 1; + } + } + return 0; + } + + public static int computeSurfaceRotationFromTileMode(AddrTileMode tileMode) + { + switch ((int)tileMode) + { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + { + return 2; + } + case 12: + case 13: + case 14: + case 15: + { + return 1; + } + } + + return 0; + } + + public static int computePipeFromCoordWoRotation(int x, int y) + { + int pipe = ((y >> 3) ^ (x >> 3)) & 1; + return pipe; + } + + public static int computeBankFromCoordWoRotation(int x, int y) + { + int bankBit0 = ((y / (16 * 2)) ^ (x >> 3)) & 1; + int bank = bankBit0 | 2 * (((y / (8 * 2)) ^ (x >> 4)) & 1); + + return bank; + } + + public static int computeMacroTileAspectRatio(AddrTileMode tileMode) + { + switch (tileMode) + { + case AddrTileMode.ADDR_TM_2B_TILED_THIN1: + case AddrTileMode.ADDR_TM_3D_TILED_THIN1: + case AddrTileMode.ADDR_TM_3B_TILED_THIN1: + { + return 1; + } + case AddrTileMode.ADDR_TM_2D_TILED_THIN2: + case AddrTileMode.ADDR_TM_2B_TILED_THIN2: + { + return 2; + } + case AddrTileMode.ADDR_TM_2D_TILED_THIN4: + case AddrTileMode.ADDR_TM_2B_TILED_THIN4: + { + return 4; + } + } + + return 1; + } + + public static int computeSurfaceBankSwappedWidth(AddrTileMode tileMode, int bpp, int numSamples, int pitch, int pSlicesPerTile) + { + int bankSwapWidth = 0; + int numBanks = 4; + int numPipes = 2; + int swapSize = 256; + int rowSize = 2048; + int splitSize = 2048; + int groupSize = 256; + int slicesPerTile = 1; + int bytesPerSample = 8 * bpp & 0x1FFFFFFF; + int samplesPerTile = splitSize / bytesPerSample; + + if ((splitSize / bytesPerSample) != 0) + { + slicesPerTile = numSamples / samplesPerTile; + if ((numSamples / samplesPerTile) == 0) + { + slicesPerTile = 1; + } + } + + if (pSlicesPerTile != 0) + { + pSlicesPerTile = slicesPerTile; + } + + if (isThickMacroTiled(tileMode) == 1) + { + numSamples = 4; + } + + int bytesPerTileSlice = numSamples * bytesPerSample / slicesPerTile; + + if (isBankSwappedTileMode(tileMode) == 1) + { + int v7; + int v8; + int v9; + + int factor = computeMacroTileAspectRatio(tileMode); + int swapTiles = (swapSize >> 1) / bpp; + + if (swapTiles != 0) + { + v9 = swapTiles; + } + else + { + v9 = 1; + } + + int swapWidth = v9 * 8 * numBanks; + int heightBytes = numSamples * factor * numPipes * bpp / slicesPerTile; + int swapMax = numPipes * numBanks * rowSize / heightBytes; + int swapMin = groupSize * 8 * numBanks / bytesPerTileSlice; + + if (swapMax >= swapWidth) + { + if (swapMin <= swapWidth) + { + v7 = swapWidth; + } + else + { + v7 = swapMin; + } + + v8 = v7; + } + else + { + v8 = swapMax; + } + + bankSwapWidth = v8; + + while (bankSwapWidth >= (2 * pitch)) + { + bankSwapWidth >>= 1; + } + } + + return bankSwapWidth; + } + + public static int computePixelIndexWithinMicroTile(int x, int y, int z, int bpp, AddrTileMode tileMode, int microTileType) + { + int pixelBit0 = 0; + int pixelBit1 = 0; + int pixelBit2 = 0; + int pixelBit3 = 0; + int pixelBit4 = 0; + int pixelBit5 = 0; + int pixelBit6 = 0; + int pixelBit7 = 0; + int pixelBit8 = 0; + int thickness = computeSurfaceThickness(tileMode); + + if (microTileType == 3) + { + pixelBit0 = x & 1; + pixelBit1 = y & 1; + pixelBit2 = z & 1; + pixelBit3 = (x & 2) >> 1; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (z & 2) >> 1; + pixelBit6 = (x & 4) >> 2; + pixelBit7 = (y & 4) >> 2; + } + else + { + if (microTileType != 0) + { + pixelBit0 = x & 1; + pixelBit1 = y & 1; + pixelBit2 = (x & 2) >> 1; + pixelBit3 = (y & 2) >> 1; + pixelBit4 = (x & 4) >> 2; + pixelBit5 = (y & 4) >> 2; + } + else + { + if (bpp == 0x08) + { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = (x & 4) >> 2; + pixelBit3 = (y & 2) >> 1; + pixelBit4 = y & 1; + pixelBit5 = (y & 4) >> 2; + } + else if (bpp == 0x10) + { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = (x & 4) >> 2; + pixelBit3 = y & 1; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + else if (bpp == 0x20 || bpp == 0x60) + { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = y & 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + else if (bpp == 0x40) + { + pixelBit0 = x & 1; + pixelBit1 = y & 1; + pixelBit2 = (x & 2) >> 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + else if (bpp == 0x80) + { + pixelBit0 = y & 1; + pixelBit1 = x & 1; + pixelBit2 = (x & 2) >> 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + else + { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = y & 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + } + + if (thickness > 1) + { + pixelBit6 = z & 1; + pixelBit7 = (z & 2) >> 1; + } + } + + if (thickness == 8) + { + pixelBit8 = (z & 4) >> 2; + } + + return pixelBit0 | + (pixelBit8 << 8) | + (pixelBit7 << 7) | + (pixelBit6 << 6) | + (pixelBit5 << 5) | + (pixelBit4 << 4) | + (pixelBit3 << 3) | + (pixelBit2 << 2) | + (pixelBit1 << 1); + } + + public static int surfaceAddrFromCoordMacroTiled( + int x, int y, int slice, int sample, int bpp, + int pitch, int height, int numSamples, AddrTileMode tileMode, + int isDepth, int tileBase, int compBits, + int pipeSwizzle, int bankSwizzle + ) + { + const int numPipes = 2; + const int numBanks = 4; + const int numGroupBits = 8; + const int numPipeBits = 1; + const int numBankBits = 2; + + int microTileThickness = computeSurfaceThickness(tileMode); + int microTileBits = numSamples * bpp * (microTileThickness * (8 * 8)); + int microTileBytes = microTileBits >> 3; + int microTileType = (isDepth == 1) ? 1 : 0; + int pixelIndex = computePixelIndexWithinMicroTile(x, y, slice, bpp, tileMode, microTileType); + + int sampleOffset; + int pixelOffset; + if (isDepth == 1) + { + if (compBits != 0 && compBits != bpp) + { + sampleOffset = tileBase + compBits * sample; + pixelOffset = numSamples * compBits * pixelIndex; + } + else + { + sampleOffset = bpp * sample; + pixelOffset = numSamples * compBits * pixelIndex; + } + } + else + { + sampleOffset = sample * (microTileBits / numSamples); + pixelOffset = bpp * pixelIndex; + } + + int elemOffset = pixelOffset + sampleOffset; + int bytesPerSample = microTileBytes / numSamples; + + int samplesPerSlice; + int numSampleSplits; + int sampleSlice; + + if (numSamples <= 1 || microTileBytes <= 2048) + { + samplesPerSlice = numSamples; + numSampleSplits = 1; + sampleSlice = 0; + } + else + { + samplesPerSlice = 2048 / bytesPerSample; + numSampleSplits = numSamples / samplesPerSlice; + numSamples = samplesPerSlice; + sampleSlice = elemOffset / (microTileBits / numSampleSplits); + elemOffset %= microTileBits / numSampleSplits; + } + + elemOffset >>= 3; + + int pipe = computePipeFromCoordWoRotation(x, y); + int bank = computeBankFromCoordWoRotation(x, y); + int bankPipe = pipe + numPipes * bank; + int rotation = computeSurfaceRotationFromTileMode(tileMode); + int swizzle = pipeSwizzle + numPipes * bankSwizzle; + int sliceIn = slice; + + if (isThickMacroTiled(tileMode) == 1) + { + sliceIn >>= 2; + } + + bankPipe ^= numPipes * sampleSlice * ((numBanks >> 1) + 1) ^ (swizzle + sliceIn * rotation); + bankPipe %= numPipes * numBanks; + pipe = bankPipe % numPipes; + bank = bankPipe / numPipes; + + int sliceBytes = (height * pitch * microTileThickness * bpp * numSamples + 7) / 8; + int sliceOffset = sliceBytes * ((sampleSlice + numSampleSplits * slice) / microTileThickness); + int macroTilePitch = 8 * 4; // m_banks + int macroTileHeight = 8 * 2; // m_pipes + int v18 = (int)tileMode - 5; + + if ((int)tileMode == 5 || (int)tileMode == 9) + { + macroTilePitch >>= 1; + macroTileHeight *= 2; + } + else if ((int)tileMode == 6 || (int)tileMode == 10) + { + macroTilePitch >>= 2; + macroTileHeight *= 4; + } + + int macroTilesPerRow = pitch / macroTilePitch; + int macroTileBytes = (numSamples * microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) >> 3; + int macroTileIndexX = x / macroTilePitch; + int macroTileIndexY = y / macroTileHeight; + int macroTileOffset = (x / macroTilePitch + pitch / macroTilePitch * (y / macroTileHeight)) * macroTileBytes; + + int bankSwapWidth; + int swapIndex; + int bankMask; + + byte[] bankSwapOrder = { 0, 1, 3, 2 }; + switch ((int)tileMode) + { + case 8: + case 9: + case 10: + case 11: + case 14: + case 15: + { + bankSwapWidth = computeSurfaceBankSwappedWidth(tileMode, bpp, numSamples, pitch, 0); + swapIndex = macroTilePitch * macroTileIndexX / bankSwapWidth; + bankMask = 3; // m_banks-1 + bank ^= bankSwapOrder[swapIndex & bankMask]; + } + break; + } + + int p4 = pipe << numGroupBits; + int p5 = bank << (numPipeBits + numGroupBits); + int numSwizzleBits = numBankBits + numPipeBits; + int unk1 = (macroTileOffset + sliceOffset) >> numSwizzleBits; + int unk2 = ~((1 << numGroupBits) - 1); + int unk3 = (elemOffset + unk1) & unk2; + int groupMask = (1 << numGroupBits) - 1; + int offset1 = macroTileOffset + sliceOffset; + int unk4 = elemOffset + (offset1 >> numSwizzleBits); + + int subOffset1 = unk3 << numSwizzleBits; + int subOffset2 = groupMask & unk4; + + return subOffset1 | subOffset2 | p4 | p5; + } + + public static byte[] swizzleBC(GX2Surface surface) + { + //std::vector result; + //List result = new List(); + + //result.resize(surface->imageSize); + + //u8 *data = (u8*)surface->imagePtr; + byte[] data = surface.data; + byte[] result = new byte[surface.imageSize]; + + int width = surface.width / 4; + int height = surface.height / 4; + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + int bpp = getFormatBpp(surface.format); + int pos = 0; + + switch (surface.tileMode) + { + case 0: + case 1: + { + // pos = surfaceAddrFromCoordLinear( + // x, y, 0, 0, bpp, + // surface->pitch, height, surface->depth, 0 + // ); + + //printf("Unsupported tilemode %d\n", surface->tileMode); + //exit(1); + } + break; + + case 2: + case 3: + { + // pos = surfaceAddrFromCoordMicroTiled( + // x, y, 0, bpp, surface->pitch, height, + // surface->tileMode, 0, 0, 0, 0 + // ); + + //printf("Unsupported tilemode %d\n", surface->tileMode); + //exit(1); + } + break; + + default: + { + pos = surfaceAddrFromCoordMacroTiled( + x, y, 0, 0, bpp, surface.pitch, height, + 1, (AddrTileMode)surface.tileMode, 0, 0, 0, + (surface.swizzle >> 8) & 1, + (surface.swizzle >> 9) & 3 + ); + } + break; + } + + int q = y * width + x; + switch (surface.format) + { + case 0x31: + case 0x34: + case 0x234: + case 0x431: + { + System.Array.Copy(data, pos, result, q * 8, 8); + //memcpy(result.data() + q*8, data+pos, 8); + } + break; + + default: + { + System.Array.Copy(data, pos, result, q * 16, 16); + //memcpy(result.data() + q*16, data+pos, 16); + } + break; + } + } + } + + return result; + + //memcpy(data, result.data(), result.size()); + } + } +} diff --git a/Switch_FileFormatsMain/FileFormats/Texture/TexConv.cs b/Switch_FileFormatsMain/FileFormats/Texture/TexConv.cs new file mode 100644 index 00000000..c4281b14 --- /dev/null +++ b/Switch_FileFormatsMain/FileFormats/Texture/TexConv.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FirstPlugin +{ + class TexConv + { + + } +} diff --git a/Switch_FileFormatsMain/FileFormats/Texture/XTX.cs b/Switch_FileFormatsMain/FileFormats/Texture/XTX.cs index 1b27c060..3e662faf 100644 --- a/Switch_FileFormatsMain/FileFormats/Texture/XTX.cs +++ b/Switch_FileFormatsMain/FileFormats/Texture/XTX.cs @@ -20,11 +20,12 @@ namespace FirstPlugin public CompressionType CompressionType { get; set; } = CompressionType.None; public byte[] Data { get; set; } public string FileName { get; set; } - public TreeNode EditorRoot { get; set; } + public TreeNodeFile EditorRoot { get; set; } public bool IsActive { get; set; } = false; public bool UseEditMenu { get; set; } = false; - public int Alignment { get; set; } = 0; public string FilePath { get; set; } + public IFileInfo IFileInfo { get; set; } + public Type[] Types { get diff --git a/Switch_FileFormatsMain/FileFormats/GL/BFRES_Render.cs b/Switch_FileFormatsMain/GL/BFRES_Render.cs similarity index 100% rename from Switch_FileFormatsMain/FileFormats/GL/BFRES_Render.cs rename to Switch_FileFormatsMain/GL/BFRES_Render.cs