diff --git a/Heroes.StormReplayParser/MpqFiles/ReplayAttributeEvents.cs b/Heroes.StormReplayParser/MpqFiles/ReplayAttributeEvents.cs index cad9e32..a800b24 100644 --- a/Heroes.StormReplayParser/MpqFiles/ReplayAttributeEvents.cs +++ b/Heroes.StormReplayParser/MpqFiles/ReplayAttributeEvents.cs @@ -38,7 +38,7 @@ public static void Parse(StormReplay replay, ReadOnlySpan source) switch (attribute) { - case ReplayAttributeEventType.PlayerTypeAttribute: + case ReplayAttributeEventType.PlayerType: { if (upperValue.SequenceEqual("COMP") || upperValue.SequenceEqual("HUMN")) { @@ -57,13 +57,13 @@ public static void Parse(StormReplay replay, ReadOnlySpan source) break; } - case ReplayAttributeEventType.TeamSizeAttribute: + case ReplayAttributeEventType.TeamSize: { replay.TeamSize = value.Trim('\0').ToString(); break; } - case ReplayAttributeEventType.DifficultyLevelAttribute: + case ReplayAttributeEventType.DifficultyLevel: { StormPlayer player = replay.PlayersWithOpenSlots[playerId - 1]; @@ -80,7 +80,7 @@ public static void Parse(StormReplay replay, ReadOnlySpan source) break; } - case ReplayAttributeEventType.GameSpeedAttribute: + case ReplayAttributeEventType.GameSpeed: { replay.GameSpeed = upperValue switch { @@ -95,7 +95,7 @@ Span _ when upperValue.SequenceEqual("FASR") => StormGameSpeed.Faster, break; } - case ReplayAttributeEventType.GameModeAttribute: + case ReplayAttributeEventType.GameMode: { switch (upperValue) { @@ -125,7 +125,7 @@ Span _ when upperValue.SequenceEqual("FASR") => StormGameSpeed.Faster, break; } - case ReplayAttributeEventType.SkinAndSkinTintAttributeId: + case ReplayAttributeEventType.SkinAndSkinTint: { StormPlayer player = replay.PlayersWithOpenSlots[playerId - 1]; @@ -137,7 +137,7 @@ Span _ when upperValue.SequenceEqual("FASR") => StormGameSpeed.Faster, break; } - case ReplayAttributeEventType.MountAndMountTintAttributeId: + case ReplayAttributeEventType.MountAndMountTint: { StormPlayer player = replay.PlayersWithOpenSlots[playerId - 1]; @@ -146,7 +146,7 @@ Span _ when upperValue.SequenceEqual("FASR") => StormGameSpeed.Faster, break; } - case ReplayAttributeEventType.BannerAttributeId: + case ReplayAttributeEventType.Banner: { StormPlayer player = replay.PlayersWithOpenSlots[playerId - 1]; @@ -155,7 +155,7 @@ Span _ when upperValue.SequenceEqual("FASR") => StormGameSpeed.Faster, break; } - case ReplayAttributeEventType.SprayAttributeId: + case ReplayAttributeEventType.Spray: { StormPlayer player = replay.PlayersWithOpenSlots[playerId - 1]; @@ -164,7 +164,7 @@ Span _ when upperValue.SequenceEqual("FASR") => StormGameSpeed.Faster, break; } - case ReplayAttributeEventType.VoiceLineAttributeId: + case ReplayAttributeEventType.VoiceLine: { StormPlayer player = replay.PlayersWithOpenSlots[playerId - 1]; @@ -173,7 +173,7 @@ Span _ when upperValue.SequenceEqual("FASR") => StormGameSpeed.Faster, break; } - case ReplayAttributeEventType.AnnouncerAttributeId: + case ReplayAttributeEventType.Announcer: { StormPlayer player = replay.PlayersWithOpenSlots[playerId - 1]; @@ -250,6 +250,10 @@ Span _ when upperValue.SequenceEqual("FASR") => StormGameSpeed.Faster, } break; + + default: + string ds = value.ToString(); + break; } } } diff --git a/Heroes.StormReplayParser/MpqFiles/ReplayServerBattlelobby.cs b/Heroes.StormReplayParser/MpqFiles/ReplayServerBattlelobby.cs index bb11436..2300243 100644 --- a/Heroes.StormReplayParser/MpqFiles/ReplayServerBattlelobby.cs +++ b/Heroes.StormReplayParser/MpqFiles/ReplayServerBattlelobby.cs @@ -1,15 +1,17 @@ -namespace Heroes.StormReplayParser.MpqFiles; +using System; + +namespace Heroes.StormReplayParser.MpqFiles; internal static class ReplayServerBattlelobby { private const string _exceptionHeader = "battlelobby"; - private const string _emptyAttributeId = "\0\0\0\0"; - private const string _randomAttributeId = "Rand"; public static string FileName { get; } = "replay.server.battlelobby"; public static void Parse(StormReplay replay, ReadOnlySpan source) { + List stormBattleLobbyAttributes = new(); + BitReader bitReader = new(source, EndianType.BigEndian); uint dependenciesLength = bitReader.ReadBits(6); @@ -29,1452 +31,876 @@ public static void Parse(StormReplay replay, ReadOnlySpan source) bitReader.ReadAlignedBytes(36); } - //bitReader.ReadUnalignedBytes(9); - - bitReader.ReadBits(9); - //bitReader.ReadUnalignedBytes(1); - //bitReader.ReadBits(1); - - int sl = bitReader.ReadInt32Unaligned(); - // bitReader.ReadBits(1); + /* Attribute section */ - bitReader.ReadBits(32); - uint attributeLength = bitReader.ReadBits(12); + uint count = bitReader.ReadBits(9); // total number of attributes - for (int i = 0; i < attributeLength; i++) + for (int i = 0; i < count; i++) { - bitReader.ReadStringFromUnalignedBytes(4); // Part, Watch - Participant, Watcher - bitReader.ReadBits(29); - } + bitReader.ReadInt32Unaligned(); // namespace - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + StormBattleLobbyAttribute stormBattleLobbyAttribute = new((ReplayAttributeEventType)bitReader.ReadInt32Unaligned()); - attributeLength = bitReader.ReadBits(12); + int attributeCount = (int)bitReader.ReadBits(12); - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Open, Humn, Comp - } + for (int j = 0; j < attributeCount; j++) + { + StormBattleLobbyAttributeValue stormBattleLobbyAttributeValue = new() + { + Value = bitReader.ReadStringFromUnalignedBytes(4), // attribute value + }; - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); + if (bitReader.ReadBoolean()) // first + { + if (bitReader.ReadBoolean()) // f32 + { + bitReader.ReadBits(32); + } - // player typer attributes - closed, human, etc... - List playerTypeAttributes = new(); + bitReader.ReadBits(6); // NewField6 + stormBattleLobbyAttributeValue.Id = bitReader.ReadInt16Unaligned(); // word - id for s2ml xml + } - uint playerTypeAttributeLength = bitReader.ReadBits(12); - for (int i = 0; i < playerTypeAttributeLength; i++) - { - playerTypeAttributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + if (bitReader.ReadBoolean()) // second + { + if (bitReader.ReadBoolean()) // f32 + { + bitReader.ReadBits(32); + } - bitReader.ReadUnalignedBytes(44); - bitReader.ReadBits(4); + bitReader.ReadBits(6); // NewField6 + bitReader.ReadBits(16); // word - id for s2ml xml + } - // hero roles - tank, healer, ranged assassin, etc... - List heroRoleAttributes = new(); + if (bitReader.ReadBoolean()) // f70 + { + bitReader.ReadBits(6); // NewField6 + bitReader.ReadBits(32); // NewField32 + bitReader.ReadBits(16); // NewField16 + bitReader.ReadBits(16); // NewField16 + } - uint heroRoleAttributeLength = bitReader.ReadBits(12); - for (int i = 0; i < heroRoleAttributeLength; i++) - { - heroRoleAttributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + bitReader.ReadBitArray(1); // f1 + bitReader.ReadBitArray(1); // f1 + bitReader.ReadBitArray(1); // f1 - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + stormBattleLobbyAttribute.AttributeValues.Add(stormBattleLobbyAttributeValue); + } - attributeLength = bitReader.ReadBits(12); + // EnabledWithAttribs + if (bitReader.ReadBoolean()) + { + bitReader.ReadBitArray(25); // NewField25 - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Humn - } + uint typeCount = bitReader.ReadBits(5); - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); + for (int x = 0; x < typeCount; x++) + { + bitReader.ReadBitArray(5); - // team attributes section 1 - Team 1, Team 2, Team 3, repeat, etc... - List teamAttributesS1 = new(); + bitReader.ReadInt32Unaligned(); // namespace - uint teamAttributesS1Length = bitReader.ReadBits(12); - for (int i = 0; i < teamAttributesS1Length; i++) - { - teamAttributesS1.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + _ = (ReplayAttributeEventType)bitReader.ReadInt32Unaligned(); // should be still the same - bitReader.ReadUnalignedBytes(26); - bitReader.ReadBits(5); + attributeCount = (int)bitReader.ReadBits(12); - attributeLength = bitReader.ReadBits(12); + for (int j = 0; j < attributeCount; j++) + { + StormBattleLobbyEnabledAttributeValue stormBattleLobbyEnabledAttributeValue = new(); - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant - } + stormBattleLobbyEnabledAttributeValue.Value = bitReader.ReadStringFromUnalignedBytes(4); // attribute value - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); + stormBattleLobbyAttribute.EnabledValueAttributes.Add(stormBattleLobbyEnabledAttributeValue); + } + } + } - // 0 -> 255 attributes - List zeroTo255Attributes = new(); + // _end + bitReader.ReadBitArray(5); // NewField5 + bitReader.ReadBitArray(5); // NewField5 + bitReader.ReadBitArray(5); // NewField5 - uint zeroTo255AttributeLength = bitReader.ReadBits(12); - for (int i = 0; i < zeroTo255AttributeLength; i++) - { - zeroTo255Attributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + bitReader.ReadBits(32); // newfield - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + uint a170 = bitReader.ReadBits(8); + for (int y = 0; y < a170; y++) + { + bitReader.ReadBitArray(5); // b5 + bitReader.ReadBitArray(165); // b165 + bitReader.ReadBitArray(15); // b15 + } - attributeLength = bitReader.ReadBits(12); + // NewField + bitReader.ReadBitArray(11); // b11 + bitReader.ReadBits(1); // f1 + bitReader.ReadBits(9); // b9 - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Humn, Comp + stormBattleLobbyAttributes.Add(stormBattleLobbyAttribute); } - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); - - // hero attribute section 1 - List heroAttributesS1 = new(); - - uint heroAttributeS1Length = bitReader.ReadBits(12); - for (int i = 0; i < heroAttributeS1Length; i++) - { - heroAttributesS1.Add(bitReader.ReadStringFromBits(32)); - bitReader.ReadBits(29); - } + /* Player attribute selections */ - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + bitReader.ReadBitArray(30); // NewField + bitReader.ReadBits(16); // NewField + bitReader.ReadBitArray(81); // NewField - attributeLength = bitReader.ReadBits(12); + count = bitReader.ReadBits(9); // attribute count should be the same as previous - for (int i = 0; i < attributeLength; i++) + for (int i = 0; i < count; i++) { - bitReader.ReadStringFromUnalignedBytes(4); // drft - Draft - } - - bitReader.ReadUnalignedBytes(17); - bitReader.ReadBits(4); + bitReader.ReadInt32Unaligned(); // namespace - // hero attribute section 2 - List heroAttributesS2 = new(); + _ = (ReplayAttributeEventType)bitReader.ReadInt32Unaligned(); - uint heroAttributeS2Length = bitReader.ReadBits(12); - for (int i = 0; i < heroAttributeS2Length; i++) - { - heroAttributesS2.Add(bitReader.ReadStringFromBits(32)); - bitReader.ReadBits(29); + PlayerAttributeChoice(ref bitReader); } - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + /* Locales section */ - attributeLength = bitReader.ReadBits(12); + uint s2mvCacheHandlesLength = bitReader.ReadBits(6); - for (int i = 0; i < attributeLength; i++) + for (int i = 0; i < s2mvCacheHandlesLength; i++) { - bitReader.ReadStringFromUnalignedBytes(4); // drft - Draft - } - - bitReader.ReadUnalignedBytes(17); - bitReader.ReadBits(4); - - // team attributes section 2 - Team 1 -> Team 10, repeat, etc... - List teamAttributesS2 = new(); + if (bitReader.ReadStringFromAlignedBytes(4) != "s2mv") + throw new StormParseException($"{_exceptionHeader}: s2mv"); - uint teamAttributesS2Length = bitReader.ReadBits(12); - for (int i = 0; i < teamAttributesS2Length; i++) - { - teamAttributesS2.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); + bitReader.ReadAlignedBytes(36); } - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + uint localesLength = bitReader.ReadBits(5); // number of locales - attributeLength = bitReader.ReadBits(12); - - for (int i = 0; i < attributeLength; i++) + for (int i = 0; i < localesLength; i++) { - bitReader.ReadStringFromUnalignedBytes(4); // CuTa - Custom Teams Archon ??? - } + bitReader.ReadStringFromUnalignedBytes(4); - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); + uint s2mlSize = bitReader.ReadBits(6); // number of s2ml in locale - attributeLength = bitReader.ReadBits(12); + for (int j = 0; j < s2mlSize; j++) + { + if (bitReader.ReadStringFromAlignedBytes(4) != "s2ml") + throw new StormParseException($"{_exceptionHeader}: s2ml"); - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant + bitReader.ReadAlignedBytes(36); + } } - bitReader.ReadUnalignedBytes(17); - bitReader.ReadUnalignedBytes(22); - bitReader.ReadBits(13); + bitReader.ReadBitArray(128); // fil - // hero skin attributes - List heroSkinAttributes = new(); + uint usedModsLength = bitReader.ReadBits(5); - uint heroSkinAttributeLength = bitReader.ReadBits(12); - for (int i = 0; i < heroSkinAttributeLength; i++) + for (int i = 0; i < usedModsLength; i++) { - heroSkinAttributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - if (bitReader.ReadBoolean()) - bitReader.ReadBits(28); - else - bitReader.ReadBits(5); - } + // ModId + bitReader.ReadBits(32); // id + bitReader.ReadBits(32); // ofs - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + // Priority + bitReader.ReadBits(4); + } - attributeLength = bitReader.ReadBits(12); + uint mods2Length = bitReader.ReadBits(7); - for (int i = 0; i < attributeLength; i++) + for (int i = 0; i < mods2Length; i++) { - bitReader.ReadStringFromUnalignedBytes(4); // Dflt - Default - } + // ModId + bitReader.ReadBits(32); // id + bitReader.ReadBits(32); // ofs - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); + // n16 + bitReader.ReadBits(16); // n16 + bitReader.ReadBits(16); // n16 + bitReader.ReadBits(24); // n24 + bitReader.ReadBits(7); // n7 + bitReader.ReadBits(15); // n15 - attributeLength = bitReader.ReadBits(12); + bitReader.ReadBitArray(2); // f2 + bitReader.ReadBitArray(74); // fil - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Humn, Comp - } + uint s2mvCacheHandlesLength2 = bitReader.ReadBits(6); - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); + for (int j = 0; j < s2mvCacheHandlesLength2; j++) + { + if (bitReader.ReadStringFromAlignedBytes(4) != "s2mv") + throw new StormParseException($"{_exceptionHeader}: s2mv"); - attributeLength = bitReader.ReadBits(12); + bitReader.ReadAlignedBytes(36); + } - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant - } + bitReader.ReadBitArray(1); // flag - bitReader.ReadUnalignedBytes(101); - bitReader.ReadBits(7); + // string toon + bitReader.ReadBits(8); // region + bitReader.ReadStringFromUnalignedBytes(4); // programId + bitReader.ReadBits(32); // realm - // banner attributes - List bannerAttributes = new(); + int idLength = (int)bitReader.ReadBits(7) + 2; + bitReader.ReadStringFromAlignedBytes(idLength); // m_globalName - uint bannerAttributeLength = bitReader.ReadBits(12); - for (int i = 0; i < bannerAttributeLength; i++) - { - string bannerAttribute = bitReader.ReadStringFromUnalignedBytes(4); + // toon + bitReader.ReadBits(8); // region + bitReader.ReadStringFromUnalignedBytes(4); // programId + bitReader.ReadBits(32); // realm + bitReader.ReadLongBits(64); // id - bannerAttributes.Add(bannerAttribute); + // trttt - if (bannerAttribute == "TST2") + // b32 + if (bitReader.ReadBoolean()) { - bitReader.ReadBits(6); + bitReader.ReadBitArray(32); } - else + + // b23 + if (bitReader.ReadBoolean()) { - if (bitReader.ReadBoolean()) - bitReader.ReadBits(28); - else - bitReader.ReadBits(5); + bitReader.ReadBitArray(23); } - } - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + bitReader.ReadBitArray(1); // b1 - attributeLength = bitReader.ReadBits(12); - - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Dflt - Default - } - - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); - - attributeLength = bitReader.ReadBits(12); - - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Humn, Comp - } + // b32 + if (bitReader.ReadBoolean()) + { + bitReader.ReadBitArray(32); + } - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); + bitReader.ReadBitArray(3); // b3 - attributeLength = bitReader.ReadBits(12); + bitReader.ReadBits(30); // b30 - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant - } + bitReader.ReadBitArray(3); // b3 - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); + // filler + bitReader.ReadStringFromUnalignedBytes(2); // word + bitReader.ReadBitArray(8); // b8 + bitReader.ReadBitArray(8); // b8 + bitReader.ReadBitArray(34); // b34 - // team attributes section 3 - Team 1 -> Team 9, repeat, etc... - List teamAttributesS3 = new(); + if (bitReader.ReadBoolean()) + { + bitReader.ReadBitArray(54); // b54 + } - uint teamAttributesS3Length = bitReader.ReadBits(12); - for (int i = 0; i < teamAttributesS3Length; i++) - { - teamAttributesS3.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + bitReader.ReadBitArray(1); // b1 + bitReader.ReadBitArray(5); // b5 + bitReader.ReadBitArray(5); // b5 + bitReader.ReadBitArray(5); // b5 - bitReader.ReadUnalignedBytes(26); - bitReader.ReadBits(5); + // bbbb + // ModId + bitReader.ReadBits(32); // id + bitReader.ReadBits(32); // ofs - attributeLength = bitReader.ReadBits(12); + bitReader.ReadBits(32); // n32 + bitReader.ReadBitArray(1); // f + bitReader.ReadBits(21); // 21 - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant - } + bitReader.ReadBits(32); // CAFEBABE - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); + // fil28 + bitReader.ReadBits(28); - // hero attribute section 3 - List heroAttributesS3 = new(); + // fil + bitReader.ReadBitArray(36); - uint heroAttributeS3Length = bitReader.ReadBits(12); - for (int i = 0; i < heroAttributeS3Length; i++) - { - heroAttributesS3.Add(bitReader.ReadStringFromBits(32)); - bitReader.ReadBits(29); - } + // hero + if (bitReader.ReadBoolean()) + { + bitReader.ReadBitArray(11); // b11 - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + uint size = bitReader.ReadBits(4); - attributeLength = bitReader.ReadBits(12); + for (int j = 0; j < size; j++) + { + bitReader.ReadBits(6); // num + bitReader.ReadBits(32); // b32 + bitReader.ReadBits(16); // b16 + bitReader.ReadBits(16); // b16 + bitReader.ReadBits(23); // b23 + } - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // drft - Draft - } + bitReader.ReadBitArray(4); // b4 - bitReader.ReadUnalignedBytes(17); - bitReader.ReadBits(4); + ChoiceSection2(ref bitReader); - // hero attribute section 4 - List heroAttributesS4 = new(); + uint size2 = bitReader.ReadBits(7); - uint heroAttributeS4Length = bitReader.ReadBits(12); - for (int i = 0; i < heroAttributeS4Length; i++) - { - heroAttributesS4.Add(bitReader.ReadStringFromBits(32)); - bitReader.ReadBits(29); - } + for (int x = 0; x < size2; x++) + { + bitReader.ReadBitArray(23); + } - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + bitReader.ReadBitArray(11); // b11 - attributeLength = bitReader.ReadBits(12); + if (bitReader.ReadBoolean()) + { + bitReader.ReadBitArray(38); // b38 + bitReader.ReadBitArray(16); // b16 + bitReader.ReadBitArray(16); // b16 + } - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // drft - Draft - } + bitReader.ReadBitArray(1); // b1 - bitReader.ReadUnalignedBytes(19); - bitReader.BitReversement(12); + uint heroLength = bitReader.ReadBits(5); - // 22, 0 -> 15 attributes - List zeroTo15Attributes = new(); + for (int x = 0; x < heroLength; x++) + { + bitReader.ReadBitArray(32); // b32 + } - uint zeroTo15AttributeLength = bitReader.ReadBits(12); - for (int i = 0; i < zeroTo15AttributeLength; i++) - { - zeroTo15Attributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + bitReader.ReadBitArray(23); // b23 + } - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + // ModDependencies + uint modDependenciesLength = bitReader.ReadBits(6); - attributeLength = bitReader.ReadBits(12); + for (int j = 0; j < modDependenciesLength; j++) + { + bitReader.ReadBits(32); // id + bitReader.ReadBits(32); // ofs + } - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Open, Clsd, Humn, Comp + // fil + bitReader.ReadBitArray(32); // b32 } - bitReader.ReadUnalignedBytes(46); - bitReader.ReadBits(30); + // s2mh + uint s2mhCacheHandlesLength = bitReader.ReadBits(6); - // Hmmr, 0 -> 15 attributes - // same length as the one above? - List zeroTo15HmmrAttritbute = new(); - - for (int i = 0; i < zeroTo15AttributeLength; i++) + for (int i = 0; i < s2mhCacheHandlesLength; i++) { - zeroTo15HmmrAttritbute.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } - - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + if (bitReader.ReadStringFromAlignedBytes(4) != "s2mh") + throw new StormParseException($"{_exceptionHeader}: s2mh"); - attributeLength = bitReader.ReadBits(12); - - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // drft, tour - Draft, Tournamnet Draft + bitReader.ReadAlignedBytes(36); } - bitReader.ReadUnalignedBytes(17); - bitReader.ReadBits(4); - // team attributes section 4 - Team 1 -> Team 8, repeat, etc... - List teamAttributesS4 = new(); + /* Skin collection */ + uint skinCollectionLength = bitReader.ReadBits(16); - uint teamAttributesS4Length = bitReader.ReadBits(12); - for (int i = 0; i < teamAttributesS4Length; i++) + for (int i = 0; i < skinCollectionLength; i++) { - teamAttributesS4.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); + bitReader.ReadStringFromUnalignedBytes(8); } - bitReader.ReadUnalignedBytes(26); - bitReader.ReadBits(5); - - attributeLength = bitReader.ReadBits(12); + uint hasSkinCollectionLength = bitReader.ReadBits(32); - for (int i = 0; i < attributeLength; i++) + for (int i = 0; i < hasSkinCollectionLength; i++) { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant - } - - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); - - // met / unmet attributes - List unmetAttributes = new(); + // 16 is total player slots + for (int j = 0; j < 16; j++) + { + bitReader.ReadAlignedByte(); + bitReader.ReadAlignedByte(); // more likely a boolean to get the value - uint unmetAttributesLength = bitReader.ReadBits(12); - for (int i = 0; i < unmetAttributesLength; i++) - { - unmetAttributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); + if (replay.ReplayBuild < 55929) + { + // when the identifier is a string, set the value to the appropriate array index + } + } } - bitReader.ReadUnalignedBytes(44); - bitReader.ReadBits(4); - // hero general roles - warrior, assassin, etc... - List heroGeneralRoleAttributes = new(); + /* filler */ + //if (replay.ReplayBuild >= 85027) + //{ + // // m_disabledHeroList + // uint disabledHeroListLength = bitReader.ReadBits(8); - uint heroGeneralRoleAttributeLength = bitReader.ReadBits(12); - for (int i = 0; i < heroGeneralRoleAttributeLength; i++) - { - heroGeneralRoleAttributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + // bitReader.EndianType = EndianType.LittleEndian; - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + // for (int i = 0; i < disabledHeroListLength; i++) + // { + // string disabledHeroAttributeId = bitReader.ReadStringFromBits(32); - attributeLength = bitReader.ReadBits(12); + // if (replay.DisabledHeroAttributeIdList.Count == 0) + // replay.DisabledHeroAttributeIdList.Add(disabledHeroAttributeId); + // } - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Humn - } + // bitReader.EndianType = EndianType.BigEndian; + //} - bitReader.ReadUnalignedBytes(48); - bitReader.ReadBits(14); + //replay.RandomValue = bitReader.ReadBits(32); // m_randomSeed - bitReader.BitReversement(29); - bitReader.BitReversement(32); + bitReader.ReadBitArray(72); + //bitReader.ReadBitArray(32); - bitReader.BitReversement(12); - // HMT attributes - List hmtAttributes = new(); + uint playersLength = bitReader.ReadBits(5); - uint hmtAttributesLength = bitReader.ReadBits(12); - for (int i = 0; i < hmtAttributesLength; i++) + for (int i = 0; i < playersLength; i++) { - hmtAttributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } - - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + // m_Unk1 + bitReader.ReadBitArray(32); // m_Unk1 + uint slotId = bitReader.ReadBits(5); - attributeLength = bitReader.ReadBits(12); - - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Humn, Comp - } + // toon handle + bitReader.ReadBits(8); // region + bitReader.ReadStringFromUnalignedBytes(4); // programId + bitReader.ReadBits(32); // realm + bitReader.ReadLongBits(64); // id - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); + // string toon T:xxxxxxxx + bitReader.ReadBits(8); // region + bitReader.ReadStringFromUnalignedBytes(4); // programId + bitReader.ReadBits(32); // realm - // tcXX attributes - List tcAttributes = new(); + int idLength = (int)bitReader.ReadBits(7) + 2; + bitReader.ReadStringFromAlignedBytes(idLength); // m_globalName - uint tcAttributesLength = bitReader.ReadBits(12); - for (int i = 0; i < tcAttributesLength; i++) - { - tcAttributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + bitReader.ReadBitArray(1); // m_flags + bitReader.ReadBitArray(2); // m_Unk4 - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + if (bitReader.ReadBoolean()) + { + bitReader.ReadBitArray(2); // m_Unk1 - attributeLength = bitReader.ReadBits(12); + // string toon T:xxxxxxxx + bitReader.ReadBits(8); // region + bitReader.ReadStringFromUnalignedBytes(4); // programId + bitReader.ReadBits(32); // realm - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Dflt - Default - } + int idLength2 = (int)bitReader.ReadBits(7) + 2; + bitReader.ReadStringFromAlignedBytes(idLength2); // m_globalName - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); + bitReader.ReadBitArray(1); // m_flags + } - attributeLength = bitReader.ReadBits(12); + // m_Unk2 + bitReader.ReadBitArray(198); - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Humn, Comp - } + // m_Unk3 + if (bitReader.ReadBoolean()) + { + bitReader.ReadBitArray(64); + } - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); + // m_Unk4 + bitReader.ReadBitArray(2); // m_UnkFlags1 + bitReader.ReadBitArray(35); // m_Unk1 - attributeLength = bitReader.ReadBits(12); + bitReader.ReadBitArray(4); // m_Skins2 - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant - } + // m_PartyInfo + if (bitReader.ReadBoolean()) + { + bitReader.ReadBitArray(64); + } - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); + // m_TagInfo + if (bitReader.ReadBoolean()) + { + string battleTagName = bitReader.ReadBlobAsString(7); + } - // 22, 0 -> 15 attributes - List zeroTo15Attributes2 = new(); + int playerLevel = (int)bitReader.ReadBits(32); // in custom games, this is a 0 - uint zeroTo15AttributeLength2 = bitReader.ReadBits(12); - for (int i = 0; i < zeroTo15AttributeLength2; i++) - { - zeroTo15Attributes2.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); + // m_UnkFlags + bitReader.ReadBitArray(1); } - bitReader.ReadUnalignedBytes(37); + /* _end */ - // team attributes section 5 - Team 1 -> Team 2, repeat, etc... - List teamAttributesS5 = new(); + bitReader.ReadBitArray(62); // sss - uint teamAttributeS5Length = bitReader.ReadBits(12); - for (int i = 0; i < teamAttributeS5Length; i++) - { - teamAttributesS5.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + // m_toon + ChoiceSection3(ref bitReader); - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + bitReader.ReadBitArray(87); // b87 - attributeLength = bitReader.ReadBits(12); - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // \06v6 - } - - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); - - attributeLength = bitReader.ReadBits(12); + uint length = bitReader.ReadBits(5) + 1; - for (int i = 0; i < attributeLength; i++) + for (int i = 0; i < length; i++) { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant + bitReader.ReadBitArray(38); } - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); + // Neut + length = bitReader.ReadBits(4) + 1; - // voiceline attributes - List voicelineAttributes = new(); - - uint voicelineAttributeLength = bitReader.ReadBits(12); - for (int i = 0; i < voicelineAttributeLength; i++) + for (int i = 0; i < length; i++) { - voicelineAttributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - - if (bitReader.ReadBoolean()) - bitReader.ReadBits(28); - else - bitReader.ReadBits(5); + bitReader.ReadBitArray(32); } - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + uint heroLength2 = bitReader.ReadBits(5); - attributeLength = bitReader.ReadBits(12); - - for (int i = 0; i < attributeLength; i++) + for (int i = 0; i < heroLength2; i++) { - bitReader.ReadStringFromUnalignedBytes(4); // Dflt - Default - } - - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); - - attributeLength = bitReader.ReadBits(12); + bitReader.ReadBits(32); - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Humn, Comp - } + uint heroIconLength = bitReader.ReadBits(11); - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); + for (int j = 0; j < heroIconLength; j++) + { + bitReader.ReadStringFromUnalignedBytes(4); // HERO + bitReader.ReadStringFromUnalignedBytes(4); // ICON - attributeLength = bitReader.ReadBits(12); + bitReader.ReadBits(32); + } - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant + bitReader.ReadBitArray(11); // b11 } - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); - - // team attributes section 6 - Team 1 -> Team 2 - List teamAttributesS6 = new(); - - uint teamAttributeS6Length = bitReader.ReadBits(12); - for (int i = 0; i < teamAttributeS6Length; i++) - { - teamAttributesS6.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + // CSTM - bitReader.ReadUnalignedBytes(21); - bitReader.ReadBits(3); - // team attributes section 7 - Team 1 -> Team 2, repeat, etc... - List teamAttributesS7 = new(); + return; - uint teamAttributeS7Length = bitReader.ReadBits(12); - for (int i = 0; i < teamAttributeS7Length; i++) - { - teamAttributesS7.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + #region + // uint collectionSize; - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + // // strings gone starting with build (ptr) 55929 + // if (replay.ReplayBuild >= 48027) + // collectionSize = bitReader.ReadBits(16); + // else + // collectionSize = bitReader.ReadBits(32); - attributeLength = bitReader.ReadBits(12); + // for (uint i = 0; i < collectionSize; i++) + // { + // if (replay.ReplayBuild >= 55929) + // bitReader.ReadAlignedBytes(8); // most likey an identifier for the item; first six bytes are 0x00 + // else + // bitReader.ReadStringFromAlignedBytes(bitReader.ReadAlignedByte()); + // } - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // \03v3 - } + // // use to determine if the collection item is usable by the player (owns/free to play/internet cafe) + // if (bitReader.ReadBits(32) != collectionSize) + // throw new StormParseException($"{_exceptionHeader}: collection difference"); - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); + // for (int i = 0; i < collectionSize; i++) + // { + // // 16 is total player slots + // for (int j = 0; j < 16; j++) + // { + // bitReader.ReadAlignedByte(); + // bitReader.ReadAlignedByte(); // more likely a boolean to get the value - attributeLength = bitReader.ReadBits(12); + // if (replay.ReplayBuild < 55929) + // { + // // when the identifier is a string, set the value to the appropriate array index + // } + // } + // } - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant - } + // // Player info - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); + // if (replay.ReplayBuild <= 47479 || replay.ReplayBuild == 47801 || replay.ReplayBuild == 47903) + // { + // // Builds that are not yet supported for detailed parsing + // // build 47801 is a ptr build that had new data in the battletag section, the data was changed in 47944 (patch for 47801) + // // GetBattleTags(replay, source); + // return; + // } - // evry, team - everyone, team - attributeLength = bitReader.ReadBits(12); + // if (replay.ReplayBuild >= 85027) + // { + // // m_disabledHeroList + // uint disabledHeroListLength = bitReader.ReadBits(8); - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); - bitReader.ReadBits(29); - } + // bitReader.EndianType = EndianType.LittleEndian; - bitReader.ReadUnalignedBytes(21); - bitReader.ReadBits(3); + // for (int i = 0; i < disabledHeroListLength; i++) + // { + // string disabledHeroAttributeId = bitReader.ReadStringFromBits(32); - // team attributes section 8 - Team 1 -> Team 7, repeat, etc... - List teamAttributesS8 = new(); + // if (replay.DisabledHeroAttributeIdList.Count == 0) + // replay.DisabledHeroAttributeIdList.Add(disabledHeroAttributeId); + // } - uint teamAttributeS8Length = bitReader.ReadBits(12); - for (int i = 0; i < teamAttributeS8Length; i++) - { - teamAttributesS8.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + // bitReader.EndianType = EndianType.BigEndian; + // } - bitReader.ReadUnalignedBytes(26); - bitReader.ReadBits(5); + // replay.RandomValue = bitReader.ReadBits(32); // m_randomSeed - attributeLength = bitReader.ReadBits(12); + // bitReader.ReadAlignedBytes(4); - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant - } + // uint playerListLength = bitReader.ReadBits(5); - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); + // if (replay.PlayersWithObserversCount != playerListLength) + // throw new StormParseException($"{_exceptionHeader}: mismatch on player list length - {playerListLength} to {replay.PlayersWithObserversCount}"); + #endregion + // for (uint i = 0; i < playerListLength; i++) + // { + // bitReader.ReadBits(32); - // team attributes section 9 - Team 1 -> Team 2, repeat, etc... - List teamAttributesS9 = new(); + // uint playerIndex = bitReader.ReadBits(5); // player index - uint teamAttributeS9Length = bitReader.ReadBits(12); - for (int i = 0; i < teamAttributeS9Length; i++) - { - teamAttributesS9.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + // StormPlayer player = replay.ClientListByUserID[playerIndex]; - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + // // toon handle + // uint playerRegion = bitReader.ReadBits(8); // m_region - attributeLength = bitReader.ReadBits(12); + // bitReader.EndianType = EndianType.LittleEndian; + // if (bitReader.ReadBits(32) != 1869768008) // m_programId + // throw new StormParseException($"{_exceptionHeader}: Not Hero"); + // bitReader.EndianType = EndianType.BigEndian; - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // \02v2 - } + // uint playerRealm = bitReader.ReadBits(32); // m_realm + // long playerId = bitReader.ReadLongBits(64); // m_id - bitReader.ReadUnalignedBytes(8); - bitReader.ReadBits(5); + // if (player.PlayerType == PlayerType.Human) + // { + // if (player.ToonHandle!.Region != playerRegion) + // throw new StormParseException($"{_exceptionHeader}: Mismatch on player region"); + // if (player.ToonHandle.Realm != playerRealm) + // throw new StormParseException($"{_exceptionHeader}: Mismatch on player realm"); + // if (player.ToonHandle.Id != playerId) + // throw new StormParseException($"{_exceptionHeader}: Mismatch on player id"); + // } + // else if (player.PlayerType == PlayerType.Observer || player.PlayerType == PlayerType.Unknown) + // { + // // observers don't have the information carried over to the details file and sometimes not the initdata file + // player.ToonHandle ??= new ToonHandle(); + + // player.ToonHandle.Region = (int)playerRegion; + // player.ToonHandle.ProgramId = 1869768008; + // player.ToonHandle.Realm = (int)playerRealm; + // player.ToonHandle.Id = (int)playerId; + // player.PlayerType = PlayerType.Observer; + // player.Team = StormTeam.Observer; + // } - attributeLength = bitReader.ReadBits(12); + // // toon handle again but with T_ shortcut + // bitReader.ReadBits(8); // m_region - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // Part - Participant - } + // bitReader.EndianType = EndianType.LittleEndian; + // if (bitReader.ReadBits(32) != 1869768008) // m_programId (Hero) + // throw new StormParseException($"{_exceptionHeader}: Not Hero"); + // bitReader.EndianType = EndianType.BigEndian; - bitReader.ReadUnalignedBytes(40); - bitReader.ReadBits(5); + // bitReader.ReadBits(32); // m_realm - // draft ban mode attributes - List draftBanModeAttributes = new(); + // int idLength = (int)bitReader.ReadBits(7) + 2; - uint draftBanModeAttributesLength = bitReader.ReadBits(12); - for (int i = 0; i < draftBanModeAttributesLength; i++) - { - draftBanModeAttributes.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + // player.ToonHandle ??= new ToonHandle(); + // player.ToonHandle.ShortcutId = bitReader.ReadStringFromAlignedBytes(idLength); - bitReader.ReadUnalignedBytes(12); - bitReader.ReadBits(4); + // bitReader.ReadBits(6); - attributeLength = bitReader.ReadBits(12); + // if (replay.ReplayBuild <= 47479) + // { + // // toon handle repeat again with T_ shortcut + // bitReader.ReadBits(8); // m_region + // if (bitReader.ReadStringFromBits(32) != "Hero") // m_programId + // throw new StormParseException($"{_exceptionHeader}: Not Hero"); + // bitReader.ReadBits(32); // m_realm - for (int i = 0; i < attributeLength; i++) - { - bitReader.ReadStringFromUnalignedBytes(4); // drft, tour - Draft, Tournamnet Draft - } + // idLength = (int)bitReader.ReadBits(7) + 2; + // if (player.ToonHandle.ShortcutId != bitReader.ReadStringFromAlignedBytes(idLength)) + // throw new StormParseException($"{_exceptionHeader}: Duplicate shortcut id does not match"); - bitReader.ReadUnalignedBytes(17); - bitReader.ReadBits(4); + // bitReader.ReadBits(6); + // } - // met / unmet attributes - List unmetAttributes2 = new(); + // bitReader.ReadBits(2); + // bitReader.ReadUnalignedBytes(25); + // bitReader.ReadBits(24); - uint unmetAttributesLength2 = bitReader.ReadBits(12); - for (int i = 0; i < unmetAttributesLength2; i++) - { - unmetAttributes2.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.ReadBits(29); - } + // // ai games have 8 more bytes somewhere around here + // if (replay.GameMode == StormGameMode.Cooperative) + // bitReader.ReadUnalignedBytes(8); - bitReader.ReadUnalignedBytes(46); - //attributeLength = bitReader.ReadBits(12); + // bitReader.ReadBits(7); - //for (int i = 0; i < attributeLength; i++) - //{ - // bitReader.ReadStringFromUnalignedBytes(4); - // bitReader.ReadBits(29); - //} + // if (!bitReader.ReadBoolean()) + // { + // // repeat of the collection section above + // if (replay.ReplayBuild > 51609 || replay.ReplayBuild == 47903 || replay.ReplayBuild == 47479) + // { + // bitReader.ReadBitArray(bitReader.ReadBits(12)); + // } + // else if (replay.ReplayBuild > 47219) + // { + // // each byte has a max value of 0x7F (127) + // bitReader.ReadUnalignedBytes((int)bitReader.ReadBits(15) * 2); + // } + // else + // { + // bitReader.ReadBitArray(bitReader.ReadBits(9)); + // } + + // bitReader.ReadBoolean(); + // } - //bitReader.BitReversement(29); - //bitReader.BitReversement(32); + // bool isSilenced = bitReader.ReadBoolean(); // m_hasSilencePenalty + // if (player.PlayerType == PlayerType.Observer) + // player.IsSilenced = isSilenced; - //bitReader.ReadStringFromUnalignedBytes(4); + // if (replay.ReplayBuild >= 61718) + // { + // bitReader.ReadBoolean(); + // bool isVoiceSilenced = bitReader.ReadBoolean(); // m_hasVoiceSilencePenalty + // if (player.PlayerType == PlayerType.Observer) + // player.IsVoiceSilenced = isVoiceSilenced; + // } - //bitReader.ReadBits(29); - //bitReader.ReadStringFromUnalignedBytes(4); // assn + // if (replay.ReplayBuild >= 66977) + // { + // bool isBlizzardStaff = bitReader.ReadBoolean(); // m_isBlizzardStaff + // if (player.PlayerType == PlayerType.Observer) + // player.IsBlizzardStaff = isBlizzardStaff; + // } + // if (bitReader.ReadBoolean()) // is player in party + // player.PartyValue = bitReader.ReadLongBits(64); // players in same party will have the same exact 8 bytes of data + // bitReader.ReadBoolean(); - // bitReader.ReadStringFromUnalignedBytes(4); - // bitReader.ReadBits(29); - // bitReader.ReadStringFromUnalignedBytes(4); - List items333 = new(); - for (int i = 0; i < 9999; i++) - { - items333.Add(bitReader.ReadStringFromUnalignedBytes(4)); - bitReader.BitReversement(31); - } + // string battleTagName = bitReader.ReadBlobAsString(7); + // int poundIndex = battleTagName.IndexOf('#'); - // skip to the fifth hero attribute section - for (; ;) - { - List items = new(); - if (bitReader.ReadStringFromBits(32) == "Abat") // tabA [BE - 1096966516] 0x41626174 - { - bitReader.BitReversement(32); + // if (!string.IsNullOrEmpty(battleTagName) && poundIndex < 0) + // throw new StormParseException($"{_exceptionHeader}: Invalid battletag"); - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); + // // check if there is no tag number + // if (poundIndex >= 0) + // { + // ReadOnlySpan namePart = battleTagName.AsSpan(0, poundIndex); + // if (!namePart.SequenceEqual(player.Name)) + // throw new StormParseException($"{_exceptionHeader}: Mismatch on battletag name with player name"); + // } - bitReader.ReadStringFromBits(32); // first actual item is \0\0\0\0 - bitReader.BitReversement(32); + // player.BattleTagName = battleTagName; - bitReader.BitReversement(12); - uint heroAttributeSizeS1 = bitReader.ReadBits(12); // get collection size + // if (replay.ReplayBuild >= 52860 || (replay.ReplayVersion.Major == 2 && replay.ReplayBuild >= 51978)) + // player.AccountLevel = (int)bitReader.ReadBits(32); // in custom games, this is a 0 - for (int i = 0; i < heroAttributeSizeS1; i++) - { - items.Add(bitReader.ReadStringFromBits(32)); - bitReader.ReadBits(29); - } + // if (replay.ReplayBuild >= 69947) + // { + // bool hasActiveBoost = bitReader.ReadBoolean(); // m_hasActiveBoost + // if (player.PlayerType == PlayerType.Observer) + // player.HasActiveBoost = hasActiveBoost; + // } + // } - break; - } - else - { - bitReader.BitReversement(31); - } - } + // replay.IsBattleLobbyPlayerInfoParsed = true; + + + // // skip to the optional 8th Abat attribute + // //for (; ; ) + // //{ + // // if (bitReader.ReadStringFromBits(32) == "tabA") // Abat 1096966516 0x41626174 + // // { + // // List itemsss = new(); + // // for (int i = 0; i < 100; i++) + // // { + // // bitReader.ReadBits(29); // no + // // itemsss.Add(bitReader.ReadStringFromBits(32)); + // // } + + // // break; + // // } + // // else + // // { + // // bitReader.BitReversement(31); + // // } + // //} + } - // skip to the sixth hero attribute section - for (; ;) + // ('_choice', [(0,8),{0:('GlobalValue',6256),1:('ByPlayerValue',6260)}]), #6261 + private static void PlayerAttributeChoice(ref BitReader bitReader) + { + switch (bitReader.ReadBits(8)) { - List items = new(); - if (bitReader.ReadStringFromBits(32) == "Abat") // tabA [BE - 1096966516] 0x41626174 - { - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - bitReader.ReadStringFromBits(32); // first actual item is \0\0\0\0 - bitReader.BitReversement(32); - - bitReader.BitReversement(12); - uint heroAttributeSizeS1 = bitReader.ReadBits(12); // get collection size - - for (int i = 0; i < heroAttributeSizeS1; i++) + case 0: // GlobalValue { - items.Add(bitReader.ReadStringFromBits(32)); - bitReader.ReadBits(29); - } + bitReader.ReadBitArray(11); // ValueIndex + bitReader.ReadBitArray(1); // IsSet - break; - } - else - { - bitReader.BitReversement(31); - } - } - - // skip to the seventh hero attribute section - for (; ;) - { - List items = new(); - if (bitReader.ReadStringFromBits(32) == "Abat") // tabA [BE - 1096966516] 0x41626174 - { - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); + break; + } - bitReader.ReadStringFromBits(32); // first actual item is \0\0\0\0 - bitReader.BitReversement(32); + case 1: // ByPlayerValue + { + uint count = bitReader.ReadBits(5); - bitReader.BitReversement(12); - uint heroAttributeSizeS1 = bitReader.ReadBits(12); // get collection size + for (int i = 0; i < count; i++) + { + bitReader.ReadBitArray(11); // ValueIndex + bitReader.ReadBitArray(1); // IsSet + } - for (int i = 0; i < heroAttributeSizeS1; i++) - { - items.Add(bitReader.ReadStringFromBits(32)); - bitReader.ReadBits(29); + break; } - break; - } - else - { - bitReader.BitReversement(31); - } + default: + throw new NotImplementedException(); } + } - // skip to the spray attribute section - for (; ;) + // ('_choice', [(0,4),{0:('None',6353),1:('None',6354),2:('None',6355),3:('None',6356),4:('None',6357),5:('Data',6363)}]), #6364 + private static void ChoiceSection2(ref BitReader bitReader) + { + switch (bitReader.ReadBits(4)) { - List items = new(); - if (bitReader.ReadStringFromBits(32) == "Rand") - { - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - bitReader.ReadStringFromBits(32); // first actual item is \0\0\0\0 - bitReader.BitReversement(32); - - bitReader.BitReversement(12); - uint sprayAttributeSizeS1 = bitReader.ReadBits(12); // get collection size - - for (int i = 0; i < sprayAttributeSizeS1; i++) + case 0: // None { - items.Add(bitReader.ReadStringFromBits(32)); - bitReader.ReadBits(29); + break; } - break; - } - else - { - bitReader.BitReversement(31); - } - } - - // skip to the mount attribute section - for (; ;) - { - List items = new(); - if (bitReader.ReadStringFromBits(32) == "Tilu") - { - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - string ko = bitReader.ReadStringFromBits(32); - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - string ko2 = bitReader.ReadStringFromBits(32); - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - string ko3 = bitReader.ReadStringFromBits(32); - - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - string ko4 = bitReader.ReadStringFromBits(32); - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - string ko5 = bitReader.ReadStringFromBits(32); - - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - string ko6 = bitReader.ReadStringFromBits(32); - - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - bitReader.ReadStringFromBits(32); // first actual item is \0\0\0\0 - bitReader.BitReversement(32); - - bitReader.BitReversement(12); - uint mountAttributeSizeS1 = bitReader.ReadBits(12); // get collection size - - for (int i = 0; i < mountAttributeSizeS1; i++) + case 1: // None { - items.Add(bitReader.ReadStringFromBits(32)); - if (bitReader.ReadBoolean()) - bitReader.ReadBits(28); - else - bitReader.ReadBits(5); + break; } - break; - } - else - { - bitReader.BitReversement(31); - } - } - - // skip to the announcer attribute section - for (; ;) - { - List items = new(); - if (bitReader.ReadStringFromBits(32) == "Rand") - { - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - string ko = bitReader.ReadStringFromBits(32); - bitReader.BitReversement(32); - - // back to get the first item - bitReader.BitReversement(29); - bitReader.BitReversement(32); - - bitReader.ReadStringFromBits(32); // first actual item is \0\0\0\0 - bitReader.BitReversement(32); - - bitReader.BitReversement(12); - uint announcerAttributeSizeS1 = bitReader.ReadBits(12); // get collection size - - for (int i = 0; i < announcerAttributeSizeS1; i++) + case 2: // None { - items.Add(bitReader.ReadStringFromBits(32)); - if (bitReader.ReadBoolean()) - bitReader.ReadBits(28); - else - bitReader.ReadBits(5); + break; } - break; - } - else - { - bitReader.BitReversement(31); - } - } - - //////////////////// - //// Player selections here - //////////////////// - - // skip down to s2mv /w locales section - for (; ;) - { - List items = new(); - if (bitReader.ReadStringFromUnalignedBytes(4) == "s2mv") - { - bitReader.BitReversement(32); - - bitReader.BitReversement(6); - - uint s2mvCacheHandlesLength = bitReader.ReadBits(6); - - for (int i = 0; i < s2mvCacheHandlesLength; i++) + case 3: // None { - if (bitReader.ReadStringFromAlignedBytes(4) != "s2mv") - throw new StormParseException($"{_exceptionHeader}: s2mv"); - - bitReader.ReadAlignedBytes(36); + break; } - uint localesLength = bitReader.ReadBits(5); // number of locales - - for (int i = 0; i < localesLength; i++) + case 4: // None { - items.Add(bitReader.ReadStringFromUnalignedBytes(4)); - - uint s2mlSize = bitReader.ReadBits(6); // number of s2ml in locale - - bitReader.AlignToByte(); - - for (int j = 0; j < s2mlSize; j++) - { - if (bitReader.ReadStringFromAlignedBytes(4) != "s2ml") - throw new StormParseException($"{_exceptionHeader}: s2ml"); - - bitReader.ReadAlignedBytes(36); - } + break; } - break; - } - else - { - bitReader.BitReversement(31); - } - } - - //////////////////// - //// s2mv w/ blizzmaps players section - //////////////////// - - // second s2mv hit - /* - for (; ; ) - { - if (source.ReadStringFromBytes(4) == "s2mv") - { - BitReader.Index -= 4; - break; - } - else - { - BitReader.Index -= 3; - } - } - for (int i = 0; i < 2; i++) - { - if (source.ReadStringFromBytes(4) != "s2mv") - throw new StormParseException($"{ExceptionHeader}: s2mv cache"); - - source.ReadAlignedBytes(36); - } - - source.ReadBits(1); - - uint region = source.ReadBits(8); // m_region - if (source.ReadStringFromBits(32) != "Hero") // m_programId - throw new StormParseException($"{ExceptionHeader}: Not Hero"); - source.ReadBits(32); // m_realm - - int blizzIdLength = (int)source.ReadBits(7); - - if (region >= 90) - { - if (source.ReadStringFromBytes(2) != "T:") - throw new StormParseException($"{ExceptionHeader}: Not blizz T:"); - source.ReadStringFromBytes(blizzIdLength); - } - else - { - source.ReadStringFromBytes(blizzIdLength); - source.ReadStringFromBytes(2); - } - - source.ReadBits(8); // m_region - if (source.ReadStringFromBits(32) != "Hero") // m_programId - throw new StormParseException($"{ExceptionHeader}: Not Hero"); - source.ReadBits(32); // m_realm - source.ReadLongBits(64); // m_id - - - int klj = (int)source.ReadBits(12); - - int sdfad = (int)source.ReadBits(12); - - - source.ReadBits(1); //temp - - */ - - // skip down to s2mh section - for (; ;) - { - if (bitReader.ReadStringFromUnalignedBytes(4) == "s2mh") - { - bitReader.BitReversement(32); - - bitReader.BitReversement(7); - - uint s2mhCacheHandlesLength = bitReader.ReadBits(7); - for (int i = 0; i < s2mhCacheHandlesLength; i++) + case 5: // Data { - if (bitReader.ReadStringFromAlignedBytes(4) != "s2mh") - throw new StormParseException($"{_exceptionHeader}: s2mh"); + bitReader.ReadBitArray(40); // b40 + bitReader.ReadBitArray(16); // b16 + bitReader.ReadBitArray(79); // b79 + bitReader.ReadBitArray(160); // b160 + bitReader.ReadBitArray(105); // b105 - bitReader.ReadAlignedBytes(36); + break; } - break; - } - else - { - bitReader.BitReversement(31); - } - } - - // player collections - - uint collectionSize; - - // strings gone starting with build (ptr) 55929 - if (replay.ReplayBuild >= 48027) - collectionSize = bitReader.ReadBits(16); - else - collectionSize = bitReader.ReadBits(32); - - for (uint i = 0; i < collectionSize; i++) - { - if (replay.ReplayBuild >= 55929) - bitReader.ReadAlignedBytes(8); // most likey an identifier for the item; first six bytes are 0x00 - else - bitReader.ReadStringFromAlignedBytes(bitReader.ReadAlignedByte()); + default: + throw new NotImplementedException(); } + } - // use to determine if the collection item is usable by the player (owns/free to play/internet cafe) - if (bitReader.ReadBits(32) != collectionSize) - throw new StormParseException($"{_exceptionHeader}: collection difference"); - - for (int i = 0; i < collectionSize; i++) + // ('_choice', [(0,1),{0:('filler',6437),1:('m_toon',6442)}]), #6443 + private static void ChoiceSection3(ref BitReader bitReader) + { + switch (bitReader.ReadBits(1)) { - // 16 is total player slots - for (int j = 0; j < 16; j++) - { - bitReader.ReadAlignedByte(); - bitReader.ReadAlignedByte(); // more likely a boolean to get the value - - if (replay.ReplayBuild < 55929) + case 0: // filler { - // when the identifier is a string, set the value to the appropriate array index - } - } - } - - // Player info - - if (replay.ReplayBuild <= 47479 || replay.ReplayBuild == 47801 || replay.ReplayBuild == 47903) - { - // Builds that are not yet supported for detailed parsing - // build 47801 is a ptr build that had new data in the battletag section, the data was changed in 47944 (patch for 47801) - // GetBattleTags(replay, source); - return; - } - - if (replay.ReplayBuild >= 85027) - { - // m_disabledHeroList - uint disabledHeroListLength = bitReader.ReadBits(8); - - bitReader.EndianType = EndianType.LittleEndian; - - for (int i = 0; i < disabledHeroListLength; i++) - { - string disabledHeroAttributeId = bitReader.ReadStringFromBits(32); - - if (replay.DisabledHeroAttributeIdList.Count == 0) - replay.DisabledHeroAttributeIdList.Add(disabledHeroAttributeId); - } - - bitReader.EndianType = EndianType.BigEndian; - } - - replay.RandomValue = bitReader.ReadBits(32); // m_randomSeed - - bitReader.ReadAlignedBytes(4); - - uint playerListLength = bitReader.ReadBits(5); - - if (replay.PlayersWithObserversCount != playerListLength) - throw new StormParseException($"{_exceptionHeader}: mismatch on player list length - {playerListLength} to {replay.PlayersWithObserversCount}"); - - for (uint i = 0; i < playerListLength; i++) - { - bitReader.ReadBits(32); - - uint playerIndex = bitReader.ReadBits(5); // player index + bitReader.ReadBitArray(16); - StormPlayer player = replay.ClientListByUserID[playerIndex]; + // toon handle + bitReader.ReadBits(8); // region + bitReader.ReadStringFromUnalignedBytes(4); // programId + bitReader.ReadBits(32); // realm + bitReader.ReadLongBits(64); // id - // toon handle - uint playerRegion = bitReader.ReadBits(8); // m_region - - bitReader.EndianType = EndianType.LittleEndian; - if (bitReader.ReadBits(32) != 1869768008) // m_programId - throw new StormParseException($"{_exceptionHeader}: Not Hero"); - bitReader.EndianType = EndianType.BigEndian; - - uint playerRealm = bitReader.ReadBits(32); // m_realm - long playerId = bitReader.ReadLongBits(64); // m_id - - if (player.PlayerType == PlayerType.Human) - { - if (player.ToonHandle!.Region != playerRegion) - throw new StormParseException($"{_exceptionHeader}: Mismatch on player region"); - if (player.ToonHandle.Realm != playerRealm) - throw new StormParseException($"{_exceptionHeader}: Mismatch on player realm"); - if (player.ToonHandle.Id != playerId) - throw new StormParseException($"{_exceptionHeader}: Mismatch on player id"); - } - else if (player.PlayerType == PlayerType.Observer || player.PlayerType == PlayerType.Unknown) - { - // observers don't have the information carried over to the details file and sometimes not the initdata file - player.ToonHandle ??= new ToonHandle(); - - player.ToonHandle.Region = (int)playerRegion; - player.ToonHandle.ProgramId = 1869768008; - player.ToonHandle.Realm = (int)playerRealm; - player.ToonHandle.Id = (int)playerId; - player.PlayerType = PlayerType.Observer; - player.Team = StormTeam.Observer; - } - - // toon handle again but with T_ shortcut - bitReader.ReadBits(8); // m_region - - bitReader.EndianType = EndianType.LittleEndian; - if (bitReader.ReadBits(32) != 1869768008) // m_programId (Hero) - throw new StormParseException($"{_exceptionHeader}: Not Hero"); - bitReader.EndianType = EndianType.BigEndian; - - bitReader.ReadBits(32); // m_realm - - int idLength = (int)bitReader.ReadBits(7) + 2; - - player.ToonHandle ??= new ToonHandle(); - player.ToonHandle.ShortcutId = bitReader.ReadStringFromAlignedBytes(idLength); - - bitReader.ReadBits(6); - - if (replay.ReplayBuild <= 47479) - { - // toon handle repeat again with T_ shortcut - bitReader.ReadBits(8); // m_region - if (bitReader.ReadStringFromBits(32) != "Hero") // m_programId - throw new StormParseException($"{_exceptionHeader}: Not Hero"); - bitReader.ReadBits(32); // m_realm - - idLength = (int)bitReader.ReadBits(7) + 2; - if (player.ToonHandle.ShortcutId != bitReader.ReadStringFromAlignedBytes(idLength)) - throw new StormParseException($"{_exceptionHeader}: Duplicate shortcut id does not match"); - - bitReader.ReadBits(6); - } - - bitReader.ReadBits(2); - bitReader.ReadUnalignedBytes(25); - bitReader.ReadBits(24); - - // ai games have 8 more bytes somewhere around here - if (replay.GameMode == StormGameMode.Cooperative) - bitReader.ReadUnalignedBytes(8); - - bitReader.ReadBits(7); - - if (!bitReader.ReadBoolean()) - { - // repeat of the collection section above - if (replay.ReplayBuild > 51609 || replay.ReplayBuild == 47903 || replay.ReplayBuild == 47479) - { - bitReader.ReadBitArray(bitReader.ReadBits(12)); - } - else if (replay.ReplayBuild > 47219) - { - // each byte has a max value of 0x7F (127) - bitReader.ReadUnalignedBytes((int)bitReader.ReadBits(15) * 2); + break; } - else + + case 1: // None { - bitReader.ReadBitArray(bitReader.ReadBits(9)); + break; } - bitReader.ReadBoolean(); - } - - bool isSilenced = bitReader.ReadBoolean(); // m_hasSilencePenalty - if (player.PlayerType == PlayerType.Observer) - player.IsSilenced = isSilenced; - - if (replay.ReplayBuild >= 61718) - { - bitReader.ReadBoolean(); - bool isVoiceSilenced = bitReader.ReadBoolean(); // m_hasVoiceSilencePenalty - if (player.PlayerType == PlayerType.Observer) - player.IsVoiceSilenced = isVoiceSilenced; - } - - if (replay.ReplayBuild >= 66977) - { - bool isBlizzardStaff = bitReader.ReadBoolean(); // m_isBlizzardStaff - if (player.PlayerType == PlayerType.Observer) - player.IsBlizzardStaff = isBlizzardStaff; - } - - if (bitReader.ReadBoolean()) // is player in party - player.PartyValue = bitReader.ReadLongBits(64); // players in same party will have the same exact 8 bytes of data - - bitReader.ReadBoolean(); - - string battleTagName = bitReader.ReadBlobAsString(7); - int poundIndex = battleTagName.IndexOf('#'); - - if (!string.IsNullOrEmpty(battleTagName) && poundIndex < 0) - throw new StormParseException($"{_exceptionHeader}: Invalid battletag"); - - // check if there is no tag number - if (poundIndex >= 0) - { - ReadOnlySpan namePart = battleTagName.AsSpan(0, poundIndex); - if (!namePart.SequenceEqual(player.Name)) - throw new StormParseException($"{_exceptionHeader}: Mismatch on battletag name with player name"); - } - - player.BattleTagName = battleTagName; - - if (replay.ReplayBuild >= 52860 || (replay.ReplayVersion.Major == 2 && replay.ReplayBuild >= 51978)) - player.AccountLevel = (int)bitReader.ReadBits(32); // in custom games, this is a 0 - - if (replay.ReplayBuild >= 69947) - { - bool hasActiveBoost = bitReader.ReadBoolean(); // m_hasActiveBoost - if (player.PlayerType == PlayerType.Observer) - player.HasActiveBoost = hasActiveBoost; - } + default: + throw new NotImplementedException(); } - - replay.IsBattleLobbyPlayerInfoParsed = true; - - - // skip to the optional 8th Abat attribute - //for (; ; ) - //{ - // if (bitReader.ReadStringFromBits(32) == "tabA") // Abat 1096966516 0x41626174 - // { - // List itemsss = new(); - // for (int i = 0; i < 100; i++) - // { - // bitReader.ReadBits(29); // no - // itemsss.Add(bitReader.ReadStringFromBits(32)); - // } - - // break; - // } - // else - // { - // bitReader.BitReversement(31); - // } - //} } } diff --git a/Heroes.StormReplayParser/ReplayAttributeEventType.cs b/Heroes.StormReplayParser/ReplayAttributeEventType.cs index a4e779a..21426aa 100644 --- a/Heroes.StormReplayParser/ReplayAttributeEventType.cs +++ b/Heroes.StormReplayParser/ReplayAttributeEventType.cs @@ -3,29 +3,29 @@ [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "No need")] internal enum ReplayAttributeEventType { - PlayerTypeAttribute = 500, + PlayerType = 500, Rules = 1000, IsPremadeGame = 1001, /* 2000 - 2024 are related to team sizes */ - TeamSizeAttribute = 2001, - PlayerTeam1v1Attribute = 2002, - PlayerTeam2v2Attribute = 2003, - PlayerTeam3v3Attribute = 2004, - PlayerTeam4v4Attribute = 2005, - PlayerTeamFFAAttribute = 2006, + TeamSize = 2001, + PlayerTeam1v1 = 2002, + PlayerTeam2v2 = 2003, + PlayerTeam3v3 = 2004, + PlayerTeam4v4 = 2005, + PlayerTeamFFA = 2006, - GameSpeedAttribute = 3000, - PlayerRaceAttribute = 3001, - TeamColorIndexAttribute = 3002, - PlayerHandicapAttribute = 3003, - DifficultyLevelAttribute = 3004, + GameSpeed = 3000, + PlayerRace = 3001, + TeamColorIndex = 3002, + PlayerHandicap = 3003, + DifficultyLevel = 3004, ComputerRace = 3005, LobbyDelay = 3006, ParticipantRole = 3007, WatcherType = 3008, - GameModeAttribute = 3009, + GameMode = 3009, LockedAlliances = 3010, PlayerLogo = 3011, TandemLeader = 3012, @@ -38,11 +38,11 @@ internal enum ReplayAttributeEventType PrivacyOption = 4000, UsingCustomObserverUI = 4001, HeroAttributeId = 4002, - SkinAndSkinTintAttributeId = 4003, - MountAndMountTintAttributeId = 4004, + SkinAndSkinTint = 4003, + MountAndMountTint = 4004, Ready = 4005, HeroType = 4006, - HeroRole = 4007, + HeroGeneralRole = 4007, HeroLevel = 4008, CanReady = 4009, LobbyMode = 4010, @@ -65,10 +65,10 @@ internal enum ReplayAttributeEventType DraftTeam1Ban2 = 4025, DraftTeam1Ban2LockedIn = 4026, - BannerAttributeId = 4032, - SprayAttributeId = 4033, - VoiceLineAttributeId = 4034, - AnnouncerAttributeId = 4035, + Banner = 4032, + Spray = 4033, + VoiceLine = 4034, + Announcer = 4035, /* 4036 - 4042 ??? */ @@ -85,5 +85,7 @@ internal enum ReplayAttributeEventType /* 4047 - 4053 ??? */ + HeroSpecificRole = 4053, + /* 4100 - 4200 are related to Artifacts, no longer in the game */ } diff --git a/Heroes.StormReplayParser/StormBattleLobbyAttribute.cs b/Heroes.StormReplayParser/StormBattleLobbyAttribute.cs new file mode 100644 index 0000000..49f614a --- /dev/null +++ b/Heroes.StormReplayParser/StormBattleLobbyAttribute.cs @@ -0,0 +1,15 @@ +namespace Heroes.StormReplayParser; + +internal class StormBattleLobbyAttribute +{ + public StormBattleLobbyAttribute(ReplayAttributeEventType replayAttributeEventType) + { + ReplayAttributeEventType = replayAttributeEventType; + } + + public ReplayAttributeEventType ReplayAttributeEventType { get; } + + public List AttributeValues { get; set; } = new(); + + public List EnabledValueAttributes { get; set; } = new(); +} diff --git a/Heroes.StormReplayParser/StormBattleLobbyAttributeValue.cs b/Heroes.StormReplayParser/StormBattleLobbyAttributeValue.cs new file mode 100644 index 0000000..2efcccf --- /dev/null +++ b/Heroes.StormReplayParser/StormBattleLobbyAttributeValue.cs @@ -0,0 +1,8 @@ +namespace Heroes.StormReplayParser; + +internal class StormBattleLobbyAttributeValue +{ + public string Value { get; set; } = string.Empty; + + public int Id { get; set; } +} diff --git a/Heroes.StormReplayParser/StormBattleLobbyEnabledAttributeValue.cs b/Heroes.StormReplayParser/StormBattleLobbyEnabledAttributeValue.cs new file mode 100644 index 0000000..dcb2dad --- /dev/null +++ b/Heroes.StormReplayParser/StormBattleLobbyEnabledAttributeValue.cs @@ -0,0 +1,6 @@ +namespace Heroes.StormReplayParser; + +internal class StormBattleLobbyEnabledAttributeValue +{ + public string Value { get; set; } = string.Empty; +} diff --git a/Heroes.StormReplayParser/StormReplayBattleLobby.cs b/Heroes.StormReplayParser/StormReplayBattleLobby.cs new file mode 100644 index 0000000..8b5a275 --- /dev/null +++ b/Heroes.StormReplayParser/StormReplayBattleLobby.cs @@ -0,0 +1,5 @@ +namespace Heroes.StormReplayParser; + +public class StormReplayBattleLobby +{ +}