/// <summary>
        /// Extracts the next packet from the collated buffer.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// For collated packets, this decodes and decompresses the next packet. For non-collated
        /// packets, this simply returns the packet given to <see cref="PacketBuffer"/>, then
        /// <code>null</code> on the following call.
        /// </remarks>
        public PacketBuffer Next()
        {
            PacketBuffer next = null;

            if (_packetStream != null)
            {
                PacketHeader header = new PacketHeader();
                // Read the header via the network reader to do the network byte order swap as required.
                if (_targetBytes - _decodedBytes > PacketHeader.Size && header.Read(_streamReader))
                {
                    _decodedBytes += (uint)PacketHeader.Size;
                    next           = new PacketBuffer(header.PacketSize);
                    next.WriteHeader(header);
                    // Read payload data as it to preserve network byte order.
                    next.Emplace(_packetStream, header.DataSize);
                    next.FinalisePacket(true);
                    _decodedBytes += (uint)header.DataSize;
                    // Skip CRC if present.
                    if ((header.Flags & (byte)PacketFlag.NoCrc) == 0)
                    {
                        // Skip CRC.
                        _streamReader.ReadUInt16();
                        _decodedBytes += 2;
                    }
                }
            }
            else
            {
                // Not compressed.
                next = _packet;
                SetPacket(null);
            }

            // Check for failure or completion.
            if (next == null || _decodedBytes >= _targetBytes)
            {
                SetPacket(null);
            }

            return(next);
        }
Exemplo n.º 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);
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Decode the contents of the <paramref name="keyframeStream"/> and add packets to
        /// <paramref name="packets"/>.
        /// </summary>
        /// <param name="snapStream">The keyframe stream to decode</param>
        /// <param name="packets">The packet list to add to.</param>
        /// <returns></returns>
        private bool DecodeKeyframeStream(Stream snapStream, System.Collections.Generic.List <PacketBuffer> packets)
        {
            if (snapStream == null)
            {
                return(false);
            }

            bool         ok = true;
            bool         allowCompression = true;
            int          bytesRead        = 0;
            PacketHeader header           = new PacketHeader();

            byte[] headerBuffer      = new byte[PacketHeader.Size];
            uint   queuedPacketCount = 0;

            try
            {
                do
                {
                    if (allowCompression && GZipUtil.IsGZipStream(snapStream))
                    {
                        allowCompression = false;
                        snapStream       = new GZipStream(snapStream, CompressionMode.Decompress);
                    }
                    bytesRead = snapStream.Read(headerBuffer, 0, headerBuffer.Length);

                    // ok = false if done, true when we read something.
                    ok = bytesRead == 0;
                    if (bytesRead == headerBuffer.Length)
                    {
                        if (header.Read(new NetworkReader(new MemoryStream(headerBuffer, false))))
                        {
                            // Read the header. Determine the expected size and read that much more data.
                            int          crcSize = ((header.Flags & (byte)PacketFlag.NoCrc) == 0) ? Crc16.CrcSize : 0;
                            PacketBuffer packet  = new PacketBuffer(header.PacketSize + crcSize);
                            packet.Emplace(headerBuffer, bytesRead);
                            packet.Emplace(snapStream, header.PacketSize + crcSize - bytesRead);
                            if (packet.Status == PacketBufferStatus.Complete)
                            {
                                // Check for end of frame messages to yield on.
                                packets.Add(packet);
                                ok = true;
                                ++queuedPacketCount;
                            }
                            else
                            {
                                switch (packet.Status)
                                {
                                case PacketBufferStatus.CrcError:
                                    Log.Error("Failed to decode packet CRC.");
                                    break;

                                case PacketBufferStatus.Collating:
                                    Log.Error("Insufficient data for packet.");
                                    break;

                                default:
                                    break;
                                }
                            }
                        }
                    }
                } while (ok && bytesRead != 0);

                Log.Diag("Decoded {0} packets from keyframe", queuedPacketCount);
            }
            catch (Exception e)
            {
                ok = false;
                Log.Exception(e);
            }

            return(ok);
        }