public void AddMessageHandlers(HalfLifeDemoParser parser) { this.parser = parser; parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_event, MessageEvent); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_sound, MessageSound); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_serverinfo, MessageServerInfo); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_updateuserinfo, MessageUpdateUserInfo); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_deltadescription, MessageDeltaDescription); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_clientdata, MessageClientData); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_pings, MessagePings); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_spawnbaseline, MessageSpawnBaseline); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_newusermsg, MessageNewUserMsg); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_packetentities, MessagePacketEntities); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_deltapacketentities, MessageDeltaPacketEntities); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_resourcelist, MessageResourceList); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_hltv, MessageHltv); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_director, MessageDirector); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_voiceinit, MessageVoiceInit); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_timescale, MessageTimeScale); parser.AddUserMessageHandler("ClCorpse", MessageClCorpse); parser.AddUserMessageHandler("ScreenFade", MessageScreenFade); parser.AddUserMessageHandler("SendAudio", MessageSendAudio); parser.AddUserMessageHandler("TextMsg", MessageTextMsg); Procedure<String> removeMessage = (s) => { Int32 startOffset = parser.BitBuffer.CurrentByte; Int32 messageLength = parser.FindUserMessageLength(s); Int32 endOffset = parser.BitBuffer.CurrentByte + messageLength; parser.Seek(startOffset - 1, SeekOrigin.Begin); parser.BitBuffer.RemoveBytes(endOffset - startOffset + 1); }; parser.AddUserMessageHandler("CDChallenge", () => { removeMessage("CDChallenge"); }); parser.AddUserMessageHandler("CDSalt", () => { removeMessage("CDSalt"); }); }
private void ReadingThreadWorker() { FileStream fs = null; BinaryReader br = null; // read header try { fs = File.OpenRead(FileFullPath); br = new BinaryReader(fs); fileLengthInBytes = fs.Length; if (fileLengthInBytes < HeaderSizeInBytes) { throw new ApplicationException("File length is too short to parse the header."); } ReadHeader(br); } finally { if (br != null) { br.Close(); } if (fs != null) { fs.Close(); } } // create parser parser = new HalfLifeDemoParser(this); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_setview, ReadMessageSetView); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_print, ReadMessagePrint); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_serverinfo, ReadMessageServerInfo); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_updateuserinfo, ReadMessageUpdateUserInfo); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_hltv, ReadMessageHltv); parser.Open(); parser.Seek(HeaderSizeInBytes); // seek past header try { // read and parse frames until the end of the loading segment while (true) { HalfLifeDemoParser.FrameHeader frameHeader = parser.ReadFrameHeader(); // "no loading segment" bug if (frameHeader.Type == 1) { if (serverInfoParsed) { break; } } if (frameHeader.Type == 0 || frameHeader.Type == 1) { HalfLifeDemoParser.GameDataFrameHeader gameDataFrameHeader = parser.ReadGameDataFrameHeader(); if (gameDataFrameHeader.Length > 0) { Byte[] frameData = parser.Reader.ReadBytes((Int32)gameDataFrameHeader.Length); try { parser.ParseGameDataMessages(frameData); } catch (ThreadAbortException) { throw; } catch (Exception ex) { throw new ApplicationException("Error parsing gamedata frame.\n\n" + parser.ComputeMessageLog(), ex); } } } else { parser.SkipFrame(frameHeader.Type); } } } finally { parser.Close(); } // get demo recorder's name if (perspective == Perspectives.Pov) { foreach (Player p in playerList) { if (p.Slot == recorderSlot) { recorderName = p.InfoKeys["name"]; } } } }
/// <summary> /// Writes the demo to the destination folder while performing modifications such as removing the scoreboard or fade to black, possibly converting messages to the current network protocol, as well as re-writing directory entries. /// </summary> /// <param name="_destinationPath">The destination folder.</param> protected override void WritingThread(object _destinationFileName) { firstFrameToWriteIndex = 0; try { /* * Converted demos: pre-process the loading segment and get the frame index of the last * svc_serverinfo message in the loading segment. * * This fixes several bugs: * 1. long (for Half-Life) loading times, since the resources of several maps may be * loaded. * * 2. wrong map in resource list * * 3. random SendAudio CTD (indirectly) */ if (ConvertNetworkProtocol() && !IsBetaSteam()) { currentFrameIndex = 0; // initialise parser parser = new HalfLifeDemoParser(this); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_serverinfo, PreWriteMessageServerInfo); parser.Open(); try { parser.Seek(HeaderSizeInBytes); while (true) { HalfLifeDemoParser.FrameHeader frameHeader = parser.ReadFrameHeader(); if (frameHeader.Type == 1) { break; } if (frameHeader.Type == 0) { HalfLifeDemoParser.GameDataFrameHeader gameDataFrameHeader = parser.ReadGameDataFrameHeader(); Byte[] frameData = parser.Reader.ReadBytes((Int32)gameDataFrameHeader.Length); parser.ParseGameDataMessages(frameData); } else if (frameHeader.Type != 5) { parser.SkipFrame(frameHeader.Type); } currentFrameIndex++; } } finally { parser.Close(); } } // demo writer HalfLifeDemoConverter demoConverter = new HalfLifeDemoConverter(this); HalfLifeDemoWriter demoWriter = new HalfLifeDemoWriter(this, (IHalfLifeDemoWriter)demoConverter, writeProgressWindowInterface, firstFrameToWriteIndex); demoWriter.ThreadWorker((String)_destinationFileName); } catch (ThreadAbortException) { throw; } catch (HalfLifeDemoWriter.AbortWritingException) { writeProgressWindowInterface.CloseWithResult(false); return; } catch (Exception ex) { writeProgressWindowInterface.Error("Error writing demo file \"" + fileFullPath + "\".", ex, false, null); writeProgressWindowInterface.CloseWithResult(false); return; } writeProgressWindowInterface.CloseWithResult(true); }
public void ThreadWorker(String destinationFileName) { FileStream stream = File.Open(destinationFileName, FileMode.Create, FileAccess.Write, FileShare.None); writer = new BinaryWriter(stream); Boolean insertEndOfSegment = false; parser = new HalfLifeDemoParser(demo); // add message handlers demoWriterInterface.AddMessageHandlers(parser); try { parser.Open(); // read and write header Byte[] header = parser.Reader.ReadBytes(HalfLifeDemo.HeaderSizeInBytes); demoWriterInterface.ProcessHeader(ref header); writer.Write(header); HalfLifeDemoParser.FrameHeader lastFrameHeader = new HalfLifeDemoParser.FrameHeader(); Int32 currentFrameIndex = 0; while (true) { UpdateProgress(); // read frame header HalfLifeDemoParser.FrameHeader frameHeader = new HalfLifeDemoParser.FrameHeader(); try { frameHeader = ReadFrameHeader(); durationInSeconds = frameHeader.Timestamp; } catch (ThreadAbortException) { throw; } catch (Exception) { insertEndOfSegment = true; break; } // check for end of segment if (frameHeader.Type == 5) { if (!foundPlaybackOffset) { playbackSegmentOffset = stream.Position + 9; // 9 for end of segment that hasn't been written yet } else { // end of playback segment, stop parsing so we can write directory entries durationInSeconds = frameHeader.Timestamp; // write frame header (no data for frame type 5) before breaking from the loop WriteFrameHeader(frameHeader); break; } } // read frame data Byte[] frameData; try { Boolean writeFrame; frameData = ReadFrameData(frameHeader, out writeFrame); if (!writeFrame) { // don't write frame currentFrameIndex++; continue; } } catch (ThreadAbortException) { throw; } catch (AbortWritingException) { throw; } catch (Exception) { insertEndOfSegment = true; break; } if (currentFrameIndex >= firstFrameToWriteIndex) { // write frame header WriteFrameHeader(frameHeader); // write frame data if (frameData != null) { WriteFrameData(frameData); } } currentFrameIndex++; lastFrameHeader = frameHeader; } if (!foundPlaybackOffset) { throw new ApplicationException("Tried to write directory entries without a playback segment offset. Demo is too corrupt to play."); } if (insertEndOfSegment) { HalfLifeDemoParser.FrameHeader frameHeader = new HalfLifeDemoParser.FrameHeader(); frameHeader.Timestamp = lastFrameHeader.Timestamp; frameHeader.Number = lastFrameHeader.Number + 1; frameHeader.Type = 5; WriteFrameHeader(frameHeader); } // write directory entries Int64 directoryEntriesOffset = stream.Position; writer.Write((Int32)2); // no. of entries WriteDirectoryEntry(0, "LOADING", 0.0f, 0, HalfLifeDemo.HeaderSizeInBytes, (UInt32)playbackSegmentOffset - HalfLifeDemo.HeaderSizeInBytes); WriteDirectoryEntry(1, "Playback", durationInSeconds, nPlaybackFrames, (UInt32)playbackSegmentOffset, (UInt32)(directoryEntriesOffset - playbackSegmentOffset)); // go back and write the directory entries offset into the header stream.Seek(540, SeekOrigin.Begin); stream.Write(BitConverter.GetBytes((UInt32)directoryEntriesOffset), 0, 4); demo.DurationInSeconds = durationInSeconds; } finally { writer.Close(); parser.Close(); } }
private void WriteFrameHeader(HalfLifeDemoParser.FrameHeader frameHeader) { writer.Write(frameHeader.Type); writer.Write(frameHeader.Timestamp); writer.Write(frameHeader.Number); }
private Byte[] ReadFrameData(HalfLifeDemoParser.FrameHeader frameHeader, out Boolean writeFrame) { Byte[] result = null; writeFrame = true; if (frameHeader.Type == 0 || frameHeader.Type == 1) { // frame header Byte[] frameHeaderDemoInfo = parser.Reader.ReadBytes(parser.GameDataDemoInfoLength); Byte[] frameHeaderSequenceInfo = parser.Reader.ReadBytes(parser.GameDataSequenceInfoLength); UInt32 gameDataLength = parser.Reader.ReadUInt32(); // frame data Byte[] frameData = null; if (gameDataLength != 0) { // read frame data frameData = parser.Reader.ReadBytes((Int32)gameDataLength); if (frameData.Length != gameDataLength) { throw new ApplicationException("Gamedata frame length doesn't match header."); } // Give the writer interface a chance to insert any new messages into the first gamedata frame. if (frameHeader.Type == 1 && !foundPlaybackOffset) { demoWriterInterface.ProcessFirstGameDataFrame(ref frameData); } // parse frame messages try { if (demoWriterInterface.ShouldParseGameDataMessages(frameHeader.Type)) { parser.ParseGameDataMessages(frameData, demoWriterInterface.GetNewUserMessageId); // set frame data to version modified by parsing frameData = parser.BitBuffer.Data; } } catch (ThreadAbortException) { throw; } catch (Exception ex) { Error("Error parsing gamedata frame.\n\n" + parser.ComputeMessageLog(), ex, true); if (lastErrorResult != MessageWindow.Result.Continue) { throw new AbortWritingException(); } else { writeFrame = false; return null; } } } // check for end of loading segment if (frameHeader.Type == 1) { if (!foundPlaybackOffset) { // last 5 frame (end of segment) will have stored the correct offset foundPlaybackOffset = true; } // count playback segment gamedata frames nPlaybackFrames++; } // copy contents of frame into memorystream, return result MemoryStream ms = new MemoryStream(); demoWriterInterface.WriteDemoInfo(frameHeaderDemoInfo, ms); ms.Write(frameHeaderSequenceInfo, 0, frameHeaderSequenceInfo.Length); if (gameDataLength == 0) { ms.Write(BitConverter.GetBytes(gameDataLength), 0, 4); } else { ms.Write(BitConverter.GetBytes(frameData.Length), 0, 4); ms.Write(frameData, 0, frameData.Length); } return ms.ToArray(); } else if (frameHeader.Type == 3) // client command { String command = Common.ReadNullTerminatedString(parser.Reader, 64); if (!demoWriterInterface.ShouldWriteClientCommand(command)) { // don't write frame writeFrame = false; return null; } parser.Seek(-64); result = parser.Reader.ReadBytes(64); if (result.Length != 64) { throw new ApplicationException("Unexpected client command frame data length."); } } else if (Config.Settings.PlaybackRemoveWeaponAnimations && frameHeader.Type == 7) { parser.Seek(8); writeFrame = false; return null; } else if (frameHeader.Type != 5) { Int32 frameLength = parser.GetFrameLength(frameHeader.Type); if (frameLength != 0) { result = parser.Reader.ReadBytes(frameLength); if (result.Length != frameLength) { throw new ApplicationException("Unexpected frame data length."); } } } return result; }
public void Parse() { parser = new HalfLifeDemoParser(demo); // add svc message handlers parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_print, MessagePrint); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_stufftext, MessageStuffText); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_serverinfo, MessageServerInfo); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_updateuserinfo, MessageUpdateUserInfo); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_pings, MessagePings); parser.AddMessageHandler((Byte)HalfLifeDemoParser.MessageId.svc_event_reliable, MessageEventReliable); // add user message handlers parser.AddUserMessageHandler("SayText", MessageSayText); parser.AddUserMessageHandler("TextMsg", MessageTextMsg); parser.AddUserMessageHandler("ResetHUD", MessageResetHUD); parser.AddUserMessageHandler("DeathMsg", MessageDeathMsg); parser.AddUserMessageHandler("ScoreInfo", MessageScoreInfo); parser.AddUserMessageHandler("TeamInfo", MessageTeamInfo); parser.AddUserMessageHandler("TeamScore", MessageTeamScore); parser.Open(); parser.Seek(HalfLifeDemo.HeaderSizeInBytes); // seek past header Int32 percentRead = 0; Boolean foundPlaybackSegment = false; try { while (true) { HalfLifeDemoParser.FrameHeader frameHeader = parser.ReadFrameHeader(); if (frameHeader.Type == 0 || frameHeader.Type == 1) { if (!foundPlaybackSegment && frameHeader.Type == 1) { foundPlaybackSegment = true; } HalfLifeDemoParser.GameDataFrameHeader gameDataFrameHeader = parser.ReadGameDataFrameHeader(); currentTimestamp = frameHeader.Timestamp; // length can be 0 // e.g. GotFrag Demo 15111 (volcano vs 4k).zip if (gameDataFrameHeader.Length > 0) { Byte[] frameData = parser.Reader.ReadBytes((Int32)gameDataFrameHeader.Length); if (frameData.Length != gameDataFrameHeader.Length) { throw new ApplicationException("Gamedata frame length doesn't match header."); } try { parser.ParseGameDataMessages(frameData); } catch (ThreadAbortException) { throw; } catch (Exception ex) { throw new ApplicationException("Message parsing error.\n\n" + parser.ComputeMessageLog(), ex); } } } else if (frameHeader.Type == 5) // end of segment { if (foundPlaybackSegment) { progressWindowInterface.UpdateProgress(100); break; } } else { parser.SkipFrame(frameHeader.Type); } // calculate what percent of the file has been read Int32 oldPercentRead = percentRead; percentRead = (Int32)(parser.Position / (Single)parser.FileLength * 100.0f); if (percentRead != oldPercentRead) { progressWindowInterface.UpdateProgress(percentRead); } } } finally { parser.Close(); } }