From 364e99d849378546323d1d06307b6773e813b742 Mon Sep 17 00:00:00 2001 From: Treeki Date: Sun, 19 Feb 2012 06:28:18 +0100 Subject: added what is probably horribly broken support for reading animations --- NW4RTools/BrresReader.cs | 488 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 481 insertions(+), 7 deletions(-) (limited to 'NW4RTools/BrresReader.cs') diff --git a/NW4RTools/BrresReader.cs b/NW4RTools/BrresReader.cs index 8b9fcb1..f120943 100644 --- a/NW4RTools/BrresReader.cs +++ b/NW4RTools/BrresReader.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using NW4RTools.Models; +using NW4RTools.Models.Animation; namespace NW4RTools { public class BrresReader { @@ -98,6 +99,9 @@ namespace NW4RTools { case "AnmTexSrt(NW4R)": File.Add(name, ReadAndConvertDict(ins, ConvertAnmTexSRTResource)); break; + case "External": + File.Add(name, ReadAndConvertDict(ins, ConvertExternalResource)); + break; default: Debug.Send("Not implemented"); return; @@ -106,6 +110,14 @@ namespace NW4RTools { } + private byte[] ConvertExternalResource(string name, InputStream ins) { + using (var c = Debug.Push(name)) { + OffsetMap.Add(ins.Position, "External: " + name); + return null; + } + } + + private Texture ConvertTextureResource(string name, InputStream ins) { using (var c = Debug.Push(name)) { @@ -161,12 +173,263 @@ namespace NW4RTools { OffsetMap.Add(startPos, "Character Animation: " + name); - // TODO - + byte[] magic = ins.ReadBytes(4); + UInt32 size = ins.ReadUInt32(); + UInt32 version = ins.ReadUInt32(); + + Debug.Send("Offset: 0x{0:X}; Size: 0x{1:X}; Version: {2}", startPos, size, version); + + CHR_ANIM_HACK = anim; + + Int32 resFileOffset = ins.ReadInt32(); + Int32 dataOffset = ins.ReadInt32(); + ins.Skip(4); // No idea + Int32 nameOffset = ins.ReadInt32(); + ins.Skip(4); // No idea + + anim.FrameCount = ins.ReadUInt16(); + UInt16 nodeCount = ins.ReadUInt16(); + + anim.Loop = (ins.ReadUInt32() > 0); + anim.ScaleMode = (Model.ScaleModeType)ins.ReadUInt32(); + + ins.Seek(startPos + dataOffset); + anim.Nodes = ReadAndConvertDict(ins, ConvertAnmChrNode); + + CHR_ANIM_HACK = null; + return anim; } } + // HACK HACK HACK + private CharacterAnim CHR_ANIM_HACK; + + private Models.Animation.CharacterAnim.Node ConvertAnmChrNode(string name, InputStream ins) { + using (var c = Debug.Push(name)) { + var node = new Models.Animation.CharacterAnim.Node(); + + int startPos = ins.Position; + OffsetMap.Add(startPos, "AnmChr Node: " + name); + + UInt32 nameOffset = ins.ReadUInt32(); + CharacterAnim.Flags flags = (CharacterAnim.Flags)ins.ReadUInt32(); + + Debug.Send("{0:X}", (UInt32)flags); + Debug.Send("{0}", flags); + node.ScaleFormat = (CharacterAnim.STFormatType)((UInt32)(flags & CharacterAnim.Flags.ScaleFormatMask) >> 25); + node.RotateFormat = (CharacterAnim.RotateFormatType)((UInt32)(flags & CharacterAnim.Flags.RotateFormatMask) >> 27); + node.TranslateFormat = (CharacterAnim.STFormatType)((UInt32)(flags & CharacterAnim.Flags.TranslateFormatMask) >> 30); + + node.Flags = flags & CharacterAnim.Flags.NonComputableFlags; + + // SCALES + bool needScaleYZ = true; + + if ((flags & CharacterAnim.Flags.ScaleXNotExist) == 0) { + node.ScaleX = ReadAnmChrElement( + ins, startPos, node, flags, + CharacterAnim.Flags.ScaleXConstant, + (CharacterAnim.RotateFormatType)node.ScaleFormat); + + if ((flags & CharacterAnim.Flags.ScaleUniform) != 0) { + node.ScaleY = node.ScaleX; + node.ScaleZ = node.ScaleX; + needScaleYZ = false; + } + } else { + node.ScaleX = new KeyframeAnim(1.0f); + } + + if (needScaleYZ) { + if ((flags & CharacterAnim.Flags.ScaleYNotExist) == 0) + node.ScaleY = ReadAnmChrElement( + ins, startPos, node, flags, + CharacterAnim.Flags.ScaleYConstant, + (CharacterAnim.RotateFormatType)node.ScaleFormat); + else + node.ScaleY = new KeyframeAnim(1.0f); + + if ((flags & CharacterAnim.Flags.ScaleZNotExist) == 0) + node.ScaleZ = ReadAnmChrElement( + ins, startPos, node, flags, + CharacterAnim.Flags.ScaleZConstant, + (CharacterAnim.RotateFormatType)node.ScaleFormat); + else + node.ScaleZ = new KeyframeAnim(1.0f); + } + + // ROTATIONS + if ((flags & CharacterAnim.Flags.RotateXNotExist) == 0) { + node.RotateX = ReadAnmChrElement( + ins, startPos, node, flags, + CharacterAnim.Flags.RotateXConstant, + node.RotateFormat); + } else { + node.RotateX = new KeyframeAnim(0.0f); + } + + if ((flags & CharacterAnim.Flags.RotateYNotExist) == 0) { + node.RotateY = ReadAnmChrElement( + ins, startPos, node, flags, + CharacterAnim.Flags.RotateYConstant, + node.RotateFormat); + } else { + node.RotateY = new KeyframeAnim(0.0f); + } + + if ((flags & CharacterAnim.Flags.RotateZNotExist) == 0) { + node.RotateZ = ReadAnmChrElement( + ins, startPos, node, flags, + CharacterAnim.Flags.RotateZConstant, + node.RotateFormat); + } else { + node.RotateZ = new KeyframeAnim(0.0f); + } + + // TRANSLATIIONS + + if ((flags & CharacterAnim.Flags.TranslateXNotExist) == 0) { + node.TranslateX = ReadAnmChrElement( + ins, startPos, node, flags, + CharacterAnim.Flags.TranslateXConstant, + (CharacterAnim.RotateFormatType)node.TranslateFormat); + } else { + node.TranslateX = new KeyframeAnim(0.0f); + } + + if ((flags & CharacterAnim.Flags.TranslateYNotExist) == 0) { + node.TranslateY = ReadAnmChrElement( + ins, startPos, node, flags, + CharacterAnim.Flags.TranslateYConstant, + (CharacterAnim.RotateFormatType)node.TranslateFormat); + } else { + node.TranslateY = new KeyframeAnim(0.0f); + } + + if ((flags & CharacterAnim.Flags.TranslateZNotExist) == 0) { + node.TranslateZ = ReadAnmChrElement( + ins, startPos, node, flags, + CharacterAnim.Flags.TranslateZConstant, + (CharacterAnim.RotateFormatType)node.TranslateFormat); + } else { + node.TranslateZ = new KeyframeAnim(0.0f); + } + + return node; + } + } + + private KeyframeAnim ReadAnmChrElement( + InputStream ins, int startPos, CharacterAnim.Node node, + CharacterAnim.Flags flags, CharacterAnim.Flags isConstF, CharacterAnim.RotateFormatType format) { + + var elem = new KeyframeAnim(); + + elem.IsConstant = (flags & isConstF) != 0; + OffsetMap.Add(ins.Position, String.Format("Element [{0}]", elem.IsConstant ? "Constant" : "Keyframes")); + + if (elem.IsConstant) { + elem.ConstValue = ins.ReadFloat(); + } else if ( + format == CharacterAnim.RotateFormatType.Data32 || + format == CharacterAnim.RotateFormatType.Data48 || + format == CharacterAnim.RotateFormatType.Data96) { + // KEYFRAME DATA + int savePos = ins.Position; + ins.Seek(startPos + ins.ReadInt32()); + int kfPos = ins.Position; + + UInt16 kfCount = ins.ReadUInt16(); + ins.Skip(2); + + OffsetMap[kfPos] = String.Format("Element Keyframe Data for {0:X} [{1} frames]", savePos, kfCount); + + float invKeyFrameRange = ins.ReadFloat(); + elem.Keyframes = new Keyframe[kfCount]; + + Debug.Send("{0}", format); + for (int i = 0; i < kfCount; i++) { + switch (format) { + case CharacterAnim.RotateFormatType.Data32: + UInt32 val = ins.ReadUInt32(); + + // Is this correct? + elem.Keyframes[i].Frame = val >> 24; + elem.Keyframes[i].Value = (val >> 12) & 0xFFF; + elem.Keyframes[i].Slope = ((float)(val & 0xFFF)) / 32.0f; + break; + + case CharacterAnim.RotateFormatType.Data48: + // Is this correct? + elem.Keyframes[i].Frame = ((float)ins.ReadInt16()) / 32.0f; + elem.Keyframes[i].Value = ((float)ins.ReadUInt16()) * (360.0f / 0x10000); + elem.Keyframes[i].Slope = ((float)ins.ReadInt16()) / 256.0f; + break; + + case CharacterAnim.RotateFormatType.Data96: + elem.Keyframes[i].Frame = ins.ReadFloat(); + elem.Keyframes[i].Value = ins.ReadFloat(); + elem.Keyframes[i].Slope = ins.ReadFloat(); + break; + + } + } + + ins.Seek(savePos + 4); + + } else if ( + format == CharacterAnim.RotateFormatType.DataUnk4 || + format == CharacterAnim.RotateFormatType.DataUnk5) { + // SOMETHING ELSE + int savePos = ins.Position; + ins.Seek(startPos + ins.ReadInt32()); + int kfPos = ins.Position; + + OffsetMap[kfPos] = String.Format("Element Multiplied Frame Data for {0:X}", savePos); + + elem.Multiplier = ins.ReadFloat(); + elem.BaseValue = ins.ReadFloat(); + elem.Values = new int[CHR_ANIM_HACK.FrameCount]; + + for (int i = 0; i < CHR_ANIM_HACK.FrameCount; i++) { + switch (format) { + case CharacterAnim.RotateFormatType.DataUnk4: + elem.Values[i] = ins.ReadByte(); + break; + case CharacterAnim.RotateFormatType.DataUnk5: + elem.Values[i] = ins.ReadUInt16(); + break; + } + } + + ins.Seek(savePos + 4); + + } else if ( + format == CharacterAnim.RotateFormatType.DataUnk6) { + // WHAT THE FUCK + int savePos = ins.Position; + ins.Seek(startPos + ins.ReadInt32()); + int kfPos = ins.Position; + + OffsetMap[kfPos] = String.Format("Element Type 6 Frame Data for {0:X}", savePos); + + elem.BaseValue = ins.ReadFloat(); + elem.FloatValues = new float[CHR_ANIM_HACK.FrameCount]; + + for (int i = 0; i < CHR_ANIM_HACK.FrameCount; i++) { + elem.FloatValues[i] = ins.ReadFloat(); + } + + ins.Seek(savePos + 4); + + } else { + Debug.Send("Unimplemented format {0} at {1:X}", format, ins.Position); + } + + return elem; + } + private Models.Animation.ColorAnim ConvertAnmClrResource(string name, InputStream ins) { @@ -176,13 +439,88 @@ namespace NW4RTools { int startPos = ins.Position; OffsetMap.Add(startPos, "Color Animation: " + name); - - // TODO - + + byte[] magic = ins.ReadBytes(4); + UInt32 size = ins.ReadUInt32(); + UInt32 version = ins.ReadUInt32(); + + Debug.Send("Offset: 0x{0:X}; Size: 0x{1:X}; Version: {2}", startPos, size, version); + + Int32 resFileOffset = ins.ReadInt32(); + Int32 dataOffset = ins.ReadInt32(); + ins.Skip(4); // No idea + Int32 nameOffset = ins.ReadInt32(); + ins.Skip(4); // No idea + + anim.FrameCount = ins.ReadUInt16(); + UInt16 nodeCount = ins.ReadUInt16(); + + anim.Loop = (ins.ReadUInt32() > 0); + + COLOR_ANIM_HACK = anim; + ins.Seek(startPos + dataOffset); + anim.Nodes = ReadAndConvertDict(ins, ConvertAnmClrNode); + COLOR_ANIM_HACK = null; + return anim; } } + // HACK HACK HACK + private ColorAnim COLOR_ANIM_HACK; + + private ColorAnim.Node ConvertAnmClrNode(string name, InputStream ins) { + using (var c = Debug.Push(name)) { + var node = new ColorAnim.Node(); + + int startPos = ins.Position; + OffsetMap[startPos] = "AnmClr Node: " + name; + + UInt32 nameOffset = ins.ReadUInt32(); + UInt32 flags = ins.ReadUInt32(); + + 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); + flags >>= 2; + } + + return node; + } + } + + private ColorAnim.Element ReadAnmClrElement(InputStream ins, int startPos, UInt32 flags) { + var elem = new ColorAnim.Element(); + + elem.Exists = (flags & 1) != 0; + elem.IsConstant = (flags & 2) != 0; + + OffsetMap[ins.Position] = String.Format("Element [{0}]", elem.IsConstant ? "Constant" : "Colours"); + + elem.Mask = ins.ReadUInt32(); + + if (!elem.Exists) + return elem; + + if (elem.IsConstant) { + elem.ConstValue = ins.ReadColor(); + } else { + int savePos = ins.Position; + ins.Seek(startPos + ins.ReadInt32()); + + elem.Colors = new Color[COLOR_ANIM_HACK.FrameCount + 1]; + + for (int i = 0; i < elem.Colors.Length; i++) { + elem.Colors[i] = ins.ReadColor(); + } + + ins.Seek(savePos + 4); + } + + return elem; + } + private Models.Animation.TextureSRTAnim ConvertAnmTexSRTResource(string name, InputStream ins) { @@ -193,12 +531,148 @@ namespace NW4RTools { OffsetMap.Add(startPos, "Texture SRT Animation: " + name); - // TODO - + byte[] magic = ins.ReadBytes(4); + UInt32 size = ins.ReadUInt32(); + UInt32 version = ins.ReadUInt32(); + + Debug.Send("Offset: 0x{0:X}; Size: 0x{1:X}; Version: {2}", startPos, size, version); + + Int32 resFileOffset = ins.ReadInt32(); + Int32 dataOffset = ins.ReadInt32(); + ins.Skip(4); // No idea + Int32 nameOffset = ins.ReadInt32(); + ins.Skip(4); // No idea + + anim.FrameCount = ins.ReadUInt16(); + UInt16 nodeCount = ins.ReadUInt16(); + + anim.MatrixMode = (Model.TexMatrixModeType)ins.ReadUInt32(); + anim.Loop = (ins.ReadUInt32() > 0); + + ins.Seek(startPos + dataOffset); + anim.Nodes = ReadAndConvertDict(ins, ConvertAnmTexSRTNode); + return anim; } } + private TextureSRTAnim.Node ConvertAnmTexSRTNode(string name, InputStream ins) { + using (var c = Debug.Push(name)) { + var node = new TextureSRTAnim.Node(); + + int startPos = ins.Position; + OffsetMap.Add(startPos, "AnmTexSRT Node: " + name); + + UInt32 nameOffset = ins.ReadUInt32(); + UInt32 flags = ins.ReadUInt32(); + UInt32 indFlags = ins.ReadUInt32(); + Debug.Send("Flags: {0:X}, {1:X}", flags, indFlags); + + node.Textures = new TextureSRTAnim.Element[8]; + node.IndirectTextures = new TextureSRTAnim.Element[3]; + + for (int i = 0; i < 8; i++) { + Debug.Send("Texture {0}", i); + if ((flags & 1) == 0) + node.Textures[i].Exists = false; + else + node.Textures[i] = ReadAnmTexSRTElement(ins, startPos); + flags >>= 1; + } + + for (int i = 0; i < 3; i++) { + Debug.Send("Indirect Texture {0}", i); + if ((indFlags & 1) == 0) + node.IndirectTextures[i].Exists = false; + else + node.IndirectTextures[i] = ReadAnmTexSRTElement(ins, startPos); + indFlags >>= 1; + } + + return node; + } + } + + private TextureSRTAnim.Element ReadAnmTexSRTElement(InputStream ins, int startPos) { + var elem = new TextureSRTAnim.Element(); + + int savePos = ins.Position; + ins.Seek(startPos + ins.ReadInt32()); + + OffsetMap.Add(ins.Position, String.Format("Element")); + + Debug.Send("Offset: {0:X}", ins.Position); + UInt32 flags = ins.ReadUInt32(); + Debug.Send("Element Flags: {0:X}", flags); + + if ((flags & 2) != 0) { + Debug.Send("Scale 1"); + elem.ScaleS = new KeyframeAnim(1.0f); + elem.ScaleT = new KeyframeAnim(1.0f); + } else { + Debug.Send("Reading Scale S @ {0:X}", ins.Position); + elem.ScaleS = ReadKeyframeAnim(ins, (flags & 0x20) != 0); + if ((flags & 0x10) != 0) { + Debug.Send("Uniform Scale"); + elem.ScaleT = elem.ScaleS; + } else { + Debug.Send("Reading Scale T @ {0:X}", ins.Position); + elem.ScaleT = ReadKeyframeAnim(ins, (flags & 0x40) != 0); + } + } + + if ((flags & 4) != 0) { + Debug.Send("Rotate 0"); + elem.Rotate = new KeyframeAnim(0.0f); + } else { + Debug.Send("Reading Rotate @ {0:X}", ins.Position); + elem.Rotate = ReadKeyframeAnim(ins, (flags & 0x80) != 0); + } + + if ((flags & 8) != 0) { + Debug.Send("Trans 0"); + elem.TransS = new KeyframeAnim(0.0f); + elem.TransT = new KeyframeAnim(0.0f); + } else { + Debug.Send("Reading Trans S @ {0:X}", ins.Position); + elem.TransS = ReadKeyframeAnim(ins, (flags & 0x100) != 0); + Debug.Send("Reading Trans T @ {0:X}", ins.Position); + elem.TransT = ReadKeyframeAnim(ins, (flags & 0x200) != 0); + } + + ins.Seek(savePos + 4); + + return elem; + } + + + private KeyframeAnim ReadKeyframeAnim(InputStream ins, bool isConst) { + var output = new KeyframeAnim(); + + output.IsConstant = isConst; + if (isConst) { + output.ConstValue = ins.ReadFloat(); + } else { + int basePos = ins.Position; + int offset = ins.ReadInt32(); + ins.Seek(basePos + offset); + + UInt16 kfCount = ins.ReadUInt16(); + ins.Skip(2); + float invKeyFrameRange = ins.ReadFloat(); + + output.Keyframes = new Keyframe[kfCount]; + for (int i = 0; i < kfCount; i++) { + output.Keyframes[i].Frame = ins.ReadFloat(); + output.Keyframes[i].Value = ins.ReadFloat(); + output.Keyframes[i].Slope = ins.ReadFloat(); + } + + ins.Seek(basePos + 4); + } + + return output; + } private Model ConvertModelResource(string name, InputStream ins) { -- cgit v1.2.3