Beispiel #1
0
        private BinaryWriter CreateOutputWriter()
        {
            try
            {
                string filePath = GenerateNewOutputFile();
                if (string.IsNullOrEmpty(filePath))
                {
                    Console.WriteLine($"Unable to generate a numbered file name using the prefix: {OutputPrefix}. Try cleaning up the output directory.");
                    return(null);
                }
                Console.WriteLine($"Recording to: {filePath}");

                Stream     stream     = null;
                FileStream fileStream = new FileStream(filePath, FileMode.Create);
                stream = fileStream;

                // Write the recording header uncompressed to the file.
                // We'll rewind here later and update the frame count.
                // Write to a memory stream to prevent corruption of the file stream when we wrap it
                // in a GZipStream.
                MemoryStream headerStream = new MemoryStream(512);
                BinaryWriter writer       = new NetworkWriter(headerStream);
                WriteRecordingHeader(writer);
                writer.Flush();
                byte[] headerBytes = headerStream.ToArray();

                // Copy header to the file.
                fileStream.Write(headerBytes, 0, headerBytes.Length);
                // Dispose of the temporary objects.
                headerBytes  = null;
                writer       = null;
                headerStream = null;

                switch (DecodeMode)
                {
                case Mode.CollateAndCompress:
                    stream = new CollationStream(fileStream, true);
                    break;

                case Mode.CollateOnly:
                    stream = new CollationStream(fileStream, false);
                    break;

                case Mode.FileCompression:
                    stream = new GZipStream(fileStream, CompressionLevel.Fastest);
                    break;

                case Mode.Uncompressed:
                    stream = fileStream;
                    break;
                }

                return(new NetworkWriter(stream));
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e);
            }
            return(null);
        }
Beispiel #2
0
        private void FinaliseOutput(BinaryWriter writer, uint frameCount)
        {
            // Rewind the stream to the beginning and find the first RoutingID.ServerInfo message
            // and RoutingID.Control message with a ControlMessageID.FrameCount ID. These should be
            // the first and second messages in the stream.
            // We'll limit searching to the first 5 messages.
            long serverInfoMessageStart = -1;
            long frameCountMessageStart = -1;

            writer.Flush();

            // Extract the stream from the writer. The first stream may be a GZip stream, in which case we must
            // extract the stream that is writing to instead as we can't rewind compression streams
            // and we wrote the header raw.
            writer.BaseStream.Flush();

            Stream          outStream = null;
            CollationStream zipStream = writer.BaseStream as CollationStream;

            if (zipStream != null)
            {
                outStream = zipStream.BaseStream;
                zipStream.Flush();
            }
            else
            {
                outStream = writer.BaseStream;
            }

            // Check we are allowed to seek the stream.
            if (!outStream.CanSeek)
            {
                // Stream does not support seeking. The frame count will not be fixed.
                return;
            }

            // Record the initial stream position to restore later.
            long restorePos = outStream.Position;

            outStream.Seek(0, SeekOrigin.Begin);

            long streamPos = 0;

            byte[]       headerBuffer = new byte[PacketHeader.Size];
            PacketHeader header       = new PacketHeader();

            byte[] markerValidationBytes = BitConverter.GetBytes(Tes.IO.Endian.ToNetwork(PacketHeader.PacketMarker));
            byte[] markerBytes           = new byte[markerValidationBytes.Length];
            bool   markerValid           = false;

            int attemptsRemaining = 5;
            int byteReadLimit     = 0;

            markerBytes[0] = 0;
            while ((frameCountMessageStart < 0 || serverInfoMessageStart < 0) && attemptsRemaining > 0 && outStream.CanRead)
            {
                --attemptsRemaining;
                markerValid = false;

                // Limit the number of bytes we try read in each attempt.
                byteReadLimit = 1024;
                while (byteReadLimit > 0)
                {
                    --byteReadLimit;
                    outStream.Read(markerBytes, 0, 1);
                    if (markerBytes[0] == markerValidationBytes[0])
                    {
                        markerValid = true;
                        int i = 1;
                        for (i = 1; markerValid && outStream.CanRead && i < markerValidationBytes.Length; ++i)
                        {
                            outStream.Read(markerBytes, i, 1);
                            markerValid = markerValid && markerBytes[i] == markerValidationBytes[i];
                        }

                        if (markerValid)
                        {
                            break;
                        }
                        else
                        {
                            // We've failed to fully validate the maker. However, we did read and validate
                            // one byte in the marker, then continued reading until the failure. It's possible
                            // that the last byte read, the failed byte, may be the start of the actual marker.
                            // We check this below, and if so, we rewind the stream one byte in order to
                            // start validation from there on the next iteration. We can ignore the byte if
                            // it is does not match the first validation byte. We are unlikely to ever make this
                            // match though.
                            --i; // Go back to the last read byte.
                            if (markerBytes[i] == markerValidationBytes[0])
                            {
                                // Potentially the start of a new marker. Rewind the stream to attempt to validate it.
                                outStream.Seek(-1, SeekOrigin.Current);
                            }
                        }
                    }
                }

                if (markerValid && outStream.CanRead)
                {
                    // Potential packet target. Record the stream position at the start of the marker.
                    streamPos = outStream.Position - markerBytes.Length;
                    outStream.Seek(streamPos, SeekOrigin.Begin);

                    // Test the packet.
                    int bytesRead = outStream.Read(headerBuffer, 0, headerBuffer.Length);
                    if (bytesRead == headerBuffer.Length)
                    {
                        // Create a packet.
                        if (header.Read(new NetworkReader(new MemoryStream(headerBuffer, false))))
                        {
                            // Header is OK. Looking for RoutingID.Control
                            if (header.RoutingID == (ushort)RoutingID.ServerInfo)
                            {
                                serverInfoMessageStart = streamPos;
                            }
                            else if (header.RoutingID == (ushort)RoutingID.Control)
                            {
                                // It's control message. Complete and validate the packet.
                                // Read the header. Determine the expected size and read that much more data.
                                PacketBuffer packet = new PacketBuffer(header.PacketSize + Crc16.CrcSize);
                                packet.Emplace(headerBuffer, bytesRead);
                                packet.Emplace(outStream, header.PacketSize + Crc16.CrcSize - bytesRead);
                                if (packet.Status == PacketBufferStatus.Complete)
                                {
                                    // Packet complete. Extract the control message.
                                    NetworkReader  packetReader = new NetworkReader(packet.CreateReadStream(true));
                                    ControlMessage message      = new ControlMessage();
                                    if (message.Read(packetReader) && header.MessageID == (ushort)ControlMessageID.FrameCount)
                                    {
                                        // Found the message location.
                                        frameCountMessageStart = streamPos;
                                    }
                                }
                            }
                            else
                            {
                                // At this point, we've failed to find the right kind of header. We could use the payload size to
                                // skip ahead in the stream which should align exactly to the next message.
                                // Not done for initial testing.
                            }
                        }
                    }
                }
            }

            if (serverInfoMessageStart >= 0)
            {
                // Found the correct location. Seek the stream to here and write a new FrameCount control message.
                outStream.Seek(serverInfoMessageStart, SeekOrigin.Begin);
                PacketBuffer packet = new PacketBuffer();

                header = PacketHeader.Create((ushort)RoutingID.ServerInfo, 0);

                packet.WriteHeader(header);
                _serverInfo.Write(packet);
                packet.FinalisePacket();
                BinaryWriter patchWriter = new Tes.IO.NetworkWriter(outStream);
                packet.ExportTo(patchWriter);
                patchWriter.Flush();
            }

            if (frameCountMessageStart >= 0)
            {
                // Found the correct location. Seek the stream to here and write a new FrameCount control message.
                outStream.Seek(frameCountMessageStart, SeekOrigin.Begin);
                PacketBuffer   packet        = new PacketBuffer();
                ControlMessage frameCountMsg = new ControlMessage();

                header = PacketHeader.Create((ushort)RoutingID.Control, (ushort)ControlMessageID.FrameCount);

                frameCountMsg.ControlFlags = 0;
                frameCountMsg.Value32      = frameCount; // Placeholder. Frame count is currently unknown.
                frameCountMsg.Value64      = 0;
                packet.WriteHeader(header);
                frameCountMsg.Write(packet);
                packet.FinalisePacket();
                BinaryWriter patchWriter = new Tes.IO.NetworkWriter(outStream);
                packet.ExportTo(patchWriter);
                patchWriter.Flush();
            }

            if (outStream.Position != restorePos)
            {
                outStream.Seek(restorePos, SeekOrigin.Begin);
            }
        }