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];
        }
        /// <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 PlayerLeftEvent(Player player, Timestamp time)
     : base(player, time)
 {
     this.EventType = GameEventType.Inactive;
 }
        /// <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();
            }
        }
 public GameEventBase(Player player, Timestamp time)
 {
     this.Player = player;
     this.Time = time;
 }
Example #6
0
 /// <summary> Initializes a new instance of the <see cref = "Replay" /> class. </summary>
 internal Replay()
 {
     GameUnits = new Dictionary<int, Unit>();
     ClientList = new Player[0x10];
 }
 private static void TrackLeavingPlayer(List<Player> remainingPlayers, Player player)
 {
     if (remainingPlayers.Contains(player))
     {
         remainingPlayers.Remove(player);
     }
 }
        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> Update the wireframe for a player using the data in the event </summary>
        void UpdateWireframe(Player player)
        {
            List<Unit> affectedWireframe;
            if (WireframeIndex == 10)
            {
                affectedWireframe = player.Wireframe;
            }
            else
            {
                affectedWireframe = player.Hotkeys[WireframeIndex];
            }

            if (!ClearSelection)
            {
                // Remove removed units, add added units, sort by unit id (top 14 bits)
                foreach (Unit unit in RemovedUnits)
                {
                    affectedWireframe.Remove(unit);
                }

                foreach (Unit unit in AddedUnits)
                {
                    affectedWireframe.Add(unit);
                }

                affectedWireframe.Sort((m, n) => m.Id - n.Id);
            }
            else
            {
                // Copy added units only to wireframe
                if (WireframeIndex == 10)
                {
                    player.Wireframe = new List<Unit>(AddedUnits);
                    player.Wireframe.Sort((m, n) => m.Id - n.Id);
                }
                else
                {
                    player.Hotkeys[WireframeIndex] = new List<Unit>(AddedUnits);
                    player.Hotkeys[WireframeIndex].Sort((m, n) => m.Id - n.Id);
                }
            }
        }
        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 #11
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]);
        }
 public HotkeyEvent(Player player, Timestamp time)
     : base(player, time)
 {
     this.EventType = GameEventType.Other;
 }