Beispiel #1
0
        /// <summary> Parses the replay.server.battlelobby file in a replay file. </summary>
        /// <param name="replay"> The replay file to apply the parsed data to. </param>
        /// <param name="buffer"> The buffer containing the replay.server.battlelobby file. </param>
        public static void Parse(Replay replay, byte[] buffer)
        {
            using (var stream = new MemoryStream(buffer))
            {
                var bitReader = new BitReader(stream);

                // 52124 and 52381 are non-tested ptr builds
                if (replay.ReplayBuild < 38793 ||
                    replay.ReplayBuild == 52124 || replay.ReplayBuild == 52381 ||
                    replay.GameMode == GameMode.Unknown)
                {
                    GetBattleTags(replay, bitReader);
                    return;
                }

                int s2mArrayLength = bitReader.ReadByte();
                int stringLength   = bitReader.ReadByte();

                bitReader.ReadString(stringLength);

                for (var i = 1; i < s2mArrayLength; i++)
                {
                    bitReader.Read(16);
                    bitReader.ReadString(stringLength);
                }

                if (bitReader.ReadByte() != s2mArrayLength)
                {
                    throw new Exception("s2ArrayLength not equal");
                }

                for (var i = 0; i < s2mArrayLength; i++)
                {
                    bitReader.ReadString(4); // s2m
                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                if (replay.ReplayBuild < 55929)
                {
                    bitReader.stream.Position = bitReader.stream.Position + 2632;

                    if (bitReader.ReadString(8) != "HumnComp")
                    {
                        throw new DetailedParsedException("Not HumnComp");
                    }
                }

                DetailedParse(bitReader, replay, s2mArrayLength);
            }
        }
 private static void ReadByte0x00(BitReader bitReader)
 {
     if (bitReader.ReadByte() != 0)
     {
         throw new Exception("Not 0x00");
     }
 }
        // Detect the change that happended in build 47479 on November 2, 2016
        private static bool DetectBattleTagChangeBuild47479(Replay replay, BitReader bitReader)
        {
            if (replay.ReplayBuild != 47479)
            {
                return(false);
            }

            bool changed = false;

            var offset = bitReader.ReadByte();

            bitReader.ReadString(2);           // T:
            bitReader.ReadString(12 + offset); // TId

            bitReader.ReadBytes(6);

            for (int i = 0; i < 3; i++)
            {
                if (bitReader.Read(8) != 0)
                {
                    changed = true;
                }

                offset += 1;

                if (changed)
                {
                    break;
                }
            }

            bitReader.stream.Position = bitReader.stream.Position - 21 - offset;
            return(changed);
        }
        private static void GetBattleTags(Replay replay, BitReader reader)
        {
            // Search for the BattleTag for each player
            var battleTagDigits = new List <char>();

            for (var playerNum = 0; playerNum < replay.Players.Length; playerNum++)
            {
                var player = replay.Players[playerNum];
                if (player == null)
                {
                    continue;
                }

                // Find each player's name, and then their associated BattleTag
                battleTagDigits.Clear();
                var playerNameBytes = Encoding.UTF8.GetBytes(player.Name);
                while (!reader.EndOfStream)
                {
                    var isFound = true;
                    for (var i = 0; i < playerNameBytes.Length + 1; i++)
                    {
                        if ((i == playerNameBytes.Length && reader.ReadByte() != 35 /* '#' Character */) || (i < playerNameBytes.Length && reader.ReadByte() != playerNameBytes[i]))
                        {
                            isFound = false;
                            break;
                        }
                    }

                    if (isFound)
                    {
                        break;
                    }
                }

                // Get the digits from the BattleTag
                while (!reader.EndOfStream)
                {
                    var currentCharacter = (char)reader.ReadByte();

                    if (playerNum == 9 && (currentCharacter == 'z' || currentCharacter == 'Ø'))
                    {
                        // If player is in slot 9, there's a chance that an extra digit could be appended to the BattleTag
                        battleTagDigits.RemoveAt(battleTagDigits.Count - 1);
                        break;
                    }
                    else if (char.IsDigit(currentCharacter))
                    {
                        battleTagDigits.Add(currentCharacter);
                    }
                    else
                    {
                        break;
                    }
                }

                if (reader.EndOfStream)
                {
                    break;
                }

                player.BattleTag = int.Parse(string.Join("", battleTagDigits));
            }
        }
        // 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;
            }

            for (int i = 0; i < replay.ClientListByUserID.Length; i++)
            {
                if (replay.ClientListByUserID[i] == null)
                {
                    break;
                }

                string TId;
                string TId_2;

                // this first one is weird, nothing to indicate the length of the string
                if (i == 0)
                {
                    var offset = bitReader.ReadByte();
                    bitReader.ReadString(2); // T:
                    TId = bitReader.ReadString(12 + offset);

                    if (replay.ReplayBuild <= 47479 && !changed47479)
                    {
                        bitReader.ReadBytes(6);
                        ReadByte0x00(bitReader);
                        ReadByte0x00(bitReader);
                        ReadByte0x00(bitReader);
                        bitReader.Read(6);

                        // get T: again
                        TId_2 = Encoding.UTF8.GetString(ReadSpecialBlob(bitReader, 8));

                        if (TId != TId_2)
                        {
                            throw new Exception("TID dup not equal");
                        }
                    }
                }
                else
                {
                    ReadByte0x00(bitReader);
                    ReadByte0x00(bitReader);
                    ReadByte0x00(bitReader);
                    bitReader.Read(6);

                    // get XXXXXXXX#YYY
                    TId = Encoding.UTF8.GetString(ReadSpecialBlob(bitReader, 8));

                    if (replay.ReplayBuild <= 47479 && !changed47479)
                    {
                        bitReader.ReadBytes(6);
                        ReadByte0x00(bitReader);
                        ReadByte0x00(bitReader);
                        ReadByte0x00(bitReader);
                        bitReader.Read(6);

                        // get T: again
                        TId_2 = Encoding.UTF8.GetString(ReadSpecialBlob(bitReader, 8));

                        if (TId != TId_2)
                        {
                            throw new Exception("TID dup not equal");
                        }
                    }
                }
                replay.ClientListByUserID[i].BattleNetTId = TId;

                // next 31 bytes
                bitReader.ReadBytes(4); // same for all players
                bitReader.ReadByte();
                bitReader.ReadBytes(8); // same for all players
                bitReader.ReadBytes(4);

                bitReader.ReadBytes(14); // same for all players

                if (replay.ReplayBuild >= 47903 || changed47479)
                {
                    bitReader.ReadBytes(40);
                }
                else if (replay.ReplayBuild >= 47219 || replay.ReplayBuild == 47024)
                {
                    bitReader.ReadBytes(39);
                }
                else if (replay.ReplayBuild >= 45889)
                {
                    bitReader.ReadBytes(38);
                }
                else if (replay.ReplayBuild >= 45228)
                {
                    bitReader.ReadBytes(37);
                }
                else if (replay.ReplayBuild >= 44468)
                {
                    bitReader.ReadBytes(36);
                }
                else
                {
                    bitReader.ReadBytes(35);
                }

                if (replay.ReplayBuild >= 47903 || changed47479)
                {
                    bitReader.Read(1);
                }
                else if (replay.ReplayBuild >= 47219 || replay.ReplayBuild == 47024)
                {
                    bitReader.Read(6);
                }
                else if (replay.ReplayBuild >= 46690 || replay.ReplayBuild == 46416)
                {
                    bitReader.Read(5);
                }
                else if (replay.ReplayBuild >= 45889)
                {
                    bitReader.Read(2);
                }
                else if (replay.ReplayBuild >= 45228)
                {
                    bitReader.Read(3);
                }
                else
                {
                    bitReader.Read(5);
                }

                if (bitReader.ReadBoolean())
                {
                    // use this to determine who is in a party
                    // those in the same party will have the same exact 8 bytes of data
                    // the party leader is the first one (in the order of the client list)
                    replay.ClientListByUserID[i].PartyValue = bitReader.ReadInt32() + bitReader.ReadInt32();
                }

                bitReader.Read(1);
                var battleTag = Encoding.UTF8.GetString(bitReader.ReadBlobPrecededWithLength(7)).Split('#'); // battleTag <name>#xxxxx

                if (battleTag.Length != 2 || battleTag[0] != replay.ClientListByUserID[i].Name)
                {
                    throw new Exception("Couldn't find BattleTag");
                }

                replay.ClientListByUserID[i].BattleTag = int.Parse(battleTag[1]);

                // these similar bytes don't occur for last player
                bitReader.ReadBytes(27);
            }

            // some more bytes after (at least 700)
            // theres some HeroICONs and other repetitive stuff
        }
        /// <summary> Parses the replay.server.battlelobby file in a replay file. </summary>
        /// <param name="replay"> The replay file to apply the parsed data to. </param>
        /// <param name="buffer"> The buffer containing the replay.server.battlelobby file. </param>
        public static void Parse(Replay replay, byte[] buffer)
        {
            using (var stream = new MemoryStream(buffer))
            {
                var bitReader = new BitReader(stream);

                // 52124 and 52381 are non-tested ptr builds
                if (replay.ReplayBuild < 38793 ||
                    replay.ReplayBuild == 52124 || replay.ReplayBuild == 52381 ||
                    replay.GameMode == GameMode.Unknown)
                {
                    GetBattleTags(replay, bitReader);
                    return;
                }

                int s2mArrayLength = bitReader.ReadByte();
                int stringLength   = bitReader.ReadByte();

                bitReader.ReadString(stringLength);

                for (var i = 1; i < s2mArrayLength; i++)
                {
                    bitReader.Read(16);
                    bitReader.ReadString(stringLength);
                }

                if (bitReader.ReadByte() != s2mArrayLength)
                {
                    throw new Exception("s2ArrayLength not equal");
                }

                for (var i = 0; i < s2mArrayLength; i++)
                {
                    bitReader.ReadString(4); // s2m
                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                // seems to be in all replays
                bitReader.ReadInt16();
                bitReader.stream.Position = bitReader.stream.Position + 684;

                // seems to be in all replays
                bitReader.ReadInt16();
                bitReader.stream.Position = bitReader.stream.Position + 1944;

                if (bitReader.ReadString(8) != "HumnComp")
                {
                    throw new Exception("Not HumnComp");
                }

                // seems to be in all replays
                bitReader.stream.Position = bitReader.stream.Position = bitReader.stream.Position + 19859;

                // next section is language libraries?
                // ---------------------------------------
                bitReader.Read(8);
                bitReader.Read(8);

                for (int i = 0; ; i++)                                   // no idea how to determine the count
                {
                    if (bitReader.ReadString(4).Substring(0, 2) != "s2") // s2mv; not sure if its going to be 'mv' all the time
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 4;
                        break;
                    }

                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                bitReader.Read(32);
                bitReader.Read(8);

                bitReader.ReadByte();
                for (int i = 0; ; i++)                                   // no idea how to determine the count
                {
                    if (bitReader.ReadString(4).Substring(0, 2) != "s2") // s2ml
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 4;
                        break;
                    }

                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                for (int k = 0; k < 11; k++)
                {
                    // ruRU, zhCN, plPL, esMX, frFR, esES
                    // ptBR, itIT, enUs, deDe, koKR
                    bitReader.ReadString(4);

                    bitReader.ReadByte();
                    for (int i = 0; ; i++)
                    {
                        if (bitReader.ReadString(4).Substring(0, 2) != "s2") // s2ml
                        {
                            bitReader.stream.Position = bitReader.stream.Position - 4;
                            break;
                        }
                        bitReader.ReadString(4); // s2ml
                        bitReader.ReadBytes(2);  // 0x00 0x00
                        bitReader.ReadString(2); // Realm
                        bitReader.ReadBytes(32);
                    }
                }

                // new section, can't find a pattern
                // has blizzmaps#1, Hero, s2mv
                // --------------------
                bitReader.ReadBytes(8); // all 0x00

                for (;;)
                {
                    // we're just going to skip all the way down to the s2mh
                    if (bitReader.ReadString(4) == "s2mh")
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 4;
                        break;
                    }
                    else
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 3;
                    }
                }

                for (var i = 0; i < s2mArrayLength; i++)
                {
                    bitReader.ReadString(4); // s2mh
                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                // Player collections - starting with HOTS 2.0 (live build 52860)
                // --------------------------------------------------------------
                List <string> playerCollection = new List <string>();

                int collectionSize = 0;

                if (replay.ReplayBuild >= 48027)
                {
                    collectionSize = bitReader.ReadInt16();
                }
                else
                {
                    collectionSize = bitReader.ReadInt32();
                }

                if (collectionSize > 5000)
                {
                    throw new Exception("collectionSize is an unusually large number");
                }

                for (int i = 0; i < collectionSize; i++)
                {
                    playerCollection.Add(bitReader.ReadString(bitReader.ReadByte()));
                }

                // use to determine if the collection item is usable by the player (owns/free to play/internet cafe)
                if (bitReader.ReadInt32() != collectionSize)
                {
                    throw new Exception("skinArrayLength not equal");
                }

                for (int i = 0; i < collectionSize; i++)
                {
                    for (int j = 0; j < 16; j++) // 16 is total player slots
                    {
                        bitReader.ReadByte();

                        var num = bitReader.Read(8);
                        if (replay.ClientListByUserID[j] != null)
                        {
                            if (num > 0)
                            {
                                replay.ClientListByUserID[j].PlayerCollectionDictionary.Add(playerCollection[i], true);
                            }
                            else if (num == 0)
                            {
                                replay.ClientListByUserID[j].PlayerCollectionDictionary.Add(playerCollection[i], false);
                            }
                            else
                            {
                                throw new NotImplementedException();
                            }
                        }
                    }
                }

                // Player info
                // ------------------------
                if (replay.ReplayBuild <= 43259 || replay.ReplayBuild == 47801)
                {
                    // 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, bitReader);
                    return;
                }

                bitReader.ReadInt32(); // m_randomSeed
                bitReader.ReadBytes(32);

                bitReader.ReadInt32(); // 0x19

                if (replay.ReplayBuild <= 47479 || replay.ReplayBuild == 47903)
                {
                    ExtendedBattleTagParsingOld(replay, bitReader);
                    return;
                }

                for (int player = 0; player < replay.ClientListByUserID.Length; player++)
                {
                    if (replay.ClientListByUserID[player] == null)
                    {
                        break;
                    }

                    string TId;

                    if (player == 0)
                    {
                        var offset = bitReader.ReadByte();
                        bitReader.ReadString(2); // T:
                        TId = bitReader.ReadString(12 + offset);
                    }
                    else
                    {
                        ReadByte0x00(bitReader);
                        ReadByte0x00(bitReader);
                        ReadByte0x00(bitReader);
                        bitReader.Read(6);

                        // get XXXXXXXX#YYY
                        TId = Encoding.UTF8.GetString(ReadSpecialBlob(bitReader, 8));
                    }

                    replay.ClientListByUserID[player].BattleNetTId = TId;

                    // next 18 bytes
                    bitReader.ReadBytes(4); // same for all players
                    bitReader.ReadBytes(14);

                    if (replay.ReplayBuild >= 53548 || replay.ReplayBuild == 53270)
                    {
                        bitReader.ReadBytes(232);
                        bitReader.Read(1);
                    }
                    else if (replay.ReplayBuild >= 52860)
                    {
                        bitReader.ReadBytes(228);
                        bitReader.Read(7);
                    }
                    else if (replay.ReplayVersionMajor == 2 && replay.ReplayBuild >= 52561)
                    {
                        bitReader.ReadBytes(227);
                        bitReader.Read(4);
                    }
                    else if (replay.ReplayVersionMajor == 2 && replay.ReplayBuild >= 51978)
                    {
                        bitReader.ReadBytes(224);
                        bitReader.Read(0);
                    }
                    else if (replay.ReplayBuild >= 52124)
                    {
                        bitReader.ReadBytes(59);
                        bitReader.Read(4);
                    }
                    else if (replay.ReplayBuild >= 51609)
                    {
                        bitReader.ReadBytes(58);
                        bitReader.Read(7);
                    }
                    else
                    {
                        bitReader.ReadBytes(12);

                        // each byte has a max value of 0x7F (127)
                        if (replay.ReplayBuild >= 48027)
                        {
                            bitReader.ReadInt16();
                        }
                        else
                        {
                            bitReader.ReadInt32();
                        }


                        // this data is a repeat of the usable skins section above
                        // bitReader.stream.Position = bitReader.stream.Position = bitReader.stream.Position + (skinArrayLength * 2);
                        for (int i = 0; i < collectionSize; i++)
                        {
                            // each byte has a max value of 0x7F (127)
                            int value = 0;
                            int x     = (int)bitReader.Read(8);
                            if (x > 0)
                            {
                                value += x + 127;
                            }
                            value += (int)bitReader.Read(8);
                        }

                        bitReader.Read(1);
                    }

                    if (bitReader.ReadBoolean())
                    {
                        // use this to determine who is in a party
                        // those in the same party will have the same exact 8 bytes of data
                        // the party leader is the first one (in the order of the client list)
                        replay.ClientListByUserID[player].PartyValue = bitReader.ReadInt32() + bitReader.ReadInt32();
                    }

                    bitReader.Read(1);
                    var battleTag = Encoding.UTF8.GetString(bitReader.ReadBlobPrecededWithLength(7)).Split('#'); // battleTag <name>#xxxxx

                    if (battleTag.Length != 2 || battleTag[0] != replay.ClientListByUserID[player].Name)
                    {
                        throw new Exception("Couldn't find BattleTag");
                    }

                    replay.ClientListByUserID[player].BattleTag = int.Parse(battleTag[1]);

                    if (replay.ReplayBuild >= 52860 || (replay.ReplayVersionMajor == 2 && replay.ReplayBuild >= 51978))
                    {
                        replay.ClientListByUserID[player].AccountLevel = bitReader.ReadInt32(); // player's account level, not available in custom games
                    }
                    bitReader.ReadBytes(27);                                                    // these similar bytes don't occur for last player
                }

                // some more data after this
            }
        }
        /// <summary> Parses the replay.server.battlelobby file in a replay file. </summary>
        /// <param name="replay"> The replay file to apply the parsed data to. </param>
        /// <param name="buffer"> The buffer containing the replay.server.battlelobby file. </param>
        public static void Parse(Replay replay, byte[] buffer)
        {
            using (var stream = new MemoryStream(buffer))
            {
                var bitReader = new BitReader(stream);

                if (replay.ReplayBuild < 38793)
                {
                    GetBattleTags(replay, bitReader);
                    return;
                }

                int s2mArrayLength = bitReader.ReadByte();
                int stringLength   = bitReader.ReadByte();

                bitReader.ReadString(stringLength);

                for (var i = 1; i < s2mArrayLength; i++)
                {
                    bitReader.Read(16);
                    bitReader.ReadString(stringLength);
                }

                if (bitReader.ReadByte() != s2mArrayLength)
                {
                    throw new Exception("s2ArrayLength not equal");
                }

                for (var i = 0; i < s2mArrayLength; i++)
                {
                    bitReader.ReadString(4); // s2m
                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                // seems to be in all replays
                bitReader.ReadInt16();
                bitReader.stream.Position = bitReader.stream.Position + 684;

                // seems to be in all replays
                bitReader.ReadInt16();
                bitReader.stream.Position = bitReader.stream.Position + 1944;

                if (bitReader.ReadString(8) != "HumnComp")
                {
                    throw new Exception("Not HumnComp");
                }

                // seems to be in all replays
                bitReader.stream.Position = bitReader.stream.Position = bitReader.stream.Position + 19859;

                // next section is language libraries?
                // ---------------------------------------
                bitReader.Read(8);
                bitReader.Read(8);

                for (int i = 0; ; i++)                                   // no idea how to determine the count
                {
                    if (bitReader.ReadString(4).Substring(0, 2) != "s2") // s2mv; not sure if its going to be 'mv' all the time
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 4;
                        break;
                    }

                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                bitReader.Read(32);
                bitReader.Read(8);

                bitReader.ReadByte();
                for (int i = 0; ; i++)                                   // no idea how to determine the count
                {
                    if (bitReader.ReadString(4).Substring(0, 2) != "s2") // s2ml
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 4;
                        break;
                    }

                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                for (int k = 0; k < 11; k++)
                {
                    // ruRU, zhCN, plPL, esMX, frFR, esES
                    // ptBR, itIT, enUs, deDe, koKR
                    bitReader.ReadString(4);

                    bitReader.ReadByte();
                    for (int i = 0; ; i++)
                    {
                        if (bitReader.ReadString(4).Substring(0, 2) != "s2") // s2ml
                        {
                            bitReader.stream.Position = bitReader.stream.Position - 4;
                            break;
                        }
                        bitReader.ReadString(4); // s2ml
                        bitReader.ReadBytes(2);  // 0x00 0x00
                        bitReader.ReadString(2); // Realm
                        bitReader.ReadBytes(32);
                    }
                }

                // new section, can't find a pattern
                // has blizzmaps#1, Hero, s2mv
                // --------------------
                bitReader.ReadBytes(8); // all 0x00

                for (;;)
                {
                    // we're just going to skip all the way down to the s2mh
                    if (bitReader.ReadString(4) == "s2mh")
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 4;
                        break;
                    }
                    else
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 3;
                    }
                }

                for (var i = 0; i < s2mArrayLength; i++)
                {
                    bitReader.ReadString(4); // s2mh
                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                // All the Heroes, skins, mounts, effects, some other weird stuff (Cocoon, ArtifactSlot2, TestMountRideSurf, etc...)
                // --------------------------------------------------------------
                int skinArrayLength = bitReader.ReadInt32();
                for (int i = 0; i < skinArrayLength; i++)
                {
                    bitReader.ReadString(bitReader.ReadByte()); // the name of the "skin"
                }

                // this next part is just a whole bunch of 0x00 and 0x01
                // use to determine if the heroes, skins, mounts are usable by the player (owns/free to play/internet cafe)
                if (bitReader.ReadInt32() != skinArrayLength)
                {
                    throw new Exception("skinArrayLength not equal");
                }

                for (int i = 0; i < skinArrayLength; i++)
                {
                    for (int j = 0; j < 16; j++) // 16 is total player slots
                    {
                        ReadByte0x00(bitReader);
                        var num = bitReader.Read(8);
                        if (num == 1)
                        {
                        }   // true;
                        else if (num == 0)
                        {
                        }   // false;
                        else
                        {
                            throw new NotImplementedException();
                        }
                    }
                }

                // Player info
                // ------------------------
                if (replay.ReplayBuild <= 43259)
                {
                    // Builds that are not yet supported for detailed parsing
                    GetBattleTags(replay, bitReader);
                    return;
                }

                bitReader.ReadInt32();
                bitReader.ReadBytes(33);

                ReadByte0x00(bitReader);
                ReadByte0x00(bitReader);
                bitReader.ReadByte();  // why 0x19?

                if (replay.ReplayBuild < 47479)
                {
                    ExtendedBattleTagParsingOld(replay, bitReader);
                    return;
                }
                else if (replay.ReplayBuild == 47479)
                {
                    // build 47479, after November 2, 2016 around 7pm CDT the data in this section changed slightly
                    // there is no longer a duplicated TId
                    if (!DetectBattleTagChangeBuild47479(replay, bitReader))
                    {
                        ExtendedBattleTagParsingOld(replay, bitReader);
                        return;
                    }
                }

                // for builds after 47479
                for (int i = 0; i < replay.ClientListByUserID.Length; i++)
                {
                    if (replay.ClientListByUserID[i] == null)
                    {
                        break;
                    }

                    string TId;

                    // this first one is weird, nothing to indicate the length of the string
                    if (i == 0)
                    {
                        var offset = bitReader.ReadByte();
                        bitReader.ReadString(2); // T:
                        TId = bitReader.ReadString(12 + offset);
                    }
                    else
                    {
                        ReadByte0x00(bitReader);
                        ReadByte0x00(bitReader);
                        ReadByte0x00(bitReader);
                        bitReader.Read(6);

                        // get XXXXXXXX#YYY
                        TId = Encoding.UTF8.GetString(ReadSpecialBlob(bitReader, 8));
                    }

                    // next 31 bytes
                    bitReader.ReadBytes(4);  // same for all players
                    bitReader.ReadBytes(14);
                    bitReader.ReadBytes(13); // same for all players

                    bitReader.ReadBytes(40);

                    bitReader.Read(1);

                    if (bitReader.ReadBoolean())
                    {
                        // use this to determine who is in a party
                        // those in the same party will have the same exact 8 bytes of data
                        // the party leader is the first one (in the order of the client list)
                        bitReader.ReadBytes(8);
                    }

                    bitReader.Read(1);
                    var battleTag = Encoding.UTF8.GetString(bitReader.ReadBlobPrecededWithLength(7)).Split('#'); // battleTag <name>#xxxxx

                    if (battleTag.Length != 2 || battleTag[0] != replay.ClientListByUserID[i].Name)
                    {
                        throw new Exception("Couldn't find BattleTag");
                    }

                    replay.ClientListByUserID[i].BattleTag = int.Parse(battleTag[1]);

                    // these similar bytes don't occur for last player
                    bitReader.ReadBytes(27);
                }

                // some more bytes after (at least 700)
                // theres some HeroICONs and other repetitive stuff
                // --------------------------------
            }
        }
Beispiel #8
0
        /// <summary> Parses the replay.server.battlelobby file in a replay file. </summary>
        /// <param name="replay"> The replay file to apply the parsed data to. </param>
        /// <param name="buffer"> The buffer containing the replay.initdata file. </param>
        public static void Parse(Replay replay, byte[] buffer)
        {
            using (var stream = new MemoryStream(buffer))
            {
                var reader = new BitReader(stream);

                int arrayLength  = reader.ReadByte();
                var stringLength = reader.ReadByte();
                for (var i = 0; i < arrayLength; i++)
                {
                    reader.ReadString(stringLength);
                    reader.ReadBytes(2); // Unknown
                }

                // This is not always here; we can't blindly wait for 's2mh'

                /* while (!reader.EndOfStream)
                 *  if (reader.ReadString(1) == "s" && reader.ReadString(1) == "2" && reader.ReadString(1) == "m" && reader.ReadString(1) == "h")
                 *  {
                 *      reader.stream.Position -= 4;
                 *      break;
                 *  }
                 *
                 * if (reader.EndOfStream)
                 *  return;
                 *
                 * for (var j = 0; j < arrayLength; j++)
                 * {
                 *  reader.ReadString(4); // s2mh
                 *  reader.ReadBytes(2); // 0x00 0x00
                 *  reader.ReadBytes(2); // 'Realm'
                 *  reader.ReadBytes(32); // 'DepHash'
                 * }
                 * reader.ReadBytes(2); // 0x00 0x00
                 *
                 * // Different Skins / Artifacts / Characters - I think this is what users mouse over in the UI before the game
                 * arrayLength = reader.ReadInt16();
                 * for (var j = 0; j < arrayLength; j++)
                 *  reader.ReadString(reader.ReadByte());
                 *
                 * reader.ReadBytes(2); // 0x00 0x00
                 * reader.ReadInt16();
                 *
                 * do
                 *  arrayLength = reader.ReadByte();
                 * while (!reader.EndOfStream && (arrayLength == 0 || arrayLength == 1));
                 *
                 * if (reader.EndOfStream)
                 *  return; */

                // Now get the BattleTag for each player
                var battleTagDigits = new List <char>();
                foreach (var player in replay.Players.Where(i => i != null))
                {
                    // Find each player's name, and then their associated BattleTag
                    battleTagDigits.Clear();
                    var playerNameBytes = Encoding.UTF8.GetBytes(player.Name);
                    while (!reader.EndOfStream)
                    {
                        var isFound = true;
                        for (var i = 0; i < playerNameBytes.Length + 1; i++)
                        {
                            if ((i == playerNameBytes.Length && reader.ReadByte() != 35 /* '#' Character */) || (i < playerNameBytes.Length && reader.ReadByte() != playerNameBytes[i]))
                            {
                                isFound = false;
                                break;
                            }
                        }

                        if (isFound)
                        {
                            break;
                        }
                    }

                    // Get the numbers from the BattleTag
                    while (!reader.EndOfStream)
                    {
                        var currentCharacter = (char)reader.ReadByte();
                        if (char.IsDigit(currentCharacter))
                        {
                            battleTagDigits.Add(currentCharacter);
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (reader.EndOfStream)
                    {
                        break;
                    }

                    player.BattleTag = int.Parse(string.Join("", battleTagDigits));
                }
            }
        }
Beispiel #9
0
        internal static void DetailedParse(BitReader bitReader, Replay replay, int s2mArrayLength)
        {
            bitReader.AlignToByte();
            for (; ;)
            {
                // we're just going to skip all the way down to the s2mh
                if (bitReader.ReadString(4) == "s2mh")
                {
                    bitReader.stream.Position = bitReader.stream.Position - 4;
                    break;
                }
                else
                {
                    bitReader.stream.Position = bitReader.stream.Position - 3;
                }
            }

            for (var i = 0; i < s2mArrayLength; i++)
            {
                bitReader.ReadString(4); // s2mh
                bitReader.ReadBytes(2);  // 0x00 0x00
                bitReader.ReadString(2); // Realm
                bitReader.ReadBytes(32);
            }

            // Player collections - starting with HOTS 2.0 (live build 52860)
            // strings gone starting with build (ptr) 55929
            // --------------------------------------------------------------
            List <string> playerCollection = new List <string>();

            int collectionSize = 0;

            if (replay.ReplayBuild >= 48027)
            {
                collectionSize = bitReader.ReadInt16();
            }
            else
            {
                collectionSize = bitReader.ReadInt32();
            }

            if (collectionSize > 8000)
            {
                throw new DetailedParsedException("collectionSize is an unusually large number");
            }

            for (int i = 0; i < collectionSize; i++)
            {
                if (replay.ReplayBuild >= 55929)
                {
                    bitReader.ReadBytes(8); // most likey an identifier for the item; first six bytes are 0x00
                }
                else
                {
                    playerCollection.Add(bitReader.ReadString(bitReader.ReadByte()));
                }
            }

            // use to determine if the collection item is usable by the player (owns/free to play/internet cafe)
            if (bitReader.ReadInt32() != collectionSize)
            {
                throw new DetailedParsedException("skinArrayLength not equal");
            }

            for (int i = 0; i < collectionSize; i++)
            {
                for (int j = 0; j < 16; j++) // 16 is total player slots
                {
                    bitReader.ReadByte();

                    var num = bitReader.Read(8);

                    if (replay.ReplayBuild < 55929)
                    {
                        if (replay.ClientListByUserID[j] != null)
                        {
                            if (num > 0)
                            {
                                replay.ClientListByUserID[j].PlayerCollectionDictionary.Add(playerCollection[i], true);
                            }
                            else if (num == 0)
                            {
                                replay.ClientListByUserID[j].PlayerCollectionDictionary.Add(playerCollection[i], false);
                            }
                            else
                            {
                                throw new NotImplementedException();
                            }
                        }
                    }
                }
            }

            // Player info
            // ------------------------
            if (replay.ReplayBuild <= 43259 || replay.ReplayBuild == 47801)
            {
                // 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, bitReader);
                return;
            }

            // m_randomSeed, set it if it hasn't been set
            if (replay.RandomValue == 0)
            {
                replay.RandomValue = (uint)bitReader.ReadInt32();
            }
            else
            {
                bitReader.ReadInt32();
            }

            bitReader.ReadBytes(32);
            bitReader.ReadInt32(); // 0x19

            if (replay.ReplayBuild <= 47479 || replay.ReplayBuild == 47903)
            {
                ExtendedBattleTagParsingOld(replay, bitReader);
                return;
            }

            for (int player = 0; player < replay.ClientListByUserID.Length; player++)
            {
                if (replay.ClientListByUserID[player] == null)
                {
                    break;
                }

                if (player == 0)
                {
                    var offset = bitReader.ReadByte();
                    bitReader.ReadString(2);                                                            // T:
                    replay.ClientListByUserID[player].BattleNetTId = bitReader.ReadString(12 + offset); // TId
                }
                else
                {
                    ReadByte0x00(bitReader);
                    ReadByte0x00(bitReader);
                    ReadByte0x00(bitReader);
                    bitReader.Read(6);

                    // get XXXXXXXX#YYY
                    replay.ClientListByUserID[player].BattleNetTId = Encoding.UTF8.GetString(ReadSpecialBlob(bitReader, 8)); // TId
                }

                // next 30 bytes
                bitReader.ReadBytes(4); // same for all players
                bitReader.ReadBytes(25);
                bitReader.Read(7);
                bool noCollection = bitReader.ReadBoolean();

                // repeat of the collection section above
                if (replay.ReplayBuild >= 51609 && !noCollection)
                {
                    int size = (int)bitReader.Read(12); // 3 bytes
                    if (size == collectionSize)
                    {
                        int bytesSize = collectionSize / 8;
                        int bitsSize  = collectionSize % 8;

                        bitReader.ReadBytes(bytesSize);
                        bitReader.Read(bitsSize);

                        bitReader.ReadBoolean();
                    }
                    // else if not equal, then data isn't available, most likely an observer
                }
                else if (!noCollection)
                {
                    if (replay.ReplayBuild >= 48027)
                    {
                        bitReader.ReadInt16();
                    }
                    else
                    {
                        bitReader.ReadInt32();
                    }

                    // each byte has a max value of 0x7F (127)
                    bitReader.stream.Position = bitReader.stream.Position + (collectionSize * 2);
                }

                bitReader.ReadBoolean(); // m_hasSilencePenalty

                if (replay.ReplayBuild >= 61718)
                {
                    bitReader.ReadBoolean();
                    bitReader.ReadBoolean(); // m_hasVoiceSilencePenalty
                }

                if (replay.ReplayBuild >= 66977)
                {
                    bitReader.ReadBoolean();                                                                      // m_isBlizzardStaff
                }
                if (bitReader.ReadBoolean())                                                                      // is player in party
                {
                    replay.ClientListByUserID[player].PartyValue = bitReader.ReadInt32() + bitReader.ReadInt32(); // players in same party will have the same exact 8 bytes of data
                }
                bitReader.ReadBoolean();

                var battleTag = Encoding.UTF8.GetString(bitReader.ReadBlobPrecededWithLength(7)).Split('#'); // battleTag <name>#xxxxx

                if (battleTag.Length != 2 || battleTag[0] != replay.ClientListByUserID[player].Name)
                {
                    throw new DetailedParsedException("Couldn't find BattleTag");
                }

                replay.ClientListByUserID[player].BattleTag = int.Parse(battleTag[1]);

                if (replay.ReplayBuild >= 52860 || (replay.ReplayVersionMajor == 2 && replay.ReplayBuild >= 51978))
                {
                    replay.ClientListByUserID[player].AccountLevel = bitReader.ReadInt32(); // player's account level, not available in custom games
                }
                bitReader.ReadBytes(27);                                                    // these similar bytes don't occur for last player
            }

            // some more data after this
            // there is also a CSTM string down here, if it exists, the game is a custom game
        }
Beispiel #10
0
        /// <summary> Parses the replay.initdata file in a replay file. </summary>
        /// <param name="replay"> The replay file to apply the parsed data to. </param>
        /// <param name="buffer"> The buffer containing the replay.initdata file. </param>
        public static void Parse(Replay replay, byte[] buffer)
        {
            using (var stream = new MemoryStream(buffer))
            {
                var reader = new BitReader(stream);

                var i = reader.ReadByte();

                var playerList = new string[i];
                for (int j = 0; j < i; j++)
                {
                    var nameLength = reader.ReadByte();
                    var str        = reader.ReadString(nameLength);

                    playerList[j] = str;

                    if (reader.ReadBoolean())
                    {
                        var strLength = reader.ReadByte();
                        reader.AlignToByte();

                        // Clan Tag
                        reader.ReadString(strLength);
                    }

                    if (reader.ReadBoolean())
                    {
                        reader.ReadByte().ToString(); // Highest league
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.ReadInt32().ToString(); // Swarm level
                    }
                    reader.ReadInt32();                // Random seed (So far, always 0 in Heroes)

                    if (reader.ReadBoolean())
                    {
                        reader.ReadByte().ToString(); // Race Preference
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.ReadByte().ToString(); // Team Preference
                    }
                    reader.ReadBoolean();             //test map
                    reader.ReadBoolean();             //test auto
                    reader.ReadBoolean();             //examine
                    reader.ReadBoolean();             //custom interface
                    reader.Read(2);                   //observer

                    reader.AlignToByte();
                    reader.ReadBytes(11); // Bunch of garbage \0
                }

                // Marked as 'Random Value', so I will use as seed
                replay.RandomValue = (uint)reader.ReadInt32();

                reader.ReadBlobPrecededWithLength(10);  // Dflt

                reader.ReadBoolean();                   // Lock Teams
                reader.ReadBoolean();                   // Teams Together
                reader.ReadBoolean();                   // Advanced Shared Control
                reader.ReadBoolean();                   // Random Races
                reader.ReadBoolean();                   // BattleNet
                reader.ReadBoolean();                   // AMM
                reader.ReadBoolean();                   // Competitive
                reader.ReadBoolean();                   // No Victory Or Defeat
                reader.ReadBoolean();                   // Unknown 0
                reader.ReadBoolean();                   // Unknown 1
                reader.ReadBoolean();                   // Unknown 2
                reader.Read(2);                         // Fog
                reader.Read(2);                         // Observers
                reader.Read(2);                         // User Difficulty
                reader.ReadInt32(); reader.ReadInt32(); // 64 bit int: Client Debug Flags

                reader.Read(3);                         // Game Speed

                // Not sure what this 'Game Type' is
                reader.Read(3);

                var maxPlayers = reader.Read(5);
                if (maxPlayers != 10) // Max Players
                {
                    replay.GameMode = GameMode.TryMe;
                }

                // About 1000 bytes from here is a list of characters, character skins, character mounts, artifact selections, and other data
            }
        }
Beispiel #11
0
        /// <summary> Parses the replay.server.battlelobby file in a replay file. </summary>
        /// <param name="replay"> The replay file to apply the parsed data to. </param>
        /// <param name="buffer"> The buffer containing the replay.server.battlelobby file. </param>
        public static void Parse(Replay replay, byte[] buffer)
        {
            using (var stream = new MemoryStream(buffer))
            {
                var bitReader = new BitReader(stream);

                if (replay.ReplayBuild < 38793)
                {
                    GetBattleTags(replay, bitReader);
                    return;
                }

                int s2mArrayLength = bitReader.ReadByte();
                int stringLength   = bitReader.ReadByte();

                bitReader.ReadString(stringLength);

                for (var i = 1; i < s2mArrayLength; i++)
                {
                    bitReader.Read(16);
                    bitReader.ReadString(stringLength);
                }

                if (bitReader.ReadByte() != s2mArrayLength)
                {
                    throw new Exception("s2ArrayLength not equal");
                }

                for (var i = 0; i < s2mArrayLength; i++)
                {
                    bitReader.ReadString(4); // s2m
                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                // seems to be in all replays
                bitReader.ReadInt16();
                bitReader.stream.Position = bitReader.stream.Position + 684;

                // seems to be in all replays
                bitReader.ReadInt16();
                bitReader.stream.Position = bitReader.stream.Position + 1944;

                if (bitReader.ReadString(8) != "HumnComp")
                {
                    throw new Exception("Not HumnComp");
                }

                // seems to be in all replays
                bitReader.stream.Position = bitReader.stream.Position = bitReader.stream.Position + 19859;

                // next section is language libraries?
                // ---------------------------------------
                bitReader.Read(8);
                bitReader.Read(8);

                for (int i = 0; ; i++)                                   // no idea how to determine the count
                {
                    if (bitReader.ReadString(4).Substring(0, 2) != "s2") // s2mv; not sure if its going to be 'mv' all the time
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 4;
                        break;
                    }

                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                bitReader.Read(32);
                bitReader.Read(8);

                bitReader.ReadByte();
                for (int i = 0; ; i++)                                   // no idea how to determine the count
                {
                    if (bitReader.ReadString(4).Substring(0, 2) != "s2") // s2ml
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 4;
                        break;
                    }

                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                for (int k = 0; k < 11; k++)
                {
                    // ruRU, zhCN, plPL, esMX, frFR, esES
                    // ptBR, itIT, enUs, deDe, koKR
                    bitReader.ReadString(4);

                    bitReader.ReadByte();
                    for (int i = 0; ; i++)
                    {
                        if (bitReader.ReadString(4).Substring(0, 2) != "s2") // s2ml
                        {
                            bitReader.stream.Position = bitReader.stream.Position - 4;
                            break;
                        }
                        bitReader.ReadString(4); // s2ml
                        bitReader.ReadBytes(2);  // 0x00 0x00
                        bitReader.ReadString(2); // Realm
                        bitReader.ReadBytes(32);
                    }
                }

                // new section, can't find a pattern
                // has blizzmaps#1, Hero, s2mv
                // --------------------
                bitReader.ReadBytes(8); // all 0x00

                for (;;)
                {
                    // we're just going to skip all the way down to the s2mh
                    if (bitReader.ReadString(4) == "s2mh")
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 4;
                        break;
                    }
                    else
                    {
                        bitReader.stream.Position = bitReader.stream.Position - 3;
                    }
                }

                for (var i = 0; i < s2mArrayLength; i++)
                {
                    bitReader.ReadString(4); // s2mh
                    bitReader.ReadBytes(2);  // 0x00 0x00
                    bitReader.ReadString(2); // Realm
                    bitReader.ReadBytes(32);
                }

                // All the Heroes, skins, mounts, effects, some other weird stuff (Cocoon, ArtifactSlot2, TestMountRideSurf, etc...)
                // --------------------------------------------------------------
                List <string> skins = new List <string>();

                int skinArrayLength = 0;

                if (replay.ReplayBuild >= 48027)
                {
                    skinArrayLength = bitReader.ReadInt16();
                }
                else
                {
                    skinArrayLength = bitReader.ReadInt32();
                }

                if (skinArrayLength > 1000)
                {
                    throw new Exception("skinArrayLength is an unusually large number");
                }

                for (int i = 0; i < skinArrayLength; i++)
                {
                    skins.Add(bitReader.ReadString(bitReader.ReadByte()));
                }

                // use to determine if the heroes, skins, mounts are usable by the player (owns/free to play/internet cafe)
                if (bitReader.ReadInt32() != skinArrayLength)
                {
                    throw new Exception("skinArrayLength not equal");
                }

                for (int i = 0; i < skinArrayLength; i++)
                {
                    for (int j = 0; j < 16; j++) // 16 is total player slots
                    {
                        bitReader.ReadByte();

                        // new values beginning on ptr 47801
                        // 0xC3 = free to play?
                        // more values: 0xC1, 0x02, 0x83
                        var num = bitReader.Read(8);
                        if (replay.ClientListByUserID[j] != null)
                        {
                            if (num > 0)
                            {
                                // usable
                            }
                            else if (num == 0)
                            {
                                // not usable
                            }
                            else
                            {
                                throw new NotImplementedException();
                            }
                        }
                    }
                }

                // Player info
                // ------------------------
                if (replay.ReplayBuild <= 43259 || replay.ReplayBuild == 47801)
                {
                    // 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, bitReader);
                    return;
                }

                bitReader.ReadInt32();
                bitReader.ReadBytes(33);

                ReadByte0x00(bitReader);
                ReadByte0x00(bitReader);
                bitReader.ReadByte(); // 0x19

                if (replay.ReplayBuild <= 47479 || replay.ReplayBuild == 47903)
                {
                    ExtendedBattleTagParsingOld(replay, bitReader);
                    return;
                }

                for (int player = 0; player < replay.ClientListByUserID.Length; player++)
                {
                    if (replay.ClientListByUserID[player] == null)
                    {
                        break;
                    }

                    string TId;

                    if (player == 0)
                    {
                        var offset = bitReader.ReadByte();
                        bitReader.ReadString(2); // T:
                        TId = bitReader.ReadString(12 + offset);
                    }
                    else
                    {
                        ReadByte0x00(bitReader);
                        ReadByte0x00(bitReader);
                        ReadByte0x00(bitReader);
                        bitReader.Read(6);

                        // get XXXXXXXX#YYY
                        TId = Encoding.UTF8.GetString(ReadSpecialBlob(bitReader, 8));
                    }

                    // next 30 bytes
                    bitReader.ReadBytes(4);  // same for all players
                    bitReader.ReadBytes(14);
                    bitReader.ReadBytes(12); // same for all players

                    // these were important in ptr build 47801, not sure what it's used for now
                    // each byte has a max value of 0x7F (127)
                    if (replay.ReplayBuild >= 48027)
                    {
                        bitReader.ReadInt16();
                    }
                    else
                    {
                        bitReader.ReadInt32();
                    }

                    // this data is a repeat of the usable skins section above
                    //bitReader.stream.Position = bitReader.stream.Position = bitReader.stream.Position + (skinArrayLength * 2);
                    for (int i = 0; i < skinArrayLength; i++)
                    {
                        // each byte has a max value of 0x7F (127)
                        int value = 0;
                        int x     = (int)bitReader.Read(8);
                        if (x > 0)
                        {
                            value += x + 127;
                        }
                        value += (int)bitReader.Read(8);
                    }

                    bitReader.Read(1);

                    if (bitReader.ReadBoolean())
                    {
                        // use this to determine who is in a party
                        // those in the same party will have the same exact 8 bytes of data
                        // the party leader is the first one (in the order of the client list)
                        bitReader.ReadBytes(8);
                    }

                    bitReader.Read(1);
                    var battleTag = Encoding.UTF8.GetString(bitReader.ReadBlobPrecededWithLength(7)).Split('#'); // battleTag <name>#xxxxx

                    if (battleTag.Length != 2 || battleTag[0] != replay.ClientListByUserID[player].Name)
                    {
                        throw new Exception("Couldn't find BattleTag");
                    }

                    replay.ClientListByUserID[player].BattleTag = int.Parse(battleTag[1]);

                    // these similar bytes don't occur for last player
                    bitReader.ReadBytes(27);
                }

                // some more data after this
            }
        }
        /// <summary> Parses the replay.initdata file in a replay file. </summary>
        /// <param name="replay"> The replay file to apply the parsed data to. </param>
        /// <param name="buffer"> The buffer containing the replay.initdata file. </param>
        public static void Parse(Replay replay, byte[] buffer)
        {
            using (var stream = new MemoryStream(buffer))
            {
                var reader = new BitReader(stream);

                var i = reader.ReadByte();

                var playerList = new string[i];
                for (int j = 0; j < i; j++)
                {
                    playerList[j] = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(8));

                    if (reader.ReadBoolean())
                    {
                        var clanTag = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(8));
                        // Console.WriteLine(clanTag);
                    }

                    if (reader.ReadBoolean())
                    {
                        // Clan Logo
                        reader.ReadBlobPrecededWithLength(40);
                    }

                    if (reader.ReadBoolean())
                    {
                        var highestLeague = reader.Read(8);
                        // Console.WriteLine(highestLeague);
                    }

                    if (reader.ReadBoolean())
                    {
                        var combinedRaceLevels = reader.ReadInt32();
                        // Console.WriteLine(combinedRaceLevels);
                    }

                    reader.ReadInt32(); // Random seed (So far, always 0 in Heroes)

                    if (reader.ReadBoolean())
                    {
                        reader.Read(8); // Race Preference
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.Read(8);   // Team Preference
                    }
                    reader.ReadBoolean(); //test map
                    reader.ReadBoolean(); //test auto
                    reader.ReadBoolean(); //examine
                    reader.ReadBoolean(); //custom interface

                    var unknown1 = reader.ReadInt32();

                    reader.Read(2);       //observer

                    var unknown2 = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(7));
                    var unknown3 = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(7));
                    var unknown4 = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(7));
                    var unknown5 = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(7));
                    var unknown6 = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(7));
                    var unknown7 = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(7));

                    // Console.WriteLine(unknown1 + unknown2 + unknown3 + unknown4 + unknown5 + unknown6 + unknown7);
                }

                // Marked as 'Random Value', so I will use as seed
                replay.RandomValue = (uint)reader.ReadInt32();

                reader.ReadBlobPrecededWithLength(10);  // Dflt

                reader.ReadBoolean();                   // Lock Teams
                reader.ReadBoolean();                   // Teams Together
                reader.ReadBoolean();                   // Advanced Shared Control
                reader.ReadBoolean();                   // Random Races
                reader.ReadBoolean();                   // BattleNet
                reader.ReadBoolean();                   // AMM
                reader.ReadBoolean();                   // Competitive
                reader.ReadBoolean();                   // No Victory Or Defeat
                reader.ReadBoolean();                   // Unknown 0
                reader.ReadBoolean();                   // Unknown 1
                reader.ReadBoolean();                   // Unknown 2
                reader.Read(2);                         // Fog
                reader.Read(2);                         // Observers
                reader.Read(2);                         // User Difficulty
                reader.ReadInt32(); reader.ReadInt32(); // 64 bit int: Client Debug Flags

                reader.Read(3);                         // Game Speed

                // Not sure what this 'Game Type' is
                reader.Read(3);

                var maxUsers = reader.Read(5);
                if (maxUsers != 10) // Max Players
                {
                    replay.GameMode = GameMode.TryMe;
                }

                reader.Read(5); // Max Observers
                reader.Read(5); // Max Players
                reader.Read(4); // + 1 = Max Teams
                reader.Read(6); // Max Colors
                reader.Read(8); // + 1 = Max Races
                reader.Read(8); // Max Controls

                replay.MapSize = new Point {
                    X = (int)reader.Read(8), Y = (int)reader.Read(8)
                };
                if (replay.MapSize.Y == 1)
                {
                    replay.MapSize.Y = replay.MapSize.X;
                }
                else if (replay.MapSize.X == 0)
                {
                    replay.MapSize.X = replay.MapSize.Y;
                }

                // About 1000 bytes from here is a list of characters, character skins, character mounts, artifact selections, and other data
            }
        }