Exemple #1
0
        //Precondition: datablock cannot be null
        //Parses Gamename, playerNames and the team indices of the player.
        private void ParseGameHeaderBlock(DataBlock dataBlock, ReplayData replayData, out int endIndex)
        {
            //Skip 4 (According to specification, first 4 bytes is unknown)
            int currIndex = 4;

            byte[] gameHeaderData = dataBlock.DecompressedDataBlockBytes;

            byte recordId = gameHeaderData[currIndex];
            currIndex++;
            byte playerId = gameHeaderData[currIndex];
            currIndex++;

            string playerName = ByteUtility.GetNullTerminatedString(gameHeaderData, currIndex, out currIndex);

            replayData.AddPlayerInfo(new PlayerInfo(playerName,playerId,recordId));
            
            //Custom data byte. We can safely ignore this.
            Debug.Assert(gameHeaderData[currIndex] == 0x01);
            currIndex++;
            //Null byte. Ignore this as well
            Debug.Assert(gameHeaderData[currIndex] == 0x00);
            currIndex++;

            replayData.GameName = ByteUtility.GetNullTerminatedString(gameHeaderData, currIndex, out currIndex);

            //Null byte.
            Debug.Assert(gameHeaderData[currIndex] == 0x00);
            currIndex++;
            
            //Refers to Section 4.4, 4.5 (Game Settings, Map&Creator Name)
            //We don't actually need this information, but it's kept just in case we need it in the future
            string encodedString = ByteUtility.GetReplayEncodedString(gameHeaderData, currIndex, out currIndex);

            //According to spec, this is player count but in reality, it's mapslotcount
            uint mapSlotCount = ByteUtility.ReadDoubleWord(gameHeaderData, currIndex);
            currIndex += 4;

            //Game Type byte. Safely skip (Section 4.7)
            currIndex++;

            //Private Flag. Safely Skip (Section 4.7)
            currIndex++;

            //Unknown Word. Safely Skip (Section 4.7)
            currIndex += 2;

            //Language ID. Safely Skip (Section 4.8)
            currIndex += 4;

            //Loop until we find all players
            while (gameHeaderData[currIndex] == 0x16)
            {
                recordId = gameHeaderData[currIndex];
                currIndex++;
                playerId = gameHeaderData[currIndex];
                currIndex++;

                playerName = ByteUtility.GetNullTerminatedString(gameHeaderData, currIndex, out currIndex);
                replayData.AddPlayerInfo(new PlayerInfo(playerName, playerId, recordId));

                //Custom data byte. We can safely ignore this.
                Debug.Assert(gameHeaderData[currIndex] == 0x01);
                currIndex++;

                //Skip 4 unknown bytes (Section 4.9)
                currIndex += 4;

                //Skip null byte
                while (gameHeaderData[currIndex] == 0x00)
                {
                    currIndex++;
                }
            }
            
            //Skip Record Id (Section 4.10, always 0x19)
            currIndex++;

            //Number of data bytes following
            ushort dataByteCount = ByteUtility.ReadWord(gameHeaderData, currIndex);
            currIndex += 2;

            //number of available slots (For fate, always 12)
            int slotCount = gameHeaderData[currIndex];
            currIndex++;

            //int slotRecordIndex = 0;
            for (int i = 0; i < slotCount; i++ )
            {
                playerId = gameHeaderData[currIndex];
                currIndex++;
                if (playerId == 0x00) //Computer player. Skip to next one
                {
                    currIndex += 8;
                    continue;
                }

                //Skip map download percent
                currIndex++;

                byte slotStatus = gameHeaderData[currIndex];
                if (slotStatus == 0x00) //Empty slot. Skip to next one.
                {
                    currIndex += 7;
                    continue;
                }
                currIndex++;

                //Skip computer player flag
                currIndex++;

                byte teamNumber = gameHeaderData[currIndex];
                PlayerInfo player = replayData.GetPlayerInfoByPlayerReplayId(playerId);
                if (player == null)
                    throw new InvalidDataException("Player Id not found! ID: " + playerId);
                player.Team = teamNumber;
                replayData.PlayerCount++;
                currIndex++;

                //Skip rest of bytes (color, raceflags, AI strength, handicap)
                currIndex += 4;
            }
            
            //Skip randomseed (Section 4.12)
            currIndex += 4;

            //Skip selectMode
            byte selectMode = gameHeaderData[currIndex];
            //For fate, Team & Race is not selectable.
            
            currIndex++;

            //Skip StartSpotCount
            currIndex++;

            endIndex = currIndex;
        }
Exemple #2
0
        private void ParseActionBlock(int actionBlockLength, ref int currIndex, byte[] gameData, ReplayData replayData)
        {
            actionBlockLength += currIndex;
            while(currIndex < actionBlockLength)
            {
                byte actionId = gameData[currIndex];
                currIndex++;

                switch (actionId)
                {
                    case 0x01: //Pause Game
                        break;
                    case 0x02: //Resume Game
                        break;
                    case 0x03: //Set game speed (Single Player)
                        currIndex++;
                        break;
                    case 0x04: //Increase game speed (Single Player)
                        break;
                    case 0x05: //Decrease game speed (Single Player)
                        break;
                    case 0x06: //Save game
                        string saveName = ByteUtility.GetNullTerminatedString(gameData, currIndex, out currIndex);
                        break;
                    case 0x07: //save game finished
                        currIndex += 4;
                        break;
                    case 0x10: //Unit/Building Ability
                        currIndex += 14;
                        break;
                    case 0x11: //Unit/Building Ability with target position
                        currIndex += 22;
                        break;
                    case 0x12: //Unit/Building ability (With target position and target object id)
                        currIndex += 30;
                        break;
                    case 0x13: //Give item to Unit / Drop item on ground
                        currIndex += 38;
                        break;
                    case 0x14: //Unit building ability (With two target positions and two item ids)
                        currIndex += 43;
                        break;
                    case 0x16:
                    case 0x17: //Change selection, assign group hotkey
                        currIndex++; //Select Mode
                        ushort selectionCount = ByteUtility.ReadWord(gameData, currIndex);
                        currIndex += 2;
                        currIndex += selectionCount*8; //n * 8 bytes (Two DWORD repeated);
                        break;
                    case 0x18: //Select Group Hotkey
                        currIndex += 2;
                        break;
                    case 0x19: //Select Subgroup
                        currIndex += 12;
                        break;
                    case 0x1A: //Pre Subselection

                        break;
                    case 0x1B: //Unknown
                        currIndex += 9;
                        break;
                    case 0x1C: //Select Ground Item
                        currIndex += 9;
                        break;
                    case 0x1D: //Cancel Hero Revival
                        currIndex += 8;
                        break;
                    case 0x1E: //Remove unit from building queue
                        currIndex += 5;
                        break;
                    case 0x21: //unknown
                        currIndex += 8;
                        break;
                    case 0x50: //Change ally options
                        currIndex += 5;
                        break;
                    case 0x51: //Transfer Resources
                        currIndex += 9;
                        break;
                    case 0x60: //Map Trigger Chat Command
                        currIndex += 8; //Two Unknown Double Words
                        List<byte> encodedChatBytes = new List<byte>();
                        while (gameData[currIndex] != 0x00)
                        {
                            encodedChatBytes.Add(gameData[currIndex]);
                            currIndex++;
                        }
                        currIndex++;
                        string chatString = Encoding.UTF8.GetString(encodedChatBytes.ToArray());
                        break;
                    case 0x61: //ESC Pressed
                        break;
                    case 0x62: //Scenario Trigger
                        currIndex += 12;
                        break;
                    case 0x66: //Choose hero skill submenu
                        break;
                    case 0x67: //Choose building submenu
                        break;
                    case 0x68: //Minimap Ping
                        currIndex += 12;
                        break;
                    case 0x69: //Continue Game (Block B)
                        currIndex += 16;
                        break;
                    case 0x6A: //Continue Game (Block A)
                        currIndex += 16;
                        break;
                    case 0x75: //Unknown
                        currIndex++;
                        break;
                    case 0x20: //Cheats
                    case 0x22:
                    case 0x23:
                    case 0x24:
                    case 0x25:
                    case 0x26:
                    case 0x29:
                    case 0x2A:
                    case 0x2B:
                    case 0x2C:
                    case 0x2F:
                    case 0x30:
                    case 0x31:
                    case 0x32:
                        break;
                    case 0x70: //SyncStoredInteger. The most important part.
                        string gameCacheName = ByteUtility.GetNullTerminatedString(gameData, currIndex, out currIndex);
                        string eventCategory = ByteUtility.GetNullTerminatedString(gameData, currIndex, out currIndex);
                        string[] eventDetailId = ByteUtility.GetNullTerminatedString(gameData, currIndex, out currIndex).Split(new[] { "/" }, StringSplitOptions.None);
                        string eventId = eventDetailId[0];
                        string eventDetail = string.Join("/",eventDetailId.Skip(1).Take(4));
                        _frsEventCallList.Add(new FRSEvent(eventId, gameCacheName,eventCategory,eventDetail));


                        break;
                    default:
                        throw new Exception(String.Format("Unexpected Action ID found at currIndex: {0} Input: {1}",
                                                          currIndex-1, actionId));
                }
            }
        }