public PlayerJoinEvent(BitReader bitReader, Replay replay, int playerIndex)
        {
            this.EventType = GameEventType.Inactive;

            // This should probably be a series of {shl; or} on .Read(1)
            // to make it version-independent
            if (replay.ReplayBuild < 22612)
            {
                this.JoinFlags = (int)bitReader.Read(4);
            }
            else
            {
                this.JoinFlags = (int)bitReader.Read(12); // unknown
            }

            // Initialize player if not exists (true for observers)
            Player player = replay.GetPlayerById(playerIndex);
            if (player == null)
            {
                var p = new Player { PlayerType = PlayerType.Spectator };
                replay.ClientList[playerIndex] = player = p;
            }

            // Initialize wireframe
            player.Wireframe = new List<Unit>();
            player.WireframeSubgroup = 0;

            // Initialize control groups
            player.Hotkeys = new List<Unit>[10];
        }
        public CameraEvent(BitReader bitReader, Replay replay)
        {
            TargetX = CFixedToDouble(bitReader.Read(16));
            TargetY = CFixedToDouble(bitReader.Read(16));

            HasDistance = bitReader.Read(1) != 0;
            if (HasDistance)
            {
                Distance = CFixedToDouble(bitReader.Read(16));
            }

            HasPitch = bitReader.Read(1) != 0;
            if (HasPitch)
            {
                Pitch = RotationAmountToDegrees(bitReader.Read(16));
            }

            HasYaw = bitReader.Read(1) != 0;
            if (HasYaw)
            {
                Yaw = RotationAmountToDegrees(bitReader.Read(16));
            }

            HasHeightOffset = bitReader.Read(1) != 0;
            if (HasHeightOffset)
            {
                // Debug since we're unsure
                HeightOffset = CFixedToDouble(bitReader.Read(16));
            }

            this.EventType = GameEventType.Other;
        }
        public ReplayEditorViewModel(string replayPath)
        {
            this.CurrentFile = replayPath;

            try
            {
                this.replay = Replay.Parse(this.CurrentFile);
            }
            catch (Exception ex)
            {
                MessageBox.Show("The replay failed to load. Reason: " + ex.Message);
                return;
            }

            this.ReplayHeader = Path.GetFileName(this.CurrentFile);
            var messages = new ObservableCollection<PlayerChatMessage>();

            foreach (var message in this.replay.ChatMessages)
            {
                int id = message.PlayerId - 1;
                if (id < this.replay.Players.Length)
                {
                    var chat = new PlayerChatMessage { ChatMessage = message, Player = this.replay.Players[message.PlayerId - 1] };
                    messages.Add(chat);
                }
            }

            this.ChatMessageEditor = new ChatMessageEditViewModel { PlayerList = this.replay.Players };

            this.ChatMessages = messages;
        }
        /// <summary> Adds a single chat message to a replay. </summary>
        /// <param name="fileName"> The file name.  </param>
        /// <param name="messages"> The messages to add.  </param>
        public static void AddChatMessageToReplay(string fileName, IEnumerable<ChatMessage> messages)
        {
            var replay = new Replay();

            // File in the version numbers for later use.
            MpqHeader.ParseHeader(replay, fileName);

            using (var archive = new CArchive(fileName))
            {
                var files = archive.FindFiles("replay.*");
                {
                    const string CurFile = "replay.message.events";
                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    foreach (var message in messages)
                    {
                        buffer = GenerateChatMessage(
                            buffer,
                            message.Message,
                            message.MessageTarget,
                            message.PlayerId,
                            (int)message.Timestamp.TotalSeconds);
                    }

                    archive.ImportFile("replay.message.events", buffer);
                }

                archive.Close();
            }
        }
        public SendResourcesEvent(BitReader bitReader, Replay replay)
        {
            this.EventType = GameEventType.Other;

            var playerId = (int)bitReader.Read(4);
            Target = replay.GetPlayerById(playerId);

            var someFlags = (int)bitReader.Read(3);

            if (someFlags-- > 0) // 4
            {
                MineralsSent = ReadSignedAmount(bitReader.Read(32));
            }
            if (someFlags-- > 0) // 3
            {
                VespeneSent = ReadSignedAmount(bitReader.Read(32));
            }
            if (someFlags-- > 0) // 2
            {
                TerrazineSent = ReadSignedAmount(bitReader.Read(32));
            }
            if (someFlags-- > 0) // 1
            {
                CustomSent = ReadSignedAmount(bitReader.Read(32));
            }
        }
        /// <summary> Adds a single chat message to a replay. </summary>
        /// <param name="fileName"> The file name.  </param>
        /// <param name="message"> The message.  </param>
        /// <param name="target"> The target. </param>
        /// <param name="playerId"> The player id.  </param>
        /// <param name="numSeconds"> The number of in-game seconds to insert the message at.  </param>
        public static void AddChatMessageToReplay(
            string fileName, string message, ChatMessageTarget target, int playerId, int numSeconds)
        {
            var replay = new Replay();

            // File in the version numbers for later use.
            MpqHeader.ParseHeader(replay, fileName);

            using (var archive = new CArchive(fileName))
            {
                var files = archive.FindFiles("replay.*");
                {
                    const string CurFile = "replay.message.events";
                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    var arr = GenerateChatMessage(buffer, message, target, playerId, numSeconds);
                    archive.ImportFile("replay.message.events", arr);
                }

                archive.Close();
            }
        }
        public static void Parse(Replay replay, byte[] buffer)
        {
            using (var stream = new MemoryStream(buffer))
            {
                using (var reader = new BinaryReader(stream))
                {
                    var i = reader.ReadByte();

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

                        playerList[j] = str;
                        reader.ReadBytes(5);
                    }

                    if (positionAfter(reader, new byte[] { 115, 50, 109, 97 }))
                    {
                        reader.ReadBytes(2);
                        var gatewayStr = reader.ReadBytes(2);

                        var gateway = Encoding.UTF8.GetString(gatewayStr);
                        replay.Gateway = gateway;
                    }
                    else
                    {
                        replay.GameType = GameType.SinglePlayer;
                    }
                }
            }
        }
        /// <summary> Parses the replay.details file, applying it to a Replay object. </summary>
        /// <param name="replay"> The replay object to apply the parsed information to. </param>
        /// <param name="buffer"> The buffer containing the replay.details file. </param>
        public static void Parse(Replay replay, byte[] buffer)
        {
            using (var stream = new MemoryStream(buffer, false))
            {
                Parse(replay, stream);

                stream.Close();
            }
        }
        /// <summary> Parses the replay.details file, applying it to a Replay object. </summary>
        /// <param name="replay"> The replay object to apply the parsed information to. </param>
        /// <param name="stream"> The stream containing the replay.details file. </param>
        public static void Parse(Replay replay, Stream stream)
        {
            using (var reader = new BinaryReader(stream))
            {
                reader.ReadBytes(6);
                var playerCount = reader.ReadByte() >> 1;

                // Parsing Player Info
                var players = new Player[playerCount];

                for (int i = 0; i < playerCount; i++)
                {
                    players[i] = PlayerDetails.Parse(reader);
                }

                replay.Players = players;

                var mapNameLength = KeyValueStruct.Parse(reader).Value;

                var mapBytes = reader.ReadBytes(mapNameLength);

                replay.Map = Encoding.UTF8.GetString(mapBytes);

                var stringLength = KeyValueStruct.Parse(reader).Value;

                // This is typically an empty string, no need to decode.
                var unknownString = reader.ReadBytes(stringLength);

                reader.ReadBytes(3);

                var mapPreviewNameLength = KeyValueStruct.Parse(reader).Value;
                var mapPreviewNameBytes = reader.ReadBytes(mapPreviewNameLength);

                // What have I learned:
                // While I can get the name of the map preview file, apparently MPQLib.dll will not
                // Support exporting the file since its not in the file list. I tried, and it threw an error...
                // Maybe my buffer wasn't big enough, but it was some temporary file error, so I don't think so.
                var mapPreviewName = Encoding.UTF8.GetString(mapPreviewNameBytes);

                reader.ReadBytes(3);

                var saveTime = KeyValueLongStruct.Parse(reader).Value;
                var saveTimeZone = KeyValueLongStruct.Parse(reader).Value;
                var time = DateTime.FromFileTime(saveTime);

                // Subtract the timezone to get the appropriate UTC time.
                time = time.Subtract(new TimeSpan(saveTimeZone));

                // We create a new timestamp so we can properly set this as UTC time.
                replay.Timestamp = new DateTime(time.Ticks, DateTimeKind.Utc);

                reader.Close();
            }
        }
        public static void Parse(Replay replay, byte[] buffer)
        {
            int headerSize = 4;

            if (replay.ReplayBuild >= 17326) // 1.2.0
            {
                headerSize = 5;
            }

            var numAttributes = BitConverter.ToInt32(buffer, headerSize);

            var attributes = new ReplayAttribute[numAttributes];

            int initialOffset = 4 + headerSize;

            for (int i = 0; i < numAttributes; i++)
            {
                attributes[i] = ReplayAttribute.Parse(buffer, initialOffset + (i*13));
            }

            var rae = new ReplayAttributeEvents { Attributes = attributes };
            rae.ApplyAttributes(replay);
        }
        public RequestResourcesEvent(BitReader bitReader, Replay replay)
        {
            this.EventType = GameEventType.Other;

            var someFlags = (int)bitReader.Read(3);

            if (someFlags-- > 0) // 4
            {
                MineralsRequested = ReadSignedAmount(bitReader.Read(32));
            }
            if (someFlags-- > 0) // 3
            {
                VespeneRequested = ReadSignedAmount(bitReader.Read(32));
            }
            if (someFlags-- > 0) // 2
            {
                TerrazineRequested = ReadSignedAmount(bitReader.Read(32));
            }
            if (someFlags-- > 0) // 1
            {
                CustomRequested = ReadSignedAmount(bitReader.Read(32));
            }
        }
        /// <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))
            {
                using (var reader = new BinaryReader(stream))
                {
                    var i = reader.ReadByte();

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

                        playerList[j] = str;
                        reader.ReadBytes(5);
                    }

                    // Save the full list of clients.
                    // This is no longer necessary since we get the client list elsewhere.
                    //// replay.ClientList = playerList;

                    if (PositionAfter(reader, new byte[] { 115, 50, 109, 97 }))
                    {
                        reader.ReadBytes(2);
                        var gatewayStr = reader.ReadBytes(2);

                        var gateway = Encoding.UTF8.GetString(gatewayStr);
                        replay.Gateway = gateway;
                    }
                    else
                    {
                        replay.GameType = GameType.SinglePlayer;
                    }
                }
            }
        }
Example #13
0
        /// <summary>
        /// Parses the MPQ header on a file to determine version and build numbers.
        /// </summary>
        /// <param name="replay">Replay object to store </param>
        /// <param name="filename">Filename of the file to open.</param>
        public static void ParseHeader(Replay replay, string filename)
        {
            using (var reader = new BinaryReader(new FileStream(filename, FileMode.Open)))
            {
                byte[] magic = reader.ReadBytes(3);
                byte format = reader.ReadByte();

                byte[] buffer = reader.ReadBytes(4);
                var dataMaxSize = BitConverter.ToInt32(buffer, 0);

                buffer = reader.ReadBytes(4);
                int headerOffset = BitConverter.ToInt32(buffer, 0);

                buffer = reader.ReadBytes(4);
                int userDataHeaderSize = BitConverter.ToInt32(buffer, 0);

                int dataType = reader.ReadByte(); // Should be 0x05 = Array w/ Keys

                int numElements = ParseVlfNumber(reader);

                // The first value is the words: "Starcraft II Replay 11"
                int index = ParseVlfNumber(reader);

                int type = reader.ReadByte(); // Should be 0x02 = binary data

                int numValues = ParseVlfNumber(reader);
                byte[] starcraft2 = reader.ReadBytes(numValues);

                int index2 = ParseVlfNumber(reader);
                int type2 = reader.ReadByte(); // Should be 0x05 = Array w/ Keys

                int numElementsVersion = ParseVlfNumber(reader);
                var version = new int[numElementsVersion];

                while (numElementsVersion > 0)
                {
                    int i = ParseVlfNumber(reader);
                    int t = reader.ReadByte(); // Type;

                    if (t == 0x09)
                    {
                        // VLF
                        version[i] = ParseVlfNumber(reader);
                    }
                    else if (t == 0x06)
                    {
                        // Byte
                        version[i] = reader.ReadByte();
                    }
                    else if (t == 0x07)
                    {
                        // 4 Bytes
                        version[i] = BitConverter.ToInt32(reader.ReadBytes(4), 0);
                    }

                    numElementsVersion--;
                }

                // We now have the version. Just parse.
                replay.ReplayVersion = string.Format("{0}.{1}.{2}.{3}", version[0], version[1], version[2], version[3]);
                replay.ReplayBuild = version[4];

                reader.Close();
            }
        }
        public AbilityEvent(BitReader bitReader, Replay replay, Player player, AbilityData abilityData, UnitData unitData)
        {
            uint flags;
            //   1.3.3 patch notes:
            //   - Fixed an issue where the APM statistic could be artificially increased.
            // This adds the "failed" flag, which is triggered usually by holding down a
            // hotkey, leading to key repeat spamming the event throughout a single tick.
            if (replay.ReplayBuild < 18574) // < 1.3.3
            {
                flags = bitReader.Read(17);
            }
            else if (replay.ReplayBuild < 22612) // < 1.5.0
            {
                flags = bitReader.Read(18);
            }
            else
            {
                flags = bitReader.Read(20);
            }
            Queued = (flags & 2) != 0;
            RightClick = (flags & 8) != 0;
            WireframeClick = (flags & 0x20) != 0;
            ToggleAbility = (flags & 0x40) != 0;
            EnableAutoCast = (flags & 0x80) != 0;
            AbilityUsed = (flags & 0x100) != 0;
            WireframeUnload = (flags & 0x200) != 0;
            WireframeCancel = (flags & 0x400) != 0;
            MinimapClick = (flags & 0x10000) != 0;
            AbilityFailed = (flags & 0x20000) != 0;

            // flags & 0xf815 -> Debug for unknown flags
            // Never found any across all test data.

            DefaultAbility = (bitReader.Read(1) == 0);
            DefaultActor = true;
            if (!DefaultAbility)
            {
                AbilityType = abilityData.GetAbilityType(
                    (int)bitReader.Read(16),
                    (int)bitReader.Read(5));
                DefaultActor = (bitReader.Read(1) == 0);
                if (!DefaultActor)
                {   // I'm thinking this would be an array type... but I can't
                    // find anything that causes this bit to be set.
                    throw new InvalidOperationException("Unsupported: non-default actor");
                }
            }
            if (DefaultActor)
            {
                // Deep copy the current wireframe as the actor list
                // -----
                // If a user wants to deal with subgroups to get a more
                // concise actor list, the data is all here.  We're not
                // going to bother, though, because there are several
                // exceptions to account for in determining event actors.
                Actors = new List<Unit>(player.Wireframe.Count);
                foreach (var unit in player.Wireframe)
                {
                    Actors.Add(new Unit(unit));
                }
            }

            var targetType = bitReader.Read(2);
            if (targetType == 1) // Location target
            {
                var targetX = bitReader.Read(20);
                var targetY = bitReader.Read(20);
                var targetZ = bitReader.Read(32);
                TargetLocation = Location.FromEventFormat(targetX, targetY, targetZ);
            }
            else if (targetType == 2) // Unit + Location target
            {
                TargetFlags = (int)bitReader.Read(8);
                WireframeIndex = (int)bitReader.Read(8);

                var unitId = (int)bitReader.Read(32);
                var unit = replay.GetUnitById(unitId);
                var unitTypeId = (int)bitReader.Read(16);
                if (unit == null)
                {
                    var unitType = unitData.GetUnitType(unitTypeId);
                    unit = new Unit(unitId, unitType);
                    unit.typeId = unitTypeId;
                    replay.GameUnits.Add(unitId, unit);
                }

                TargetUnit = unit;

                var targetHasPlayer = bitReader.Read(1) == 1;
                if (targetHasPlayer)
                {
                    TargetPlayer = (int)bitReader.Read(4);
                }

                // 1.4.0 -- Don't really know what this was meant to fix
                if (replay.ReplayBuild >= 19595)
                {
                    var targetHasTeam = bitReader.Read(1) == 1;
                    if (targetHasTeam)
                    {
                        TargetTeam = (int)bitReader.Read(4);
                    }
                }

                var targetX = bitReader.Read(20);
                var targetY = bitReader.Read(20);
                var targetZ = bitReader.Read(32);
                TargetLocation = Location.FromEventFormat(targetX, targetY, targetZ);
            }
            else if (targetType == 3) // Unit target
            {
                var id = bitReader.Read(32);
                // Again, if the user wants to determine exactly which
                // queue item is canceled in the case of a queue cancel
                // event (the most common case of this target specifier's
                // occurence), they can; however, it requires an additional
                // data structure that I don't want to bother with; however,
                // all the underlying data is available in the events list.
                TargetId = id;
            }

            var lastBit = bitReader.Read(1); // Should be 0; if not, misalignment is likely

            if (!AbilityFailed)
            {
                if (RightClick)
                {
                    this.EventType = GameEventType.RightClick;
                }
                else
                {
                    this.EventType = EventData.GetInstance().GetEventType(this.AbilityType);
                }
            }
            else
            {
                this.EventType = GameEventType.Inactive;
            }
        }
        public SelectionEvent(BitReader bitReader, Replay replay, Player player, UnitData data)
        {
            int wireframeLength = 8;
            if (replay.ReplayBuild >= 22612)
            {
                wireframeLength = 9; // Maximum selection size has been increased to 500, up from 255.
            }

            // Parse select event and update player wireframe accordingly
            WireframeIndex = (int)bitReader.Read(4);
            player.WireframeSubgroup = SubgroupIndex = (int)bitReader.Read(wireframeLength);

            if (WireframeIndex == 10)
            {
                this.EventType = GameEventType.Selection;
            }
            else // This is a control group update, likely from a CAbilMorph
            {
                this.EventType = GameEventType.Inactive;
            }

            List<Unit> affectedWireframe;
            if (WireframeIndex == 10)
            {
                affectedWireframe = player.Wireframe;
            }
            else
            {
                affectedWireframe = player.Hotkeys[WireframeIndex];
            }

            RemovedUnits = new List<Unit>();

            var updateFlags = (int)bitReader.Read(2);

            ClearSelection = false;

            if (updateFlags == 1)
            {
                var numBits = (int)bitReader.Read(wireframeLength);
                var unitsRemoved = new bool[numBits];

                var wireframeIndex = 0;
                while (numBits >= 8)
                {
                    numBits -= 8;
                    var flags = bitReader.Read(8);
                    for (int i = 0; i < 8; i++)
                    {
                        unitsRemoved[wireframeIndex + i] = (flags & (1 << i)) != 0;
                    }
                    wireframeIndex += 8;
                }
                if (numBits != 0)
                {
                    var flags = bitReader.Read(numBits);
                    for (int i = 0; i < numBits; i++)
                    {
                        unitsRemoved[wireframeIndex + i] = (flags & (1 << i)) != 0;
                    }
                    wireframeIndex += numBits;
                }

                for (int i = 0; i < wireframeIndex; i++)
                {
                    if (unitsRemoved[i])
                    {
                        RemovedUnits.Add(affectedWireframe[i]);
                    }
                }
            }
            else if (updateFlags == 2)
            {
                var indexArrayLength = (int)bitReader.Read(wireframeLength);
                if (indexArrayLength > 0)
                {
                    for (int i = 0; i < indexArrayLength; i++)
                    {
                        RemovedUnits.Add(affectedWireframe[(int)bitReader.Read(wireframeLength)]);
                    }
                }
            }
            else if (updateFlags == 3)
            {
                var indexArrayLength = (int)bitReader.Read(wireframeLength);
                if (indexArrayLength > 0)
                {
                    AddedUnits = new List<Unit>(indexArrayLength);
                    for (int i = 0; i < indexArrayLength; i++)
                    {
                        AddedUnits.Add(affectedWireframe[(int)bitReader.Read(wireframeLength)]);
                    }
                }

                ClearSelection = true;
            }

            // Build removed unit types
            RemovedUnitTypes = new Dictionary<UnitType, int>();
            foreach (var unit in RemovedUnits)
            {
                if (!RemovedUnitTypes.ContainsKey(unit.Type))
                {
                    RemovedUnitTypes.Add(unit.Type, 1);
                }
                else
                {
                    RemovedUnitTypes[unit.Type]++;
                }
            }

            HandleUnitArrays(bitReader, replay, data);

            // Now, update the player wireframe.
            UpdateWireframe(player);

            // Check for Morph update
            if (AddedUnits.SequenceEqual(RemovedUnits))
            {
                this.EventType = GameEventType.Inactive;
            }
        }
Example #16
0
        public SelectionEvent(BitReader bitReader, Replay replay, Player player, UnitData data)
        {
            int wireframeLength = 8;

            if (replay.ReplayBuild >= 22612)
            {
                wireframeLength = 9; // Maximum selection size has been increased to 500, up from 255.
            }

            // Parse select event and update player wireframe accordingly
            WireframeIndex           = (int)bitReader.Read(4);
            player.WireframeSubgroup = SubgroupIndex = (int)bitReader.Read(wireframeLength);

            if (WireframeIndex == 10)
            {
                this.EventType = GameEventType.Selection;
            }
            else // This is a control group update, likely from a CAbilMorph
            {
                this.EventType = GameEventType.Inactive;
            }

            List <Unit> affectedWireframe;

            if (WireframeIndex == 10)
            {
                affectedWireframe = player.Wireframe;
            }
            else
            {
                affectedWireframe = player.Hotkeys[WireframeIndex];
            }

            RemovedUnits = new List <Unit>();

            var updateFlags = (int)bitReader.Read(2);

            ClearSelection = false;

            if (updateFlags == 1)
            {
                var numBits      = (int)bitReader.Read(wireframeLength);
                var unitsRemoved = new bool[numBits];

                var wireframeIndex = 0;
                while (numBits >= 8)
                {
                    numBits -= 8;
                    var flags = bitReader.Read(8);
                    for (int i = 0; i < 8; i++)
                    {
                        unitsRemoved[wireframeIndex + i] = (flags & (1 << i)) != 0;
                    }
                    wireframeIndex += 8;
                }
                if (numBits != 0)
                {
                    var flags = bitReader.Read(numBits);
                    for (int i = 0; i < numBits; i++)
                    {
                        unitsRemoved[wireframeIndex + i] = (flags & (1 << i)) != 0;
                    }
                    wireframeIndex += numBits;
                }

                for (int i = 0; i < wireframeIndex; i++)
                {
                    if (unitsRemoved[i])
                    {
                        RemovedUnits.Add(affectedWireframe[i]);
                    }
                }
            }
            else if (updateFlags == 2)
            {
                var indexArrayLength = (int)bitReader.Read(wireframeLength);
                if (indexArrayLength > 0)
                {
                    for (int i = 0; i < indexArrayLength; i++)
                    {
                        RemovedUnits.Add(affectedWireframe[(int)bitReader.Read(wireframeLength)]);
                    }
                }
            }
            else if (updateFlags == 3)
            {
                var indexArrayLength = (int)bitReader.Read(wireframeLength);
                if (indexArrayLength > 0)
                {
                    AddedUnits = new List <Unit>(indexArrayLength);
                    for (int i = 0; i < indexArrayLength; i++)
                    {
                        AddedUnits.Add(affectedWireframe[(int)bitReader.Read(wireframeLength)]);
                    }
                }

                ClearSelection = true;
            }

            // Build removed unit types
            RemovedUnitTypes = new Dictionary <UnitType, int>();
            foreach (var unit in RemovedUnits)
            {
                if (!RemovedUnitTypes.ContainsKey(unit.Type))
                {
                    RemovedUnitTypes.Add(unit.Type, 1);
                }
                else
                {
                    RemovedUnitTypes[unit.Type]++;
                }
            }

            HandleUnitArrays(bitReader, replay, data);

            // Now, update the player wireframe.
            UpdateWireframe(player);

            // Check for Morph update
            if (AddedUnits.SequenceEqual(RemovedUnits))
            {
                this.EventType = GameEventType.Inactive;
            }
        }
Example #17
0
        /// <summary> Parses a .SC2Replay file and returns relevant replay information.  </summary>
        /// <param name="fileName"> Full path to a .SC2Replay file.  </param>
        /// <returns> Returns the fully parsed Replay object. </returns>
        public static Replay Parse(string fileName)
        {
            if (File.Exists(fileName) == false)
            {
                throw new FileNotFoundException("The specified file does not exist.", fileName);
            }

            var replay = new Replay();

            // File in the version numbers for later use.
            MpqHeader.ParseHeader(replay, fileName);

            CArchive archive;

            try
            {
                archive = new CArchive(fileName);
            }
            catch (IOException)
            {
                // Usually thrown if the archive name contains korean. Copy it to a local file and open.
                var tmpPath = Path.GetTempFileName();

                File.Copy(fileName, tmpPath, true);

                archive = new CArchive(tmpPath);
            }

            try
            {
                var files = archive.FindFiles("replay.*");
                {
                    const string CurFile = "replay.initData";

                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    ReplayInitData.Parse(replay, buffer);
                }

                {
                    // Local scope allows the byte[] to be GC sooner, and prevents misreferences
                    const string CurFile = "replay.details";

                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    ReplayDetails.Parse(replay, buffer);
                }

                {
                    const string CurFile = "replay.attributes.events";
                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    ReplayAttributeEvents.Parse(replay, buffer);
                }

                {
                    const string CurFile = "replay.message.events";
                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    replay.ChatMessages = ReplayMessageEvents.Parse(buffer);
                }

                try
                {
                    const string CurFile = "replay.game.events";

                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    replay.PlayerEvents = ReplayGameEvents.Parse(replay, buffer);
                }
                catch (Exception)
                {
                    // In the current state, the parsing commonly fails.
                    // Incase of failing, we should probably just ignore the results of the parse
                    // And return.
                }
            }
            finally
            {
                archive.Dispose();
            }

            replay.Timestamp = File.GetCreationTime(fileName);

            return replay;
        }
Example #18
0
        /// <summary> Parses a .SC2Replay file and returns relevant replay information.  </summary>
        /// <param name="fileName"> Full path to a .SC2Replay file.  </param>
        /// <param name="noEvents"> True if you don't want to parse events (uses about 5~10 MB on a pro replay, half on an amateur replay) </param>
        /// <returns> Returns the fully parsed Replay object. </returns>
        public static Replay Parse(string fileName, bool noEvents = false)
        {
            if (File.Exists(fileName) == false)
            {
                throw new FileNotFoundException("The specified file does not exist.", fileName);
            }

            var replay = new Replay();

            // File in the version numbers for later use.
            MpqHeader.ParseHeader(replay, fileName);

            CArchive archive;

            try
            {
                archive = new CArchive(fileName);
            }
            catch (IOException)
            {
                // Usually thrown if the archive name contains korean. Copy it to a local file and open.
                var tmpPath = Path.GetTempFileName();

                File.Copy(fileName, tmpPath, true);

                archive = new CArchive(tmpPath);
            }

            try
            {
                var files = archive.FindFiles("replay.*");
                {
                    const string CurFile = "replay.initData";

                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    ReplayInitData.Parse(replay, buffer);
                }

                {
                    // Local scope allows the byte[] to be GC sooner, and prevents misreferences
                    const string CurFile = "replay.details";

                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    ReplayDetails.Parse(replay, buffer);
                }

                {
                    const string CurFile = "replay.attributes.events";
                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    ReplayAttributeEvents.Parse(replay, buffer);
                }

                {
                    const string CurFile = "replay.message.events";
                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);
                    try
                    {
                        replay.ChatMessages = ReplayMessageEvents.Parse(buffer);
                    }
                    catch // Chat may have been removed without maintaining the structure
                          // Example:  LiquidHayPro vs MFTarga.SC2Replay from TLPro pack #36
                          // You can see a date on the file in MPQ editor, and viewing the
                          // replay in SC2 results in no chat at all.
                    {
                        replay.ChatMessages = new List<ChatMessage>();
                    }
                }

                try
                {
                    if (!noEvents)
                    {
                        const string CurFile = "replay.game.events";

                        var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                        var buffer = new byte[fileSize];

                        archive.ExportFile(CurFile, buffer);

                        replay.PlayerEvents = ReplayGameEvents.Parse(replay, buffer);
                    }
                }
                catch (Exception)
                {
                    // Likely to happen with any non-standard (i.e. format isn't melee nvn, locked alliances) replay.
                }
            }
            finally
            {
                archive.Dispose();
            }

            replay.Timestamp = File.GetCreationTime(fileName);

            return replay;
        }
        /// <summary> Erases the entire chat log of a file. </summary>
        /// <param name="fileName"> The file name.  </param>
        public static void ClearChatLog(string fileName)
        {
            var replay = new Replay();

            // File in the version numbers for later use.
            MpqHeader.ParseHeader(replay, fileName);

            using (var archive = new CArchive(fileName))
            {
                var files = archive.FindFiles("replay.*");
                {
                    const string CurFile = "replay.message.events";
                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    var arr = ClearChatLog(buffer);
                    archive.ImportFile("replay.message.events", arr);
                }

                archive.Close();
            }
        }
        /// <summary>
        /// Parses the MPQ header on a file to determine version and build numbers.
        /// </summary>
        /// <param name="replay">Replay object to store </param>
        /// <param name="filename">Filename of the file to open.</param>
        public static void ParseHeader(Replay replay, string filename)
        {
            using (var reader = new BinaryReader(new FileStream(filename, FileMode.Open)))
            {
                byte[] magic  = reader.ReadBytes(3);
                byte   format = reader.ReadByte();

                byte[] buffer      = reader.ReadBytes(4);
                var    dataMaxSize = BitConverter.ToInt32(buffer, 0);

                buffer = reader.ReadBytes(4);
                int headerOffset = BitConverter.ToInt32(buffer, 0);

                buffer = reader.ReadBytes(4);
                int userDataHeaderSize = BitConverter.ToInt32(buffer, 0);

                int dataType = reader.ReadByte(); // Should be 0x05 = Array w/ Keys

                int numElements = ParseVlfNumber(reader);

                // The first value is the words: "Starcraft II Replay 11"
                int index = ParseVlfNumber(reader);

                int type = reader.ReadByte(); // Should be 0x02 = binary data

                int    numValues  = ParseVlfNumber(reader);
                byte[] starcraft2 = reader.ReadBytes(numValues);

                int index2 = ParseVlfNumber(reader);
                int type2  = reader.ReadByte(); // Should be 0x05 = Array w/ Keys

                int numElementsVersion = ParseVlfNumber(reader);
                var version            = new int[numElementsVersion];

                while (numElementsVersion > 0)
                {
                    int i = ParseVlfNumber(reader);
                    int t = reader.ReadByte(); // Type;

                    if (t == 0x09)
                    {
                        // VLF
                        version[i] = ParseVlfNumber(reader);
                    }
                    else if (t == 0x06)
                    {
                        // Byte
                        version[i] = reader.ReadByte();
                    }
                    else if (t == 0x07)
                    {
                        // 4 Bytes
                        version[i] = BitConverter.ToInt32(reader.ReadBytes(4), 0);
                    }

                    numElementsVersion--;
                }

                // We now have the version. Just parse.
                replay.ReplayVersion = string.Format("{0}.{1}.{2}.{3}", version[0], version[1], version[2], version[3]);
                replay.ReplayBuild   = version[4];

                reader.Close();
            }
        }
        public AbilityEvent(BitReader bitReader, Replay replay, Player player, AbilityData abilityData, UnitData unitData)
        {
            uint flags;

            //   1.3.3 patch notes:
            //   - Fixed an issue where the APM statistic could be artificially increased.
            // This adds the "failed" flag, which is triggered usually by holding down a
            // hotkey, leading to key repeat spamming the event throughout a single tick.
            if (replay.ReplayBuild < 18574) // < 1.3.3
            {
                flags = bitReader.Read(17);
            }
            else if (replay.ReplayBuild < 22612) // < 1.5.0
            {
                flags = bitReader.Read(18);
            }
            else
            {
                flags = bitReader.Read(20);
            }
            Queued          = (flags & 2) != 0;
            RightClick      = (flags & 8) != 0;
            WireframeClick  = (flags & 0x20) != 0;
            ToggleAbility   = (flags & 0x40) != 0;
            EnableAutoCast  = (flags & 0x80) != 0;
            AbilityUsed     = (flags & 0x100) != 0;
            WireframeUnload = (flags & 0x200) != 0;
            WireframeCancel = (flags & 0x400) != 0;
            MinimapClick    = (flags & 0x10000) != 0;
            AbilityFailed   = (flags & 0x20000) != 0;

            // flags & 0xf815 -> Debug for unknown flags
            // Never found any across all test data.

            DefaultAbility = (bitReader.Read(1) == 0);
            DefaultActor   = true;
            if (!DefaultAbility)
            {
                AbilityType = abilityData.GetAbilityType(
                    (int)bitReader.Read(16),
                    (int)bitReader.Read(5));
                DefaultActor = (bitReader.Read(1) == 0);
                if (!DefaultActor)
                {   // I'm thinking this would be an array type... but I can't
                    // find anything that causes this bit to be set.
                    throw new InvalidOperationException("Unsupported: non-default actor");
                }
            }
            if (DefaultActor)
            {
                // Deep copy the current wireframe as the actor list
                // -----
                // If a user wants to deal with subgroups to get a more
                // concise actor list, the data is all here.  We're not
                // going to bother, though, because there are several
                // exceptions to account for in determining event actors.
                Actors = new List <Unit>(player.Wireframe.Count);
                foreach (var unit in player.Wireframe)
                {
                    Actors.Add(new Unit(unit));
                }
            }

            var targetType = bitReader.Read(2);

            if (targetType == 1) // Location target
            {
                var targetX = bitReader.Read(20);
                var targetY = bitReader.Read(20);
                var targetZ = bitReader.Read(32);
                TargetLocation = Location.FromEventFormat(targetX, targetY, targetZ);
            }
            else if (targetType == 2) // Unit + Location target
            {
                TargetFlags    = (int)bitReader.Read(8);
                WireframeIndex = (int)bitReader.Read(8);

                var unitId     = (int)bitReader.Read(32);
                var unit       = replay.GetUnitById(unitId);
                var unitTypeId = (int)bitReader.Read(16);
                if (unit == null)
                {
                    var unitType = unitData.GetUnitType(unitTypeId);
                    unit        = new Unit(unitId, unitType);
                    unit.typeId = unitTypeId;
                    replay.GameUnits.Add(unitId, unit);
                }

                TargetUnit = unit;

                var targetHasPlayer = bitReader.Read(1) == 1;
                if (targetHasPlayer)
                {
                    TargetPlayer = (int)bitReader.Read(4);
                }

                // 1.4.0 -- Don't really know what this was meant to fix
                if (replay.ReplayBuild >= 19595)
                {
                    var targetHasTeam = bitReader.Read(1) == 1;
                    if (targetHasTeam)
                    {
                        TargetTeam = (int)bitReader.Read(4);
                    }
                }

                var targetX = bitReader.Read(20);
                var targetY = bitReader.Read(20);
                var targetZ = bitReader.Read(32);
                TargetLocation = Location.FromEventFormat(targetX, targetY, targetZ);
            }
            else if (targetType == 3) // Unit target
            {
                var id = bitReader.Read(32);
                // Again, if the user wants to determine exactly which
                // queue item is canceled in the case of a queue cancel
                // event (the most common case of this target specifier's
                // occurence), they can; however, it requires an additional
                // data structure that I don't want to bother with; however,
                // all the underlying data is available in the events list.
                TargetId = id;
            }

            var lastBit = bitReader.Read(1); // Should be 0; if not, misalignment is likely

            if (!AbilityFailed)
            {
                if (RightClick)
                {
                    this.EventType = GameEventType.RightClick;
                }
                else
                {
                    this.EventType = EventData.GetInstance().GetEventType(this.AbilityType);
                }
            }
            else
            {
                this.EventType = GameEventType.Inactive;
            }
        }
        /// <summary>
        /// Applies the set of attributes to a replay.
        /// </summary>
        /// <param name="replay">Replay to apply the attributes to.</param>
        public void ApplyAttributes(Replay replay)
        {
            // I'm not entirely sure this is the right encoding here. Might be unicode...
            Encoding encoding = Encoding.UTF8;

            var attributes1   = new List <ReplayAttribute>();
            var attributes2   = new List <ReplayAttribute>();
            var attributes3   = new List <ReplayAttribute>();
            var attributes4   = new List <ReplayAttribute>();
            var attributesffa = new List <ReplayAttribute>();

            foreach (var attribute in this.Attributes)
            {
                switch (attribute.AttributeId)
                {
                case PlayerTypeAttribute:     // 500
                {
                    string type = encoding.GetString(attribute.Value.Reverse().ToArray());

                    if (type.ToLower().Equals("comp"))
                    {
                        replay.ClientList[attribute.PlayerId].PlayerType = PlayerType.Computer;
                    }
                    else if (type.ToLower().Equals("humn"))
                    {
                        replay.ClientList[attribute.PlayerId].PlayerType = PlayerType.Human;
                    }
                    else
                    {
                        throw new Exception("Unexpected value");
                    }

                    break;
                }

                case TeamSizeAttribute:
                {
                    // This fixes issues with reversing the string before encoding. Without this, you get "\01v1"
                    var teamSizeChar = encoding.GetString(attribute.Value, 0, 3).Reverse().ToArray();

                    replay.TeamSize = new string(teamSizeChar);
                    break;
                }

                case DifficultyLevelAttribute:
                {
                    string diffLevel = encoding.GetString(attribute.Value.Reverse().ToArray());
                    diffLevel = diffLevel.ToLower();

                    var player = replay.ClientList[attribute.PlayerId];

                    switch (diffLevel)
                    {
                    case "vyey":
                        player.Difficulty = Difficulty.VeryEasy;
                        break;

                    case "easy":
                        player.Difficulty = Difficulty.Easy;
                        break;

                    case "medi":
                        player.Difficulty = Difficulty.Medium;
                        break;

                    case "hard":
                        player.Difficulty = Difficulty.Hard;
                        break;

                    case "vyhd":
                        player.Difficulty = Difficulty.VeryHard;
                        break;

                    case "insa":
                        player.Difficulty = Difficulty.Insane;
                        break;
                    }

                    break;
                }

                case GameSpeedAttribute:
                {
                    string speed = encoding.GetString(attribute.Value.Reverse().ToArray());
                    speed = speed.ToLower();
                    switch (speed)
                    {
                    case "slor":
                        replay.GameSpeed = GameSpeed.Slower;
                        break;

                    case "slow":
                        replay.GameSpeed = GameSpeed.Slow;
                        break;

                    case "norm":
                        replay.GameSpeed = GameSpeed.Normal;
                        break;

                    case "fast":
                        replay.GameSpeed = GameSpeed.Fast;
                        break;

                    case "fasr":
                        replay.GameSpeed = GameSpeed.Faster;
                        break;

                        // Otherwise, Game Speed will remain "Unknown"
                    }

                    break;
                }

                case PlayerRaceAttribute:
                {
                    var race   = encoding.GetString(attribute.Value.Reverse().ToArray()).ToLower();
                    var player = replay.ClientList[attribute.PlayerId];

                    switch (race)
                    {
                    case "prot":
                        player.SelectedRace = Race.Protoss;
                        break;

                    case "zerg":
                        player.SelectedRace = Race.Zerg;
                        break;

                    case "terr":
                        player.SelectedRace = Race.Terran;
                        break;

                    case "rand":
                        player.SelectedRace = Race.Random;
                        break;
                    }

                    break;
                }

                case PlayerTeam1v1Attribute:
                {
                    attributes1.Add(attribute);
                    break;
                }

                case PlayerTeam2v2Attribute:
                {
                    attributes2.Add(attribute);
                    break;
                }

                case PlayerTeam3v3Attribute:
                {
                    attributes3.Add(attribute);
                    break;
                }

                case PlayerTeam4v4Attribute:
                {
                    attributes4.Add(attribute);
                    break;
                }

                case PlayerTeamFFAAttribute:
                {
                    attributesffa.Add(attribute);
                    break;
                }


                case GameTypeAttribute:
                {
                    string gameTypeStr = encoding.GetString(attribute.Value.Reverse().ToArray());
                    gameTypeStr = gameTypeStr.ToLower().Trim('\0');

                    switch (gameTypeStr)
                    {
                    case "priv":
                        replay.GameType = GameType.Private;
                        break;

                    case "amm":
                        replay.GameType = GameType.Open;
                        break;
                    }

                    break;
                }
                }
            }

            List <ReplayAttribute> currentList = null;

            if (replay.TeamSize.Equals("1v1"))
            {
                currentList = attributes1;
            }
            else if (replay.TeamSize.Equals("2v2"))
            {
                currentList = attributes2;
            }
            else if (replay.TeamSize.Equals("3v3"))
            {
                currentList = attributes3;
            }
            else if (replay.TeamSize.Equals("4v4"))
            {
                currentList = attributes4;
            }
            else if (replay.TeamSize.Equals("FFA"))
            {
                currentList = attributesffa;
            }

            if (currentList != null)
            {
                foreach (var att in currentList)
                {
                    // Reverse the values then parse, you don't notice the effects of this until theres 10+ teams o.o
                    var team = encoding.GetString(att.Value.Reverse().ToArray()).Trim('\0', 'T');
                    replay.ClientList[att.PlayerId].Team = int.Parse(team);
                }
            }

            // Skipping parsing the handicap, colors, and handicap since this is parsed elsewhere.
        }
        public static List<IGameEvent> Parse(Replay replay, byte[] buffer)
        {
            // The GameEvent file changes significantly after 16561.
            // This is sometime around the first patch after release. Since
            // parsing replays this old should be extremely rare, I don't believe
            // its worth the effort to try to support both. If it is, it should be
            // done in another method.
            if (replay.ReplayBuild < 16561)
            {
                throw new NotSupportedException(
                    "Replay builds under 16561 are not supported for parsing GameEvent log.");
            }

            var events = new List<IGameEvent>();

            using (var stream = new MemoryStream(buffer))
            {
                using (var reader = new BinaryReader(stream))
                {
                    var currentTime = 0;
                    var numEvents = 0;

                    while (reader.BaseStream.Position < reader.BaseStream.Length)
                    {
                        bool knownEvent = true;

                        var timestamp = Timestamp.Parse(reader).Value;
                        var nextByte = reader.ReadByte();

                        var eventType = nextByte >> 5; // 3 lowest bits
                        var globalEventFlag = nextByte & 16; // 4th bit
                        var playerId = nextByte & 15; // bits 5-8

                        Player player = null;
                        if (playerId > 0)
                        {
                            player = replay.GetPlayerById(playerId);
                        }

                        var eventCode = reader.ReadByte();

                        currentTime += timestamp;

                        var time = Timestamp.Create(currentTime);

                        numEvents++;

                        switch (eventType)
                        {
                            case 0x00: // initialization
                                switch (eventCode)
                                {
                                    case 0x0B: // Player enters game
                                    case 0x0C: // for build >= 17326
                                    case 0x17:
                                    case 0x1B:
                                    case 0x2B:
                                    case 0x2C:
                                        break;
                                    case 0x05: // game starts
                                        break;
                                    default:
                                        knownEvent = false;
                                        break;
                                }

                                break;

                            case 0x01: // Action
                                switch (eventCode)
                                {
                                    case 0x09: // player quits the game
                                        events.Add(new PlayerLeftEvent(player, time));
                                        break;
                                    case 0x1B:
                                    case 0x2B:
                                    case 0x3B:
                                    case 0x4B:
                                    case 0x5B:
                                    case 0x6B:
                                    case 0x7B:
                                    case 0x8B:
                                    case 0x9B:
                                    case 0x0B: // player uses an ability
                                        int ability = -1;
                                        byte firstByte = reader.ReadByte();
                                        byte temp = reader.ReadByte();

                                        if (replay.ReplayBuild >= 18317)
                                        {
                                            byte lastTemp;

                                            ability = reader.Read() << 16 | reader.ReadByte() << 8
                                                      | (lastTemp = reader.ReadByte());

                                            // 18574 should be the correct build? not sure
                                            if ((firstByte & 0x0c) == 0x0c && (firstByte & 1) == 0)
                                            {
                                                reader.ReadBytes(4);
                                            }
                                            else if (temp == 64 || temp == 66)
                                            {
                                                if (lastTemp > 14)
                                                {
                                                    if ((lastTemp & 0x40) != 0)
                                                    {
                                                        reader.ReadBytes(2);
                                                        reader.ReadBytes(4);

                                                        reader.ReadBytes(2);
                                                    }
                                                    else
                                                    {
                                                        reader.ReadBytes(6);
                                                    }
                                                }
                                            }
                                            else if (temp == 8 || temp == 10)
                                            {
                                                reader.ReadBytes(7);
                                            }
                                            else if (temp == 136 || temp == 138)
                                            {
                                                reader.ReadBytes(15);
                                            }
                                            /*
                                            {
                                                    if ((temp & 0x80) == 0x80)
                                                    {
                                                        reader.ReadBytes(8);
                                                    }

                                                    reader.ReadBytes(10);
                                                    ability = 0;
                                                }
                                                else
                                                {
                                                    byte lastTemp;

                                                    ability = reader.Read() << 16 | reader.ReadByte() << 8
                                                              | (lastTemp = reader.ReadByte());

                                                    if ((temp & 0x60) == 0x60)
                                                    {
                                                        reader.ReadBytes(4);
                                                    }
                                                    else
                                                    {
                                                        var flaga = ability & 0xF0; // some kind of flag
                                                        if ((flaga & 0x20) == 0x20)
                                                        {
                                                            reader.ReadBytes(9);
                                                            if ((firstByte & 8) == 8)
                                                            {
                                                                reader.ReadBytes(9);
                                                            }
                                                        }
                                                        else if ((flaga & 0x10) == 0x10)
                                                        {
                                                            reader.ReadBytes(9);
                                                        }
                                                        else if ((flaga & 0x40) == 0x40)
                                                        {
                                                            reader.ReadBytes(18);
                                                        }
                                                    }
                                                }
                                            }
                                            */
                                            if (ability != -1)
                                            {
                                                events.Add(
                                                    new GameEventBase(
                                                        replay.GetPlayerById(playerId), Timestamp.Create(currentTime)));
                                            }
                                        }

                                        if (ability == -1)
                                        {
                                            ability = (reader.ReadByte() << 16) |
                                                            (reader.ReadByte() << 8) |
                                                            (reader.ReadByte() & 0x3F);

                                            if (temp == 0x20 || temp == 0x22)
                                            {
                                                var nByte = ability & 0xFF;

                                                if (nByte > 0x07)
                                                {
                                                    if (firstByte == 0x29 || firstByte == 0x19)
                                                    {
                                                        reader.ReadBytes(4); // Advance 4 bytes.
                                                        break;
                                                    }

                                                    reader.ReadBytes(9);

                                                    if ((nByte & 0x20) > 0)
                                                    {
                                                        reader.ReadBytes(9);
                                                    }
                                                }
                                            }
                                            else if (temp == 0x48 || temp == 0x4A)
                                            {
                                                reader.ReadBytes(7);
                                            }
                                            else if (temp == 0x88 || temp == 0x8A)
                                            {
                                                reader.ReadBytes(15);
                                            }

                                            if ((temp & 0x20) != 0)
                                            {
                                                // TODO: Record player ability.
                                                // This is wrong, right?
                                                events.Add(new GameEventBase(replay.GetPlayerById(playerId), Timestamp.Create(currentTime)));
                                            }

                                            events.Add(new GameEventBase(replay.GetPlayerById(playerId), Timestamp.Create(currentTime)));
                                        }

                                        break;
                                    case 0x0C: // automatic update of hotkey?
                                    case 0x1C:
                                    case 0x2C:
                                    case 0x3C: // 01 01 01 01 11 01 03 02 02 38 00 01 02 3c 00 01 00
                                    case 0x4C: // 01 02 02 01 0d 00 02 01 01 a8 00 00 01
                                    case 0x5C: // 01 01 01 01 16 03 01 01 03 18 00 01 00
                                    case 0x6C: // 01 04 08 01 03 00 02 01 01 34 c0 00 01
                                    case 0x7C: // 01 05 10 01 01 10 02 01 01 1a a0 00 01
                                    case 0x8C:
                                    case 0x9C:
                                    case 0xAC: // player changes selection
                                        if (replay.ReplayBuild >= 16561)
                                        {
                                            int bitmask = 0;
                                            byte nByte = 0;
                                            reader.ReadByte(); // skip flag byte
                                            var deselectFlags = reader.ReadByte();
                                            if ((deselectFlags & 3) == 1)
                                            {
                                                nByte = reader.ReadByte();
                                                var deselectionBits = (deselectFlags & 0xFC) | (nByte & 3);
                                                while (deselectionBits > 6)
                                                {
                                                    nByte = reader.ReadByte();
                                                    deselectionBits -= 8;
                                                }
                                                deselectionBits += 2;
                                                deselectionBits = deselectionBits % 8;

                                                bitmask = (int)Math.Pow(2, deselectionBits) - 1;
                                            }
                                            else if ((deselectFlags & 3) == 2 || (deselectFlags & 3) == 3)
                                            {
                                                nByte = reader.ReadByte();
                                                var deselectionBytes = (deselectFlags & 0xFC) | (nByte & 3);
                                                while (deselectionBytes > 0)
                                                {
                                                    nByte = reader.ReadByte();
                                                    deselectionBytes--;
                                                }

                                                bitmask = 3;
                                            }
                                            else if ((deselectFlags & 3) == 0)
                                            {
                                                bitmask = 3;
                                                nByte = deselectFlags;
                                            }

                                            int numUnitTypeIDs = 0;

                                            var prevByte = nByte;
                                            nByte = reader.ReadByte();

                                            if (bitmask > 0)
                                            {
                                                numUnitTypeIDs = (prevByte & (0xFF - bitmask)) | (nByte & bitmask);
                                            }
                                            else
                                            {
                                                numUnitTypeIDs = nByte;
                                            }

                                            for (int i = 0; i < numUnitTypeIDs; i++)
                                            {
                                                int unitTypeID = 0;
                                                int unitTypeCount = 0;

                                                for (int j = 0; j < 3; j++)
                                                {
                                                    byte by = 0;

                                                    prevByte = nByte;
                                                    nByte = reader.ReadByte();

                                                    if (bitmask > 0) // Line 610 of sc2replay.php
                                                    {
                                                        by = (byte)((prevByte & (0xFF - bitmask)) | (nByte & bitmask));
                                                    }
                                                    else
                                                    {
                                                        by = nByte;
                                                    }

                                                    unitTypeID = by << ((2 - j) * 8) | unitTypeID;
                                                }

                                                prevByte = nByte;
                                                nByte = reader.ReadByte();

                                                if (bitmask > 0)
                                                {
                                                    unitTypeCount = (prevByte & (0xFF - bitmask)) | (nByte & bitmask);
                                                }
                                                else
                                                {
                                                    unitTypeCount = nByte;
                                                }

                                                // $uType[$i + 1]['count'] = $unitTypeCount;
                                                // $uType[$i + 1]['id'] = $unitTypeID;
                                            }

                                            var numUnits = 0;
                                            prevByte = nByte;
                                            nByte = reader.ReadByte();
                                            if (bitmask > 0)
                                            {
                                                numUnits = (prevByte & (0xFF - bitmask)) | (nByte & bitmask);
                                            }
                                            else
                                            {
                                                numUnits = nByte;
                                            }

                                            for (int i = 0; i < numUnits; i++)
                                            {
                                                var unitID = 0;
                                                byte by = 0;

                                                for (int j = 0; j < 4; j++)
                                                {
                                                    prevByte = nByte;
                                                    nByte = reader.ReadByte();

                                                    if (bitmask > 0)
                                                    {
                                                        by =
                                                            (byte)((prevByte & (0xFF - bitmask)) | (nextByte & bitmask));
                                                    }
                                                    else
                                                    {
                                                        by = nByte;
                                                    }

                                                    if (j < 2)
                                                    {
                                                        unitID = (by << ((1 - j) * 8)) | unitID;
                                                    }
                                                }

                                                // TODO: Record unitID
                                                // unitIDs[] = unitID;
                                            }

                                            var a = 0;

                                            //foreach($uType as $unitType){
                                            //for($i = 1; $i <= $unitType['count']; $i++){
                                            //    $uid = $unitIDs[$a];
                                            //    //Bytes 3 + 4 contain flag info (perhaps same as in 1.00)
                                            //    $this->addSelectedUnit($uid, $unitType['id'], $playerId, floor($time / 16));
                                            //    if ($this->debug) {
                                            //        $this->debug(sprintf("  0x%06X -> 0x%04X", $unitType['id'], $uid));
                                            //    }
                                            //    $a++;
                                            //}

                                            if (eventCode == 0xAC)
                                            {
                                                events.Add(new GameEventBase(replay.GetPlayerById(playerId), Timestamp.Create(currentTime)));
                                                // $this->addPlayerAction($playerId, floor($time / 16));
                                            }

                                            break;
                                        } // sc2replay.php: Line 666

                                        throw new NotSupportedException("Event logic for builds < 16561 not implemented");
                                    case 0x0D: // manually uses hotkey
                                    case 0x1D:
                                    case 0x2D:
                                    case 0x3D:
                                    case 0x4D:
                                    case 0x5D:
                                    case 0x6D:
                                    case 0x7D:
                                    case 0x8D:
                                    case 0x9D: // sc2replay.php: Line 802
                                        HotkeyEvent hotkey = new HotkeyEvent(player, time);
                                        hotkey.Key = eventCode >> 4;
                                        var byte1 = reader.ReadByte();

                                        int flag = byte1 & 0x03;
                                        hotkey.Action = flag;

                                        if (flag == 2)
                                        {
                                            hotkey.EventType = GameEventType.Selection;
                                        }

                                        byte byte2 = 0;

                                        if ((byte1 < 16) && ((byte1 & 0x8) == 8))
                                        {
                                            byte b2 = (byte)(reader.ReadByte() & 0xF);
                                            reader.ReadBytes(b2);
                                        }
                                        else if (byte1 > 4)
                                        {
                                            int j;
                                            if (byte1 < 8)
                                            {
                                                j = reader.ReadByte();
                                                if ((j & 0x7) > 4)
                                                {
                                                    reader.ReadByte();
                                                }
                                                if ((j & 0x8) != 0)
                                                {
                                                    reader.ReadByte();
                                                }
                                            }
                                            else
                                            {
                                                j = reader.ReadByte();
                                                int shift = (byte1 >> 3) + ((j & 0xF) > 4 ? 1 : 0) +
                                                            ((j & 0xF) > 12 ? 1 : 0);
                                                reader.ReadBytes(shift);

                                                if (replay.ReplayBuild >= 18574)
                                                {
                                                    if (byte1 == 30 && j == 1)
                                                    {
                                                        reader.ReadBytes(14);
                                                    }
                                                }
                                            }
                                        }

                                        events.Add(hotkey);
                                        break;
                                    case 0x1F: // send resources
                                    case 0x2F:
                                    case 0x3F:
                                    case 0x4F:
                                    case 0x5F:
                                    case 0x6F:
                                    case 0x7F:
                                    case 0x8F:
                                        reader.ReadByte(); // 0x84
                                        var sender = playerId;
                                        var receiver = (eventCode & 0xF0) >> 4;
                                        // sent minerals
                                        var bytes = reader.ReadBytes(4);
                                        var mineralValue = (((bytes[0] << 20) | (bytes[1] << 12) | bytes[2] << 4 ) >> 1) + (bytes[3] & 0x0F);

                                        // sent gas
                                        bytes = reader.ReadBytes(4);
                                        var gasValue = (((bytes[0] << 20) | (bytes[1] << 12) | bytes[2] << 4 ) >> 1) + (bytes[3] & 0x0F);

                                        // last 8 bytes are unknown
                                        reader.ReadBytes(8);
                                        break;
                                    default:
                                        knownEvent = false;
                                        break;
                                }
                                break;
                            case 0x02: // weird
                                switch (eventCode)
                                {
                                    case 0x06:
                                        reader.ReadBytes(8);
                                        break;
                                    case 0x07:
                                        reader.ReadBytes(4);
                                        break;
                                    case 0x49:
                                        // Unknown...
                                        break;
                                    default:
                                        knownEvent = false;
                                        break;
                                }
                                break;
                            case 0x03: // replay
                                switch (eventCode)
                                {
                                    case 0x87:
                                        reader.ReadBytes(8);
                                        break;
                                    case 0x08:
                                        reader.ReadBytes(10);
                                        break;
                                    case 0x18:
                                        reader.ReadBytes(162);
                                        break;
                                    case 0x01: // camera movement
                                    case 0x11:
                                    case 0x21:
                                    case 0x31:
                                    case 0x41:
                                    case 0x51:
                                    case 0x61:
                                    case 0x71:
                                    case 0x81:
                                    case 0x91:
                                    case 0xA1:
                                    case 0xB1:
                                    case 0xC1:
                                    case 0xD1:
                                    case 0xE1:
                                    case 0xF1:
                                        reader.ReadBytes(3);
                                        var nByte = reader.ReadByte();

                                        var aByte = nByte & 0x70;
                                        switch (aByte)
                                        {
                                            case 0x10: // zoom camera up or down
                                            case 0x20:
                                            case 0x30: // only 0x10 matters, but due to 0x70 mask in comparison, check for this too
                                            case 0x40: // rotate camera
                                            case 0x50:

                                                if (aByte == 0x10 || aByte == 0x30 || aByte == 0x50)
                                                {
                                                    reader.ReadByte();
                                                    nByte = reader.ReadByte();
                                                }

                                                if (aByte != 0x40)
                                                {
                                                    if ((nByte & 0x20) > 0)
                                                    {
                                                        // zooming, if comparison is 0 max/min zoom reached
                                                        reader.ReadByte();
                                                        nByte = reader.ReadByte();
                                                    }

                                                    if ((nByte & 0x40) == 0) break;
                                                }

                                                reader.ReadBytes(2);
                                                //events.Add(new PlayerEvent(playerId, currentTime));
                                                break;
                                        }
                                        break;
                                    default:
                                        knownEvent = false;
                                        break;
                                }
                                break;
                            case 0x04: // inaction
                                if ((eventCode & 0x0F) == 2)
                                {
                                    reader.ReadBytes(2);
                                    break;
                                }

                                if ((eventCode & 0x0C) == 2)
                                {
                                    break;
                                }

                                if ((eventCode & 0x0F) == 12)
                                {
                                    break;
                                }

                                switch(eventCode)
                                {
                                    case 0x16:
                                        reader.ReadBytes(24);
                                        break;
                                    case 0xC6:
                                        reader.ReadBytes(16);
                                        break;
                                    case 0x18:
                                        reader.ReadBytes(4);
                                        break;
                                    case 0x87:
                                        reader.ReadBytes(4);
                                        break;
                                    default:
                                        knownEvent = false;
                                        break;
                                }

                                break;
                            case 0x05: // system
                                switch (eventCode)
                                {
                                    case 0x89: // automatic sync?
                                        reader.ReadBytes(4);
                                        break;
                                    default:
                                        knownEvent = false;
                                        break;
                                }

                                break;
                            default:
                                knownEvent = false;
                                break;
                        }

                        if (knownEvent == false)
                        {
                            Debug.WriteLine("Unknown Event: " + eventCode + "," + currentTime + "," + eventType);
                            throw new FormatException("An unknown event prevented the events file from being correctly parsed.");
                        }
                    }
                }
            }

            return events;
        }
        public static List<IGameEvent> Parse(Replay replay, byte[] buffer)
        {
            // The GameEvent file changes significantly after 16561.
            // This is sometime around the first patch after release. Since
            // parsing replays this old should be extremely rare, I don't believe
            // its worth the effort to try to support both. If it is, it should be
            // done in another method.
            //
            // Still a bitstream, but stuff's moved around and event codes are different.
            if (replay.ReplayBuild < 16561)
            {
                throw new NotSupportedException(
                    "Replay builds under 16561 are not supported for parsing GameEvent log.");
            }

            // Initialize Ability and Unit data.
            var effectiveBuild = BuildData.GetInstance().GetEffectiveBuild(replay.ReplayBuild);
            if (effectiveBuild == 0)
            {
                throw new NotSupportedException(
                    String.Format("Replay build {0} is not supported by the current event database", replay.ReplayBuild));
            }
            var abilityData = new AbilityData(effectiveBuild);
            var unitData = new UnitData(effectiveBuild);

            var events = new List<IGameEvent>();

            // Keep a reference to know the game length.
            var ticksElapsed = 0;

            using (var stream = new MemoryStream(buffer))
            {
                var bitReader = new BitReader(stream);

                var playersGone = new bool[0x10];

                while (!bitReader.EndOfStream)
                {
                    var intervalLength = 6 + (bitReader.Read(2) << 3);
                    var interval = bitReader.Read(intervalLength);
                    ticksElapsed += (int)interval;
                    var playerIndex = (int)bitReader.Read(5);
                    Player player;
                    if (playerIndex < 0x10)
                    {
                        player = replay.GetPlayerById(playerIndex);
                    }
                    else
                    {
                        player = Player.Global;
                    }

                    var eventType = bitReader.Read(7);
                    IGameEvent gameEvent;
                    switch (eventType)
                    {
                        case 0x05: // Game start
                            gameEvent = new GameStartEvent();
                            break;
                        case 0x0b:
                        case 0x0c: // Join game
                            gameEvent = new PlayerJoinEvent(bitReader, replay, playerIndex);
                            break;
                        case 0x19: // Leave game
                            gameEvent = new PlayerLeftEvent(player);
                            playersGone[playerIndex] = true;
                            DetectWinners(playersGone, replay);
                            break;
                        case 0x1b: // Ability
                            gameEvent = new AbilityEvent(bitReader, replay, player, abilityData, unitData);
                            break;
                        case 0x1c: // Selection
                            gameEvent = new SelectionEvent(bitReader, replay, player, unitData);
                            break;
                        case 0x1d: // Control groups
                            gameEvent = new HotkeyEvent(bitReader, replay, player);
                            break;
                        case 0x1f: // Send resources
                            gameEvent = new SendResourcesEvent(bitReader, replay);
                            break;
                        case 0x23: // ??
                            gameEvent = new GameEventBase();
                            gameEvent.EventType = GameEventType.Inactive;
                            bitReader.Read(8);
                            break;
                        case 0x26: // ??
                            gameEvent = new GameEventBase();
                            gameEvent.EventType = GameEventType.Inactive;
                            bitReader.Read(32);
                            bitReader.Read(32);
                            break;
                        case 0x27: // Target critter - special
                            gameEvent = new GameEventBase();
                            gameEvent.EventType = GameEventType.Selection;
                            var unitId = bitReader.Read(32);
                            break;
                        case 0x31: // Camera
                            gameEvent = new CameraEvent(bitReader, replay);
                            break;
                        case 0x37: // UI Event
                            gameEvent = new GameEventBase();
                            gameEvent.EventType = GameEventType.Other;
                            bitReader.Read(32);
                            bitReader.Read(32);
                            break;
                        case 0x38: // weird sync event
                            {
                                gameEvent = new GameEventBase();
                                gameEvent.EventType = GameEventType.Other;
                                for (var j = 0; j < 2; j++)
                                {
                                    var length = bitReader.Read(8);
                                    for (var i = 0; i < length; i++)
                                    {
                                        bitReader.Read(32);
                                    }
                                }
                                break;
                            }
                        case 0x3c: // ???
                            gameEvent = new GameEventBase();
                            gameEvent.EventType = GameEventType.Inactive;
                            bitReader.Read(16);
                            break;
                        case 0x46: // Request resources
                            gameEvent = new RequestResourcesEvent(bitReader, replay);
                            break;
                        case 0x47: // ?? -- associated with send minerals
                            gameEvent = new GameEventBase();
                            gameEvent.EventType = GameEventType.Inactive;
                            bitReader.Read(32);
                            break;
                        case 0x48: // ?? -- sync event
                            gameEvent = new GameEventBase();
                            gameEvent.EventType = GameEventType.Inactive;
                            bitReader.Read(32);
                            break;
                        case 0x4C: // ?? -- seen with spectator
                            bitReader.Read(4);
                            gameEvent = new GameEventBase();
                            gameEvent.EventType = GameEventType.Inactive;
                            break;
                        case 0x59: // ?? -- sync flags maybe?
                            bitReader.Read(32);
                            gameEvent = new GameEventBase();
                            gameEvent.EventType = GameEventType.Inactive;
                            break;
                        default: // debug
                            throw new InvalidOperationException(String.Format(
                                "Unknown event type {0:x} at {1:x} in replay.game.events",
                                eventType, bitReader.Cursor));
                    }

                    gameEvent.Player = player;
                    gameEvent.Time = Timestamp.Create(ticksElapsed);
                    events.Add(gameEvent);

                    bitReader.AlignToByte();
                }
            }

            replay.GameLength = Timestamp.Create(ticksElapsed).TimeSpan;

            return events;
        }
        /// <summary> Parses a .SC2Replay file and returns relevant replay information.  </summary>
        /// <param name="fileName"> Full path to a .SC2Replay file.  </param>
        /// <param name="noEvents"> True if you don't want to parse events (uses about 5~10 MB on a pro replay, half on an amateur replay) </param>
        /// <returns> Returns the fully parsed Replay object. </returns>
        public static Replay Parse(string fileName, bool noEvents = false)
        {
            if (File.Exists(fileName) == false)
            {
                throw new FileNotFoundException("The specified file does not exist.", fileName);
            }

            var replay = new Replay();

            // File in the version numbers for later use.
            MpqHeader.ParseHeader(replay, fileName);

            CArchive archive;

            try
            {
                archive = new CArchive(fileName);
            }
            catch (IOException)
            {
                // Usually thrown if the archive name contains korean. Copy it to a local file and open.
                var tmpPath = Path.GetTempFileName();

                File.Copy(fileName, tmpPath, true);

                archive = new CArchive(tmpPath);
            }

            try
            {
                var files = archive.FindFiles("replay.*");
                {
                    const string CurFile = "replay.initData";

                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    ReplayInitData.Parse(replay, buffer);
                }

                {
                    // Local scope allows the byte[] to be GC sooner, and prevents misreferences
                    const string CurFile = "replay.details";

                    var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    ReplayDetails.Parse(replay, buffer);
                }

                {
                    const string CurFile  = "replay.attributes.events";
                    var          fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);

                    ReplayAttributeEvents.Parse(replay, buffer);
                }

                {
                    const string CurFile  = "replay.message.events";
                    var          fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                    var buffer = new byte[fileSize];

                    archive.ExportFile(CurFile, buffer);
                    try
                    {
                        replay.ChatMessages = ReplayMessageEvents.Parse(buffer);
                    }
                    catch // Chat may have been removed without maintaining the structure
                          // Example:  LiquidHayPro vs MFTarga.SC2Replay from TLPro pack #36
                          // You can see a date on the file in MPQ editor, and viewing the
                          // replay in SC2 results in no chat at all.
                    {
                        replay.ChatMessages = new List <ChatMessage>();
                    }
                }

                try
                {
                    if (!noEvents)
                    {
                        const string CurFile = "replay.game.events";

                        var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size;

                        var buffer = new byte[fileSize];

                        archive.ExportFile(CurFile, buffer);

                        replay.PlayerEvents = ReplayGameEvents.Parse(replay, buffer);
                    }
                }
                catch (Exception)
                {
                    // Likely to happen with any non-standard (i.e. format isn't melee nvn, locked alliances) replay.
                }
            }
            finally
            {
                archive.Dispose();
            }

            replay.Timestamp = File.GetCreationTime(fileName);

            return(replay);
        }
Example #26
0
        public HotkeyEvent(BitReader bitReader, Replay replay, Player player)
        {
            int wireframeLength = 8;

            if (replay.ReplayBuild >= 22612)
            {
                wireframeLength = 9; // Maximum selection size has been increased to 500, up from 255.
            }
            this.EventType = GameEventType.Selection;

            ControlGroup = (int)bitReader.Read(4);

            // throws
            ActionType = (HotkeyActionType)(int)bitReader.Read(2);

            var updateType = (int)bitReader.Read(2);

            // This is an internal update that is somewhat asynchronous to
            // the main wireframe.
            var unitsRemovedList = new List <Unit>();

            if (updateType == 1) // Remove by flags
            {
                var numBits        = (int)bitReader.Read(wireframeLength);
                var unitsRemoved   = new bool[numBits];
                var wireframeIndex = 0;

                while (numBits >= 8)
                {
                    numBits -= 8;
                    var flags = bitReader.Read(8);
                    for (int i = 0; i < 8; i++)
                    {
                        unitsRemoved[wireframeIndex + i] = (flags & (1 << i)) != 0;
                    }
                    wireframeIndex += 8;
                }
                if (numBits != 0)
                {
                    var flags = bitReader.Read(numBits);
                    for (int i = 0; i < numBits; i++)
                    {
                        unitsRemoved[wireframeIndex + i] = (flags & (1 << i)) != 0;
                    }
                    wireframeIndex += numBits;
                }

                for (int i = 0; i < wireframeIndex; i++)
                {
                    if (unitsRemoved[i])
                    {
                        unitsRemovedList.Add(player.Hotkeys[ControlGroup][i]);
                    }
                }
            }
            else if (updateType == 2)
            {
                var numIndices = (int)bitReader.Read(wireframeLength);
                for (int i = 0; i < numIndices; i++)
                {
                    unitsRemovedList.Add(player.Hotkeys[ControlGroup][(int)bitReader.Read(wireframeLength)]);
                }
            }
            else if (updateType == 3) // Replace control group with portion of control group
            {
                // This happens fairly rarely, so I'll just invert the output
                unitsRemovedList = new List <Unit>(player.Hotkeys[ControlGroup]);

                var numIndices = (int)bitReader.Read(wireframeLength);
                for (int i = 0; i < numIndices; i++)
                {
                    unitsRemovedList.Remove(player.Hotkeys[ControlGroup][(int)bitReader.Read(wireframeLength)]);
                }
            }

            if (ActionType == HotkeyActionType.AddToControlGroup)
            {
                var         oldControlgroup = player.Hotkeys[ControlGroup];
                List <Unit> newControlgroup;
                if (oldControlgroup != null)
                {
                    newControlgroup = new List <Unit>(player.Wireframe.Count + oldControlgroup.Count);

                    foreach (Unit unit in oldControlgroup)
                    {
                        newControlgroup.Add(unit);
                    }
                }
                else
                {
                    newControlgroup = new List <Unit>(player.Wireframe.Count);
                }

                foreach (Unit unit in player.Wireframe)
                {
                    if (oldControlgroup == null || !oldControlgroup.Contains(unit))
                    {
                        newControlgroup.Add(unit);
                    }
                }
                newControlgroup.Sort((m, n) => m.Id - n.Id);

                player.Hotkeys[ControlGroup] = newControlgroup;
            }
            else if (ActionType == HotkeyActionType.SelectControlGroup)
            {
                player.Wireframe = new List <Unit>(player.Hotkeys[ControlGroup]);

                // Only see these two together because of the nature of it
                foreach (Unit unit in unitsRemovedList)
                {
                    player.Wireframe.Remove(unit);
                }
            }
            else if (ActionType == HotkeyActionType.SetControlGroup)
            {
                player.Hotkeys[ControlGroup] = new List <Unit>(player.Wireframe);
            }

            // Copy ref list to property.  Idk if this is a great idea, but it's likely
            // never more than 30 ish dwords per event?  Can't be more than another meg
            // or three per replay.  i.e. Can't be more than lolJava.
            ControlGroupUnits = new List <Unit>(player.Hotkeys[ControlGroup]);
        }
        private static void DetectWinners(bool[] playersGone, Replay replay)
        {
            var teamsStillActive = new bool[0x10];
            for (var i = 0; i < playersGone.Length; i++)
            {
                var player = replay.GetPlayerById(i);
                if (player != null && // player exists
                    player.Team != 0 && // player is not neutral
                    // -- Technically player team is 16 for spectators I think, but not defined here => 0
                    player.PlayerType != PlayerType.Spectator && // player is playing
                    playersGone[i] == false) // player is still in-game
                {
                    teamsStillActive[player.Team] = true;
                }
            }

            var winCandidate = 0;
            for (var i = 1; i < 0x10; i++)
            {
                if (teamsStillActive[i] && winCandidate == 0)
                {
                    winCandidate = i;
                }
                else if (teamsStillActive[i]) // .Count(n=>n) > 0
                {
                    winCandidate = -1;
                }
            }

            if (winCandidate > 0)
            {
                foreach (var player in replay.ClientList)
                {
                    if (player != null && player.Team == winCandidate)
                    {
                        player.IsWinner = true;
                    }
                }
            }
        }
Example #28
0
        public HotkeyEvent(BitReader bitReader, Replay replay, Player player)
        {
            int wireframeLength = 8;
            if (replay.ReplayBuild >= 22612)
            {
                wireframeLength = 9; // Maximum selection size has been increased to 500, up from 255.
            }
            this.EventType = GameEventType.Selection;

            ControlGroup = (int)bitReader.Read(4);

            // throws
            ActionType = (HotkeyActionType)(int)bitReader.Read(2);

            var updateType = (int)bitReader.Read(2);

            // This is an internal update that is somewhat asynchronous to
            // the main wireframe.
            var unitsRemovedList = new List<Unit>();
            if (updateType == 1) // Remove by flags
            {
                var numBits = (int)bitReader.Read(wireframeLength);
                var unitsRemoved = new bool[numBits];
                var wireframeIndex = 0;

                while (numBits >= 8)
                {
                    numBits -= 8;
                    var flags = bitReader.Read(8);
                    for (int i = 0; i < 8; i++)
                    {
                        unitsRemoved[wireframeIndex + i] = (flags & (1 << i)) != 0;
                    }
                    wireframeIndex += 8;
                }
                if (numBits != 0)
                {
                    var flags = bitReader.Read(numBits);
                    for (int i = 0; i < numBits; i++)
                    {
                        unitsRemoved[wireframeIndex + i] = (flags & (1 << i)) != 0;
                    }
                    wireframeIndex += numBits;
                }

                for (int i = 0; i < wireframeIndex; i++)
                {
                    if (unitsRemoved[i])
                    {
                        unitsRemovedList.Add(player.Hotkeys[ControlGroup][i]);
                    }
                }
            }
            else if (updateType == 2)
            {
                var numIndices = (int)bitReader.Read(wireframeLength);
                for (int i = 0; i < numIndices; i++)
                {
                    unitsRemovedList.Add(player.Hotkeys[ControlGroup][(int)bitReader.Read(wireframeLength)]);
                }
            }
            else if (updateType == 3) // Replace control group with portion of control group
            {
                // This happens fairly rarely, so I'll just invert the output
                unitsRemovedList = new List<Unit>(player.Hotkeys[ControlGroup]);

                var numIndices = (int)bitReader.Read(wireframeLength);
                for (int i = 0; i < numIndices; i++)
                {
                    unitsRemovedList.Remove(player.Hotkeys[ControlGroup][(int)bitReader.Read(wireframeLength)]);
                }
            }

            if (ActionType == HotkeyActionType.AddToControlGroup)
            {
                var oldControlgroup = player.Hotkeys[ControlGroup];
                List<Unit> newControlgroup;
                if (oldControlgroup != null)
                {
                    newControlgroup = new List<Unit>(player.Wireframe.Count + oldControlgroup.Count);

                    foreach (Unit unit in oldControlgroup)
                    {
                        newControlgroup.Add(unit);
                    }
                }
                else
                {
                    newControlgroup = new List<Unit>(player.Wireframe.Count);
                }

                foreach (Unit unit in player.Wireframe)
                {
                    if (oldControlgroup == null || !oldControlgroup.Contains(unit))
                    {
                        newControlgroup.Add(unit);
                    }
                }
                newControlgroup.Sort((m, n) => m.Id - n.Id);

                player.Hotkeys[ControlGroup] = newControlgroup;
            }
            else if (ActionType == HotkeyActionType.SelectControlGroup)
            {
                player.Wireframe = new List<Unit>(player.Hotkeys[ControlGroup]);

                // Only see these two together because of the nature of it
                foreach (Unit unit in unitsRemovedList)
                {
                    player.Wireframe.Remove(unit);
                }
            }
            else if (ActionType == HotkeyActionType.SetControlGroup)
            {
                player.Hotkeys[ControlGroup] = new List<Unit>(player.Wireframe);
            }

            // Copy ref list to property.  Idk if this is a great idea, but it's likely
            // never more than 30 ish dwords per event?  Can't be more than another meg
            // or three per replay.  i.e. Can't be more than lolJava.
            ControlGroupUnits = new List<Unit>(player.Hotkeys[ControlGroup]);
        }
        /// <summary> Parses the replay.details file, applying it to a Replay object. </summary>
        /// <param name="replay"> The replay object to apply the parsed information to. </param>
        /// <param name="stream"> The stream containing the replay.details file. </param>
        public static void Parse(Replay replay, Stream stream)
        {
            using (var reader = new BinaryReader(stream))
            {
                reader.ReadBytes(6);
                var playerCount = reader.ReadByte() >> 1;

                var players = new Player[playerCount];

                // Parsing Player Info
                for (int i = 0; i < playerCount; i++)
                {
                    var parsedPlayer = PlayerDetails.Parse(reader);

                    // The references between both of these classes are the same on purpose.
                    // We want updates to one to propogate to the other.
                    players[i] = parsedPlayer;
                    replay.ClientList[i + 1] = parsedPlayer;
                }

                replay.Players = players;

                var mapNameLength = KeyValueStruct.Parse(reader).Value;

                var mapBytes = reader.ReadBytes(mapNameLength);

                replay.Map = Encoding.UTF8.GetString(mapBytes);

                var stringLength = KeyValueStruct.Parse(reader).Value;

                // This is typically an empty string, no need to decode.
                var unknownString = reader.ReadBytes(stringLength);

                reader.ReadBytes(3);

                var mapPreviewNameLength = KeyValueStruct.Parse(reader).Value;
                var mapPreviewNameBytes = reader.ReadBytes(mapPreviewNameLength);

                replay.MapPreviewName = Encoding.UTF8.GetString(mapPreviewNameBytes);

                reader.ReadBytes(3);

                var saveTime = KeyValueLongStruct.Parse(reader).Value;
                var saveTimeZone = KeyValueLongStruct.Parse(reader).Value;
                var time = DateTime.FromFileTime(saveTime);

                // Subtract the timezone to get the appropriate UTC time.
                time = time.Subtract(new TimeSpan(saveTimeZone));

                // We create a new timestamp so we can properly set this as UTC time.
                replay.Timestamp = new DateTime(time.Ticks, DateTimeKind.Utc);

                // don't know what the next 14 bytes are for, so we skip them
                reader.ReadBytes(14);

                var resources =  new List<ResourceInfo>();
                reader.ReadBytes(2); // there are 2 bytes before each "s2ma" string
                var s2ma = Encoding.UTF8.GetString(reader.ReadBytes(4));

                while(s2ma == "s2ma") {
                    reader.ReadBytes(2); // 0x00, 0x00

                    resources.Add(new ResourceInfo {
                        Gateway = Encoding.UTF8.GetString(reader.ReadBytes(2)),
                        Hash = reader.ReadBytes(32),
                    });

                    reader.ReadBytes(2);
                    s2ma = Encoding.UTF8.GetString(reader.ReadBytes(4));
                }

                var map = resources.Last();
                replay.MapGateway = map.Gateway;
                replay.MapHash = map.Hash;

                reader.Close();
            }
        }
        /// <summary>
        /// Reads the 8 {16, 8, 8}, 8 {32} struct; the result is in AddedUnits / AddedUnitTypes.
        /// </summary>
        void HandleUnitArrays(BitReader bitReader, Replay replay, UnitData data)
        {
            int wireframeLength = 8;
            if (replay.ReplayBuild >= 22612)
            {
                wireframeLength = 9; // Maximum selection size has been increased to 500, up from 255.
            }

            var typesLength = (int)bitReader.Read(wireframeLength);
            AddedUnitTypes = new Dictionary<UnitType, int>(typesLength);

            // Guarantee order is maintained
            var subgroups = new List<KeyValuePair<UnitType, int>>(typesLength);

            for (int i = 0; i < typesLength; i++)
            {
                var unitTypeId = (int)bitReader.Read(16);
                var unitType = data.GetUnitType(unitTypeId);

                var unitSubtype = bitReader.Read(8);
                if (unitSubtype == 2) // hallucination -- cheers, Graylin
                {
                    unitType = data.GetHallucination(unitType);
                }

                var unitCountType = (int)bitReader.Read(wireframeLength);
                if (unitType == UnitType.Unknown && AddedUnitTypes.ContainsKey(UnitType.Unknown))
                {
                    AddedUnitTypes[UnitType.Unknown] += unitCountType;
                }
                else
                {
                    AddedUnitTypes.Add(unitType, unitCountType);
                }
                subgroups.Add(new KeyValuePair<UnitType, int>(unitType, unitCountType));
            }

            var idsLength = (int)bitReader.Read(wireframeLength);
            AddedUnits = AddedUnits ?? new List<Unit>(idsLength);
            if (idsLength == 0) return;

            var subgroupsEnumerator = subgroups.GetEnumerator();

            int currentSubgroupIndex;
            if (subgroupsEnumerator.MoveNext())
            {
                currentSubgroupIndex = subgroupsEnumerator.Current.Value;
            }
            else return;

            for (int i = 0; i < idsLength; i++)
            {
                var unitId = (int)bitReader.Read(32);
                var unit = replay.GetUnitById(unitId);
                var unitType = subgroupsEnumerator.Current.Key;
                if (unit == null)
                {
                    unit = new Unit(unitId, unitType);
                    replay.GameUnits.Add(unitId, unit);
                }
                else
                {
                    unit.UpdateType(unitType);
                }
                AddedUnits.Add(unit);

                if (--currentSubgroupIndex <= 0)
                {
                    if (subgroupsEnumerator.MoveNext())
                    {
                        currentSubgroupIndex = subgroupsEnumerator.Current.Value;
                    }
                }
            }
        }
        /// <summary>
        /// Applies the set of attributes to a replay.
        /// </summary>
        /// <param name="replay">Replay to apply the attributes to.</param>
        public void ApplyAttributes(Replay replay)
        {
            // I'm not entirely sure this is the right encoding here. Might be unicode...
            Encoding encoding = Encoding.UTF8;

            var attributes1 = new List<ReplayAttribute>();
            var attributes2 = new List<ReplayAttribute>();
            var attributes3 = new List<ReplayAttribute>();
            var attributes4 = new List<ReplayAttribute>();
            var attributesffa = new List<ReplayAttribute>();

            foreach (var attribute in this.Attributes)
            {
                switch (attribute.AttributeId)
                {
                    case PlayerTypeAttribute: // 500
                        {
                            string type = encoding.GetString(attribute.Value.Reverse().ToArray());

                            if (type.ToLower().Equals("comp"))
                            {
                                replay.Players[attribute.PlayerId].PlayerType = PlayerType.Computer;
                            }
                            else if (type.ToLower().Equals("humn"))
                            {
                                replay.Players[attribute.PlayerId].PlayerType = PlayerType.Human;
                            }
                            else
                            {
                                throw new Exception("Unexpected value");
                            }

                            break;
                        }

                    case TeamSizeAttribute:
                        {
                            // This fixes issues with reversing the string before encoding. Without this, you get "\01v1"
                            var teamSizeChar = encoding.GetString(attribute.Value, 0, 3).Reverse().ToArray();

                            replay.TeamSize = new string(teamSizeChar);
                            break;
                        }

                    case DifficultyLevelAttribute:
                        {
                            string diffLevel = encoding.GetString(attribute.Value.Reverse().ToArray());
                            diffLevel = diffLevel.ToLower();

                            var player = replay.Players[attribute.PlayerId];

                            switch (diffLevel)
                            {
                                case "vyey":
                                    player.Difficulty = Difficulty.VeryEasy;
                                    break;
                                case "easy":
                                    player.Difficulty = Difficulty.Easy;
                                    break;
                                case "medi":
                                    player.Difficulty = Difficulty.Medium;
                                    break;
                                case "hard":
                                    player.Difficulty = Difficulty.Hard;
                                    break;
                                case "vyhd":
                                    player.Difficulty = Difficulty.VeryHard;
                                    break;
                                case "insa":
                                    player.Difficulty = Difficulty.Insane;
                                    break;
                            }

                            break;
                        }

                    case GameSpeedAttribute:
                        {
                            string speed = encoding.GetString(attribute.Value.Reverse().ToArray());
                            speed = speed.ToLower();
                            switch (speed)
                            {
                                case "slor":
                                    replay.GameSpeed = GameSpeed.Slower;
                                    break;
                                case "slow":
                                    replay.GameSpeed = GameSpeed.Slow;
                                    break;
                                case "norm":
                                    replay.GameSpeed = GameSpeed.Normal;
                                    break;
                                case "fast":
                                    replay.GameSpeed = GameSpeed.Fast;
                                    break;
                                case "fasr":
                                    replay.GameSpeed = GameSpeed.Faster;
                                    break;

                                // Otherwise, Game Speed will remain "Unknown"
                            }

                            break;
                        }

                    case PlayerRaceAttribute:
                        {
                            var race = encoding.GetString(attribute.Value.Reverse().ToArray()).ToLower();
                            var player = replay.Players[attribute.PlayerId];

                            switch (race)
                            {
                                case "prot":
                                    player.SelectedRace = Race.Protoss;
                                    break;
                                case "zerg":
                                    player.SelectedRace = Race.Zerg;
                                    break;
                                case "terr":
                                    player.SelectedRace = Race.Terran;
                                    break;
                                case "rand":
                                    player.SelectedRace = Race.Random;
                                    break;
                            }

                            break;
                        }

                    case PlayerTeam1v1Attribute:
                        {
                            attributes1.Add(attribute);
                            break;
                        }

                    case PlayerTeam2v2Attribute:
                        {
                            attributes2.Add(attribute);
                            break;
                        }

                    case PlayerTeam3v3Attribute:
                        {
                            attributes3.Add(attribute);
                            break;
                        }

                    case PlayerTeam4v4Attribute:
                        {
                            attributes4.Add(attribute);
                            break;
                        }

                    case PlayerTeamFFAAttribute:
                        {
                            attributesffa.Add(attribute);
                            break;
                        }

                    case GameTypeAttribute:
                        {
                            string gameTypeStr = encoding.GetString(attribute.Value.Reverse().ToArray());
                            gameTypeStr = gameTypeStr.ToLower().Trim('\0');

                            switch (gameTypeStr)
                            {
                                case "priv":
                                    replay.GameType = GameType.Private;
                                    break;
                                case "amm":
                                    replay.GameType = GameType.Open;
                                    break;
                            }

                            break;
                        }

                }
            }

            List<ReplayAttribute> currentList = null;

            if (replay.TeamSize.Equals("1v1"))
            {
                currentList = attributes1;
            }
            else if (replay.TeamSize.Equals("2v2"))
            {
                currentList = attributes2;
            }
            else if (replay.TeamSize.Equals("3v3"))
            {
                currentList = attributes3;
            }
            else if (replay.TeamSize.Equals("4v4"))
            {
                currentList = attributes4;
            }
            else if (replay.TeamSize.Equals("FFA"))
            {
                currentList = attributesffa;
            }

            if (currentList != null)
            {
                foreach (var att in currentList)
                {
                    // Reverse the values then parse, you don't notice the effects of this until theres 10+ teams o.o
                    var team = encoding.GetString(att.Value.Reverse().ToArray()).Trim('\0', 'T');
                    replay.Players[att.PlayerId].Team = int.Parse(team);
                }
            }

            // Skipping parsing the handicap, colors, and handicap since this is parsed elsewhere.
        }
Example #32
0
        /// <summary>
        /// Reads the 8 {16, 8, 8}, 8 {32} struct; the result is in AddedUnits / AddedUnitTypes.
        /// </summary>
        void HandleUnitArrays(BitReader bitReader, Replay replay, UnitData data)
        {
            int wireframeLength = 8;

            if (replay.ReplayBuild >= 22612)
            {
                wireframeLength = 9; // Maximum selection size has been increased to 500, up from 255.
            }

            var typesLength = (int)bitReader.Read(wireframeLength);

            AddedUnitTypes = new Dictionary <UnitType, int>(typesLength);

            // Guarantee order is maintained
            var subgroups = new List <KeyValuePair <UnitType, int> >(typesLength);

            for (int i = 0; i < typesLength; i++)
            {
                var unitTypeId = (int)bitReader.Read(16);
                var unitType   = data.GetUnitType(unitTypeId);

                var unitSubtype = bitReader.Read(8);
                if (unitSubtype == 2) // hallucination -- cheers, Graylin
                {
                    unitType = data.GetHallucination(unitType);
                }

                var unitCountType = (int)bitReader.Read(wireframeLength);
                if (unitType == UnitType.Unknown && AddedUnitTypes.ContainsKey(UnitType.Unknown))
                {
                    AddedUnitTypes[UnitType.Unknown] += unitCountType;
                }
                else
                {
                    AddedUnitTypes.Add(unitType, unitCountType);
                }
                subgroups.Add(new KeyValuePair <UnitType, int>(unitType, unitCountType));
            }

            var idsLength = (int)bitReader.Read(wireframeLength);

            AddedUnits = AddedUnits ?? new List <Unit>(idsLength);
            if (idsLength == 0)
            {
                return;
            }

            var subgroupsEnumerator = subgroups.GetEnumerator();

            int currentSubgroupIndex;

            if (subgroupsEnumerator.MoveNext())
            {
                currentSubgroupIndex = subgroupsEnumerator.Current.Value;
            }
            else
            {
                return;
            }

            for (int i = 0; i < idsLength; i++)
            {
                var unitId   = (int)bitReader.Read(32);
                var unit     = replay.GetUnitById(unitId);
                var unitType = subgroupsEnumerator.Current.Key;
                if (unit == null)
                {
                    unit = new Unit(unitId, unitType);
                    replay.GameUnits.Add(unitId, unit);
                }
                else
                {
                    unit.UpdateType(unitType);
                }
                AddedUnits.Add(unit);

                if (--currentSubgroupIndex <= 0)
                {
                    if (subgroupsEnumerator.MoveNext())
                    {
                        currentSubgroupIndex = subgroupsEnumerator.Current.Value;
                    }
                }
            }
        }