context: #region Copyright & License Information
  /*
   * Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation, either version 3 of
   * the License, or (at your option) any later version. For more
   * information, see COPYING.
   */
  #endregion
  
  using System;
  using System.Linq;
  
  namespace OpenRA.Mods.Cnc.FileFormats
  {
  	/* TODO: Convert this direct C port into readable code. */
  
  	class BlowfishKeyProvider
  	{
  		const string PublicKeyString = "AihRvNoIbTn85FZRYNZRcT+i6KpU+maCsEqr3Q5q+LDB5tH7Tz2qQ38V";
  
  		class PublicKey
  		{
  			public readonly uint[] KeyOne = new uint[64];
  			public readonly uint[] KeyTwo = new uint[64];
  			public uint Len;
  		}
  
  		readonly PublicKey pubkey = new PublicKey();
  
  		readonly uint[] globOne = new uint[64];
  		uint globOneBitLen, globOneLenXTwo;
  		readonly uint[] globTwo = new uint[130];
  		readonly uint[] globOneHigh = new uint[4];
  		readonly uint[] globOneHighInv = new uint[4];
  		uint globOneHighBitLen;
  		uint globOneHighInvLow, globOneHighInvHigh;
  
  		static void InitBigNum(uint[] n, uint val, uint len)
  		{
  			for (var i = 0; i < len; i++) n[i] = 0;
  			n[0] = val;
  		}
  
  		static void MoveKeyToBig(uint[] n, byte[] key, uint klen, uint blen)
  		{
  			byte sign;
  
  			if ((key[0] & 0x80) != 0) sign = 0xff;
  			else sign = 0;
  
  			unsafe
  			{
  				fixed (uint* tempPn = &n[0])
  				{
  					var pn = (byte*)tempPn;
  					var i = blen * 4;
  					for (; i > klen; i--) pn[i - 1] = sign;
  					for (; i > 0; i--) pn[i - 1] = key[klen - i];
  				}
  			}
  		}
  
  		static void KeyToBigNum(uint[] n, byte[] key, uint len)
  		{
  			uint keylen;
  			int i;
  
  			var j = 0;
  
  			if (key[j] != 2) return;
  			j++;
  
  			if ((key[j] & 0x80) != 0)
  			{
  				keylen = 0;
  				for (i = 0; i < (key[j] & 0x7f); i++) keylen = (keylen << 8) | key[j + i + 1];
  				j += (key[j] & 0x7f) + 1;
  			}
  			else
  			{
  				keylen = key[j];
  				j++;
  			}
  
  			if (keylen <= len * 4)
  				MoveKeyToBig(n, key.Skip(j).ToArray(), keylen, len);
  		}
  
  		static uint LenBigNum(uint[] n, uint len)
  		{
  			uint i;
  			i = len - 1;
  			while ((i >= 0) && (n[i] == 0)) i--;
  			return i + 1;
  		}
  
  		static uint BitLenBigNum(uint[] n, uint len)
  		{
  			uint ddlen, bitlen, mask;
  			ddlen = LenBigNum(n, len);
  			if (ddlen == 0) return 0;
  			bitlen = ddlen * 32;
  			mask = 0x80000000;
  			while ((mask & n[ddlen - 1]) == 0)
  			{
  				mask >>= 1;
  				bitlen--;
  			}
  
  			return bitlen;
  		}
  
  		void InitPublicKey()
  		{
  			InitBigNum(pubkey.KeyTwo, 0x10001, 64);
  
  			KeyToBigNum(pubkey.KeyOne, Convert.FromBase64String(PublicKeyString), 64);
  			pubkey.Len = BitLenBigNum(pubkey.KeyOne, 64) - 1;
  		}
  
  		static int CompareBigNum(uint[] n1, uint[] n2, uint len)
  		{
  			while (len > 0)
  			{
  				--len;
  				if (n1[len] < n2[len]) return -1;
  				if (n1[len] > n2[len]) return 1;
  			}
  
  			return 0;
  		}
  
  		static void MoveBigNum(uint[] dest, uint[] src, uint len)
  		{
  			Array.Copy(src, dest, len);
  		}
  
  		static void ShrBigNum(uint[] n, int bits, int len)
  		{
  			int i; var i2 = bits / 32;
  
  			if (i2 > 0)
  			{
  				for (i = 0; i < len - i2; i++) n[i] = n[i + i2];
  				for (; i < len; i++) n[i] = 0;
  				bits = bits % 32;
  			}
  
  			if (bits == 0) return;
  			for (i = 0; i < len - 1; i++) n[i] = (n[i] >> bits) | (n[i + 1] << (32 - bits));
  			n[i] = n[i] >> bits;
  		}
  
  		static void ShlBigNum(uint[] n, int bits, int len)
  		{
  			int i, i2;
  
  			i2 = bits / 32;
  			if (i2 > 0)
  			{
  				for (i = len - 1; i > i2; i--) n[i] = n[i - i2];
  				for (; i > 0; i--) n[i] = 0;
  				bits = bits % 32;
  			}
  
  			if (bits == 0) return;
  			for (i = len - 1; i > 0; i--) n[i] = (n[i] << bits) | (n[i - 1] >> (32 - bits));
  			n[0] <<= bits;
  		}
  
  		static uint SubBigNum(uint[] dest, uint[] src1, uint[] src2, uint carry, int len)
  		{
  			uint i1, i2;
  
  			len += len;
  			unsafe
  			{
  				fixed (uint* tempPs1 = &src1[0])
  				fixed (uint* tempPs2 = &src2[0])
  				fixed (uint* tempPd = &dest[0])
  				{
  					var ps1 = (ushort*)tempPs1;
  					var ps2 = (ushort*)tempPs2;
  					var pd = (ushort*)tempPd;
  
  					while (--len != -1)
  					{
  						i1 = *ps1++;
  						i2 = *ps2++;
  						*pd++ = (ushort)(i1 - i2 - carry);
  						if (((i1 - i2 - carry) & 0x10000) != 0) carry = 1; else carry = 0;
  					}
  				}
  			}
  
  			return carry;
  		}
  
  		static unsafe uint SubBigNum(uint* dest, uint* src1, uint* src2, uint carry, int len)
  		{
  			uint i1, i2;
  
  			len += len;
  
  			var ps1 = (ushort*)src1;
  			var ps2 = (ushort*)src2;
  			var pd = (ushort*)dest;
  
  			while (--len != -1)
  			{
  				i1 = *ps1++;
  				i2 = *ps2++;
  				*pd++ = (ushort)(i1 - i2 - carry);
  				if (((i1 - i2 - carry) & 0x10000) != 0) carry = 1; else carry = 0;
  			}
  
  			return carry;
  		}
  
  		static void InvertBigNum(uint[] n1, uint[] n2, uint len)
  		{
  			var nTmp = new uint[64];
  			uint nTwoByteLen, bit;
  			int nTwoBitLen;
  
  			var j = 0;
  
  			InitBigNum(nTmp, 0, len);
  			InitBigNum(n1, 0, len);
  			nTwoBitLen = (int)BitLenBigNum(n2, len);
  			bit = 1U << (nTwoBitLen % 32);
  			j = ((nTwoBitLen + 32) / 32) - 1;
  			nTwoByteLen = (uint)((nTwoBitLen - 1) / 32) * 4;
  			nTmp[nTwoByteLen / 4] |= 1U << ((nTwoBitLen - 1) & 0x1f);
  
  			while (nTwoBitLen > 0)
  			{
  				nTwoBitLen--;
  				ShlBigNum(nTmp, 1, (int)len);
  				if (CompareBigNum(nTmp, n2, len) != -1)
  				{
  					SubBigNum(nTmp, nTmp, n2, 0, (int)len);
  					n1[j] |= bit;
  				}
  
  				bit >>= 1;
  				if (bit == 0)
  				{
  					j--;
  					bit = 0x80000000;
  				}
  			}
  
  			InitBigNum(nTmp, 0, len);
  		}
  
  		static void IncrementBigNum(uint[] n, uint len)
  		{
  			var i = 0;
  			while ((++n[i] == 0) && (--len > 0)) i++;
  		}
  
  		void InitTwoDw(uint[] n, uint len)
  		{
  			MoveBigNum(globOne, n, len);
  			globOneBitLen = BitLenBigNum(globOne, len);
  			globOneLenXTwo = (globOneBitLen + 15) / 16;
  			MoveBigNum(globOneHigh, globOne.Skip((int)LenBigNum(globOne, len) - 2).ToArray(), 2);
  			globOneHighBitLen = BitLenBigNum(globOneHigh, 2) - 32;
  			ShrBigNum(globOneHigh, (int)globOneHighBitLen, 2);
  			InvertBigNum(globOneHighInv, globOneHigh, 2);
  			ShrBigNum(globOneHighInv, 1, 2);
  			globOneHighBitLen = (globOneHighBitLen + 15) % 16 + 1;
  			IncrementBigNum(globOneHighInv, 2);
  			if (BitLenBigNum(globOneHighInv, 2) > 32)
  			{
  				ShrBigNum(globOneHighInv, 1, 2);
  				globOneHighBitLen--;
  			}
  
  			globOneHighInvLow = (ushort)globOneHighInv[0];
  			globOneHighInvHigh = (ushort)(globOneHighInv[0] >> 16);
  		}
  
  		static unsafe void MulBignumWord(ushort* pn1, uint[] n2, uint mul, uint len)
  		{
  			uint i, tmp;
  			unsafe
  			{
  				fixed (uint* tempPn2 = &n2[0])
  				{
  					var pn2 = (ushort*)tempPn2;
  
  					tmp = 0;
  					for (i = 0; i < len; i++)
  					{
  						tmp = mul * (*pn2) + (*pn1) + tmp;
  						*pn1 = (ushort)tmp;
  						pn1++;
  						pn2++;
  						tmp >>= 16;
  					}
  
  					*pn1 += (ushort)tmp;
  				}
  			}
  		}
  
  		static void MulBigNum(uint[] dest, uint[] src1, uint[] src2, uint len)
  		{
  			uint i;
  
  			unsafe
  			{
  				fixed (uint* tempSrc2 = &src2[0])
  
gt: 				fixed (uint* tempPdest = &dest[0])