mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-09-17 22:57:24 +08:00
412 lines
12 KiB
C#
412 lines
12 KiB
C#
using SharpDX;
|
|
using System.IO;
|
|
using TC = System.ComponentModel.TypeConverterAttribute;
|
|
using EXP = System.ComponentModel.ExpandableObjectConverter;
|
|
using System.Text;
|
|
using System.Xml;
|
|
|
|
namespace CodeWalker.GameFiles
|
|
{
|
|
public class YpdbFile : GameFile, PackedFile
|
|
{
|
|
public int SerializerVersion { get; set; } // 2
|
|
public int PoseMatcherVersion { get; set; } // 0
|
|
public uint Signature { get; set; }
|
|
public int SamplesCount { get; set; }
|
|
public PoseMatcherMatchSample[] Samples { get; set; }
|
|
public int BoneTagsCount { get; set; }
|
|
public ushort[] BoneTags { get; set; }
|
|
public PoseMatcherWeightSet WeightSet { get; set; }
|
|
public float Unk7 { get; set; } // 0.033333f
|
|
public int Unk8 { get; set; } // 1
|
|
|
|
public YpdbFile() : base(null, GameFileType.Ypdb)
|
|
{
|
|
}
|
|
public YpdbFile(RpfFileEntry entry) : base(entry, GameFileType.Ypdb)
|
|
{
|
|
}
|
|
|
|
public void Load(byte[] data, RpfFileEntry entry)
|
|
{
|
|
if (entry != null)
|
|
{
|
|
RpfFileEntry = entry;
|
|
Name = entry.Name;
|
|
}
|
|
|
|
using (MemoryStream ms = new MemoryStream(data))
|
|
{
|
|
DataReader r = new DataReader(ms, Endianess.LittleEndian);
|
|
|
|
Read(r);
|
|
}
|
|
|
|
Loaded = true;
|
|
|
|
}
|
|
|
|
public byte[] Save()
|
|
{
|
|
MemoryStream s = new MemoryStream();
|
|
DataWriter w = new DataWriter(s);
|
|
|
|
Write(w);
|
|
|
|
var buf = new byte[s.Length];
|
|
s.Position = 0;
|
|
s.Read(buf, 0, buf.Length);
|
|
return buf;
|
|
}
|
|
|
|
private void Read(DataReader r)
|
|
{
|
|
byte magic = r.ReadByte();
|
|
if (magic != 0x1A) // 0x1A indicates to the game deserializer that it's binary instead of text
|
|
{ } // no hit
|
|
|
|
SerializerVersion = r.ReadInt32();
|
|
PoseMatcherVersion = r.ReadInt32();
|
|
Signature = r.ReadUInt32();
|
|
|
|
SamplesCount = r.ReadInt32();
|
|
if (SamplesCount > 0)
|
|
{
|
|
Samples = new PoseMatcherMatchSample[SamplesCount];
|
|
|
|
for (int i = 0; i < SamplesCount; i++)
|
|
Samples[i] = new PoseMatcherMatchSample(r);
|
|
}
|
|
|
|
BoneTagsCount = r.ReadInt32();
|
|
if (BoneTagsCount > 0)
|
|
{
|
|
BoneTags = new ushort[BoneTagsCount];
|
|
|
|
for (int i = 0; i < BoneTagsCount; i++)
|
|
BoneTags[i] = r.ReadUInt16();
|
|
}
|
|
|
|
WeightSet = new PoseMatcherWeightSet(r);
|
|
|
|
Unk7 = r.ReadSingle();
|
|
Unk8 = r.ReadInt32();
|
|
|
|
uint signature2 = r.ReadUInt32();
|
|
|
|
if (SerializerVersion != 2)
|
|
{ } // no hit
|
|
if (PoseMatcherVersion != 0)
|
|
{ } // no hit
|
|
if (BoneTagsCount != WeightSet.WeightsCount)
|
|
{ } // no hit
|
|
if (Unk7 != 0.033333f)
|
|
{ } // no hit
|
|
if (Unk8 != 1)
|
|
{ } // no hit
|
|
if (Signature != signature2)
|
|
{ } // no hit
|
|
|
|
if (r.Position != r.Length)
|
|
{ }
|
|
}
|
|
|
|
private void Write(DataWriter w)
|
|
{
|
|
w.Write((byte)0x1A);
|
|
w.Write(SerializerVersion);
|
|
w.Write(PoseMatcherVersion);
|
|
w.Write(Signature);
|
|
|
|
w.Write(SamplesCount);
|
|
if (SamplesCount > 0)
|
|
{
|
|
foreach (var entry in Samples)
|
|
entry.Write(w);
|
|
}
|
|
|
|
w.Write(BoneTagsCount);
|
|
if (BoneTagsCount > 0)
|
|
{
|
|
foreach (var boneTag in BoneTags)
|
|
w.Write(boneTag);
|
|
}
|
|
|
|
WeightSet.Write(w);
|
|
|
|
w.Write(Unk7);
|
|
w.Write(Unk8);
|
|
|
|
w.Write(Signature);
|
|
}
|
|
|
|
|
|
public void WriteXml(StringBuilder sb, int indent)
|
|
{
|
|
//YpdbXml.ValueTag(sb, indent, "SerializerVersion", SerializerVersion.ToString());
|
|
//YpdbXml.ValueTag(sb, indent, "PoseMatcherVersion", PoseMatcherVersion.ToString());
|
|
YpdbXml.ValueTag(sb, indent, "Signature", Signature.ToString());
|
|
//YpdbXml.ValueTag(sb, indent, "Unk7", FloatUtil.ToString(Unk7));
|
|
//YpdbXml.ValueTag(sb, indent, "Unk8", Unk8.ToString());
|
|
YpdbXml.WriteRawArray(sb, BoneTags, indent, "BoneTags", "");
|
|
WeightSet?.WriteXml(sb, indent);
|
|
YpdbXml.WriteItemArray(sb, Samples, indent, "Samples");
|
|
}
|
|
public void ReadXml(XmlNode node)
|
|
{
|
|
SerializerVersion = 2;// Xml.GetChildIntAttribute(node, "SerializerVersion");
|
|
PoseMatcherVersion = 0;// Xml.GetChildIntAttribute(node, "PoseMatcherVersion");
|
|
Signature = Xml.GetChildUIntAttribute(node, "Signature");
|
|
Unk7 = 0.033333f;// Xml.GetChildFloatAttribute(node, "Unk7");
|
|
Unk8 = 1;// Xml.GetChildIntAttribute(node, "Unk8");
|
|
BoneTags = Xml.GetChildRawUshortArray(node, "BoneTags");
|
|
BoneTagsCount = (BoneTags?.Length ?? 0);
|
|
WeightSet = new PoseMatcherWeightSet(node);
|
|
Samples = XmlMeta.ReadItemArray<PoseMatcherMatchSample>(node, "Samples");
|
|
SamplesCount = (Samples?.Length ?? 0);
|
|
}
|
|
}
|
|
|
|
[TC(typeof(EXP))] public class PoseMatcherMatchSample : IMetaXmlItem
|
|
{
|
|
// rage::crPoseMatcherData::MatchSample
|
|
public MetaHash ClipSet { get; set; } // from clip_sets.ymt/xml
|
|
public MetaHash Clip { get; set; }
|
|
public float Offset { get; set; }//probably time offset, allows for multiple samples per clip
|
|
public PoseMatcherPointCloud PointCloud { get; set; }
|
|
|
|
public PoseMatcherMatchSample()
|
|
{ }
|
|
public PoseMatcherMatchSample(DataReader r)
|
|
{
|
|
ClipSet = r.ReadUInt32();
|
|
Clip = r.ReadUInt32();
|
|
|
|
Offset = r.ReadSingle();
|
|
|
|
PointCloud = new PoseMatcherPointCloud(r);
|
|
|
|
switch (Offset)
|
|
{
|
|
case 0:
|
|
case 0.266f:
|
|
case 0.576f:
|
|
case 0.366933f:
|
|
case 0.599466f:
|
|
case 0.506333f:
|
|
case 1.09f:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void Write(DataWriter w)
|
|
{
|
|
w.Write(ClipSet);
|
|
w.Write(Clip);
|
|
|
|
w.Write(Offset);
|
|
|
|
PointCloud.Write(w);
|
|
}
|
|
|
|
public void WriteXml(StringBuilder sb, int indent)
|
|
{
|
|
YpdbXml.StringTag(sb, indent, "ClipSet", YpdbXml.HashString(ClipSet));
|
|
YpdbXml.StringTag(sb, indent, "Clip", YpdbXml.HashString(Clip));
|
|
YpdbXml.ValueTag(sb, indent, "Offset", FloatUtil.ToString(Offset));
|
|
PointCloud?.WriteXml(sb, indent);
|
|
}
|
|
public void ReadXml(XmlNode node)
|
|
{
|
|
ClipSet = XmlMeta.GetHash(Xml.GetChildInnerText(node, "ClipSet"));
|
|
Clip = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Clip"));
|
|
Offset = Xml.GetChildFloatAttribute(node, "Offset");
|
|
PointCloud = new PoseMatcherPointCloud(node);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"{ClipSet}, {Clip}";
|
|
}
|
|
}
|
|
|
|
[TC(typeof(EXP))] public class PoseMatcherPointCloud
|
|
{
|
|
// rage::crpmPointCloud
|
|
public int PointsCount { get; set; }
|
|
public Vector3[] Points { get; set; }
|
|
public int WeightsCount { get; set; } // == PointsCount
|
|
public float[] Weights { get; set; }
|
|
public Vector3 BoundsMin { get; set; }
|
|
public Vector3 BoundsMax { get; set; }
|
|
public float WeightsSum { get; set; }
|
|
|
|
public PoseMatcherPointCloud(XmlNode n)
|
|
{
|
|
BoundsMin = Xml.GetChildVector3Attributes(n, "BoundsMin");
|
|
BoundsMax = Xml.GetChildVector3Attributes(n, "BoundsMax");
|
|
Points = Xml.GetChildRawVector3Array(n, "Points");
|
|
PointsCount = (Points?.Length ?? 0);
|
|
Weights = Xml.GetChildRawFloatArray(n, "Weights");
|
|
WeightsCount = (Weights?.Length ?? 0);
|
|
|
|
var sum = 0.0f;
|
|
if (Weights != null) foreach (var v in Weights) sum += v;
|
|
WeightsSum = sum;
|
|
}
|
|
public PoseMatcherPointCloud(DataReader r)
|
|
{
|
|
PointsCount = r.ReadInt32();
|
|
if (PointsCount > 0)
|
|
{
|
|
Points = new Vector3[PointsCount];
|
|
|
|
for (int i = 0; i < PointsCount; i++)
|
|
Points[i] = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
|
|
}
|
|
|
|
WeightsCount = r.ReadInt32();
|
|
if (WeightsCount > 0)
|
|
{
|
|
Weights = new float[WeightsCount];
|
|
|
|
for (int i = 0; i < WeightsCount; i++)
|
|
Weights[i] = r.ReadSingle();
|
|
}
|
|
|
|
BoundsMin = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
|
|
BoundsMax = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
|
|
|
|
WeightsSum = r.ReadSingle();
|
|
|
|
if (PointsCount != WeightsCount)
|
|
{ } // no hit
|
|
|
|
//var sum = 0.0f;
|
|
//if (Weights != null) foreach (var v in Weights) sum += v;
|
|
//if (WeightsSum != sum)
|
|
//{ } //no hit
|
|
}
|
|
|
|
public void Write(DataWriter w)
|
|
{
|
|
w.Write(PointsCount);
|
|
if (PointsCount > 0)
|
|
{
|
|
foreach (var point in Points)
|
|
w.Write(point);
|
|
}
|
|
|
|
w.Write(WeightsCount);
|
|
if (WeightsCount > 0)
|
|
{
|
|
foreach (var entry in Weights)
|
|
w.Write(entry);
|
|
}
|
|
|
|
w.Write(BoundsMin);
|
|
w.Write(BoundsMax);
|
|
|
|
w.Write(WeightsSum);
|
|
}
|
|
|
|
public void WriteXml(StringBuilder sb, int indent)
|
|
{
|
|
YpdbXml.SelfClosingTag(sb, indent, "BoundsMin " + FloatUtil.GetVector3XmlString(BoundsMin));
|
|
YpdbXml.SelfClosingTag(sb, indent, "BoundsMax " + FloatUtil.GetVector3XmlString(BoundsMax));
|
|
YpdbXml.WriteRawArray(sb, Points, indent, "Points", "", YpdbXml.FormatVector3, 1);
|
|
YpdbXml.WriteRawArray(sb, Weights, indent, "Weights", "", FloatUtil.ToString);
|
|
}
|
|
}
|
|
|
|
[TC(typeof(EXP))] public class PoseMatcherWeightSet
|
|
{
|
|
// rage::crWeightSet
|
|
public int WeightsCount { get; set; }
|
|
public float[] Weights { get; set; }
|
|
|
|
public PoseMatcherWeightSet(DataReader r)
|
|
{
|
|
WeightsCount = r.ReadInt32();
|
|
if (WeightsCount > 0)
|
|
{
|
|
Weights = new float[WeightsCount];
|
|
|
|
for (int i = 0; i < WeightsCount; i++)
|
|
Weights[i] = r.ReadSingle();
|
|
}
|
|
}
|
|
public PoseMatcherWeightSet(XmlNode n)
|
|
{
|
|
Weights = Xml.GetChildRawFloatArray(n, "Weights");
|
|
WeightsCount = (Weights?.Length ?? 0);
|
|
}
|
|
|
|
public void Write(DataWriter w)
|
|
{
|
|
w.Write(WeightsCount);
|
|
if (WeightsCount > 0)
|
|
{
|
|
foreach (var weight in Weights)
|
|
w.Write(weight);
|
|
}
|
|
}
|
|
|
|
public void WriteXml(StringBuilder sb, int indent)
|
|
{
|
|
YpdbXml.WriteRawArray(sb, Weights, indent, "Weights", "", FloatUtil.ToString);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class YpdbXml : MetaXmlBase
|
|
{
|
|
|
|
public static string GetXml(YpdbFile ypdb)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.AppendLine(XmlHeader);
|
|
|
|
if ((ypdb != null) && (ypdb.WeightSet != null))
|
|
{
|
|
var name = "PoseMatcher";
|
|
|
|
OpenTag(sb, 0, name);
|
|
|
|
ypdb.WriteXml(sb, 1);
|
|
|
|
CloseTag(sb, 0, name);
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
}
|
|
|
|
public class XmlYpdb
|
|
{
|
|
|
|
public static YpdbFile GetYpdb(string xml)
|
|
{
|
|
XmlDocument doc = new XmlDocument();
|
|
doc.LoadXml(xml);
|
|
return GetYpdb(doc);
|
|
}
|
|
|
|
public static YpdbFile GetYpdb(XmlDocument doc)
|
|
{
|
|
YpdbFile ypdb = new YpdbFile();
|
|
ypdb.ReadXml(doc.DocumentElement);
|
|
return ypdb;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|