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; } } } }