From fdf2c166f742142dd3f8401330433050d2e8b73c Mon Sep 17 00:00:00 2001 From: Disquse Date: Sat, 12 Jun 2021 01:23:15 +0300 Subject: [PATCH 1/5] Basic parsing support for MRF files --- .../GameFiles/FileTypes/MrfFile.cs | 2057 +++++++++++++++++ CodeWalker.Core/GameFiles/GameFile.cs | 1 + CodeWalker.Core/GameFiles/GameFileCache.cs | 24 + CodeWalker/ExploreForm.cs | 15 +- 4 files changed, 2095 insertions(+), 2 deletions(-) create mode 100644 CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs diff --git a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs new file mode 100644 index 0000000..c779a20 --- /dev/null +++ b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs @@ -0,0 +1,2057 @@ +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; +using TC = System.ComponentModel.TypeConverterAttribute; +using EXP = System.ComponentModel.ExpandableObjectConverter; + +namespace CodeWalker.GameFiles +{ + // Unused by GTAV: 11, 12, 14, 16 + public enum MrfNodeInfoType : ushort + { + None = 0, + StateMachineClass = 1, + Tail = 2, + InlinedStateMachine = 3, + Unk4 = 4, + Blend = 5, + AddSubtract = 6, + Filter = 7, + Unk8 = 8, + Frame = 9, + Unk10 = 10, + BlendN = 13, + Clip = 15, + Unk17 = 17, + Unk18 = 18, + Expression = 19, + Unk20 = 20, + Proxy = 21, + AddN = 22, + Identity = 23, + Unk24 = 24, + Unk25 = 25, + MergeN = 26, + State = 27, + Invalid = 28, + Unk29 = 29, + SubNetworkClass = 30, + Unk31 = 31, + Max = 32 + } + + #region mrf node abstractions + [TC(typeof(EXP))] + public abstract class MrfNodeInfoBase + { + public abstract void Parse(DataReader r); + + public abstract long CalculateSize(DataReader r); + + public abstract MrfNodeInfoType GetInfoType(); + } + + // Not real classes, just abstractions for sharing some parsers. + + [TC(typeof(EXP))] + public abstract class MrfNodeEightBytesBase : MrfNodeInfoBase + { + public MrfHeaderNodeInfo Header { get; set; } + public uint Value { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNodeInfo(r); + Value = r.ReadUInt32(); + } + + public override long CalculateSize(DataReader r) + { + return 8; + } + } + + [TC(typeof(EXP))] + public abstract class MrfNodeBlendAddSubtractBase : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + public uint Unk3 { get; set; } + public uint Unk4 { get; set; } + public uint Unk5 { get; set; } + public uint Unk6 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + Unk1 = r.ReadUInt32(); + Unk2 = r.ReadUInt32(); + + if ((Header.Flags & 0x180000) == 0x80000) + Unk3 = r.ReadUInt32(); + + if ((Header.Flags & 3) != 0) + Unk4 = r.ReadUInt32(); + + var unkTypeFlag = (Header.Flags >> 2) & 3; + + // FIXME: optimized switch-case? + if (unkTypeFlag != 2) + { + if (unkTypeFlag != 1) + return; + + Unk5 = r.ReadUInt32(); + } + + Unk6 = r.ReadUInt32(); + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + long result = 20; + + r.Position += 8; + var headerFlag = r.ReadUInt32(); + r.Position = startPos; + + var unkTypeFlag = (headerFlag >> 2) & 3; + + if ((headerFlag & 0x180000) == 0x80000) + result = 24; + + if ((headerFlag & 3) != 0) + result += 4; + + if (unkTypeFlag == 1) + result += 8; + else if (unkTypeFlag == 2) + result += 4; + + return result; + } + } + + [TC(typeof(EXP))] + public abstract class MrfNodeFilterUnkBase : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + public uint Unk3 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + Unk1 = r.ReadUInt32(); + + var unkTypeFlag = Header.Flags & 3; + + // FIXME: optimized switch-case? + if (unkTypeFlag != 2) + { + if (unkTypeFlag != 1) + return; + + Unk2 = r.ReadUInt32(); + } + + Unk3 = r.ReadUInt32(); + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + + r.Position += 8; + var headerFlag = r.ReadUInt32(); + r.Position = startPos; + + var unkTypeFlag = (headerFlag & 3); + + if (unkTypeFlag == 2) + return 20; + + if (unkTypeFlag == 1) + return 24; + + return 16; + } + } + + [TC(typeof(EXP))] + public abstract class MrfNodeHeaderOnlyBase : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + } + + public override long CalculateSize(DataReader r) + { + return 12; + } + } + + [TC(typeof(EXP))] + public abstract class MrfNodeNegativeBase : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + public byte[] Unk2 { get; set; } + public uint Unk3 { get; set; } + public uint Unk4 { get; set; } + public uint Unk5 { get; set; } + public uint Unk6 { get; set; } + public uint[] Unk7 { get; set; } + public MrfStructNegativeInfoDataUnk7[] Unk7_Items { get; set; } + public uint[] Unk8 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + + var unkTypeFlag1 = Header.Flags & 3; + var unkTypeFlag2 = (Header.Flags >> 2) & 3; + var unk7Count = Header.Flags >> 26; + + if ((Header.Flags & 0x180000) == 0x80000) + Unk1 = r.ReadUInt32(); + + if (unkTypeFlag1 == 1) + Unk2 = r.ReadBytes(76); + else if (unkTypeFlag1 == 2) + Unk3 = r.ReadUInt32(); + + if (unkTypeFlag2 == 1) + { + Unk4 = r.ReadUInt32(); + Unk5 = r.ReadUInt32(); + } + else if (unkTypeFlag2 == 2) + Unk6 = r.ReadUInt32(); + + if (unk7Count != 0) + { + Unk7 = new uint[unk7Count]; + + for (int i = 0; i < unk7Count; i++) + Unk7[i] = r.ReadUInt32(); + } + + var bufferBasePos = r.Position; + var unk8Count = (((2 * unk7Count) | 7) + 1) >> 3; + + if (unk8Count != 0) + { + Unk8 = new uint[unk8Count]; + + for (int i = 0; i < unk8Count; i++) + Unk8[i] = r.ReadUInt32(); + } + + if (unk7Count == 0) + return; + + Unk7_Items = new MrfStructNegativeInfoDataUnk7[unk7Count]; + var iteration = 0; + + for (int i = 0; i < unk7Count; i++) + { + var bufferPos = r.Position; + var readPos = bufferBasePos + 4 * (iteration >> 3); + + r.Position = readPos; + var unkInt = r.ReadUInt32() >> (4 * (iteration & 7)); + r.Position = bufferPos; + + var item = new MrfStructNegativeInfoDataUnk7(); + var unkTypeFlag3 = (unkInt >> 4) & 3; + + if ((unkInt & 3) != 0) + item.Unk1 = r.ReadUInt32(); + + if (unkTypeFlag3 == 1) + { + item.Unk2 = r.ReadUInt32(); + item.Unk3 = r.ReadUInt32(); + } + else if (unkTypeFlag3 == 2) + item.Unk4 = r.ReadUInt32(); + + Unk7_Items[i] = item; + + iteration += 2; + } + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + var baseSize = 12; + + r.Position += 8; + var flags = r.ReadUInt32(); + r.Position = startPos; + + var unkTypeFlag = (flags >> 2) & 3; + var unkCount = flags >> 26; + + if ((flags & 0x180000) == 0x80000) + baseSize += 4; + + if ((flags & 3) == 2) + baseSize = 4; + + if (unkTypeFlag == 2) + baseSize += 4; + else if (unkTypeFlag == 1) + baseSize += 8; + + long result = baseSize + 4 * unkCount + 4 * ((((2 * unkCount) | 7) + 1) >> 3); + + if (unkCount == 0) + return result; + + var iteration = 0; + var lastPos = r.Position + (baseSize + 4 * unkCount); + + for (int i = 0; i < unkCount; i++) + { + var readPos = lastPos + 4 * (iteration >> 3); + r.Position = readPos; + var unkInt = r.ReadUInt32(); + r.Position = startPos; + + var v9 = unkInt >> (4 * (iteration & 7)); + var v10 = (v9 >> 4) & 3; + + if ((v9 & 3) != 0) + result += 4; + + if (v10 == 2) + result += 4; + else if (v10 == 1) + result += 8; + + iteration += 2; + } + + r.Position = startPos; + + return result; + } + } + #endregion + + #region mrf node structs + [TC(typeof(EXP))] + public struct MrfStructHeaderUnk1 + { + public uint Size { get; set; } + public byte[] Bytes { get; set; } + + public MrfStructHeaderUnk1(DataReader r) + { + Size = r.ReadUInt32(); + Bytes = r.ReadBytes((int)Size); + } + } + + [TC(typeof(EXP))] + public struct MrfStructHeaderUnk2 + { + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + + public MrfStructHeaderUnk2(DataReader r) + { + Unk1 = r.ReadUInt32(); + Unk2 = r.ReadUInt32(); + } + } + + [TC(typeof(EXP))] + public struct MrfStructHeaderUnk3 + { + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + + public MrfStructHeaderUnk3(DataReader r) + { + Unk1 = r.ReadUInt32(); + Unk2 = r.ReadUInt32(); + } + } + + [TC(typeof(EXP))] + public struct MrfStructStateMainSection + { + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + public uint Unk3 { get; set; } + public uint Unk4 { get; set; } + public uint Unk5 { get; set; } + public uint Unk6 { get; set; } + public uint Unk7 { get; set; } + public uint Unk8 { get; set; } + public List Items { get; set; } + + public MrfStructStateMainSection(DataReader r) + { + Unk1 = r.ReadUInt32(); + Unk2 = r.ReadUInt32(); + Unk3 = r.ReadUInt32(); + Unk4 = r.ReadUInt32(); + Unk5 = r.ReadUInt32(); + Unk6 = r.ReadUInt32(); + + uint flags = Unk1 & 0xFFFBFFFF; + var iterations = (flags >> 20) & 0xF; + + Items = new List(); + + // FIXME: for-loop? + while (iterations != 0) + { + Items.Add(new MrfStructStateLoopSection(r)); + + if (--iterations == 0) + break; + } + + if ((flags & 0x40000000) != 0) + { + Unk7 = r.ReadUInt32(); + Unk8 = r.ReadUInt32(); + } + else + { + Unk7 = 0; + Unk8 = 0; + } + } + } + + [TC(typeof(EXP))] + public struct MrfStructStateLoopSection + { + public short UnkType { get; } + public short UnkIndex { get; } + public uint Unk1_1 { get; } + public uint Unk1_2 { get; } + public uint Unk1_3 { get; } + public uint Unk2_1 { get; } + public uint Unk3_1 { get; } + public uint Unk3_2 { get; } + + public MrfStructStateLoopSection(DataReader r) + { + Unk1_1 = 0; + Unk1_2 = 0; + Unk1_3 = 0; + Unk2_1 = 0; + Unk3_1 = 0; + Unk3_2 = 0; + + UnkType = r.ReadInt16(); + UnkIndex = r.ReadInt16(); + + switch (UnkType) + { + case 0: + case 1: + { + Unk1_1 = r.ReadUInt32(); + Unk1_2 = r.ReadUInt32(); + Unk1_3 = r.ReadUInt32(); + break; + } + case 9: + case 10: + { + Unk2_1 = r.ReadUInt32(); + break; + } + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 11: + case 12: + { + Unk3_1 = r.ReadUInt32(); + Unk3_2 = r.ReadUInt32(); + break; + } + } + } + } + + [TC(typeof(EXP))] + public struct MrfStructStateSection + { + public MrfStructStateMainSection[] Sections { get; set; } + + public MrfStructStateSection(DataReader r, uint count) + { + Sections = new MrfStructStateMainSection[(int)count]; + + // FIXME: for-loop? + while (true) + { + Sections[(int)count - 1] = new MrfStructStateMainSection(r); + + if (--count == 0) + return; + } + } + } + + [TC(typeof(EXP))] + public struct MrfStructStateInfoUnk2 + { + public uint Unk1 { get; } + public ushort Unk2 { get; } + public ushort Unk3 { get; } + public uint Unk4 { get; } + + public MrfStructStateInfoUnk2(DataReader r) + { + Unk1 = r.ReadUInt32(); + Unk2 = r.ReadUInt16(); + Unk3 = r.ReadUInt16(); + Unk4 = r.ReadUInt32(); + } + } + + [TC(typeof(EXP))] + public struct MrfStructStateInfoEvent + { + public ushort Unk1 { get; } + public ushort Unk2 { get; } + public uint HashName { get; } + + public MrfStructStateInfoEvent(DataReader r) + { + Unk1 = r.ReadUInt16(); + Unk2 = r.ReadUInt16(); + HashName = r.ReadUInt32(); + } + } + + [TC(typeof(EXP))] + public struct MrfStructStateInfoUnk6 + { + public uint Unk1 { get; } + public ushort Unk2 { get; } + public ushort Unk3 { get; } + public uint Unk4 { get; } + + public MrfStructStateInfoUnk6(DataReader r) + { + Unk1 = r.ReadUInt32(); + Unk2 = r.ReadUInt16(); + Unk3 = r.ReadUInt16(); + Unk4 = r.ReadUInt32(); + } + } + + [TC(typeof(EXP))] + public struct MrfStructStateInfoSignalDataUnk3 + { + public uint UnkValue; + public uint UnkDefault; + public ulong UnkRange; + + public MrfStructStateInfoSignalDataUnk3(DataReader r) + { + UnkValue = r.ReadUInt32(); + UnkDefault = r.ReadUInt32(); + UnkRange = r.ReadUInt64(); // 2 merged 32 bit values? + } + } + + [TC(typeof(EXP))] + public struct MrfStructStateInfoSignalData + { + 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 MrfStructStateInfoSignalDataUnk3[] Unk3_Items { get; } + + public MrfStructStateInfoSignalData(DataReader r) + { + Unk1 = 0; + Unk2 = 0; + Unk3_Count = 0; + Unk3_Items = null; + Unk4 = 0; + + UnkType = r.ReadUInt32(); + NameHash = r.ReadUInt32(); + + if (UnkType != 5) + return; + + Unk1 = r.ReadUInt32(); + Unk2 = r.ReadUInt32(); // Default value too? + Unk3_Count = r.ReadUInt32(); + Unk3_Items = new MrfStructStateInfoSignalDataUnk3[Unk3_Count]; + Unk4 = r.ReadUInt32(); + + for (int i = 0; i < Unk3_Count; i++) + Unk3_Items[i] = new MrfStructStateInfoSignalDataUnk3(r); + } + } + + [TC(typeof(EXP))] + public struct MrfStructStateInfoSignal + { + public ushort Header1 { get; } + public ushort Header2 { get; } + public ushort Header3 { get; } + public ushort Header4 { get; } + public List Items { get; } + + public MrfStructStateInfoSignal(DataReader r) + { + Items = new List(); + + Header1 = r.ReadUInt16(); + Header2 = r.ReadUInt16(); + Header3 = r.ReadUInt16(); + Header4 = r.ReadUInt16(); + + uint shouldContinue; + + // FIXME: those loops looks weird + do + { + while (true) + { + var data = new MrfStructStateInfoSignalData(r); + Items.Add(data); + + shouldContinue = data.UnkType; + + if (data.UnkType != 5) + break; + } + } + while (shouldContinue != 0); + } + } + + [TC(typeof(EXP))] + public struct MrfStructNegativeInfoDataUnk7 + { + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + public uint Unk3 { get; set; } + public uint Unk4 { get; set; } + + public MrfStructNegativeInfoDataUnk7(DataReader r) + { + Unk1 = 0; + Unk2 = 0; + Unk3 = 0; + Unk4 = 0; + } + } + #endregion + + #region mrf node headers + [TC(typeof(EXP))] + public class MrfHeaderNodeInfo + { + public MrfNodeInfoType NodeInfoType { get; set; } + + public ushort NodeInfoUnk { get; set; } + + public static long Length { get; set; } + + public MrfHeaderNodeInfo(DataReader r) + { + Length = 2 + 2; + NodeInfoType = (MrfNodeInfoType)r.ReadUInt16(); + NodeInfoUnk = r.ReadUInt16(); + } + } + + [TC(typeof(EXP))] + public class MrfHeaderNameFlag : MrfHeaderNodeInfo + { + public MetaHash NameHash { get; set; } + public uint Flags { get; set; } + + public MrfHeaderNameFlag(DataReader r) : base(r) + { + Length = 4 + 4 + 4; + NameHash = new MetaHash(r.ReadUInt32()); + Flags = r.ReadUInt32(); + } + } + + [TC(typeof(EXP))] + public class MrfHeaderStateMachine : MrfHeaderNodeInfo + { + public MetaHash StateName { get; set; } // Used as an identifier for transitions + public uint Unk2 { get; set; } + public uint Unk3 { get; set; } + public byte Unk4 { get; set; } + public byte Unk5 { get; set; } + public byte Unk6 { get; set; } + public byte Unk7 { get; set; } + public uint Unk8 { get; set; } + public uint Unk9 { get; set; } + public uint Unk10 { get; set; } + + public MrfHeaderStateMachine(DataReader r) : base(r) + { + StateName = new MetaHash(r.ReadUInt32()); + Unk2 = r.ReadUInt32(); + Unk3 = r.ReadUInt32(); + Unk4 = r.ReadByte(); + Unk5 = r.ReadByte(); + Unk6 = r.ReadByte(); + Unk7 = r.ReadByte(); + Unk8 = r.ReadUInt32(); + Unk9 = r.ReadUInt32(); + Unk10 = r.ReadUInt32(); + } + } + #endregion + + #region mrf node classes + // rage__mvNodeStateMachineClass (1) + [TC(typeof(EXP))] + public class MrfNodeStateMachineClassInfo : MrfNodeInfoBase + { + public MrfHeaderStateMachine Header { get; set; } + public int[][] Items { get; set; } + public MrfStructStateSection Header_Unk7_Data { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderStateMachine(r); + Items = new int[Header.Unk6][]; + + for (int i = 0; i < Header.Unk6; i++) + { + var unk1 = r.ReadInt32(); + var unk2 = r.ReadInt32(); + + Items[i] = new int[] { unk1, unk2 }; + } + + if (Header.Unk7 != 0) + { + Header_Unk7_Data = new MrfStructStateSection(r, Header.Unk7); + } + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + long result = 0; + + { + r.Position += 18; + var unk1 = r.ReadByte(); + r.Position = startPos; + + result += 8 * unk1 + 32; + } + + { + r.Position += 19; + var optimizedCount = r.ReadByte(); + r.Position = startPos; + + if (optimizedCount != 0) + { + r.Position += 28; + var nextSectionOff = r.Position + r.ReadUInt32(); + + for (int i = 0; i < optimizedCount; i++) + { + r.Position = nextSectionOff; + var sectionSize = (r.ReadUInt32() >> 4) & 0x3FFF; + nextSectionOff += sectionSize; + result += sectionSize; + } + + } + + r.Position = startPos; + } + + return result; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.StateMachineClass; + } + } + + // rage__mvNodeTail (2) + [TC(typeof(EXP))] + public class MrfNodeTailInfo : MrfNodeEightBytesBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.Tail; + } + + // rage__mvNodeInlinedStateMachine (3) + [TC(typeof(EXP))] + public class MrfNodeInlinedStateMachineInfo : MrfNodeInfoBase + { + public MrfHeaderStateMachine Header { get; set; } + public uint Unk { get; set; } + public int[][] Items { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderStateMachine(r); + Items = new int[Header.Unk6][]; + + Unk = r.ReadUInt32(); + + for (int i = 0; i < Header.Unk6; i++) + { + var unk1 = r.ReadInt32(); + var unk2 = r.ReadInt32(); + + Items[i] = new int[] { unk1, unk2 }; + } + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + + r.Position += 18; + var length = r.ReadByte(); + r.Position = startPos; + + return 8 * length + 36; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.InlinedStateMachine; + } + } + + // rage__mvNode* (4) + [TC(typeof(EXP))] + public class MrfNodeUnk4Info : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + public uint Unk2_Count { get; set; } + public byte[] Unk2_Bytes { get; set; } + public uint Unk3 { get; set; } + public uint Unk4 { get; set; } + public uint Unk5 { get; set; } + public uint Unk6 { get; set; } + public uint Unk7 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + + switch (Header.Flags & 3) + { + case 1: + { + Unk2_Count = r.ReadUInt32(); + Unk2_Bytes = r.ReadBytes((int)Unk2_Count); + break; + } + case 2: + Unk1 = r.ReadUInt32(); + break; + } + + if (((Header.Flags >> 2) & 3) != 0) + Unk3 = r.ReadUInt32(); + + if (((Header.Flags >> 4) & 3) != 0) + Unk4 = r.ReadUInt32(); + + if (((Header.Flags >> 6) & 3) != 0) + Unk5 = r.ReadUInt32(); + + if (((Header.Flags >> 8) & 3) != 0) + Unk6 = r.ReadUInt32(); + + if (((Header.Flags >> 10) & 3) != 0) + Unk7 = r.ReadUInt32(); + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + long result = 12; + + r.Position += 8; + var headerFlags = r.ReadUInt32(); + r.Position = startPos; + + switch (headerFlags & 3) + { + case 1: + { + r.Position += 12; + var length = r.ReadUInt32(); + r.Position = startPos; + + result = length + 16; + break; + } + case 2: + result = 16; + break; + } + + if (((headerFlags >> 2) & 3) != 0) + result += 4; + + if (((headerFlags >> 4) & 3) != 0) + result += 4; + + if (((headerFlags >> 6) & 3) != 0) + result += 4; + + if (((headerFlags >> 8) & 3) != 0) + result += 4; + + if (((headerFlags >> 10) & 3) != 0) + result += 4; + + return result; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.Unk4; + } + } + + // rage__mvNodeBlend (5) + [TC(typeof(EXP))] + public class MrfNodeBlendInfo : MrfNodeBlendAddSubtractBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.Blend; + } + + // rage__mvNodeAddSubtract (6) + [TC(typeof(EXP))] + public class MrfNodeAddSubstractInfo : MrfNodeBlendAddSubtractBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.AddSubtract; + } + + // rage__mvNodeFilter (7) + [TC(typeof(EXP))] + public class MrfNodeFilterInfo : MrfNodeFilterUnkBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.Filter; + } + + // rage__mvNode* (8) + [TC(typeof(EXP))] + public class MrfNodeUnk8Info : MrfNodeFilterUnkBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.Unk8; + } + + // rage__mvNodeFrame (9) + [TC(typeof(EXP))] + public class MrfNodeFrameInfo : MrfNodeInfoBase + { + public MrfHeaderNodeInfo Header { get; set; } + public uint Unk1 { get; set; } + public uint Flags { get; set; } + public uint Unk2 { get; set; } + public uint Unk3 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNodeInfo(r); + + Unk1 = r.ReadUInt32(); + Flags = r.ReadUInt32(); + + if ((Flags & 3) != 0) + Unk2 = r.ReadUInt32(); + + if ((Flags & 0x30) != 0) + Unk3 = r.ReadUInt32(); + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + long result = 12; + + r.Position += 8; + var flags = r.ReadUInt32(); + r.Position = startPos; + + if ((flags & 3) != 0) + result = 16; + + if ((flags & 0x30) != 0) + result += 4; + + return result; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.Frame; + } + } + + // rage__mvNode* (10) + [TC(typeof(EXP))] + public class MrfNodeUnk10Info : MrfNodeEightBytesBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.Tail; + } + + // rage__mvNodeBlendN (13) + [TC(typeof(EXP))] + public class MrfNodeBlendNInfo : MrfNodeNegativeBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.BlendN; + } + + // rage__mvNodeClip (15) + [TC(typeof(EXP))] + public class MrfNodeClipInfo : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + public MetaHash Unk3 { get; set; } + public uint Unk4 { get; set; } + public uint Unk5 { get; set; } + public uint Unk6 { get; set; } + public uint Unk7 { get; set; } + public uint Unk8 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + + switch (Header.Flags & 3) + { + case 1: + { + Unk2 = r.ReadUInt32(); + Unk3 = new MetaHash(r.ReadUInt32()); + + if (Unk2 != 3) + Unk4 = r.ReadUInt32(); + + break; + } + case 2: + Unk1 = r.ReadUInt32(); + break; + } + + if (((Header.Flags >> 2) & 3) != 0) + Unk5 = r.ReadUInt32(); + + if (((Header.Flags >> 4) & 3) != 0) + Unk6 = r.ReadUInt32(); + + if (((Header.Flags >> 6) & 3) != 0) + Unk7 = r.ReadUInt32(); + + if (((Header.Flags >> 6) & 3) != 0) + Unk8 = r.ReadUInt32(); + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + long result = 4; + + r.Position += 8; + var headerFlags = r.ReadUInt32(); + r.Position = startPos; + + switch (headerFlags & 3) + { + case 1: + { + r.Position += 12; + var unk4 = r.ReadUInt32(); + r.Position = startPos; + + result = (unk4 != 3) ? 24 : 20; + break; + } + case 2: + result = 16; + break; + } + + if (((headerFlags >> 2) & 3) != 0) + result += 4; + + if (((headerFlags >> 4) & 3) != 0) + result += 4; + + if (((headerFlags >> 6) & 3) != 0) + result += 4; + + if (((headerFlags >> 8) & 3) != 0) + result += 4; + + return result; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.Clip; + } + } + + // rage__mvNode* (17) + [TC(typeof(EXP))] + public class MrfNodeUnk17Info : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1_Count { get; set; } + public byte[] Unk1_Bytes { get; set; } + public uint Unk2 { get; set; } + public uint Unk3 { get; set; } + public uint Unk4 { get; set; } + public uint Unk5 { get; set; } + public uint[] Unk6 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + + if ((Header.Flags & 3) == 1) + { + Unk1_Count = r.ReadUInt32(); + Unk1_Bytes = r.ReadBytes((int)Unk1_Count); + } + else if ((Header.Flags & 3) == 2) + Unk2 = r.ReadUInt32(); + + if (((Header.Flags >> 2) & 3) != 0) + Unk3 = r.ReadUInt32(); + + if (((Header.Flags >> 4) & 3) != 0) + Unk4 = r.ReadUInt32(); + + if ((Header.Flags >> 6) != 0) + Unk5 = r.ReadUInt32(); + + var flags = Header.Flags >> 19; + var unk6Count = (Header.Flags >> 10) & 0xF; + + Unk6 = new uint[unk6Count]; + + for (int i = 0; i < unk6Count; i++) + { + if ((flags & 3) != 0) + Unk6[i] = r.ReadUInt32(); + + flags >>= 2; + } + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + long result = 12; + + r.Position += 8; + var headerFlags = r.ReadUInt32(); + r.Position = startPos; + + if ((headerFlags & 3) == 1) + { + r.Position += 12; + var length = r.ReadUInt32(); + r.Position = startPos; + + result = length + 16; + } + else if ((headerFlags & 3) == 2) + result = 16; + + if (((headerFlags >> 2) & 3) != 0) + result += 4; + + if (((headerFlags >> 4) & 3) != 0) + result += 4; + + if ((headerFlags >> 6) != 0) + result += 4; + + var unk6Count = (headerFlags >> 10) & 0xF; + + if (unk6Count == 0) + return result; + + var flags = headerFlags >> 19; + + for (int i = 0; i < unk6Count; i++) + { + if ((flags & 3) != 0) + result += 4; + + flags >>= 2; + } + + return result; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.Unk17; + } + } + + // rage__mvNode* (18) + [TC(typeof(EXP))] + public class MrfNodeUnk18Info : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + Unk1 = r.ReadUInt32(); + + if ((Header.Flags & 3) != 0) + Unk2 = r.ReadUInt32(); + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + + r.Position += 8; + var headerFlags = r.ReadUInt32(); + r.Position = startPos; + + return ((headerFlags & 3) != 0) ? 20 : 16; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.Unk18; + } + } + + // rage__mvNodeExpression (19) + [TC(typeof(EXP))] + public class MrfNodeExpressionInfo : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + public uint Unk3 { get; set; } + public uint Unk4 { get; set; } + public uint Unk5 { get; set; } + public uint Unk6 { get; set; } + public uint[][] Unk7 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + Unk1 = r.ReadUInt32(); + + switch (Header.Flags & 3) + { + case 1: + Unk3 = r.ReadUInt32(); + Unk4 = r.ReadUInt32(); + break; + case 2: + Unk5 = r.ReadUInt32(); + break; + } + + if (((Header.Flags >> 2) & 3) != 0) + Unk6 = r.ReadUInt32(); + + var unk7Count = (Header.Flags >> 28); + + if (unk7Count == 0) + return; + + var unkOffset = (Header.Flags >> 4) & 0xFFFFFF; + var offset = 0; + + Unk7 = new uint[unk7Count][]; + + for (int i = 0; i < unk7Count; i++) + { + var unkFlag = (unkOffset >> offset) & 3; + + var value1 = r.ReadUInt32(); + uint value2 = 0; + + if (unkFlag == 2 || unkFlag == 1) + value2 = r.ReadUInt32(); + + Unk7[i] = new uint[2] { value1, value2 }; + + offset += 2; + } + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + long result = 16; + + r.Position += 8; + var headerFlag = r.ReadUInt32(); + r.Position = startPos; + + switch (headerFlag & 3) + { + case 1: + result = 24; + break; + case 2: + result = 2; + break; + } + + if (((headerFlag >> 2) & 3) != 0) + result += 4; + + var unk7Count = (headerFlag >> 28); + + if (unk7Count == 0) + return result; + + var unkOffset = (headerFlag >> 4) & 0xFFFFFF; + var offset = 0; + + for (int i = 0; i < unk7Count; i++) + { + result += 4; + var unkFlag = (unkOffset >> offset) & 3; + + if (unkFlag == 2 || unkFlag == 1) + result += 4; + + offset += 2; + } + + return result; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.Expression; + } + } + + // rage__mvNode* (20) + [TC(typeof(EXP))] + public class MrfNodeUnk20Info : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + public uint Unk3 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + Unk1 = r.ReadUInt32(); + + if ((Header.Flags & 3) != 0) + Unk2 = r.ReadUInt32(); + + if ((Header.Flags & 0x30) != 0) + Unk3 = r.ReadUInt32(); + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + long result = 16; + + r.Position += 8; + var flags = r.ReadUInt32(); + r.Position = startPos; + + if ((flags & 3) != 0) + result = 20; + + if (((flags & 3) & 0x30) != 0) + result += 4; + + return result; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.Unk20; + } + } + + // rage__mvNodeProxy (21) + [TC(typeof(EXP))] + public class MrfNodeProxyInfo : MrfNodeHeaderOnlyBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.Proxy; + } + + // rage__mvNodeAddN (22) + [TC(typeof(EXP))] + public class MrfNodeAddNInfo : MrfNodeNegativeBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.AddN; + } + + // rage__mvNodeIdentity (23) + [TC(typeof(EXP))] + public class MrfNodeIdentityInfo : MrfNodeEightBytesBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.Identity; + } + + // rage__mvNode* (24) + [TC(typeof(EXP))] + public class MrfNodeUnk24Info : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + public uint Unk2 { get; set; } + public uint Unk3 { get; set; } + public uint Unk4 { get; set; } + public uint Unk5 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + Unk2 = r.ReadUInt32(); + Unk3 = r.ReadUInt32(); + + if ((Header.Flags & 0x180000) == 0x80000) + Unk3 = r.ReadUInt32(); + + var unkTypeFlag = (Header.Flags & 3); + + // FIXME: optimized switch-case? + if (unkTypeFlag != 2) + { + if (unkTypeFlag != 1) + return; + + Unk4 = r.ReadUInt32(); + } + + Unk5 = r.ReadUInt32(); + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + long result = 20; + + r.Position += 8; + var headerFlags = r.ReadUInt32(); + r.Position = startPos; + + if ((headerFlags & 0x180000) == 0x80000) + result = 24; + + switch (headerFlags & 3) + { + case 1: + result += 8; + break; + case 2: + result += 4; + break; + } + + return result; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.Unk24; + } + } + + // rage__mvNode* (25) + [TC(typeof(EXP))] + public class MrfNodeUnk25Info : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + + if ((Header.Flags & 3) != 0) + Unk1 = r.ReadUInt32(); + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + + r.Position += 8; + var headerFlag = r.ReadUInt32(); + r.Position = startPos; + + return ((headerFlag & 3) != 0) ? 16 : 12; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.Unk25; + } + } + + // rage__mvNodeMergeN (26) + [TC(typeof(EXP))] + public class MrfNodeMergeNInfo : MrfNodeNegativeBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.MergeN; + } + + // rage__mvNodeState (27) + [TC(typeof(EXP))] + public class MrfNodeStateInfo : MrfNodeInfoBase + { + public MrfHeaderStateMachine Header { get; set; } + public uint Unk1 { get; set; } + public uint Unk2_Count { get; set; } + public uint Unk3 { get; set; } + public uint EventsCount { get; set; } + public uint Unk5 { get; set; } + public uint Unk6_Count { get; set; } + public uint Unk7 { get; set; } + public uint SignalsCount { get; set; } + + public MrfStructStateSection Header_Unk7_Data { get; set; } + public MrfStructStateInfoUnk2[] Unk2_Items { get; set; } + public MrfStructStateInfoEvent[] EventsItems { get; set; } + public MrfStructStateInfoUnk6[] Unk6_Items { get; set; } + public MrfStructStateInfoSignal[] SignalsItems { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderStateMachine(r); + + Unk1 = r.ReadUInt32(); + Unk2_Count = r.ReadUInt32(); + Unk3 = r.ReadUInt32(); + EventsCount = r.ReadUInt32(); + Unk5 = r.ReadUInt32(); + Unk6_Count = r.ReadUInt32(); + Unk7 = r.ReadUInt32(); + SignalsCount = r.ReadUInt32(); + + Unk2_Items = new MrfStructStateInfoUnk2[Unk2_Count]; + EventsItems = new MrfStructStateInfoEvent[EventsCount]; + Unk6_Items = new MrfStructStateInfoUnk6[Unk6_Count]; + SignalsItems = new MrfStructStateInfoSignal[SignalsCount]; + + if (Header.Unk7 != 0) + Header_Unk7_Data = new MrfStructStateSection(r, Header.Unk7); + + for (int i = 0; i < Unk2_Count; i++) + Unk2_Items[i] = new MrfStructStateInfoUnk2(r); + + for (int i = 0; i < EventsCount; i++) + EventsItems[i] = new MrfStructStateInfoEvent(r); + + for (int i = 0; i < Unk6_Count; i++) + Unk6_Items[i] = new MrfStructStateInfoUnk6(r); + + if (SignalsCount != 0) + { + for (int i = 0; i < SignalsCount; i++) + SignalsItems[i] = new MrfStructStateInfoSignal(r); + } + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + long result = 64; + + { + r.Position += 19; + var optimizedCount = r.ReadByte(); + r.Position = startPos; + + if (optimizedCount != 0) + { + r.Position += 28; + var nextSectionOff = r.Position + r.ReadUInt32(); + + for (int i = 0; i < optimizedCount; i++) + { + r.Position = nextSectionOff; + var sectionSize = (r.ReadUInt32() >> 4) & 0x3FFF; + nextSectionOff += sectionSize; + result += sectionSize; + } + } + + r.Position = startPos; + } + + { + r.Position += 36; + var unk12 = r.ReadUInt32(); + r.Position = startPos; + + r.Position += 52; + var unk14 = r.ReadUInt32(); + r.Position = startPos; + + r.Position += 44; + var unk16 = r.ReadUInt32(); + r.Position = startPos; + + result += 12 * (unk12 + unk14) + 8 * unk16; + } + + { + r.Position += 60; + var iterations = r.ReadUInt32(); + r.Position = startPos; + + if (iterations != 0) + { + r.Position += 56; + var offsetPtr = r.Position + r.ReadUInt32(); + r.Position = startPos; + + for (int i = 0; i < iterations; i++) + { + result += 8; + offsetPtr += 8; + + uint shouldContinue; + + // FIXME: those loops can be simplified + do + { + while (true) + { + r.Position = offsetPtr; + shouldContinue = r.ReadUInt32(); + r.Position = offsetPtr; + + if (shouldContinue != 5) + break; + + r.Position = offsetPtr + 4; + var unkCount = r.ReadUInt32(); + r.Position = offsetPtr; + + long targetOffset = (offsetPtr + 4 + unkCount); + + r.Position = targetOffset + 8; + var length1 = r.ReadUInt32(); + r.Position = targetOffset + 12; + var length2 = r.ReadInt32(); + r.Position = targetOffset; + + result += 16 * length1 + 24; + offsetPtr = (targetOffset + 16 * length1 + 12 + length2); + } + + result += 8; + offsetPtr += 8; + } + while (shouldContinue != 0); + } + } + } + + r.Position = startPos; + + return result; + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.State; + } + } + + // rage__mvNodeInvalid (28) + [TC(typeof(EXP))] + public class MrfNodeInvalidInfo : MrfNodeEightBytesBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.Invalid; + } + + // rage__mvNode* (29) + [TC(typeof(EXP))] + public class MrfNodeUnk29Info : MrfNodeFilterUnkBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.Unk29; + } + + // rage__mvNodeSubNetworkClass (30) + [TC(typeof(EXP))] + public class MrfNodeSubNetworkClassInfo : MrfNodeHeaderOnlyBase + { + public override MrfNodeInfoType GetInfoType() => MrfNodeInfoType.SubNetworkClass; + } + + // rage__mvNode* (31) + [TC(typeof(EXP))] + public class MrfNodeUnk31Info : MrfNodeInfoBase + { + public MrfHeaderNameFlag Header { get; set; } + public uint Unk1 { get; set; } + public uint Unk2_Count { get; set; } + public uint[][] Unk2_Items { get; set; } + public uint Unk3_Count { get; set; } + public uint[][] Unk3_Items { get; set; } + public uint Unk4_Count { get; set; } + public uint[][] Unk4_Items { get; set; } + public uint Unk5_Count { get; set; } + public uint[][] Unk5_Items { get; set; } + + public override void Parse(DataReader r) + { + Header = new MrfHeaderNameFlag(r); + Unk1 = r.ReadUInt32(); + Unk2_Count = r.ReadUInt32(); + Unk3_Count = r.ReadUInt32(); + Unk4_Count = r.ReadUInt32(); + Unk5_Count = r.ReadUInt32(); + + if (Unk3_Count > 0) + { + Unk3_Items = new uint[Unk3_Count][]; + + for (int i = 0; i < Unk3_Count; i++) + { + var value1 = r.ReadUInt32(); + var value2 = r.ReadUInt32(); + Unk3_Items[i] = new uint[] { value1, value2 }; + } + } + + if (Unk4_Count > 0) + { + Unk4_Items = new uint[Unk4_Count][]; + + for (int i = 0; i < Unk4_Count; i++) + { + var value1 = r.ReadUInt32(); + var value2 = r.ReadUInt32(); + Unk4_Items[i] = new uint[] { value1, value2 }; + } + } + + if (Unk5_Count > 0) + { + Unk5_Items = new uint[Unk5_Count][]; + + for (int i = 0; i < Unk5_Count; i++) + { + var value1 = r.ReadUInt32(); + var value2 = r.ReadUInt32(); + Unk5_Items[i] = new uint[] { value1, value2 }; + } + } + + if (Unk2_Count > 0) + { + Unk2_Items = new uint[Unk2_Count][]; + + for (int i = 0; i < Unk2_Count; i++) + { + var value1 = r.ReadUInt32(); + var value2 = r.ReadUInt32(); + var value3 = r.ReadUInt32(); + Unk2_Items[i] = new uint[] { value1, value2, value3 }; + } + } + } + + public override long CalculateSize(DataReader r) + { + var startPos = r.Position; + + r.Position += 16; + var count1 = r.ReadUInt32(); + var count2 = r.ReadUInt32(); + var count3 = r.ReadUInt32(); + var count4 = r.ReadUInt32(); + r.Position = startPos; + + return 12 * count1 + 8 * (count2 + count3 + count4 + 4); + } + + public override MrfNodeInfoType GetInfoType() + { + return MrfNodeInfoType.Unk31; + } + } + #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 MrfNodeInfoBase[] Nodes { 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() + { + // TODO + var buf = new byte[4]; + return buf; + } + + private void Write(DataWriter w) + { + } + + 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!"); + + 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 nodeInfos = new List(); + + while (true) + { + // TODO: probably not safe enough as there might be a "lucky" case of bytes that will fit node type index. + var nodeType = GetNextMrfNodeTypeIndex(r); + + if (nodeType == MrfNodeInfoType.None) + break; + + var nodeHandler = GetNextMrfNodeHandler(nodeType); + var expectedSize = nodeHandler.CalculateSize(r); + var expectedPos = r.Position + expectedSize; + + if (expectedPos > r.Length) + throw new Exception($"Calculated mrf node ({nodeType}) expected size is invalid ({expectedSize})"); + + if (nodeType != nodeHandler.GetInfoType()) + throw new Exception($"Parsed mrf node type ({nodeHandler.GetInfoType()}) is not equals expected type ({nodeType})"); + + // Finally we can parse it. + nodeHandler.Parse(r); + + if (expectedPos != r.Position) + throw new Exception($"Position of reader ({r.Position}) is not equals expected position ({expectedPos})"); + + nodeInfos.Add(nodeHandler); + } + + Nodes = new MrfNodeInfoBase[nodeInfos.Count]; + + // Hacky remapping... + for (int i = 0; i < nodeInfos.Count; i++) + Nodes[i] = nodeInfos[i]; + + if (FlagsCount != 0) + { + FlagsItems = new byte[FlagsCount]; + + for (int i = 0; i < FlagsCount; i++) + FlagsItems[i] = r.ReadByte(); + } + + if (r.Position != r.Length) + throw new Exception($"Failed to read MRF ({r.Position} / {r.Length})"); + } + + private MrfNodeInfoType GetNextMrfNodeTypeIndex(DataReader r) + { + var startPos = r.Position; + var nodeInfoType = (MrfNodeInfoType)r.ReadUInt16(); + r.Position = startPos; + + if (nodeInfoType <= MrfNodeInfoType.None || nodeInfoType >= MrfNodeInfoType.Max) + return MrfNodeInfoType.None; + + return nodeInfoType; + } + + private MrfNodeInfoBase GetNextMrfNodeHandler(MrfNodeInfoType infoType) + { + if (infoType == MrfNodeInfoType.None) + throw new Exception($"Attempt to get a handler of none mrf node"); + + switch (infoType) + { + case MrfNodeInfoType.StateMachineClass: + return new MrfNodeStateMachineClassInfo(); + case MrfNodeInfoType.Tail: + return new MrfNodeTailInfo(); + case MrfNodeInfoType.InlinedStateMachine: + return new MrfNodeInlinedStateMachineInfo(); + case MrfNodeInfoType.Unk4: + return new MrfNodeUnk4Info(); + case MrfNodeInfoType.Blend: + return new MrfNodeBlendInfo(); + case MrfNodeInfoType.AddSubtract: + return new MrfNodeAddSubstractInfo(); + case MrfNodeInfoType.Filter: + return new MrfNodeFilterInfo(); + case MrfNodeInfoType.Unk8: + return new MrfNodeUnk8Info(); + case MrfNodeInfoType.Frame: + return new MrfNodeFrameInfo(); + case MrfNodeInfoType.Unk10: + return new MrfNodeUnk10Info(); + case MrfNodeInfoType.BlendN: + return new MrfNodeBlendNInfo(); + case MrfNodeInfoType.Clip: + return new MrfNodeClipInfo(); + case MrfNodeInfoType.Unk17: + return new MrfNodeUnk17Info(); + case MrfNodeInfoType.Unk18: + return new MrfNodeUnk18Info(); + case MrfNodeInfoType.Expression: + return new MrfNodeExpressionInfo(); + case MrfNodeInfoType.Unk20: + return new MrfNodeUnk20Info(); + case MrfNodeInfoType.Proxy: + return new MrfNodeProxyInfo(); + case MrfNodeInfoType.AddN: + return new MrfNodeAddNInfo(); + case MrfNodeInfoType.Identity: + return new MrfNodeIdentityInfo(); + case MrfNodeInfoType.Unk24: + return new MrfNodeUnk24Info(); + case MrfNodeInfoType.Unk25: + return new MrfNodeUnk25Info(); + case MrfNodeInfoType.MergeN: + return new MrfNodeMergeNInfo(); + case MrfNodeInfoType.State: + return new MrfNodeStateInfo(); + case MrfNodeInfoType.Invalid: + return new MrfNodeInvalidInfo(); + case MrfNodeInfoType.Unk29: + return new MrfNodeUnk29Info(); + case MrfNodeInfoType.SubNetworkClass: + return new MrfNodeSubNetworkClassInfo(); + case MrfNodeInfoType.Unk31: + return new MrfNodeUnk31Info(); + } + + throw new Exception($"A handler for ({infoType}) mrf node type is not implemented"); + } + } +} diff --git a/CodeWalker.Core/GameFiles/GameFile.cs b/CodeWalker.Core/GameFiles/GameFile.cs index a436c2f..c9e7b3f 100644 --- a/CodeWalker.Core/GameFiles/GameFile.cs +++ b/CodeWalker.Core/GameFiles/GameFile.cs @@ -82,6 +82,7 @@ namespace CodeWalker.GameFiles Yfd = 26, Heightmap = 27, Watermap = 28, + Mrf = 29, } diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index 8ba3019..0e1fe6a 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -197,6 +197,7 @@ namespace CodeWalker.GameFiles //TestYpts(); //TestYnvs(); //TestYmaps(); + //TestMrfs(); //TestPlacements(); //TestDrawables(); //TestHeightmaps(); @@ -4179,6 +4180,29 @@ namespace CodeWalker.GameFiles } } } + public void TestMrfs() + { + foreach (RpfFile file in AllRpfs) + { + foreach (RpfEntry entry in file.AllEntries) + { + try + { + if (entry.NameLower.EndsWith(".mrf")) + { + UpdateStatus(string.Format(entry.Path)); + MrfFile mrffile = RpfMan.GetFile(entry); + if (mrffile != null) + { } + } + } + catch (Exception ex) + { + UpdateStatus("Error! " + ex.ToString()); + } + } + } + } public void TestPlacements() { //int totplacements = 0; diff --git a/CodeWalker/ExploreForm.cs b/CodeWalker/ExploreForm.cs index 7d119e3..0c6274c 100644 --- a/CodeWalker/ExploreForm.cs +++ b/CodeWalker/ExploreForm.cs @@ -297,7 +297,7 @@ namespace CodeWalker InitFileType(".png", "Portable Network Graphics", 16); InitFileType(".dds", "DirectDraw Surface", 16); InitFileType(".ytd", "Texture Dictionary", 16, FileTypeAction.ViewYtd, true); - InitFileType(".mrf", "MRF File", 18); + InitFileType(".mrf", "Move Network File", 18, FileTypeAction.ViewMrf); InitFileType(".ycd", "Clip Dictionary", 18, FileTypeAction.ViewYcd, true); InitFileType(".ypt", "Particle Effect", 18, FileTypeAction.ViewModel, true); InitFileType(".ybn", "Static Collisions", 19, FileTypeAction.ViewModel, true); @@ -1401,6 +1401,7 @@ namespace CodeWalker case FileTypeAction.ViewYld: case FileTypeAction.ViewYfd: case FileTypeAction.ViewHeightmap: + case FileTypeAction.ViewMrf: return true; case FileTypeAction.ViewHex: default: @@ -1530,6 +1531,9 @@ namespace CodeWalker case FileTypeAction.ViewHeightmap: ViewHeightmap(name, path, data, fe); break; + case FileTypeAction.ViewMrf: + ViewMrf(name, path, data, fe); + break; case FileTypeAction.ViewHex: default: ViewHex(name, path, data); @@ -1771,7 +1775,13 @@ namespace CodeWalker f.Show(); f.LoadMeta(heightmap); } - + private void ViewMrf(string name, string path, byte[] data, RpfFileEntry e) + { + var mrf = RpfFile.GetFile(e, data); + GenericForm f = new GenericForm(this); + f.Show(); + f.LoadFile(mrf, mrf.RpfFileEntry); + } private RpfFileEntry CreateFileEntry(string name, string path, ref byte[] data) { //this should only really be used when loading a file from the filesystem. @@ -4693,6 +4703,7 @@ namespace CodeWalker ViewYld = 21, ViewYfd = 22, ViewHeightmap = 23, + ViewMrf = 24, } From 8d399945cd88088762246119483160302645f070 Mon Sep 17 00:00:00 2001 From: Disquse Date: Sat, 12 Jun 2021 14:20:21 +0300 Subject: [PATCH 2/5] A bit of findings about MRF structs --- .../GameFiles/FileTypes/MrfFile.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs index c779a20..c56f227 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs @@ -80,7 +80,7 @@ namespace CodeWalker.GameFiles public uint Unk2 { get; set; } public uint Unk3 { get; set; } public uint Unk4 { get; set; } - public uint Unk5 { get; set; } + public MetaHash Unk5 { get; set; } public uint Unk6 { get; set; } public override void Parse(DataReader r) @@ -103,7 +103,7 @@ namespace CodeWalker.GameFiles if (unkTypeFlag != 1) return; - Unk5 = r.ReadUInt32(); + Unk5 = new MetaHash(r.ReadUInt32()); } Unk6 = r.ReadUInt32(); @@ -140,7 +140,7 @@ namespace CodeWalker.GameFiles { public MrfHeaderNameFlag Header { get; set; } public uint Unk1 { get; set; } - public uint Unk2 { get; set; } + public MetaHash Unk2 { get; set; } public uint Unk3 { get; set; } public override void Parse(DataReader r) @@ -156,7 +156,7 @@ namespace CodeWalker.GameFiles if (unkTypeFlag != 1) return; - Unk2 = r.ReadUInt32(); + Unk2 = new MetaHash(r.ReadUInt32()); } Unk3 = r.ReadUInt32(); @@ -1049,9 +1049,9 @@ namespace CodeWalker.GameFiles public MrfHeaderNameFlag Header { get; set; } public uint Unk1 { get; set; } public uint Unk2 { get; set; } - public MetaHash Unk3 { get; set; } - public uint Unk4 { get; set; } - public uint Unk5 { get; set; } + public MetaHash DictName { get; set; } + public MetaHash ClipName { get; set; } + public float Unk5 { get; set; } public uint Unk6 { get; set; } public uint Unk7 { get; set; } public uint Unk8 { get; set; } @@ -1065,10 +1065,10 @@ namespace CodeWalker.GameFiles case 1: { Unk2 = r.ReadUInt32(); - Unk3 = new MetaHash(r.ReadUInt32()); + DictName = new MetaHash(r.ReadUInt32()); if (Unk2 != 3) - Unk4 = r.ReadUInt32(); + ClipName = new MetaHash(r.ReadUInt32()); break; } @@ -1078,7 +1078,7 @@ namespace CodeWalker.GameFiles } if (((Header.Flags >> 2) & 3) != 0) - Unk5 = r.ReadUInt32(); + Unk5 = r.ReadSingle(); if (((Header.Flags >> 4) & 3) != 0) Unk6 = r.ReadUInt32(); @@ -1278,10 +1278,10 @@ namespace CodeWalker.GameFiles public MrfHeaderNameFlag Header { get; set; } public uint Unk1 { get; set; } public uint Unk2 { get; set; } - public uint Unk3 { get; set; } + public MetaHash Unk3 { get; set; } public uint Unk4 { get; set; } public uint Unk5 { get; set; } - public uint Unk6 { get; set; } + public float Unk6 { get; set; } public uint[][] Unk7 { get; set; } public override void Parse(DataReader r) @@ -1292,7 +1292,7 @@ namespace CodeWalker.GameFiles switch (Header.Flags & 3) { case 1: - Unk3 = r.ReadUInt32(); + Unk3 = new MetaHash(r.ReadUInt32()); Unk4 = r.ReadUInt32(); break; case 2: @@ -1301,7 +1301,7 @@ namespace CodeWalker.GameFiles } if (((Header.Flags >> 2) & 3) != 0) - Unk6 = r.ReadUInt32(); + Unk6 = r.ReadSingle(); var unk7Count = (Header.Flags >> 28); @@ -1453,7 +1453,7 @@ namespace CodeWalker.GameFiles public uint Unk1 { get; set; } public uint Unk2 { get; set; } public uint Unk3 { get; set; } - public uint Unk4 { get; set; } + public MetaHash Unk4 { get; set; } public uint Unk5 { get; set; } public override void Parse(DataReader r) @@ -1473,7 +1473,7 @@ namespace CodeWalker.GameFiles if (unkTypeFlag != 1) return; - Unk4 = r.ReadUInt32(); + Unk4 = new MetaHash(r.ReadUInt32()); } Unk5 = r.ReadUInt32(); From 17b89cf4ee3bf3fa0c3446c8e9b34106a461f359 Mon Sep 17 00:00:00 2001 From: Disquse Date: Fri, 16 Jul 2021 20:24:01 +0300 Subject: [PATCH 3/5] More MRF corrections --- .../GameFiles/FileTypes/MrfFile.cs | 81 ++++++++++--------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs index c56f227..5bb9a90 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs @@ -76,24 +76,24 @@ namespace CodeWalker.GameFiles public abstract class MrfNodeBlendAddSubtractBase : MrfNodeInfoBase { public MrfHeaderNameFlag Header { get; set; } - public uint Unk1 { get; set; } - public uint Unk2 { get; set; } + public int Unk1 { get; set; } + public int Unk2 { get; set; } public uint Unk3 { get; set; } - public uint Unk4 { get; set; } + public float Unk4 { get; set; } public MetaHash Unk5 { get; set; } public uint Unk6 { get; set; } public override void Parse(DataReader r) { Header = new MrfHeaderNameFlag(r); - Unk1 = r.ReadUInt32(); - Unk2 = r.ReadUInt32(); + Unk1 = r.ReadInt32(); + Unk2 = r.ReadInt32(); if ((Header.Flags & 0x180000) == 0x80000) Unk3 = r.ReadUInt32(); if ((Header.Flags & 3) != 0) - Unk4 = r.ReadUInt32(); + Unk4 = r.ReadSingle(); var unkTypeFlag = (Header.Flags >> 2) & 3; @@ -141,7 +141,7 @@ namespace CodeWalker.GameFiles public MrfHeaderNameFlag Header { get; set; } public uint Unk1 { get; set; } public MetaHash Unk2 { get; set; } - public uint Unk3 { get; set; } + public MetaHash Unk3 { get; set; } public override void Parse(DataReader r) { @@ -156,10 +156,10 @@ namespace CodeWalker.GameFiles if (unkTypeFlag != 1) return; - Unk2 = new MetaHash(r.ReadUInt32()); + Unk2 = new MetaHash(r.ReadUInt32()); // Filter Frame dict hash } - Unk3 = r.ReadUInt32(); + Unk3 = new MetaHash(r.ReadUInt32()); // Filter Frame name hash } public override long CalculateSize(DataReader r) @@ -393,8 +393,8 @@ namespace CodeWalker.GameFiles public struct MrfStructStateMainSection { public uint Unk1 { get; set; } - public uint Unk2 { get; set; } - public uint Unk3 { get; set; } + public int Unk2 { get; set; } + public float Unk3 { get; set; } public uint Unk4 { get; set; } public uint Unk5 { get; set; } public uint Unk6 { get; set; } @@ -405,8 +405,8 @@ namespace CodeWalker.GameFiles public MrfStructStateMainSection(DataReader r) { Unk1 = r.ReadUInt32(); - Unk2 = r.ReadUInt32(); - Unk3 = r.ReadUInt32(); + Unk2 = r.ReadInt32(); + Unk3 = r.ReadSingle(); Unk4 = r.ReadUInt32(); Unk5 = r.ReadUInt32(); Unk6 = r.ReadUInt32(); @@ -568,9 +568,9 @@ namespace CodeWalker.GameFiles [TC(typeof(EXP))] public struct MrfStructStateInfoSignalDataUnk3 { - public uint UnkValue; - public uint UnkDefault; - public ulong UnkRange; + public uint UnkValue { get; } + public uint UnkDefault { get; } + public ulong UnkRange { get; } public MrfStructStateInfoSignalDataUnk3(DataReader r) { @@ -580,6 +580,19 @@ namespace CodeWalker.GameFiles } } + [TC(typeof(EXP))] + public struct MrfStructStateMachineEntry + { + public MetaHash Value { get; } + public uint Unk { get; } + + public MrfStructStateMachineEntry(DataReader r) + { + Value = new MetaHash(r.ReadUInt32()); + Unk = r.ReadUInt32(); + } + } + [TC(typeof(EXP))] public struct MrfStructStateInfoSignalData { @@ -741,21 +754,16 @@ namespace CodeWalker.GameFiles public class MrfNodeStateMachineClassInfo : MrfNodeInfoBase { public MrfHeaderStateMachine Header { get; set; } - public int[][] Items { get; set; } + public MrfStructStateMachineEntry[] Items { get; set; } public MrfStructStateSection Header_Unk7_Data { get; set; } public override void Parse(DataReader r) { Header = new MrfHeaderStateMachine(r); - Items = new int[Header.Unk6][]; + Items = new MrfStructStateMachineEntry[Header.Unk6]; for (int i = 0; i < Header.Unk6; i++) - { - var unk1 = r.ReadInt32(); - var unk2 = r.ReadInt32(); - - Items[i] = new int[] { unk1, unk2 }; - } + Items[i] = new MrfStructStateMachineEntry(r); if (Header.Unk7 != 0) { @@ -821,22 +829,17 @@ namespace CodeWalker.GameFiles { public MrfHeaderStateMachine Header { get; set; } public uint Unk { get; set; } - public int[][] Items { get; set; } + public MrfStructStateMachineEntry[] Items { get; set; } public override void Parse(DataReader r) { Header = new MrfHeaderStateMachine(r); - Items = new int[Header.Unk6][]; + Items = new MrfStructStateMachineEntry[Header.Unk6]; Unk = r.ReadUInt32(); for (int i = 0; i < Header.Unk6; i++) - { - var unk1 = r.ReadInt32(); - var unk2 = r.ReadInt32(); - - Items[i] = new int[] { unk1, unk2 }; - } + Items[i] = new MrfStructStateMachineEntry(r); } public override long CalculateSize(DataReader r) @@ -1052,7 +1055,7 @@ namespace CodeWalker.GameFiles public MetaHash DictName { get; set; } public MetaHash ClipName { get; set; } public float Unk5 { get; set; } - public uint Unk6 { get; set; } + public float Unk6 { get; set; } public uint Unk7 { get; set; } public uint Unk8 { get; set; } @@ -1081,7 +1084,7 @@ namespace CodeWalker.GameFiles Unk5 = r.ReadSingle(); if (((Header.Flags >> 4) & 3) != 0) - Unk6 = r.ReadUInt32(); + Unk6 = r.ReadSingle(); if (((Header.Flags >> 6) & 3) != 0) Unk7 = r.ReadUInt32(); @@ -1278,8 +1281,8 @@ namespace CodeWalker.GameFiles public MrfHeaderNameFlag Header { get; set; } public uint Unk1 { get; set; } public uint Unk2 { get; set; } - public MetaHash Unk3 { get; set; } - public uint Unk4 { get; set; } + public MetaHash ExpressionDict { get; set; } + public MetaHash ExpressionName { get; set; } public uint Unk5 { get; set; } public float Unk6 { get; set; } public uint[][] Unk7 { get; set; } @@ -1292,8 +1295,8 @@ namespace CodeWalker.GameFiles switch (Header.Flags & 3) { case 1: - Unk3 = new MetaHash(r.ReadUInt32()); - Unk4 = r.ReadUInt32(); + ExpressionDict = new MetaHash(r.ReadUInt32()); + ExpressionName = new MetaHash(r.ReadUInt32()); break; case 2: Unk5 = r.ReadUInt32(); @@ -1344,7 +1347,7 @@ namespace CodeWalker.GameFiles result = 24; break; case 2: - result = 2; + result = 20; break; } From 11eb44dbd64a30944283713e27c0019b1fc5a222 Mon Sep 17 00:00:00 2001 From: Disquse Date: Sat, 6 Nov 2021 03:17:53 +0300 Subject: [PATCH 4/5] Early MRF saving code and tweaks Saving is very fragile and might fail and generate wrong data if something is changed without respecting flags --- .../GameFiles/FileTypes/MrfFile.cs | 1034 ++++++++++++++--- 1 file changed, 841 insertions(+), 193 deletions(-) diff --git a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs index 5bb9a90..b19b73c 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs @@ -1,13 +1,13 @@ using System; using System.IO; -using System.Diagnostics; using System.Collections.Generic; using TC = System.ComponentModel.TypeConverterAttribute; using EXP = System.ComponentModel.ExpandableObjectConverter; namespace CodeWalker.GameFiles { - // Unused by GTAV: 11, 12, 14, 16 + // 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 MrfNodeInfoType : ushort { None = 0, @@ -45,10 +45,12 @@ namespace CodeWalker.GameFiles [TC(typeof(EXP))] public abstract class MrfNodeInfoBase { - public abstract void Parse(DataReader r); + public abstract void Read(DataReader r); public abstract long CalculateSize(DataReader r); + public abstract void Write(DataWriter w); + public abstract MrfNodeInfoType GetInfoType(); } @@ -60,7 +62,7 @@ namespace CodeWalker.GameFiles public MrfHeaderNodeInfo Header { get; set; } public uint Value { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNodeInfo(r); Value = r.ReadUInt32(); @@ -70,6 +72,12 @@ namespace CodeWalker.GameFiles { return 8; } + + public override void Write(DataWriter w) + { + Header.Write(w); + w.Write(Value); + } } [TC(typeof(EXP))] @@ -79,11 +87,11 @@ namespace CodeWalker.GameFiles public int Unk1 { get; set; } public int Unk2 { get; set; } public uint Unk3 { get; set; } - public float Unk4 { get; set; } + public MetaHash Unk4 { get; set; } public MetaHash Unk5 { get; set; } - public uint Unk6 { get; set; } + public MetaHash Unk6 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); Unk1 = r.ReadInt32(); @@ -93,20 +101,18 @@ namespace CodeWalker.GameFiles Unk3 = r.ReadUInt32(); if ((Header.Flags & 3) != 0) - Unk4 = r.ReadSingle(); + Unk4 = new MetaHash(r.ReadUInt32()); - var unkTypeFlag = (Header.Flags >> 2) & 3; - - // FIXME: optimized switch-case? - if (unkTypeFlag != 2) + switch ((Header.Flags >> 2) & 3) { - if (unkTypeFlag != 1) - return; - - Unk5 = new MetaHash(r.ReadUInt32()); + case 1: + Unk5 = new MetaHash(r.ReadUInt32()); + Unk6 = new MetaHash(r.ReadUInt32()); + break; + case 2: + Unk6 = new MetaHash(r.ReadUInt32()); + break; } - - Unk6 = r.ReadUInt32(); } public override long CalculateSize(DataReader r) @@ -133,6 +139,30 @@ namespace CodeWalker.GameFiles return result; } + + public override void Write(DataWriter w) + { + Header.Write(w); + w.Write(Unk1); + w.Write(Unk2); + + if ((Header.Flags & 0x180000) == 0x80000) + w.Write(Unk3); + + if ((Header.Flags & 3) != 0) + w.Write(Unk4); + + switch ((Header.Flags >> 2) & 3) + { + case 1: + w.Write(Unk5); + w.Write(Unk6); + break; + case 2: + w.Write(Unk6); + break; + } + } } [TC(typeof(EXP))] @@ -143,23 +173,21 @@ namespace CodeWalker.GameFiles public MetaHash Unk2 { get; set; } public MetaHash Unk3 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); Unk1 = r.ReadUInt32(); - var unkTypeFlag = Header.Flags & 3; - - // FIXME: optimized switch-case? - if (unkTypeFlag != 2) + switch (Header.Flags & 3) { - if (unkTypeFlag != 1) - return; - - Unk2 = new MetaHash(r.ReadUInt32()); // Filter Frame dict hash + case 1: + Unk2 = new MetaHash(r.ReadUInt32()); // Filter Frame dict hash + Unk3 = new MetaHash(r.ReadUInt32()); // Filter Frame name hash + break; + case 2: + Unk3 = new MetaHash(r.ReadUInt32()); + break; } - - Unk3 = new MetaHash(r.ReadUInt32()); // Filter Frame name hash } public override long CalculateSize(DataReader r) @@ -170,7 +198,7 @@ namespace CodeWalker.GameFiles var headerFlag = r.ReadUInt32(); r.Position = startPos; - var unkTypeFlag = (headerFlag & 3); + var unkTypeFlag = headerFlag & 3; if (unkTypeFlag == 2) return 20; @@ -180,6 +208,23 @@ namespace CodeWalker.GameFiles return 16; } + + public override void Write(DataWriter w) + { + Header.Write(w); + w.Write(Unk1); + + switch (Header.Flags & 3) + { + case 1: + w.Write(Unk2); + w.Write(Unk3); + break; + case 2: + w.Write(Unk3); + break; + } + } } [TC(typeof(EXP))] @@ -187,7 +232,7 @@ namespace CodeWalker.GameFiles { public MrfHeaderNameFlag Header { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); } @@ -196,6 +241,11 @@ namespace CodeWalker.GameFiles { return 12; } + + public override void Write(DataWriter w) + { + Header.Write(w); + } } [TC(typeof(EXP))] @@ -208,11 +258,11 @@ namespace CodeWalker.GameFiles public uint Unk4 { get; set; } public uint Unk5 { get; set; } public uint Unk6 { get; set; } - public uint[] Unk7 { get; set; } + public int[] Unk7 { get; set; } public MrfStructNegativeInfoDataUnk7[] Unk7_Items { get; set; } public uint[] Unk8 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); @@ -224,7 +274,7 @@ namespace CodeWalker.GameFiles Unk1 = r.ReadUInt32(); if (unkTypeFlag1 == 1) - Unk2 = r.ReadBytes(76); + Unk2 = r.ReadBytes(76); // Unused? else if (unkTypeFlag1 == 2) Unk3 = r.ReadUInt32(); @@ -238,13 +288,12 @@ namespace CodeWalker.GameFiles if (unk7Count != 0) { - Unk7 = new uint[unk7Count]; + Unk7 = new int[unk7Count]; for (int i = 0; i < unk7Count; i++) - Unk7[i] = r.ReadUInt32(); + Unk7[i] = r.ReadInt32(); } - var bufferBasePos = r.Position; var unk8Count = (((2 * unk7Count) | 7) + 1) >> 3; if (unk8Count != 0) @@ -259,21 +308,17 @@ namespace CodeWalker.GameFiles return; Unk7_Items = new MrfStructNegativeInfoDataUnk7[unk7Count]; - var iteration = 0; + int iteration = 0; for (int i = 0; i < unk7Count; i++) { - var bufferPos = r.Position; - var readPos = bufferBasePos + 4 * (iteration >> 3); - - r.Position = readPos; - var unkInt = r.ReadUInt32() >> (4 * (iteration & 7)); - r.Position = bufferPos; + var unk8Value = Unk8[iteration >> 3]; + var unk7Flag = unk8Value >> (4 * (iteration & 7)); + var unkTypeFlag3 = (unk7Flag >> 4) & 3; var item = new MrfStructNegativeInfoDataUnk7(); - var unkTypeFlag3 = (unkInt >> 4) & 3; - if ((unkInt & 3) != 0) + if ((unk7Flag & 3) != 0) item.Unk1 = r.ReadUInt32(); if (unkTypeFlag3 == 1) @@ -346,12 +391,81 @@ namespace CodeWalker.GameFiles return result; } + + public override void Write(DataWriter w) + { + Header.Write(w); + + var unkTypeFlag1 = Header.Flags & 3; + var unkTypeFlag2 = (Header.Flags >> 2) & 3; + var unk7Count = Header.Flags >> 26; + + if ((Header.Flags & 0x180000) == 0x80000) + w.Write(Unk1); + + if (unkTypeFlag1 == 1) + w.Write(Unk2); + else if (unkTypeFlag1 == 2) + w.Write(Unk3); + + if (unkTypeFlag2 == 1) + { + w.Write(Unk4); + w.Write(Unk5); + } + else if (unkTypeFlag2 == 2) + w.Write(Unk6); + + if (unk7Count > 0) + { + foreach (var value in Unk7) + w.Write(value); + } + + var unk8Count = (((2 * unk7Count) | 7) + 1) >> 3; + + if (unk8Count > 0) + { + foreach (var value in Unk8) + w.Write(value); + } + + if (unk7Count == 0) + return; + + int iteration = 0; + + foreach (var item in Unk7_Items) + { + var unk8Value = Unk8[iteration >> 3]; + var unk7Flag = unk8Value >> (4 * (iteration & 7)); + var unkTypeFlag3 = (unk7Flag >> 4) & 3; + + if ((unk7Flag & 3) != 0) + w.Write(item.Unk1); + + if (unkTypeFlag3 == 1) + { + w.Write(item.Unk2); + w.Write(item.Unk3); + } + else if (unkTypeFlag3 == 2) + w.Write(item.Unk4); + + iteration += 2; + } + } } #endregion #region mrf node structs + public abstract class MrfStructBase + { + public abstract void Write(DataWriter w); + } + [TC(typeof(EXP))] - public struct MrfStructHeaderUnk1 + public class MrfStructHeaderUnk1 : MrfStructBase { public uint Size { get; set; } public byte[] Bytes { get; set; } @@ -361,10 +475,16 @@ namespace CodeWalker.GameFiles Size = r.ReadUInt32(); Bytes = r.ReadBytes((int)Size); } + + public override void Write(DataWriter w) + { + w.Write(Size); + w.Write(Bytes); + } } [TC(typeof(EXP))] - public struct MrfStructHeaderUnk2 + public class MrfStructHeaderUnk2 : MrfStructBase { public uint Unk1 { get; set; } public uint Unk2 { get; set; } @@ -374,10 +494,16 @@ namespace CodeWalker.GameFiles Unk1 = r.ReadUInt32(); Unk2 = r.ReadUInt32(); } + + public override void Write(DataWriter w) + { + w.Write(Unk1); + w.Write(Unk2); + } } [TC(typeof(EXP))] - public struct MrfStructHeaderUnk3 + public class MrfStructHeaderUnk3 : MrfStructBase { public uint Unk1 { get; set; } public uint Unk2 { get; set; } @@ -387,10 +513,16 @@ namespace CodeWalker.GameFiles Unk1 = r.ReadUInt32(); Unk2 = r.ReadUInt32(); } + + public override void Write(DataWriter w) + { + w.Write(Unk1); + w.Write(Unk2); + } } [TC(typeof(EXP))] - public struct MrfStructStateMainSection + public class MrfStructStateMainSection : MrfStructBase { public uint Unk1 { get; set; } public int Unk2 { get; set; } @@ -400,7 +532,7 @@ namespace CodeWalker.GameFiles public uint Unk6 { get; set; } public uint Unk7 { get; set; } public uint Unk8 { get; set; } - public List Items { get; set; } + public MrfStructStateLoopSection[] Items { get; set; } public MrfStructStateMainSection(DataReader r) { @@ -414,12 +546,12 @@ namespace CodeWalker.GameFiles uint flags = Unk1 & 0xFFFBFFFF; var iterations = (flags >> 20) & 0xF; - Items = new List(); + Items = new MrfStructStateLoopSection[iterations]; // FIXME: for-loop? while (iterations != 0) { - Items.Add(new MrfStructStateLoopSection(r)); + Items[iterations - 1] = new MrfStructStateLoopSection(r); if (--iterations == 0) break; @@ -436,10 +568,33 @@ namespace CodeWalker.GameFiles Unk8 = 0; } } + + public override void Write(DataWriter w) + { + w.Write(Unk1); + w.Write(Unk2); + w.Write(Unk3); + w.Write(Unk4); + w.Write(Unk5); + w.Write(Unk6); + + // FIXME: might be broken if changed without flags, see "iterations" + foreach (var item in Items) + item.Write(w); + + // FIXME: might be broken if changed without flags + uint flags = Unk1 & 0xFFFBFFFF; + + if ((flags & 0x40000000) != 0) + { + w.Write(Unk7); + w.Write(Unk8); + } + } } [TC(typeof(EXP))] - public struct MrfStructStateLoopSection + public class MrfStructStateLoopSection : MrfStructBase { public short UnkType { get; } public short UnkIndex { get; } @@ -494,10 +649,49 @@ namespace CodeWalker.GameFiles } } } + + public override void Write(DataWriter w) + { + w.Write(UnkType); + w.Write(UnkIndex); + + // FIXME: might be broken if changed outside + switch (UnkType) + { + case 0: + case 1: + { + w.Write(Unk1_1); + w.Write(Unk1_2); + w.Write(Unk1_3); + break; + } + case 9: + case 10: + { + w.Write(Unk2_1); + break; + } + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 11: + case 12: + { + w.Write(Unk3_1); + w.Write(Unk3_2); + break; + } + } + } } [TC(typeof(EXP))] - public struct MrfStructStateSection + public class MrfStructStateSection : MrfStructBase { public MrfStructStateMainSection[] Sections { get; set; } @@ -514,59 +708,88 @@ namespace CodeWalker.GameFiles return; } } - } - [TC(typeof(EXP))] - public struct MrfStructStateInfoUnk2 - { - public uint Unk1 { get; } - public ushort Unk2 { get; } - public ushort Unk3 { get; } - public uint Unk4 { get; } - - public MrfStructStateInfoUnk2(DataReader r) + public override void Write(DataWriter w) { - Unk1 = r.ReadUInt32(); - Unk2 = r.ReadUInt16(); - Unk3 = r.ReadUInt16(); - Unk4 = r.ReadUInt32(); + foreach (var section in Sections) + section.Write(w); } } [TC(typeof(EXP))] - public struct MrfStructStateInfoEvent + public class MrfStructStateInfoVariable : MrfStructBase + { + public MetaHash VariableName { get; } + public ushort Unk2 { get; } + public ushort Unk3 { get; } + public uint Unk4 { get; } + + public MrfStructStateInfoVariable(DataReader r) + { + VariableName = new MetaHash(r.ReadUInt32()); + Unk2 = r.ReadUInt16(); + Unk3 = r.ReadUInt16(); + Unk4 = r.ReadUInt32(); + } + + public override void Write(DataWriter w) + { + w.Write(VariableName); + w.Write(Unk2); + w.Write(Unk3); + w.Write(Unk4); + } + } + + [TC(typeof(EXP))] + public class MrfStructStateInfoEvent : MrfStructBase { public ushort Unk1 { get; } public ushort Unk2 { get; } - public uint HashName { get; } + public MetaHash NameHash { get; } public MrfStructStateInfoEvent(DataReader r) { Unk1 = r.ReadUInt16(); Unk2 = r.ReadUInt16(); - HashName = r.ReadUInt32(); + NameHash = new MetaHash(r.ReadUInt32()); + } + + public override void Write(DataWriter w) + { + w.Write(Unk1); + w.Write(Unk2); + w.Write(NameHash); } } [TC(typeof(EXP))] - public struct MrfStructStateInfoUnk6 + public class MrfStructStateInfoUnk6 : MrfStructBase { - public uint Unk1 { get; } + public MetaHash Unk1 { get; } public ushort Unk2 { get; } public ushort Unk3 { get; } public uint Unk4 { get; } public MrfStructStateInfoUnk6(DataReader r) { - Unk1 = r.ReadUInt32(); + Unk1 = new MetaHash(r.ReadUInt32()); Unk2 = r.ReadUInt16(); Unk3 = r.ReadUInt16(); Unk4 = r.ReadUInt32(); } + + public override void Write(DataWriter w) + { + w.Write(Unk1); + w.Write(Unk2); + w.Write(Unk3); + w.Write(Unk4); + } } [TC(typeof(EXP))] - public struct MrfStructStateInfoSignalDataUnk3 + public class MrfStructStateInfoSignalDataUnk3 : MrfStructBase { public uint UnkValue { get; } public uint UnkDefault { get; } @@ -578,23 +801,40 @@ namespace CodeWalker.GameFiles UnkDefault = r.ReadUInt32(); UnkRange = r.ReadUInt64(); // 2 merged 32 bit values? } - } - [TC(typeof(EXP))] - public struct MrfStructStateMachineEntry - { - public MetaHash Value { get; } - public uint Unk { get; } - - public MrfStructStateMachineEntry(DataReader r) + public override void Write(DataWriter w) { - Value = new MetaHash(r.ReadUInt32()); - Unk = r.ReadUInt32(); + w.Write(UnkValue); + w.Write(UnkDefault); + w.Write(UnkRange); } } [TC(typeof(EXP))] - public struct MrfStructStateInfoSignalData + public class MrfStructStateMachineState : MrfStructBase + { + public MetaHash StateName { get; } + public uint UnkValue { get; } + + public MrfNodeStateInfo __StateInfoNode { get; set; } + + public MrfStructStateMachineState(DataReader r) + { + StateName = new MetaHash(r.ReadUInt32()); + UnkValue = r.ReadUInt32(); + } + + public override void Write(DataWriter w) + { + w.Write(StateName); + w.Write(UnkValue); + } + } + + // FIXME: most likely broken + + [TC(typeof(EXP))] + public class MrfStructStateInfoSignalData : MrfStructBase { public uint UnkType { get; } public uint NameHash { get; } @@ -607,12 +847,6 @@ namespace CodeWalker.GameFiles public MrfStructStateInfoSignalData(DataReader r) { - Unk1 = 0; - Unk2 = 0; - Unk3_Count = 0; - Unk3_Items = null; - Unk4 = 0; - UnkType = r.ReadUInt32(); NameHash = r.ReadUInt32(); @@ -622,33 +856,51 @@ namespace CodeWalker.GameFiles Unk1 = r.ReadUInt32(); Unk2 = r.ReadUInt32(); // Default value too? Unk3_Count = r.ReadUInt32(); - Unk3_Items = new MrfStructStateInfoSignalDataUnk3[Unk3_Count]; Unk4 = r.ReadUInt32(); + Unk3_Items = new MrfStructStateInfoSignalDataUnk3[Unk3_Count]; + for (int i = 0; i < Unk3_Count; i++) Unk3_Items[i] = new MrfStructStateInfoSignalDataUnk3(r); } + + 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); + } } [TC(typeof(EXP))] - public struct MrfStructStateInfoSignal + public class MrfStructStateInfoSignal : MrfStructBase { - public ushort Header1 { get; } - public ushort Header2 { get; } - public ushort Header3 { get; } - public ushort Header4 { get; } - public List Items { get; } + public ushort Unk1 { get; } + public ushort Unk2 { get; } + public ushort Unk3 { get; } + public ushort Unk4 { get; } + public MrfStructStateInfoSignalData[] Items { get; } public MrfStructStateInfoSignal(DataReader r) { - Items = new List(); - - Header1 = r.ReadUInt16(); - Header2 = r.ReadUInt16(); - Header3 = r.ReadUInt16(); - Header4 = r.ReadUInt16(); + Unk1 = r.ReadUInt16(); + Unk2 = r.ReadUInt16(); + Unk3 = r.ReadUInt16(); + Unk4 = r.ReadUInt16(); uint shouldContinue; + var itemsList = new List(); // FIXME: those loops looks weird do @@ -656,7 +908,7 @@ namespace CodeWalker.GameFiles while (true) { var data = new MrfStructStateInfoSignalData(r); - Items.Add(data); + itemsList.Add(data); shouldContinue = data.UnkType; @@ -665,24 +917,45 @@ namespace CodeWalker.GameFiles } } while (shouldContinue != 0); + + Items = itemsList.ToArray(); + } + + public override void Write(DataWriter w) + { + w.Write(Unk1); + w.Write(Unk2); + w.Write(Unk3); + w.Write(Unk4); + + foreach (var item in Items) + item.Write(w); } } [TC(typeof(EXP))] - public struct MrfStructNegativeInfoDataUnk7 + public class MrfStructNegativeInfoDataUnk7 : MrfStructBase { public uint Unk1 { get; set; } public uint Unk2 { get; set; } public uint Unk3 { get; set; } public uint Unk4 { get; set; } - public MrfStructNegativeInfoDataUnk7(DataReader r) + public MrfStructNegativeInfoDataUnk7() { Unk1 = 0; Unk2 = 0; Unk3 = 0; Unk4 = 0; } + + public override void Write(DataWriter w) + { + w.Write(Unk1); + w.Write(Unk2); + w.Write(Unk3); + w.Write(Unk4); + } } #endregion @@ -702,6 +975,12 @@ namespace CodeWalker.GameFiles NodeInfoType = (MrfNodeInfoType)r.ReadUInt16(); NodeInfoUnk = r.ReadUInt16(); } + + public virtual void Write(DataWriter w) + { + w.Write((ushort)NodeInfoType); + w.Write(NodeInfoUnk); + } } [TC(typeof(EXP))] @@ -716,35 +995,57 @@ namespace CodeWalker.GameFiles NameHash = new MetaHash(r.ReadUInt32()); Flags = r.ReadUInt32(); } + + public override void Write(DataWriter w) + { + base.Write(w); + w.Write(NameHash); + w.Write(Flags); + } } [TC(typeof(EXP))] - public class MrfHeaderStateMachine : MrfHeaderNodeInfo + public class MrfHeaderStateBase : MrfHeaderNodeInfo { - public MetaHash StateName { get; set; } // Used as an identifier for transitions + public MetaHash NameHash { get; set; } // Used as an identifier for transitions public uint Unk2 { get; set; } public uint Unk3 { get; set; } public byte Unk4 { get; set; } public byte Unk5 { get; set; } - public byte Unk6 { get; set; } + public byte StateCount { get; set; } public byte Unk7 { get; set; } public uint Unk8 { get; set; } public uint Unk9 { get; set; } public uint Unk10 { get; set; } - public MrfHeaderStateMachine(DataReader r) : base(r) + public MrfHeaderStateBase(DataReader r) : base(r) { - StateName = new MetaHash(r.ReadUInt32()); + NameHash = new MetaHash(r.ReadUInt32()); Unk2 = r.ReadUInt32(); Unk3 = r.ReadUInt32(); Unk4 = r.ReadByte(); Unk5 = r.ReadByte(); - Unk6 = r.ReadByte(); + StateCount = r.ReadByte(); Unk7 = r.ReadByte(); Unk8 = r.ReadUInt32(); Unk9 = r.ReadUInt32(); Unk10 = r.ReadUInt32(); } + + public override void Write(DataWriter w) + { + base.Write(w); + w.Write(NameHash); + w.Write(Unk2); + w.Write(Unk3); + w.Write(Unk4); + w.Write(Unk5); + w.Write(StateCount); + w.Write(Unk7); + w.Write(Unk8); + w.Write(Unk9); + w.Write(Unk10); + } } #endregion @@ -753,22 +1054,20 @@ namespace CodeWalker.GameFiles [TC(typeof(EXP))] public class MrfNodeStateMachineClassInfo : MrfNodeInfoBase { - public MrfHeaderStateMachine Header { get; set; } - public MrfStructStateMachineEntry[] Items { get; set; } + public MrfHeaderStateBase Header { get; set; } + public MrfStructStateMachineState[] States { get; set; } public MrfStructStateSection Header_Unk7_Data { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { - Header = new MrfHeaderStateMachine(r); - Items = new MrfStructStateMachineEntry[Header.Unk6]; + Header = new MrfHeaderStateBase(r); + States = new MrfStructStateMachineState[Header.StateCount]; - for (int i = 0; i < Header.Unk6; i++) - Items[i] = new MrfStructStateMachineEntry(r); + for (int i = 0; i < Header.StateCount; i++) + States[i] = new MrfStructStateMachineState(r); if (Header.Unk7 != 0) - { Header_Unk7_Data = new MrfStructStateSection(r, Header.Unk7); - } } public override long CalculateSize(DataReader r) @@ -810,6 +1109,17 @@ namespace CodeWalker.GameFiles return result; } + public override void Write(DataWriter w) + { + Header.Write(w); + + foreach (var item in States) + item.Write(w); + + if (Header.Unk7 != 0) + Header_Unk7_Data.Write(w); + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.StateMachineClass; @@ -827,19 +1137,19 @@ namespace CodeWalker.GameFiles [TC(typeof(EXP))] public class MrfNodeInlinedStateMachineInfo : MrfNodeInfoBase { - public MrfHeaderStateMachine Header { get; set; } + public MrfHeaderStateBase Header { get; set; } public uint Unk { get; set; } - public MrfStructStateMachineEntry[] Items { get; set; } + public MrfStructStateMachineState[] Items { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { - Header = new MrfHeaderStateMachine(r); - Items = new MrfStructStateMachineEntry[Header.Unk6]; + Header = new MrfHeaderStateBase(r); + Items = new MrfStructStateMachineState[Header.StateCount]; Unk = r.ReadUInt32(); - for (int i = 0; i < Header.Unk6; i++) - Items[i] = new MrfStructStateMachineEntry(r); + for (int i = 0; i < Header.StateCount; i++) + Items[i] = new MrfStructStateMachineState(r); } public override long CalculateSize(DataReader r) @@ -853,13 +1163,22 @@ namespace CodeWalker.GameFiles return 8 * length + 36; } + public override void Write(DataWriter w) + { + Header.Write(w); + w.Write(Unk); + + foreach (var item in Items) + item.Write(w); + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.InlinedStateMachine; } } - // rage__mvNode* (4) + // rage__mvNode* (4) not used in final game [TC(typeof(EXP))] public class MrfNodeUnk4Info : MrfNodeInfoBase { @@ -873,7 +1192,7 @@ namespace CodeWalker.GameFiles public uint Unk6 { get; set; } public uint Unk7 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); @@ -949,6 +1268,39 @@ namespace CodeWalker.GameFiles return result; } + public override void Write(DataWriter w) + { + Header.Write(w); + + switch (Header.Flags & 3) + { + case 1: + { + w.Write(Unk2_Count); + w.Write(Unk2_Bytes); + break; + } + case 2: + w.Write(Unk1); + break; + } + + if (((Header.Flags >> 2) & 3) != 0) + w.Write(Unk3); + + if (((Header.Flags >> 4) & 3) != 0) + w.Write(Unk4); + + if (((Header.Flags >> 6) & 3) != 0) + w.Write(Unk5); + + if (((Header.Flags >> 8) & 3) != 0) + w.Write(Unk6); + + if (((Header.Flags >> 10) & 3) != 0) + w.Write(Unk7); + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.Unk4; @@ -993,7 +1345,7 @@ namespace CodeWalker.GameFiles public uint Unk2 { get; set; } public uint Unk3 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNodeInfo(r); @@ -1025,6 +1377,20 @@ namespace CodeWalker.GameFiles return result; } + public override void Write(DataWriter w) + { + Header.Write(w); + + w.Write(Unk1); + w.Write(Flags); + + if ((Flags & 3) != 0) + w.Write(Unk2); + + if ((Flags & 0x30) != 0) + w.Write(Unk3); + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.Frame; @@ -1050,16 +1416,16 @@ namespace CodeWalker.GameFiles public class MrfNodeClipInfo : MrfNodeInfoBase { public MrfHeaderNameFlag Header { get; set; } - public uint Unk1 { get; set; } + public MetaHash VariableName { get; set; } public uint Unk2 { get; set; } public MetaHash DictName { get; set; } public MetaHash ClipName { get; set; } - public float Unk5 { get; set; } - public float Unk6 { get; set; } + public MetaHash Unk5 { get; set; } + public MetaHash Unk6 { get; set; } public uint Unk7 { get; set; } public uint Unk8 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); @@ -1076,15 +1442,15 @@ namespace CodeWalker.GameFiles break; } case 2: - Unk1 = r.ReadUInt32(); + VariableName = new MetaHash(r.ReadUInt32()); break; } if (((Header.Flags >> 2) & 3) != 0) - Unk5 = r.ReadSingle(); + Unk5 = new MetaHash(r.ReadUInt32()); if (((Header.Flags >> 4) & 3) != 0) - Unk6 = r.ReadSingle(); + Unk6 = new MetaHash(r.ReadUInt32()); if (((Header.Flags >> 6) & 3) != 0) Unk7 = r.ReadUInt32(); @@ -1133,6 +1499,40 @@ namespace CodeWalker.GameFiles return result; } + public override void Write(DataWriter w) + { + Header.Write(w); + + switch (Header.Flags & 3) + { + case 1: + { + w.Write(Unk2); + w.Write(DictName); + + if (Unk2 != 3) + w.Write(ClipName); + + break; + } + case 2: + w.Write(VariableName); + break; + } + + if (((Header.Flags >> 2) & 3) != 0) + w.Write(Unk5); + + if (((Header.Flags >> 4) & 3) != 0) + w.Write(Unk6); + + if (((Header.Flags >> 6) & 3) != 0) + w.Write(Unk7); + + if (((Header.Flags >> 6) & 3) != 0) + w.Write(Unk8); + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.Clip; @@ -1152,7 +1552,7 @@ namespace CodeWalker.GameFiles public uint Unk5 { get; set; } public uint[] Unk6 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); @@ -1234,6 +1634,36 @@ namespace CodeWalker.GameFiles return result; } + public override void Write(DataWriter w) + { + Header.Write(w); + + if ((Header.Flags & 3) == 1) + { + w.Write(Unk1_Count); + w.Write(Unk1_Bytes); + } + else if ((Header.Flags & 3) == 2) + w.Write(Unk2); + + if (((Header.Flags >> 2) & 3) != 0) + w.Write(Unk3); + + if (((Header.Flags >> 4) & 3) != 0) + w.Write(Unk4); + + if ((Header.Flags >> 6) != 0) + w.Write(Unk5); + + var unk6Count = (Header.Flags >> 10) & 0xF; + + if (unk6Count > 0) + { + foreach (var value in Unk6) + w.Write(value); + } + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.Unk17; @@ -1248,7 +1678,7 @@ namespace CodeWalker.GameFiles public uint Unk1 { get; set; } public uint Unk2 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); Unk1 = r.ReadUInt32(); @@ -1268,6 +1698,15 @@ namespace CodeWalker.GameFiles return ((headerFlags & 3) != 0) ? 20 : 16; } + public override void Write(DataWriter w) + { + Header.Write(w); + w.Write(Unk1); + + if ((Header.Flags & 3) != 0) + w.Write(Unk2); + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.Unk18; @@ -1283,11 +1722,11 @@ namespace CodeWalker.GameFiles public uint Unk2 { get; set; } public MetaHash ExpressionDict { get; set; } public MetaHash ExpressionName { get; set; } - public uint Unk5 { get; set; } - public float Unk6 { get; set; } + public MetaHash VariableName { get; set; } + public uint Unk6 { get; set; } public uint[][] Unk7 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); Unk1 = r.ReadUInt32(); @@ -1299,31 +1738,31 @@ namespace CodeWalker.GameFiles ExpressionName = new MetaHash(r.ReadUInt32()); break; case 2: - Unk5 = r.ReadUInt32(); + VariableName = new MetaHash(r.ReadUInt32()); break; } if (((Header.Flags >> 2) & 3) != 0) - Unk6 = r.ReadSingle(); + Unk6 = r.ReadUInt32(); var unk7Count = (Header.Flags >> 28); if (unk7Count == 0) return; - var unkOffset = (Header.Flags >> 4) & 0xFFFFFF; + var unkHeaderFlag = (Header.Flags >> 4) & 0xFFFFFF; var offset = 0; Unk7 = new uint[unk7Count][]; for (int i = 0; i < unk7Count; i++) { - var unkFlag = (unkOffset >> offset) & 3; + var unkTypeFlag = (unkHeaderFlag >> offset) & 3; var value1 = r.ReadUInt32(); uint value2 = 0; - if (unkFlag == 2 || unkFlag == 1) + if (unkTypeFlag == 2 || unkTypeFlag == 1) value2 = r.ReadUInt32(); Unk7[i] = new uint[2] { value1, value2 }; @@ -1376,6 +1815,46 @@ namespace CodeWalker.GameFiles return result; } + public override void Write(DataWriter w) + { + Header.Write(w); + w.Write(Unk1); + + switch (Header.Flags & 3) + { + case 1: + w.Write(ExpressionDict); + w.Write(ExpressionName); + break; + case 2: + w.Write(VariableName); + break; + } + + if (((Header.Flags >> 2) & 3) != 0) + w.Write(Unk6); + + var unk7Count = (Header.Flags >> 28); + + if (unk7Count == 0) + return; + + var unkHeaderFlag = (Header.Flags >> 4) & 0xFFFFFF; + var offset = 0; + + foreach (var item in Unk7) + { + var unkTypeFlag = (unkHeaderFlag >> offset) & 3; + + w.Write(item[0]); + + if (unkTypeFlag == 2 || unkTypeFlag == 1) + w.Write(item[1]); + + offset += 2; + } + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.Expression; @@ -1391,7 +1870,7 @@ namespace CodeWalker.GameFiles public uint Unk2 { get; set; } public uint Unk3 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); Unk1 = r.ReadUInt32(); @@ -1421,6 +1900,18 @@ namespace CodeWalker.GameFiles return result; } + public override void Write(DataWriter w) + { + Header.Write(w); + w.Write(Unk1); + + if ((Header.Flags & 3) != 0) + w.Write(Unk2); + + if ((Header.Flags & 0x30) != 0) + w.Write(Unk3); + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.Unk20; @@ -1457,29 +1948,27 @@ namespace CodeWalker.GameFiles public uint Unk2 { get; set; } public uint Unk3 { get; set; } public MetaHash Unk4 { get; set; } - public uint Unk5 { get; set; } + public MetaHash Unk5 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); + Unk1 = r.ReadUInt32(); Unk2 = r.ReadUInt32(); - Unk3 = r.ReadUInt32(); if ((Header.Flags & 0x180000) == 0x80000) Unk3 = r.ReadUInt32(); - var unkTypeFlag = (Header.Flags & 3); - - // FIXME: optimized switch-case? - if (unkTypeFlag != 2) + switch (Header.Flags & 3) { - if (unkTypeFlag != 1) - return; - - Unk4 = new MetaHash(r.ReadUInt32()); + case 1: + Unk4 = new MetaHash(r.ReadUInt32()); + Unk5 = new MetaHash(r.ReadUInt32()); + break; + case 2: + Unk5 = new MetaHash(r.ReadUInt32()); + break; } - - Unk5 = r.ReadUInt32(); } public override long CalculateSize(DataReader r) @@ -1507,6 +1996,27 @@ namespace CodeWalker.GameFiles return result; } + public override void Write(DataWriter w) + { + Header.Write(w); + w.Write(Unk1); + w.Write(Unk2); + + if ((Header.Flags & 0x180000) == 0x80000) + w.Write(Unk3); + + switch (Header.Flags & 3) + { + case 1: + w.Write(Unk4); + w.Write(Unk5); + break; + case 2: + w.Write(Unk5); + break; + } + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.Unk24; @@ -1520,7 +2030,7 @@ namespace CodeWalker.GameFiles public MrfHeaderNameFlag Header { get; set; } public uint Unk1 { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); @@ -1539,6 +2049,14 @@ namespace CodeWalker.GameFiles return ((headerFlag & 3) != 0) ? 16 : 12; } + public override void Write(DataWriter w) + { + Header.Write(w); + + if ((Header.Flags & 3) != 0) + w.Write(Unk1); + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.Unk25; @@ -1556,9 +2074,9 @@ namespace CodeWalker.GameFiles [TC(typeof(EXP))] public class MrfNodeStateInfo : MrfNodeInfoBase { - public MrfHeaderStateMachine Header { get; set; } + public MrfHeaderStateBase Header { get; set; } public uint Unk1 { get; set; } - public uint Unk2_Count { get; set; } + public uint VariablesCount { get; set; } public uint Unk3 { get; set; } public uint EventsCount { get; set; } public uint Unk5 { get; set; } @@ -1567,17 +2085,17 @@ namespace CodeWalker.GameFiles public uint SignalsCount { get; set; } public MrfStructStateSection Header_Unk7_Data { get; set; } - public MrfStructStateInfoUnk2[] Unk2_Items { get; set; } - public MrfStructStateInfoEvent[] EventsItems { get; set; } + public MrfStructStateInfoVariable[] Variables { get; set; } + public MrfStructStateInfoEvent[] Events { get; set; } public MrfStructStateInfoUnk6[] Unk6_Items { get; set; } - public MrfStructStateInfoSignal[] SignalsItems { get; set; } + public MrfStructStateInfoSignal[] Signals { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { - Header = new MrfHeaderStateMachine(r); + Header = new MrfHeaderStateBase(r); Unk1 = r.ReadUInt32(); - Unk2_Count = r.ReadUInt32(); + VariablesCount = r.ReadUInt32(); Unk3 = r.ReadUInt32(); EventsCount = r.ReadUInt32(); Unk5 = r.ReadUInt32(); @@ -1585,28 +2103,25 @@ namespace CodeWalker.GameFiles Unk7 = r.ReadUInt32(); SignalsCount = r.ReadUInt32(); - Unk2_Items = new MrfStructStateInfoUnk2[Unk2_Count]; - EventsItems = new MrfStructStateInfoEvent[EventsCount]; + Variables = new MrfStructStateInfoVariable[VariablesCount]; + Events = new MrfStructStateInfoEvent[EventsCount]; Unk6_Items = new MrfStructStateInfoUnk6[Unk6_Count]; - SignalsItems = new MrfStructStateInfoSignal[SignalsCount]; + Signals = new MrfStructStateInfoSignal[SignalsCount]; if (Header.Unk7 != 0) Header_Unk7_Data = new MrfStructStateSection(r, Header.Unk7); - for (int i = 0; i < Unk2_Count; i++) - Unk2_Items[i] = new MrfStructStateInfoUnk2(r); + for (int i = 0; i < VariablesCount; i++) + Variables[i] = new MrfStructStateInfoVariable(r); for (int i = 0; i < EventsCount; i++) - EventsItems[i] = new MrfStructStateInfoEvent(r); + Events[i] = new MrfStructStateInfoEvent(r); for (int i = 0; i < Unk6_Count; i++) Unk6_Items[i] = new MrfStructStateInfoUnk6(r); - if (SignalsCount != 0) - { - for (int i = 0; i < SignalsCount; i++) - SignalsItems[i] = new MrfStructStateInfoSignal(r); - } + for (int i = 0; i < SignalsCount; i++) + Signals[i] = new MrfStructStateInfoSignal(r); } public override long CalculateSize(DataReader r) @@ -1711,6 +2226,35 @@ namespace CodeWalker.GameFiles return result; } + public override void Write(DataWriter w) + { + Header.Write(w); + + w.Write(Unk1); + w.Write(VariablesCount); + w.Write(Unk3); + w.Write(EventsCount); + w.Write(Unk5); + w.Write(Unk6_Count); + w.Write(Unk7); + w.Write(SignalsCount); + + if (Header.Unk7 != 0) + Header_Unk7_Data.Write(w); + + foreach (var item in Variables) + item.Write(w); + + foreach (var item in Events) + item.Write(w); + + foreach (var item in Unk6_Items) + item.Write(w); + + foreach (var item in Signals) + item.Write(w); + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.State; @@ -1753,7 +2297,7 @@ namespace CodeWalker.GameFiles public uint Unk5_Count { get; set; } public uint[][] Unk5_Items { get; set; } - public override void Parse(DataReader r) + public override void Read(DataReader r) { Header = new MrfHeaderNameFlag(r); Unk1 = r.ReadUInt32(); @@ -1826,6 +2370,53 @@ namespace CodeWalker.GameFiles return 12 * count1 + 8 * (count2 + count3 + count4 + 4); } + public override void Write(DataWriter w) + { + Header.Write(w); + w.Write(Unk1); + w.Write(Unk2_Count); + w.Write(Unk3_Count); + w.Write(Unk4_Count); + w.Write(Unk5_Count); + + if (Unk3_Count > 0) + { + foreach (var entry in Unk3_Items) + { + w.Write(entry[0]); + w.Write(entry[1]); + } + } + + if (Unk4_Count > 0) + { + foreach (var entry in Unk4_Items) + { + w.Write(entry[0]); + w.Write(entry[1]); + } + } + + if (Unk5_Count > 0) + { + foreach (var entry in Unk5_Items) + { + w.Write(entry[0]); + w.Write(entry[1]); + } + } + + if (Unk2_Count > 0) + { + foreach (var entry in Unk2_Items) + { + w.Write(entry[0]); + w.Write(entry[1]); + w.Write(entry[2]); + } + } + } + public override MrfNodeInfoType GetInfoType() { return MrfNodeInfoType.Unk31; @@ -1883,13 +2474,72 @@ namespace CodeWalker.GameFiles public byte[] Save() { - // TODO - var buf = new byte[4]; + 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 (Nodes != null) + { + foreach (var node in Nodes) + node.Write(w); + } + + for (int i = 0; i < FlagsCount; i++) + w.Write(FlagsItems[i]); } private void Read(DataReader r) @@ -1905,6 +2555,7 @@ namespace CodeWalker.GameFiles 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) { @@ -1953,7 +2604,7 @@ namespace CodeWalker.GameFiles throw new Exception($"Parsed mrf node type ({nodeHandler.GetInfoType()}) is not equals expected type ({nodeType})"); // Finally we can parse it. - nodeHandler.Parse(r); + nodeHandler.Read(r); if (expectedPos != r.Position) throw new Exception($"Position of reader ({r.Position}) is not equals expected position ({expectedPos})"); @@ -1993,9 +2644,6 @@ namespace CodeWalker.GameFiles private MrfNodeInfoBase GetNextMrfNodeHandler(MrfNodeInfoType infoType) { - if (infoType == MrfNodeInfoType.None) - throw new Exception($"Attempt to get a handler of none mrf node"); - switch (infoType) { case MrfNodeInfoType.StateMachineClass: @@ -2054,7 +2702,7 @@ namespace CodeWalker.GameFiles return new MrfNodeUnk31Info(); } - throw new Exception($"A handler for ({infoType}) mrf node type is not implemented"); + throw new Exception($"A handler for ({infoType}) mrf node type is not valid"); } } } From ca8eb31ae21be7b0a657afba442710585f47e5a3 Mon Sep 17 00:00:00 2001 From: Disquse Date: Sat, 6 Nov 2021 07:28:20 +0300 Subject: [PATCH 5/5] Resolve MRF saving issues Should now be able to save any MRF with no difference comparing to original files Remove debug dield from MrfFile --- .../GameFiles/FileTypes/MrfFile.cs | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs index b19b73c..93c1abc 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs @@ -579,8 +579,8 @@ namespace CodeWalker.GameFiles w.Write(Unk6); // FIXME: might be broken if changed without flags, see "iterations" - foreach (var item in Items) - item.Write(w); + for (int i = Items.Length - 1; i >= 0; i--) + Items[i].Write(w); // FIXME: might be broken if changed without flags uint flags = Unk1 & 0xFFFBFFFF; @@ -699,20 +699,14 @@ namespace CodeWalker.GameFiles { Sections = new MrfStructStateMainSection[(int)count]; - // FIXME: for-loop? - while (true) - { - Sections[(int)count - 1] = new MrfStructStateMainSection(r); - - if (--count == 0) - return; - } + for (int i = (int)count - 1; i >= 0; i--) + Sections[i] = new MrfStructStateMainSection(r); } public override void Write(DataWriter w) { - foreach (var section in Sections) - section.Write(w); + for (int i = Sections.Length - 1; i >= 0; i--) + Sections[i].Write(w); } } @@ -816,8 +810,6 @@ namespace CodeWalker.GameFiles public MetaHash StateName { get; } public uint UnkValue { get; } - public MrfNodeStateInfo __StateInfoNode { get; set; } - public MrfStructStateMachineState(DataReader r) { StateName = new MetaHash(r.ReadUInt32()); @@ -1113,8 +1105,8 @@ namespace CodeWalker.GameFiles { Header.Write(w); - foreach (var item in States) - item.Write(w); + foreach (var state in States) + state.Write(w); if (Header.Unk7 != 0) Header_Unk7_Data.Write(w);