From ee82fb121867d6bf0b1e75e4afc0a8142889f967 Mon Sep 17 00:00:00 2001 From: KillzXGaming Date: Sun, 10 Nov 2019 09:45:39 -0500 Subject: [PATCH] Some LM3 adjustments --- .../FileFormats/NLG/LM3/LM3_ChunkTable.cs | 3 - .../FileFormats/NLG/LM3/LM3_DICT.cs | 189 +++++++++++------- .../FileFormats/NLG/LM3/LM3_Enums.cs | 11 +- .../FileFormats/NLG/LM3/LM3_Model.cs | 12 +- .../FileFormats/NLG/LM3/TexturePOWE.cs | 48 +++-- .../FileFormats/NLG/NLG_Common.cs | 2 +- 6 files changed, 164 insertions(+), 101 deletions(-) diff --git a/File_Format_Library/FileFormats/NLG/LM3/LM3_ChunkTable.cs b/File_Format_Library/FileFormats/NLG/LM3/LM3_ChunkTable.cs index 2710e469..330af5ed 100644 --- a/File_Format_Library/FileFormats/NLG/LM3/LM3_ChunkTable.cs +++ b/File_Format_Library/FileFormats/NLG/LM3/LM3_ChunkTable.cs @@ -57,7 +57,6 @@ namespace FirstPlugin.LuigisMansion3 byte unk = tableReader.ReadByte(); byte chunkFlags = tableReader.ReadByte(); entry.ChunkSubCount = tableReader.ReadByte(); //Uncertain about this. 2 for textures (info + block). Some sections however use large numbers. - Console.WriteLine($"ChunkSubCount {entry.ChunkSubCount} {(entry.ChunkSubCount >> 4)}"); tableReader.ReadByte(); tableReader.ReadByte(); tableReader.ReadByte(); @@ -83,8 +82,6 @@ namespace FirstPlugin.LuigisMansion3 var chunkFlags = tableReader.ReadUInt16(); byte blockFlag = (byte)(chunkFlags >> 12); - //Console.WriteLine($"blockFlag {chunkFlags >> 1} {chunkFlags >> 2} {chunkFlags >> 3} {chunkFlags >> 4} {chunkFlags >> 5} {chunkFlags >> 6} {chunkFlags >> 7} {chunkFlags >> 8}"); - Console.WriteLine($"blockFlag {blockFlag} { subEntry.ChunkType}"); subEntry.ChunkSize = tableReader.ReadUInt32(); subEntry.ChunkOffset = tableReader.ReadUInt32(); diff --git a/File_Format_Library/FileFormats/NLG/LM3/LM3_DICT.cs b/File_Format_Library/FileFormats/NLG/LM3/LM3_DICT.cs index 1fda3780..af986b00 100644 --- a/File_Format_Library/FileFormats/NLG/LM3/LM3_DICT.cs +++ b/File_Format_Library/FileFormats/NLG/LM3/LM3_DICT.cs @@ -99,31 +99,7 @@ namespace FirstPlugin.LuigisMansion3 TreeNode materialNamesFolder = new TreeNode("Material Names"); TreeNode chunkFolder = new TreeNode("Chunks"); - public static Dictionary HashNames = new Dictionary(); - - - public List StringList = new List(); - public static void LoadHashes() - { - foreach (string hashStr in Properties.Resources.LM3_Hashes.Split('\n')) - { - string HashString = hashStr.TrimEnd(); - - uint hash = (uint)NLG_Common.StringToHash(HashString); - if (!HashNames.ContainsKey(hash)) - HashNames.Add(hash, HashString); - - string[] hashPaths = HashString.Split('/'); - for (int i = 0; i < hashPaths?.Length; i++) - { - hash = (uint)NLG_Common.StringToHash(hashPaths[i]); - if (!HashNames.ContainsKey(hash)) - HashNames.Add(hash, HashString); - } - } - } - public System.IO.Stream GetFileBufferData() { @@ -144,7 +120,6 @@ namespace FirstPlugin.LuigisMansion3 { CanSave = false; - LoadHashes(); modelFolder = new LM3_ModelFolder(this); DrawableContainer.Name = FileName; Renderer = new LM3_Renderer(); @@ -291,7 +266,6 @@ namespace FirstPlugin.LuigisMansion3 uint havokFileIndex = 0; foreach (var chunk in ChunkTable.ChunkSubEntries) { - Console.WriteLine($"chunk.ChunkType {chunk.ChunkType}"); var chunkEntry = new ChunkDataEntry(this, chunk); chunkEntry.Text = $"{chunkId} {chunk.ChunkType.ToString("X")} {chunk.ChunkType} {chunk.ChunkOffset} {chunk.ChunkSize}"; @@ -309,6 +283,7 @@ namespace FirstPlugin.LuigisMansion3 using (var textureReader = new FileReader(chunkEntry.FileData, true)) { currentTexture = new TexturePOWE(); + currentTexture.HeaderOffset = chunk.ChunkOffset; currentTexture.ImageKey = "texture"; currentTexture.SelectedImageKey = currentTexture.ImageKey; currentTexture.Index = ImageHeaderIndex; @@ -318,8 +293,8 @@ namespace FirstPlugin.LuigisMansion3 else currentTexture.Text = $"Texture {currentTexture.ID2.ToString("X")}"; - if (HashNames.ContainsKey(currentTexture.ID2)) - currentTexture.Text = HashNames[currentTexture.ID2]; + if (NLG_Common.HashNames.ContainsKey(currentTexture.ID2)) + currentTexture.Text = NLG_Common.HashNames[currentTexture.ID2]; textureFolder.Nodes.Add(currentTexture); if (!Renderer.TextureList.ContainsKey(currentTexture.ID2.ToString("x"))) @@ -335,7 +310,28 @@ namespace FirstPlugin.LuigisMansion3 currentTexture.DataOffset = chunk.ChunkOffset; currentTexture.ImageData = chunkEntry.FileData.ToBytes(); break; - case SubDataType.ModelStart: + case SubDataType.ModelInfo: + chunkEntry.DataFile = File052Data; + + uint numModels = chunk.ChunkSize / 12; + using (var dataReader = new FileReader(chunkEntry.FileData, true)) + { + for (int i = 0; i < numModels; i++) + { + uint hashID = dataReader.ReadUInt32(); + uint numMeshes = dataReader.ReadUInt32(); + dataReader.ReadUInt32(); //0 + + string text = hashID.ToString("X"); + if (NLG_Common.HashNames.ContainsKey(hashID)) + text = NLG_Common.HashNames[hashID]; + + + currentModel.Text = text; + } + } + break; + case SubDataType.MaterailData: currentModelChunk = new TreeNode($"Model {modelIndex}"); chunkFolder.Nodes.Add(currentModelChunk); @@ -347,8 +343,8 @@ namespace FirstPlugin.LuigisMansion3 if (ModelHashes.Count > modelIndex) { currentModel.Text = $"Model {modelIndex} {ModelHashes[(int)modelIndex].ToString("x")}"; - if (HashNames.ContainsKey(ModelHashes[(int)modelIndex])) - currentModel.Text = HashNames[ModelHashes[(int)modelIndex]]; + if (NLG_Common.HashNames.ContainsKey(ModelHashes[(int)modelIndex])) + currentModel.Text = NLG_Common.HashNames[ModelHashes[(int)modelIndex]]; } modelIndex++; @@ -366,8 +362,8 @@ namespace FirstPlugin.LuigisMansion3 chunkEntry.DataFile = File052Data; int MeshCount = (int)chunkEntry.FileData.Length / 0x40; - using (var vtxPtrReader = new FileReader(currentVertexPointerList.FileData)) - using (var meshReader = new FileReader(chunkEntry.FileData)) + using (var vtxPtrReader = new FileReader(currentVertexPointerList.FileData, true)) + using (var meshReader = new FileReader(chunkEntry.FileData, true)) { for (uint i = 0; i < MeshCount; i++) { @@ -376,8 +372,6 @@ namespace FirstPlugin.LuigisMansion3 mesh.Read(meshReader); currentModel.Meshes.Add(mesh); - Console.WriteLine($"mesh.Unknown3 {mesh.Unknown3 }"); - var buffer = new LM3_Model.PointerInfo(); buffer.Read(vtxPtrReader, mesh.Unknown3 != 4294967295); currentModel.VertexBufferPointers.Add(buffer); @@ -388,7 +382,7 @@ namespace FirstPlugin.LuigisMansion3 break; case SubDataType.ModelTransform: chunkEntry.DataFile = File052Data; - using (var transformReader = new FileReader(chunkEntry.FileData)) + using (var transformReader = new FileReader(chunkEntry.FileData, true)) { //This is possibly very wrong //The data isn't always per mesh, but sometimes is @@ -416,7 +410,7 @@ namespace FirstPlugin.LuigisMansion3 if (chunk.ChunkSize > 0x40 && currentModel.Skeleton == null) { chunkEntry.DataFile = File052Data; - using (var boneReader = new FileReader(chunkEntry.FileData)) + using (var boneReader = new FileReader(chunkEntry.FileData, true)) { currentModel.Skeleton = new STSkeleton(); DrawableContainer.Drawables.Add(currentModel.Skeleton); @@ -425,27 +419,38 @@ namespace FirstPlugin.LuigisMansion3 for (int i = 0; i < numBones; i++) { boneReader.SeekBegin(i * 0x40); - uint hash = boneReader.ReadUInt32(); - + uint HashID = boneReader.ReadUInt32(); + boneReader.ReadUInt32(); //unk + boneReader.ReadUInt32(); //unk + boneReader.ReadSingle(); //0 + var Scale = new OpenTK.Vector3( + boneReader.ReadSingle(), + boneReader.ReadSingle(), + boneReader.ReadSingle()); + boneReader.ReadSingle(); //0 + var Rotate = new OpenTK.Vector3( + boneReader.ReadSingle(), + boneReader.ReadSingle(), + boneReader.ReadSingle()); + boneReader.ReadSingle(); //0 + var Position = new OpenTK.Vector3( + boneReader.ReadSingle(), + boneReader.ReadSingle(), + boneReader.ReadSingle()); + float test = boneReader.ReadSingle(); //1 STBone bone = new STBone(currentModel.Skeleton); - bone.Text = hash.ToString("x"); - if (HashNames.ContainsKey(hash)) - bone.Text = HashNames[hash]; - bone.position = new float[3] { 0, 0, 0 }; - bone.rotation = new float[4] { 0, 0, 0, 1 }; + bone.Text = HashID.ToString("x"); + // if (HashNames.ContainsKey(HashID)) + // bone.Text = HashNames[HashID]; + // else + // Console.WriteLine($"bone hash {HashID}"); + + bone.position = new float[3] { Position.X, Position.Z, Position.Y }; + bone.rotation = new float[4] { Rotate.X, Rotate.Y, Rotate.Z, 1 }; bone.scale = new float[3] { 0.2f, 0.2f, 0.2f }; - - boneReader.SeekBegin(48 + (i * 0x40)); - var Position = new OpenTK.Vector3(boneReader.ReadSingle(), boneReader.ReadSingle(), boneReader.ReadSingle()); - Position = OpenTK.Vector3.TransformPosition(Position, OpenTK.Matrix4.CreateRotationX(OpenTK.MathHelper.DegreesToRadians(90))); - bone.position[0] = Position.X; - bone.position[2] = Position.Y; - bone.position[1] = Position.Z; - - bone.RotationType = STBone.BoneRotationType.Euler; - // currentModel.Skeleton.bones.Add(bone); + currentModel.Skeleton.bones.Add(bone); } currentModel.Skeleton.reset(); @@ -483,16 +488,14 @@ namespace FirstPlugin.LuigisMansion3 break;*/ case SubDataType.BoneHashList: chunkEntry.DataFile = File053Data; - Console.WriteLine("Model Check! " + currentModel.Text); - - using (var chunkReader = new FileReader(chunkEntry.FileData)) + using (var chunkReader = new FileReader(chunkEntry.FileData, true)) { while (chunkReader.Position <= chunkReader.BaseStream.Length - 4) { uint hash = chunkReader.ReadUInt32(); - if (HashNames.ContainsKey(hash)) - Console.WriteLine("Hash Match! " + HashNames[hash]); + // if (HashNames.ContainsKey(hash)) + // Console.WriteLine("Hash Match! " + HashNames[hash]); } } break; @@ -501,7 +504,8 @@ namespace FirstPlugin.LuigisMansion3 break; } - if (chunk.ChunkType == SubDataType.ModelStart || + if (chunk.ChunkType == SubDataType.MaterailData || + chunk.ChunkType == SubDataType.ModelInfo || chunk.ChunkType == SubDataType.MeshBuffers || chunk.ChunkType == SubDataType.MeshIndexTable || chunk.ChunkType == SubDataType.SubmeshInfo || @@ -578,26 +582,52 @@ namespace FirstPlugin.LuigisMansion3 for (int i = 0; i < fileEntries.Count; i++) { uint offset = (uint)dataWriter.Position; - var decomp = fileEntries[i].GetData(); + var decompStream = fileEntries[i].GetData(); + var mem = new System.IO.MemoryStream(); + var decompSize = mem.Length; - if (i == 65) + if (i == 63) { - using (var imageDataWriter = new FileWriter(decomp)) + using (var imageDataWriter = new FileWriter(mem, true)) { - foreach (TexturePOWE image in textureFolder.Nodes) { + imageDataWriter.Write(decompStream.ToBytes()); + foreach (TexturePOWE image in textureFolder.Nodes) + { + imageDataWriter.SeekBegin(image.HeaderOffset); + imageDataWriter.Write(image.ID2); + imageDataWriter.Write((ushort)image.Width); + imageDataWriter.Write((ushort)image.Height); + imageDataWriter.Write((ushort)image.Unknown); + imageDataWriter.Write((byte)image.ArrayCount); + imageDataWriter.Write((byte)image.Unknown2); + imageDataWriter.Write((byte)image.TexFormat); + imageDataWriter.Write((byte)image.Unknown3); + imageDataWriter.Write((ushort)image.Unknown4); + } + } + } + else if (i == 65) + { + using (var imageDataWriter = new FileWriter(mem, true)) + { + imageDataWriter.Write(decompStream.ToBytes()); + foreach (TexturePOWE image in textureFolder.Nodes) + { imageDataWriter.SeekBegin(image.DataOffset); imageDataWriter.Write(image.ImageData); } } } + else + mem = new System.IO.MemoryStream(decompStream.ToBytes()); - var comp = STLibraryCompression.ZLIB.Compress(decomp.ToBytes()); + var comp = STLibraryCompression.ZLIB.Compress(mem.ToBytes()); maxDataSize = Math.Max(maxDataSize, (uint)comp.Length); dataWriter.Write(comp); writer.Write(offset); - writer.Write((uint)decomp.Length); + writer.Write((uint)decompSize); writer.Write((uint)comp.Length); writer.Write(fileEntries[i].Unknown1); @@ -615,13 +645,14 @@ namespace FirstPlugin.LuigisMansion3 writer.Write(maxDataSize); } - - //After saving is done remove the existing file - System.IO.File.Delete(DataPath); - - //Now move and rename our temp file to the new file path - System.IO.File.Move(TempPath, DataPath); + dataWriter.Close(); } + + //After saving is done remove the existing file + System.IO.File.Delete(DataPath); + + //Now move and rename our temp file to the new file path + System.IO.File.Move(TempPath, DataPath); } private uint GetLargestFileSize() @@ -762,9 +793,21 @@ namespace FirstPlugin.LuigisMansion3 { List Items = new List(); Items.Add(new STToolStipMenuItem("Export Raw Data", null, Export, Keys.Control | Keys.E)); + Items.Add(new STToolStipMenuItem("Replace Raw Data", null, Replace, Keys.Control | Keys.R)); return Items.ToArray(); } + private void Replace(object sender, EventArgs args) + { + /* OpenFileDialog ofd = new OpenFileDialog(); + ofd.FileName = Text; + ofd.Filter = "Raw Data (*.*)|*.*"; + + if (ofd.ShowDialog() == DialogResult.OK) + DataStream = new System.IO.FileStream(ofd.FileName, System.IO.FileMode.Open, + System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite);*/ + } + private void Export(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); @@ -819,6 +862,8 @@ namespace FirstPlugin.LuigisMansion3 return new SubStream(reader.BaseStream, Offset, DecompressedSize); } } + else + Console.WriteLine("Path does not exist! " + DataFile); return Data; } diff --git a/File_Format_Library/FileFormats/NLG/LM3/LM3_Enums.cs b/File_Format_Library/FileFormats/NLG/LM3/LM3_Enums.cs index 8102d9b5..1381014c 100644 --- a/File_Format_Library/FileFormats/NLG/LM3/LM3_Enums.cs +++ b/File_Format_Library/FileFormats/NLG/LM3/LM3_Enums.cs @@ -6,11 +6,11 @@ using System.Threading.Tasks; namespace FirstPlugin.LuigisMansion3 { - public enum DataType : uint + public enum DataType : ushort { - Texture = 0xBA41B500, - Model = 0x8101B000, - Unknown = 0x08C17000, + Texture = 0xB500, + Model = 0xB000, + Unknown = 0x7000, } public enum VertexDataFormat @@ -51,10 +51,11 @@ namespace FirstPlugin.LuigisMansion3 { TextureHeader = 0xB501, TextureData = 0xB502, - ModelStart = 0xB006, + MaterailData = 0xB006, //Also marks start of model data SubmeshInfo = 0xB003, //Or polygon groups? VertexStartPointers = 0xB004, ModelTransform = 0xB001, //Matrix4x4. 0x40 in size + ModelInfo = 0xB002, //Contains mesh count and model hash MeshBuffers = 0xB005, //vertex and index buffer MaterialName = 0xB333, MeshIndexTable = 0xB007, diff --git a/File_Format_Library/FileFormats/NLG/LM3/LM3_Model.cs b/File_Format_Library/FileFormats/NLG/LM3/LM3_Model.cs index a1977c51..a1381b81 100644 --- a/File_Format_Library/FileFormats/NLG/LM3/LM3_Model.cs +++ b/File_Format_Library/FileFormats/NLG/LM3/LM3_Model.cs @@ -211,7 +211,7 @@ namespace FirstPlugin.LuigisMansion3 { Nodes.Clear(); - using (var reader = new FileReader(DataDictionary.GetFileBufferData())) + using (var reader = new FileReader(DataDictionary.GetFileBufferData(), true)) { TreeNode texturesList = new TreeNode("Texture Maps"); TreeNode skeletonNode = new TreeNode("Skeleton"); @@ -249,8 +249,8 @@ namespace FirstPlugin.LuigisMansion3 genericObj.Mesh = mesh; genericObj.Checked = true; genericObj.Text = $"Mesh {i} {mesh.HashID.ToString("X")}"; - if (LM3_DICT.HashNames.ContainsKey(mesh.HashID)) - genericObj.Text = LM3_DICT.HashNames[mesh.HashID]; + if (NLG_Common.HashNames.ContainsKey(mesh.HashID)) + genericObj.Text = NLG_Common.HashNames[mesh.HashID]; genericObj.SetMaterial(mesh.Material); RenderedMeshes.Add(genericObj); @@ -474,6 +474,8 @@ namespace FirstPlugin.LuigisMansion3 reader.Position = 0; + return; + var meshSize = reader.BaseStream.Length / model.Meshes.Count; for (int i = 0; i < model.Meshes.Count; i++) { @@ -628,10 +630,6 @@ namespace FirstPlugin.LuigisMansion3 else IndexFormat = IndexFormat.Index_16; - Console.WriteLine($"type {type} indexFlags {indexFlags} IndexCount {IndexCount}"); - // IndexFormat = reader.ReadEnum(false); - // if (IndexFormat != (IndexFormat)0x8000 && IndexFormat != 0 && IndexFormat != IndexFormat.Index_16_0x2) - VertexCount = reader.ReadUInt32(); reader.ReadUInt32(); //unknown BufferPtrOffset = reader.ReadUInt16(); //I believe this might be for the buffer pointers. It shifts by 4 for each mesh diff --git a/File_Format_Library/FileFormats/NLG/LM3/TexturePOWE.cs b/File_Format_Library/FileFormats/NLG/LM3/TexturePOWE.cs index e9fdb0c2..3f0f5c32 100644 --- a/File_Format_Library/FileFormats/NLG/LM3/TexturePOWE.cs +++ b/File_Format_Library/FileFormats/NLG/LM3/TexturePOWE.cs @@ -12,6 +12,7 @@ namespace FirstPlugin.LuigisMansion3 { public class TexturePOWE : STGenericTexture { + public long HeaderOffset; public long DataOffset; public static readonly uint Identifier = 0xE977D350; @@ -30,6 +31,14 @@ namespace FirstPlugin.LuigisMansion3 [Browsable(false)] public uint ID { get; set; } + public uint HashIDUint + { + get + { + return ID; + } + } + public string HashID { get @@ -72,9 +81,10 @@ namespace FirstPlugin.LuigisMansion3 }; public byte TexFormat; - public byte Unknown; + public ushort Unknown; public byte Unknown2; - public ushort Unknown3; + public byte Unknown3; + public ushort Unknown4; public void Read(FileReader reader) { @@ -91,13 +101,14 @@ namespace FirstPlugin.LuigisMansion3 ID2 = reader.ReadUInt32(); Width = reader.ReadUInt16(); Height = reader.ReadUInt16(); - var numMips = reader.ReadByte(); - var unk = reader.ReadByte(); //padding? + Unknown = reader.ReadUInt16(); var numArray = reader.ReadByte(); - Unknown = reader.ReadByte(); - TexFormat = reader.ReadByte(); Unknown2 = reader.ReadByte(); - Unknown3 = reader.ReadUInt16(); + TexFormat = reader.ReadByte(); + Unknown3 = reader.ReadByte(); + Unknown4 = reader.ReadUInt16(); + + Console.WriteLine(ID2); if (FormatTable.ContainsKey(TexFormat)) Format = FormatTable[TexFormat]; @@ -110,11 +121,16 @@ namespace FirstPlugin.LuigisMansion3 MipCount = 1; ArrayCount = numArray; + UpdateProperties(); + } + + private void UpdateProperties() + { properties = new POWEProperties(); properties.ID = ID2; properties.Width = Width; properties.Height = Height; - properties.NumMips = numMips; + properties.NumMips = (byte)MipCount; properties.Format = Format; } @@ -150,17 +166,20 @@ namespace FirstPlugin.LuigisMansion3 var surfacesNew = tex.GetSurfaces(); var surfaces = GetSurfaces(); - if (surfaces[0].mipmaps[0].Length != surfacesNew[0].mipmaps[0].Length) - throw new Exception($"Image must be the same size! {surfaces[0].mipmaps[0].Length}"); - - if (Width != tex.Texture.Width || Height != tex.Texture.Height) - throw new Exception("Image size must be the same!"); + if (surfaces[0].mipmaps[0].Length > surfacesNew[0].mipmaps[0].Length) + throw new Exception($"Image must be the same size or smaller! {surfaces[0].mipmaps[0].Length}"); ImageData = tex.Texture.TextureData[0][0]; Width = tex.Texture.Width; Height = tex.Texture.Height; MipCount = tex.Texture.MipCount; + ArrayCount = tex.Texture.ArrayLength; + Format = tex.Format; + TexFormat = FormatTable.FirstOrDefault(x => x.Value == tex.Format).Key; + UpdateProperties(); + + Console.WriteLine($"TexFormat {TexFormat.ToString("X")}"); surfacesNew.Clear(); surfaces.Clear(); @@ -204,6 +223,9 @@ namespace FirstPlugin.LuigisMansion3 GenerateMipsAndCompress(bitmap, MipCount, Format), MipCount); ImageData = Utils.CombineByteArray(mipmaps.ToArray()); + ArrayCount = tex.ArrayLength; + TexFormat = FormatTable.FirstOrDefault(x => x.Value == Format).Key; + UpdateProperties(); } public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0) diff --git a/File_Format_Library/FileFormats/NLG/NLG_Common.cs b/File_Format_Library/FileFormats/NLG/NLG_Common.cs index d44b6710..8a5d643b 100644 --- a/File_Format_Library/FileFormats/NLG/NLG_Common.cs +++ b/File_Format_Library/FileFormats/NLG/NLG_Common.cs @@ -9,7 +9,7 @@ namespace FirstPlugin { public class NLG_Common { - private static Dictionary hashNames; + private static Dictionary hashNames = new Dictionary(); public static Dictionary HashNames {