summaryrefslogtreecommitdiff
path: root/NW4RTools/BrresWriter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'NW4RTools/BrresWriter.cs')
-rwxr-xr-xNW4RTools/BrresWriter.cs548
1 files changed, 288 insertions, 260 deletions
diff --git a/NW4RTools/BrresWriter.cs b/NW4RTools/BrresWriter.cs
index 033007e..0b81a65 100755
--- a/NW4RTools/BrresWriter.cs
+++ b/NW4RTools/BrresWriter.cs
@@ -80,12 +80,12 @@ 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 Dictionary<Material, int> MaterialSizes;
-
- public ModelCalcInfo() {
- UniqueShaders = new List<Shader>();
- MaterialSizes = new Dictionary<Material,int>();
+ 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;
@@ -133,11 +133,11 @@ namespace NW4RTools {
#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];
+ public int[] IndirectTextureOffsets;
+
+ public TexSRTAnmNodeInfo() {
+ TextureOffsets = new int[8];
+ IndirectTextureOffsets = new int[3];
}
}
@@ -478,8 +478,8 @@ namespace NW4RTools {
MaterialDLOffsets[kv.Value] = CurrentPos;
CurrentPos += 0x20 + 0x80 + 0x40 + 0xA0;
- // Sized!
- int size = CurrentPos - MaterialOffsets[kv.Value];
+ // Sized!
+ int size = CurrentPos - MaterialOffsets[kv.Value];
ModelCalcInfos[m].MaterialSizes[kv.Value] = size;
}
}
@@ -830,14 +830,14 @@ namespace NW4RTools {
case CharacterAnim.RotateFormatType.Data96:
CurrentPos += 8 + (kf.Keyframes.Length * 12);
break;
- case CharacterAnim.RotateFormatType.DataUnk4:
+ case CharacterAnim.RotateFormatType.DataFrm8:
CurrentPos += 8 + (kf.Values.Length);
break;
- case CharacterAnim.RotateFormatType.DataUnk5:
+ case CharacterAnim.RotateFormatType.DataFrm16:
CurrentPos += 8 + (kf.Values.Length * 2);
break;
- case CharacterAnim.RotateFormatType.DataUnk6:
- CurrentPos += 4 + (kf.FloatValues.Length * 4);
+ case CharacterAnim.RotateFormatType.DataFrm32:
+ CurrentPos += (kf.FloatValues.Length * 4);
break;
default:
throw new NotImplementedException();
@@ -942,8 +942,8 @@ namespace NW4RTools {
CurrentPos += GetSizeForResDict(anim.Nodes.Count);
var anminfo = new TexSRTAnmInfo();
- TexSRTAnmInfos.Add(anim, anminfo);
-
+ TexSRTAnmInfos.Add(anim, anminfo);
+
// Now size each node
foreach (var nodepair in anim.Nodes) {
AddString(nodepair.Key);
@@ -954,29 +954,29 @@ namespace NW4RTools {
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]);
- }
+ 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]);
+ }
}
}
@@ -984,51 +984,51 @@ namespace NW4RTools {
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 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;
@@ -1277,7 +1277,7 @@ namespace NW4RTools {
var op4 = insn as ByteCode.DrawShapeInstruction;
Output.WriteUInt16(op4.MaterialID);
Output.WriteUInt16(op4.ShapeID);
- Output.WriteUInt16(op4.NodeID);
+ Output.WriteUInt16(op4.NodeID);
Output.WriteByte(op4.Unk);
break;
@@ -1346,7 +1346,7 @@ namespace NW4RTools {
Material mat = kv.Value;
int startPos = Output.Position;
- // size, model offset, name offset, index
+ // size, model offset, name offset, index
var modelInfo = ModelCalcInfos[m];
Output.WriteUInt32((UInt32)modelInfo.MaterialSizes[mat]);
Output.WriteInt32(ModelOffsets[m] - startPos);
@@ -1907,6 +1907,19 @@ namespace NW4RTools {
Output.WriteFloat(invKeyFrameRange);
}
+ if (format == CharacterAnim.RotateFormatType.DataFrm8 ||
+ format == CharacterAnim.RotateFormatType.DataFrm16) {
+ // Header for CV 8 and 16
+ Output.WriteFloat(kf.Multiplier);
+ Output.WriteFloat(kf.BaseValue);
+ Misc.Assert(kf.Values.Length == (anm.FrameCount + 1));
+ }
+
+ if (format == CharacterAnim.RotateFormatType.DataFrm32) {
+ // Assert this
+ Misc.Assert(kf.FloatValues.Length == (anm.FrameCount + 1));
+ }
+
switch (format) {
case CharacterAnim.RotateFormatType.Data32:
for (int i = 0; i < kf.Keyframes.Length; i++) {
@@ -1930,8 +1943,23 @@ namespace NW4RTools {
Output.WriteFloat(kf.Keyframes[i].Slope);
}
break;
+ case CharacterAnim.RotateFormatType.DataFrm8:
+ for (int i = 0; i < (anm.FrameCount + 1); i++) {
+ Output.WriteByte((byte)kf.Values[i]);
+ }
+ break;
+ case CharacterAnim.RotateFormatType.DataFrm16:
+ for (int i = 0; i < (anm.FrameCount + 1); i++) {
+ Output.WriteUInt16((ushort)kf.Values[i]);
+ }
+ break;
+ case CharacterAnim.RotateFormatType.DataFrm32:
+ for (int i = 0; i < (anm.FrameCount + 1); i++) {
+ Output.WriteFloat(kf.FloatValues[i]);
+ }
+ break;
default:
- throw new NotImplementedException();
+ throw new NotImplementedException(String.Format("Unknown format: {0}", format));
}
Output.AlignTo(4);
@@ -1999,7 +2027,7 @@ namespace NW4RTools {
Output.WriteUInt32(flags);
for (int i = 0; i < node.Elements.Length; i++) {
- if (node.Elements[i].Exists) {
+ if (node.Elements[i].Exists) {
Output.WriteUInt32(node.Elements[i].Mask);
if (node.Elements[i].IsConstant) {
Output.WriteColor(node.Elements[i].ConstValue);
@@ -2041,176 +2069,176 @@ namespace NW4RTools {
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
+ #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;