//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $Workfile:     $
// $Date:         $
// $NoKeywords: $
//=============================================================================//

#include <stdio.h>
#include "tgaloader.h"
#include "tier0/dbg.h"
#include "basetypes.h"
#include <math.h>
#include "utlvector.h"
#include "utlbuffer.h"
#ifndef TGALOADER_USE_FOPEN
#include "filesystem.h"
#endif

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

namespace TGALoader
{


//-----------------------------------------------------------------------------
// Format of the TGA header on disk
//-----------------------------------------------------------------------------

#pragma pack (1)

struct TGAHeader_t
{
	unsigned char 	id_length;
	unsigned char	colormap_type;
	unsigned char	image_type;
	unsigned short	colormap_index;
	unsigned short	colormap_length;
	unsigned char	colormap_size;
	unsigned short	x_origin;
	unsigned short	y_origin;
	unsigned short	width;
	unsigned short	height;
	unsigned char	pixel_size;
	unsigned char	attributes;
};


//-----------------------------------------------------------------------------
// read a row into an RGBA8888 array.
//-----------------------------------------------------------------------------

typedef void (*ReadRowFunc_t)( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDstMemory );


//-----------------------------------------------------------------------------
// output a RGBA8888 row into the destination format.
//-----------------------------------------------------------------------------

typedef void (*OutputRowFunc_t)( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDstMemory );


//-----------------------------------------------------------------------------
// Important constants
//-----------------------------------------------------------------------------

#define TGA_MAX_COLORMAP_SIZE ( 256 * 4 )
#define TGA_MAX_ROW_LENGTH_IN_PIXELS IMAGE_MAX_DIM


//-----------------------------------------------------------------------------
// Globals... blech
//-----------------------------------------------------------------------------

static unsigned char g_ColorMap[TGA_MAX_COLORMAP_SIZE];

// run-length state from row to row for RLE images
static bool g_IsRunLengthPacket;
static int g_PixelsLeftInPacket;

static unsigned char g_SrcGammaTable[256];
static unsigned char g_DstGammaTable[256];

#ifndef TGALOADER_USE_FOPEN
static IBaseFileSystem *s_pFileSystem = NULL;
#endif

typedef CUtlMemory<unsigned char> CTempImage;


//-----------------------------------------------------------------------------
// Reads in a file, sticks it into a UtlVector
//-----------------------------------------------------------------------------

static bool ReadFile( char const* pFileName, CTempImage& image, int maxbytes = -1 )
{
	Assert( pFileName );
#ifndef TGALOADER_USE_FOPEN
	Assert( s_pFileSystem );
	if( !s_pFileSystem )
	{
		return false;
	}

	FileHandle_t fileHandle;
	fileHandle = s_pFileSystem->Open( pFileName, "rb" );
	if( !fileHandle )
		return false;

	// How big is the file?
	long pos;
	if (maxbytes < 0)
	{
		pos = s_pFileSystem->Size( fileHandle );
	}
	else
	{
		pos = maxbytes;
	}
	
	// Allocate enough space
	image.EnsureCapacity( pos );

	// Back to the start of the file
	s_pFileSystem->Seek( fileHandle, 0, FILESYSTEM_SEEK_HEAD );

	// Read the file into the vector memory
	int len = s_pFileSystem->Read( image.Base(), pos, fileHandle );

	// Close the file
	s_pFileSystem->Close( fileHandle );

	// It's an error if we didn't read in enough goodies
	return len == pos;
#else
	FILE *fp;
	fp = fopen( pFileName, "rb" );
	if( !fp)
		return false;

	// How big is the file?
	long pos;
	if (maxbytes < 0)
	{
		fseek( fp, 0, SEEK_END );
		pos = ftell( fp );
	}
	else
	{
		pos = maxbytes;
	}
	
	// Allocate enough space
	image.EnsureCapacity( pos );

	// Back to the start of the file
	fseek( fp, 0, SEEK_SET );

	// Read the file into the vector memory
	int len = fread( image.Base(), 1, pos, fp );

	// Close the file
	fclose( fp );

	// It's an error if we didn't read in enough goodies
	return len == pos;
#endif
}


//-----------------------------------------------------------------------------
// Reads in the TGA Header
//-----------------------------------------------------------------------------
static void ReadHeader( CUtlBuffer& buf, TGAHeader_t& header )
{
	buf.Get( &header, sizeof(TGAHeader_t) );
}


//-----------------------------------------------------------------------------
// Figures out TGA information
//-----------------------------------------------------------------------------
bool GetInfo( CUtlBuffer& buf, int *width, int *height, 
						ImageFormat *imageFormat, float *sourceGamma )
{
	TGAHeader_t header;

	ReadHeader( buf, header );

	switch( header.image_type )
	{
	case 1: // 8 bit uncompressed TGA image
	case 3: // 8 bit monochrome uncompressed TGA image
	case 9: // 8 bit compressed TGA image
		*imageFormat = IMAGE_FORMAT_I8; 
		break;
	case 2: // 24/32 bit uncompressed TGA image
	case 10: // 24/32 bit compressed TGA image
		if( header.pixel_size == 32 )
		{
			*imageFormat = IMAGE_FORMAT_ABGR8888;
		}
		else if( header.pixel_size == 24 )
		{
			*imageFormat = IMAGE_FORMAT_BGR888;
		}
		else
		{
			return false;
		}
		break;

	default:
		return false;
		break;
	}

	*width = header.width;
	*height = header.height;
	*sourceGamma = ARTWORK_GAMMA;

	return true;
}

//-----------------------------------------------------------------------------
// Set the file system to use for handling TGA files.
//-----------------------------------------------------------------------------
#ifndef TGALOADER_USE_FOPEN
bool SetFileSystem( CreateInterfaceFn fileSystemFactory )
{
	s_pFileSystem = ( IFileSystem * )fileSystemFactory( BASEFILESYSTEM_INTERFACE_VERSION, NULL );
	if( s_pFileSystem )
	{
		return true;
	}
	else
	{
		return false;
	}
}
#endif

//-----------------------------------------------------------------------------
// Gets info about a TGA file
//-----------------------------------------------------------------------------
bool GetInfo( char const* pFileName, int *width, int *height, 
				ImageFormat *imageFormat, float *sourceGamma )
{
	// temporary memory
	CTempImage image;

	// try to read in the file
	if (!ReadFile( pFileName, image, sizeof(TGAHeader_t) ))
		return false;

	// Serialization buffer
	CUtlBuffer buf( image.Base(), image.NumAllocated() );

	return GetInfo( buf, width, height, imageFormat, sourceGamma );
}


//-----------------------------------------------------------------------------
// Various output methods
//-----------------------------------------------------------------------------

void OutputRowRGBA8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 4 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		pDst[0] = pSrc[0];
		pDst[1] = pSrc[1];
		pDst[2] = pSrc[2];
		pDst[3] = pSrc[3];
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowABGR8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 4 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		pDst[3] = pSrc[0];
		pDst[2] = pSrc[1];
		pDst[1] = pSrc[2];
		pDst[0] = pSrc[3];
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowRGB888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 3 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		pDst[0] = pSrc[0];
		pDst[1] = pSrc[1];
		pDst[2] = pSrc[2];
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowBGR888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 3 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		pDst[2] = pSrc[0];
		pDst[1] = pSrc[1];
		pDst[0] = pSrc[2];
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowRGB565( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	Assert( 0 );
}

void OutputRowI8( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, ++pDst )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();

		if( ( pSrc[0] == pSrc[1] ) && ( pSrc[1] == pSrc[2] ) )
		{
			pDst[0] = pSrc[0];
		}
		else
		{
			pDst[0] = ( unsigned char )( 0.299f * pSrc[0] + 0.587f * pSrc[1] + 0.114f * pSrc[2] );
		}

		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowIA88( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 2 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();

		if( ( pSrc[0] == pSrc[1] ) && ( pSrc[1] == pSrc[2] ) )
		{
			pDst[0] = pSrc[0];
		}
		else
		{
			pDst[0] = ( unsigned char )( 0.299f * pSrc[0] + 0.587f * pSrc[1] + 0.114f * pSrc[2] );
		}
		pDst[1] = pSrc[3];

		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowA8( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, ++pDst )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		pDst[0] = pSrc[3];
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowRGB888BlueScreen( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 3 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		pDst[0] = (unsigned char)(( ( int )pSrc[0] * ( int )pSrc[3] ) >> 8);
		pDst[1] = (unsigned char)(( ( int )pSrc[1] * ( int )pSrc[3] ) >> 8);
		pDst[2] = (( ( ( ( int )pSrc[2] * ( int )pSrc[3] ) ) >> 8 ) + ( 255 - pSrc[3] ));
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowBGR888BlueScreen( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 3 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		pDst[2] = (unsigned char)(( ( int )pSrc[0] * ( int )pSrc[3] ) >> 8);
		pDst[1] = (unsigned char)(( ( int )pSrc[1] * ( int )pSrc[3] ) >> 8);
		pDst[0] = (unsigned char)(( ( ( ( int )pSrc[2] * ( int )pSrc[3] ) ) >> 8 ) + ( 255 - pSrc[3] ));
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowARGB8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 4 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		pDst[0] = pSrc[3];
		pDst[1] = pSrc[0];
		pDst[2] = pSrc[1];
		pDst[3] = pSrc[2];
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowBGRA8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 4 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		pDst[0] = pSrc[2];
		pDst[1] = pSrc[1];
		pDst[2] = pSrc[0];
		pDst[3] = pSrc[3];
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowBGRX8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 4 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		pDst[0] = pSrc[2];
		pDst[1] = pSrc[1];
		pDst[2] = pSrc[0];
		pDst[3] = 255;
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowBGR565( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 2 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		unsigned short rgba = (pSrc[2] & 0x1F) | ((pSrc[1] & 0x3F) << 5) | 
			((pSrc[0] & 0x1F) << 11);

		pDst[0] = rgba & 0xFF;
		pDst[1] = rgba >> 8;
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

void OutputRowBGRX5551( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 2 )
	{
		unsigned char* pSrc = (unsigned char*)buf.PeekGet();
		unsigned short rgba = (pSrc[2] & 0x1F) | ((pSrc[1] & 0x1F) << 5) | 
			((pSrc[0] & 0x1F) << 10) | 0x8000;

		pDst[0] = rgba & 0xFF;
		pDst[1] = rgba >> 8;
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
	}
}

static OutputRowFunc_t GetOutputRowFunc( ImageFormat imageFormat )
{
	switch( imageFormat )
	{
	case IMAGE_FORMAT_RGBA8888:
		return &OutputRowRGBA8888;
	case IMAGE_FORMAT_ABGR8888:
		return &OutputRowABGR8888;
	case IMAGE_FORMAT_RGB888:
		return &OutputRowRGB888;
	case IMAGE_FORMAT_BGR888:
		return &OutputRowBGR888;
	case IMAGE_FORMAT_RGB565:
		return &OutputRowRGB565;
	case IMAGE_FORMAT_I8:
		return &OutputRowI8;
	case IMAGE_FORMAT_IA88:
		return &OutputRowIA88;
	case IMAGE_FORMAT_A8:
		return &OutputRowA8;
	case IMAGE_FORMAT_RGB888_BLUESCREEN:
		return &OutputRowRGB888BlueScreen;
	case IMAGE_FORMAT_BGR888_BLUESCREEN:
		return &OutputRowBGR888BlueScreen;
	case IMAGE_FORMAT_ARGB8888:
		return &OutputRowARGB8888;
	case IMAGE_FORMAT_BGRA8888:
		return &OutputRowBGRA8888;
	case IMAGE_FORMAT_BGRX8888:
		return &OutputRowBGRX8888;
	case IMAGE_FORMAT_BGR565:
		return &OutputRowBGR565;
	case IMAGE_FORMAT_BGRX5551:
		return &OutputRowBGRX5551;
	default:
		return NULL;
		break;
	}
}

static void InitSourceGammaConversionTable( float srcGamma )
{
	static float lastSrcGamma = -1;
	if (lastSrcGamma == srcGamma)
		return;

	lastSrcGamma = srcGamma;
	ImageLoader::ConstructGammaTable( g_SrcGammaTable, srcGamma, 1.0f );
}

static void InitDestGammaConversionTable( float dstGamma )
{
	static float lastDstGamma = -1;
	if (lastDstGamma == dstGamma)
		return;

	lastDstGamma = dstGamma;
	ImageLoader::ConstructGammaTable( g_DstGammaTable, 1.0f, dstGamma );
}


//-----------------------------------------------------------------------------
// Reads an 8-bit palettized TGA image
//-----------------------------------------------------------------------------

void ReadRow8BitUncompressedWithColormap( CUtlBuffer& buf, 
		TGAHeader_t const& header, unsigned char* pDst )
{
	int i;
	unsigned char* colormapEntry;

	switch( header.colormap_size )
	{
	case 8:
		for( i = 0; i < header.width; ++i, pDst += 4 )
		{
			int pal = buf.GetUnsignedChar();

			colormapEntry = &g_ColorMap[pal];
			pDst[0] = colormapEntry[0];
			pDst[1] = colormapEntry[0];
			pDst[2] = colormapEntry[0];
			pDst[3] = 255;
		}
		break;

	case 24:
		for( i = 0; i < header.width; ++i, pDst += 4 )
		{
			int pal = buf.GetUnsignedChar();

			colormapEntry = &g_ColorMap[pal * 3];
			pDst[0] = colormapEntry[2];
			pDst[1] = colormapEntry[1];
			pDst[2] = colormapEntry[0];
			pDst[3] = 255;
		}
		break;

	case 32:
		for( i = 0; i < header.width; ++i, pDst += 4 )
		{
			int pal = buf.GetUnsignedChar();

			colormapEntry = &g_ColorMap[pal * 4];
			pDst[0] = colormapEntry[3];
			pDst[1] = colormapEntry[2];
			pDst[2] = colormapEntry[1];
			pDst[3] = colormapEntry[0];
		}
		break;

	default:
		Assert( 0 );
		break;
	}
}


//-----------------------------------------------------------------------------
// Reads an 8-bit greyscale TGA image
//-----------------------------------------------------------------------------

void ReadRow8BitUncompressedWithoutColormap( CUtlBuffer& buf, 
		TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 4 )
	{
		pDst[0] = pDst[1] = pDst[2] = buf.GetUnsignedChar();
		pDst[3] = 255;
	}
}

//-----------------------------------------------------------------------------
// Reads a 24-bit TGA image
//-----------------------------------------------------------------------------

void ReadRow24BitUncompressedWithoutColormap( CUtlBuffer& buf, 
		TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 4 )
	{
		pDst[2] = buf.GetUnsignedChar();
		pDst[1] = buf.GetUnsignedChar();
		pDst[0] = buf.GetUnsignedChar();
		pDst[3] = 255;
	}
}

//-----------------------------------------------------------------------------
// Reads a 32-bit TGA image
//-----------------------------------------------------------------------------

void ReadRow32BitUncompressedWithoutColormap( CUtlBuffer& buf, 
		TGAHeader_t const& header, unsigned char* pDst )
{
	for( int i = 0; i < header.width; ++i, pDst += 4 )
	{
		pDst[2] = buf.GetUnsignedChar();
		pDst[1] = buf.GetUnsignedChar();
		pDst[0] = buf.GetUnsignedChar();
		pDst[3] = buf.GetUnsignedChar();
	}
}


//-----------------------------------------------------------------------------
// Decompresses a run-length encoded row of bytes
//-----------------------------------------------------------------------------

static void DecompressRow( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
{
	int bytesPerPixel = header.pixel_size >> 3;
	int pixelsLeftInRow = header.width;
	int numPixelsToProcess;

#ifdef _DEBUG
	unsigned char *pLast = pDst + header.width * bytesPerPixel;
#endif

	unsigned char repeat[4];
	do
	{
		if( !g_PixelsLeftInPacket )
		{
			// start a new packet.
			unsigned char packetHeader = buf.GetUnsignedChar();
			g_PixelsLeftInPacket = 1 + ( packetHeader & 0x7f );
			if( packetHeader & 0x80 )
			{
				g_IsRunLengthPacket = true;

				// Read what I'm supposed to repeat
				for (int i = 0; i < bytesPerPixel; ++i)
				{
					repeat[i] = buf.GetUnsignedChar();
				}
			}
			else
			{
				g_IsRunLengthPacket = false;
			}
		}

		// already in the middle of a packet of data.
		numPixelsToProcess = g_PixelsLeftInPacket;
		if( numPixelsToProcess > pixelsLeftInRow )
		{
			numPixelsToProcess = pixelsLeftInRow;
		}
		if( g_IsRunLengthPacket )
		{
			for( int i = numPixelsToProcess; --i >= 0; pDst += bytesPerPixel )
			{
				for (int j = 0; j < bytesPerPixel; ++j )
				{
					pDst[j] = repeat[j];
				}
			}
		}
		else
		{
			buf.Get( pDst, numPixelsToProcess * bytesPerPixel );
			pDst += numPixelsToProcess * bytesPerPixel;
		}

		g_PixelsLeftInPacket -= numPixelsToProcess;
		pixelsLeftInRow -= numPixelsToProcess;

	} while( pixelsLeftInRow );

	Assert( pDst == pLast );
}

//-----------------------------------------------------------------------------
// Reads a compressed 8-bit palettized TGA image
//-----------------------------------------------------------------------------

void ReadRow8BitCompressedWithColormap( CUtlBuffer& buf, 
		TGAHeader_t const& header, unsigned char* pDst )
{
	unsigned char rowI_8[TGA_MAX_ROW_LENGTH_IN_PIXELS];

	DecompressRow( buf, header, rowI_8 );

	CUtlBuffer uncompressedBuf( rowI_8, TGA_MAX_ROW_LENGTH_IN_PIXELS );
	ReadRow8BitUncompressedWithColormap( uncompressedBuf, header, pDst );
}

//-----------------------------------------------------------------------------
// Reads a compressed 8-bit greyscale TGA image
//-----------------------------------------------------------------------------

void ReadRow8BitCompressedWithoutColormap( CUtlBuffer& buf, 
		TGAHeader_t const& header, unsigned char* pDst )
{
	unsigned char rowI_8[TGA_MAX_ROW_LENGTH_IN_PIXELS];

	DecompressRow( buf, header, rowI_8 );

	CUtlBuffer uncompressedBuf( rowI_8, TGA_MAX_ROW_LENGTH_IN_PIXELS );
	ReadRow8BitUncompressedWithoutColormap( uncompressedBuf, header, pDst );
}

//-----------------------------------------------------------------------------
// Reads a compressed 24-bit TGA image
//-----------------------------------------------------------------------------

void ReadRow24BitCompressedWithoutColormap( CUtlBuffer& buf, 
		TGAHeader_t const& header, unsigned char* pDst )
{
	unsigned char rowBGR_888[TGA_MAX_ROW_LENGTH_IN_PIXELS * 3];

	DecompressRow( buf, header, rowBGR_888 );

	CUtlBuffer uncompressedBuf( rowBGR_888, TGA_MAX_ROW_LENGTH_IN_PIXELS * 3 );
	ReadRow24BitUncompressedWithoutColormap( uncompressedBuf, header, pDst );
}

//-----------------------------------------------------------------------------
// Reads a compressed 32-bit TGA image
//-----------------------------------------------------------------------------

void ReadRow32BitCompressedWithoutColormap( CUtlBuffer& buf, 
		TGAHeader_t const& header, unsigned char* pDst )
{
	unsigned char rowBGRA_8888[TGA_MAX_ROW_LENGTH_IN_PIXELS << 2];

	DecompressRow( buf, header, rowBGRA_8888 );

	CUtlBuffer uncompressedBuf( rowBGRA_8888, TGA_MAX_ROW_LENGTH_IN_PIXELS << 2 );
	ReadRow32BitUncompressedWithoutColormap( uncompressedBuf, header, pDst );
}

//-----------------------------------------------------------------------------
// Method used to read the TGA
//-----------------------------------------------------------------------------

static ReadRowFunc_t GetReadRowFunc( TGAHeader_t const& header )
{
	switch( header.image_type )
	{
	case 1: // 8 bit uncompressed TGA image
	case 3: // 8 bit monochrome uncompressed TGA image
		if( header.colormap_length )
		{
			return &ReadRow8BitUncompressedWithColormap;
		}
		else
		{
			return &ReadRow8BitUncompressedWithoutColormap;
		}
	case 9: // 8 bit compressed TGA image
		if( header.colormap_length )
		{
			return &ReadRow8BitCompressedWithColormap;
		}
		else
		{
			return &ReadRow8BitCompressedWithoutColormap;
		}
	case 2: // 24/32 bit uncompressed TGA image
		if( header.colormap_length )
		{
			// Error( "colormaps not support with 24/32 bit TGAs." );
			return 0;
		}
		else
		{
			switch( header.pixel_size )
			{
			case 24:
				return &ReadRow24BitUncompressedWithoutColormap;
				break;
			case 32:
				return &ReadRow32BitUncompressedWithoutColormap;
				break;
			default:
				//Error( "unsupported tga colordepth: %d", TGAHeader_t.pixel_size" );
				return 0;
				break;
			}
		}
	case 10: // 24/32 bit compressed TGA image
		if( header.colormap_length )
		{
			// Error( "colormaps not support with 24/32 bit TGAs." );
			return 0;
		}
		else
		{
			switch( header.pixel_size )
			{
			case 24:
				return &ReadRow24BitCompressedWithoutColormap;
				break;
			case 32:
				return &ReadRow32BitCompressedWithoutColormap;
				break;
			default:
				//Error( "unsupported tga colordepth: %d", TGAHeader_t.pixel_size" );
				return NULL;
				break;
			}
		}
	default:
		// Error( "unsupported tga pixel format" );
		return 0;
		break;
	}
}


//-----------------------------------------------------------------------------
// Reads the color map
//-----------------------------------------------------------------------------

static bool ReadColormap( CUtlBuffer& buf, TGAHeader_t const& header )
{
	int numColormapBytes = header.colormap_length * ( header.colormap_size >> 3 );
	if( numColormapBytes > TGA_MAX_COLORMAP_SIZE )
	{
		// Error( "colormap bigger than TGA_MAX_COLORMAP_SIZE" );
		return false;
	}

	// read colormap
	buf.Get( g_ColorMap, numColormapBytes );

	return true;
}


//-----------------------------------------------------------------------------
// Reads the source image
//-----------------------------------------------------------------------------
static bool ReadSourceImage( CUtlBuffer& buf, TGAHeader_t& header, CTempImage& image )
{
	// Figure out our reading and riting
	ReadRowFunc_t ReadRowFunc = GetReadRowFunc( header );
	if( !ReadRowFunc )
		return false;

	// HACK: Fixme: We really shouldn't be using globals here
	// Init RLE vars
	g_PixelsLeftInPacket = 0;

	// Only allocate the memory once
	int memRequired = ImageLoader::GetMemRequired( header.width, header.height, 
		IMAGE_FORMAT_RGBA8888, false );
	image.EnsureCapacity( memRequired );

	// read each row and process it. Note the image is upside-down from
	// the way we want it.
	unsigned char* pDstBits;
	// flip the image vertically if necessary.
	if (header.attributes & 0x20)
	{
		for( int row = 0; row < header.height; ++row )
		{
			pDstBits = image.Base() + 
				row * header.width * ImageLoader::SizeInBytes(IMAGE_FORMAT_RGBA8888);
			ReadRowFunc( buf, header, pDstBits );
		}
	}
	else
	{
		for( int row = header.height; --row >= 0; )
		{
			pDstBits = image.Base() + 
				row * header.width * ImageLoader::SizeInBytes(IMAGE_FORMAT_RGBA8888);
			ReadRowFunc( buf, header, pDstBits );
		}
	}

	return true;
}


//-----------------------------------------------------------------------------
// Outputs the final image
//-----------------------------------------------------------------------------
static bool OutputImage( CTempImage& image, TGAHeader_t& header, 
						 ImageFormat imageFormat, unsigned char* pDst )
{
	// How do we write?
	OutputRowFunc_t OutputRowFunc = GetOutputRowFunc( imageFormat );
	if( !OutputRowFunc )
		return false;

	CUtlBuffer buf( image.Base(), image.NumAllocated() );
	unsigned char* pDstBits;
	for( int row = 0; row < header.height; ++row )
	{
		pDstBits = pDst + 
			row * header.width * ImageLoader::SizeInBytes(imageFormat);
		OutputRowFunc( buf, header, pDstBits );
	}

	return true;
}


//-----------------------------------------------------------------------------
// Parses the lovely bits previously read from disk
//-----------------------------------------------------------------------------
bool Load( unsigned char *pOutputImage, CUtlBuffer& buf, int width, 
		int height, ImageFormat imageFormat, float targetGamma, bool mipmap )
{
	TGAHeader_t header;

	// Read the TGA header
	ReadHeader( buf, header );

	// skip TARGA image comment
	if( header.id_length != 0 )
	{
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, header.id_length );
	}

	// Read the color map for palettized images
	if( header.colormap_length != 0 )
	{
		if (!ReadColormap( buf, header ))
			return false;
	}
	
	// Stores the RGBA8888 temp version of the image which we'll use to
	// to do mipmapping...
	CTempImage tmpImage;
	if (!ReadSourceImage( buf, header, tmpImage ))
		return false;

	// Erg... what if header.width * header.height > width * height? 
	// Then don't do anything, this is an error condition...
	if ((width * height) < (header.width * header.height))
		return false;

	// Now that we've got the source image, generate the mip-map levels
	ImageLoader::GenerateMipmapLevels( tmpImage.Base(), pOutputImage,
		header.width, header.height, imageFormat, ARTWORK_GAMMA, targetGamma,
		mipmap ? 0 : 1 );

	return true;
}


//-----------------------------------------------------------------------------
// Reads a TGA image from a file
//-----------------------------------------------------------------------------
bool Load( unsigned char *pOutputImage, const char *pFileName, int width, int height, 
			ImageFormat imageFormat, float targetGamma, bool mipmap )
{
	Assert( pOutputImage && pFileName );

	// memory for the file
	CTempImage vec;

	// Read that puppy in!
	if (!ReadFile( pFileName, vec ))
		return false;

	// Make an unserialization buffer
	CUtlBuffer buf( vec.Base(), vec.NumAllocated() );
	
	// Do the dirty deed
	return Load( pOutputImage, buf, width, height, imageFormat, targetGamma, mipmap );
}


//-----------------------------------------------------------------------------
// Creates a map in linear space
//-----------------------------------------------------------------------------
static bool LoadRGBA8888( CUtlBuffer& buf, CUtlMemory<unsigned char> &outputData, int &outWidth, int &outHeight )
{
	TGAHeader_t header;

	// Read the TGA header
	ReadHeader( buf, header );

	// skip TARGA image comment
	if( header.id_length != 0 )
	{
		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, header.id_length );
	}

	// Read the color map for palettized images
	if( header.colormap_length != 0 )
	{
		if (!ReadColormap( buf, header ))
			return false;
	}
	
	// Stores the RGBA8888 temp version of the image which we'll use to
	// to do mipmapping...
	int memSize = ImageLoader::GetMemRequired( 
		header.width, header.height, IMAGE_FORMAT_RGBA8888, false );

	outputData.EnsureCapacity( memSize );
	if (!ReadSourceImage( buf, header, outputData ))
		return false;

	outWidth = header.width;
	outHeight = header.height;
	return true;
}

//-----------------------------------------------------------------------------
// Reads a TGA, keeps it in RGBA8888
//-----------------------------------------------------------------------------

bool LoadRGBA8888( const char *pFileName, CUtlMemory<unsigned char> &outputData, int &outWidth, int &outHeight )
{
	Assert( pFileName );

	// memory for the file
	CTempImage vec;

	// Read that puppy in!
	if (!ReadFile( pFileName, vec ))
		return false;

	// Make an unserialization buffer
	CUtlBuffer buf( vec.Base(), vec.NumAllocated() );
	
	// Do the dirty deed
	return LoadRGBA8888( buf, outputData, outWidth, outHeight );
}

} // end namespace TGALoader
