using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; namespace AssetStudio { public class Keyframe : IYAMLExportable where T : IYAMLExportable { public float time; public T value; public T inSlope; public T outSlope; public int weightedMode; public T inWeight; public T outWeight; public Keyframe(float time, T value, T inSlope, T outSlope, T weight) { this.time = time; this.value = value; this.inSlope = inSlope; this.outSlope = outSlope; weightedMode = 0; inWeight = weight; outWeight = weight; } public Keyframe(ObjectReader reader, Func readerFunc) { time = reader.ReadSingle(); value = readerFunc(); inSlope = readerFunc(); outSlope = readerFunc(); if (reader.version[0] >= 2018) //2018 and up { weightedMode = reader.ReadInt32(); inWeight = readerFunc(); outWeight = readerFunc(); } } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.AddSerializedVersion(ToSerializedVersion(version)); node.Add(nameof(time), time); node.Add(nameof(value), value.ExportYAML(version)); node.Add(nameof(inSlope), inSlope.ExportYAML(version)); node.Add(nameof(outSlope), outSlope.ExportYAML(version)); if (version[0] >= 2018) //2018 and up { node.Add(nameof(weightedMode), weightedMode); node.Add(nameof(inWeight), inWeight.ExportYAML(version)); node.Add(nameof(outWeight), outWeight.ExportYAML(version)); } return node; } private int ToSerializedVersion(int[] version) { if (version[0] >= 2018) //2018 and up { return 3; } else if (version[0] > 5 || (version[0] == 5 && version[1] >= 5)) { return 2; } return 1; } } public class AnimationCurve : IYAMLExportable where T : IYAMLExportable { public List> m_Curve; public int m_PreInfinity; public int m_PostInfinity; public int m_RotationOrder; public AnimationCurve() { m_PreInfinity = 2; m_PostInfinity = 2; m_RotationOrder = 4; m_Curve = new List>(); } public AnimationCurve(ObjectReader reader, Func readerFunc) { var version = reader.version; int numCurves = reader.ReadInt32(); m_Curve = new List>(); for (int i = 0; i < numCurves; i++) { m_Curve.Add(new Keyframe(reader, readerFunc)); } m_PreInfinity = reader.ReadInt32(); m_PostInfinity = reader.ReadInt32(); if (version[0] > 5 || (version[0] == 5 && version[1] >= 3))//5.3 and up { m_RotationOrder = reader.ReadInt32(); } } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.AddSerializedVersion(ToSerializedVersion(version)); node.Add(nameof(m_Curve), m_Curve.ExportYAML(version)); node.Add(nameof(m_PreInfinity), m_PreInfinity); node.Add(nameof(m_PostInfinity), m_PostInfinity); if (version[0] > 5 || (version[0] == 5 && version[1] >= 3))//5.3 and up { node.Add(nameof(m_RotationOrder), m_RotationOrder); } return node; } private int ToSerializedVersion(int[] version) { if (version[0] > 2 || (version[0] == 2 && version[1] >= 1)) { return 2; } return 1; } } public class QuaternionCurve : IYAMLExportable { public AnimationCurve curve; public string path; public QuaternionCurve(string path) { curve = new AnimationCurve(); this.path = path; } public QuaternionCurve(ObjectReader reader) { curve = new AnimationCurve(reader, reader.ReadQuaternion); path = reader.ReadAlignedString(); } public YAMLNode ExportYAML(int[] version) { YAMLMappingNode node = new YAMLMappingNode(); node.Add(nameof(curve), curve.ExportYAML(version)); node.Add(nameof(path), path); return node; } public override bool Equals(object obj) { if (obj is QuaternionCurve quaternionCurve) { return path == quaternionCurve.path; } return false; } public override int GetHashCode() { int hash = 199; unchecked { hash = 617 + hash * path.GetHashCode(); } return hash; } } public class PackedFloatVector : IYAMLExportable { public uint m_NumItems; public float m_Range; public float m_Start; public byte[] m_Data; public byte m_BitSize; public PackedFloatVector(ObjectReader reader) { m_NumItems = reader.ReadUInt32(); m_Range = reader.ReadSingle(); m_Start = reader.ReadSingle(); int numData = reader.ReadInt32(); m_Data = reader.ReadBytes(numData); reader.AlignStream(); m_BitSize = reader.ReadByte(); reader.AlignStream(); } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.Add(nameof(m_NumItems), m_NumItems); node.Add(nameof(m_Range), m_Range); node.Add(nameof(m_Start), m_Start); node.Add(nameof(m_Data), m_Data.ExportYAML()); node.Add(nameof(m_BitSize), m_BitSize); return node; } public float[] UnpackFloats(int itemCountInChunk, int chunkStride, int start = 0, int numChunks = -1) { int bitPos = m_BitSize * start; int indexPos = bitPos / 8; bitPos %= 8; float scale = 1.0f / m_Range; if (numChunks == -1) numChunks = (int)m_NumItems / itemCountInChunk; var end = chunkStride * numChunks / 4; var data = new List(); for (var index = 0; index != end; index += chunkStride / 4) { for (int i = 0; i < itemCountInChunk; ++i) { uint x = 0; int bits = 0; while (bits < m_BitSize) { x |= (uint)((m_Data[indexPos] >> bitPos) << bits); int num = Math.Min(m_BitSize - bits, 8 - bitPos); bitPos += num; bits += num; if (bitPos == 8) { indexPos++; bitPos = 0; } } x &= (uint)(1 << m_BitSize) - 1u; data.Add(x / (scale * ((1 << m_BitSize) - 1)) + m_Start); } } return data.ToArray(); } } public class PackedIntVector : IYAMLExportable { public uint m_NumItems; public byte[] m_Data; public byte m_BitSize; public PackedIntVector(ObjectReader reader) { m_NumItems = reader.ReadUInt32(); int numData = reader.ReadInt32(); m_Data = reader.ReadBytes(numData); reader.AlignStream(); m_BitSize = reader.ReadByte(); reader.AlignStream(); } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.Add(nameof(m_NumItems), m_NumItems); node.Add(nameof(m_Data), m_Data.ExportYAML()); node.Add(nameof(m_BitSize), m_BitSize); return node; } public int[] UnpackInts() { var data = new int[m_NumItems]; int indexPos = 0; int bitPos = 0; for (int i = 0; i < m_NumItems; i++) { int bits = 0; data[i] = 0; while (bits < m_BitSize) { data[i] |= (m_Data[indexPos] >> bitPos) << bits; int num = Math.Min(m_BitSize - bits, 8 - bitPos); bitPos += num; bits += num; if (bitPos == 8) { indexPos++; bitPos = 0; } } data[i] &= (1 << m_BitSize) - 1; } return data; } } public class PackedQuatVector : IYAMLExportable { public uint m_NumItems; public byte[] m_Data; public PackedQuatVector(ObjectReader reader) { m_NumItems = reader.ReadUInt32(); int numData = reader.ReadInt32(); m_Data = reader.ReadBytes(numData); reader.AlignStream(); } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.Add(nameof(m_NumItems), m_NumItems); node.Add(nameof(m_Data), m_Data.ExportYAML()); return node; } public Quaternion[] UnpackQuats() { var data = new Quaternion[m_NumItems]; int indexPos = 0; int bitPos = 0; for (int i = 0; i < m_NumItems; i++) { uint flags = 0; int bits = 0; while (bits < 3) { flags |= (uint)((m_Data[indexPos] >> bitPos) << bits); int num = Math.Min(3 - bits, 8 - bitPos); bitPos += num; bits += num; if (bitPos == 8) { indexPos++; bitPos = 0; } } flags &= 7; var q = new Quaternion(); float sum = 0; for (int j = 0; j < 4; j++) { if ((flags & 3) != j) { int bitSize = ((flags & 3) + 1) % 4 == j ? 9 : 10; uint x = 0; bits = 0; while (bits < bitSize) { x |= (uint)((m_Data[indexPos] >> bitPos) << bits); int num = Math.Min(bitSize - bits, 8 - bitPos); bitPos += num; bits += num; if (bitPos == 8) { indexPos++; bitPos = 0; } } x &= (uint)((1 << bitSize) - 1); q[j] = x / (0.5f * ((1 << bitSize) - 1)) - 1; sum += q[j] * q[j]; } } int lastComponent = (int)(flags & 3); q[lastComponent] = (float)Math.Sqrt(1 - sum); if ((flags & 4) != 0u) q[lastComponent] = -q[lastComponent]; data[i] = q; } return data; } } public class CompressedAnimationCurve : IYAMLExportable { public string m_Path; public PackedIntVector m_Times; public PackedQuatVector m_Values; public PackedFloatVector m_Slopes; public int m_PreInfinity; public int m_PostInfinity; public CompressedAnimationCurve(ObjectReader reader) { m_Path = reader.ReadAlignedString(); m_Times = new PackedIntVector(reader); m_Values = new PackedQuatVector(reader); m_Slopes = new PackedFloatVector(reader); m_PreInfinity = reader.ReadInt32(); m_PostInfinity = reader.ReadInt32(); } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.Add(nameof(m_Path), m_Path); node.Add(nameof(m_Times), m_Times.ExportYAML(version)); node.Add(nameof(m_Values), m_Values.ExportYAML(version)); node.Add(nameof(m_Slopes), m_Slopes.ExportYAML(version)); node.Add(nameof(m_PreInfinity), m_PreInfinity); node.Add(nameof(m_PostInfinity), m_PostInfinity); return node; } } public class Vector3Curve : IYAMLExportable { public AnimationCurve curve; public string path; public Vector3Curve(string path) { curve = new AnimationCurve(); this.path = path; } public Vector3Curve(ObjectReader reader) { curve = new AnimationCurve(reader, reader.ReadVector3); path = reader.ReadAlignedString(); } public YAMLNode ExportYAML(int[] version) { YAMLMappingNode node = new YAMLMappingNode(); node.Add(nameof(curve), curve.ExportYAML(version)); node.Add(nameof(path), path); return node; } public override bool Equals(object obj) { if (obj is Vector3Curve vector3Curve) { return path == vector3Curve.path; } return false; } public override int GetHashCode() { int hash = 577; unchecked { hash = 419 + hash * path.GetHashCode(); } return hash; } } public class FloatCurve : IYAMLExportable { public AnimationCurve curve; public string attribute; public string path; public ClassIDType classID; public PPtr script; public FloatCurve(string path, string attribute, ClassIDType classID, PPtr script) { curve = new AnimationCurve(); this.attribute = attribute; this.path = path; this.classID = classID; this.script = script; } public FloatCurve(ObjectReader reader) { curve = new AnimationCurve(reader, reader.ReadFloat); attribute = reader.ReadAlignedString(); path = reader.ReadAlignedString(); classID = (ClassIDType)reader.ReadInt32(); script = new PPtr(reader); } public YAMLNode ExportYAML(int[] version) { YAMLMappingNode node = new YAMLMappingNode(); node.Add(nameof(curve), curve.ExportYAML(version)); node.Add(nameof(attribute), attribute); node.Add(nameof(path), path); node.Add(nameof(classID), (int)classID); if (version[0] >= 2) { node.Add(nameof(script), script.ExportYAML(version)); } return node; } public override bool Equals(object obj) { if (obj is FloatCurve floatCurve) { return attribute == floatCurve.attribute && path == floatCurve.path && classID == floatCurve.classID; } return false; } public override int GetHashCode() { int hash = 17; unchecked { hash = hash * 23 + path.GetHashCode(); } return hash; } } public class PPtrKeyframe : IYAMLExportable { public float time; public PPtr value; public PPtrKeyframe(float time, PPtr value) { this.time = time; this.value = value; } public PPtrKeyframe(ObjectReader reader) { time = reader.ReadSingle(); value = new PPtr(reader); } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.Add(nameof(time), time); node.Add(nameof(value), value.ExportYAML(version)); return node; } } public class PPtrCurve : IYAMLExportable { public List curve; public string attribute; public string path; public int classID; public PPtr script; public PPtrCurve(string path, string attribute, ClassIDType classID, PPtr script) { curve = new List(); this.attribute = attribute; this.path = path; this.classID = (int)classID; this.script = script; } public PPtrCurve(ObjectReader reader) { int numCurves = reader.ReadInt32(); curve = new List(); for (int i = 0; i < numCurves; i++) { curve.Add(new PPtrKeyframe(reader)); } attribute = reader.ReadAlignedString(); path = reader.ReadAlignedString(); classID = reader.ReadInt32(); script = new PPtr(reader); } public YAMLNode ExportYAML(int[] version) { YAMLMappingNode node = new YAMLMappingNode(); node.Add(nameof(curve), curve.ExportYAML(version)); node.Add(nameof(attribute), attribute); node.Add(nameof(path), path); node.Add(nameof(classID), ((int)classID).ToString()); node.Add(nameof(script), script.ExportYAML(version)); return node; } public override bool Equals(object obj) { if (obj is PPtrCurve pptrCurve) { return this == pptrCurve; } return false; } public override int GetHashCode() { int hash = 113; unchecked { hash = hash + 457 * attribute.GetHashCode(); hash = hash * 433 + path.GetHashCode(); hash = hash * 223 + classID.GetHashCode(); hash = hash * 911 + script.GetHashCode(); } return hash; } } public class AABB : IYAMLExportable { public Vector3 m_Center; public Vector3 m_Extent; public AABB(ObjectReader reader) { m_Center = reader.ReadVector3(); m_Extent = reader.ReadVector3(); } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.Add(nameof(m_Center), m_Center.ExportYAML(version)); node.Add(nameof(m_Extent), m_Extent.ExportYAML(version)); return node; } } public class HandPose { public XForm m_GrabX; public float[] m_DoFArray; public float m_Override; public float m_CloseOpen; public float m_InOut; public float m_Grab; public HandPose() { } public HandPose(ObjectReader reader) { m_GrabX = reader.ReadXForm(); m_DoFArray = reader.ReadSingleArray(); m_Override = reader.ReadSingle(); m_CloseOpen = reader.ReadSingle(); m_InOut = reader.ReadSingle(); m_Grab = reader.ReadSingle(); } public static HandPose ParseGI(ObjectReader reader) { var handPose = new HandPose(); handPose.m_GrabX = reader.ReadXForm4(); handPose.m_DoFArray = reader.ReadSingleArray(20); handPose.m_Override = reader.ReadSingle(); handPose.m_CloseOpen = reader.ReadSingle(); handPose.m_InOut = reader.ReadSingle(); handPose.m_Grab = reader.ReadSingle(); return handPose; } } public class HumanGoal { public XForm m_X; public float m_WeightT; public float m_WeightR; public Vector3 m_HintT; public float m_HintWeightT; public HumanGoal() { } public HumanGoal(ObjectReader reader) { var version = reader.version; m_X = reader.ReadXForm(); m_WeightT = reader.ReadSingle(); m_WeightR = reader.ReadSingle(); if (version[0] >= 5)//5.0 and up { m_HintT = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up m_HintWeightT = reader.ReadSingle(); } } public static HumanGoal ParseGI(ObjectReader reader) { var humanGoal = new HumanGoal(); humanGoal.m_X = reader.ReadXForm4(); humanGoal.m_WeightT = reader.ReadSingle(); humanGoal.m_WeightR = reader.ReadSingle(); humanGoal.m_HintT = (Vector3)reader.ReadVector4(); humanGoal.m_HintWeightT = reader.ReadSingle(); var m_HintR = (Vector3)reader.ReadVector4(); var m_HintWeightR = reader.ReadSingle(); return humanGoal; } } public class HumanPose { public XForm m_RootX; public Vector3 m_LookAtPosition; public Vector4 m_LookAtWeight; public List m_GoalArray; public HandPose m_LeftHandPose; public HandPose m_RightHandPose; public float[] m_DoFArray; public Vector3[] m_TDoFArray; public HumanPose() { } public HumanPose(ObjectReader reader) { var version = reader.version; m_RootX = reader.ReadXForm(); m_LookAtPosition = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up m_LookAtWeight = reader.ReadVector4(); int numGoals = reader.ReadInt32(); m_GoalArray = new List(); for (int i = 0; i < numGoals; i++) { m_GoalArray.Add(new HumanGoal(reader)); } m_LeftHandPose = new HandPose(reader); m_RightHandPose = new HandPose(reader); m_DoFArray = reader.ReadSingleArray(); if (version[0] > 5 || (version[0] == 5 && version[1] >= 2))//5.2 and up { m_TDoFArray = reader.ReadVector3Array(); } } public static HumanPose ParseGI(ObjectReader reader) { var version = reader.version; var humanPose = new HumanPose(); humanPose.m_RootX = reader.ReadXForm4(); humanPose.m_LookAtPosition = (Vector3)reader.ReadVector4(); humanPose.m_LookAtWeight = reader.ReadVector4(); humanPose.m_GoalArray = new List(); for (int i = 0; i < 4; i++) { humanPose.m_GoalArray.Add(HumanGoal.ParseGI(reader)); } humanPose.m_LeftHandPose = HandPose.ParseGI(reader); humanPose.m_RightHandPose = HandPose.ParseGI(reader); humanPose.m_DoFArray = reader.ReadSingleArray(0x37); humanPose.m_TDoFArray = reader.ReadVector4Array(0x15).Select(x => (Vector3)x).ToArray(); reader.Position += 4; return humanPose; } } public abstract class ACLClip { public virtual bool IsSet => false; public virtual uint CurveCount => 0; public abstract void Read(ObjectReader reader); } public class EmptyACLClip : ACLClip { public override void Read(ObjectReader reader) { } } public class MHYACLClip : ACLClip { public uint m_CurveCount; public uint m_ConstCurveCount; public byte[] m_ClipData; public override bool IsSet => !m_ClipData.IsNullOrEmpty(); public override uint CurveCount => m_CurveCount; public MHYACLClip() { m_CurveCount = 0; m_ConstCurveCount = 0; m_ClipData = Array.Empty(); } public override void Read(ObjectReader reader) { var byteCount = reader.ReadInt32(); if (reader.Game.Type.IsSRGroup()) { byteCount *= 4; } m_ClipData = reader.ReadBytes(byteCount); reader.AlignStream(); m_CurveCount = reader.ReadUInt32(); if (reader.Game.Type.IsSRGroup()) { m_ConstCurveCount = reader.ReadUInt32(); } } } public class GIACLClip : ACLClip { public uint m_CurveCount; public uint m_ConstCurveCount; public byte[] m_ClipData; public byte[] m_DatabaseData; public override bool IsSet => !m_ClipData.IsNullOrEmpty() && !m_DatabaseData.IsNullOrEmpty(); public override uint CurveCount => m_CurveCount; public GIACLClip() { m_CurveCount = 0; m_ConstCurveCount = 0; m_ClipData = Array.Empty(); m_DatabaseData = Array.Empty(); } public override void Read(ObjectReader reader) { var aclTracksCount = (int)reader.ReadUInt64(); var aclTracksOffset = reader.Position + reader.ReadInt64(); var aclTracksCurveCount = reader.ReadUInt32(); if (aclTracksOffset > reader.Length) { throw new IOException("Offset outside of range"); } var pos = reader.Position; reader.Position = aclTracksOffset; var tracksBytes = reader.ReadBytes(aclTracksCount); reader.AlignStream(); using var tracksMS = new MemoryStream(); tracksMS.Write(tracksBytes); tracksMS.AlignStream(); m_CurveCount = aclTracksCurveCount; m_ClipData = tracksMS.ToArray(); reader.Position = pos; var aclDatabaseCount = reader.ReadInt32(); var aclDatabaseOffset = reader.Position + reader.ReadInt64(); var aclDatabaseCurveCount = (uint)reader.ReadUInt64(); if (aclDatabaseOffset > reader.Length) { throw new IOException("Offset outside of range"); } pos = reader.Position; reader.Position = aclDatabaseOffset; var databaseBytes = reader.ReadBytes(aclDatabaseCount); reader.AlignStream(); using var databaseMS = new MemoryStream(); databaseMS.Write(databaseBytes); databaseMS.AlignStream(); m_ConstCurveCount = aclDatabaseCurveCount; m_DatabaseData = databaseMS.ToArray(); reader.Position = pos; } } public class StreamedClip { public uint[] data; public uint curveCount; public StreamedClip() { } public StreamedClip(ObjectReader reader) { data = reader.ReadUInt32Array(); curveCount = reader.ReadUInt32(); } public static StreamedClip ParseGI(ObjectReader reader) { var streamedClipCount = (int)reader.ReadUInt64(); var streamedClipOffset = reader.Position + reader.ReadInt64(); var streamedClipCurveCount = (uint)reader.ReadUInt64(); if (streamedClipOffset > reader.Length) { throw new IOException("Offset outside of range"); } var pos = reader.Position; reader.Position = streamedClipOffset; var streamedClip = new StreamedClip() { data = reader.ReadUInt32Array(streamedClipCount), curveCount = streamedClipCurveCount }; reader.Position = pos; return streamedClip; } public class StreamedCurveKey { public int index; public float[] coeff; public float value; public float outSlope; public float inSlope; public StreamedCurveKey(EndianBinaryReader reader) { index = reader.ReadInt32(); coeff = reader.ReadSingleArray(4); outSlope = coeff[2]; value = coeff[3]; } public float CalculateNextInSlope(float dx, StreamedCurveKey rhs) { //Stepped if (coeff[0] == 0f && coeff[1] == 0f && coeff[2] == 0f) { return float.PositiveInfinity; } dx = Math.Max(dx, 0.0001f); var dy = rhs.value - value; var length = 1.0f / (dx * dx); var d1 = outSlope * dx; var d2 = dy + dy + dy - d1 - d1 - coeff[1] / length; return d2 / dx; } } public class StreamedFrame { public float time; public List keyList; public StreamedFrame(EndianBinaryReader reader) { time = reader.ReadSingle(); int numKeys = reader.ReadInt32(); keyList = new List(); for (int i = 0; i < numKeys; i++) { keyList.Add(new StreamedCurveKey(reader)); } } } public List ReadData() { var frameList = new List(); var buffer = new byte[data.Length * 4]; Buffer.BlockCopy(data, 0, buffer, 0, buffer.Length); using (var reader = new EndianBinaryReader(new MemoryStream(buffer), EndianType.LittleEndian)) { while (reader.BaseStream.Position < reader.BaseStream.Length) { frameList.Add(new StreamedFrame(reader)); } } for (int frameIndex = 2; frameIndex < frameList.Count - 1; frameIndex++) { var frame = frameList[frameIndex]; foreach (var curveKey in frame.keyList) { for (int i = frameIndex - 1; i >= 0; i--) { var preFrame = frameList[i]; var preCurveKey = preFrame.keyList.FirstOrDefault(x => x.index == curveKey.index); if (preCurveKey != null) { curveKey.inSlope = preCurveKey.CalculateNextInSlope(frame.time - preFrame.time, curveKey); break; } } } } return frameList; } } public class DenseClip { public int m_FrameCount; public uint m_CurveCount; public float m_SampleRate; public float m_BeginTime; public float[] m_SampleArray; public DenseClip() { } public DenseClip(ObjectReader reader) { m_FrameCount = reader.ReadInt32(); m_CurveCount = reader.ReadUInt32(); m_SampleRate = reader.ReadSingle(); m_BeginTime = reader.ReadSingle(); m_SampleArray = reader.ReadSingleArray(); } public static DenseClip ParseGI(ObjectReader reader) { var denseClip = new DenseClip(); denseClip.m_FrameCount = reader.ReadInt32(); denseClip.m_CurveCount = reader.ReadUInt32(); denseClip.m_SampleRate = reader.ReadSingle(); denseClip.m_BeginTime = reader.ReadSingle(); var denseClipCount = (int)reader.ReadUInt64(); var denseClipOffset = reader.Position + reader.ReadInt64(); if (denseClipOffset > reader.Length) { throw new IOException("Offset outside of range"); } var pos = reader.Position; reader.Position = denseClipOffset; denseClip.m_SampleArray = reader.ReadSingleArray(denseClipCount); reader.Position = pos; return denseClip; } } public class ArkDenseClip : DenseClip { public int m_ACLType; public byte[] m_ACLArray; public float m_PositionFactor; public float m_EulerFactor; public float m_ScaleFactor; public float m_FloatFactor; public uint m_nPositionCurves; public uint m_nRotationCurves; public uint m_nEulerCurves; public uint m_nScaleCurves; public ArkDenseClip(ObjectReader reader) : base(reader) { m_ACLType = reader.ReadInt32(); m_ACLArray = reader.ReadUInt8Array(); reader.AlignStream(); m_PositionFactor = reader.ReadSingle(); m_EulerFactor = reader.ReadSingle(); m_ScaleFactor = reader.ReadSingle(); m_FloatFactor = reader.ReadSingle(); m_nPositionCurves = reader.ReadUInt32(); m_nRotationCurves = reader.ReadUInt32(); m_nEulerCurves = reader.ReadUInt32(); m_nScaleCurves = reader.ReadUInt32(); Process(); } private void Process() { if (m_ACLType == 0 || !m_SampleArray.IsNullOrEmpty()) { return; } var sampleArray = new List(); var size = m_ACLType >> 2; var factor = (float)((1 << m_ACLType) - 1); var aclSpan = m_ACLArray.ToUInt4Array().AsSpan(); var buffer = (stackalloc byte[8]); for (int i = 0; i < m_FrameCount; i++) { var index = i * (int)(m_CurveCount * size); for (int j = 0; j < m_nPositionCurves; j++) { sampleArray.Add(ReadCurve(aclSpan, m_PositionFactor, ref index)); } for (int j = 0; j < m_nRotationCurves; j++) { sampleArray.Add(ReadCurve(aclSpan, 1.0f, ref index)); } for (int j = 0; j < m_nEulerCurves; j++) { sampleArray.Add(ReadCurve(aclSpan, m_EulerFactor, ref index)); } for (int j = 0; j < m_nScaleCurves; j++) { sampleArray.Add(ReadCurve(aclSpan, m_ScaleFactor, ref index)); } var m_nFloatCurves = m_CurveCount - (m_nPositionCurves + m_nRotationCurves + m_nEulerCurves + m_nScaleCurves); for (int j = 0; j < m_nFloatCurves; j++) { sampleArray.Add(ReadCurve(aclSpan, m_FloatFactor, ref index)); } } m_SampleArray = sampleArray.ToArray(); } private float ReadCurve(Span aclSpan, float curveFactor, ref int curveIndex) { var buffer = (stackalloc byte[8]); var curveSize = m_ACLType >> 2; var factor = (float)((1 << m_ACLType) - 1); aclSpan.Slice(curveIndex, curveSize).CopyTo(buffer); var temp = buffer.ToArray().ToUInt8Array(0, curveSize); buffer.Clear(); temp.CopyTo(buffer); float curve; var value = BitConverter.ToUInt64(buffer); if (value != 0) { curve = ((value / factor) - 0.5f) * 2; } else { curve = -1.0f; } curve *= curveFactor; curveIndex += curveSize; return curve; } } public class ConstantClip { public float[] data; public ConstantClip() { } public ConstantClip(ObjectReader reader) { data = reader.ReadSingleArray(); } public static ConstantClip ParseGI(ObjectReader reader) { var constantClipCount = (int)reader.ReadUInt64(); var constantClipOffset = reader.Position + reader.ReadInt64(); if (constantClipOffset > reader.Length) { throw new IOException("Offset outside of range"); } var pos = reader.Position; reader.Position = constantClipOffset; var constantClip = new ConstantClip(); constantClip.data = reader.ReadSingleArray(constantClipCount); reader.Position = pos; return constantClip; } } public class ValueConstant { public uint m_ID; public uint m_TypeID; public uint m_Type; public uint m_Index; public ValueConstant(ObjectReader reader) { var version = reader.version; m_ID = reader.ReadUInt32(); if (version[0] < 5 || (version[0] == 5 && version[1] < 5))//5.5 down { m_TypeID = reader.ReadUInt32(); } m_Type = reader.ReadUInt32(); m_Index = reader.ReadUInt32(); } } public class ValueArrayConstant { public List m_ValueArray; public ValueArrayConstant(ObjectReader reader) { int numVals = reader.ReadInt32(); m_ValueArray = new List(); for (int i = 0; i < numVals; i++) { m_ValueArray.Add(new ValueConstant(reader)); } } } public class Clip { public ACLClip m_ACLClip = new EmptyACLClip(); public StreamedClip m_StreamedClip; public DenseClip m_DenseClip; public ConstantClip m_ConstantClip; public ValueArrayConstant m_Binding; public Clip() { } public Clip(ObjectReader reader) { var version = reader.version; m_StreamedClip = new StreamedClip(reader); if (reader.Game.Type.IsArknightsEndfield()) { m_DenseClip = new ArkDenseClip(reader); } else { m_DenseClip = new DenseClip(reader); } if (reader.Game.Type.IsSRGroup()) { m_ACLClip = new MHYACLClip(); m_ACLClip.Read(reader); } if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up { m_ConstantClip = new ConstantClip(reader); } if (reader.Game.Type.IsGIGroup() || reader.Game.Type.IsBH3Group() || reader.Game.Type.IsZZZCB1()) { m_ACLClip = new MHYACLClip(); m_ACLClip.Read(reader); } if (version[0] < 2018 || (version[0] == 2018 && version[1] < 3)) //2018.3 down { m_Binding = new ValueArrayConstant(reader); } } public static Clip ParseGI(ObjectReader reader) { var clipOffset = reader.Position + reader.ReadInt64(); if (clipOffset > reader.Length) { throw new IOException("Offset outside of range"); } var pos = reader.Position; reader.Position = clipOffset; var clip = new Clip(); clip.m_StreamedClip = StreamedClip.ParseGI(reader); clip.m_DenseClip = DenseClip.ParseGI(reader); clip.m_ConstantClip = ConstantClip.ParseGI(reader); clip.m_ACLClip = new GIACLClip(); clip.m_ACLClip.Read(reader); reader.Position = pos; return clip; } public AnimationClipBindingConstant ConvertValueArrayToGenericBinding() { var bindings = new AnimationClipBindingConstant(); var genericBindings = new List(); var values = m_Binding; for (int i = 0; i < values.m_ValueArray.Count;) { var curveID = values.m_ValueArray[i].m_ID; var curveTypeID = values.m_ValueArray[i].m_TypeID; var binding = new GenericBinding(); genericBindings.Add(binding); if (curveTypeID == 4174552735) //CRC(PositionX)) { binding.path = curveID; binding.attribute = 1; //kBindTransformPosition binding.typeID = ClassIDType.Transform; i += 3; } else if (curveTypeID == 2211994246) //CRC(QuaternionX)) { binding.path = curveID; binding.attribute = 2; //kBindTransformRotation binding.typeID = ClassIDType.Transform; i += 4; } else if (curveTypeID == 1512518241) //CRC(ScaleX)) { binding.path = curveID; binding.attribute = 3; //kBindTransformScale binding.typeID = ClassIDType.Transform; i += 3; } else { binding.typeID = ClassIDType.Animator; binding.path = 0; binding.attribute = curveID; i++; } } bindings.genericBindings = genericBindings; return bindings; } } public class ValueDelta { public float m_Start; public float m_Stop; public ValueDelta(ObjectReader reader) { m_Start = reader.ReadSingle(); m_Stop = reader.ReadSingle(); } } public class ClipMuscleConstant : IYAMLExportable { public HumanPose m_DeltaPose; public XForm m_StartX; public XForm m_StopX; public XForm m_LeftFootStartX; public XForm m_RightFootStartX; public XForm m_MotionStartX; public XForm m_MotionStopX; public Vector3 m_AverageSpeed; public Clip m_Clip; public float m_StartTime; public float m_StopTime; public float m_OrientationOffsetY; public float m_Level; public float m_CycleOffset; public float m_AverageAngularSpeed; public int[] m_IndexArray; public List m_ValueArrayDelta; public float[] m_ValueArrayReferencePose; public bool m_Mirror; public bool m_LoopTime; public bool m_LoopBlend; public bool m_LoopBlendOrientation; public bool m_LoopBlendPositionY; public bool m_LoopBlendPositionXZ; public bool m_StartAtOrigin; public bool m_KeepOriginalOrientation; public bool m_KeepOriginalPositionY; public bool m_KeepOriginalPositionXZ; public bool m_HeightFromFeet; public static bool HasShortIndexArray(SerializedType type) => type.Match("E708B1872AE48FD688AC012DF4A7A178") || type.Match("055AA41C7639327940F8900103A10356"); public ClipMuscleConstant() { } public ClipMuscleConstant(ObjectReader reader) { var version = reader.version; m_DeltaPose = new HumanPose(reader); m_StartX = reader.ReadXForm(); if (version[0] > 5 || (version[0] == 5 && version[1] >= 5))//5.5 and up { m_StopX = reader.ReadXForm(); } m_LeftFootStartX = reader.ReadXForm(); m_RightFootStartX = reader.ReadXForm(); if (version[0] < 5)//5.0 down { m_MotionStartX = reader.ReadXForm(); m_MotionStopX = reader.ReadXForm(); } m_AverageSpeed = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up m_Clip = new Clip(reader); m_StartTime = reader.ReadSingle(); m_StopTime = reader.ReadSingle(); m_OrientationOffsetY = reader.ReadSingle(); m_Level = reader.ReadSingle(); m_CycleOffset = reader.ReadSingle(); m_AverageAngularSpeed = reader.ReadSingle(); if (reader.Game.Type.IsSR() && HasShortIndexArray(reader.serializedType)) { m_IndexArray = reader.ReadInt16Array().Select(x => (int)x).ToArray(); } else { m_IndexArray = reader.ReadInt32Array(); } if (version[0] < 4 || (version[0] == 4 && version[1] < 3)) //4.3 down { var m_AdditionalCurveIndexArray = reader.ReadInt32Array(); } int numDeltas = reader.ReadInt32(); m_ValueArrayDelta = new List(); for (int i = 0; i < numDeltas; i++) { m_ValueArrayDelta.Add(new ValueDelta(reader)); } if (version[0] > 5 || (version[0] == 5 && version[1] >= 3))//5.3 and up { m_ValueArrayReferencePose = reader.ReadSingleArray(); } m_Mirror = reader.ReadBoolean(); if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up { m_LoopTime = reader.ReadBoolean(); } m_LoopBlend = reader.ReadBoolean(); m_LoopBlendOrientation = reader.ReadBoolean(); m_LoopBlendPositionY = reader.ReadBoolean(); m_LoopBlendPositionXZ = reader.ReadBoolean(); if (version[0] > 5 || (version[0] == 5 && version[1] >= 5))//5.5 and up { m_StartAtOrigin = reader.ReadBoolean(); } m_KeepOriginalOrientation = reader.ReadBoolean(); m_KeepOriginalPositionY = reader.ReadBoolean(); m_KeepOriginalPositionXZ = reader.ReadBoolean(); m_HeightFromFeet = reader.ReadBoolean(); reader.AlignStream(); } public static ClipMuscleConstant ParseGI(ObjectReader reader) { var version = reader.version; var clipMuscleConstant = new ClipMuscleConstant(); clipMuscleConstant.m_DeltaPose = HumanPose.ParseGI(reader); clipMuscleConstant.m_StartX = reader.ReadXForm4(); clipMuscleConstant.m_StopX = reader.ReadXForm4(); clipMuscleConstant.m_LeftFootStartX = reader.ReadXForm4(); clipMuscleConstant.m_RightFootStartX = reader.ReadXForm4(); clipMuscleConstant.m_AverageSpeed = (Vector3)reader.ReadVector4(); clipMuscleConstant.m_Clip = Clip.ParseGI(reader); clipMuscleConstant.m_StartTime = reader.ReadSingle(); clipMuscleConstant.m_StopTime = reader.ReadSingle(); clipMuscleConstant.m_OrientationOffsetY = reader.ReadSingle(); clipMuscleConstant.m_Level = reader.ReadSingle(); clipMuscleConstant.m_CycleOffset = reader.ReadSingle(); clipMuscleConstant.m_AverageAngularSpeed = reader.ReadSingle(); clipMuscleConstant.m_IndexArray = reader.ReadInt16Array(0xC8).Select(x => (int)x).ToArray(); var valueArrayDeltaCount = (int)reader.ReadUInt64(); var valueArrayDeltaOffset = reader.Position + reader.ReadInt64(); if (valueArrayDeltaOffset > reader.Length) { throw new IOException("Offset outside of range"); } var valueArrayReferencePoseCount = (int)reader.ReadUInt64(); var valueArrayReferencePoseOffset = reader.Position + reader.ReadInt64(); if (valueArrayReferencePoseOffset > reader.Length) { throw new IOException("Offset outside of range"); } clipMuscleConstant.m_Mirror = reader.ReadBoolean(); clipMuscleConstant.m_LoopTime = reader.ReadBoolean(); clipMuscleConstant.m_LoopBlend = reader.ReadBoolean(); clipMuscleConstant.m_LoopBlendOrientation = reader.ReadBoolean(); clipMuscleConstant.m_LoopBlendPositionY = reader.ReadBoolean(); clipMuscleConstant.m_LoopBlendPositionXZ = reader.ReadBoolean(); clipMuscleConstant.m_StartAtOrigin = reader.ReadBoolean(); clipMuscleConstant.m_KeepOriginalOrientation = reader.ReadBoolean(); clipMuscleConstant.m_KeepOriginalPositionY = reader.ReadBoolean(); clipMuscleConstant.m_KeepOriginalPositionXZ = reader.ReadBoolean(); clipMuscleConstant.m_HeightFromFeet = reader.ReadBoolean(); reader.AlignStream(); if (valueArrayDeltaCount > 0) { reader.Position = valueArrayDeltaOffset; clipMuscleConstant.m_ValueArrayDelta = new List(); for (int i = 0; i < valueArrayDeltaCount; i++) { clipMuscleConstant.m_ValueArrayDelta.Add(new ValueDelta(reader)); } } if (valueArrayReferencePoseCount > 0) { reader.Position = valueArrayReferencePoseOffset; clipMuscleConstant.m_ValueArrayReferencePose = reader.ReadSingleArray(valueArrayReferencePoseCount); } return clipMuscleConstant; } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.AddSerializedVersion(ToSerializedVersion(version)); node.Add(nameof(m_StartTime), m_StartTime); node.Add(nameof(m_StopTime), m_StopTime); node.Add(nameof(m_OrientationOffsetY), m_OrientationOffsetY); node.Add(nameof(m_Level), m_Level); node.Add(nameof(m_CycleOffset), m_CycleOffset); node.Add(nameof(m_LoopTime), m_LoopTime); node.Add(nameof(m_LoopBlend), m_LoopBlend); node.Add(nameof(m_LoopBlendOrientation), m_LoopBlendOrientation); node.Add(nameof(m_LoopBlendPositionY), m_LoopBlendPositionY); node.Add(nameof(m_LoopBlendPositionXZ), m_LoopBlendPositionXZ); node.Add(nameof(m_KeepOriginalOrientation), m_KeepOriginalOrientation); node.Add(nameof(m_KeepOriginalPositionY), m_KeepOriginalPositionY); node.Add(nameof(m_KeepOriginalPositionXZ), m_KeepOriginalPositionXZ); node.Add(nameof(m_HeightFromFeet), m_HeightFromFeet); node.Add(nameof(m_Mirror), m_Mirror); return node; } private int ToSerializedVersion(int[] version) { if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) { return 3; } else if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) { return 2; } return 1; } } public class GenericBinding : IYAMLExportable { public int[] version; public uint path; public uint attribute; public PPtr script; public ClassIDType typeID; public byte customType; public byte isPPtrCurve; public byte isIntCurve; public GenericBinding() { } public GenericBinding(ObjectReader reader) { version = reader.version; path = reader.ReadUInt32(); attribute = reader.ReadUInt32(); script = new PPtr(reader); if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up { typeID = (ClassIDType)reader.ReadInt32(); } else { typeID = (ClassIDType)reader.ReadUInt16(); } customType = reader.ReadByte(); isPPtrCurve = reader.ReadByte(); if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 1)) //2022.1 and up { isIntCurve = reader.ReadByte(); } reader.AlignStream(); } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.Add(nameof(path), path); node.Add(nameof(attribute), attribute); node.Add(nameof(script), script.ExportYAML(version)); node.Add("classID", ((int)typeID).ToString()); node.Add(nameof(customType), customType); node.Add(nameof(isPPtrCurve), isPPtrCurve); return node; } } public class AnimationClipBindingConstant : IYAMLExportable { public List genericBindings; public List> pptrCurveMapping; public AnimationClipBindingConstant() { } public AnimationClipBindingConstant(ObjectReader reader) { int numBindings = reader.ReadInt32(); genericBindings = new List(); for (int i = 0; i < numBindings; i++) { genericBindings.Add(new GenericBinding(reader)); } int numMappings = reader.ReadInt32(); pptrCurveMapping = new List>(); for (int i = 0; i < numMappings; i++) { pptrCurveMapping.Add(new PPtr(reader)); } } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.Add(nameof(genericBindings), genericBindings.ExportYAML(version)); node.Add(nameof(pptrCurveMapping), pptrCurveMapping.ExportYAML(version)); return node; } public GenericBinding FindBinding(int index) { int curves = 0; foreach (var b in genericBindings) { if (b.typeID == ClassIDType.Transform) { switch (b.attribute) { case 1: //kBindTransformPosition case 3: //kBindTransformScale case 4: //kBindTransformEuler curves += 3; break; case 2: //kBindTransformRotation curves += 4; break; default: curves += 1; break; } } else { curves += 1; } if (curves > index) { return b; } } return null; } } public class AnimationEvent : IYAMLExportable { public float time; public string functionName; public string data; public PPtr objectReferenceParameter; public float floatParameter; public int intParameter; public int messageOptions; public AnimationEvent(ObjectReader reader) { var version = reader.version; time = reader.ReadSingle(); functionName = reader.ReadAlignedString(); data = reader.ReadAlignedString(); objectReferenceParameter = new PPtr(reader); floatParameter = reader.ReadSingle(); if (version[0] >= 3) //3 and up { intParameter = reader.ReadInt32(); } messageOptions = reader.ReadInt32(); } public YAMLNode ExportYAML(int[] version) { var node = new YAMLMappingNode(); node.Add(nameof(time), time); node.Add(nameof(functionName), functionName); node.Add(nameof(data), data); node.Add(nameof(objectReferenceParameter), objectReferenceParameter.ExportYAML(version)); node.Add(nameof(floatParameter), floatParameter); node.Add(nameof(intParameter), intParameter); node.Add(nameof(messageOptions), messageOptions); return node; } } public enum AnimationType { Legacy = 1, Generic = 2, Humanoid = 3 }; public sealed class AnimationClip : NamedObject { public AnimationType m_AnimationType; public bool m_Legacy; public bool m_Compressed; public bool m_UseHighQualityCurve; public List m_RotationCurves; public List m_CompressedRotationCurves; public List m_EulerCurves; public List m_PositionCurves; public List m_ScaleCurves; public List m_FloatCurves; public List m_PPtrCurves; public float m_SampleRate; public int m_WrapMode; public AABB m_Bounds; public uint m_MuscleClipSize; public ClipMuscleConstant m_MuscleClip; public AnimationClipBindingConstant m_ClipBindingConstant; public List m_Events; public StreamingInfo m_StreamData; private bool hasStreamingInfo = false; public AnimationClip(ObjectReader reader) : base(reader) { if (version[0] >= 5)//5.0 and up { m_Legacy = reader.ReadBoolean(); } else if (version[0] >= 4)//4.0 and up { m_AnimationType = (AnimationType)reader.ReadInt32(); if (m_AnimationType == AnimationType.Legacy) m_Legacy = true; } else { m_Legacy = true; } m_Compressed = reader.ReadBoolean(); if (version[0] > 4 || (version[0] == 4 && version[1] >= 3))//4.3 and up { m_UseHighQualityCurve = reader.ReadBoolean(); } reader.AlignStream(); int numRCurves = reader.ReadInt32(); m_RotationCurves = new List(); for (int i = 0; i < numRCurves; i++) { m_RotationCurves.Add(new QuaternionCurve(reader)); } int numCRCurves = reader.ReadInt32(); m_CompressedRotationCurves = new List(); for (int i = 0; i < numCRCurves; i++) { m_CompressedRotationCurves.Add(new CompressedAnimationCurve(reader)); } if (version[0] > 5 || (version[0] == 5 && version[1] >= 3))//5.3 and up { int numEulerCurves = reader.ReadInt32(); m_EulerCurves = new List(); for (int i = 0; i < numEulerCurves; i++) { m_EulerCurves.Add(new Vector3Curve(reader)); } } int numPCurves = reader.ReadInt32(); m_PositionCurves = new List(); for (int i = 0; i < numPCurves; i++) { m_PositionCurves.Add(new Vector3Curve(reader)); } int numSCurves = reader.ReadInt32(); m_ScaleCurves = new List(); for (int i = 0; i < numSCurves; i++) { m_ScaleCurves.Add(new Vector3Curve(reader)); } int numFCurves = reader.ReadInt32(); m_FloatCurves = new List(); for (int i = 0; i < numFCurves; i++) { m_FloatCurves.Add(new FloatCurve(reader)); } if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up { int numPtrCurves = reader.ReadInt32(); m_PPtrCurves = new List(); for (int i = 0; i < numPtrCurves; i++) { m_PPtrCurves.Add(new PPtrCurve(reader)); } } m_SampleRate = reader.ReadSingle(); m_WrapMode = reader.ReadInt32(); if (reader.Game.Type.IsArknightsEndfield()) { var m_aclType = reader.ReadInt32(); } if (version[0] > 3 || (version[0] == 3 && version[1] >= 4)) //3.4 and up { m_Bounds = new AABB(reader); } if (version[0] >= 4)//4.0 and up { if (reader.Game.Type.IsGI()) { var muscleClipSize = reader.ReadInt32(); if (muscleClipSize < 0) { hasStreamingInfo = true; m_MuscleClipSize = reader.ReadUInt32(); m_MuscleClip = ClipMuscleConstant.ParseGI(reader); } else if (muscleClipSize > 0) { m_MuscleClipSize = (uint)muscleClipSize; m_MuscleClip = new ClipMuscleConstant(reader); } } else { m_MuscleClipSize = reader.ReadUInt32(); m_MuscleClip = new ClipMuscleConstant(reader); } } if (reader.Game.Type.IsSRGroup()) { var m_AclClipData = reader.ReadUInt8Array(); var aclBindingsCount = reader.ReadInt32(); var m_AclBindings = new List(); for (int i = 0; i < aclBindingsCount; i++) { m_AclBindings.Add(new GenericBinding(reader)); } var m_AclRange = new KeyValuePair(reader.ReadSingle(), reader.ReadSingle()); } if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up { m_ClipBindingConstant = new AnimationClipBindingConstant(reader); } if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 3)) //2018.3 and up { var m_HasGenericRootTransform = reader.ReadBoolean(); var m_HasMotionFloatCurves = reader.ReadBoolean(); reader.AlignStream(); } int numEvents = reader.ReadInt32(); m_Events = new List(); for (int i = 0; i < numEvents; i++) { m_Events.Add(new AnimationEvent(reader)); } if (version[0] >= 2017) //2017 and up { reader.AlignStream(); } if (hasStreamingInfo) { m_StreamData = new StreamingInfo(reader); if (!string.IsNullOrEmpty(m_StreamData?.path)) { var aclClip = m_MuscleClip.m_Clip.m_ACLClip as GIACLClip; var resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size); var ms = new MemoryStream(); ms.Write(aclClip.m_DatabaseData); ms.Write(resourceReader.GetData()); ms.AlignStream(); aclClip.m_DatabaseData = ms.ToArray(); } } } } }