From c98c526b5f799284ad5455b52ed7034ea4301eb1 Mon Sep 17 00:00:00 2001 From: dexy Date: Sat, 16 Nov 2019 03:18:23 +1100 Subject: [PATCH] Animations XML conversion progress --- CodeWalker.Core/GameFiles/GameFileCache.cs | 344 +++++++------- .../GameFiles/MetaTypes/MetaXml.cs | 7 + CodeWalker.Core/GameFiles/Resources/Clip.cs | 434 +++++++++++++++--- ExploreForm.cs | 16 + Peds/PedsForm.cs | 2 + 5 files changed, 578 insertions(+), 225 deletions(-) diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index 2b8e9ba..f432b88 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -2990,6 +2990,8 @@ namespace CodeWalker.GameFiles if (ycd1.ClipDictionary == null) { continue; } + var data1 = ycd1.Save(); + var t = true; if (t)//just here to test loading only { continue; } @@ -3000,207 +3002,210 @@ namespace CodeWalker.GameFiles var ycd2 = new YcdFile(); RpfFile.LoadResourceFile(ycd2, data, 46);//full roundtrip - - if (ycd2 == null) - { continue; } - if (ycd2.ClipDictionary == null) - { continue; } - - var c1 = ycd1.ClipDictionary.Clips?.data_items; - var c2 = ycd2.ClipDictionary.Clips?.data_items; - if ((c1 == null) || (c2 == null)) - { continue; } - if (c1.Length != c2.Length) - { continue; } - - var a1 = ycd1.ClipDictionary.Animations?.Animations?.data_items; - var a2 = ycd2.ClipDictionary.Animations?.Animations?.data_items; - if ((a1 == null) || (a2 == null)) - { continue; } - if (a1.Length != a2.Length) - { continue; } - - var m1 = ycd1.AnimMap; - var m2 = ycd2.AnimMap; - if ((m1 == null) || (m2 == null)) - { continue; } - if (m1.Count != m2.Count) - { continue; } - foreach (var kvp1 in m1) { - var an1 = kvp1.Value; - var an2 = an1; - if(!m2.TryGetValue(kvp1.Key, out an2)) + if (ycd2 == null) + { continue; } + if (ycd2.ClipDictionary == null) { continue; } - var sa1 = an1?.Animation?.Sequences?.data_items; - var sa2 = an2?.Animation?.Sequences?.data_items; - if ((sa1 == null) || (sa2 == null)) + var c1 = ycd1.ClipDictionary.Clips?.data_items; + var c2 = ycd2.ClipDictionary.Clips?.data_items; + if ((c1 == null) || (c2 == null)) { continue; } - if (sa1.Length != sa2.Length) + if (c1.Length != c2.Length) { continue; } - for (int s = 0; s < sa1.Length; s++) + + var a1 = ycd1.ClipDictionary.Animations?.Animations?.data_items; + var a2 = ycd2.ClipDictionary.Animations?.Animations?.data_items; + if ((a1 == null) || (a2 == null)) + { continue; } + if (a1.Length != a2.Length) + { continue; } + + var m1 = ycd1.AnimMap; + var m2 = ycd2.AnimMap; + if ((m1 == null) || (m2 == null)) + { continue; } + if (m1.Count != m2.Count) + { continue; } + foreach (var kvp1 in m1) { - var s1 = sa1[s]; - var s2 = sa2[s]; - if ((s1?.Sequences == null) || (s2?.Sequences == null)) + var an1 = kvp1.Value; + var an2 = an1; + if (!m2.TryGetValue(kvp1.Key, out an2)) { continue; } - if (s1.NumFrames != s2.NumFrames) - { } - if (s1.ChunkSize != s2.ChunkSize) - { } - if (s1.FrameOffset != s2.FrameOffset) - { } - if (s1.DataLength != s2.DataLength) - { } - else + var sa1 = an1?.Animation?.Sequences?.data_items; + var sa2 = an2?.Animation?.Sequences?.data_items; + if ((sa1 == null) || (sa2 == null)) + { continue; } + if (sa1.Length != sa2.Length) + { continue; } + for (int s = 0; s < sa1.Length; s++) { - //for (int b = 0; b < s1.DataLength; b++) - //{ - // var b1 = s1.Data[b]; - // var b2 = s2.Data[b]; - // if (b1 != b2) - // { } - //} - } - - for (int ss = 0; ss < s1.Sequences.Length; ss++) - { - var ss1 = s1.Sequences[ss]; - var ss2 = s2.Sequences[ss]; - if ((ss1?.Channels == null) || (ss2?.Channels == null)) - { continue; } - if (ss1.Channels.Length != ss2.Channels.Length) + var s1 = sa1[s]; + var s2 = sa2[s]; + if ((s1?.Sequences == null) || (s2?.Sequences == null)) { continue; } - - for (int c = 0; c < ss1.Channels.Length; c++) + if (s1.NumFrames != s2.NumFrames) + { } + if (s1.ChunkSize != s2.ChunkSize) + { } + if (s1.FrameOffset != s2.FrameOffset) + { } + if (s1.DataLength != s2.DataLength) + { } + else { - var sc1 = ss1.Channels[c]; - var sc2 = ss2.Channels[c]; - if ((sc1 == null) || (sc2 == null)) + //for (int b = 0; b < s1.DataLength; b++) + //{ + // var b1 = s1.Data[b]; + // var b2 = s2.Data[b]; + // if (b1 != b2) + // { } + //} + } + + for (int ss = 0; ss < s1.Sequences.Length; ss++) + { + var ss1 = s1.Sequences[ss]; + var ss2 = s2.Sequences[ss]; + if ((ss1?.Channels == null) || (ss2?.Channels == null)) { continue; } - if (sc1.Type == AnimChannelType.LinearFloat) + if (ss1.Channels.Length != ss2.Channels.Length) { continue; } - if (sc1.Type != sc2.Type) - { continue; } - if (sc1.Index != sc2.Index) - { continue; } - if (sc1.Type == AnimChannelType.StaticQuaternion) + + + for (int c = 0; c < ss1.Channels.Length; c++) { - var acsq1 = sc1 as AnimChannelStaticQuaternion; - var acsq2 = sc2 as AnimChannelStaticQuaternion; - var vdiff = acsq1.Value - acsq2.Value; - var len = vdiff.Length(); - var v1len = Math.Max(acsq1.Value.Length(), 1); - if (len > 1e-2f * v1len) + var sc1 = ss1.Channels[c]; + var sc2 = ss2.Channels[c]; + if ((sc1 == null) || (sc2 == null)) { continue; } - } - else if (sc1.Type == AnimChannelType.StaticVector3) - { - var acsv1 = sc1 as AnimChannelStaticVector3; - var acsv2 = sc2 as AnimChannelStaticVector3; - var vdiff = acsv1.Value - acsv2.Value; - var len = vdiff.Length(); - var v1len = Math.Max(acsv1.Value.Length(), 1); - if (len > 1e-2f * v1len) + if (sc1.Type == AnimChannelType.LinearFloat) { continue; } - } - else if (sc1.Type == AnimChannelType.StaticFloat) - { - var acsf1 = sc1 as AnimChannelStaticFloat; - var acsf2 = sc2 as AnimChannelStaticFloat; - var vdiff = Math.Abs(acsf1.Value - acsf2.Value); - var v1len = Math.Max(Math.Abs(acsf1.Value), 1); - if (vdiff > 1e-2f * v1len) + if (sc1.Type != sc2.Type) { continue; } - } - else if (sc1.Type == AnimChannelType.RawFloat) - { - var acrf1 = sc1 as AnimChannelRawFloat; - var acrf2 = sc2 as AnimChannelRawFloat; - for (int v = 0; v < acrf1.Values.Length; v++) + if (sc1.Index != sc2.Index) + { continue; } + if (sc1.Type == AnimChannelType.StaticQuaternion) { - var v1 = acrf1.Values[v]; - var v2 = acrf2.Values[v]; - var vdiff = Math.Abs(v1 - v2); - var v1len = Math.Max(Math.Abs(v1), 1); + var acsq1 = sc1 as AnimChannelStaticQuaternion; + var acsq2 = sc2 as AnimChannelStaticQuaternion; + var vdiff = acsq1.Value - acsq2.Value; + var len = vdiff.Length(); + var v1len = Math.Max(acsq1.Value.Length(), 1); + if (len > 1e-2f * v1len) + { continue; } + } + else if (sc1.Type == AnimChannelType.StaticVector3) + { + var acsv1 = sc1 as AnimChannelStaticVector3; + var acsv2 = sc2 as AnimChannelStaticVector3; + var vdiff = acsv1.Value - acsv2.Value; + var len = vdiff.Length(); + var v1len = Math.Max(acsv1.Value.Length(), 1); + if (len > 1e-2f * v1len) + { continue; } + } + else if (sc1.Type == AnimChannelType.StaticFloat) + { + var acsf1 = sc1 as AnimChannelStaticFloat; + var acsf2 = sc2 as AnimChannelStaticFloat; + var vdiff = Math.Abs(acsf1.Value - acsf2.Value); + var v1len = Math.Max(Math.Abs(acsf1.Value), 1); if (vdiff > 1e-2f * v1len) - { break; } + { continue; } } - } - else if (sc1.Type == AnimChannelType.QuantizeFloat) - { - var acqf1 = sc1 as AnimChannelQuantizeFloat; - var acqf2 = sc2 as AnimChannelQuantizeFloat; - if (acqf1.ValueBits != acqf2.ValueBits) - { continue; } - if (Math.Abs(acqf1.Offset - acqf2.Offset) > (0.001f * Math.Abs(acqf1.Offset))) - { continue; } - if (Math.Abs(acqf1.Quantum - acqf2.Quantum) > 0.00001f) - { continue; } - for (int v = 0; v < acqf1.Values.Length; v++) + else if (sc1.Type == AnimChannelType.RawFloat) { - var v1 = acqf1.Values[v]; - var v2 = acqf2.Values[v]; - var vdiff = Math.Abs(v1 - v2); - var v1len = Math.Max(Math.Abs(v1), 1); - if (vdiff > 1e-2f * v1len) - { break; } + var acrf1 = sc1 as AnimChannelRawFloat; + var acrf2 = sc2 as AnimChannelRawFloat; + for (int v = 0; v < acrf1.Values.Length; v++) + { + var v1 = acrf1.Values[v]; + var v2 = acrf2.Values[v]; + var vdiff = Math.Abs(v1 - v2); + var v1len = Math.Max(Math.Abs(v1), 1); + if (vdiff > 1e-2f * v1len) + { break; } + } } - } - else if (sc1.Type == AnimChannelType.IndirectQuantizeFloat) - { - var aciqf1 = sc1 as AnimChannelIndirectQuantizeFloat; - var aciqf2 = sc2 as AnimChannelIndirectQuantizeFloat; - if (aciqf1.FrameBits != aciqf2.FrameBits) - { continue; } - if (aciqf1.ValueBits != aciqf2.ValueBits) - { continue; } - if (Math.Abs(aciqf1.Offset - aciqf2.Offset) > (0.001f * Math.Abs(aciqf1.Offset))) - { continue; } - if (Math.Abs(aciqf1.Quantum - aciqf2.Quantum) > 0.00001f) - { continue; } - for (int f = 0; f < aciqf1.Frames.Length; f++) + else if (sc1.Type == AnimChannelType.QuantizeFloat) { - if (aciqf1.Frames[f] != aciqf2.Frames[f]) - { break; } + var acqf1 = sc1 as AnimChannelQuantizeFloat; + var acqf2 = sc2 as AnimChannelQuantizeFloat; + if (acqf1.ValueBits != acqf2.ValueBits) + { continue; } + if (Math.Abs(acqf1.Offset - acqf2.Offset) > (0.001f * Math.Abs(acqf1.Offset))) + { continue; } + if (Math.Abs(acqf1.Quantum - acqf2.Quantum) > 0.00001f) + { continue; } + for (int v = 0; v < acqf1.Values.Length; v++) + { + var v1 = acqf1.Values[v]; + var v2 = acqf2.Values[v]; + var vdiff = Math.Abs(v1 - v2); + var v1len = Math.Max(Math.Abs(v1), 1); + if (vdiff > 1e-2f * v1len) + { break; } + } } - for (int v = 0; v < aciqf1.Values.Length; v++) + else if (sc1.Type == AnimChannelType.IndirectQuantizeFloat) { - var v1 = aciqf1.Values[v]; - var v2 = aciqf2.Values[v]; - var vdiff = Math.Abs(v1 - v2); - var v1len = Math.Max(Math.Abs(v1), 1); - if (vdiff > 1e-2f * v1len) - { break; } + var aciqf1 = sc1 as AnimChannelIndirectQuantizeFloat; + var aciqf2 = sc2 as AnimChannelIndirectQuantizeFloat; + if (aciqf1.FrameBits != aciqf2.FrameBits) + { continue; } + if (aciqf1.ValueBits != aciqf2.ValueBits) + { continue; } + if (Math.Abs(aciqf1.Offset - aciqf2.Offset) > (0.001f * Math.Abs(aciqf1.Offset))) + { continue; } + if (Math.Abs(aciqf1.Quantum - aciqf2.Quantum) > 0.00001f) + { continue; } + for (int f = 0; f < aciqf1.Frames.Length; f++) + { + if (aciqf1.Frames[f] != aciqf2.Frames[f]) + { break; } + } + for (int v = 0; v < aciqf1.Values.Length; v++) + { + var v1 = aciqf1.Values[v]; + var v2 = aciqf2.Values[v]; + var vdiff = Math.Abs(v1 - v2); + var v1len = Math.Max(Math.Abs(v1), 1); + if (vdiff > 1e-2f * v1len) + { break; } + } } - } - else if ((sc1.Type == AnimChannelType.CachedQuaternion1)||(sc1.Type == AnimChannelType.CachedQuaternion2)) - { - var acrf1 = sc1 as AnimChannelCachedQuaternion; - var acrf2 = sc2 as AnimChannelCachedQuaternion; - if (acrf1.QuatIndex != acrf2.QuatIndex) - { continue; } + else if ((sc1.Type == AnimChannelType.CachedQuaternion1) || (sc1.Type == AnimChannelType.CachedQuaternion2)) + { + var acrf1 = sc1 as AnimChannelCachedQuaternion; + var acrf2 = sc2 as AnimChannelCachedQuaternion; + if (acrf1.QuatIndex != acrf2.QuatIndex) + { continue; } + } + + + + } - - + //for (int f = 0; f < s1.NumFrames; f++) + //{ + // var v1 = ss1.EvaluateVector(f); + // var v2 = ss2.EvaluateVector(f); + // var vdiff = v1 - v2; + // var len = vdiff.Length(); + // var v1len = Math.Max(v1.Length(), 1); + // if (len > 1e-2f*v1len) + // { } + //} } - //for (int f = 0; f < s1.NumFrames; f++) - //{ - // var v1 = ss1.EvaluateVector(f); - // var v2 = ss2.EvaluateVector(f); - // var vdiff = v1 - v2; - // var len = vdiff.Length(); - // var v1len = Math.Max(v1.Length(), 1); - // if (len > 1e-2f*v1len) - // { } - //} } @@ -3209,9 +3214,6 @@ namespace CodeWalker.GameFiles } - - - } } //if (entry.NameLower.EndsWith(".awc")) //awcs can also contain clip dicts.. diff --git a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs index f51d0b9..3a85a34 100644 --- a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs +++ b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs @@ -121,6 +121,12 @@ namespace CodeWalker.GameFiles filename = fn + ".xml"; return YndXml.GetXml(ynd); } + public static string GetXml(YcdFile ycd, out string filename) + { + var fn = (ycd?.RpfFileEntry?.Name) ?? ""; + filename = fn + ".xml"; + return YcdXml.GetXml(ycd); + } @@ -1928,6 +1934,7 @@ namespace CodeWalker.GameFiles CacheFile = 4, AudioRel = 5, Ynd = 6, + Ycd = 7, } } diff --git a/CodeWalker.Core/GameFiles/Resources/Clip.cs b/CodeWalker.Core/GameFiles/Resources/Clip.cs index 58f6137..70db741 100644 --- a/CodeWalker.Core/GameFiles/Resources/Clip.cs +++ b/CodeWalker.Core/GameFiles/Resources/Clip.cs @@ -582,6 +582,8 @@ namespace CodeWalker.GameFiles public override void Write(ResourceDataWriter writer, params object[] parameters) { + //BuildSequencesData(); + // write structure data writer.Write(this.VFT); writer.Write(this.Unknown_04h); @@ -607,6 +609,8 @@ namespace CodeWalker.GameFiles public override Tuple[] GetParts() { + BuildSequencesData();//TODO: move this somewhere better? + return new Tuple[] { new Tuple(0x40, Sequences), new Tuple(0x50, BoneIds) @@ -644,6 +648,21 @@ namespace CodeWalker.GameFiles } } + public void BuildSequencesData() + { + AssignSequenceBoneIds(); + + if (Sequences?.data_items != null) + { + foreach (var seq in Sequences.data_items) + { + seq.BuildData(); + } + } + + CalculateMaxSeqBlockLength(); + } + public void WriteXml(StringBuilder sb, int indent) { @@ -676,7 +695,6 @@ namespace CodeWalker.GameFiles Sequences.data_items = XmlMeta.ReadItemArrayNullable(node, "Sequences"); AssignSequenceBoneIds(); - CalculateMaxSeqBlockLength(); } } [TypeConverter(typeof(ExpandableObjectConverter))] public struct AnimationBoneId : IMetaXmlItem @@ -722,6 +740,9 @@ namespace CodeWalker.GameFiles public int Sequence { get; set; } public int Index { get; set; } + public int DataOffset { get; set; } + public int FrameOffset { get; set; } + public abstract void Read(AnimChannelDataReader reader); public virtual void Write(AnimChannelDataWriter writer) { } @@ -730,6 +751,10 @@ namespace CodeWalker.GameFiles public virtual void WriteFrame(AnimChannelDataWriter writer) { } + public virtual int GetReferenceIndex() + { return Index; } + public virtual int GetFrameBits() + { return 0; } public virtual float EvaluateFloat(int frame) => 0.0f; @@ -785,6 +810,10 @@ namespace CodeWalker.GameFiles } + public override string ToString() + { + return Sequence.ToString() + ": " + Index.ToString() + ": " + Type.ToString() + " DataOffset: " + DataOffset.ToString() + " FrameOffset: " + FrameOffset.ToString(); + } } [TypeConverter(typeof(ExpandableObjectConverter))] public class AnimChannelStaticFloat : AnimChannel { @@ -929,10 +958,18 @@ namespace CodeWalker.GameFiles //} //Values = valueList.ToArray(); + if (FrameBits < 2) + { } + if (ValueBits < 3) + { } + } public override void Write(AnimChannelDataWriter writer) { - FrameBits = writer.BitCount(Frames); + var frameBits = Math.Max(writer.BitCount(Frames), 2); + if (frameBits != FrameBits) + { } + FrameBits = frameBits; var valueCount = Values?.Length ?? 0; var valueList = new uint[valueCount]; @@ -940,7 +977,10 @@ namespace CodeWalker.GameFiles { valueList[i] = GetQuanta(Values[i]); } - ValueBits = writer.BitCount(valueList); + var valueBits = Math.Max(writer.BitCount(valueList), 3); + if (valueBits != ValueBits) + { } + ValueBits = valueBits; writer.ResetBitstream(); for (int i = 0; i < valueCount; i++) @@ -969,11 +1009,17 @@ namespace CodeWalker.GameFiles writer.WriteFrameBits(Frames[writer.Frame], FrameBits); } + public override int GetFrameBits() + { + return FrameBits; + } + private uint GetQuanta(float v) { var q = (v - Offset) / Quantum; - return (uint)Math.Round(Math.Max(q, 0));//any better way? + return (uint)q; + //return (uint)Math.Round(q, 0);//any better way? } @@ -1018,6 +1064,9 @@ namespace CodeWalker.GameFiles Quantum = reader.ReadSingle(); Offset = reader.ReadSingle(); Values = new float[reader.NumFrames]; + + if (ValueBits < 1) + { } } public override void Write(AnimChannelDataWriter writer) { @@ -1027,7 +1076,10 @@ namespace CodeWalker.GameFiles { valueList[i] = GetQuanta(Values[i]); } - ValueBits = writer.BitCount(valueList); + var valueBits = Math.Max(writer.BitCount(valueList), 1); + if (valueBits != ValueBits) + { } + ValueBits = valueBits; writer.Write(ValueBits); writer.Write(Quantum); @@ -1045,10 +1097,17 @@ namespace CodeWalker.GameFiles writer.WriteFrameBits(bits, ValueBits); } + public override int GetFrameBits() + { + return ValueBits; + } + + private uint GetQuanta(float v) { var q = (v - Offset) / Quantum; - return (uint)Math.Round(Math.Max(q, 0));//any better way? + return (uint)q; + //return (uint)Math.Round(q, 0);//any better way? } @@ -1143,7 +1202,7 @@ namespace CodeWalker.GameFiles var delta = (Count3 != 0) ? (int)reader.ReadBits(Count3) : 0; var so = reader.BitPosition; - var maxso = Math.Min(so + 32 - Count3, streamLength); // streamLength;// + var maxso = streamLength;//Math.Min(so + 32 - Count3, streamLength); // uint b = 0; while (b == 0) // scan for a '1' bit { @@ -1179,6 +1238,8 @@ namespace CodeWalker.GameFiles } public override void Write(AnimChannelDataWriter writer) { + //TODO: fix this! + var numFrames = writer.NumFrames; var numChunks = writer.NumChunks; byte chunkSize = 64; //seems to always be 64 for this @@ -1200,7 +1261,7 @@ namespace CodeWalker.GameFiles for (int i = 0; i < numChunks; i++) { var cframe = (i * chunkSize);//chunk start frame - var cvalue = (cframe < numFrames) ? valueList[cframe] : valueList[numFrames - 1]; + var cvalue = (cframe < valueCount) ? valueList[cframe] : valueList[valueCount - 1]; var cdeltas = new int[chunkSize]; var cinc = 0; chunkValues[i] = cvalue; @@ -1228,7 +1289,7 @@ namespace CodeWalker.GameFiles for (int j = 1; j < chunkSize; j++) { var delta = cdeltas[j]; - coffset += (uint)Count3 + ((delta == 0) ? 1u : 2u); + coffset += (uint)Count3 + ((delta < 0) ? 2u : 1u); } } Count1 = writer.BitCount(chunkOffsets); //number of offset bits for each chunk @@ -1297,7 +1358,9 @@ namespace CodeWalker.GameFiles public override void WriteXml(StringBuilder sb, int indent) { - Type = AnimChannelType.QuantizeFloat;//just export this as a quantize float to avoid import issues.. + //base.WriteXml(sb, indent); + + Type = AnimChannelType.QuantizeFloat;//TODO - FIX! temporary: just export this as a quantize float to avoid import issues.. base.WriteXml(sb, indent); Type = AnimChannelType.LinearFloat; float minVal = float.MaxValue; @@ -1394,7 +1457,7 @@ namespace CodeWalker.GameFiles for (int i = 0; i < 4; i++) { - if (i != Index) + if (i != 3) { channels[ch] = blockStream.Sequences[Sequence].Channels[i]; ch++; @@ -1428,6 +1491,11 @@ namespace CodeWalker.GameFiles this.blockStream = reader; } + public override int GetReferenceIndex() + { + return QuatIndex; + } + public override float EvaluateFloat(int frame) { if (Values?.Length > 0) return Values[frame % Values.Length]; @@ -1474,7 +1542,6 @@ namespace CodeWalker.GameFiles FrameOffset = frameOffset; FrameLength = frameLength; ChannelListOffset = (int)FrameOffset + (FrameLength * NumFrames); - if (ChannelListOffset > Data?.Length) ChannelListOffset = 0;//any reason for this really? ChannelDataOffset = ChannelListOffset + (9 * 2); ChannelFrameOffset = 0; } @@ -1565,6 +1632,13 @@ namespace CodeWalker.GameFiles ChannelDataOffset += 2; return channelDataBit; } + public byte[] ReadChannelDataBytes(int n) + { + var r = new byte[n]; + Buffer.BlockCopy(Data, ChannelDataOffset, r, 0, n); + ChannelDataOffset += n; + return r; + } public void AlignChannelDataOffset(int channelCount) { @@ -1636,18 +1710,27 @@ namespace CodeWalker.GameFiles ChannelItemWriter.Write(c); ChannelItemOffset += 2; } - public void AlignChannelItemData(int channelCount) + public void AlignChannelItemData(int channelCount, int sequenceCount) { int remainder = channelCount % 4; if (remainder > 0) { + ushort writeval = (ushort)(sequenceCount << 2); int addamt = (4 - remainder); for (int i = 0; i < addamt; i++) { - WriteChannelItemData(0); + WriteChannelItemData(writeval); } } } + public void WriteChannelItemDataBytes(byte[] data) + { + if (data?.Length > 0) + { + ChannelItemWriter.Write(data); + ChannelItemOffset += data.Length; + } + } public void Write(int i) @@ -1816,7 +1899,8 @@ namespace CodeWalker.GameFiles ); } - var t7 = (AnimChannelCachedQuaternion)Channels[3]; + var t7 = Channels[3] as AnimChannelCachedQuaternion;//type 1 + if (t7 == null) t7 = Channels[4] as AnimChannelCachedQuaternion;//type 2 var x = Channels[0].EvaluateFloat(frame); var y = Channels[1].EvaluateFloat(frame); @@ -1909,12 +1993,74 @@ namespace CodeWalker.GameFiles } } + + public override string ToString() + { + return "AnimSequence: " + (Channels?.Length??0).ToString() + " channels"; + } + } + [TypeConverter(typeof(ExpandableObjectConverter))] public class SequenceRootChannelRef : IMetaXmlItem + { + public byte[] Bytes { get; set; } + public byte ChannelType { get { return Bytes[0]; } set { Bytes[0] = value; } } + public byte ChannelIndex { get { return Bytes[1]; } set { Bytes[1] = value; } } + public ushort DataIntOffset + { + get { return (ushort)(Bytes[2] + (Bytes[3] << 8)); } + set + { + Bytes[2] = (byte)(value & 0xFF); + Bytes[3] = (byte)((value >> 8) & 0xFF); + } + } + public ushort FrameBitOffset + { + get { return (ushort)(Bytes[4] + (Bytes[5] << 8)); } + set + { + Bytes[4] = (byte)(value & 0xFF); + Bytes[5] = (byte)((value >> 8) & 0xFF); + } + } + + + public SequenceRootChannelRef() + { + Bytes = new byte[6]; + } + public SequenceRootChannelRef(AnimChannelType type, int channelIndex) + { + Bytes = new byte[6]; + ChannelType = (byte)type; + ChannelIndex = (byte)channelIndex; + } + public SequenceRootChannelRef(byte[] bytes) + { + Bytes = bytes; + } + public override string ToString() + { + if (Bytes?.Length >= 6) + { + return ChannelType.ToString() + ", " + ChannelIndex.ToString() + ", " + DataIntOffset.ToString() + ", " + FrameBitOffset.ToString(); + } + return "(empty)"; + } + + public void WriteXml(StringBuilder sb, int indent) + { + YcdXml.WriteRawArray(sb, Bytes, indent, "Bytes", "", YcdXml.FormatHexByte, 6); + } + public void ReadXml(XmlNode node) + { + Bytes = Xml.GetChildRawByteArray(node, "Bytes"); + } } [TypeConverter(typeof(ExpandableObjectConverter))] public class Sequence : ResourceSystemBlock, IMetaXmlItem { public override long BlockLength { - get { return 32 + (Data?.Length??0); } + get { return 32 + (Data?.Length ?? 0); } } // structure data @@ -1929,7 +2075,7 @@ namespace CodeWalker.GameFiles public ushort IndirectQuantizeFloatNumInts { get; set; } //total number of ints that the indirect quantize float channels take (not the frames data though!) public ushort QuantizeFloatValueBits { get; set; } //total number of quantize float value bits per frame? public byte ChunkSize { get; set; } //64|255 0x40|0xFF - public byte Unknown_1Fh_Type { get; set; } //0|17|20|21|49|52|53 0x11|0x14|0x15|0x31|0x34|0x35 + public byte RootMotionRefCounts { get; set; } //0|17|20|21|49|52|53 0x11|0x14|0x15|0x31|0x34|0x35 public byte[] Data { get; set; } @@ -1937,6 +2083,26 @@ namespace CodeWalker.GameFiles // parsed data public AnimSequence[] Sequences { get; set; } + public SequenceRootChannelRef[] RootPositionRefs { get; set; } + public SequenceRootChannelRef[] RootRotationRefs { get; set; } + public int RootPositionRefCount + { + get { return (RootMotionRefCounts >> 4) & 0xF; } + set + { + var rrc = RootMotionRefCounts & 0xF; + RootMotionRefCounts = (byte)(rrc + ((value & 0xF) << 4)); + } + } + public int RootRotationRefCount + { + get { return RootMotionRefCounts & 0xF; } + set + { + var rpc = (RootMotionRefCounts >> 4) & 0xF; + RootMotionRefCounts = (byte)(rpc + (value & 0xF)); + } + } class AnimChannelListItem @@ -1967,7 +2133,7 @@ namespace CodeWalker.GameFiles this.IndirectQuantizeFloatNumInts = reader.ReadUInt16();//0 0 106 0 this.QuantizeFloatValueBits = reader.ReadUInt16(); //0 17 0 0 this.ChunkSize = reader.ReadByte(); //64 255 255 64 - this.Unknown_1Fh_Type = reader.ReadByte(); //0 0 0 0 + this.RootMotionRefCounts = reader.ReadByte(); //0 0 0 0 this.Data = reader.ReadBytes((int)DataLength); @@ -1977,6 +2143,8 @@ namespace CodeWalker.GameFiles public override void Write(ResourceDataWriter writer, params object[] parameters) { + //BuildData should be called before this + // write structure data writer.Write(this.Unknown_00h); writer.Write(this.DataLength); @@ -1989,7 +2157,7 @@ namespace CodeWalker.GameFiles writer.Write(this.IndirectQuantizeFloatNumInts); writer.Write(this.QuantizeFloatValueBits); writer.Write(this.ChunkSize); - writer.Write(this.Unknown_1Fh_Type); + writer.Write(this.RootMotionRefCounts); writer.Write(this.Data); } @@ -2013,17 +2181,18 @@ namespace CodeWalker.GameFiles for (int c = 0; c < channelCount; c++) //construct and read channels { var channel = AnimChannel.ConstructChannel(ctype); - channel?.Read(reader); - channels[c] = channel; var channelDataBit = reader.ReadChannelDataBits(); if (channel != null)//read channel sequences and indexes { + channel.DataOffset = reader.Position / 4; + channel.Read(reader); + channels[c] = channel; var sequence = channelDataBit >> 2; var index = channelDataBit & 3; if (channel is AnimChannelCachedQuaternion t7) { t7.QuatIndex = index; - index = 3; + index = (channel.Type == AnimChannelType.CachedQuaternion1) ? 3 : 4; } channel.Associate(sequence, index); channelList.Add(new AnimChannelListItem(sequence, index, channel)); @@ -2057,12 +2226,12 @@ namespace CodeWalker.GameFiles { continue; } Sequences[i].Channels = new AnimChannel[thisSeq.Max(a => a.Index) + 1]; - + for (int j = 0; j < Sequences[i].Channels.Length; j++) { Sequences[i].Channels[j] = thisSeq.FirstOrDefault(a => a.Index == j)?.Channel; - if (Sequences[i].Channels[j] is AnimChannelCachedQuaternion) + if (Sequences[i].Channels[j].Type == AnimChannelType.CachedQuaternion1)// is AnimChannelCachedQuaternion) { Sequences[i].IsType7Quat = true; } @@ -2073,33 +2242,33 @@ namespace CodeWalker.GameFiles - var qfvb = GetQuantizeFloatValueBits(); - if (qfvb != QuantizeFloatValueBits) - { } - - var ifni = GetIndirectQuantizeFloatNumInts(); - if (ifni != IndirectQuantizeFloatNumInts) - { } - - switch (Unknown_1Fh_Type) + int numPosRefs = RootPositionRefCount; + int numRotRefs = RootRotationRefCount; + if (numPosRefs > 0) { - case 0: - break; - case 17: - break; - case 20: - break; - case 21: - break; - case 49: - break; - case 52: - break; - case 53: - break; - default: - break; + RootPositionRefs = new SequenceRootChannelRef[numPosRefs]; + for (int i = 0; i < numPosRefs; i++) + { + var pref = new SequenceRootChannelRef(reader.ReadChannelDataBytes(6)); + RootPositionRefs[i] = pref; + } } + if (numRotRefs > 0) + { + RootRotationRefs = new SequenceRootChannelRef[numRotRefs]; + for (int i = 0; i < numRotRefs; i++) + { + var rref = new SequenceRootChannelRef(reader.ReadChannelDataBytes(6)); + RootRotationRefs[i] = rref; + } + } + if (reader.ChannelDataOffset != Data.Length) + { + var brem = Data.Length - reader.ChannelDataOffset; + } + + + } @@ -2148,9 +2317,13 @@ namespace CodeWalker.GameFiles var channel = channelList[c]; var channelDataBit = (ushort)((channel?.Index ?? 0) + ((channel?.Sequence ?? 0) << 2)); writer.WriteChannelItemData(channelDataBit); - channel?.Write(writer); + if (channel != null) + { + channel.DataOffset = writer.Position / 4; + channel?.Write(writer); + } } - writer.AlignChannelItemData(channelCount); + writer.AlignChannelItemData(channelCount, Sequences.Length); } for (int f = 0; f < NumFrames; f++)//write channel frame data @@ -2170,6 +2343,41 @@ namespace CodeWalker.GameFiles } + + var frameOffset = 0; + for (int i = 0; i < channelLists.Length; i++) + { + var chanList = channelLists[i]; + if (chanList == null) continue; + for (int c = 0; c < chanList.Count; c++) + { + var chan = chanList[c]; + if (chan == null) continue; + chan.FrameOffset = frameOffset; + frameOffset += chan.GetFrameBits(); + } + } + + + + UpdateRootMotionRefs(); + if (RootPositionRefs != null) + { + for (int i = 0; i < RootPositionRefs.Length; i++) + { + writer.WriteChannelItemDataBytes(RootPositionRefs[i].Bytes); + } + } + if (RootRotationRefs != null) + { + for (int i = 0; i < RootRotationRefs.Length; i++) + { + writer.WriteChannelItemDataBytes(RootRotationRefs[i].Bytes); + } + } + + + var mainData = writer.GetMainDataBytes(); var frameData = writer.GetFrameDataBytes(); var channelListData = writer.GetChannelListDataBytes(); @@ -2183,6 +2391,33 @@ namespace CodeWalker.GameFiles Buffer.BlockCopy(channelListData, 0, data, curpos, channelListData.Length); curpos += channelListData.Length; Buffer.BlockCopy(channelItemData, 0, data, curpos, channelItemData.Length); + + + //if (FrameLength != writer.FrameLength) + //{ } + //else if (Data != null) + //{ + // if (Data.Length != data.Length) + // { + // if ((channelLists[6]?.Count ?? 0) == 0) + // { } + // } + // else + // { + // for (int b = 0; b < Data.Length; b++) + // { + // if (Data[b] != data[b]) + // { + // if ((channelLists[6]?.Count ?? 0) == 0) + // { } + // break; + // } + // } + // } + //} + + + Data = data; DataLength = (uint)data.Length; FrameOffset = (uint)mainData.Length; @@ -2191,6 +2426,7 @@ namespace CodeWalker.GameFiles ChunkSize = (writer.ChunkSize > 0) ? writer.ChunkSize : (byte)255; QuantizeFloatValueBits = GetQuantizeFloatValueBits(); IndirectQuantizeFloatNumInts = GetIndirectQuantizeFloatNumInts(); + RootMotionRefCounts = (byte)((((uint)(RootPositionRefs?.Length??0))<<4) | ((uint)(RootRotationRefs?.Length ?? 0))); } @@ -2243,24 +2479,114 @@ namespace CodeWalker.GameFiles return (ushort)b; } + public void UpdateRootMotionRefs() + { + // OFFSETS - [ChannelType], [Index], [DataIntOffset 0xFFFF], [FrameBitOffset 0xFFFF] + + var newPosRefs = new List(); + var newRotRefs = new List(); + + for (int i = 0; i < Sequences.Length; i++) + { + var seq = Sequences[i]; + if (seq == null) continue; + if (seq.BoneId.Track == 5) //root position + { + for (var c = 0; c < seq.Channels?.Length; c++) + { + var chan = seq.Channels[c]; + var newPosRef = new SequenceRootChannelRef(chan.Type, chan.GetReferenceIndex()); + newPosRef.DataIntOffset = (ushort)chan.DataOffset; + newPosRef.FrameBitOffset = (ushort)chan.FrameOffset; + newPosRefs.Add(newPosRef); + } + } + if (seq.BoneId.Track == 6) //root rotation + { + for (var c = 0; c < seq.Channels?.Length; c++) + { + var chan = seq.Channels[c]; + var newRotRef = new SequenceRootChannelRef(chan.Type, chan.GetReferenceIndex()); + newRotRef.DataIntOffset = (ushort)chan.DataOffset; + newRotRef.FrameBitOffset = (ushort)chan.FrameOffset; + newRotRefs.Add(newRotRef); + } + } + } + + + int compare(SequenceRootChannelRef a, SequenceRootChannelRef b) + { + var v1 = a.ChannelType.CompareTo(b.ChannelType); + if (v1 != 0) return v1; + var v2 = a.ChannelIndex.CompareTo(b.ChannelIndex); + if (v2 != 0) return v2; + return 0; + } + newPosRefs.Sort((a, b) => { return compare(a, b); }); + newRotRefs.Sort((a, b) => { return compare(a, b); }); + + + if (RootPositionRefs != null) + { + if (RootPositionRefs.Length != newPosRefs.Count) + { } + else + { + for (int i = 0; i < RootPositionRefs.Length; i++) + { + var oldRef = RootPositionRefs[i]; + var newRef = newPosRefs[i]; + for (int b = 0; b < 2; b++) + { + if (oldRef.Bytes[b] != newRef.Bytes[b]) + { } + } + } + } + } + if (RootRotationRefs != null) + { + if (RootRotationRefs.Length != newRotRefs.Count) + { } + else + { + for (int i = 0; i < RootRotationRefs.Length; i++) + { + var oldRef = RootRotationRefs[i]; + var newRef = newRotRefs[i]; + for (int b = 0; b < 2; b++) + { + if (oldRef.Bytes[b] != newRef.Bytes[b]) + { } + } + } + } + } + + + RootPositionRefs = (newPosRefs.Count > 0) ? newPosRefs.ToArray() : null; + RootRotationRefs = (newRotRefs.Count > 0) ? newRotRefs.ToArray() : null; + + + } + + public void WriteXml(StringBuilder sb, int indent) { YcdXml.StringTag(sb, indent, "Hash", YcdXml.HashString(Unknown_00h)); YcdXml.ValueTag(sb, indent, "FrameCount", NumFrames.ToString()); - YcdXml.ValueTag(sb, indent, "Unknown1F", Unknown_1Fh_Type.ToString()); YcdXml.WriteItemArray(sb, Sequences, indent, "SequenceData"); } public void ReadXml(XmlNode node) { Unknown_00h = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Hash")); NumFrames = (ushort)Xml.GetChildUIntAttribute(node, "FrameCount", "value"); - Unknown_1Fh_Type = (byte)Xml.GetChildUIntAttribute(node, "Unknown1F", "value"); Sequences = XmlMeta.ReadItemArray(node, "SequenceData"); AssociateSequenceChannels(); - BuildData(); } } diff --git a/ExploreForm.cs b/ExploreForm.cs index ef1fa9c..b18ea6f 100644 --- a/ExploreForm.cs +++ b/ExploreForm.cs @@ -1326,6 +1326,7 @@ namespace CodeWalker case FileTypeAction.ViewCut: case FileTypeAction.ViewRel: case FileTypeAction.ViewYnd: + case FileTypeAction.ViewYcd: return true; } return false; @@ -2423,6 +2424,10 @@ namespace CodeWalker { mformat = MetaFormat.Ynd; } + if (fnamel.EndsWith(".ycd.xml")) + { + mformat = MetaFormat.Ycd; + } fname = fname.Substring(0, fname.Length - trimlength); fnamel = fnamel.Substring(0, fnamel.Length - trimlength); @@ -2487,6 +2492,17 @@ namespace CodeWalker data = ynd.Save(); break; } + case MetaFormat.Ycd: + { + var ycd = XmlYcd.GetYcd(doc); + if (ycd.ClipDictionary == null) + { + MessageBox.Show(fname + ": Schema not supported.", "Cannot import YCD XML"); + continue; + } + data = ycd.Save(); + break; + } } diff --git a/Peds/PedsForm.cs b/Peds/PedsForm.cs index 0659172..376708a 100644 --- a/Peds/PedsForm.cs +++ b/Peds/PedsForm.cs @@ -353,6 +353,7 @@ namespace CodeWalker.Peds } GameFileCache.EnableDlc = true; + GameFileCache.EnableMods = true; GameFileCache.LoadPeds = true; GameFileCache.LoadArchetypes = false;//to speed things up a little GameFileCache.BuildExtendedJenkIndex = false;//to speed things up a little @@ -987,6 +988,7 @@ namespace CodeWalker.Peds //if (ycd != null) //{ // ////// TESTING XML CONVERSIONS + // //var data = ycd.Save(); // var xml = YcdXml.GetXml(ycd); // var ycd2 = XmlYcd.GetYcd(xml); // var data = ycd2.Save();