/// <summary> Erases the entire chat log of a file. </summary> /// <param name="fileName"> The file name. </param> public static void ClearChatLog(string fileName) { var replay = new Replay(); // File in the version numbers for later use. MpqHeader.ParseHeader(replay, fileName); using (var archive = new CArchive(fileName)) { var files = archive.FindFiles("replay.*"); { const string CurFile = "replay.message.events"; var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size; var buffer = new byte[fileSize]; archive.ExportFile(CurFile, buffer); var arr = ClearChatLog(buffer); archive.ImportFile("replay.message.events", arr); } archive.Close(); } }
/// <summary> Adds a single chat message to a replay. </summary> /// <param name="fileName"> The file name. </param> /// <param name="messages"> The messages to add. </param> public static void AddChatMessageToReplay(string fileName, IEnumerable <ChatMessage> messages) { var replay = new Replay(); // File in the version numbers for later use. MpqHeader.ParseHeader(replay, fileName); using (var archive = new CArchive(fileName)) { var files = archive.FindFiles("replay.*"); { const string CurFile = "replay.message.events"; var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size; var buffer = new byte[fileSize]; archive.ExportFile(CurFile, buffer); foreach (var message in messages) { buffer = GenerateChatMessage( buffer, message.Message, message.MessageTarget, message.PlayerId, (int)message.Timestamp.TotalSeconds); } archive.ImportFile("replay.message.events", buffer); } archive.Close(); } }
/// <summary> Adds a single chat message to a replay. </summary> /// <param name="fileName"> The file name. </param> /// <param name="message"> The message. </param> /// <param name="target"> The target. </param> /// <param name="playerId"> The player id. </param> /// <param name="numSeconds"> The number of in-game seconds to insert the message at. </param> public static void AddChatMessageToReplay( string fileName, string message, ChatMessageTarget target, int playerId, int numSeconds) { var replay = new Replay(); // File in the version numbers for later use. MpqHeader.ParseHeader(replay, fileName); using (var archive = new CArchive(fileName)) { var files = archive.FindFiles("replay.*"); { const string CurFile = "replay.message.events"; var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size; var buffer = new byte[fileSize]; archive.ExportFile(CurFile, buffer); var arr = GenerateChatMessage(buffer, message, target, playerId, numSeconds); archive.ImportFile("replay.message.events", arr); } archive.Close(); } }
/// <summary> Parses a .SC2Replay file and returns relevant replay information. </summary> /// <param name="fileName"> Full path to a .SC2Replay file. </param> /// <param name="noEvents"> True if you don't want to parse events (uses about 5~10 MB on a pro replay, half on an amateur replay) </param> /// <returns> Returns the fully parsed Replay object. </returns> public static Replay Parse(string fileName, bool noEvents = false) { if (File.Exists(fileName) == false) { throw new FileNotFoundException("The specified file does not exist.", fileName); } var replay = new Replay(); // File in the version numbers for later use. MpqHeader.ParseHeader(replay, fileName); CArchive archive; try { archive = new CArchive(fileName); } catch (IOException) { // Usually thrown if the archive name contains korean. Copy it to a local file and open. var tmpPath = Path.GetTempFileName(); File.Copy(fileName, tmpPath, true); archive = new CArchive(tmpPath); } try { var files = archive.FindFiles("replay.*"); { const string CurFile = "replay.initData"; var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size; var buffer = new byte[fileSize]; archive.ExportFile(CurFile, buffer); ReplayInitData.Parse(replay, buffer); } { // Local scope allows the byte[] to be GC sooner, and prevents misreferences const string CurFile = "replay.details"; var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size; var buffer = new byte[fileSize]; archive.ExportFile(CurFile, buffer); ReplayDetails.Parse(replay, buffer); } { const string CurFile = "replay.attributes.events"; var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size; var buffer = new byte[fileSize]; archive.ExportFile(CurFile, buffer); ReplayAttributeEvents.Parse(replay, buffer); } { const string CurFile = "replay.message.events"; var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size; var buffer = new byte[fileSize]; archive.ExportFile(CurFile, buffer); try { replay.ChatMessages = ReplayMessageEvents.Parse(buffer); } catch // Chat may have been removed without maintaining the structure // Example: LiquidHayPro vs MFTarga.SC2Replay from TLPro pack #36 // You can see a date on the file in MPQ editor, and viewing the // replay in SC2 results in no chat at all. { replay.ChatMessages = new List <ChatMessage>(); } } try { if (!noEvents) { const string CurFile = "replay.game.events"; var fileSize = (from f in files where f.FileName.Equals(CurFile) select f).Single().Size; var buffer = new byte[fileSize]; archive.ExportFile(CurFile, buffer); replay.PlayerEvents = ReplayGameEvents.Parse(replay, buffer); } } catch (Exception) { // Likely to happen with any non-standard (i.e. format isn't melee nvn, locked alliances) replay. } } finally { archive.Dispose(); } replay.Timestamp = File.GetCreationTime(fileName); return(replay); }