diff --git a/CodeWalker.Core/GameFiles/FileTypes/DistantLightsFile.cs b/CodeWalker.Core/GameFiles/FileTypes/DistantLightsFile.cs new file mode 100644 index 0000000..0ddc67d --- /dev/null +++ b/CodeWalker.Core/GameFiles/FileTypes/DistantLightsFile.cs @@ -0,0 +1,326 @@ +using SharpDX; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TC = System.ComponentModel.TypeConverterAttribute; +using EXP = System.ComponentModel.ExpandableObjectConverter; + +namespace CodeWalker.GameFiles +{ + [TC(typeof(EXP))] public class DistantLightsFile : GameFile, PackedFile + { + public bool HD { get; set; } = true; + public uint GridSize { get; set; } = 32; + public uint CellSize { get; set; } = 512; + public uint CellCount { get; set; } = 1024; + public uint NodeCount { get; set; } + public uint PathCount { get; set; } + public uint[] PathIndices { get; set; } //CellCount + public uint[] PathCounts1 { get; set; } //CellCount + public uint[] PathCounts2 { get; set; } //CellCount + public DistantLightsNode[] Nodes { get; set; } //NodeCount + public DistantLightsPath[] Paths { get; set; } //PathCount + public DistantLightsCell[] Cells { get; set; } //CellCount (built from loaded data) + + + public DistantLightsFile() : base(null, GameFileType.DistantLights) + { + } + public DistantLightsFile(RpfFileEntry entry) : base(entry, GameFileType.DistantLights) + { + RpfFileEntry = entry; + } + + + public void Load(byte[] data, RpfFileEntry entry) + { + if (entry != null) + { + RpfFileEntry = entry; + Name = entry.Name; + + if (!entry.NameLower.EndsWith("_hd.dat")) + { + HD = false; + GridSize = 16; + CellSize = 1024; + CellCount = 256; + } + } + + using (MemoryStream ms = new MemoryStream(data)) + { + DataReader r = new DataReader(ms, Endianess.BigEndian); + + Read(r); + }; + + Loaded = true; + } + public byte[] Save() + { + MemoryStream s = new MemoryStream(); + DataWriter w = new DataWriter(s); + + Write(w); + + var buf = new byte[s.Length]; + s.Position = 0; + s.Read(buf, 0, buf.Length); + return buf; + } + + + private void Read(DataReader r) + { + NodeCount = r.ReadUInt32(); + PathCount = r.ReadUInt32(); + PathIndices = new uint[CellCount]; + PathCounts1 = new uint[CellCount]; + PathCounts2 = new uint[CellCount]; + Nodes = new DistantLightsNode[NodeCount]; + Paths = new DistantLightsPath[PathCount]; + for (uint i = 0; i < CellCount; i++) + { + PathIndices[i] = r.ReadUInt32(); + } + for (uint i = 0; i < CellCount; i++) + { + PathCounts1[i] = r.ReadUInt32(); + } + for (uint i = 0; i < CellCount; i++) + { + PathCounts2[i] = r.ReadUInt32(); + } + for (uint i = 0; i < NodeCount; i++) + { + Nodes[i] = new DistantLightsNode(r); + } + for (uint i = 0; i < PathCount; i++) + { + Paths[i] = new DistantLightsPath(r, HD); + } + + + BuildCells(); + + } + private void Write(DataWriter w) + { + w.Write(NodeCount); + w.Write(PathCount); + + for (uint i = 0; i < CellCount; i++) + { + w.Write(PathIndices[i]); + } + for (uint i = 0; i < CellCount; i++) + { + w.Write(PathCounts1[i]); + } + for (uint i = 0; i < CellCount; i++) + { + w.Write(PathCounts2[i]); + } + for (uint i = 0; i < NodeCount; i++) + { + Nodes[i].Write(w); + } + for (uint i = 0; i < PathCount; i++) + { + Paths[i].Write(w, HD); + } + + } + + + private void BuildCells() + { + for (uint i = 0; i < PathCount; i++) + { + var path = Paths[i]; + path.Nodes = new DistantLightsNode[path.NodeCount]; + for (uint n = 0; n < path.NodeCount; n++) + { + path.Nodes[n] = Nodes[path.NodeIndex + n]; + } + } + + Cells = new DistantLightsCell[CellCount]; + for (uint x = 0; x < GridSize; x++) + { + for (uint y = 0; y < GridSize; y++) + { + var i = x * GridSize + y; + var cell = new DistantLightsCell(); + cell.Index = i; + cell.CellX = x; + cell.CellY = y; + cell.CellMin = new Vector2(x, y) * CellSize - 8192.0f; + cell.CellMax = cell.CellMin + CellSize; + var pc1 = PathCounts1[i]; + if (pc1 > 0) + { + cell.Paths1 = new DistantLightsPath[pc1]; + for (uint l = 0; l < pc1; l++) + { + cell.Paths1[l] = Paths[PathIndices[i] + l]; + } + } + var pc2 = PathCounts2[i]; + if (pc2 > 0) + { + cell.Paths2 = new DistantLightsPath[pc2]; + for (uint l = 0; l < pc2; l++) + { + cell.Paths2[l] = Paths[PathIndices[i] + l + pc1]; + } + } + Cells[i] = cell; + } + } + + } + + } + + [TC(typeof(EXP))] public class DistantLightsNode + { + public short X { get; set; } + public short Y { get; set; } + public short Z { get; set; } + + public DistantLightsNode() + { } + public DistantLightsNode(DataReader r) + { + Read(r); + } + + public void Read(DataReader r) + { + X = r.ReadInt16(); + Y = r.ReadInt16(); + Z = r.ReadInt16(); + } + public void Write(DataWriter w) + { + w.Write(X); + w.Write(Y); + w.Write(Z); + } + + public Vector3 Vector + { + get { return new Vector3(X, Y, Z); } + set { X = (short)Math.Round(value.X); Y = (short)Math.Round(value.Y); Z = (short)Math.Round(value.Z); } + } + + public override string ToString() + { + return Vector.ToString(); + } + } + + [TC(typeof(EXP))] public class DistantLightsPath + { + public short CenterX { get; set; } + public short CenterY { get; set; } + public ushort SizeX { get; set; } + public ushort SizeY { get; set; } + public ushort NodeIndex { get; set; } + public ushort NodeCount { get; set; } + public ushort Short7 { get; set; } + public ushort Short8 { get; set; } + public float Float1 { get; set; } + public byte Byte1 { get; set; } + public byte Byte2 { get; set; } + public byte Byte3 { get; set; } + public byte Byte4 { get; set; } + + public DistantLightsNode[] Nodes { get; set; } + + public DistantLightsPath() + { } + public DistantLightsPath(DataReader r, bool hd) + { + Read(r, hd); + } + + public void Read(DataReader r, bool hd) + { + CenterX = r.ReadInt16(); + CenterY = r.ReadInt16(); + SizeX = r.ReadUInt16(); + SizeY = r.ReadUInt16(); + NodeIndex = r.ReadUInt16(); + NodeCount = r.ReadUInt16(); + if (hd) + { + Short7 = r.ReadUInt16(); + Short8 = r.ReadUInt16(); + Float1 = r.ReadSingle(); + Byte1 = r.ReadByte(); + Byte2 = r.ReadByte(); + Byte3 = r.ReadByte(); + Byte4 = r.ReadByte(); + } + else + { + Byte1 = r.ReadByte(); + Byte2 = r.ReadByte(); + } + } + public void Write(DataWriter w, bool hd) + { + w.Write(CenterX); + w.Write(CenterY); + w.Write(SizeX); + w.Write(SizeY); + w.Write(NodeIndex); + w.Write(NodeCount); + if (hd) + { + w.Write(Short7); + w.Write(Short8); + w.Write(Float1); + w.Write(Byte1); + w.Write(Byte2); + w.Write(Byte3); + w.Write(Byte4); + } + else + { + w.Write(Byte1); + w.Write(Byte2); + } + } + + public override string ToString() + { + return CenterX.ToString() + ", " + CenterY.ToString() + ", " + SizeX.ToString() + ", " + SizeY.ToString() + ", " + + NodeIndex.ToString() + ", " + NodeCount.ToString() + ", " + Short7.ToString() + ", " + Short8.ToString() + ", " + + FloatUtil.ToString(Float1) + ", " + Byte1.ToString() + ", " + Byte2.ToString() + ", " + Byte3.ToString() + ", " + Byte4.ToString(); + } + } + + [TC(typeof(EXP))] public class DistantLightsCell + { + public uint Index { get; set; } + public uint CellX { get; set; } + public uint CellY { get; set; } + public Vector2 CellMin { get; set; } + public Vector2 CellMax { get; set; } + public DistantLightsPath[] Paths1 { get; set; } + public DistantLightsPath[] Paths2 { get; set; } + + public override string ToString() + { + return Index.ToString() + " (" + CellX.ToString() + ", " + CellY.ToString() + ") - " + + (Paths1?.Length ?? 0).ToString() + ", " + (Paths2?.Length ?? 0).ToString() + " - (" + + CellMin.ToString() + " - " + CellMax.ToString() + ")"; + } + } + +} diff --git a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs index 882c6ed..967c1cc 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs @@ -6,6 +6,399 @@ using EXP = System.ComponentModel.ExpandableObjectConverter; namespace CodeWalker.GameFiles { + [TC(typeof(EXP))] public class MrfFile : GameFile, PackedFile + { + public byte[] RawFileData { get; set; } + public uint Magic { get; set; } = 0x45566F4D; // 'MoVE' + public uint Version { get; set; } = 2; + public uint HeaderUnk1 { get; set; } = 0; + public uint HeaderUnk2 { get; set; } = 0; + public uint HeaderUnk3 { get; set; } = 0; + public uint DataLength { get; set; } + public uint FlagsCount { get; set; } + public uint Unk1_Count { get; set; } + public uint Unk2_Count { get; set; } + public uint Unk3_Count { get; set; } + + public MrfStructHeaderUnk1[] Unk1_Items { get; set; } + public MrfStructHeaderUnk2[] Unk2_Items { get; set; } + public MrfStructHeaderUnk3[] Unk3_Items { get; set; } + public byte[] FlagsItems { get; set; } + + public MrfNode[] AllNodes { get; set; } + public MrfNode RootNode { get; set; } + + public MrfFile() : base(null, GameFileType.Mrf) + { + } + + public MrfFile(RpfFileEntry entry) : base(entry, GameFileType.Mrf) + { + RpfFileEntry = entry; + } + + public void Load(byte[] data, RpfFileEntry entry) + { + RawFileData = data; + if (entry != null) + { + RpfFileEntry = entry; + Name = entry.Name; + } + + using (MemoryStream ms = new MemoryStream(data)) + { + DataReader r = new DataReader(ms, Endianess.LittleEndian); + + Read(r); + }; + } + + public byte[] Save() + { + MemoryStream s = new MemoryStream(); + DataWriter w = new DataWriter(s); + + Write(w); + + var buf = new byte[s.Length]; + s.Position = 0; + s.Read(buf, 0, buf.Length); + return buf; + } + + private void Write(DataWriter w) + { + if (Magic != 0x45566F4D || Version != 2 || HeaderUnk1 != 0 || HeaderUnk2 != 0) + throw new Exception("Failed to write MRF, header is invalid!"); + + w.Write(Magic); + w.Write(Version); + w.Write(HeaderUnk1); + w.Write(HeaderUnk2); + w.Write(HeaderUnk3); + w.Write(DataLength); + w.Write(FlagsCount); + + // Unused in final game + w.Write(Unk1_Count); + + if (Unk1_Count > 0) + { + foreach (var entry in Unk1_Items) + { + w.Write(entry.Size); + w.Write(entry.Bytes); + } + } + + w.Write(Unk2_Count); + + if (Unk2_Count > 0) + { + foreach (var entry in Unk2_Items) + { + w.Write(entry.Unk1); + w.Write(entry.Unk2); + } + } + + w.Write(Unk3_Count); + + if (Unk3_Count > 0) + { + foreach (var entry in Unk3_Items) + { + w.Write(entry.Unk1); + w.Write(entry.Unk2); + } + } + + if (AllNodes != null) + { + foreach (var node in AllNodes) + node.Write(w); + } + + for (int i = 0; i < FlagsCount; i++) + w.Write(FlagsItems[i]); + } + + private void Read(DataReader r) + { + Magic = r.ReadUInt32(); // Should be 'MoVE' + Version = r.ReadUInt32(); // GTA5 = 2, RDR3 = 11 + HeaderUnk1 = r.ReadUInt32(); // Should be 0 + HeaderUnk2 = r.ReadUInt32(); + HeaderUnk3 = r.ReadUInt32(); // Should be 0 + DataLength = r.ReadUInt32(); + FlagsCount = r.ReadUInt32(); + + if (Magic != 0x45566F4D || Version != 2 || HeaderUnk1 != 0 || HeaderUnk2 != 0) + throw new Exception("Failed to read MRF, header is invalid!"); + + // Unused in final game + Unk1_Count = r.ReadUInt32(); + if (Unk1_Count > 0) + { + Unk1_Items = new MrfStructHeaderUnk1[Unk1_Count]; + + for (int i = 0; i < Unk1_Count; i++) + Unk1_Items[i] = new MrfStructHeaderUnk1(r); + } + + Unk2_Count = r.ReadUInt32(); + if (Unk2_Count > 0) + { + Unk2_Items = new MrfStructHeaderUnk2[Unk2_Count]; + + for (int i = 0; i < Unk2_Count; i++) + Unk2_Items[i] = new MrfStructHeaderUnk2(r); + } + + Unk3_Count = r.ReadUInt32(); + if (Unk3_Count > 0) + { + Unk3_Items = new MrfStructHeaderUnk3[Unk3_Count]; + + for (int i = 0; i < Unk3_Count; i++) + Unk3_Items[i] = new MrfStructHeaderUnk3(r); + } + + var nodes = new List(); + + while (true) + { + var index = nodes.Count; + var offset = (int)r.Position; + + var node = ReadNode(r); + + if (node == null) break; + + node.FileIndex = index; + node.FileOffset = offset; + node.FileDataSize = ((int)r.Position) - offset; + + nodes.Add(node); + } + + AllNodes = nodes.ToArray(); + + if (FlagsCount != 0) + { + FlagsItems = new byte[FlagsCount]; + + for (int i = 0; i < FlagsCount; i++) + FlagsItems[i] = r.ReadByte(); + } + + + BuildNodeHierarchy(); + + + if (r.Position != r.Length) + throw new Exception($"Failed to read MRF ({r.Position} / {r.Length})"); + } + + private MrfNode ReadNode(DataReader r) + { + var startPos = r.Position; + var nodeType = (MrfNodeType)r.ReadUInt16(); + r.Position = startPos; + + if (nodeType <= MrfNodeType.None || nodeType >= MrfNodeType.Max) + { + if (r.Position != r.Length)//should only be at EOF + { } + return null; + } + + var node = CreateNode(nodeType); + + node.Read(r); + + return node; + } + + private MrfNode CreateNode(MrfNodeType infoType) + { + switch (infoType) + { + case MrfNodeType.StateMachineClass: + return new MrfNodeStateMachineClass(); + case MrfNodeType.Tail: + return new MrfNodeTail(); + case MrfNodeType.InlinedStateMachine: + return new MrfNodeInlinedStateMachine(); + case MrfNodeType.Unk4: + return new MrfNodeUnk4(); + case MrfNodeType.Blend: + return new MrfNodeBlend(); + case MrfNodeType.AddSubtract: + return new MrfNodeAddSubstract(); + case MrfNodeType.Filter: + return new MrfNodeFilter(); + case MrfNodeType.Unk8: + return new MrfNodeUnk8(); + case MrfNodeType.Frame: + return new MrfNodeFrame(); + case MrfNodeType.Unk10: + return new MrfNodeUnk10(); + case MrfNodeType.BlendN: + return new MrfNodeBlendN(); + case MrfNodeType.Clip: + return new MrfNodeClip(); + case MrfNodeType.Unk17: + return new MrfNodeUnk17(); + case MrfNodeType.Unk18: + return new MrfNodeUnk18(); + case MrfNodeType.Expression: + return new MrfNodeExpression(); + case MrfNodeType.Unk20: + return new MrfNodeUnk20(); + case MrfNodeType.Proxy: + return new MrfNodeProxy(); + case MrfNodeType.AddN: + return new MrfNodeAddN(); + case MrfNodeType.Identity: + return new MrfNodeIdentity(); + case MrfNodeType.Unk24: + return new MrfNodeUnk24(); + case MrfNodeType.Unk25: + return new MrfNodeUnk25(); + case MrfNodeType.MergeN: + return new MrfNodeMergeN(); + case MrfNodeType.State: + return new MrfNodeState(); + case MrfNodeType.Invalid: + return new MrfNodeInvalid(); + case MrfNodeType.Unk29: + return new MrfNodeUnk29(); + case MrfNodeType.SubNetworkClass: + return new MrfNodeSubNetworkClass(); + case MrfNodeType.Unk31: + return new MrfNodeUnk31(); + } + + throw new Exception($"A handler for ({infoType}) mrf node type is not valid"); + } + + + private int BuildNodeHierarchy(MrfNode node = null, int index = 0) + { + if (AllNodes == null) return 0; + if (AllNodes.Length <= index) return 0; + + if (node == null) + { + var rlist = new List(); + for (int i = 0; i < AllNodes.Length; i++) + { + var rnode = AllNodes[i]; + rlist.Add(rnode); + i += BuildNodeHierarchy(rnode, i); + } + + var smlist = new List(); + var imlist = new List(); + var snlist = new List(); + foreach (var n in AllNodes) + { + if (n is MrfNodeStateMachineClass sm) + { + smlist.Add(sm); + if (sm.ChildNodes != null) for (int i = 0; i < sm.ChildNodes.Length; i++) if (sm.ChildNodes[i].NodeIndex != i) + { }//sanity check - don't get here + } + if (n is MrfNodeInlinedStateMachine im) + { + imlist.Add(im); + if (im.ChildNodes != null) for (int i = 0; i < im.ChildNodes.Length; i++) if (im.ChildNodes[i].NodeIndex != i) + { }//sanity check - don't get here + } + if (n is MrfNodeState sn) + { + snlist.Add(sn); + if (sn.ChildNodes != null) for (int i = 0; i < sn.ChildNodes.Length; i++) if (sn.ChildNodes[i].NodeIndex != i) + { }//sanity check - don't get here + } + } + + if (rlist.Count > 0) + { + RootNode = rlist[0]; + } + if (rlist.Count != 1) + { }//sanity check - don't get here + if (AllNodes.Length > 1000) + { } + return 0; + } + + if (node is MrfNodeStateBase snode) + { + var c = 0; + var clist = new List(); + int ccount = snode.StateChildCount; + if (ccount == 0) + { + return 0; + } + for (int i = 0; i <= ccount; i++) + { + if ((i == ccount) && (node is MrfNodeState)) + { break; } + var cind = index + c + i + 1; + if (cind >= AllNodes.Length) + { + if (i != ccount) + { }//don't get here (tried to continue past the end of the array!) + break; + } + var cnode = AllNodes[cind]; + if (cnode is MrfNodeTail) + { + i--; + c++; + if (clist.Count > 0) + { + var prevnode = clist[clist.Count - 1]; + if (prevnode is MrfNodeStateBase sprevnode) + { + if (sprevnode.TailNode != null) + { }//don't get here (tail node was already assigned?!?) + sprevnode.TailNode = cnode; + if (clist.Count == ccount) + { break; }//list is full, don't continue + } + else + { }//don't get here (previous node isn't a state??) + } + else + { }//don't get here (can't have tail without a previous node!) + continue; + } + else if (clist.Count == ccount) + { break; }//this node isn't a tail, but the list is already full, so it must belong to another node + if (cnode.NodeIndex != i) + { break; }//don't get here (node index mismatch!) + clist.Add(cnode); + c += BuildNodeHierarchy(cnode, cind);//recurse... + } + if (clist.Count != ccount) + { }//don't get here (sanity check) + snode.ChildNodes = clist.ToArray(); + return c + clist.Count; + } + + return 0; + } + + } + + + // Unused node indexes by GTAV: 11, 12, 14, 16 // Exist in GTAV but not used in MRFs: 4, 8, 10, 17, 21, 22, 28, 29, 31, 32 public enum MrfNodeType : ushort @@ -784,32 +1177,6 @@ namespace CodeWalker.GameFiles } } - [TC(typeof(EXP))] public class MrfStructStateSignalDataUnk3 : MrfStruct - { - public uint UnkValue { get; } - public uint UnkDefault { get; } - public ulong UnkRange { get; } - - public MrfStructStateSignalDataUnk3(DataReader r) - { - UnkValue = r.ReadUInt32(); - UnkDefault = r.ReadUInt32(); - UnkRange = r.ReadUInt64(); // 2 merged 32 bit values? - } - - public override void Write(DataWriter w) - { - w.Write(UnkValue); - w.Write(UnkDefault); - w.Write(UnkRange); - } - - public override string ToString() - { - return UnkValue.ToString() + " - " + UnkDefault.ToString() + " - " + UnkRange.ToString(); - } - } - [TC(typeof(EXP))] public class MrfStructStateMachineState : MrfStruct { public MetaHash StateName { get; } @@ -833,61 +1200,95 @@ namespace CodeWalker.GameFiles } } - - // FIXME: most likely broken - - [TC(typeof(EXP))] public class MrfStructStateSignalData : MrfStruct + [TC(typeof(EXP))] public class MrfStructStateSignalVariable : MrfStruct { - public uint UnkType { get; } - public uint NameHash { get; } - public uint Unk1 { get; } - public uint Unk2 { get; } - public uint Unk3_Count { get; } - public uint Unk4 { get; } + public float Value { get; }//always 0 - probably float? + public float Default { get; } + public float RangeMin { get; } + public float RangeMax { get; } - public MrfStructStateSignalDataUnk3[] Unk3_Items { get; } - - public MrfStructStateSignalData(DataReader r) + public MrfStructStateSignalVariable(DataReader r) { - UnkType = r.ReadUInt32(); - NameHash = r.ReadUInt32(); - - if (UnkType != 5) - return; - - Unk1 = r.ReadUInt32(); - Unk2 = r.ReadUInt32(); // Default value too? - Unk3_Count = r.ReadUInt32(); - Unk4 = r.ReadUInt32(); - - Unk3_Items = new MrfStructStateSignalDataUnk3[Unk3_Count]; - - for (int i = 0; i < Unk3_Count; i++) - Unk3_Items[i] = new MrfStructStateSignalDataUnk3(r); + Value = r.ReadSingle(); + Default = r.ReadSingle(); + RangeMin = r.ReadSingle(); + RangeMax = r.ReadSingle(); } public override void Write(DataWriter w) { - w.Write(UnkType); - w.Write(NameHash); - - if (UnkType != 5) - return; - - w.Write(Unk1); - w.Write(Unk2); - w.Write(Unk3_Count); - w.Write(Unk4); - - // FIXME: might be broken if changed outside - foreach (var item in Unk3_Items) - item.Write(w); + w.Write(Value); + w.Write(Default); + w.Write(RangeMin); + w.Write(RangeMax); } public override string ToString() { - return UnkType.ToString() + " - " + NameHash.ToString() + " - " + - Unk1.ToString() + " - " + Unk2.ToString() + " - C:" + Unk3_Count.ToString() + " - " + Unk4.ToString(); + return Value.ToString() + " - " + Default.ToString() + " - (" + RangeMin.ToString() + " - " + RangeMax.ToString() + ")"; + } + } + + [TC(typeof(EXP))] public class MrfStructStateSignalData : MrfStruct + { + public uint Type { get; set; } //0, 2, 4, 5 + public uint NameHash { get; set; } //only for Type==2, always 0 otherwise + public uint Unk1 { get; set; } //only for Type==5, always 4 + public float Unk2 { get; set; } //only for Type==5, values: 0.0, 0.95, 0.8, 1.4, 1.8, 2.2, 0.2, 0.5 - maybe min value? always less than Unk3 when nonzero + public float Unk3 { get; set; } //only for Type==5, values: 1.0, 1.4, 1.8, 2.2, 2.95, 0.8, 0.5 - maybe max value? + public uint ItemCount { get; set; } //only for Type==5 + public uint Unk4 { get; set; } //only for Type==5, always 4 + + public MrfStructStateSignalVariable[] Items { get; set; } + + public MrfStructStateSignalData(DataReader r) + { + Type = r.ReadUInt32(); + if (Type == 5) + { + Unk1 = r.ReadUInt32(); + Unk2 = r.ReadSingle(); + Unk3 = r.ReadSingle(); + ItemCount = r.ReadUInt32(); + Unk4 = r.ReadUInt32(); + Items = new MrfStructStateSignalVariable[ItemCount]; + for (int i = 0; i < ItemCount; i++) + { + Items[i] = new MrfStructStateSignalVariable(r); + } + } + else + { + NameHash = r.ReadUInt32(); + } + } + + public override void Write(DataWriter w) + { + w.Write(Type); + if (Type == 5) + { + w.Write(Unk1); + w.Write(Unk2); + w.Write(Unk3); + w.Write(ItemCount); + w.Write(Unk4); + + // FIXME: might be broken if changed outside + foreach (var item in Items) + item.Write(w); + } + else + { + w.Write(NameHash); + } + + } + + public override string ToString() + { + return Type.ToString() + " - " + NameHash.ToString() + " - " + + Unk1.ToString() + " - " + Unk2.ToString() + " - " + FloatUtil.ToString(Unk3) + " - " + Unk4.ToString() + " - C:" + ItemCount.ToString(); } } @@ -895,7 +1296,7 @@ namespace CodeWalker.GameFiles { public ushort Unk1 { get; } public ushort Unk2 { get; } - public ushort Unk3 { get; } + public ushort Unk3 { get; }//Items.Length * 8 public ushort Unk4 { get; } public MrfStructStateSignalData[] Items { get; } @@ -906,26 +1307,20 @@ namespace CodeWalker.GameFiles Unk3 = r.ReadUInt16(); Unk4 = r.ReadUInt16(); - uint shouldContinue; var itemsList = new List(); - // FIXME: those loops looks weird - do + while (true) { - while (true) + var item = new MrfStructStateSignalData(r); + itemsList.Add(item); + if (item.Type == 0) { - var data = new MrfStructStateSignalData(r); - itemsList.Add(data); - - shouldContinue = data.UnkType; - - if (data.UnkType != 5) - break; + break; } } - while (shouldContinue != 0); Items = itemsList.ToArray(); + } public override void Write(DataWriter w) @@ -946,6 +1341,9 @@ namespace CodeWalker.GameFiles } } + + // FIXME: most likely broken + [TC(typeof(EXP))] public class MrfStructNegativeDataUnk7 : MrfStruct { public uint Unk1 { get; set; } @@ -1264,7 +1662,7 @@ namespace CodeWalker.GameFiles if (((Flags >> 6) & 3) != 0) Unk7 = r.ReadUInt32(); - if (((Flags >> 6) & 3) != 0) + if (((Flags >> 8) & 3) != 0) Unk8 = r.ReadUInt32(); } @@ -1298,7 +1696,7 @@ namespace CodeWalker.GameFiles if (((Flags >> 6) & 3) != 0) w.Write(Unk7); - if (((Flags >> 6) & 3) != 0) + if (((Flags >> 8) & 3) != 0) w.Write(Unk8); } @@ -1910,394 +2308,4 @@ namespace CodeWalker.GameFiles #endregion - [TC(typeof(EXP))] public class MrfFile : GameFile, PackedFile - { - public byte[] RawFileData { get; set; } - public uint Magic { get; set; } = 0x45566F4D; // 'MoVE' - public uint Version { get; set; } = 2; - public uint HeaderUnk1 { get; set; } = 0; - public uint HeaderUnk2 { get; set; } = 0; - public uint HeaderUnk3 { get; set; } = 0; - public uint DataLength { get; set; } - public uint FlagsCount { get; set; } - public uint Unk1_Count { get; set; } - public uint Unk2_Count { get; set; } - public uint Unk3_Count { get; set; } - - public MrfStructHeaderUnk1[] Unk1_Items { get; set; } - public MrfStructHeaderUnk2[] Unk2_Items { get; set; } - public MrfStructHeaderUnk3[] Unk3_Items { get; set; } - public byte[] FlagsItems { get; set; } - - public MrfNode[] AllNodes { get; set; } - public MrfNode RootNode { get; set; } - - public MrfFile() : base(null, GameFileType.Mrf) - { - } - - public MrfFile(RpfFileEntry entry) : base(entry, GameFileType.Mrf) - { - RpfFileEntry = entry; - } - - public void Load(byte[] data, RpfFileEntry entry) - { - RawFileData = data; - if (entry != null) - { - RpfFileEntry = entry; - Name = entry.Name; - } - - using (MemoryStream ms = new MemoryStream(data)) - { - DataReader r = new DataReader(ms, Endianess.LittleEndian); - - Read(r); - }; - } - - public byte[] Save() - { - MemoryStream s = new MemoryStream(); - DataWriter w = new DataWriter(s); - - Write(w); - - var buf = new byte[s.Length]; - s.Position = 0; - s.Read(buf, 0, buf.Length); - return buf; - } - - private void Write(DataWriter w) - { - if (Magic != 0x45566F4D || Version != 2 || HeaderUnk1 != 0 || HeaderUnk2 != 0) - throw new Exception("Failed to write MRF, header is invalid!"); - - w.Write(Magic); - w.Write(Version); - w.Write(HeaderUnk1); - w.Write(HeaderUnk2); - w.Write(HeaderUnk3); - w.Write(DataLength); - w.Write(FlagsCount); - - // Unused in final game - w.Write(Unk1_Count); - - if (Unk1_Count > 0) - { - foreach (var entry in Unk1_Items) - { - w.Write(entry.Size); - w.Write(entry.Bytes); - } - } - - w.Write(Unk2_Count); - - if (Unk2_Count > 0) - { - foreach (var entry in Unk2_Items) - { - w.Write(entry.Unk1); - w.Write(entry.Unk2); - } - } - - w.Write(Unk3_Count); - - if (Unk3_Count > 0) - { - foreach (var entry in Unk3_Items) - { - w.Write(entry.Unk1); - w.Write(entry.Unk2); - } - } - - if (AllNodes != null) - { - foreach (var node in AllNodes) - node.Write(w); - } - - for (int i = 0; i < FlagsCount; i++) - w.Write(FlagsItems[i]); - } - - private void Read(DataReader r) - { - Magic = r.ReadUInt32(); // Should be 'MoVE' - Version = r.ReadUInt32(); // GTA5 = 2, RDR3 = 11 - HeaderUnk1 = r.ReadUInt32(); // Should be 0 - HeaderUnk2 = r.ReadUInt32(); - HeaderUnk3 = r.ReadUInt32(); // Should be 0 - DataLength = r.ReadUInt32(); - FlagsCount = r.ReadUInt32(); - - if (Magic != 0x45566F4D || Version != 2 || HeaderUnk1 != 0 || HeaderUnk2 != 0) - throw new Exception("Failed to read MRF, header is invalid!"); - - // Unused in final game - Unk1_Count = r.ReadUInt32(); - if (Unk1_Count > 0) - { - Unk1_Items = new MrfStructHeaderUnk1[Unk1_Count]; - - for (int i = 0; i < Unk1_Count; i++) - Unk1_Items[i] = new MrfStructHeaderUnk1(r); - } - - Unk2_Count = r.ReadUInt32(); - if (Unk2_Count > 0) - { - Unk2_Items = new MrfStructHeaderUnk2[Unk2_Count]; - - for (int i = 0; i < Unk2_Count; i++) - Unk2_Items[i] = new MrfStructHeaderUnk2(r); - } - - Unk3_Count = r.ReadUInt32(); - if (Unk3_Count > 0) - { - Unk3_Items = new MrfStructHeaderUnk3[Unk3_Count]; - - for (int i = 0; i < Unk3_Count; i++) - Unk3_Items[i] = new MrfStructHeaderUnk3(r); - } - - var nodes = new List(); - - while (true) - { - var index = nodes.Count; - var offset = (int)r.Position; - - var node = ReadNode(r); - - if (node == null) break; - - node.FileIndex = index; - node.FileOffset = offset; - node.FileDataSize = ((int)r.Position) - offset; - - nodes.Add(node); - } - - AllNodes = nodes.ToArray(); - - if (FlagsCount != 0) - { - FlagsItems = new byte[FlagsCount]; - - for (int i = 0; i < FlagsCount; i++) - FlagsItems[i] = r.ReadByte(); - } - - - BuildNodeHierarchy(); - - - if (r.Position != r.Length) - throw new Exception($"Failed to read MRF ({r.Position} / {r.Length})"); - } - - private MrfNode ReadNode(DataReader r) - { - var startPos = r.Position; - var nodeType = (MrfNodeType)r.ReadUInt16(); - r.Position = startPos; - - if (nodeType <= MrfNodeType.None || nodeType >= MrfNodeType.Max) - { - if (r.Position != r.Length)//should only be at EOF - { } - return null; - } - - var node = CreateNode(nodeType); - - node.Read(r); - - return node; - } - - private MrfNode CreateNode(MrfNodeType infoType) - { - switch (infoType) - { - case MrfNodeType.StateMachineClass: - return new MrfNodeStateMachineClass(); - case MrfNodeType.Tail: - return new MrfNodeTail(); - case MrfNodeType.InlinedStateMachine: - return new MrfNodeInlinedStateMachine(); - case MrfNodeType.Unk4: - return new MrfNodeUnk4(); - case MrfNodeType.Blend: - return new MrfNodeBlend(); - case MrfNodeType.AddSubtract: - return new MrfNodeAddSubstract(); - case MrfNodeType.Filter: - return new MrfNodeFilter(); - case MrfNodeType.Unk8: - return new MrfNodeUnk8(); - case MrfNodeType.Frame: - return new MrfNodeFrame(); - case MrfNodeType.Unk10: - return new MrfNodeUnk10(); - case MrfNodeType.BlendN: - return new MrfNodeBlendN(); - case MrfNodeType.Clip: - return new MrfNodeClip(); - case MrfNodeType.Unk17: - return new MrfNodeUnk17(); - case MrfNodeType.Unk18: - return new MrfNodeUnk18(); - case MrfNodeType.Expression: - return new MrfNodeExpression(); - case MrfNodeType.Unk20: - return new MrfNodeUnk20(); - case MrfNodeType.Proxy: - return new MrfNodeProxy(); - case MrfNodeType.AddN: - return new MrfNodeAddN(); - case MrfNodeType.Identity: - return new MrfNodeIdentity(); - case MrfNodeType.Unk24: - return new MrfNodeUnk24(); - case MrfNodeType.Unk25: - return new MrfNodeUnk25(); - case MrfNodeType.MergeN: - return new MrfNodeMergeN(); - case MrfNodeType.State: - return new MrfNodeState(); - case MrfNodeType.Invalid: - return new MrfNodeInvalid(); - case MrfNodeType.Unk29: - return new MrfNodeUnk29(); - case MrfNodeType.SubNetworkClass: - return new MrfNodeSubNetworkClass(); - case MrfNodeType.Unk31: - return new MrfNodeUnk31(); - } - - throw new Exception($"A handler for ({infoType}) mrf node type is not valid"); - } - - - private int BuildNodeHierarchy(MrfNode node = null, int index = 0) - { - if (AllNodes == null) return 0; - if (AllNodes.Length <= index) return 0; - - if (node == null) - { - var rlist = new List(); - for (int i = 0; i < AllNodes.Length; i++) - { - var rnode = AllNodes[i]; - rlist.Add(rnode); - i += BuildNodeHierarchy(rnode, i); - } - - var smlist = new List(); - var imlist = new List(); - var snlist = new List(); - foreach (var n in AllNodes) - { - if (n is MrfNodeStateMachineClass sm) - { - smlist.Add(sm); - if (sm.ChildNodes != null) for (int i = 0; i < sm.ChildNodes.Length; i++) if (sm.ChildNodes[i].NodeIndex != i) - { }//sanity check - don't get here - } - if (n is MrfNodeInlinedStateMachine im) - { - imlist.Add(im); - if (im.ChildNodes != null) for (int i = 0; i < im.ChildNodes.Length; i++) if (im.ChildNodes[i].NodeIndex != i) - { }//sanity check - don't get here - } - if (n is MrfNodeState sn) - { - snlist.Add(sn); - if (sn.ChildNodes != null) for (int i = 0; i < sn.ChildNodes.Length; i++) if (sn.ChildNodes[i].NodeIndex != i) - { }//sanity check - don't get here - } - } - - if (rlist.Count > 0) - { - RootNode = rlist[0]; - } - if (rlist.Count != 1) - { }//sanity check - don't get here - if (AllNodes.Length > 1000) - { } - return 0; - } - - if (node is MrfNodeStateBase snode) - { - var c = 0; - var clist = new List(); - int ccount = snode.StateChildCount; - if (ccount == 0) - { - return 0; - } - for (int i = 0; i <= ccount; i++) - { - if ((i == ccount) && (node is MrfNodeState)) - { break; } - var cind = index + c + i + 1; - if (cind >= AllNodes.Length) - { - if (i != ccount) - { }//don't get here (tried to continue past the end of the array!) - break; - } - var cnode = AllNodes[cind]; - if (cnode is MrfNodeTail) - { - i--; - c++; - if (clist.Count > 0) - { - var prevnode = clist[clist.Count - 1]; - if (prevnode is MrfNodeStateBase sprevnode) - { - if (sprevnode.TailNode != null) - { }//don't get here (tail node was already assigned?!?) - sprevnode.TailNode = cnode; - if (clist.Count == ccount) - { break; }//list is full, don't continue - } - else - { }//don't get here (previous node isn't a state??) - } - else - { }//don't get here (can't have tail without a previous node!) - continue; - } - else if (clist.Count == ccount) - { break; }//this node isn't a tail, but the list is already full, so it must belong to another node - if (cnode.NodeIndex != i) - { break; }//don't get here (node index mismatch!) - clist.Add(cnode); - c += BuildNodeHierarchy(cnode, cind);//recurse... - } - if (clist.Count != ccount) - { }//don't get here (sanity check) - snode.ChildNodes = clist.ToArray(); - return c + clist.Count; - } - - return 0; - } - - } } diff --git a/CodeWalker.Core/GameFiles/FileTypes/YedFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YedFile.cs index 2081198..6e62ab4 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/YedFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/YedFile.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; namespace CodeWalker.GameFiles { @@ -56,10 +58,68 @@ namespace CodeWalker.GameFiles InitDictionaries(); } + public byte[] Save() + { + byte[] data = ResourceBuilder.Build(ExpressionDictionary, 25); //yed is type/version 25... + + return data; + } + + public void InitDictionaries() { ExprMap = ExpressionDictionary?.ExprMap ?? new Dictionary(); } } + + + public class YedXml : MetaXmlBase + { + + public static string GetXml(YedFile yed, string outputFolder = "") + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(XmlHeader); + + if (yed?.ExpressionDictionary != null) + { + ExpressionDictionary.WriteXmlNode(yed.ExpressionDictionary, sb, 0); + } + + return sb.ToString(); + } + + } + + public class XmlYed + { + + public static YedFile GetYed(string xml, string inputFolder = "") + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + return GetYed(doc, inputFolder); + } + + public static YedFile GetYed(XmlDocument doc, string inputFolder = "") + { + YedFile r = new YedFile(); + + var node = doc.DocumentElement; + if (node != null) + { + r.ExpressionDictionary = ExpressionDictionary.ReadXmlNode(node); + + r.InitDictionaries(); + } + + r.Name = Path.GetFileName(inputFolder); + + return r; + } + + } + + } diff --git a/CodeWalker.Core/GameFiles/FileTypes/YvrFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YvrFile.cs index eaf07a0..ec6a2de 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/YvrFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/YvrFile.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; namespace CodeWalker.GameFiles { @@ -58,4 +60,51 @@ namespace CodeWalker.GameFiles } + + + public class YvrXml : MetaXmlBase + { + + public static string GetXml(YvrFile yvr) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(XmlHeader); + + if (yvr?.Records != null) + { + VehicleRecordList.WriteXmlNode(yvr.Records, sb, 0); + } + + return sb.ToString(); + } + + } + + public class XmlYvr + { + + public static YvrFile GetYvr(string xml, string inputFolder = "") + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + return GetYvr(doc, inputFolder); + } + + public static YvrFile GetYvr(XmlDocument doc, string inputFolder = "") + { + YvrFile r = new YvrFile(); + + var node = doc.DocumentElement; + if (node != null) + { + r.Records = VehicleRecordList.ReadXmlNode(node); + } + + r.Name = Path.GetFileName(inputFolder); + + return r; + } + + } + } diff --git a/CodeWalker.Core/GameFiles/FileTypes/YwrFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YwrFile.cs index 3e82b08..3f0b636 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/YwrFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/YwrFile.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; namespace CodeWalker.GameFiles { @@ -58,4 +60,52 @@ namespace CodeWalker.GameFiles } + + + public class YwrXml : MetaXmlBase + { + + public static string GetXml(YwrFile ywr) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(XmlHeader); + + if (ywr?.Waypoints != null) + { + WaypointRecordList.WriteXmlNode(ywr.Waypoints, sb, 0); + } + + return sb.ToString(); + } + + } + + public class XmlYwr + { + + public static YwrFile GetYwr(string xml, string inputFolder = "") + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + return GetYwr(doc, inputFolder); + } + + public static YwrFile GetYwr(XmlDocument doc, string inputFolder = "") + { + YwrFile r = new YwrFile(); + + var node = doc.DocumentElement; + if (node != null) + { + r.Waypoints = WaypointRecordList.ReadXmlNode(node); + } + + r.Name = Path.GetFileName(inputFolder); + + return r; + } + + } + + } diff --git a/CodeWalker.Core/GameFiles/GameFile.cs b/CodeWalker.Core/GameFiles/GameFile.cs index c9e7b3f..7e55e0c 100644 --- a/CodeWalker.Core/GameFiles/GameFile.cs +++ b/CodeWalker.Core/GameFiles/GameFile.cs @@ -83,6 +83,7 @@ namespace CodeWalker.GameFiles Heightmap = 27, Watermap = 28, Mrf = 29, + DistantLights = 30, } diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index 753664e..874764e 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -212,6 +212,8 @@ namespace CodeWalker.GameFiles //TestYfts(); //TestYpts(); //TestYnvs(); + //TestYvrs(); + //TestYwrs(); //TestYmaps(); //TestMrfs(); //TestPlacements(); @@ -3518,6 +3520,15 @@ namespace CodeWalker.GameFiles YedFile yed = new YedFile(rfe); RpfMan.LoadFile(yed, rfe); + var xml = YedXml.GetXml(yed); + var yed2 = XmlYed.GetYed(xml); + var data2 = yed2.Save(); + var yed3 = new YedFile(); + RpfFile.LoadResourceFile(yed3, data2, 25);//full roundtrip + var xml2 = YedXml.GetXml(yed3); + if (xml != xml2) + { } + } } #if !DEBUG @@ -4396,6 +4407,102 @@ namespace CodeWalker.GameFiles if (errorfiles.Count > 0) { } } + public void TestYvrs() + { + + var exceptions = new List(); + + foreach (RpfFile file in AllRpfs) + { + foreach (RpfEntry entry in file.AllEntries) + { +#if !DEBUG + try +#endif + { + var rfe = entry as RpfFileEntry; + if (rfe == null) continue; + + if (rfe.NameLower.EndsWith(".yvr")) + { + if (rfe.NameLower == "agencyprep001.yvr") continue; //this file seems corrupted + + UpdateStatus(string.Format(entry.Path)); + + YvrFile yvr = new YvrFile(rfe); + RpfMan.LoadFile(yvr, rfe); + + var xml = YvrXml.GetXml(yvr); + var yvr2 = XmlYvr.GetYvr(xml); + var data2 = yvr2.Save(); + var yvr3 = new YvrFile(); + RpfFile.LoadResourceFile(yvr3, data2, 1);//full roundtrip + var xml2 = YvrXml.GetXml(yvr3); + if (xml != xml2) + { } + + } + } +#if !DEBUG + catch (Exception ex) + { + UpdateStatus("Error! " + ex.ToString()); + exceptions.Add(ex); + } +#endif + } + } + + if (exceptions.Count > 0) + { } + } + public void TestYwrs() + { + + var exceptions = new List(); + + foreach (RpfFile file in AllRpfs) + { + foreach (RpfEntry entry in file.AllEntries) + { +#if !DEBUG + try +#endif + { + var rfe = entry as RpfFileEntry; + if (rfe == null) continue; + + if (rfe.NameLower.EndsWith(".ywr")) + { + UpdateStatus(string.Format(entry.Path)); + + YwrFile ywr = new YwrFile(rfe); + RpfMan.LoadFile(ywr, rfe); + + var xml = YwrXml.GetXml(ywr); + var ywr2 = XmlYwr.GetYwr(xml); + var data2 = ywr2.Save(); + var ywr3 = new YwrFile(); + RpfFile.LoadResourceFile(ywr3, data2, 1);//full roundtrip + var xml2 = YwrXml.GetXml(ywr3); + if (xml != xml2) + { } + + } + } +#if !DEBUG + catch (Exception ex) + { + UpdateStatus("Error! " + ex.ToString()); + exceptions.Add(ex); + } +#endif + } + } + + if (exceptions.Count > 0) + { } + } public void TestYmaps() { foreach (RpfFile file in AllRpfs) diff --git a/CodeWalker.Core/GameFiles/MetaTypes/MetaNames.cs b/CodeWalker.Core/GameFiles/MetaTypes/MetaNames.cs index b0b167e..b0c2927 100644 --- a/CodeWalker.Core/GameFiles/MetaTypes/MetaNames.cs +++ b/CodeWalker.Core/GameFiles/MetaTypes/MetaNames.cs @@ -18617,6 +18617,91 @@ namespace CodeWalker.GameFiles + + + //MRF related hashes from Disquse + statemachine = 429383484, + success = 975994832, + cutting = 1599956773, + leftfailure = 1765753571, + enterfinish = 663756144, + approach = 61579965, + win = 3732660434, + loss = 42942348, + altwalk = 2629052680, + standidle = 1244639780, + bottlehold = 1250922115, + bottleshake = 2761060053, + champagnespray = 2153350042, + back = 927041140, + front = 2314350754, + partone = 1335537449, + parttwo = 2585437493, + partthree = 3894308744, + partfour = 4103663607, + partfive = 288022579, + backpartone = 3350093298, + backparttwo = 3195341626, + backpartthree = 3228129062, + backpartfour = 1672309095, + backpartfive = 3417257801, + wobble = 3603821314, + x_axis = 3546530265, + y_axis = 2645075825, + z_axis = 1604254135, + drill_force = 1362721915, + shake_intensity = 612782192, + horizontal_aim = 3036116190, + verticle_aim = 3709155216, + direction_up_down = 1282217224, + phase_out = 3243435260, + upperbodyonoff = 2718654972, + introphase = 3905075704, + pitch = 1061927116, + isblocked = 2611301023, + isfirstperson = 1776409970, + idle2 = 2046496619, + idle3 = 2202608135, + idle4 = 1261318610, + lossfinished = 2337837154, + winfinished = 3589054106, + introfinished = 3677288433, + outrofinished = 601717917, + leftfailfinish = 1788424853, + readyforfadein = 3896574076, + idlestarted = 1321201090, + blendout = 1842457532, + idle_a = 3851133436, + escape = 3655182927, + upperbodyclipended = 3141014298, + result_perfect = 2007828787, + result_good = 1148916770, + result_average = 1939741573, + result_bad = 1402208964, + perfectresult = 452682106, + goodresult = 1109180461, + averageresult = 2674371639, + badresult = 3234678822, + cancelneutral = 3758646108, + dance = 1152043290, + stage_1_loop = 2623060900, + stage_2_loop = 3632312862, + stage_3_loop = 125163859, + stage_4_loop = 931907947, + no_bag = 429567348, + nomover_filter = 819979025, + botharms_filter = 384947232, + rightarm_nospine_filter = 1924927269, + upperbodyfeathered_filter = 265147401, + ignoremoverblend_filter = 2738335535, + upperbodyandik_filter = 4248997943, + bonemask_head_neck_and_l_arm = 3177231635, + bonemask_head_neck_and_r_arm = 3921347753, + bonemask_head_neck_and_arms = 1573924551, + bonemask_headonly = 697476694, + bonemask_upperonly = 3633914286, + bonemask_armonly_l = 1739926164, + bonemask_armonly_r = 3772128476, } diff --git a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs index 12af07f..a2ee470 100644 --- a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs +++ b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs @@ -105,7 +105,22 @@ namespace CodeWalker.GameFiles else if (fnl.EndsWith(".yld")) { YldFile yld = RpfFile.GetFile(e, data); - return GetXml(yld, out filename, outputfolder); + return GetXml(yld, out filename); + } + else if (fnl.EndsWith(".yed")) + { + YedFile yed = RpfFile.GetFile(e, data); + return GetXml(yed, out filename); + } + else if (fnl.EndsWith(".ywr")) + { + YwrFile ywr = RpfFile.GetFile(e, data); + return GetXml(ywr, out filename); + } + else if (fnl.EndsWith(".yvr")) + { + YvrFile yvr = RpfFile.GetFile(e, data); + return GetXml(yvr, out filename); } else if (fnl.EndsWith(".awc")) { @@ -235,11 +250,29 @@ namespace CodeWalker.GameFiles filename = fn + ".xml"; return YptXml.GetXml(ypt, outputfolder); } - public static string GetXml(YldFile yld, out string filename, string outputfolder) + public static string GetXml(YldFile yld, out string filename) { var fn = (yld?.Name) ?? ""; filename = fn + ".xml"; - return YldXml.GetXml(yld, outputfolder); + return YldXml.GetXml(yld); + } + public static string GetXml(YedFile yed, out string filename) + { + var fn = (yed?.Name) ?? ""; + filename = fn + ".xml"; + return YedXml.GetXml(yed); + } + public static string GetXml(YwrFile ywr, out string filename) + { + var fn = (ywr?.Name) ?? ""; + filename = fn + ".xml"; + return YwrXml.GetXml(ywr); + } + public static string GetXml(YvrFile yvr, out string filename) + { + var fn = (yvr?.Name) ?? ""; + filename = fn + ".xml"; + return YvrXml.GetXml(yvr); } public static string GetXml(AwcFile awc, out string filename, string outputfolder) { @@ -2169,8 +2202,11 @@ namespace CodeWalker.GameFiles Yft = 13, Ypt = 14, Yld = 15, - Awc = 16, - Heightmap = 17, + Yed = 16, + Ywr = 17, + Yvr = 18, + Awc = 19, + Heightmap = 20, } } diff --git a/CodeWalker.Core/GameFiles/Resources/Expression.cs b/CodeWalker.Core/GameFiles/Resources/Expression.cs index a3ff470..47b726f 100644 --- a/CodeWalker.Core/GameFiles/Resources/Expression.cs +++ b/CodeWalker.Core/GameFiles/Resources/Expression.cs @@ -6,6 +6,9 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using TC = System.ComponentModel.TypeConverterAttribute; +using EXP = System.ComponentModel.ExpandableObjectConverter; +using System.Xml; /* Copyright(c) 2017 Neodymium @@ -33,17 +36,17 @@ using System.Threading.Tasks; namespace CodeWalker.GameFiles { - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionDictionary : ResourceFileBase + [TC(typeof(EXP))] public class ExpressionDictionary : ResourceFileBase { // pgDictionaryBase // pgDictionary public override long BlockLength => 0x40; // structure data - public uint Unknown_10h { get; set; } - public uint Unknown_14h { get; set; } - public uint Unknown_18h { get; set; } - public uint Unknown_1Ch { get; set; } + public uint Unknown_10h { get; set; } = 0; + public uint Unknown_14h { get; set; } = 0; + public uint Unknown_18h { get; set; } = 1; + public uint Unknown_1Ch { get; set; } = 0; public ResourceSimpleList64_s ExpressionNameHashes { get; set; } public ResourcePointerList64 Expressions { get; set; } @@ -64,6 +67,7 @@ namespace CodeWalker.GameFiles this.Expressions = reader.ReadBlock>(); BuildMap(); + } public override void Write(ResourceDataWriter writer, params object[] parameters) { @@ -77,6 +81,66 @@ namespace CodeWalker.GameFiles writer.WriteBlock(this.ExpressionNameHashes); writer.WriteBlock(this.Expressions); } + public void WriteXml(StringBuilder sb, int indent) + { + + if (Expressions?.data_items != null) + { + foreach (var e in Expressions.data_items) + { + YedXml.OpenTag(sb, indent, "Item"); + e.WriteXml(sb, indent + 1); + YedXml.CloseTag(sb, indent, "Item"); + } + } + + } + public void ReadXml(XmlNode node) + { + var expressions = new List(); + var expressionhashes = new List(); + + var inodes = node.SelectNodes("Item"); + if (inodes != null) + { + foreach (XmlNode inode in inodes) + { + var e = new Expression(); + e.ReadXml(inode); + expressions.Add(e); + expressionhashes.Add(e.NameHash); + } + } + + ExpressionNameHashes = new ResourceSimpleList64_s(); + ExpressionNameHashes.data_items = expressionhashes.ToArray(); + Expressions = new ResourcePointerList64(); + Expressions.data_items = expressions.ToArray(); + + BuildMap(); + } + public static void WriteXmlNode(ExpressionDictionary d, StringBuilder sb, int indent, string name = "ExpressionDictionary") + { + if (d == null) return; + if ((d.Expressions?.data_items == null) || (d.Expressions.data_items.Length == 0)) + { + YedXml.SelfClosingTag(sb, indent, name); + } + else + { + YedXml.OpenTag(sb, indent, name); + d.WriteXml(sb, indent + 1); + YedXml.CloseTag(sb, indent, name); + } + } + public static ExpressionDictionary ReadXmlNode(XmlNode node) + { + if (node == null) return null; + var ed = new ExpressionDictionary(); + ed.ReadXml(node); + return ed; + } + public override IResourceBlock[] GetReferences() { @@ -104,7 +168,8 @@ namespace CodeWalker.GameFiles for (int i = 0; i < exprs.Length; i++) { var expr = exprs[i]; - var name = (i < names.Length) ? names[i] : (MetaHash)JenkHash.GenHash(expr?.Name?.ToString()?.ToLowerInvariant() ?? ""); + var name = (i < names.Length) ? names[i] : (MetaHash)JenkHash.GenHash(expr?.GetShortName() ?? ""); + expr.NameHash = name; ExprMap[name] = expr; } } @@ -115,7 +180,7 @@ namespace CodeWalker.GameFiles - [TypeConverter(typeof(ExpandableObjectConverter))] public class Expression : ResourceSystemBlock + [TC(typeof(EXP))] public class Expression : ResourceSystemBlock { // pgBase // crExpressions @@ -130,18 +195,17 @@ namespace CodeWalker.GameFiles public uint Unknown_14h { get; set; } // 0x00000000 public uint Unknown_18h { get; set; } // 0x00000000 public uint Unknown_1Ch { get; set; } // 0x00000000 - public ResourcePointerList64 Unknown_20h { get; set; } - public ResourceSimpleList64_s BoneTracks { get; set; } // bone tags / animation tracks..?? - public ResourceSimpleList64 Unknown_40h { get; set; } - public ResourceSimpleList64_s Unknown_50h { get; set; } // only for: faceinit.expr, independent_mover.expr + public ResourcePointerList64 Streams { get; set; } + public ResourceSimpleList64_s BoneTracks { get; set; } // bone tags / animation tracks + public ResourceSimpleList64 JiggleData { get; set; } //compiled list of jiggles data from all jiggle Stream nodes + public ResourceSimpleList64_s ItemHashes { get; set; } // maybe variables? only for: faceinit.expr, independent_mover.expr public ulong NamePointer { get; set; } public ushort NameLength { get; set; } // name len public ushort NameCapacity { get; set; } // name len+1 public uint Unknown_6Ch { get; set; } // 0x00000000 public uint Unknown_70h { get; set; } = 1; public MetaHash Unknown_74h { get; set; } // seems to be a hash? - public ushort Unk1ItemLength { get; set; } // max length of any Unk1 item in Unknown_20h - public ushort Unknown_7Ah { get; set; } // 0x0000 + public uint MaxStreamSize { get; set; } // max length of any item in Streams public uint Unknown_7Ch { get; set; } // 3 or 2 public uint Unknown_80h { get; set; } // 0x00000000 public uint Unknown_84h { get; set; } // 0x00000000 @@ -149,8 +213,8 @@ namespace CodeWalker.GameFiles public uint Unknown_8Ch { get; set; } // 0x00000000 // reference data - public string_r Name; - + public string_r Name { get; set; } + public MetaHash NameHash { get; set; } public Dictionary BoneTracksDict { get; set; } @@ -166,18 +230,17 @@ namespace CodeWalker.GameFiles this.Unknown_14h = reader.ReadUInt32(); this.Unknown_18h = reader.ReadUInt32(); this.Unknown_1Ch = reader.ReadUInt32(); - this.Unknown_20h = reader.ReadBlock>(); + this.Streams = reader.ReadBlock>(); this.BoneTracks = reader.ReadBlock>(); - this.Unknown_40h = reader.ReadBlock>(); - this.Unknown_50h = reader.ReadBlock>(); + this.JiggleData = reader.ReadBlock>(); + this.ItemHashes = reader.ReadBlock>(); this.NamePointer = reader.ReadUInt64(); this.NameLength = reader.ReadUInt16(); this.NameCapacity = reader.ReadUInt16(); this.Unknown_6Ch = reader.ReadUInt32(); this.Unknown_70h = reader.ReadUInt32(); this.Unknown_74h = reader.ReadUInt32(); - this.Unk1ItemLength = reader.ReadUInt16(); - this.Unknown_7Ah = reader.ReadUInt16(); + this.MaxStreamSize = reader.ReadUInt32(); this.Unknown_7Ch = reader.ReadUInt32(); this.Unknown_80h = reader.ReadUInt32(); this.Unknown_84h = reader.ReadUInt32(); @@ -189,15 +252,47 @@ namespace CodeWalker.GameFiles this.NamePointer // offset ); - //if (Unknown_50h?.data_items?.Length > 0) - //{ } // faceinit.expr, independent_mover.expr + JenkIndex.Ensure(GetShortName()); + BuildBoneTracksDict(); + + #region testing + + //if (Streams?.data_items != null) + //{ } + + ////if ((this.JiggleData?.data_items?.Length ?? 0) > 0) + //{ + // var cnt1 = JiggleData?.data_items?.Length ?? 0; + // var cnt2 = 0; + // foreach (var stream in Streams.data_items) + // { + // foreach (var node in stream.Items) + // { + // if (node is ExpressionNodeJiggle jnode) + // { + // var trackrot = BoneTracks.data_items[jnode.BoneTrackRot]; + // var trackpos = BoneTracks.data_items[jnode.BoneTrackPos]; + // var jd1 = jnode.JiggleData; + // var jd2 = JiggleData.data_items[cnt2].JiggleData; + // if (!jd1.Compare(jd2)) + // { }//no hit + // if (jd2.BoneTag != trackrot.BoneTag) + // { }//no hit + // cnt2++; + // } + // } + // } + // if (cnt1 != cnt2) + // { }//no hit + //} + //long tlen = 0; - //if (Unknown_20h?.data_items != null) foreach (var item in Unknown_20h.data_items) tlen = Math.Max(tlen, item.BlockLength); - //if (Unk1ItemLength != tlen) + //if (Streams?.data_items != null) foreach (var item in Streams.data_items) tlen = Math.Max(tlen, item.BlockLength); + //if (MaxStreamSize != tlen) //{ }//no hit //if (Unknown_4h != 1) @@ -227,8 +322,6 @@ namespace CodeWalker.GameFiles // default: // break; //} - //if (Unknown_7Ah != 0) - //{ }//no hit //switch (Unknown_7Ch) //{ // case 3: @@ -261,24 +354,61 @@ namespace CodeWalker.GameFiles writer.Write(this.Unknown_14h); writer.Write(this.Unknown_18h); writer.Write(this.Unknown_1Ch); - writer.WriteBlock(this.Unknown_20h); + writer.WriteBlock(this.Streams); writer.WriteBlock(this.BoneTracks); - writer.WriteBlock(this.Unknown_40h); - writer.WriteBlock(this.Unknown_50h); + writer.WriteBlock(this.JiggleData); + writer.WriteBlock(this.ItemHashes); writer.Write(this.NamePointer); writer.Write(this.NameLength); writer.Write(this.NameCapacity); writer.Write(this.Unknown_6Ch); writer.Write(this.Unknown_70h); writer.Write(this.Unknown_74h); - writer.Write(this.Unk1ItemLength); - writer.Write(this.Unknown_7Ah); + writer.Write(this.MaxStreamSize); writer.Write(this.Unknown_7Ch); writer.Write(this.Unknown_80h); writer.Write(this.Unknown_84h); writer.Write(this.Unknown_88h); writer.Write(this.Unknown_8Ch); } + public void WriteXml(StringBuilder sb, int indent) + { + YedXml.StringTag(sb, indent, "Name", Name?.Value ?? ""); + YedXml.StringTag(sb, indent, "Unk74", YedXml.HashString(Unknown_74h)); + YedXml.ValueTag(sb, indent, "Unk7C", Unknown_7Ch.ToString()); + + if ((BoneTracks?.data_items?.Length ?? 0) > 0) + { + YedXml.WriteItemArray(sb, BoneTracks.data_items, indent, "BoneTracks"); + } + + if ((Streams?.data_items?.Length ?? 0) > 0) + { + YedXml.WriteItemArray(sb, Streams.data_items, indent, "Streams"); + } + + } + public void ReadXml(XmlNode node) + { + Name = new string_r(); + Name.Value = Xml.GetChildInnerText(node, "Name"); + NameLength = (ushort)Name.Value.Length; + NameCapacity = (ushort)(NameLength + 1); + NameHash = JenkHash.GenHash(GetShortName()); + Unknown_74h = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Unk74")); + Unknown_7Ch = Xml.GetChildUIntAttribute(node, "Unk7C", "value"); + + BoneTracks = new ResourceSimpleList64_s(); + BoneTracks.data_items = XmlMeta.ReadItemArray(node, "BoneTracks"); + + Streams = new ResourcePointerList64(); + Streams.data_items = XmlMeta.ReadItemArray(node, "Streams"); + + BuildBoneTracksDict(); + BuildJiggleData(); + UpdateItemHashes(); + UpdateStreamBuffers(); + } public override IResourceBlock[] GetReferences() { @@ -289,10 +419,10 @@ namespace CodeWalker.GameFiles public override Tuple[] GetParts() { return new Tuple[] { - new Tuple(0x20, Unknown_20h), + new Tuple(0x20, Streams), new Tuple(0x30, BoneTracks), - new Tuple(0x40, Unknown_40h), - new Tuple(0x50, Unknown_50h) + new Tuple(0x40, JiggleData), + new Tuple(0x50, ItemHashes) }; } @@ -320,6 +450,90 @@ namespace CodeWalker.GameFiles } + public void BuildJiggleData() + { + var jiggles = new List(); + if (Streams?.data_items != null) + { + foreach (var stream in Streams.data_items) + { + foreach (var node in stream.Items) + { + if (node is ExpressionNodeJiggle jnode) + { + var jiggle = new ExpressionJiggleBlock(); + jiggle.JiggleData = jnode.JiggleData.Clone(); + jiggles.Add(jiggle); + } + } + } + } + JiggleData = new ResourceSimpleList64(); + JiggleData.data_items = jiggles.ToArray(); + } + + public void UpdateItemHashes() + { + var dict = new Dictionary(); + if (Streams?.data_items != null) + { + foreach (var stream in Streams.data_items) + { + foreach (var item in stream.Items) + { + if (item is ExpressionNodeS3 s3) + { + dict[s3.Hash] = 0; + } + } + } + } + var list = dict.Keys.ToList(); + list.Sort((a, b) => a.Hash.CompareTo(b.Hash)); + for (int i = 0; i < list.Count; i++) + { + dict[list[i]] = i; + } + if (Streams?.data_items != null) + { + foreach (var stream in Streams.data_items) + { + foreach (var item in stream.Items) + { + if (item is ExpressionNodeS3 s3) + { + var index = dict[s3.Hash]; + s3.HashIndex = (byte)index; + } + } + } + } + + ItemHashes = new ResourceSimpleList64_s(); + ItemHashes.data_items = list.ToArray(); + + } + + public void UpdateStreamBuffers() + { + MaxStreamSize = 0; + if (Streams?.data_items != null) + { + foreach (var item in Streams.data_items) + { + //item.FlattenHierarchy(); + item.WriteItems(); //makes sure the data buffers are updated to the correct length! + MaxStreamSize = Math.Max(MaxStreamSize, (uint)item.BlockLength); + } + } + } + + + public string GetShortName() + { + return Path.GetFileNameWithoutExtension(Name?.Value ?? "").ToLowerInvariant(); + } + public override string ToString() { @@ -329,137 +543,7 @@ namespace CodeWalker.GameFiles - public class ExpressionTreeReader - { - public byte[] Data1 { get; set; } - public byte[] Data2 { get; set; } - public byte[] Data3 { get; set; } - - public ExpressionUnk1_Base[] Items { get; set; } - - public ExpressionTreeReader(byte[] d1, byte[] d2, byte[] d3) - { - Data1 = d1; - Data2 = d2; - Data3 = d3; - } - - public ExpressionUnk1_Base[] ReadItems() - { - - var ms1 = new MemoryStream(Data1); - var ms2 = new MemoryStream(Data2); - var ms3 = new MemoryStream(Data3); - var dr1 = new DataReader(ms1); - var dr2 = new DataReader(ms2); - var dr3 = new DataReader(ms3); - - var items = new List(); - while (ms3.Position < ms3.Length) - { - var type = dr3.ReadByte(); - if (type == 0) - { - if (ms3.Position != ms3.Length) - { } - break; - } - var item = ExpressionUnk1.CreateItem(type); - item.Type = type; - item.Read(dr1, dr2, dr3); - items.Add(item); - } - Items = items.ToArray(); - - if ((dr1.Length - dr1.Position) != 0) - { } - if ((dr2.Length - dr2.Position) != 0) - { } - if ((dr3.Length - dr3.Position) != 0) - { } - - return Items; - } - - public ExpressionUnk1_Base[] ReadTree() - { - - if (Items == null) return null; - - var stack = new Stack(); - - for (int i = 0; i < Items.Length; i++) - { - var item = Items[i]; - - switch (item.Type) - { - case 0x01: break; - case 0x03: break; - case 0x04: break; - case 0x05: break; - case 0x06: item.Children = new[] { stack.Pop() }; break; - case 0x07: break; - case 0x09: break; - case 0x0A: break; - case 0x0B: break; - case 0x0E: break; - case 0x10: item.Children = new[] { stack.Pop() }; break; //####### maybe not - case 0x11: item.Children = new[] { stack.Pop() }; break; - case 0x1B: item.Children = new[] { stack.Pop() }; break; - case 0x1D: item.Children = new[] { stack.Pop() }; break; - case 0x1E: item.Children = new[] { stack.Pop() }; break; - case 0x1F: item.Children = new[] { stack.Pop() }; break; - case 0x20: break;//first in list - case 0x21: item.Children = new[] { stack.Pop() }; break; - case 0x22: item.Children = new[] { stack.Pop() }; break; - case 0x23: item.Children = new[] { stack.Pop() }; break; - case 0x26: item.Children = new[] { stack.Pop() }; break; - case 0x27: item.Children = new[] { stack.Pop() }; break; - case 0x28: item.Children = new[] { stack.Pop() }; break; - case 0x2A: item.Children = new[] { stack.Pop() }; break; - case 0x2B: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; - case 0x2C: item.Children = new[] { stack.Pop() }; break; - case 0x2D: item.Children = new[] { stack.Pop(), stack.Pop() }; break;//4 maybe? - case 0x2E: item.Children = new[] { stack.Pop(), stack.Pop() }; break; - case 0x2F: item.Children = new[] { stack.Pop(), stack.Pop() }; break; - case 0x30: item.Children = new[] { stack.Pop(), stack.Pop() }; break; - case 0x31: item.Children = new[] { stack.Pop(), stack.Pop() }; break; - case 0x32: item.Children = new[] { stack.Pop(), stack.Pop() }; break; - case 0x33: item.Children = new[] { stack.Pop(), stack.Pop() }; break; - case 0x35: item.Children = new[] { stack.Pop(), stack.Pop() }; break;//can't be more than 2 - case 0x36: item.Children = new[] { stack.Pop(), stack.Pop() }; break; //can't be more than 2 - case 0x37: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop() }; break; - case 0x38: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; - case 0x39: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; - case 0x3A: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; - case 0x3B: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; //####### maybe not - case 0x3C: item.Children = new[] { stack.Pop(), stack.Pop() }; break; - case 0x3D: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; - case 0x3E: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; - case 0x3F: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; - case 0x40: item.Children = new[] { stack.Pop(), stack.Pop() }; break; - case 0x42: break;//first in list - case 0x43: item.Children = new[] { stack.Pop() }; break; - case 0x44: break; - case 0x45: break; - case 0x46: item.Children = new[] { stack.Pop() }; break; - case 0x49: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; - default: break;//no hit - } - - stack.Push(item); - } - - var roots = stack.Reverse().ToArray(); - - return roots; - } - - - } - - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionUnk1 : ResourceSystemBlock + [TC(typeof(EXP))] public class ExpressionStream : ResourceSystemBlock, IMetaXmlItem { public override long BlockLength { @@ -467,40 +551,44 @@ namespace CodeWalker.GameFiles } // structure data - public MetaHash Unknown_0h { get; set; } + public MetaHash NameHash { get; set; }//presumably name hash public uint Data1Length { get; set; } public uint Data2Length { get; set; } public ushort Data3Length { get; set; } - public ushort Unknown_Eh { get; set; } + public ushort Unk0E { get; set; }//what is this? possibly max hierarchy depth public byte[] Data1 { get; set; } public byte[] Data2 { get; set; } public byte[] Data3 { get; set; } - public ExpressionUnk1_Base[] Items { get; set; } - public ExpressionUnk1_Base[] Roots { get; set; } + public ExpressionNodeBase[] Items { get; set; } + public ExpressionNodeBase[] Roots { get; set; } public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data - this.Unknown_0h = reader.ReadUInt32(); + this.NameHash = reader.ReadUInt32(); this.Data1Length = reader.ReadUInt32(); this.Data2Length = reader.ReadUInt32(); this.Data3Length = reader.ReadUInt16(); - this.Unknown_Eh = reader.ReadUInt16(); + this.Unk0E = reader.ReadUInt16(); this.Data1 = reader.ReadBytes((int)Data1Length); this.Data2 = reader.ReadBytes((int)Data2Length); this.Data3 = reader.ReadBytes((int)Data3Length); - var treereader = new ExpressionTreeReader(Data1, Data2, Data3); - Items = treereader.ReadItems(); - Roots = treereader.ReadTree(); + ReadItems(); + BuildHierarchy(); + + + #region testing - //switch (Unknown_Eh) + //FlattenHierarchy(); + //WriteItems(); + //switch (Unk1) //{ // case 2: // case 8: @@ -550,128 +638,448 @@ namespace CodeWalker.GameFiles } public override void Write(ResourceDataWriter writer, params object[] parameters) { + + //FlattenHierarchy(); + //WriteItems();//should already be done by Expression.UpdateStreamBuffers + // write structure data - writer.Write(this.Unknown_0h); + writer.Write(this.NameHash); writer.Write(this.Data1Length); writer.Write(this.Data2Length); writer.Write(this.Data3Length); - writer.Write(this.Unknown_Eh); + writer.Write(this.Unk0E); writer.Write(this.Data1); writer.Write(this.Data2); writer.Write(this.Data3); } + public void WriteXml(StringBuilder sb, int indent) + { + YedXml.StringTag(sb, indent, "Name", YedXml.HashString(NameHash)); + YedXml.ValueTag(sb, indent, "Unk0E", Unk0E.ToString()); + + //TODO: use hierarchy! + YedXml.OpenTag(sb, indent, "Instructions"); + var cind = indent + 1; + var cind2 = cind + 1; + foreach (var item in Items) + { + if (item is ExpressionNodeEmpty) + { + YedXml.SelfClosingTag(sb, cind, "Item type=\"" + item.Type.ToString() + "\""); + } + else + { + YedXml.OpenTag(sb, cind, "Item type=\"" + item.Type.ToString() + "\""); + item.WriteXml(sb, cind2); + YedXml.CloseTag(sb, cind, "Item"); + } + } + YedXml.CloseTag(sb, indent, "Instructions"); + + + } + public void ReadXml(XmlNode node) + { + NameHash = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Name")); + Unk0E = (ushort)Xml.GetChildUIntAttribute(node, "Unk0E", "value"); + + var items = new List(); + var instnode = node.SelectSingleNode("Instructions"); + if (instnode != null) + { + var inodes = instnode.SelectNodes("Item"); + if (inodes?.Count > 0) + { + foreach (XmlNode inode in inodes) + { + if (Enum.TryParse(Xml.GetStringAttribute(inode, "type"), out var type)) + { + var item = CreateItem(type); + item.Type = type; + item.ReadXml(inode); + items.Add(item); + } + } + } + } + Items = items.ToArray(); + BuildHierarchy(); + + } - public static ExpressionUnk1_Base CreateItem(byte type) + + public void ReadItems() + { + var s1 = new MemoryStream(Data1); + var s2 = new MemoryStream(Data2); + var s3 = new MemoryStream(Data3); + var r1 = new DataReader(s1); + var r2 = new DataReader(s2); + var r3 = new DataReader(s3); + + var items = new List(); + while (s3.Position < s3.Length) + { + var type = (ExpressionNodeType)r3.ReadByte(); + if (type == ExpressionNodeType.None) + { + if (s3.Position != s3.Length) + { }//no hit + break; + } + var item = CreateItem(type); + item.Type = type; + item.Read(r1, r2); + items.Add(item); + } + + if ((r1.Length - r1.Position) != 0) + { }//no hit + if ((r2.Length - r2.Position) != 0) + { }//no hit + if ((r3.Length - r3.Position) != 0) + { }//no hit + + Items = items.ToArray(); + } + + public void BuildHierarchy() + { + if (Items == null) return; + + var stack = new Stack(); + + for (int i = 0; i < Items.Length; i++) + { + var item = Items[i]; + + switch (item.Type) + { + case ExpressionNodeType.Unk01: break; + case ExpressionNodeType.Unk03: break; + case ExpressionNodeType.Unk04: break; + case ExpressionNodeType.Unk05: break; + case ExpressionNodeType.Unk06: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk07: break; + case ExpressionNodeType.Unk09: break; + case ExpressionNodeType.Unk0A: break; + case ExpressionNodeType.Unk0B: break; + case ExpressionNodeType.Jiggle: break; + case ExpressionNodeType.Unk10: item.Children = new[] { stack.Pop() }; break; //####### maybe not + case ExpressionNodeType.Unk11: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk1B: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk1D: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk1E: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk1F: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk20: break;//first in list + case ExpressionNodeType.Unk21: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk22: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk23: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk26: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk27: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk28: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk2A: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk2B: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk2C: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk2D: item.Children = new[] { stack.Pop(), stack.Pop() }; break;//4 maybe? + case ExpressionNodeType.Unk2E: item.Children = new[] { stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk2F: item.Children = new[] { stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk30: item.Children = new[] { stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk31: item.Children = new[] { stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk32: item.Children = new[] { stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk33: item.Children = new[] { stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk35: item.Children = new[] { stack.Pop(), stack.Pop() }; break;//can't be more than 2 + case ExpressionNodeType.Unk36: item.Children = new[] { stack.Pop(), stack.Pop() }; break; //can't be more than 2 + case ExpressionNodeType.Unk37: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk38: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk39: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk3A: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk3B: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; //####### maybe not + case ExpressionNodeType.Unk3C: item.Children = new[] { stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk3D: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk3E: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk3F: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk40: item.Children = new[] { stack.Pop(), stack.Pop() }; break; + case ExpressionNodeType.Unk42: break;//first in list + case ExpressionNodeType.Unk43: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk44: break; + case ExpressionNodeType.Unk45: break; + case ExpressionNodeType.Unk46: item.Children = new[] { stack.Pop() }; break; + case ExpressionNodeType.Unk49: item.Children = new[] { stack.Pop(), stack.Pop(), stack.Pop() }; break; + default: break;//no hit + } + + stack.Push(item); + } + + + Roots = stack.Reverse().ToArray(); + + + foreach (var item in Items) //probably using the stack caused child arrays to be reversed, so reverse them back again + { + if ((item.Children?.Length ?? 0) > 1) + { + Array.Reverse(item.Children); + } + } + + + } + + public void FlattenHierarchy(ExpressionNodeBase node = null, List list = null) + { + if (node == null) + { + if (Roots == null) return; + + list = new List(); + + foreach (var root in Roots) + { + FlattenHierarchy(root, list); + } + + Items = list.ToArray(); + } + else + { + if (node.Children != null) + { + foreach (var child in node.Children) + { + FlattenHierarchy(child, list); + } + } + list.Add(node); + } + } + + public void WriteItems() + { + var s1 = new MemoryStream(); + var s2 = new MemoryStream(); + var s3 = new MemoryStream(); + var w1 = new DataWriter(s1); + var w2 = new DataWriter(s2); + var w3 = new DataWriter(s3); + + foreach (var item in Items) + { + w3.Write((byte)item.Type); + item.Write(w1, w2); + } + w3.Write((byte)0); + + var b1 = new byte[s1.Length]; + var b2 = new byte[s2.Length]; + var b3 = new byte[s3.Length]; + s1.Position = 0; + s2.Position = 0; + s3.Position = 0; + s1.Read(b1, 0, b1.Length); + s2.Read(b2, 0, b2.Length); + s3.Read(b3, 0, b3.Length); + + Data1 = b1; + Data2 = b2; + Data3 = b3; + Data1Length = (uint)Data1.Length; + Data2Length = (uint)Data2.Length; + Data3Length = (ushort)Data3.Length; + } + + + + + public static ExpressionNodeBase CreateItem(ExpressionNodeType type) { switch (type) { - //case 0: - case 0x21: - case 0x30: - case 0x3D: - case 0x03: - case 0x01: - case 0x33: - case 0x2E: - case 0x31: - case 0x32: - case 0x2F: - case 0x04: - case 0x10: - case 0x3B: - case 0x36: - case 0x35: - case 0x39: - case 0x38: - case 0x37: - case 0x1D: - case 0x1E: - case 0x46: - case 0x1F: - case 0x3A: - case 0x22: - case 0x49: - case 0x3F: - case 0x1B: - case 0x40: - case 0x3C: - case 0x11: - return new ExpressionUnk1_Base(); + case ExpressionNodeType.Unk01: + case ExpressionNodeType.Unk03: + case ExpressionNodeType.Unk04: + case ExpressionNodeType.Unk10: + case ExpressionNodeType.Unk11: + case ExpressionNodeType.Unk1B: + case ExpressionNodeType.Unk1D: + case ExpressionNodeType.Unk1E: + case ExpressionNodeType.Unk1F: + case ExpressionNodeType.Unk21: + case ExpressionNodeType.Unk22: + case ExpressionNodeType.Unk2E: + case ExpressionNodeType.Unk2F: + case ExpressionNodeType.Unk30: + case ExpressionNodeType.Unk31: + case ExpressionNodeType.Unk32: + case ExpressionNodeType.Unk33: + case ExpressionNodeType.Unk35: + case ExpressionNodeType.Unk36: + case ExpressionNodeType.Unk37: + case ExpressionNodeType.Unk38: + case ExpressionNodeType.Unk39: + case ExpressionNodeType.Unk3A: + case ExpressionNodeType.Unk3B: + case ExpressionNodeType.Unk3C: + case ExpressionNodeType.Unk3D: + case ExpressionNodeType.Unk3F: + case ExpressionNodeType.Unk40: + case ExpressionNodeType.Unk46: + case ExpressionNodeType.Unk49: + return new ExpressionNodeEmpty(); - case 0x45: - case 0x44: - return new ExpressionUnk1_S1(); + case ExpressionNodeType.Unk44: + case ExpressionNodeType.Unk45: + return new ExpressionNodeS1(); - case 0x07: - case 0x09: - case 0x0A: - case 0x23: - case 0x26: - case 0x27: - case 0x28: - case 0x20: - case 0x42: - case 0x43: - case 0x2A: - case 0x06: - return new ExpressionUnk1_S2(); + case ExpressionNodeType.Unk06: + case ExpressionNodeType.Unk07: + case ExpressionNodeType.Unk09: + case ExpressionNodeType.Unk0A: + case ExpressionNodeType.Unk20: + case ExpressionNodeType.Unk23: + case ExpressionNodeType.Unk26: + case ExpressionNodeType.Unk27: + case ExpressionNodeType.Unk28: + case ExpressionNodeType.Unk2A: + return new ExpressionNodeS2(); - case 0x2B: - case 0x2C: - case 0x2D: - return new ExpressionUnk1_2B2C2D(); + case ExpressionNodeType.Unk42: + case ExpressionNodeType.Unk43: + return new ExpressionNodeS3(); + + case ExpressionNodeType.Unk2B: + case ExpressionNodeType.Unk2C: + case ExpressionNodeType.Unk2D: + return new ExpressionNode2B(); - case 0x0B: return new ExpressionUnk1_0B(); - case 0x05: return new ExpressionUnk1_05(); - case 0x3E: return new ExpressionUnk1_3E(); - case 0x0E: return new ExpressionUnk1_0E(); + case ExpressionNodeType.Unk05: return new ExpressionNode05(); + case ExpressionNodeType.Unk0B: return new ExpressionNode0B(); + case ExpressionNodeType.Jiggle: return new ExpressionNodeJiggle(); + case ExpressionNodeType.Unk3E: return new ExpressionNode3E(); } - return new ExpressionUnk1_Base(); + return new ExpressionNodeBase(); } public override string ToString() { - return Unknown_0h.ToString(); + return NameHash.ToString() + " (" + (Items?.Length??0).ToString() + " items, " + (Roots?.Length??0).ToString() + " roots)"; } + } - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionUnk1_Base + + public enum ExpressionNodeType : byte { - public byte Type { get; set; } - public string TypeStr { get => Type.ToString("X").PadLeft(2, '0'); } + None = 0, + Unk01 = 0x01, + Unk03 = 0x03, + Unk04 = 0x04, + Unk05 = 0x05, + Unk06 = 0x06, + Unk07 = 0x07, + Unk09 = 0x09, + Unk0A = 0x0A, + Unk0B = 0x0B, + Jiggle = 0x0E, + Unk10 = 0x10, + Unk11 = 0x11, + Unk1B = 0x1B, + Unk1D = 0x1D, + Unk1E = 0x1E, + Unk1F = 0x1F, + Unk20 = 0x20, + Unk21 = 0x21, + Unk22 = 0x22, + Unk23 = 0x23, + Unk26 = 0x26, + Unk27 = 0x27, + Unk28 = 0x28, + Unk2A = 0x2A, + Unk2B = 0x2B, + Unk2C = 0x2C, + Unk2D = 0x2D, + Unk2E = 0x2E, + Unk2F = 0x2F, + Unk30 = 0x30, + Unk31 = 0x31, + Unk32 = 0x32, + Unk33 = 0x33, + Unk35 = 0x35, + Unk36 = 0x36, + Unk37 = 0x37, + Unk38 = 0x38, + Unk39 = 0x39, + Unk3A = 0x3A, + Unk3B = 0x3B, + Unk3C = 0x3C, + Unk3D = 0x3D, + Unk3E = 0x3E, + Unk3F = 0x3F, + Unk40 = 0x40, + Unk42 = 0x42, + Unk43 = 0x43, + Unk44 = 0x44, + Unk45 = 0x45, + Unk46 = 0x46, + Unk49 = 0x49, + } - public ExpressionUnk1_Base[] Children { get; set; } - public virtual void Read(DataReader r1, DataReader r2, DataReader r3) + [TC(typeof(EXP))] public class ExpressionNodeBase + { + + /* + possible type names: +crExprInstrBone +crExprInstrRBFs +crExprInstrSpring +crExprInstrConstraint +crExprInstrLinear +crExprInstrSpringChain +crExprInstrCurve +crExprInstrFrame +crExprInstrLookAt +crExprInstrVariable + */ + + + public ExpressionNodeType Type { get; set; } + + public ExpressionNodeBase[] Children { get; set; } + + public virtual void Read(DataReader r1, DataReader r2) { } - + public virtual void Write(DataWriter w1, DataWriter w2) + { } + public virtual void WriteXml(StringBuilder sb, int indent) + { } + public virtual void ReadXml(XmlNode node) + { } + public override string ToString() { - return TypeStr; + return Type.ToString(); } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionUnk1_S1 : ExpressionUnk1_Base + [TC(typeof(EXP))] public class ExpressionNodeEmpty : ExpressionNodeBase + { } + [TC(typeof(EXP))] public class ExpressionNodeS1 : ExpressionNodeBase { - public uint Length { get; set; } - public uint ItemCount { get; set; } - public uint ItemType { get; set; } - public uint Unk1 { get; set; } // 0x00000000 - - [TypeConverter(typeof(ExpandableObjectConverter))] public class Struct1 + [TC(typeof(EXP))] public class ItemStruct : IMetaXmlItem { - public ushort Unk1 { get; set; }//bone index? + public ushort BoneTrack { get; set; } public ushort Unk2 { get; set; } - public float[] Values { get; set; } public void Read(DataReader r) { - Unk1 = r.ReadUInt16(); + BoneTrack = r.ReadUInt16(); Unk2 = r.ReadUInt16(); switch (Unk2) @@ -684,9 +1092,27 @@ namespace CodeWalker.GameFiles break;//no hit } } + public void Write(DataWriter w) + { + w.Write(BoneTrack); + w.Write(Unk2); + } + public void WriteXml(StringBuilder sb, int indent) + { + YedXml.ValueTag(sb, indent, "BoneTrack", BoneTrack.ToString()); + YedXml.ValueTag(sb, indent, "Unk2", Unk2.ToString()); + YedXml.WriteRawArray(sb, Values, indent, "Values", "", FloatUtil.ToString, Values.Length); + } + public void ReadXml(XmlNode node) + { + BoneTrack = (ushort)Xml.GetChildUIntAttribute(node, "BoneTrack", "value"); + Unk2 = (ushort)Xml.GetChildUIntAttribute(node, "Unk2", "value"); + Values = Xml.GetChildRawFloatArray(node, "Values"); + } + public override string ToString() { - var str = Unk1.ToString() + ", " + Unk2.ToString(); + var str = BoneTrack.ToString() + ", " + Unk2.ToString(); if (Values != null) { str += " - "; @@ -699,28 +1125,32 @@ namespace CodeWalker.GameFiles return str; } } - public Struct1[] Items { get; set; } + public uint Length { get; set; }//updated automatically + public uint ItemCount { get; set; }//updated automatically + public uint ItemType { get; set; } + public uint Unk1 { get; set; } // 0x00000000 + public ItemStruct[] Items { get; set; } - public override void Read(DataReader r1, DataReader r2, DataReader r3) + public override void Read(DataReader r1, DataReader r2) { Length = r1.ReadUInt32(); ItemCount = r1.ReadUInt32(); ItemType = r1.ReadUInt32(); Unk1 = r1.ReadUInt32(); - var valcnt = ((int)ItemType - 1) * 9 + 6; - var hlen = (int)ItemCount * 4 + 16; - var dlen = (int)Length - hlen; - var tlen = (int)ItemCount * valcnt * 4; + var valcnt = (ItemType - 1) * 9 + 6; + var hlen = ItemCount * 4 + 16; + var dlen = Length - hlen; + var tlen = ItemCount * valcnt * 4; if (tlen != dlen) { }//no hitting - Items = new Struct1[ItemCount]; + Items = new ItemStruct[ItemCount]; for (int i = 0; i < ItemCount; i++) { - var item1 = new Struct1(); + var item1 = new ItemStruct(); item1.Read(r1); Items[i] = item1; Items[i].Values = new float[valcnt]; @@ -770,52 +1200,164 @@ namespace CodeWalker.GameFiles if (Unk1 != 0) { }//no hit } + public override void Write(DataWriter w1, DataWriter w2) + { + ItemCount = (uint)(Items?.Length ?? 0); + ItemType = Math.Max(ItemType, 1); + var valcnt = (ItemType - 1) * 9 + 6; + var hlen = ItemCount * 4 + 16; + var tlen = ItemCount * valcnt * 4; + Length = hlen + tlen; + + w1.Write(Length); + w1.Write(ItemCount); + w1.Write(ItemType); + w1.Write(Unk1); + + for (int i = 0; i < ItemCount; i++) + { + Items[i].Write(w1); + } + for (int n = 0; n < valcnt; n++) + { + for (int i = 0; i < ItemCount; i++) + { + var vals = Items[i].Values; + w1.Write((n < (vals?.Length ?? 0)) ? vals[n] : 0.0f); + } + } + + } + public override void WriteXml(StringBuilder sb, int indent) + { + YedXml.ValueTag(sb, indent, "ItemType", ItemType.ToString()); + YedXml.WriteItemArray(sb, Items, indent, "Items"); + } + public override void ReadXml(XmlNode node) + { + ItemType = Xml.GetChildUIntAttribute(node, "ItemType", "value"); + Items = XmlMeta.ReadItemArray(node, "Items"); + } + public override string ToString() { - var str = TypeStr + ", 1: - " + Length.ToString() + ", " + ItemCount.ToString() + ", " + ItemType.ToString(); + var str = base.ToString() + " - " + Length.ToString() + ", " + ItemCount.ToString() + ", " + ItemType.ToString(); if (Items != null) { - str += " - " + Items.Length.ToString() + " items"; + str += " (S1) - " + Items.Length.ToString() + " items"; } return str; } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionUnk1_S2 : ExpressionUnk1_Base + [TC(typeof(EXP))] public class ExpressionNodeS2 : ExpressionNodeBase { - public ushort Unk00 { get; set; } - public ushort Unk02 { get; set; } + public ushort BoneTrack { get; set; } //index of the BoneTag in the Expression.BoneTracks array + public ushort BoneTag { get; set; } + public byte Unk01 { get; set; } + public byte Unk02 { get; set; } + public byte Unk03 { get; set; } public byte Unk04 { get; set; } - public byte Unk05 { get; set; } - public byte Unk06 { get; set; } - public byte Unk07 { get; set; } - public override void Read(DataReader r1, DataReader r2, DataReader r3) + public override void Read(DataReader r1, DataReader r2) { - Unk00 = r2.ReadUInt16(); - Unk02 = r2.ReadUInt16(); + BoneTrack = r2.ReadUInt16(); + BoneTag = r2.ReadUInt16(); + Unk01 = r2.ReadByte(); + Unk02 = r2.ReadByte(); + Unk03 = r2.ReadByte(); Unk04 = r2.ReadByte(); - Unk05 = r2.ReadByte(); - Unk06 = r2.ReadByte(); - Unk07 = r2.ReadByte(); } + public override void Write(DataWriter w1, DataWriter w2) + { + w2.Write(BoneTrack); + w2.Write(BoneTag); + w2.Write(Unk01); + w2.Write(Unk02); + w2.Write(Unk03); + w2.Write(Unk04); + } + public override void WriteXml(StringBuilder sb, int indent) + { + YedXml.ValueTag(sb, indent, "BoneTrack", BoneTrack.ToString()); + YedXml.ValueTag(sb, indent, "BoneTag", BoneTag.ToString()); + YedXml.ValueTag(sb, indent, "Unk01", Unk01.ToString()); + YedXml.ValueTag(sb, indent, "Unk02", Unk02.ToString()); + YedXml.ValueTag(sb, indent, "Unk03", Unk03.ToString()); + YedXml.ValueTag(sb, indent, "Unk04", Unk04.ToString()); + } + public override void ReadXml(XmlNode node) + { + BoneTrack = (ushort)Xml.GetChildUIntAttribute(node, "BoneTrack", "value"); + BoneTag = (ushort)Xml.GetChildUIntAttribute(node, "BoneTag", "value"); + Unk01 = (byte)Xml.GetChildUIntAttribute(node, "Unk01", "value"); + Unk02 = (byte)Xml.GetChildUIntAttribute(node, "Unk02", "value"); + Unk03 = (byte)Xml.GetChildUIntAttribute(node, "Unk03", "value"); + Unk04 = (byte)Xml.GetChildUIntAttribute(node, "Unk04", "value"); + } + public override string ToString() { - return TypeStr + ", 2: - " + Unk00.ToString() + ", " + Unk02.ToString() + ", " + Unk04.ToString() + ", " + Unk05.ToString() + ", " + Unk06.ToString() + ", " + Unk07.ToString(); + return base.ToString() + " (S2) - Track:" + BoneTrack.ToString() + ", Tag:" + BoneTag.ToString() + ", " + Unk01.ToString() + ", " + Unk02.ToString() + ", " + Unk03.ToString() + ", " + Unk04.ToString(); } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionUnk1_2B2C2D : ExpressionUnk1_Base + [TC(typeof(EXP))] public class ExpressionNodeS3 : ExpressionNodeBase { - public uint UnkUint1 { get; set; } - public uint UnkUint2 { get; set; } - public uint UnkUint3 { get; set; } + public MetaHash Hash { get; set; } + public byte HashIndex { get; set; } //index of the hash in the Expression.ItemHashes array (autoupdated - don't need in XML) + public byte Unk03 { get; set; }//0 + public byte Unk04 { get; set; }//0 + public byte Unk05 { get; set; }//0 - public override void Read(DataReader r1, DataReader r2, DataReader r3) + public override void Read(DataReader r1, DataReader r2) { - UnkUint1 = r2.ReadUInt32(); - UnkUint2 = r2.ReadUInt32(); - UnkUint3 = r2.ReadUInt32(); + Hash = r2.ReadUInt32(); + HashIndex = r2.ReadByte(); + Unk03 = r2.ReadByte(); + Unk04 = r2.ReadByte(); + Unk05 = r2.ReadByte(); - switch (UnkUint1)//flags? + if (Unk03 != 0) + { } + if (Unk04 != 0) + { } + if (Unk05 != 0) + { } + } + public override void Write(DataWriter w1, DataWriter w2) + { + w2.Write(Hash); + w2.Write(HashIndex); + w2.Write(Unk03); + w2.Write(Unk04); + w2.Write(Unk05); + } + public override void WriteXml(StringBuilder sb, int indent) + { + YedXml.StringTag(sb, indent, "Hash", YedXml.HashString(Hash)); + } + public override void ReadXml(XmlNode node) + { + Hash = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Hash")); + } + + public override string ToString() + { + return base.ToString() + " (S3) - Hash:" + Hash.ToString() + ", " + HashIndex.ToString() + ", " + Unk03.ToString() + ", " + Unk04.ToString() + ", " + Unk05.ToString(); + } + } + [TC(typeof(EXP))] public class ExpressionNode2B : ExpressionNodeBase + { + public uint Unk01 { get; set; } + public uint Unk02 { get; set; } + public uint Unk03 { get; set; } + + public override void Read(DataReader r1, DataReader r2) + { + Unk01 = r2.ReadUInt32(); + Unk02 = r2.ReadUInt32(); + Unk03 = r2.ReadUInt32(); + + switch (Unk01)//flags? { case 0: case 128: @@ -827,7 +1369,7 @@ namespace CodeWalker.GameFiles default: break;//no hit } - switch (UnkUint2)//some index? + switch (Unk02)//some index? { case 24: case 12: @@ -863,7 +1405,7 @@ namespace CodeWalker.GameFiles default: break;//no hit } - switch (UnkUint3)//some index? + switch (Unk03)//some index? { case 5: case 4: @@ -896,57 +1438,146 @@ namespace CodeWalker.GameFiles } } + public override void Write(DataWriter w1, DataWriter w2) + { + w2.Write(Unk01); + w2.Write(Unk02); + w2.Write(Unk03); + } + public override void WriteXml(StringBuilder sb, int indent) + { + YedXml.ValueTag(sb, indent, "Unk01", Unk01.ToString()); + YedXml.ValueTag(sb, indent, "Unk02", Unk02.ToString()); + YedXml.ValueTag(sb, indent, "Unk03", Unk03.ToString()); + } + public override void ReadXml(XmlNode node) + { + Unk01 = Xml.GetChildUIntAttribute(node, "Unk01", "value"); + Unk02 = Xml.GetChildUIntAttribute(node, "Unk02", "value"); + Unk03 = Xml.GetChildUIntAttribute(node, "Unk03", "value"); + } + public override string ToString() { - return TypeStr + ", 2: - " + UnkUint1.ToString() + ", " + UnkUint2.ToString() + ", " + UnkUint3.ToString(); + return base.ToString() + " (S2B) - " + Unk01.ToString() + ", " + Unk02.ToString() + ", " + Unk03.ToString(); } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionUnk1_0B : ExpressionUnk1_Base + [TC(typeof(EXP))] public class ExpressionNode05 : ExpressionNodeBase { - public Vector4 UnkVec1 { get; set; } + public float Unk01 { get; set; } - public override void Read(DataReader r1, DataReader r2, DataReader r3) + public override void Read(DataReader r1, DataReader r2) { - UnkVec1 = r1.ReadVector4(); + Unk01 = r2.ReadSingle(); + } + public override void Write(DataWriter w1, DataWriter w2) + { + w2.Write(Unk01); + } + public override void WriteXml(StringBuilder sb, int indent) + { + YedXml.ValueTag(sb, indent, "Unk01", FloatUtil.ToString(Unk01)); + } + public override void ReadXml(XmlNode node) + { + Unk01 = Xml.GetChildFloatAttribute(node, "Unk01", "value"); } public override string ToString() { - var str = TypeStr + ", 1: - " + UnkVec1.ToString(); + return base.ToString() + " (S05) - " + Unk01.ToString(); + } + } + [TC(typeof(EXP))] public class ExpressionNode0B : ExpressionNodeBase + { + public Vector4 Unk01 { get; set; } + + public override void Read(DataReader r1, DataReader r2) + { + Unk01 = r1.ReadVector4(); + } + public override void Write(DataWriter w1, DataWriter w2) + { + w1.Write(Unk01); + } + public override void WriteXml(StringBuilder sb, int indent) + { + YedXml.SelfClosingTag(sb, indent, "Unk01 " + FloatUtil.GetVector4XmlString(Unk01)); + } + public override void ReadXml(XmlNode node) + { + Unk01 = Xml.GetChildVector4Attributes(node, "Unk01"); + } + + public override string ToString() + { + var str = base.ToString() + " (S0B) - " + Unk01.ToString(); return str; } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionUnk1_05 : ExpressionUnk1_Base + [TC(typeof(EXP))] public class ExpressionNodeJiggle : ExpressionNodeBase { - public float UnkFloat { get; set; } + public ExpressionJiggleData JiggleData { get; set; } + public uint BoneTrackRot { get; set; } + public uint BoneTrackPos { get; set; } + public uint UnkUint13 { get; set; }//0 + public uint UnkUint14 { get; set; }//0 - public override void Read(DataReader r1, DataReader r2, DataReader r3) + public override void Read(DataReader r1, DataReader r2) { - UnkFloat = r2.ReadSingle(); + JiggleData = new ExpressionJiggleData(); + JiggleData.Read(r1); + BoneTrackRot = r1.ReadUInt32(); + BoneTrackPos = r1.ReadUInt32(); + UnkUint13 = r1.ReadUInt32(); + UnkUint14 = r1.ReadUInt32(); + } + public override void Write(DataWriter w1, DataWriter w2) + { + if (JiggleData == null) JiggleData = new ExpressionJiggleData(); + JiggleData.Write(w1); + w1.Write(BoneTrackRot); + w1.Write(BoneTrackPos); + w1.Write(UnkUint13); + w1.Write(UnkUint14); + } + public override void WriteXml(StringBuilder sb, int indent) + { + if (JiggleData == null) JiggleData = new ExpressionJiggleData(); + JiggleData.WriteXml(sb, indent); + YedXml.ValueTag(sb, indent, "BoneTrackRot", BoneTrackRot.ToString()); + YedXml.ValueTag(sb, indent, "BoneTrackPos", BoneTrackPos.ToString()); + } + public override void ReadXml(XmlNode node) + { + JiggleData = new ExpressionJiggleData(); + JiggleData.ReadXml(node); + BoneTrackRot = Xml.GetChildUIntAttribute(node, "BoneTrackRot", "value"); + BoneTrackPos = Xml.GetChildUIntAttribute(node, "BoneTrackPos", "value"); } public override string ToString() { - return TypeStr + ", 2: - " + UnkFloat.ToString(); + return base.ToString() + " - " + BoneTrackRot.ToString() + ", " + BoneTrackPos.ToString(); } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionUnk1_3E : ExpressionUnk1_Base + [TC(typeof(EXP))] public class ExpressionNode3E : ExpressionNodeBase { - public Vector4 UnkVec1 { get; set; } - public uint UnkUint1 { get; set; } // 0, 1, 2 - public uint UnkUint2 { get; set; } // 0, 2 - public uint UnkUint3 { get; set; } // 0, 2 - public uint UnkUint4 { get; set; } // 0x00000000 + public Vector4 Unk01 { get; set; } + public uint Unk02 { get; set; } // 0, 1, 2 + public uint Unk03 { get; set; } // 0, 2 + public uint Unk04 { get; set; } // 0, 2 + public uint Unk05 { get; set; } // 0x00000000 - public override void Read(DataReader r1, DataReader r2, DataReader r3) + public override void Read(DataReader r1, DataReader r2) { - UnkVec1 = r1.ReadVector4(); - UnkUint1 = r1.ReadUInt32(); - UnkUint2 = r1.ReadUInt32(); - UnkUint3 = r1.ReadUInt32(); - UnkUint4 = r1.ReadUInt32(); + Unk01 = r1.ReadVector4(); + Unk02 = r1.ReadUInt32(); + Unk03 = r1.ReadUInt32(); + Unk04 = r1.ReadUInt32(); + Unk05 = r1.ReadUInt32(); - switch (UnkUint1) + switch (Unk02) { case 0: case 2: @@ -955,7 +1586,7 @@ namespace CodeWalker.GameFiles default: break;//no hit } - switch (UnkUint2) + switch (Unk03) { case 2: case 0: @@ -963,7 +1594,7 @@ namespace CodeWalker.GameFiles default: break;//no hit } - switch (UnkUint3) + switch (Unk04) { case 2: case 0: @@ -971,7 +1602,7 @@ namespace CodeWalker.GameFiles default: break;//no hit } - switch (UnkUint4) + switch (Unk05) { case 0: break; @@ -979,217 +1610,326 @@ namespace CodeWalker.GameFiles break;//no hit } } - public override string ToString() + public override void Write(DataWriter w1, DataWriter w2) { - return TypeStr + ", 1: - " + UnkVec1.ToString() + " - " + UnkUint1.ToString() + ", " + UnkUint2.ToString() + ", " + UnkUint3.ToString() + ", " + UnkUint4.ToString(); + w1.Write(Unk01); + w1.Write(Unk02); + w1.Write(Unk03); + w1.Write(Unk04); + w1.Write(Unk05); + } + public override void WriteXml(StringBuilder sb, int indent) + { + YedXml.SelfClosingTag(sb, indent, "Unk01 " + FloatUtil.GetVector4XmlString(Unk01)); + YedXml.ValueTag(sb, indent, "Unk02", Unk02.ToString()); + YedXml.ValueTag(sb, indent, "Unk03", Unk03.ToString()); + YedXml.ValueTag(sb, indent, "Unk04", Unk04.ToString()); + } + public override void ReadXml(XmlNode node) + { + Unk01 = Xml.GetChildVector4Attributes(node, "Unk01"); + Unk02 = Xml.GetChildUIntAttribute(node, "Unk02", "value"); + Unk03 = Xml.GetChildUIntAttribute(node, "Unk03", "value"); + Unk04 = Xml.GetChildUIntAttribute(node, "Unk04", "value"); } - } - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionUnk1_0E : ExpressionUnk1_Base - { - public Vector3 UnkVec1 { get; set; } - public MetaHash UnkUint1 { get; set; } - public Vector3 UnkVec2 { get; set; } - public MetaHash UnkUint2 { get; set; } - public Vector3 UnkVec3 { get; set; } - public MetaHash UnkUint3 { get; set; } - public Vector3 UnkVec4 { get; set; } - public MetaHash UnkUint4 { get; set; } - public Vector3 UnkVec5 { get; set; } - public MetaHash UnkUint5 { get; set; } - public Vector3 UnkVec6 { get; set; } - public MetaHash UnkUint6 { get; set; } - public Vector3 UnkVec7 { get; set; } - public MetaHash UnkUint7 { get; set; } - public Vector3 UnkVec8 { get; set; } - public MetaHash UnkUint8 { get; set; } - public Vector3 UnkVec9 { get; set; } - public MetaHash UnkUint9 { get; set; } - public Vector3 UnkVec10 { get; set; } - public MetaHash UnkUint10 { get; set; } - public MetaHash UnkUint11 { get; set; } - public MetaHash UnkUint12 { get; set; } - public MetaHash UnkUint13 { get; set; } - public MetaHash UnkUint14 { get; set; } - public override void Read(DataReader r1, DataReader r2, DataReader r3) - { - UnkVec1 = r1.ReadVector3(); - UnkUint1 = r1.ReadUInt32();//2x short - UnkVec2 = r1.ReadVector3(); - UnkUint2 = r1.ReadUInt32();//2x short - UnkVec3 = r1.ReadVector3(); - UnkUint3 = r1.ReadUInt32();//2x short - UnkVec4 = r1.ReadVector3(); - UnkUint4 = r1.ReadUInt32();//1x short, 2 bytes (first 0) - UnkVec5 = r1.ReadVector3();//0 - UnkUint5 = r1.ReadUInt32();//1x short, 2 bytes (first 0) - UnkVec6 = r1.ReadVector3();//0 - UnkUint6 = r1.ReadUInt32();//0 - UnkVec7 = r1.ReadVector3();//0 - UnkUint7 = r1.ReadUInt32();//1x short, 2 bytes (first 0) - UnkVec8 = r1.ReadVector3();//0 - UnkUint8 = r1.ReadUInt32();//1x short (0), 2 bytes - UnkVec9 = r1.ReadVector3();//down vector (0,0,-1) - UnkUint9 = r1.ReadUInt32();//1x short, 2 bytes (first 0) - UnkVec10 = r1.ReadVector3();//gravity vector? (0, 0, -9.8) - UnkUint10 = r1.ReadUInt32();//2x short (2nd 0) - UnkUint11 = r1.ReadUInt32(); - UnkUint12 = r1.ReadUInt32(); - UnkUint13 = r1.ReadUInt32(); - UnkUint14 = r1.ReadUInt32(); - } public override string ToString() { - return TypeStr + ", 1: - " + UnkVec1.ToString() + ", " + UnkVec2.ToString() + ", " + UnkVec3.ToString() + ", " + UnkVec4.ToString() + " ..."; + return base.ToString() + " (S3E) - " + Unk01.ToString() + " - " + Unk02.ToString() + ", " + Unk03.ToString() + ", " + Unk04.ToString() + ", " + Unk05.ToString(); } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class ExpressionUnk2 : ResourceSystemBlock + + + [TC(typeof(EXP))] public class ExpressionJiggleBlock : ResourceSystemBlock { public override long BlockLength => 0xA0; - // structure data - public float Unknown_00h { get; set; } - public float Unknown_04h { get; set; } - public float Unknown_08h { get; set; } - public MetaHash Unknown_0Ch { get; set; } - public float Unknown_10h { get; set; } - public float Unknown_14h { get; set; } - public float Unknown_18h { get; set; } - public MetaHash Unknown_1Ch { get; set; } - public float Unknown_20h { get; set; } - public float Unknown_24h { get; set; } - public float Unknown_28h { get; set; } - public MetaHash Unknown_2Ch { get; set; } - public float Unknown_30h { get; set; } - public float Unknown_34h { get; set; } - public float Unknown_38h { get; set; } - public MetaHash Unknown_3Ch { get; set; } - public float Unknown_40h { get; set; } - public float Unknown_44h { get; set; } - public float Unknown_48h { get; set; } - public MetaHash Unknown_4Ch { get; set; } - public float Unknown_50h { get; set; } - public float Unknown_54h { get; set; } - public float Unknown_58h { get; set; } - public MetaHash Unknown_5Ch { get; set; } - public float Unknown_60h { get; set; } - public float Unknown_64h { get; set; } - public float Unknown_68h { get; set; } - public MetaHash Unknown_6Ch { get; set; } - public float Unknown_70h { get; set; } - public float Unknown_74h { get; set; } - public float Unknown_78h { get; set; } - public MetaHash Unknown_7Ch { get; set; } - public float Unknown_80h { get; set; } - public float Unknown_84h { get; set; } - public float Unknown_88h { get; set; } - public MetaHash Unknown_8Ch { get; set; } - public float Unknown_90h { get; set; } - public float Unknown_94h { get; set; } - public float Unknown_98h { get; set; } - public MetaHash Unknown_9Ch { get; set; } + public ExpressionJiggleData JiggleData { get; set; } public override void Read(ResourceDataReader reader, params object[] parameters) { - // read structure data - this.Unknown_00h = reader.ReadSingle(); - this.Unknown_04h = reader.ReadSingle(); - this.Unknown_08h = reader.ReadSingle(); - this.Unknown_0Ch = reader.ReadUInt32(); - this.Unknown_10h = reader.ReadSingle(); - this.Unknown_14h = reader.ReadSingle(); - this.Unknown_18h = reader.ReadSingle(); - this.Unknown_1Ch = reader.ReadUInt32(); - this.Unknown_20h = reader.ReadSingle(); - this.Unknown_24h = reader.ReadSingle(); - this.Unknown_28h = reader.ReadSingle(); - this.Unknown_2Ch = reader.ReadUInt32(); - this.Unknown_30h = reader.ReadSingle(); - this.Unknown_34h = reader.ReadSingle(); - this.Unknown_38h = reader.ReadSingle(); - this.Unknown_3Ch = reader.ReadUInt32(); - this.Unknown_40h = reader.ReadSingle(); - this.Unknown_44h = reader.ReadSingle(); - this.Unknown_48h = reader.ReadSingle(); - this.Unknown_4Ch = reader.ReadUInt32(); - this.Unknown_50h = reader.ReadSingle(); - this.Unknown_54h = reader.ReadSingle(); - this.Unknown_58h = reader.ReadSingle(); - this.Unknown_5Ch = reader.ReadUInt32(); - this.Unknown_60h = reader.ReadSingle(); - this.Unknown_64h = reader.ReadSingle(); - this.Unknown_68h = reader.ReadSingle(); - this.Unknown_6Ch = reader.ReadUInt32(); - this.Unknown_70h = reader.ReadSingle(); - this.Unknown_74h = reader.ReadSingle(); - this.Unknown_78h = reader.ReadSingle(); - this.Unknown_7Ch = reader.ReadUInt32(); - this.Unknown_80h = reader.ReadSingle(); - this.Unknown_84h = reader.ReadSingle(); - this.Unknown_88h = reader.ReadSingle(); - this.Unknown_8Ch = reader.ReadUInt32(); - this.Unknown_90h = reader.ReadSingle(); - this.Unknown_94h = reader.ReadSingle(); - this.Unknown_98h = reader.ReadSingle(); - this.Unknown_9Ch = reader.ReadUInt32(); + JiggleData = new ExpressionJiggleData(); + JiggleData.Read(reader); } public override void Write(ResourceDataWriter writer, params object[] parameters) { - // write structure data - writer.Write(this.Unknown_00h); - writer.Write(this.Unknown_04h); - writer.Write(this.Unknown_08h); - writer.Write(this.Unknown_0Ch); - writer.Write(this.Unknown_10h); - writer.Write(this.Unknown_14h); - writer.Write(this.Unknown_18h); - writer.Write(this.Unknown_1Ch); - writer.Write(this.Unknown_20h); - writer.Write(this.Unknown_24h); - writer.Write(this.Unknown_28h); - writer.Write(this.Unknown_2Ch); - writer.Write(this.Unknown_30h); - writer.Write(this.Unknown_34h); - writer.Write(this.Unknown_38h); - writer.Write(this.Unknown_3Ch); - writer.Write(this.Unknown_40h); - writer.Write(this.Unknown_44h); - writer.Write(this.Unknown_48h); - writer.Write(this.Unknown_4Ch); - writer.Write(this.Unknown_50h); - writer.Write(this.Unknown_54h); - writer.Write(this.Unknown_58h); - writer.Write(this.Unknown_5Ch); - writer.Write(this.Unknown_60h); - writer.Write(this.Unknown_64h); - writer.Write(this.Unknown_68h); - writer.Write(this.Unknown_6Ch); - writer.Write(this.Unknown_70h); - writer.Write(this.Unknown_74h); - writer.Write(this.Unknown_78h); - writer.Write(this.Unknown_7Ch); - writer.Write(this.Unknown_80h); - writer.Write(this.Unknown_84h); - writer.Write(this.Unknown_88h); - writer.Write(this.Unknown_8Ch); - writer.Write(this.Unknown_90h); - writer.Write(this.Unknown_94h); - writer.Write(this.Unknown_98h); - writer.Write(this.Unknown_9Ch); + if (JiggleData == null) JiggleData = new ExpressionJiggleData(); + JiggleData.Write(writer); + } + + public override string ToString() + { + return JiggleData?.ToString() ?? base.ToString(); } } + [TC(typeof(EXP))] public class ExpressionJiggleData + { + public Vector3 Vector01 { get; set; } + public ushort Ushort01a { get; set; } + public ushort Ushort01b { get; set; } + public Vector3 Vector02 { get; set; } + public ushort Ushort02a { get; set; } + public ushort Ushort02b { get; set; } + public Vector3 Vector03 { get; set; } + public ushort Ushort03a { get; set; } + public ushort Ushort03b { get; set; } + public Vector3 Vector04 { get; set; } + public ushort Ushort04a { get; set; } + public ushort Ushort04b { get; set; } + public Vector3 Vector05 { get; set; } + public ushort Ushort05a { get; set; } + public ushort Ushort05b { get; set; } + public Vector3 Vector06 { get; set; } + public ushort Ushort06a { get; set; } + public ushort Ushort06b { get; set; } + public Vector3 Vector07 { get; set; } + public ushort Ushort07a { get; set; } + public ushort Ushort07b { get; set; } + public Vector3 Vector08 { get; set; } + public ushort Ushort08a { get; set; } + public ushort Ushort08b { get; set; } + public Vector3 Vector09 { get; set; } + public ushort Ushort09a { get; set; } + public ushort Ushort09b { get; set; } + public Vector3 Gravity { get; set; } + public ushort BoneTag { get; set; } + public ushort Ushort10b { get; set; }//0 - [TypeConverter(typeof(ExpandableObjectConverter))] public struct ExpressionBoneTrack + public void Read(DataReader r) + { + Vector01 = r.ReadVector3(); + Ushort01a = r.ReadUInt16(); + Ushort01b = r.ReadUInt16(); + Vector02 = r.ReadVector3(); + Ushort02a = r.ReadUInt16(); + Ushort02b = r.ReadUInt16(); + Vector03 = r.ReadVector3(); + Ushort03a = r.ReadUInt16(); + Ushort03b = r.ReadUInt16(); + Vector04 = r.ReadVector3(); + Ushort04a = r.ReadUInt16(); + Ushort04b = r.ReadUInt16(); + Vector05 = r.ReadVector3(); + Ushort05a = r.ReadUInt16(); + Ushort05b = r.ReadUInt16(); + Vector06 = r.ReadVector3(); + Ushort06a = r.ReadUInt16(); + Ushort06b = r.ReadUInt16(); + Vector07 = r.ReadVector3(); + Ushort07a = r.ReadUInt16(); + Ushort07b = r.ReadUInt16(); + Vector08 = r.ReadVector3(); + Ushort08a = r.ReadUInt16(); + Ushort08b = r.ReadUInt16(); + Vector09 = r.ReadVector3(); + Ushort09a = r.ReadUInt16(); + Ushort09b = r.ReadUInt16(); + Gravity = r.ReadVector3(); + BoneTag = r.ReadUInt16(); + Ushort10b = r.ReadUInt16(); + + //if (Ushort10b != 0) + //{ }//no hit + } + public void Write(DataWriter w) + { + w.Write(Vector01); + w.Write(Ushort01a); + w.Write(Ushort01b); + w.Write(Vector02); + w.Write(Ushort02a); + w.Write(Ushort02b); + w.Write(Vector03); + w.Write(Ushort03a); + w.Write(Ushort03b); + w.Write(Vector04); + w.Write(Ushort04a); + w.Write(Ushort04b); + w.Write(Vector05); + w.Write(Ushort05a); + w.Write(Ushort05b); + w.Write(Vector06); + w.Write(Ushort06a); + w.Write(Ushort06b); + w.Write(Vector07); + w.Write(Ushort07a); + w.Write(Ushort07b); + w.Write(Vector08); + w.Write(Ushort08a); + w.Write(Ushort08b); + w.Write(Vector09); + w.Write(Ushort09a); + w.Write(Ushort09b); + w.Write(Gravity); + w.Write(BoneTag); + w.Write(Ushort10b); + } + public void WriteXml(StringBuilder sb, int indent) + { + YedXml.SelfClosingTag(sb, indent, "Vector01 " + FloatUtil.GetVector3XmlString(Vector01)); + YedXml.SelfClosingTag(sb, indent, "Vector02 " + FloatUtil.GetVector3XmlString(Vector02)); + YedXml.SelfClosingTag(sb, indent, "Vector03 " + FloatUtil.GetVector3XmlString(Vector03)); + YedXml.SelfClosingTag(sb, indent, "Vector04 " + FloatUtil.GetVector3XmlString(Vector04)); + YedXml.SelfClosingTag(sb, indent, "Vector05 " + FloatUtil.GetVector3XmlString(Vector05)); + YedXml.SelfClosingTag(sb, indent, "Vector06 " + FloatUtil.GetVector3XmlString(Vector06)); + YedXml.SelfClosingTag(sb, indent, "Vector07 " + FloatUtil.GetVector3XmlString(Vector07)); + YedXml.SelfClosingTag(sb, indent, "Vector08 " + FloatUtil.GetVector3XmlString(Vector08)); + YedXml.SelfClosingTag(sb, indent, "Vector09 " + FloatUtil.GetVector3XmlString(Vector09)); + YedXml.SelfClosingTag(sb, indent, "Gravity " + FloatUtil.GetVector3XmlString(Gravity)); + YedXml.ValueTag(sb, indent, "Ushort01a", Ushort01a.ToString()); + YedXml.ValueTag(sb, indent, "Ushort01b", Ushort01b.ToString()); + YedXml.ValueTag(sb, indent, "Ushort02a", Ushort02a.ToString()); + YedXml.ValueTag(sb, indent, "Ushort02b", Ushort02b.ToString()); + YedXml.ValueTag(sb, indent, "Ushort03a", Ushort03a.ToString()); + YedXml.ValueTag(sb, indent, "Ushort03b", Ushort03b.ToString()); + YedXml.ValueTag(sb, indent, "Ushort04a", Ushort04a.ToString()); + YedXml.ValueTag(sb, indent, "Ushort04b", Ushort04b.ToString()); + YedXml.ValueTag(sb, indent, "Ushort05a", Ushort05a.ToString()); + YedXml.ValueTag(sb, indent, "Ushort05b", Ushort05b.ToString()); + YedXml.ValueTag(sb, indent, "Ushort06a", Ushort06a.ToString()); + YedXml.ValueTag(sb, indent, "Ushort06b", Ushort06b.ToString()); + YedXml.ValueTag(sb, indent, "Ushort07a", Ushort07a.ToString()); + YedXml.ValueTag(sb, indent, "Ushort07b", Ushort07b.ToString()); + YedXml.ValueTag(sb, indent, "Ushort08a", Ushort08a.ToString()); + YedXml.ValueTag(sb, indent, "Ushort08b", Ushort08b.ToString()); + YedXml.ValueTag(sb, indent, "Ushort09a", Ushort09a.ToString()); + YedXml.ValueTag(sb, indent, "Ushort09b", Ushort09b.ToString()); + YedXml.ValueTag(sb, indent, "BoneTag", BoneTag.ToString()); + } + public void ReadXml(XmlNode node) + { + Vector01 = Xml.GetChildVector3Attributes(node, "Vector01"); + Vector02 = Xml.GetChildVector3Attributes(node, "Vector02"); + Vector03 = Xml.GetChildVector3Attributes(node, "Vector03"); + Vector04 = Xml.GetChildVector3Attributes(node, "Vector04"); + Vector05 = Xml.GetChildVector3Attributes(node, "Vector05"); + Vector06 = Xml.GetChildVector3Attributes(node, "Vector06"); + Vector07 = Xml.GetChildVector3Attributes(node, "Vector07"); + Vector08 = Xml.GetChildVector3Attributes(node, "Vector08"); + Vector09 = Xml.GetChildVector3Attributes(node, "Vector09"); + Gravity = Xml.GetChildVector3Attributes(node, "Gravity"); + Ushort01a = (ushort)Xml.GetChildUIntAttribute(node, "Ushort01a", "value"); + Ushort01b = (ushort)Xml.GetChildUIntAttribute(node, "Ushort01b", "value"); + Ushort02a = (ushort)Xml.GetChildUIntAttribute(node, "Ushort02a", "value"); + Ushort02b = (ushort)Xml.GetChildUIntAttribute(node, "Ushort02b", "value"); + Ushort03a = (ushort)Xml.GetChildUIntAttribute(node, "Ushort03a", "value"); + Ushort03b = (ushort)Xml.GetChildUIntAttribute(node, "Ushort03b", "value"); + Ushort04a = (ushort)Xml.GetChildUIntAttribute(node, "Ushort04a", "value"); + Ushort04b = (ushort)Xml.GetChildUIntAttribute(node, "Ushort04b", "value"); + Ushort05a = (ushort)Xml.GetChildUIntAttribute(node, "Ushort05a", "value"); + Ushort05b = (ushort)Xml.GetChildUIntAttribute(node, "Ushort05b", "value"); + Ushort06a = (ushort)Xml.GetChildUIntAttribute(node, "Ushort06a", "value"); + Ushort06b = (ushort)Xml.GetChildUIntAttribute(node, "Ushort06b", "value"); + Ushort07a = (ushort)Xml.GetChildUIntAttribute(node, "Ushort07a", "value"); + Ushort07b = (ushort)Xml.GetChildUIntAttribute(node, "Ushort07b", "value"); + Ushort08a = (ushort)Xml.GetChildUIntAttribute(node, "Ushort08a", "value"); + Ushort08b = (ushort)Xml.GetChildUIntAttribute(node, "Ushort08b", "value"); + Ushort09a = (ushort)Xml.GetChildUIntAttribute(node, "Ushort09a", "value"); + Ushort09b = (ushort)Xml.GetChildUIntAttribute(node, "Ushort09b", "value"); + BoneTag = (ushort)Xml.GetChildUIntAttribute(node, "BoneTag", "value"); + } + + public bool Compare(ExpressionJiggleData o) + { + if (o.Vector01 != Vector01) return false; + if (o.Ushort01a != Ushort01a) return false; + if (o.Ushort01b != Ushort01b) return false; + if (o.Vector02 != Vector02) return false; + if (o.Ushort02a != Ushort02a) return false; + if (o.Ushort02b != Ushort02b) return false; + if (o.Vector03 != Vector03) return false; + if (o.Ushort03a != Ushort03a) return false; + if (o.Ushort03b != Ushort03b) return false; + if (o.Vector04 != Vector04) return false; + if (o.Ushort04a != Ushort04a) return false; + if (o.Ushort04b != Ushort04b) return false; + if (o.Vector05 != Vector05) return false; + if (o.Ushort05a != Ushort05a) return false; + if (o.Ushort05b != Ushort05b) return false; + if (o.Vector06 != Vector06) return false; + if (o.Ushort06a != Ushort06a) return false; + if (o.Ushort06b != Ushort06b) return false; + if (o.Vector07 != Vector07) return false; + if (o.Ushort07a != Ushort07a) return false; + if (o.Ushort07b != Ushort07b) return false; + if (o.Vector08 != Vector08) return false; + if (o.Ushort08a != Ushort08a) return false; + if (o.Ushort08b != Ushort08b) return false; + if (o.Vector09 != Vector09) return false; + if (o.Ushort09a != Ushort09a) return false; + if (o.Ushort09b != Ushort09b) return false; + if (o.Gravity != Gravity) return false; + if (o.BoneTag != BoneTag) return false; + if (o.Ushort10b != Ushort10b) return false; + return true; + } + public ExpressionJiggleData Clone() + { + var n = new ExpressionJiggleData(); + n.Vector01 = Vector01; + n.Ushort01a = Ushort01a; + n.Ushort01b = Ushort01b; + n.Vector02 = Vector02; + n.Ushort02a = Ushort02a; + n.Ushort02b = Ushort02b; + n.Vector03 = Vector03; + n.Ushort03a = Ushort03a; + n.Ushort03b = Ushort03b; + n.Vector04 = Vector04; + n.Ushort04a = Ushort04a; + n.Ushort04b = Ushort04b; + n.Vector05 = Vector05; + n.Ushort05a = Ushort05a; + n.Ushort05b = Ushort05b; + n.Vector06 = Vector06; + n.Ushort06a = Ushort06a; + n.Ushort06b = Ushort06b; + n.Vector07 = Vector07; + n.Ushort07a = Ushort07a; + n.Ushort07b = Ushort07b; + n.Vector08 = Vector08; + n.Ushort08a = Ushort08a; + n.Ushort08b = Ushort08b; + n.Vector09 = Vector09; + n.Ushort09a = Ushort09a; + n.Ushort09b = Ushort09b; + n.Gravity = Gravity; + n.BoneTag = BoneTag; + n.Ushort10b = Ushort10b; + return n; + } + + public override string ToString() + { + return BoneTag.ToString(); + } + } + + + [TC(typeof(EXP))] public struct ExpressionBoneTrack : IMetaXmlItem { public ushort BoneTag { get; set; } public byte Track { get; set; } public byte Flags { get; set; } + public void WriteXml(StringBuilder sb, int indent) + { + YedXml.ValueTag(sb, indent, "BoneTag", BoneTag.ToString()); + YedXml.ValueTag(sb, indent, "Track", Track.ToString()); + YedXml.ValueTag(sb, indent, "Flags", Flags.ToString()); + } + public void ReadXml(XmlNode node) + { + BoneTag = (ushort)Xml.GetChildUIntAttribute(node, "BoneTag", "value"); + Track = (byte)Xml.GetChildUIntAttribute(node, "Track", "value"); + Flags = (byte)Xml.GetChildUIntAttribute(node, "Flags", "value"); + } + public override string ToString() { return BoneTag.ToString() + ", " + Track.ToString() + ", " + Flags.ToString(); diff --git a/CodeWalker.Core/GameFiles/Resources/VehicleRecord.cs b/CodeWalker.Core/GameFiles/Resources/VehicleRecord.cs index 6bdcdbf..723f428 100644 --- a/CodeWalker.Core/GameFiles/Resources/VehicleRecord.cs +++ b/CodeWalker.Core/GameFiles/Resources/VehicleRecord.cs @@ -1,8 +1,10 @@ -using System; +using SharpDX; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; namespace CodeWalker.GameFiles { @@ -11,7 +13,7 @@ namespace CodeWalker.GameFiles { public override long BlockLength => 0x20; - public ResourceSimpleList64 Entries; + public ResourceSimpleList64 Entries { get; set; } public VehicleRecordList() { @@ -24,13 +26,68 @@ namespace CodeWalker.GameFiles this.Entries = reader.ReadBlock>(); } - public override void Write(ResourceDataWriter writer, params object[] parameters) { base.Write(writer, parameters); writer.WriteBlock(this.Entries); } + public void WriteXml(StringBuilder sb, int indent) + { + + if (Entries?.data_items != null) + { + foreach (var e in Entries.data_items) + { + YvrXml.OpenTag(sb, indent, "Item"); + e.WriteXml(sb, indent + 1); + YvrXml.CloseTag(sb, indent, "Item"); + } + } + + } + public void ReadXml(XmlNode node) + { + var entries = new List(); + + var inodes = node.SelectNodes("Item"); + if (inodes != null) + { + foreach (XmlNode inode in inodes) + { + var e = new VehicleRecordEntry(); + e.ReadXml(inode); + entries.Add(e); + } + } + + Entries = new ResourceSimpleList64(); + Entries.data_items = entries.ToArray(); + + } + public static void WriteXmlNode(VehicleRecordList l, StringBuilder sb, int indent, string name = "VehicleRecordList") + { + if (l == null) return; + if ((l.Entries?.data_items == null) || (l.Entries.data_items.Length == 0)) + { + YvrXml.SelfClosingTag(sb, indent, name); + } + else + { + YvrXml.OpenTag(sb, indent, name); + l.WriteXml(sb, indent + 1); + YvrXml.CloseTag(sb, indent, name); + } + } + public static VehicleRecordList ReadXmlNode(XmlNode node) + { + if (node == null) return null; + var l = new VehicleRecordList(); + l.ReadXml(node); + return l; + } + + public override Tuple[] GetParts() { @@ -50,22 +107,104 @@ namespace CodeWalker.GameFiles // structure data public uint Time; - public short VelocityX; + public short VelocityX; //factor to convert to m/s is 273.0583 .. or 1/0.0036622214, or 32767/120 public short VelocityY; public short VelocityZ; public sbyte RightX; public sbyte RightY; public sbyte RightZ; - public sbyte TopX; - public sbyte TopY; - public sbyte TopZ; - public byte SteeringAngle; - public byte GasPedalPower; - public byte BrakePedalPower; - public byte HandbrakeUsed; - public float PositionX; - public float PositionY; - public float PositionZ; + public sbyte ForwardX; + public sbyte ForwardY; + public sbyte ForwardZ; + public sbyte SteeringAngle; // factor to convert to game angle is 20 (ie radians) + public sbyte GasPedalPower; //-100 to +100, negative = reverse + public sbyte BrakePedalPower;//0 to 100 + public byte HandbrakeUsed;//0 or 1 + public Vector3 Position; + + public Vector3 Velocity + { + get + { + return new Vector3(VelocityX / 273.0583f, VelocityY / 273.0583f, VelocityZ / 273.0583f); + } + set + { + VelocityX = (short)Math.Round(value.X * 273.0583f); + VelocityY = (short)Math.Round(value.Y * 273.0583f); + VelocityZ = (short)Math.Round(value.Z * 273.0583f); + } + } + public Vector3 Forward + { + get + { + return new Vector3(ForwardX / 127.0f, ForwardY / 127.0f, ForwardZ / 127.0f); + } + set + { + ForwardX = (sbyte)Math.Round(value.X * 127.0f); + ForwardY = (sbyte)Math.Round(value.Y * 127.0f); + ForwardZ = (sbyte)Math.Round(value.Z * 127.0f); + } + } + public Vector3 Right + { + get + { + return new Vector3(RightX / 127.0f, RightY / 127.0f, RightZ / 127.0f); + } + set + { + RightX = (sbyte)Math.Round(value.X * 127.0f); + RightY = (sbyte)Math.Round(value.Y * 127.0f); + RightZ = (sbyte)Math.Round(value.Z * 127.0f); + } + } + public float Steering + { + get + { + return SteeringAngle / 20.0f; + } + set + { + SteeringAngle = (sbyte)Math.Round(value * 20.0f); + } + } + public float GasPedal + { + get + { + return GasPedalPower / 100.0f; + } + set + { + GasPedalPower = (sbyte)Math.Round(value * 100.0f); + } + } + public float BrakePedal + { + get + { + return BrakePedalPower / 100.0f; + } + set + { + BrakePedalPower = (sbyte)Math.Round(value * 100.0f); + } + } + public bool Handbrake + { + get + { + return HandbrakeUsed == 1; + } + set + { + HandbrakeUsed = value ? (byte)1 : (byte)0; + } + } public override void Read(ResourceDataReader reader, params object[] parameters) { @@ -77,18 +216,15 @@ namespace CodeWalker.GameFiles this.RightX = (sbyte)reader.ReadByte(); this.RightY = (sbyte)reader.ReadByte(); this.RightZ = (sbyte)reader.ReadByte(); - this.TopX = (sbyte)reader.ReadByte(); - this.TopY = (sbyte)reader.ReadByte(); - this.TopZ = (sbyte)reader.ReadByte(); - this.SteeringAngle = reader.ReadByte(); - this.GasPedalPower = reader.ReadByte(); - this.BrakePedalPower = reader.ReadByte(); + this.ForwardX = (sbyte)reader.ReadByte(); + this.ForwardY = (sbyte)reader.ReadByte(); + this.ForwardZ = (sbyte)reader.ReadByte(); + this.SteeringAngle = (sbyte)reader.ReadByte(); + this.GasPedalPower = (sbyte)reader.ReadByte(); + this.BrakePedalPower = (sbyte)reader.ReadByte(); this.HandbrakeUsed = reader.ReadByte(); - this.PositionX = reader.ReadSingle(); - this.PositionY = reader.ReadSingle(); - this.PositionZ = reader.ReadSingle(); + this.Position = reader.ReadVector3(); } - public override void Write(ResourceDataWriter writer, params object[] parameters) { // write structure data @@ -96,20 +232,43 @@ namespace CodeWalker.GameFiles writer.Write(this.VelocityX); writer.Write(this.VelocityY); writer.Write(this.VelocityZ); - writer.Write(this.RightX); - writer.Write(this.RightY); - writer.Write(this.RightZ); - writer.Write(this.TopX); - writer.Write(this.TopY); - writer.Write(this.TopZ); - writer.Write(this.SteeringAngle); - writer.Write(this.GasPedalPower); - writer.Write(this.BrakePedalPower); + writer.Write((byte)this.RightX); + writer.Write((byte)this.RightY); + writer.Write((byte)this.RightZ); + writer.Write((byte)this.ForwardX); + writer.Write((byte)this.ForwardY); + writer.Write((byte)this.ForwardZ); + writer.Write((byte)this.SteeringAngle); + writer.Write((byte)this.GasPedalPower); + writer.Write((byte)this.BrakePedalPower); writer.Write(this.HandbrakeUsed); - writer.Write(this.PositionX); - writer.Write(this.PositionY); - writer.Write(this.PositionZ); + writer.Write(this.Position); } + public void WriteXml(StringBuilder sb, int indent) + { + YvrXml.ValueTag(sb, indent, "Time", Time.ToString()); + YvrXml.SelfClosingTag(sb, indent, "Position " + FloatUtil.GetVector3XmlString(Position)); + YvrXml.SelfClosingTag(sb, indent, "Velocity " + FloatUtil.GetVector3XmlString(Velocity)); + YvrXml.SelfClosingTag(sb, indent, "Forward " + FloatUtil.GetVector3XmlString(Forward)); + YvrXml.SelfClosingTag(sb, indent, "Right " + FloatUtil.GetVector3XmlString(Right)); + YvrXml.ValueTag(sb, indent, "Steering", FloatUtil.ToString(Steering)); + YvrXml.ValueTag(sb, indent, "GasPedal", FloatUtil.ToString(GasPedal)); + YvrXml.ValueTag(sb, indent, "BrakePedal", FloatUtil.ToString(BrakePedal)); + YvrXml.ValueTag(sb, indent, "Handbrake", Handbrake.ToString()); + } + public void ReadXml(XmlNode node) + { + Time = Xml.GetChildUIntAttribute(node, "Time", "value"); + Position = Xml.GetChildVector3Attributes(node, "Position"); + Velocity = Xml.GetChildVector3Attributes(node, "Velocity"); + Forward = Xml.GetChildVector3Attributes(node, "Forward"); + Right = Xml.GetChildVector3Attributes(node, "Right"); + Steering = Xml.GetChildFloatAttribute(node, "Steering", "value"); + GasPedal = Xml.GetChildFloatAttribute(node, "GasPedal", "value"); + BrakePedal = Xml.GetChildFloatAttribute(node, "BrakePedal", "value"); + Handbrake = Xml.GetChildBoolAttribute(node, "Handbrake", "value"); + } + } diff --git a/CodeWalker.Core/GameFiles/Resources/WaypointRecord.cs b/CodeWalker.Core/GameFiles/Resources/WaypointRecord.cs index 96fc785..8421d08 100644 --- a/CodeWalker.Core/GameFiles/Resources/WaypointRecord.cs +++ b/CodeWalker.Core/GameFiles/Resources/WaypointRecord.cs @@ -1,8 +1,10 @@ -using System; +using SharpDX; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; namespace CodeWalker.GameFiles { @@ -38,7 +40,6 @@ namespace CodeWalker.GameFiles this.EntriesCount ); } - public override void Write(ResourceDataWriter writer, params object[] parameters) { base.Write(writer, parameters); @@ -56,6 +57,61 @@ namespace CodeWalker.GameFiles writer.Write(this.Unknown_28h); writer.Write(this.Unknown_2Ch); } + public void WriteXml(StringBuilder sb, int indent) + { + + if (Entries?.Data != null) + { + foreach (var e in Entries.Data) + { + YwrXml.OpenTag(sb, indent, "Item"); + e.WriteXml(sb, indent + 1); + YwrXml.CloseTag(sb, indent, "Item"); + } + } + + } + public void ReadXml(XmlNode node) + { + var entries = new List(); + + var inodes = node.SelectNodes("Item"); + if (inodes != null) + { + foreach (XmlNode inode in inodes) + { + var e = new WaypointRecordEntry(); + e.ReadXml(inode); + entries.Add(e); + } + } + + Entries = new ResourceSimpleArray(); + Entries.Data = entries; + + } + public static void WriteXmlNode(WaypointRecordList l, StringBuilder sb, int indent, string name = "WaypointRecordList") + { + if (l == null) return; + if ((l.Entries?.Data == null) || (l.Entries.Data.Count == 0)) + { + YwrXml.SelfClosingTag(sb, indent, name); + } + else + { + YwrXml.OpenTag(sb, indent, name); + l.WriteXml(sb, indent + 1); + YwrXml.CloseTag(sb, indent, name); + } + } + public static WaypointRecordList ReadXmlNode(XmlNode node) + { + if (node == null) return null; + var l = new WaypointRecordList(); + l.ReadXml(node); + return l; + } + public override IResourceBlock[] GetReferences() { @@ -70,9 +126,7 @@ namespace CodeWalker.GameFiles { public override long BlockLength => 20; - public float PositionX; - public float PositionY; - public float PositionZ; + public Vector3 Position; public ushort Unk0; public ushort Unk1; public ushort Unk2; @@ -81,26 +135,38 @@ namespace CodeWalker.GameFiles public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data - this.PositionX = reader.ReadSingle(); - this.PositionY = reader.ReadSingle(); - this.PositionZ = reader.ReadSingle(); + this.Position = reader.ReadVector3(); this.Unk0 = reader.ReadUInt16(); this.Unk1 = reader.ReadUInt16(); this.Unk2 = reader.ReadUInt16(); this.Unk3 = reader.ReadUInt16(); } - public override void Write(ResourceDataWriter writer, params object[] parameters) { // write structure data - writer.Write(this.PositionX); - writer.Write(this.PositionY); - writer.Write(this.PositionZ); + writer.Write(this.Position); writer.Write(this.Unk0); writer.Write(this.Unk1); writer.Write(this.Unk2); writer.Write(this.Unk3); } + public void WriteXml(StringBuilder sb, int indent) + { + YwrXml.SelfClosingTag(sb, indent, "Position " + FloatUtil.GetVector3XmlString(Position)); + YwrXml.ValueTag(sb, indent, "Unk0", Unk0.ToString()); + YwrXml.ValueTag(sb, indent, "Unk1", Unk1.ToString()); + YwrXml.ValueTag(sb, indent, "Unk2", Unk2.ToString()); + YwrXml.ValueTag(sb, indent, "Unk3", Unk3.ToString()); + } + public void ReadXml(XmlNode node) + { + Position = Xml.GetChildVector3Attributes(node, "Position"); + Unk0 = (ushort)Xml.GetChildUIntAttribute(node, "Unk0", "value"); + Unk1 = (ushort)Xml.GetChildUIntAttribute(node, "Unk1", "value"); + Unk2 = (ushort)Xml.GetChildUIntAttribute(node, "Unk2", "value"); + Unk3 = (ushort)Xml.GetChildUIntAttribute(node, "Unk3", "value"); + } + } diff --git a/CodeWalker/ExploreForm.cs b/CodeWalker/ExploreForm.cs index 5ae0156..6209187 100644 --- a/CodeWalker/ExploreForm.cs +++ b/CodeWalker/ExploreForm.cs @@ -276,10 +276,10 @@ namespace CodeWalker InitFileType(".gfx", "Scaleform Flash", 7); InitFileType(".ynd", "Path Nodes", 8, FileTypeAction.ViewYnd, true); InitFileType(".ynv", "Nav Mesh", 9, FileTypeAction.ViewModel, true); - InitFileType(".yvr", "Vehicle Record", 9, FileTypeAction.ViewYvr); - InitFileType(".ywr", "Waypoint Record", 9, FileTypeAction.ViewYwr); + InitFileType(".yvr", "Vehicle Record", 9, FileTypeAction.ViewYvr, true); + InitFileType(".ywr", "Waypoint Record", 9, FileTypeAction.ViewYwr, true); InitFileType(".fxc", "Compiled Shaders", 9, FileTypeAction.ViewFxc); - InitFileType(".yed", "Expression Dictionary", 9, FileTypeAction.ViewYed); + InitFileType(".yed", "Expression Dictionary", 9, FileTypeAction.ViewYed, true); InitFileType(".yld", "Cloth Dictionary", 9, FileTypeAction.ViewYld, true); InitFileType(".yfd", "Frame Filter Dictionary", 9, FileTypeAction.ViewYfd); InitFileType(".asi", "ASI Plugin", 9); @@ -313,6 +313,8 @@ namespace CodeWalker InitSubFileType(".dat", "cache_y.dat", "Cache File", 6, FileTypeAction.ViewCacheDat, true); InitSubFileType(".dat", "heightmap.dat", "Heightmap", 6, FileTypeAction.ViewHeightmap, true); InitSubFileType(".dat", "heightmapheistisland.dat", "Heightmap", 6, FileTypeAction.ViewHeightmap, true); + InitSubFileType(".dat", "distantlights.dat", "Distant Lights", 6, FileTypeAction.ViewDistantLights); + InitSubFileType(".dat", "distantlights_hd.dat", "Distant Lights", 6, FileTypeAction.ViewDistantLights); } private void InitFileType(string ext, string name, int imgidx, FileTypeAction defaultAction = FileTypeAction.ViewHex, bool xmlConvertible = false) { @@ -1404,6 +1406,7 @@ namespace CodeWalker case FileTypeAction.ViewYfd: case FileTypeAction.ViewHeightmap: case FileTypeAction.ViewMrf: + case FileTypeAction.ViewDistantLights: return true; case FileTypeAction.ViewHex: default: @@ -1539,6 +1542,9 @@ namespace CodeWalker case FileTypeAction.ViewNametable: ViewNametable(name, path, data, fe); break; + case FileTypeAction.ViewDistantLights: + ViewDistantLights(name, path, data, fe); + break; case FileTypeAction.ViewHex: default: ViewHex(name, path, data); @@ -1748,16 +1754,16 @@ namespace CodeWalker private void ViewYed(string name, string path, byte[] data, RpfFileEntry e) { var yed = RpfFile.GetFile(e, data); - GenericForm f = new GenericForm(this); + MetaForm f = new MetaForm(this); f.Show(); - f.LoadFile(yed, yed.RpfFileEntry); + f.LoadMeta(yed); } private void ViewYld(string name, string path, byte[] data, RpfFileEntry e) { var yld = RpfFile.GetFile(e, data); - GenericForm f = new GenericForm(this); + MetaForm f = new MetaForm(this); f.Show(); - f.LoadFile(yld, yld.RpfFileEntry); + f.LoadMeta(yld); } private void ViewYfd(string name, string path, byte[] data, RpfFileEntry e) { @@ -1793,6 +1799,13 @@ namespace CodeWalker f.Show(); f.LoadNametable(name, path, data, e); } + private void ViewDistantLights(string name, string path, byte[] data, RpfFileEntry e) + { + var dlf = RpfFile.GetFile(e, data); + GenericForm f = new GenericForm(this); + f.Show(); + f.LoadFile(dlf, dlf.RpfFileEntry); + } private RpfFileEntry CreateFileEntry(string name, string path, ref byte[] data) { @@ -2741,6 +2754,18 @@ namespace CodeWalker { mformat = MetaFormat.Yld; } + if (fnamel.EndsWith(".yed.xml")) + { + mformat = MetaFormat.Yed; + } + if (fnamel.EndsWith(".ywr.xml")) + { + mformat = MetaFormat.Ywr; + } + if (fnamel.EndsWith(".yvr.xml")) + { + mformat = MetaFormat.Yvr; + } if (fnamel.EndsWith(".awc.xml")) { mformat = MetaFormat.Awc; @@ -2924,6 +2949,39 @@ namespace CodeWalker data = yld.Save(); break; } + case MetaFormat.Yed: + { + var yed = XmlYed.GetYed(doc, fpathin); + if (yed.ExpressionDictionary == null) + { + MessageBox.Show(fname + ": Schema not supported.", "Cannot import YED XML"); + continue; + } + data = yed.Save(); + break; + } + case MetaFormat.Ywr: + { + var ywr = XmlYwr.GetYwr(doc, fpathin); + if (ywr.Waypoints == null) + { + MessageBox.Show(fname + ": Schema not supported.", "Cannot import YWR XML"); + continue; + } + data = ywr.Save(); + break; + } + case MetaFormat.Yvr: + { + var yvr = XmlYvr.GetYvr(doc, fpathin); + if (yvr.Records == null) + { + MessageBox.Show(fname + ": Schema not supported.", "Cannot import YVR XML"); + continue; + } + data = yvr.Save(); + break; + } case MetaFormat.Awc: { var awc = XmlAwc.GetAwc(doc, fpathin); @@ -4819,6 +4877,7 @@ namespace CodeWalker ViewHeightmap = 23, ViewMrf = 24, ViewNametable = 25, + ViewDistantLights = 26, } diff --git a/CodeWalker/Forms/MetaForm.cs b/CodeWalker/Forms/MetaForm.cs index 3ef2a1a..3e188fa 100644 --- a/CodeWalker/Forms/MetaForm.cs +++ b/CodeWalker/Forms/MetaForm.cs @@ -318,6 +318,34 @@ namespace CodeWalker.Forms metaFormat = MetaFormat.Ynd; } } + public void LoadMeta(YldFile yld) + { + var fn = ((yld?.RpfFileEntry?.Name) ?? "") + ".xml"; + Xml = MetaXml.GetXml(yld, out fn); + FileName = fn; + RawPropertyGrid.SelectedObject = yld; + rpfFileEntry = yld?.RpfFileEntry; + modified = false; + metaFormat = MetaFormat.XML; + if (yld?.RpfFileEntry != null) + { + metaFormat = MetaFormat.Yld; + } + } + public void LoadMeta(YedFile yed) + { + var fn = ((yed?.RpfFileEntry?.Name) ?? "") + ".xml"; + Xml = MetaXml.GetXml(yed, out fn); + FileName = fn; + RawPropertyGrid.SelectedObject = yed; + rpfFileEntry = yed?.RpfFileEntry; + modified = false; + metaFormat = MetaFormat.XML; + if (yed?.RpfFileEntry != null) + { + metaFormat = MetaFormat.Yed; + } + } public void LoadMeta(CacheDatFile cachedat) { var fn = ((cachedat?.FileEntry?.Name) ?? "") + ".xml"; @@ -404,6 +432,24 @@ namespace CodeWalker.Forms } data = ynd.Save(); break; + case MetaFormat.Yld: + var yld = XmlYld.GetYld(doc); + if (yld.ClothDictionary == null) + { + MessageBox.Show("Schema not supported.", "Cannot import YLD XML"); + return false; + } + data = yld.Save(); + break; + case MetaFormat.Yed: + var yed = XmlYed.GetYed(doc); + if (yed.ExpressionDictionary == null) + { + MessageBox.Show("Schema not supported.", "Cannot import YED XML"); + return false; + } + data = yed.Save(); + break; case MetaFormat.CacheFile: MessageBox.Show("Sorry, CacheFile import is not supported.", "Cannot import CacheFile XML"); return false; diff --git a/CodeWalker/Forms/YvrForm.Designer.cs b/CodeWalker/Forms/YvrForm.Designer.cs index 386bff3..c190601 100644 --- a/CodeWalker/Forms/YvrForm.Designer.cs +++ b/CodeWalker/Forms/YvrForm.Designer.cs @@ -41,9 +41,9 @@ this.RightXColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.RightYColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.RightZColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.TopXColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.TopYColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.TopZColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ForwardXColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ForwardYColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ForwardZColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.SteeringAngleColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.GasPedalPowerColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.BrakePedalPowerColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); @@ -81,9 +81,9 @@ this.RightXColumn, this.RightYColumn, this.RightZColumn, - this.TopXColumn, - this.TopYColumn, - this.TopZColumn, + this.ForwardXColumn, + this.ForwardYColumn, + this.ForwardZColumn, this.SteeringAngleColumn, this.GasPedalPowerColumn, this.BrakePedalPowerColumn, @@ -146,20 +146,20 @@ this.RightZColumn.Text = "Right Z"; this.RightZColumn.Width = 48; // - // TopXColumn + // ForwardXColumn // - this.TopXColumn.Text = "Top X"; - this.TopXColumn.Width = 44; + this.ForwardXColumn.Text = "Fwd X"; + this.ForwardXColumn.Width = 44; // - // TopYColumn + // ForwardYColumn // - this.TopYColumn.Text = "Top Y"; - this.TopYColumn.Width = 44; + this.ForwardYColumn.Text = "Fwd Y"; + this.ForwardYColumn.Width = 44; // - // TopZColumn + // ForwardZColumn // - this.TopZColumn.Text = "Top Z"; - this.TopZColumn.Width = 44; + this.ForwardZColumn.Text = "Fwd Z"; + this.ForwardZColumn.Width = 44; // // SteeringAngleColumn // @@ -236,9 +236,9 @@ private System.Windows.Forms.ColumnHeader RightXColumn; private System.Windows.Forms.ColumnHeader RightYColumn; private System.Windows.Forms.ColumnHeader RightZColumn; - private System.Windows.Forms.ColumnHeader TopXColumn; - private System.Windows.Forms.ColumnHeader TopYColumn; - private System.Windows.Forms.ColumnHeader TopZColumn; + private System.Windows.Forms.ColumnHeader ForwardXColumn; + private System.Windows.Forms.ColumnHeader ForwardYColumn; + private System.Windows.Forms.ColumnHeader ForwardZColumn; private System.Windows.Forms.ColumnHeader SteeringAngleColumn; private System.Windows.Forms.ColumnHeader GasPedalPowerColumn; private System.Windows.Forms.ColumnHeader BrakePedalPowerColumn; diff --git a/CodeWalker/Forms/YvrForm.cs b/CodeWalker/Forms/YvrForm.cs index 847aa29..5f1c0e3 100644 --- a/CodeWalker/Forms/YvrForm.cs +++ b/CodeWalker/Forms/YvrForm.cs @@ -66,42 +66,42 @@ namespace CodeWalker.Forms private string GenerateText() { StringBuilder sb = new StringBuilder(); - sb.AppendLine("PositionX, PositionY, PositionZ, Time, VelocityX, VelocityY, VelocityZ, RightX, RightY, RightZ, TopX, TopY, TopZ, SteeringAngle, GasPedalPower, BrakePedalPower, HandbrakeUsed"); + sb.AppendLine("PositionX, PositionY, PositionZ, Time, VelocityX, VelocityY, VelocityZ, RightX, RightY, RightZ, ForwardX, ForwardY, ForwardZ, SteeringAngle, GasPedalPower, BrakePedalPower, HandbrakeUsed"); foreach (var entry in yvr.Records.Entries.data_items) { - sb.Append(FloatUtil.ToString(entry.PositionX)); + sb.Append(FloatUtil.ToString(entry.Position.X)); sb.Append(", "); - sb.Append(FloatUtil.ToString(entry.PositionY)); + sb.Append(FloatUtil.ToString(entry.Position.Y)); sb.Append(", "); - sb.Append(FloatUtil.ToString(entry.PositionZ)); + sb.Append(FloatUtil.ToString(entry.Position.Z)); sb.Append(", "); sb.Append(entry.Time.ToString()); sb.Append(", "); - sb.Append(entry.VelocityX.ToString()); + sb.Append(FloatUtil.ToString(entry.Velocity.X)); sb.Append(", "); - sb.Append(entry.VelocityY.ToString()); + sb.Append(FloatUtil.ToString(entry.Velocity.Y)); sb.Append(", "); - sb.Append(entry.VelocityZ.ToString()); + sb.Append(FloatUtil.ToString(entry.Velocity.Z)); sb.Append(", "); - sb.Append(entry.RightX.ToString()); + sb.Append(FloatUtil.ToString(entry.Right.X)); sb.Append(", "); - sb.Append(entry.RightY.ToString()); + sb.Append(FloatUtil.ToString(entry.Right.Y)); sb.Append(", "); - sb.Append(entry.RightZ.ToString()); + sb.Append(FloatUtil.ToString(entry.Right.Z)); sb.Append(", "); - sb.Append(entry.TopX.ToString()); + sb.Append(FloatUtil.ToString(entry.Forward.X)); sb.Append(", "); - sb.Append(entry.TopY.ToString()); + sb.Append(FloatUtil.ToString(entry.Forward.Y)); sb.Append(", "); - sb.Append(entry.TopZ.ToString()); + sb.Append(FloatUtil.ToString(entry.Forward.Z)); sb.Append(", "); - sb.Append(entry.SteeringAngle.ToString()); + sb.Append(FloatUtil.ToString(entry.Steering)); sb.Append(", "); - sb.Append(entry.GasPedalPower.ToString()); + sb.Append(FloatUtil.ToString(entry.GasPedal)); sb.Append(", "); - sb.Append(entry.BrakePedalPower.ToString()); + sb.Append(FloatUtil.ToString(entry.BrakePedal)); sb.Append(", "); - sb.Append(entry.HandbrakeUsed.ToString()); + sb.Append(entry.Handbrake.ToString()); sb.AppendLine(); } return sb.ToString(); @@ -114,23 +114,23 @@ namespace CodeWalker.Forms { string[] row = { - FloatUtil.ToString(entry.PositionX), - FloatUtil.ToString(entry.PositionY), - FloatUtil.ToString(entry.PositionZ), + FloatUtil.ToString(entry.Position.X), + FloatUtil.ToString(entry.Position.Y), + FloatUtil.ToString(entry.Position.Z), entry.Time.ToString(), - entry.VelocityX.ToString(), - entry.VelocityY.ToString(), - entry.VelocityZ.ToString(), - entry.RightX.ToString(), - entry.RightY.ToString(), - entry.RightZ.ToString(), - entry.TopX.ToString(), - entry.TopY.ToString(), - entry.TopZ.ToString(), - entry.SteeringAngle.ToString(), - entry.GasPedalPower.ToString(), - entry.BrakePedalPower.ToString(), - entry.HandbrakeUsed.ToString(), + FloatUtil.ToString(entry.Velocity.X), + FloatUtil.ToString(entry.Velocity.Y), + FloatUtil.ToString(entry.Velocity.Z), + FloatUtil.ToString(entry.Right.X), + FloatUtil.ToString(entry.Right.Y), + FloatUtil.ToString(entry.Right.Z), + FloatUtil.ToString(entry.Forward.X), + FloatUtil.ToString(entry.Forward.Y), + FloatUtil.ToString(entry.Forward.Z), + FloatUtil.ToString(entry.Steering), + FloatUtil.ToString(entry.GasPedal), + FloatUtil.ToString(entry.BrakePedal), + entry.Handbrake.ToString(), }; MainListView.Items.Add(new ListViewItem(row)); } diff --git a/CodeWalker/Forms/YwrForm.cs b/CodeWalker/Forms/YwrForm.cs index 0010fdb..e154b14 100644 --- a/CodeWalker/Forms/YwrForm.cs +++ b/CodeWalker/Forms/YwrForm.cs @@ -72,11 +72,11 @@ namespace CodeWalker.Forms sb.AppendLine("PositionX, PositionY, PositionZ, Unk0, Unk1, Unk2, Unk3"); foreach (var entry in ywr.Waypoints.Entries) { - sb.Append(FloatUtil.ToString(entry.PositionX)); + sb.Append(FloatUtil.ToString(entry.Position.X)); sb.Append(", "); - sb.Append(FloatUtil.ToString(entry.PositionY)); + sb.Append(FloatUtil.ToString(entry.Position.Y)); sb.Append(", "); - sb.Append(FloatUtil.ToString(entry.PositionZ)); + sb.Append(FloatUtil.ToString(entry.Position.Z)); sb.Append(", "); sb.Append(entry.Unk0.ToString()); sb.Append(", "); @@ -97,9 +97,9 @@ namespace CodeWalker.Forms { string[] row = { - FloatUtil.ToString(entry.PositionX), - FloatUtil.ToString(entry.PositionY), - FloatUtil.ToString(entry.PositionZ), + FloatUtil.ToString(entry.Position.X), + FloatUtil.ToString(entry.Position.Y), + FloatUtil.ToString(entry.Position.Z), entry.Unk0.ToString(), entry.Unk1.ToString(), entry.Unk2.ToString(),