From 6b14bc71cb699b72ca6cf164b7b800add414dec6 Mon Sep 17 00:00:00 2001 From: Treeki Date: Mon, 28 Feb 2011 03:23:02 +0100 Subject: progress on writing: offset calculation for models and textures complete! --- NW4RTools/BrresReader.cs | 41 +++- NW4RTools/BrresWriter.cs | 367 ++++++++++++++++++++++++++++++- NW4RTools/Models/Animation/TextureSRT.cs | 8 + NW4RTools/Models/OpenGL/GLTexture.cs | 10 +- NW4RTools/Models/Shape.cs | 1 + NW4RTools/NW4RTools.csproj | 2 + NW4RTools/NW4RTools.pidb | Bin 545355 -> 546082 bytes NW4RTools/Texture.cs | 24 +- NW4RTools/bin/Debug/NW4RTools.dll | Bin 181760 -> 189952 bytes NW4RTools/bin/Debug/NW4RTools.dll.mdb | Bin 95922 -> 97520 bytes 10 files changed, 434 insertions(+), 19 deletions(-) create mode 100644 NW4RTools/Models/Animation/TextureSRT.cs (limited to 'NW4RTools') diff --git a/NW4RTools/BrresReader.cs b/NW4RTools/BrresReader.cs index 518096d..db5d7c2 100644 --- a/NW4RTools/BrresReader.cs +++ b/NW4RTools/BrresReader.cs @@ -67,6 +67,9 @@ namespace NW4RTools { case "Textures(NW4R)": File.Add(name, ReadAndConvertDict(ins, ConvertTextureResource)); break; + case "AnmTexSrt(NW4R)": + File.Add(name, ReadAndConvertDict(ins, ConvertAnmTexSRTResource)); + break; default: Debug.Send("Not implemented"); return; @@ -101,13 +104,20 @@ namespace NW4RTools { Int16 height = ins.ReadInt16(); TextureFormat format = (TextureFormat)ins.ReadUInt32(); - tex.MipMapCount = ins.ReadUInt32(); + int mipmapCount = ins.ReadInt32(); tex.MinLOD = ins.ReadFloat(); tex.MaxLOD = ins.ReadFloat(); ins.Seek(startPos + dataOffset); - OffsetMap.Add(ins.Position, String.Format("Texture Data for: {0} [{1}, {2}x{3}]", name, format, width, height)); - tex.ImportData(ins.ReadBytes(Texture.GetDataSize(width, height, format)), width, height, format); + OffsetMap.Add(ins.Position, String.Format("Texture Data for: {0} [{1}, {2}x{3}, {4} mipmaps]", name, format, width, height, mipmapCount)); + + // handle mipmaps + tex.Images = new System.Drawing.Bitmap[mipmapCount]; + for (int i = 0; i < mipmapCount; i++) { + tex.ImportData(i, ins.ReadBytes(Texture.GetDataSize(width, height, format)), width, height, format); + width /= 2; + height /= 2; + } return tex; } @@ -115,6 +125,22 @@ namespace NW4RTools { + private Models.Animation.TextureSRT ConvertAnmTexSRTResource(string name, InputStream ins) { + using (var c = Debug.Push(name)) { + var anim = new Models.Animation.TextureSRT(); + + int startPos = ins.Position; + + OffsetMap.Add(startPos, "Texture SRT Animation: " + name); + + // TODO + + return anim; + } + } + + + private Model ConvertModelResource(string name, InputStream ins) { using (var c = Debug.Push(name)) { Model mdl = new Model(); @@ -167,6 +193,7 @@ namespace NW4RTools { ins.Seek(infoStructPos + mtxDataOffset); + OffsetMap.Add(ins.Position, "Matrix ID to Node ID Data for: " + name); UInt32 mtxDataCount = ins.ReadUInt32(); mdl.MatrixIDtoNodeID = new int[mtxDataCount]; @@ -450,6 +477,8 @@ namespace NW4RTools { n.RawData = ins.ReadBytes(n.EntrySize * n.EntryCount); n.Parse(); + OffsetMap.Add(startPos + dataOffset, n.GetType().ToString() + " Data"); + ins.Seek(structEndPos); } @@ -721,21 +750,23 @@ namespace NW4RTools { int dlBase1 = ins.Position; // 0x18 - UInt32 bufferSize1 = ins.ReadUInt32(); + s.DLBufferSize1 = ins.ReadUInt32(); // 0x1C UInt32 dataSize1 = ins.ReadUInt32(); // 0x20 Int32 offset1 = ins.ReadInt32(); s.DisplayList1 = ins.At(dlBase1 + offset1).ReadBytes((int)dataSize1); + OffsetMap.Add(dlBase1 + offset1, String.Format("Shape DL 1: {0} [Buffer Size: 0x{1:X}; Data Size: 0x{2:X}]", name, s.DLBufferSize1, dataSize1)); int dlBase2 = ins.Position; // 0x24 - UInt32 bufferSize2 = ins.ReadUInt32(); + s.DLBufferSize2 = ins.ReadUInt32(); // 0x28 UInt32 dataSize2 = ins.ReadUInt32(); // 0x2C Int32 offset2 = ins.ReadInt32(); s.DisplayList2 = ins.At(dlBase2 + offset2).ReadBytes((int)dataSize2); + OffsetMap.Add(dlBase2 + offset2, String.Format("Shape DL 2: {0} [Buffer Size: 0x{1:X}; Data Size: 0x{2:X}]", name, s.DLBufferSize2, dataSize2)); // 0x30 s.DataFlags = ins.ReadUInt32(); diff --git a/NW4RTools/BrresWriter.cs b/NW4RTools/BrresWriter.cs index 5590ab5..1b103fc 100644 --- a/NW4RTools/BrresWriter.cs +++ b/NW4RTools/BrresWriter.cs @@ -31,6 +31,12 @@ namespace NW4RTools { Output.WriteBytes(StringTableData.GetBuffer()); + using (var c = Debug.Push("Offset Map")) { + foreach (var e in OffsetMap) { + Debug.Send("0x{0:X} : {1}", e.Key, e.Value); + } + } + return Output.GetBuffer(); } @@ -38,6 +44,24 @@ namespace NW4RTools { // each written element, and use that to build up a list of offsets. Then, I'll actually write out the data. // This code will also handle building the string table. + #region Offset Data Storage + private Dictionary ModelOffsets; + private Dictionary BytecodeOffsets; + private Dictionary NodeOffsets; + private Dictionary VtxPosOffsets; + private Dictionary VtxNrmOffsets; + private Dictionary VtxClrOffsets; + private Dictionary VtxTexCoordOffsets; + private Dictionary MaterialOffsets; + private Dictionary ShaderOffsets; + private Dictionary ShapeOffsets; + private Dictionary, int> PairingOffsets; + + private Dictionary TextureOffsets; + private Dictionary TextureDataOffsets; + + #endregion + #region Offset/String Table Calculation private int CurrentPos; @@ -50,11 +74,13 @@ namespace NW4RTools { // First block, and ResDict CurrentPos += 8; - CurrentPos += GetSizeForResDict(File); + CurrentPos += GetSizeForResDict(File.Count); // Now do each ResDict in the File foreach (object dict in File.Values) { - CurrentPos += GetSizeForResDict(dict as ResDict); + // yay stupid hack + OffsetMap.Add(CurrentPos, "ResDict: " + dict.GetType().GetGenericArguments()[0].ToString()); + CurrentPos += GetSizeForResDict((dict as ICollection).Count); } // OK, so that's done. Process each type @@ -62,7 +88,10 @@ namespace NW4RTools { AddString(name); switch (name) { case "3DModels(NW4R)": - // do that stuff + CalculateModels(); + break; + case "Textures(NW4R)": + CalculateTextures(); break; default: Debug.Send("[[ UNIMPLEMENTED {0} ]]", name); @@ -74,9 +103,337 @@ namespace NW4RTools { CalculateStringTable(); } + #region Model Calculation + private void CalculateModels() { + ModelOffsets = new Dictionary(); + BytecodeOffsets = new Dictionary(); + NodeOffsets = new Dictionary(); + VtxPosOffsets = new Dictionary(); + VtxNrmOffsets = new Dictionary(); + VtxClrOffsets = new Dictionary(); + VtxTexCoordOffsets = new Dictionary(); + MaterialOffsets = new Dictionary(); + ShaderOffsets = new Dictionary(); + ShapeOffsets = new Dictionary(); + PairingOffsets = new Dictionary, int>(); + + + var modelDict = File.GetGroup("3DModels(NW4R)"); + + foreach (var kv in modelDict) { + AddString(kv.Key); + AlignCalcPos(0x20); + // 0x40? dunno + Model model = kv.Value; + + OffsetMap.Add(CurrentPos, "Model: " + kv.Key); + CurrentPos += 0x4C; + OffsetMap.Add(CurrentPos, "Model Info Struct for: " + kv.Key); + CurrentPos += 0x40; + + OffsetMap.Add(CurrentPos, "Matrix ID to Node ID Data for: " + kv.Key); + CurrentPos += 4 + (model.MatrixIDtoNodeID.Length * 4); + + + OffsetMap.Add(CurrentPos, "ResDict: ByteCode"); + CurrentPos += GetSizeForResDict(model.Bytecode.Count); + OffsetMap.Add(CurrentPos, "ResDict: Nodes"); + CurrentPos += GetSizeForResDict(model.Nodes.Count); + OffsetMap.Add(CurrentPos, "ResDict: VertexPosData"); + CurrentPos += GetSizeForResDict(model.VtxPosData.Count); + + if (model.VtxNrmData.Count > 0) { + OffsetMap.Add(CurrentPos, "ResDict: VertexNrmData"); + CurrentPos += GetSizeForResDict(model.VtxNrmData.Count); + } + + if (model.VtxClrData.Count > 0) { + OffsetMap.Add(CurrentPos, "ResDict: VertexClrData"); + CurrentPos += GetSizeForResDict(model.VtxClrData.Count); + } + + if (model.VtxTexCoordData.Count > 0) { + OffsetMap.Add(CurrentPos, "ResDict: VertexTexCoordData"); + CurrentPos += GetSizeForResDict(model.VtxTexCoordData.Count); + } + + OffsetMap.Add(CurrentPos, "ResDict: Materials"); + CurrentPos += GetSizeForResDict(model.Materials.Count); + OffsetMap.Add(CurrentPos, "ResDict: Shaders"); + CurrentPos += GetSizeForResDict(model.Shaders.Count); + OffsetMap.Add(CurrentPos, "ResDict: Shapes"); + CurrentPos += GetSizeForResDict(model.Shapes.Count); + + if (model.PairingLookupByTexture.Count > 0) { + OffsetMap.Add(CurrentPos, "ResDict: Texture Lookup"); + CurrentPos += GetSizeForResDict(model.PairingLookupByTexture.Count); + } + + // todo: palette lookup, checking if dicts are empty or do not exist + // todo: can dicts even NOT exist? must find this out + + CalculatePairings(model, model.PairingLookupByTexture); + CalculateBytecode(model); + CalculateNodes(model); + CalculateMaterials(model); + CalculateShaders(model); + CalculateShapes(model); + CalculateVtxPosData(model); + CalculateVtxNrmData(model); + CalculateVtxClrData(model); + CalculateVtxTexCoordData(model); + } + } + + private void CalculatePairings(Model m, ResDict> dict) { + foreach (var kv in dict) { + OffsetMap.Add(CurrentPos, "Texture/Material Pairing List for: " + kv.Key); + PairingOffsets.Add(kv.Value, CurrentPos); + + CurrentPos += 4 + (kv.Value.Count * 8); + } + } + + private void CalculateBytecode(Model m) { + foreach (var kv in m.Bytecode) { + AddString(kv.Key); + ByteCode bc = kv.Value; + + OffsetMap.Add(CurrentPos, "ByteCode: " + kv.Key); + BytecodeOffsets.Add(kv.Value, CurrentPos); + + foreach (var insn in bc.Instructions) { + switch (insn.GetOp()) { + case ByteCode.OpType.None: + CurrentPos += 1; + break; + case ByteCode.OpType.Done: + CurrentPos += 1; + break; + case ByteCode.OpType.AssignNodeToParentMtx: + CurrentPos += 5; + break; + case ByteCode.OpType.BlendMatrices: + CurrentPos += 4 + (6 * (insn as ByteCode.BlendMatricesInstruction).BlendedMatrices.Length); + break; + case ByteCode.OpType.DrawShape: + CurrentPos += 8; + break; + case ByteCode.OpType.AssignMtxToNode: + CurrentPos += 5; + break; + } + } + } + + AlignCalcPos(4); // should be per-bytecode maybe? + } + + private void CalculateNodes(Model m) { + foreach (var kv in m.Nodes) { + AddString(kv.Key); + + OffsetMap.Add(CurrentPos, "Node: " + kv.Key); + NodeOffsets.Add(kv.Value, CurrentPos); + + CurrentPos += 0xD0; + } + } + + private void CalculateMaterials(Model m) { + foreach (var kv in m.Materials) { + AddString(kv.Key); + + OffsetMap.Add(CurrentPos, "Material: " + kv.Key); + MaterialOffsets.Add(kv.Value, CurrentPos); + + // Base material struct + CurrentPos += 0x14; + + // ResGenMode + CurrentPos += 8; + + // ResMatMode + CurrentPos += 0xC; + + // other stuff + CurrentPos += 0x18; + + // ResTexObj + CurrentPos += 0x104; + + // ResTlutObj + CurrentPos += 0x64; + + // ResTexSrt + CurrentPos += 8 + (0x14 * 8) + (0x34 * 8); + + // ResMatChan + CurrentPos += 0x28; + + // Texture Infos + if (kv.Value.TextureInfos.Count > 0) + OffsetMap.Add(CurrentPos, "Material Texture Infos: " + kv.Key); + + CurrentPos += (kv.Value.TextureInfos.Count * 0x34); + + // Display Lists + AlignCalcPos(0x20); + OffsetMap.Add(CurrentPos, "Material Display Lists: " + kv.Key); + CurrentPos += 0x20 + 0x80 + 0x40 + 0xA0; + } + } + + private void CalculateShaders(Model m) { + foreach (var kv in m.Shaders) { + AddString(kv.Key); + + OffsetMap.Add(CurrentPos, "Shader: " + kv.Key); + ShaderOffsets.Add(kv.Value, CurrentPos); + + CurrentPos += 0x200; + } + } + + private void CalculateShapes(Model m) { + foreach (var kv in m.Shapes) { + AddString(kv.Key); + + OffsetMap.Add(CurrentPos, "Shape: " + kv.Key); + ShapeOffsets.Add(kv.Value, CurrentPos); + + CurrentPos += 0x68; + + AlignCalcPos(0x20); + + OffsetMap.Add(CurrentPos, "Shape DL 1: " + kv.Key); + CurrentPos += (int)kv.Value.DLBufferSize1; + AlignCalcPos(0x20); + + OffsetMap.Add(CurrentPos, "Shape DL 2: " + kv.Key); + CurrentPos += (int)kv.Value.DLBufferSize2; + AlignCalcPos(0x20); + } + } + + private void CalculateVtxPosData(Model m) { + foreach (var kv in m.VtxPosData) { + AddString(kv.Key); + + OffsetMap.Add(CurrentPos, "VertexPosData: " + kv.Key); + VtxPosOffsets.Add(kv.Value, CurrentPos); + + // Main data + CurrentPos += 0x20; + + // Minimum/maximum VEC3 (specific to VtxPosData) + CurrentPos += 0x18; + + AlignCalcPos(0x20); + OffsetMap.Add(CurrentPos, "Data: " + kv.Key); + CurrentPos += kv.Value.EntryCount * kv.Value.EntrySize; + AlignCalcPos(0x20); + } + } + + private void CalculateVtxNrmData(Model m) { + foreach (var kv in m.VtxNrmData) { + AddString(kv.Key); + + OffsetMap.Add(CurrentPos, "VertexNrmData: " + kv.Key); + VtxNrmOffsets.Add(kv.Value, CurrentPos); + + // Main data + CurrentPos += 0x20; + + AlignCalcPos(0x20); + OffsetMap.Add(CurrentPos, "Data: " + kv.Key); + CurrentPos += kv.Value.EntryCount * kv.Value.EntrySize; + AlignCalcPos(0x20); + } + } + + private void CalculateVtxClrData(Model m) { + foreach (var kv in m.VtxClrData) { + AddString(kv.Key); + + OffsetMap.Add(CurrentPos, "VertexClrData: " + kv.Key); + VtxClrOffsets.Add(kv.Value, CurrentPos); + + // Main data + CurrentPos += 0x20; + + AlignCalcPos(0x20); + OffsetMap.Add(CurrentPos, "Data: " + kv.Key); + CurrentPos += kv.Value.EntryCount * kv.Value.EntrySize; + AlignCalcPos(0x20); + } + } + + private void CalculateVtxTexCoordData(Model m) { + foreach (var kv in m.VtxTexCoordData) { + AddString(kv.Key); + + OffsetMap.Add(CurrentPos, "VertexTexCoordData: " + kv.Key); + VtxTexCoordOffsets.Add(kv.Value, CurrentPos); + + // Main data + CurrentPos += 0x20; + + // Minimum/maximum VEC2 (specific to VtxTexCoordData) + CurrentPos += 0x10; + + AlignCalcPos(0x20); + OffsetMap.Add(CurrentPos, "Data: " + kv.Key); + CurrentPos += kv.Value.EntryCount * kv.Value.EntrySize; + AlignCalcPos(0x20); + } + } + #endregion + + #region Texture Calculation + private void CalculateTextures() { + TextureOffsets = new Dictionary(); + TextureDataOffsets = new Dictionary(); + + + var textureDict = File.GetGroup("Textures(NW4R)"); + + foreach (var kv in textureDict) { + AddString(kv.Key); + AlignCalcPos(0x20); + Texture texture = kv.Value; + + Debug.Send("Current: {0}", kv.Key); + + + OffsetMap.Add(CurrentPos, "Texture: " + kv.Key); + TextureOffsets.Add(kv.Value, CurrentPos); + + CurrentPos += 0x30; + AlignCalcPos(0x20); + + + OffsetMap.Add(CurrentPos, "Texture Data for: " + kv.Key); + TextureDataOffsets.Add(kv.Value, CurrentPos); + + CurrentPos += texture.GetDataSize(); + AlignCalcPos(0x20); + } + } + #endregion + + + + private void AlignCalcPos(int alignTo) { + if ((CurrentPos & (alignTo - 1)) == 0) + return; + + CurrentPos += (alignTo - (CurrentPos & (alignTo - 1))); + } - private int GetSizeForResDict(ResDict dict) { - return 8 + ((dict.Count + 1) * 0x10); + private int GetSizeForResDict(int entryCount) { + return 8 + ((entryCount + 1) * 0x10); } #endregion diff --git a/NW4RTools/Models/Animation/TextureSRT.cs b/NW4RTools/Models/Animation/TextureSRT.cs new file mode 100644 index 0000000..5c35905 --- /dev/null +++ b/NW4RTools/Models/Animation/TextureSRT.cs @@ -0,0 +1,8 @@ +using System; +namespace NW4RTools.Models.Animation { + public class TextureSRT { + public TextureSRT() { + } + } +} + diff --git a/NW4RTools/Models/OpenGL/GLTexture.cs b/NW4RTools/Models/OpenGL/GLTexture.cs index c4cdee6..d56d6ca 100644 --- a/NW4RTools/Models/OpenGL/GLTexture.cs +++ b/NW4RTools/Models/OpenGL/GLTexture.cs @@ -26,16 +26,18 @@ namespace NW4RTools.Models.OpenGL { //byte[] pixelData = new byte[tex.BaseImage.Width * tex.BaseImage.Height * 4]; - var lb = tex.BaseImage.LockBits( - new System.Drawing.Rectangle(0, 0, tex.BaseImage.Width, tex.BaseImage.Height), + // TODO: mipmaps + + var lb = tex.Images[0].LockBits( + new System.Drawing.Rectangle(0, 0, tex.Images[0].Width, tex.Images[0].Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Four, - tex.BaseImage.Width, tex.BaseImage.Height, 0, + tex.Images[0].Width, tex.Images[0].Height, 0, PixelFormat.Bgra, PixelType.UnsignedByte, lb.Scan0); - tex.BaseImage.UnlockBits(lb); + tex.Images[0].UnlockBits(lb); } public void Bind(TextureTarget target) { diff --git a/NW4RTools/Models/Shape.cs b/NW4RTools/Models/Shape.cs index 88d6670..6bc7e44 100644 --- a/NW4RTools/Models/Shape.cs +++ b/NW4RTools/Models/Shape.cs @@ -5,6 +5,7 @@ namespace NW4RTools.Models { public Int32 MatrixID; public byte[] Unk; public byte[] DisplayList1, DisplayList2; + public UInt32 DLBufferSize1, DLBufferSize2; public UInt32 DataFlags; public UInt32 Flags; public UInt32 Index; diff --git a/NW4RTools/NW4RTools.csproj b/NW4RTools/NW4RTools.csproj index 3afef3b..493cc46 100644 --- a/NW4RTools/NW4RTools.csproj +++ b/NW4RTools/NW4RTools.csproj @@ -72,12 +72,14 @@ + + diff --git a/NW4RTools/NW4RTools.pidb b/NW4RTools/NW4RTools.pidb index 317dedf..7330f88 100644 Binary files a/NW4RTools/NW4RTools.pidb and b/NW4RTools/NW4RTools.pidb differ diff --git a/NW4RTools/Texture.cs b/NW4RTools/Texture.cs index 7063056..0561435 100644 --- a/NW4RTools/Texture.cs +++ b/NW4RTools/Texture.cs @@ -64,10 +64,9 @@ namespace NW4RTools { - public Bitmap BaseImage; + public Bitmap[] Images; public TextureFormat Format; - public UInt32 MipMapCount; public float MinLOD, MaxLOD; public Texture() { @@ -82,11 +81,26 @@ namespace NW4RTools { // align width, height up width = Misc.AlignUp(width, info.TexelWidth); height = Misc.AlignUp(height, info.TexelHeight); - + + // SPECIAL CASE: + return width * height * info.NybblesPerPixel / 2; } - unsafe public void ImportData(byte[] imgdata, int width, int height, TextureFormat format) { + + public int GetDataSize() { + int size = 0; + for (int i = 0; i < Images.Length; i++) { + size += GetDataSize(i); + } + return size; + } + + public int GetDataSize(int imageID) { + return GetDataSize(Images[imageID].Width, Images[imageID].Height, Format); + } + + unsafe public void ImportData(int imageID, byte[] imgdata, int width, int height, TextureFormat format) { var image = new Bitmap(width, height, PixelFormat.Format32bppArgb); var bits = image.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, image.PixelFormat); @@ -274,7 +288,7 @@ namespace NW4RTools { image.UnlockBits(bits); - BaseImage = image; + Images[imageID] = image; Format = format; } diff --git a/NW4RTools/bin/Debug/NW4RTools.dll b/NW4RTools/bin/Debug/NW4RTools.dll index 3962f15..13eb22e 100755 Binary files a/NW4RTools/bin/Debug/NW4RTools.dll and b/NW4RTools/bin/Debug/NW4RTools.dll differ diff --git a/NW4RTools/bin/Debug/NW4RTools.dll.mdb b/NW4RTools/bin/Debug/NW4RTools.dll.mdb index f5f1318..a2acbbc 100644 Binary files a/NW4RTools/bin/Debug/NW4RTools.dll.mdb and b/NW4RTools/bin/Debug/NW4RTools.dll.mdb differ -- cgit v1.2.3