Example #1
0
 public static IEnumerable <int> GetPauseTicks(SourceDemo demo)
 {
     return
         (from packet in demo.FilterForPacket <Packet>()
          from messageTup in packet.MessageStream
          where messageTup.message?.GetType() == typeof(SvcSetPause)
          where ((SvcSetPause)messageTup.message).IsPaused
          select packet.Tick);
 }
Example #2
0
        public static void ChangeDemoDir(SourceDemo demo, Stream s, string newDir)
        {
            void Write(byte[] buf) => s.Write(buf, 0, buf.Length);

            string old = demo.Header.GameDirectory;

            if (old == newDir)
            {
                // no difference
                Write(demo.Reader.Data);
                return;
            }
            int             lenDiff = newDir.Length - old.Length;
            BitStreamReader bsr     = demo.Reader;

            byte[] dirBytes = Encoding.ASCII.GetBytes(newDir);

            Write(bsr.ReadBytes(796));
            Write(dirBytes);             // header doesn't matter but I change it anyway
            Write(new byte[260 - newDir.Length]);
            bsr.SkipBytes(260);
            Write(bsr.ReadBytes(12));
            byte[] tmp = BitConverter.GetBytes((uint)(bsr.ReadUInt() + lenDiff));
            if (!BitConverter.IsLittleEndian)
            {
                tmp = tmp.Reverse().ToArray();
            }
            Write(tmp);

            foreach (SignOn signOn in demo.FilterForPacket <SignOn>().Where(signOn => signOn.FilterForMessage <SvcServerInfo>().Any()))
            {
                // catch up to signOn packet
                int byteCount = (signOn.Reader.AbsoluteBitIndex - bsr.AbsoluteBitIndex) / 8;
                Write(bsr.ReadBytes(byteCount));
                bsr.SkipBits(signOn.Reader.BitLength);

                BitStreamWriter bsw          = new BitStreamWriter();
                BitStreamReader signOnReader = signOn.Reader;
                bsw.WriteBits(signOnReader.ReadRemainingBits());
                signOnReader = signOnReader.FromBeginning();
                int bytesToMessageStreamSize = demo.DemoInfo.MaxSplitscreenPlayers * 76 + 8;
                signOnReader.SkipBytes(bytesToMessageStreamSize);
                // edit the message stream length - read uint, and edit at index before the reading of said uint
                bsw.EditIntAtIndex((int)(signOnReader.ReadUInt() + lenDiff), signOnReader.CurrentBitIndex - 32, 32);

                // actually change the game dir
                SvcServerInfo serverInfo = signOn.FilterForMessage <SvcServerInfo>().Single();
                int           editIndex  = serverInfo.GameDirBitIndex - signOn.Reader.AbsoluteBitIndex;
                bsw.RemoveBitsAtIndex(editIndex, old.Length * 8);
                bsw.InsertBitsAtIndex(dirBytes, editIndex, newDir.Length * 8);
                Write(bsw.AsArray);
            }
            Write(bsr.ReadRemainingBits().bytes);
        }
        // writes a edited version of the given demo to the stream
        // this is all manually hacked together and it shall stay this way for now
        public static void RemoveCaptions(SourceDemo demo, Stream s)
        {
            void Write(byte[] buf) => s.Write(buf, 0, buf.Length);

            Packet[] closeCaptionPackets = demo.FilterForPacket <Packet>()
                                           .Where(packet => packet.FilterForMessage <SvcUserMessage>()
                                                  .Any(frame => frame.MessageType == UserMessageType.CloseCaption)).ToArray();

            if (closeCaptionPackets.Length == 0)
            {
                Write(demo.Reader.Data);
                return;
            }

            int changedPackets = 0;

            Write(demo.Header.Reader.ReadRemainingBits().bytes);

            foreach (PacketFrame frame in demo.Frames)
            {
                if (frame.Packet != closeCaptionPackets[changedPackets])
                {
                    Write(frame.Reader.ReadRemainingBits().bytes);                     // write frames that aren't changed
                }
                else
                {
                    Packet          p    = (Packet)frame.Packet;
                    BitStreamWriter bsw  = new BitStreamWriter(frame.Reader.BitLength / 8);
                    var             last = p.MessageStream.Last().message;
                    int             len  = last.Reader.AbsoluteStart - frame.Reader.AbsoluteStart + last.Reader.BitLength;
                    bsw.WriteBits(frame.Reader.ReadBits(len), len);
                    int msgSizeOffset = p.MessageStream.Reader.AbsoluteStart - frame.Reader.AbsoluteStart;
                    int typeInfoLen   = demo.DemoInfo.NetMsgTypeBits + demo.DemoInfo.UserMessageLengthBits + 8;
                    bsw.RemoveBitsAtIndices(p.FilterForUserMessage <CloseCaption>()
                                            .Select(caption => (caption.Reader.AbsoluteStart - frame.Reader.AbsoluteStart - typeInfoLen,
                                                                caption.Reader.BitLength + typeInfoLen)));
                    bsw.WriteUntilByteBoundary();
                    bsw.EditIntAtIndex((bsw.BitLength - msgSizeOffset - 32) >> 3, msgSizeOffset, 32);
                    Write(bsw.AsArray);

                    // if we've edited all the packets, write the rest of the data in the demo
                    if (++changedPackets == closeCaptionPackets.Length)
                    {
                        BitStreamReader tmp = demo.Reader;
                        tmp.SkipBits(frame.Reader.AbsoluteStart + frame.Reader.BitLength);
                        Write(tmp.ReadRemainingBits().bytes);
                        break;
                    }
                }
            }
        }
        public static void WriteDataTableDump(StreamWriter sw, SourceDemo demo, DataTableDumpMode mode)
        {
            PrettyStreamWriter pw = new PrettyStreamWriter(sw.BaseStream);
            int datatablesCount   = 0;           // spliced demos have more than one datatables packet

            foreach (DataTables dataTables in demo.FilterForPacket <DataTables>())
            {
                if (datatablesCount++ > 0)
                {
                    pw.Append("\n\n\n");
                }
                if (mode != DataTableDumpMode.TreeOnly)
                {
                    switch (mode)
                    {
                    case DataTableDumpMode.PacketAndTree:
                        dataTables.PrettyWrite(pw);
                        break;

                    case DataTableDumpMode.Flattened:
                        var tableParser = new DataTableParser(demo, dataTables);
                        tableParser.FlattenClasses(false);
                        foreach ((ServerClass sClass, List <FlattenedProp> fProps) in tableParser.FlattenedProps !)
                        {
                            pw.AppendLine($"{sClass.ClassName} ({sClass.DataTableName}) ({fProps.Count} props):");
                            for (var i = 0; i < fProps.Count; i++)
                            {
                                FlattenedProp fProp = fProps[i];
                                pw.Append($"\t({i}): ");
                                pw.Append(fProp.TypeString().PadRight(12));
                                pw.AppendLine(fProp.ArrayElementPropInfo == null
                                                                                ? fProp.PropInfo.ToStringNoType()
                                                                                : fProp.ArrayElementPropInfo.ToStringNoType());
                            }
                        }
                        break;

                    default:
                        throw new ArgProcessProgrammerException($"invalid data table dump mode: \"{mode}\"");
                    }
                }
                if (mode != DataTableDumpMode.TreeOnly)
                {
                    pw.Append("\n\n\n");
                }
                pw.Append("Datatable hierarchy:\n\n\n");
                new DataTableTree(dataTables, true).PrettyWrite(pw);
            }
            // see note at PrettyStreamWriter
            pw.Flush();
        }
        public static void WriteStringTableDump(StreamWriter sw, SourceDemo demo)
        {
            PrettyStreamWriter pw = new PrettyStreamWriter(sw.BaseStream);
            int stringTablesCount = 0;             // spliced demos surely have more than one stringtables packet (unsure about regular demos)

            foreach (StringTables stringTablesPacket in demo.FilterForPacket <StringTables>())
            {
                if (stringTablesCount++ > 0)
                {
                    pw.Append("\n\n\n");
                }
                stringTablesPacket.PrettyWrite(pw);
            }
            // see note at PrettyStreamWriter
            pw.Flush();
        }
        public static IEnumerable <(int tick, string repr)> GetUserInputs(SourceDemo demo, InputDisplayMode mode)
        {
            Buttons?prevButtons = null;
            int     prevTick    = int.MinValue;

            foreach (UserCmd u in demo.FilterForPacket <UserCmd>())
            {
                Buttons?b = u.Buttons;
                // don't want pauses taking up all the space
                if (prevButtons == b && prevTick == u.Tick)
                {
                    continue;
                }
                prevButtons = b;
                prevTick    = u.Tick;
                yield return(mode switch {
                    InputDisplayMode.Text => (u.Tick, b?.ToString() ?? "none"),
                    InputDisplayMode.Int => (u.Tick, b.HasValue ? ((uint)b.Value).ToString() : "0"),
                    InputDisplayMode.Flags => (u.Tick, Convert.ToString(b.HasValue ? (uint)b.Value : 0, 2).PadLeft(32, '0')),
                    _ => throw new ArgProcessProgrammerException($"invalid input display mode: \"{mode}\"")
                });