//
// Tools_PSP_Texture_Writer.cpp
//
// Created by KJFL - 2004-02-11 11:14:34
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "ImageProcessing/Tools_PSP_Texture_Writer.h"

// Asura headers
#include "Core/Asura_TextureFlags.h"
#include "Core/Memory/Asura_Mem.h"
#include "Debugging/Asura_Assert.h"
#ifdef ASURA_MAIN
#include "Load&Save/LevelExport/Tools_Level_Export.h"
#endif
#include "Load&Save/Tools_TextureConversionData.h"

PALETTE_GENERATION_PTR Tools_PSP_Texture_Writer::s_pfnPaletteGenerationOverride = NULL;
Tools_PSP_Texture_Writer::PaletteAdjustmentHook* Tools_PSP_Texture_Writer::s_pfnPaletteAdjustment = NULL;

PALETTE_GENERATION_PTR Tools_PSP_Texture_Writer::GetPaletteGenerationFunctionFromDllForPSP()
{
	static PALETTE_GENERATION_PTR PaletteGeneration = 0;
	static struct RuntimeDll
	{
		RuntimeDll(){hDLL = 0;}
		~RuntimeDll(){if(hDLL) FreeLibrary(hDLL);}
		HMODULE hDLL;
	} dll;
		
	if(!dll.hDLL)
	{
		//try loading the dll
		dll.hDLL = LoadLibrary("PaletteGeneration.dll");


		if (dll.hDLL == NULL)
		{
#ifdef ASURA_MAIN
			Tools_Level_Export::AddMessage(Tools_Level_Export::MT_ERROR,"Failed to load PaletteGeneration.dll");
#else
			Asura_Assert(false, "Failed to load PaletteGeneration.dll");
#endif
			return 0;
		}

		//grab the function
		PaletteGeneration = (PALETTE_GENERATION_PTR)GetProcAddress(dll.hDLL,"PaletteGeneration");
	}
	
	return PaletteGeneration;
}

// KJFL - From code given by Michael Anderson on SCE PSP newsgroup en.toptips 23/09/2004
void Tools_PSP_Texture_Writer::SwizzleTexture( unsigned char* const texels, const unsigned int stride, const unsigned int height )
{
     // Swizzle cache needs to be large enough to fit a full row of
     // swizzle blocks for the largest supported texture stride.
     // The largest texture stride: 512 * 4 = 2048.
     // The byte size of a swizzle block: 16 * 8 = 128.
     // The number of swizzle blocks in largest stride: 2048 / 16 = 128.
     // The required swizzle cache size: 128 * 128 = 16384.
     // NOTE: this is a perfect scratch pad candidate.
     static unsigned char cache[16384];

     // Calc the number of swizzle blocks in a row, each block is 16 bytes wide.
     unsigned int blockWd = stride >> 4;
     unsigned int blockX;

     // Calc the number of swizzle block rows, each block is 8 texels high.
     unsigned int blockHt = height >> 3;
     unsigned int blockY;

     // Calc swizzle block row stride, each block is 8 texels high.
     unsigned int rowStride = stride << 3;

     // Declare source and destination pointers.
     unsigned char* dest = texels;
     unsigned char* src;
     unsigned int row;

     // Swizzle texture.
     for (blockY = 0; blockY < blockHt; blockY++)
     {
         // Cache full row of swizzle blocks.
         memcpy( cache, dest, rowStride );

         for (blockX = 0; blockX < blockWd; blockX++)
         {
             // Set cache source pointer for block.
             src = &cache[blockX << 4];

             // Swizzle block.
             for (row = 0; row < 8; row++)
             {
                 // Try and take advantage of multiple pipes.
                 dest[ 0] = src[ 0]; dest[ 1] = src[ 1]; dest[ 2] = src[ 2]; dest[ 3] = src[ 3];
                 dest[ 4] = src[ 4]; dest[ 5] = src[ 5]; dest[ 6] = src[ 6]; dest[ 7] = src[ 7];
                 dest[ 8] = src[ 8]; dest[ 9] = src[ 9]; dest[10] = src[10]; dest[11] = src[11];
                 dest[12] = src[12]; dest[13] = src[13]; dest[14] = src[14]; dest[15] = src[15];
                 dest += 16;
                 src += stride;
             }
         }
     }
}

// From code listed by Mark Bruegelmans (SCEE) on SCE PSP newsgroup en.toptips 06/09/2004
// KOR - currently just used for 4-bit as SwizzleTexture does it without needing another texture, but doesn't handle 4-bit.
void Tools_PSP_Texture_Writer::SwizzleBitmap(u_char* pucDest, u_char* pucSrc, int iWidth, int iHeight, int iFormat )
{
	u_char* pucCopy;
	int iPixelsize, iXChunks, iYChunks;

	switch(iFormat)
	{
	case ASURA_PSP_TEXTUREFORMAT_IDX4:
		iPixelsize = 1;
		iWidth >>= 1;
		break;

	case ASURA_PSP_TEXTUREFORMAT_IDX8:
		iPixelsize = 1;
		break;

	case ASURA_PSP_TEXTUREFORMAT_5650:
	case ASURA_PSP_TEXTUREFORMAT_5551:
	case ASURA_PSP_TEXTUREFORMAT_4444:
	case ASURA_PSP_TEXTUREFORMAT_IDX16:
		iPixelsize = 2;
		break;

	case ASURA_PSP_TEXTUREFORMAT_8888:
	case ASURA_PSP_TEXTUREFORMAT_IDX32:
		iPixelsize = 4;
		break;
	}

	iXChunks = (iWidth * iPixelsize) >> 4; // width in bytes / 16
	iYChunks = iHeight >> 3;    // height in pixels / 8

	for(int y = 0; y < iYChunks; y ++)
	{
		for(int x = 0; x < iXChunks; x ++)
		{
			pucCopy = &pucSrc[(x * 16) + (y * iXChunks * 16 * 8)];

			for(int h = 0; h < 8; h ++)
			{
				for(int iBytes = 0; iBytes < 16; iBytes ++)
				{
					*pucDest ++ = *pucCopy ++;
				}

				pucCopy += (iXChunks - 1) * 16;
			}
		}
	}
}

NV_ERROR_CODE Tools_PSP_Texture_Writer::PSPMipMapCallback(const void* pPixelData, size_t uCount, const MIPMapData* pxMipMapData, void* pUserData)
{
	Asura_Assert(pUserData,"");
	return ((Tools_PSP_Texture_Writer*)pUserData)->PSPMipMapCallback(pPixelData,uCount, pxMipMapData);
}

NV_ERROR_CODE Tools_PSP_Texture_Writer::PSPMipMapCallback(const void* pPixelData, size_t uCount, const MIPMapData* pxMipMapData)
{
	// do we have space?
	if (MAXIMUM_NUMBER_OF_IMAGES == m_uNumberOfImages)
	{
		return NV_OK;
	}

	if(!pxMipMapData)
	{
		//The first few calls to this function just provide some header information that we aren't interested in.
		//RAR 15/06/2006
		return NV_OK;
	}
	
	u_int uMipLevel = pxMipMapData ? pxMipMapData->mipLevel : 0;

	// Is this an expected mipmap level? (zero corresponds to the 'original' image)
	if ( uMipLevel < 0 || uMipLevel > MAXIMUM_NUMBER_OF_IMAGES )
	{
		Asura_Assertf( false, ("Invalid mipmap level: %u", uMipLevel ));
		return NV_FAIL;
	}
	
	Tools_PSP_ImageData& xImage = m_axImage[m_uNumberOfImages];


	if (xImage.IsTooSmall(m_xHeader.m_ucTextureFormatID)) // Swizzling code doesn't work with image sizes below 16 bytes. TODO - should probably be able to find a way to do it with padding if we need to.
	{
		return NV_OK;
	}

	// Is the size of this image what we were expecting?
	if ( uCount != xImage.m_uImageSizeInBytes)
	{
		Asura_Assertf( false, ("Mipmap size == %d, but ImageSize == %d. Non-square texture?", uCount, xImage.m_uImageSizeInBytes) );
		return NV_FAIL;
	}


	if(!xImage.m_puPixelData)
	{
		switch(m_xHeader.m_ucTextureFormatID)
		{
			default:
			// if straight 32bit format, or if using a CLUT, we need 8888 data
			case ASURA_PSP_TEXTUREFORMAT_8888:
			case ASURA_PSP_TEXTUREFORMAT_IDX4:
			case ASURA_PSP_TEXTUREFORMAT_IDX8:
			case ASURA_PSP_TEXTUREFORMAT_IDX16:
			case ASURA_PSP_TEXTUREFORMAT_IDX32:
			{
				xImage.m_puPixelData = new u_int[uCount];

				//memcpy( xImage.m_puPixelData, pPixelData, iSize );
				const u_char* pucSource = reinterpret_cast<const u_char*>(pPixelData);
				u_char* pucDest = reinterpret_cast<u_char*>(xImage.m_puPixelData);
				for (u_int u=0; u<uCount/4; u++)
				{
					//Used to swap component order here. This is now dealt with after palette generation.
					//RAR 19/03/2007
					pucDest[0] = pucSource[0];
					pucDest[1] = pucSource[1];
					pucDest[2] = pucSource[2];
					pucDest[3] = pucSource[3];

					pucDest+=4;
					pucSource+=4;
				}

				break;
			}
		}
	}

	m_uNumberOfImages++;

	return NV_OK;
}





Tools_PSP_Texture_Writer::Tools_PSP_Texture_Writer()
{
	m_uNumberOfImages = 0;
	m_puPalette = 0;
	m_uSizeOfPaletteInBytes = 0;
}

Tools_PSP_Texture_Writer::~Tools_PSP_Texture_Writer()
{
	// delete the images' pixel data etc
	for (u_int u=0; u<MAXIMUM_NUMBER_OF_IMAGES; u++)
	{
		m_axImage[u].Reset();
	}

	// delete the palette, if there is one
	delete [] m_puPalette;
}

void Tools_PSP_Texture_Writer::Initialise(const u_int uMipLevel, const u_int uWidth, const u_int uHeight, const u_int uPitch, const void* pPixels)
{
	if(uMipLevel >= MAXIMUM_NUMBER_OF_IMAGES)
	{
		return;
	}

	if(!uMipLevel)
	{
		//first image , intiailise the main image's size ,and that of all the mip maps
		m_axImage[0].m_uImageSizeInBytes = uWidth*uHeight*4;
		m_axImage[0].m_uHeight = uHeight;
		m_axImage[0].m_uWidth = uWidth;

		for (u_int u=1; u<MAXIMUM_NUMBER_OF_IMAGES; u++)
		{
			m_axImage[u].m_uHeight = m_axImage[u-1].m_uHeight/2;
			m_axImage[u].m_uWidth = m_axImage[u-1].m_uWidth/2;
			m_axImage[u].m_uImageSizeInBytes = m_axImage[u-1].m_uImageSizeInBytes/4;
		}
	}
	else
	{
		//ignore mip levels that are going to be too small. RAR 15/06/2006
		if(m_axImage[uMipLevel].IsTooSmall()) return;
		
		//make sure this is the expected width and height
		if(m_axImage[uMipLevel].m_uHeight != uHeight || m_axImage[uMipLevel].m_uWidth != uWidth)
		{
			Asura_Assertf(0,("Unexpected width/height for mip map %d.\nExpecting %dx%d. Got %dx%d",uMipLevel,m_axImage[uMipLevel].m_uWidth,m_axImage[uMipLevel].m_uHeight,uWidth,uHeight));
		}
	}

	Tools_PSP_ImageData& xImage = m_axImage[uMipLevel];

	//copy the image data
	xImage.m_puPixelData = new u_int[xImage.m_uWidth*xImage.m_uHeight];
	
	const u_char* pucSource = reinterpret_cast<const u_char*>(pPixels);
	u_char* pucDest = reinterpret_cast<u_char*>(xImage.m_puPixelData);
	for(u_int h=0; h<uHeight; h++)
	{
		for(u_int w=0; w<uWidth; w++)
		{

			//Used to swap component order here. This is now dealt with after palette generation.
			//RAR 19/03/2007
			pucDest[0] = pucSource[0];
			pucDest[1] = pucSource[1];
			pucDest[2] = pucSource[2];
			pucDest[3] = pucSource[3];

			pucDest+=4;
			pucSource+=4;
		}
		//move to start of next line
		pucSource += uPitch - (uWidth*4);
	}
}

#define WRITE_DATA(_data,_datasize) \
{\
	WriteFile(xFile,_data,_datasize,&uBytesWritten,0);\
	uTotalWritten+=uBytesWritten;\
	if((unsigned)_datasize!=uBytesWritten)\
	{\
		CloseHandle(xFile);\
		return false;\
	}\
}

bool Tools_PSP_Texture_Writer::WriteOutFile(const char* szOutputFilename, const struct TextureConversionData& xConvertData, bool bSkybox /* = false */)
{
	if(!m_axImage[0].m_puPixelData)
	{
		Asura_Assert(0,"Main image hasn't been set in PSP texture writer");
		return false;		
	}
	const int iTextureFlags = xConvertData.m_iTextureFlags;
	bool bAllow512 = (iTextureFlags & AsuraTextureFlag_PS2Allow512) == AsuraTextureFlag_PS2Allow512;

	// Always use an 8888 CLUT
	const enum ASURA_PSP_CLUTFORMAT eCLUTFormat = ASURA_PSP_CLUTFORMAT_32BIT_8888;
	enum ASURA_PSP_TEXTUREFORMAT eTextureFormat;
	#if 0
	if(b8BitAlpha)
	{
		eTextureFormat = ASURA_PSP_TEXTUREFORMAT_8888;
	}
	else 
	#endif
	if(xConvertData.m_bUseFixedPalette)
	{
		u_int uPaletteSize;
		const u_int* puPalette;
		TextureConversionDataBase::GetFixedPalette(uPaletteSize,puPalette);

		if(!uPaletteSize)
		{
			Asura_Assert(0,"Fixed palette hasn't been set");
			return false;
		}

		if(uPaletteSize <= 16)
		{
			eTextureFormat = ASURA_PSP_TEXTUREFORMAT_IDX4;
		}
		else
		{
			eTextureFormat = ASURA_PSP_TEXTUREFORMAT_IDX8;
		}
		
	}
	else if(iTextureFlags & AsuraTextureFlag_16ColourPalette)
	{
		eTextureFormat = ASURA_PSP_TEXTUREFORMAT_IDX4;
	}
	else
	{
		eTextureFormat = ASURA_PSP_TEXTUREFORMAT_IDX8;
	}

	// fill in texture header info
	m_xHeader.m_ucTextureFormatID = static_cast<u_char>(eTextureFormat);
	//m_xHeader.m_usWidth = static_cast<u_short>(uWidth);
	//m_xHeader.m_usHeight = static_cast<u_short>(uHeight);
	// Mipmap drops will be reduced at write-out time if more than possible
	//m_xHeader.m_ucNumberOfMipmaps = ((iTextureFlags & AsuraTextureFlag_NoMipMaps) == AsuraTextureFlag_NoMipMaps)? 0 : static_cast<u_char>(xConvertData.m_uMaxMipDrops);
	m_xHeader.m_usFlags = 0;

	//Make sure the main image being used isn't too small. RAR 10/09/2007
	if(m_axImage[0].IsTooSmall(eTextureFormat))
	{
		//The original texture was too small ,so enlarge it
		//Determine how large it needs to be made
		u_int uScale = 1;
		Tools_PSP_ImageData xTestImage;
		do
		{
			uScale *= 2;
			xTestImage.m_uWidth = m_axImage[0].m_uWidth*uScale;
			xTestImage.m_uHeight = m_axImage[0].m_uHeight*uScale;
			xTestImage.m_uImageSizeInBytes = m_axImage[0].m_uImageSizeInBytes*uScale*uScale;
		}while(xTestImage.IsTooSmall(eTextureFormat));

		//copy the existing pixel data , streatching it to fill the enlarged space
		u_int* puPixelData = new u_int[xTestImage.m_uWidth*xTestImage.m_uHeight];

		u_int* puDest = puPixelData;
			
		for(u_int uY=0; uY<xTestImage.m_uHeight; uY++)
		{
			for(u_int uX=0; uX<xTestImage.m_uWidth; uX++)
			{
				u_int uSourceIndex = (uX/uScale) + (uY/uScale)*m_axImage[0].m_uWidth;
				
				*puDest++ = m_axImage[0].m_puPixelData[uSourceIndex];
			}
		}

		//update the main image with the enlarged version
		delete [] m_axImage[0].m_puPixelData;
		m_axImage[0].m_puPixelData = puPixelData;
		m_axImage[0].m_uWidth = xTestImage.m_uWidth;			
		m_axImage[0].m_uHeight = xTestImage.m_uHeight;			
		m_axImage[0].m_uImageSizeInBytes = xTestImage.m_uImageSizeInBytes;
	}
	



	// get the images please
	GetImages();

	// now calculate a palette if necessary (this modifies the image data, btw)
	if ( m_xHeader.m_ucTextureFormatID == ASURA_PSP_TEXTUREFORMAT_IDX8 || m_xHeader.m_ucTextureFormatID == ASURA_PSP_TEXTUREFORMAT_IDX4 )
	{
		if(!GeneratePalette( eTextureFormat, eCLUTFormat,xConvertData ))
		{
			//Abort if palette generation failed , rather than writing out junk images.
			//RAR 04/10/2006
			return false;
		}
	}

	// swizzle (and cope with change in size for palettised textures)
	ConvertPixelData( eTextureFormat );



	unsigned long uBytesWritten = 0;
	unsigned long uTotalWritten = 0;

	u_int uImageToUse = 0;

#ifdef ASURA_MAIN
	u_int uMaxSize = 128;
#else
	u_int uMaxSize = 256;
#endif
	if (bAllow512)
	{
		uMaxSize = 512;
	}
	else if(bSkybox)
	{
		uMaxSize = 256;
	}
	for (u_int u=0; u<m_uNumberOfImages; u++)
	{
		if (m_axImage[u].m_uWidth <= uMaxSize)
		{
			uImageToUse = u;
			break;
		}
	}

	if(iTextureFlags & AsuraTextureFlag_ReduceExtraForConsoles)
	{	
		//Drop an extra mip map level if possible. RAR 24/03/2006
		if((uImageToUse+1)<m_uNumberOfImages) uImageToUse++;
	}

	if(iTextureFlags & AsuraTextureFlag_HalfForPSP)
	{	
		//Drop an extra mip map level if possible. RAR 15/05/2007
		if((uImageToUse+1)<m_uNumberOfImages) uImageToUse++;
	}

	//Reduce mip levels if the final size is larger than the maximum specified in the conversion data.
	//This could be combined in with uMaxSize above , but I'm doing it as a separate pass at the end for
	//consistency with all the other texture conversion processes.
	//RAR 26/11/2007
	if(xConvertData.m_uMaximumTextureSize)
	{
		for (u_int u=uImageToUse; u<m_uNumberOfImages; u++)
		{
			if (m_axImage[u].m_uWidth <= xConvertData.m_uMaximumTextureSize)
			{
				uImageToUse = u;
				break;
			}
		}
	}



	u_char ucMaxMipMaps = ((iTextureFlags & AsuraTextureFlag_NoMipMaps) == AsuraTextureFlag_NoMipMaps)? 0 : static_cast<u_char>(xConvertData.m_uMaxMipDrops);
	// The test for too small mipmaps is now width in bytes instead of size in bytes
	// KOR - 07/12/2005 - This is someone else's evil code which needs to be broken up. I've stuck in some line breaks to at least make it a little more readable.
	for (	  m_xHeader.m_ucNumberOfMipmaps = 0;
			 (m_xHeader.m_ucNumberOfMipmaps < ucMaxMipMaps) &&
			((m_xHeader.m_ucNumberOfMipmaps + uImageToUse + 1) < m_uNumberOfImages) &&
			(!m_axImage[m_xHeader.m_ucNumberOfMipmaps + uImageToUse + 1].IsTooSmall(eTextureFormat));
			m_xHeader.m_ucNumberOfMipmaps++)
	{
	}

	HANDLE xFile = CreateFile(szOutputFilename,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0);

	if(xFile == INVALID_HANDLE_VALUE) return false;

	m_xHeader.m_usWidth = static_cast<u_short>(m_axImage[uImageToUse].m_uWidth);
	m_xHeader.m_usHeight = static_cast<u_short>(m_axImage[uImageToUse].m_uHeight);
	
	// write out the header
	WRITE_DATA( (&m_xHeader), sizeof(m_xHeader) );

	for (u_int u=0; u<=m_xHeader.m_ucNumberOfMipmaps; u++)
	{
		// write out image data
		WRITE_DATA( (m_axImage[uImageToUse+u].m_puPixelData), (m_axImage[uImageToUse+u].m_uImageSizeInBytes) );
	}

	// write out the palette if there is one
	if (m_puPalette)
	{
		WRITE_DATA( m_puPalette, m_uSizeOfPaletteInBytes );
	}

	CloseHandle(xFile);

	return true;
	
}


void Tools_PSP_Texture_Writer::GetImages()
{
	// KJFL - 2004-02-17 12:44:38 - we'll be using the Nvidia dds/mipmap creation library
#if 0 // old nvidia lib
	{
		tPixel xColour;
		xColour.u = 0; // make a black colour

		//fill in the options for the mip map generation thingy
		CompressionOptions xOptions;
		xOptions.MipMapsInImage = false;
		xOptions.MipMapType = dGenerateMipMaps;
		xOptions.BinaryAlpha = false;// (AlphaType == ALPHA_1BIT); // clamp alpha zero or one
		xOptions.NormalMap = false;      // only renormalize MIP maps
		xOptions.AlphaBorder = false;    // make an alpha border
		xOptions.Border = false;         // make a colour border
		xOptions.BorderColor = xColour;  // colour of border
		xOptions.Fade = false;           // fade to colour over MIP maps
		xOptions.FadeToColor = xColour;  // colour to fade to
		xOptions.FadeToMipMaps = 0;      // number of MIPs to fade over
		xOptions.Dither = true;        // enable dithering during 16 bit conversion
		xOptions.TextureType = dTextureType2D;    // regular decal, cube or volume  
		xOptions.TextureFormat = (short) d8888;
		//  dDXT1, dDXT1a, dDXT3, dDXT5, d4444, 
		//  d1555, 	d565,	d8888, 	d888, 	d555, 

		// now call the nvidia dds/mip map creation function
		// KJFL - 2004-02-17 14:53:47 - the Nvidia library has no idea of const, but doesn't change the original data
		u_char* pucPixelData = reinterpret_cast<u_char*>(const_cast<void*>(pPixels));

		//HRESULT hres = 
		nvDXTcompress
		(
			pucPixelData,//raw_data
			uWidth,
			uHeight,
			uPitch,
			&xOptions,//compression options
			4,//depth
			PSPMipMapCallback//mip map generation callback
		);
	}
#else
	{
		nvCompressionOptions xOptions;
		xOptions.SetDefaultOptions();
		xOptions.bDitherColor = true;
		xOptions.textureFormat = k8888;
		xOptions.user_data = this;

		u_int uPitch = m_axImage[0].m_uWidth*4;

		#if defined ASURA_ASSERT
		NV_ERROR_CODE eError =
		#endif
			Asura_PC_CompressDXT(
				reinterpret_cast<const u_char*>(m_axImage[0].m_puPixelData),//raw_data
				m_axImage[0].m_uWidth,
				m_axImage[0].m_uHeight,
				uPitch,
				nvBGRA,	// pixel order
				&xOptions,//compression options
				PSPMipMapCallback,	// mip callback
				NULL);	// subrect (use defaults).

		Asura_Assertf(eError == NV_OK, ("Error in Asura_PC_CompressDXT: %s", Asura_PC_CompressDXT_GetErrorString(eError)));
	}
#endif
}

bool Tools_PSP_Texture_Writer::GeneratePalette( const enum ASURA_PSP_TEXTUREFORMAT eTextureFormat, const enum ASURA_PSP_CLUTFORMAT eCLUTFormat,const struct TextureConversionData& xConvertData )
{
	u_int uNumberOfPaletteEntries;

	switch (eTextureFormat)
	{
		case ASURA_PSP_TEXTUREFORMAT_IDX8:
		{	
			uNumberOfPaletteEntries = 256;
			break;
		}
		
		case ASURA_PSP_TEXTUREFORMAT_IDX4:
		{	
			uNumberOfPaletteEntries = 16;
			break;
		}

		default: // format not supported or is invalid
		{
			return false;
		}
	}

	// currently only using 8888 CLUT, so check for that before we start allocating memory etc
	if (eCLUTFormat != ASURA_PSP_CLUTFORMAT_32BIT_8888)
	{
		return false;
	}

	// KJFL - 2004-04-14 - allocate space for the palette
	// The 16bit palette modes still seem to take up the same amount of space as the 32bit, btw
	
	m_puPalette = new u_int[uNumberOfPaletteEntries];
	m_uSizeOfPaletteInBytes = uNumberOfPaletteEntries*sizeof(u_int);
	memset( m_puPalette, m_uSizeOfPaletteInBytes, 1 );

	//fill in the image data to be passed
	PaletteGenerationImage axPGImageData[MAXIMUM_NUMBER_OF_IMAGES];
	
	for(u_int u=0; u<m_uNumberOfImages; u++)
	{
		axPGImageData[u].Width = m_axImage[u].m_uWidth;
		axPGImageData[u].Height = m_axImage[u].m_uHeight;
		axPGImageData[u].PixelData = m_axImage[u].m_puPixelData;
	}

	//fill in the rest of the paramaters
	PaletteGenerationParam xPGParam;

	xPGParam.NumImages	= m_uNumberOfImages;
	xPGParam.Images		= axPGImageData;

	if(xConvertData.m_bUseFixedPalette)
	{
		xPGParam.m_bUseExistingPalette = true;
		
		//fill in the fixed palette
		const u_int* puPalette;
		u_int uPaletteSize;
		TextureConversionDataBase::GetFixedPalette(uPaletteSize,puPalette);
		Asura_Assert(uPaletteSize,"");
		Asura_Assert(uPaletteSize <= uNumberOfPaletteEntries,"");

		
		Asura_Mem::Copy(m_puPalette,puPalette,sizeof(u_int)*uPaletteSize);

	}

	xPGParam.PaletteSize = uNumberOfPaletteEntries;
	xPGParam.Palette	 = reinterpret_cast<int*>(m_puPalette);
	
	switch (eCLUTFormat)
	{
		default:
		case ASURA_PSP_CLUTFORMAT_32BIT_8888 :
		{
			xPGParam.Precision = PGPrecision_32;
			break;
		}
	}

	// TODO alpha support
	xPGParam.Alpha = PGAlpha_8;
	xPGParam.AlphaUsedAsNormalAlpha = false;

	xPGParam.m_bDisableDithering = xConvertData.m_bDisableDithering;



	bool bPalletised = false;

	// (SB - 28/03/07) If there is a palette generation override hook try it first
	if (s_pfnPaletteGenerationOverride != NULL)
	{
		bPalletised = s_pfnPaletteGenerationOverride(&xPGParam);
	}
	// (SB - 28/03/07) If no hook or hook returns false then use palette generation from DLL
	if (!bPalletised)
	{
		//get the function from the dll
		PALETTE_GENERATION_PTR PaletteGeneration = GetPaletteGenerationFunctionFromDllForPSP();
		
		if(!PaletteGeneration)
		{
#ifdef ASURA_MAIN
			Tools_Level_Export::AddMessage(Tools_Level_Export::MT_ERROR,"Failed to find palette generation function");
#else
			Asura_Assert(false, "Failed to find palette generation function");
#endif
			return false;
		}
	
		// and call it
		PaletteGeneration(&xPGParam);
	}

	if(xConvertData.m_bWritePaletteAsRedScaleIndices)
	{
		if (NULL == s_pfnPaletteAdjustment)
		{
			//Palette gets obliterated with this option , and replaced by index values in the red components
			//RAR 26/10/2006
			for(u_int i=0;i<uNumberOfPaletteEntries;i++)
			{
				//m_puPalette[i] = i<<16;
				m_puPalette[i] = 0xffffff00 | i;
			}
		}
		else
		{
			s_pfnPaletteAdjustment(uNumberOfPaletteEntries, m_puPalette);
		}
	}
	else
	{
		//Input format is ARGB but PSP wants ABGR , so convert the palette now
		//RAR 19/03/2007
		for(u_int i=0;i<uNumberOfPaletteEntries;i++)
		{
			u_int uA = (m_puPalette[i] >> 24) & 0xff;
			u_int uR = (m_puPalette[i] >> 16) & 0xff;
			u_int uG = (m_puPalette[i] >> 8) & 0xff;
			u_int uB = (m_puPalette[i] >> 0) & 0xff;

			m_puPalette[i] = (uA<<24) | (uB<<16) | (uG<<8) | uR;

		}

	}

	return true;
}
void Tools_PSP_Texture_Writer::ConvertPixelData( const enum ASURA_PSP_TEXTUREFORMAT eTextureFormat )
{
	for( u_int u=0; u<m_uNumberOfImages; u++)
	{
		Tools_PSP_ImageData& xImage = m_axImage[u];

		switch (eTextureFormat )
		{
			default:
			{
				// my work here is done...
				break;
			}

			case ASURA_PSP_TEXTUREFORMAT_IDX8:
			{
				// KJFL - 2004-04-14 16:54:46 - 
				// image data has already been palettised, but needs to be packed down into chars
				// since we'll be packing down into a smaller size, we can modify the image as we go
				// without worrying about overwriting stuff. (No need to allocate another buffer.)

				u_int* puSource = xImage.m_puPixelData;
				u_char* pcDest = reinterpret_cast<u_char*>(puSource);
				u_char* pcFinalDest = pcDest;
				u_int uNumberOfPixels = xImage.m_uWidth * xImage.m_uHeight;

				for (u_int v=0; v<uNumberOfPixels; v++)
				{
					*pcDest++ = static_cast<u_char>(*puSource++);
				}
				
				// mark the image with it's new size
				xImage.m_uImageSizeInBytes = uNumberOfPixels;

				SwizzleTexture( pcFinalDest, xImage.m_uWidth, xImage.m_uHeight );

				break;
			}

			case ASURA_PSP_TEXTUREFORMAT_IDX4:
			{
				u_int* puSource = xImage.m_puPixelData;
				u_char* pcDest = reinterpret_cast<u_char*>(puSource);
				u_char* pcFinalDest = pcDest;
				const u_int uNumberOfPixelsOver2 = (xImage.m_uWidth * xImage.m_uHeight) >> 1;

				for (u_int v = 0; v < uNumberOfPixelsOver2; ++v)
				{
					const u_char uPixel0 = static_cast<u_char>(*puSource++);
					const u_char uPixel1 = static_cast<u_char>(*puSource++);
					Asura_Assert(uPixel0 < 16 && uPixel1 < 16, "Indexed colour out of range");
					*pcDest++ = (uPixel1 << 4) | (uPixel0);
				}

				// mark the image with it's new size
				xImage.m_uImageSizeInBytes = uNumberOfPixelsOver2;

				static u_char aucBuffer[1024*1024]; // AJG - 16/09/04 - Unfortunately we do need an extra buffer for this bit.
				SwizzleBitmap(aucBuffer, pcFinalDest, static_cast<int>(xImage.m_uWidth), static_cast<int>(xImage.m_uHeight), ASURA_PSP_TEXTUREFORMAT_IDX4);
				memcpy(pcFinalDest, aucBuffer, xImage.m_uImageSizeInBytes);

				break;
			}

			case ASURA_PSP_TEXTUREFORMAT_8888:
			{
				u_int* puSource = xImage.m_puPixelData;

				//Input format is ARGB but PSP wants ABGR , so convert the image data now. Could try doing it in
				//the swizzling process , but this way is less likely to result in mistakes.
				//RAR 19/03/2007
				u_int uNumPixels = xImage.m_uWidth* xImage.m_uHeight;
				for(u_int i=0;i<uNumPixels;i++)
				{
					u_int uA = (puSource[i] >> 24) & 0xff;
					u_int uR = (puSource[i] >> 16) & 0xff;
					u_int uG = (puSource[i] >> 8) & 0xff;
					u_int uB = (puSource[i] >> 0) & 0xff;

					puSource[i] = (uA<<24) | (uB<<16) | (uG<<8) | uR;
				}
				

				u_char* pcDest = reinterpret_cast<u_char*>(puSource);

				SwizzleTexture( pcDest, xImage.m_uWidth * 4, xImage.m_uHeight );

				break;
			}
		}
	}
}
