Esempio n. 1
0
        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));
            }
        }
        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 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));
            }
        }
Esempio n. 4
0
        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];
        }
Esempio n. 5
0
        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;
                    }
                }
            }
        }
Esempio n. 6
0
        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);
        }
        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;
                    }
                }
            }
        }
        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;
        }
        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;
        }