//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; }