diff --git a/Heroes.ReplayParser.ConsoleApplication/Program.cs b/Heroes.ReplayParser.ConsoleApplication/Program.cs
index fe937c1..abaf5f8 100644
--- a/Heroes.ReplayParser.ConsoleApplication/Program.cs
+++ b/Heroes.ReplayParser.ConsoleApplication/Program.cs
@@ -1,10 +1,9 @@
-using System.IO;
-using System;
-using System.Collections.Generic;
+using System;
+using System.IO;
using System.Linq;
using Heroes.ReplayParser;
-namespace ConsoleApplication1
+namespace ConsoleApplication
{
class Program
{
@@ -15,13 +14,11 @@ static void Main(string[] args)
// Attempt to parse the replay
// Ignore errors can be set to true if you want to attempt to parse currently unsupported replays, such as 'VS AI' or 'PTR Region' replays
- var replayParseResult = DataParser.ParseReplay(randomReplayFileName, ignoreErrors: false, deleteFile: false);
+ var (replayParseResult, replay) = DataParser.ParseReplay(randomReplayFileName, ignoreErrors: false, deleteFile: false);
// If successful, the Replay object now has all currently available information
- if (replayParseResult.Item1 == DataParser.ReplayParseResult.Success)
+ if (replayParseResult == DataParser.ReplayParseResult.Success)
{
- var replay = replayParseResult.Item2;
-
Console.WriteLine("Replay Build: " + replay.ReplayBuild);
Console.WriteLine("Map: " + replay.Map);
foreach (var player in replay.Players.OrderByDescending(i => i.IsWinner))
@@ -30,7 +27,7 @@ static void Main(string[] args)
Console.WriteLine("Press Any Key to Close");
}
else
- Console.WriteLine("Failed to Parse Replay: " + replayParseResult.Item1);
+ Console.WriteLine("Failed to Parse Replay: " + replayParseResult);
Console.Read();
}
diff --git a/Heroes.ReplayParser/BitReader.cs b/Heroes.ReplayParser/BitReader.cs
index b6321a3..5c1941f 100644
--- a/Heroes.ReplayParser/BitReader.cs
+++ b/Heroes.ReplayParser/BitReader.cs
@@ -1,9 +1,9 @@
-namespace Heroes.ReplayParser.Streams
-{
- using System;
- using System.IO;
- using System.Text;
+using System;
+using System.IO;
+using System.Text;
+namespace Heroes.ReplayParser
+{
///
/// A basic little-endian bitstream reader.
///
@@ -31,13 +31,7 @@ public BitReader(Stream stream)
///
/// Gets a value indicating whether the end of stream has been reached.
///
- public bool EndOfStream
- {
- get
- {
- return (this.Cursor >> 3) == this.stream.Length;
- }
- }
+ public bool EndOfStream => (this.Cursor >> 3) == this.stream.Length;
///
/// Reads up to 32 bits from the stream, returning them as a uint.
diff --git a/Heroes.ReplayParser/DataParser.cs b/Heroes.ReplayParser/DataParser.cs
index 327e079..e33251f 100644
--- a/Heroes.ReplayParser/DataParser.cs
+++ b/Heroes.ReplayParser/DataParser.cs
@@ -3,6 +3,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using Heroes.ReplayParser.MPQFiles;
+using MpqHeader = Heroes.ReplayParser.MPQFiles.MpqHeader;
namespace Heroes.ReplayParser
{
diff --git a/Heroes.ReplayParser/MPQFiles/MpqHeader.cs b/Heroes.ReplayParser/MPQFiles/MpqHeader.cs
index 00f9771..0817276 100644
--- a/Heroes.ReplayParser/MPQFiles/MpqHeader.cs
+++ b/Heroes.ReplayParser/MPQFiles/MpqHeader.cs
@@ -1,8 +1,8 @@
-namespace Heroes.ReplayParser
-{
- using System;
- using System.IO;
+using System;
+using System.IO;
+namespace Heroes.ReplayParser.MPQFiles
+{
/// Parses the header at the beginning of the MPQ file structure.
public static class MpqHeader
{
@@ -37,7 +37,8 @@ private static void ParseHeader(Replay replay, BinaryReader reader)
// [0] = Blob, "Heroes of the Storm replay 11" - Strange backward arrow before 11 as well. I don't think the '11' will change, as I believe it was also always '11' in Starcraft 2 replays.
- replay.ReplayVersion = string.Format("{0}.{1}.{2}.{3}", headerStructure.dictionary[1].dictionary[1].vInt.Value, headerStructure.dictionary[1].dictionary[2].vInt.Value, headerStructure.dictionary[1].dictionary[3].vInt.Value, headerStructure.dictionary[1].dictionary[4].vInt.Value);
+ replay.ReplayVersion =
+ $"{headerStructure.dictionary[1].dictionary[1].vInt.Value}.{headerStructure.dictionary[1].dictionary[2].vInt.Value}.{headerStructure.dictionary[1].dictionary[3].vInt.Value}.{headerStructure.dictionary[1].dictionary[4].vInt.Value}";
replay.ReplayBuild = (int)headerStructure.dictionary[1].dictionary[4].vInt.Value;
diff --git a/Heroes.ReplayParser/MPQFiles/ReplayAttributeEvents.cs b/Heroes.ReplayParser/MPQFiles/ReplayAttributeEvents.cs
index 20a2745..f70d15e 100644
--- a/Heroes.ReplayParser/MPQFiles/ReplayAttributeEvents.cs
+++ b/Heroes.ReplayParser/MPQFiles/ReplayAttributeEvents.cs
@@ -1,11 +1,10 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Text;
-namespace Heroes.ReplayParser
+namespace Heroes.ReplayParser.MPQFiles
{
- using System.Collections.Generic;
-
public static class ReplayAttributeEvents
{
public const string FileName = "replay.attributes.events";
@@ -16,7 +15,7 @@ public static void Parse(Replay replay, byte[] buffer)
var attributes = new ReplayAttribute[BitConverter.ToInt32(buffer, headerSize)];
- var initialOffset = 4 + headerSize;
+ const int initialOffset = 4 + headerSize;
for (var i = 0; i < attributes.Length; i++)
{
@@ -68,15 +67,21 @@ private static void ApplyAttributes(Replay replay, ReplayAttribute[] Attributes)
if (type == "comp" || type == "humn")
replay.PlayersWithOpenSlots[attribute.PlayerId - 1] = replay.Players[attribute.PlayerId - replayPlayersWithOpenSlotsIndex];
- if (type == "comp")
- replay.PlayersWithOpenSlots[attribute.PlayerId - 1].PlayerType = PlayerType.Computer;
- else if (type == "humn")
- replay.PlayersWithOpenSlots[attribute.PlayerId - 1].PlayerType = PlayerType.Human;
- else if (type == "open")
+ switch (type)
+ {
+ case "comp":
+ replay.PlayersWithOpenSlots[attribute.PlayerId - 1].PlayerType = PlayerType.Computer;
+ break;
+ case "humn":
+ replay.PlayersWithOpenSlots[attribute.PlayerId - 1].PlayerType = PlayerType.Human;
+ break;
// Less than 10 players in a Custom game
- replayPlayersWithOpenSlotsIndex++;
- else
- throw new Exception("Unexpected value for PlayerType");
+ case "open":
+ replayPlayersWithOpenSlotsIndex++;
+ break;
+ default:
+ throw new Exception("Unexpected value for PlayerType");
+ }
break;
}
diff --git a/Heroes.ReplayParser/MPQFiles/ReplayDetails.cs b/Heroes.ReplayParser/MPQFiles/ReplayDetails.cs
index fcd02f2..84860ba 100644
--- a/Heroes.ReplayParser/MPQFiles/ReplayDetails.cs
+++ b/Heroes.ReplayParser/MPQFiles/ReplayDetails.cs
@@ -1,10 +1,9 @@
-using System.Linq;
+using System;
+using System.IO;
+using System.Linq;
-namespace Heroes.ReplayParser
+namespace Heroes.ReplayParser.MPQFiles
{
- using System;
- using System.IO;
-
public static class ReplayDetails
{
public const string FileName = "replay.details";
@@ -51,13 +50,18 @@ public static void Parse(Replay replay, byte[] buffer, bool ignoreErrors = false
replay.Timestamp = DateTime.FromFileTimeUtc(replayDetailsStructure.dictionary[5].vInt.Value); // m_timeUTC
- // There was a bug during the below builds where timestamps were buggy for the Mac build of Heroes of the Storm
- // The replay, as well as viewing these replays in the game client, showed years such as 1970, 1999, etc
- // I couldn't find a way to get the correct timestamp, so I am just estimating based on when these builds were live
- if (replay.ReplayBuild == 34053 && replay.Timestamp < new DateTime(2015, 2, 8))
- replay.Timestamp = new DateTime(2015, 2, 13);
- else if (replay.ReplayBuild == 34190 && replay.Timestamp < new DateTime(2015, 2, 15))
- replay.Timestamp = new DateTime(2015, 2, 20);
+ switch (replay.ReplayBuild)
+ {
+ // There was a bug during the below builds where timestamps were buggy for the Mac build of Heroes of the Storm
+ // The replay, as well as viewing these replays in the game client, showed years such as 1970, 1999, etc
+ // I couldn't find a way to get the correct timestamp, so I am just estimating based on when these builds were live
+ case 34053 when replay.Timestamp < new DateTime(2015, 2, 8):
+ replay.Timestamp = new DateTime(2015, 2, 13);
+ break;
+ case 34190 when replay.Timestamp < new DateTime(2015, 2, 15):
+ replay.Timestamp = new DateTime(2015, 2, 20);
+ break;
+ }
// [6] - m_timeLocalOffset - For Windows replays, this is Utc offset. For Mac replays, this is actually the entire Local Timestamp
// [7] - m_description - Empty String
diff --git a/Heroes.ReplayParser/MPQFiles/ReplayGameEvents.cs b/Heroes.ReplayParser/MPQFiles/ReplayGameEvents.cs
index baed4eb..a3b4a35 100644
--- a/Heroes.ReplayParser/MPQFiles/ReplayGameEvents.cs
+++ b/Heroes.ReplayParser/MPQFiles/ReplayGameEvents.cs
@@ -1,12 +1,10 @@
-using System.Linq;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
-namespace Heroes.ReplayParser
+namespace Heroes.ReplayParser.MPQFiles
{
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Text;
-
public class ReplayGameEvents
{
public const string FileName = "replay.game.events";
@@ -20,7 +18,7 @@ public static List Parse(byte[] buffer, Player[] clientList, int repl
var ticksElapsed = 0;
using (var stream = new MemoryStream(buffer))
{
- var bitReader = new Streams.BitReader(stream);
+ var bitReader = new BitReader(stream);
while (!bitReader.EndOfStream)
{
var gameEvent = new GameEvent();
@@ -453,10 +451,7 @@ public static List Parse(byte[] buffer, Player[] clientList, int repl
break;
case GameEventType.CGameUserLeaveEvent:
// m_leaveReason
- if(replayBuild >= 55929)
- bitReader.Read(5);
- else
- bitReader.Read(4);
+ bitReader.Read(replayBuild >= 55929 ? 5 : 4);
break;
case GameEventType.CGameUserJoinEvent:
gameEvent.data = new TrackerEventStructure { array = new TrackerEventStructure[5] };
@@ -527,12 +522,12 @@ public class GameEvent
public Player player = null;
public bool isGlobal = false;
public int ticksElapsed;
- public TimeSpan TimeSpan { get { return new TimeSpan(0, 0, (int)(ticksElapsed / 16.0)); } }
+ public TimeSpan TimeSpan => new TimeSpan(0, 0, (int)(ticksElapsed / 16.0));
public TrackerEventStructure data = null;
public override string ToString()
{
- return data != null ? data.ToString() : null;
+ return data?.ToString();
}
}
diff --git a/Heroes.ReplayParser/MPQFiles/ReplayInitData.cs b/Heroes.ReplayParser/MPQFiles/ReplayInitData.cs
index 0b955bc..d66bac9 100644
--- a/Heroes.ReplayParser/MPQFiles/ReplayInitData.cs
+++ b/Heroes.ReplayParser/MPQFiles/ReplayInitData.cs
@@ -1,10 +1,10 @@
-namespace Heroes.ReplayParser
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace Heroes.ReplayParser.MPQFiles
{
- using Heroes.ReplayParser.Streams;
- using System;
- using System.IO;
- using System.Linq;
- using System.Text;
/// Parses the replay.Initdata file in the replay file.
public class ReplayInitData
{
@@ -142,10 +142,7 @@ public static void Parse(Replay replay, byte[] buffer)
reader.Read(8); // + 1 = Max Races
// Max Controls
- if (replay.ReplayBuild < 59279)
- reader.Read(8);
- else
- reader.Read(4);
+ reader.Read(replay.ReplayBuild < 59279 ? 8 : 4);
replay.MapSize = new Point { X = (int)reader.Read(8), Y = (int)reader.Read(8) };
if (replay.MapSize.Y == 1)
@@ -171,10 +168,7 @@ public static void Parse(Replay replay, byte[] buffer)
reader.ReadBitArray(reader.Read(6)); // m_allowedDifficulty
// m_allowedControls
- if (replay.ReplayBuild < 59279)
- reader.ReadBitArray(reader.Read(8));
- else
- reader.ReadBitArray(reader.Read(4));
+ reader.ReadBitArray(replay.ReplayBuild < 59279 ? reader.Read(8) : reader.Read(4));
reader.ReadBitArray(reader.Read(2)); // m_allowedObserveTypes
reader.ReadBitArray(reader.Read(7)); // m_allowedAIBuilds
@@ -222,7 +216,7 @@ public static void Parse(Replay replay, byte[] buffer)
reader.Read(32); // m_logoIndex
- string heroId = Encoding.ASCII.GetString(reader.ReadBlobPrecededWithLength(9)); // m_hero
+ var heroId = Encoding.ASCII.GetString(reader.ReadBlobPrecededWithLength(9)); // m_hero
var skinAndSkinTint = Encoding.ASCII.GetString(reader.ReadBlobPrecededWithLength(9)); // m_skin
if (skinAndSkinTint == "")
@@ -296,19 +290,19 @@ public static void Parse(Replay replay, byte[] buffer)
if (replay.ReplayVersionMajor >= 2)
{
- string banner = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_banner
+ var banner = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_banner
if (!string.IsNullOrEmpty(banner) && userID.HasValue)
replay.ClientListByUserID[userID.Value].Banner = banner;
- string spray = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_spray
+ var spray = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_spray
if (!string.IsNullOrEmpty(spray) && userID.HasValue)
replay.ClientListByUserID[userID.Value].Spray = spray;
- string announcer = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_announcerPack
+ var announcer = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_announcerPack
if (!string.IsNullOrEmpty(announcer) && userID.HasValue)
replay.ClientListByUserID[userID.Value].AnnouncerPack = announcer;
- string voiceLine = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_voiceLine
+ var voiceLine = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_voiceLine
if (!string.IsNullOrEmpty(voiceLine) && userID.HasValue)
replay.ClientListByUserID[userID.Value].VoiceLine = voiceLine;
@@ -318,8 +312,8 @@ public static void Parse(Replay replay, byte[] buffer)
var heroMasteryTiersLength = reader.Read(10);
for (var j = 0; j < heroMasteryTiersLength; j++)
{
- string heroAttributeName = new string(BitConverter.GetBytes(reader.Read(32)).Select(k => (char)k).Reverse().ToArray()); // m_hero
- int tier = (int)reader.Read(8); // m_tier
+ var heroAttributeName = new string(BitConverter.GetBytes(reader.Read(32)).Select(k => (char)k).Reverse().ToArray()); // m_hero
+ var tier = (int)reader.Read(8); // m_tier
if (userID.HasValue)
{
diff --git a/Heroes.ReplayParser/MPQFiles/ReplayMessageEvents.cs b/Heroes.ReplayParser/MPQFiles/ReplayMessageEvents.cs
index 25be1ef..b8704bb 100644
--- a/Heroes.ReplayParser/MPQFiles/ReplayMessageEvents.cs
+++ b/Heroes.ReplayParser/MPQFiles/ReplayMessageEvents.cs
@@ -1,9 +1,9 @@
-namespace Heroes.ReplayParser
-{
- using System;
- using System.IO;
- using System.Text;
+using System;
+using System.IO;
+using System.Text;
+namespace Heroes.ReplayParser.MPQFiles
+{
public class ReplayMessageEvents
{
public const string FileName = "replay.message.events";
@@ -20,7 +20,7 @@ public static void Parse(Replay replay, byte[] buffer)
var ticksElapsed = 0;
using (var stream = new MemoryStream(buffer))
{
- var bitReader = new Streams.BitReader(stream);
+ var bitReader = new BitReader(stream);
while (!bitReader.EndOfStream)
{
@@ -41,23 +41,24 @@ public static void Parse(Replay replay, byte[] buffer)
{
case MessageEventType.SChatMessage:
{
- ChatMessage chatMessage = new ChatMessage();
-
- chatMessage.MessageTarget = (MessageTarget)bitReader.Read(3); // m_recipient (the target)
- chatMessage.Message = Encoding.UTF8.GetString(bitReader.ReadBlobPrecededWithLength(11)); // m_string
-
+ var chatMessage = new ChatMessage
+ {
+ MessageTarget = (MessageTarget) bitReader.Read(3), // m_recipient (the target)
+ Message = Encoding.UTF8.GetString(bitReader.ReadBlobPrecededWithLength(11)) // m_string
+ };
+
message.ChatMessage = chatMessage;
replay.Messages.Add(message);
break;
}
case MessageEventType.SPingMessage:
{
- PingMessage pingMessage = new PingMessage();
-
- pingMessage.MessageTarget = (MessageTarget)bitReader.Read(3); // m_recipient (the target)
-
- pingMessage.XCoordinate = bitReader.ReadInt32() - (-2147483648); // m_point x
- pingMessage.YCoordinate = bitReader.ReadInt32() - (-2147483648); // m_point y
+ var pingMessage = new PingMessage
+ {
+ MessageTarget = (MessageTarget) bitReader.Read(3), // m_recipient (the target)
+ XCoordinate = bitReader.ReadInt32() - (-2147483648), // m_point x
+ YCoordinate = bitReader.ReadInt32() - (-2147483648) // m_point y
+ };
message.PingMessage = pingMessage;
replay.Messages.Add(message);
@@ -81,9 +82,7 @@ public static void Parse(Replay replay, byte[] buffer)
}
case MessageEventType.SPlayerAnnounceMessage:
{
- PlayerAnnounceMessage announceMessage = new PlayerAnnounceMessage();
-
- announceMessage.AnnouncementType = (AnnouncementType)bitReader.Read(2);
+ var announceMessage = new PlayerAnnounceMessage {AnnouncementType = (AnnouncementType) bitReader.Read(2)};
switch (announceMessage.AnnouncementType)
{
@@ -93,10 +92,11 @@ public static void Parse(Replay replay, byte[] buffer)
}
case AnnouncementType.Ability:
{
- AbilityAnnouncment ability = new AbilityAnnouncment();
- ability.AbilityLink = bitReader.ReadInt16(); // m_abilLink
- ability.AbilityIndex = (int)bitReader.Read(5); // m_abilCmdIndex
- ability.ButtonLink = bitReader.ReadInt16(); // m_buttonLink
+ var ability = new AbilityAnnouncment {
+ AbilityLink = bitReader.ReadInt16(), // m_abilLink
+ AbilityIndex = (int) bitReader.Read(5), // m_abilCmdIndex
+ ButtonLink = bitReader.ReadInt16() // m_buttonLink
+ };
announceMessage.AbilityAnnouncement = ability;
break;
@@ -110,8 +110,7 @@ public static void Parse(Replay replay, byte[] buffer)
}
case AnnouncementType.Vitals:
{
- VitalAnnouncment vital = new VitalAnnouncment();
- vital.VitalType = (VitalType)(bitReader.ReadInt16() - (-32768));
+ var vital = new VitalAnnouncment {VitalType = (VitalType) (bitReader.ReadInt16() - (-32768))};
announceMessage.VitalAnnouncement = vital;
break;
diff --git a/Heroes.ReplayParser/MPQFiles/ReplayResumableEvents.cs b/Heroes.ReplayParser/MPQFiles/ReplayResumableEvents.cs
index dad4154..c58d4a5 100644
--- a/Heroes.ReplayParser/MPQFiles/ReplayResumableEvents.cs
+++ b/Heroes.ReplayParser/MPQFiles/ReplayResumableEvents.cs
@@ -1,11 +1,9 @@
-using System.Linq;
+using System;
+using System.IO;
+using System.Text;
-namespace Heroes.ReplayParser
+namespace Heroes.ReplayParser.MPQFiles
{
- using System;
- using System.IO;
- using System.Text;
-
public static class ReplayResumableEvents
{
public const string FileName = "replay.resumable.events";
diff --git a/Heroes.ReplayParser/MPQFiles/ReplayServerBattlelobby.cs b/Heroes.ReplayParser/MPQFiles/ReplayServerBattlelobby.cs
index e5c8e4a..1b445cb 100644
--- a/Heroes.ReplayParser/MPQFiles/ReplayServerBattlelobby.cs
+++ b/Heroes.ReplayParser/MPQFiles/ReplayServerBattlelobby.cs
@@ -1,13 +1,12 @@
-namespace Heroes.ReplayParser
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Heroes.ReplayParser.MPQFiles
{
- using System.IO;
- using Streams;
- using System;
- using System.Text;
- using System.Collections.Generic;
- using System.Text.RegularExpressions;
- using System.Linq;
-
/// Parses the replay.server.battlelobby file in the replay file.
public class ReplayServerBattlelobby
{
@@ -65,7 +64,7 @@ public static void Parse(Replay replay, byte[] buffer)
}
}
- internal static void DetailedParse(BitReader bitReader, Replay replay, int s2mArrayLength)
+ private static void DetailedParse(BitReader bitReader, Replay replay, int s2mArrayLength)
{
bitReader.AlignToByte();
for (; ; )
@@ -91,19 +90,16 @@ internal static void DetailedParse(BitReader bitReader, Replay replay, int s2mAr
// Player collections - starting with HOTS 2.0 (live build 52860)
// strings gone starting with build (ptr) 55929
// --------------------------------------------------------------
- List playerCollection = new List();
+ var playerCollection = new List();
- int collectionSize = 0;
+ var collectionSize = 0;
- if (replay.ReplayBuild >= 48027)
- collectionSize = bitReader.ReadInt16();
- else
- collectionSize = bitReader.ReadInt32();
+ collectionSize = replay.ReplayBuild >= 48027 ? bitReader.ReadInt16() : bitReader.ReadInt32();
if (collectionSize > 8000)
throw new DetailedParsedException("collectionSize is an unusually large number");
- for (int i = 0; i < collectionSize; i++)
+ for (var i = 0; i < collectionSize; i++)
{
if (replay.ReplayBuild >= 55929)
bitReader.ReadBytes(8); // most likey an identifier for the item; first six bytes are 0x00
@@ -115,9 +111,9 @@ internal static void DetailedParse(BitReader bitReader, Replay replay, int s2mAr
if (bitReader.ReadInt32() != collectionSize)
throw new DetailedParsedException("skinArrayLength not equal");
- for (int i = 0; i < collectionSize; i++)
+ for (var i = 0; i < collectionSize; i++)
{
- for (int j = 0; j < 16; j++) // 16 is total player slots
+ for (var j = 0; j < 16; j++) // 16 is total player slots
{
bitReader.ReadByte();
@@ -163,7 +159,7 @@ internal static void DetailedParse(BitReader bitReader, Replay replay, int s2mAr
return;
}
- for (int player = 0; player < replay.ClientListByUserID.Length; player++)
+ for (var player = 0; player < replay.ClientListByUserID.Length; player++)
{
if (replay.ClientListByUserID[player] == null)
break;
@@ -194,11 +190,11 @@ internal static void DetailedParse(BitReader bitReader, Replay replay, int s2mAr
// repeat of the collection section above
if (replay.ReplayBuild >= 51609)
{
- int size = (int)bitReader.Read(12); // 3 bytes
+ var size = (int)bitReader.Read(12); // 3 bytes
if (size == collectionSize)
{
- int bytesSize = collectionSize / 8;
- int bitsSize = collectionSize % 8;
+ var bytesSize = collectionSize / 8;
+ var bitsSize = collectionSize % 8;
bitReader.ReadBytes(bytesSize);
bitReader.Read(bitsSize);
@@ -265,12 +261,9 @@ internal static void DetailedParse(BitReader bitReader, Replay replay, int s2mAr
// used for builds <= 47479 and 47903
private static void ExtendedBattleTagParsingOld(Replay replay, BitReader bitReader)
{
- bool changed47479 = false;
-
- if (replay.ReplayBuild == 47479 && DetectBattleTagChangeBuild47479(replay, bitReader))
- changed47479 = true;
+ bool changed47479 = replay.ReplayBuild == 47479 && DetectBattleTagChangeBuild47479(replay, bitReader);
- for (int i = 0; i < replay.ClientListByUserID.Length; i++)
+ for (var i = 0; i < replay.ClientListByUserID.Length; i++)
{
if (replay.ClientListByUserID[i] == null)
break;
@@ -391,7 +384,7 @@ private static bool DetectBattleTagChangeBuild47479(Replay replay, BitReader bit
if (replay.ReplayBuild != 47479)
return false;
- bool changed = false;
+ var changed = false;
var offset = bitReader.ReadByte();
bitReader.ReadString(2); // T:
@@ -399,7 +392,7 @@ private static bool DetectBattleTagChangeBuild47479(Replay replay, BitReader bit
bitReader.ReadBytes(6);
- for (int i = 0; i < 3; i++)
+ for (var i = 0; i < 3; i++)
{
if (bitReader.Read(8) != 0)
changed = true;
@@ -477,7 +470,7 @@ private static void GetBattleTags(Replay replay, BitReader reader)
}
}
- public static byte[] ReadSpecialBlob(BitReader bitReader, int numBitsForLength)
+ private static byte[] ReadSpecialBlob(BitReader bitReader, int numBitsForLength)
{
var stringLength = bitReader.Read(numBitsForLength);
bitReader.AlignToByte();
diff --git a/Heroes.ReplayParser/MPQFiles/ReplayTrackerEvents.cs b/Heroes.ReplayParser/MPQFiles/ReplayTrackerEvents.cs
index fec9ed0..de8ab5f 100644
--- a/Heroes.ReplayParser/MPQFiles/ReplayTrackerEvents.cs
+++ b/Heroes.ReplayParser/MPQFiles/ReplayTrackerEvents.cs
@@ -1,12 +1,11 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.IO;
using System.Linq;
+using System.Text;
-namespace Heroes.ReplayParser
+namespace Heroes.ReplayParser.MPQFiles
{
- using System;
- using System.IO;
- using System.Text;
-
///
/// Parses the replay.tracker.events file in the MPQ Archive
///
@@ -122,7 +121,7 @@ public class TrackerEventStructure
public TrackerEventStructure[] array = null;
public Dictionary dictionary = null;
public byte[] blob = null;
- public string blobText { get { return blob != null ? Encoding.UTF8.GetString(blob) : null; } }
+ public string blobText => blob != null ? Encoding.UTF8.GetString(blob) : null;
public int? choiceFlag = null;
public TrackerEventStructure choiceData = null;
public TrackerEventStructure optionalData = null;
@@ -195,7 +194,7 @@ public override string ToString()
{
case 0x00: // array
return array != null
- ? '[' + string.Join(", ", array.Select(i => i != null ? i.ToString() : null)) + ']'
+ ? '[' + string.Join(", ", array.Select(i => i?.ToString())) + ']'
: null;
case 0x01: // bitarray, weird alignment requirements, hasn't been used yet
throw new NotImplementedException();
@@ -204,11 +203,9 @@ public override string ToString()
case 0x03: // choice
return "Choice: Flag: " + choiceFlag + ", Data: " + choiceData;
case 0x04: // optional
- return optionalData != null
- ? optionalData.ToString()
- : null;
+ return optionalData?.ToString();
case 0x05: // struct
- return '{' + string.Join(", ", dictionary.Values.Select(i => i != null ? i.ToString() : null)) + '}';
+ return '{' + string.Join(", ", dictionary.Values.Select(i => i?.ToString())) + '}';
case 0x06: // u8
case 0x07: // u32
case 0x08: // u64
diff --git a/Heroes.ReplayParser/Message.cs b/Heroes.ReplayParser/Message.cs
index 48575ed..ce11e0d 100644
--- a/Heroes.ReplayParser/Message.cs
+++ b/Heroes.ReplayParser/Message.cs
@@ -1,5 +1,5 @@
using System;
-using static Heroes.ReplayParser.ReplayMessageEvents;
+using Heroes.ReplayParser.MPQFiles;
namespace Heroes.ReplayParser
{
@@ -8,10 +8,10 @@ public class Message
public Player MessageSender { get; set; }
public int PlayerIndex { get; set; }
public TimeSpan Timestamp { get; set; }
- public MessageEventType MessageEventType { get; set; }
- public ChatMessage ChatMessage { get; set; }
- public PingMessage PingMessage { get; set; }
- public PlayerAnnounceMessage PlayerAnnounceMessage { get; set; }
+ public ReplayMessageEvents.MessageEventType MessageEventType { get; set; }
+ public ReplayMessageEvents.ChatMessage ChatMessage { get; set; }
+ public ReplayMessageEvents.PingMessage PingMessage { get; set; }
+ public ReplayMessageEvents.PlayerAnnounceMessage PlayerAnnounceMessage { get; set; }
public override string ToString()
{
diff --git a/Heroes.ReplayParser/Replay.cs b/Heroes.ReplayParser/Replay.cs
index 62ead27..f9f08ab 100644
--- a/Heroes.ReplayParser/Replay.cs
+++ b/Heroes.ReplayParser/Replay.cs
@@ -1,4 +1,6 @@
-namespace Heroes.ReplayParser
+using Heroes.ReplayParser.MPQFiles;
+
+namespace Heroes.ReplayParser
{
using System;
using System.Collections.Generic;
@@ -96,7 +98,7 @@ public class PeriodicXPBreakdown
public int StructureXP { get; set; }
public int HeroXP { get; set; }
public int TrickleXP { get; set; }
- public int TotalXP { get { return this.MinionXP + this.CreepXP + this.StructureXP + this.HeroXP + this.TrickleXP; } }
+ public int TotalXP => this.MinionXP + this.CreepXP + this.StructureXP + this.HeroXP + this.TrickleXP;
}
public class TeamObjective
diff --git a/Heroes.ReplayParser/Statistics.cs b/Heroes.ReplayParser/Statistics.cs
index 27d176f..0c52c0c 100644
--- a/Heroes.ReplayParser/Statistics.cs
+++ b/Heroes.ReplayParser/Statistics.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Heroes.ReplayParser.MPQFiles;
namespace Heroes.ReplayParser
{
@@ -31,9 +32,9 @@ public static void Parse(Replay replay)
replay.TeamObjectives[(ownerChangeEvent != null ? ownerChangeEvent.PlayerNewOwner : vehicleUnit.PlayerControlledBy).Team].Add(new TeamObjective {
Player = ownerChangeEvent != null ? ownerChangeEvent.PlayerNewOwner : vehicleUnit.PlayerControlledBy,
- TimeSpan = ownerChangeEvent != null ? ownerChangeEvent.TimeSpanOwnerChanged : vehicleUnit.TimeSpanAcquired.Value,
+ TimeSpan = ownerChangeEvent?.TimeSpanOwnerChanged ?? vehicleUnit.TimeSpanAcquired.Value,
TeamObjectiveType = vehicleUnit.Name == "VehiclePlantHorror" ? TeamObjectiveType.GardenOfTerrorGardenTerrorActivatedWithGardenTerrorDurationSeconds : TeamObjectiveType.DragonShireDragonKnightActivatedWithDragonDurationSeconds,
- Value = (int) ((vehicleUnit.TimeSpanDied ?? replay.ReplayLength) - (ownerChangeEvent != null ? ownerChangeEvent.TimeSpanOwnerChanged : vehicleUnit.TimeSpanAcquired.Value)).TotalSeconds });
+ Value = (int) ((vehicleUnit.TimeSpanDied ?? replay.ReplayLength) - (ownerChangeEvent?.TimeSpanOwnerChanged ?? vehicleUnit.TimeSpanAcquired.Value)).TotalSeconds });
}
// Braxis Holdout Zerg Strength
@@ -69,7 +70,7 @@ public static void Parse(Replay replay)
teamZergUnitCount[zergUnits[i].Team.Value]++;
// Check to see if the objective was not completed before the game ended
- if (!teamZergUnitCount.Any(j => j == zergSpawnNumberToStrength.Count - 1))
+ if (teamZergUnitCount.All(j => j != zergSpawnNumberToStrength.Count - 1))
break;
}
@@ -399,7 +400,7 @@ public static void Parse(Replay replay)
if (trackerEvent.Data.dictionary[2].optionalData.array[2].dictionary[1].vInt.Value == 0) // Not sure why, but sometimes 'TeamID' = 0. I've seen it 3 times in about ~60 Sky Temple games
break;
- var recentSkyTempleShotsFiredTeamObjective = replay.TeamObjectives[trackerEvent.Data.dictionary[2].optionalData.array[2].dictionary[1].vInt.Value - 1].Where(i => i.TeamObjectiveType == TeamObjectiveType.SkyTempleShotsFiredWithSkyTempleShotsDamage && i.TimeSpan > trackerEvent.TimeSpan.Add(TimeSpan.FromSeconds(-130))).SingleOrDefault();
+ var recentSkyTempleShotsFiredTeamObjective = replay.TeamObjectives[trackerEvent.Data.dictionary[2].optionalData.array[2].dictionary[1].vInt.Value - 1].SingleOrDefault(i => i.TeamObjectiveType == TeamObjectiveType.SkyTempleShotsFiredWithSkyTempleShotsDamage && i.TimeSpan > trackerEvent.TimeSpan.Add(TimeSpan.FromSeconds(-130)));
if (recentSkyTempleShotsFiredTeamObjective != null)
recentSkyTempleShotsFiredTeamObjective.Value += (int) trackerEvent.Data.dictionary[3].optionalData.array[0].dictionary[1].vInt.Value;
diff --git a/Heroes.ReplayParser/Unit.cs b/Heroes.ReplayParser/Unit.cs
index 1b1dfd8..9e659c9 100644
--- a/Heroes.ReplayParser/Unit.cs
+++ b/Heroes.ReplayParser/Unit.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Heroes.ReplayParser.MPQFiles;
namespace Heroes.ReplayParser
{
@@ -515,11 +516,18 @@ public static void ParseUnitData(Replay replay)
activeHeroUnits[newUnit.PlayerControlledBy].Positions.Add(new Position { TimeSpan = newUnit.TimeSpanBorn, Point = newUnit.PointBorn });
else if (isCheckingForAbathurLocusts)
{
- // For Abathur locusts, we need to make sure they aren't spawning from a locust nest (Level 20 talent)
- if (newUnit.Name == "AbathurLocustNest")
- isCheckingForAbathurLocusts = false;
- else if (newUnit.Name == "AbathurLocustNormal" || newUnit.Name == "AbathurLocustAssaultStrain" || newUnit.Name == "AbathurLocustBombardStrain")
- activeHeroUnits[newUnit.PlayerControlledBy].Positions.Add(new Position { TimeSpan = newUnit.TimeSpanBorn, Point = newUnit.PointBorn });
+ switch (newUnit.Name)
+ {
+ // For Abathur locusts, we need to make sure they aren't spawning from a locust nest (Level 20 talent)
+ case "AbathurLocustNest":
+ isCheckingForAbathurLocusts = false;
+ break;
+ case "AbathurLocustNormal":
+ case "AbathurLocustAssaultStrain":
+ case "AbathurLocustBombardStrain":
+ activeHeroUnits[newUnit.PlayerControlledBy].Positions.Add(new Position { TimeSpan = newUnit.TimeSpanBorn, Point = newUnit.PointBorn });
+ break;
+ }
}
}
break;
@@ -701,14 +709,14 @@ public static void ParseUnitData(Replay replay)
// Group minion units by lane
var currentIndex = 0;
- var minionUnitsPerWave = 7;
+ const int minionUnitsPerWave = 7;
while (currentIndex < minionUnits.Length)
- for (var i = 0; i < unitsPerLaneTemp.Length; i++)
+ foreach (var unitPerLaneTemp in unitsPerLaneTemp)
for (var j = 0; j < minionUnitsPerWave; j++)
{
if (currentIndex == minionUnits.Length)
break;
- unitsPerLaneTemp[i].Add(minionUnits[currentIndex++]);
+ unitPerLaneTemp.Add(minionUnits[currentIndex++]);
// CatapultMinions don't seem to spawn exactly with their minion wave, which is strange
// For now I will leave them out of this, which means they may appear to travel through walls
@@ -726,10 +734,7 @@ public static void ParseUnitData(Replay replay)
// For each lane, take the forts in that lane, and see if the minions in that lane walked beyond this
var currentLaneUnitsToAdjust = unitsPerLane[i].Where(j => j.Positions.Any() || j.TimeSpanDied.HasValue);
var currentLaneWaypoints = minionWayPoints.Skip(numberOfStructureTiers * i).Take(numberOfStructureTiers);
- if (team == 0)
- currentLaneWaypoints = currentLaneWaypoints.OrderBy(j => j.X);
- else
- currentLaneWaypoints = currentLaneWaypoints.OrderByDescending(j => j.X);
+ currentLaneWaypoints = team == 0 ? currentLaneWaypoints.OrderBy(j => j.X) : currentLaneWaypoints.OrderByDescending(j => j.X);
foreach (var laneUnit in currentLaneUnitsToAdjust)
{