summaryrefslogtreecommitdiff
path: root/NW4RTools/BrresReader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'NW4RTools/BrresReader.cs')
-rw-r--r--NW4RTools/BrresReader.cs488
1 files changed, 481 insertions, 7 deletions
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<Models.Animation.TextureSRTAnim>(ins, ConvertAnmTexSRTResource));
break;
+ case "External":
+ File.Add(name, ReadAndConvertDict<byte[]>(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<Models.Animation.CharacterAnim.Node>(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<ColorAnim.Node>(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<TextureSRTAnim.Node>(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) {