protected override void Parse(ref BitStreamReader bsr) { PacketInfo = new CmdInfo[DemoInfo.MaxSplitscreenPlayers]; for (int i = 0; i < PacketInfo.Length; i++) { PacketInfo[i] = new CmdInfo(DemoRef); PacketInfo[i].ParseStream(ref bsr); } InSequence = bsr.ReadUInt(); OutSequence = bsr.ReadUInt(); MessageStream = new MessageStream(DemoRef); MessageStream.ParseStream(ref bsr); // After we're doing with the packet, we can process all the messages. // Most things should be processed during parsing, but any additional checks should be done here. var netTickMessages = MessageStream.Where(tuple => tuple.messageType == MessageType.NetTick).ToList(); if (netTickMessages.Count > 1) { DemoRef.LogError("there's more than 2 net tick messages in this packet"); } NetTick?tickInfo = (NetTick?)netTickMessages.FirstOrDefault().message; if (tickInfo != null) { if (DemoRef.EntitySnapshot != null) { DemoRef.EntitySnapshot.EngineTick = tickInfo.EngineTick; } } // todo fill prop handles with data here TimingAdjustment.AdjustFromPacket(this); }
protected override void Parse(ref BitStreamReader bsr) { ClassCount = bsr.ReadUShort(); CreateOnClient = bsr.ReadBool(); if (!CreateOnClient) { // if this ever gets used then it should update the mutable tables string s = $"I haven't implemented {GetType().Name} to update the C_string tables."; DemoRef.LogError(s); Debug.WriteLine(s); ServerClasses = new ServerClass[ClassCount]; for (int i = 0; i < ServerClasses.Length; i++) { ServerClasses[i] = new ServerClass(DemoRef, this); ServerClasses[i].ParseStream(ref bsr); // this is an assumption I make in the structure of all the entity stuff, very critical if (i != ServerClasses[i].DataTableId) { throw new ConstraintException("server class ID does not match it's index in the list"); } } } DemoRef.EntBaseLines ??= new EntityBaseLines(DemoRef, ClassCount); }
// I don't think I've seen this in demos yet, I'll just log it for now and deal with it later protected override void Parse(ref BitStreamReader bsr) { NeedsDecoder = bsr.ReadBool(); ushort len = bsr.ReadUShort(); _props = bsr.Split(len); DemoRef.LogError($"unprocessed {GetType().Name} message"); // todo se2007/engine/dt_send_eng.cpp line 800 }
protected override void Parse(ref BitStreamReader bsr) { StringTablesManager manager = DemoRef.StringTablesManager; if (!manager.TableReadable.GetValueOrDefault(_tableName)) { DemoRef.LogError($"{_tableName} table is marked as non-readable, can't update :/"); _exceptionWhileParsing = true; bsr.SkipToEnd(); return; } if (manager.CreationLookup.Single(table => table.TableName == _tableName).Flags == StringTableFlags.Fake) { DemoRef.LogError($"{_tableName} table was created manually - not parsed in SvcServerInfo"); _exceptionWhileParsing = true; bsr.SkipToEnd(); return; } try { // se2007/engine/networkstringtable.cpp line 595 MutableStringTable tableToUpdate = manager.Tables[_tableName]; int?decompressedIndex = null; if (tableToUpdate.Flags.HasValue && (tableToUpdate.Flags & StringTableFlags.DataCompressed) != 0 && _canCompress) { // decompress the data - engine/baseclientstate.cpp (hl2_src) line 1364 int uncompressedSize = bsr.ReadSInt(); int compressedSize = bsr.ReadSInt(); byte[] data = Compression.Decompress(ref bsr, compressedSize - 8); // -8 to ignore header decompressedIndex = DemoRef.DecompressedLookup.Count; DemoRef.DecompressedLookup.Add(data); // so that we can access the reader for the entries later if (data.Length != uncompressedSize) { throw new Exception("could not decompress data in string table update"); } bsr = new BitStreamReader(data); } int entryIndex = -1; var history = new C5.CircularQueue <string>(32); for (int i = 0; i < _numUpdatedEntries; i++) { entryIndex++; if (!bsr.ReadBool()) { entryIndex = (int)bsr.ReadUInt(BitUtils.HighestBitIndex(tableToUpdate.MaxEntries)); } string?entryName = null; if (bsr.ReadBool()) { if (bsr.ReadBool()) // the first part of the string may be the same as for other entries { int index = (int)bsr.ReadUInt(5); int subStrLen = (int)bsr.ReadUInt(SubStringBits); entryName = history[index][..subStrLen];
protected override void Parse(ref BitStreamReader bsr) { Count = bsr.ReadByte(); if (Count != 1) { DemoRef.LogError($"{GetType()} is borking, there should only be one string but count is {Count}"); } KeyString = bsr.ReadNullTerminatedString(); }
/* Okay, this is pretty wacky. First I read a byte, and based off of that I try to determine the type of * user message. If I don't have a lookup list for whatever game this is or the type seems bogus, I log an * error. Otherwise, create the message instance, and if it's not empty, try to parse it. If parsing fails, * log an error. Finally, if not all bits of the message are parsed, then it's likely that I did something * wrong, (since it seems like the user messages use up all the bits in the message) so log an error. */ protected override void Parse(ref BitStreamReader bsr) { byte typeVal = bsr.ReadByte(); MessageType = UserMessage.ByteToUserMessageType(DemoInfo, typeVal); uint messageLength = bsr.ReadUInt(DemoInfo.UserMessageLengthBits); string?errorStr = null; var uMessageReader = bsr.SplitAndSkip(messageLength); switch (MessageType) { case UserMessageType.Unknown: errorStr = $"There is no SvcUserMessage list for this game, type {typeVal} was found"; break; case UserMessageType.Invalid: errorStr = $"SvcUserMessage with value {typeVal} is invalid"; break; default: UserMessage = SvcUserMessageFactory.CreateUserMessage(DemoRef, MessageType) !; if (UserMessage == null) { errorStr = $"Unimplemented SvcUserMessage: {MessageType}"; _unimplemented = true; } else { try { // empty messages might still have 1-2 bytes, might need to do something 'bout that if (UserMessage.ParseStream(uMessageReader) != 0) { errorStr = $"{GetType().Name} - {MessageType} ({typeVal}) didn't parse all bits"; } } catch (Exception e) { errorStr = $"{GetType().Name} - {MessageType} ({typeVal}) " + $"threw exception during parsing, message: {e.Message}"; } } break; } #region error logging // if parsing fails, just convert to an unknown type - the byte array that it will print is still useful if (errorStr != null) { int rem = uMessageReader.BitsRemaining; DemoRef.LogError($"{errorStr}, ({rem} bit{(rem == 1 ? "" : "s")}) - " + $"{uMessageReader.FromBeginning().ToHexString()}"); UserMessage = new UnknownUserMessage(DemoRef); UserMessage.ParseStream(uMessageReader); } #endregion }
protected override void Parse(ref BitStreamReader bsr) { DataType = (CustomDataType)bsr.ReadSInt(); uint size = bsr.ReadUInt(); DataMessage = CustomDataFactory.CreateCustomDataMessage(DemoRef, DataType); try { DataMessage.ParseStream(bsr.SplitAndSkip(size * 8)); } catch (Exception e) { DemoRef.LogError($"error while parsing custom data of type: {DataType}... {e.Message}"); DataMessage = new UnknownCustomDataMessage(DemoRef); } }
protected override void Parse(ref BitStreamReader bsr) { int byteSize = (int)bsr.ReadUInt(); int indexBeforeData = bsr.CurrentBitIndex; try { Tables = new List <SendTable>(); while (bsr.ReadBool()) { var table = new SendTable(DemoRef); Tables.Add(table); table.ParseStream(ref bsr); } ushort classCount = bsr.ReadUShort(); ServerClasses = new List <ServerClass>(classCount); for (int i = 0; i < classCount; i++) { var @class = new ServerClass(DemoRef, null); ServerClasses.Add(@class); @class.ParseStream(ref bsr); // I assume in many places that the ID of the table matches its index if (i != ServerClasses[i].DataTableId) { throw new ConstraintException("server class ID does not match its index in the list"); } } // in case SvcServerInfo parsing fails DemoRef.EntBaseLines ??= new EntityBaseLines(DemoRef, ServerClasses.Count); // re-init the baselines if the count doesn't match (maybe I should just init them from here?) if (DemoRef.EntBaseLines !.Baselines.Length != classCount) { DemoRef.EntBaseLines.ClearBaseLineState(classCount); } // create the prop list for each class DemoRef.DataTableParser = new DataTableParser(DemoRef, this); DemoRef.DataTableParser.FlattenClasses(true); } catch (Exception e) { DemoRef.LogError($"exception while parsing datatables\n\texception: {e.Message}"); Debug.WriteLine(e); } bsr.CurrentBitIndex = indexBeforeData + (byteSize << 3); }
protected override void Parse(ref BitStreamReader bsr) { Reliable = bsr.ReadBool(); int soundCount = Reliable ? 1 : bsr.ReadByte(); int dataBitLen = (int)bsr.ReadUInt(Reliable ? 8 : 16); BitStreamReader soundBsr = bsr.SplitAndSkip(dataBitLen); SoundInfo sound = new SoundInfo(DemoRef); SoundInfo delta = new SoundInfo(DemoRef); delta.SetDefault(); Exception?e = null; try { Sounds = new SoundInfo[soundCount]; for (int i = 0; i < soundCount; i++) { sound.ParseDelta(ref soundBsr, delta); delta = sound; if (Reliable) // client is incrementing the reliable sequence numbers itself { DemoRef.ClientSoundSequence = ++DemoRef.ClientSoundSequence & SndSeqNumMask; if (sound.SequenceNumber != 0) { throw new ArgumentException($"expected sequence number 0, got: {sound.SequenceNumber}"); } sound.SequenceNumber = DemoRef.ClientSoundSequence; } Sounds[i] = new SoundInfo(sound); } } catch (Exception exp) { e = exp; } if (e != null) { Sounds = null; DemoRef.LogError($"exception while parsing {nameof(SoundInfo)}: {e.Message}"); } else if (soundBsr.BitsRemaining != 0) { Sounds = null; DemoRef.LogError($"exception while parsing {nameof(SoundInfo)}: {soundBsr.BitsRemaining} bits left to read"); } }
protected override void Parse(ref BitStreamReader bsr) { var soundIndexBits = DemoInfo.IsLeft4Dead2() ? DemoInfo.Game >= SourceGame.L4D2_2091 ? 15 : 14 : 13; SoundIndex = (int)bsr.ReadUInt(soundIndexBits); var mgr = DemoRef.StringTablesManager; if (mgr.TableReadable.GetValueOrDefault(TableNames.SoundPreCache)) { if (SoundIndex >= mgr.Tables[TableNames.SoundPreCache].Entries.Count) { DemoRef.LogError($"{GetType().Name} - sound index out of range: {SoundIndex}"); } else if (SoundIndex != 0) { SoundName = mgr.Tables[TableNames.SoundPreCache].Entries[SoundIndex].EntryName; } } }
protected override void Parse(ref BitStreamReader bsr) { EntityIndex = bsr.ReadBool() ? bsr.ReadUInt(bsr.ReadBool() ? 5 : DemoInfo.MaxEdictBits) : _deltaTmp.EntityIndex; #pragma warning disable 8629 if (DemoInfo.NewDemoProtocol) { Flags = (SoundFlags?)bsr.ReadUIntIfExists(DemoInfo.SoundFlagBitsEncode) ?? _deltaTmp.Flags; if ((Flags & SoundFlags.IsScriptHandle) != 0) { ScriptHash = bsr.ReadUInt(); } else { SoundNum = (int?)bsr.ReadUIntIfExists(DemoInfo.MaxSndIndexBits) ?? _deltaTmp.SoundNum; } } else { SoundNum = (int?)bsr.ReadUIntIfExists(DemoInfo.MaxSndIndexBits) ?? _deltaTmp.SoundNum; Flags = (SoundFlags?)bsr.ReadUIntIfExists(DemoInfo.SoundFlagBitsEncode) ?? _deltaTmp.Flags; } Chan = (Channel?)bsr.ReadUIntIfExists(3) ?? _deltaTmp.Chan; #pragma warning restore 8629 #region get sound name if (SoundNum.HasValue) { var mgr = DemoRef.StringTablesManager; if (mgr.TableReadable.GetValueOrDefault(TableNames.SoundPreCache)) { _soundTableReadable = true; if (SoundNum >= mgr.Tables[TableNames.SoundPreCache].Entries.Count) { DemoRef.LogError($"{GetType().Name} - sound index out of range: {SoundNum}"); } else if (SoundNum != 0) { SoundName = mgr.Tables[TableNames.SoundPreCache].Entries[SoundNum.Value].EntryName; } } } #endregion IsAmbient = bsr.ReadBool(); IsSentence = bsr.ReadBool(); if (Flags != SoundFlags.Stop) { if (bsr.ReadBool()) { SequenceNumber = _deltaTmp.SequenceNumber; } else if (bsr.ReadBool()) { SequenceNumber = _deltaTmp.SequenceNumber + 1; } else { SequenceNumber = bsr.ReadUInt(SndSeqNumberBits); } Volume = bsr.ReadUIntIfExists(7) / 127.0f ?? _deltaTmp.Volume; SoundLevel = bsr.ReadUIntIfExists(MaxSndLvlBits) ?? _deltaTmp.SoundLevel; Pitch = bsr.ReadUIntIfExists(8) ?? _deltaTmp.Pitch; if (!DemoInfo.NewDemoProtocol && DemoRef.Header.NetworkProtocol > 21) { SpecialDspCount = bsr.ReadByteIfExists() ?? _deltaTmp.SpecialDspCount; } if (DemoInfo.NewDemoProtocol) { RandomSeed = bsr.ReadSIntIfExists(6) ?? _deltaTmp.RandomSeed; // 6, 18, or 29 Delay = bsr.ReadFloatIfExists() ?? _deltaTmp.Delay; } else { if (bsr.ReadBool()) { Delay = bsr.ReadSInt(MaxSndDelayMSecEncodeBits) / 1000.0f; if (Delay < 0) { Delay *= 10.0f; } Delay -= SndDelayOffset; } else { Delay = _deltaTmp.Delay; } } Origin = new Vector3 { X = bsr.ReadSIntIfExists(PropDecodeConsts.CoordIntBits - 2) * 8 ?? _deltaTmp.Origin.X, Y = bsr.ReadSIntIfExists(PropDecodeConsts.CoordIntBits - 2) * 8 ?? _deltaTmp.Origin.Y, Z = bsr.ReadSIntIfExists(PropDecodeConsts.CoordIntBits - 2) * 8 ?? _deltaTmp.Origin.Z }; SpeakerEntity = bsr.ReadSIntIfExists(DemoInfo.MaxEdictBits + 1) ?? _deltaTmp.SpeakerEntity; } else { ClearStopFields(); } }