diff --git a/Heroes.ReplayParser/BitReader.cs b/Heroes.ReplayParser/BitReader.cs index 2570dd1..d475ab6 100644 --- a/Heroes.ReplayParser/BitReader.cs +++ b/Heroes.ReplayParser/BitReader.cs @@ -51,7 +51,7 @@ public uint Read(int numBits) { if (numBits > 32) { - throw new ArgumentOutOfRangeException("numBits", "Number of bits must be less than 32."); + throw new ArgumentOutOfRangeException("numBits", "Number of bits must be less than 33."); } uint value = 0; @@ -75,6 +75,46 @@ public uint Read(int numBits) return value; } + /// + /// Reads up to 64 bits from the stream, returning them as a long. + /// + /// + /// The number of bits to read. + /// + /// + /// The . + /// + /// + /// Thrown if numBits is greater than 64. + /// + public long ReadLong(int numBits) + { + if (numBits > 64) + { + throw new ArgumentOutOfRangeException("numBits", "Number of bits must be less than 65."); + } + + long value = 0; + + while (numBits > 0) + { + var bytePos = Cursor & 7; + int bitsLeftInByte = 8 - bytePos; + if (bytePos == 0) + { + currentByte = stream.ReadByte(); + } + + var bitsToRead = (bitsLeftInByte > numBits) ? numBits : bitsLeftInByte; + + value = (value << bitsToRead) | ((uint)currentByte >> bytePos) & ((1u << bitsToRead) - 1u); + Cursor += bitsToRead; + numBits -= bitsToRead; + } + + return value; + + } /// /// Skip specified number of bits in stream. /// @@ -198,5 +238,19 @@ public byte[] ReadBlobPrecededWithLength(int numBitsForLength) AlignToByte(); return ReadBytes((int)stringLength); } + + public string ReadStringFromBits(int numberOfBits, bool reverseEndianType = false) + { + byte[] bytes = BitConverter.GetBytes(Read(numberOfBits)); + if (reverseEndianType) + { + Array.Reverse(bytes); + return Encoding.UTF8.GetString(bytes); + } + else + { + return Encoding.UTF8.GetString(bytes); + } + } } } diff --git a/Heroes.ReplayParser/MPQFiles/ReplayServerBattlelobby.cs b/Heroes.ReplayParser/MPQFiles/ReplayServerBattlelobby.cs index bfdf3d4..91fb6d0 100644 --- a/Heroes.ReplayParser/MPQFiles/ReplayServerBattlelobby.cs +++ b/Heroes.ReplayParser/MPQFiles/ReplayServerBattlelobby.cs @@ -153,39 +153,54 @@ private static void DetailedParse(BitReader bitReader, Replay replay, uint s2maC for (uint i = 0; i < playerListLength; i++) { - bitReader.Read(3); - bitReader.ReadBytes(24); - bitReader.Read(24); - bitReader.Read(16); - bitReader.Read(10); + bitReader.Read(32); + + bitReader.Read(5); // player index + + // toon + bitReader.Read(8); // m_region + if (bitReader.ReadStringFromBits(32, true) != "Hero") // m_programId + throw new DetailedParsedException("Not Hero"); + bitReader.Read(32); // m_realm + bitReader.ReadLong(64); // m_id + + // internal toon + bitReader.Read(8); // m_region + if (bitReader.ReadStringFromBits(32, true) != "Hero") // m_programId + throw new DetailedParsedException("Not Hero"); + bitReader.Read(32); // m_realm int idLength = (int)bitReader.Read(7); bitReader.AlignToByte(); if (bitReader.ReadString(2) != "T:") - throw new Exception("Not T:"); + throw new DetailedParsedException("Not T:"); replay.ClientListByUserID[i].BattleNetTId = bitReader.ReadString(idLength); - bitReader.ReadBytes(4); + bitReader.Read(6); if (replay.ReplayBuild <= 47479) { - bitReader.ReadBytes(5); - bitReader.Read(5); + // internal toon repeat + bitReader.Read(8); // m_region + if (bitReader.ReadStringFromBits(32, true) != "Hero") // m_programId + throw new DetailedParsedException("Not Hero"); + bitReader.Read(32); // m_realm idLength = (int)bitReader.Read(7); + bitReader.AlignToByte(); if (bitReader.ReadString(2) != "T:") - throw new Exception(" Not T: (duplicate)"); + throw new DetailedParsedException("Not T: (duplicate)"); if (replay.ClientListByUserID[i].BattleNetTId != bitReader.ReadString(idLength)) - throw new Exception($"Duplicate TID does not match"); - } - else - { - bitReader.ReadBytes(25); + throw new DetailedParsedException("Duplicate TID does not match"); } + bitReader.Read(2); + bitReader.ReadBytes(25); + bitReader.Read(24); + // bitReader.ReadBytes(8); ai games have 8 more bytes somewhere around here bitReader.Read(7); @@ -245,11 +260,6 @@ private static void DetailedParse(BitReader bitReader, Replay replay, uint s2maC if (replay.ReplayBuild >= 69947) { bitReader.ReadBoolean(); // m_hasActiveBoost - bitReader.Read(2); - } - else - { - bitReader.Read(3); } } @@ -423,7 +433,7 @@ private static void GetBattleTags(Replay replay, BitReader reader) for (var playerNum = 0; playerNum < replay.Players.Length; playerNum++) { var player = replay.Players[playerNum]; - if (player == null) + if (player == null || string.IsNullOrEmpty(player.Name)) continue; // Find each player's name, and then their associated BattleTag