Frame readPacketBlock()   // an obsolete type but we may see it
        {
            blockStart     = r.BaseStream.Position - 4;
            blockLength    = r.ReadUInt32();
            nextBlockStart = blockStart + blockLength;

            Frame  nf          = null;
            UInt16 InterfaceID = read2Bytes();  // InterfaceID is 0-based
            UInt16 DropsCount  = read2Bytes();
            UInt32 timestampHi = read4Bytes();
            UInt32 timestampLo = read4Bytes();
            UInt32 capturedLen = read4Bytes();
            UInt32 packetLen   = read4Bytes();

            InterfaceOptions intOpt = (InterfaceOptions)(interfaceOptions[InterfaceID]);

            frameNumber++;
            nf                = new Frame();
            nf.frameLength    = packetLen;
            nf.bytesAvailable = capturedLen;
            nf.frameNumber    = frameNumber;
            nf.length         = capturedLen;
            nf.ticks          = timeToTicks(timestampHi, timestampLo, intOpt.timeResolution, intOpt.timestampOffset);
            nf.data           = r.ReadBytes((Int32)capturedLen);
            nf.linkType       = (UInt16)intOpt.LinkType;

            // Seek to beginning of the next block
            r.BaseStream.Seek(nextBlockStart, SeekOrigin.Begin);

            return(nf);
        }
        Frame readSimplePacketBlock()  // not supported because it does not contain a timestamp
        {
            blockStart     = r.BaseStream.Position - 4;
            blockLength    = r.ReadUInt32();
            nextBlockStart = blockStart + blockLength;

            if (interfaceOptions == null || interfaceOptions.Count == 0)
            {
                throw new Exception("Invalid PCAPNG file. Simple Packet Block at offset " + blockStart + " not allowed. Section at offset " + sectionStart + " has no Interface Description Blocks.");
            }

            if (interfaceOptions.Count > 1)
            {
                throw new Exception("Invalid PCAPNG file. Simple Packet Block at offset " + blockStart + " not allowed. Section at offset " + sectionStart + " has more than one Interface Description Block.");
            }

            // does not have timestamps, so we really do not want these ...

            InterfaceOptions intOpt = (InterfaceOptions)(interfaceOptions[0]);

            if (intOpt.LinkType == 1)   // Ethernet - return null for all others
            {
                throw new Exception("Simple Packet Block not allowed since it does not contain any timestamp information.");
            }

            // ignore the block if not Ethernet - perhaps a new section will have Ethernet traffic

            // Seek to beginning of the next block
            r.BaseStream.Seek(nextBlockStart, SeekOrigin.Begin);

            return(null);
        }
        Frame readEnahancedPacketBlock()
        {
            blockStart     = r.BaseStream.Position - 4;
            blockLength    = r.ReadUInt32();
            nextBlockStart = blockStart + blockLength;

            //
            // The main change from readPacketBlock and readEnhancedPacketBlock is that the InterfaceID is now 32-bits instead of 16 bits
            // and we no longer read the number of dropped packets (16 bite) as part of the block header. For dropped packet count, need to
            // read packet options after the frame data: OptID = 4, OptLen = 8
            //
            // See readInterfaceBlock for implementation details.
            // Need to find the remainder for the frame data to align to DWORD boundary before reading options.
            //

            Frame  nf          = null;
            UInt32 InterfaceID = read4Bytes();   // InterfaceID is 0-based
            UInt32 timestampHi = read4Bytes();
            UInt32 timestampLo = read4Bytes();
            UInt32 capturedLen = read4Bytes();
            UInt32 packetLen   = read4Bytes();

            InterfaceOptions intOpt = (InterfaceOptions)(interfaceOptions[(Int32)InterfaceID]);

            if (intOpt.LinkType == 1)   // Ethernet - return null for all others
            {
                frameNumber++;
                nf                = new Frame();
                nf.frameLength    = packetLen;
                nf.bytesAvailable = capturedLen;
                nf.frameNumber    = frameNumber;
                nf.length         = capturedLen;
                nf.ticks          = timeToTicks(timestampHi, timestampLo, intOpt.timeResolution, intOpt.timestampOffset);
                nf.data           = r.ReadBytes((Int32)capturedLen);
            }

            // Seek to beginning of the next block
            r.BaseStream.Seek(nextBlockStart, SeekOrigin.Begin);

            return(nf);
        }
        void readInterfaceBlock()
        {
            //
            // There must be at least one of these following the header block and before the first packet block.
            // According to the spec, it must be immediately following, but the app doesn't care as long as we haven't hit a packet without a corresponding entry.
            //
            // We only support Ethernet (LinkType = InterfaceID = 1). All other packet blocks are ignored.
            //
            // For Simple Packet blocks, there must be 1 and only 1 Interface block in that section. Other packet types have the InterfaceID == LinkType in them.
            // We do not support Simple Packet blocks because they do not contain a timestamp.
            //

            blockStart     = r.BaseStream.Position - 4;
            blockLength    = r.ReadUInt32();
            nextBlockStart = blockStart + blockLength;

            InterfaceOptions intOpt = new InterfaceOptions();

            intOpt.LinkType = read2Bytes();
            read2Bytes(); // skip unused portion of block
            intOpt.maxBytesRead = read4Bytes();

            interfaceOptions.Add(intOpt);

            // read interface options

            UInt16 OptType = 0;
            UInt16 OptLen  = 0;
            int    padding = 0;

            if (blockLength > 40)
            {
                OptType = read2Bytes();
                OptLen  = read2Bytes();
                padding = OptLen % 4;

                while (OptType != 0)
                {
                    switch (OptType)
                    {
                    case 9:
                    {
                        intOpt.timeResolution = r.ReadByte();
                        r.ReadBytes(padding);
                        break;
                    }

                    case 14:
                    {
                        intOpt.timestampOffset = (Int32)read8Bytes();
                        break;
                    }

                    default:      // skip unhandled option types
                    {
                        r.ReadBytes(OptLen + padding);
                        break;
                    }
                    }

                    OptType = read2Bytes();
                    OptLen  = read2Bytes();
                    padding = OptLen % 4;
                }
            }

            // Seek to beginning of the next block
            r.BaseStream.Seek(nextBlockStart, SeekOrigin.Begin);
        }