#include "stdafx.h"

static Mix::Plugin::INFORMATION g_Info = { Mix::Plugin::TEXTURE_LOADER, L"BMP", L"1.0.0.0" };

////////////////////////////////////////////////////////////////////////////////////////////////////
// プラグイン情報の取得
////////////////////////////////////////////////////////////////////////////////////////////////////

const Mix::Plugin::INFORMATION& __stdcall GetInformation( void )
{
	return g_Info;
}

int __stdcall Load( Mix::Plugin::IReader* pSrc, Mix::Plugin::Graphics::ITextureKit* pDst )
{
	BITMAPFILEHEADER fileHeader;
	BITMAPV5HEADER infoHeader;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pSrc->Read( &fileHeader, sizeof( BITMAPFILEHEADER ) ) != sizeof( BITMAPFILEHEADER ) )
	{
		return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}

	if( fileHeader.bfType != ( 'B' | ( 'M' << 8 ) ) )
	{
		return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 情報ヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	::ZeroMemory( &infoHeader, sizeof( BITMAPV5HEADER ) );

	// 最初のサイズだけ読み込む
	if( pSrc->Read( &infoHeader, sizeof( DWORD ) ) != sizeof( DWORD ) )
	{
		return Mix::Plugin::Graphics::TP_ILLEGAL_FORMAT;
	}

	if( infoHeader.bV5Size == sizeof( BITMAPCOREHEADER ) )
	{
		// BITMAPCOREHEADER

		BITMAPCOREHEADER temp;
		unsigned char* pHeader = reinterpret_cast<unsigned char*>( &temp ) + sizeof( DWORD );
		unsigned int headerSize = infoHeader.bV5Size - sizeof( DWORD );

		if( pSrc->Read( pHeader, headerSize ) != headerSize )
		{
			return Mix::Plugin::Graphics::TP_ILLEGAL_FORMAT;
		}

		infoHeader.bV5Width = temp.bcWidth;
		infoHeader.bV5Height = temp.bcHeight;
		infoHeader.bV5Planes = temp.bcPlanes;
		infoHeader.bV5BitCount = temp.bcBitCount;
	}
	else if( infoHeader.bV5Size > sizeof( BITMAPCOREHEADER ) )
	{
		// BITMAPINFOHEADER or BITMAPV4HEADER or BITMAPV5HEADER

		unsigned char* pHeader = reinterpret_cast<unsigned char*>( &infoHeader ) + sizeof( DWORD );
		unsigned int headerSize = infoHeader.bV5Size - sizeof( DWORD );

		if( fileHeader.bfOffBits < ( sizeof( BITMAPFILEHEADER ) + infoHeader.bV5Size ) )
		{
			return Mix::Plugin::Graphics::TP_ILLEGAL_FORMAT;
		}

		if( pSrc->Read( pHeader, headerSize ) != headerSize )
		{
			return Mix::Plugin::Graphics::TP_ILLEGAL_FORMAT;
		}
	}
	else
	{
		//不明な情報ヘッダ
		return Mix::Plugin::Graphics::TP_ILLEGAL_FORMAT;
	}

	if( infoHeader.bV5BitCount < 24 )
	{
		// 24、32 Bit のみサポート
		return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}

	if( infoHeader.bV5Size >= sizeof( BITMAPINFOHEADER ) )
	{
		// BITMAPV4HEADER or BITMAPV5HEADER

		if( infoHeader.bV5Compression == BI_BITFIELDS )
		{
			if( ( infoHeader.bV5AlphaMask != 0x00000000 ) &&
				( infoHeader.bV5AlphaMask != 0x000000FF ) )
			{
				// アルファチャンネルが 8Bit のみサポート
				return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
			}

			if( ( infoHeader.bV5BlueMask  != 0x0000FF00 ) ||
				( infoHeader.bV5GreenMask != 0x00FF0000 ) ||
				( infoHeader.bV5RedMask   != 0xFF000000 ) )
			{
				// 各チャンネルが 8Bit の RGB のみサポート
				return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
			}
		}
		else if( infoHeader.bV5Compression == BI_RGB )
		{
			// チャンネルの確認用
			infoHeader.bV5AlphaMask = 0x00000000;
			infoHeader.bV5BlueMask  = 0x0000FF00;
			infoHeader.bV5GreenMask = 0x00FF0000;
			infoHeader.bV5RedMask   = 0xFF000000;
		}
		else
		{
			//不明なコンプレッション
			return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
		}
	}
	else
	{
		// BITMAPCOREHEADER or BITMAPINFOHEADER

		// チャンネルの確認用
		infoHeader.bV5AlphaMask = 0x00000000;
		infoHeader.bV5BlueMask  = 0x0000FF00;
		infoHeader.bV5GreenMask = 0x00FF0000;
		infoHeader.bV5RedMask   = 0xFF000000;
	}

	// 残りをスキップ
	pSrc->Seek(	Mix::Plugin::IReader::CURRENT,
					fileHeader.bfOffBits - sizeof( BITMAPFILEHEADER ) - infoHeader.bV5Size );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 画像データ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	unsigned int size = infoHeader.bV5Width * infoHeader.bV5Height;
	unsigned int memSize = size * 4;
	unsigned int memPitch = infoHeader.bV5Width * 4;
	void* pMem;

	unsigned int srcPitch;
	unsigned int readResult;

	const void* pTemp;
	const unsigned char* pTexSrc;
	const unsigned char* pTexSrcEnd;

	unsigned int* pTexDstBegin;
	unsigned int* pTexDst;

	unsigned int r;
	unsigned int g;
	unsigned int b;
	unsigned int a;

	pDst->SetType( Mix::Plugin::Graphics::ITextureKit::PLANE );
	pDst->SetFormat( Mix::Plugin::Graphics::ITextureKit::FORMAT::R8G8B8A8 );
	pDst->SetSize( infoHeader.bV5Width, infoHeader.bV5Height );
	pDst->SetDepth( 1 );
	pDst->SetMipLevels( 1 );
	pDst->SetFlags( 0 );

	pMem = pDst->AddSubResourceData( memSize, memPitch );
	if( pMem == NULL )
	{
		return Mix::Plugin::Graphics::TP_OUT_OF_MEMORY;
	}

	if( infoHeader.bV5BitCount == 24 )
	{
		srcPitch = infoHeader.bV5Width * 3;
		pTexDstBegin = reinterpret_cast<unsigned int*>( pMem ) + size - infoHeader.bV5Width;

		for( int y = 0; y < infoHeader.bV5Height; y++ )
		{
			pTemp = pSrc->Read( srcPitch, readResult );
			if( srcPitch != readResult )
			{
				return Mix::Plugin::Graphics::TP_ILLEGAL_FORMAT;
			}

			pTexSrc = reinterpret_cast<const unsigned char*>( pTemp );
			pTexSrcEnd = pTexSrc + srcPitch;
			pTexDst = pTexDstBegin;

			a = 0xFF;

			while( pTexSrc != pTexSrcEnd )
			{
				b = *pTexSrc++;
				g = *pTexSrc++;
				r = *pTexSrc++;

				*pTexDst++ = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) + b;
			}

			pTexDstBegin -= infoHeader.bV5Width;
		}
	}
	else if( infoHeader.bV5BitCount == 32 )
	{
		srcPitch = memPitch;
		pTexDstBegin = reinterpret_cast<unsigned int*>( pMem ) + size - infoHeader.bV5Width;

		for( int y = 0; y < infoHeader.bV5Height; y++ )
		{
			pTemp = pSrc->Read( srcPitch, readResult );
			if( srcPitch != readResult )
			{
				return Mix::Plugin::Graphics::TP_ILLEGAL_FORMAT;
			}

			pTexSrc = reinterpret_cast<const unsigned char*>( pTemp );
			pTexSrcEnd = pTexSrc + srcPitch;
			pTexDst = pTexDstBegin;

			if( infoHeader.bV5AlphaMask == 0 )
			{
				a = 0xFF;

				while( pTexSrc != pTexSrcEnd )
				{
					pTexSrc++;
					b = *pTexSrc++;
					g = *pTexSrc++;
					r = *pTexSrc++;

					*pTexDst++ = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) + b;
				}
			}
			else
			{
				while( pTexSrc != pTexSrcEnd )
				{
					a = *pTexSrc++;
					b = *pTexSrc++;
					g = *pTexSrc++;
					r = *pTexSrc++;

					*pTexDst++ = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) + b;
				}
			}

			pTexDstBegin -= infoHeader.bV5Width;
		}
	}

	return Mix::Plugin::Graphics::TP_OK;
}
