/// <summary>
        ///     Decrypts data from server-side packets (sent from servers to clients.)
        /// </summary>
        /// <param name="source">The encrypted packet data.</param>
        /// <param name="key">Key to decrypt with.</param>
        /// <returns>The decrypted packet data.</returns>
        /// <exception cref="ArgumentOutOfRangeException">
        ///     Thrown if the key is invalid or the packet data is too short.
        /// </exception>
        public static byte[] Decrypt(byte[] source, byte key)
        {
            if (key >= 0x10)
            {
                throw new ArgumentOutOfRangeException(nameof(key), $"Key too large ({key} >= 0x10)");
            }

            if (source.Length < 8)
            {
                throw new ArgumentOutOfRangeException(nameof(source), $"Packet too small ({source.Length} < 8)");
            }

            var oracleByte = CryptoOracle.CryptTable2[(key << 8) + source[0]];
            var buffer     = (byte[])source.Clone();

            buffer[7] ^= oracleByte;

            for (var i = 10; i < source.Length; i++)
            {
                buffer[i] ^= buffer[i - 4];
            }

            var compressedData = new byte[source.Length - 8];

            Array.Copy(buffer, 8, compressedData, 0, source.Length - 8);
            return(MiniLzo.Decompress(compressedData));
        }
        /// <summary>
        ///     Encrypts data for server-side packets (sent from servers to clients.)
        /// </summary>
        /// <param name="source">The decrypted packet data.</param>
        /// <param name="key">Key to encrypt with.</param>
        /// <param name="salt">Random salt value to encrypt with.</param>
        /// <returns>The encrypted packet data.</returns>
        /// <exception cref="ArgumentOutOfRangeException">
        ///     Thrown if an invalid key is specified.
        /// </exception>
        public static byte[] Encrypt(byte[] source, byte key, byte salt)
        {
            if (key >= 0x10)
            {
                throw new ArgumentOutOfRangeException(nameof(key), $"Key too large ({key} >= 0x10)");
            }

            var oracleIndex    = (key << 8) + salt;
            var compressedData = MiniLzo.Compress(source);
            var buffer         = new byte[compressedData.Length + 8];
            var pLen           = buffer.Length - 3;

            var u = source.Length;
            var x = (u + u / 255) & 0xff;
            var v = (u - x) / 255;
            var y = (v + v / 255) & 0xff;
            var w = (v - y) / 255;
            var z = (w + w / 255) & 0xff;

            buffer[0] = salt;
            buffer[1] = (byte)((pLen >> 0) & 0xFF);
            buffer[2] = (byte)((pLen >> 8) & 0xFF);
            buffer[3] = (byte)(CryptoOracle.CryptTable1[oracleIndex] ^ CryptoOracle.CryptTable2[oracleIndex]);
            buffer[5] = (byte)z;
            buffer[6] = (byte)y;
            buffer[7] = (byte)x;

            Array.Copy(compressedData, 0, buffer, 8, compressedData.Length);

            for (var i = buffer.Length - 1; i >= 10; i--)
            {
                buffer[i] ^= buffer[i - 4];
            }

            buffer[7] ^= CryptoOracle.CryptTable2[oracleIndex];

            return(buffer);
        }