summaryrefslogtreecommitdiff
path: root/NW4RTools
diff options
context:
space:
mode:
Diffstat (limited to 'NW4RTools')
-rwxr-xr-x[-rw-r--r--]NW4RTools/BrresReader.cs33
-rwxr-xr-x[-rw-r--r--]NW4RTools/BrresWriter.cs967
-rw-r--r--NW4RTools/Misc.cs40
-rw-r--r--NW4RTools/Models/Animation/CharacterAnim.cs2
-rw-r--r--NW4RTools/Models/Animation/Shared.cs40
-rwxr-xr-x[-rw-r--r--]NW4RTools/Models/ByteCode.cs3
-rw-r--r--NW4RTools/NW4RTools.csproj183
-rw-r--r--NW4RTools/Texture.cs15
-rw-r--r--NW4RTools/Types.cs10
9 files changed, 1174 insertions, 119 deletions
diff --git a/NW4RTools/BrresReader.cs b/NW4RTools/BrresReader.cs
index f120943..9616b5b 100644..100755
--- 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
index 04750de..033007e 100644..100755
--- 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<Model, int> ModelOffsets;
private Dictionary<ByteCode, int> BytecodeOffsets;
private Dictionary<Node, int> 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<Shader> UniqueShaders;
+ public List<Shader> UniqueShaders;
+ public Dictionary<Material, int> MaterialSizes;
+
+ public ModelCalcInfo() {
+ UniqueShaders = new List<Shader>();
+ MaterialSizes = new Dictionary<Material,int>();
+ }
}
private Dictionary<Model, ModelCalcInfo> ModelCalcInfos;
+ #endregion
+
+ #region Character Animation Data Storage
+ private class ChrAnmNodeInfo {
+ public CharacterAnim.Flags Flags;
+ }
+
+ private class ChrAnmInfo {
+ public KeyframeWriterInfo KFInfo;
+ public Dictionary<CharacterAnim.Node, ChrAnmNodeInfo> NodeInfo;
+ public Dictionary<CharacterAnim.Node, int> NodeOffsets;
+ public int BlockSize;
+
+ public ChrAnmInfo() {
+ KFInfo = new KeyframeWriterInfo();
+ NodeInfo = new Dictionary<CharacterAnim.Node, ChrAnmNodeInfo>();
+ NodeOffsets = new Dictionary<CharacterAnim.Node, int>();
+ }
+ }
+ private Dictionary<CharacterAnim, ChrAnmInfo> ChrAnmInfos;
+ private Dictionary<CharacterAnim, int> ChrAnmOffsets;
+ #endregion
+
+ #region Color Animation Data Storage
+ private class ClrAnmInfo {
+ public Dictionary<ColorAnim.Node, int> NodeOffsets;
+ public int BlockSize;
+
+ public List<Color[]> UsedArrays;
+ public List<int> UsedArrayPositions;
+
+ public ClrAnmInfo() {
+ NodeOffsets = new Dictionary<ColorAnim.Node, int>();
+ UsedArrays = new List<Color[]>();
+ UsedArrayPositions = new List<int>();
+ }
+ }
+ private Dictionary<ColorAnim, ClrAnmInfo> ClrAnmInfos;
+ private Dictionary<ColorAnim, int> 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<TextureSRTAnim.Node, TexSRTAnmNodeInfo> NodeInfo;
+ public Dictionary<TextureSRTAnim.Node, int> NodeOffsets;
+ public int BlockSize;
+
+ public TexSRTAnmInfo() {
+ KFInfo = new KeyframeWriterInfo();
+ NodeInfo = new Dictionary<TextureSRTAnim.Node,TexSRTAnmNodeInfo>();
+ NodeOffsets = new Dictionary<TextureSRTAnim.Node, int>();
+ }
+ }
+ private Dictionary<TextureSRTAnim, TexSRTAnmInfo> TexSRTAnmInfos;
+ private Dictionary<TextureSRTAnim, int> TexSRTAnmOffsets;
+ #endregion
+
+ private class KeyframeWriterInfo {
+ public Dictionary<Pair<object, KeyframeAnim>, int> KeyframeAnimOffsets;
+ public List<Pair<object, KeyframeAnim>> KeyframeAnims;
+
+ public KeyframeWriterInfo() {
+ KeyframeAnimOffsets = new Dictionary<Pair<object, KeyframeAnim>, int>();
+ KeyframeAnims = new List<Pair<object, KeyframeAnim>>();
+ }
+
+ public void Add(KeyframeAnim anim) {
+ Add(new Pair<object, KeyframeAnim>(null, anim));
+ }
+
+ public void Add(object obj, KeyframeAnim anim) {
+ Add(new Pair<object, KeyframeAnim>(obj, anim));
+ }
+
+ public void Add(Pair<object, KeyframeAnim> 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<object, KeyframeAnim>(null, anim)];
+ }
+
+ public int GetPosition(object obj, KeyframeAnim anim) {
+ return KeyframeAnimOffsets[new Pair<object, KeyframeAnim>(obj, anim)];
+ }
+ }
private Dictionary<Texture, int> TextureOffsets;
private Dictionary<Texture, int> 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<Shader>();
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<CharacterAnim, int>();
+ ChrAnmInfos = new Dictionary<CharacterAnim, ChrAnmInfo>();
+
+ var chrAnmDict = File.GetGroup<CharacterAnim>("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<ColorAnim, int>();
+ ClrAnmInfos = new Dictionary<ColorAnim, ClrAnmInfo>();
+
+ var clrAnmDict = File.GetGroup<ColorAnim>("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<TextureSRTAnim, int>();
+ TexSRTAnmInfos = new Dictionary<TextureSRTAnim, TexSRTAnmInfo>();
+
+ var chrAnmDict = File.GetGroup<TextureSRTAnim>("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<Texture>(kv.Value as ResDict<Texture>, TextureOffsets);
break;
+ case "AnmChr(NW4R)":
+ WriteResDict<CharacterAnim>(kv.Value as ResDict<CharacterAnim>, ChrAnmOffsets);
+ break;
+ case "AnmClr(NW4R)":
+ WriteResDict<ColorAnim>(kv.Value as ResDict<ColorAnim>, ClrAnmOffsets);
+ break;
+ case "AnmTexSrt(NW4R)":
+ WriteResDict<TextureSRTAnim>(kv.Value as ResDict<TextureSRTAnim>, 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<CharacterAnim>("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<CharacterAnim.Node>(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<ColorAnim>("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<ColorAnim.Node>(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<TextureSRTAnim>("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<TextureSRTAnim.Node>(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<TextureSRTAnim.Element>();
+
+ 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>(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>(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
index 6df6dee..62968e9 100644..100755
--- 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 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProductVersion>9.0.21022</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{A9C9FABD-0A5F-4DAB-979D-9F288F96866F}</ProjectGuid>
- <OutputType>Library</OutputType>
- <RootNamespace>NW4RTools</RootNamespace>
- <AssemblyName>NW4RTools</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>bin\Debug</OutputPath>
- <DefineConstants>DEBUG</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <ConsolePause>false</ConsolePause>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>none</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>bin\Release</OutputPath>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <ConsolePause>false</ConsolePause>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="System" />
- <Reference Include="System.Xml" />
- <Reference Include="System.Drawing" />
- <Reference Include="System.Core" />
- <Reference Include="OpenTK">
- <HintPath>..\ExternalLibs\OpenTK.dll</HintPath>
- </Reference>
- </ItemGroup>
- <ItemGroup>
- <Compile Include="AssemblyInfo.cs" />
- <Compile Include="Logger.cs" />
- <Compile Include="InputStream.cs" />
- <Compile Include="Types.cs" />
- <Compile Include="ResDict.cs" />
- <Compile Include="BrresReader.cs" />
- <Compile Include="ResFile.cs" />
- <Compile Include="Models\Model.cs" />
- <Compile Include="Models\ByteCode.cs" />
- <Compile Include="Models\Node.cs" />
- <Compile Include="Util\IOrderedDictionary.cs" />
- <Compile Include="Util\OrderedDictionary.cs" />
- <Compile Include="Models\VertexData.cs" />
- <Compile Include="Models\Material.cs" />
- <Compile Include="Models\Shader.cs" />
- <Compile Include="Models\Shape.cs" />
- <Compile Include="Models\TextureInfo.cs" />
- <Compile Include="Enums.cs" />
- <Compile Include="DisplayList.cs" />
- <Compile Include="VertexSettings.cs" />
- <Compile Include="Util\collada_schema_1_4.cs" />
- <Compile Include="Texture.cs" />
- <Compile Include="Misc.cs" />
- <Compile Include="Models\OpenGL\GLModel.cs" />
- <Compile Include="Models\OpenGL\GLTexture.cs" />
- <Compile Include="Models\OpenGL\GLDisplayList.cs" />
- <Compile Include="BrresWriter.cs" />
- <Compile Include="OutputStream.cs" />
- <Compile Include="Models\Animation\ColorAnim.cs" />
- <Compile Include="Models\Animation\CharacterAnim.cs" />
- <Compile Include="Models\Animation\TextureSRTAnim.cs" />
- <Compile Include="Util\NVDXT.cs" />
- <Compile Include="ColladaExporter.cs" />
- <Compile Include="ObjExporter.cs" />
- <Compile Include="ObjImporter.cs" />
- <Compile Include="DisplayListWriter.cs" />
- <Compile Include="Models\Animation\Shared.cs" />
- </ItemGroup>
- <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
- <ItemGroup>
- <Folder Include="Models\" />
- <Folder Include="Util\" />
- <Folder Include="Models\OpenGL\" />
- <Folder Include="Models\Animation\" />
- </ItemGroup>
- <ItemGroup>
- <None Include="OpenTK.dll.config">
- <CopyToOutputDirectory>Always</CopyToOutputDirectory>
- </None>
- </ItemGroup>
-</Project>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{A9C9FABD-0A5F-4DAB-979D-9F288F96866F}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>NW4RTools</RootNamespace>
+ <AssemblyName>NW4RTools</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG DEV_DONT_MODIFY_TEXTURE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>none</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <DefineConstants>DEV_DONT_MODIFY_TEXTURE</DefineConstants>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Core" />
+ <Reference Include="OpenTK">
+ <HintPath>..\ExternalLibs\OpenTK.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AssemblyInfo.cs" />
+ <Compile Include="Logger.cs" />
+ <Compile Include="InputStream.cs" />
+ <Compile Include="Types.cs" />
+ <Compile Include="ResDict.cs" />
+ <Compile Include="BrresReader.cs" />
+ <Compile Include="ResFile.cs" />
+ <Compile Include="Models\Model.cs" />
+ <Compile Include="Models\ByteCode.cs" />
+ <Compile Include="Models\Node.cs" />
+ <Compile Include="Util\IOrderedDictionary.cs" />
+ <Compile Include="Util\OrderedDictionary.cs" />
+ <Compile Include="Models\VertexData.cs" />
+ <Compile Include="Models\Material.cs" />
+ <Compile Include="Models\Shader.cs" />
+ <Compile Include="Models\Shape.cs" />
+ <Compile Include="Models\TextureInfo.cs" />
+ <Compile Include="Enums.cs" />
+ <Compile Include="DisplayList.cs" />
+ <Compile Include="VertexSettings.cs" />
+ <Compile Include="Util\collada_schema_1_4.cs" />
+ <Compile Include="Texture.cs" />
+ <Compile Include="Misc.cs" />
+ <Compile Include="Models\OpenGL\GLModel.cs" />
+ <Compile Include="Models\OpenGL\GLTexture.cs" />
+ <Compile Include="Models\OpenGL\GLDisplayList.cs" />
+ <Compile Include="BrresWriter.cs" />
+ <Compile Include="OutputStream.cs" />
+ <Compile Include="Models\Animation\ColorAnim.cs" />
+ <Compile Include="Models\Animation\CharacterAnim.cs" />
+ <Compile Include="Models\Animation\TextureSRTAnim.cs" />
+ <Compile Include="Util\NVDXT.cs" />
+ <Compile Include="ColladaExporter.cs" />
+ <Compile Include="ObjExporter.cs" />
+ <Compile Include="ObjImporter.cs" />
+ <Compile Include="DisplayListWriter.cs" />
+ <Compile Include="Models\Animation\Shared.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <None Include="OpenTK.dll.config">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+</Project> \ 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<TOne, TTwo> {
+ public TOne one;
+ public TTwo two;
+
+ public Pair(TOne _one, TTwo _two) {
+ one = _one;
+ two = _two;
+ }
+ }
+
public enum ByteEndian {
LittleEndian,
BigEndian