mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2026-05-14 17:05:10 +08:00
Animations XML conversion
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user