Esempio n. 1
0
        private DataBlock CreateParsedDataBlock(byte[] replayBytes, int currIndex, out int movedIndex)
        {
            DataBlock dataBlock = new DataBlock();
            if (currIndex >= replayBytes.Length)
            {
                movedIndex = currIndex;
                return null;
            }

            ushort compressedDataBlockSize = ByteUtility.ReadWord(replayBytes, currIndex);
            currIndex += 2;

            ushort decompressedDataBlockSize = ByteUtility.ReadWord(replayBytes, currIndex);
            currIndex += 2;

            uint checkSum = ByteUtility.ReadDoubleWord(replayBytes, currIndex);
            currIndex += 4;

            byte[] compressedDataBlockBytes = replayBytes.SubArray(currIndex, compressedDataBlockSize);
            byte[] decompressedDataBlockBytes = Ionic.Zlib.ZlibStream.UncompressBuffer(compressedDataBlockBytes);

            dataBlock.CompressedDataBlockSize = compressedDataBlockSize;
            dataBlock.DecompressedDataBlockSize = decompressedDataBlockSize;
            dataBlock.CheckSum = checkSum;
            dataBlock.CompressedDataBlockBytes = compressedDataBlockBytes;
            dataBlock.DecompressedDataBlockBytes = decompressedDataBlockBytes;

            currIndex += compressedDataBlockSize;

            movedIndex = currIndex; 
            return dataBlock;
        }
Esempio n. 2
0
        private string GetChatMessage(ReplayData replayData, ref int currIndex, byte[] gameData)
        {
            int playerId = gameData[currIndex];
            currIndex++;
            PlayerInfo playerInfo = replayData.GetPlayerInfoByPlayerReplayId(playerId);
            if (playerInfo == null)
                throw new InvalidDataException(
                    String.Format("Player Id Not found in method ParseGameReplayDataFromBlock. Input : {0}", playerId));
            ushort numberOfBytes = ByteUtility.ReadWord(gameData, currIndex);
            currIndex += 2;

            byte checkFlag = gameData[currIndex];
            currIndex++;

            uint chatMode = ByteUtility.ReadDoubleWord(gameData, currIndex);
            currIndex += 4;
            string chatMessage = String.Empty;

            //Mix Teams messes up team orientation
            //For now, not appending all or allied chat until better solution is found
            //if (chatMode == 0x00)
            //    chatMessage += "[A]";
            //else if (chatMode == 0x01)
            //    chatMessage += "[T" + (playerInfo.Team + 1) + "]";

            chatMessage += "[" + playerInfo.PlayerName + "]";
            List<byte> encodedChatMessage = new List<byte>();
            while (gameData[currIndex] != 0x00)
            {
                encodedChatMessage.Add(gameData[currIndex]);
                currIndex++;
            }
            chatMessage += Encoding.UTF8.GetString(encodedChatMessage.ToArray());
            currIndex++;
            return chatMessage;
        }
Esempio n. 3
0
        public ReplayHeader ParseReplayHeader(byte[] totalReplayBytes, out int currIndex)
        {
            currIndex = 0;
            byte[]       replayHeaderBytes  = totalReplayBytes.SubArray(0, HEADER_SIZE);
            ReplayHeader parsedReplayHeader = new ReplayHeader(replayHeaderBytes);

            //Check if the replay file starts with the expected header string of "Warcraft III recorded game"
            byte[] replayStartHeader = replayHeaderBytes.SubArray(0, REPLAY_HEADER_ST_BYTES.Length);
            if (!replayStartHeader.SequenceEqual(REPLAY_HEADER_ST_BYTES))
            {
                throw new InvalidDataException(String.Format(ERROR_INVALID_HEADER_START_STRING, Encoding.UTF8.GetString(replayStartHeader)));
            }
            currIndex += REPLAY_HEADER_ST_BYTES.Length; //Move 26 characters forward

            //Get file offset
            uint replayFileOffset = ByteUtility.ReadDoubleWord(replayHeaderBytes, currIndex);

            if (replayFileOffset != FILE_OFFSET_FIRST_COMPRESSED_DATA_BLOCK)
            {
                throw new InvalidDataException(String.Format(ERROR_INVALID_REPLAY_FILE_OFFSET,
                                                             BitConverter.ToString(BitConverter.GetBytes(replayFileOffset))));
            }
            currIndex += 4;

            //Get overall size of compressed file
            uint compressedFileSize = ByteUtility.ReadDoubleWord(replayHeaderBytes, currIndex);

            parsedReplayHeader.CompressedFileSize = compressedFileSize;
            currIndex += 4;

            //Get replay version
            uint replayHeaderVersion = ByteUtility.ReadDoubleWord(replayHeaderBytes, currIndex);

            if (replayHeaderVersion != SUPPORTED_REPLAY_HEADER_VERSION)
            {
                throw new InvalidDataException(String.Format(ERROR_INVALID_REPLAY_HEADER_VERSION,
                                                             BitConverter.ToString(BitConverter.GetBytes(replayHeaderVersion))));
            }
            currIndex += 4;

            //Get overall size of decompressed file
            uint decompressedFileSize = ByteUtility.ReadDoubleWord(replayHeaderBytes, currIndex);

            parsedReplayHeader.DecompressedFileSize = decompressedFileSize;
            currIndex += 4;

            //Get total number of compressed data blocks in file
            uint compressedDataBlockCount = ByteUtility.ReadDoubleWord(replayHeaderBytes, currIndex);

            parsedReplayHeader.CompressedDataBlockCount = (int)compressedDataBlockCount;
            currIndex += 4;

            //Start SubHeaderParsing (Version 1)
            //Get version identifier (Classic, TFT)
            string clientType = ByteUtility.ReadDoubleWordString(replayHeaderBytes, currIndex);

            if (clientType != SUPPORTED_WC3_CLIENT_TYPE)
            {
                throw new InvalidDataException(String.Format(ERROR_INVALID_REPLAY_CLIENT_TYPE, clientType));
            }
            currIndex += 4;

            //Get Client Version Number
            string replayVersion      = ByteUtility.ReadDoubleWordString(replayHeaderBytes, currIndex);
            double replayVersionValue = 0;

            Double.TryParse(replayVersion, out replayVersionValue);
            //For some reason, replays generated by GHost doesn't follow the standard replay format
            //This part is commented out until I figure out what's going on

            //if (replayVersionValue < 1.0 && replayVersionValue > 2.0)
            //{
            //    throw new InvalidDataException(String.Format(ERROR_INVALID_REPLAY_VERSION, replayVersion));
            //}
            parsedReplayHeader.ReplayVersion = replayVersion;
            currIndex += 4;

            ushort buildNumber = ByteUtility.ReadWord(replayHeaderBytes, currIndex);

            parsedReplayHeader.BuildNumber = buildNumber;
            currIndex += 2;

            ushort flag = ByteUtility.ReadWord(replayHeaderBytes, currIndex);

            if (flag != FLAG_MULTIPLAYER)
            {
                //throw new InvalidDataException(String.Format(ERROR_INVALID_GAME_TYPE, BitConverter.ToString(BitConverter.GetBytes(flag))));
            }
            currIndex += 2;

            uint replayLength = ByteUtility.ReadDoubleWord(replayHeaderBytes, currIndex);

            parsedReplayHeader.ReplayLength = replayLength;
            currIndex += 4;

            uint checksum = ByteUtility.ReadDoubleWord(replayHeaderBytes, currIndex);

            parsedReplayHeader.CRC32 = checksum;
            currIndex += 4;

            return(parsedReplayHeader);
        }
Esempio n. 4
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;
        }