diff --git a/CodeWalker.Core/GameFiles/FileTypes/YpdbFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YpdbFile.cs new file mode 100644 index 0000000..0405714 --- /dev/null +++ b/CodeWalker.Core/GameFiles/FileTypes/YpdbFile.cs @@ -0,0 +1,270 @@ +using SharpDX; +using System.IO; +using TC = System.ComponentModel.TypeConverterAttribute; +using EXP = System.ComponentModel.ExpandableObjectConverter; + +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); + } + } + + [TC(typeof(EXP))] + public class PoseMatcherMatchSample + { + // rage::crPoseMatcherData::MatchSample + public MetaHash ClipSet { get; set; } // from clip_sets.ymt/xml + public MetaHash Clip { get; set; } + public float Unk3 { get; set; } + public PoseMatcherPointCloud PointCloud { get; set; } + + public PoseMatcherMatchSample(DataReader r) + { + ClipSet = r.ReadUInt32(); + Clip = r.ReadUInt32(); + + Unk3 = r.ReadSingle(); + + PointCloud = new PoseMatcherPointCloud(r); + } + + public void Write(DataWriter w) + { + w.Write(ClipSet); + w.Write(Clip); + + w.Write(Unk3); + + PointCloud.Write(w); + } + + 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 Unk2_Count { get; set; } // == PointsCount + public float[] Unk2_Items { get; set; } + public Vector3 BoundsMin { get; set; } + public Vector3 BoundsMax { get; set; } + public float Unk5 { get; set; } + + 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()); + } + + Unk2_Count = r.ReadInt32(); + if (Unk2_Count > 0) + { + Unk2_Items = new float[Unk2_Count]; + + for (int i = 0; i < Unk2_Count; i++) + Unk2_Items[i] = r.ReadSingle(); + } + + BoundsMin = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); + BoundsMax = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); + + Unk5 = r.ReadSingle(); + + if (PointsCount != Unk2_Count) + { } // no hit + } + + public void Write(DataWriter w) + { + w.Write(PointsCount); + if (PointsCount > 0) + { + foreach (var point in Points) + w.Write(point); + } + + w.Write(Unk2_Count); + if (Unk2_Count > 0) + { + foreach (var entry in Unk2_Items) + w.Write(entry); + } + + w.Write(BoundsMin); + w.Write(BoundsMax); + + w.Write(Unk5); + } + } + + [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 void Write(DataWriter w) + { + w.Write(WeightsCount); + if (WeightsCount > 0) + { + foreach (var weight in Weights) + w.Write(weight); + } + } + } +} diff --git a/CodeWalker.Core/GameFiles/GameFile.cs b/CodeWalker.Core/GameFiles/GameFile.cs index 7e55e0c..05d2064 100644 --- a/CodeWalker.Core/GameFiles/GameFile.cs +++ b/CodeWalker.Core/GameFiles/GameFile.cs @@ -84,6 +84,7 @@ namespace CodeWalker.GameFiles Watermap = 28, Mrf = 29, DistantLights = 30, + Ypdb = 31, } diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index 874764e..1d33e4c 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -215,6 +215,7 @@ namespace CodeWalker.GameFiles //TestYvrs(); //TestYwrs(); //TestYmaps(); + //TestYpdbs(); //TestMrfs(); //TestPlacements(); //TestDrawables(); @@ -4526,6 +4527,48 @@ namespace CodeWalker.GameFiles } } } + public void TestYpdbs() + { + foreach (RpfFile file in AllRpfs) + { + foreach (RpfEntry entry in file.AllEntries) + { + var rfe = entry as RpfFileEntry; + if (rfe == null) continue; + + try + { + if (rfe.NameLower.EndsWith(".ypdb")) + { + UpdateStatus(string.Format(entry.Path)); + YpdbFile ypdb = RpfMan.GetFile(entry); + if (ypdb != null) + { + var odata = entry.File.ExtractFile(entry as RpfFileEntry); + var ndata = ypdb.Save(); + if (ndata.Length == odata.Length) + { + for (int i = 0; i < ndata.Length; i++) + { + if (ndata[i] != odata[i]) + { break; } + } + } + else + { } + } + else + { } + } + } + catch (Exception ex) + { + UpdateStatus("Error! " + ex.ToString()); + } + + } + } + } public void TestMrfs() { foreach (RpfFile file in AllRpfs) diff --git a/CodeWalker/ExploreForm.cs b/CodeWalker/ExploreForm.cs index 6209187..a78c984 100644 --- a/CodeWalker/ExploreForm.cs +++ b/CodeWalker/ExploreForm.cs @@ -309,6 +309,7 @@ namespace CodeWalker InitFileType(".awc", "Audio Wave Container", 22, FileTypeAction.ViewAwc, true); InitFileType(".rel", "Audio Data (REL)", 23, FileTypeAction.ViewRel, true); InitFileType(".nametable", "Name Table", 5, FileTypeAction.ViewNametable); + InitFileType(".ypdb", "Pose Matcher Database", 9, FileTypeAction.ViewYpdb); InitSubFileType(".dat", "cache_y.dat", "Cache File", 6, FileTypeAction.ViewCacheDat, true); InitSubFileType(".dat", "heightmap.dat", "Heightmap", 6, FileTypeAction.ViewHeightmap, true); @@ -1545,6 +1546,9 @@ namespace CodeWalker case FileTypeAction.ViewDistantLights: ViewDistantLights(name, path, data, fe); break; + case FileTypeAction.ViewYpdb: + ViewYpdb(name, path, data, fe); + break; case FileTypeAction.ViewHex: default: ViewHex(name, path, data); @@ -1806,6 +1810,13 @@ namespace CodeWalker f.Show(); f.LoadFile(dlf, dlf.RpfFileEntry); } + private void ViewYpdb(string name, string path, byte[] data, RpfFileEntry e) + { + var ypdb = RpfFile.GetFile(e, data); + GenericForm f = new GenericForm(this); + f.Show(); + f.LoadFile(ypdb, ypdb.RpfFileEntry); + } private RpfFileEntry CreateFileEntry(string name, string path, ref byte[] data) { @@ -4878,6 +4889,7 @@ namespace CodeWalker ViewMrf = 24, ViewNametable = 25, ViewDistantLights = 26, + ViewYpdb = 27, }