From 31380b4bb93d1fb65faff8f71753de80fb0a8c9d Mon Sep 17 00:00:00 2001 From: Treeki Date: Mon, 20 Feb 2012 05:30:01 +0100 Subject: AnmChr/Clr/TexSrt writing done for now. Not 100% perfect but it works! --- NW4RTools/BrresReader.cs | 33 +- NW4RTools/BrresWriter.cs | 967 +++++++++++++++++++++++++++- NW4RTools/Misc.cs | 40 ++ NW4RTools/Models/Animation/CharacterAnim.cs | 2 + NW4RTools/Models/Animation/Shared.cs | 40 ++ NW4RTools/Models/ByteCode.cs | 3 +- NW4RTools/NW4RTools.csproj | 183 +++--- NW4RTools/Texture.cs | 15 +- NW4RTools/Types.cs | 10 + 9 files changed, 1174 insertions(+), 119 deletions(-) mode change 100644 => 100755 NW4RTools/BrresReader.cs mode change 100644 => 100755 NW4RTools/BrresWriter.cs mode change 100644 => 100755 NW4RTools/Models/ByteCode.cs (limited to 'NW4RTools') diff --git a/NW4RTools/BrresReader.cs b/NW4RTools/BrresReader.cs old mode 100644 new mode 100755 index f120943..9616b5b --- a/NW4RTools/BrresReader.cs +++ b/NW4RTools/BrresReader.cs @@ -153,8 +153,15 @@ namespace NW4RTools { // handle mipmaps tex.Images = new System.Drawing.Bitmap[mipmapCount]; +#if DEV_DONT_MODIFY_TEXTURE + tex.ImageData = new byte[mipmapCount][]; +#endif for (int i = 0; i < mipmapCount; i++) { - tex.ImportData(i, ins.ReadBytes(Texture.GetDataSize(width, height, format)), width, height, format); + byte[] data = ins.ReadBytes(Texture.GetDataSize(width, height, format)); +#if DEV_DONT_MODIFY_TEXTURE + tex.ImageData[i] = data; +#endif + tex.ImportData(i, data, width, height, format); width /= 2; height /= 2; } @@ -223,6 +230,10 @@ namespace NW4RTools { node.Flags = flags & CharacterAnim.Flags.NonComputableFlags; + node.ScaleUseModel = ((flags & CharacterAnim.Flags.ScaleUseModel) != 0); + node.RotateUseModel = ((flags & CharacterAnim.Flags.RotateUseModel) != 0); + node.TranslateUseModel = ((flags & CharacterAnim.Flags.TranslateUseModel) != 0); + // SCALES bool needScaleYZ = true; @@ -482,7 +493,7 @@ namespace NW4RTools { node.Elements = new ColorAnim.Element[(int)ColorAnim.TargetType.Count]; for (int i = 0; i < node.Elements.Length; i++) { - node.Elements[i] = ReadAnmClrElement(ins, startPos, flags); + node.Elements[i] = ReadAnmClrElement(ins, flags); flags >>= 2; } @@ -490,7 +501,7 @@ namespace NW4RTools { } } - private ColorAnim.Element ReadAnmClrElement(InputStream ins, int startPos, UInt32 flags) { + private ColorAnim.Element ReadAnmClrElement(InputStream ins, UInt32 flags) { var elem = new ColorAnim.Element(); elem.Exists = (flags & 1) != 0; @@ -498,16 +509,16 @@ namespace NW4RTools { OffsetMap[ins.Position] = String.Format("Element [{0}]", elem.IsConstant ? "Constant" : "Colours"); - elem.Mask = ins.ReadUInt32(); - if (!elem.Exists) return elem; + elem.Mask = ins.ReadUInt32(); + if (elem.IsConstant) { elem.ConstValue = ins.ReadColor(); - } else { + } else { int savePos = ins.Position; - ins.Seek(startPos + ins.ReadInt32()); + ins.Seek(savePos + ins.ReadInt32()); elem.Colors = new Color[COLOR_ANIM_HACK.FrameCount + 1]; @@ -597,7 +608,9 @@ namespace NW4RTools { var elem = new TextureSRTAnim.Element(); int savePos = ins.Position; - ins.Seek(startPos + ins.ReadInt32()); + ins.Seek(startPos + ins.ReadInt32()); + + elem.Exists = true; OffsetMap.Add(ins.Position, String.Format("Element")); @@ -863,8 +876,8 @@ namespace NW4RTools { var insn4 = new ByteCode.DrawShapeInstruction(); insn4.MaterialID = ins.ReadUInt16(); insn4.ShapeID = ins.ReadUInt16(); - insn4.NodeID = ins.ReadUInt16(); - ins.Skip(1); + insn4.NodeID = ins.ReadUInt16(); + insn4.Unk = ins.ReadByte(); bc.Instructions.Add(insn4); break; diff --git a/NW4RTools/BrresWriter.cs b/NW4RTools/BrresWriter.cs old mode 100644 new mode 100755 index 04750de..033007e --- a/NW4RTools/BrresWriter.cs +++ b/NW4RTools/BrresWriter.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using NW4RTools.Models; +using NW4RTools.Models.Animation; namespace NW4RTools { public class BrresWriter { @@ -53,6 +54,7 @@ namespace NW4RTools { // This code will also handle building the string table. #region Offset Data Storage + #region Model Data Storage private Dictionary ModelOffsets; private Dictionary BytecodeOffsets; private Dictionary NodeOffsets; @@ -78,9 +80,118 @@ namespace NW4RTools { public int PairingLookupByTexture; public int BlockSize; // Duplicate shader structs are removed from the model, so they require special tracking - public List UniqueShaders; + public List UniqueShaders; + public Dictionary MaterialSizes; + + public ModelCalcInfo() { + UniqueShaders = new List(); + MaterialSizes = new Dictionary(); + } } private Dictionary ModelCalcInfos; + #endregion + + #region Character Animation Data Storage + private class ChrAnmNodeInfo { + public CharacterAnim.Flags Flags; + } + + private class ChrAnmInfo { + public KeyframeWriterInfo KFInfo; + public Dictionary NodeInfo; + public Dictionary NodeOffsets; + public int BlockSize; + + public ChrAnmInfo() { + KFInfo = new KeyframeWriterInfo(); + NodeInfo = new Dictionary(); + NodeOffsets = new Dictionary(); + } + } + private Dictionary ChrAnmInfos; + private Dictionary ChrAnmOffsets; + #endregion + + #region Color Animation Data Storage + private class ClrAnmInfo { + public Dictionary NodeOffsets; + public int BlockSize; + + public List UsedArrays; + public List UsedArrayPositions; + + public ClrAnmInfo() { + NodeOffsets = new Dictionary(); + UsedArrays = new List(); + UsedArrayPositions = new List(); + } + } + private Dictionary ClrAnmInfos; + private Dictionary ClrAnmOffsets; + #endregion + + #region Texture SRT Animation Data Storage + private class TexSRTAnmNodeInfo { + public int[] TextureOffsets; + public int[] IndirectTextureOffsets; + + public TexSRTAnmNodeInfo() { + TextureOffsets = new int[8]; + IndirectTextureOffsets = new int[3]; + } + } + + private class TexSRTAnmInfo { + public KeyframeWriterInfo KFInfo; + public Dictionary NodeInfo; + public Dictionary NodeOffsets; + public int BlockSize; + + public TexSRTAnmInfo() { + KFInfo = new KeyframeWriterInfo(); + NodeInfo = new Dictionary(); + NodeOffsets = new Dictionary(); + } + } + private Dictionary TexSRTAnmInfos; + private Dictionary TexSRTAnmOffsets; + #endregion + + private class KeyframeWriterInfo { + public Dictionary, int> KeyframeAnimOffsets; + public List> KeyframeAnims; + + public KeyframeWriterInfo() { + KeyframeAnimOffsets = new Dictionary, int>(); + KeyframeAnims = new List>(); + } + + public void Add(KeyframeAnim anim) { + Add(new Pair(null, anim)); + } + + public void Add(object obj, KeyframeAnim anim) { + Add(new Pair(obj, anim)); + } + + public void Add(Pair pair) { + if (pair.two.IsConstant) + return; + if (KeyframeAnimOffsets.ContainsKey(pair)) + return; + + KeyframeAnims.Add(pair); + KeyframeAnimOffsets.Add(pair, -1); + } + + public int GetPosition(KeyframeAnim anim) { + return KeyframeAnimOffsets[new Pair(null, anim)]; + } + + public int GetPosition(object obj, KeyframeAnim anim) { + return KeyframeAnimOffsets[new Pair(obj, anim)]; + } + } private Dictionary TextureOffsets; private Dictionary TextureDataOffsets; @@ -130,6 +241,15 @@ namespace NW4RTools { case "Textures(NW4R)": CalculateTextures(); break; + case "AnmChr(NW4R)": + CalculateAnmChr(); + break; + case "AnmClr(NW4R)": + CalculateAnmClr(); + break; + case "AnmTexSrt(NW4R)": + CalculateAnmTexSrt(); + break; default: Debug.Send("[[ UNIMPLEMENTED {0} ]]", name); break; @@ -241,7 +361,6 @@ namespace NW4RTools { CalculateBytecode(model); CalculateNodes(model); CalculateMaterials(model); - calcInfo.UniqueShaders = new List(); CalculateShaders(model); CalculateShapes(model); CalculateVtxPosData(model); @@ -358,6 +477,10 @@ namespace NW4RTools { LogPosition("Material Display Lists: " + kv.Key); MaterialDLOffsets[kv.Value] = CurrentPos; CurrentPos += 0x20 + 0x80 + 0x40 + 0xA0; + + // Sized! + int size = CurrentPos - MaterialOffsets[kv.Value]; + ModelCalcInfos[m].MaterialSizes[kv.Value] = size; } } @@ -549,8 +672,363 @@ namespace NW4RTools { } #endregion + #region Character Animation Calculation + private void CalculateAnmChr() { + ChrAnmOffsets = new Dictionary(); + ChrAnmInfos = new Dictionary(); + + var chrAnmDict = File.GetGroup("AnmChr(NW4R)"); + + foreach (var kv in chrAnmDict) { + AddString(kv.Key); + AlignCalcPos(0x20); + CharacterAnim anim = kv.Value; + + LogPosition("Character Anim: " + kv.Key); + ChrAnmOffsets.Add(anim, CurrentPos); + + CurrentPos += 0x20; + LogPosition("Character Anim Info Struct: " + kv.Key); + + CurrentPos += 0xC; + LogPosition("Character Anim Node Dictionary: " + kv.Key); + CurrentPos += GetSizeForResDict(anim.Nodes.Count); + + var anminfo = new ChrAnmInfo(); + ChrAnmInfos.Add(anim, anminfo); + + // Now size each node + foreach (var nodepair in anim.Nodes) { + AddString(nodepair.Key); + CharacterAnim.Node node = nodepair.Value; + + anminfo.NodeOffsets.Add(node, CurrentPos); + + LogPosition("Character Anim Node: " + nodepair.Key); + CurrentPos += 8; + + var nodeinfo = new ChrAnmNodeInfo(); + anminfo.NodeInfo.Add(node, nodeinfo); + + nodeinfo.Flags = CharacterAnim.Flags.AlwaysSet | (node.Flags & CharacterAnim.Flags.NonComputableFlags); + + if (node.ScaleUseModel) + nodeinfo.Flags |= CharacterAnim.Flags.ScaleUseModel; + if (node.RotateUseModel) + nodeinfo.Flags |= CharacterAnim.Flags.RotateUseModel; + if (node.TranslateUseModel) + nodeinfo.Flags |= CharacterAnim.Flags.TranslateUseModel; + + // Scale + if (node.ScaleX.IsConstWith(1) && node.ScaleY.IsConstWith(1) && node.ScaleZ.IsConstWith(1)) { + nodeinfo.Flags |= CharacterAnim.Flags.ScaleOne; + nodeinfo.Flags |= CharacterAnim.Flags.ScaleUniform; + nodeinfo.Flags |= CharacterAnim.Flags.ScaleXConstant; + nodeinfo.Flags |= CharacterAnim.Flags.ScaleYConstant; + nodeinfo.Flags |= CharacterAnim.Flags.ScaleZConstant; + } else { + nodeinfo.Flags |= CharacterAnim.Flags.CalcScale; + + anminfo.KFInfo.Add(node.ScaleFormat, node.ScaleX); + + if (node.ScaleX.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.ScaleXConstant; + CurrentPos += 4; + + if (node.ScaleX.Equals(node.ScaleY) && node.ScaleX.Equals(node.ScaleZ)) { + nodeinfo.Flags |= CharacterAnim.Flags.ScaleUniform; + if (node.ScaleX.IsConstant) { + nodeinfo.Flags |= CharacterAnim.Flags.ScaleYConstant; + nodeinfo.Flags |= CharacterAnim.Flags.ScaleZConstant; + } + } else { + anminfo.KFInfo.Add(node.ScaleFormat, node.ScaleY); + anminfo.KFInfo.Add(node.ScaleFormat, node.ScaleZ); + + if (node.ScaleY.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.ScaleYConstant; + if (node.ScaleZ.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.ScaleZConstant; + CurrentPos += 8; + } + } + + // Rotation + if (node.RotateX.IsConstWith(0) && node.RotateY.IsConstWith(0) && node.RotateZ.IsConstWith(0)) { + nodeinfo.Flags |= CharacterAnim.Flags.RotateZero; + nodeinfo.Flags |= CharacterAnim.Flags.RotateXConstant; + nodeinfo.Flags |= CharacterAnim.Flags.RotateYConstant; + nodeinfo.Flags |= CharacterAnim.Flags.RotateZConstant; + } else { + nodeinfo.Flags |= CharacterAnim.Flags.CalcRotate; + + anminfo.KFInfo.Add(node.RotateFormat, node.RotateX); + anminfo.KFInfo.Add(node.RotateFormat, node.RotateY); + anminfo.KFInfo.Add(node.RotateFormat, node.RotateZ); + + if (node.RotateX.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.RotateXConstant; + if (node.RotateY.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.RotateYConstant; + if (node.RotateZ.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.RotateZConstant; + CurrentPos += 12; + } + + // Translation + if (node.TranslateX.IsConstWith(0) && node.TranslateY.IsConstWith(0) && node.TranslateZ.IsConstWith(0)) { + nodeinfo.Flags |= CharacterAnim.Flags.TranslateZero; + nodeinfo.Flags |= CharacterAnim.Flags.TranslateXConstant; + nodeinfo.Flags |= CharacterAnim.Flags.TranslateYConstant; + nodeinfo.Flags |= CharacterAnim.Flags.TranslateZConstant; + } else { + nodeinfo.Flags |= CharacterAnim.Flags.CalcTranslate; + + anminfo.KFInfo.Add(node.TranslateFormat, node.TranslateX); + anminfo.KFInfo.Add(node.TranslateFormat, node.TranslateY); + anminfo.KFInfo.Add(node.TranslateFormat, node.TranslateZ); + + if (node.TranslateX.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.TranslateXConstant; + if (node.TranslateY.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.TranslateYConstant; + if (node.TranslateZ.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.TranslateZConstant; + CurrentPos += 12; + } + + + if ((nodeinfo.Flags & CharacterAnim.Flags.RotateZero) != 0 && + (nodeinfo.Flags & CharacterAnim.Flags.TranslateZero) != 0) + nodeinfo.Flags |= CharacterAnim.Flags.RtZero; + + if ((nodeinfo.Flags & CharacterAnim.Flags.RtZero) != 0 && + (nodeinfo.Flags & CharacterAnim.Flags.ScaleOne) != 0) + nodeinfo.Flags |= CharacterAnim.Flags.Identity; + + nodeinfo.Flags |= (CharacterAnim.Flags)((uint)node.ScaleFormat << 25); + nodeinfo.Flags |= (CharacterAnim.Flags)((uint)node.RotateFormat << 27); + nodeinfo.Flags |= (CharacterAnim.Flags)((uint)node.TranslateFormat << 30); + } + + + // And do all keyframes + foreach (var pair in anminfo.KFInfo.KeyframeAnims) { + CharacterAnim.RotateFormatType format = (CharacterAnim.RotateFormatType)pair.one; + KeyframeAnim kf = pair.two; + + LogPosition("Keyframes"); + anminfo.KFInfo.KeyframeAnimOffsets[pair] = CurrentPos; + + switch (format) { + case CharacterAnim.RotateFormatType.Data32: + CurrentPos += 8 + (kf.Keyframes.Length * 4); + break; + case CharacterAnim.RotateFormatType.Data48: + CurrentPos += 8 + (kf.Keyframes.Length * 6); + break; + case CharacterAnim.RotateFormatType.Data96: + CurrentPos += 8 + (kf.Keyframes.Length * 12); + break; + case CharacterAnim.RotateFormatType.DataUnk4: + CurrentPos += 8 + (kf.Values.Length); + break; + case CharacterAnim.RotateFormatType.DataUnk5: + CurrentPos += 8 + (kf.Values.Length * 2); + break; + case CharacterAnim.RotateFormatType.DataUnk6: + CurrentPos += 4 + (kf.FloatValues.Length * 4); + break; + default: + throw new NotImplementedException(); + } + + AlignCalcPos(4); + } + + + anminfo.BlockSize = CurrentPos - ChrAnmOffsets[anim]; + } + } + #endregion + + #region Color Animation Calculation + private void CalculateAnmClr() { + ClrAnmOffsets = new Dictionary(); + ClrAnmInfos = new Dictionary(); + + var clrAnmDict = File.GetGroup("AnmClr(NW4R)"); + + foreach (var kv in clrAnmDict) { + AddString(kv.Key); + ColorAnim anim = kv.Value; + + LogPosition("Color Anim: " + kv.Key); + ClrAnmOffsets.Add(anim, CurrentPos); + + CurrentPos += 0x20; + LogPosition("Color Anim Info Struct: " + kv.Key); + + CurrentPos += 0x8; + LogPosition("Color Anim Node Dictionary: " + kv.Key); + CurrentPos += GetSizeForResDict(anim.Nodes.Count); + + var anminfo = new ClrAnmInfo(); + ClrAnmInfos.Add(anim, anminfo); + + // Now size each node + foreach (var nodepair in anim.Nodes) { + AddString(nodepair.Key); + ColorAnim.Node node = nodepair.Value; + + anminfo.NodeOffsets.Add(node, CurrentPos); + + LogPosition("Color Anim Node: " + nodepair.Key); + CurrentPos += 8; + + for (int i = 0; i < node.Elements.Length; i++) { + if (node.Elements[i].Exists) { + CurrentPos += 8; + if (!node.Elements[i].IsConstant) { + // Add the array + // But check to make sure it's not already there + bool wasFound = false; + + foreach (var arr in anminfo.UsedArrays) { + if (Misc.ArrayCompare(node.Elements[i].Colors, arr)) { + wasFound = true; + break; + } + } + + if (!wasFound) + anminfo.UsedArrays.Add(node.Elements[i].Colors); + } + } + } + } + + // Finally, add every array + foreach (var arr in anminfo.UsedArrays) { + anminfo.UsedArrayPositions.Add(CurrentPos); + CurrentPos += (arr.Length * 4); + } + + anminfo.BlockSize = CurrentPos - ClrAnmOffsets[anim]; + } + } + #endregion + + #region Texture SRT Animation Calculation + private void CalculateAnmTexSrt() { + TexSRTAnmOffsets = new Dictionary(); + TexSRTAnmInfos = new Dictionary(); + + var chrAnmDict = File.GetGroup("AnmTexSrt(NW4R)"); + + foreach (var kv in chrAnmDict) { + AddString(kv.Key); + AlignCalcPos(0x20); + TextureSRTAnim anim = kv.Value; + + LogPosition("TextureSRT Anim: " + kv.Key); + TexSRTAnmOffsets.Add(anim, CurrentPos); + + CurrentPos += 0x20; + LogPosition("TextureSRT Anim Info Struct: " + kv.Key); + + CurrentPos += 0xC; + LogPosition("TextureSRT Anim Node Dictionary: " + kv.Key); + CurrentPos += GetSizeForResDict(anim.Nodes.Count); + + var anminfo = new TexSRTAnmInfo(); + TexSRTAnmInfos.Add(anim, anminfo); + + // Now size each node + foreach (var nodepair in anim.Nodes) { + AddString(nodepair.Key); + TextureSRTAnim.Node node = nodepair.Value; + + anminfo.NodeOffsets.Add(node, CurrentPos); + + LogPosition("TextureSRT Anim Node: " + nodepair.Key); + CurrentPos += 12; + + var nodeinfo = new TexSRTAnmNodeInfo(); + anminfo.NodeInfo.Add(node, nodeinfo); + + // Figure out what anims we're using + for (int i = 0; i < 8; i++) + if (node.Textures[i].Exists) + CurrentPos += 4; + for (int i = 0; i < 3; i++) + if (node.IndirectTextures[i].Exists) + CurrentPos += 4; + + for (int i = 0; i < 8; i++) { + if (node.Textures[i].Exists) { + nodeinfo.TextureOffsets[i] = CurrentPos; + CalculateAnmTexSrtElement(anminfo.KFInfo, node.Textures[i]); + } + } + + for (int i = 0; i < 3; i++) { + if (node.IndirectTextures[i].Exists) { + nodeinfo.IndirectTextureOffsets[i] = CurrentPos; + CalculateAnmTexSrtElement(anminfo.KFInfo, node.IndirectTextures[i]); + } + } + } + CalculateKeyframeData(anminfo.KFInfo); + anminfo.BlockSize = CurrentPos - TexSRTAnmOffsets[anim]; + } + } + + private void CalculateAnmTexSrtElement(KeyframeWriterInfo kfinfo, TextureSRTAnim.Element elem) { + CurrentPos += 4; + + if (!(elem.ScaleS.IsConstWith(1) && elem.ScaleT.IsConstWith(1))) { + // Scale is not all 1 + kfinfo.Add(elem.ScaleS); + CurrentPos += 4; + + if (elem.ScaleT != elem.ScaleS) { + // Not uniform + kfinfo.Add(elem.ScaleT); + CurrentPos += 4; + } + } + + if (!elem.Rotate.IsConstWith(0)) { + // Rotate is not zero + kfinfo.Add(elem.Rotate); + CurrentPos += 4; + } + + if (!(elem.TransS.IsConstWith(0) && elem.TransT.IsConstWith(0))) { + // Translate is not all zero + kfinfo.Add(elem.TransS); + kfinfo.Add(elem.TransT); + CurrentPos += 8; + } + } + #endregion + + #region Keyframe Animation Calculation + private void CalculateKeyframeData(KeyframeWriterInfo kfinfo) { + foreach (var pair in kfinfo.KeyframeAnims) { + var anim = pair.two; + + kfinfo.KeyframeAnimOffsets[pair] = CurrentPos; + + CurrentPos += 8 + (anim.Keyframes.Length * 12); + } + } + #endregion + + private void AlignCalcPos(int alignTo) { if ((CurrentPos & (alignTo - 1)) == 0) return; @@ -594,6 +1072,15 @@ namespace NW4RTools { case "Textures(NW4R)": WriteResDict(kv.Value as ResDict, TextureOffsets); break; + case "AnmChr(NW4R)": + WriteResDict(kv.Value as ResDict, ChrAnmOffsets); + break; + case "AnmClr(NW4R)": + WriteResDict(kv.Value as ResDict, ClrAnmOffsets); + break; + case "AnmTexSrt(NW4R)": + WriteResDict(kv.Value as ResDict, TexSRTAnmOffsets); + break; default: // for testing, boo Output.AddPadding(GetSizeForResDict((kv.Value as ICollection).Count)); @@ -613,6 +1100,15 @@ namespace NW4RTools { case "Textures(NW4R)": WriteTextures(); break; + case "AnmChr(NW4R)": + WriteAnmChr(); + break; + case "AnmClr(NW4R)": + WriteAnmClr(); + break; + case "AnmTexSrt(NW4R)": + WriteAnmTexSrt(); + break; default: Debug.Send("UNHANDLED RESOURCE TYPE: {0}", kv.Key); break; @@ -637,7 +1133,9 @@ namespace NW4RTools { Model model = kv.Value; var calcInfo = ModelCalcInfos[model]; - // Base struct: magic 'MDL0', block size, version [currently 11], resfile offset + Misc.Assert(startPos == ModelOffsets[model]); + + // Base struct: magic 'MDL0', block size, version[currently 11], resfile offset Output.WriteUInt32(0x4D444C30); Output.WriteUInt32((uint)calcInfo.BlockSize); Output.WriteUInt32(11); @@ -680,6 +1178,7 @@ namespace NW4RTools { Output.WriteUInt32((uint)model.VertexCount); Output.WriteUInt32((uint)model.TriangleCount); Output.WriteUInt32(0); + // TODO: This is not right, fix it Output.WriteUInt32((uint)model.MatrixIDtoNodeID.Length); Output.WriteByte(model.UsesNrmMtxArray ? (byte)1 : (byte)0); Output.WriteByte(model.UsesTexMtxArray ? (byte)1 : (byte)0); @@ -778,8 +1277,8 @@ namespace NW4RTools { var op4 = insn as ByteCode.DrawShapeInstruction; Output.WriteUInt16(op4.MaterialID); Output.WriteUInt16(op4.ShapeID); - Output.WriteUInt16(op4.NodeID); - Output.WriteByte(0); + Output.WriteUInt16(op4.NodeID); + Output.WriteByte(op4.Unk); break; case ByteCode.OpType.AssignMtxToNode: @@ -847,9 +1346,9 @@ namespace NW4RTools { Material mat = kv.Value; int startPos = Output.Position; - // size, model offset, name offset, index - // is size static, or does it change? must verify - Output.WriteUInt32(0x5E8); + // size, model offset, name offset, index + var modelInfo = ModelCalcInfos[m]; + Output.WriteUInt32((UInt32)modelInfo.MaterialSizes[mat]); Output.WriteInt32(ModelOffsets[m] - startPos); Output.WriteInt32(StringPositions[kv.Key] - startPos); Output.WriteUInt32((uint)currentIndex); @@ -1233,7 +1732,9 @@ namespace NW4RTools { int startPos = Output.Position; Texture tex = kv.Value; - // Base struct: magic 'TEX0', block size, version [currently 11], resfile offset + Misc.Assert(startPos == TextureOffsets[tex]); + + // Base struct: magic 'TEX0', block size, version[currently 3], resfile offset Output.WriteUInt32(0x54455830); Output.WriteUInt32((uint)(0x40 + tex.GetDataSize())); Output.WriteUInt32(3); @@ -1257,13 +1758,459 @@ namespace NW4RTools { Output.AlignTo(0x20); for (int i = 0; i < tex.Images.Length; i++) +#if DEV_DONT_MODIFY_TEXTURE + Output.WriteBytes(tex.ImageData[i]); +#else Output.WriteBytes(tex.ExportData(i)); +#endif + } + } + } + #endregion + + #region Character Animation Writing + private void WriteAnmChr() { + foreach (var kv in File.GetGroup("AnmChr(NW4R)")) { + using (var c = Debug.Push("Character Animation: {0}", kv.Key)) { + Output.AlignTo(0x20); + + int startPos = Output.Position; + CharacterAnim anm = kv.Value; + var info = ChrAnmInfos[anm]; + + Misc.Assert(startPos == ChrAnmOffsets[anm]); + + // Base struct: magic 'CHR0', block size, version[currently 5], resfile offset + Output.WriteUInt32(0x43485230); + Output.WriteUInt32((UInt32)info.BlockSize); + Output.WriteUInt32(5); + Output.WriteInt32(-startPos); + + // Data offset, name offset, other crap + Output.WriteInt32(0x2C); + Output.WriteUInt32(0); + Output.WriteInt32(StringPositions[kv.Key] - startPos); + Output.WriteUInt32(0); + + Output.WriteUInt16((UInt16)anm.FrameCount); + Output.WriteUInt16((UInt16)anm.Nodes.Count); + + Output.WriteUInt32(anm.Loop ? 1U : 0U); + Output.WriteUInt32((UInt32)anm.ScaleMode); + + WriteResDict(anm.Nodes, info.NodeOffsets); + + WriteAnmChrNodes(anm); + WriteAnmChrKeyframes(anm); + } + } + } + + private void WriteAnmChrNodes(CharacterAnim anm) { + var info = ChrAnmInfos[anm]; + + foreach (var kv in anm.Nodes) { + using (var c = Debug.Push("Element: {0}", kv.Key)) { + int startPos = Output.Position; + CharacterAnim.Node node = kv.Value; + ChrAnmNodeInfo nodeinfo = info.NodeInfo[node]; + + Misc.Assert(startPos == info.NodeOffsets[node]); + + Output.WriteInt32(StringPositions[kv.Key] - startPos); + Output.WriteUInt32((UInt32)nodeinfo.Flags); + + if ((nodeinfo.Flags & CharacterAnim.Flags.ScaleXNotExist) == 0) { + if (node.ScaleX.IsConstant) + Output.WriteFloat(node.ScaleX.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.ScaleFormat, node.ScaleX) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.ScaleYNotExist) == 0) { + if (node.ScaleY.IsConstant) + Output.WriteFloat(node.ScaleY.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.ScaleFormat, node.ScaleY) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.ScaleZNotExist) == 0) { + if (node.ScaleZ.IsConstant) + Output.WriteFloat(node.ScaleZ.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.ScaleFormat, node.ScaleZ) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.RotateXNotExist) == 0) { + if (node.RotateX.IsConstant) + Output.WriteFloat(node.RotateX.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.RotateFormat, node.RotateX) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.RotateYNotExist) == 0) { + if (node.RotateY.IsConstant) + Output.WriteFloat(node.RotateY.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.RotateFormat, node.RotateY) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.RotateZNotExist) == 0) { + if (node.RotateZ.IsConstant) + Output.WriteFloat(node.RotateZ.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.RotateFormat, node.RotateZ) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.TranslateXNotExist) == 0) { + if (node.TranslateX.IsConstant) + Output.WriteFloat(node.TranslateX.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.TranslateFormat, node.TranslateX) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.TranslateYNotExist) == 0) { + if (node.TranslateY.IsConstant) + Output.WriteFloat(node.TranslateY.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.TranslateFormat, node.TranslateY) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.TranslateZNotExist) == 0) { + if (node.TranslateZ.IsConstant) + Output.WriteFloat(node.TranslateZ.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.TranslateFormat, node.TranslateZ) - startPos); + } + } + } + } + + private void WriteAnmChrKeyframes(CharacterAnim anm) { + var anminfo = ChrAnmInfos[anm]; + foreach (var kv in anminfo.KFInfo.KeyframeAnimOffsets) { + var format = (CharacterAnim.RotateFormatType)kv.Key.one; + var kf = kv.Key.two; + var expectedOffset = kv.Value; + + Misc.Assert(Output.Position == expectedOffset); + + if (format == CharacterAnim.RotateFormatType.Data32 || + format == CharacterAnim.RotateFormatType.Data48 || + format == CharacterAnim.RotateFormatType.Data96) { + // Header for FVS + Output.WriteUInt16((UInt16)kf.Keyframes.Length); + Output.AddPadding(2); + + float delta = kf.Keyframes[kf.Keyframes.Length - 1].Frame - kf.Keyframes [0].Frame; + float invKeyFrameRange = (1.0f / delta); + Output.WriteFloat(invKeyFrameRange); } + + switch (format) { + case CharacterAnim.RotateFormatType.Data32: + for (int i = 0; i < kf.Keyframes.Length; i++) { + Output.WriteUInt32( + (((UInt32)kf.Keyframes[i].Frame) << 24) | + ((((UInt32)kf.Keyframes[i].Value) & 0xFFF) << 12) | + (((UInt32)(kf.Keyframes[i].Slope * 32.0f)) & 0xFFF)); + } + break; + case CharacterAnim.RotateFormatType.Data48: + for (int i = 0; i < kf.Keyframes.Length; i++) { + Output.WriteInt16((Int16)(kf.Keyframes[i].Frame * 32.0f)); + Output.WriteUInt16((UInt16)(kf.Keyframes[i].Value / (360.0f / 0x10000))); + Output.WriteInt16((Int16)(kf.Keyframes[i].Slope * 256.0f)); + } + break; + case CharacterAnim.RotateFormatType.Data96: + for (int i = 0; i < kf.Keyframes.Length; i++) { + Output.WriteFloat(kf.Keyframes[i].Frame); + Output.WriteFloat(kf.Keyframes[i].Value); + Output.WriteFloat(kf.Keyframes[i].Slope); + } + break; + default: + throw new NotImplementedException(); + } + + Output.AlignTo(4); } } #endregion - #region ResDicts + #region Color Animation Writing + private void WriteAnmClr() { + foreach (var kv in File.GetGroup("AnmClr(NW4R)")) { + using (var c = Debug.Push("Color Animation: {0}", kv.Key)) { + int startPos = Output.Position; + ColorAnim anm = kv.Value; + var info = ClrAnmInfos[anm]; + + Misc.Assert(startPos == ClrAnmOffsets[anm]); + + // Base struct: magic 'CLR0', block size, version[currently 5], resfile offset + Output.WriteUInt32(0x434C5230); + Output.WriteUInt32((UInt32)info.BlockSize); + Output.WriteUInt32(4); + Output.WriteInt32(-startPos); + + // Data offset, name offset, other crap + Output.WriteInt32(0x28); + Output.WriteUInt32(0); + Output.WriteInt32(StringPositions[kv.Key] - startPos); + Output.WriteUInt32(0); + + Output.WriteUInt16((UInt16)anm.FrameCount); + Output.WriteUInt16((UInt16)anm.Nodes.Count); + + Output.WriteUInt32(anm.Loop ? 1U : 0U); + + WriteResDict(anm.Nodes, info.NodeOffsets); + + WriteAnmClrNodes(anm); + WriteAnmClrArrays(anm); + } + } + } + + private void WriteAnmClrNodes(ColorAnim anm) { + ClrAnmInfo info = ClrAnmInfos[anm]; + + foreach (var kv in anm.Nodes) { + using (var c = Debug.Push("Element: {0}", kv.Key)) { + int startPos = Output.Position; + ColorAnim.Node node = kv.Value; + + Misc.Assert(startPos == info.NodeOffsets[node]); + + Output.WriteInt32(StringPositions[kv.Key] - startPos); + + UInt32 flags = 0; + + for (int i = node.Elements.Length - 1; i >= 0; i--) { + flags <<= 2; + if (node.Elements[i].Exists) + flags |= 1; + if (node.Elements[i].IsConstant) + flags |= 2; + } + + Output.WriteUInt32(flags); + + for (int i = 0; i < node.Elements.Length; i++) { + if (node.Elements[i].Exists) { + Output.WriteUInt32(node.Elements[i].Mask); + if (node.Elements[i].IsConstant) { + Output.WriteColor(node.Elements[i].ConstValue); + } else { + // Search for the matching array + int find = 0; + bool wasFound = false; + foreach (var arr in info.UsedArrays) { + if (Misc.ArrayCompare(node.Elements[i].Colors, arr)) { + wasFound = true; + break; + } + find++; + } + + if (!wasFound) + throw new Exception(); + + int arrayPos = info.UsedArrayPositions[find]; + Output.WriteInt32(arrayPos - Output.Position); + } + } + } + + } + } + } + + private void WriteAnmClrArrays(ColorAnim anm) { + var info = ClrAnmInfos[anm]; + + int i = 0; + foreach (var arr in info.UsedArrays) { + Misc.Assert(info.UsedArrayPositions[i] == Output.Position); + + for (int j = 0; j < arr.Length; j++) + Output.WriteColor(arr[j]); + + i++; + } + } + #endregion + + #region Texture SRT Animation Writing + private void WriteAnmTexSrt() { + foreach (var kv in File.GetGroup("AnmTexSrt(NW4R)")) { + using (var c = Debug.Push("TextureSRT Animation: {0}", kv.Key)) { + Output.AlignTo(0x20); + + int startPos = Output.Position; + TextureSRTAnim anm = kv.Value; + var info = TexSRTAnmInfos[anm]; + + Misc.Assert(startPos == TexSRTAnmOffsets[anm]); + + // Base struct: magic 'SRT0', block size, version[currently 5], resfile offset + Output.WriteUInt32(0x53525430); + Output.WriteUInt32((UInt32)info.BlockSize); + Output.WriteUInt32(5); + Output.WriteInt32(-startPos); + + // Data offset, name offset, other crap + Output.WriteInt32(0x2C); + Output.WriteUInt32(0); + Output.WriteInt32(StringPositions[kv.Key] - startPos); + Output.WriteUInt32(0); + + Output.WriteUInt16((UInt16)anm.FrameCount); + Output.WriteUInt16((UInt16)anm.Nodes.Count); + + Output.WriteUInt32((UInt32)anm.MatrixMode); + Output.WriteUInt32(anm.Loop ? 1U : 0U); + + WriteResDict(anm.Nodes, info.NodeOffsets); + + WriteAnmTexSrtNodes(anm); + WriteKeyframeData(info.KFInfo); + } + } + } + + private void WriteAnmTexSrtNodes(TextureSRTAnim anm) { + var info = TexSRTAnmInfos[anm]; + + foreach (var kv in anm.Nodes) { + using (var c = Debug.Push("Element: {0}", kv.Key)) { + int startPos = Output.Position; + TextureSRTAnim.Node node = kv.Value; + TexSRTAnmNodeInfo nodeinfo = info.NodeInfo[node]; + + Misc.Assert(startPos == info.NodeOffsets[node]); + + Output.WriteInt32(StringPositions[kv.Key] - startPos); + + UInt32 flags = 0, indFlags = 0; + + for (int i = 0; i < 8; i++) + if (node.Textures[i].Exists) + flags |= (UInt32)(1 << i); + for (int i = 0; i < 3; i++) + if (node.IndirectTextures[i].Exists) + indFlags |= (UInt32)(1 << i); + + Output.WriteUInt32(flags); + Output.WriteUInt32(indFlags); + + var elemsToAddLater = new List(); + + for (int i = 0; i < 8; i++) + if (node.Textures[i].Exists) { + Output.WriteInt32(nodeinfo.TextureOffsets[i] - startPos); + elemsToAddLater.Add(node.Textures[i]); + } + + for (int i = 0; i < 3; i++) + if (node.IndirectTextures[i].Exists) { + Output.WriteInt32(nodeinfo.IndirectTextureOffsets[i] - startPos); + elemsToAddLater.Add(node.IndirectTextures[i]); + } + + foreach (var elem in elemsToAddLater) + WriteAnmTexSrtElement(elem, info.KFInfo); + } + } + } + + private void WriteAnmTexSrtElement(TextureSRTAnim.Element elem, KeyframeWriterInfo kfinfo) { + UInt32 flags = 1; + + int flagPos = Output.Position; + Output.WriteUInt32(0); // placeholder! + + if (elem.ScaleS.IsConstWith(1) && elem.ScaleT.IsConstWith(1)) { + flags |= 2 | 0x10 | 0x20 | 0x40; + } else { + WriteKeyframeAnim(elem.ScaleS, kfinfo); + if (elem.ScaleS.IsConstant) + flags |= 0x20; + + if (elem.ScaleS == elem.ScaleT) { + flags |= 0x10; + if (elem.ScaleS.IsConstant) + flags |= 0x40; + } else { + WriteKeyframeAnim(elem.ScaleT, kfinfo); + if (elem.ScaleT.IsConstant) + flags |= 0x40; + } + } + + if (elem.Rotate.IsConstWith(0)) { + flags |= 4 | 0x80; + } else { + WriteKeyframeAnim(elem.Rotate, kfinfo); + if (elem.Rotate.IsConstant) + flags |= 0x80; + } + + if (elem.TransS.IsConstWith(0) && elem.TransT.IsConstWith(0)) { + flags |= 8 | 0x100 | 0x200; + } else { + WriteKeyframeAnim(elem.TransS, kfinfo); + WriteKeyframeAnim(elem.TransT, kfinfo); + + if (elem.TransS.IsConstant) + flags |= 0x100; + if (elem.TransT.IsConstant) + flags |= 0x200; + } + + // Replace the flags + int savePos = Output.Position; + Output.Seek(flagPos); + Output.WriteUInt32(flags); + Output.Seek(savePos); + } + #endregion + + #region Keyframe Data Writing + private void WriteKeyframeAnim(KeyframeAnim anim, KeyframeWriterInfo kfinfo) { + if (anim.IsConstant) + Output.WriteFloat(anim.ConstValue); + else + Output.WriteInt32(kfinfo.GetPosition(anim) - Output.Position); + } + + private void WriteKeyframeData(KeyframeWriterInfo kfinfo) { + foreach (var pair in kfinfo.KeyframeAnims) { + var anim = pair.two; + var kf = pair.two; + var expectedOffset = kfinfo.KeyframeAnimOffsets[pair]; + + Misc.Assert(expectedOffset == Output.Position); + + Output.WriteUInt16((UInt16)kf.Keyframes.Length); + Output.AddPadding(2); + + float delta = kf.Keyframes[kf.Keyframes.Length - 1].Frame - kf.Keyframes[0].Frame; + float invKeyFrameRange = (1.0f / delta); + Output.WriteFloat(invKeyFrameRange); + + for (int i = 0; i < kf.Keyframes.Length; i++) { + Output.WriteFloat(kf.Keyframes[i].Frame); + Output.WriteFloat(kf.Keyframes[i].Value); + Output.WriteFloat(kf.Keyframes[i].Slope); + } + } + } + #endregion + + #region ResDicts private struct RawDictEntry { public ushort Ref; public ushort Unk; diff --git a/NW4RTools/Misc.cs b/NW4RTools/Misc.cs index c42b185..118f164 100644 --- a/NW4RTools/Misc.cs +++ b/NW4RTools/Misc.cs @@ -16,6 +16,46 @@ namespace NW4RTools { public static uint AlignDown(uint val, uint to) { return val & ~(to - 1); } + + + public static void Assert(bool condition) { + if (!condition) + throw new Exception("Assert failed"); + } + public static void Assert(bool condition, string format, params object[] args) { + if (!condition) + throw new Exception(String.Format(format, args)); + } + + + public static bool ArrayCompare(T[] one, T[] two) { + if (one == two) + return true; + if (one == null || two == null) + return false; + if (one.Length != two.Length) + return false; + + for (int i = 0; i < one.Length; i++) { + if (!one[i].Equals(two[i])) + return false; + } + + return true; + } + + + public static int ArrayHash(T[] arr) { + if (arr == null) + return 0xEA7BEEF; + + int hash = 0; + + for (int i = 0; i < arr.Length; i++) + hash ^= arr[i].GetHashCode(); + + return hash; + } } } diff --git a/NW4RTools/Models/Animation/CharacterAnim.cs b/NW4RTools/Models/Animation/CharacterAnim.cs index dd496f6..72efd56 100644 --- a/NW4RTools/Models/Animation/CharacterAnim.cs +++ b/NW4RTools/Models/Animation/CharacterAnim.cs @@ -81,6 +81,8 @@ namespace NW4RTools.Models.Animation { public STFormatType ScaleFormat; public STFormatType TranslateFormat; public RotateFormatType RotateFormat; + + public bool ScaleUseModel, RotateUseModel, TranslateUseModel; } public CharacterAnim() { diff --git a/NW4RTools/Models/Animation/Shared.cs b/NW4RTools/Models/Animation/Shared.cs index f87bea6..62cca94 100644 --- a/NW4RTools/Models/Animation/Shared.cs +++ b/NW4RTools/Models/Animation/Shared.cs @@ -28,5 +28,45 @@ namespace NW4RTools.Models.Animation { Values = null; FloatValues = null; } + + + public bool IsConstWith(float cv) { + return (IsConstant && (ConstValue == cv)); + } + + + public override bool Equals(object obj) { + return obj is KeyframeAnim && this == (KeyframeAnim)obj; + } + + public override int GetHashCode() { + return + IsConstant.GetHashCode() ^ + ConstValue.GetHashCode() ^ + BaseValue.GetHashCode() ^ + Multiplier.GetHashCode() ^ + Misc.ArrayHash(Keyframes) ^ + Misc.ArrayHash(Values) ^ + Misc.ArrayHash(FloatValues); + } + + public static bool operator ==(KeyframeAnim x, KeyframeAnim y) { + return + (x.IsConstant == y.IsConstant) && + (x.ConstValue == y.ConstValue) && + (x.BaseValue == y.BaseValue) && + (x.Multiplier == y.Multiplier) && + Misc.ArrayCompare(x.Keyframes, y.Keyframes) && + Misc.ArrayCompare(x.Values, y.Values) && + Misc.ArrayCompare(x.FloatValues, y.FloatValues); + } + + public static bool operator !=(KeyframeAnim x, KeyframeAnim y) { + return !(x == y); + } + + public void Dump() { + Console.WriteLine("IsC:{0} CV:{1} KF:{2} BV:{3} M:{4} V:{5} FV:{6}", IsConstant, ConstValue, Keyframes, BaseValue, Multiplier, Values, FloatValues); + } } } diff --git a/NW4RTools/Models/ByteCode.cs b/NW4RTools/Models/ByteCode.cs old mode 100644 new mode 100755 index 6df6dee..62968e9 --- a/NW4RTools/Models/ByteCode.cs +++ b/NW4RTools/Models/ByteCode.cs @@ -50,7 +50,8 @@ namespace NW4RTools.Models { public UInt16 MaterialID; public UInt16 ShapeID; - public UInt16 NodeID; + public UInt16 NodeID; + public byte Unk; } // According to BrawlLib: in NodeMix, this is for "primary influences" diff --git a/NW4RTools/NW4RTools.csproj b/NW4RTools/NW4RTools.csproj index bcdc413..50b4320 100644 --- a/NW4RTools/NW4RTools.csproj +++ b/NW4RTools/NW4RTools.csproj @@ -1,94 +1,89 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {A9C9FABD-0A5F-4DAB-979D-9F288F96866F} - Library - NW4RTools - NW4RTools - v3.5 - - - true - full - false - bin\Debug - DEBUG - prompt - 4 - false - true - - - none - false - bin\Release - prompt - 4 - false - true - - - - - - - - ..\ExternalLibs\OpenTK.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {A9C9FABD-0A5F-4DAB-979D-9F288F96866F} + Library + NW4RTools + NW4RTools + v3.5 + + + true + full + false + bin\Debug + DEBUG DEV_DONT_MODIFY_TEXTURE + prompt + 4 + false + true + + + none + false + bin\Release + prompt + 4 + false + true + DEV_DONT_MODIFY_TEXTURE + + + + + + + + ..\ExternalLibs\OpenTK.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + \ No newline at end of file diff --git a/NW4RTools/Texture.cs b/NW4RTools/Texture.cs index a328648..f2cf231 100644 --- a/NW4RTools/Texture.cs +++ b/NW4RTools/Texture.cs @@ -63,6 +63,9 @@ namespace NW4RTools { } +#if DEV_DONT_MODIFY_TEXTURE + public byte[][] ImageData; +#endif public Bitmap[] Images; @@ -95,7 +98,11 @@ namespace NW4RTools { } public int GetDataSize(int imageID) { +#if DEV_DONT_MODIFY_TEXTURE + return ImageData[imageID].Length; +#else return GetDataSize(Images[imageID].Width, Images[imageID].Height, Format); +#endif } @@ -104,9 +111,9 @@ namespace NW4RTools { int blkWidth = info.TexelWidth; int blkHeight = info.TexelHeight; - int width = Images[imageID].Width; - int height = Images[imageID].Height; - var bits = Images[imageID].LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + int width = Images [imageID].Width; + int height = Images [imageID].Height; + var bits = Images [imageID].LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); var data = new OutputStream(ByteEndian.BigEndian); @@ -238,7 +245,7 @@ namespace NW4RTools { for (int iBlockX = 0; iBlockX < 2; iBlockX++) { var block = Util.NVDXT.compressDXT1a((Util.NVDXT.ARGBPixel*)bits.Scan0, x + (iBlockX * 4), y + (iBlockY * 4), - bits.Width, bits.Height); + (bits.Stride / 4), bits.Height); data.WriteUInt16(block.Color0); data.WriteUInt16(block.Color1); diff --git a/NW4RTools/Types.cs b/NW4RTools/Types.cs index 619303e..9bfa0db 100644 --- a/NW4RTools/Types.cs +++ b/NW4RTools/Types.cs @@ -1,6 +1,16 @@ using System; namespace NW4RTools { + public struct Pair { + public TOne one; + public TTwo two; + + public Pair(TOne _one, TTwo _two) { + one = _one; + two = _two; + } + } + public enum ByteEndian { LittleEndian, BigEndian -- cgit v1.2.3