/// <summary> /// Deduct the map type from game information /// </summary> /// <param name="replay"></param> /// <returns></returns> public static Maps GetMapType(ReplayHeader replay) { // Check if any players have killed jungle creeps, Rules out HA var JungleCheck = (from player in replay.MatchMetadata.Players where player["NEUTRAL_MINIONS_KILLED"].ToObject <int>() > 0 select player); // Check if any players have placed wards, Rules out TT and HA var WardCheck = (from player in replay.MatchMetadata.Players where player["WARD_PLACED"].ToObject <int>() > 0 select player); // Double check between TT and SR var DragonCheck = (from player in replay.MatchMetadata.Players where player["DRAGON_KILLS"].ToObject <int>() > 0 select player); if (JungleCheck.Count() > 0) { if (WardCheck.Count() == 0 && DragonCheck.Count() == 0) { return(Maps.TwistedTreeline); } else { return(Maps.SummonersRift); } } else { return(Maps.HowlingAbyss); } }
private async void DetailForm_Load(object sender, EventArgs e) { var filename = DetailWindowManager.GetReplayFilename(replaypath); GeneralGameFileLabel.Text = filename; try { fileinfo = await ReplayReader.ReadReplayFileAsync(replaypath); ImageDownloader.SetDataDragonVersion(fileinfo.MatchMetadata.GameVersion.ToString()); } catch (Exception ex) { MessageBox.Show("Error Parsing Replay: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Environment.Exit(1); } if (fileinfo != null) { DetailWindowManager.PopulatePlayerData(fileinfo.MatchMetadata, this); DetailWindowManager.PopulateGeneralReplayData(fileinfo, this); } else { MessageBox.Show("Error Parsing Replay", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Environment.Exit(1); } // Set version text in about tab this.AboutVersionLabel.Text = RoflSettings.Default.VersionString; }
public void SampleTest() { int currIndex = 0; ReplayHeader replayHeader = parser.ParseReplayHeader(sampleBytes, out currIndex); Assert.Greater(replayHeader.CompressedFileSize, 0); }
private void ValidateReplayHeaderData(ReplayHeader replayHeader) { var errors = new List <string>(); if (string.IsNullOrEmpty(ReplayHeader.Game.Map)) { errors.Add("Map is not set or is empty"); } if (errors.Any()) { var errorStr = string.Join("; ", errors); throw new Exception("Replay header data was invalid: " + errorStr); } }
public static async Task <bool> WriteReplayHeaderToFile(string path, ReplayHeader header) { try { using (var writer = new StreamWriter(path)) { await writer.WriteLineAsync(JsonConvert.SerializeObject(header)); } } catch (Exception ex) { return(false); } return(true); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject o = JObject.Load(reader); ReplayHeader h = new ReplayHeader(); serializer.Populate(o.CreateReader(), h); foreach (var prop in o.Properties()) { if (prop.Name.StartsWith("player_")) { var player = JsonConvert.DeserializeObject <Player>(prop.Value.ToString()); h.Players.Add(player); } } return(h); }
///////////////////// Debug Methods private async void GeneralDebugDumpJsonButton_Click(object sender, EventArgs e) { if (fileinfo == null) { //fileinfo = (await LeagueManager.LoadAndParseReplayHeaders(replaypath)).Result; fileinfo = ReplayReader.ReadReplayFile(replaypath); } if (!string.IsNullOrEmpty(replaypath)) { var outputfile = Path.Combine(Path.GetDirectoryName(replaypath), Path.GetFileNameWithoutExtension(replaypath) + ".json"); var success = await DetailWindowManager.WriteReplayHeaderToFile(outputfile, fileinfo); if (success) { MessageBox.Show("Dumped JSON!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("Failed to dump JSON", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } }
public Replay(Stream stream, CASCHandler handler, Dictionary <ulong, Record> records = null) { if (records != null) { this.records = records; } this.handler = handler; root = handler?.Root as OwRootHandler; Util.MapCMF(root, handler, records, null, null); using (BinaryReader reader = new BinaryReader(stream)) { header = reader.Read <ReplayHeader>(); if (header.Magic != MAGIC_CONSTANT) { throw new InvalidDataException("Data stream is not a replay!"); } using (Decompressor decompressor = new Decompressor()) { byte[] temp = reader.ReadBytes((int)(stream.Length - stream.Position)); byte[] dec = decompressor.Unwrap(temp); decompressedStream = new MemoryStream(dec); } } }
public static void PopulateGeneralReplayData(ReplayHeader data, Form form) { var map = GameDetailsReader.GetMapType(data); var maptask = ImageDownloader.GetMinimapImageAsync(map); form.BeginInvoke((Action)(async() => { form.Controls.Find("GeneralGameVersionDataLabel", true)[0].Text = data.MatchMetadata.GameVersion; var time = ((decimal)(data.MatchMetadata.GameDuration / 1000) / 60); var minutes = (int)time; var seconds = (int)((time % 1.0m) * 60); form.Controls.Find("GeneralGameLengthDataLabel", true)[0].Text = $"{minutes} minutes and {seconds} seconds"; form.Controls.Find("GeneralGameMatchIDData", true)[0].Text = data.MatchHeader.MatchId.ToString(); var mapimg = (PictureBox)form.Controls.Find($"GeneralGamePictureBox", true)[0]; new ToolTip().SetToolTip(mapimg, map.ToString()); var imgpath = await maptask; if (!string.IsNullOrEmpty(imgpath)) { mapimg.WaitOnLoad = false; mapimg.LoadAsync(imgpath); } })); var blueplayers = (from player in data.MatchMetadata.Players where player["TEAM"].ToString() == "100" select player).DefaultIfEmpty(); var purpleplayers = (from player in data.MatchMetadata.Players where player["TEAM"].ToString() == "200" select player).DefaultIfEmpty(); string wongame = "No Contest"; if (blueplayers.ElementAt(0) != null) { if (blueplayers.ElementAt(0)["WIN"].ToString().ToUpper() == "WIN") { wongame = "Blue Victory"; } else { wongame = "Purple Victory"; } var counter = 1; foreach (var player in blueplayers) { var getimgtask = ImageDownloader.GetChampionIconImageAsync(player["SKIN"].ToString()); form.BeginInvoke((Action)(async() => { var namelabel = form.Controls.Find($"GeneralPlayerName{counter}", true)[0]; namelabel.Text = player["NAME"].ToString(); var champimg = (PictureBox)form.Controls.Find($"GeneralPlayerImage{counter}", true)[0]; new ToolTip().SetToolTip(champimg, player["SKIN"].ToString()); if (player["NAME"].ToString().ToUpper() == RoflSettings.Default.Username.ToUpper()) { namelabel.Font = new System.Drawing.Font(namelabel.Font.FontFamily, namelabel.Font.Size, System.Drawing.FontStyle.Bold); } counter++; var imgpath = await getimgtask; if (!string.IsNullOrEmpty(imgpath)) { champimg.WaitOnLoad = false; champimg.LoadAsync(imgpath); } else { champimg.Image = champimg.ErrorImage; } })); } } if (purpleplayers.ElementAt(0) != null) { if (purpleplayers.ElementAt(0)["WIN"].ToString().ToUpper() == "WIN") { wongame = "Purple Victory"; } else { wongame = "Blue Victory"; } var counter = 7; foreach (var player in purpleplayers) { var getimgtask = ImageDownloader.GetChampionIconImageAsync(player["SKIN"].ToString()); form.BeginInvoke((Action)(async() => { var namelabel = form.Controls.Find($"GeneralPlayerName{counter}", true)[0]; namelabel.Text = player["NAME"].ToString(); var champimg = (PictureBox)form.Controls.Find($"GeneralPlayerImage{counter}", true)[0]; new ToolTip().SetToolTip(champimg, player["SKIN"].ToString()); if (player["NAME"].ToString().ToUpper() == RoflSettings.Default.Username.ToUpper()) { namelabel.Font = new System.Drawing.Font(namelabel.Font.FontFamily, namelabel.Font.Size, System.Drawing.FontStyle.Bold); } counter++; var imgpath = await getimgtask; if (!string.IsNullOrEmpty(imgpath)) { champimg.WaitOnLoad = false; champimg.LoadAsync(imgpath); } else { champimg.Image = champimg.ErrorImage; } })); } } form.BeginInvoke((Action)(() => { form.Controls.Find("GeneralMatchWinnerLabel", true)[0].Text = wongame; })); return; }
/// <summary> /// Fill out the list of player names and images. Set the victory text /// </summary> /// <param name="data"></param> /// <param name="form"></param> public static void PopulateGeneralReplayData(RequestManager requests, ReplayHeader data, Form form) { // Figure out which map the replay is for and download map image var map = data.InferredData.MapID; Task <ResponseBase> maptask = requests.MakeRequestAsync(new MapRequest() { MapID = map.ToString("D"), MapName = map.ToString("G") }); form.BeginInvoke((Action)(async() => { form.Controls.Find("GeneralGameVersionDataLabel", true)[0].Text = data.MatchMetadata.GameVersion; form.Controls.Find("GeneralGameMatchIDData", true)[0].Text = data.PayloadFields.MatchId.ToString(); // Calculate game duration var time = ((decimal)(data.MatchMetadata.GameDuration / 1000) / 60); var minutes = (int)time; var seconds = (int)((time % 1.0m) * 60); form.Controls.Find("GeneralGameLengthDataLabel", true)[0].Text = $"{minutes} minutes and {seconds} seconds"; // Find the map picturebox and set the tooltip var mapimg = (PictureBox)form.Controls.Find($"GeneralGamePictureBox", true)[0]; new ToolTip().SetToolTip(mapimg, map.ToString()); // Set the map image ResponseBase mapImageResponse = await maptask; if (!mapImageResponse.IsFaulted) { mapimg.WaitOnLoad = false; mapimg.Image = mapImageResponse.ResponseImage; } else { mapimg.Image = mapimg.ErrorImage; } })); // Default victory text to draw string wongame = "No Contest"; // If there are any blue players if (data.MatchMetadata.BluePlayers.ElementAt(0) != null) { // Since we're looking at blue players first, check who won if (data.MatchMetadata.BluePlayers.ElementAt(0).SafeGet("WIN").ToUpper() == "WIN") { wongame = "Blue Victory"; } else { wongame = "Red Victory"; } var counter = 1; // Counter used to match player number to UI views foreach (var player in data.MatchMetadata.BluePlayers) { // Kick off task to download champion image Task <ResponseBase> champImgTask = requests.MakeRequestAsync(new ChampionRequest() { ChampionName = player.SafeGet("SKIN") }); form.BeginInvoke((Action)(async() => { var namelabel = form.Controls.Find($"GeneralPlayerName{counter}", true)[0]; namelabel.Text = player.SafeGet("NAME"); // Set the tooltip for champion image var champimg = (PictureBox)form.Controls.Find($"GeneralPlayerImage{counter}", true)[0]; new ToolTip().SetToolTip(champimg, player.SafeGet("SKIN")); // Bold the name of the user if (player.SafeGet("NAME").ToUpper() == RoflSettings.Default.Username.ToUpper()) { namelabel.Font = new Font(namelabel.Font.FontFamily, namelabel.Font.Size, FontStyle.Bold); } counter++; // Set the champion image ResponseBase imgResponse = await champImgTask; if (!imgResponse.IsFaulted) { champimg.WaitOnLoad = false; champimg.Image = imgResponse.ResponseImage; } else { champimg.Image = champimg.ErrorImage; } })); } // Hide labels for extra player spots for (int i = data.MatchMetadata.BluePlayers.Count() + 1; i <= 6; i++) { var namelabel = form.Controls.Find($"GeneralPlayerName{i}", true)[0]; namelabel.Visible = false; } } // If there are any red players if (data.MatchMetadata.RedPlayers.ElementAt(0) != null) { // Maybe there were no blue players, so lets see if red won (this seems redundant...) if (data.MatchMetadata.RedPlayers.ElementAt(0).SafeGet("WIN").ToUpper() == "WIN") { wongame = "Red Victory"; } else { wongame = "Blue Victory"; } var counter = 7; // Counter used to match player number to UI views foreach (var player in data.MatchMetadata.RedPlayers) { // Kick off task to download champion image Task <ResponseBase> champImgTask = requests.MakeRequestAsync(new ChampionRequest() { ChampionName = player.SafeGet("SKIN") }); form.BeginInvoke((Action)(async() => { var namelabel = form.Controls.Find($"GeneralPlayerName{counter}", true)[0]; namelabel.Text = player.SafeGet("NAME"); // Set the tooltip for champion image var champimg = (PictureBox)form.Controls.Find($"GeneralPlayerImage{counter}", true)[0]; new ToolTip().SetToolTip(champimg, player.SafeGet("SKIN")); // Bold the name of the user if (player.SafeGet("NAME").ToUpper() == RoflSettings.Default.Username.ToUpper()) { namelabel.Font = new System.Drawing.Font(namelabel.Font.FontFamily, namelabel.Font.Size, FontStyle.Bold); } counter++; // Set the champion image ResponseBase imgResponse = await champImgTask; if (!imgResponse.IsFaulted) { champimg.WaitOnLoad = false; champimg.Image = imgResponse.ResponseImage; } else { champimg.Image = champimg.ErrorImage; } })); } // Hide labels for extra player spots for (int i = data.MatchMetadata.RedPlayers.Count() + 7; i <= 12; i++) { var namelabel = form.Controls.Find($"GeneralPlayerName{i}", true)[0]; namelabel.Visible = false; } } // We should know who won by now form.BeginInvoke((Action)(() => { form.Controls.Find("GeneralMatchWinnerLabel", true)[0].Text = wongame; })); }
public void Write(ReplayHeader header) { byte[] data = header.ToRaw(); Write((ushort)data.Length); // 2 bytes Write(data, 0, data.Length); // n bytes }
public ReplayData ParseReplayData() { if (!File.Exists(_replayFilePath)) { throw new FileNotFoundException(String.Format(ERROR_FILE_DOES_NOT_EXIST, _replayFilePath)); } if (Path.GetExtension(_replayFilePath) != SUPPORTED_FILE_EXTENSION) { throw new ArgumentException(String.Format(ERROR_FILE_EXTENSION_NOT_SUPPORTED, _replayFilePath)); } for (int i = 0; i < READ_MAX_TRY; i++) { try { // Ensure that file can be opened var fileStream = File.Open(_replayFilePath, FileMode.Open, FileAccess.Read); fileStream.Close(); break; } catch (IOException) { logger.Trace($"File Open Failure [Trial #{i + 1}]"); Thread.Sleep(READ_RETRY_DELAY); } } byte[] replayFileBytes = File.ReadAllBytes(_replayFilePath); if (replayFileBytes.Length < REPLAY_MINIMUM_BYTE_LENGTH) { throw new ArgumentException(String.Format(ERROR_FILE_BYTE_LENGTH_TOO_SMALL, _replayFilePath)); } int currIndex = 0; ReplayData replayData = new ReplayData(replayFileBytes); //Parse until the end of replay header //Retrieve the current index, which is the beginning point of DataBlocks ReplayHeader replayHeader = _fateReplayHeaderParser.ParseReplayHeader(replayFileBytes,out currIndex); replayData.ReplayHeader = replayHeader; replayData.ReplayUrl = _replayFilePath; //Begin Datablock parsing List<byte> dataBlockBytes = new List<byte>(); while (true) { //Each datablock is divided into 8k or less bytes. //CurrIndex keeps track of the current position to parse each data block. DataBlock dataBlock = CreateParsedDataBlock(replayFileBytes, currIndex, out currIndex); if (dataBlock == null) break; replayData.AddDataBlock(dataBlock); dataBlockBytes.AddRange(dataBlock.DecompressedDataBlockBytes); } if (!replayData.DataBlockList.Any()) throw new InvalidDataException(ERROR_DATA_BLOCK_IS_EMPTY); //Get game datetime from file name FileInfo replayFileInfo = new FileInfo(_replayFilePath); DateTime? gameDate = GetGameDateTimeFromFileName(replayFileInfo.Name) ?? DateTime.Now; replayData.GameDateTime = (DateTime)gameDate; //First block has replay game header information + action block ParseGameHeaderBlock(replayData.DataBlockList[0], replayData, out currIndex); //Parse main game data ParseGameReplayDataFromBlock(dataBlockBytes.ToArray(), replayData, currIndex); //Parse injected event data FRSEventParser.ParseEventAPI(_frsEventCallList, replayData); //Determine observers from game SetObservers(replayData.PlayerInfoList); return replayData; }
private async Task ValidateAndReadHeader() { if (_dataStream == null) { throw new InvalidDataException("Data stream was null!"); } byte[] buf = new byte[4]; int bytesRead = await _dataStream.ReadAsync(buf, 0, 4); if (bytesRead != 4 || Encoding.ASCII.GetString(buf) != "ESAV") { throw new Exception("Invalid replay file header!"); } int replayLengthOffset = 4 /*ESAV*/ + 4 /*4 bytes after rply*/; bool foundRply = false; while (await _dataStream.ReadAsync(buf, 0, 4) == 4) { replayLengthOffset += 4; if (Encoding.ASCII.GetString(buf) == "rply") { foundRply = true; break; } } if (!foundRply) { throw new Exception("Invalid replay file header!"); } var headerDataPos = replayLengthOffset + 8; _dataStream.Seek(replayLengthOffset, SeekOrigin.Begin); bytesRead = await _dataStream.ReadAsync(buf, 0, 4); if (bytesRead != 4) { throw new Exception("Unexpected number of bytes read while reading header!"); } if (BitConverter.IsLittleEndian) { buf = buf.Reverse().ToArray(); } int headerDataLength = BitConverter.ToInt32(buf, 0); if (headerDataLength <= 0 || headerDataLength > 50000) { throw new Exception("Unexpected header data length read, expected a value between 0 and 50 000!"); } _dataStream.Seek(headerDataPos, 0); buf = new byte[headerDataLength]; bytesRead = await _dataStream.ReadAsync(buf, 0, headerDataLength); if (bytesRead != headerDataLength) { throw new Exception(string.Format("Could not read the full header data section: expected {0} bytes, read {1} bytes.", headerDataLength, bytesRead)); } string headerJson = Encoding.UTF8.GetString(buf); _dataStream.Seek(0, 0); if (_ownsStream) { _dataStream.Dispose(); } ReplayHeader = JsonConvert.DeserializeObject <ReplayHeader>(headerJson); ValidateReplayHeaderData(ReplayHeader); }
public ChunkArchive(Memory <byte> memory, DemoHeader demoHeader, ReplayHeader replayHeader) : base(demoHeader, replayHeader) { _reader = new MemoryReader(memory, Endianness.Little); }
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); }
public Archive(DemoHeader demoHeader, ReplayHeader replayHeader) { DemoHeader = demoHeader; ReplayHeader = replayHeader; }
public BitArchive(Memory <byte> data, DemoHeader demoHeader, ReplayHeader replayHeader) : base(demoHeader, replayHeader) { _bitReader = new BitReader(data); }