#include "pch.hpp"
//===========================================================================//
// File:	 Loader_BMP.cpp													 //
// Project:  GameOS					                                         //
// Contents: Used to load textures from .BMP files							 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 06/02/99  AG     Created													 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "Texture Manager.hpp"
#include "DXRasterizer.hpp"
#include "DirectX.hpp"
#include "MemoryManager.hpp"

void DecodeBMPDimensions( const char* FileName, BYTE *Data, DWORD DataSize, DWORD* pTextureWidth, DWORD* pTextureHeight )
{
	LOG_BLOCK("Gos3D::Texture.DecodeBMPDimensions");
	Data += sizeof(BITMAPFILEHEADER);
	*pTextureWidth = ((BITMAPINFOHEADER *)Data)->biWidth;
	*pTextureHeight = ((BITMAPINFOHEADER *)Data)->biHeight;
}

//
// Read in a BMP file and return a 32bit memory surface containing it.
//
//
// pBitmap contains the BMP file
// Size is the length of the BMP file
//
//
void* DecodeBMP( const char* FileName, BYTE* Data, DWORD DataSize, DWORD* TextureWidth, DWORD* TextureHeight, bool TextureLoad, void *pDestSurf )
{
	LOG_BLOCK("Gos3D::Texture.DecodeBMP");
	IDirectDrawSurface7* DWSurface7=0;
	DDSURFACEDESC2 ddsd;
	BITMAPFILEHEADER bfh;
	BITMAPINFOHEADER bih;
	DWORD* Memory=0;
	DWORD Palette[256];
	DWORD Pixel=0;
	DWORD Pitch=0;
	DWORD* Dest=0;
	DWORD Format=0;
	memset( &ddsd, 0, sizeof(ddsd) );
//
// Only 32 or 24 bit, not indexed textures supported
//
	memcpy( &bfh, Data, sizeof(bfh) );

	if( bfh.bfType!=0x4d42 )
		STOP(( "\"%s\" is an invalid .BMP file",FileName ));

	memcpy( &bih, Data+sizeof(bfh), sizeof(bih) );

	if( bih.biSize!=sizeof(BITMAPINFOHEADER) ||
		(bih.biBitCount!=8 && bih.biBitCount!=16 && bih.biBitCount!=24 && bih.biBitCount!=32) ||
		(bih.biCompression!=BI_RGB && bih.biCompression!=BI_RLE8 && bih.biCompression!=BI_BITFIELDS) )
		STOP(( "Only 8,16,24 or 32 bit RGB,RLE8, or BITFIELD BMPs supported '%s'",FileName ));

	if( bih.biCompression==BI_BITFIELDS && bih.biBitCount!=16)
		STOP(( "Only 16 bit BITFIELD supported '%s'",FileName ));

	if( bih.biCompression==BI_BITFIELDS )
	{
		DWORD * Mask = (DWORD *)(Data+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER));

		if (Mask[0] == 0xf800 && Mask[1] == 0x07e0 && Mask[2] == 0x001f)
			Format = 565;
		else if (Mask[0] == 0x7c00 && Mask[1] == 0x03e0 && Mask[2] == 0x001f)
			Format = 1555;
		else if (Mask[0] == 0x0f00 && Mask[1] == 0x00f0 && Mask[2] == 0x000f)
			Format = 4444;
		else
			STOP(( "Only 565, 1555, or 4444 formats are supported for 16 bit BMPs",FileName ));
	}

//
// Get image information
//
	bool Compressed=bih.biCompression==BI_RLE8;
	DWORD Width=bih.biWidth;
	DWORD Height=bih.biHeight;
//
// Support for rectangular textures (Always show in debug console)
//
	if( TextureLoad )
	{
		if( ((Width|Height)&(15|~2047L)) || (Width&(Width-1)) || (Height&(Height-1)) )
			STOP(( "Only power of 2 textures up to 1024x1024 supported (%d,%d) '%s'",Width,Height,FileName ));

		if( pDestSurf )
		{
			DWSurface7 = (IDirectDrawSurface7*)pDestSurf;
		}
		else
		{
//
// Setup format of the DWORD version of the system memory image
//
			ddsd.dwSize								= sizeof(ddsd);
			ddsd.dwFlags							= DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
			ddsd.ddsCaps.dwCaps						= DDSCAPS_SYSTEMMEMORY | DDSCAPS_TEXTURE;
			ddsd.dwWidth							= Width;
			ddsd.dwHeight							= Height;
			ddsd.ddpfPixelFormat.dwSize				= sizeof(DDPIXELFORMAT);
			ddsd.ddpfPixelFormat.dwFlags			= DDPF_RGB;
			ddsd.ddpfPixelFormat.dwRGBBitCount		= 32;
			ddsd.ddpfPixelFormat.dwRGBAlphaBitMask	= 0xff000000;
			ddsd.ddpfPixelFormat.dwRBitMask			= 0x00ff0000;
			ddsd.ddpfPixelFormat.dwGBitMask			= 0x0000ff00;
			ddsd.ddpfPixelFormat.dwBBitMask			= 0x000000ff;
//
// Create system memory surface
//
			wCreateSurface( DDobject, &ddsd, &DWSurface7, NULL );
//
// Calculate memory useage
//
			gos_ChangeHeapSize( Heap_TextureOther, Width*Height*4, 1 );
		}
//
// Lock the surface and get pointer to bottom left (BMP's are stored upside down)
//
		ddsd.dwSize = sizeof(ddsd);
		wLock( DWSurface7, NULL, &ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL );
		Pitch=ddsd.lPitch>>2;
		Dest=(DWORD*)ddsd.lpSurface+Pitch*(Height-1);
	}
	else
//
// Loading into a memory surface
//
	{
		Memory=Dest=(DWORD*)gos_Malloc(Width*Height*4, Heap_TextureOther);
		Pitch=Width;
		Dest+=Pitch*(Height-1);
	}
//
// If using a palette, read it in convert it to a 768 byte palette
//
	BYTE* Source=(BYTE*)Data+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
	bool UsePalette=bih.biBitCount==8;
	if( UsePalette )
	{
		DWORD Colors=bih.biClrUsed ? bih.biClrUsed : 256;
		for( int t0=0; t0<Colors; t0++ )
		{
			Palette[t0]=((*(Source+2))<<16) + ((*(Source+1))<<8) + *Source + 0xff000000;
			Source+=4;
		}
	}

	Source=Data+bfh.bfOffBits;

//
// Compressed BMP's are never non-square and always have palettes
//
	if( Compressed )
	{
//
// Must clear whole destination texture (in case absolute position is used)
//
		DWORD* tempDest=Dest;
		for( int y=0; y<Height; y++ )
		{
			memset( Dest, 0, Width*4 ); 
			Dest-=Pitch;
		}
		Dest=tempDest;
//
// Now decompress texture
//
		BYTE Code;
		DWORD* StartofLine=Dest;
		while(1)
		{
			Code=*Source++;			
			if( Code )
			{
				Pixel=Palette[*Source++];
				for( int t0=0; t0<Code; t0++ )
					*Dest++=Pixel;
			}
			else
			{
				Code=*Source++;
				if( Code==0 )
				{
					StartofLine-=Pitch;
					Dest=StartofLine;
				}
				else if( Code==1 )
				{
					break;
				}
				else if( Code==2 )
				{
					Dest+=*Source++;
					Code=*Source++;
					Dest-=Code*Pitch;
					StartofLine-=Code*Pitch;
				}
				else
				{
					for( int t0=0; t0<Code; t0++ )
					{
						Pixel=Palette[*Source++];
						*Dest++=Pixel;
					}
					if( (int)Source&1 )
						Source++;
				}
			}
		}
	}
	else
	{
//
// Now decode BMP into system memory texture surface
//
		for( int y=0; y<Height; y++ )
		{
			DWORD* tempDest=Dest;
			for( int x=0; x<Width; x++ )
			{
//
// Read the next pixel
//
				if( UsePalette )
				{
					Pixel=Palette[*Source++];   
				}
				else if( bih.biBitCount==16 )
				{
					DWORD Red, Green, Blue, Alpha;

					if( Format == 565 )
					{
						Red   = ((*(WORD *)Source) & 0xf800) >> 8;
						Green = ((*(WORD *)Source) & 0x07e0) >> 3;
						Blue  = ((*(WORD *)Source) & 0x001f) << 3;
						Alpha = 0xff;
					}
					else if( Format == 1555 )
					{
						Red   = ((*(WORD *)Source) & 0x7c00) >> 7;
						Green = ((*(WORD *)Source) & 0x03e0) >> 2;
						Blue  = ((*(WORD *)Source) & 0x001f) << 3;

						if( (*(WORD *)Source) & 0x8000 )
						{
							Alpha = 0xff;
						}
						else
						{
							Alpha = 0x00;
						}
					}
					else if( Format == 4444 )
					{
						Red   = ((*(WORD *)Source) & 0x0f00) >> 4;
						Green = ((*(WORD *)Source) & 0x00f0);
						Blue  = ((*(WORD *)Source) & 0x000f) << 4;
						Alpha = ((*(WORD *)Source) & 0xf000) >> 8;

						Alpha += ((Alpha & 0x10) >> 1) + 
								 ((Alpha & 0x10) >> 2) +
								 ((Alpha & 0x10) >> 3) +
								 ((Alpha & 0x10) >> 4);

					}
					else
					{
						Red   = 0;
						Green = 0;
						Blue  = 0;
						Alpha = 0;
					}

					Pixel = (Alpha << 24) + (Red << 16) + (Green << 8) + Blue;
					Source+=2;
				}
				else if( bih.biBitCount==24 )
				{
					Pixel=((*(Source+2))<<16) + ((*(Source+1))<<8) + *Source + 0xff000000;
					Source+=3;
				}
				else
				{
					Pixel=*(DWORD*)Source | 0xff000000;
					Source+=4;
				}
//
// Write the pixel to the bitmap
//
				*Dest++=Pixel;
			}
			Dest=tempDest-Pitch;
			Source += (Width) % 4;
		}
	}
//
// Update structure with information about surface
//
	*TextureHeight=Height;
	*TextureWidth=Width;
//
// Loading into a texture?
//
	if( TextureLoad )
	{
		wUnlock( DWSurface7, NULL );
		return (void*)DWSurface7;
	}
	else
	{
		return (void*)Memory;
	}
}



