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