static uint32 rol(uint32 var, uint32 amount)
{
	amount &= 31;
	return ((var << amount) | (var >> (32 - amount)));
}

static uint32 ror(uint32 var, uint32 amount)
{
	amount &= 31;
	return ((var >> amount) | (var << (32 - amount)));
}

void encrypt(void * target, const void * source, uint32 packetlen, const uint32 * key)
{
	const uint32 * decPacket = (const uint32 *)source;
	uint32 * encPacket = (uint32 *)target;

	uint32 sizeInBlocks = packetlen / 8;
	uint32 bytesRemaining = packetlen;

	uint32 lastUpperhalf = 0;
	uint32 lastLowerhalf = 0;

	for(uint32 i = 0; i < sizeInBlocks; ++i)
	{
		uint32 lowerhalf = decPacket[0] ^ lastLowerhalf;
		uint32 upperhalf = decPacket[1] ^ lastUpperhalf;

		for(uint32 j = 0; j < 20; ++j)
		{
			lowerhalf += key[j++];
			lowerhalf = rol(lowerhalf, upperhalf & 0xFF);

			upperhalf += key[j];
			upperhalf = rol(upperhalf, lowerhalf & 0xFF);
		}

		encPacket[0] = lowerhalf;
		encPacket[1] = upperhalf;

		encPacket += 2;
		decPacket += 2;

		lastLowerhalf = lowerhalf;
		lastUpperhalf = upperhalf;

		bytesRemaining -= 8;
	}

	if(bytesRemaining >= 4)
	{
		uint32 block = decPacket[0] ^ lastLowerhalf;

		for(uint32 i = 0; i < 20; ++i)
		{
			block += key[i++];
			block = rol(block, lastUpperhalf & 0xFF);

			lastUpperhalf += key[i];
		}

		encPacket[0] = block;
		++encPacket;
		++decPacket;

		lastLowerhalf = block;
		bytesRemaining -= 4;
	}

	uint8 * encPacketBytes = (uint8 *)encPacket;
	const uint8 * decPacketBytes = (const uint8 *)decPacket;
	for(uint32 i = 0; i < bytesRemaining; ++i)
	{
		uint8 currentByte = decPacketBytes[0] ^ lastLowerhalf;

		for(uint32 j = 0; j < 20; ++j)
		{
			currentByte += key[j++] & 0xFF;
			currentByte += lastUpperhalf & 0xFF;

			lastUpperhalf += key[j];
		}

		encPacketBytes[0] = currentByte;

		++encPacketBytes;
		++decPacketBytes;

		lastLowerhalf = currentByte;
	}
}

void decrypt(void * target, const void * source, uint32 packetlen, const uint32 * key)
{
	uint32 * decPacket = (uint32 *)target;
	const uint32 * encPacket = (const uint32 *)source;

	uint32 sizeInBlocks = packetlen / 8;
	uint32 bytesRemaining = packetlen;

	uint32 lastUpperhalf = 0;
	uint32 lastLowerhalf = 0;

	for(uint32 i = 0; i < sizeInBlocks; ++i)
	{
		uint32 lowerhalf = encPacket[0];
		uint32 upperhalf = encPacket[1];

		for(int32 j = 19; j >= 0; --j)
		{
			upperhalf = ror(upperhalf, lowerhalf & 0xFF);
			upperhalf -= key[j--];

			lowerhalf = ror(lowerhalf, upperhalf & 0xFF);
			lowerhalf -= key[j];
		}

		lowerhalf ^= lastLowerhalf;
		upperhalf ^= lastUpperhalf;

		decPacket[0] = lowerhalf;
		decPacket[1] = upperhalf;
		decPacket += 2;

		lastLowerhalf = encPacket[0];
		lastUpperhalf = encPacket[1];
		encPacket += 2;

		bytesRemaining -= 8;
	}

	if(bytesRemaining >= 4)
	{
		uint32 roramount = lastUpperhalf;
		uint32 block = encPacket[0];

		for(uint32 i = 1; i < 20; i += 2)
			roramount += key[i];

		lastUpperhalf = roramount;

		for(int32 i = 19; i > 0; i -= 2)
		{
			roramount -= key[i];
			block = ror(block, roramount & 0xFF);
			block -= key[i - 1];
		}

		block ^= lastLowerhalf;

		decPacket[0] = block;
		++decPacket;

		lastLowerhalf = encPacket[0];
		++encPacket;

		bytesRemaining -= 4;
	}

	const uint8 * encPacketBytes = (const uint8 *)encPacket;
	uint8 * decPacketBytes = (uint8 *)decPacket;
	for(uint32 i = 0; i < bytesRemaining; ++i)
	{
		uint8 currentByte = encPacketBytes[0];
		uint32 accum = lastUpperhalf;

		for(uint32 j = 1; j < 20; j += 2)
			accum += key[j];

		lastUpperhalf = accum;

		for(int32 j = 19; j > 0; j -= 2)
		{
			accum -= key[j];
			currentByte -= ((key[j - 1] & 0xFF) + (accum & 0xFF));
		}

		currentByte ^= (lastLowerhalf & 0xFF);

		decPacketBytes[0] = currentByte;
		++decPacketBytes;

		lastLowerhalf = encPacketBytes[0];
		++encPacketBytes;
	}
}
