////////////////////////////////////////////////////////////////////////////
//
// WriteTexturesGCN
//
// Export the textures for the GameCube
//
////////////////////////////////////////////////////////////////////////////


#include "ExportTexturesPch.h"
#include "ProcessTup.h"
#include "s3_intrf.h"

#define GREEN_HIGH_MASK 0x07
#define GREEN_LOW_MASK	0xC0
#define BLUE_MASK		0x3E
#define ALPHA_MASK		0x01
#define GREEN_565_MASK  0x03

// Texture Block Writer
class TBlock
{
public:
	TupArray<u8> m_data;
	void WriteBlock(FILE* pDBLFile);
	void WriteBlock32Bit(FILE* pDBLFile);
};

void TBlock::WriteBlock(FILE* pDBLFile)
{
	int i;
	for(i=0;i<m_data.GetSize();i++)
	{
		DBLWrite::WriteRecord((const char *)&m_data[i],pDBLFile);
	}
}

void TBlock::WriteBlock32Bit(FILE* pDBLFile)
{
	int i;

	// For 32bit RGBA write out AR Block
	for(i=0;i<m_data.GetSize();i+=4)
	{
		DBLWrite::WriteRecord((const char *)&m_data[i],pDBLFile);
		DBLWrite::WriteRecord((const char *)&m_data[i+1],pDBLFile);
	}

	for(i=2;i<m_data.GetSize();i+=4)
	{
		DBLWrite::WriteRecord((const char *)&m_data[i],pDBLFile);
		DBLWrite::WriteRecord((const char *)&m_data[i+1],pDBLFile);
	}
}

void ProcessTup::WriteGCNTexture(TextureEntry &textureEntry,TupArray<TupArray<ts_TextureChunkRecord> > &textureChunkRecordArray,int frameNum,int mapIndex,bool bAnimation)
{
	ts_TextureChunkRecord &textureChunkRecord = textureChunkRecordArray[frameNum][mapIndex];

	FILE *pDBLFile = DBLUtil::GetDBLFile();

	// get image data from image manager
	ImageManager &imageManager = m_dataManager.GetImageManager();
	TupperwareAggregate *pImageDataAgg = imageManager.GetImageData(textureEntry.m_filePathName);
	assert(pImageDataAgg);
	// get the first frame from the image data
	TupImageData tupImageData(pImageDataAgg);
	int numFrames = tupImageData.GetNumImageDataFrames();
	assert(numFrames>0);

	// if this image needs to point to another
	TupperwareScalar *pRemapImageReferenceScalar = textureEntry.m_pTextureAgg->FindScalarByKey("ExportTextures_RemapImageRef");
	if (pRemapImageReferenceScalar)
	{
		int parentImageReference = pRemapImageReferenceScalar->GetAsInt();
		ts_TextureChunkRecord &parentTextureChunkRecord = textureChunkRecordArray[parentImageReference][mapIndex];
		// use the same date as my parent
		textureChunkRecord.s16TextureWidth = parentTextureChunkRecord.s16TextureWidth;
		textureChunkRecord.s16TextureHeight = parentTextureChunkRecord.s16TextureHeight;
		textureChunkRecord.u32TextureOffset = parentTextureChunkRecord.u32TextureOffset;
		textureChunkRecord.u32PaletteOffset = parentTextureChunkRecord.u32PaletteOffset;
		return;
	}


	// if this is not when we save animations and it is one then get out
	if (!bAnimation && numFrames>1)
	{
		// if this is animated texture and were are dealing with the entry in the main texture set
		// we need to set some flags based on the textures within the image 
		// the frames get saved out later
		TupperwareAggregate *pImageDataFrameAgg = tupImageData.GetImageDataFrame(0);
		assert(pImageDataFrameAgg);
		TupImageDataFrame tupImageDataFrame(pImageDataFrameAgg);
		int formatFlags;
		tupImageDataFrame.GetFormatFlags(formatFlags);
		switch(formatFlags&TupImageDataFrame::FORMAT_MASK)
		{
		case TupImageDataFrame::RGBA_8888:
			textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
			break;
		case TupImageDataFrame::INDEXED_8:
		case TupImageDataFrame::INDEXED_4:
			int numAlphasUsed = textureEntry.m_pTextureAgg->FindScalarByKey("ExportTextures_PaletteAlphasUsed")->GetAsInt();
			if (numAlphasUsed>2)
				textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
			break;
		}
		return;
	}

	// which frame to get from the image (depends on if we are doing animation or not)
	int animationFrameNum = bAnimation ? frameNum : 0;

	TupperwareAggregate *pImageDataFrameAgg = tupImageData.GetImageDataFrame(animationFrameNum);
	assert(pImageDataFrameAgg);
	TupImageDataFrame tupImageDataFrame(pImageDataFrameAgg);
	int formatFlags;
	tupImageDataFrame.GetFormatFlags(formatFlags);
	// get first map from frame
	int numMaps = tupImageDataFrame.GetNumImageDataMaps();
	assert(numMaps>0);
	TupperwareAggregate *pImageDataMap = tupImageDataFrame.GetImageDataMap(mapIndex);
	assert(pImageDataMap);
	TupImageDataMap tupImageDataMap(pImageDataMap);
	// get the bitmap data
	int width;
	tupImageDataMap.GetWidth(width);
	int height;
	tupImageDataMap.GetHeight(height);

	int imageSize;
	u8 *pImageData;

	TBlock ***ppBlock;
	TBlock ***ppAlphaBlock;

	int nBlockWidth, nBlockHeight, nTotalBlockSize;  // in blocks of 32 Bytes
	int nAlphaBlockWidth, nAlphaBlockHeight, nAlphaTotalBlockSize;  // in blocks of 32 Bytes

	bool bCompressed = false;
	bool bSeparateAlpha = false;

	switch(formatFlags&TupImageDataFrame::FORMAT_MASK)
	{
	case TupImageDataFrame::RGB_888:
	case TupImageDataFrame::RGBA_8888:
		nBlockWidth = width/4;
		nBlockHeight = height/4;
		if(width%4!=0)
			nBlockWidth++;
		if(height%4!=0)
			nBlockHeight++;
		nTotalBlockSize = 64;

		nAlphaBlockWidth = width/8;
		nAlphaBlockHeight = height/4;
		if(width%8!=0)
			nBlockWidth++;
		if(height%4!=0)
			nBlockHeight++;
		nAlphaTotalBlockSize = 32;
		break;
	case TupImageDataFrame::RGB_555:
	case TupImageDataFrame::RGB_565:
	case TupImageDataFrame::RGBA_1555:
		nBlockWidth = width/4;
		nBlockHeight = height/4;
		if(width%4!=0)
			nBlockWidth++;
		if(height%4!=0)
			nBlockHeight++;
		nTotalBlockSize = 32;
		nAlphaBlockWidth = nBlockWidth;
		nAlphaBlockHeight = nBlockHeight;
		break;
	case TupImageDataFrame::INDEXED_8:
		nBlockWidth = width/8;
		nBlockHeight = height/4;
		if(width%8!=0)
			nBlockWidth++;
		if(height%4!=0)
			nBlockHeight++;
		nTotalBlockSize = 32;
		nAlphaBlockWidth = nBlockWidth;
		nAlphaBlockHeight = nBlockHeight;
		break;
	case TupImageDataFrame::INDEXED_4:
		nBlockWidth = width/8;
		nBlockHeight = height/8;
		if(width%8!=0)
			nBlockWidth++;
		if(height%8!=0)
			nBlockHeight++;
		nTotalBlockSize = 32;
		nAlphaBlockWidth = nBlockWidth;
		nAlphaBlockHeight = nBlockHeight;
		break;
	default:
		assert(false);
	}
	int nLineSize = width<<2;
	u8 padChar = 0xff;
	int x,y;

	// Create 2D Block Pointer Array
	try
	{
		ppBlock = new TBlock**[nBlockWidth];
		for(x=0;x<nBlockWidth;x++)
		{
			ppBlock[x] = new TBlock*[nBlockHeight];
		}
		for(x=0;x<nBlockWidth;x++)
		{
			for(y=0;y<nBlockHeight;y++)
			{
				ppBlock[x][y] = new TBlock;
			}
		}
		ppAlphaBlock = new TBlock**[nAlphaBlockWidth];
		for( x = 0; x < nAlphaBlockWidth; x++)
		{
			ppAlphaBlock[x] = new TBlock*[nAlphaBlockHeight];
		}
		for( x = 0; x < nAlphaBlockWidth; x++)
		{
			for( y = 0; y < nAlphaBlockHeight; y++)
			{
				ppAlphaBlock[x][y] = new TBlock;
			}
		}
	}
	catch(...)
	{
		throw(PluginException(ERROR_FATAL,"Creating GCN Texture Block Array width=%d,height=%d",nBlockWidth,nBlockHeight));
	}

	switch(formatFlags&TupImageDataFrame::FORMAT_MASK)
	{
	case TupImageDataFrame::RGBA_8888:
		{
			if(1) // nSourceDataType != SOURCE_DATA_TEXTURE_LIST)
			{
				imageSize = width*height*4;
				assert(imageSize);
				int actualSize;
				tupImageDataMap.GetData((void**)&pImageData,actualSize);
				assert(imageSize==actualSize);

				textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
				bSeparateAlpha = true;

				unsigned int	nBytesEncode = S3TC_GetEncodeSize( width, height, S3TC_ENCODE_RGB_FULL | S3TC_ENCODE_ALPHA_NONE);

				u8 *pEncodedImage;

				S3_TEXTURE s3texIn,s3texOut;

				bCompressed = true;
				nTotalBlockSize = 8;
				memset( &s3texIn, 0, sizeof(S3_TEXTURE));
				s3texIn.lWidth = width;
				s3texIn.lHeight = height;
				s3texIn.lPitch = width * sizeof(ts_bRGBA);
				s3texIn.pSurface = pImageData;
				s3texIn.PixelFormat.nARGBBitCount = 32;
				s3texIn.PixelFormat.nRedMask = 0xFF << 0;
				s3texIn.PixelFormat.nGreenMask = 0xFF << 8;
				s3texIn.PixelFormat.nBlueMask = 0xFF << 16;

				pEncodedImage = new u8[nBytesEncode];

				int row,col;
				u8 lo,hi;
				int pixelOffset = 0;

				S3TC_Encode( &s3texIn, &s3texOut, pEncodedImage, S3TC_ENCODE_RGB_FULL | S3TC_ENCODE_ALPHA_NONE, NULL, 0, 0);

				for( row = 0; row < nBlockHeight; row++)
				{
					for( col = 0; col < nBlockWidth; col++)
					{
						//output color0
						lo = pEncodedImage[pixelOffset++];
						hi = pEncodedImage[pixelOffset++];
						ppBlock[col][row]->m_data.Add(hi);
						ppBlock[col][row]->m_data.Add(lo);

						//output color1
						lo = pEncodedImage[pixelOffset++];
						hi = pEncodedImage[pixelOffset++];
						ppBlock[col][row]->m_data.Add(hi);
						ppBlock[col][row]->m_data.Add(lo);

						//output 4 bytes for indices, swap the order of the 4 2-bit indices
						for( int i = 0; i < 4; i++)
						{
							lo = pEncodedImage[pixelOffset++];
							hi =	( ( lo & 0x03) << 6 ) |
								( ( lo & 0x0C) << 2 ) |
								( ( lo & 0x30) >> 2 ) |
								( ( lo & 0xC0) >> 6 );
							ppBlock[col][row]->m_data.Add(hi);
						}
					}
				}
				delete pEncodedImage;

				pixelOffset = 0;
				int blockY,blockX;
				for(y=0;y<height;y++)
				{
					blockY = y>>2;
					for(x=0;x<width;x++)
					{
						blockX = x>>3;
						hi = pImageData[pixelOffset + 3];
						pixelOffset += 4;
						ppAlphaBlock[blockX][blockY]->m_data.Add(hi);
					}
					for(x=0;x<(nAlphaBlockWidth * 8-nLineSize);x++)
					{
						ppAlphaBlock[blockX][blockY]->m_data.Add(padChar);
					}
				}
			}
			else // do the 32 bits per pixel texture (here in case we need it back)
			{
				imageSize = width*height*4;
				assert(imageSize);
				int actualSize;
				tupImageDataMap.GetData((void**)&pImageData,actualSize);
				assert(imageSize==actualSize);
				int blockY,blockX;
				u8 r,g,b,a;
				int pixelOffset = 0;
				for (y=0;y<height;y++)
				{
					blockY = y>>2;
					for (x=0;x<width;x++)
					{
						blockX = x>>2;
						r = pImageData[pixelOffset++];
						g = pImageData[pixelOffset++];
						b = pImageData[pixelOffset++];
						a = pImageData[pixelOffset++];
						ppBlock[blockX][blockY]->m_data.Add(a);
						ppBlock[blockX][blockY]->m_data.Add(r);
						ppBlock[blockX][blockY]->m_data.Add(g);
						ppBlock[blockX][blockY]->m_data.Add(b);
					}
					for (x=0;x<(nBlockWidth*8-nLineSize);x++)
					{
						ppBlock[blockX][blockY]->m_data.Add(padChar);
					}
				}
			}
		}
		break;
	case TupImageDataFrame::RGB_888:
		{
			if(1) // nSourceDataType != SOURCE_DATA_TEXTURE_LIST)
			{
				imageSize = width*height*3;
				assert(imageSize);
				int actualSize;
				tupImageDataMap.GetData((void**)&pImageData,actualSize);
				assert(imageSize==actualSize);

				unsigned int	nBytesEncode = S3TC_GetEncodeSize( width, height, S3TC_ENCODE_RGB_FULL | S3TC_ENCODE_ALPHA_NONE);

				u8 *pEncodedImage;

				S3_TEXTURE s3texIn,s3texOut;

				bCompressed = true;
				nTotalBlockSize = 8;
				memset( &s3texIn, 0, sizeof(S3_TEXTURE));
				s3texIn.lWidth = width;
				s3texIn.lHeight = height;
				s3texIn.lPitch = width * sizeof(ts_bRGB);
				s3texIn.pSurface = pImageData;
				s3texIn.PixelFormat.nARGBBitCount = 24;
				s3texIn.PixelFormat.nRedMask = 0xFF << 0;
				s3texIn.PixelFormat.nGreenMask = 0xFF << 8;
				s3texIn.PixelFormat.nBlueMask = 0xFF << 16;

				pEncodedImage = new u8[nBytesEncode];

				int row,col;
				u8 lo,hi;
				int pixelOffset = 0;

				S3TC_Encode( &s3texIn, &s3texOut, pEncodedImage, S3TC_ENCODE_RGB_FULL | S3TC_ENCODE_ALPHA_NONE, NULL, 0, 0);

				for( row = 0; row < nBlockHeight; row++)
				{
					for( col = 0; col < nBlockWidth; col++)
					{
						//output color0
						lo = pEncodedImage[pixelOffset++];
						hi = pEncodedImage[pixelOffset++];
						ppBlock[col][row]->m_data.Add(hi);
						ppBlock[col][row]->m_data.Add(lo);

						//output color1
						lo = pEncodedImage[pixelOffset++];
						hi = pEncodedImage[pixelOffset++];
						ppBlock[col][row]->m_data.Add(hi);
						ppBlock[col][row]->m_data.Add(lo);

						//output 4 bytes for indices, swap the order of the 4 2-bit indices
						for( int i = 0; i < 4; i++)
						{
							lo = pEncodedImage[pixelOffset++];
							hi =	( ( lo & 0x03) << 6 ) |
								( ( lo & 0x0C) << 2 ) |
								( ( lo & 0x30) >> 2 ) |
								( ( lo & 0xC0) >> 6 );
							ppBlock[col][row]->m_data.Add(hi);
						}
					}
				}
				delete pEncodedImage;
			}
			else // do the 24 bits per pixel texture (here in case we need it back)
			{
				imageSize = width*height*3;
				assert(imageSize);
				int actualSize;
				tupImageDataMap.GetData((void**)&pImageData,actualSize);
				assert(imageSize==actualSize);
				int blockY,blockX;
				u8 r,g,b;
				int pixelOffset = 0;
				for (y=0;y<height;y++)
				{
					blockY = y>>2;
					for (x=0;x<width;x++)
					{
						blockX = x>>2;
						r = pImageData[pixelOffset++];
						g = pImageData[pixelOffset++];
						b = pImageData[pixelOffset++];
						ppBlock[blockX][blockY]->m_data.Add(0xff);
						ppBlock[blockX][blockY]->m_data.Add(r);
						ppBlock[blockX][blockY]->m_data.Add(g);
						ppBlock[blockX][blockY]->m_data.Add(b);
					}
					for (x=0;x<(nBlockWidth*8-nLineSize);x++)
					{
						ppBlock[blockX][blockY]->m_data.Add(padChar);
					}
				}
			}
		}
		break;
	case TupImageDataFrame::RGB_555:
	case TupImageDataFrame::RGB_565:
		{
			imageSize = width*height*2;
			assert(imageSize);
			int actualSize;
			tupImageDataMap.GetData((void**)&pImageData,actualSize);
			assert(imageSize==actualSize);
			unsigned int	nBytesEncode = S3TC_GetEncodeSize( width, height, S3TC_ENCODE_RGB_FULL | S3TC_ENCODE_ALPHA_NONE);

			u8	*pEncodedImage;

			S3_TEXTURE s3texIn, s3texOut;

			bCompressed = true;
			nTotalBlockSize = 8;
			memset( &s3texIn, 0, sizeof(S3_TEXTURE));
			s3texIn.lWidth = width;
			s3texIn.lHeight = height;
			s3texIn.lPitch = width * sizeof(u16);
			s3texIn.pSurface = pImageData;
			s3texIn.PixelFormat.nARGBBitCount = 16;
			if ((formatFlags&TupImageDataFrame::FORMAT_MASK) == TupImageDataFrame::RGB_555)
			{
				s3texIn.PixelFormat.nRedMask = 0x1F << 10;
				s3texIn.PixelFormat.nGreenMask = 0x1F << 5;
				s3texIn.PixelFormat.nBlueMask = 0x1F << 0;
			}
			else
			{
				s3texIn.PixelFormat.nRedMask = 0x1F << 11;
				s3texIn.PixelFormat.nGreenMask = 0x3F << 5;
				s3texIn.PixelFormat.nBlueMask = 0x1F << 0;
			}
			pEncodedImage = new u8[nBytesEncode];
			int row,col;
			u8 lo,hi;
			int pixelOffset = 0;

			S3TC_Encode( &s3texIn, &s3texOut, pEncodedImage, S3TC_ENCODE_RGB_FULL | S3TC_ENCODE_ALPHA_NONE, NULL, 0, 0);

			for( row = 0; row < nBlockHeight; row++)
			{
				for( col = 0; col < nBlockWidth; col++)
				{
					//output color0
					lo = pEncodedImage[pixelOffset++];
					hi = pEncodedImage[pixelOffset++];
					ppBlock[col][row]->m_data.Add(hi);
					ppBlock[col][row]->m_data.Add(lo);

					//output color1
					lo = pEncodedImage[pixelOffset++];
					hi = pEncodedImage[pixelOffset++];
					ppBlock[col][row]->m_data.Add(hi);
					ppBlock[col][row]->m_data.Add(lo);

					//output 4 bytes for indices, swap the order of the 4 2-bit indices
					for( int i = 0; i < 4; i++)
					{
						lo = pEncodedImage[pixelOffset++];
						hi =	( ( lo & 0x03) << 6 ) |
							( ( lo & 0x0C) << 2 ) |
							( ( lo & 0x30) >> 2 ) |
							( ( lo & 0xC0) >> 6 );
						ppBlock[col][row]->m_data.Add(hi);
					}
				}
			}
			delete pEncodedImage;
		}
		break;
	case TupImageDataFrame::RGBA_1555:
		{
			imageSize = width*height*2;
			assert(imageSize);
			int actualSize;
			tupImageDataMap.GetData((void**)&pImageData,actualSize);
			assert(imageSize==actualSize);
			unsigned int	nBytesEncode = S3TC_GetEncodeSize( width, height, S3TC_ENCODE_RGB_ALPHA_COMPARE);
			S3TC_SetAlphaReference( 255);
			
			u8	*pEncodedImage;
			
			S3_TEXTURE s3texIn, s3texOut;
			
			bCompressed = true;
			nTotalBlockSize = 8;
			memset( &s3texIn, 0, sizeof(S3_TEXTURE));
			s3texIn.lWidth = width;
			s3texIn.lHeight = height;
			s3texIn.lPitch = width * sizeof(u16);
			s3texIn.pSurface = pImageData;
			s3texIn.PixelFormat.nARGBBitCount = 16;
			s3texIn.PixelFormat.nAlphaMask = 0x01 << 15;
			s3texIn.PixelFormat.nRedMask = 0x1F << 10;
			s3texIn.PixelFormat.nGreenMask = 0x1F << 5;
			s3texIn.PixelFormat.nBlueMask = 0x1F << 0;
			s3texIn.PixelFormat.nFlags = S3_TF_HASALPHA;
			pEncodedImage = new u8[nBytesEncode];
			int row,col;
			u8 lo,hi;
			int pixelOffset = 0;
			
			S3TC_Encode( &s3texIn, &s3texOut, pEncodedImage, S3TC_ENCODE_RGB_ALPHA_COMPARE, NULL, 0, 0);
			
			for( row = 0; row < nBlockHeight; row++)
			{
				for( col = 0; col < nBlockWidth; col++)
				{
					//output color0
					lo = pEncodedImage[pixelOffset++];
					hi = pEncodedImage[pixelOffset++];
					ppBlock[col][row]->m_data.Add(hi);
					ppBlock[col][row]->m_data.Add(lo);
					
					//output color1
					lo = pEncodedImage[pixelOffset++];
					hi = pEncodedImage[pixelOffset++];
					ppBlock[col][row]->m_data.Add(hi);
					ppBlock[col][row]->m_data.Add(lo);
					
					//output 4 bytes for indices, swap the order of the 4 2-bit indices
					for( int i = 0; i < 4; i++)
					{
						lo = pEncodedImage[pixelOffset++];
						hi =	( ( lo & 0x03) << 6 ) |
							( ( lo & 0x0C) << 2 ) |
							( ( lo & 0x30) >> 2 ) |
							( ( lo & 0xC0) >> 6 );
						ppBlock[col][row]->m_data.Add(hi);
					}
				}
			}
			delete pEncodedImage;
		}
		break;
	case TupImageDataFrame::INDEXED_8:
		{
			imageSize = width*height;
			assert(imageSize);
			int actualSize;
			tupImageDataMap.GetData((void**)&pImageData,actualSize);
			assert(imageSize==actualSize);
			DBLUtil::AlignChunkData32Byte();
			u8 hi, lo;
			// save out palette
			bool bSavePalette = true;

			if (mapIndex!=0) // if we are a mip map
			{
				bSavePalette = false; // we are using a parent palette
				// use the same offset as my first map
				textureChunkRecord.u32PaletteOffset = textureChunkRecordArray[frameNum][0].u32PaletteOffset;
				assert(textureChunkRecord.u32PaletteOffset); // this better be set
				// set the separate alpha plane flag the same as my parent
				if (textureChunkRecordArray[frameNum][0].u32Flags & TEX_FLAG_SEPARATE_ALPHA_PLANE)
					textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
			}
			else // first map
			{
				// check if we are doing palette sharing
				if (bAnimation)
				{
					TupperwareScalar *pParentPaletteFrameScalar = pImageDataFrameAgg->FindScalarByKey("ExportTextures_ParentPaletteFrame");
					if (pParentPaletteFrameScalar)
					{
						bSavePalette = false; // we are using a parent palette
						int parentPaletteFrame = pParentPaletteFrameScalar->GetAsInt();
						assert(parentPaletteFrame<frameNum); // this better be before me
						// use the same offset as my parent
						textureChunkRecord.u32PaletteOffset = textureChunkRecordArray[parentPaletteFrame][0].u32PaletteOffset;
						assert(textureChunkRecord.u32PaletteOffset); // this better be set
						// set the separate alpha plane flag the same as my parent
						if (textureChunkRecordArray[parentPaletteFrame][0].u32Flags & TEX_FLAG_SEPARATE_ALPHA_PLANE)
							textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
					}
				}
				else
				{
					TupperwareScalar *pParentPaletteReferenceScalar = textureEntry.m_pTextureAgg->FindScalarByKey("ExportTextures_ParentPaletteReference");
					if (pParentPaletteReferenceScalar)
					{
						bSavePalette = false; // we are using a parent palette
						int parentPaletteReference = pParentPaletteReferenceScalar->GetAsInt();
						assert(parentPaletteReference!=-1);
						assert(parentPaletteReference<frameNum); // this better be before me
						// use the same offset as my parent
						textureChunkRecord.u32PaletteOffset = textureChunkRecordArray[parentPaletteReference][0].u32PaletteOffset;
						assert(textureChunkRecord.u32PaletteOffset); // this better be set
						// set the separate alpha plane flag the same as my parent
						if (textureChunkRecordArray[parentPaletteReference][0].u32Flags & TEX_FLAG_SEPARATE_ALPHA_PLANE)
							textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
					}
				}
			}
			if (bSavePalette)
			{
				textureChunkRecord.u32PaletteOffset = ftell(pDBLFile) - DBLUtil::GetChunkDataStart();
				TupperwareAggregate *pImageDataPaletteAgg = tupImageDataFrame.GetImageDataPalette();
				assert(pImageDataPaletteAgg);
				TupImageDataPalette tupImageDataPalette(pImageDataPaletteAgg);
				ts_bRGBA *pPaletteData;
				int numBytes;
				tupImageDataPalette.GetData((void **)&pPaletteData,numBytes);
				assert(pPaletteData);
				assert(numBytes==256*4);
				int numAlphasUsed = textureEntry.m_pTextureAgg->FindScalarByKey("ExportTextures_PaletteAlphasUsed")->GetAsInt();

				if (numAlphasUsed>2)
				{
					textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
					//Write Out 16-bit GameCube Palette entries	565
					int i;
					for(i=0;i<256;i++)
					{
						u16 u16Pixel = ((pPaletteData[i].R >> 3) << 11) | ((pPaletteData[i].G >> 2) << 5) | ((pPaletteData[i].B >> 3) << 0);
						hi = (u8) (u16Pixel >> 8);
						lo = (u8) u16Pixel;
						DBLWrite::WriteRecord( &hi, pDBLFile);
						DBLWrite::WriteRecord( &lo, pDBLFile);
					}
					DBLUtil::AlignChunkData32Byte();
					for(i=0;i<256;i++)
					{
						u16 u16Pixel = ((pPaletteData[i].R >> 3) << 11) | ((pPaletteData[i].G >> 2) << 5) | ((pPaletteData[i].B >> 3) << 0);
						hi = pPaletteData[i].A;
						lo = 0xff;
						DBLWrite::WriteRecord( &hi, pDBLFile);
						DBLWrite::WriteRecord( &lo, pDBLFile);
					}
				}
				else
				{
					//Write Out 16-bit GameCube Palette entries	5551
					for(int i=0;i<256;i++)
					{
						u16 u16Pixel = ((pPaletteData[i].R >> 3) << 10) | ((pPaletteData[i].G >> 3) << 5) | ((pPaletteData[i].B >> 3) << 0);
						if( pPaletteData[i].A == 0)
						{
							u16Pixel = 0;
						}
						else
						{
							u16Pixel |= 0x8000;
						}
						hi = (u8) (u16Pixel >> 8);
						lo = (u8) u16Pixel;
						DBLWrite::WriteRecord( &hi, pDBLFile);
						DBLWrite::WriteRecord( &lo, pDBLFile);
					}
				}
				DBLUtil::AlignChunkData32Byte();
			}

			int blockY,blockX;
			int pixelOffset = 0;
			for (y=0;y<height;y++)
			{
				blockY = y>>2;
				for (x=0;x<width;x++)
				{
					blockX = x>>3;
					hi = pImageData[pixelOffset++];
					ppBlock[blockX][blockY]->m_data.Add(hi);
				}
				for (x=0;x<(nBlockWidth*8-nLineSize);x++)
				{
					ppBlock[blockX][blockY]->m_data.Add(padChar);
				}
			}
		}
		break;
	case TupImageDataFrame::INDEXED_4:
		{
			imageSize = (width>>1)*height;
			assert(imageSize);
			int actualSize;
			tupImageDataMap.GetData((void**)&pImageData,actualSize);
			assert(imageSize==actualSize);
			DBLUtil::AlignChunkData32Byte();
			u8 hi, lo;
			// save out palette
			bool bSavePalette = true;

			if (mapIndex!=0) // if we are a mip map
			{
				bSavePalette = false; // we are using a parent palette
				// use the same offset as my first map
				textureChunkRecord.u32PaletteOffset = textureChunkRecordArray[frameNum][0].u32PaletteOffset;
				assert(textureChunkRecord.u32PaletteOffset); // this better be set
				// set the separate alpha plane flag the same as my parent
				if (textureChunkRecordArray[frameNum][0].u32Flags & TEX_FLAG_SEPARATE_ALPHA_PLANE)
					textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
			}
			else // first frame
			{
				// check if we are doing palette sharing
				if (bAnimation)
				{
					TupperwareScalar *pParentPaletteFrameScalar = pImageDataFrameAgg->FindScalarByKey("ExportTextures_ParentPaletteFrame");
					if (pParentPaletteFrameScalar)
					{
						bSavePalette = false; // we are using a parent palette
						int parentPaletteFrame = pParentPaletteFrameScalar->GetAsInt();
						assert(parentPaletteFrame<frameNum); // this better be before me
						// use the same offset as my parent
						textureChunkRecord.u32PaletteOffset = textureChunkRecordArray[parentPaletteFrame][0].u32PaletteOffset;
						assert(textureChunkRecord.u32PaletteOffset); // this better be set
						// set the separate alpha plane flag the same as my parent
						if (textureChunkRecordArray[parentPaletteFrame][0].u32Flags & TEX_FLAG_SEPARATE_ALPHA_PLANE)
							textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
					}
				}
				else
				{
					TupperwareScalar *pParentPaletteReferenceScalar = textureEntry.m_pTextureAgg->FindScalarByKey("ExportTextures_ParentPaletteReference");
					if (pParentPaletteReferenceScalar)
					{
						bSavePalette = false; // we are using a parent palette
						int parentPaletteReference = pParentPaletteReferenceScalar->GetAsInt();
						assert(parentPaletteReference!=-1);
						assert(parentPaletteReference<frameNum); // this better be before me
						// use the same offset as my parent
						textureChunkRecord.u32PaletteOffset = textureChunkRecordArray[parentPaletteReference][0].u32PaletteOffset;
						assert(textureChunkRecord.u32PaletteOffset); // this better be set
						// set the separate alpha plane flag the same as my parent
						if (textureChunkRecordArray[parentPaletteReference][0].u32Flags & TEX_FLAG_SEPARATE_ALPHA_PLANE)
							textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
					}
				}
			}
			if (bSavePalette)
			{
				textureChunkRecord.u32PaletteOffset = ftell(pDBLFile) - DBLUtil::GetChunkDataStart();
				TupperwareAggregate *pImageDataPaletteAgg = tupImageDataFrame.GetImageDataPalette();
				assert(pImageDataPaletteAgg);
				TupImageDataPalette tupImageDataPalette(pImageDataPaletteAgg);
				ts_bRGBA *pPaletteData;
				int numBytes;
				tupImageDataPalette.GetData((void **)&pPaletteData,numBytes);
				assert(pPaletteData);
				assert(numBytes==16*4);
				int numAlphasUsed = textureEntry.m_pTextureAgg->FindScalarByKey("ExportTextures_PaletteAlphasUsed")->GetAsInt();

				if (numAlphasUsed>2)
				{
					textureChunkRecord.u32Flags |= TEX_FLAG_SEPARATE_ALPHA_PLANE;
					//Write Out 16-bit GameCube Palette entries	565
					int i;
					for(i=0;i<16;i++)
					{
						u16 u16Pixel = ((pPaletteData[i].R >> 3) << 11) | ((pPaletteData[i].G >> 2) << 5) | ((pPaletteData[i].B >> 3) << 0);
						hi = (u8) (u16Pixel >> 8);
						lo = (u8) u16Pixel;
						DBLWrite::WriteRecord( &hi, pDBLFile);
						DBLWrite::WriteRecord( &lo, pDBLFile);
					}
					DBLUtil::AlignChunkData32Byte();
					for(i=0;i<16;i++)
					{
						u16 u16Pixel = ((pPaletteData[i].R >> 3) << 11) | ((pPaletteData[i].G >> 2) << 5) | ((pPaletteData[i].B >> 3) << 0);
						hi = pPaletteData[i].A;
						lo = 0xff;
						DBLWrite::WriteRecord( &hi, pDBLFile);
						DBLWrite::WriteRecord( &lo, pDBLFile);
					}
				}
				else
				{
					//Write Out 16-bit GameCube Palette entries	5551
					for(int i=0;i<16;i++)
					{
						u16 u16Pixel = ((pPaletteData[i].R >> 3) << 10) | ((pPaletteData[i].G >> 3) << 5) | ((pPaletteData[i].B >> 3) << 0);
						if( pPaletteData[i].A == 0)
						{
							u16Pixel = 0;
						}
						else
						{
							u16Pixel |= 0x8000;
						}
						hi = (u8) (u16Pixel >> 8);
						lo = (u8) u16Pixel;
						DBLWrite::WriteRecord( &hi, pDBLFile);
						DBLWrite::WriteRecord( &lo, pDBLFile);
					}
				}
				DBLUtil::AlignChunkData32Byte();
			}

			int blockY,blockX;
			int pixelOffset = 0;
			for (y=0;y<height;y++)
			{
				blockY = y>>3;
				for (x=0;x<width;x+=2)
				{
					blockX = x>>3;
					hi = pImageData[pixelOffset++];
					ppBlock[blockX][blockY]->m_data.Add(hi);
				}
				for (x=0;x<(nBlockWidth*8-nLineSize);x++)
				{
					ppBlock[blockX][blockY]->m_data.Add(padChar);
				}
			}
		}
		break;
	default:
		assert(false);
	}

	// fill in the rest of the blocks with the padChar value
	for(x=0;x<nBlockWidth;x++)
	{
		for(y=0;y<nBlockHeight;y++)
		{
			int nSize = ppBlock[x][y]->m_data.GetSize();
			if(nSize<nTotalBlockSize)
			{
				for(int p=0;p<(nTotalBlockSize-nSize);p++)
				{
					ppBlock[x][y]->m_data.Add(padChar);
				}
			}
		}
	}

	if(bSeparateAlpha)
	{
		for(x=0;x<nAlphaBlockWidth;x++)
		{
			for(y=0;y<nAlphaBlockHeight;y++)
			{
				int nSize = ppAlphaBlock[x][y]->m_data.GetSize();
				if(nSize<nAlphaTotalBlockSize)
				{
					for(int p=0;p<(nAlphaTotalBlockSize-nSize);p++)
					{
						ppAlphaBlock[x][y]->m_data.Add(padChar);
					}
				}
			}
		}
	}

	DBLUtil::AlignChunkData32Byte();
	textureChunkRecord.u32TextureOffset = ftell(pDBLFile) - DBLUtil::GetChunkDataStart();

	// Write the Data from each Block out to file
	if(bCompressed)
	{
		bool		bAddPadCol = ( nBlockWidth & 1) == 1,
			bAddPadRow = ( nBlockHeight & 1) == 1;

		nBlockWidth &= ~1;
		nBlockHeight &= ~1;

		int row,col;

		for( row = 0; row < nBlockHeight; row += 2)
		{
			for( col = 0; col < nBlockWidth; col += 2)
			{
				ppBlock[col][row]->WriteBlock(pDBLFile);
				ppBlock[col + 1][row]->WriteBlock(pDBLFile);
				ppBlock[col][row + 1]->WriteBlock(pDBLFile);
				ppBlock[col + 1][row + 1]->WriteBlock(pDBLFile);
			}
			if( bAddPadCol)
			{
				ppBlock[col][row]->WriteBlock(pDBLFile);
				ppBlock[col][row]->WriteBlock(pDBLFile);
				ppBlock[col][row + 1]->WriteBlock(pDBLFile);
				ppBlock[col][row + 1]->WriteBlock(pDBLFile);
			}
		}
		if( bAddPadRow)
		{
			for( col = 0; col < nBlockWidth; col += 2)
			{
				ppBlock[col][row]->WriteBlock(pDBLFile);
				ppBlock[col + 1][row]->WriteBlock(pDBLFile);
				ppBlock[col][row]->WriteBlock(pDBLFile);
				ppBlock[col + 1][row]->WriteBlock(pDBLFile);
			}
			if( bAddPadCol)
			{
				ppBlock[col][row]->WriteBlock(pDBLFile);
				ppBlock[col][row]->WriteBlock(pDBLFile);
				ppBlock[col][row]->WriteBlock(pDBLFile);
				ppBlock[col][row]->WriteBlock(pDBLFile);
			}
		}
	}
	else
	{
		switch(formatFlags&TupImageDataFrame::FORMAT_MASK)
		{
		case TupImageDataFrame::RGBA_8888:
		case TupImageDataFrame::RGB_888:
			{
				for(y=0;y<nBlockHeight;y++)
				{
					for(x=0;x<nBlockWidth;x++)
					{
						ppBlock[x][y]->WriteBlock32Bit(pDBLFile);
					}
				}
			}
			break;
		case TupImageDataFrame::RGBA_1555:
		case TupImageDataFrame::INDEXED_8:
		case TupImageDataFrame::INDEXED_4:
			{
				for(y=0;y<nBlockHeight;y++)
				{
					for(x=0;x<nBlockWidth;x++)
					{
						ppBlock[x][y]->WriteBlock(pDBLFile);
					}
				}
			}
			break;
		default:
			assert(false);
		}
	}

	if(bSeparateAlpha)
	{
		for(y=0;y<nAlphaBlockHeight;y++)
		{
			for(x=0;x<nAlphaBlockWidth;x++)
			{
				ppAlphaBlock[x][y]->WriteBlock(pDBLFile);
			}
		}
	}

	//Cleanup Blocks
	for(x=0;x<nBlockWidth;x++)
	{
		for(y=0;y<nBlockHeight;y++)
		{
			delete ppBlock[x][y];
		}
	}
	// CleanUp 2D Block Pointer Array
	for(x=0;x<nBlockWidth;x++)
	{
		delete[] ppBlock[x];
	}
	delete[] ppBlock;

	//Cleanup Alpha Blocks
	for(x=0;x<nAlphaBlockWidth;x++)
	{
		for(y=0;y<nAlphaBlockHeight;y++)
		{
			delete ppAlphaBlock[x][y];
		}
	}
	// CleanUp Alpha 2D Block Pointer Array
	for(x=0;x<nAlphaBlockWidth;x++)
	{
		delete[] ppAlphaBlock[x];
	}
	delete[] ppAlphaBlock;
}


void ProcessTup::SetGCNTextureRecord(TextureEntry &textureEntry,TupArray<TupArray<ts_TextureChunkRecord> > &textureChunkRecordArray,int frameNum,int mapIndex,bool bAnimation)
{
	ts_TextureChunkRecord &textureChunkRecord = textureChunkRecordArray[frameNum][mapIndex];

	FILE *pDBLFile = DBLUtil::GetDBLFile();

	TupperwareScalar *pRemapImageReferenceScalar = textureEntry.m_pTextureAgg->FindScalarByKey("ExportTextures_RemapImageRef");
	if (pRemapImageReferenceScalar)
	{
		int parentImageReference = pRemapImageReferenceScalar->GetAsInt();
		ts_TextureChunkRecord &parentTextureChunkRecord = textureChunkRecordArray[parentImageReference][mapIndex];
		// use the same date as my parent
		textureChunkRecord.s16TextureWidth = parentTextureChunkRecord.s16TextureWidth;
		textureChunkRecord.s16TextureHeight = parentTextureChunkRecord.s16TextureHeight;
		textureChunkRecord.u32Flags = parentTextureChunkRecord.u32Flags;

		// copy name for easier debugging in game engine 
		// copy right part of name
		int startingNameIndex = strlen(textureEntry.m_filePathName)-(TEXTURE_NAME_LENGTH-1);
		if (startingNameIndex<0)
			startingNameIndex=0;

		strncpy(textureChunkRecord.acTextureName, &((const char *)textureEntry.m_filePathName)[startingNameIndex], TEXTURE_NAME_LENGTH);

		textureChunkRecord.acTextureName[TEXTURE_NAME_LENGTH - 1] = 0;	// make sure it's null-terminated
		textureChunkRecord.u16MipmapDistance = 0;

		return;
	}


	// get image data from image manager
	ImageManager &imageManager = m_dataManager.GetImageManager();
	TupperwareAggregate *pImageDataAgg = imageManager.GetImageData(textureEntry.m_filePathName);
	assert(pImageDataAgg);
	// get the first frame from the image data
	TupImageData tupImageData(pImageDataAgg);
	int numFrames = tupImageData.GetNumImageDataFrames();
	assert(numFrames>0);
	// mark texture as animated if using more than 1 frame and not part of an animated texture set)
	if (!bAnimation && numFrames>1)
		textureChunkRecord.u32Flags |= TEX_FLAG_ANIMATED_TEXTURE;

	// set flag if uv is reversed
	TupperwareScalar *pRotatedImageScalar = textureEntry.m_pTextureAgg->FindScalarByKey("ExportTextures_ImageReversed");
	if (pRotatedImageScalar)
	{
		textureChunkRecord.u32Flags |= TEX_FLAG_UVREVERSED;
	}

	TupperwareAggregate *pImageDataFrame = tupImageData.GetImageDataFrame(0);
	assert(pImageDataFrame);
	TupImageDataFrame tupImageDataFrame(pImageDataFrame);
	int formatFlags;
	tupImageDataFrame.GetFormatFlags(formatFlags);
	// get first map from frame
	int numMaps = tupImageDataFrame.GetNumImageDataMaps();
	assert(numMaps>0);
	TupperwareAggregate *pImageDataMap = tupImageDataFrame.GetImageDataMap(mapIndex);
	assert(pImageDataMap);
	TupImageDataMap tupImageDataMap(pImageDataMap);
	// get the bitmap data
	int width;
	tupImageDataMap.GetWidth(width);
	int height;
	tupImageDataMap.GetHeight(height);

	textureChunkRecord.s16TextureWidth = (s16) width;
	textureChunkRecord.s16TextureHeight = (s16) height;

	switch(formatFlags&TupImageDataFrame::FORMAT_MASK)
	{
	case TupImageDataFrame::RGBA_8888:
		textureChunkRecord.u32Flags = TEX_FLAG_SIZE_32_BIT | TEX_FLAG_FMT_RGBA;
		break;
	case TupImageDataFrame::RGB_888:
		textureChunkRecord.u32Flags = TEX_FLAG_SIZE_32_BIT | TEX_FLAG_FMT_RGBA;
		break;
	case TupImageDataFrame::RGB_555:
	case TupImageDataFrame::RGB_565:
	case TupImageDataFrame::RGBA_1555:
		textureChunkRecord.u32Flags = TEX_FLAG_SIZE_S3TC;
		break;
	case TupImageDataFrame::INDEXED_8:
		textureChunkRecord.u32Flags = TEX_FLAG_SIZE_8_BIT | TEX_FLAG_FMT_CI;
		break;
	case TupImageDataFrame::INDEXED_4:
		textureChunkRecord.u32Flags = TEX_FLAG_SIZE_4_BIT | TEX_FLAG_FMT_CI;
		break;
	default:
		assert(false);
	}

	// mark diffuse textures 
	TupperwareScalar *pDiffuseMapScalar = textureEntry.m_pTextureAgg->FindScalarByKey("ExportTextures_DiffuseMap");
	if (pDiffuseMapScalar && pDiffuseMapScalar->GetAsInt() == TRUE)
		textureChunkRecord.u32Flags |= TEX_FLAG_DIFFUSE;

	// copy name for easier debugging in game engine 
	// copy right part of name
	int startingNameIndex = strlen(textureEntry.m_filePathName)-(TEXTURE_NAME_LENGTH-1);
	if (startingNameIndex<0)
		startingNameIndex=0;

	strncpy(textureChunkRecord.acTextureName, &((const char *)(textureEntry.m_filePathName))[startingNameIndex], TEXTURE_NAME_LENGTH);

	/* set mipmap switch distance */
	textureChunkRecord.u16MipmapDistance = 0;
}

