/// <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
            }
        }
Exemplo n.º 2
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
        }
        /// <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
                // --------------------------------
            }
        }
Exemplo n.º 4
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
            }
        }