Animations XML conversion

This commit is contained in:
dexy
2019-11-14 18:58:20 +11:00
Unverified
parent 918ed7fccf
commit 7e43271a67
15 changed files with 2996 additions and 737 deletions
+101 -75
View File
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace CodeWalker.GameFiles
{
@@ -19,6 +20,7 @@ namespace CodeWalker.GameFiles
public ClipMapEntry[] ClipMapEntries { get; set; }
public AnimationMapEntry[] AnimMapEntries { get; set; }
public string LoadException { get; set; }
public YcdFile() : base(null, GameFileType.Ycd)
{
@@ -41,91 +43,58 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)");
}
ResourceDataReader rd = new ResourceDataReader(resentry, data);
ClipDictionary = rd.ReadBlock<ClipDictionary>();
ClipMap = new Dictionary<MetaHash, ClipMapEntry>();
AnimMap = new Dictionary<MetaHash, AnimationMapEntry>();
if (ClipDictionary != null)
ResourceDataReader rd = null;
try
{
if ((ClipDictionary.Clips != null) && (ClipDictionary.Clips.data_items != null))
{
foreach (var cme in ClipDictionary.Clips.data_items)
{
if (cme != null)
{
ClipMap[cme.Hash] = cme;
var nxt = cme.Next;
while (nxt != null)
{
ClipMap[nxt.Hash] = nxt;
nxt = nxt.Next;
}
}
}
}
if ((ClipDictionary.Animations != null) && (ClipDictionary.Animations.Animations != null) && (ClipDictionary.Animations.Animations.data_items != null))
{
foreach (var ame in ClipDictionary.Animations.Animations.data_items)
{
if (ame != null)
{
AnimMap[ame.Hash] = ame;
var nxt = ame.NextEntry;
while (nxt != null)
{
AnimMap[nxt.Hash] = nxt;
nxt = nxt.NextEntry;
}
}
}
}
rd = new ResourceDataReader(resentry, data);
}
catch (Exception ex)
{
//data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString();
}
ClipDictionary = rd?.ReadBlock<ClipDictionary>();
InitDictionaries();
}
public void InitDictionaries()
{
ClipMap = ClipDictionary?.ClipMap ?? new Dictionary<MetaHash, ClipMapEntry>();
AnimMap = ClipDictionary?.AnimMap ?? new Dictionary<MetaHash, AnimationMapEntry>();
foreach (var cme in ClipMap.Values)
{
var clip = cme.Clip;
if (clip == null) continue;
clip.Ycd = this;
if (string.IsNullOrEmpty(clip.Name)) continue;
string name = clip.Name.Replace('\\', '/');
var slidx = name.LastIndexOf('/');
if ((slidx >= 0) && (slidx < name.Length - 1))
{
name = name.Substring(slidx + 1);
}
var didx = name.LastIndexOf('.');
if ((didx > 0) && (didx < name.Length))
{
name = name.Substring(0, didx);
}
clip.ShortName = name;
name = name.ToLowerInvariant();
JenkIndex.Ensure(name);
//if (name.EndsWith("_uv_0")) //hash for these entries match string with this removed, +1
//{
//}
//if (name.EndsWith("_uv_1")) //same as above, but +2
//{
//}
if (cme?.Clip != null) cme.Clip.Ycd = this;
}
foreach (var ame in AnimMap.Values)
{
var anim = ame.Animation;
if (anim == null) continue;
anim.Ycd = this;
if (ame?.Animation != null) ame.Animation.Ycd = this;
}
ClipMapEntries = ClipMap.Values.ToArray();
AnimMapEntries = AnimMap.Values.ToArray();
}
public byte[] Save()
{
//if (BuildStructsOnSave)
//{
// BuildStructs();
//}
byte[] data = ResourceBuilder.Build(ClipDictionary, 46); //ycd is 46...
return data;
}
public void SaveOpenFormatsAnimation(Animation crAnim, Stream outStream)
{
var seqs = new int[(crAnim.Frames / crAnim.SequenceFrameLimit) + 1];
@@ -171,7 +140,7 @@ namespace CodeWalker.GameFiles
}
else if (chList.Length == 1)
{
if (chList[0] is AnimChannelStaticSmallestThreeQuaternion)
if (chList[0] is AnimChannelStaticQuaternion)
{
isRotation = true;
}
@@ -201,7 +170,7 @@ namespace CodeWalker.GameFiles
return " Static";
}
}
else if (chan is AnimChannelStaticFloat || chan is AnimChannelStaticVector3 || chan is AnimChannelStaticSmallestThreeQuaternion)
else if (chan is AnimChannelStaticFloat || chan is AnimChannelStaticVector3 || chan is AnimChannelStaticQuaternion)
{
return " Static";
}
@@ -245,10 +214,10 @@ namespace CodeWalker.GameFiles
switch (chan)
{
case AnimChannelStaticFloat sf:
return $" {sf.FloatValue}\r\n";
return $" {sf.Value}\r\n";
case AnimChannelStaticVector3 v3:
return $" {v3.Value[0]} {v3.Value[1]} {v3.Value[2]}\r\n";
case AnimChannelStaticSmallestThreeQuaternion q3:
case AnimChannelStaticQuaternion q3:
return $" {q3.Value[0]} {q3.Value[1]} {q3.Value[2]} {q3.Value[3]}\r\n";
default:
{
@@ -303,4 +272,61 @@ namespace CodeWalker.GameFiles
writer.Flush();
}
}
public class YcdXml : MetaXmlBase
{
public static string GetXml(YcdFile ycd)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if ((ycd != null) && (ycd.ClipDictionary != null))
{
var name = "ClipDictionary";
OpenTag(sb, 0, name);
ycd.ClipDictionary.WriteXml(sb, 1);
CloseTag(sb, 0, name);
}
return sb.ToString();
}
}
public class XmlYcd
{
public static YcdFile GetYcd(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return GetYcd(doc);
}
public static YcdFile GetYcd(XmlDocument doc)
{
YcdFile ycd = new YcdFile();
ycd.ClipDictionary = new ClipDictionary();
ycd.ClipDictionary.ReadXml(doc.DocumentElement);
ycd.InitDictionaries();
//ycd.BuildStructsOnSave = false; //structs don't need to be rebuilt here!
return ycd;
}
}
}
+257 -21
View File
@@ -2964,38 +2964,274 @@ namespace CodeWalker.GameFiles
}
public void TestYcds()
{
var errorfiles = new List<YcdFile>();
var errorentries = new List<RpfEntry>();
foreach (RpfFile file in AllRpfs)
{
foreach (RpfEntry entry in file.AllEntries)
{
try
//try
//{
if (entry.NameLower.EndsWith(".ycd"))
{
if (entry.NameLower.EndsWith(".ycd"))
UpdateStatus(string.Format(entry.Path));
YcdFile ycd1 = RpfMan.GetFile<YcdFile>(entry);
if (ycd1 == null)
{
UpdateStatus(string.Format(entry.Path));
YcdFile ycdfile = RpfMan.GetFile<YcdFile>(entry);
if ((ycdfile != null))// && (ycdfile.Meta != null))
{ }
errorentries.Add(entry);
}
else if (ycd1?.LoadException != null)
{
errorfiles.Add(ycd1);//these ones have file corruption issues and won't load as resource...
}
else
{
if (ycd1.ClipDictionary == null)
{ continue; }
var t = true;
if (t)//just here to test loading only
{ continue; }
var xml = YcdXml.GetXml(ycd1);
var ycdX = XmlYcd.GetYcd(xml);
var data = ycdX.Save();
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))
{ continue; }
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++)
{
var s1 = sa1[s];
var s2 = sa2[s];
if ((s1?.Sequences == null) || (s2?.Sequences == null))
{ continue; }
if (s1.NumFrames != s2.NumFrames)
{ }
if (s1.ChunkSize != s2.ChunkSize)
{ }
if (s1.FrameOffset != s2.FrameOffset)
{ }
if (s1.DataLength != s2.DataLength)
{ }
else
{
//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)
{ continue; }
for (int c = 0; c < ss1.Channels.Length; c++)
{
var sc1 = ss1.Channels[c];
var sc2 = ss2.Channels[c];
if ((sc1 == null) || (sc2 == null))
{ continue; }
if (sc1.Type == AnimChannelType.LinearFloat)
{ continue; }
if (sc1.Type != sc2.Type)
{ continue; }
if (sc1.Index != sc2.Index)
{ continue; }
if (sc1.Type == AnimChannelType.StaticQuaternion)
{
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)
{ 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++)
{
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.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++)
{
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; }
}
}
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++)
{
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; }
}
}
//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)
// { }
//}
}
}
}
}
//if (entry.NameLower.EndsWith(".awc")) //awcs can also contain clip dicts..
//{
// UpdateStatus(string.Format(entry.Path));
// AwcFile awcfile = RpfMan.GetFile<AwcFile>(entry);
// if ((awcfile != null))
// { }
//}
}
catch (Exception ex)
{
UpdateStatus("Error! " + ex.ToString());
}
//if (entry.NameLower.EndsWith(".awc")) //awcs can also contain clip dicts..
//{
// UpdateStatus(string.Format(entry.Path));
// AwcFile awcfile = RpfMan.GetFile<AwcFile>(entry);
// if ((awcfile != null))
// { }
//}
//}
//catch (Exception ex)
//{
// UpdateStatus("Error! " + ex.ToString());
//}
}
}
//var sd = Sequence.SeqDict;
//if (sd != null)
//{
//}
if (errorfiles.Count > 0)
{ }
}
public void TestYtds()
{
+26 -15
View File
@@ -1384,18 +1384,6 @@ namespace CodeWalker.GameFiles
}
public static string XmlEscape(string unescaped)
{
if (unescaped == null) return null;
XmlDocument doc = new XmlDocument();
XmlNode node = doc.CreateElement("root");
node.InnerText = unescaped;
var escaped = node.InnerXml;
if (escaped != unescaped)
{ }
return node.InnerXml;
}
public class PsoCont
{
@@ -1742,9 +1730,16 @@ namespace CodeWalker.GameFiles
var cind2 = ind + 2;
for (int i = 0; i < itemCount; i++)
{
OpenTag(sb, cind, "Item");
arr[i].WriteXml(sb, cind2);
CloseTag(sb, cind, "Item");
if (arr[i] != null)
{
OpenTag(sb, cind, "Item");
arr[i].WriteXml(sb, cind2);
CloseTag(sb, cind, "Item");
}
else
{
SelfClosingTag(sb, cind, "Item");
}
}
CloseTag(sb, ind, name);
}
@@ -1891,6 +1886,22 @@ namespace CodeWalker.GameFiles
public static string XmlEscape(string unescaped)
{
if (unescaped == null) return null;
XmlDocument doc = new XmlDocument();
XmlNode node = doc.CreateElement("root");
node.InnerText = unescaped;
var escaped = node.InnerXml;
if (escaped != unescaped)
{ }
return node.InnerXml;
}
public enum XmlTagMode
{
None = 0,
@@ -813,6 +813,78 @@ namespace CodeWalker.GameFiles
return 0;
}
public static T[] ReadItemArray<T>(XmlNode node, string name) where T : IMetaXmlItem, new()
{
var vnode2 = node.SelectSingleNode(name);
if (vnode2 != null)
{
var inodes = vnode2.SelectNodes("Item");
if (inodes?.Count > 0)
{
var vlist = new List<T>();
foreach (XmlNode inode in inodes)
{
var v = new T();
v.ReadXml(inode);
vlist.Add(v);
}
return vlist.ToArray();
}
}
return null;
}
public static T[] ReadItemArrayNullable<T>(XmlNode node, string name) where T : IMetaXmlItem, new()
{
var vnode2 = node.SelectSingleNode(name);
if (vnode2 != null)
{
var inodes = vnode2.SelectNodes("Item");
if (inodes?.Count > 0)
{
var vlist = new List<T>();
foreach (XmlNode inode in inodes)
{
if (inode.HasChildNodes)
{
var v = new T();
v.ReadXml(inode);
vlist.Add(v);
}
else
{
vlist.Add(default(T));
}
}
return vlist.ToArray();
}
}
return null;
}
public static MetaHash[] ReadHashItemArray(XmlNode node, string name)
{
var vnode = node.SelectSingleNode(name);
if (vnode != null)
{
var inodes = vnode.SelectNodes("Item");
if (inodes?.Count > 0)
{
var vlist = new List<MetaHash>();
foreach (XmlNode inode in inodes)
{
vlist.Add(GetHash(inode.InnerText));
}
return vlist.ToArray();
}
}
return null;
}
}
struct ArrayResults
File diff suppressed because it is too large Load Diff
@@ -748,7 +748,7 @@ namespace CodeWalker.GameFiles
public ushort EntriesCapacity { get; private set; }
// reference data
public T[] data_items { get; private set; }
public T[] data_items { get; set; }
private ResourceSystemStructBlock<T> data_block;//used for saving.