From 090495c2883b4361b3edafbb6733b1d956cd19e6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:35:43 +0000 Subject: [PATCH] SAModel Cleanup - Part 1 (#276) * GCAttach * Remaining GC Cleanup * Ninja Binary Cleanup * XJ --- .../SAEditorCommon/Import/AssimpStuff.cs | 48 +- Libraries/SAModel/GC/GCAttach.cs | 842 +++++++++++------- Libraries/SAModel/GC/GCEnums.cs | 68 +- Libraries/SAModel/GC/GCMesh.cs | 308 ++++--- Libraries/SAModel/GC/GCParameter.cs | 350 +++----- Libraries/SAModel/GC/GCPrimitive.cs | 303 +++---- Libraries/SAModel/GC/GCVertexSet.cs | 305 ++++--- Libraries/SAModel/GC/Structs.cs | 301 +++---- Libraries/SAModel/Ninja Binary/NJReader.cs | 225 ++--- Libraries/SAModel/Ninja Binary/NJTLHelper.cs | 36 +- Libraries/SAModel/Ninja Binary/POF0Helper.cs | 99 +- Libraries/SAModel/VertexData.cs | 6 +- Libraries/SAModel/XJ/XJAttach.cs | 131 ++- Libraries/SAModel/XJ/XJMesh.cs | 147 +-- Libraries/SAModel/XJ/XJVertexSet.cs | 116 ++- SAMDL/MainForm.cs | 46 +- 16 files changed, 1743 insertions(+), 1588 deletions(-) diff --git a/Libraries/SAEditorCommon/Import/AssimpStuff.cs b/Libraries/SAEditorCommon/Import/AssimpStuff.cs index 02ee9e86e..84453eab0 100644 --- a/Libraries/SAEditorCommon/Import/AssimpStuff.cs +++ b/Libraries/SAEditorCommon/Import/AssimpStuff.cs @@ -426,12 +426,12 @@ public static List ExportAttachMeshes(this Attach attach, Scene scene, stri if (attach is GC.GCAttach gcAttach) { int nameMeshIndex = 0; - foreach (GC.GCMesh m in gcAttach.opaqueMeshes) + foreach (GC.GCMesh m in gcAttach.OpaqueMeshes) { result.Add(scene.Meshes.Count); scene.Meshes.Add(ExportGCMesh(gcAttach, m, scene, texInfo, ref nameMeshIndex)); } - foreach (GC.GCMesh m in gcAttach.translucentMeshes) + foreach (GC.GCMesh m in gcAttach.TranslucentMeshes) { result.Add(scene.Meshes.Count); scene.Meshes.Add(ExportGCMesh(gcAttach, m, scene, texInfo, ref nameMeshIndex)); @@ -511,13 +511,13 @@ private static Assimp.Mesh ExportGCMesh(GC.GCAttach gcAttach, GC.GCMesh m, Scene List normals = new List(); List texcoords = new List(); List colors = new List(); - foreach (GC.GCParameter param in m.parameters) + foreach (GC.GCParameter param in m.Parameters) { - if (param.type == GC.ParameterType.Texture) + if (param.Type == GC.ParameterType.Texture) { GC.TextureParameter tex = param as GC.TextureParameter; MaterialBuffer.UseTexture = true; - MaterialBuffer.TextureID = tex.TextureID; + MaterialBuffer.TextureID = tex.TextureId; if (tex.Tile.HasFlag(GC.GCTileMode.MirrorU)) MaterialBuffer.FlipU = true; if (tex.Tile.HasFlag(GC.GCTileMode.MirrorV)) @@ -530,14 +530,14 @@ private static Assimp.Mesh ExportGCMesh(GC.GCAttach gcAttach, GC.GCMesh m, Scene MaterialBuffer.ClampU &= tex.Tile.HasFlag(GC.GCTileMode.Unk_1); MaterialBuffer.ClampV &= tex.Tile.HasFlag(GC.GCTileMode.Unk_1); } - else if (param.type == GC.ParameterType.TexCoordGen) + else if (param.Type == GC.ParameterType.TexCoordGen) { GC.TexCoordGenParameter gen = param as GC.TexCoordGenParameter; if (gen.TexGenSrc == GC.GCTexGenSrc.Normal) MaterialBuffer.EnvironmentMap = true; else MaterialBuffer.EnvironmentMap = false; } - else if (param.type == GC.ParameterType.BlendAlpha) + else if (param.Type == GC.ParameterType.BlendAlpha) { GC.BlendAlphaParameter blend = param as GC.BlendAlphaParameter; MaterialBuffer.SourceAlpha = blend.NJSourceAlpha; @@ -545,12 +545,12 @@ private static Assimp.Mesh ExportGCMesh(GC.GCAttach gcAttach, GC.GCMesh m, Scene } } - List gcPositions = gcAttach.vertexData.Find(x => x.attribute == GC.GCVertexAttribute.Position)?.data; - List gcNormals = gcAttach.vertexData.Find(x => x.attribute == GC.GCVertexAttribute.Normal)?.data; - List gcColors = gcAttach.vertexData.Find(x => x.attribute == GC.GCVertexAttribute.Color0)?.data; - List gcUVs = gcAttach.vertexData.Find(x => x.attribute == GC.GCVertexAttribute.Tex0)?.data; + List gcPositions = gcAttach.VertexData.Find(x => x.Attribute == GC.GCVertexAttribute.Position)?.Data; + List gcNormals = gcAttach.VertexData.Find(x => x.Attribute == GC.GCVertexAttribute.Normal)?.Data; + List gcColors = gcAttach.VertexData.Find(x => x.Attribute == GC.GCVertexAttribute.Color0)?.Data; + List gcUVs = gcAttach.VertexData.Find(x => x.Attribute == GC.GCVertexAttribute.Tex0)?.Data; - foreach (GC.GCPrimitive prim in m.primitives) + foreach (GC.GCPrimitive prim in m.Primitives) { for (int i = 0; i < prim.ToTriangles().Count; i += 3) { @@ -559,11 +559,11 @@ private static Assimp.Mesh ExportGCMesh(GC.GCAttach gcAttach, GC.GCMesh m, Scene for (int j = 0; j < 3; j++) { GC.Vector3 vertex = (GC.Vector3)gcPositions[prim.ToTriangles()[i + j].PositionIndex]; - positions.Add(new Vector3D(vertex.x, vertex.y, vertex.z)); + positions.Add(new Vector3D(vertex.X, vertex.Y, vertex.Z)); if (gcNormals != null) { GC.Vector3 normal = (GC.Vector3)gcNormals[prim.ToTriangles()[i + j].NormalIndex]; - normals.Add(new Vector3D(normal.x, normal.y, normal.z)); + normals.Add(new Vector3D(normal.X, normal.Y, normal.Z)); } if (gcUVs != null) { @@ -1492,7 +1492,7 @@ private static GC.GCAttach AssimpImportGC(List materials, List materials, List 0) { GC.GCVertexSet vtxUV = new GC.GCVertexSet(GC.GCVertexAttribute.Tex0); - vtxUV.data.AddRange(texcoords); - attach.vertexData.Add(vtxUV); + vtxUV.Data.AddRange(texcoords); + attach.VertexData.Add(vtxUV); } if (colors.Count > 0) { GC.GCVertexSet vtxColors = new GC.GCVertexSet(GC.GCVertexAttribute.Color0); - vtxColors.data.AddRange(colors); - attach.vertexData.Add(vtxColors); + vtxColors.Data.AddRange(colors); + attach.VertexData.Add(vtxColors); } else { GC.GCVertexSet vtxNormals = new GC.GCVertexSet(GC.GCVertexAttribute.Normal); - vtxNormals.data.AddRange(gcnormals); - attach.vertexData.Add(vtxNormals); + vtxNormals.Data.AddRange(gcnormals); + attach.VertexData.Add(vtxNormals); } - attach.opaqueMeshes.AddRange(gcmeshes); + attach.OpaqueMeshes.AddRange(gcmeshes); return attach; } #endregion diff --git a/Libraries/SAModel/GC/GCAttach.cs b/Libraries/SAModel/GC/GCAttach.cs index 9c747e34e..e17543437 100644 --- a/Libraries/SAModel/GC/GCAttach.cs +++ b/Libraries/SAModel/GC/GCAttach.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; namespace SAModel.GC @@ -14,19 +15,19 @@ public class GCAttach : Attach /// /// The seperate sets of vertex data in this attach /// - public List vertexData; + public List VertexData; public string VertexName { get; set; } - public uint gap { get; set; } + public uint Gap { get; set; } /// /// The meshes with opaque rendering properties /// - public List opaqueMeshes; + public List OpaqueMeshes; public string OpaqueMeshName { get; set; } /// /// The meshes with translucent rendering properties /// - public List translucentMeshes; + public List TranslucentMeshes; public string TranslucentMeshName { get; set; } /// @@ -34,25 +35,26 @@ public class GCAttach : Attach /// public GCAttach() { - Name = "gcattach_" + Extensions.GenerateIdentifier(); + Name = $"gcattach_{Extensions.GenerateIdentifier()}"; - vertexData = new List(); - VertexName = "vertex_" + Extensions.GenerateIdentifier(); + VertexData = []; + VertexName = $"vertex_{Extensions.GenerateIdentifier()}"; Bounds = new BoundingSphere(); } - public GCAttach(bool HasOPoly, bool HasTPoly) + public GCAttach(bool hasOPoly, bool hasTPoly) :this() { - if (HasOPoly) + if (hasOPoly) { - opaqueMeshes = new List(); - OpaqueMeshName = "opoly_" + Extensions.GenerateIdentifier(); + OpaqueMeshes = []; + OpaqueMeshName = $"opoly_{Extensions.GenerateIdentifier()}"; } - if (HasTPoly) + + if (hasTPoly) { - translucentMeshes = new List(); - TranslucentMeshName = "tpoly_" + Extensions.GenerateIdentifier(); + TranslucentMeshes = []; + TranslucentMeshName = $"tpoly_{Extensions.GenerateIdentifier()}"; } } @@ -68,74 +70,105 @@ public GCAttach(byte[] file, int address, uint imageBase) : this(file, address, imageBase, new Dictionary()) { } + public GCAttach(byte[] file, int address, uint imageBase, Dictionary labels) { - if (labels.ContainsKey(address)) - Name = labels[address]; + if (labels.TryGetValue(address, out var name)) + { + Name = name; + } else + { Name = "attach_" + address.ToString("X8"); + } // The struct is 36/0x24 bytes long - uint vertexAddress = ByteConverter.ToUInt32(file, address) - imageBase; - uint gap = ByteConverter.ToUInt32(file, address + 4); - int opaqueAddress = (int)(ByteConverter.ToInt32(file, address + 8) - imageBase); - int translucentAddress = (int)(ByteConverter.ToInt32(file, address + 12) - imageBase); + var vertexAddress = ByteConverter.ToUInt32(file, address) - imageBase; + Gap = ByteConverter.ToUInt32(file, address + 4); + var opaqueAddress = (int)(ByteConverter.ToInt32(file, address + 8) - imageBase); + var translucentAddress = (int)(ByteConverter.ToInt32(file, address + 12) - imageBase); int opaqueCount = ByteConverter.ToInt16(file, address + 16); int translucentCount = ByteConverter.ToInt16(file, address + 18); Bounds = new BoundingSphere(file, address + 20); - // reading vertex data - vertexData = new List(); - GCVertexSet vertexSet = new GCVertexSet(file, vertexAddress, imageBase, labels); - if (labels.ContainsKey((int)vertexAddress)) - VertexName = labels[(int)vertexAddress]; + // Reading vertex data + VertexData = []; + var vertexSet = new GCVertexSet(file, vertexAddress, imageBase, labels); + + if (labels.TryGetValue((int)vertexAddress, out var vertexName)) + { + VertexName = vertexName; + } else - VertexName = "vertex_" + vertexAddress.ToString("X8"); - while (vertexSet.attribute != GCVertexAttribute.Null) { - vertexData.Add(vertexSet); + VertexName = $"vertex_{vertexAddress:X8}"; + } + + while (vertexSet.Attribute != GCVertexAttribute.Null) + { + VertexData.Add(vertexSet); vertexAddress += 16; vertexSet = new GCVertexSet(file, vertexAddress, imageBase, labels); } - // reading geometry - GCIndexAttributeFlags indexFlags = GCIndexAttributeFlags.HasPosition; - opaqueMeshes = new List(); + + // Reading geometry + var indexFlags = GCIndexAttributeFlags.HasPosition; + OpaqueMeshes = []; + if (opaqueCount != 0) { - if (labels.ContainsKey(opaqueAddress)) - OpaqueMeshName = labels[opaqueAddress]; + if (labels.TryGetValue(opaqueAddress, out var oMeshName)) + { + OpaqueMeshName = oMeshName; + } else - OpaqueMeshName = "opoly_" + opaqueAddress.ToString("X8"); + { + OpaqueMeshName = $"opoly_{opaqueAddress:X8}"; + } } - for (int i = 0; i < opaqueCount; i++) + + for (var i = 0; i < opaqueCount; i++) { - GCMesh mesh = new GCMesh(file, opaqueAddress, imageBase, labels, indexFlags); + var mesh = new GCMesh(file, opaqueAddress, imageBase, labels, indexFlags); - GCIndexAttributeFlags? t = mesh.IndexFlags; - if (t.HasValue) indexFlags = t.Value; + var t = mesh.IndexFlags; + if (t.HasValue) + { + indexFlags = t.Value; + } - opaqueMeshes.Add(mesh); + OpaqueMeshes.Add(mesh); opaqueAddress += 16; } - translucentMeshes = new List(); + + TranslucentMeshes = []; + if (translucentCount != 0) { - if (labels.ContainsKey(translucentAddress)) - TranslucentMeshName = labels[translucentAddress]; + if (labels.TryGetValue(translucentAddress, out var tMeshName)) + { + TranslucentMeshName = tMeshName; + } else - TranslucentMeshName = "tpoly_" + translucentAddress.ToString("X8"); + { + TranslucentMeshName = $"tpoly_{translucentAddress:X8}"; + } } - for (int i = 0; i < translucentCount; i++) + + for (var i = 0; i < translucentCount; i++) { - GCMesh mesh = new GCMesh(file, translucentAddress, imageBase, labels, indexFlags); + var mesh = new GCMesh(file, translucentAddress, imageBase, labels, indexFlags); - GCIndexAttributeFlags? t = mesh.IndexFlags; - if (t.HasValue) indexFlags = t.Value; + var t = mesh.IndexFlags; + if (t.HasValue) + { + indexFlags = t.Value; + } - translucentMeshes.Add(mesh); + TranslucentMeshes.Add(mesh); translucentAddress += 16; } } @@ -144,191 +177,266 @@ public GCAttach(byte[] file, int address, uint imageBase, Dictionary /// The files imagebase - /// Unused + /// Unused /// The files labels + /// /// /// - public override byte[] GetBytes(uint imageBase, bool DX, Dictionary labels, List njOffsets, out uint address) + public override byte[] GetBytes(uint imageBase, bool dx, Dictionary labels, List njOffsets, out uint address) { - List result = new List(); - + var result = new List(); uint vertexAddress = 0; - if (vertexData != null && vertexData.Count > 0) + + if (VertexData != null && VertexData.Count > 0) { - if (labels.ContainsKey(VertexName)) - vertexAddress = labels[VertexName]; + if (labels.TryGetValue(VertexName, out var vertAddr)) + { + vertexAddress = vertAddr; + } else { - uint[] vdataAddrs = new uint[vertexData.Count]; - for (int i = 0; i < vertexData.Count; i++) + var vDataAddrs = new uint[VertexData.Count]; + for (var i = 0; i < VertexData.Count; i++) { { - if (labels.ContainsKey(vertexData[i].DataName)) - vdataAddrs[i] = labels[vertexData[i].DataName]; + if (labels.TryGetValue(VertexData[i].DataName, out var dataAddr)) + { + vDataAddrs[i] = dataAddr; + } else { result.Align(4); - vdataAddrs[i] = (uint)result.Count + imageBase; - labels.Add(vertexData[i].DataName, vdataAddrs[i]); - for (int j = 0; j < vertexData[i].data.Count; j++) - result.AddRange(vertexData[i].data[j].GetBytes()); + vDataAddrs[i] = (uint)result.Count + imageBase; + labels.Add(VertexData[i].DataName, vDataAddrs[i]); + + foreach (var data in VertexData[i].Data) + { + result.AddRange(data.GetBytes()); + } } } } + vertexAddress = (uint)result.Count + imageBase; labels.Add(VertexName, vertexAddress); - for (int i = 0; i < vertexData.Count; i++) + + for (var i = 0; i < VertexData.Count; i++) { - //POF0 - if (vdataAddrs[i] != 0) + // POF0 + if (vDataAddrs[i] != 0) + { njOffsets.Add((uint)(result.Count + imageBase + 0x8)); + } - result.AddRange(vertexData[i].GetBytes(vdataAddrs[i])); + result.AddRange(VertexData[i].GetBytes(vDataAddrs[i])); } result.Add(255); result.AddRange(new byte[15]); } } - uint opolyAddress = 0; - // writing geometry data - GCIndexAttributeFlags indexFlags = GCIndexAttributeFlags.HasPosition; - if (opaqueMeshes != null && opaqueMeshes.Count > 0) + + uint oPolyAddress = 0; + // Writing geometry data + var indexFlags = GCIndexAttributeFlags.HasPosition; + + if (OpaqueMeshes != null && OpaqueMeshes.Count > 0) { - if (labels.ContainsKey(OpaqueMeshName)) - opolyAddress = labels[OpaqueMeshName]; + if (labels.TryGetValue(OpaqueMeshName, out var polyAddr)) + { + oPolyAddress = polyAddr; + } else { - uint[] paramAddrs = new uint[opaqueMeshes.Count]; - uint[] primAddrs = new uint[opaqueMeshes.Count]; - for (int i = 0; i < opaqueMeshes.Count; i++) + var paramAddrs = new uint[OpaqueMeshes.Count]; + var primAddrs = new uint[OpaqueMeshes.Count]; + + for (var i = 0; i < OpaqueMeshes.Count; i++) { - if (opaqueMeshes[i].parameters != null && opaqueMeshes[i].parameters.Count > 0) + if (OpaqueMeshes[i].Parameters != null && OpaqueMeshes[i].Parameters.Count > 0) { - if (labels.ContainsKey(opaqueMeshes[i].ParameterName)) - paramAddrs[i] = labels[opaqueMeshes[i].ParameterName]; + if (labels.TryGetValue(OpaqueMeshes[i].ParameterName, out var paramAddr)) + { + paramAddrs[i] = paramAddr; + } else { result.Align(4); paramAddrs[i] = (uint)result.Count + imageBase; - labels.Add(opaqueMeshes[i].ParameterName, paramAddrs[i]); - for (int j = 0; j < opaqueMeshes[i].parameters.Count; j++) - result.AddRange(opaqueMeshes[i].parameters[j].GetBytes()); + labels.Add(OpaqueMeshes[i].ParameterName, paramAddrs[i]); + + foreach (var parameter in OpaqueMeshes[i].Parameters) + { + result.AddRange(parameter.GetBytes()); + } } } } - for (int i = 0; i < opaqueMeshes.Count; i++) + + for (var i = 0; i < OpaqueMeshes.Count; i++) { - if (opaqueMeshes[i].primitives != null && opaqueMeshes[i].primitives.Count > 0) + if (OpaqueMeshes[i].Primitives != null && OpaqueMeshes[i].Primitives.Count > 0) { - if (labels.ContainsKey(opaqueMeshes[i].PrimitiveName)) - primAddrs[i] = labels[opaqueMeshes[i].PrimitiveName]; + if (labels.TryGetValue(OpaqueMeshes[i].PrimitiveName, out var primAddr)) + { + primAddrs[i] = primAddr; + } else { result.Align(32); primAddrs[i] = (uint)result.Count + imageBase; - labels.Add(opaqueMeshes[i].PrimitiveName, primAddrs[i]); - GCIndexAttributeFlags? t = opaqueMeshes[i].IndexFlags; - if (t.HasValue) indexFlags = t.Value; - for (int j = 0; j < opaqueMeshes[i].primitives.Count; j++) - result.AddRange(opaqueMeshes[i].primitives[j].GetBytes(indexFlags)); + labels.Add(OpaqueMeshes[i].PrimitiveName, primAddrs[i]); + + var t = OpaqueMeshes[i].IndexFlags; + if (t.HasValue) + { + indexFlags = t.Value; + } + + foreach (var primitive in OpaqueMeshes[i].Primitives) + { + result.AddRange(primitive.GetBytes(indexFlags)); + } } } } + result.Align(32); - opolyAddress = (uint)result.Count + imageBase; - labels.Add(OpaqueMeshName, opolyAddress); - for (int i = 0; i < opaqueMeshes.Count; i++) + oPolyAddress = (uint)result.Count + imageBase; + labels.Add(OpaqueMeshName, oPolyAddress); + + for (var i = 0; i < OpaqueMeshes.Count; i++) { - //POF0 + // POF0 if (paramAddrs[i] != 0) + { njOffsets.Add((uint)(result.Count + imageBase)); + } + if (primAddrs[i] != 0) + { njOffsets.Add((uint)(result.Count + imageBase + 0x8)); + } - result.AddRange(opaqueMeshes[i].GetBytes(paramAddrs[i], primAddrs[i], indexFlags)); + result.AddRange(OpaqueMeshes[i].GetBytes(paramAddrs[i], primAddrs[i])); } } } + result.Align(4); - uint tpolyAddress = 0; - if (translucentMeshes != null && translucentMeshes.Count > 0) + uint tPolyAddress = 0; + + if (TranslucentMeshes != null && TranslucentMeshes.Count > 0) { - if (labels.ContainsKey(TranslucentMeshName)) - tpolyAddress = labels[TranslucentMeshName]; + if (labels.TryGetValue(TranslucentMeshName, out var polyAddr)) + { + tPolyAddress = polyAddr; + } else { - uint[] paramAddrs = new uint[translucentMeshes.Count]; - uint[] primAddrs = new uint[translucentMeshes.Count]; - for (int i = 0; i < translucentMeshes.Count; i++) + var paramAddrs = new uint[TranslucentMeshes.Count]; + var primAddrs = new uint[TranslucentMeshes.Count]; + + for (var i = 0; i < TranslucentMeshes.Count; i++) { - if (translucentMeshes[i].parameters != null && translucentMeshes[i].parameters.Count > 0) + if (TranslucentMeshes[i].Parameters != null && TranslucentMeshes[i].Parameters.Count > 0) { - if (labels.ContainsKey(translucentMeshes[i].ParameterName)) - paramAddrs[i] = labels[translucentMeshes[i].ParameterName]; + if (labels.TryGetValue(TranslucentMeshes[i].ParameterName, out var paramAddr)) + { + paramAddrs[i] = paramAddr; + } else { result.Align(4); paramAddrs[i] = (uint)result.Count + imageBase; - labels.Add(translucentMeshes[i].ParameterName, paramAddrs[i]); - for (int j = 0; j < translucentMeshes[i].parameters.Count; j++) - result.AddRange(translucentMeshes[i].parameters[j].GetBytes()); + labels.Add(TranslucentMeshes[i].ParameterName, paramAddrs[i]); + + foreach (var parameter in TranslucentMeshes[i].Parameters) + { + result.AddRange(parameter.GetBytes()); + } } } } - for (int i = 0; i < translucentMeshes.Count; i++) + + for (var i = 0; i < TranslucentMeshes.Count; i++) { - if (translucentMeshes[i].primitives != null && translucentMeshes[i].primitives.Count > 0) + if (TranslucentMeshes[i].Primitives != null && TranslucentMeshes[i].Primitives.Count > 0) { - if (labels.ContainsKey(translucentMeshes[i].PrimitiveName)) - primAddrs[i] = labels[translucentMeshes[i].PrimitiveName]; + if (labels.TryGetValue(TranslucentMeshes[i].PrimitiveName, out var primAddr)) + { + primAddrs[i] = primAddr; + } else { result.Align(32); primAddrs[i] = (uint)result.Count + imageBase; - labels.Add(translucentMeshes[i].PrimitiveName, primAddrs[i]); - GCIndexAttributeFlags? t = translucentMeshes[i].IndexFlags; - if (t.HasValue) indexFlags = t.Value; - for (int j = 0; j < translucentMeshes[i].primitives.Count; j++) - result.AddRange(translucentMeshes[i].primitives[j].GetBytes(indexFlags)); + labels.Add(TranslucentMeshes[i].PrimitiveName, primAddrs[i]); + + var t = TranslucentMeshes[i].IndexFlags; + if (t.HasValue) + { + indexFlags = t.Value; + } + + foreach (var primitive in TranslucentMeshes[i].Primitives) + { + result.AddRange(primitive.GetBytes(indexFlags)); + } } } } + result.Align(32); - tpolyAddress = (uint)result.Count + imageBase; - labels.Add(TranslucentMeshName, tpolyAddress); - for (int i = 0; i < translucentMeshes.Count; i++) + tPolyAddress = (uint)result.Count + imageBase; + labels.Add(TranslucentMeshName, tPolyAddress); + + for (var i = 0; i < TranslucentMeshes.Count; i++) { - //POF0 + // POF0 if (paramAddrs[i] != 0) + { njOffsets.Add((uint)(result.Count + imageBase)); + } + if (primAddrs[i] != 0) + { njOffsets.Add((uint)(result.Count + imageBase + 0x8)); + } - result.AddRange(translucentMeshes[i].GetBytes(paramAddrs[i], primAddrs[i], indexFlags)); + result.AddRange(TranslucentMeshes[i].GetBytes(paramAddrs[i], primAddrs[i])); } } } - result.Align(4); - address = (uint)result.Count; - - //POF0 - if (vertexAddress != 0) - njOffsets.Add((uint)(result.Count + imageBase)); - if (opolyAddress != 0) - njOffsets.Add((uint)(result.Count + imageBase + 0x8)); - if (tpolyAddress != 0) - njOffsets.Add((uint)(result.Count + imageBase + 0xC)); - - result.AddRange(ByteConverter.GetBytes(vertexAddress)); - result.AddRange(new byte[4]); - result.AddRange(ByteConverter.GetBytes(opolyAddress)); - result.AddRange(ByteConverter.GetBytes(tpolyAddress)); - result.AddRange(ByteConverter.GetBytes((ushort)opaqueMeshes.Count)); - result.AddRange(ByteConverter.GetBytes((ushort)translucentMeshes.Count)); - result.AddRange(Bounds.GetBytes()); - labels.Add(Name, address + imageBase); - return result.ToArray(); + result.Align(4); + address = (uint)result.Count; + + // POF0 + if (vertexAddress != 0) + { + njOffsets.Add((uint)(result.Count + imageBase)); + } + + if (oPolyAddress != 0) + { + njOffsets.Add((uint)(result.Count + imageBase + 0x8)); + } + + if (tPolyAddress != 0) + { + njOffsets.Add((uint)(result.Count + imageBase + 0xC)); + } + + result.AddRange(ByteConverter.GetBytes(vertexAddress)); + result.AddRange(new byte[4]); + result.AddRange(ByteConverter.GetBytes(oPolyAddress)); + result.AddRange(ByteConverter.GetBytes(tPolyAddress)); + result.AddRange(ByteConverter.GetBytes((ushort)OpaqueMeshes.Count)); + result.AddRange(ByteConverter.GetBytes((ushort)TranslucentMeshes.Count)); + result.AddRange(Bounds.GetBytes()); + labels.Add(Name, address + imageBase); + + return result.ToArray(); } /// @@ -336,22 +444,18 @@ public override byte[] GetBytes(uint imageBase, bool DX, Dictionary public override void ProcessVertexData() { - List meshInfo = new List(); - - List positions = vertexData.Find(x => x.attribute == GCVertexAttribute.Position)?.data; - List normals = vertexData.Find(x => x.attribute == GCVertexAttribute.Normal)?.data; - List colors = vertexData.Find(x => x.attribute == GCVertexAttribute.Color0)?.data; - List uvs = vertexData.Find(x => x.attribute == GCVertexAttribute.Tex0)?.data; + var positions = VertexData.Find(x => x.Attribute == GCVertexAttribute.Position)?.Data; + var normals = VertexData.Find(x => x.Attribute == GCVertexAttribute.Normal)?.Data; + var colors = VertexData.Find(x => x.Attribute == GCVertexAttribute.Color0)?.Data; + var uvs = VertexData.Find(x => x.Attribute == GCVertexAttribute.Tex0)?.Data; - NJS_MATERIAL mat = new NJS_MATERIAL(); + var mat = new NJS_MATERIAL(); - mat.UseAlpha = false; - foreach (GCMesh m in opaqueMeshes) - meshInfo.Add(m.Process(mat, positions, normals, colors, uvs)); + mat.UseAlpha = false; + var meshInfo = OpaqueMeshes.Select(m => m.Process(mat, positions, normals, colors, uvs)).ToList(); - mat.UseAlpha = true; - foreach (GCMesh m in translucentMeshes) - meshInfo.Add(m.Process(mat, positions, normals, colors, uvs)); + mat.UseAlpha = true; + meshInfo.AddRange(TranslucentMeshes.Select(m => m.Process(mat, positions, normals, colors, uvs))); MeshInfo = meshInfo.ToArray(); } @@ -359,22 +463,22 @@ public override void ProcessVertexData() /// /// Creates a C Struct string identical to the data given (WIP) /// - /// Unused + /// Unused /// - public override string ToStruct(bool DX) + public override string ToStruct(bool dx) { - StringBuilder result = new StringBuilder("{ "); - result.Append(vertexData != null ? VertexName : "NULL"); + var result = new StringBuilder("{ "); + result.Append(VertexData != null ? VertexName : "NULL"); result.Append(", "); - result.Append(gap); + result.Append(Gap); result.Append(", "); - result.Append(opaqueMeshes.Count != 0 ? OpaqueMeshName : "NULL"); + result.Append(OpaqueMeshes.Count != 0 ? OpaqueMeshName : "NULL"); result.Append(", "); - result.Append(translucentMeshes.Count != 0 ? TranslucentMeshName : "NULL"); + result.Append(TranslucentMeshes.Count != 0 ? TranslucentMeshName : "NULL"); result.Append(", "); - result.Append(opaqueMeshes.Count != 0 ? "LengthOfArray(" + OpaqueMeshName + ")" : "0"); + result.Append(OpaqueMeshes.Count != 0 ? $"LengthOfArray({OpaqueMeshName})" : "0"); result.Append(", "); - result.Append(translucentMeshes.Count != 0 ? "LengthOfArray(" + TranslucentMeshName + ")" : "0"); + result.Append(TranslucentMeshes.Count != 0 ? $"LengthOfArray({TranslucentMeshName})" : "0"); result.Append(", "); result.Append(Bounds.ToStruct()); result.Append(" }"); @@ -385,19 +489,20 @@ public override string ToStruct(bool DX) /// WIP /// /// - /// Unused + /// Unused /// /// - public override void ToStructVariables(TextWriter writer, bool DX, List labels, string[] textures = null) + public override void ToStructVariables(TextWriter writer, bool dx, List labels, string[] textures = null) { - if (vertexData.Count != 0 && !labels.Contains(VertexName)) + if (VertexData.Count != 0 && !labels.Contains(VertexName)) { - for (int i = 0; i < vertexData.Count; i++) + foreach (var data in VertexData) { - if (!labels.Contains(vertexData[i].DataName)) + if (!labels.Contains(data.DataName)) { - labels.Add(vertexData[i].DataName); - switch (vertexData[i].attribute) + labels.Add(data.DataName); + + switch (data.Attribute) { case GCVertexAttribute.Position: writer.Write("SA2B_PositionData "); @@ -412,243 +517,316 @@ public override void ToStructVariables(TextWriter writer, bool DX, List writer.Write("SA2B_Tex0Data "); break; } - writer.Write(vertexData[i].DataName); + + writer.Write(data.DataName); writer.WriteLine("[] = {"); - List vtx = new List(vertexData[i].data.Count); - switch (vertexData[i].attribute) + var vtx = new List(data.Data.Count); + + switch (data.Attribute) { case GCVertexAttribute.Position: case GCVertexAttribute.Normal: + foreach (Vector3 item in data.Data) { - foreach (Vector3 item in vertexData[i].data) - vtx.Add(item.ToStruct()); + vtx.Add(item.ToStruct()); } break; case GCVertexAttribute.Color0: + foreach (Color item in data.Data) { - foreach (Color item in vertexData[i].data) - vtx.Add(item.ToStruct()); + vtx.Add(item.ToStruct()); } break; case GCVertexAttribute.Tex0: + foreach (UV item in data.Data) { - foreach (UV item in vertexData[i].data) - vtx.Add(item.ToStruct()); + vtx.Add(item.ToStruct()); } break; } - writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", vtx.ToArray())); + + writer.WriteLine("\t" + string.Join($",{Environment.NewLine}\t", vtx.ToArray())); writer.WriteLine("};"); writer.WriteLine(); - } } + labels.Add(VertexName); writer.Write("SA2B_VertexData "); writer.Write(VertexName); writer.Write("[] = {"); - List chunks = new List(vertexData.Count); - foreach (GCVertexSet item in vertexData) - chunks.Add(item.ToStruct()); - writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", chunks.ToArray()) + ","); - writer.WriteLine("\t" + string.Join(Environment.NewLine + "\t", "{ 255, 0, 0, 0, NULL, 0 }")); + + var chunks = new List(VertexData.Count); + chunks.AddRange(VertexData.Select(item => item.ToStruct())); + + writer.WriteLine($"\t{string.Join($",{Environment.NewLine}\t", chunks.ToArray())},"); + writer.WriteLine($"\t{string.Join($"{Environment.NewLine}\t", "{ 255, 0, 0, 0, NULL, 0 }")}"); writer.WriteLine(" };"); writer.WriteLine(); } - GCIndexAttributeFlags indexFlags = GCIndexAttributeFlags.HasPosition; - if (opaqueMeshes.Count != 0 && !labels.Contains(OpaqueMeshName)) + + var indexFlags = GCIndexAttributeFlags.HasPosition; + if (OpaqueMeshes.Count != 0 && !labels.Contains(OpaqueMeshName)) { - for (int i = 0; i < opaqueMeshes.Count; i++) + foreach (var mesh in OpaqueMeshes) { - for (int j = 0; j < opaqueMeshes[i].parameters.Count; j++) + foreach (var parameter in mesh.Parameters) { - if (opaqueMeshes[i].parameters[j] != null && !labels.Contains(opaqueMeshes[i].ParameterName)) + if (parameter == null || labels.Contains(mesh.ParameterName)) { - labels.Add(opaqueMeshes[i].ParameterName); - writer.Write("SA2B_ParameterData "); - writer.Write(opaqueMeshes[i].ParameterName); - writer.WriteLine("[] = {"); - List param = new List(opaqueMeshes[i].parameters.Count); - foreach (GCParameter item in opaqueMeshes[i].parameters) - param.Add(item.ToStruct()); - writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", param.ToArray())); - writer.WriteLine("};"); - writer.WriteLine(); + continue; } + + labels.Add(mesh.ParameterName); + writer.Write("SA2B_ParameterData "); + writer.Write(mesh.ParameterName); + writer.WriteLine("[] = {"); + + var param = new List(mesh.Parameters.Count); + param.AddRange(mesh.Parameters.Select(item => item.ToStruct())); + + writer.WriteLine($"\t{string.Join($",{Environment.NewLine}\t", param.ToArray())}"); + writer.WriteLine("};"); + writer.WriteLine(); } } - for (int i = 0; i < opaqueMeshes.Count; i++) + + foreach (var mesh in OpaqueMeshes) { - if (!labels.Contains(opaqueMeshes[i].PrimitiveName)) + if (labels.Contains(mesh.PrimitiveName)) { - labels.Add(opaqueMeshes[i].PrimitiveName); - writer.Write("Uint8 "); - writer.Write(opaqueMeshes[i].PrimitiveName); - writer.WriteLine("[] = {"); - List pbytes = new List(); - foreach (GCPrimitive item in opaqueMeshes[i].primitives) + continue; + } + + labels.Add(mesh.PrimitiveName); + writer.Write("Uint8 "); + writer.Write(mesh.PrimitiveName); + writer.WriteLine("[] = {"); + + var pBytes = new List(); + + foreach (var item in mesh.Primitives) + { + var t = mesh.IndexFlags; + if (t.HasValue) { - GCIndexAttributeFlags? t = opaqueMeshes[i].IndexFlags; - if (t.HasValue) indexFlags = t.Value; - for (int k = 0; k < opaqueMeshes[i].primitives.Count; k++) - pbytes.AddRange(opaqueMeshes[i].primitives[k].GetBytes(indexFlags)); + indexFlags = t.Value; + } + + foreach (var primitive in mesh.Primitives) + { + pBytes.AddRange(primitive.GetBytes(indexFlags)); } - byte[] cb = pbytes.ToArray(); - int dataSize = Convert.ToInt32(Math.Ceiling(decimal.Divide(cb.Length, 32)) * 32); - int buffsize = (int)dataSize - cb.Length; - List s = new List(dataSize); - for (int j = 0; j < cb.Length; j++) - s.Add("0x" + cb[j].ToString("X") + (cb[j] < 0 ? "u" : "")); - for (int l = 0; l < buffsize; l++) - s.Add("0x0"); - writer.Write(string.Join(", ", s.ToArray())); - // - //List prim = new List(opaqueMeshes[i].primitives.Count); - //foreach (GCPrimitive item in opaqueMeshes[i].primitives) - //prim.Add(item.ToStruct()); - //writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", prim.ToArray())); - writer.WriteLine("};"); - writer.WriteLine(); } + + var cb = pBytes.ToArray(); + var dataSize = Convert.ToInt32(Math.Ceiling(decimal.Divide(cb.Length, 32)) * 32); + var buffSize = dataSize - cb.Length; + var s = new List(dataSize); + + s.AddRange(cb.Select(b => $"0x{b:X}")); + + for (var l = 0; l < buffSize; l++) + { + s.Add("0x0"); + } + + writer.Write(string.Join(", ", s.ToArray())); + // + //List prim = new List(opaqueMeshes[i].primitives.Count); + //foreach (GCPrimitive item in opaqueMeshes[i].primitives) + //prim.Add(item.ToStruct()); + //writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", prim.ToArray())); + writer.WriteLine("};"); + writer.WriteLine(); } + labels.Add(OpaqueMeshName); writer.Write("SA2B_GeometryData "); writer.Write(OpaqueMeshName); writer.Write("[] = {"); - List chunks = new List(); - foreach (GCMesh item in opaqueMeshes) - chunks.Add(item.ToStruct()); - writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", chunks.ToArray())); + writer.WriteLine($"\t{string.Join($",{Environment.NewLine}\t", OpaqueMeshes.Select(item => item.ToStruct()).ToArray())}"); writer.WriteLine(" };"); writer.WriteLine(); } - if (translucentMeshes.Count != 0 && !labels.Contains(TranslucentMeshName)) + + if (TranslucentMeshes.Count != 0 && !labels.Contains(TranslucentMeshName)) { - for (int i = 0; i < translucentMeshes.Count; i++) + foreach (var mesh in TranslucentMeshes) { - for (int j = 0; j < translucentMeshes[i].parameters.Count; j++) + foreach (var parameter in mesh.Parameters) { - if (translucentMeshes[i].parameters[j] != null && !labels.Contains(translucentMeshes[i].ParameterName)) + if (parameter == null || labels.Contains(mesh.ParameterName)) { - labels.Add(translucentMeshes[i].ParameterName); - writer.Write("SA2B_ParameterData "); - writer.Write(translucentMeshes[i].ParameterName); - writer.WriteLine("[] = {"); - List param = new List(translucentMeshes[i].parameters.Count); - foreach (GCParameter item in translucentMeshes[i].parameters) - param.Add(item.ToStruct()); - writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", param.ToArray())); - writer.WriteLine("};"); - writer.WriteLine(); + continue; } + + labels.Add(mesh.ParameterName); + writer.Write("SA2B_ParameterData "); + writer.Write(mesh.ParameterName); + writer.WriteLine("[] = {"); + + var param = new List(mesh.Parameters.Count); + param.AddRange(mesh.Parameters.Select(item => item.ToStruct())); + + writer.WriteLine($"\t{string.Join($",{Environment.NewLine}\t", param.ToArray())}"); + writer.WriteLine("};"); + writer.WriteLine(); } } - for (int i = 0; i < translucentMeshes.Count; i++) + + foreach (var mesh in TranslucentMeshes) { - if (!labels.Contains(translucentMeshes[i].PrimitiveName)) + if (labels.Contains(mesh.PrimitiveName)) { - labels.Add(translucentMeshes[i].PrimitiveName); - writer.Write("Uint8 "); - writer.Write(translucentMeshes[i].PrimitiveName); - writer.WriteLine("[] = {"); - List pbytes = new List(); - foreach (GCPrimitive item in translucentMeshes[i].primitives) + continue; + } + + labels.Add(mesh.PrimitiveName); + writer.Write("Uint8 "); + writer.Write(mesh.PrimitiveName); + writer.WriteLine("[] = {"); + + var pbytes = new List(); + foreach (var item in mesh.Primitives) + { + var t = mesh.IndexFlags; + if (t.HasValue) { - GCIndexAttributeFlags? t = translucentMeshes[i].IndexFlags; - if (t.HasValue) indexFlags = t.Value; - for (int k = 0; k < translucentMeshes[i].primitives.Count; k++) - pbytes.AddRange(translucentMeshes[i].primitives[k].GetBytes(indexFlags)); + indexFlags = t.Value; } - byte[] cb = pbytes.ToArray(); - int dataSize = Convert.ToInt32(Math.Ceiling(decimal.Divide(cb.Length, 32)) * 32); - int buffsize = dataSize - cb.Length; - List s = new List(dataSize); - for (int j = 0; j < cb.Length; j++) - s.Add("0x" + cb[j].ToString("X") + (cb[j] < 0 ? "u" : "")); - for (int l = 0; l < buffsize; l++) - s.Add("0x0"); - writer.Write(string.Join(", ", s.ToArray())); - //writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", s.ToArray())); - writer.WriteLine("};"); - writer.WriteLine(); + + foreach (var primitive in mesh.Primitives) + { + pbytes.AddRange(primitive.GetBytes(indexFlags)); + } + } + + var cb = pbytes.ToArray(); + var dataSize = Convert.ToInt32(Math.Ceiling(decimal.Divide(cb.Length, 32)) * 32); + var buffSize = dataSize - cb.Length; + var s = new List(dataSize); + + s.AddRange(cb.Select(b => $"0x{b:X}")); + + for (var l = 0; l < buffSize; l++) + { + s.Add("0x0"); } + + writer.Write(string.Join(", ", s.ToArray())); + //writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", s.ToArray())); + writer.WriteLine("};"); + writer.WriteLine(); } + labels.Add(TranslucentMeshName); writer.Write("SA2B_GeometryData "); writer.Write(TranslucentMeshName); writer.Write("[] = {"); - List chunks = new List(); - foreach (GCMesh item in translucentMeshes) - chunks.Add(item.ToStruct()); - writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", chunks.ToArray())); + + writer.WriteLine($"\t{string.Join($",{Environment.NewLine}\t", TranslucentMeshes.Select(item => item.ToStruct()).ToArray())}"); writer.WriteLine(" };"); writer.WriteLine(); } + writer.Write("SA2B_Model "); writer.Write(Name); writer.Write(" = "); - writer.Write(ToStruct(DX)); + writer.Write(ToStruct(dx)); writer.WriteLine(";"); } - //WIP + // WIP public void ToNJA(TextWriter writer, List labels, string[] textures) { if (!labels.Contains(VertexName)) { - writer.WriteLine("VLIST " + VertexName + "[]"); - writer.WriteLine("START" + Environment.NewLine); - foreach (GCVertexSet item in vertexData) + writer.WriteLine($"VLIST {VertexName}[]"); + writer.WriteLine($"START{Environment.NewLine}"); + + foreach (var item in VertexData) + { item.ToNJA(writer); - foreach (GCVertexSet item in vertexData) + } + + foreach (var item in VertexData) + { item.RefToNJA(writer); - writer.WriteLine("\tVertAttr NULL" + ","); - writer.WriteLine("\tElementSize 0" + ","); - writer.WriteLine("\tPoints 0" + ","); - writer.WriteLine("\tType NULL" + ","); - writer.WriteLine("\tName NULL" + ","); - writer.WriteLine("\tCheckSize 0" + ","); - writer.Write("END" + Environment.NewLine + Environment.NewLine); + } + + writer.WriteLine("\tVertAttr NULL,"); + writer.WriteLine("\tElementSize 0,"); + writer.WriteLine("\tPoints 0,"); + writer.WriteLine("\tType NULL,"); + writer.WriteLine("\tName NULL,"); + writer.WriteLine("\tCheckSize 0,"); + writer.Write($"END{Environment.NewLine}{Environment.NewLine}"); } - if (opaqueMeshes.Count != 0 && !labels.Contains(OpaqueMeshName)) + if (OpaqueMeshes.Count != 0 && !labels.Contains(OpaqueMeshName)) { - writer.WriteLine("OPLIST " + OpaqueMeshName + "[]"); - writer.WriteLine("START" + Environment.NewLine); - foreach (GCMesh item in opaqueMeshes) + writer.WriteLine($"OPLIST {OpaqueMeshName}[]"); + writer.WriteLine($"START{Environment.NewLine}"); + + foreach (var item in OpaqueMeshes) + { item.ToNJA(writer); - foreach (GCMesh item in opaqueMeshes) + } + + foreach (var item in OpaqueMeshes) + { item.RefToNJA(writer); - writer.Write("END" + Environment.NewLine + Environment.NewLine); + } + + writer.Write($"END{Environment.NewLine}{Environment.NewLine}"); } - if (translucentMeshes.Count != 0 && !labels.Contains(TranslucentMeshName)) + if (TranslucentMeshes.Count != 0 && !labels.Contains(TranslucentMeshName)) { - writer.WriteLine("APLIST " + TranslucentMeshName + "[]"); - writer.WriteLine("START" + Environment.NewLine); - foreach (GCMesh item in translucentMeshes) + writer.WriteLine($"APLIST {TranslucentMeshName}[]"); + writer.WriteLine($"START{Environment.NewLine}"); + + foreach (var item in TranslucentMeshes) + { item.ToNJA(writer); - foreach (GCMesh item in translucentMeshes) + } + + foreach (var item in TranslucentMeshes) + { item.RefToNJA(writer); - writer.Write("END" + Environment.NewLine + Environment.NewLine); + } + + writer.Write($"END{Environment.NewLine}{Environment.NewLine}"); } - writer.WriteLine("GINJAMODEL " + Name + "[]"); + writer.WriteLine($"GINJAMODEL {Name}[]"); writer.WriteLine("START"); - writer.WriteLine("VList " + VertexName + ","); - if (opaqueMeshes.Count != 0 && !labels.Contains(OpaqueMeshName)) - writer.WriteLine("OPList " + OpaqueMeshName + ","); + writer.WriteLine($"VList {VertexName},"); + + if (OpaqueMeshes.Count != 0 && !labels.Contains(OpaqueMeshName)) + { + writer.WriteLine($"OPList {OpaqueMeshName},"); + } else + { writer.WriteLine("OPList NULL,"); - if (translucentMeshes.Count != 0 && !labels.Contains(TranslucentMeshName)) - writer.WriteLine("APList " + TranslucentMeshName + ","); + } + + if (TranslucentMeshes.Count != 0 && !labels.Contains(TranslucentMeshName)) + { + writer.WriteLine($"APList {TranslucentMeshName},"); + } else + { writer.WriteLine("APList NULL,"); - writer.WriteLine("OPNum " + opaqueMeshes.Count + ","); - writer.WriteLine("APNum " + translucentMeshes.Count + ","); - writer.WriteLine("Center " + Bounds.Center.X.ToNJA() + ", " + Bounds.Center.Y.ToNJA() + ", " + Bounds.Center.Z.ToNJA() + ","); - writer.WriteLine("Radius " + Bounds.Radius.ToNJA() + ","); - writer.Write("END" + Environment.NewLine + Environment.NewLine); + } + + writer.WriteLine($"OPNum {OpaqueMeshes.Count},"); + writer.WriteLine($"APNum {TranslucentMeshes.Count},"); + writer.WriteLine($"Center {Bounds.Center.X.ToNJA()}, {Bounds.Center.Y.ToNJA()}, {Bounds.Center.Z.ToNJA()},"); + writer.WriteLine($"Radius {Bounds.Radius.ToNJA()},"); + writer.Write($"END{Environment.NewLine}{Environment.NewLine}"); } #region Unused @@ -670,26 +848,38 @@ public override void ProcessShapeMotionVertexData(NJS_MOTION motion, float frame /// public override Attach Clone() { - GCAttach result = (GCAttach)MemberwiseClone(); - if (vertexData != null) + var result = (GCAttach)MemberwiseClone(); + + if (VertexData != null) { - result.vertexData = new List(vertexData.Count); - foreach (GCVertexSet item in vertexData) - result.vertexData.Add(item.Clone()); + result.VertexData = new List(VertexData.Count); + + foreach (var item in VertexData) + { + result.VertexData.Add(item.Clone()); + } } - if (opaqueMeshes != null) + + if (OpaqueMeshes != null) { - result.opaqueMeshes = new List(opaqueMeshes.Count); - foreach (GCMesh item in opaqueMeshes) - result.opaqueMeshes.Add(item.Clone()); + result.OpaqueMeshes = new List(OpaqueMeshes.Count); + + foreach (var item in OpaqueMeshes) + { + result.OpaqueMeshes.Add(item.Clone()); + } } - if (translucentMeshes != null) + if (TranslucentMeshes != null) { - result.translucentMeshes = new List(translucentMeshes.Count); - foreach (GCMesh item in translucentMeshes) - result.translucentMeshes.Add(item.Clone()); + result.TranslucentMeshes = new List(TranslucentMeshes.Count); + + foreach (var item in TranslucentMeshes) + { + result.TranslucentMeshes.Add(item.Clone()); + } } + result.Bounds = Bounds.Clone(); return result; } diff --git a/Libraries/SAModel/GC/GCEnums.cs b/Libraries/SAModel/GC/GCEnums.cs index f3d8a4445..4bdce5a92 100644 --- a/Libraries/SAModel/GC/GCEnums.cs +++ b/Libraries/SAModel/GC/GCEnums.cs @@ -3,7 +3,7 @@ namespace SAModel.GC { /// - /// The blendmode for the geometries surface + /// The blend mode for the geometries surface /// public enum GCBlendModeControl { @@ -273,7 +273,7 @@ public enum GCVertexAttribute } /// - /// Holds information about the vertex data thats stored in the geometry + /// Holds information about the vertex data that's stored in the geometry /// [Flags] public enum GCIndexAttributeFlags : ushort @@ -345,7 +345,7 @@ public enum GCIndexAttributeFlags : ushort } /// - /// Texture tilemode + /// Texture tile mode /// [Flags] public enum GCTileMode @@ -387,52 +387,34 @@ public static class GCEnumConverter { public static AlphaInstruction GXToNJAlphaInstruction(GCBlendModeControl gx) { - switch (gx) + return gx switch { - case GCBlendModeControl.SrcAlpha: - return AlphaInstruction.SourceAlpha; - case GCBlendModeControl.DstAlpha: - return AlphaInstruction.DestinationAlpha; - case GCBlendModeControl.InverseSrcAlpha: - return AlphaInstruction.InverseSourceAlpha; - case GCBlendModeControl.InverseDstAlpha: - return AlphaInstruction.InverseDestinationAlpha; - case GCBlendModeControl.SrcColor: - return AlphaInstruction.OtherColor; - case GCBlendModeControl.InverseSrcColor: - return AlphaInstruction.InverseOtherColor; - case GCBlendModeControl.One: - return AlphaInstruction.One; - case GCBlendModeControl.Zero: - return AlphaInstruction.Zero; - } - - return AlphaInstruction.Zero; + GCBlendModeControl.SrcAlpha => AlphaInstruction.SourceAlpha, + GCBlendModeControl.DstAlpha => AlphaInstruction.DestinationAlpha, + GCBlendModeControl.InverseSrcAlpha => AlphaInstruction.InverseSourceAlpha, + GCBlendModeControl.InverseDstAlpha => AlphaInstruction.InverseDestinationAlpha, + GCBlendModeControl.SrcColor => AlphaInstruction.OtherColor, + GCBlendModeControl.InverseSrcColor => AlphaInstruction.InverseOtherColor, + GCBlendModeControl.One => AlphaInstruction.One, + GCBlendModeControl.Zero => AlphaInstruction.Zero, + _ => AlphaInstruction.Zero + }; } public static GCBlendModeControl NJtoGXBlendModeControl(AlphaInstruction nj) { - switch (nj) + return nj switch { - case AlphaInstruction.SourceAlpha: - return GCBlendModeControl.SrcAlpha; - case AlphaInstruction.DestinationAlpha: - return GCBlendModeControl.DstAlpha; - case AlphaInstruction.InverseSourceAlpha: - return GCBlendModeControl.InverseSrcAlpha; - case AlphaInstruction.InverseDestinationAlpha: - return GCBlendModeControl.InverseDstAlpha; - case AlphaInstruction.OtherColor: - return GCBlendModeControl.SrcColor; - case AlphaInstruction.InverseOtherColor: - return GCBlendModeControl.InverseSrcColor; - case AlphaInstruction.One: - return GCBlendModeControl.One; - case AlphaInstruction.Zero: - return GCBlendModeControl.Zero; - } - - return GCBlendModeControl.Zero; + AlphaInstruction.SourceAlpha => GCBlendModeControl.SrcAlpha, + AlphaInstruction.DestinationAlpha => GCBlendModeControl.DstAlpha, + AlphaInstruction.InverseSourceAlpha => GCBlendModeControl.InverseSrcAlpha, + AlphaInstruction.InverseDestinationAlpha => GCBlendModeControl.InverseDstAlpha, + AlphaInstruction.OtherColor => GCBlendModeControl.SrcColor, + AlphaInstruction.InverseOtherColor => GCBlendModeControl.InverseSrcColor, + AlphaInstruction.One => GCBlendModeControl.One, + AlphaInstruction.Zero => GCBlendModeControl.Zero, + _ => GCBlendModeControl.Zero + }; } } diff --git a/Libraries/SAModel/GC/GCMesh.cs b/Libraries/SAModel/GC/GCMesh.cs index 94eabbbfa..77e3c7108 100644 --- a/Libraries/SAModel/GC/GCMesh.cs +++ b/Libraries/SAModel/GC/GCMesh.cs @@ -1,11 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; -using System.Runtime.InteropServices; -using System.Xml.Linq; -using System.Diagnostics.Eventing.Reader; namespace SAModel.GC { @@ -18,12 +14,12 @@ public class GCMesh /// /// The parameters that this mesh sets /// - public List parameters { get; private set; } + public List Parameters { get; private set; } /// /// The polygon data /// - public List primitives { get; private set; } + public List Primitives { get; private set; } /// /// The index attribute flags of this mesh. If it has no IndexAttribParam, it will return null @@ -32,37 +28,35 @@ public GCIndexAttributeFlags? IndexFlags { get { - IndexAttributeParameter index_param = (IndexAttributeParameter)parameters.Find(x => x.type == ParameterType.IndexAttributeFlags); - if (index_param == null) return null; - else return index_param.IndexAttributes; + var indexParam = (IndexAttributeParameter)Parameters.Find(x => x.Type == ParameterType.IndexAttributeFlags); + return indexParam?.IndexAttributes; } } /// /// The location to which the parameters have been written /// - private uint paramAddress; + private uint _paramAddress; public string ParameterName { get; set; } /// /// The location to which the primitives have been written /// - private uint primitiveAddress; + private uint _primitiveAddress; public string PrimitiveName { get; set; } /// /// The amount of bytes which have been written for the primitives /// - private uint primitiveSize; - + private uint _primitiveSize; /// /// Create an empty mesh /// public GCMesh() { - parameters = new List(); - primitives = new List(); + Parameters = []; + Primitives = []; } /// @@ -72,8 +66,8 @@ public GCMesh() /// public GCMesh(List parameters, List primitives) { - this.parameters = parameters; - this.primitives = primitives; + Parameters = parameters; + Primitives = primitives; } public GCMesh(byte[] file, int address, uint imageBase, GCIndexAttributeFlags indexFlags) @@ -86,228 +80,274 @@ public GCMesh(byte[] file, int address, uint imageBase, GCIndexAttributeFlags in /// /// The files contents /// The address at which the mesh is located - /// The imagebase (used for when reading from an exe) - /// Indexattribute parameter of the previous mesh + /// The image base (used for when reading from an exe) + /// + /// public GCMesh(byte[] file, int address, uint imageBase, Dictionary labels, GCIndexAttributeFlags indexFlags) { - // getting the addresses and sizes - int parameters_offset = (int)(ByteConverter.ToInt32(file, address) - imageBase); - int parameters_count = ByteConverter.ToInt32(file, address + 4); + // Getting the addresses and sizes + var parametersOffset = (int)(ByteConverter.ToInt32(file, address) - imageBase); + var parametersCount = ByteConverter.ToInt32(file, address + 4); - int primitives_offset = (int)(ByteConverter.ToInt32(file, address + 8) - imageBase); - uint primitives_size = ByteConverter.ToUInt32(file, address + 12); + var primitivesOffset = (int)(ByteConverter.ToInt32(file, address + 8) - imageBase); + var primitivesSize = ByteConverter.ToUInt32(file, address + 12); - // reading the parameters - parameters = new List(); - if (parameters_count != 0) + // Reading the parameters + Parameters = []; + + if (parametersCount != 0) { - if (labels.ContainsKey(parameters_offset)) - ParameterName = labels[parameters_offset]; + if (labels.TryGetValue(parametersOffset, out var parameterName)) + { + ParameterName = parameterName; + } else - ParameterName = "parameter_" + parameters_offset.ToString("X8"); + { + ParameterName = "parameter_" + parametersOffset.ToString("X8"); + } } - for (int i = 0; i < parameters_count; i++) + + for (var i = 0; i < parametersCount; i++) { - parameters.Add(GCParameter.Read(file, parameters_offset)); - parameters_offset += 8; + Parameters.Add(GCParameter.Read(file, parametersOffset)); + parametersOffset += 8; } - // getting the index attribute parameter - GCIndexAttributeFlags? flags = IndexFlags; + // Getting the index attribute parameter + var flags = IndexFlags; if (flags.HasValue) + { indexFlags = flags.Value; + } - // reading the primitives - primitives = new List(); - if (primitives_size != 0) + // Reading the primitives + Primitives = []; + if (primitivesSize != 0) { - if (labels.ContainsKey(primitives_offset)) - PrimitiveName = labels[primitives_offset]; + if (labels.TryGetValue(primitivesOffset, out var primitiveName)) + { + PrimitiveName = primitiveName; + } else - PrimitiveName = "primitive_" + primitives_offset.ToString("X8"); + { + PrimitiveName = "primitive_" + primitivesOffset.ToString("X8"); + } } - int end_pos = primitives_offset + (int)primitives_size; + var endPos = primitivesOffset + (int)primitivesSize; - while (primitives_offset < end_pos) + while (primitivesOffset < endPos) { - // if the primitive isnt valid - if (file[primitives_offset] == 0) break; - primitives.Add(new GCPrimitive(file, primitives_offset, indexFlags, out primitives_offset)); + // If the primitive isn't valid + if (file[primitivesOffset] == 0) + { + break; + } + + Primitives.Add(new GCPrimitive(file, primitivesOffset, indexFlags, out primitivesOffset)); } - primitiveSize = primitives_size; + + _primitiveSize = primitivesSize; } /// /// Writes the parameters and primitives to a stream /// - /// The ouput stream - /// The index flags - - public byte[] GetBytes(uint parameterAddress, uint primitiveAddress, GCIndexAttributeFlags indexFlags) + /// + /// + public byte[] GetBytes(uint parameterAddress, uint primitiveAddress) { - uint primsize = Convert.ToUInt32(Math.Ceiling((decimal)primitiveSize / 32) * 32); - List result = new List(); + var primitiveSize = Convert.ToUInt32(Math.Ceiling((decimal)_primitiveSize / 32) * 32); + var result = new List(); + result.AddRange(ByteConverter.GetBytes(parameterAddress)); - result.AddRange(ByteConverter.GetBytes((uint)parameters.Count)); + result.AddRange(ByteConverter.GetBytes((uint)Parameters.Count)); result.AddRange(ByteConverter.GetBytes(primitiveAddress)); - result.AddRange(ByteConverter.GetBytes(primsize)); + result.AddRange(ByteConverter.GetBytes(primitiveSize)); + return result.ToArray(); } public string ToStruct() { - uint primsize = Convert.ToUInt32(Math.Ceiling((decimal)primitiveSize / 32) * 32); - StringBuilder result = new StringBuilder("{ "); - result.Append(parameters.Count != 0 ? ParameterName : "NULL"); + var primitiveSize = Convert.ToUInt32(Math.Ceiling((decimal)_primitiveSize / 32) * 32); + var result = new StringBuilder("{ "); + + result.Append(Parameters.Count != 0 ? ParameterName : "NULL"); result.Append(", "); - result.Append(parameters != null ? (uint)parameters.Count : 0); + result.Append(Parameters != null ? (uint)Parameters.Count : 0); result.Append(", "); - result.Append(primitiveSize != 0 ? PrimitiveName : "NULL"); + result.Append(_primitiveSize != 0 ? PrimitiveName : "NULL"); result.Append(", "); - result.Append(primitives != null ? (uint)primsize : 0); + result.Append(Primitives != null ? primitiveSize : 0); result.Append(" }"); + return result.ToString(); } - //WIP + // WIP public void ToNJA(TextWriter writer) { - if (parameters != null && parameters.Count != 0) + if (Parameters != null && Parameters.Count != 0) { - writer.WriteLine("PARAMETER " + ParameterName + "[]"); + writer.WriteLine($"PARAMETER {ParameterName}[]"); writer.WriteLine("START"); - foreach (GCParameter item in parameters) + + foreach (var item in Parameters) + { item.ToNJA(writer); - writer.Write("END" + Environment.NewLine + Environment.NewLine); + } + + writer.Write($"END{Environment.NewLine}{Environment.NewLine}"); } - if (primitives != null) + if (Primitives != null) { - writer.WriteLine("PRIMITIVE " + PrimitiveName + "[]"); + writer.WriteLine($"PRIMITIVE {PrimitiveName}[]"); writer.WriteLine("START"); - foreach (GCPrimitive item in primitives) + + foreach (var item in Primitives) + { item.ToNJA(writer); - writer.Write("END" + Environment.NewLine + Environment.NewLine); + } + + writer.Write($"END{Environment.NewLine}{Environment.NewLine}"); } } + public void RefToNJA(TextWriter writer) { - if (parameters != null && parameters.Count != 0) + if (Parameters != null && Parameters.Count != 0) { - writer.WriteLine("Parameter " + ParameterName + ","); - writer.WriteLine("ParamNum " + parameters.Count + ","); + writer.WriteLine($"Parameter {ParameterName},"); } else { - writer.WriteLine("Parameter NULL" + ParameterName + ","); - writer.WriteLine("ParamNum " + parameters.Count + ","); + writer.WriteLine($"Parameter NULL{ParameterName},"); } - if (primitives != null) + + writer.WriteLine($"ParamNum {Parameters.Count},"); + + if (Primitives != null) { - writer.WriteLine("Primitive " + PrimitiveName + ","); - writer.WriteLine("PrimNum " + primitiveSize + ","); + writer.WriteLine($"Primitive {PrimitiveName},"); } else { - writer.WriteLine("Primitive NULL" + PrimitiveName + ","); - writer.WriteLine("PrimNum " + primitiveSize + ","); + writer.WriteLine($"Primitive NULL{PrimitiveName},"); } + + writer.WriteLine($"PrimNum {_primitiveSize},"); } -/// -/// Creates meshinfo to render -/// -/// A material with the current material properties -/// The position data -/// The normal data -/// The color data -/// The uv data -/// A mesh info for the mesh -public MeshInfo Process(NJS_MATERIAL material, List positions, List normals, List colors, List uvs) + /// + /// Creates meshinfo to render + /// + /// A material with the current material properties + /// The position data + /// The normal data + /// The color data + /// The uv data + /// A mesh info for the mesh + public MeshInfo Process(NJS_MATERIAL material, List positions, List normals, List colors, List uvs) { - // setting the material properties according to the parameters - foreach (GCParameter param in parameters) + // Setting the material properties according to the parameters + foreach (var param in Parameters) { - switch (param.type) - { - case ParameterType.BlendAlpha: - BlendAlphaParameter blend = param as BlendAlphaParameter; - material.SourceAlpha = blend.NJSourceAlpha; + switch (param.Type) + { + case ParameterType.BlendAlpha: + var blend = param as BlendAlphaParameter; + material.SourceAlpha = blend.NJSourceAlpha; material.DestinationAlpha = blend.NJDestAlpha; break; case ParameterType.AmbientColor: - AmbientColorParameter ambientCol = param as AmbientColorParameter; - material.DiffuseColor = ambientCol.AmbientColor.SystemCol; + var ambientCol = param as AmbientColorParameter; + material.DiffuseColor = ambientCol.AmbientColor.SystemCol; break; case ParameterType.Texture: - TextureParameter tex = param as TextureParameter; - material.TextureID = tex.TextureID; + var tex = param as TextureParameter; + material.TextureID = tex.TextureId; material.FlipU = tex.Tile.HasFlag(GCTileMode.MirrorU); material.FlipV = tex.Tile.HasFlag(GCTileMode.MirrorV); material.ClampU = tex.Tile.HasFlag(GCTileMode.WrapU); material.ClampV = tex.Tile.HasFlag(GCTileMode.WrapV); - // no idea why, but ok + // No idea why, but ok material.ClampU &= tex.Tile.HasFlag(GCTileMode.Unk_1); material.ClampV &= tex.Tile.HasFlag(GCTileMode.Unk_1); break; case ParameterType.TexCoordGen: - TexCoordGenParameter gen = param as TexCoordGenParameter; + var gen = param as TexCoordGenParameter; material.EnvironmentMap = gen.TexGenSrc == GCTexGenSrc.Normal; break; } } - // filtering out the double loops - List corners = new List(); - List polys = new List(); + // Filtering out the double loops + var corners = new List(); + var polys = new List(); - foreach (GCPrimitive prim in primitives) + foreach (var prim in Primitives) { - int j = 0; - ushort[] indices = new ushort[prim.loops.Count]; - foreach (Loop l in prim.loops) + var j = 0; + var indices = new ushort[prim.Loops.Count]; + + foreach (var l in prim.Loops) { - ushort t = (ushort)corners.FindIndex(x => x.Equals(l)); + var t = (ushort)corners.FindIndex(x => x.Equals(l)); if (t == 0xFFFF) { indices[j] = (ushort)corners.Count; corners.Add(l); } - else indices[j] = t; + else + { + indices[j] = t; + } + j++; } - // creating the polygons - if (prim.primitiveType == GCPrimitiveType.Triangles) - for (int i = 0; i < indices.Length; i += 3) - { - Triangle t = new Triangle(); - t.Indexes[0] = indices[i]; - t.Indexes[1] = indices[i + 1]; - t.Indexes[2] = indices[i + 2]; + // Creating the polygons + if (prim.PrimitiveType == GCPrimitiveType.Triangles) + { + for (var i = 0; i < indices.Length; i += 3) + { + var t = new Triangle + { + Indexes = + { + [0] = indices[i], + [1] = indices[i + 1], + [2] = indices[i + 2] + } + }; + polys.Add(t); } - else if (prim.primitiveType == GCPrimitiveType.TriangleStrip) + } + else if (prim.PrimitiveType == GCPrimitiveType.TriangleStrip) + { polys.Add(new Strip(indices, false)); + } } - // creating the vertex data - VertexData[] vertData = new VertexData[corners.Count]; - bool hasNormals = normals != null; - bool hasColors = colors != null; - bool hasUVs = uvs != null; + // Creating the vertex data + var vertData = new VertexData[corners.Count]; + var hasNormals = normals != null; + var hasColors = colors != null; + var hasUVs = uvs != null; - for (int i = 0; i < corners.Count; i++) + for (var i = 0; i < corners.Count; i++) { - Loop l = corners[i]; + var l = corners[i]; vertData[i] = new VertexData( - (Vector3)positions[l.PositionIndex], - hasNormals ? (Vector3)normals[l.NormalIndex] : new Vector3(0, 1, 0), - hasColors ? (Color)colors[l.Color0Index] : new Color(255, 255, 255, 255), - hasUVs ? (UV)uvs[l.UV0Index] : new UV(0, 0) - ); + (Vector3)positions[l.PositionIndex], + hasNormals ? (Vector3)normals[l.NormalIndex] : new Vector3(0, 1, 0), + hasColors ? (Color)colors[l.Color0Index] : new Color(255, 255, 255, 255), + hasUVs ? (UV)uvs[l.UV0Index] : new UV(0, 0) + ); } return new MeshInfo(new NJS_MATERIAL(material), polys.ToArray(), vertData, hasUVs, hasColors); @@ -316,7 +356,7 @@ public MeshInfo Process(NJS_MATERIAL material, List positions, List(Vertices.Count); //foreach (Vertex item in Vertices) // result.Vertices.Add(item.Clone()); diff --git a/Libraries/SAModel/GC/GCParameter.cs b/Libraries/SAModel/GC/GCParameter.cs index 987871169..6e864806d 100644 --- a/Libraries/SAModel/GC/GCParameter.cs +++ b/Libraries/SAModel/GC/GCParameter.cs @@ -30,12 +30,12 @@ public abstract class GCParameter /// /// The type of parameter /// - public readonly ParameterType type; + public readonly ParameterType Type; /// /// All parameter data is stored in these 4 bytes /// - protected uint data; + protected uint Data; /// /// Base constructor for an empty parameter.
@@ -44,8 +44,8 @@ public abstract class GCParameter /// The type of parameter to create protected GCParameter(ParameterType type) { - this.type = type; - data = 0; + Type = type; + Data = 0; } /// @@ -56,38 +56,22 @@ protected GCParameter(ParameterType type) /// Any of the parameter types public static GCParameter Read(byte[] file, int address) { - GCParameter result = null; - ParameterType paramType = (ParameterType)file[address]; + var paramType = (ParameterType)file[address]; - switch (paramType) + GCParameter result = paramType switch { - case ParameterType.VtxAttrFmt: - result = new VtxAttrFmtParameter(GCVertexAttribute.Null); - break; - case ParameterType.IndexAttributeFlags: - result = new IndexAttributeParameter(); - break; - case ParameterType.Lighting: - result = new LightingParameter(); - break; - case ParameterType.BlendAlpha: - result = new BlendAlphaParameter(); - break; - case ParameterType.AmbientColor: - result = new AmbientColorParameter(); - break; - case ParameterType.Texture: - result = new TextureParameter(); - break; - case ParameterType.Unknown_9: - result = new Unknown9Parameter(); - break; - case ParameterType.TexCoordGen: - result = new TexCoordGenParameter(); - break; - } + ParameterType.VtxAttrFmt => new VtxAttrFmtParameter(GCVertexAttribute.Null), + ParameterType.IndexAttributeFlags => new IndexAttributeParameter(), + ParameterType.Lighting => new LightingParameter(), + ParameterType.BlendAlpha => new BlendAlphaParameter(), + ParameterType.AmbientColor => new AmbientColorParameter(), + ParameterType.Texture => new TextureParameter(), + ParameterType.Unknown_9 => new Unknown9Parameter(), + ParameterType.TexCoordGen => new TexCoordGenParameter(), + _ => null + }; - result.data = ByteConverter.ToUInt32(file, address + 4); + result.Data = ByteConverter.ToUInt32(file, address + 4); return result; } @@ -98,68 +82,75 @@ public static GCParameter Read(byte[] file, int address) /// The stream writer public void Write(BinaryWriter writer) { - writer.Write((byte)type); - writer.Write(data); + writer.Write((byte)Type); + writer.Write(Data); } public byte[] GetBytes() { - List result = new List(); - result.Add((byte)type); + List result = []; + + result.Add((byte)Type); result.AddRange(new byte[3]); - result.AddRange(ByteConverter.GetBytes(data)); + result.AddRange(ByteConverter.GetBytes(Data)); + return result.ToArray(); } public string ToStruct() { - StringBuilder result = new StringBuilder("{ "); - result.Append((byte)type); + var result = new StringBuilder("{ "); + + result.Append((byte)Type); result.Append(", "); result.Append("0, 0, 0"); result.Append(", "); - result.AppendFormat(data.ToCHex()); + result.AppendFormat(Data.ToCHex()); result.Append(" }"); + return result.ToString(); } public void ToNJA(TextWriter writer) { - switch (type) + switch (Type) { case ParameterType.VtxAttrFmt: - if ((data >> 16) != 0x5) - writer.WriteLine("\tGJD_PARAM_IDX " + "( " + ((GCVertexAttribute)(data >> 16)).ToString() + ", " + (byte)(data >> 8) + ", " + (byte)data + " ),"); + if (Data >> 16 != 0x5) + { + writer.WriteLine($"\tGJD_PARAM_IDX ( {(GCVertexAttribute)(Data >> 16)}, {(byte)(Data >> 8)}, {(byte)Data} ),"); + } else - writer.WriteLine("\tGJD_PARAM_IDX " + "( " + ((GCVertexAttribute)(data >> 16)).ToString() + ", " + (byte)(data >> 8) + ", " + (GCUVScale)(byte)data + " ),"); + { + writer.WriteLine($"\tGJD_PARAM_IDX ( {(GCVertexAttribute)(Data >> 16)}, {(byte)(Data >> 8)}, {(GCUVScale)(byte)Data} ),"); + } break; case ParameterType.IndexAttributeFlags: - writer.WriteLine("\tGJD_PARAM_VFLAGS " + "( " + ((GCIndexAttributeFlags)data).ToString().Replace(", ", " | ") + " ),"); + writer.WriteLine($"\tGJD_PARAM_VFLAGS ( {((GCIndexAttributeFlags)Data).ToString().Replace(", ", " | ")} ),"); break; case ParameterType.Lighting: - writer.WriteLine("\tGJD_PARAM_LIGHT " + "( " + (short)data + ", " + (byte)((data >> 16) & 0xF) + ", " + (byte)((data >> 20) & 0xF) + ", " + (byte)((data >> 24) & 0xFF) + " ),"); + writer.WriteLine($"\tGJD_PARAM_LIGHT ( {(short)Data}, {(byte)((Data >> 16) & 0xF)}, {(byte)((Data >> 20) & 0xF)}, {(byte)((Data >> 24) & 0xFF)} ),"); break; case ParameterType.BlendAlpha: - writer.WriteLine("\tGJD_PARAM_BLEND " + "( " + (GCBlendModeControl)((data >> 11) & 7) + ", " + (GCBlendModeControl)((data >> 8) & 7) + " ),"); + writer.WriteLine($"\tGJD_PARAM_BLEND ( {(GCBlendModeControl)((Data >> 11) & 7)}, {(GCBlendModeControl)((Data >> 8) & 7)} ),"); break; case ParameterType.AmbientColor: - writer.WriteLine("\tGJD_PARAM_ACOLOR " + "( " + (byte)data + ", " + (byte)(data >> 8) + ", " + (byte)(data >> 16) + ", " + (byte)(data >> 24) + " ),"); + writer.WriteLine($"\tGJD_PARAM_ACOLOR ( {(byte)Data}, {(byte)(Data >> 8)}, {(byte)(Data >> 16)}, {(byte)(Data >> 24)} ),"); break; case ParameterType.Texture: - writer.WriteLine("\tGJD_PARAM_TEX " + "( " + (short)data + ", " + ((GCTileMode)(short)(data >> 16)).ToString().Replace(", ", " | ") + " ),"); + writer.WriteLine($"\tGJD_PARAM_TEX ( {(short)Data}, {((GCTileMode)(short)(Data >> 16)).ToString().Replace(", ", " | ")} ),"); break; - case ParameterType.Unknown_9: - writer.WriteLine("\tGJD_PARAM_UNK " + "( " + (short)data + ", " + ((short)(data >> 16)) + " ),"); + case ParameterType.Unknown_9: writer.WriteLine($"\tGJD_PARAM_UNK ( {(short)Data}, {(short)(Data >> 16)} ),"); break; case ParameterType.TexCoordGen: - writer.WriteLine("\tGJD_PARAM_TEXCOORD " + "( " + (GCTexCoordID)((data >> 16) & 0xFF) + ", " + (GCTexGenType)((data >> 12) & 0xF) + ", " + (GCTexGenSrc)((data >> 4) & 0xFF) + ", " + (GCTexGenMatrix)(data & 0xF) + " ),"); + writer.WriteLine($"\tGJD_PARAM_TEXCOORD ( {(GCTexCoordID)((Data >> 16) & 0xFF)}, {(GCTexGenType)((Data >> 12) & 0xF)}, {(GCTexGenSrc)((Data >> 4) & 0xFF)}, {(GCTexGenMatrix)(Data & 0xF)} ),"); break; } } } /// - /// Parameter that is relevent for Vertex data.
+ /// Parameter that is relevant for Vertex data.
/// A geometry object needs to have one for each ///
[Serializable] @@ -170,14 +161,11 @@ public class VtxAttrFmtParameter : GCParameter ///
public GCVertexAttribute VertexAttribute { - get - { - return (GCVertexAttribute)(data >> 16); - } + get => (GCVertexAttribute)(Data >> 16); set { - data &= 0xFFFF; - data |= ((uint)value) << 16; + Data &= 0xFFFF; + Data |= (uint)value << 16; } } @@ -187,14 +175,11 @@ public GCVertexAttribute VertexAttribute ///
public ushort Unknown { - get - { - return (ushort)(data & 0xFFFF); - } + get => (ushort)(Data & 0xFFFF); set { - data &= 0xFFFF0000; - data |= value; + Data &= 0xFFFF0000; + Data |= value; } } @@ -221,42 +206,34 @@ public VtxAttrFmtParameter(GCVertexAttribute vertexAttrib) : base(ParameterType. case GCVertexAttribute.Tex0: Unknown = 33544; break; - default: - break; } } /// /// Allows to manually create a Vertex attribute parameter /// - /// + /// /// The vertex attribute type that the parameter is for - public VtxAttrFmtParameter(ushort Unknown, GCVertexAttribute vertexAttrib) : base(ParameterType.VtxAttrFmt) + public VtxAttrFmtParameter(ushort unknown, GCVertexAttribute vertexAttrib) : base(ParameterType.VtxAttrFmt) { - this.Unknown = Unknown; + Unknown = unknown; VertexAttribute = vertexAttrib; } } /// - /// Holds information about the vertex data thats stored in the geometry + /// Holds information about the vertex data that's stored in the geometry /// [Serializable] public class IndexAttributeParameter : GCParameter { /// - /// Holds information about the vertex data thats stored in the geometry + /// Holds information about the vertex data that's stored in the geometry /// public GCIndexAttributeFlags IndexAttributes { - get - { - return (GCIndexAttributeFlags)data; - } - set - { - data = (uint)value; - } + get => (GCIndexAttributeFlags)Data; + set => Data = (uint)value; } /// @@ -264,7 +241,7 @@ public GCIndexAttributeFlags IndexAttributes /// public IndexAttributeParameter() : base(ParameterType.IndexAttributeFlags) { - //this always exists + // This always exists IndexAttributes &= GCIndexAttributeFlags.HasPosition; } @@ -290,57 +267,45 @@ public class LightingParameter : GCParameter ///
public ushort LightingFlags { - get - { - return (ushort)(data & 0xFFFF); - } + get => (ushort)(Data & 0xFFFF); set { - data &= 0xFFFF0000; - data |= value; + Data &= 0xFFFF0000; + Data |= value; } } /// /// Which shadow stencil the geometry should use.
- /// Ranges from 0 - 15 + /// Ranges from 0 to 15 ///
public byte ShadowStencil { - get - { - return (byte)((data >> 16) & 0xF); - } + get => (byte)((Data >> 16) & 0xF); set { - data &= 0xFFF0FFFF; - data |= (uint)((value & 0xF) << 16); + Data &= 0xFFF0FFFF; + Data |= (uint)((value & 0xF) << 16); } } public byte Unknown1 { - get - { - return (byte)((data >> 20) & 0xF); - } + get => (byte)((Data >> 20) & 0xF); set { - data &= 0xFFF0FFFF; - data |= (uint)((value & 0xF) << 20); + Data &= 0xFFF0FFFF; + Data |= (uint)((value & 0xF) << 20); } } public byte Unknown2 { - get - { - return (byte)((data >> 24) & 0xFF); - } + get => (byte)((Data >> 24) & 0xFF); set { - data &= 0xFFF0FFFF; - data |= (uint)(value << 24); + Data &= 0xFFF0FFFF; + Data |= (uint)(value << 24); } } @@ -349,7 +314,7 @@ public byte Unknown2 /// public LightingParameter() : base(ParameterType.Lighting) { - //default value + // Default value LightingFlags = 0xB11; ShadowStencil = 1; } @@ -365,80 +330,63 @@ public LightingParameter(ushort lightingFlags, byte shadowStencil) : base(Parame /// The blending information for the surface of the geometry /// [Serializable] - public class BlendAlphaParameter : GCParameter + public class BlendAlphaParameter() : GCParameter(ParameterType.BlendAlpha) { /// - /// NJ Blendmode for the source alpha + /// NJ Blend mode for the source alpha /// public AlphaInstruction NJSourceAlpha { - get - { - return GCEnumConverter.GXToNJAlphaInstruction((GCBlendModeControl)((data >> 11) & 7)); - } + get => GCEnumConverter.GXToNJAlphaInstruction((GCBlendModeControl)((Data >> 11) & 7)); set { - uint inst = (uint)GCEnumConverter.NJtoGXBlendModeControl(value); - data &= 0xFFFFC7FF; // ~(7 << 11) - data |= (inst & 7) << 11; + var inst = (uint)GCEnumConverter.NJtoGXBlendModeControl(value); + Data &= 0xFFFFC7FF; // ~(7 << 11) + Data |= (inst & 7) << 11; } } /// - /// NJ Blendmode for the destination alpha + /// NJ Blend mode for the destination alpha /// public AlphaInstruction NJDestAlpha { - get - { - return GCEnumConverter.GXToNJAlphaInstruction((GCBlendModeControl)((data >> 8) & 7)); - } + get => GCEnumConverter.GXToNJAlphaInstruction((GCBlendModeControl)((Data >> 8) & 7)); set { - uint inst = (uint)GCEnumConverter.NJtoGXBlendModeControl(value); - data &= 0xFFFFF8FF; // ~(7 << 8) - data |= (inst & 7) << 8; + var inst = (uint)GCEnumConverter.NJtoGXBlendModeControl(value); + Data &= 0xFFFFF8FF; // ~(7 << 8) + Data |= (inst & 7) << 8; } } /// - /// Blendmode for the source alpha + /// Blend mode for the source alpha /// public GCBlendModeControl SourceAlpha { - get - { - return (GCBlendModeControl)((data >> 11) & 7); - } + get => (GCBlendModeControl)((Data >> 11) & 7); set { - uint inst = (uint)value; - data &= 0xFFFFC7FF; // ~(7 << 11) - data |= (inst & 7) << 11; + var inst = (uint)value; + Data &= 0xFFFFC7FF; // ~(7 << 11) + Data |= (inst & 7) << 11; } } /// - /// Blendmode for the destination alpha + /// Blend mode for the destination alpha /// public GCBlendModeControl DestAlpha { - get - { - return (GCBlendModeControl)((data >> 8) & 7); - } + get => (GCBlendModeControl)((Data >> 8) & 7); set { - uint inst = (uint)value; - data &= 0xFFFFF8FF; // ~(7 << 8) - data |= (inst & 7) << 8; + var inst = (uint)value; + Data &= 0xFFFFF8FF; // ~(7 << 8) + Data |= (inst & 7) << 8; } } - - public BlendAlphaParameter() : base(ParameterType.BlendAlpha) - { - - } } /// @@ -448,27 +396,25 @@ public BlendAlphaParameter() : base(ParameterType.BlendAlpha) public class AmbientColorParameter : GCParameter { /// - /// The Color of the gemoetry + /// The Color of the geometry /// public Color AmbientColor { get { - Color col = new Color() + var col = new Color { - ARGB = data + ARGB = Data }; + return col; } - set - { - data = value.ARGB; - } + set => Data = value.ARGB; } public AmbientColorParameter() : base(ParameterType.AmbientColor) { - data = uint.MaxValue; // white is default + Data = uint.MaxValue; // White is default } } @@ -481,16 +427,13 @@ public class TextureParameter : GCParameter /// /// The id of the texture /// - public ushort TextureID + public ushort TextureId { - get - { - return (ushort)(data & 0xFFFF); - } + get => (ushort)(Data & 0xFFFF); set { - data &= 0xFFFF0000; - data |= value; + Data &= 0xFFFF0000; + Data |= value; } } @@ -499,26 +442,23 @@ public ushort TextureID /// public GCTileMode Tile { - get - { - return (GCTileMode)(data >> 16); - } + get => (GCTileMode)(Data >> 16); set { - data &= 0xFFFF; - data |= ((uint)value) << 16; + Data &= 0xFFFF; + Data |= (uint)value << 16; } } public TextureParameter() : base(ParameterType.Texture) { - TextureID = 0; + TextureId = 0; Tile = GCTileMode.WrapU | GCTileMode.WrapV; } - public TextureParameter(ushort TexID, GCTileMode tileMode) : base(ParameterType.Texture) + public TextureParameter(ushort textureId, GCTileMode tileMode) : base(ParameterType.Texture) { - TextureID = TexID; + TextureId = textureId; Tile = tileMode; } } @@ -534,14 +474,11 @@ public class Unknown9Parameter : GCParameter /// public ushort Unknown1 { - get - { - return (ushort)(data & 0xFFFF); - } + get => (ushort)(Data & 0xFFFF); set { - data &= 0xFFFF0000; - data |= (uint)value; + Data &= 0xFFFF0000; + Data |= value; } } @@ -550,20 +487,17 @@ public ushort Unknown1 /// public ushort Unknown2 { - get - { - return (ushort)(data >> 16); - } + get => (ushort)(Data >> 16); set { - data &= 0xFFFF; - data |= (uint)value << 16; + Data &= 0xFFFF; + Data |= (uint)value << 16; } } public Unknown9Parameter() : base(ParameterType.Unknown_9) { - // default values + // Default values Unknown1 = 4; Unknown2 = 0; } @@ -578,16 +512,13 @@ public class TexCoordGenParameter : GCParameter /// /// The output location of the generated texture coordinates /// - public GCTexCoordID TexCoordID + public GCTexCoordID TexCoordId { - get - { - return (GCTexCoordID)((data >> 16) & 0xFF); - } + get => (GCTexCoordID)((Data >> 16) & 0xFF); set { - data &= 0xFF00FFFF; - data |= (uint)value << 16; + Data &= 0xFF00FFFF; + Data |= (uint)value << 16; } } @@ -596,14 +527,11 @@ public GCTexCoordID TexCoordID /// public GCTexGenType TexGenType { - get - { - return (GCTexGenType)((data >> 12) & 0xF); - } + get => (GCTexGenType)((Data >> 12) & 0xF); set { - data &= 0xFFFF0FFF; - data |= (uint)value << 12; + Data &= 0xFFFF0FFF; + Data |= (uint)value << 12; } } @@ -612,30 +540,24 @@ public GCTexGenType TexGenType /// public GCTexGenSrc TexGenSrc { - get - { - return (GCTexGenSrc)((data >> 4) & 0xFF); - } + get => (GCTexGenSrc)((Data >> 4) & 0xFF); set { - data &= 0xFFFFF00F; - data |= (uint)value << 4; + Data &= 0xFFFFF00F; + Data |= (uint)value << 4; } } /// /// The id of the matrix to use for generating the texture coordinates /// - public GCTexGenMatrix MatrixID + public GCTexGenMatrix MatrixId { - get - { - return (GCTexGenMatrix)(data & 0xF); - } + get => (GCTexGenMatrix)(Data & 0xF); set { - data &= 0xFFFFFFF0; - data |= (uint)value; + Data &= 0xFFFFFFF0; + Data |= (uint)value; } } @@ -647,16 +569,16 @@ public TexCoordGenParameter() : base(ParameterType.TexCoordGen) /// /// Create a custom Texture coordinate generation parameter /// - /// The output location of the generated texture coordinates + /// The output location of the generated texture coordinates /// The function to use for generating the texture coordinates /// The source which should be used to generate the texture coordinates - /// The id of the matrix to use for generating the texture coordinates - public TexCoordGenParameter(GCTexCoordID texCoordID, GCTexGenType texGenType, GCTexGenSrc texGenSrc, GCTexGenMatrix matrixID) : base(ParameterType.TexCoordGen) + /// The id of the matrix to use for generating the texture coordinates + public TexCoordGenParameter(GCTexCoordID texCoordId, GCTexGenType texGenType, GCTexGenSrc texGenSrc, GCTexGenMatrix matrixId) : base(ParameterType.TexCoordGen) { - TexCoordID = texCoordID; + TexCoordId = texCoordId; TexGenType = texGenType; TexGenSrc = texGenSrc; - MatrixID = matrixID; + MatrixId = matrixId; } } } diff --git a/Libraries/SAModel/GC/GCPrimitive.cs b/Libraries/SAModel/GC/GCPrimitive.cs index f596e0d5e..efee43486 100644 --- a/Libraries/SAModel/GC/GCPrimitive.cs +++ b/Libraries/SAModel/GC/GCPrimitive.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Collections.Generic; +using System.Linq; using System.Text; namespace SAModel.GC @@ -51,12 +52,12 @@ public class GCPrimitive /// /// The way in which triangles are being stored /// - public GCPrimitiveType primitiveType; + public GCPrimitiveType PrimitiveType; /// /// The stored polygons /// - public List loops { get; set; } + public List Loops { get; set; } = []; /// /// Create a new empty Primitive @@ -64,111 +65,103 @@ public class GCPrimitive /// The type of primitive public GCPrimitive(GCPrimitiveType type) { - primitiveType = type; - loops = new List(); + PrimitiveType = type; } - /// + /// /// Read a primitive object from a file /// /// The files contents as a byte array /// The starting address of the primitive /// How the indices of the loops are structured + /// public GCPrimitive(byte[] file, int address, GCIndexAttributeFlags indexFlags, out int end) { - primitiveType = (GCPrimitiveType)file[address]; + PrimitiveType = (GCPrimitiveType)file[address]; - bool wasBigEndian = ByteConverter.BigEndian; + var wasBigEndian = ByteConverter.BigEndian; ByteConverter.BigEndian = true; - ushort vtxCount = ByteConverter.ToUInt16(file, address + 1); - - // checking the flags - bool hasFlag(GCIndexAttributeFlags flag) - { - return indexFlags.HasFlag(flag); - } - - // position always exists - bool has_color = hasFlag(GCIndexAttributeFlags.HasColor); - bool has_normal = hasFlag(GCIndexAttributeFlags.HasNormal); - bool has_uv = hasFlag(GCIndexAttributeFlags.HasUV); + var vtxCount = ByteConverter.ToUInt16(file, address + 1); - //whether any of the indices use 16 bits instead of 8 - bool pos16bit = hasFlag(GCIndexAttributeFlags.Position16BitIndex); - bool col16bit = hasFlag(GCIndexAttributeFlags.Color16BitIndex); - bool nrm16bit = hasFlag(GCIndexAttributeFlags.Normal16BitIndex); - bool uv16bit = hasFlag(GCIndexAttributeFlags.UV16BitIndex); + // Position always exists + var hasColor = indexFlags.HasFlag(GCIndexAttributeFlags.HasColor); + var hasNormal = indexFlags.HasFlag(GCIndexAttributeFlags.HasNormal); + var hasUv = indexFlags.HasFlag(GCIndexAttributeFlags.HasUV); - int tmpaddr = address + 3; + // Whether any of the indices use 16 bits instead of 8 + var pos16Bit = indexFlags.HasFlag(GCIndexAttributeFlags.Position16BitIndex); + var col16Bit = indexFlags.HasFlag(GCIndexAttributeFlags.Color16BitIndex); + var nrm16Bit = indexFlags.HasFlag(GCIndexAttributeFlags.Normal16BitIndex); + var uv16Bit = indexFlags.HasFlag(GCIndexAttributeFlags.UV16BitIndex); - loops = new List(); + var tempAddr = address + 3; for (ushort i = 0; i < vtxCount; i++) { - Loop l = new Loop(); + var l = new Loop(); - // reading position, which should always exist - if(pos16bit) + // Reading position, which should always exist + if (pos16Bit) { - l.PositionIndex = ByteConverter.ToUInt16(file, tmpaddr); - tmpaddr += 2; + l.PositionIndex = ByteConverter.ToUInt16(file, tempAddr); + tempAddr += 2; } else { - l.PositionIndex = file[tmpaddr]; - tmpaddr++; + l.PositionIndex = file[tempAddr]; + tempAddr++; } - // reading normals - if(has_normal) + // Reading normals + if (hasNormal) { - if (nrm16bit) + if (nrm16Bit) { - l.NormalIndex = ByteConverter.ToUInt16(file, tmpaddr); - tmpaddr += 2; + l.NormalIndex = ByteConverter.ToUInt16(file, tempAddr); + tempAddr += 2; } else { - l.NormalIndex = file[tmpaddr]; - tmpaddr++; + l.NormalIndex = file[tempAddr]; + tempAddr++; } } - // reading colors - if (has_color) + // Reading colors + if (hasColor) { - if (col16bit) + if (col16Bit) { - l.Color0Index = ByteConverter.ToUInt16(file, tmpaddr); - tmpaddr += 2; + l.Color0Index = ByteConverter.ToUInt16(file, tempAddr); + tempAddr += 2; } else { - l.Color0Index = file[tmpaddr]; - tmpaddr++; + l.Color0Index = file[tempAddr]; + tempAddr++; } } - // reading uvs - if (has_uv) + // Reading uvs + if (hasUv) { - if (uv16bit) + if (uv16Bit) { - l.UV0Index = ByteConverter.ToUInt16(file, tmpaddr); - tmpaddr += 2; + l.UV0Index = ByteConverter.ToUInt16(file, tempAddr); + tempAddr += 2; } else { - l.UV0Index = file[tmpaddr]; - tmpaddr++; + l.UV0Index = file[tempAddr]; + tempAddr++; } } - loops.Add(l); + Loops.Add(l); } - end = tmpaddr; + end = tempAddr; ByteConverter.BigEndian = wasBigEndian; } @@ -176,59 +169,49 @@ bool hasFlag(GCIndexAttributeFlags flag) /// /// Write the contents /// - /// The output stream /// How the indices of the loops are structured - public byte[] GetBytes(GCIndexAttributeFlags indexFlags) { - List result = new List(); - result.Add((byte)primitiveType); - - byte[] big_endian_count = BitConverter.GetBytes((ushort)loops.Count); - // writing count as big endian - result.Add(big_endian_count[1]); - result.Add(big_endian_count[0]); - - // checking the flags - bool hasFlag(GCIndexAttributeFlags flag) - { - return indexFlags.HasFlag(flag); - } + List result = [(byte)PrimitiveType]; + var bigEndianCount = BitConverter.GetBytes((ushort)Loops.Count); + // Writing count as big endian + result.Add(bigEndianCount[1]); + result.Add(bigEndianCount[0]); + // position always exists - bool has_color = hasFlag(GCIndexAttributeFlags.HasColor); - bool has_normal = hasFlag(GCIndexAttributeFlags.HasNormal); - bool has_uv = hasFlag(GCIndexAttributeFlags.HasUV); + var hasColor = indexFlags.HasFlag(GCIndexAttributeFlags.HasColor); + var hasNormal = indexFlags.HasFlag(GCIndexAttributeFlags.HasNormal); + var hasUv = indexFlags.HasFlag(GCIndexAttributeFlags.HasUV); - bool is_position_16bit = hasFlag(GCIndexAttributeFlags.Position16BitIndex); - bool is_color_16bit = hasFlag(GCIndexAttributeFlags.Color16BitIndex); - bool is_normal_16bit = hasFlag(GCIndexAttributeFlags.Normal16BitIndex); - bool is_uv_16bit = hasFlag(GCIndexAttributeFlags.UV16BitIndex); + var isPosition16Bit = indexFlags.HasFlag(GCIndexAttributeFlags.Position16BitIndex); + var isColor16Bit = indexFlags.HasFlag(GCIndexAttributeFlags.Color16BitIndex); + var isNormal16Bit = indexFlags.HasFlag(GCIndexAttributeFlags.Normal16BitIndex); + var isUv16Bit = indexFlags.HasFlag(GCIndexAttributeFlags.UV16BitIndex); - foreach (Loop v in loops) + foreach (var v in Loops) { // Position should always exist - - if (is_position_16bit) + if (isPosition16Bit) { - byte[] big_endian_pos = BitConverter.GetBytes(v.PositionIndex); - // writing count as big endian - result.Add(big_endian_pos[1]); - result.Add(big_endian_pos[0]); + var bigEndianPos = BitConverter.GetBytes(v.PositionIndex); + // Writing count as big endian + result.Add(bigEndianPos[1]); + result.Add(bigEndianPos[0]); } else { result.Add((byte)v.PositionIndex); } - if (has_normal) + if (hasNormal) { - if (is_normal_16bit) + if (isNormal16Bit) { - byte[] big_endian_nrm = BitConverter.GetBytes(v.NormalIndex); - // writing count as big endian - result.Add(big_endian_nrm[1]); - result.Add(big_endian_nrm[0]); + var bigEndianNrm = BitConverter.GetBytes(v.NormalIndex); + // Writing count as big endian + result.Add(bigEndianNrm[1]); + result.Add(bigEndianNrm[0]); } else { @@ -236,14 +219,14 @@ bool hasFlag(GCIndexAttributeFlags flag) } } - if (has_color) + if (hasColor) { - if (is_color_16bit) + if (isColor16Bit) { - byte[] big_endian_col = BitConverter.GetBytes(v.Color0Index); - // writing count as big endian - result.Add(big_endian_col[1]); - result.Add(big_endian_col[0]); + var bigEndianCol = BitConverter.GetBytes(v.Color0Index); + // Writing count as big endian + result.Add(bigEndianCol[1]); + result.Add(bigEndianCol[0]); } else { @@ -251,14 +234,14 @@ bool hasFlag(GCIndexAttributeFlags flag) } } - if (has_uv) + if (hasUv) { - if (is_uv_16bit) + if (isUv16Bit) { - byte[] big_endian_uv = BitConverter.GetBytes(v.UV0Index); - // writing count as big endian - result.Add(big_endian_uv[1]); - result.Add(big_endian_uv[0]); + var bigEndianUv = BitConverter.GetBytes(v.UV0Index); + // Writing count as big endian + result.Add(bigEndianUv[1]); + result.Add(bigEndianUv[0]); } else { @@ -266,55 +249,51 @@ bool hasFlag(GCIndexAttributeFlags flag) } } } + return result.ToArray(); } public string LoopStruct() { - List s = new List(loops.Count); - for (int i = 0; i < loops.Count; i++) - s.Add(loops[i].ToString()); + var s = new List(Loops.Count); + s.AddRange(Loops.Select(loop => loop.ToString())); + return string.Join(", ", s.ToArray()); } + public virtual string ToStruct() { - StringBuilder result = new StringBuilder("{ "); - result.Append((byte)primitiveType); + var result = new StringBuilder("{ "); + + result.Append((byte)PrimitiveType); result.Append(", "); - result.Append((ushort)loops.Count); + result.Append((ushort)Loops.Count); result.Append(", { "); result.Append(LoopStruct()); result.Append(" }"); result.Append(" }"); + return result.ToString(); } + public void ToNJA(TextWriter writer) { - string primtype = null; - switch (primitiveType) + var primtype = PrimitiveType switch { - case GCPrimitiveType.Triangles: - primtype = "GJD_PRIM_TRIANGLE"; - break; - case GCPrimitiveType.TriangleStrip: - primtype = "GJD_PRIM_TRISTRIP"; - break; - case GCPrimitiveType.TriangleFan: - primtype = "GJD_PRIM_TRIFAN"; - break; - case GCPrimitiveType.Lines: - primtype = "GJD_PRIM_LINE"; - break; - case GCPrimitiveType.LineStrip: - primtype = "GJD_PRIM_LINESTRIP"; - break; - case GCPrimitiveType.Points: - primtype = "GJD_PRIM_POINT"; - break; + GCPrimitiveType.Triangles => "GJD_PRIM_TRIANGLE", + GCPrimitiveType.TriangleStrip => "GJD_PRIM_TRISTRIP", + GCPrimitiveType.TriangleFan => "GJD_PRIM_TRIFAN", + GCPrimitiveType.Lines => "GJD_PRIM_LINE", + GCPrimitiveType.LineStrip => "GJD_PRIM_LINESTRIP", + GCPrimitiveType.Points => "GJD_PRIM_POINT", + _ => null + }; + + writer.WriteLine($"\t{primtype}({Loops.Count}),"); + foreach (var loop in Loops) + { + writer.WriteLine($"\t{loop},"); } - writer.WriteLine($"\t{primtype}(" + loops.Count + "),"); - for (int i = 0; i < loops.Count; i++) - writer.WriteLine("\t" + loops[i] + ","); } /// @@ -323,64 +302,74 @@ public void ToNJA(TextWriter writer) /// public List ToTriangles() { - List sorted_vertices = new List(); - int degTriangles = 0; + var sortedVertices = new List(); + var degTriangles = 0; - switch (primitiveType) + switch (PrimitiveType) { case GCPrimitiveType.Triangles: - return loops; + return Loops; case GCPrimitiveType.TriangleStrip: - bool isEven = false; - for (int v = 2; v < loops.Count; v++) + var isEven = false; + for (var v = 2; v < Loops.Count; v++) { - Loop[] newTri = new Loop[] + var newTri = new[] { - loops[v - 2], - isEven ? loops[v] : loops[v - 1], - isEven ? loops[v - 1] : loops[v] + Loops[v - 2], + isEven ? Loops[v] : Loops[v - 1], + isEven ? Loops[v - 1] : Loops[v] }; isEven = !isEven; // Check against degenerate triangles (a triangle which shares indexes) if (newTri[0] != newTri[1] && newTri[1] != newTri[2] && newTri[2] != newTri[0]) - sorted_vertices.AddRange(newTri); - else degTriangles++; + { + sortedVertices.AddRange(newTri); + } + else + { + degTriangles++; + } } break; case GCPrimitiveType.TriangleFan: - for (int v = 1; v < loops.Count - 1; v++) + for (var v = 1; v < Loops.Count - 1; v++) { // Triangle is always, v, v+1, and index[0]? - Loop[] newTri = new Loop[] + var newTri = new[] { - loops[v], - loops[v + 1], - loops[0], + Loops[v], + Loops[v + 1], + Loops[0], }; // Check against degenerate triangles (a triangle which shares indexes) if (newTri[0] != newTri[1] && newTri[1] != newTri[2] && newTri[2] != newTri[0]) - sorted_vertices.AddRange(newTri); - else degTriangles++; + { + sortedVertices.AddRange(newTri); + } + else + { + degTriangles++; + } } break; default: - Console.WriteLine($"Attempted to triangulate primitive type { primitiveType }"); + Console.WriteLine($"Attempted to triangulate primitive type { PrimitiveType }"); break; } if (degTriangles > 0) { - Console.WriteLine("Degenerate triangles skipped: " + degTriangles); + Console.WriteLine($"Degenerate triangles skipped: {degTriangles}"); } - return sorted_vertices; + return sortedVertices; } public override string ToString() - { - return $"{primitiveType}: {loops.Count}"; - } + { + return $"{PrimitiveType}: {Loops.Count}"; } } +} diff --git a/Libraries/SAModel/GC/GCVertexSet.cs b/Libraries/SAModel/GC/GCVertexSet.cs index 448145b32..b6b3f9ffb 100644 --- a/Libraries/SAModel/GC/GCVertexSet.cs +++ b/Libraries/SAModel/GC/GCVertexSet.cs @@ -14,17 +14,17 @@ public class GCVertexSet /// /// The type of vertex data that is stored /// - public readonly GCVertexAttribute attribute; + public readonly GCVertexAttribute Attribute; /// /// The datatype as which the data is stored /// - public readonly GCDataType dataType; + public readonly GCDataType DataType; /// /// The structure in which the data is stored /// - public readonly GCStructType structType; + public readonly GCStructType StructType; /// /// The size of a single object in the list in bytes @@ -33,36 +33,29 @@ public uint StructSize { get { - uint num_components = 1; - - switch (structType) + uint numComponents = StructType switch { - case GCStructType.Position_XY: - case GCStructType.TexCoord_ST: - num_components = 2; - break; - case GCStructType.Position_XYZ: - case GCStructType.Normal_XYZ: - num_components = 3; - break; - } + GCStructType.Position_XY or GCStructType.TexCoord_ST => 2, + GCStructType.Position_XYZ or GCStructType.Normal_XYZ => 3, + _ => 1 + }; - switch (dataType) + switch (DataType) { case GCDataType.Unsigned8: case GCDataType.Signed8: - return num_components; + return numComponents; case GCDataType.Unsigned16: case GCDataType.Signed16: case GCDataType.RGB565: case GCDataType.RGBA4: - return num_components * 2; + return numComponents * 2; case GCDataType.Float32: case GCDataType.RGBA8: case GCDataType.RGB8: case GCDataType.RGBX8: default: - return num_components * 4; + return numComponents * 4; } } } @@ -70,12 +63,12 @@ public uint StructSize /// /// The vertex data /// - public List data; + public List Data; /// /// The address of the vertex attribute (gets set after writing) /// - private uint dataAddress; + private uint _dataAddress; public string DataName { get; set; } @@ -85,31 +78,31 @@ public uint StructSize /// The attribute type of the vertex attribute public GCVertexSet(GCVertexAttribute attributeType) { - attribute = attributeType; + Attribute = attributeType; - switch (attribute) + switch (Attribute) { case GCVertexAttribute.Position: - dataType = GCDataType.Float32; - structType = GCStructType.Position_XYZ; + DataType = GCDataType.Float32; + StructType = GCStructType.Position_XYZ; break; case GCVertexAttribute.Normal: - dataType = GCDataType.Float32; - structType = GCStructType.Normal_XYZ; + DataType = GCDataType.Float32; + StructType = GCStructType.Normal_XYZ; break; case GCVertexAttribute.Color0: - dataType = GCDataType.RGBA8; - structType = GCStructType.Color_RGBA; + DataType = GCDataType.RGBA8; + StructType = GCStructType.Color_RGBA; break; case GCVertexAttribute.Tex0: - dataType = GCDataType.Signed16; - structType = GCStructType.TexCoord_ST; + DataType = GCDataType.Signed16; + StructType = GCStructType.TexCoord_ST; break; default: - throw new ArgumentException($"Datatype { attribute } is not a valid vertex type for SA2"); + throw new ArgumentException($"Datatype { Attribute } is not a valid vertex type for SA2"); } - data = new List(); + Data = []; } /// @@ -118,13 +111,12 @@ public GCVertexSet(GCVertexAttribute attributeType) /// /// /// - /// public GCVertexSet(GCVertexAttribute attribute, GCDataType dataType, GCStructType structType) { - this.attribute = attribute; - this.dataType = dataType; - this.structType = structType; - data = new List(); + Attribute = attribute; + DataType = dataType; + StructType = structType; + Data = []; } public GCVertexSet(byte[] file, uint address, uint imageBase) @@ -140,70 +132,94 @@ public GCVertexSet(byte[] file, uint address, uint imageBase) /// The image base of the addresses public GCVertexSet(byte[] file, uint address, uint imageBase, Dictionary labels) { - attribute = (GCVertexAttribute)file[address]; - if (attribute == GCVertexAttribute.Null) return; + Attribute = (GCVertexAttribute)file[address]; + if (Attribute == GCVertexAttribute.Null) + { + return; + } - uint structure = ByteConverter.ToUInt32(file, (int)address + 4); - structType = (GCStructType)(structure & 0x0F); - dataType = (GCDataType)((structure >> 4) & 0x0F); + var structure = ByteConverter.ToUInt32(file, (int)address + 4); + StructType = (GCStructType)(structure & 0x0F); + DataType = (GCDataType)((structure >> 4) & 0x0F); + if (file[address + 1] != StructSize) { throw new Exception($"Read structure size doesnt match calculated structure size: {file[address + 1]} != {StructSize}"); } - // reading the data + // Reading the data int count = ByteConverter.ToUInt16(file, (int)address + 2); - int tmpaddr = (int)(ByteConverter.ToUInt32(file, (int)address + 8) - imageBase); + var tempAddr = (int)(ByteConverter.ToUInt32(file, (int)address + 8) - imageBase); - data = new List(); + Data = []; - switch (attribute) + switch (Attribute) { case GCVertexAttribute.Position: - if (labels.ContainsKey(tmpaddr)) - DataName = labels[tmpaddr]; + if (labels.TryGetValue(tempAddr, out var positionName)) + { + DataName = positionName; + } else - DataName = "position_" + tmpaddr.ToString("X8"); - for (int i = 0; i < count; i++) { - data.Add(new Vector3(file, tmpaddr)); - tmpaddr += 12; + DataName = $"position_{tempAddr:X8}"; + } + + for (var i = 0; i < count; i++) + { + Data.Add(new Vector3(file, tempAddr)); + tempAddr += 12; } break; case GCVertexAttribute.Normal: - if (labels.ContainsKey(tmpaddr)) - DataName = labels[tmpaddr]; + if (labels.TryGetValue(tempAddr, out var noramlName)) + { + DataName = noramlName; + } else - DataName = "normal_" + tmpaddr.ToString("X8"); - for (int i = 0; i < count; i++) { - data.Add(new Vector3(file, tmpaddr)); - tmpaddr += 12; + DataName = $"normal_{tempAddr:X8}"; + } + + for (var i = 0; i < count; i++) + { + Data.Add(new Vector3(file, tempAddr)); + tempAddr += 12; } break; case GCVertexAttribute.Color0: - if (labels.ContainsKey(tmpaddr)) - DataName = labels[tmpaddr]; + if (labels.TryGetValue(tempAddr, out var colorName)) + { + DataName = colorName; + } else - DataName = "vcolor_" + tmpaddr.ToString("X8"); - for (int i = 0; i < count; i++) { - data.Add(new Color(file, tmpaddr, dataType, out tmpaddr)); + DataName = $"vcolor_{tempAddr:X8}"; + } + + for (var i = 0; i < count; i++) + { + Data.Add(new Color(file, tempAddr, DataType, out tempAddr)); } break; case GCVertexAttribute.Tex0: - if (labels.ContainsKey(tmpaddr)) - DataName = labels[tmpaddr]; + if (labels.TryGetValue(tempAddr, out var texName)) + { + DataName = texName; + } else - DataName = "uv_" + tmpaddr.ToString("X8"); - for (int i = 0; i < count; i++) { - data.Add(new UV(file, tmpaddr)); - tmpaddr += 4; + DataName = $"uv_{tempAddr:X8}"; + } + + for (var i = 0; i < count; i++) + { + Data.Add(new UV(file, tempAddr)); + tempAddr += 4; } break; default: - throw new ArgumentException($"Attribute type not valid sa2 type: {attribute}"); + throw new ArgumentException($"Attribute type not valid sa2 type: {Attribute}"); } } @@ -211,14 +227,13 @@ public GCVertexSet(byte[] file, uint address, uint imageBase, Dictionary /// The output stream - /// The imagebase public void WriteData(BinaryWriter writer) { - dataAddress = (uint)writer.BaseStream.Length; + _dataAddress = (uint)writer.BaseStream.Length; - foreach(IOVtx vtx in data) + foreach(var vtx in Data) { - vtx.Write(writer, dataType, structType); + vtx.Write(writer, DataType, StructType); } } @@ -227,121 +242,135 @@ public void WriteData(BinaryWriter writer) /// Assumes that has been called prior /// /// The output stream - /// The imagebase - public void WriteAttribute(BinaryWriter writer, uint imagebase, List njOffsets) + /// The image base + /// + public void WriteAttribute(BinaryWriter writer, uint imageBase, List njOffsets) { - if (dataAddress == 0) + if (_dataAddress == 0) + { throw new Exception("Data has not been written yet!"); + } - //POF0 Offsets + // POF0 Offsets njOffsets.Add((uint)(writer.BaseStream.Position + 8)); - writer.Write((byte)attribute); + writer.Write((byte)Attribute); writer.Write((byte)StructSize); - writer.Write((ushort)data.Count); - uint structure = (uint)structType; - structure |= (uint)((byte)dataType << 4); + writer.Write((ushort)Data.Count); + + var structure = (uint)StructType; + structure |= (uint)((byte)DataType << 4); + writer.Write(structure); - writer.Write(dataAddress + imagebase); - writer.Write((uint)(data.Count * StructSize)); + writer.Write(_dataAddress + imageBase); + writer.Write((uint)(Data.Count * StructSize)); - dataAddress = 0; + _dataAddress = 0; } public byte[] GetBytes(uint dataAddress) { - List result = new List(); - result.Add((byte)attribute); - result.Add((byte)StructSize); - result.AddRange(ByteConverter.GetBytes((ushort)data.Count)); - uint structure = (uint)structType; - structure |= (uint)((byte)dataType << 4); + List result = + [ + (byte)Attribute, + (byte)StructSize + ]; + + result.AddRange(ByteConverter.GetBytes((ushort)Data.Count)); + + var structure = (uint)StructType; + structure |= (uint)((byte)DataType << 4); + result.AddRange(ByteConverter.GetBytes(structure)); result.AddRange(ByteConverter.GetBytes(dataAddress)); - result.AddRange(ByteConverter.GetBytes((uint)(data.Count * StructSize))); + result.AddRange(ByteConverter.GetBytes((uint)(Data.Count * StructSize))); + return result.ToArray(); } + public string ToStruct() { - StringBuilder result = new StringBuilder("{ "); - result.Append((byte)attribute); + var result = new StringBuilder("{ "); + + result.Append((byte)Attribute); result.Append(", "); result.Append(StructSize); result.Append(", "); - result.Append(data.Count); + result.Append(Data.Count); result.Append(", "); - uint structure = (uint)structType; - structure |= (uint)((byte)dataType << 4); + + var structure = (uint)StructType; + structure |= (uint)((byte)DataType << 4); + result.Append(structure); result.Append(", "); - result.Append(data != null ? DataName : "NULL"); + result.Append(Data != null ? DataName : "NULL"); result.Append(", "); - result.Append((uint)(data.Count * StructSize)); + result.Append((uint)(Data.Count * StructSize)); result.Append(" }"); + return result.ToString(); } - //WIP + // WIP public void ToNJA(TextWriter writer) { - string verttype = null; - string verttype2 = null; - switch (attribute) + string vertType = null; + string vertType2 = null; + + switch (Attribute) { case GCVertexAttribute.Position: - verttype = "POINT"; - verttype2 = "POSITION "; + vertType = "POINT"; + vertType2 = "POSITION "; break; case GCVertexAttribute.Normal: - verttype = "NORMAL"; - verttype2 = "NORMAL "; + vertType = "NORMAL"; + vertType2 = "NORMAL "; break; case GCVertexAttribute.Color0: - verttype = "COLOR"; - verttype2 = "COLOR0 "; + vertType = "COLOR"; + vertType2 = "COLOR0 "; break; case GCVertexAttribute.Tex0: - verttype = "UV"; - verttype2 = "TEX0 "; + vertType = "UV"; + vertType2 = "TEX0 "; break; } - writer.WriteLine($"{verttype2}" + DataName + "[]"); + + writer.WriteLine($"{vertType2}{DataName}[]"); writer.WriteLine("START"); - foreach (IOVtx vtx in data) + + foreach (var vtx in Data) { - vtx.ToNJA(writer, verttype); + vtx.ToNJA(writer, vertType); } - writer.WriteLine("END" + Environment.NewLine); + + writer.WriteLine($"END{Environment.NewLine}"); } + public void RefToNJA(TextWriter writer) { - string verttype = null; - switch (attribute) + var vertType = Attribute switch { - case GCVertexAttribute.Position: - verttype = "POSITION"; - break; - case GCVertexAttribute.Normal: - verttype = "NORMAL"; - break; - case GCVertexAttribute.Color0: - verttype = "COLOR0"; - break; - case GCVertexAttribute.Tex0: - verttype = "TEX0"; - break; - } - writer.WriteLine("\tVertAttr " + $"{verttype}" + ","); - writer.WriteLine("\tElementSize " + StructSize + ","); - writer.WriteLine("\tPoints " + data.Count + ","); - writer.WriteLine("\tType " + "( " + structType + ", " + dataType + " ),"); - writer.WriteLine("\tName " + DataName + ","); - writer.WriteLine("\tCheckSize " + (data.Count * StructSize) + "," + Environment.NewLine); + GCVertexAttribute.Position => "POSITION", + GCVertexAttribute.Normal => "NORMAL", + GCVertexAttribute.Color0 => "COLOR0", + GCVertexAttribute.Tex0 => "TEX0", + _ => null + }; + + writer.WriteLine($"\tVertAttr {vertType},"); + writer.WriteLine($"\tElementSize {StructSize},"); + writer.WriteLine($"\tPoints {Data.Count},"); + writer.WriteLine($"\tType ( {StructType}, {DataType} ),"); + writer.WriteLine($"\tName {DataName},"); + writer.WriteLine($"\tCheckSize {Data.Count * StructSize},{Environment.NewLine}"); } public GCVertexSet Clone() { - GCVertexSet result = (GCVertexSet)MemberwiseClone(); + var result = (GCVertexSet)MemberwiseClone(); //result.data = new List(data.Count); //foreach (IOVtx item in data) //result.data.Add(item.Clone()); diff --git a/Libraries/SAModel/GC/Structs.cs b/Libraries/SAModel/GC/Structs.cs index 07f60c150..686795629 100644 --- a/Libraries/SAModel/GC/Structs.cs +++ b/Libraries/SAModel/GC/Structs.cs @@ -14,99 +14,83 @@ public interface IOVtx /// Writes the struct /// /// The output stream - /// + /// + /// void Write(BinaryWriter writer, GCDataType dataType, GCStructType structType); - public abstract byte[] GetBytes(); + public byte[] GetBytes(); void ToNJA(TextWriter writer, string vtype); } [Serializable] - public class Vector3 : IOVtx + public class Vector3(float x, float y, float z) : IOVtx { - public float x; - public float y; - public float z; + public float X = x; + public float Y = y; + public float Z = z; - public Vector3(float x, float y, float z) + public Vector3(byte[] file, int address) : this(ByteConverter.ToSingle(file, address), ByteConverter.ToSingle(file, address + 4), ByteConverter.ToSingle(file, address + 8)) { - this.x = x; - this.y = y; - this.z = z; - } - - public Vector3(byte[] file, int address) - { - x = ByteConverter.ToSingle(file, address); - y = ByteConverter.ToSingle(file, address + 4); - z = ByteConverter.ToSingle(file, address + 8); } public void Write(BinaryWriter writer, GCDataType dataType, GCStructType structType) { - writer.Write(x); - writer.Write(y); - writer.Write(z); + writer.Write(X); + writer.Write(Y); + writer.Write(Z); } public byte[] GetBytes() { - List result = new List(); - result.AddRange(ByteConverter.GetBytes(x)); - result.AddRange(ByteConverter.GetBytes(y)); - result.AddRange(ByteConverter.GetBytes(z)); + List result = []; + + result.AddRange(ByteConverter.GetBytes(X)); + result.AddRange(ByteConverter.GetBytes(Y)); + result.AddRange(ByteConverter.GetBytes(Z)); + return result.ToArray(); } public string ToStruct() { - StringBuilder result = new StringBuilder("{ "); - result.Append(x.ToC()); + var result = new StringBuilder("{ "); + + result.Append(X.ToC()); result.Append(", "); - result.Append(y.ToC()); + result.Append(Y.ToC()); result.Append(", "); - result.Append(z.ToC()); + result.Append(Z.ToC()); result.Append(" }"); + return result.ToString(); } public void ToNJA(TextWriter writer, string vtype) { - writer.WriteLine($"\t{vtype}( " + x.ToNJA() + ", " + y.ToNJA() + ", " + z.ToNJA() + " ),"); + writer.WriteLine($"\t{vtype}( {X.ToNJA()}, {Y.ToNJA()}, {Z.ToNJA()} ),"); } } [Serializable] public class UV : IOVtx { - public short x; - public short y; + public short X; + public short Y; public float XF { - get - { - return x / 256f; - } - set - { - x = (short)(value * 256); - } + get => X / 256f; + set => X = (short)(value * 256); } + public float YF { - get - { - return y / 256f; - } - set - { - y = (short)(value * 256); - } + get => Y / 256f; + set => Y = (short)(value * 256); } public UV(short x, short y) { - this.x = x; - this.y = y; + X = x; + Y = y; } public UV(float x, float y) @@ -117,36 +101,41 @@ public UV(float x, float y) public UV(byte[] file, int address) { - x = ByteConverter.ToInt16(file, address); - y = ByteConverter.ToInt16(file, address + 2); + X = ByteConverter.ToInt16(file, address); + Y = ByteConverter.ToInt16(file, address + 2); } public void Write(BinaryWriter writer, GCDataType dataType, GCStructType structType) { - writer.Write(x); - writer.Write(y); + writer.Write(X); + writer.Write(Y); } public byte[] GetBytes() { - List result = new List(); - result.AddRange(ByteConverter.GetBytes(x)); - result.AddRange(ByteConverter.GetBytes(y)); + List result = []; + + result.AddRange(ByteConverter.GetBytes(X)); + result.AddRange(ByteConverter.GetBytes(Y)); + return result.ToArray(); } public string ToStruct() { - StringBuilder result = new StringBuilder("{ "); - result.Append(x); + var result = new StringBuilder("{ "); + + result.Append(X); result.Append(", "); - result.Append(y); + result.Append(Y); result.Append(" }"); + return result.ToString(); } + public void ToNJA(TextWriter writer, string vtype) { - writer.WriteLine($"\t{vtype}( " + x.ToString() + ", " + y.ToString() + " ),"); + writer.WriteLine($"\t{vtype}( {X}, {Y} ),"); } } @@ -159,75 +148,54 @@ public class Color : IOVtx /// /// Red value /// - public byte red; + public byte Red; /// /// Green value /// - public byte green; + public byte Green; /// /// Blue value /// - public byte blue; + public byte Blue; /// /// Alpha value /// - public byte alpha; + public byte Alpha; /// /// Red float value. Ranges from 0 - 1 /// public float RedF { - get - { - return red / 255.0f; - } - set - { - red = (byte)Math.Round(value * 255); - } + get => Red / 255.0f; + set => Red = (byte)Math.Round(value * 255); } + /// /// Green float value. Ranges from 0 - 1 /// public float GreenF { - get - { - return green / 255.0f; - } - set - { - green = (byte)Math.Round(value * 255); - } + get => Green / 255.0f; + set => Green = (byte)Math.Round(value * 255); } + /// /// Blue float value. Ranges from 0 - 1 /// public float BlueF { - get - { - return blue / 255.0f; - } - set - { - blue = (byte)Math.Round(value * 255); - } + get => Blue / 255.0f; + set => Blue = (byte)Math.Round(value * 255); } + /// /// Alpha float value. Ranges from 0 - 1 /// public float AlphaF { - get - { - return alpha / 255.0f; - } - set - { - alpha = (byte)Math.Round(value * 255); - } + get => Alpha / 255.0f; + set => Alpha = (byte)Math.Round(value * 255); } /// @@ -235,16 +203,13 @@ public float AlphaF /// public uint RGBA { - get - { - return (uint)(red | (green << 8) | (blue << 16) | (alpha << 24)); - } + get => (uint)(Red | (Green << 8) | (Blue << 16) | (Alpha << 24)); set { - red = (byte)(value & 0xFF); - green = (byte)((value >> 8) & 0xFF); - blue = (byte)((value >> 16) & 0xFF); - alpha = (byte)(value >> 24); + Red = (byte)(value & 0xFF); + Green = (byte)((value >> 8) & 0xFF); + Blue = (byte)((value >> 16) & 0xFF); + Alpha = (byte)(value >> 24); } } @@ -253,16 +218,13 @@ public uint RGBA /// public uint ARGB { - get - { - return (uint)(alpha | (red << 8) | (green << 16) | (blue << 24)); - } + get => (uint)(Alpha | (Red << 8) | (Green << 16) | (Blue << 24)); set { - alpha = (byte)(value & 0xFF); - red = (byte)((value >> 8) & 0xFF); - green = (byte)((value >> 16) & 0xFF); - blue = (byte)(value >> 24); + Alpha = (byte)(value & 0xFF); + Red = (byte)((value >> 8) & 0xFF); + Green = (byte)((value >> 16) & 0xFF); + Blue = (byte)(value >> 24); } } @@ -271,16 +233,13 @@ public uint ARGB /// public System.Drawing.Color SystemCol { - get - { - return System.Drawing.Color.FromArgb(alpha, red, green, blue); - } + get => System.Drawing.Color.FromArgb(Alpha, Red, Green, Blue); set { - alpha = value.A; - red = value.R; - green = value.G; - blue = value.B; + Alpha = value.A; + Red = value.R; + Green = value.G; + Blue = value.B; } } @@ -298,10 +257,10 @@ public Color() /// Alpha color value public Color(byte red, byte green, byte blue, byte alpha) { - this.red = red; - this.green = green; - this.blue = blue; - this.alpha = alpha; + Red = red; + Green = green; + Blue = blue; + Alpha = alpha; } /// @@ -311,7 +270,7 @@ public Color(byte red, byte green, byte blue, byte alpha) /// /// /// - public Color(float red, float green, float blue, float alpha) : this((byte)0,0,0,0) + public Color(float red, float green, float blue, float alpha) : this(0,0,0,0) { RedF = red; GreenF = green; @@ -324,36 +283,46 @@ public Color(byte[] file, int address, GCDataType dataType, out int endaddr) switch (dataType) { case GCDataType.RGB565: - short colorShort = ByteConverter.ToInt16(file, address); - red = (byte)((colorShort & 0xF800) >> 8); - green = (byte)((colorShort & 0x07E0) >> 3); - blue = (byte)((colorShort & 0x001F) << 3); + var colorShort = ByteConverter.ToInt16(file, address); + + Red = (byte)((colorShort & 0xF800) >> 8); + Green = (byte)((colorShort & 0x07E0) >> 3); + Blue = (byte)((colorShort & 0x001F) << 3); + endaddr = address + 2; return; case GCDataType.RGBA4: - ushort colorShortA = ByteConverter.ToUInt16(file, address); - // multiplying all by 0x11, so that e.g. 0xF becomes 0xFF - red = (byte)(((colorShortA & 0xF000) >> 12) * 0x11); - green = (byte)(((colorShortA & 0x0F00) >> 8) * 0x11); - blue = (byte)(((colorShortA & 0x00F0) >> 4) * 0x11); - alpha = (byte)((colorShortA & 0x000F) * 0x11); + var colorShortA = ByteConverter.ToUInt16(file, address); + + // Multiplying all by 0x11, so that e.g. 0xF becomes 0xFF + Red = (byte)(((colorShortA & 0xF000) >> 12) * 0x11); + Green = (byte)(((colorShortA & 0x0F00) >> 8) * 0x11); + Blue = (byte)(((colorShortA & 0x00F0) >> 4) * 0x11); + Alpha = (byte)((colorShortA & 0x000F) * 0x11); + endaddr = address + 2; return; case GCDataType.RGBA6: - uint colorInt = ByteConverter.ToUInt32(file, address); - // shifting all 2 less to the left, so that they are more accurate to the color that they should represent - red = (byte)((colorInt & 0xFC0000) >> 16); - green = (byte)((colorInt & 0x03F000) >> 10); - blue = (byte)((colorInt & 0x000FC0) >> 4); - alpha = (byte)((colorInt & 0x00003F) << 2); + var colorInt = ByteConverter.ToUInt32(file, address); + + // Shifting all 2 less to the left, so that they are more accurate to the color that they should represent + Red = (byte)((colorInt & 0xFC0000) >> 16); + Green = (byte)((colorInt & 0x03F000) >> 10); + Blue = (byte)((colorInt & 0x000FC0) >> 4); + Alpha = (byte)((colorInt & 0x00003F) << 2); + endaddr = address + 3; return; case GCDataType.RGB8: case GCDataType.RGBX8: case GCDataType.RGBA8: RGBA = ByteConverter.ToUInt32(file, address); + if (dataType != GCDataType.RGBA8) - alpha = 255; + { + Alpha = 255; + } + endaddr = address + 4; return; default: @@ -365,22 +334,23 @@ public Color(byte[] file, int address, GCDataType dataType, out int endaddr) /// Write the color data to a stream /// /// - /// + /// + /// public void Write(BinaryWriter writer, GCDataType dataType, GCStructType structType) { switch (dataType) { case GCDataType.RGB8: - writer.Write(red); - writer.Write(green); - writer.Write(blue); + writer.Write(Red); + writer.Write(Green); + writer.Write(Blue); writer.Write((byte)255); break; case GCDataType.RGBA8: - writer.Write(red); - writer.Write(green); - writer.Write(blue); - writer.Write(alpha); + writer.Write(Red); + writer.Write(Green); + writer.Write(Blue); + writer.Write(Alpha); break; default: throw new ArgumentException($"{dataType} is not a valid output color type"); @@ -389,34 +359,39 @@ public void Write(BinaryWriter writer, GCDataType dataType, GCStructType structT public byte[] GetBytes() { - List result = new List(); + List result = []; + if (ByteConverter.BigEndian) { - result.Add(alpha); - result.Add(blue); - result.Add(green); - result.Add(red); + result.Add(Alpha); + result.Add(Blue); + result.Add(Green); + result.Add(Red); } else { - result.Add(red); - result.Add(green); - result.Add(blue); - result.Add(alpha); + result.Add(Red); + result.Add(Green); + result.Add(Blue); + result.Add(Alpha); } + return result.ToArray(); } public string ToStruct() { - StringBuilder result = new StringBuilder("{ "); - result.Append("0x" + $"{alpha.ToString("X2")}" + $"{blue.ToString("X2")}" + $"{green.ToString("X2")}" + $"{red.ToString("X2")}"); - result.Append(" }"); + var result = new StringBuilder("{ "); + + result.Append($"0x{Alpha:X2}{Blue:X2}{Green:X2}{Red:X2}"); + result.Append(" }"); + return result.ToString(); } + public void ToNJA(TextWriter writer, string vtype) { - writer.WriteLine($"\t{vtype}( " + red.ToString() + ", " + green.ToString() + ", " + blue.ToString() + ", " + alpha.ToString() + " ),"); + writer.WriteLine($"\t{vtype}( {Red}, {Green}, {Blue}, {Alpha} ),"); } } } diff --git a/Libraries/SAModel/Ninja Binary/NJReader.cs b/Libraries/SAModel/Ninja Binary/NJReader.cs index e4999a3aa..207ff9422 100644 --- a/Libraries/SAModel/Ninja Binary/NJReader.cs +++ b/Libraries/SAModel/Ninja Binary/NJReader.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Text; -using System.Windows.Forms; // Dreamcast Ninja Binary (.nj) format and its platform variations (.gj, .xj) namespace SAModel { public class NinjaBinaryFile { - enum NinjaBinaryChunkType + private enum NinjaBinaryChunkType { BasicModel, ChunkModel, @@ -20,47 +19,48 @@ enum NinjaBinaryChunkType Invalid } - public List Models; // In NJBM or NJCM - public List Motions; // In NMDM - public List Texnames; // In NJTL + public readonly List Models; // In NJBM or NJCM + public readonly List Motions; // In NMDM + public readonly List Texnames; // In NJTL - private class NinjaDataChunk + private class NinjaDataChunk(NinjaBinaryChunkType type, byte[] data) { - public NinjaBinaryChunkType Type; + public readonly NinjaBinaryChunkType Type = type; public int ImageBase; - public byte[] Data; - - public NinjaDataChunk(NinjaBinaryChunkType type, byte[] data) - { - Type = type; - Data = data; - } + public readonly byte[] Data = data; } - public enum BigEndianResult + private enum BigEndianResult { LittleEndian, BigEndian, CantTell } - public BigEndianResult CheckPointerBigEndian(byte[] data, int address) + private static BigEndianResult CheckPointerBigEndian(byte[] data, int address) { - BigEndianResult result = BigEndianResult.CantTell; + var result = BigEndianResult.CantTell; // Back up Big Endian mode - bool bk = ByteConverter.BigEndian; + var bk = ByteConverter.BigEndian; // Set Big Endian mode ByteConverter.BigEndian = true; + // Get Little Endian version - uint pnt_little = BitConverter.ToUInt32(data, address); + var pntLittle = BitConverter.ToUInt32(data, address); // Get Big Endian version - uint png_big = ByteConverter.ToUInt32(data, address); - // If Little is bigger, it's likely Big Endian - if (pnt_little > png_big) + var pngBig = ByteConverter.ToUInt32(data, address); + + if (pntLittle > pngBig) + { + // If Little is bigger, it's likely Big Endian result = BigEndianResult.BigEndian; - // If Big is bigger, it's likely Little Endian - else if (pnt_little < png_big) + } + else if (pntLittle < pngBig) + { + // If Big is bigger, it's likely Little Endian result = BigEndianResult.LittleEndian; + } + // Restore Big Endian mode ByteConverter.BigEndian = bk; return result; @@ -68,175 +68,182 @@ public BigEndianResult CheckPointerBigEndian(byte[] data, int address) public NinjaBinaryFile(byte[] data, ModelFormat format) { - Models = new List(); - Motions = new List(); - Texnames = new List(); - int startoffset = 0; // Current reading position. - int modelcount = 0; // This is used to keep track of the model added last to get data for motions. - int currentchunk = 0; // Keep track of current data chunk in case a POF0 chunk is found. - int imgBase = 0; // Key added to pointers. - bool sizeIsLittleEndian = true; // In Gamecube games, size can be either Big or Little Endian. - List chunks = new List(); + Models = []; + Motions = []; + Texnames = []; + + var startOffset = 0; // Current reading position. + var modelCount = 0; // This is used to keep track of the model added last to get data for motions. + var currentChunk = 0; // Keep track of current data chunk in case a POF0 chunk is found. + var imgBase = 0; // Key added to pointers. + var sizeIsLittleEndian = true; // In Gamecube games, size can be either Big or Little Endian. + var chunks = new List(); // Back up Big Endian mode - bool bigEndianBk = ByteConverter.BigEndian; + var bigEndianBk = ByteConverter.BigEndian; + // Read the file until the end - while (startoffset < data.Length - 8) // 8 is the size of chunk ID + chunk size + while (startOffset < data.Length - 8) // 8 is the size of chunk ID + chunk size { // Skip padding and unrecognized data - if (IdentifyChunk(data, startoffset) == NinjaBinaryChunkType.Invalid) + if (IdentifyChunk(data, startOffset) == NinjaBinaryChunkType.Invalid) { - while (IdentifyChunk(data, startoffset) == NinjaBinaryChunkType.Invalid) + while (IdentifyChunk(data, startOffset) == NinjaBinaryChunkType.Invalid) { // Stop if reached the end of file - if (startoffset >= data.Length - 4) + if (startOffset >= data.Length - 4) + { break; - startoffset += 1; + } + + startOffset += 1; } } + // Stop if reached the end of file - if (startoffset >= data.Length - 4) + if (startOffset >= data.Length - 4) + { break; + } + // Get Ninja data chunk type - NinjaBinaryChunkType idtype = IdentifyChunk(data, startoffset); + var idType = IdentifyChunk(data, startOffset); // Endianness checks for the first chunk - if (currentchunk == 0) + if (currentChunk == 0) { // This check is done because in PSO GC chunk size is in Little Endian despite the rest of the data being Big Endian. // First, determine whether size is Big Endian or not. ByteConverter.BigEndian = true; - sizeIsLittleEndian = BitConverter.ToUInt32(data, startoffset + 4) < ByteConverter.ToUInt32(data, startoffset + 4); + sizeIsLittleEndian = BitConverter.ToUInt32(data, startOffset + 4) < ByteConverter.ToUInt32(data, startOffset + 4); // Then, check if the actual data is Big Endian. Unfortunately this is just guessing so it may not always work. // startoffset + 8 is where the data begins - switch (idtype) + + switch (idType) { case NinjaBinaryChunkType.BasicModel: case NinjaBinaryChunkType.ChunkModel: // Check attach pointer - BigEndianResult res_endian = CheckPointerBigEndian(data, startoffset + 8 + 4); - if (res_endian == BigEndianResult.CantTell) + var resEndian = CheckPointerBigEndian(data, startOffset + 8 + 4); + if (resEndian == BigEndianResult.CantTell) { // Check child pointer - res_endian = CheckPointerBigEndian(data, startoffset + 8 + 0x2C); - if (res_endian == BigEndianResult.CantTell) + resEndian = CheckPointerBigEndian(data, startOffset + 8 + 0x2C); + if (resEndian == BigEndianResult.CantTell) { // Check sibling pointer - res_endian = CheckPointerBigEndian(data, startoffset + 8 + 0x30); + resEndian = CheckPointerBigEndian(data, startOffset + 8 + 0x30); } } - ByteConverter.BigEndian = res_endian == BigEndianResult.BigEndian; + ByteConverter.BigEndian = resEndian == BigEndianResult.BigEndian; break; case NinjaBinaryChunkType.Motion: // Number of frames case NinjaBinaryChunkType.SimpleShapeMotion: // Number of frames case NinjaBinaryChunkType.Texlist: // Number of texnames - ByteConverter.BigEndian = BitConverter.ToUInt32(data, startoffset + 12) > ByteConverter.ToUInt32(data, startoffset + 12); + ByteConverter.BigEndian = BitConverter.ToUInt32(data, startOffset + 12) > ByteConverter.ToUInt32(data, startOffset + 12); break; default: // Old check - ByteConverter.BigEndian = BitConverter.ToUInt32(data, startoffset + 8) > ByteConverter.ToUInt32(data, startoffset + 8); + ByteConverter.BigEndian = BitConverter.ToUInt32(data, startOffset + 8) > ByteConverter.ToUInt32(data, startOffset + 8); break; } - //MessageBox.Show(ByteConverter.BigEndian.ToString()); } - int size = sizeIsLittleEndian ? BitConverter.ToInt32(data, startoffset + 4) : ByteConverter.ToInt32(data, startoffset + 4); - //MessageBox.Show(idtype.ToString() + " chunk at " + (startoffset + 8).ToString("X8") + " size " + size.ToString()); + + var size = sizeIsLittleEndian ? BitConverter.ToInt32(data, startOffset + 4) : ByteConverter.ToInt32(data, startOffset + 4); // Add the chunk to the list to process - chunks.Add(new NinjaDataChunk(idtype, new byte[size])); - Array.Copy(data, startoffset + 8, chunks[currentchunk].Data, 0, chunks[currentchunk].Data.Length); - // If a POF0 chunk is reached, fix up the previous chunk's pointers - if (idtype == NinjaBinaryChunkType.POF0) + chunks.Add(new NinjaDataChunk(idType, new byte[size])); + Array.Copy(data, startOffset + 8, chunks[currentChunk].Data, 0, chunks[currentChunk].Data.Length); + + if (idType == NinjaBinaryChunkType.POF0) { - List offs = POF0Helper.GetPointerListFromPOF(chunks[currentchunk].Data); - //MessageBox.Show("POF at " + (startoffset + 8).ToString("X") + " imgBase: " + imgBase.ToString("X") + " size " + chunks[currentchunk].Data.Length.ToString()); - POF0Helper.FixPointersWithPOF(chunks[currentchunk - 1].Data, offs, imgBase); - chunks[currentchunk - 1].ImageBase = imgBase; - //System.IO.File.WriteAllBytes("C:\\Users\\PkR\\Desktop\\chunk\\" + currentchunk.ToString("D3") + "_pof.bin", chunks[currentchunk].Data); - //System.IO.File.WriteAllBytes("C:\\Users\\PkR\\Desktop\\chunk\\" + currentchunk.ToString("D3") + ".bin", chunks[currentchunk - 1].Data); - startoffset += chunks[currentchunk].Data.Length + 8; + // If a POF0 chunk is reached, fix up the previous chunk's pointers + var offs = POF0Helper.GetPointerListFromPOF(chunks[currentChunk].Data); + POF0Helper.FixPointersWithPOF(chunks[currentChunk - 1].Data, offs, imgBase); + chunks[currentChunk - 1].ImageBase = imgBase; } - // Otherwise advance the reading position and pointer image base else { - imgBase += startoffset; - startoffset += chunks[currentchunk].Data.Length + 8; + // Otherwise advance the reading position and pointer image base + imgBase += startOffset; } - currentchunk++; + + startOffset += chunks[currentChunk].Data.Length + 8; + currentChunk++; } + // Go over the fixed chunks and add final data - foreach (NinjaDataChunk chunk in chunks) + foreach (var chunk in chunks) { switch (chunk.Type) { case NinjaBinaryChunkType.BasicModel: - //MessageBox.Show("Basic model at " + chunk.ImageBase.ToString("X") + " size " + chunk.Data.Length.ToString()); // Add a label so that all models aren't called "object_00000000" - Dictionary labelb = new Dictionary(); - labelb.Add(0, "object_" + chunk.ImageBase.ToString("X8")); - Models.Add(new NJS_OBJECT(chunk.Data, 0, (uint)chunk.ImageBase, ModelFormat.Basic, labelb, new Dictionary())); - modelcount++; + Dictionary basicLabels = []; + basicLabels.Add(0, "object_" + chunk.ImageBase.ToString("X8")); + Models.Add(new NJS_OBJECT(chunk.Data, 0, (uint)chunk.ImageBase, ModelFormat.Basic, basicLabels, new Dictionary())); + modelCount++; break; case NinjaBinaryChunkType.ChunkModel: - //MessageBox.Show(format.ToString() + " model at " + chunk.ImageBase.ToString("X") + " size " + chunk.Data.Length.ToString()); // Add a label so that all models aren't called "object_00000000" - Dictionary labelc = new Dictionary(); - labelc.Add(0, "object_" + chunk.ImageBase.ToString("X8")); + Dictionary chunkLabels = []; + chunkLabels.Add(0, "object_" + chunk.ImageBase.ToString("X8")); // NJCM can be Chunk (NJ file, Big or Little Endian), Ginja (GJ file) or XJ (XJ file) - Models.Add(new NJS_OBJECT(chunk.Data, 0, (uint)chunk.ImageBase, format, labelc, new Dictionary())); - modelcount++; + Models.Add(new NJS_OBJECT(chunk.Data, 0, (uint)chunk.ImageBase, format, chunkLabels, new Dictionary())); + modelCount++; break; case NinjaBinaryChunkType.Texlist: - //MessageBox.Show("Texlist at " + chunk.ImageBase.ToString("X") + " size " + chunk.Data.Length.ToString()); - int firstEntry = ByteConverter.ToInt32(chunk.Data, 0) - chunk.ImageBase; // Prooobably, seems to be 8 always - int numTextures = ByteConverter.ToInt32(chunk.Data, 0x4); - List texNames = new List(); + var firstEntry = ByteConverter.ToInt32(chunk.Data, 0) - chunk.ImageBase; // Prooobably, seems to be 8 always + var numTextures = ByteConverter.ToInt32(chunk.Data, 0x4); + List texNames = []; + // Add texture names - for (int i = 0; i < numTextures; i++) + for (var i = 0; i < numTextures; i++) { - int textAddress = ByteConverter.ToInt32(chunk.Data, firstEntry + i * 0xC) - chunk.ImageBase; // 0xC is the size of NJS_TEXNAME + var textAddress = ByteConverter.ToInt32(chunk.Data, firstEntry + i * 0xC) - chunk.ImageBase; // 0xC is the size of NJS_TEXNAME // Read the null terminated string - List namestring = new List(); - byte namechar = (chunk.Data[textAddress]); - int j = 0; - while (namechar != 0) + List nameString = []; + var nameChar = chunk.Data[textAddress]; + var j = 0; + + while (nameChar != 0) { - namestring.Add(namechar); + nameString.Add(nameChar); j++; - namechar = (chunk.Data[textAddress + j]); + nameChar = chunk.Data[textAddress + j]; } - texNames.Add(Encoding.ASCII.GetString(namestring.ToArray())); + + texNames.Add(Encoding.ASCII.GetString(nameString.ToArray())); } + Texnames.Add(texNames.ToArray()); break; case NinjaBinaryChunkType.Motion: - //MessageBox.Show("Motion with ImgBase " + chunk.ImageBase.ToString("X") + " size " + chunk.Data.Length.ToString()); try { // Add a label so that all motions aren't called "motion_00000000" - Dictionary labelm = new Dictionary(); - labelm.Add(0, "motion_" + chunk.ImageBase.ToString("X8")); - Motions.Add(new NJS_MOTION(chunk.Data, 0, (uint)chunk.ImageBase, Models.Count > 0 ? Models[modelcount - 1].CountAnimated() : -1, labelm, objectName: Models.Count > 0 ? Models[modelcount - 1].Name : "")); + Dictionary motionLabels = []; + motionLabels.Add(0, $"motion_{chunk.ImageBase:X8}"); + Motions.Add(new NJS_MOTION(chunk.Data, 0, (uint)chunk.ImageBase, Models.Count > 0 ? Models[modelCount - 1].CountAnimated() : -1, motionLabels, objectName: Models.Count > 0 ? Models[modelCount - 1].Name : "")); } catch (Exception ex) { - MessageBox.Show("Error adding motion at 0x" + chunk.ImageBase.ToString("X") + ": " + ex.Message.ToString()); + throw new Exception($"Error adding motion at 0x{chunk.ImageBase:X}: {ex.Message}"); } break; case NinjaBinaryChunkType.SimpleShapeMotion: - //MessageBox.Show("Shape Motion with ImgBase " + chunk.ImageBase.ToString("X") + " size " + chunk.Data.Length.ToString()); try { // Add a label so that all motions aren't called "motion_00000000" - Dictionary labels = new Dictionary(); - labels.Add(0, "shape_" + chunk.ImageBase.ToString("X8")); - Motions.Add(new NJS_MOTION(chunk.Data, 0, (uint)chunk.ImageBase, Models.Count > 0 ? Models[modelcount - 1].CountAnimated() : -1, labels, numverts: Models[modelcount].GetVertexCounts())); + Dictionary shapeLabels = []; + shapeLabels.Add(0, $"shape_{chunk.ImageBase:X8}"); + Motions.Add(new NJS_MOTION(chunk.Data, 0, (uint)chunk.ImageBase, Models.Count > 0 ? Models[modelCount - 1].CountAnimated() : -1, shapeLabels, numverts: Models[modelCount].GetVertexCounts())); } catch (Exception ex) { - MessageBox.Show("Error adding shape motion at 0x" + chunk.ImageBase.ToString("X") + ": " + ex.Message.ToString()); + throw new Exception($"Error adding shape motion at 0x{chunk.ImageBase:X}: {ex.Message}"); } break; } } - //MessageBox.Show("Models: " + Models.Count.ToString() + " Animations: " + Motions.Count.ToString() + " Texlists: " + Texnames.Count.ToString() + ", Texture arrays: " + Textures.Count.ToString()); + // Restore Big Endian mode ByteConverter.BigEndian = bigEndianBk; } @@ -244,10 +251,16 @@ public NinjaBinaryFile(byte[] data, ModelFormat format) private NinjaBinaryChunkType IdentifyChunk(byte[] data, int offset) { if (offset >= data.Length - 8) + { return NinjaBinaryChunkType.Invalid; + } + if (BitConverter.ToUInt32(data, offset + 4) == 0) + { return NinjaBinaryChunkType.Invalid; - switch (System.Text.Encoding.ASCII.GetString(data, offset, 4)) + } + + switch (Encoding.ASCII.GetString(data, offset, 4)) { // Implemented chunk types case "NJBM": diff --git a/Libraries/SAModel/Ninja Binary/NJTLHelper.cs b/Libraries/SAModel/Ninja Binary/NJTLHelper.cs index ecd35d509..73c9c409c 100644 --- a/Libraries/SAModel/Ninja Binary/NJTLHelper.cs +++ b/Libraries/SAModel/Ninja Binary/NJTLHelper.cs @@ -8,23 +8,18 @@ public class NJTLHelper { public static byte[] GenerateNJTexList(string[] texList, bool isGC, bool sizeLittleEndian) { - List njTexList = new List(); - List njTLHeader = new List(); - List pof0List = new List(); + List njTexList = []; + List njTLHeader = []; + List pof0List = []; - if (isGC) - { - njTLHeader.AddRange(new byte[] { 0x47, 0x4A, 0x54, 0x4C }); - } - else - { - njTLHeader.AddRange(new byte[] { 0x4E, 0x4A, 0x54, 0x4C }); - } + njTLHeader.AddRange(isGC ? "GJTL"u8 : "NTJL"u8); + njTexList.AddRange(ByteConverter.GetBytes(0x8)); njTexList.AddRange(ByteConverter.GetBytes(texList.Length)); - int offset = texList.Length * 0xC + 0x8; - for (int i = 0; i < texList.Length; i++) + var offset = texList.Length * 0xC + 0x8; + + for (var i = 0; i < texList.Length; i++) { if (i > 0) { @@ -34,25 +29,29 @@ public static byte[] GenerateNJTexList(string[] texList, bool isGC, bool sizeLit njTexList.AddRange(ByteConverter.GetBytes(0)); njTexList.AddRange(ByteConverter.GetBytes(0)); } - for (int i = 0; i < texList.Length; i++) + + foreach (var tex in texList) { - njTexList.AddRange(Encoding.ASCII.GetBytes(texList[i])); + njTexList.AddRange(Encoding.ASCII.GetBytes(tex)); njTexList.Add(0); } + njTexList.Align(0x4); njTLHeader.AddRange(BitConverter.GetBytes(njTexList.Count)); pof0List.Add(0x40); pof0List.Add(0x42); - for (int i = 1; i < texList.Length; i++) + + for (var i = 1; i < texList.Length; i++) { pof0List.Add(0x43); } + pof0List.Align(4); - int pofLength = pof0List.Count; - byte[] magic = { 0x50, 0x4F, 0x46, 0x30 }; + var pofLength = pof0List.Count; + byte[] magic = [0x50, 0x4F, 0x46, 0x30]; pof0List.InsertRange(0, sizeLittleEndian ? BitConverter.GetBytes(pofLength) : ByteConverter.GetBytes(pofLength)); pof0List.InsertRange(0, magic); @@ -62,6 +61,5 @@ public static byte[] GenerateNJTexList(string[] texList, bool isGC, bool sizeLit return njTexList.ToArray(); } - } } diff --git a/Libraries/SAModel/Ninja Binary/POF0Helper.cs b/Libraries/SAModel/Ninja Binary/POF0Helper.cs index f9b596adc..654da5d50 100644 --- a/Libraries/SAModel/Ninja Binary/POF0Helper.cs +++ b/Libraries/SAModel/Ninja Binary/POF0Helper.cs @@ -6,7 +6,7 @@ namespace SAModel // Class to work with pointer offset lists (POF) in Ninja Binary files class POF0Helper { - enum POFOffsetType: byte + private enum POFOffsetType: byte { Padding = 0x00, Char = 0x40, @@ -19,21 +19,24 @@ enum POFOffsetType: byte // Creates a POF chunk out of a list of absolute offsets public static byte[] GetPOFData(List offsets) { - List result = new List(); - byte[] magic = { 0x50, 0x4F, 0x46, 0x30 }; // POF0 + List result = []; + byte[] magic = [0x50, 0x4F, 0x46, 0x30]; // POF0 + result.AddRange(magic); - for (int i = 0; i < offsets.Count; i++) + + for (var i = 0; i < offsets.Count; i++) { - uint offsetDiff = (i == 0) ? offsets[i] : offsets[i] - offsets[i - 1]; - uint offsetDiv = offsetDiff / 4; + var offsetDiff = i == 0 ? offsets[i] : offsets[i] - offsets[i - 1]; + var offsetDiv = offsetDiff / 4; byte[] offsetBytes; + // Write offset with mask if (offsetDiff > 0xFF) { // Long if (offsetDiff > 0xFFFF) { - byte[] converted = BitConverter.GetBytes(offsetDiv); // Same Endianness + var converted = BitConverter.GetBytes(offsetDiv); // Same Endianness offsetBytes = new byte[4]; offsetBytes[0] = (byte)(converted[3] | (byte)POFOffsetType.Long); offsetBytes[1] = converted[2]; @@ -43,8 +46,8 @@ public static byte[] GetPOFData(List offsets) // Short else { - ushort shortCalc = (ushort)offsetDiv; - byte[] converted = BitConverter.GetBytes(shortCalc); // Same Endianness + var shortCalc = (ushort)offsetDiv; + var converted = BitConverter.GetBytes(shortCalc); // Same Endianness offsetBytes = new byte[2]; offsetBytes[0] = (byte)(converted[1] | (byte)POFOffsetType.Short); offsetBytes[1] = converted[0]; @@ -53,11 +56,13 @@ public static byte[] GetPOFData(List offsets) // Char else { - byte byteCalc = (byte)(offsetDiv | (byte)POFOffsetType.Char); - offsetBytes = new byte[] { byteCalc }; + var byteCalc = (byte)(offsetDiv | (byte)POFOffsetType.Char); + offsetBytes = [byteCalc]; } + result.AddRange(offsetBytes); } + result.Align(4); // Add length result.InsertRange(4, ByteConverter.GetBytes(result.Count - 4)); // Variable Endianness @@ -67,20 +72,19 @@ public static byte[] GetPOFData(List offsets) // Adjusts pointers in a data chunk using a pointer list public static void FixPointersWithPOF(byte[] data, List pointerList, int imgBase) { - //System.Text.StringBuilder sb = new StringBuilder(); - //System.Windows.Forms.MessageBox.Show(data.Length.ToString()); - int currentPos = 0; - foreach (int pointer in pointerList) + var currentPos = 0; + + foreach (var pointer in pointerList) { currentPos += pointer; - //System.Windows.Forms.MessageBox.Show("Pointer: " + pointer.ToString("X") + " currentPos: " + currentPos.ToString("X")); - int oldPointer = ByteConverter.ToInt32(data, currentPos); + var oldPointer = ByteConverter.ToInt32(data, currentPos); + if (oldPointer != 0) + { oldPointer += imgBase; - byte[] newPointerBytes = ByteConverter.GetBytes(oldPointer); - //sb.Append("\n" + "Fixing pointer at " + (currentPos).ToString("X") + ": " + (oldPointer < imgBase ? "0" : ((uint)oldPointer - (uint)imgBase).ToString("X")) + " to " + oldPointer.ToString("X")); - //System.IO.File.WriteAllText("C:\\Users\\PkR\\Desktop\\sb" + imgBase.ToString("X") + ".txt", sb.ToString()); - //System.Windows.Forms.MessageBox.Show("Fixing pointer at " + (currentPos).ToString("X") + ": " + oldPointer.ToString("X")); + } + + var newPointerBytes = ByteConverter.GetBytes(oldPointer); Array.Copy(newPointerBytes, 0, data, currentPos, 4); } } @@ -88,62 +92,41 @@ public static void FixPointersWithPOF(byte[] data, List pointerList, int im // Gets a pointer list out of a POF data chunk public static List GetPointerListFromPOF(byte[] pofdata) { - int chars = 0; - int pads = 0; - int shorts = 0; - int longs = 0; - List offsets = new List(); - int currentoff = 0; - //MessageBox.Show("Pof length: " + pofdata.Length.ToString()); - while (currentoff < pofdata.Length) + List offsets = []; + var currentOffset = 0; + + while (currentOffset < pofdata.Length) { - //MessageBox.Show("Tock " + currentoff.ToString()); - byte first = (byte)(pofdata[currentoff] & (byte)POFOffsetType.DataMask); - POFOffsetType type = (POFOffsetType)(pofdata[currentoff] & (byte)POFOffsetType.TypeMask); - currentoff++; + var first = (byte)(pofdata[currentOffset] & (byte)POFOffsetType.DataMask); + var type = (POFOffsetType)(pofdata[currentOffset] & (byte)POFOffsetType.TypeMask); + currentOffset++; + switch (type) { // Padding case POFOffsetType.Padding: - //MessageBox.Show("This is padding: " + pofdata[currentoff-1].ToString("X") + "at " + currentoff.ToString()); - pads++; break; // Single byte case POFOffsetType.Char: offsets.Add(4 * first); - chars++; break; // Two bytes case POFOffsetType.Short: - byte second = pofdata[currentoff]; + var second = pofdata[currentOffset]; offsets.Add(4 * ((first << 8) | second)); - currentoff += 1; - shorts++; + currentOffset += 1; break; // Four bytes case POFOffsetType.Long: - byte second_l = pofdata[currentoff]; - byte third_l = pofdata[currentoff + 1]; - byte fourth_l = pofdata[currentoff + 2]; - offsets.Add(4 * ((first << 24) | (second_l << 16) | (third_l << 8) | fourth_l)); - currentoff += 3; - longs++; + var secondL = pofdata[currentOffset]; + var thirdL = pofdata[currentOffset + 1]; + var fourthL = pofdata[currentOffset + 2]; + offsets.Add(4 * ((first << 24) | (secondL << 16) | (thirdL << 8) | fourthL)); + currentOffset += 3; break; } } - /* - StringBuilder sb = new StringBuilder(); - int currentPos = 0; - for (int i = 0; i < offsets.Count; i++) - { - currentPos += offsets[i]; - sb.Append(", " + currentPos.ToString("X")); - } - sb.AppendJoin(',', offsets.ToArray()); - - System.Windows.Forms.MessageBox.Show("offsets in POF: " + offsets.Count.ToString() + ", pads " + pads.ToString() + ", chars " + chars.ToString() + ", shorts " + shorts.ToString()); - System.IO.File.WriteAllText("C:\\Users\\PkR\\Desktop\\sb_.txt", sb.ToString()); - */ + return offsets; } } diff --git a/Libraries/SAModel/VertexData.cs b/Libraries/SAModel/VertexData.cs index 85f4604bb..05c26cb5a 100644 --- a/Libraries/SAModel/VertexData.cs +++ b/Libraries/SAModel/VertexData.cs @@ -92,14 +92,14 @@ public VertexData(Vertex position, Vertex normal, Color? color, UV uv) public VertexData(GC.Vector3 position, GC.Vector3 normal, GC.Color color, GC.UV uv) { - Position = new Vertex(position.x, position.y, position.z); - Normal = new Vertex(normal.x, normal.y, normal.z) ?? Vertex.UpNormal; + Position = new Vertex(position.X, position.Y, position.Z); + Normal = new Vertex(normal.X, normal.Y, normal.Z) ?? Vertex.UpNormal; //why does this work, i fed R in as A //Color = System.Drawing.Color.FromArgb((int)(color.R), (int)(color.G), (int)(color.B), (int)(color.A)); //Color = color.SystemCol; - Color = System.Drawing.Color.FromArgb(color.red, color.alpha, color.blue, color.green); + Color = System.Drawing.Color.FromArgb(color.Red, color.Alpha, color.Blue, color.Green); //Color = color; UV = new UV() { U = uv.XF, V = uv.YF }; diff --git a/Libraries/SAModel/XJ/XJAttach.cs b/Libraries/SAModel/XJ/XJAttach.cs index 961455d8b..d3b32375c 100644 --- a/Libraries/SAModel/XJ/XJAttach.cs +++ b/Libraries/SAModel/XJ/XJAttach.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; namespace SAModel.XJ { @@ -12,36 +13,22 @@ public class XJAttach : Attach /// /// Flags for the XJ Attach /// - public uint flags; + private readonly uint _flags; /// /// The seperate sets of vertex data in this attach /// - public readonly List vertexData = new List(); + private readonly List _vertexData = []; /// /// The meshes with opaque rendering properties /// - public readonly List opaqueMeshes = new List(); + private readonly List _opaqueMeshes = []; /// /// The meshes with translucent rendering properties /// - public readonly List translucentMeshes = new List(); - - /// - /// Create a new empty XJ attach - /// - public XJAttach() - { - Name = "xjattach_" + Extensions.GenerateIdentifier(); - - flags = 0; - vertexData = new List(); - opaqueMeshes = new List(); - translucentMeshes = new List(); - Bounds = new BoundingSphere(); - } + private readonly List _translucentMeshes = []; /// /// Reads a XJ attach from a file @@ -49,74 +36,84 @@ public XJAttach() /// The files contents /// The address at which the attach is located /// The imagebase of the file - /// The labels of the file - /// public XJAttach(byte[] file, int address, uint imageBase) : this(file, address, imageBase, new Dictionary()) { } + public XJAttach(byte[] file, int address, uint imageBase, Dictionary labels) { - if (labels.ContainsKey(address)) - Name = labels[address]; + if (labels.TryGetValue(address, out var name)) + { + Name = name; + } else - Name = "attach_" + address.ToString("X8"); - flags = ByteConverter.ToUInt32(file, address); - uint vertexSetOffset = ByteConverter.ToUInt32(file, address + 0x4) - imageBase; - uint vertexSetCount = ByteConverter.ToUInt32(file, address + 0x8); //Should always be 1? - uint opaqueMeshesOffset = ByteConverter.ToUInt32(file, address + 0xC) - imageBase; - uint opaqueMeshesCount = ByteConverter.ToUInt32(file, address + 0x10); - uint translucentMeshesOffset = ByteConverter.ToUInt32(file, address + 0x14) - imageBase; - uint translucentMeshesCount = ByteConverter.ToUInt32(file, address + 0x18); + { + Name = $"attach_{address:X8}"; + } + + _flags = ByteConverter.ToUInt32(file, address); + + var vertexSetOffset = ByteConverter.ToUInt32(file, address + 0x4) - imageBase; + var vertexSetCount = ByteConverter.ToUInt32(file, address + 0x8); //Should always be 1? + var opaqueMeshesOffset = ByteConverter.ToUInt32(file, address + 0xC) - imageBase; + var opaqueMeshesCount = ByteConverter.ToUInt32(file, address + 0x10); + var translucentMeshesOffset = ByteConverter.ToUInt32(file, address + 0x14) - imageBase; + var translucentMeshesCount = ByteConverter.ToUInt32(file, address + 0x18); + Bounds = new BoundingSphere(file, address + 0x1C); - for(int i = 0; i < vertexSetCount; i++) + for (var i = 0; i < vertexSetCount; i++) { - vertexData.Add(new XJVertexSet(file, (int)(vertexSetOffset + (0x10 * i)), imageBase)); + _vertexData.Add(new XjVertexSet(file, (int)(vertexSetOffset + 0x10 * i), imageBase)); } - for (int i = 0; i < opaqueMeshesCount; i++) + for (var i = 0; i < opaqueMeshesCount; i++) { - opaqueMeshes.Add(new XJMesh(file, (uint)(opaqueMeshesOffset + (0x14 * i)), imageBase)); + _opaqueMeshes.Add(new XJMesh(file, (uint)(opaqueMeshesOffset + 0x14 * i), imageBase)); } - for (int i = 0; i < translucentMeshesCount; i++) + for (var i = 0; i < translucentMeshesCount; i++) { - translucentMeshes.Add(new XJMesh (file, (uint)(translucentMeshesOffset + (0x14 * i)), imageBase)); + _translucentMeshes.Add(new XJMesh (file, (uint)(translucentMeshesOffset + 0x14 * i), imageBase)); } } - public override byte[] GetBytes(uint imageBase, bool DX, Dictionary labels, List njOffsets, out uint address) + public override byte[] GetBytes(uint imageBase, bool dx, Dictionary labels, List njOffsets, out uint address) { - List result = new List(); - List vdataAddresses = new List(); + List result = []; + List vDataAddresses = []; - foreach(var vdata in vertexData) + foreach(var vData in _vertexData) { - result.AddRange(vdata.GetBytes((uint)(imageBase + result.Count), DX, labels, njOffsets, out uint vdataAddress)); - vdataAddresses.Add(vdataAddress + imageBase); + result.AddRange(vData.GetBytes((uint)(imageBase + result.Count), dx, labels, njOffsets, out var vDataAddress)); + vDataAddresses.Add(vDataAddress + imageBase); } + result.Align(0x10); - int curCount = result.Count; - result.AddRange(XJMesh.GetBytes((uint)(imageBase + result.Count), DX, labels, njOffsets, opaqueMeshes, out uint opaqueAddress)); + var curCount = result.Count; + + result.AddRange(XJMesh.GetBytes((uint)(imageBase + result.Count), dx, labels, njOffsets, _opaqueMeshes, out var opaqueAddress)); opaqueAddress += (uint)curCount; result.Align(0x10); curCount = result.Count; - result.AddRange(XJMesh.GetBytes((uint)(imageBase + result.Count), DX, labels, njOffsets, translucentMeshes, out uint translucentAddress)); + result.AddRange(XJMesh.GetBytes((uint)(imageBase + result.Count), dx, labels, njOffsets, _translucentMeshes, out var translucentAddress)); translucentAddress += (uint)curCount; result.Align(0x10); - address = (uint)(result.Count); + address = (uint)result.Count; + njOffsets.Add((uint)(imageBase + result.Count + 0x4)); njOffsets.Add((uint)(imageBase + result.Count + 0xC)); njOffsets.Add((uint)(imageBase + result.Count + 0x14)); - result.AddRange(ByteConverter.GetBytes(flags)); - result.AddRange(ByteConverter.GetBytes(vdataAddresses[0])); - result.AddRange(ByteConverter.GetBytes(vdataAddresses.Count)); + + result.AddRange(ByteConverter.GetBytes(_flags)); + result.AddRange(ByteConverter.GetBytes(vDataAddresses[0])); + result.AddRange(ByteConverter.GetBytes(vDataAddresses.Count)); result.AddRange(ByteConverter.GetBytes(opaqueAddress + imageBase)); - result.AddRange(ByteConverter.GetBytes(opaqueMeshes.Count)); + result.AddRange(ByteConverter.GetBytes(_opaqueMeshes.Count)); result.AddRange(ByteConverter.GetBytes(translucentAddress + imageBase)); - result.AddRange(ByteConverter.GetBytes(translucentMeshes.Count)); + result.AddRange(ByteConverter.GetBytes(_translucentMeshes.Count)); result.AddRange(Bounds.GetBytes()); result.Align(0x10); @@ -125,38 +122,36 @@ public override byte[] GetBytes(uint imageBase, bool DX, Dictionary meshInfo = new List(); - List> verts = new List>(); - List hasNormals = new List(); - List hasColors = new List(); - List hasUVs = new List(); + List meshInfo = []; + List> verts = []; + List hasNormals = []; + List hasColors = []; + List hasUVs = []; - if (vertexData != null) + if (_vertexData != null) { - for(int vd = 0; vd < vertexData.Count; vd++) //There should only be 1 of these, but in case there's not... + for (var vd = 0; vd < _vertexData.Count; vd++) //There should only be 1 of these, but in case there's not... { - var vertData = vertexData[vd]; - List vertSet = new List(); + var vertData = _vertexData[vd]; + List vertSet = []; hasNormals.Add(vertData.Normals.Count > 0); hasColors.Add(vertData.Colors.Count > 0); hasUVs.Add(vertData.UVs.Count > 0); - for(int i = 0; i < vertData.Positions.Count; i++) + for (var i = 0; i < vertData.Positions.Count; i++) { var normal = hasNormals[vd] ? vertData.Normals[i] : new Vertex(0, 1, 0); var color = hasColors[vd] ? vertData.Colors[i] : System.Drawing.Color.White; var uv = hasUVs[vd] ? vertData.UVs[i] : new UV(0, 0); + vertSet.Add(new VertexData(vertData.Positions[i], normal, color, uv)); } verts.Add(vertSet); } } - foreach(XJMesh m in opaqueMeshes) - meshInfo.Add(m.Process(verts[0], hasNormals[0], hasColors[0])); - - foreach (XJMesh m in translucentMeshes) - meshInfo.Add(m.Process(verts[0], hasNormals[0], hasColors[0])); + meshInfo.AddRange(_opaqueMeshes.Select(m => m.Process(verts[0], hasNormals[0], hasColors[0]))); + meshInfo.AddRange(_translucentMeshes.Select(m => m.Process(verts[0], hasNormals[0], hasColors[0]))); MeshInfo = meshInfo.ToArray(); } @@ -171,12 +166,12 @@ public override void ProcessShapeMotionVertexData(NJS_MOTION motion, float frame { throw new NotImplementedException(); } - public override string ToStruct(bool DX) + public override string ToStruct(bool dx) { throw new NotImplementedException(); } - public override void ToStructVariables(TextWriter writer, bool DX, List labels, string[] textures = null) + public override void ToStructVariables(TextWriter writer, bool dx, List labels, string[] textures = null) { throw new NotImplementedException(); } diff --git a/Libraries/SAModel/XJ/XJMesh.cs b/Libraries/SAModel/XJ/XJMesh.cs index 95f9c08d9..4862059c1 100644 --- a/Libraries/SAModel/XJ/XJMesh.cs +++ b/Libraries/SAModel/XJ/XJMesh.cs @@ -1,16 +1,13 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SAModel.XJ { - public class XJMesh { - public NJS_MATERIAL mat; - public int mat2_08; //These might just be padding sicne all mat types seem to be 0xC + private NJS_MATERIAL _mat; + + public int mat2_08; //These might just be padding since all mat types seem to be 0xC public int mat3_04; public int mat3_08; public int mat4_00; @@ -22,28 +19,29 @@ public class XJMesh public int mat6_04; public int mat6_08; - public byte[] stripIndices; + private byte[] _stripIndices; public XJMesh(byte[] file, uint address, uint imageBase) { - uint materialOffset = ByteConverter.ToUInt32(file, (int)address); - uint materialStructCount = ByteConverter.ToUInt32(file, (int)address + 0x4); - uint indexListOffset = ByteConverter.ToUInt32(file, (int)address + 0x8); - uint indexCount = ByteConverter.ToUInt32(file, (int)address + 0xC); - uint int_0x10 = ByteConverter.ToUInt32(file, (int)address + 0x10); + var materialOffset = ByteConverter.ToUInt32(file, (int)address); + var materialStructCount = ByteConverter.ToUInt32(file, (int)address + 0x4); + var indexListOffset = ByteConverter.ToUInt32(file, (int)address + 0x8); + var indexCount = ByteConverter.ToUInt32(file, (int)address + 0xC); + var int_0x10 = ByteConverter.ToUInt32(file, (int)address + 0x10); ReadMaterial(file, materialOffset - imageBase, materialStructCount); ReadStrips(file, indexListOffset - imageBase, indexCount); } - public static byte[] GetBytes(uint imageBase, bool DX, Dictionary labels, List njOffsets, List xjMeshes, out uint address) + public static byte[] GetBytes(uint imageBase, bool dx, Dictionary labels, List njOffsets, List xjMeshes, out uint address) { - List result = new List(); - List matOffsetList = new List(); - List stripOffsetList = new List(); + var result = new List(); + var matOffsetList = new List(); + var stripOffsetList = new List(); address = (uint)result.Count; - for (int i = 0; i < xjMeshes.Count; i++) + + foreach (var mesh in xjMeshes) { matOffsetList.Add(result.Count); stripOffsetList.Add(result.Count + 0x8); @@ -52,59 +50,64 @@ public static byte[] GetBytes(uint imageBase, bool DX, Dictionary result.AddRange(ByteConverter.GetBytes(0)); result.AddRange(ByteConverter.GetBytes(3)); result.AddRange(ByteConverter.GetBytes(0)); - result.AddRange(ByteConverter.GetBytes(xjMeshes[i].stripIndices.Length / 2)); + result.AddRange(ByteConverter.GetBytes(mesh._stripIndices.Length / 2)); result.AddRange(ByteConverter.GetBytes(0)); } + result.Align(0x10); - for(int i = 0; i < xjMeshes.Count; i++) + for(var i = 0; i < xjMeshes.Count; i++) { var mesh = xjMeshes[i]; - //Material + + // Material result.SetByteListInt(matOffsetList[i], (int)(result.Count + imageBase)); - //Blend types + // Blend types result.AddRange(ByteConverter.GetBytes(2)); - result.AddRange(ByteConverter.GetBytes((uint)mesh.mat.SourceAlpha)); - result.AddRange(ByteConverter.GetBytes((uint)mesh.mat.DestinationAlpha)); + result.AddRange(ByteConverter.GetBytes((uint)mesh._mat.SourceAlpha)); + result.AddRange(ByteConverter.GetBytes((uint)mesh._mat.DestinationAlpha)); result.AddRange(ByteConverter.GetBytes(0)); - //Texture ID + + // Texture ID result.AddRange(ByteConverter.GetBytes(3)); - result.AddRange(ByteConverter.GetBytes((uint)mesh.mat.TextureID)); + result.AddRange(ByteConverter.GetBytes((uint)mesh._mat.TextureID)); result.AddRange(ByteConverter.GetBytes(0)); result.AddRange(ByteConverter.GetBytes(0)); - //Diffuse color + + // Diffuse color result.AddRange(ByteConverter.GetBytes(5)); - result.AddRange(VColor.GetBytes(mesh.mat.DiffuseColor, ColorType.RGBA8888_32)); + result.AddRange(VColor.GetBytes(mesh._mat.DiffuseColor, ColorType.RGBA8888_32)); result.AddRange(ByteConverter.GetBytes(0)); result.AddRange(ByteConverter.GetBytes(0)); result.Align(0x10); - //Strips + // Strips result.SetByteListInt(stripOffsetList[i], (int)(result.Count + imageBase)); - result.AddRange(mesh.stripIndices); + result.AddRange(mesh._stripIndices); result.Align(0x10); } return result.ToArray(); } - public void ReadStrips(byte[] file, uint address, uint indexCount) + private void ReadStrips(byte[] file, uint address, uint indexCount) { - int curAddress = (int)address; - stripIndices = new byte[indexCount * 2]; - Array.Copy(file, address, stripIndices, 0, indexCount * 2); + var curAddress = (int)address; + _stripIndices = new byte[indexCount * 2]; + Array.Copy(file, address, _stripIndices, 0, indexCount * 2); } - //Nobody ever worked out the real way to read these types of strips so these will be returned double sided - public List GetTriangles() + //Nobody ever worked out the real way to read these types of strips so these will be returned double-sided + private List GetTriangles() { - List tris = new List(); - for(int i = 0; i < stripIndices.Length - 4; i += 2) + var tris = new List(); + + for (var i = 0; i < _stripIndices.Length - 4; i += 2) { - var a = ByteConverter.ToUInt16(stripIndices, i); - var b = ByteConverter.ToUInt16(stripIndices, i + 2); - var c = ByteConverter.ToUInt16(stripIndices, i + 4); + var a = ByteConverter.ToUInt16(_stripIndices, i); + var b = ByteConverter.ToUInt16(_stripIndices, i + 2); + var c = ByteConverter.ToUInt16(_stripIndices, i + 4); if(a == b || b == c || c == a) { @@ -118,56 +121,72 @@ public List GetTriangles() return tris; } - public void ReadMaterial(byte[] file, uint address, uint materialStructCount) + private void ReadMaterial(byte[] file, uint address, uint materialStructCount) { - NJS_MATERIAL newMat = new NJS_MATERIAL(); - int curAddress = (int)address; - for(int i = 0; i < materialStructCount; i++) + var newMat = new NJS_MATERIAL(); + var curAddress = (int)address; + + for (var i = 0; i < materialStructCount; i++) { - uint type = ByteConverter.ToUInt32(file, curAddress); curAddress += 4; + var type = ByteConverter.ToUInt32(file, curAddress); + curAddress += 4; + switch(type) { case 2: - newMat.SourceAlpha = (AlphaInstruction)ByteConverter.ToUInt32(file, curAddress); curAddress += 4; - newMat.DestinationAlpha = (AlphaInstruction)ByteConverter.ToUInt32(file, curAddress); curAddress += 4; - mat2_08 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; + newMat.SourceAlpha = (AlphaInstruction)ByteConverter.ToUInt32(file, curAddress); + curAddress += 4; + newMat.DestinationAlpha = (AlphaInstruction)ByteConverter.ToUInt32(file, curAddress); + curAddress += 4; + mat2_08 = ByteConverter.ToInt32(file, curAddress); break; case 3: - newMat.TextureID = ByteConverter.ToInt32(file, curAddress); curAddress += 4; - mat3_04 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; - mat3_08 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; + newMat.TextureID = ByteConverter.ToInt32(file, curAddress); + curAddress += 4; + mat3_04 = ByteConverter.ToInt32(file, curAddress); + curAddress += 4; + mat3_08 = ByteConverter.ToInt32(file, curAddress); break; case 4: - mat4_00 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; - mat4_04 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; - mat4_08 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; + mat4_00 = ByteConverter.ToInt32(file, curAddress); + curAddress += 4; + mat4_04 = ByteConverter.ToInt32(file, curAddress); + curAddress += 4; + mat4_08 = ByteConverter.ToInt32(file, curAddress); break; case 5: newMat.DiffuseColor = VColor.FromBytes(file, (int)address, ColorType.RGBA8888_32); - mat5_04 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; - mat5_08 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; + mat5_04 = ByteConverter.ToInt32(file, curAddress); + curAddress += 4; + mat5_08 = ByteConverter.ToInt32(file, curAddress); break; case 6: - mat6_00 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; - mat6_04 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; - mat6_08 = ByteConverter.ToInt32(file, curAddress); curAddress += 4; + mat6_00 = ByteConverter.ToInt32(file, curAddress); + curAddress += 4; + mat6_04 = ByteConverter.ToInt32(file, curAddress); + curAddress += 4; + mat6_08 = ByteConverter.ToInt32(file, curAddress); break; default: - ByteConverter.ToInt32(file, curAddress); curAddress += 4; - ByteConverter.ToInt32(file, curAddress); curAddress += 4; - ByteConverter.ToInt32(file, curAddress); curAddress += 4; + ByteConverter.ToInt32(file, curAddress); + curAddress += 4; + ByteConverter.ToInt32(file, curAddress); + curAddress += 4; + ByteConverter.ToInt32(file, curAddress); //Debug.WriteLine($"Unexpected xj material type {type} at {(curAddress - 4).ToString("X")}"); break; } + + curAddress += 4; } - mat = newMat; + _mat = newMat; } public MeshInfo Process(List verts, bool hasNormal, bool hasColors) { var polys = GetTriangles(); - return new MeshInfo(new NJS_MATERIAL(mat), polys.ToArray(), verts.ToArray(), hasNormal, hasColors); + return new MeshInfo(new NJS_MATERIAL(_mat), polys.ToArray(), verts.ToArray(), hasNormal, hasColors); } } } diff --git a/Libraries/SAModel/XJ/XJVertexSet.cs b/Libraries/SAModel/XJ/XJVertexSet.cs index b85e7d0b8..caf5edc73 100644 --- a/Libraries/SAModel/XJ/XJVertexSet.cs +++ b/Libraries/SAModel/XJ/XJVertexSet.cs @@ -1,58 +1,66 @@ using System; using System.Collections.Generic; using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SAModel.XJ { - public class XJVertexSet + public class XjVertexSet { - public List Positions = new List(); - public List Normals = new List(); - public List Colors = new List(); - public List UVs = new List(); - - public ushort VertexType; - public ushort Ushort_02; - public uint VertexSize; - public XJVertexSet(byte[] file, int address, uint imageBase) + public readonly List Positions = []; + public readonly List Normals = []; + public readonly List Colors = []; + public readonly List UVs = []; + + private readonly ushort _vertexType; + private readonly ushort _ushort02; + private readonly uint _vertexSize; + + public XjVertexSet(byte[] file, int address, uint imageBase) : this(file, address, imageBase, new Dictionary()) { } - public XJVertexSet(byte[] file, int address, uint imageBase, Dictionary labels) + + private XjVertexSet(byte[] file, int address, uint imageBase, Dictionary labels) { - VertexType = ByteConverter.ToUInt16(file, address); - Ushort_02 = ByteConverter.ToUInt16(file, address + 0x2); + _vertexType = ByteConverter.ToUInt16(file, address); + _ushort02 = ByteConverter.ToUInt16(file, address + 0x2); var vertListOffset = ByteConverter.ToUInt32(file, address + 0x4); - VertexSize = ByteConverter.ToUInt32(file, address + 0x8); + _vertexSize = ByteConverter.ToUInt32(file, address + 0x8); var vertCount = ByteConverter.ToUInt32(file, address + 0xC); - bool hasUV = (VertexType & 0x1) > 0; - bool hasNormal = (VertexType & 0x2) > 0; - bool hasColor = (VertexType & 0x4) > 0; - bool hasUnk0 = (VertexType & 0x8) > 0; - bool hasUnk1 = (VertexType & 0x10) > 0; - bool hasUnk2 = (VertexType & 0x20) > 0; - bool hasUnk3 = (VertexType & 0x40) > 0; - bool hasUnk4 = (VertexType & 0x80) > 0; + var hasUv = (_vertexType & 0x1) > 0; + var hasNormal = (_vertexType & 0x2) > 0; + var hasColor = (_vertexType & 0x4) > 0; + var hasUnk0 = (_vertexType & 0x8) > 0; + var hasUnk1 = (_vertexType & 0x10) > 0; + var hasUnk2 = (_vertexType & 0x20) > 0; + var hasUnk3 = (_vertexType & 0x40) > 0; + var hasUnk4 = (_vertexType & 0x80) > 0; ushort calcedVertSize = 0xC; if (hasNormal) + { calcedVertSize += 0xC; + } + if (hasColor) + { calcedVertSize += 0x4; - if (hasUV) + } + + if (hasUv) + { calcedVertSize += 0x8; + } - if(VertexSize != calcedVertSize) + if (_vertexSize != calcedVertSize) { - throw new Exception($"Vertsize {VertexSize} is not equal to Calculated Vertsize {calcedVertSize}"); + throw new Exception($"Vertsize {_vertexSize} is not equal to Calculated Vertsize {calcedVertSize}"); } address = (int)(vertListOffset - imageBase); - for(int i = 0; i < vertCount; i++) + + for (var i = 0; i < vertCount; i++) { Positions.Add(new Vertex(file, address)); address += 0xC; @@ -62,12 +70,14 @@ public XJVertexSet(byte[] file, int address, uint imageBase, Dictionary labels, List njOffsets, out uint address) + public byte[] GetBytes(uint imageBase, bool dx, Dictionary labels, List njOffsets, out uint address) { - bool hasUV = (VertexType & 0x1) > 0; - bool hasNormal = (VertexType & 0x2) > 0; - bool hasColor = (VertexType & 0x4) > 0; - bool hasUnk0 = (VertexType & 0x8) > 0; - bool hasUnk1 = (VertexType & 0x10) > 0; - bool hasUnk2 = (VertexType & 0x20) > 0; - bool hasUnk3 = (VertexType & 0x40) > 0; - bool hasUnk4 = (VertexType & 0x80) > 0; - - List result = new List(); - - for (int i = 0; i < Positions.Count; i++) + var hasUv = (_vertexType & 0x1) > 0; + var hasNormal = (_vertexType & 0x2) > 0; + var hasColor = (_vertexType & 0x4) > 0; + var hasUnk0 = (_vertexType & 0x8) > 0; + var hasUnk1 = (_vertexType & 0x10) > 0; + var hasUnk2 = (_vertexType & 0x20) > 0; + var hasUnk3 = (_vertexType & 0x40) > 0; + var hasUnk4 = (_vertexType & 0x80) > 0; + + List result = []; + + for (var i = 0; i < Positions.Count; i++) { result.AddRange(Positions[i].GetBytes()); + if(hasNormal) + { result.AddRange(Normals[i].GetBytes()); + } + if(hasColor) + { result.AddRange(VColor.GetBytes(Colors[i], ColorType.RGBA8888_32)); - if(hasUV) + } + + if(hasUv) + { result.AddRange(UVs[i].GetBytesXJ()); + } } + result.Align(0x10); - address = (uint)(result.Count); + address = (uint)result.Count; njOffsets.Add((uint)result.Count + imageBase + 4); - result.AddRange(ByteConverter.GetBytes(VertexType)); - result.AddRange(ByteConverter.GetBytes(Ushort_02)); + result.AddRange(ByteConverter.GetBytes(_vertexType)); + result.AddRange(ByteConverter.GetBytes(_ushort02)); result.AddRange(ByteConverter.GetBytes(imageBase)); - result.AddRange(ByteConverter.GetBytes(VertexSize)); + result.AddRange(ByteConverter.GetBytes(_vertexSize)); result.AddRange(ByteConverter.GetBytes(Positions.Count)); result.Align(0x10); diff --git a/SAMDL/MainForm.cs b/SAMDL/MainForm.cs index cca203ef4..86166977d 100644 --- a/SAMDL/MainForm.cs +++ b/SAMDL/MainForm.cs @@ -2045,9 +2045,9 @@ private void pasteModelToolStripMenuItem_Click(object sender, EventArgs e) case GC.GCAttach gatt: string vtype = null; gatt.VertexName = "vertex_" + Extensions.GenerateIdentifier(); - foreach (GC.GCVertexSet m in gatt.vertexData) + foreach (GC.GCVertexSet m in gatt.VertexData) { - switch (m.attribute) + switch (m.Attribute) { case GC.GCVertexAttribute.Position: vtype = "position_"; @@ -2065,13 +2065,13 @@ private void pasteModelToolStripMenuItem_Click(object sender, EventArgs e) m.DataName = $"{vtype}" + Extensions.GenerateIdentifier(); } gatt.OpaqueMeshName = "opoly_" + Extensions.GenerateIdentifier(); - foreach (GC.GCMesh m in gatt.opaqueMeshes) + foreach (GC.GCMesh m in gatt.OpaqueMeshes) { m.ParameterName = "parameter_" + Extensions.GenerateIdentifier(); m.PrimitiveName = "primitive_" + Extensions.GenerateIdentifier(); } gatt.TranslucentMeshName = "tpoly_" + Extensions.GenerateIdentifier(); - foreach (GC.GCMesh m in gatt.translucentMeshes) + foreach (GC.GCMesh m in gatt.TranslucentMeshes) { m.ParameterName = "parameter_" + Extensions.GenerateIdentifier(); m.PrimitiveName = "primitive_" + Extensions.GenerateIdentifier(); @@ -2180,9 +2180,9 @@ private void OpenGCMaterialEditor() { case GC.GCAttach gcatt: int matind = 0; - foreach (var msh in gcatt.opaqueMeshes.Concat(gcatt.translucentMeshes)) + foreach (var msh in gcatt.OpaqueMeshes.Concat(gcatt.TranslucentMeshes)) { - msh.parameters.RemoveAll(a => a is GC.TextureParameter); + msh.Parameters.RemoveAll(a => a is GC.TextureParameter); if (mats[matind].UseTexture) { GC.GCTileMode tm = GC.GCTileMode.Mask; @@ -2194,10 +2194,10 @@ private void OpenGCMaterialEditor() tm &= ~GC.GCTileMode.WrapV; if (mats[matind].FlipV) tm &= ~GC.GCTileMode.MirrorV; - msh.parameters.Add(new GC.TextureParameter((ushort)mats[matind].TextureID, tm)); + msh.Parameters.Add(new GC.TextureParameter((ushort)mats[matind].TextureID, tm)); } - msh.parameters.RemoveAll(a => a is GC.BlendAlphaParameter); - msh.parameters.Add(new GC.BlendAlphaParameter() { NJSourceAlpha = mats[matind].SourceAlpha, NJDestAlpha = mats[matind].DestinationAlpha }); + msh.Parameters.RemoveAll(a => a is GC.BlendAlphaParameter); + msh.Parameters.Add(new GC.BlendAlphaParameter() { NJSourceAlpha = mats[matind].SourceAlpha, NJDestAlpha = mats[matind].DestinationAlpha }); matind++; } break; @@ -2311,13 +2311,13 @@ private void textureRemappingToolStripMenuItem_Click(object sender, EventArgs e) tex.TextureID = (ushort)dlg.TextureMap[tex.TextureID]; break; case GC.GCAttach gatt: - foreach (var msh in gatt.opaqueMeshes.Concat(gatt.translucentMeshes)) + foreach (var msh in gatt.OpaqueMeshes.Concat(gatt.TranslucentMeshes)) { - var tp = (GC.TextureParameter)msh.parameters.LastOrDefault(a => a is GC.TextureParameter); - if (tp != null && dlg.TextureMap.ContainsKey(tp.TextureID)) + var tp = (GC.TextureParameter)msh.Parameters.LastOrDefault(a => a is GC.TextureParameter); + if (tp != null && dlg.TextureMap.ContainsKey(tp.TextureId)) { - msh.parameters.RemoveAll(a => a is GC.TextureParameter); - msh.parameters.Add(new GC.TextureParameter((ushort)dlg.TextureMap[tp.TextureID], tp.Tile)); + msh.Parameters.RemoveAll(a => a is GC.TextureParameter); + msh.Parameters.Add(new GC.TextureParameter((ushort)dlg.TextureMap[tp.TextureId], tp.Tile)); } } break; @@ -3766,7 +3766,7 @@ private void CheckLabels(NJS_OBJECT obj, List duplicateLabels) gcatt.OpaqueMeshName = FixLabel(gcatt.OpaqueMeshName, checkingLabels, out dup); if (!string.IsNullOrEmpty(dup)) duplicateLabels.Add(dup); - foreach (GC.GCMesh m in gcatt.opaqueMeshes) + foreach (GC.GCMesh m in gcatt.OpaqueMeshes) { m.ParameterName = FixLabel(m.ParameterName, checkingLabels, out dup); if (!string.IsNullOrEmpty(dup)) @@ -3779,7 +3779,7 @@ private void CheckLabels(NJS_OBJECT obj, List duplicateLabels) gcatt.TranslucentMeshName = FixLabel(gcatt.TranslucentMeshName, checkingLabels, out dup); if (!string.IsNullOrEmpty(dup)) duplicateLabels.Add(dup); - foreach (GC.GCMesh m in gcatt.translucentMeshes) + foreach (GC.GCMesh m in gcatt.TranslucentMeshes) { m.ParameterName = FixLabel(m.ParameterName, checkingLabels, out dup); if (!string.IsNullOrEmpty(dup)) @@ -3792,7 +3792,7 @@ private void CheckLabels(NJS_OBJECT obj, List duplicateLabels) gcatt.VertexName = FixLabel(gcatt.VertexName, checkingLabels, out dup); if (!string.IsNullOrEmpty(dup)) duplicateLabels.Add(dup); - foreach (GC.GCVertexSet v in gcatt.vertexData) + foreach (GC.GCVertexSet v in gcatt.VertexData) { v.DataName = FixLabel(v.DataName, checkingLabels, out dup); if (!string.IsNullOrEmpty(dup)) @@ -3864,27 +3864,27 @@ private void RandomizeLabels(NJS_OBJECT obj) string vtype = null; GC.GCAttach gcatt = (GC.GCAttach)obj.Attach; gcatt.TranslucentMeshName = "tpoly_" + Extensions.GenerateIdentifier(); - if (gcatt.translucentMeshes.Count != 0) + if (gcatt.TranslucentMeshes.Count != 0) { - foreach (GC.GCMesh m in gcatt.translucentMeshes) + foreach (GC.GCMesh m in gcatt.TranslucentMeshes) { m.ParameterName = "parameter_" + Extensions.GenerateIdentifier(); m.PrimitiveName = "primitive_" + Extensions.GenerateIdentifier(); } } gcatt.OpaqueMeshName = "opoly_" + Extensions.GenerateIdentifier(); - if (gcatt.opaqueMeshes.Count != 0) + if (gcatt.OpaqueMeshes.Count != 0) { - foreach (GC.GCMesh m in gcatt.opaqueMeshes) + foreach (GC.GCMesh m in gcatt.OpaqueMeshes) { m.ParameterName = "parameter_" + Extensions.GenerateIdentifier(); m.PrimitiveName = "primitive_" + Extensions.GenerateIdentifier(); } } gcatt.VertexName = "vertex_" + Extensions.GenerateIdentifier(); - foreach (GC.GCVertexSet v in gcatt.vertexData) + foreach (GC.GCVertexSet v in gcatt.VertexData) { - switch (v.attribute) + switch (v.Attribute) { case GC.GCVertexAttribute.Position: vtype = "position_";