Example #1
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 playerListLength = reader.Read(5);
                for (var i = 0; i < playerListLength; i++)
                {
                    var playerName = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(8)); // Player name

                    // Populate the name for each client in the client list by UserID
                    if (playerName != "")
                    {
                        replay.ClientListByUserID[i] = new Player {
                            Name = playerName
                        }
                    }
                    ;

                    if (reader.ReadBoolean())
                    {
                        reader.ReadBlobPrecededWithLength(8); // clanTag
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.ReadBlobPrecededWithLength(40); // Clan Logo
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.Read(8); // highestLeague
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.ReadInt32(); // 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

                    reader.ReadInt32();                                            // m_testType

                    reader.Read(2);                                                //observer

                    Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_hero - Currently Empty String
                    Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_skin - Currently Empty String
                    Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_mount - Currently Empty String
                    if (replay.ReplayVersionMajor >= 2)
                    {
                        Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9));                 // m_banner - Currently Empty String
                        Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9));                 // m_spray - Currently Empty String
                    }
                    Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(7));                     // m_toonHandle - Currently Empty String
                }

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

                reader.ReadBlobPrecededWithLength(10);  // m_gameCacheName - "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();                   // m_practice
                reader.ReadBoolean();                   // m_cooperative
                reader.ReadBoolean();                   // m_noVictoryOrDefeat
                reader.ReadBoolean();                   // m_heroDuplicatesAllowed
                reader.Read(2);                         // Fog
                reader.Read(2);                         // Observers
                reader.Read(2);                         // User Difficulty
                reader.ReadInt32(); reader.ReadInt32(); // 64 bit int: Client Debug Flags

                // m_ammId
                if (replay.ReplayBuild >= 43905 && reader.ReadBoolean())
                {
                    switch (reader.ReadInt32())
                    {
                    case 50021:     // Versus AI (Cooperative)
                    case 50041:     // Practice
                        break;

                    case 50001:
                        replay.GameMode = GameMode.QuickMatch;
                        break;

                    case 50031:
                        replay.GameMode = GameMode.Brawl;
                        break;

                    case 50051:
                        replay.GameMode = GameMode.UnrankedDraft;
                        break;

                    case 50061:
                        replay.GameMode = GameMode.HeroLeague;
                        break;

                    case 50071:
                        replay.GameMode = GameMode.TeamLeague;
                        break;

                    default:
                        replay.GameMode = GameMode.Unknown;
                        break;
                    }
                }

                reader.Read(3); // Game Speed

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

                var maxUsers = reader.Read(5);
                if (maxUsers != 10 && replay.GameMode != GameMode.Brawl) // 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

                // Max Controls
                if (replay.ReplayBuild < 59279)
                {
                    reader.Read(8);
                }
                else
                {
                    reader.Read(4);
                }

                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;
                }

                // I haven't tested the following code on replays before build 39595 (End of 2015)
                if (replay.ReplayBuild < 39595)
                {
                    return;
                }

                reader.Read(32);                       // m_mapFileSyncChecksum
                reader.ReadBlobPrecededWithLength(11); // m_mapFileName
                reader.ReadBlobPrecededWithLength(8);  // m_mapAuthorName
                reader.Read(32);                       // m_modFileSyncChecksum

                // m_slotDescriptions
                var slotDescriptionLength = reader.Read(5);
                for (var i = 0; i < slotDescriptionLength; i++)
                {
                    reader.ReadBitArray(reader.Read(6)); // m_allowedColors
                    reader.ReadBitArray(reader.Read(8)); // m_allowedRaces
                    reader.ReadBitArray(reader.Read(6)); // m_allowedDifficulty

                    // m_allowedControls
                    if (replay.ReplayBuild < 59279)
                    {
                        reader.ReadBitArray(reader.Read(8));
                    }
                    else
                    {
                        reader.ReadBitArray(reader.Read(4));
                    }

                    reader.ReadBitArray(reader.Read(2)); // m_allowedObserveTypes
                    reader.ReadBitArray(reader.Read(7)); // m_allowedAIBuilds
                }

                reader.Read(6); // m_defaultDifficulty
                reader.Read(7); // m_defaultAIBuild

                // m_cacheHandles
                var cacheHandlesLength = reader.Read(6);
                for (var i = 0; i < cacheHandlesLength; i++)
                {
                    reader.ReadBytes(40);
                }

                reader.ReadBoolean(); // m_hasExtensionMod
                reader.ReadBoolean(); // m_isBlizzardMap
                reader.ReadBoolean(); // m_isPremadeFFA
                reader.ReadBoolean(); // m_isCoopMode

                #region m_lobbyState

                reader.Read(3); // m_phase
                reader.Read(5); // m_maxUsers
                reader.Read(5); // m_maxObservers

                // m_slots
                var slotsLength = reader.Read(5);
                for (var i = 0; i < slotsLength; i++)
                {
                    int?userID = null;

                    reader.Read(8); // m_control
                    if (reader.ReadBoolean())
                    {
                        userID = (int)reader.Read(4); // m_userId
                    }
                    reader.Read(4);                   // m_teamId
                    if (reader.ReadBoolean())
                    {
                        reader.Read(5); // m_colorPref
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.Read(8); // m_racePref
                    }
                    reader.Read(6);     // m_difficulty
                    reader.Read(7);     // m_aiBuild
                    reader.Read(7);     // m_handicap

                    // m_observe
                    var observerStatus = reader.Read(2);

                    reader.Read(32);                                                                      // m_logoIndex

                    reader.ReadBlobPrecededWithLength(9);                                                 // m_hero

                    var skinAndSkinTint = Encoding.ASCII.GetString(reader.ReadBlobPrecededWithLength(9)); // m_skin
                    if (skinAndSkinTint == "")
                    {
                        skinAndSkinTint = null;
                    }

                    var mountAndMountTint = Encoding.ASCII.GetString(reader.ReadBlobPrecededWithLength(9)); // m_mount
                    if (mountAndMountTint == "")
                    {
                        mountAndMountTint = null;
                    }

                    // m_artifacts
                    var artifactsLength = reader.Read(4);
                    for (var j = 0; j < artifactsLength; j++)
                    {
                        reader.ReadBlobPrecededWithLength(9);
                    }

                    int?workingSetSlotID = null;
                    if (reader.ReadBoolean())
                    {
                        workingSetSlotID = (int)reader.Read(8);  // m_workingSetSlotId
                    }
                    if (userID.HasValue && workingSetSlotID.HasValue)
                    {
                        if (replay.ClientListByWorkingSetSlotID[workingSetSlotID.Value] != null)
                        {
                            replay.ClientListByUserID[userID.Value] = replay.ClientListByWorkingSetSlotID[workingSetSlotID.Value];
                        }

                        if (observerStatus == 2)
                        {
                            replay.ClientListByUserID[userID.Value].PlayerType = PlayerType.Spectator;
                        }

                        replay.ClientListByUserID[userID.Value].SkinAndSkinTint   = skinAndSkinTint;
                        replay.ClientListByUserID[userID.Value].MountAndMountTint = mountAndMountTint;
                    }

                    // m_rewards
                    var rewardsLength = reader.Read(17);
                    for (var j = 0; j < rewardsLength; j++)
                    {
                        reader.Read(32);
                    }

                    reader.ReadBlobPrecededWithLength(7); // m_toonHandle

                    // m_licenses
                    if (replay.ReplayBuild < 49582 || replay.ReplayBuild == 49838)
                    {
                        var licensesLength = reader.Read(9);
                        for (var j = 0; j < licensesLength; j++)
                        {
                            reader.Read(32);
                        }
                    }

                    if (reader.ReadBoolean())
                    {
                        reader.Read(4); // m_tandemLeaderUserId
                    }
                    if (replay.ReplayBuild <= 41504)
                    {
                        reader.ReadBlobPrecededWithLength(9); // m_commander - Empty string

                        reader.Read(32);                      // m_commanderLevel - So far, always 0
                    }

                    if (reader.ReadBoolean() && userID.HasValue)                     // m_hasSilencePenalty
                    {
                        replay.ClientListByUserID[userID.Value].IsSilenced = true;
                    }

                    if (replay.ReplayVersionMajor >= 2)
                    {
                        Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9));                         // m_banner
                        Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9));                         // m_spray
                        Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9));                         // m_announcerPack
                        Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9));                         // m_voiceLine

                        // m_heroMasteryTiers
                        if (replay.ReplayBuild >= 52561)
                        {
                            var heroMasteryTiersLength = reader.Read(10);
                            for (var j = 0; j < heroMasteryTiersLength; j++)
                            {
                                reader.Read(32);                                // m_hero
                                reader.Read(8);                                 // m_tier
                            }
                        }
                    }
                }

                if (reader.Read(32) != replay.RandomValue) // m_randomSeed
                {
                    throw new Exception("Replay Random Seed Values in Replay Init Data did not match");
                }

                if (reader.ReadBoolean())
                {
                    reader.Read(4);   // m_hostUserId
                }
                reader.ReadBoolean(); // m_isSinglePlayer

                reader.Read(8);       // m_pickedMapTag - So far, always 0

                reader.Read(32);      // m_gameDuration - So far, always 0

                reader.Read(6);       // m_defaultDifficulty

                reader.Read(7);       // m_defaultAIBuild

                #endregion
            }
        }
    }
        /// <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, bool partialParse)
        {
            using (var stream = new MemoryStream(buffer))
            {
                var reader = new BitReader(stream);

                var playerListLength = reader.Read(5);
                for (var i = 0; i < playerListLength; i++)
                {
                    var playerName = Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(8)); // Player name

                    if (replay.ClientList[i] == null && playerName != "")
                    {
                        replay.ClientList[i] = new Player {
                            Name = playerName
                        }
                    }
                    ;

                    if (reader.ReadBoolean())
                    {
                        reader.ReadBlobPrecededWithLength(8); // clanTag
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.ReadBlobPrecededWithLength(40); // Clan Logo
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.Read(8); // highestLeague
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.ReadInt32(); // 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

                    reader.ReadInt32();                                            // m_testType

                    reader.Read(2);                                                //observer

                    Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_hero - Currently Empty String
                    Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_skin - Currently Empty String
                    Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(9)); // m_mount - Currently Empty String
                    Encoding.UTF8.GetString(reader.ReadBlobPrecededWithLength(7)); // m_toonHandle - Currently Empty String
                }

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

                reader.ReadBlobPrecededWithLength(10);  // m_gameCacheName - "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();                   // m_practice
                reader.ReadBoolean();                   // m_cooperative
                reader.ReadBoolean();                   // m_noVictoryOrDefeat
                reader.ReadBoolean();                   // m_heroDuplicatesAllowed
                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;
                }

                // I haven't tested the following code on replays before build 39595 (End of 2015)
                if (partialParse || replay.ReplayBuild < 39595)
                {
                    return;
                }

                reader.Read(32);                       // m_mapFileSyncChecksum
                reader.ReadBlobPrecededWithLength(11); // m_mapFileName
                reader.ReadBlobPrecededWithLength(8);  // m_mapAuthorName
                reader.Read(32);                       // m_modFileSyncChecksum

                // m_slotDescriptions
                var slotDescriptionLength = reader.Read(5);
                for (var i = 0; i < slotDescriptionLength; i++)
                {
                    reader.ReadBitArray(reader.Read(6)); // m_allowedColors
                    reader.ReadBitArray(reader.Read(8)); // m_allowedRaces
                    reader.ReadBitArray(reader.Read(6)); // m_allowedDifficulty
                    reader.ReadBitArray(reader.Read(8)); // m_allowedControls
                    reader.ReadBitArray(reader.Read(2)); // m_allowedObserveTypes
                    reader.ReadBitArray(reader.Read(7)); // m_allowedAIBuilds
                }

                reader.Read(6); // m_defaultDifficulty
                reader.Read(7); // m_defaultAIBuild

                // m_cacheHandles
                var cacheHandlesLength = reader.Read(6);
                for (var i = 0; i < cacheHandlesLength; i++)
                {
                    reader.ReadBytes(40);
                }

                reader.ReadBoolean(); // m_hasExtensionMod
                reader.ReadBoolean(); // m_isBlizzardMap
                reader.ReadBoolean(); // m_isPremadeFFA
                reader.ReadBoolean(); // m_isCoopMode

                #region m_lobbyState

                reader.Read(3); // m_phase
                reader.Read(5); // m_maxUsers
                reader.Read(5); // m_maxObservers

                // m_slots
                var slotsLength = reader.Read(5);
                for (var i = 0; i < slotsLength; i++)
                {
                    int?clientListIndex = null;

                    reader.Read(8); // m_control
                    if (reader.ReadBoolean())
                    {
                        clientListIndex = (int)reader.Read(4); // m_userId
                    }
                    reader.Read(4);                            // m_teamId
                    if (reader.ReadBoolean())
                    {
                        reader.Read(5); // m_colorPref
                    }
                    if (reader.ReadBoolean())
                    {
                        reader.Read(8); // m_racePref
                    }
                    reader.Read(6);     // m_difficulty
                    reader.Read(7);     // m_aiBuild
                    reader.Read(7);     // m_handicap

                    // m_observe
                    var observerStatus = reader.Read(2);
                    if (observerStatus == 2)
                    {
                        replay.ClientList[clientListIndex.Value].PlayerType = PlayerType.Spectator;
                    }

                    reader.Read(32);                                                                      // m_logoIndex

                    reader.ReadBlobPrecededWithLength(9);                                                 // m_hero

                    var skinAndSkinTint = Encoding.ASCII.GetString(reader.ReadBlobPrecededWithLength(9)); // m_skin
                    if (skinAndSkinTint == "")
                    {
                        skinAndSkinTint = null;
                    }
                    if (clientListIndex.HasValue && replay.ClientList[clientListIndex.Value] != null)
                    {
                        replay.ClientList[clientListIndex.Value].SkinAndSkinTint = skinAndSkinTint;
                    }

                    var mountAndMountTint = Encoding.ASCII.GetString(reader.ReadBlobPrecededWithLength(9)); // m_mount
                    if (mountAndMountTint == "")
                    {
                        mountAndMountTint = null;
                    }
                    if (clientListIndex.HasValue && replay.ClientList[clientListIndex.Value] != null)
                    {
                        replay.ClientList[clientListIndex.Value].MountAndMountTint = mountAndMountTint;
                    }

                    // m_artifacts
                    var artifactsLength = reader.Read(4);
                    for (var j = 0; j < artifactsLength; j++)
                    {
                        reader.ReadBlobPrecededWithLength(9);
                    }

                    if (reader.ReadBoolean())
                    {
                        reader.Read(8); // m_workingSetSlotId
                    }
                    // m_rewards
                    var rewardsLength = reader.Read(17);
                    for (var j = 0; j < rewardsLength; j++)
                    {
                        reader.Read(32);
                    }

                    reader.ReadBlobPrecededWithLength(7); // m_toonHandle

                    // m_licenses
                    var licensesLength = reader.Read(9);
                    for (var j = 0; j < licensesLength; j++)
                    {
                        reader.Read(32);
                    }

                    if (reader.ReadBoolean())
                    {
                        reader.Read(4);                   // m_tandemLeaderUserId
                    }
                    reader.ReadBlobPrecededWithLength(9); // m_commander - Empty string

                    reader.Read(32);                      // m_commanderLevel - So far, always 0

                    reader.ReadBoolean();                 // m_hasSilencePenalty
                }

                if (reader.Read(32) != replay.RandomValue) // m_randomSeed
                {
                    throw new Exception("Replay Random Seed Values in Replay Init Data did not match");
                }

                if (reader.ReadBoolean())
                {
                    reader.Read(4);   // m_hostUserId
                }
                reader.ReadBoolean(); // m_isSinglePlayer

                reader.Read(8);       // m_pickedMapTag - So far, always 0

                reader.Read(32);      // m_gameDuration - So far, always 0

                reader.Read(6);       // m_defaultDifficulty

                reader.Read(7);       // m_defaultAIBuild

                #endregion
            }
        }
    }