Пример #1
0
        public void PlayDemo(string filename)
        {
            if (File.Exists(filename))
            {
                this.connState   = ConnectionState.Connected;
                this.demoPlaying = true;

                this.demoFileStream       = File.OpenRead(filename);
                this.demoStream           = new Q3DemoStream(this.demoFileStream, this);
                this.q3HuffDemoReadStream = new Q3HuffmanStream(this.demoStream, CompressionMode.Decompress);
                q3HuffDemoReadStream.InitWithQ3Data();
                q3HuffDemoReadStream.TreeIsFrozen = true;

                try {
                    while (this.connState >= ConnectionState.Connected && this.connState < ConnectionState.Primed)
                    {
                        ReadPacket(this.q3HuffDemoReadStream);
                    }

                    this.firstDemoFrameSkipped = false;

                    while (true)
                    {
                        ReadPacket(this.q3HuffDemoReadStream);
                    }
                } catch (Exception ex) {
                    // Demo end
                }
            }
            else
            {
                throw new FileNotFoundException("Demo file not found", filename);
            }
        }
Пример #2
0
        private void ReadPacket(Q3HuffmanStream stream)
        {
            List <string> cmdLog  = new List <string> ();
            PacketKind    pktKind = stream.BeginRead();

            this.reliableAcknowledge = stream.ReadInt32();
            ServerCommandType cmd;

            while (ServerCommandType.EOF != (cmd = ( ServerCommandType )stream.ReadByte()))
            {
                switch (cmd)
                {
                case ServerCommandType.Nop:
                    cmdLog.Add("Nop");
                    break;

                case ServerCommandType.ServerCommand:
                    this.ParseCommandString(stream);
                    cmdLog.Add("ServerCommand");
                    break;

                case ServerCommandType.GameState:
                    this.ParseGamestate(stream);
                    cmdLog.Add("GameState");
                    break;

                case ServerCommandType.Snapshot:
                    this.ParseSnapshot(stream);
                    cmdLog.Add("Snapshot");
                    break;

                case ServerCommandType.Download:
                    // We never download ;)
                    return;

                default:
                    // Unknown command
                    return;
                }
            }

            stream.EndRead();
        }
Пример #3
0
        public void Connect(IPAddress ip, int port)
        {
            Random rnd = new Random();

            qport = ( ushort )rnd.Next(65536);                  // Generate port from 0 to 65535 (inclusively)
            // STATIC_DEBUG
            //qport = 0x2233; // ( short ) rnd.Next ( 65536 );	// Generate port from 0 to 65535 (inclusively)
            incomingSequence = 0;
            outgoingSequence = 1;

            IPEndPoint ep = new IPEndPoint(ip, port);

            sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            try { sock.Connect(ep); } catch { throw; }
            q3NetStream   = new Q3NetworkStream(sock, this, FileAccess.ReadWrite);
            q3CryptStream = new Q3CryptStream(q3NetStream, this, FileAccess.ReadWrite);
            q3HuffCStream = new Q3HuffmanStream(q3CryptStream, CompressionMode.Compress);
            q3HuffCStream.InitWithQ3Data();
            q3HuffCStream.TreeIsFrozen = true;

            q3HuffDStream = new Q3HuffmanStream(q3CryptStream, CompressionMode.Decompress);
            q3HuffDStream.InitWithQ3Data();
            q3HuffDStream.TreeIsFrozen = true;

            if (DoConnectionlessHandshake())
            {
                // enter main loop

                SendUserInfo();                         // FIXIT: move this out of here

                while (true)
                {
                    ReadPacket(q3HuffDStream);
                    SendCmd();
                    System.Threading.Thread.Sleep(10);
                }
            }
            else
            {
                throw new Exception(string.Format("Could not connect to {0}", ep));
            }
        }
Пример #4
0
        private bool DoConnectionlessHandshake()
        {
            string command;

            byte [] data;

            this.connState = ConnectionState.Connecting;
            WriteConnectionlessPacket("getchallenge", null, 0, 0);

            if (ReadConnectionlessPacket(out command, out data))
            {
                if (command == "challengeResponse")
                {
                    this.challenge = Convert.ToInt32(Encoding.Default.GetString(data));

                    MemoryStream ms = new MemoryStream();
                    ms.Position = 2;
                    Q3HuffmanStream huff = new Q3HuffmanStream(ms, System.IO.Compression.CompressionMode.Compress);

                    string connStr = string.Format(@"\challenge\{0}\qport\{1}\protocol\{2}{3}",
                                                   this.challenge, this.qport, PROTOCOL_VERSION, userInfo);
                    huff.WriteString(connStr);
                    huff.Flush();
                    ms.Position = 0;
                    ms.Write(ExBitConverter.GetBytes(( short )connStr.Length, false), 0, 2);

                    this.connState = ConnectionState.Challenging;
                    WriteConnectionlessPacket("connect ", ms.GetBuffer(), 0, ( int )ms.Length);

                    if (ReadConnectionlessPacket(out command, out data))
                    {
                        if (command == "connectResponse")
                        {
                            this.connState = ConnectionState.Connected;
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
Пример #5
0
        private void ParseGamestate(Q3HuffmanStream stream)
        {
            this.gameState.Clear();
            this.incomingCommandSequence = stream.ReadInt32();
            EntityState nullstate = new EntityState();

            ServerCommandType cmd;

            entityBaselinesEvent.WaitOne();

            while (ServerCommandType.EOF != (cmd = ( ServerCommandType )stream.ReadByte()))
            {
                switch (cmd)
                {
                case ServerCommandType.ConfigString:
                    int i = stream.ReadInt16();
                    gameState [i] = stream.ReadString();
                    break;

                case ServerCommandType.BaseLine:
                    int newnum = stream.ReadBits(10);

                    ReadDeltaEntity(stream, nullstate, ref entityBaselines [newnum], newnum);
                    break;

                default:
                    // Unknown command
                    break;
                }
            }

            entityBaselinesEvent.Set();

            this.clientNum    = stream.ReadInt32();
            this.checksumFeed = stream.ReadInt32();

            SystemInfoChanged();
            this.connState = ConnectionState.Primed;

            //SendPureChecksums ();
        }
Пример #6
0
        private void ParseCommandString(Q3HuffmanStream stream)
        {
            this.incomingCommandSequence = stream.ReadInt32();
            string cmdStr = stream.ReadString();
            int    index  = this.incomingCommandSequence & (MAX_RELIABLE_COMMANDS - 1);

            this.incomingReliableCommands [index] = cmdStr;

            if (cmdStr.StartsWith("cs "))
            {
                string [] parts = cmdStr.Split(' ');

                int csId;

                if (parts.Length >= 2)
                {
                    try {
                        csId = Convert.ToInt32(parts [1]);
                    } catch {
                        csId = 0;
                    }

                    this.gameState [csId] = string.Join(" ", parts, 2, parts.Length - 2);

                    if (csId == 1)
                    {
                        SystemInfoChanged();
                    }
                }
            }

            if (this.ServerCommandReceived != null)
            {
                this.ServerCommandReceived(this, cmdStr);
            }
        }
Пример #7
0
        private void DeltaEntity(Q3HuffmanStream stream, Snapshot frame, int newNum, EntityState old, bool unchanged)
        {
            EntityState state;

            // save the parsed entity state into the big circular buffer so
            // it can be used as the source for a later delta
            state = this.parseEntities [this.parseEntitiesNum & (MAX_PARSE_ENTITIES - 1)];

            if (unchanged)
            {
                old.CopyTo(state);
            }
            else
            {
                this.ReadDeltaEntity(stream, old, ref state, newNum);
            }

            if (state.number == (MAX_GENTITIES - 1))
            {
                return;                         // entity was delta removed
            }
            this.parseEntitiesNum++;
            frame.numEntities++;
        }
Пример #8
0
        private void ReadDeltaPlayerstate(Q3HuffmanStream stream, PlayerState from, ref PlayerState to)
        {
            int i;

            if (from == null)
            {
                from = new PlayerState();
            }

            from.CopyTo(to);

            int      lc = stream.ReadByte();
            NetField field;
            int      trunc;

            for (i = 0, field = PlayerState.fields [i]; i < lc; i++)
            {
                field = PlayerState.fields [i];

                if (stream.ReadBits(1) == 0)
                {
                    // no change
                    KeyValueCoder.TrySetFieldValue(to, field.name,
                                                   KeyValueCoder.TryGetFieldValue(from, field.name));
                }
                else
                {
                    if (field.bits == 0)
                    {
                        // float
                        if (stream.ReadBits(1) == 0)
                        {
                            // integral float
                            trunc = stream.ReadBits(NetField.FLOAT_INT_BITS);
                            // bias to allow equal parts positive and negative
                            trunc -= NetField.FLOAT_INT_BIAS;
                            KeyValueCoder.TrySetFieldValue(to, field.name, trunc);
                        }
                        else
                        {
                            // full floating point value
                            // FIXIT: wrong conversion from 32 bits to floating point value
                            KeyValueCoder.TrySetFieldValue(to, field.name, stream.ReadInt32());
                        }
                    }
                    else
                    {
                        // integer
                        KeyValueCoder.TrySetFieldValue(to, field.name, stream.ReadBits(( int )field.bits));
                    }
                }
            }

            for (i = lc, field = PlayerState.fields [lc]; i < PlayerState.fields.Length; i++)
            {
                field = PlayerState.fields [i];

                // no change
                KeyValueCoder.TrySetFieldValue(to, field.name,
                                               KeyValueCoder.TryGetFieldValue(from, field.name));
            }

            short bits;

            // read the arrays
            if (0 != stream.ReadBits(1))
            {
                // parse stats
                if (0 != stream.ReadBits(1))
                {
                    bits = stream.ReadInt16();

                    for (i = 0; i < 16; i++)
                    {
                        if (0 != (bits & (1 << i)))
                        {
                            to.stats [i] = stream.ReadInt16();
                        }
                    }
                }

                // parse persistant stats
                if (0 != stream.ReadBits(1))
                {
                    bits = stream.ReadInt16();

                    for (i = 0; i < 16; i++)
                    {
                        if (0 != (bits & (1 << i)))
                        {
                            to.persistant [i] = stream.ReadInt16();
                        }
                    }
                }

                // parse ammo
                if (0 != stream.ReadBits(1))
                {
                    bits = stream.ReadInt16();

                    for (i = 0; i < 16; i++)
                    {
                        if (0 != (bits & (1 << i)))
                        {
                            to.ammo [i] = stream.ReadInt16();
                        }
                    }
                }

                // parse powerups
                if (0 != stream.ReadBits(1))
                {
                    bits = stream.ReadInt16();

                    for (i = 0; i < 16; i++)
                    {
                        if (0 != (bits & (1 << i)))
                        {
                            to.powerups [i] = stream.ReadInt32();
                        }
                    }
                }
            }
        }
Пример #9
0
        private void ParseSnapshot(Q3HuffmanStream stream)
        {
            Snapshot old;
            Snapshot newSnap = new Snapshot();
            int      deltaNum;

            newSnap.serverCommandNum = this.incomingCommandSequence;
            newSnap.serverTime       = stream.ReadInt32();
            newSnap.messageNum       = this.incomingSequence;

            if (0 == (deltaNum = stream.ReadByte()))
            {
                newSnap.deltaNum = -1;
            }
            else
            {
                newSnap.deltaNum = newSnap.messageNum - deltaNum;
            }

            newSnap.snapFlags = stream.ReadByte();

            // If the frame is delta compressed from data that we
            // no longer have available, we must suck up the rest of
            // the frame, but not use it, then ask for a non-compressed
            // message
            if (newSnap.deltaNum <= 0)
            {
                newSnap.valid = true;                           // uncompressed frame
                old           = null;
                //clc.demowaiting = false;	// we can start recording now
            }
            else
            {
                old = this.snapshots [newSnap.deltaNum & PACKET_MASK];

                if (!old.valid)
                {
                    // should never happen
                    // "Delta from invalid frame (not supposed to happen!)"
                }
                else if (old.messageNum != newSnap.deltaNum)
                {
                    // The frame that the server did the delta from
                    // is too old, so we can't reconstruct it properly.
                    // "Delta frame too old."
                }
                else if (parseEntitiesNum - old.parseEntitiesNum > MAX_PARSE_ENTITIES - 128)
                {
                    // "Delta parseEntitiesNum too old."
                }
                else
                {
                    newSnap.valid = true;                       // valid delta parse
                }
            }

            // read areamask
            int len = stream.ReadByte();

            stream.Read(newSnap.areamask, 0, len);

            // read playerinfo
            if (old != null)
            {
                ReadDeltaPlayerstate(stream, old.ps, ref newSnap.ps);
            }
            else
            {
                ReadDeltaPlayerstate(stream, null, ref newSnap.ps);
            }

            // read packet entities
            ParsePacketEntities(stream, old, newSnap);

            // if not valid, dump the entire thing now that it has
            // been properly read
            if (!newSnap.valid)
            {
                return;
            }

            // clear the valid flags of any snapshots between the last
            // received and this one, so if there was a dropped packet
            // it won't look like something valid to delta from next
            // time we wrap around in the buffer
            int oldMessageNum = this.snap.messageNum + 1;

            if (newSnap.messageNum - oldMessageNum >= PACKET_BACKUP)
            {
                oldMessageNum = newSnap.messageNum - (PACKET_BACKUP - 1);
            }

            for ( ; oldMessageNum < newSnap.messageNum; oldMessageNum++)
            {
                this.snapshots [oldMessageNum & PACKET_MASK].valid = false;
            }

            // copy to the current good spot
            this.snap      = newSnap;
            this.snap.ping = 999;

            // calculate ping time
            for (int i = 0; i < PACKET_BACKUP; i++)
            {
                int packetNum = (this.outgoingSequence - 1 - i) & PACKET_MASK;

                if (this.snap.ps.commandTime >= this.outPackets [packetNum].serverTime)
                {
                    this.snap.ping = ( int )(DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - this.outPackets[packetNum].realtime;
                    break;
                }
            }

            // save the frame off in the backup array for later delta comparisons
            this.snapshots [this.snap.messageNum & PACKET_MASK] = this.snap;
            this.newSnapshots = true;
        }
Пример #10
0
        private void ReadDeltaEntity(Q3HuffmanStream stream, EntityState from, ref EntityState to, int number)
        {
            // check for a remove
            if (stream.ReadBits(1) == 1)
            {
                new EntityState().CopyTo(to);
                to.number = MAX_GENTITIES - 1;

                return;
            }

            // check for no delta
            if (stream.ReadBits(1) == 0)
            {
                from.CopyTo(to);
                to.number = number;

                return;
            }

            int lc = stream.ReadByte();

            to.number = number;
            int      i;
            NetField field;
            int      trunc;

            for (i = 0, field = EntityState.fields [i]; i < lc && i < EntityState.fields.Length; i++)
            {
                field = EntityState.fields [i];

                if (stream.ReadBits(1) == 0)
                {
                    // no change
                    KeyValueCoder.TrySetFieldValue(to, field.name,
                                                   KeyValueCoder.TryGetFieldValue(from, field.name));
                }
                else
                {
                    if (field.bits == 0)
                    {
                        // float
                        if (stream.ReadBits(1) == 0)
                        {
                            KeyValueCoder.TrySetFieldValue(to, field.name, 0);
                        }
                        else
                        {
                            if (stream.ReadBits(1) == 0)
                            {
                                // integral float
                                trunc = stream.ReadBits(NetField.FLOAT_INT_BITS);
                                // bias to allow equal parts positive and negative
                                trunc -= NetField.FLOAT_INT_BIAS;
                                KeyValueCoder.TrySetFieldValue(to, field.name, trunc);
                            }
                            else
                            {
                                // full floating point value
                                // FIXIT: wrong conversion from 32 bits to floating point value
                                KeyValueCoder.TrySetFieldValue(to, field.name, stream.ReadInt32());
                            }
                        }
                    }
                    else
                    {
                        if (stream.ReadBits(1) == 0)
                        {
                            KeyValueCoder.TrySetFieldValue(to, field.name, 0);
                        }
                        else
                        {
                            // integer
                            KeyValueCoder.TrySetFieldValue(to, field.name, stream.ReadBits(( int )field.bits));
                        }
                    }
                }
            }

            for (i = lc; i < EntityState.fields.Length; i++)
            {
                field = EntityState.fields [i];

                // no change
                KeyValueCoder.TrySetFieldValue(to, field.name,
                                               KeyValueCoder.TryGetFieldValue(from, field.name));
            }
        }
Пример #11
0
        private void ParsePacketEntities(Q3HuffmanStream stream, Snapshot oldFrame, Snapshot newFrame)
        {
            EntityState oldState = null;
            int         oldIndex = 0;
            int         oldNum;

            parseEntitiesEvent.WaitOne();
            entityBaselinesEvent.WaitOne();
            newFrame.parseEntitiesNum = this.parseEntitiesNum;
            newFrame.numEntities      = 0;

            if (null == oldFrame)
            {
                oldNum = 99999;
            }
            else
            {
                if (oldIndex >= oldFrame.numEntities)
                {
                    oldNum = 99999;
                }
                else
                {
                    oldState = this.parseEntities [(oldFrame.parseEntitiesNum + oldIndex) & (MAX_PARSE_ENTITIES - 1)];
                    oldNum   = oldState.number;
                }
            }

            int newNum;

            while ((MAX_GENTITIES - 1) != (newNum = stream.ReadBits(GENTITYNUM_BITS)))
            {
                while (oldNum < newNum)
                {
                    // one or more entities from the old packet are unchanged
                    DeltaEntity(stream, newFrame, oldNum, oldState, true);

                    oldIndex++;

                    if (oldIndex >= oldFrame.numEntities)
                    {
                        oldNum = 99999;
                    }
                    else
                    {
                        oldState = this.parseEntities[(oldFrame.parseEntitiesNum + oldIndex) & (MAX_PARSE_ENTITIES - 1)];
                        oldNum   = oldState.number;
                    }
                }

                if (oldNum == newNum)
                {
                    // delta from previous
                    DeltaEntity(stream, newFrame, newNum, oldState, false);

                    oldIndex++;

                    if (oldIndex >= oldFrame.numEntities)
                    {
                        oldNum = 99999;
                    }
                    else
                    {
                        oldState = this.parseEntities[(oldFrame.parseEntitiesNum + oldIndex) & (MAX_PARSE_ENTITIES - 1)];
                        oldNum   = oldState.number;
                    }

                    continue;
                }

                if (oldNum > newNum)
                {
                    // delta from baseline
                    DeltaEntity(stream, newFrame, newNum, this.entityBaselines [newNum], false);

                    continue;
                }
            }

            // any remaining entities in the old frame are copied over
            while (oldNum != 99999)
            {
                // one or more entities from the old packet are unchanged
                DeltaEntity(stream, newFrame, oldNum, oldState, true);

                oldIndex++;

                if (oldIndex >= oldFrame.numEntities)
                {
                    oldNum = 99999;
                }
                else
                {
                    oldState = this.parseEntities[(oldFrame.parseEntitiesNum + oldIndex) & (MAX_PARSE_ENTITIES - 1)];
                    oldNum   = oldState.number;
                }
            }

            entityBaselinesEvent.Set();
            parseEntitiesEvent.Set();
        }
Пример #12
0
        //
        // Summary:
        //     Writes encoded bytes to the underlying stream from the specified byte
        //     array.
        //
        // Parameters:
        //   array:
        //     The array used to store encoded bytes.
        //
        //   offset:
        //     The location in the array to begin reading.
        //
        //   count:
        //     The number of bytes encoded.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     array is null.
        //
        //   System.InvalidOperationException:
        //     The Q3Cypher.CypherMode value was Decode when the object
        //     was created.  - or - The underlying stream does not support writing.
        //
        //   System.ArgumentOutOfRangeException:
        //     offset or count is less than zero.  -or- array length minus the index starting
        //     point is less than count.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        public override void Write(byte [] array, int offset, int count)
        {
            #region Exception checks
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }

            if (baseStream == null)
            {
                throw new ObjectDisposedException("baseStream");
            }

            if (mode == CypherMode.Decode)
            {
                throw new InvalidOperationException("Stream cannot be written while its mode set to Q3Cypher.CypherMode.Decode");
            }

            if (!baseStream.CanWrite)
            {
                throw new InvalidOperationException("Underlying stream cannot be written");
            }

            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException("offset");
            }

            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            if (array.Length < offset)
            {
                throw new ArgumentOutOfRangeException("offset");
            }

            if (array.Length < offset + count)
            {
                throw new ArgumentOutOfRangeException("count");
            }
            #endregion Exception checks

            (baseStream as Q3DatagramStream).BeginWritePacket(PacketKind.ConnectionOriented);
            Q3HuffmanStream huff = connection.Q3HuffDStream;

            int bloc                = 0;
            int serverId            = huff.DecompressBufferToInt32(array, 0, ref bloc);
            int messageAcknowledge  = huff.DecompressBufferToInt32(array, 0, ref bloc);
            int reliableAcknowledge = huff.DecompressBufferToInt32(array, 0, ref bloc);

            byte [] buffer = new byte [count];
            Array.Copy(array, buffer, CL_ENCODE_START < count ? CL_ENCODE_START : count);
            string pattern = connection.IncomingReliableCommands [reliableAcknowledge & (Q3Connection.MAX_RELIABLE_COMMANDS - 1)];
            int    index   = 0;

            byte key = ( byte )(connection.Challenge ^ serverId ^ messageAcknowledge);

            for (int i = CL_ENCODE_START; i < count; i++)
            {
                // modify the key with the last received now acknowledged server command
                if (pattern == null || index >= pattern.Length)
                {
                    index = 0;
                }

                if (pattern != null && (pattern [index] > 127 || pattern [index] == '%'))
                {
                    key ^= ( byte )('.' << (i & 1));
                }
                else
                {
                    key ^= ( byte )((pattern != null ? pattern [index] : 0) << (i & 1));
                }

                index++;

                // encode the data with this key
                buffer [i] = ( byte )(array [i + offset] ^ key);
            }

            baseStream.Write(buffer, 0, count);
        }
Пример #13
0
        //
        // Summary:
        //     Reads a number of decoded bytes into the specified byte array.
        //
        // Parameters:
        //   array:
        //     The array used to store decoded bytes.
        //
        //   offset:
        //     The byte offset in array at which to begin writing data read from the stream.
        //
        //   count:
        //     The number of bytes to decode.
        //
        // Returns:
        //     The number of bytes that were decoded into the byte array. If the end
        //     of the stream has been reached, zero or the number of bytes read is returned.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     array is null.
        //
        //   System.InvalidOperationException:
        //     The Q3Cypher.CypherMode value was Encode when the object
        //     was created.  - or - The underlying stream does not support reading.
        //
        //   System.ArgumentOutOfRangeException:
        //     offset or count is less than zero.  -or- array length minus the index starting
        //     point is less than count.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        public override int Read(byte [] array, int offset, int count)
        {
            #region Exception checks
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }

            if (baseStream == null)
            {
                throw new ObjectDisposedException("baseStream");
            }

            if (mode == CypherMode.Encode)
            {
                throw new InvalidOperationException("Stream cannot be read while its mode set to CypherMode.Encode");
            }

            if (!baseStream.CanRead)
            {
                throw new InvalidOperationException("Underlying stream cannot be read");
            }

            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException("offset");
            }

            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            if (array.Length < offset)
            {
                throw new ArgumentOutOfRangeException("offset");
            }

            if (array.Length < offset + count)
            {
                throw new ArgumentOutOfRangeException("count");
            }
            #endregion Exception checks

            if (this.readPosition == 0)
            {
                (( Q3DatagramStream )baseStream).BeginReadPacket();
            }

            int bytesRead = baseStream.Read(array, offset, count);

            int encStart = 0,
                encEnd   = 0;

            if (this.readPosition < CL_DECODE_START)
            {
                Array.Copy(array, offset,
                           this.readBuffer, this.readPosition,
                           bytesRead + this.readPosition > CL_DECODE_START ?
                           CL_DECODE_START - this.readPosition : bytesRead);

                if (this.readPosition + bytesRead >= CL_DECODE_START)
                {
                    Q3HuffmanStream huff = connection.Q3HuffDStream;
                    int             bloc = 0;
                    this.readReliableAcknowledge = huff.DecompressBufferToInt32(this.readBuffer, 0, ref bloc);
                    encStart              = CL_DECODE_START - this.readPosition + offset;
                    encEnd                = bytesRead + offset;
                    this.readPattern      = connection.OutgoingReliableCommands [this.readReliableAcknowledge & (Q3Connection.MAX_RELIABLE_COMMANDS - 1)];
                    this.readKey          = ( byte )(connection.Challenge ^ connection.IncomingSequence);
                    this.readPatternIndex = 0;
                }
            }
            else
            {
                encStart = offset;
                encEnd   = bytesRead;
            }

            for (int i = encStart; i < encEnd; i++)
            {
                // modify the key with the last received now acknowledged server command
                if (this.readPattern == null || this.readPatternIndex >= this.readPattern.Length)
                {
                    this.readPatternIndex = 0;
                }

                if (this.readPattern != null && (this.readPattern [this.readPatternIndex] > 127 || this.readPattern [this.readPatternIndex] == '%'))
                {
                    this.readKey ^= ( byte )('.' << (i & 1));
                }
                else
                {
                    this.readKey ^= ( byte )((this.readPattern != null ? this.readPattern [this.readPatternIndex] : 0) << (i & 1));
                }

                this.readPatternIndex++;
                array [i] = ( byte )(array [i] ^ this.readKey);
            }

            this.readPosition += bytesRead;

            //string dump = BufferInfo.DumpBuffer ( "Decrypted buffer", array, offset, bytesRead, true, true, true );
            //Console.Write ( dump );

            return(bytesRead);
        }