// Base64.cpp: CBase64 クラスのインプリメンテーション
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Base64.h"
#include "string.h"

static const unsigned char pr2six[256] =
{
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};

static const char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

//////////////////////////////////////////////////////////////////////
// BinaryData <-> Hex
//////////////////////////////////////////////////////////////////////
/*!
	エンコード(Bin->Hex)
*/
void CBase64::EncodeHEXbin(CBinaryData &src, CString &dest)
{
	//	エンコード後の長さ
	int	srcLen = src.GetSize();
	int encodedLen = srcLen * 2;

	//	バッファ用意
	unsigned char *srcPtr = (unsigned char *)src.GetPtr();
	TCHAR *destPtr = dest.GetBuffer(encodedLen);

	//	変換
	for(int i=0;i<srcLen;i++)
	{
		destPtr[0] = GetHexChar(*srcPtr >> 4);
		destPtr[1] = GetHexChar(*srcPtr & 0x0f);
		destPtr += 2;
		srcPtr++;
	}
	dest.ReleaseBufferSetLength(encodedLen);
}

/*!
	デコード(Hex->Bin)

	\return 0:正常終了、-1:エラー
*/
int CBase64::DecodeHEXbin(CString src, CBinaryData &dest)
{
	//	長さチェック
	int	srcLen = src.GetLength();
	if((srcLen % 2) != 0)	//	奇数長さはエラー
		return(-1);

	//	デコード後の長さ
	int decodedLen = srcLen / 2;
	dest.ReSize(decodedLen);

	//	小文字に
	src.MakeLower();

	//	バッファ用意
	TCHAR *srcPtr = src.GetBuffer();
	unsigned char *destPtr = (unsigned char *)dest.GetPtr();

	//	デコード
	for(int i=0;i<decodedLen; i++)
	{
		//	変換してみる
		int	hVal = GetHexVal(srcPtr[0]);
		int	lVal = GetHexVal(srcPtr[1]);

		//	変換エラー
		if(hVal < 0 || lVal < 0)
			return(-1);

		*destPtr = (hVal<<4) + lVal;
		destPtr++;
		srcPtr += 2;
	}

	return(0);
}

/*!
	16進数文字に変換
*/
_inline TCHAR CBase64::GetHexChar(int val)
{
	//	範囲外？
	if(val <= 0)
		return(_T('0'));
	if(val >= 15)
		return(_T('f'));

	//	変換
	if(val <= 9)
		return(_T('0') + val);
	return(_T('a') + val - 10);
}

/*!
	16進数文字を数字に(1文字ずつ変換)
	小文字のみ対応
*/
_inline int CBase64::GetHexVal(TCHAR hex)
{
	if(hex >= _T('0') && hex <= _T('9'))
		return(hex - _T('0'));
	if(hex >= _T('a') && hex <= _T('f'))
		return(hex - _T('a') + 10);

	//	範囲外
	return(-1);
}



//////////////////////////////////////////////////////////////////////
// BinaryData <-> B64
//////////////////////////////////////////////////////////////////////
/*!
	エンコード
*/
void CBase64::Encode64bin(CBinaryData &src, CString &dest)
{
	char	*buf = dest.GetBuffer(GetEncodedLength(src.GetSize()));

	int len = _Encode(buf, (unsigned char *)src.GetPtr(), src.GetSize());
	dest.ReleaseBufferSetLength(len);
}

/*!
	デコード
*/
void CBase64::Decode64bin(CString src, CBinaryData &dest)
{
	//	サイズ取得
	int		len = GetDecodedLength(src.GetBuffer(0));
	dest.ReSize(len);

	len = _Decode((unsigned char *)dest.GetPtr(), src.GetBuffer(0));
	dest.ReSize(len);
}


//////////////////////////////////////////////////////////////////////
// String <-> B64
//////////////////////////////////////////////////////////////////////
/*!
	エンコード（Text->B64）
*/
CString CBase64::Encode64(CString string)
{
	CString	ret;
	int		len = GetEncodedLength(string.GetLength());
	char	*buf = ret.GetBuffer(len);

	len = _Encode(buf,(unsigned char*)string.GetBuffer(0),string.GetLength());
	buf[len] = 0;
	ret.ReleaseBuffer();
	
	return(ret);
}

/*!
	エンコード（B64->Text）
*/
CString CBase64::Decode64(CString b64str)
{
	CString	ret;
	int		len =GetDecodedLength(b64str.GetBuffer(0));
	unsigned char	*buf = (unsigned char *)ret.GetBuffer(len);

	len = _Decode(buf,b64str.GetBuffer(0));
	buf[len] = 0;
	ret.ReleaseBuffer();
	
	return(ret);
}


//////////////////////////////////////////////////////////////////////
// エンコード
//////////////////////////////////////////////////////////////////////

//	エンコード後の長さを取得
int CBase64::GetEncodedLength(int len)
{
    return ((len + 2) / 3 * 4) + 1;
}


//	エンコード
int CBase64::_Encode(char *encoded,const unsigned char *string, int len)
{
	int i;
	char *p;

	p = encoded;
	for (i = 0; i < len - 2; i += 3)
	{
		*p++ = basis_64[(string[i] >> 2) & 0x3F];
		*p++ = basis_64[((string[i] & 0x3) << 4) |
						((int) (string[i + 1] & 0xF0) >> 4)];
		*p++ = basis_64[((string[i + 1] & 0xF) << 2) |
						((int) (string[i + 2] & 0xC0) >> 6)];
		*p++ = basis_64[string[i + 2] & 0x3F];
	}

	if (i < len)
	{
		*p++ = basis_64[(string[i] >> 2) & 0x3F];
		if (i == (len - 1))
		{
			*p++ = basis_64[((string[i] & 0x3) << 4)];
			*p++ = '=';
		}
		else
		{
			*p++ = basis_64[((string[i] & 0x3) << 4) |
							((int) (string[i + 1] & 0xF0) >> 4)];
			*p++ = basis_64[((string[i + 1] & 0xF) << 2)];
		}
		*p++ = '=';
	}

	*p++ = '\0';
	return (int)(p - encoded);
}

//////////////////////////////////////////////////////////////////////
// デコード
//////////////////////////////////////////////////////////////////////


//	デコード後の長さを取得
int CBase64::GetDecodedLength(const char *bufcoded)
{
    int nbytesdecoded;
    register const unsigned char *bufin;
    register int nprbytes;

    bufin = (const unsigned char *) bufcoded;
    while (pr2six[*(bufin++)] <= 63);

    nprbytes = (int)(bufin - (const unsigned char *) bufcoded) - 1;
    nbytesdecoded = ((nprbytes + 3) / 4) * 3;

    return nbytesdecoded + 1;
}

//	デコード
int CBase64::_Decode(unsigned char *bufplain,const char *bufcoded)
{
	int nbytesdecoded;
	const unsigned char *bufin;
	unsigned char *bufout;
	int nprbytes;

	bufin = (const unsigned char *) bufcoded;
	while (pr2six[*(bufin++)] <= 63);
	nprbytes = (int)(bufin - (const unsigned char *) bufcoded) - 1;
	nbytesdecoded = ((nprbytes + 3) / 4) * 3;

	bufout = (unsigned char *) bufplain;
	bufin = (const unsigned char *) bufcoded;

	while (nprbytes > 4)
	{
		*(bufout++) =
			(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
		*(bufout++) =
			(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
		*(bufout++) =
			(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
		bufin += 4;
		nprbytes -= 4;
	}

	/* Note: (nprbytes == 1) would be an error, so just ingore that case */
	if (nprbytes > 1)
	{
		*(bufout++) =
			(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
	}
	if (nprbytes > 2)
	{
		*(bufout++) =
			(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
	}
	if (nprbytes > 3)
	{
		*(bufout++) =
			(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
	}

	nbytesdecoded -= (4 - nprbytes) & 3;
	return nbytesdecoded;
}
