private void parseSnapshot(Q3HuffmanReader decoder)
        {
            if (client.clientConfig == null)
            {
                client.clientConfig = new Dictionary <string, string>();

                if (clc.configs.ContainsKey(Q3Const.Q3_DEMO_CFG_FIELD_GAME))
                {
                    var gameConfig = Q3Utils.split_config(clc.configs[Q3Const.Q3_DEMO_CFG_FIELD_GAME]);
                    client.isCheatsOn = Ext.GetOrZero(gameConfig, "sv_cheats") > 0;
                }
                if (clc.configs.ContainsKey(Q3Const.Q3_DEMO_CFG_FIELD_CLIENT))
                {
                    client.clientConfig    = Q3Utils.split_config(clc.configs[Q3Const.Q3_DEMO_CFG_FIELD_CLIENT]);
                    client.dfvers          = Ext.GetOrZero(client.clientConfig, "defrag_vers");
                    client.mapname         = Ext.GetOrNull(client.clientConfig, "mapname");
                    client.mapNameChecksum = getMapNameChecksum(client.mapname);
                    client.isOnline        = Ext.GetOrZero(client.clientConfig, "defrag_gametype") > 4;
                }
            }

            CLSnapshot newSnap = new CLSnapshot();
            CLSnapshot old     = null;

            newSnap.serverCommandNum = clc.serverCommandSequence;
            newSnap.serverTime       = decoder.readLong();
            newSnap.messageNum       = clc.serverMessageSequence;

            int deltaNum = decoder.readByte();

            if (deltaNum == 0)
            {
                newSnap.deltaNum = -1;
            }
            else
            {
                newSnap.deltaNum = newSnap.messageNum - deltaNum;
            }
            newSnap.snapFlags = decoder.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 = Ext2 <int, CLSnapshot> .GetOrCreate(client.snapshots, newSnap.deltaNum& Q3Const.PACKET_MASK);

                if (old == null || !old.valid)
                {
                    // should never happen
                    Q3Utils.PrintDebug(clc.errors, new ErrorDeltaFromInvalidFrame());
                }
                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.
                    Q3Utils.PrintDebug(clc.errors, new ErrorDeltaFrameTooOld());
                }
                else if ((client.parseEntitiesNum - old.parseEntitiesNum) > (Q3Const.MAX_PARSE_ENTITIES - 128))
                {
                    Q3Utils.PrintDebug(clc.errors, new ErrorDeltaParseEntitiesNumTooOld());
                }
                else
                {
                    newSnap.valid = true;  // valid delta parse
                }
            }

            int len = decoder.readByte();

            if (len > newSnap.areamask.Length)
            {
                Q3Utils.PrintDebug(clc.errors, new ErrorParseSnapshotInvalidsize());
                return;
            }
            decoder.readData(newSnap.areamask, len);
            if (old != null)
            {
                newSnap.ps.copy(old.ps);
            }
            decoder.readDeltaPlayerState(newSnap.ps);
            parsePacketEntities(decoder, 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 = client.snap.messageNum + 1;

            if (newSnap.messageNum - oldMessageNum >= Q3Const.PACKET_BACKUP)
            {
                oldMessageNum = newSnap.messageNum - (Q3Const.PACKET_BACKUP - 1);
            }
            for (; oldMessageNum < newSnap.messageNum; oldMessageNum++)
            {
                CLSnapshot s;
                if (client.snapshots.TryGetValue(oldMessageNum & Q3Const.PACKET_MASK, out s))
                {
                    if (s != null)
                    {
                        s.valid = false;
                    }
                }
            }

            // copy to the current good spot
            client.snap = newSnap;

            // skip ping calculations
            client.snap.ping = 0;

            // save the frame off in the backup array for later delta comparisons
            client.snapshots[client.snap.messageNum & Q3Const.PACKET_MASK] = client.snap;

            client.newSnapshots = true;

            updateClientEvents(newSnap);
        }