diff options
Diffstat (limited to '')
-rwxr-xr-x[-rw-r--r--] | NW4RTools/BrresWriter.cs | 967 |
1 files changed, 957 insertions, 10 deletions
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; |