using SharpDX; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; namespace CodeWalker.GameFiles { [TypeConverter(typeof(ExpandableObjectConverter))] public class ShaderGroup : ResourceSystemBlock { public override long BlockLength { get { return 64; } } // structure data public uint VFT { get; set; } public uint Unknown_4h = 1; // 0x00000001 public ulong TextureDictionaryPointer { get; set; } public ulong ShadersPointer { get; set; } public ushort ShadersCount1 { get; set; } public ushort ShadersCount2 { get; set; } public uint Unknown_1Ch; // 0x00000000 public ulong Unknown_20h; // 0x0000000000000000 public ulong Unknown_28h; // 0x0000000000000000 public uint Unknown_30h { get; set; }//wtf is this?? (shadercount-1)*3+8 ..? public uint Unknown_34h; // 0x00000000 public ulong Unknown_38h; // 0x0000000000000000 // reference data public TextureDictionary TextureDictionary { get; set; } public ResourcePointerArray64 Shaders { get; set; } public int TotalParameters { get { int c = 0; if (Shaders?.data_items != null) { foreach (var s in Shaders.data_items) { c += s.ParameterCount; } } return c; } } public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.VFT = reader.ReadUInt32(); this.Unknown_4h = reader.ReadUInt32(); this.TextureDictionaryPointer = reader.ReadUInt64(); this.ShadersPointer = reader.ReadUInt64(); this.ShadersCount1 = reader.ReadUInt16(); this.ShadersCount2 = reader.ReadUInt16(); this.Unknown_1Ch = reader.ReadUInt32(); this.Unknown_20h = reader.ReadUInt64(); this.Unknown_28h = reader.ReadUInt64(); this.Unknown_30h = reader.ReadUInt32(); this.Unknown_34h = reader.ReadUInt32(); this.Unknown_38h = reader.ReadUInt64(); // read reference data this.TextureDictionary = reader.ReadBlockAt( this.TextureDictionaryPointer // offset ); this.Shaders = reader.ReadBlockAt>( this.ShadersPointer, // offset this.ShadersCount1 ); // wtf is Unknown_30h ??? //switch (ShadersCount1) //{ // case 1: if ((Unknown_30h != 8) && (Unknown_30h != 25)) // { } // break; // case 2: if ((Unknown_30h != 11) && (Unknown_30h != 61) && (Unknown_30h != 50) && (Unknown_30h != 51)) // { } // break; // case 3: if ((Unknown_30h != 15) && (Unknown_30h != 78)) // { } // break; // case 4: if ((Unknown_30h != 18) && (Unknown_30h != 108)) // { } // break; // case 5: if ((Unknown_30h != 22) && (Unknown_30h != 135) && (Unknown_30h != 137)) // { } // break; // case 6: if (Unknown_30h != 25) // { } // break; // case 7: if (Unknown_30h != 29) // { } // break; // case 8: if (Unknown_30h != 32) // { } // break; // case 9: if (Unknown_30h != 36) // { } // break; // case 10: if (Unknown_30h != 39) // { } // break; // case 11: if (Unknown_30h != 43) // { } // break; // case 12: if (Unknown_30h != 46) // { } // break; // case 13: if (Unknown_30h != 50) // { } // break; // case 14: if (Unknown_30h != 53) // { } // break; // case 15: if (Unknown_30h != 57) // { } // break; // case 16: if (Unknown_30h != 60) // { } // break; // case 17: if (Unknown_30h != 64) // { } // break; // case 18: if (Unknown_30h != 67) // { } // break; // case 19: if (Unknown_30h != 71) // { } // break; // case 20: if (Unknown_30h != 74) // { } // break; // default: // break; //} //var cnt = 8 + ((ShadersCount1 > 0) ? ShadersCount1-1 : 0) * 3; //if (cnt != Unknown_30h) //{ } //if (Unknown_4h != 1) //{ } //if (Unknown_1Ch != 0) //{ } //if (Unknown_20h != 0) //{ } //if (Unknown_28h != 0) //{ } //if (Unknown_34h != 0) //{ } //if (Unknown_38h != 0) //{ } } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data this.TextureDictionaryPointer = (ulong)(this.TextureDictionary != null ? this.TextureDictionary.FilePosition : 0); this.ShadersPointer = (ulong)(this.Shaders != null ? this.Shaders.FilePosition : 0); this.ShadersCount1 = (ushort)(this.Shaders != null ? this.Shaders.Count : 0); this.ShadersCount2 = this.ShadersCount1; // write structure data writer.Write(this.VFT); writer.Write(this.Unknown_4h); writer.Write(this.TextureDictionaryPointer); writer.Write(this.ShadersPointer); writer.Write(this.ShadersCount1); writer.Write(this.ShadersCount2); writer.Write(this.Unknown_1Ch); writer.Write(this.Unknown_20h); writer.Write(this.Unknown_28h); writer.Write(this.Unknown_30h); writer.Write(this.Unknown_34h); writer.Write(this.Unknown_38h); } public void WriteXml(StringBuilder sb, int indent, string ddsfolder) { YdrXml.ValueTag(sb, indent, "Unknown30", Unknown_30h.ToString()); if (TextureDictionary != null) { TextureDictionary.WriteXmlNode(TextureDictionary, sb, indent, ddsfolder, "TextureDictionary"); } YdrXml.WriteItemArray(sb, Shaders?.data_items, indent, "Shaders"); } public void ReadXml(XmlNode node, string ddsfolder) { Unknown_30h = Xml.GetChildUIntAttribute(node, "Unknown30", "value"); var tnode = node.SelectSingleNode("TextureDictionary"); if (tnode != null) { TextureDictionary = TextureDictionary.ReadXmlNode(tnode, ddsfolder); } var shaders = XmlMeta.ReadItemArray(node, "Shaders"); if (shaders != null) { Shaders = new ResourcePointerArray64(); Shaders.data_items = shaders; } if ((shaders != null) && (TextureDictionary != null)) { foreach (var shader in shaders) { var sparams = shader?.ParametersList?.Parameters; if (sparams != null) { foreach (var sparam in sparams) { if (sparam.Data is TextureBase tex) { var tex2 = TextureDictionary.Lookup(tex.NameHash); if (tex2 != null) { sparam.Data = tex2;//swap the parameter out for the embedded texture } } } } } } } public override IResourceBlock[] GetReferences() { var list = new List(); if (TextureDictionary != null) list.Add(TextureDictionary); if (Shaders != null) list.Add(Shaders); return list.ToArray(); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class ShaderFX : ResourceSystemBlock, IMetaXmlItem { public override long BlockLength { get { return 48; } } // structure data public ulong ParametersPointer { get; set; } public MetaHash Name { get; set; } //decal_emissive_only, emissive, spec public uint Unknown_Ch; // 0x00000000 public byte ParameterCount { get; set; } public byte RenderBucket { get; set; } // 2, 0, public ushort Unknown_12h { get; set; } = 32768; // 32768 HasComment? public ushort ParameterSize { get; set; } //112, 208, 320 (with 16h) 10485872, 17826000, 26214720 public ushort ParameterDataSize { get; set; } //160, 272, 400 public MetaHash FileName { get; set; } //decal_emissive_only.sps, emissive.sps, spec.sps public uint Unknown_1Ch; // 0x00000000 public uint RenderBucketMask { get; set; } //65284, 65281 DrawBucketMask? (1< Parameters { get; set; } //public SimpleArrayOFFSET ParameterHashes { get; set; } public ShaderParametersBlock ParametersList { get; set; } public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.ParametersPointer = reader.ReadUInt64(); this.Name = new MetaHash(reader.ReadUInt32()); this.Unknown_Ch = reader.ReadUInt32(); this.ParameterCount = reader.ReadByte(); this.RenderBucket = reader.ReadByte(); this.Unknown_12h = reader.ReadUInt16(); this.ParameterSize = reader.ReadUInt16(); this.ParameterDataSize = reader.ReadUInt16(); this.FileName = new MetaHash(reader.ReadUInt32()); this.Unknown_1Ch = reader.ReadUInt32(); this.RenderBucketMask = reader.ReadUInt32(); this.Unknown_24h = reader.ReadUInt16(); this.Unknown_26h = reader.ReadByte(); this.TextureParametersCount = reader.ReadByte(); this.Unknown_28h = reader.ReadUInt64(); // read reference data this.ParametersList = reader.ReadBlockAt( this.ParametersPointer, // offset this.ParameterCount ); //// just testing... //if (Unknown_12h != 32768) //{ // if (Unknown_12h != 0)//des_aquaduct_root, rig_root_skin.... destructions? // { }//no hit //} //if (RenderBucketMask != ((1 << RenderBucket) | 0xFF00)) //{ }//no hit //if (ParameterSize != ParametersList?.ParametersSize) //{ }//no hit ////if (ParameterDataSize != ParametersList?.ParametersDataSize) //{ // var diff = ParameterDataSize - (ParametersList?.BlockLength ?? 0); // switch (diff) // { // case 32: // case 36: // case 40: // case 44: // break; // default: // break;//no hit // } //} //if (Unknown_24h != 0) //{ }//no hit //if (Unknown_26h != 0) //{ }//no hit //if (Unknown_Ch != 0) //{ }//no hit //if (Unknown_1Ch != 0) //{ }//no hit //if (Unknown_28h != 0) //{ }//no hit } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data this.ParametersPointer = (ulong)(this.ParametersList != null ? this.ParametersList.FilePosition : 0); this.ParameterCount = (byte)(this.ParametersList != null ? this.ParametersList.Count : 0); // write structure data writer.Write(this.ParametersPointer); writer.Write(this.Name.Hash); writer.Write(this.Unknown_Ch); writer.Write(this.ParameterCount); writer.Write(this.RenderBucket); writer.Write(this.Unknown_12h); writer.Write(this.ParameterSize); writer.Write(this.ParameterDataSize); writer.Write(this.FileName.Hash); writer.Write(this.Unknown_1Ch); writer.Write(this.RenderBucketMask); writer.Write(this.Unknown_24h); writer.Write(this.Unknown_26h); writer.Write(this.TextureParametersCount); writer.Write(this.Unknown_28h); } public void WriteXml(StringBuilder sb, int indent) { YdrXml.StringTag(sb, indent, "Name", YdrXml.HashString(Name)); YdrXml.StringTag(sb, indent, "FileName", YdrXml.HashString(FileName)); YdrXml.ValueTag(sb, indent, "RenderBucket", RenderBucket.ToString()); if (ParametersList != null) { YdrXml.OpenTag(sb, indent, "Parameters"); ParametersList.WriteXml(sb, indent + 1); YdrXml.CloseTag(sb, indent, "Parameters"); } } public void ReadXml(XmlNode node) { Name = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Name")); FileName = XmlMeta.GetHash(Xml.GetChildInnerText(node, "FileName")); RenderBucket = (byte)Xml.GetChildUIntAttribute(node, "RenderBucket", "value"); RenderBucketMask = ((1u << RenderBucket) | 0xFF00u); var pnode = node.SelectSingleNode("Parameters"); if (pnode != null) { ParametersList = new ShaderParametersBlock(); ParametersList.ReadXml(pnode); ParameterCount = (byte)ParametersList.Count; ParameterSize = ParametersList.ParametersSize; ParameterDataSize = ParametersList.ParametersDataSize;//is it right? } } public override IResourceBlock[] GetReferences() { var list = new List(); if (ParametersList != null) list.Add(ParametersList); return list.ToArray(); } public override string ToString() { return Name.ToString() + " (" + FileName.ToString() + ")"; } } [TypeConverter(typeof(ExpandableObjectConverter))] public class ShaderParameter : ResourceSystemBlock { public override long BlockLength { get { return 16; } } // structure data public byte DataType { get; set; } //0: texture, 1: vector4 public byte Unknown_1h { get; set; } public ushort Unknown_2h; // 0x0000 public uint Unknown_4h; // 0x00000000 public ulong DataPointer { get; set; } //public IResourceBlock Data { get; set; } public object Data { get; set; } public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.DataType = reader.ReadByte(); this.Unknown_1h = reader.ReadByte(); this.Unknown_2h = reader.ReadUInt16(); this.Unknown_4h = reader.ReadUInt32(); this.DataPointer = reader.ReadUInt64(); // DONT READ DATA... } public override void Write(ResourceDataWriter writer, params object[] parameters) { // write structure data writer.Write(this.DataType); writer.Write(this.Unknown_1h); writer.Write(this.Unknown_2h); writer.Write(this.Unknown_4h); writer.Write(this.DataPointer); // DONT WRITE DATA } public override IResourceBlock[] GetReferences() { var list = new List(base.GetReferences()); //if (Data != null) list.Add(Data); return list.ToArray(); } public override string ToString() { return (Data != null) ? Data.ToString() : (DataType.ToString() + ": " + DataPointer.ToString()); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class ShaderParametersBlock : ResourceSystemBlock { public override long BlockLength { get { long offset = 0; foreach (var x in Parameters) { offset += 16; offset += 16 * x.DataType; } offset += Parameters.Length * 4; return offset; } } public ushort ParametersSize { get { ushort size = (ushort)((Parameters?.Length??0) * 16); foreach (var x in Parameters) { size += (ushort)(16 * x.DataType); } return size; } } public ushort ParametersDataSize { get { ushort size = (ushort)(BlockLength + 36);//TODO: figure out how to calculate this correctly!! return size; } } public byte TextureParamsCount { get { byte c = 0; foreach (var x in Parameters) { if (x.DataType == 0) c++; } return c; } } public ShaderParameter[] Parameters { get; set; } public MetaName[] Hashes { get; set; } public int Count { get; set; } private ResourceSystemStructBlock[] ParameterDataBlocks = null; public override void Read(ResourceDataReader reader, params object[] parameters) { Count = Convert.ToInt32(parameters[0]); var paras = new List(); for (int i = 0; i < Count; i++) { paras.Add(reader.ReadBlock()); } int offset = 0; for (int i = 0; i < Count; i++) { var p = paras[i]; // read reference data switch (p.DataType) { case 0: offset += 0; p.Data = reader.ReadBlockAt(p.DataPointer); break; case 1: offset += 16; p.Data = reader.ReadStructAt((long)p.DataPointer); break; default: offset += 16 * p.DataType; p.Data = reader.ReadStructsAt(p.DataPointer, p.DataType); break; } } reader.Position += offset; //Vector4 data gets embedded here... but why pointers in params also??? var hashes = new List(); for (int i = 0; i < Count; i++) { hashes.Add((MetaName)reader.ReadUInt32()); } Parameters = paras.ToArray(); Hashes = hashes.ToArray(); //// just testing... //for (int i = 0; i < Parameters.Length; i++) //{ // var param = Parameters[i]; // if (param.DataType == 0) // { // if (param.Unknown_1h != ((param.Data == null) ? 10 : (i + 2))) // { } // } // else // { // if (param.Unknown_1h != (160 + ((Parameters.Length - 1) - i))) // { } // } //} //if (Parameters.Length > 0) //{ // var lparam = Parameters[Parameters.Length - 1]; // switch(lparam.Unknown_1h) // { // case 192: // case 160: // case 177: // case 161: // case 156: // case 162: // case 157: // case 149: // case 178: // case 72: // case 153: // case 133: // break; // case 64://in ydd's // case 130: // case 180: // break; // default: // break; // } //} } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update pointers... //foreach (var f in Parameters) // if (f.Data != null) // f.DataPointer = (ulong)f.Data.Position; // else // f.DataPointer = 0; for (int i = 0; i < Parameters.Length; i++) { var param = Parameters[i]; if (param.DataType == 0) { param.DataPointer = (ulong)((param.Data as TextureBase)?.FilePosition ?? 0); } else { var block = (i < ParameterDataBlocks?.Length) ? ParameterDataBlocks[i] : null; if (block != null) { param.DataPointer = (ulong)block.FilePosition; } else { param.DataPointer = 0;//shouldn't happen! } } } // write parameter infos foreach (var f in Parameters) writer.WriteBlock(f); // write vector data //foreach (var f in Parameters) //{ // if (f.DataType != 0) // writer.WriteBlock(f.Data); //} for (int i = 0; i < Parameters.Length; i++) { var param = Parameters[i]; if (param.DataType != 0) { var block = (i < ParameterDataBlocks?.Length) ? ParameterDataBlocks[i] : null; if (block != null) { writer.WriteBlock(block); } else { } //shouldn't happen! } } // write hashes foreach (var h in Hashes) writer.Write((uint)h); } public void WriteXml(StringBuilder sb, int indent) { var cind = indent + 1; for (int i = 0; i < Count; i++) { var param = Parameters[i]; var name = (ShaderParamNames)Hashes[i]; var typestr = ""; if (param.DataType == 0) typestr = "Texture"; else if (param.DataType == 1) typestr = "Vector"; else if (param.DataType > 1) typestr = "Array"; var otstr = "Item name=\"" + name.ToString() + "\" type=\"" + typestr + "\""; if (param.DataType == 0) { if (param.Data is TextureBase tex) { YdrXml.OpenTag(sb, indent, otstr); YdrXml.StringTag(sb, cind, "Name", YdrXml.XmlEscape(tex.Name)); YdrXml.ValueTag(sb, cind, "Unk32", tex.Unknown_32h.ToString()); YdrXml.CloseTag(sb, indent, "Item"); } else { YdrXml.SelfClosingTag(sb, indent, otstr); } } else if (param.DataType == 1) { if (param.Data is Vector4 vec) { YdrXml.SelfClosingTag(sb, indent, otstr + " " + FloatUtil.GetVector4XmlString(vec)); } else { YdrXml.SelfClosingTag(sb, indent, otstr); } } else { if (param.Data is Vector4[] arr) { YdrXml.OpenTag(sb, indent, otstr); foreach (var vec in arr) { YdrXml.SelfClosingTag(sb, cind, "Value " + FloatUtil.GetVector4XmlString(vec)); } YdrXml.CloseTag(sb, indent, "Item"); } else { YdrXml.SelfClosingTag(sb, indent, otstr); } } } } public void ReadXml(XmlNode node) { var plist = new List(); var hlist = new List(); var pnodes = node.SelectNodes("Item"); foreach (XmlNode pnode in pnodes) { var p = new ShaderParameter(); var h = (MetaName)(uint)XmlMeta.GetHash(Xml.GetStringAttribute(pnode, "name")?.ToLowerInvariant()); var type = Xml.GetStringAttribute(pnode, "type"); if (type == "Texture") { p.DataType = 0; if (pnode.SelectSingleNode("Name") != null) { var tex = new TextureBase(); tex.ReadXml(pnode, null);//embedded textures will get replaced in ShaderFX ReadXML p.Data = tex; } } else if (type == "Vector") { p.DataType = 1; float fx = Xml.GetFloatAttribute(pnode, "x"); float fy = Xml.GetFloatAttribute(pnode, "y"); float fz = Xml.GetFloatAttribute(pnode, "z"); float fw = Xml.GetFloatAttribute(pnode, "w"); p.Data = new Vector4(fx, fy, fz, fw); } else if (type == "Array") { var vecs = new List(); var inodes = pnode.SelectNodes("Value"); foreach (XmlNode inode in inodes) { float fx = Xml.GetFloatAttribute(inode, "x"); float fy = Xml.GetFloatAttribute(inode, "y"); float fz = Xml.GetFloatAttribute(inode, "z"); float fw = Xml.GetFloatAttribute(inode, "w"); vecs.Add(new Vector4(fx, fy, fz, fw)); } p.Data = vecs.ToArray(); p.DataType = (byte)vecs.Count; } plist.Add(p); hlist.Add(h); } Parameters = plist.ToArray(); Hashes = hlist.ToArray(); Count = plist.Count; for (int i = 0; i < Parameters.Length; i++) { var param = Parameters[i]; if (param.DataType == 0) { param.Unknown_1h = (byte)(i + 2);//wtf and why } } var offset = 160; for (int i = Parameters.Length - 1; i >= 0; i--) { var param = Parameters[i]; if (param.DataType != 0) { param.Unknown_1h = (byte)offset;//wtf and why offset += param.DataType; } } } public override IResourceBlock[] GetReferences() { var list = new List(); list.AddRange(base.GetReferences()); foreach (var x in Parameters) if (x.DataType == 0) list.Add(x.Data as TextureBase); return list.ToArray(); } public override Tuple[] GetParts() { var list = new List>(); list.AddRange(base.GetParts()); long offset = 0; foreach (var x in Parameters) { list.Add(new Tuple(offset, x)); offset += 16; } var blist = new List>(); foreach (var x in Parameters) { if (x.DataType != 0) { var vecs = x.Data as Vector4[]; if (vecs == null) { vecs = new[] { (Vector4)x.Data }; } if (vecs == null) { } var block = new ResourceSystemStructBlock(vecs); list.Add(new Tuple(offset, block)); blist.Add(block); } else { blist.Add(null); } offset += 16 * x.DataType; } ParameterDataBlocks = blist.ToArray(); return list.ToArray(); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class Skeleton : ResourceSystemBlock { public override long BlockLength { get { return 112; } } // structure data public uint VFT { get; set; } public uint Unknown_4h { get; set; } = 1; // 0x00000001 public ulong Unknown_8h; // 0x0000000000000000 public ulong BoneTagsPointer { get; set; } public ushort BoneTagsCapacity { get; set; } public ushort BoneTagsCount { get; set; } public FlagsUint Unknown_1Ch { get; set; } public ulong BonesPointer { get; set; } public ulong TransformationsInvertedPointer { get; set; } public ulong TransformationsPointer { get; set; } public ulong ParentIndicesPointer { get; set; } public ulong ChildIndicesPointer { get; set; } public ulong Unknown_48h; // 0x0000000000000000 public MetaHash Unknown_50h { get; set; } public MetaHash Unknown_54h { get; set; } public MetaHash Unknown_58h { get; set; } public ushort Unknown_5Ch { get; set; } = 1; // 0x0001 public ushort BonesCount { get; set; } public ushort ChildIndicesCount { get; set; } public ushort Unknown_62h; // 0x0000 public uint Unknown_64h; // 0x00000000 public ulong Unknown_68h; // 0x0000000000000000 // reference data public ResourcePointerArray64 BoneTags { get; set; } public ResourceSimpleArray Bones { get; set; } public Matrix[] TransformationsInverted { get; set; } public Matrix[] Transformations { get; set; } public short[] ParentIndices { get; set; } public short[] ChildIndices { get; set; }//mapping child->parent indices, first child index, then parent private ResourceSystemStructBlock TransformationsInvertedBlock = null;//for saving only private ResourceSystemStructBlock TransformationsBlock = null; private ResourceSystemStructBlock ParentIndicesBlock = null; private ResourceSystemStructBlock ChildIndicesBlock = null; public Dictionary BonesMap { get; set; }//for convienience finding bones by tag public Matrix3_s[] BoneTransforms; //for rendering public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.VFT = reader.ReadUInt32(); this.Unknown_4h = reader.ReadUInt32(); this.Unknown_8h = reader.ReadUInt64(); this.BoneTagsPointer = reader.ReadUInt64(); this.BoneTagsCapacity = reader.ReadUInt16(); this.BoneTagsCount = reader.ReadUInt16(); this.Unknown_1Ch = reader.ReadUInt32(); this.BonesPointer = reader.ReadUInt64(); this.TransformationsInvertedPointer = reader.ReadUInt64(); this.TransformationsPointer = reader.ReadUInt64(); this.ParentIndicesPointer = reader.ReadUInt64(); this.ChildIndicesPointer = reader.ReadUInt64(); this.Unknown_48h = reader.ReadUInt64(); this.Unknown_50h = new MetaHash(reader.ReadUInt32()); this.Unknown_54h = new MetaHash(reader.ReadUInt32()); this.Unknown_58h = new MetaHash(reader.ReadUInt32()); this.Unknown_5Ch = reader.ReadUInt16(); this.BonesCount = reader.ReadUInt16(); this.ChildIndicesCount = reader.ReadUInt16(); this.Unknown_62h = reader.ReadUInt16(); this.Unknown_64h = reader.ReadUInt32(); this.Unknown_68h = reader.ReadUInt64(); // read reference data this.BoneTags = reader.ReadBlockAt>( this.BoneTagsPointer, // offset this.BoneTagsCapacity ); this.Bones = reader.ReadBlockAt>( this.BonesPointer, // offset this.BonesCount ); this.TransformationsInverted = reader.ReadStructsAt(this.TransformationsInvertedPointer, this.BonesCount); this.Transformations = reader.ReadStructsAt(this.TransformationsPointer, this.BonesCount); this.ParentIndices = reader.ReadShortsAt(this.ParentIndicesPointer, this.BonesCount); this.ChildIndices = reader.ReadShortsAt(this.ChildIndicesPointer, this.ChildIndicesCount); AssignBoneParents(); BuildBonesMap(); //BuildIndices();//testing! //BuildBoneTags();//testing! //BuildTransformations();//testing! //if (BoneTagsCount != Math.Min(BonesCount, BoneTagsCapacity)) //{ }//no hits //if (Unknown_8h != 0) //{ } //if (Unknown_48h != 0) //{ } //if (Unknown_62h != 0) //{ } //if (Unknown_64h != 0) //{ } //if (Unknown_68h != 0) //{ } } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data this.BoneTagsPointer = (ulong)(this.BoneTags != null ? this.BoneTags.FilePosition : 0); this.BoneTagsCapacity = (ushort)(this.BoneTags != null ? this.BoneTags.Count : 0); this.BonesPointer = (ulong)(this.Bones != null ? this.Bones.FilePosition : 0); this.TransformationsInvertedPointer = (ulong)(this.TransformationsInvertedBlock != null ? this.TransformationsInvertedBlock.FilePosition : 0); this.TransformationsPointer = (ulong)(this.TransformationsBlock != null ? this.TransformationsBlock.FilePosition : 0); this.ParentIndicesPointer = (ulong)(this.ParentIndicesBlock != null ? this.ParentIndicesBlock.FilePosition : 0); this.ChildIndicesPointer = (ulong)(this.ChildIndicesBlock != null ? this.ChildIndicesBlock.FilePosition : 0); this.BonesCount = (ushort)(this.Bones != null ? this.Bones.Count : 0); this.ChildIndicesCount = (ushort)(this.ChildIndicesBlock != null ? this.ChildIndicesBlock.ItemCount : 0); this.BoneTagsCount = Math.Min(BonesCount, BoneTagsCapacity); // write structure data writer.Write(this.VFT); writer.Write(this.Unknown_4h); writer.Write(this.Unknown_8h); writer.Write(this.BoneTagsPointer); writer.Write(this.BoneTagsCapacity); writer.Write(this.BoneTagsCount); writer.Write(this.Unknown_1Ch); writer.Write(this.BonesPointer); writer.Write(this.TransformationsInvertedPointer); writer.Write(this.TransformationsPointer); writer.Write(this.ParentIndicesPointer); writer.Write(this.ChildIndicesPointer); writer.Write(this.Unknown_48h); writer.Write(this.Unknown_50h); writer.Write(this.Unknown_54h); writer.Write(this.Unknown_58h); writer.Write(this.Unknown_5Ch); writer.Write(this.BonesCount); writer.Write(this.ChildIndicesCount); writer.Write(this.Unknown_62h); writer.Write(this.Unknown_64h); writer.Write(this.Unknown_68h); } public void WriteXml(StringBuilder sb, int indent) { YdrXml.ValueTag(sb, indent, "Unknown1C", Unknown_1Ch.Value.ToString()); YdrXml.ValueTag(sb, indent, "Unknown50", Unknown_50h.Hash.ToString()); YdrXml.ValueTag(sb, indent, "Unknown54", Unknown_54h.Hash.ToString()); YdrXml.ValueTag(sb, indent, "Unknown58", Unknown_58h.Hash.ToString()); if (Bones?.Data != null) { YdrXml.WriteItemArray(sb, Bones.Data.ToArray(), indent, "Bones"); } } public void ReadXml(XmlNode node) { Unknown_1Ch = Xml.GetChildUIntAttribute(node, "Unknown1C", "value"); Unknown_50h = Xml.GetChildUIntAttribute(node, "Unknown50", "value"); Unknown_54h = Xml.GetChildUIntAttribute(node, "Unknown54", "value"); Unknown_58h = Xml.GetChildUIntAttribute(node, "Unknown58", "value"); var bones = XmlMeta.ReadItemArray(node, "Bones"); if (bones != null) { Bones = new ResourceSimpleArray(); Bones.Data = bones.ToList(); } BuildIndices(); BuildBoneTags(); AssignBoneParents(); BuildTransformations(); BuildBonesMap(); } public override IResourceBlock[] GetReferences() { BuildTransformations(); var list = new List(); if (BoneTags != null) list.Add(BoneTags); if (Bones != null) list.Add(Bones); if (TransformationsInverted != null) { TransformationsInvertedBlock = new ResourceSystemStructBlock(TransformationsInverted); list.Add(TransformationsInvertedBlock); } if (Transformations != null) { TransformationsBlock = new ResourceSystemStructBlock(Transformations); list.Add(TransformationsBlock); } if (ParentIndices != null) { ParentIndicesBlock = new ResourceSystemStructBlock(ParentIndices); list.Add(ParentIndicesBlock); } if (ChildIndices != null) { ChildIndicesBlock = new ResourceSystemStructBlock(ChildIndices); list.Add(ChildIndicesBlock); } return list.ToArray(); } public void AssignBoneParents() { if ((Bones != null) && (ParentIndices != null)) { var maxcnt = Math.Min(Bones.Count, ParentIndices.Length); for (int i = 0; i < maxcnt; i++) { var bone = Bones[i]; var pind = ParentIndices[i]; if ((pind >= 0) && (pind < Bones.Count)) { bone.Parent = Bones[pind]; } } } } public void BuildBonesMap() { BonesMap = new Dictionary(); if (Bones != null) { for (int i = 0; i < Bones.Count; i++) { var bone = Bones[i]; BonesMap[bone.Tag] = bone; bone.UpdateAnimTransform(); bone.BindTransformInv = (i < (TransformationsInverted?.Length ?? 0)) ? TransformationsInverted[i] : Matrix.Invert(bone.AnimTransform); bone.BindTransformInv.M44 = 1.0f; bone.UpdateSkinTransform(); bone.TransformUnk = (i < (Transformations?.Length ?? 0)) ? Transformations[i].Column4 : Vector4.Zero;//still dont know what this is } } } public void BuildIndices() { var parents = new List(); var childs = new List(); if (Bones != null) { Bone lastbone = null; for (int i = 0; i < Bones.Count; i++) { var bone = Bones[i]; var pind = bone.ParentIndex; parents.Add(pind); if (pind >= 0) { childs.Add(bone.Index); childs.Add(pind); lastbone = bone; } } if (lastbone != null) { var npad = 8 - (childs.Count % 8); if (npad < 8) { for (int i = 0; i < npad; i += 2) { childs.Add(lastbone.Index); childs.Add(lastbone.ParentIndex); } } } ////just testing - not really working properly - how to generate these arrays identical to originals? seem to have weird repeats? (not just end padding) //var numchilds = ChildIndices?.Length ?? 0; //if (numchilds < childs.Count) //{ } //else //{ // for (int i = 0; i < numchilds; i++) // { // var oc = ChildIndices[i]; // var nc = childs[i]; // if (nc != oc) // { } // } //} } ParentIndices = (parents.Count > 0) ? parents.ToArray() : null; ChildIndices = (childs.Count > 0) ? childs.ToArray() : null; } public void BuildBoneTags() { var tags = new List(); if (Bones?.Data != null) { for (int i = 0; i < Bones.Count; i++) { var bone = Bones[i]; var tag = new SkeletonBoneTag(); tag.BoneTag = bone.Tag; tag.BoneIndex = (uint)i; tags.Add(tag); } } if (tags.Count < 2) { if (BoneTags != null) { } BoneTags = null; return; } var numbuckets = GetNumHashBuckets(tags.Count); var buckets = new List[numbuckets]; foreach (var tag in tags) { var b = tag.BoneTag % numbuckets; var bucket = buckets[b]; if (bucket == null) { bucket = new List(); buckets[b] = bucket; } bucket.Add(tag); } var newtags = new List(); foreach (var b in buckets) { if ((b?.Count ?? 0) == 0) newtags.Add(null); else { b.Reverse(); newtags.Add(b[0]); var p = b[0]; for (int i = 1; i < b.Count; i++) { var c = b[i]; c.Next = null; p.Next = c; p = c; } } } //if (BoneTags?.data_items != null) //just testing - all ok //{ // var numtags = BoneTags.data_items.Length; // if (numbuckets != numtags) // { } // else // { // for (int i = 0; i < numtags; i++) // { // var ot = BoneTags.data_items[i]; // var nt = newtags[i]; // if ((ot == null) != (nt == null)) // { } // else if (ot != null) // { // if (ot.BoneIndex != nt.BoneIndex) // { } // if (ot.BoneTag != nt.BoneTag) // { } // } // } // } //} BoneTags = new ResourcePointerArray64(); BoneTags.data_items = newtags.ToArray(); } public void BuildTransformations() { var transforms = new List(); var transformsinv = new List(); if (Bones?.Data != null) { foreach (var bone in Bones.Data) { var pos = bone.Translation; var ori = bone.Rotation; var sca = bone.Scale; var m = Matrix.AffineTransformation(1.0f, ori, pos);//(local transform) m.ScaleVector *= sca; m.Column4 = bone.TransformUnk;// new Vector4(0, 4, -3, 0);//??? var pbone = bone.Parent; while (pbone != null) { pos = pbone.Rotation.Multiply(pos /** pbone.Scale*/) + pbone.Translation; ori = pbone.Rotation * ori; pbone = pbone.Parent; } var m2 = Matrix.AffineTransformation(1.0f, ori, pos);//(global transform) var mi = Matrix.Invert(m2); mi.Column4 = Vector4.Zero; transforms.Add(m); transformsinv.Add(mi); } } //if (Transformations != null) //just testing! - all ok //{ // if (Transformations.Length != transforms.Count) // { } // else // { // for (int i = 0; i < Transformations.Length; i++) // { // if (Transformations[i].Column1 != transforms[i].Column1) // { } // if (Transformations[i].Column2 != transforms[i].Column2) // { } // if (Transformations[i].Column3 != transforms[i].Column3) // { } // if (Transformations[i].Column4 != transforms[i].Column4) // { } // } // } // if (TransformationsInverted.Length != transformsinv.Count) // { } // else // { // for (int i = 0; i < TransformationsInverted.Length; i++) // { // if (TransformationsInverted[i].Column4 != transformsinv[i].Column4) // { } // } // } //} Transformations = (transforms.Count > 0) ? transforms.ToArray() : null; TransformationsInverted = (transformsinv.Count > 0) ? transformsinv.ToArray() : null; } public static uint GetNumHashBuckets(int nHashes) { //todo: refactor with same in Clip.cs? if (nHashes < 11) return 11; else if (nHashes < 29) return 29; else if (nHashes < 59) return 59; else if (nHashes < 107) return 107; else if (nHashes < 191) return 191; else if (nHashes < 331) return 331; else if (nHashes < 563) return 563; else if (nHashes < 953) return 953; else if (nHashes < 1609) return 1609; else if (nHashes < 2729) return 2729; else if (nHashes < 4621) return 4621; else if (nHashes < 7841) return 7841; else if (nHashes < 13297) return 13297; else if (nHashes < 22571) return 22571; else if (nHashes < 38351) return 38351; else if (nHashes < 65167) return 65167; else /*if (nHashes < 65521)*/ return 65521; //return ((uint)nHashes / 4) * 4 + 3; } public void ResetBoneTransforms() { if (Bones?.Data == null) return; foreach (var bone in Bones.Data) { bone.ResetAnimTransform(); } UpdateBoneTransforms(); } public void UpdateBoneTransforms() { if (Bones?.Data == null) return; if ((BoneTransforms == null) || (BoneTransforms.Length != Bones.Data.Count)) { BoneTransforms = new Matrix3_s[Bones.Data.Count]; } for (int i = 0; i < Bones.Data.Count; i++) { var bone = Bones.Data[i]; Matrix b = bone.SkinTransform; Matrix3_s bt = new Matrix3_s(); bt.Row1 = b.Column1; bt.Row2 = b.Column2; bt.Row3 = b.Column3; BoneTransforms[i] = bt; } } public Skeleton Clone() { var skel = new Skeleton(); skel.BoneTagsCapacity = BoneTagsCapacity; skel.BoneTagsCount = BoneTagsCount; skel.Unknown_1Ch = Unknown_1Ch; skel.Unknown_50h = Unknown_50h; skel.Unknown_54h = Unknown_54h; skel.Unknown_58h = Unknown_58h; skel.BonesCount = BonesCount; skel.ChildIndicesCount = ChildIndicesCount; if (BoneTags != null) { skel.BoneTags = new ResourcePointerArray64(); if (BoneTags.data_items != null) { skel.BoneTags.data_items = new SkeletonBoneTag[BoneTags.data_items.Length]; for (int i = 0; i < BoneTags.data_items.Length; i++) { var obt = BoneTags.data_items[i]; var nbt = new SkeletonBoneTag(); skel.BoneTags.data_items[i] = nbt; while (obt != null) { nbt.BoneTag = obt.BoneTag; nbt.BoneIndex = obt.BoneIndex; obt = obt.Next; if (obt != null) { var nxt = new SkeletonBoneTag(); nbt.Next = nxt; nbt = nxt; } } } } } if (Bones != null) { skel.Bones = new ResourceSimpleArray(); if (Bones.Data != null) { skel.Bones.Data = new List(); for (int i = 0; i < Bones.Data.Count; i++) { var ob = Bones.Data[i]; var nb = new Bone(); nb.Rotation = ob.Rotation; nb.Translation = ob.Translation; nb.Scale = ob.Scale; nb.NextSiblingIndex = ob.NextSiblingIndex; nb.ParentIndex = ob.ParentIndex; nb.Flags = ob.Flags; nb.Index = ob.Index; nb.Tag = ob.Tag; nb.Index2 = ob.Index2; nb.Name = ob.Name; nb.AnimRotation = ob.AnimRotation; nb.AnimTranslation = ob.AnimTranslation; nb.AnimScale = ob.AnimScale; nb.AnimTransform = ob.AnimTransform; nb.BindTransformInv = ob.BindTransformInv; nb.SkinTransform = ob.SkinTransform; skel.Bones.Data.Add(nb); } } } skel.TransformationsInverted = (Matrix[])TransformationsInverted?.Clone(); skel.Transformations = (Matrix[])Transformations?.Clone(); skel.ParentIndices = (short[])ParentIndices?.Clone(); skel.ChildIndices = (short[])ChildIndices?.Clone(); skel.AssignBoneParents(); skel.BuildBonesMap(); return skel; } } [TypeConverter(typeof(ExpandableObjectConverter))] public class SkeletonBoneTag : ResourceSystemBlock { public override long BlockLength { get { return 16; } } // structure data public uint BoneTag { get; set; } public uint BoneIndex { get; set; } public ulong NextPointer { get; set; } // reference data public SkeletonBoneTag Next { get; set; } //don't know why it's linked here public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.BoneTag = reader.ReadUInt32(); this.BoneIndex = reader.ReadUInt32(); this.NextPointer = reader.ReadUInt64(); // read reference data this.Next = reader.ReadBlockAt( this.NextPointer // offset ); } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data this.NextPointer = (ulong)(this.Next != null ? this.Next.FilePosition : 0); // write structure data writer.Write(this.BoneTag); writer.Write(this.BoneIndex); writer.Write(this.NextPointer); } public override IResourceBlock[] GetReferences() { var list = new List(); if (Next != null) list.Add(Next); return list.ToArray(); } public override string ToString() { return BoneTag.ToString() + ": " + BoneIndex.ToString(); } } [Flags] public enum EBoneFlags : ushort { None = 0, RotX = 0x1, RotY = 0x2, RotZ = 0x4, LimitRotation = 0x8, TransX = 0x10, TransY = 0x20, TransZ = 0x40, LimitTranslation = 0x80, ScaleX = 0x100, ScaleY = 0x200, ScaleZ = 0x400, LimitScale = 0x800, Unk0 = 0x1000, Unk1 = 0x2000, Unk2 = 0x4000, Unk3 = 0x8000, } [TypeConverter(typeof(ExpandableObjectConverter))] public class Bone : ResourceSystemBlock, IMetaXmlItem { public override long BlockLength { get { return 80; } } // structure data public Quaternion Rotation { get; set; } public Vector3 Translation { get; set; } public uint Unknown_1Ch; // 0x00000000 RHW? public Vector3 Scale { get; set; } public float Unknown_2Ch { get; set; } = 1.0f; // 1.0 RHW? public short NextSiblingIndex { get; set; } //limb end index? IK chain? public short ParentIndex { get; set; } public uint Unknown_34h; // 0x00000000 public ulong NamePointer { get; set; } public EBoneFlags Flags { get; set; } public short Index { get; set; } public ushort Tag { get; set; } public short Index2 { get; set; }//always same as Index public ulong Unknown_48h; // 0x0000000000000000 // reference data public string Name { get; set; } public Bone Parent { get; set; } private string_r NameBlock = null; //used by CW for animating skeletons. public Quaternion AnimRotation;//relative to parent public Vector3 AnimTranslation;//relative to parent public Vector3 AnimScale; public Matrix AnimTransform;//absolute world transform, animated public Matrix BindTransformInv;//inverse of bind pose transform public Matrix SkinTransform;//transform to use for skin meshes public Vector4 TransformUnk { get; set; } //unknown value (column 4) from skeleton's transform array, used for IO purposes public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.Rotation = new Quaternion(reader.ReadVector4()); this.Translation = reader.ReadVector3(); this.Unknown_1Ch = reader.ReadUInt32(); this.Scale = reader.ReadVector3(); this.Unknown_2Ch = reader.ReadSingle(); this.NextSiblingIndex = reader.ReadInt16(); this.ParentIndex = reader.ReadInt16(); this.Unknown_34h = reader.ReadUInt32(); this.NamePointer = reader.ReadUInt64(); this.Flags = (EBoneFlags)reader.ReadUInt16(); this.Index = reader.ReadInt16(); this.Tag = reader.ReadUInt16(); this.Index2 = reader.ReadInt16(); this.Unknown_48h = reader.ReadUInt64(); // read reference data this.Name = reader.ReadStringAt(//BlockAt( this.NamePointer // offset ); //if (Index2 != Index) //{ }//no hits AnimRotation = Rotation; AnimTranslation = Translation; AnimScale = Scale; //if (Unknown_1Ch != 0) //{ } //if (Unknown_34h != 0) //{ } //if (Unknown_48h != 0) //{ } } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data this.NamePointer = (ulong)(this.NameBlock != null ? this.NameBlock.FilePosition : 0); // write structure data writer.Write(this.Rotation.ToVector4()); writer.Write(this.Translation); writer.Write(this.Unknown_1Ch); writer.Write(this.Scale); writer.Write(this.Unknown_2Ch); writer.Write(this.NextSiblingIndex); writer.Write(this.ParentIndex); writer.Write(this.Unknown_34h); writer.Write(this.NamePointer); writer.Write((ushort)this.Flags); writer.Write(this.Index); writer.Write(this.Tag); writer.Write(this.Index2); writer.Write(this.Unknown_48h); } public void WriteXml(StringBuilder sb, int indent) { YdrXml.StringTag(sb, indent, "Name", Name); YdrXml.ValueTag(sb, indent, "Tag", Tag.ToString()); YdrXml.ValueTag(sb, indent, "Index", Index.ToString()); YdrXml.ValueTag(sb, indent, "ParentIndex", ParentIndex.ToString()); YdrXml.ValueTag(sb, indent, "SiblingIndex", NextSiblingIndex.ToString()); YdrXml.StringTag(sb, indent, "Flags", Flags.ToString()); YdrXml.SelfClosingTag(sb, indent, "Translation " + FloatUtil.GetVector3XmlString(Translation)); YdrXml.SelfClosingTag(sb, indent, "Rotation " + FloatUtil.GetVector4XmlString(Rotation.ToVector4())); YdrXml.SelfClosingTag(sb, indent, "Scale " + FloatUtil.GetVector3XmlString(Scale)); YdrXml.SelfClosingTag(sb, indent, "TransformUnk " + FloatUtil.GetVector4XmlString(TransformUnk)); } public void ReadXml(XmlNode node) { Name = Xml.GetChildInnerText(node, "Name"); Tag = (ushort)Xml.GetChildUIntAttribute(node, "Tag", "value"); Index = (short)Xml.GetChildIntAttribute(node, "Index", "value"); Index2 = Index; ParentIndex = (short)Xml.GetChildIntAttribute(node, "ParentIndex", "value"); NextSiblingIndex = (short)Xml.GetChildIntAttribute(node, "SiblingIndex", "value"); Flags = Xml.GetChildEnumInnerText(node, "Flags"); Translation = Xml.GetChildVector3Attributes(node, "Translation"); Rotation = Xml.GetChildVector4Attributes(node, "Rotation").ToQuaternion(); Scale = Xml.GetChildVector3Attributes(node, "Scale"); TransformUnk = Xml.GetChildVector4Attributes(node, "TransformUnk"); } public override IResourceBlock[] GetReferences() { var list = new List(); if (Name != null) { NameBlock = (string_r)Name; list.Add(NameBlock); } return list.ToArray(); } public override string ToString() { return Tag.ToString() + ": " + Name; } public void UpdateAnimTransform() { //AnimTransform = Matrix.AffineTransformation(1.0f, AnimRotation, AnimTranslation);//(local transform) var pos = AnimTranslation; var ori = AnimRotation; var sca = AnimScale; var pbone = Parent; while (pbone != null) { pos = pbone.AnimRotation.Multiply(pos /** pbone.AnimScale*/) + pbone.AnimTranslation; ori = pbone.AnimRotation * ori; pbone = pbone.Parent; } AnimTransform = Matrix.AffineTransformation(1.0f, ori, pos);//(global transform) AnimTransform.ScaleVector *= sca; } public void UpdateSkinTransform() { SkinTransform = BindTransformInv * AnimTransform; //SkinTransform = Matrix.Identity;//(for testing) } public void ResetAnimTransform() { AnimRotation = Rotation; AnimTranslation = Translation; AnimScale = Scale; UpdateAnimTransform(); UpdateSkinTransform(); } public static uint ElfHash_Uppercased(string str) { uint hash = 0; uint x = 0; uint i = 0; for (i = 0; i < str.Length; i++) { var c = ((byte)str[(int)i]); if ((byte)(c - 'a') <= 25u) // to uppercase c -= 32; hash = (hash << 4) + c; if ((x = hash & 0xF0000000) != 0) { hash ^= (x >> 24); } hash &= ~x; } return hash; } public static ushort CalculateBoneHash(string boneName) { return (ushort)(ElfHash_Uppercased(boneName) % 0xFE8F + 0x170); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class Joints : ResourceSystemBlock { public override long BlockLength { get { return 64; } } // structure data public uint VFT { get; set; } public uint Unknown_4h = 1; // 0x00000001 public ulong Unknown_8h; // 0x0000000000000000 public ulong RotationLimitsPointer { get; set; } public ulong TranslationLimitsPointer { get; set; } public ulong Unknown_20h; // 0x0000000000000000 public ulong Unknown_28h; // 0x0000000000000000 public ushort RotationLimitsCount { get; set; } public ushort TranslationLimitsCount { get; set; } public ushort Unknown_34h; // 0x0000 public ushort Unknown_36h = 1; // 0x0001 public ulong Unknown_38h; // 0x0000000000000000 // reference data public JointRotationLimit_s[] RotationLimits { get; set; } public JointTranslationLimit_s[] TranslationLimits { get; set; } private ResourceSystemStructBlock RotationLimitsBlock = null; //for saving only private ResourceSystemStructBlock TranslationLimitsBlock = null; public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.VFT = reader.ReadUInt32(); this.Unknown_4h = reader.ReadUInt32(); this.Unknown_8h = reader.ReadUInt64(); this.RotationLimitsPointer = reader.ReadUInt64(); this.TranslationLimitsPointer = reader.ReadUInt64(); this.Unknown_20h = reader.ReadUInt64(); this.Unknown_28h = reader.ReadUInt64(); this.RotationLimitsCount = reader.ReadUInt16(); this.TranslationLimitsCount = reader.ReadUInt16(); this.Unknown_34h = reader.ReadUInt16(); this.Unknown_36h = reader.ReadUInt16(); this.Unknown_38h = reader.ReadUInt64(); // read reference data this.RotationLimits = reader.ReadStructsAt(this.RotationLimitsPointer, this.RotationLimitsCount); this.TranslationLimits = reader.ReadStructsAt(this.TranslationLimitsPointer, this.TranslationLimitsCount); //if (Unknown_4h != 1) //{ } //if (Unknown_8h != 0) //{ } //if (Unknown_20h != 0) //{ } //if (Unknown_28h != 0) //{ } //if (Unknown_34h != 0) //{ } //if (Unknown_36h != 1) //{ } //if (Unknown_38h != 0) //{ } } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data this.RotationLimitsPointer = (ulong)(this.RotationLimitsBlock != null ? this.RotationLimitsBlock.FilePosition : 0); this.TranslationLimitsPointer = (ulong)(this.TranslationLimitsBlock != null ? this.TranslationLimitsBlock.FilePosition : 0); this.RotationLimitsCount = (ushort)(this.RotationLimitsBlock != null ? this.RotationLimitsBlock.ItemCount : 0); this.TranslationLimitsCount = (ushort)(this.TranslationLimitsBlock != null ? this.TranslationLimitsBlock.ItemCount : 0); // write structure data writer.Write(this.VFT); writer.Write(this.Unknown_4h); writer.Write(this.Unknown_8h); writer.Write(this.RotationLimitsPointer); writer.Write(this.TranslationLimitsPointer); writer.Write(this.Unknown_20h); writer.Write(this.Unknown_28h); writer.Write(this.RotationLimitsCount); writer.Write(this.TranslationLimitsCount); writer.Write(this.Unknown_34h); writer.Write(this.Unknown_36h); writer.Write(this.Unknown_38h); } public void WriteXml(StringBuilder sb, int indent) { if (RotationLimits != null) { YdrXml.WriteItemArray(sb, RotationLimits, indent, "RotationLimits"); } if (TranslationLimits != null) { YdrXml.WriteItemArray(sb, TranslationLimits, indent, "TranslationLimits"); } } public void ReadXml(XmlNode node) { RotationLimits = XmlMeta.ReadItemArray(node, "RotationLimits"); TranslationLimits = XmlMeta.ReadItemArray(node, "TranslationLimits"); } public override IResourceBlock[] GetReferences() { var list = new List(); if (RotationLimits != null) { RotationLimitsBlock = new ResourceSystemStructBlock(RotationLimits); list.Add(RotationLimitsBlock); } if (TranslationLimits != null) { TranslationLimitsBlock = new ResourceSystemStructBlock(TranslationLimits); list.Add(TranslationLimitsBlock); } return list.ToArray(); } } [TypeConverter(typeof(ExpandableObjectConverter))] public struct JointRotationLimit_s : IMetaXmlItem { // structure data public uint Unknown_0h { get; set; } // 0x00000000 public uint Unknown_4h { get; set; } // 0x00000000 public ushort BoneId { get; set; } public ushort Unknown_Ah { get; set; } public uint Unknown_Ch { get; set; } // 0x00000001 public uint Unknown_10h { get; set; } // 0x00000003 public uint Unknown_14h { get; set; } // 0x00000000 public uint Unknown_18h { get; set; } // 0x00000000 public uint Unknown_1Ch { get; set; } // 0x00000000 public uint Unknown_20h { get; set; } // 0x00000000 public uint Unknown_24h { get; set; } // 0x00000000 public uint Unknown_28h { get; set; } // 0x00000000 public float Unknown_2Ch { get; set; } // 1.0 public uint Unknown_30h { get; set; } // 0x00000000 public uint Unknown_34h { get; set; } // 0x00000000 public uint Unknown_38h { get; set; } // 0x00000000 public uint Unknown_3Ch { get; set; } // 0x00000000 public float Unknown_40h { get; set; } // 1.0 public uint Unknown_44h { get; set; } // 0x00000000 public uint Unknown_48h { get; set; } // 0x00000000 public uint Unknown_4Ch { get; set; } // 0x00000000 public float Unknown_50h { get; set; } // -pi public float Unknown_54h { get; set; } // pi public float Unknown_58h { get; set; } // 1.0 public Vector3 Min { get; set; } public Vector3 Max { get; set; } public float Unknown_74h { get; set; } // pi public float Unknown_78h { get; set; } // -pi public float Unknown_7Ch { get; set; } // pi public float Unknown_80h { get; set; } // pi public float Unknown_84h { get; set; } // -pi public float Unknown_88h { get; set; } // pi public float Unknown_8Ch { get; set; } // pi public float Unknown_90h { get; set; } // -pi public float Unknown_94h { get; set; } // pi public float Unknown_98h { get; set; } // pi public float Unknown_9Ch { get; set; } // -pi public float Unknown_A0h { get; set; } // pi public float Unknown_A4h { get; set; } // pi public float Unknown_A8h { get; set; } // -pi public float Unknown_ACh { get; set; } // pi public float Unknown_B0h { get; set; } // pi public float Unknown_B4h { get; set; } // -pi public float Unknown_B8h { get; set; } // pi public uint Unknown_BCh { get; set; } // 0x00000100 private void Init() { var pi = (float)Math.PI; Unknown_0h = 0; Unknown_4h = 0; BoneId = 0; Unknown_Ah = 0; Unknown_Ch = 1; Unknown_10h = 3; Unknown_14h = 0; Unknown_18h = 0; Unknown_1Ch = 0; Unknown_20h = 0; Unknown_24h = 0; Unknown_28h = 0; Unknown_2Ch = 1.0f; Unknown_30h = 0; Unknown_34h = 0; Unknown_38h = 0; Unknown_3Ch = 0; Unknown_40h = 1.0f; Unknown_44h = 0; Unknown_48h = 0; Unknown_4Ch = 0; Unknown_50h = -pi; Unknown_54h = pi; Unknown_58h = 1.0f; Min = Vector3.Zero; Max = Vector3.Zero; Unknown_74h = pi; Unknown_78h = -pi; Unknown_7Ch = pi; Unknown_80h = pi; Unknown_84h = -pi; Unknown_88h = pi; Unknown_8Ch = pi; Unknown_90h = -pi; Unknown_94h = pi; Unknown_98h = pi; Unknown_9Ch = -pi; Unknown_A0h = pi; Unknown_A4h = pi; Unknown_A8h = -pi; Unknown_ACh = pi; Unknown_B0h = pi; Unknown_B4h = -pi; Unknown_B8h = pi; Unknown_BCh = 0x100; } public void WriteXml(StringBuilder sb, int indent) { YdrXml.ValueTag(sb, indent, "BoneId", BoneId.ToString()); YdrXml.ValueTag(sb, indent, "UnknownA", Unknown_Ah.ToString()); YdrXml.SelfClosingTag(sb, indent, "Min " + FloatUtil.GetVector3XmlString(Min)); YdrXml.SelfClosingTag(sb, indent, "Max " + FloatUtil.GetVector3XmlString(Max)); } public void ReadXml(XmlNode node) { Init(); BoneId = (ushort)Xml.GetChildUIntAttribute(node, "BoneId", "value"); Unknown_Ah = (ushort)Xml.GetChildUIntAttribute(node, "UnknownA", "value"); Min = Xml.GetChildVector3Attributes(node, "Min"); Max = Xml.GetChildVector3Attributes(node, "Max"); } } [TypeConverter(typeof(ExpandableObjectConverter))] public struct JointTranslationLimit_s : IMetaXmlItem { public uint Unknown_0h { get; set; } // 0x00000000 public uint Unknown_4h { get; set; } // 0x00000000 public uint BoneId { get; set; } public uint Unknown_Ch { get; set; } // 0x00000000 public uint Unknown_10h { get; set; } // 0x00000000 public uint Unknown_14h { get; set; } // 0x00000000 public uint Unknown_18h { get; set; } // 0x00000000 public uint Unknown_1Ch { get; set; } // 0x00000000 public Vector3 Min { get; set; } public uint Unknown_2Ch { get; set; } // 0x00000000 public Vector3 Max { get; set; } public uint Unknown_3Ch { get; set; } // 0x00000000 public void WriteXml(StringBuilder sb, int indent) { YdrXml.ValueTag(sb, indent, "BoneId", BoneId.ToString()); YdrXml.SelfClosingTag(sb, indent, "Min " + FloatUtil.GetVector3XmlString(Min)); YdrXml.SelfClosingTag(sb, indent, "Max " + FloatUtil.GetVector3XmlString(Max)); } public void ReadXml(XmlNode node) { BoneId = (ushort)Xml.GetChildUIntAttribute(node, "BoneId", "value"); Min = Xml.GetChildVector3Attributes(node, "Min"); Max = Xml.GetChildVector3Attributes(node, "Max"); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class DrawableModel : ResourceSystemBlock, IMetaXmlItem { public override long BlockLength { get { return 48; } } // structure data public uint VFT { get; set; } public uint Unknown_4h = 1; // 0x00000001 public ulong GeometriesPointer { get; set; } public ushort GeometriesCount1 { get; set; } public ushort GeometriesCount2 { get; set; }//always equal to GeometriesCount1 public uint Unknown_14h; // 0x00000000 public ulong BoundsPointer { get; set; } public ulong ShaderMappingPointer { get; set; } public uint SkeletonBinding { get; set; }//4th byte is bone index, 2nd byte for skin meshes public ushort RenderMaskFlags { get; set; } //First byte is called "Mask" in GIMS EVO public ushort GeometriesCount3 { get; set; } //always equal to GeometriesCount1, is it ShaderMappingCount? public byte BoneIndex { get { return (byte)((SkeletonBinding >> 24) & 0xFF); } set { SkeletonBinding = (SkeletonBinding & 0x00FFFFFF) + ((value & 0xFFu) << 24); } } public byte SkeletonBindUnk2 //always 0 { get { return (byte)((SkeletonBinding >> 16) & 0xFF); } set { SkeletonBinding = (SkeletonBinding & 0xFF00FFFF) + ((value & 0xFFu) << 16); } } public byte HasSkin //only 0 or 1 { get { return (byte)((SkeletonBinding >> 8) & 0xFF); } set { SkeletonBinding = (SkeletonBinding & 0xFFFF00FF) + ((value & 0xFFu) << 8); } } public byte SkeletonBindUnk1 //only 0 or 43 (in rare cases, see below) { get { return (byte)((SkeletonBinding >> 0) & 0xFF); } set { SkeletonBinding = (SkeletonBinding & 0xFFFFFF00) + ((value & 0xFFu) << 0); } } public byte RenderMask { get { return (byte)((RenderMaskFlags >> 0) & 0xFF); } set { RenderMaskFlags = (ushort)((RenderMaskFlags & 0xFF00u) + ((value & 0xFFu) << 0)); } } public byte Flags { get { return (byte)((RenderMaskFlags >> 8) & 0xFF); } set { RenderMaskFlags = (ushort)((RenderMaskFlags & 0xFFu) + ((value & 0xFFu) << 8)); } } // reference data public ResourcePointerArray64 Geometries { get; set; } public AABB_s[] BoundsData { get; set; } public ushort[] ShaderMapping { get; set; } private ResourceSystemStructBlock BoundsDataBlock = null; //for saving only private ResourceSystemStructBlock ShaderMappingBlock = null; public long MemoryUsage { get { long val = 0; if ((Geometries != null) && (Geometries.data_items != null)) { foreach(var geom in Geometries.data_items) { if (geom == null) continue; if (geom.VertexData != null) { val += geom.VertexData.MemoryUsage; } if (geom.IndexBuffer != null) { val += geom.IndexBuffer.IndicesCount * 4; } if (geom.VertexBuffer != null) { if ((geom.VertexBuffer.Data1 != null) && (geom.VertexBuffer.Data1 != geom.VertexData)) { val += geom.VertexBuffer.Data1.MemoryUsage; } if ((geom.VertexBuffer.Data2 != null) && (geom.VertexBuffer.Data2 != geom.VertexData)) { val += geom.VertexBuffer.Data2.MemoryUsage; } } } } if (BoundsData != null) { val += BoundsData.Length * 32; } return val; } } public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.VFT = reader.ReadUInt32(); this.Unknown_4h = reader.ReadUInt32(); this.GeometriesPointer = reader.ReadUInt64(); this.GeometriesCount1 = reader.ReadUInt16(); this.GeometriesCount2 = reader.ReadUInt16(); this.Unknown_14h = reader.ReadUInt32(); this.BoundsPointer = reader.ReadUInt64(); this.ShaderMappingPointer = reader.ReadUInt64(); this.SkeletonBinding = reader.ReadUInt32(); this.RenderMaskFlags = reader.ReadUInt16(); this.GeometriesCount3 = reader.ReadUInt16(); // read reference data this.Geometries = reader.ReadBlockAt>( this.GeometriesPointer, // offset this.GeometriesCount1 ); this.BoundsData = reader.ReadStructsAt(this.BoundsPointer, (uint)(this.GeometriesCount1 > 1 ? this.GeometriesCount1 + 1 : this.GeometriesCount1)); this.ShaderMapping = reader.ReadUshortsAt(this.ShaderMappingPointer, this.GeometriesCount1); if (Geometries?.data_items != null) { for (int i = 0; i < Geometries.data_items.Length; i++) { var geom = Geometries.data_items[i]; if (geom != null) { geom.ShaderID = ((ShaderMapping != null) && (i < ShaderMapping.Length)) ? ShaderMapping[i] : (ushort)0; geom.AABB = (BoundsData != null) ? ((BoundsData.Length > 1) && ((i + 1) < BoundsData.Length)) ? BoundsData[i + 1] : BoundsData[0] : new AABB_s(); } } } ////just testing! //if (SkeletonBindUnk2 != 0) //{ }//no hit //switch (SkeletonBindUnk1) //{ // case 0: // break; // case 43://des_plog_light_root.ydr, des_heli_scrapyard_skin002.ydr, v_74_it1_ceiling_smoke_02_skin.ydr, buzzard2.yft, vader.yft, zombiea.yft // break; // default: // break;//no hit //} //switch (HasSkin) //{ // case 0: // case 1: // break; // default: // break;//no hit //} //if (Unknown_4h != 1) //{ }//no hit //if (Unknown_14h != 0) //{ }//no hit } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data this.GeometriesPointer = (ulong)(this.Geometries != null ? this.Geometries.FilePosition : 0); this.GeometriesCount1 = (ushort)(this.Geometries != null ? this.Geometries.Count : 0); this.GeometriesCount2 = this.GeometriesCount1;//is this correct? this.GeometriesCount3 = this.GeometriesCount1;//is this correct? this.BoundsPointer = (ulong)(this.BoundsDataBlock != null ? this.BoundsDataBlock.FilePosition : 0); this.ShaderMappingPointer = (ulong)(this.ShaderMappingBlock != null ? this.ShaderMappingBlock.FilePosition : 0); // write structure data writer.Write(this.VFT); writer.Write(this.Unknown_4h); writer.Write(this.GeometriesPointer); writer.Write(this.GeometriesCount1); writer.Write(this.GeometriesCount2); writer.Write(this.Unknown_14h); writer.Write(this.BoundsPointer); writer.Write(this.ShaderMappingPointer); writer.Write(this.SkeletonBinding); writer.Write(this.RenderMaskFlags); writer.Write(this.GeometriesCount3); } public void WriteXml(StringBuilder sb, int indent) { YdrXml.ValueTag(sb, indent, "RenderMask", RenderMask.ToString()); YdrXml.ValueTag(sb, indent, "Flags", Flags.ToString()); YdrXml.ValueTag(sb, indent, "HasSkin", HasSkin.ToString()); YdrXml.ValueTag(sb, indent, "BoneIndex", BoneIndex.ToString()); YdrXml.ValueTag(sb, indent, "Unknown1", SkeletonBindUnk1.ToString()); if (Geometries?.data_items != null) { YdrXml.WriteItemArray(sb, Geometries.data_items, indent, "Geometries"); } } public void ReadXml(XmlNode node) { RenderMask = (byte)Xml.GetChildUIntAttribute(node, "RenderMask", "value"); Flags = (byte)Xml.GetChildUIntAttribute(node, "Flags", "value"); HasSkin = (byte)Xml.GetChildUIntAttribute(node, "HasSkin", "value"); BoneIndex = (byte)Xml.GetChildUIntAttribute(node, "BoneIndex", "value"); SkeletonBindUnk1 = (byte)Xml.GetChildUIntAttribute(node, "Unknown1", "value"); var aabbs = new List(); var shids = new List(); var min = new Vector4(float.MaxValue); var max = new Vector4(float.MinValue); var geoms = XmlMeta.ReadItemArray(node, "Geometries"); if (geoms != null) { Geometries = new ResourcePointerArray64(); Geometries.data_items = geoms; foreach (var geom in geoms) { aabbs.Add(geom.AABB); shids.Add(geom.ShaderID); min = Vector4.Min(min, geom.AABB.Min); max = Vector4.Max(max, geom.AABB.Max); } GeometriesCount1 = GeometriesCount2 = GeometriesCount3 = (ushort)geoms.Length; } if (aabbs.Count > 1) { var outeraabb = new AABB_s() { Min = min, Max = max }; aabbs.Insert(0, outeraabb); } BoundsData = (aabbs.Count > 0) ? aabbs.ToArray() : null; ShaderMapping = (shids.Count > 0) ? shids.ToArray() : null; } public override IResourceBlock[] GetReferences() { var list = new List(); if (Geometries != null) list.Add(Geometries); if (BoundsData != null) { BoundsDataBlock = new ResourceSystemStructBlock(BoundsData); list.Add(BoundsDataBlock); } if (ShaderMapping != null) { ShaderMappingBlock = new ResourceSystemStructBlock(ShaderMapping); list.Add(ShaderMappingBlock); } return list.ToArray(); } public override string ToString() { return "(" + Geometries.Count + " geometr" + (Geometries.Count != 1 ? "ies)" : "y)"); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class DrawableGeometry : ResourceSystemBlock, IMetaXmlItem { public override long BlockLength { get { return 152; } } // structure data public uint VFT { get; set; } public uint Unknown_4h = 1; // 0x00000001 public ulong Unknown_8h; // 0x0000000000000000 public ulong Unknown_10h; // 0x0000000000000000 public ulong VertexBufferPointer { get; set; } public ulong Unknown_20h; // 0x0000000000000000 public ulong Unknown_28h; // 0x0000000000000000 public ulong Unknown_30h; // 0x0000000000000000 public ulong IndexBufferPointer { get; set; } public ulong Unknown_40h; // 0x0000000000000000 public ulong Unknown_48h; // 0x0000000000000000 public ulong Unknown_50h; // 0x0000000000000000 public uint IndicesCount { get; set; } public uint TrianglesCount { get; set; } public ushort VerticesCount { get; set; } public ushort Unknown_62h = 3; // 0x0003 // indices per primitive (triangle) public uint Unknown_64h; // 0x00000000 public ulong BoneIdsPointer { get; set; } public ushort VertexStride { get; set; } public ushort BoneIdsCount { get; set; } public uint Unknown_74h; // 0x00000000 public ulong VertexDataPointer { get; set; } public ulong Unknown_80h; // 0x0000000000000000 public ulong Unknown_88h; // 0x0000000000000000 public ulong Unknown_90h; // 0x0000000000000000 // reference data public VertexBuffer VertexBuffer { get; set; } public IndexBuffer IndexBuffer { get; set; } public ushort[] BoneIds { get; set; } public VertexData VertexData { get; set; } public ShaderFX Shader { get; set; }//written by parent DrawableBase, using ShaderID public ushort ShaderID { get; set; }//read/written by parent model public AABB_s AABB { get; set; }//read/written by parent model private ResourceSystemStructBlock BoneIdsBlock = null;//for saving only public bool UpdateRenderableParameters { get; set; } = false; //used by model material editor... public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.VFT = reader.ReadUInt32(); this.Unknown_4h = reader.ReadUInt32(); this.Unknown_8h = reader.ReadUInt64(); this.Unknown_10h = reader.ReadUInt64(); this.VertexBufferPointer = reader.ReadUInt64(); this.Unknown_20h = reader.ReadUInt64(); this.Unknown_28h = reader.ReadUInt64(); this.Unknown_30h = reader.ReadUInt64(); this.IndexBufferPointer = reader.ReadUInt64(); this.Unknown_40h = reader.ReadUInt64(); this.Unknown_48h = reader.ReadUInt64(); this.Unknown_50h = reader.ReadUInt64(); this.IndicesCount = reader.ReadUInt32(); this.TrianglesCount = reader.ReadUInt32(); this.VerticesCount = reader.ReadUInt16(); this.Unknown_62h = reader.ReadUInt16(); this.Unknown_64h = reader.ReadUInt32(); this.BoneIdsPointer = reader.ReadUInt64(); this.VertexStride = reader.ReadUInt16(); this.BoneIdsCount = reader.ReadUInt16(); this.Unknown_74h = reader.ReadUInt32(); this.VertexDataPointer = reader.ReadUInt64(); this.Unknown_80h = reader.ReadUInt64(); this.Unknown_88h = reader.ReadUInt64(); this.Unknown_90h = reader.ReadUInt64(); // read reference data this.VertexBuffer = reader.ReadBlockAt( this.VertexBufferPointer // offset ); this.IndexBuffer = reader.ReadBlockAt( this.IndexBufferPointer // offset ); this.BoneIds = reader.ReadUshortsAt(this.BoneIdsPointer, this.BoneIdsCount); if (this.BoneIds != null) //skinned mesh bones to use? peds, also yft props... { } if (this.VertexBuffer != null) { this.VertexData = this.VertexBuffer.Data1 ?? this.VertexBuffer.Data2; if (this.VerticesCount == 0) { this.VerticesCount = (ushort)(this.VertexData?.VertexCount ?? 0); } //if (VertexBuffer.Data1 != VertexBuffer.Data2) //{ }//no hit //if (VertexDataPointer == 0) //{ }//no hit //else if (VertexDataPointer != VertexBuffer.DataPointer1) //{ // ////some mods hit here! // //try // //{ // // this.VertexData = reader.ReadBlockAt( // // this.VertexDataPointer, // offset // // this.VertexStride, // // this.VerticesCount, // // this.VertexBuffer.Info // // ); // //} // //catch // //{ } //} //if (VertexStride != VertexBuffer.VertexStride) //{ }//no hit //if (VertexStride != (VertexBuffer.Info?.Stride ?? 0)) //{ }//no hit } //else //{ }//no hit //if (Unknown_4h != 1) //{ } //if (Unknown_8h != 0) //{ } //if (Unknown_10h != 0) //{ } //if (Unknown_20h != 0) //{ } //if (Unknown_28h != 0) //{ } //if (Unknown_30h != 0) //{ } //if (Unknown_40h != 0) //{ } //if (Unknown_48h != 0) //{ } //if (Unknown_50h != 0) //{ } //if (Unknown_64h != 0) //{ } //if (Unknown_74h != 0) //{ } //if (Unknown_80h != 0) //{ } //if (Unknown_88h != 0) //{ } //if (Unknown_90h != 0) //{ } } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data this.VertexBufferPointer = (ulong)(this.VertexBuffer != null ? this.VertexBuffer.FilePosition : 0); this.IndexBufferPointer = (ulong)(this.IndexBuffer != null ? this.IndexBuffer.FilePosition : 0); this.BoneIdsPointer = (ulong)(this.BoneIdsBlock != null ? this.BoneIdsBlock.FilePosition : 0); this.BoneIdsCount = (ushort)(this.BoneIdsBlock != null ? this.BoneIdsBlock.ItemCount : 0); this.VertexDataPointer = (ulong)(this.VertexData != null ? this.VertexData.FilePosition : 0); this.VerticesCount = (ushort)(this.VertexData != null ? this.VertexData.VertexCount : 0); //TODO: fix? this.VertexStride = (ushort)(this.VertexBuffer != null ? this.VertexBuffer.VertexStride : 0); //TODO: fix? this.IndicesCount = (this.IndexBuffer != null ? this.IndexBuffer.IndicesCount : 0); //TODO: fix? this.TrianglesCount = this.IndicesCount / 3; //TODO: fix? // write structure data writer.Write(this.VFT); writer.Write(this.Unknown_4h); writer.Write(this.Unknown_8h); writer.Write(this.Unknown_10h); writer.Write(this.VertexBufferPointer); writer.Write(this.Unknown_20h); writer.Write(this.Unknown_28h); writer.Write(this.Unknown_30h); writer.Write(this.IndexBufferPointer); writer.Write(this.Unknown_40h); writer.Write(this.Unknown_48h); writer.Write(this.Unknown_50h); writer.Write(this.IndicesCount); writer.Write(this.TrianglesCount); writer.Write(this.VerticesCount); writer.Write(this.Unknown_62h); writer.Write(this.Unknown_64h); writer.Write(this.BoneIdsPointer); writer.Write(this.VertexStride); writer.Write(this.BoneIdsCount); writer.Write(this.Unknown_74h); writer.Write(this.VertexDataPointer); writer.Write(this.Unknown_80h); writer.Write(this.Unknown_88h); writer.Write(this.Unknown_90h); } public void WriteXml(StringBuilder sb, int indent) { YdrXml.ValueTag(sb, indent, "ShaderIndex", ShaderID.ToString()); YdrXml.SelfClosingTag(sb, indent, "BoundingBoxMin " + FloatUtil.GetVector4XmlString(AABB.Min)); YdrXml.SelfClosingTag(sb, indent, "BoundingBoxMax " + FloatUtil.GetVector4XmlString(AABB.Max)); if (BoneIds != null) { var ids = new StringBuilder(); foreach (var id in BoneIds) { if (ids.Length > 0) ids.Append(", "); ids.Append(id.ToString()); } YdrXml.StringTag(sb, indent, "BoneIDs", ids.ToString()); } if (VertexBuffer != null) { YdrXml.OpenTag(sb, indent, "VertexBuffer"); VertexBuffer.WriteXml(sb, indent + 1); YdrXml.CloseTag(sb, indent, "VertexBuffer"); } if (IndexBuffer != null) { YdrXml.OpenTag(sb, indent, "IndexBuffer"); IndexBuffer.WriteXml(sb, indent + 1); YdrXml.CloseTag(sb, indent, "IndexBuffer"); } } public void ReadXml(XmlNode node) { ShaderID = (ushort)Xml.GetChildUIntAttribute(node, "ShaderIndex", "value"); var aabb = new AABB_s(); aabb.Min = Xml.GetChildVector4Attributes(node, "BoundingBoxMin"); aabb.Max = Xml.GetChildVector4Attributes(node, "BoundingBoxMax"); AABB = aabb; var bnode = node.SelectSingleNode("BoneIDs"); if (bnode != null) { var astr = bnode.InnerText; var arr = astr.Split(','); var blist = new List(); foreach (var bstr in arr) { var tstr = bstr?.Trim(); if (string.IsNullOrEmpty(tstr)) continue; if (ushort.TryParse(tstr, out ushort u)) { blist.Add(u); } } BoneIds = (blist.Count > 0) ? blist.ToArray() : null; } var vnode = node.SelectSingleNode("VertexBuffer"); if (vnode != null) { VertexBuffer = new VertexBuffer(); VertexBuffer.ReadXml(vnode); VertexData = VertexBuffer.Data1 ?? VertexBuffer.Data2; } var inode = node.SelectSingleNode("IndexBuffer"); if (inode != null) { IndexBuffer = new IndexBuffer(); IndexBuffer.ReadXml(inode); } } public override IResourceBlock[] GetReferences() { var list = new List(); if (VertexBuffer != null) list.Add(VertexBuffer); if (IndexBuffer != null) list.Add(IndexBuffer); if (BoneIds != null) { BoneIdsBlock = new ResourceSystemStructBlock(BoneIds); list.Add(BoneIdsBlock); } if (VertexData != null) list.Add(VertexData); return list.ToArray(); } public override string ToString() { return VerticesCount.ToString() + " verts, " + Shader.ToString(); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class VertexBuffer : ResourceSystemBlock { public override long BlockLength { get { return 128; } } // structure data public uint VFT { get; set; } public uint Unknown_4h = 1; // 0x00000001 public ushort VertexStride { get; set; } public ushort Flags { get; set; } //only 0 or 1024 public uint Unknown_Ch; // 0x00000000 public ulong DataPointer1 { get; set; } public uint VertexCount { get; set; } public uint Unknown_1Ch; // 0x00000000 public ulong DataPointer2 { get; set; } public ulong Unknown_28h; // 0x0000000000000000 public ulong InfoPointer { get; set; } public ulong Unknown_38h; // 0x0000000000000000 public ulong Unknown_40h; // 0x0000000000000000 public ulong Unknown_48h; // 0x0000000000000000 public ulong Unknown_50h; // 0x0000000000000000 public ulong Unknown_58h; // 0x0000000000000000 public ulong Unknown_60h; // 0x0000000000000000 public ulong Unknown_68h; // 0x0000000000000000 public ulong Unknown_70h; // 0x0000000000000000 public ulong Unknown_78h; // 0x0000000000000000 // reference data public VertexData Data1 { get; set; } public VertexData Data2 { get; set; } public VertexDeclaration Info { get; set; } public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.VFT = reader.ReadUInt32(); this.Unknown_4h = reader.ReadUInt32(); this.VertexStride = reader.ReadUInt16(); this.Flags = reader.ReadUInt16(); this.Unknown_Ch = reader.ReadUInt32(); this.DataPointer1 = reader.ReadUInt64(); this.VertexCount = reader.ReadUInt32(); this.Unknown_1Ch = reader.ReadUInt32(); this.DataPointer2 = reader.ReadUInt64(); this.Unknown_28h = reader.ReadUInt64(); this.InfoPointer = reader.ReadUInt64(); this.Unknown_38h = reader.ReadUInt64(); this.Unknown_40h = reader.ReadUInt64(); this.Unknown_48h = reader.ReadUInt64(); this.Unknown_50h = reader.ReadUInt64(); this.Unknown_58h = reader.ReadUInt64(); this.Unknown_60h = reader.ReadUInt64(); this.Unknown_68h = reader.ReadUInt64(); this.Unknown_70h = reader.ReadUInt64(); this.Unknown_78h = reader.ReadUInt64(); // read reference data this.Info = reader.ReadBlockAt( this.InfoPointer // offset ); this.Data1 = reader.ReadBlockAt( this.DataPointer1, // offset this.VertexStride, this.VertexCount, this.Info ); this.Data2 = reader.ReadBlockAt( this.DataPointer2, // offset this.VertexStride, this.VertexCount, this.Info ); //switch (Flags) //{ // case 0: // break; // case 1024://micro flag? //micro_brow_down.ydr, micro_chin_pointed.ydr // break; // default: // break; //} //if (Unknown_4h != 1) //{ } //if (Unknown_Ch != 0) //{ } //if (Unknown_1Ch != 0) //{ } //if (Unknown_28h != 0) //{ } //if (Unknown_38h != 0) //{ } //if (Unknown_40h != 0) //{ } //if (Unknown_48h != 0) //{ } //if (Unknown_50h != 0) //{ } //if (Unknown_58h != 0) //{ } //if (Unknown_60h != 0) //{ } //if (Unknown_68h != 0) //{ } //if (Unknown_70h != 0) //{ } //if (Unknown_78h != 0) //{ } } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data this.VertexCount = (uint)(this.Data1 != null ? this.Data1.VertexCount : this.Data2 != null ? this.Data2.VertexCount : 0); this.DataPointer1 = (ulong)(this.Data1 != null ? this.Data1.FilePosition : 0); this.DataPointer2 = (ulong)(this.Data2 != null ? this.Data2.FilePosition : 0); this.InfoPointer = (ulong)(this.Info != null ? this.Info.FilePosition : 0); // write structure data writer.Write(this.VFT); writer.Write(this.Unknown_4h); writer.Write(this.VertexStride); writer.Write(this.Flags); writer.Write(this.Unknown_Ch); writer.Write(this.DataPointer1); writer.Write(this.VertexCount); writer.Write(this.Unknown_1Ch); writer.Write(this.DataPointer2); writer.Write(this.Unknown_28h); writer.Write(this.InfoPointer); writer.Write(this.Unknown_38h); writer.Write(this.Unknown_40h); writer.Write(this.Unknown_48h); writer.Write(this.Unknown_50h); writer.Write(this.Unknown_58h); writer.Write(this.Unknown_60h); writer.Write(this.Unknown_68h); writer.Write(this.Unknown_70h); writer.Write(this.Unknown_78h); } public void WriteXml(StringBuilder sb, int indent) { YdrXml.ValueTag(sb, indent, "Flags", Flags.ToString()); if (Info != null) { Info.WriteXml(sb, indent, "Layout"); } if (Data1 != null) { YdrXml.OpenTag(sb, indent, "Data"); Data1.WriteXml(sb, indent + 1); YdrXml.CloseTag(sb, indent, "Data"); } if ((Data2 != null) && (Data2 != Data1)) { YdrXml.OpenTag(sb, indent, "Data2"); Data2.WriteXml(sb, indent + 1); YdrXml.CloseTag(sb, indent, "Data2"); } } public void ReadXml(XmlNode node) { Flags = (ushort)Xml.GetChildUIntAttribute(node, "Flags", "value"); var inode = node.SelectSingleNode("Layout"); if (inode != null) { Info = new VertexDeclaration(); Info.ReadXml(inode); VertexStride = Info.Stride; } var dnode = node.SelectSingleNode("Data"); if (dnode != null) { Data1 = new VertexData(); Data1.ReadXml(dnode, Info); Data2 = Data1; VertexCount = (uint)Data1.VertexCount; } var dnode2 = node.SelectSingleNode("Data2"); if (dnode2 != null) { Data2 = new VertexData(); Data2.ReadXml(dnode2, Info); } } public override IResourceBlock[] GetReferences() { var list = new List(); if (Data1 != null) list.Add(Data1); if (Data2 != null) list.Add(Data2); if (Info != null) list.Add(Info); return list.ToArray(); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class VertexData : ResourceSystemBlock { //private int length = 0; public override long BlockLength { get { return VertexBytes?.Length ?? 0; //this.length; } } public int VertexStride { get; set; } public int VertexCount { get; set; } public VertexDeclaration Info { get; set; } public VertexType VertexType { get; set; } public byte[] VertexBytes { get; set; } public long MemoryUsage { get { return (long)VertexCount * (long)VertexStride; } } public override void Read(ResourceDataReader reader, params object[] parameters) { VertexStride = Convert.ToInt32(parameters[0]); VertexCount = Convert.ToInt32(parameters[1]); Info = (VertexDeclaration)parameters[2]; VertexType = (VertexType)Info.Flags; VertexBytes = reader.ReadBytes(VertexCount * VertexStride); switch (Info.Types) { case VertexDeclarationTypes.GTAV1: //YDR - 0x7755555555996996 break; case VertexDeclarationTypes.GTAV2: //YFT - 0x030000000199A006 switch (Info.Flags) { case 16473: VertexType = VertexType.PCCH2H4; break; // PCCH2H4 default:break; } break; case VertexDeclarationTypes.GTAV3: //YFT - 0x0300000001996006 PNCH2H4 switch (Info.Flags) { case 89: VertexType = VertexType.PNCH2; break; // PNCH2 default: break; } break; default: break; } } public override void Write(ResourceDataWriter writer, params object[] parameters) { if (VertexBytes != null) { writer.Write(VertexBytes); //not dealing with individual vertex data here any more! } } public void WriteXml(StringBuilder sb, int indent) { var flags = Info?.Flags ?? 0; var row = new StringBuilder(); for (int v = 0; v < VertexCount; v++) { row.Clear(); for (int k = 0; k < 16; k++) { if (((flags >> k) & 0x1) == 1) { if (row.Length > 0) row.Append(" "); var str = GetString(v, k, " "); row.Append(str); } } YdrXml.Indent(sb, indent); sb.AppendLine(row.ToString()); } } public void ReadXml(XmlNode node, VertexDeclaration info) { Info = info; VertexType = (VertexType)(info?.Flags ?? 0); if (Info != null) { var flags = Info.Flags; var stride = Info.Stride; var vstrs = new List(); var coldelim = new[] { ' ', '\t' }; var rowdelim = new[] { '\n' }; var rows = node?.InnerText?.Trim()?.Split(rowdelim, StringSplitOptions.RemoveEmptyEntries); if (rows != null) { foreach (var row in rows) { var rowt = row.Trim(); if (string.IsNullOrEmpty(rowt)) continue; var cols = row.Split(coldelim, StringSplitOptions.RemoveEmptyEntries); vstrs.Add(cols); } } if (vstrs.Count > 0) { AllocateData(vstrs.Count); for (int v = 0; v < vstrs.Count; v++) { var vstr = vstrs[v]; var sind = 0; for (int k = 0; k < 16; k++) { if (((flags >> k) & 0x1) == 1) { SetString(v, k, vstr, ref sind); } } } } } } public void AllocateData(int vertexCount) { if (Info != null) { var stride = Info.Stride; var byteCount = vertexCount * stride; VertexBytes = new byte[byteCount]; VertexCount = vertexCount; } } public void SetString(int v, int c, string[] strs, ref int sind) { if ((Info != null) && (VertexBytes != null) && (strs != null)) { var ind = sind; float f(int i) => FloatUtil.Parse(strs[ind + i].Trim()); byte b(int i) { if (byte.TryParse(strs[ind + i].Trim(), out byte x)) return x; else return 0; } var ct = Info.GetComponentType(c); var cc = VertexComponentTypes.GetComponentCount(ct); if (sind + cc > strs.Length) { return; } switch (ct) { case VertexComponentType.Float: SetFloat(v, c, f(0)); break; case VertexComponentType.Float2: SetVector2(v, c, new Vector2(f(0), f(1))); break; case VertexComponentType.Float3: SetVector3(v, c, new Vector3(f(0), f(1), f(2))); break; case VertexComponentType.Float4: SetVector4(v, c, new Vector4(f(0), f(1), f(2), f(3))); break; case VertexComponentType.Dec3N: SetDec3N(v, c, new Vector3(f(0), f(1), f(2))); break; case VertexComponentType.Half2: SetHalf2(v, c, new Half2(f(0), f(1))); break; case VertexComponentType.Half4: SetHalf4(v, c, new Half4(f(0), f(1), f(2), f(3))); break; case VertexComponentType.Colour: SetColour(v, c, new Color(b(0), b(1), b(2), b(3))); break; case VertexComponentType.UByte4: SetUByte4(v, c, new Color(b(0), b(1), b(2), b(3))); break; default: break; } sind += cc; } } public void SetFloat(int v, int c, float val) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 4;//sizeof(float) if (e <= VertexBytes.Length) { var b = BitConverter.GetBytes(val); Buffer.BlockCopy(b, 0, VertexBytes, o, 4); } } } public void SetVector2(int v, int c, Vector2 val) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 8;//sizeof(Vector2) if (e <= VertexBytes.Length) { var x = BitConverter.GetBytes(val.X); var y = BitConverter.GetBytes(val.Y); Buffer.BlockCopy(x, 0, VertexBytes, o + 0, 4); Buffer.BlockCopy(y, 0, VertexBytes, o + 4, 4); } } } public void SetVector3(int v, int c, Vector3 val) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 12;//sizeof(Vector3) if (e <= VertexBytes.Length) { var x = BitConverter.GetBytes(val.X); var y = BitConverter.GetBytes(val.Y); var z = BitConverter.GetBytes(val.Z); Buffer.BlockCopy(x, 0, VertexBytes, o + 0, 4); Buffer.BlockCopy(y, 0, VertexBytes, o + 4, 4); Buffer.BlockCopy(z, 0, VertexBytes, o + 8, 4); } } } public void SetVector4(int v, int c, Vector4 val) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 16;//sizeof(Vector4) if (e <= VertexBytes.Length) { var x = BitConverter.GetBytes(val.X); var y = BitConverter.GetBytes(val.Y); var z = BitConverter.GetBytes(val.Z); var w = BitConverter.GetBytes(val.W); Buffer.BlockCopy(x, 0, VertexBytes, o + 0, 4); Buffer.BlockCopy(y, 0, VertexBytes, o + 4, 4); Buffer.BlockCopy(z, 0, VertexBytes, o + 8, 4); Buffer.BlockCopy(w, 0, VertexBytes, o + 12, 4); } } } public void SetDec3N(int v, int c, Vector3 val) { //see https://docs.microsoft.com/en-us/previous-versions/windows/desktop/bb322868(v%3Dvs.85) if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 4;//sizeof(Dec3N) if (e <= VertexBytes.Length) { var sx = (val.X < 0.0f); var sy = (val.X < 0.0f); var sz = (val.X < 0.0f); var x = Math.Min((uint)(Math.Abs(val.X) * 511.0f), 511); var y = Math.Min((uint)(Math.Abs(val.Y) * 511.0f), 511); var z = Math.Min((uint)(Math.Abs(val.Z) * 511.0f), 511); var ux = ((sx ? ~x : x) & 0x1FF) + (sx ? 0x200 : 0); var uy = ((sy ? ~y : y) & 0x1FF) + (sy ? 0x200 : 0); var uz = ((sz ? ~z : z) & 0x1FF) + (sz ? 0x200 : 0); var uw = 0u; var u = ux + (uy << 10) + (uz << 20) + (uw << 30); var b = BitConverter.GetBytes(u); Buffer.BlockCopy(b, 0, VertexBytes, o, 4); } } } public void SetHalf2(int v, int c, Half2 val) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 4;//sizeof(Half2) if (e <= VertexBytes.Length) { var x = BitConverter.GetBytes(val.X.RawValue); var y = BitConverter.GetBytes(val.Y.RawValue); Buffer.BlockCopy(x, 0, VertexBytes, o + 0, 2); Buffer.BlockCopy(y, 0, VertexBytes, o + 2, 2); } } } public void SetHalf4(int v, int c, Half4 val) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 8;//sizeof(Half4) if (e <= VertexBytes.Length) { var x = BitConverter.GetBytes(val.X.RawValue); var y = BitConverter.GetBytes(val.Y.RawValue); var z = BitConverter.GetBytes(val.Z.RawValue); var w = BitConverter.GetBytes(val.W.RawValue); Buffer.BlockCopy(x, 0, VertexBytes, o + 0, 2); Buffer.BlockCopy(y, 0, VertexBytes, o + 2, 2); Buffer.BlockCopy(z, 0, VertexBytes, o + 4, 2); Buffer.BlockCopy(w, 0, VertexBytes, o + 6, 2); } } } public void SetColour(int v, int c, Color val) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 4;//sizeof(Color) if (e <= VertexBytes.Length) { var u = val.ToRgba(); var b = BitConverter.GetBytes(u); Buffer.BlockCopy(b, 0, VertexBytes, o, 4); } } } public void SetUByte4(int v, int c, Color val) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 4;//sizeof(UByte4) if (e <= VertexBytes.Length) { var u = val.ToRgba(); var b = BitConverter.GetBytes(u); Buffer.BlockCopy(b, 0, VertexBytes, o, 4); } } } public string GetString(int v, int c, string d = ", ") { if ((Info != null) && (VertexBytes != null)) { var ct = Info.GetComponentType(c); switch (ct) { case VertexComponentType.Float: return FloatUtil.ToString(GetFloat(v, c)); case VertexComponentType.Float2: return FloatUtil.GetVector2String(GetVector2(v, c), d); case VertexComponentType.Float3: return FloatUtil.GetVector3String(GetVector3(v, c), d); case VertexComponentType.Float4: return FloatUtil.GetVector4String(GetVector4(v, c), d); case VertexComponentType.Dec3N: return FloatUtil.GetVector3String(GetDec3N(v, c), d); case VertexComponentType.Half2: return FloatUtil.GetHalf2String(GetHalf2(v, c), d); case VertexComponentType.Half4: return FloatUtil.GetHalf4String(GetHalf4(v, c), d); case VertexComponentType.Colour: return FloatUtil.GetColourString(GetColour(v, c), d); case VertexComponentType.UByte4: return FloatUtil.GetColourString(GetUByte4(v, c), d); default: break; } } return string.Empty; } public float GetFloat(int v, int c) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 4;//sizeof(float) if (e <= VertexBytes.Length) { var f = BitConverter.ToSingle(VertexBytes, o); return f; } } return 0; } public Vector2 GetVector2(int v, int c) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 8;//sizeof(Vector2) if (e <= VertexBytes.Length) { var x = BitConverter.ToSingle(VertexBytes, o + 0); var y = BitConverter.ToSingle(VertexBytes, o + 4); return new Vector2(x, y); } } return Vector2.Zero; } public Vector3 GetVector3(int v, int c) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 12;//sizeof(Vector3) if (e <= VertexBytes.Length) { var x = BitConverter.ToSingle(VertexBytes, o + 0); var y = BitConverter.ToSingle(VertexBytes, o + 4); var z = BitConverter.ToSingle(VertexBytes, o + 8); return new Vector3(x, y, z); } } return Vector3.Zero; } public Vector4 GetVector4(int v, int c) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 16;//sizeof(Vector4) if (e <= VertexBytes.Length) { var x = BitConverter.ToSingle(VertexBytes, o + 0); var y = BitConverter.ToSingle(VertexBytes, o + 4); var z = BitConverter.ToSingle(VertexBytes, o + 8); var w = BitConverter.ToSingle(VertexBytes, o + 12); return new Vector4(x, y, z, w); } } return Vector4.Zero; } public Vector3 GetDec3N(int v, int c) { //see https://docs.microsoft.com/en-us/previous-versions/windows/desktop/bb322868(v%3Dvs.85) if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 4;//sizeof(Dec3N) if (e <= VertexBytes.Length) { var u = BitConverter.ToUInt32(VertexBytes, o); var ux = (u >> 0) & 0x3FF; var uy = (u >> 10) & 0x3FF; var uz = (u >> 20) & 0x3FF; var uw = (u >> 30); var sx = (ux & 0x200) > 0; var sy = (uy & 0x200) > 0; var sz = (uz & 0x200) > 0; var x = ((sx ? ~ux : ux) & 0x1FF) / (sx ? -511.0f : 511.0f); var y = ((sy ? ~uy : uy) & 0x1FF) / (sy ? -511.0f : 511.0f); var z = ((sz ? ~uz : uz) & 0x1FF) / (sz ? -511.0f : 511.0f); return new Vector3(x, y, z); } } return Vector3.Zero; } public Half2 GetHalf2(int v, int c) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 4;//sizeof(Half2) if (e <= VertexBytes.Length) { var x = BitConverter.ToUInt16(VertexBytes, o + 0); var y = BitConverter.ToUInt16(VertexBytes, o + 2); return new Half2(x, y); } } return new Half2(0, 0); } public Half4 GetHalf4(int v, int c) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 8;//sizeof(Half4) if (e <= VertexBytes.Length) { var x = BitConverter.ToUInt16(VertexBytes, o + 0); var y = BitConverter.ToUInt16(VertexBytes, o + 2); var z = BitConverter.ToUInt16(VertexBytes, o + 4); var w = BitConverter.ToUInt16(VertexBytes, o + 6); return new Half4(x, y, z, w); } } return new Half4(0, 0, 0, 0); } public Color GetColour(int v, int c) { if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 4;//sizeof(Color) if (e <= VertexBytes.Length) { var rgba = BitConverter.ToUInt32(VertexBytes, o); return new Color(rgba); } } return Color.Black; } public Color GetUByte4(int v, int c) { //Color is the same as UByte4 really if ((Info != null) && (VertexBytes != null)) { var s = Info.Stride; var co = Info.GetComponentOffset(c); var o = (v * s) + co; var e = o + 4;//sizeof(UByte4) if (e <= VertexBytes.Length) { var rgba = BitConverter.ToUInt32(VertexBytes, o); return new Color(rgba); } } return new Color(0, 0, 0, 0); } public override string ToString() { return "Type: " + VertexType.ToString() + ", Count: " + VertexCount.ToString(); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class VertexDeclaration : ResourceSystemBlock { public override long BlockLength { get { return 16; } } // structure data public uint Flags { get; set; } public ushort Stride { get; set; } public byte Unknown_6h { get; set; }//0 public byte Count { get; set; } public VertexDeclarationTypes Types { get; set; } public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.Flags = reader.ReadUInt32(); this.Stride = reader.ReadUInt16(); this.Unknown_6h = reader.ReadByte(); this.Count = reader.ReadByte(); this.Types = (VertexDeclarationTypes)reader.ReadUInt64(); ////just testing! //UpdateCountAndStride(); //if (Unknown_6h != 0) //{ }//no hit } public override void Write(ResourceDataWriter writer, params object[] parameters) { // write structure data writer.Write(this.Flags); writer.Write(this.Stride); writer.Write(this.Unknown_6h); writer.Write(this.Count); writer.Write((ulong)this.Types); } public void WriteXml(StringBuilder sb, int indent, string name) { YdrXml.OpenTag(sb, indent, name + " type=\"" + Types.ToString() + "\""); for (int k = 0; k < 16; k++) { if (((Flags >> k) & 0x1) == 1) { var componentSemantic = (VertexSemantics)k; var tag = componentSemantic.ToString(); YdrXml.SelfClosingTag(sb, indent + 1, tag); } } YdrXml.CloseTag(sb, indent, name); } public void ReadXml(XmlNode node) { if (node == null) return; Types = Xml.GetEnumValue(Xml.GetStringAttribute(node, "type")); uint f = 0; foreach (XmlNode cnode in node.ChildNodes) { if (cnode is XmlElement celem) { var componentSematic = Xml.GetEnumValue(celem.Name); var idx = (int)componentSematic; f = f | (1u << idx); } } Flags = f; UpdateCountAndStride(); } public ulong GetDeclarationId() { ulong res = 0; for(int i=0; i < 16; i++) { if (((Flags >> i) & 1) == 1) { res += ((ulong)Types & (0xFu << (i * 4))); } } return res; } public VertexComponentType GetComponentType(int index) { //index is the flags bit index return (VertexComponentType)(((ulong)Types >> (index * 4)) & 0x0000000F); } public int GetComponentOffset(int index) { //index is the flags bit index var offset = 0; for (int k = 0; k < index; k++) { if (((Flags >> k) & 0x1) == 1) { var componentType = GetComponentType(k); offset += VertexComponentTypes.GetSizeInBytes(componentType); } } return offset; } public void UpdateCountAndStride() { var cnt = 0; var str = 0; for (int k = 0; k < 16; k++) { if (((Flags >> k) & 0x1) == 1) { var componentType = GetComponentType(k); str += VertexComponentTypes.GetSizeInBytes(componentType); cnt++; } } ////just testing //if (Count != cnt) //{ }//no hit //if (Stride != str) //{ }//no hit Count = (byte)cnt; Stride = (ushort)str; } public override string ToString() { return Stride.ToString() + ": " + Count.ToString() + ": " + Flags.ToString() + ": " + Types.ToString(); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class IndexBuffer : ResourceSystemBlock { public override long BlockLength { get { return 96; } } // structure data public uint VFT { get; set; } public uint Unknown_4h = 1; // 0x00000001 public uint IndicesCount { get; set; } public uint Unknown_Ch; // 0x00000000 public ulong IndicesPointer { get; set; } public ulong Unknown_18h; // 0x0000000000000000 public ulong Unknown_20h; // 0x0000000000000000 public ulong Unknown_28h; // 0x0000000000000000 public ulong Unknown_30h; // 0x0000000000000000 public ulong Unknown_38h; // 0x0000000000000000 public ulong Unknown_40h; // 0x0000000000000000 public ulong Unknown_48h; // 0x0000000000000000 public ulong Unknown_50h; // 0x0000000000000000 public ulong Unknown_58h; // 0x0000000000000000 // reference data //public ResourceSimpleArray Indices; public ushort[] Indices { get; set; } private ResourceSystemStructBlock IndicesBlock = null; //only used when saving public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data this.VFT = reader.ReadUInt32(); this.Unknown_4h = reader.ReadUInt32(); this.IndicesCount = reader.ReadUInt32(); this.Unknown_Ch = reader.ReadUInt32(); this.IndicesPointer = reader.ReadUInt64(); this.Unknown_18h = reader.ReadUInt64(); this.Unknown_20h = reader.ReadUInt64(); this.Unknown_28h = reader.ReadUInt64(); this.Unknown_30h = reader.ReadUInt64(); this.Unknown_38h = reader.ReadUInt64(); this.Unknown_40h = reader.ReadUInt64(); this.Unknown_48h = reader.ReadUInt64(); this.Unknown_50h = reader.ReadUInt64(); this.Unknown_58h = reader.ReadUInt64(); // read reference data //this.Indices = reader.ReadBlockAt>( // this.IndicesPointer, // offset // this.IndicesCount //); this.Indices = reader.ReadUshortsAt(this.IndicesPointer, this.IndicesCount); //if (Unknown_4h != 1) //{ } //if (Unknown_Ch != 0) //{ } //if (Unknown_18h != 0) //{ } //if (Unknown_20h != 0) //{ } //if (Unknown_28h != 0) //{ } //if (Unknown_30h != 0) //{ } //if (Unknown_38h != 0) //{ } //if (Unknown_40h != 0) //{ } //if (Unknown_48h != 0) //{ } //if (Unknown_50h != 0) //{ } //if (Unknown_58h != 0) //{ } } public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data this.IndicesCount = (uint)(this.IndicesBlock != null ? this.IndicesBlock.ItemCount : 0); this.IndicesPointer = (ulong)(this.IndicesBlock != null ? this.IndicesBlock.FilePosition : 0); // write structure data writer.Write(this.VFT); writer.Write(this.Unknown_4h); writer.Write(this.IndicesCount); writer.Write(this.Unknown_Ch); writer.Write(this.IndicesPointer); writer.Write(this.Unknown_18h); writer.Write(this.Unknown_20h); writer.Write(this.Unknown_28h); writer.Write(this.Unknown_30h); writer.Write(this.Unknown_38h); writer.Write(this.Unknown_40h); writer.Write(this.Unknown_48h); writer.Write(this.Unknown_50h); writer.Write(this.Unknown_58h); } public void WriteXml(StringBuilder sb, int indent) { if (Indices != null) { YdrXml.WriteRawArray(sb, Indices, indent, "Data", "", null, 24); } } public void ReadXml(XmlNode node) { var inode = node.SelectSingleNode("Data"); if (inode != null) { Indices = Xml.GetRawUshortArray(node); IndicesCount = (uint)(Indices?.Length ?? 0); } } public override IResourceBlock[] GetReferences() { var list = new List(); if (Indices != null) { IndicesBlock = new ResourceSystemStructBlock(Indices); list.Add(IndicesBlock); } return list.ToArray(); } } public enum LightType : byte { Point = 1, Spot = 2, Capsule = 4, } [TypeConverter(typeof(ExpandableObjectConverter))] public struct LightAttributes_s : IMetaXmlItem { // structure data public uint Unknown_0h { get; set; } // 0x00000000 public uint Unknown_4h { get; set; } // 0x00000000 public Vector3 Position { get; set; } public uint Unknown_14h { get; set; } // 0x00000000 public byte ColorR { get; set; } public byte ColorG { get; set; } public byte ColorB { get; set; } public byte Flashiness { get; set; } public float Intensity { get; set; } public uint Flags { get; set; } public ushort BoneId { get; set; } public LightType Type { get; set; } public byte GroupId { get; set; } public uint TimeFlags { get; set; } public float Falloff { get; set; } public float FalloffExponent { get; set; } public Vector3 CullingPlaneNormal { get; set; } public float CullingPlaneOffset { get; set; } public byte ShadowBlur { get; set; } public byte Unknown_45h { get; set; } public ushort Unknown_46h { get; set; } public uint Unknown_48h { get; set; } // 0x00000000 public float VolumeIntensity { get; set; } public float VolumeSizeScale { get; set; } public byte VolumeOuterColorR { get; set; } public byte VolumeOuterColorG { get; set; } public byte VolumeOuterColorB { get; set; } public byte LightHash { get; set; } public float VolumeOuterIntensity { get; set; } public float CoronaSize { get; set; } public float VolumeOuterExponent { get; set; } public byte LightFadeDistance { get; set; } public byte ShadowFadeDistance { get; set; } public byte SpecularFadeDistance { get; set; } public byte VolumetricFadeDistance { get; set; } public float ShadowNearClip { get; set; } public float CoronaIntensity { get; set; } public float CoronaZBias { get; set; } public Vector3 Direction { get; set; } public Vector3 Tangent { get; set; } public float ConeInnerAngle { get; set; } public float ConeOuterAngle { get; set; } public Vector3 Extent { get; set; } public MetaHash ProjectedTextureHash { get; set; } public uint Unknown_A4h { get; set; } // 0x00000000 public void WriteXml(StringBuilder sb, int indent) { YdrXml.SelfClosingTag(sb, indent, "Position " + FloatUtil.GetVector3XmlString(Position)); YdrXml.SelfClosingTag(sb, indent, string.Format("Colour r=\"{0}\" g=\"{1}\" b=\"{2}\"", ColorR, ColorG, ColorB)); YdrXml.ValueTag(sb, indent, "Flashiness", Flashiness.ToString()); YdrXml.ValueTag(sb, indent, "Intensity", FloatUtil.ToString(Intensity)); YdrXml.ValueTag(sb, indent, "Flags", Flags.ToString()); YdrXml.ValueTag(sb, indent, "BoneId", BoneId.ToString()); YdrXml.StringTag(sb, indent, "Type", Type.ToString()); YdrXml.ValueTag(sb, indent, "GroupId", GroupId.ToString()); YdrXml.ValueTag(sb, indent, "TimeFlags", TimeFlags.ToString()); YdrXml.ValueTag(sb, indent, "Falloff", FloatUtil.ToString(Falloff)); YdrXml.ValueTag(sb, indent, "FalloffExponent", FloatUtil.ToString(FalloffExponent)); YdrXml.SelfClosingTag(sb, indent, "CullingPlaneNormal " + FloatUtil.GetVector3XmlString(CullingPlaneNormal)); YdrXml.ValueTag(sb, indent, "CullingPlaneOffset", FloatUtil.ToString(CullingPlaneOffset)); YdrXml.ValueTag(sb, indent, "Unknown45", Unknown_45h.ToString()); YdrXml.ValueTag(sb, indent, "Unknown46", Unknown_46h.ToString()); YdrXml.ValueTag(sb, indent, "VolumeIntensity", FloatUtil.ToString(VolumeIntensity)); YdrXml.ValueTag(sb, indent, "VolumeSizeScale", FloatUtil.ToString(VolumeSizeScale)); YdrXml.SelfClosingTag(sb, indent, string.Format("VolumeOuterColour r=\"{0}\" g=\"{1}\" b=\"{2}\"", VolumeOuterColorR, VolumeOuterColorG, VolumeOuterColorB)); YdrXml.ValueTag(sb, indent, "LightHash", LightHash.ToString()); YdrXml.ValueTag(sb, indent, "VolumeOuterIntensity", FloatUtil.ToString(VolumeOuterIntensity)); YdrXml.ValueTag(sb, indent, "CoronaSize", FloatUtil.ToString(CoronaSize)); YdrXml.ValueTag(sb, indent, "VolumeOuterExponent", FloatUtil.ToString(VolumeOuterExponent)); YdrXml.ValueTag(sb, indent, "LightFadeDistance", LightFadeDistance.ToString()); YdrXml.ValueTag(sb, indent, "ShadowFadeDistance", ShadowFadeDistance.ToString()); YdrXml.ValueTag(sb, indent, "SpecularFadeDistance", SpecularFadeDistance.ToString()); YdrXml.ValueTag(sb, indent, "VolumetricFadeDistance", VolumetricFadeDistance.ToString()); YdrXml.ValueTag(sb, indent, "ShadowNearClip", FloatUtil.ToString(ShadowNearClip)); YdrXml.ValueTag(sb, indent, "CoronaIntensity", FloatUtil.ToString(CoronaIntensity)); YdrXml.ValueTag(sb, indent, "CoronaZBias", FloatUtil.ToString(CoronaZBias)); YdrXml.SelfClosingTag(sb, indent, "Direction " + FloatUtil.GetVector3XmlString(Direction)); YdrXml.SelfClosingTag(sb, indent, "Tangent " + FloatUtil.GetVector3XmlString(Tangent)); YdrXml.ValueTag(sb, indent, "ConeInnerAngle", FloatUtil.ToString(ConeInnerAngle)); YdrXml.ValueTag(sb, indent, "ConeOuterAngle", FloatUtil.ToString(ConeOuterAngle)); YdrXml.SelfClosingTag(sb, indent, "Extent " + FloatUtil.GetVector3XmlString(Extent)); YdrXml.StringTag(sb, indent, "ProjectedTextureHash", YdrXml.HashString(ProjectedTextureHash)); } public void ReadXml(XmlNode node) { Position = Xml.GetChildVector3Attributes(node, "Position"); ColorR = (byte)Xml.GetChildUIntAttribute(node, "Colour", "r"); ColorG = (byte)Xml.GetChildUIntAttribute(node, "Colour", "g"); ColorB = (byte)Xml.GetChildUIntAttribute(node, "Colour", "b"); Flashiness = (byte)Xml.GetChildUIntAttribute(node, "Flashiness", "value"); Intensity = Xml.GetChildFloatAttribute(node, "Intensity", "value"); Flags = Xml.GetChildUIntAttribute(node, "Flags", "value"); BoneId = (ushort)Xml.GetChildUIntAttribute(node, "BoneId", "value"); Type = Xml.GetChildEnumInnerText(node, "Type"); GroupId = (byte)Xml.GetChildUIntAttribute(node, "GroupId", "value"); TimeFlags = Xml.GetChildUIntAttribute(node, "TimeFlags", "value"); Falloff = Xml.GetChildFloatAttribute(node, "Falloff", "value"); FalloffExponent = Xml.GetChildFloatAttribute(node, "FalloffExponent", "value"); CullingPlaneNormal = Xml.GetChildVector3Attributes(node, "CullingPlaneNormal"); CullingPlaneOffset = Xml.GetChildFloatAttribute(node, "CullingPlaneOffset", "value"); Unknown_45h = (byte)Xml.GetChildUIntAttribute(node, "Unknown45", "value"); Unknown_46h = (ushort)Xml.GetChildUIntAttribute(node, "Unknown46", "value"); VolumeIntensity = Xml.GetChildFloatAttribute(node, "VolumeIntensity", "value"); VolumeSizeScale = Xml.GetChildFloatAttribute(node, "VolumeSizeScale", "value"); VolumeOuterColorR = (byte)Xml.GetChildUIntAttribute(node, "VolumeOuterColour", "r"); VolumeOuterColorG = (byte)Xml.GetChildUIntAttribute(node, "VolumeOuterColour", "g"); VolumeOuterColorB = (byte)Xml.GetChildUIntAttribute(node, "VolumeOuterColour", "b"); LightHash = (byte)Xml.GetChildUIntAttribute(node, "LightHash", "value"); VolumeOuterIntensity = Xml.GetChildFloatAttribute(node, "VolumeOuterIntensity", "value"); CoronaSize = Xml.GetChildFloatAttribute(node, "CoronaSize", "value"); VolumeOuterExponent = Xml.GetChildFloatAttribute(node, "VolumeOuterExponent", "value"); LightFadeDistance = (byte)Xml.GetChildUIntAttribute(node, "LightFadeDistance", "value"); ShadowFadeDistance = (byte)Xml.GetChildUIntAttribute(node, "ShadowFadeDistance", "value"); SpecularFadeDistance = (byte)Xml.GetChildUIntAttribute(node, "SpecularFadeDistance", "value"); VolumetricFadeDistance = (byte)Xml.GetChildUIntAttribute(node, "VolumetricFadeDistance", "value"); ShadowNearClip = Xml.GetChildFloatAttribute(node, "ShadowNearClip", "value"); CoronaIntensity = Xml.GetChildFloatAttribute(node, "CoronaIntensity", "value"); CoronaZBias = Xml.GetChildFloatAttribute(node, "CoronaZBias", "value"); Direction = Xml.GetChildVector3Attributes(node, "Direction"); Tangent = Xml.GetChildVector3Attributes(node, "Tangent"); ConeInnerAngle = Xml.GetChildFloatAttribute(node, "ConeInnerAngle", "value"); ConeOuterAngle = Xml.GetChildFloatAttribute(node, "ConeOuterAngle", "value"); Extent = Xml.GetChildVector3Attributes(node, "Extent"); ProjectedTextureHash = XmlMeta.GetHash(Xml.GetChildInnerText(node, "ProjectedTextureHash")); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class DrawableBase : ResourceFileBase { public override long BlockLength { get { return 168; } } // structure data public ulong ShaderGroupPointer { get; set; } public ulong SkeletonPointer { get; set; } public Vector3 BoundingCenter { get; set; } public float BoundingSphereRadius { get; set; } public Vector3 BoundingBoxMin { get; set; } public uint Unknown_3Ch { get; set; } = 0x7f800001; public Vector3 BoundingBoxMax { get; set; } public uint Unknown_4Ch { get; set; } = 0x7f800001; public ulong DrawableModelsHighPointer { get; set; } public ulong DrawableModelsMediumPointer { get; set; } public ulong DrawableModelsLowPointer { get; set; } public ulong DrawableModelsVeryLowPointer { get; set; } public float LodDistHigh { get; set; } public float LodDistMed { get; set; } public float LodDistLow { get; set; } public float LodDistVlow { get; set; } public uint RenderMaskFlagsHigh { get; set; } public uint RenderMaskFlagsMed { get; set; } public uint RenderMaskFlagsLow { get; set; } public uint RenderMaskFlagsVlow { get; set; } public ulong JointsPointer { get; set; } public ushort Unknown_98h { get; set; } // 0x0000 public ushort Unknown_9Ah { get; set; } public uint Unknown_9Ch { get; set; } // 0x00000000 public ulong DrawableModelsXPointer { get; set; } public byte FlagsHigh { get { return (byte)(RenderMaskFlagsHigh & 0xFF); } set { RenderMaskFlagsHigh = (RenderMaskFlagsHigh & 0xFFFFFF00) + (value & 0xFFu); } } public byte FlagsMed { get { return (byte)(RenderMaskFlagsMed & 0xFF); } set { RenderMaskFlagsMed = (RenderMaskFlagsMed & 0xFFFFFF00) + (value & 0xFFu); } } public byte FlagsLow { get { return (byte)(RenderMaskFlagsLow & 0xFF); } set { RenderMaskFlagsLow = (RenderMaskFlagsLow & 0xFFFFFF00) + (value & 0xFFu); } } public byte FlagsVlow { get { return (byte)(RenderMaskFlagsVlow & 0xFF); } set { RenderMaskFlagsVlow = (RenderMaskFlagsVlow & 0xFFFFFF00) + (value & 0xFFu); } } public byte RenderMaskHigh { get { return (byte)((RenderMaskFlagsHigh >> 8) & 0xFF); } set { RenderMaskFlagsHigh = (RenderMaskFlagsHigh & 0xFFFF00FF) + ((value & 0xFFu) << 8); } } public byte RenderMaskMed { get { return (byte)((RenderMaskFlagsMed >> 8) & 0xFF); } set { RenderMaskFlagsMed = (RenderMaskFlagsMed & 0xFFFF00FF) + ((value & 0xFFu) << 8); } } public byte RenderMaskLow { get { return (byte)((RenderMaskFlagsLow >> 8) & 0xFF); } set { RenderMaskFlagsLow = (RenderMaskFlagsLow & 0xFFFF00FF) + ((value & 0xFFu) << 8); } } public byte RenderMaskVlow { get { return (byte)((RenderMaskFlagsVlow >> 8) & 0xFF); } set { RenderMaskFlagsVlow = (RenderMaskFlagsVlow & 0xFFFF00FF) + ((value & 0xFFu) << 8); } } // reference data public ShaderGroup ShaderGroup { get; set; } public Skeleton Skeleton { get; set; } public ResourcePointerList64 DrawableModelsHigh { get; set; } public ResourcePointerList64 DrawableModelsMedium { get; set; } public ResourcePointerList64 DrawableModelsLow { get; set; } public ResourcePointerList64 DrawableModelsVeryLow { get; set; } public Joints Joints { get; set; } public ResourcePointerList64 DrawableModelsX { get; set; } public DrawableModel[] AllModels { get; set; } public Dictionary VertexDecls { get; set; } public object Owner { get; set; } public long MemoryUsage { get { long val = 0; if (AllModels != null) { foreach(DrawableModel m in AllModels) { if (m != null) { val += m.MemoryUsage; } } } if ((ShaderGroup != null) && (ShaderGroup.TextureDictionary != null)) { val += ShaderGroup.TextureDictionary.MemoryUsage; } return val; } } public override void Read(ResourceDataReader reader, params object[] parameters) { base.Read(reader, parameters); // read structure data this.ShaderGroupPointer = reader.ReadUInt64(); this.SkeletonPointer = reader.ReadUInt64(); this.BoundingCenter = reader.ReadVector3(); this.BoundingSphereRadius = reader.ReadSingle(); this.BoundingBoxMin = reader.ReadVector3(); this.Unknown_3Ch = reader.ReadUInt32(); this.BoundingBoxMax = reader.ReadVector3(); this.Unknown_4Ch = reader.ReadUInt32(); this.DrawableModelsHighPointer = reader.ReadUInt64(); this.DrawableModelsMediumPointer = reader.ReadUInt64(); this.DrawableModelsLowPointer = reader.ReadUInt64(); this.DrawableModelsVeryLowPointer = reader.ReadUInt64(); this.LodDistHigh = reader.ReadSingle(); this.LodDistMed = reader.ReadSingle(); this.LodDistLow = reader.ReadSingle(); this.LodDistVlow = reader.ReadSingle(); this.RenderMaskFlagsHigh = reader.ReadUInt32(); this.RenderMaskFlagsMed = reader.ReadUInt32(); this.RenderMaskFlagsLow = reader.ReadUInt32(); this.RenderMaskFlagsVlow = reader.ReadUInt32(); this.JointsPointer = reader.ReadUInt64(); this.Unknown_98h = reader.ReadUInt16(); this.Unknown_9Ah = reader.ReadUInt16(); this.Unknown_9Ch = reader.ReadUInt32(); this.DrawableModelsXPointer = reader.ReadUInt64(); // read reference data this.ShaderGroup = reader.ReadBlockAt( this.ShaderGroupPointer // offset ); this.Skeleton = reader.ReadBlockAt( this.SkeletonPointer // offset ); this.DrawableModelsHigh = reader.ReadBlockAt>( this.DrawableModelsHighPointer // offset ); this.DrawableModelsMedium = reader.ReadBlockAt>( this.DrawableModelsMediumPointer // offset ); this.DrawableModelsLow = reader.ReadBlockAt>( this.DrawableModelsLowPointer // offset ); this.DrawableModelsVeryLow = reader.ReadBlockAt>( this.DrawableModelsVeryLowPointer // offset ); this.Joints = reader.ReadBlockAt( this.JointsPointer // offset ); this.DrawableModelsX = reader.ReadBlockAt>( this.DrawableModelsXPointer // offset ); BuildAllModels(); BuildVertexDecls(); AssignGeometryShaders(ShaderGroup); ////just testing!!! //switch (Unknown_3Ch) //{ // case 0x7f800001: // case 0: //only in yft's! // break; // default: // break; //} //switch (Unknown_4Ch) //{ // case 0x7f800001: // case 0: //only in yft's! // break; // default: // break; //} //if ((DrawableModelsHigh?.data_items != null) != (Unknown_80h != 0)) //{ }//no hit //if ((DrawableModelsMedium?.data_items != null) != (Unknown_84h != 0)) //{ }//no hit //if ((DrawableModelsLow?.data_items != null) != (Unknown_88h != 0)) //{ }//no hit //if ((DrawableModelsVeryLow?.data_items != null) != (Unknown_8Ch != 0)) //{ }//no hit //if ((Unknown_80h & 0xFFFF0000) > 0) //{ }//no hit //if ((Unknown_84h & 0xFFFF0000) > 0) //{ }//no hit //if ((Unknown_88h & 0xFFFF0000) > 0) //{ }//no hit //if ((Unknown_8Ch & 0xFFFF0000) > 0) //{ }//no hit //BuildRenderMasks(); //switch (FlagsHigh) //{ // case 2: // case 1: // case 13: // case 4: // case 12: // case 5: // case 3: // case 8: // case 9: // case 15: // case 130: // case 11: // case 10: // case 7: // case 131: // case 129: // case 75: // case 69: // case 6: // case 64: // case 14: // case 77: // case 73: // case 76: // case 71: // case 79: // case 65: // case 0://some yft's have null HD models // case 19: // case 51: // break; // default: // break; //} //switch (FlagsMed) //{ // case 0: // case 1: // case 9: // case 8: // case 13: // case 3: // case 2: // case 5: // case 11: // case 15: // case 10: // case 12: // case 4: // case 7: // case 51: // break; // default: // break; //} //switch (FlagsLow) //{ // case 0: // case 9: // case 1: // case 8: // case 5: // case 3: // case 13: // case 2: // case 11: // case 15: // case 4: // case 7: // case 51: // break; // default: // break; //} //switch (FlagsVlow) //{ // case 0: // case 1: // case 9: // case 3: // case 7: // case 5: // case 49: // case 51: // case 11: // break; // default: // break; //} //switch (Unknown_98h) //{ // case 0: // break; // default: // break;//no hit //} //switch (Unknown_9Ah)//wtf isthis? flags? //{ // case 18: // case 33: // case 34: // case 46: // case 58: // case 209: // case 71: // case 172: // case 64: // case 122: // case 96: // case 248: // case 147: // case 51: // case 159: // case 134: // case 108: // case 83: // case 336: // case 450: // case 197: // case 374: // case 184: // case 310: // case 36: // case 386: // case 285: // case 260: // case 77: // case 361: // case 235: // case 91: // case 223: // case 1207: // case 2090: // case 45: // case 52: // case 526: // case 3081: // case 294: // case 236: // case 156: // break; // default://still lots more.. // break; //} } public override void Write(ResourceDataWriter writer, params object[] parameters) { base.Write(writer, parameters); // update structure data this.ShaderGroupPointer = (ulong)(this.ShaderGroup != null ? this.ShaderGroup.FilePosition : 0); this.SkeletonPointer = (ulong)(this.Skeleton != null ? this.Skeleton.FilePosition : 0); this.DrawableModelsHighPointer = (ulong)(this.DrawableModelsHigh != null ? this.DrawableModelsHigh.FilePosition : 0); this.DrawableModelsMediumPointer = (ulong)(this.DrawableModelsMedium != null ? this.DrawableModelsMedium.FilePosition : 0); this.DrawableModelsLowPointer = (ulong)(this.DrawableModelsLow != null ? this.DrawableModelsLow.FilePosition : 0); this.DrawableModelsVeryLowPointer = (ulong)(this.DrawableModelsVeryLow != null ? this.DrawableModelsVeryLow.FilePosition : 0); this.JointsPointer = (ulong)(this.Joints != null ? this.Joints.FilePosition : 0); this.DrawableModelsXPointer = (ulong)(this.DrawableModelsX != null ? this.DrawableModelsX.FilePosition : 0); // write structure data writer.Write(this.ShaderGroupPointer); writer.Write(this.SkeletonPointer); writer.Write(this.BoundingCenter); writer.Write(this.BoundingSphereRadius); writer.Write(this.BoundingBoxMin); writer.Write(this.Unknown_3Ch); writer.Write(this.BoundingBoxMax); writer.Write(this.Unknown_4Ch); writer.Write(this.DrawableModelsHighPointer); writer.Write(this.DrawableModelsMediumPointer); writer.Write(this.DrawableModelsLowPointer); writer.Write(this.DrawableModelsVeryLowPointer); writer.Write(this.LodDistHigh); writer.Write(this.LodDistMed); writer.Write(this.LodDistLow); writer.Write(this.LodDistVlow); writer.Write(this.RenderMaskFlagsHigh); writer.Write(this.RenderMaskFlagsMed); writer.Write(this.RenderMaskFlagsLow); writer.Write(this.RenderMaskFlagsVlow); writer.Write(this.JointsPointer); writer.Write(this.Unknown_98h); writer.Write(this.Unknown_9Ah); writer.Write(this.Unknown_9Ch); writer.Write(this.DrawableModelsXPointer); } public virtual void WriteXml(StringBuilder sb, int indent, string ddsfolder) { YdrXml.SelfClosingTag(sb, indent, "BoundingSphereCenter " + FloatUtil.GetVector3XmlString(BoundingCenter)); YdrXml.ValueTag(sb, indent, "BoundingSphereRadius", FloatUtil.ToString(BoundingSphereRadius)); YdrXml.SelfClosingTag(sb, indent, "BoundingBoxMin " + FloatUtil.GetVector3XmlString(BoundingBoxMin)); YdrXml.SelfClosingTag(sb, indent, "BoundingBoxMax " + FloatUtil.GetVector3XmlString(BoundingBoxMax)); YdrXml.ValueTag(sb, indent, "LodDistHigh", FloatUtil.ToString(LodDistHigh)); YdrXml.ValueTag(sb, indent, "LodDistMed", FloatUtil.ToString(LodDistMed)); YdrXml.ValueTag(sb, indent, "LodDistLow", FloatUtil.ToString(LodDistLow)); YdrXml.ValueTag(sb, indent, "LodDistVlow", FloatUtil.ToString(LodDistVlow)); YdrXml.ValueTag(sb, indent, "FlagsHigh", FlagsHigh.ToString()); YdrXml.ValueTag(sb, indent, "FlagsMed", FlagsMed.ToString()); YdrXml.ValueTag(sb, indent, "FlagsLow", FlagsLow.ToString()); YdrXml.ValueTag(sb, indent, "FlagsVlow", FlagsVlow.ToString()); YdrXml.ValueTag(sb, indent, "Unknown9A", Unknown_9Ah.ToString()); if (ShaderGroup != null) { YdrXml.OpenTag(sb, indent, "ShaderGroup"); ShaderGroup.WriteXml(sb, indent + 1, ddsfolder); YdrXml.CloseTag(sb, indent, "ShaderGroup"); } if (Skeleton != null) { YdrXml.OpenTag(sb, indent, "Skeleton"); Skeleton.WriteXml(sb, indent + 1); YdrXml.CloseTag(sb, indent, "Skeleton"); } if (Joints != null) { YdrXml.OpenTag(sb, indent, "Joints"); Joints.WriteXml(sb, indent + 1); YdrXml.CloseTag(sb, indent, "Joints"); } if (DrawableModelsHigh?.data_items != null) { YdrXml.WriteItemArray(sb, DrawableModelsHigh.data_items, indent, "DrawableModelsHigh"); } if (DrawableModelsMedium?.data_items != null) { YdrXml.WriteItemArray(sb, DrawableModelsMedium.data_items, indent, "DrawableModelsMedium"); } if (DrawableModelsLow?.data_items != null) { YdrXml.WriteItemArray(sb, DrawableModelsLow.data_items, indent, "DrawableModelsLow"); } if (DrawableModelsVeryLow?.data_items != null) { YdrXml.WriteItemArray(sb, DrawableModelsVeryLow.data_items, indent, "DrawableModelsVeryLow"); } if ((DrawableModelsX?.data_items != null) && (DrawableModelsX != DrawableModelsHigh))//is this right? duplicates..? { YdrXml.WriteItemArray(sb, DrawableModelsX.data_items, indent, "DrawableModelsX"); } } public virtual void ReadXml(XmlNode node, string ddsfolder) { BoundingCenter = Xml.GetChildVector3Attributes(node, "BoundingSphereCenter"); BoundingSphereRadius = Xml.GetChildFloatAttribute(node, "BoundingSphereRadius", "value"); BoundingBoxMin = Xml.GetChildVector3Attributes(node, "BoundingBoxMin"); BoundingBoxMax = Xml.GetChildVector3Attributes(node, "BoundingBoxMax"); LodDistHigh = Xml.GetChildFloatAttribute(node, "LodDistHigh", "value"); LodDistMed = Xml.GetChildFloatAttribute(node, "LodDistMed", "value"); LodDistLow = Xml.GetChildFloatAttribute(node, "LodDistLow", "value"); LodDistVlow = Xml.GetChildFloatAttribute(node, "LodDistVlow", "value"); FlagsHigh = (byte)Xml.GetChildUIntAttribute(node, "FlagsHigh", "value"); FlagsMed = (byte)Xml.GetChildUIntAttribute(node, "FlagsMed", "value"); FlagsLow = (byte)Xml.GetChildUIntAttribute(node, "FlagsLow", "value"); FlagsVlow = (byte)Xml.GetChildUIntAttribute(node, "FlagsVlow", "value"); Unknown_9Ah = (ushort)Xml.GetChildUIntAttribute(node, "Unknown9A", "value"); var sgnode = node.SelectSingleNode("ShaderGroup"); if (sgnode != null) { ShaderGroup = new ShaderGroup(); ShaderGroup.ReadXml(sgnode, ddsfolder); } var sknode = node.SelectSingleNode("Skeleton"); if (sknode != null) { Skeleton = new Skeleton(); Skeleton.ReadXml(sknode); } var jnode = node.SelectSingleNode("Joints"); if (jnode != null) { Joints = new Joints(); Joints.ReadXml(jnode); } var dmhigh = XmlMeta.ReadItemArray(node, "DrawableModelsHigh"); if (dmhigh != null) { DrawableModelsHigh = new ResourcePointerList64(); DrawableModelsHigh.data_items = dmhigh; } var dmmed = XmlMeta.ReadItemArray(node, "DrawableModelsMedium"); if (dmmed != null) { DrawableModelsMedium = new ResourcePointerList64(); DrawableModelsMedium.data_items = dmmed; } var dmlow = XmlMeta.ReadItemArray(node, "DrawableModelsLow"); if (dmlow != null) { DrawableModelsLow = new ResourcePointerList64(); DrawableModelsLow.data_items = dmlow; } var dmvlow = XmlMeta.ReadItemArray(node, "DrawableModelsVeryLow"); if (dmvlow != null) { DrawableModelsVeryLow = new ResourcePointerList64(); DrawableModelsVeryLow.data_items = dmvlow; } var dmx = XmlMeta.ReadItemArray(node, "DrawableModelsX"); if (dmx != null) { DrawableModelsX = new ResourcePointerList64(); DrawableModelsX.data_items = dmx; } else { DrawableModelsX = DrawableModelsHigh; } BuildRenderMasks(); BuildAllModels(); BuildVertexDecls(); FileUnknown = 1; } public override IResourceBlock[] GetReferences() { var list = new List(base.GetReferences()); if (ShaderGroup != null) list.Add(ShaderGroup); if (Skeleton != null) list.Add(Skeleton); if (DrawableModelsHigh != null) list.Add(DrawableModelsHigh); if (DrawableModelsMedium != null) list.Add(DrawableModelsMedium); if (DrawableModelsLow != null) list.Add(DrawableModelsLow); if (DrawableModelsVeryLow != null) list.Add(DrawableModelsVeryLow); if (Joints != null) list.Add(Joints); if (DrawableModelsX != null) list.Add(DrawableModelsX); return list.ToArray(); } public void AssignGeometryShaders(ShaderGroup shaderGrp) { //if (shaderGrp != null) //{ // ShaderGroup = shaderGrp; //} //map the shaders to the geometries if (shaderGrp != null) { var shaders = shaderGrp.Shaders.data_items; foreach (DrawableModel model in AllModels) { if (model?.Geometries?.data_items == null) continue; int geomcount = model.Geometries.data_items.Length; for (int i = 0; i < geomcount; i++) { var geom = model.Geometries.data_items[i]; var sid = geom.ShaderID; geom.Shader = (sid < shaders.Length) ? shaders[sid] : null; } } } else { } } public void BuildAllModels() { var allModels = new List(); if (DrawableModelsHigh != null) allModels.AddRange(DrawableModelsHigh.data_items); if (DrawableModelsMedium != null) allModels.AddRange(DrawableModelsMedium.data_items); if (DrawableModelsLow != null) allModels.AddRange(DrawableModelsLow.data_items); if (DrawableModelsVeryLow != null) allModels.AddRange(DrawableModelsVeryLow.data_items); if ((DrawableModelsX != null) && (DrawableModelsX != DrawableModelsHigh)) { allModels.AddRange(DrawableModelsX.data_items); } AllModels = allModels.ToArray(); } public void BuildVertexDecls() { var vds = new Dictionary(); foreach (DrawableModel model in AllModels) { foreach (var geom in model.Geometries.data_items) { var info = geom.VertexBuffer.Info; var declid = info.GetDeclarationId(); if (!vds.ContainsKey(declid)) { vds.Add(declid, info); } //else //debug test //{ // if ((VertexDecls[declid].Stride != info.Stride)||(VertexDecls[declid].Types != info.Types)) // { // } //} } } VertexDecls = new Dictionary(vds); } public void BuildRenderMasks() { var hmask = BuildRenderMask(DrawableModelsHigh?.data_items); var mmask = BuildRenderMask(DrawableModelsMedium?.data_items); var lmask = BuildRenderMask(DrawableModelsLow?.data_items); var vmask = BuildRenderMask(DrawableModelsVeryLow?.data_items); ////just testing //if (hmask != RenderMaskHigh) //{ }//no hit //if (mmask != RenderMaskMed) //{ }//no hit //if (lmask != RenderMaskLow) //{ }//no hit //if (vmask != RenderMaskVlow) //{ }//no hit RenderMaskHigh = hmask; RenderMaskMed = mmask; RenderMaskLow = lmask; RenderMaskVlow = vmask; } private byte BuildRenderMask(DrawableModel[] models) { byte mask = 0; if (models != null) { foreach (var model in models) { mask = (byte)(mask | model.RenderMask); } } return mask; } public DrawableBase ShallowCopy() { DrawableBase r = null; if (this is FragDrawable fd) { var f = new FragDrawable(); f.FragMatrix = fd.FragMatrix; f.FragMatricesIndsCount = fd.FragMatricesIndsCount; f.FragMatricesCapacity = fd.FragMatricesCapacity; f.FragMatricesCount = fd.FragMatricesCount; f.Bound = fd.Bound; f.FragMatricesInds = fd.FragMatricesInds; f.FragMatrices = fd.FragMatrices; f.Name = fd.Name; f.OwnerFragment = fd.OwnerFragment; f.OwnerFragmentPhys = fd.OwnerFragmentPhys; r = f; } if (this is Drawable dd) { var d = new Drawable(); d.LightAttributes = dd.LightAttributes; d.Name = dd.Name; d.Bound = dd.Bound; r = d; } if (r != null) { r.BoundingCenter = BoundingCenter; r.BoundingSphereRadius = BoundingSphereRadius; r.BoundingBoxMin = BoundingBoxMin; r.BoundingBoxMax = BoundingBoxMax; r.LodDistHigh = LodDistHigh; r.LodDistMed = LodDistMed; r.LodDistLow = LodDistLow; r.LodDistVlow = LodDistVlow; r.RenderMaskFlagsHigh = RenderMaskFlagsHigh; r.RenderMaskFlagsMed = RenderMaskFlagsMed; r.RenderMaskFlagsLow = RenderMaskFlagsLow; r.RenderMaskFlagsVlow = RenderMaskFlagsVlow; r.Unknown_98h = Unknown_98h; r.Unknown_9Ah = Unknown_9Ah; r.ShaderGroup = ShaderGroup; r.Skeleton = Skeleton?.Clone(); r.DrawableModelsHigh = DrawableModelsHigh; r.DrawableModelsMedium = DrawableModelsMedium; r.DrawableModelsLow = DrawableModelsLow; r.DrawableModelsVeryLow = DrawableModelsVeryLow; r.DrawableModelsX = DrawableModelsX; r.Joints = Joints; r.AllModels = AllModels; r.VertexDecls = VertexDecls; r.Owner = Owner; } return r; } } [TypeConverter(typeof(ExpandableObjectConverter))] public class Drawable : DrawableBase { public override long BlockLength { get { return 208; } } // structure data public ulong NamePointer { get; set; } public ResourceSimpleList64_s LightAttributes { get; set; } public ulong UnkPointer { get; set; } public ulong BoundPointer { get; set; } // reference data public string Name { get; set; } public Bounds Bound { get; set; } public string ErrorMessage { get; set; } private string_r NameBlock = null;//only used when saving.. public override void Read(ResourceDataReader reader, params object[] parameters) { base.Read(reader, parameters); // read structure data this.NamePointer = reader.ReadUInt64(); this.LightAttributes = reader.ReadBlock>(); this.UnkPointer = reader.ReadUInt64(); this.BoundPointer = reader.ReadUInt64(); try { // read reference data this.Name = reader.ReadStringAt(//BlockAt( this.NamePointer // offset ); this.Bound = reader.ReadBlockAt( this.BoundPointer // offset ); if (Bound != null) { Bound.Owner = this; } } catch (Exception ex) { ErrorMessage = ex.ToString(); } if (UnkPointer != 0) { } } public override void Write(ResourceDataWriter writer, params object[] parameters) { base.Write(writer, parameters); // update structure data this.NamePointer = (ulong)(this.NameBlock != null ? this.NameBlock.FilePosition : 0); this.BoundPointer = (ulong)(this.Bound != null ? this.Bound.FilePosition : 0); // write structure data writer.Write(this.NamePointer); writer.WriteBlock(this.LightAttributes); writer.Write(this.UnkPointer); writer.Write(this.BoundPointer); } public override void WriteXml(StringBuilder sb, int indent, string ddsfolder) { YdrXml.StringTag(sb, indent, "Name", YdrXml.XmlEscape(Name)); base.WriteXml(sb, indent, ddsfolder); Bounds.WriteXmlNode(Bound, sb, indent); if (LightAttributes?.data_items != null) { YdrXml.WriteItemArray(sb, LightAttributes.data_items, indent, "Lights"); } } public override void ReadXml(XmlNode node, string ddsfolder) { Name = Xml.GetChildInnerText(node, "Name"); base.ReadXml(node, ddsfolder); var bnode = node.SelectSingleNode("Bounds"); if (bnode != null) { Bound = Bounds.ReadXmlNode(bnode, this); } LightAttributes = new ResourceSimpleList64_s(); LightAttributes.data_items = XmlMeta.ReadItemArray(node, "Lights"); } public static void WriteXmlNode(Drawable d, StringBuilder sb, int indent, string ddsfolder, string name = "Drawable") { if (d == null) return; YdrXml.OpenTag(sb, indent, name); d.WriteXml(sb, indent + 1, ddsfolder); YdrXml.CloseTag(sb, indent, name); } public static Drawable ReadXmlNode(XmlNode node, string ddsfolder) { if (node == null) return null; var d = new Drawable(); d.ReadXml(node, ddsfolder); return d; } public override IResourceBlock[] GetReferences() { var list = new List(base.GetReferences()); if (Name != null) { NameBlock = (string_r)Name; list.Add(NameBlock); } if (Bound != null) list.Add(Bound); return list.ToArray(); } public override Tuple[] GetParts() { return new Tuple[] { new Tuple(0xB0, LightAttributes), }; } public override string ToString() { return Name; } } [TypeConverter(typeof(ExpandableObjectConverter))] public class DrawablePtfx : DrawableBase { public override long BlockLength { get { return 176; } } // structure data public ulong UnkPointer { get; set; } public override void Read(ResourceDataReader reader, params object[] parameters) { base.Read(reader, parameters); // read structure data this.UnkPointer = reader.ReadUInt64(); if (UnkPointer != 0) { } } public override void Write(ResourceDataWriter writer, params object[] parameters) { base.Write(writer, parameters); // write structure data writer.Write(this.UnkPointer); } public override void WriteXml(StringBuilder sb, int indent, string ddsfolder) { base.WriteXml(sb, indent, ddsfolder); } public override void ReadXml(XmlNode node, string ddsfolder) { base.ReadXml(node, ddsfolder); } public static void WriteXmlNode(DrawablePtfx d, StringBuilder sb, int indent, string ddsfolder, string name = "Drawable") { if (d == null) return; YdrXml.OpenTag(sb, indent, name); d.WriteXml(sb, indent + 1, ddsfolder); YdrXml.CloseTag(sb, indent, name); } public static DrawablePtfx ReadXmlNode(XmlNode node, string ddsfolder) { if (node == null) return null; var d = new DrawablePtfx(); d.ReadXml(node, ddsfolder); return d; } } [TypeConverter(typeof(ExpandableObjectConverter))] public class DrawablePtfxDictionary : ResourceFileBase { public override long BlockLength { get { return 64; } } // structure data public ulong Unknown_10h; // 0x0000000000000000 public ulong Unknown_18h = 1; // 0x0000000000000001 public ulong HashesPointer { get; set; } public ushort HashesCount1 { get; set; } public ushort HashesCount2 { get; set; } public uint Unknown_2Ch { get; set; } public ulong DrawablesPointer { get; set; } public ushort DrawablesCount1 { get; set; } public ushort DrawablesCount2 { get; set; } public uint Unknown_3Ch { get; set; } // reference data //public ResourceSimpleArray Hashes { get; set; } public uint[] Hashes { get; set; } public ResourcePointerArray64 Drawables { get; set; } private ResourceSystemStructBlock HashesBlock = null;//only used for saving public override void Read(ResourceDataReader reader, params object[] parameters) { base.Read(reader, parameters); // read structure data this.Unknown_10h = reader.ReadUInt64(); this.Unknown_18h = reader.ReadUInt64(); this.HashesPointer = reader.ReadUInt64(); this.HashesCount1 = reader.ReadUInt16(); this.HashesCount2 = reader.ReadUInt16(); this.Unknown_2Ch = reader.ReadUInt32(); this.DrawablesPointer = reader.ReadUInt64(); this.DrawablesCount1 = reader.ReadUInt16(); this.DrawablesCount2 = reader.ReadUInt16(); this.Unknown_3Ch = reader.ReadUInt32(); // read reference data this.Hashes = reader.ReadUintsAt(this.HashesPointer, this.HashesCount1); this.Drawables = reader.ReadBlockAt>(this.DrawablesPointer, this.DrawablesCount1); //if (Unknown_10h != 0) //{ } //if (Unknown_18h != 1) //{ } //if (Unknown_2Ch != 0) //{ } //if (Unknown_3Ch != 0) //{ } } public override void Write(ResourceDataWriter writer, params object[] parameters) { base.Write(writer, parameters); // update structure data this.HashesPointer = (ulong)(this.HashesBlock != null ? this.HashesBlock.FilePosition : 0); this.HashesCount1 = (ushort)(this.HashesBlock != null ? this.HashesBlock.ItemCount : 0); this.HashesCount2 = (ushort)(this.HashesBlock != null ? this.HashesBlock.ItemCount : 0); this.DrawablesPointer = (ulong)(this.Drawables != null ? this.Drawables.FilePosition : 0); this.DrawablesCount1 = (ushort)(this.Drawables != null ? this.Drawables.Count : 0); this.DrawablesCount2 = (ushort)(this.Drawables != null ? this.Drawables.Count : 0); // write structure data writer.Write(this.Unknown_10h); writer.Write(this.Unknown_18h); writer.Write(this.HashesPointer); writer.Write(this.HashesCount1); writer.Write(this.HashesCount2); writer.Write(this.Unknown_2Ch); writer.Write(this.DrawablesPointer); writer.Write(this.DrawablesCount1); writer.Write(this.DrawablesCount2); writer.Write(this.Unknown_3Ch); } public void WriteXml(StringBuilder sb, int indent, string ddsfolder) { if (Drawables?.data_items != null) { for (int i=0; i< Drawables.data_items.Length; i++) { var d = Drawables.data_items[i]; var h = (MetaHash)((i < (Hashes?.Length ?? 0)) ? Hashes[i] : 0); YddXml.OpenTag(sb, indent, "Item"); YddXml.StringTag(sb, indent + 1, "Name", YddXml.XmlEscape(h.ToCleanString())); d.WriteXml(sb, indent + 1, ddsfolder); YddXml.CloseTag(sb, indent, "Item"); } } } public void ReadXml(XmlNode node, string ddsfolder) { var drawables = new List(); var drawablehashes = new List(); var inodes = node.SelectNodes("Item"); if (inodes != null) { foreach (XmlNode inode in inodes) { var h = XmlMeta.GetHash(Xml.GetChildInnerText(inode, "Name")); var d = new DrawablePtfx(); d.ReadXml(inode, ddsfolder); drawables.Add(d); drawablehashes.Add(h); } } Hashes = drawablehashes.ToArray(); Drawables = new ResourcePointerArray64(); Drawables.data_items = drawables.ToArray(); } public static void WriteXmlNode(DrawablePtfxDictionary d, StringBuilder sb, int indent, string ddsfolder, string name = "DrawableDictionary") { if (d == null) return; YddXml.OpenTag(sb, indent, name); d.WriteXml(sb, indent + 1, ddsfolder); YddXml.CloseTag(sb, indent, name); } public static DrawablePtfxDictionary ReadXmlNode(XmlNode node, string ddsfolder) { if (node == null) return null; var d = new DrawablePtfxDictionary(); d.ReadXml(node, ddsfolder); return d; } public override IResourceBlock[] GetReferences() { var list = new List(base.GetReferences()); if (Hashes != null) { HashesBlock = new ResourceSystemStructBlock(Hashes); list.Add(HashesBlock); } if (Drawables != null) list.Add(Drawables); return list.ToArray(); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class DrawableDictionary : ResourceFileBase { public override long BlockLength { get { return 64; } } // structure data public ulong Unknown_10h; // 0x0000000000000000 public ulong Unknown_18h = 1; // 0x0000000000000001 public ulong HashesPointer { get; set; } public ushort HashesCount1 { get; set; } public ushort HashesCount2 { get; set; } public uint Unknown_2Ch; // 0x00000000 public ulong DrawablesPointer { get; set; } public ushort DrawablesCount1 { get; set; } public ushort DrawablesCount2 { get; set; } public uint Unknown_3Ch; // 0x00000000 // reference data //public ResourceSimpleArray Hashes { get; set; } public uint[] Hashes { get; set; } public ResourcePointerArray64 Drawables { get; set; } private ResourceSystemStructBlock HashesBlock = null;//only used for saving public long MemoryUsage { get { long val = 0; if ((Drawables != null) && (Drawables.data_items != null)) { foreach(var drawable in Drawables.data_items) { val += drawable.MemoryUsage; } } return val; } } public override void Read(ResourceDataReader reader, params object[] parameters) { base.Read(reader, parameters); // read structure data this.Unknown_10h = reader.ReadUInt64(); this.Unknown_18h = reader.ReadUInt64(); this.HashesPointer = reader.ReadUInt64(); this.HashesCount1 = reader.ReadUInt16(); this.HashesCount2 = reader.ReadUInt16(); this.Unknown_2Ch = reader.ReadUInt32(); this.DrawablesPointer = reader.ReadUInt64(); this.DrawablesCount1 = reader.ReadUInt16(); this.DrawablesCount2 = reader.ReadUInt16(); this.Unknown_3Ch = reader.ReadUInt32(); // read reference data this.Hashes = reader.ReadUintsAt(this.HashesPointer, this.HashesCount1); this.Drawables = reader.ReadBlockAt>( this.DrawablesPointer, // offset this.DrawablesCount1 ); //if (Unknown_10h != 0) //{ } //if (Unknown_18h != 1) //{ } //if (Unknown_2Ch != 0) //{ } //if (Unknown_3Ch != 0) //{ } } public override void Write(ResourceDataWriter writer, params object[] parameters) { base.Write(writer, parameters); // update structure data this.HashesPointer = (ulong)(this.HashesBlock != null ? this.HashesBlock.FilePosition : 0); this.HashesCount1 = (ushort)(this.HashesBlock != null ? this.HashesBlock.ItemCount : 0); this.HashesCount2 = (ushort)(this.HashesBlock != null ? this.HashesBlock.ItemCount : 0); this.DrawablesPointer = (ulong)(this.Drawables != null ? this.Drawables.FilePosition : 0); this.DrawablesCount1 = (ushort)(this.Drawables != null ? this.Drawables.Count : 0); this.DrawablesCount2 = (ushort)(this.Drawables != null ? this.Drawables.Count : 0); // write structure data writer.Write(this.Unknown_10h); writer.Write(this.Unknown_18h); writer.Write(this.HashesPointer); writer.Write(this.HashesCount1); writer.Write(this.HashesCount2); writer.Write(this.Unknown_2Ch); writer.Write(this.DrawablesPointer); writer.Write(this.DrawablesCount1); writer.Write(this.DrawablesCount2); writer.Write(this.Unknown_3Ch); } public void WriteXml(StringBuilder sb, int indent, string ddsfolder) { if (Drawables?.data_items != null) { foreach (var d in Drawables.data_items) { YddXml.OpenTag(sb, indent, "Item"); d.WriteXml(sb, indent + 1, ddsfolder); YddXml.CloseTag(sb, indent, "Item"); } } } public void ReadXml(XmlNode node, string ddsfolder) { var drawables = new List(); var drawablehashes = new List(); var inodes = node.SelectNodes("Item"); if (inodes != null) { foreach (XmlNode inode in inodes) { var d = new Drawable(); d.ReadXml(inode, ddsfolder); drawables.Add(d); drawablehashes.Add(JenkHash.GenHash(d.Name));//TODO: check this! } } Hashes = drawablehashes.ToArray(); Drawables = new ResourcePointerArray64(); Drawables.data_items = drawables.ToArray(); } public static void WriteXmlNode(DrawableDictionary d, StringBuilder sb, int indent, string ddsfolder, string name = "DrawableDictionary") { if (d == null) return; YddXml.OpenTag(sb, indent, name); d.WriteXml(sb, indent + 1, ddsfolder); YddXml.CloseTag(sb, indent, name); } public static DrawableDictionary ReadXmlNode(XmlNode node, string ddsfolder) { if (node == null) return null; var d = new DrawableDictionary(); d.ReadXml(node, ddsfolder); return d; } public override IResourceBlock[] GetReferences() { var list = new List(base.GetReferences()); if (Hashes != null) { HashesBlock = new ResourceSystemStructBlock(Hashes); list.Add(HashesBlock); } if (Drawables != null) list.Add(Drawables); return list.ToArray(); } } }