   
#include "glover.h"






#define MAXTEXBANKS	32

#define TMAPPRINTF
//#define TMAPPRINTF	debugPrintf

static TextureBankType *texBank[MAXTEXBANKS];

#define NLASTSPR	1 
#define NEIGHTBIT	2
#define NSPLIT		4
#define NBITSTREAM	8
#define NALPHA		16


//ULONG fogpalette=NO;
//ULONG fogpaletteno;
UBYTE	VRAMblock[VRAM_PAGES][VRAM_PAGEW*VRAM_PAGEH];
UBYTE	VRAMpalBlock[VRAM_PALETTES];
int		VRAMpalHandle[VRAM_PALETTES/32];

USHORT	VRAMpalCLUT[VRAM_PALETTES];
USHORT	VRAMpalette[VRAM_PALETTES][16];

ULONG	VRAMpalette256chk[VRAM_256PALETTES];
ULONG	VRAMpalette256ret[VRAM_256PALETTES];
USHORT	VRAMpalette256ctr[VRAM_256PALETTES];

//#ifdef CLUT_FOGGING_CODE
//USHORT  VRAMfogPalette[FOGLEVELS*VRAM_FOGPALETTES];
//#endif

void textureInit256ClutSpace(void);

/*****************************************************************************************/
void textureVRAMprint()
{
	int		page, x,y;
	UBYTE	*bPtr;

	bPtr = (UBYTE *)VRAMblock;
	for(page=0; page<VRAM_PAGES; page++)
	{
		debugPrintf("PAGE %d:\n  ", page);
		for(y=0; y<VRAM_PAGEH; y++)
		{
			for(x=0; x<VRAM_PAGEW; x++)
				debugPrintf("%02x|", *bPtr++);
			debugPrintf("\n  ");
		}
	}
}

// Displays a list of rectangles within Vram who's reference counter is != 0
// (Relies on the ref-counter never getting to top-bit-set)
//  Note that rectangles may not actually match allocations, since this will see
// adjacent rectangles as being single ones.

// (uses DB rather than TMAPPRINTF, coz you should only be calling this while testing stuff anyway)
void textureVRAMcounter()
{
	int		page, x,y;
	UBYTE	*bPtr;
	int		sum = 0;
	int xx,yy;
	int high;
	UBYTE b;
	UBYTE rb;
	int n_rects;
	int area;

	bPtr = (UBYTE *)VRAMblock;

	DB(" === Map of Vram Usage ===\n");

	n_rects = 0;
	area = 0;

	for(page=0; page<VRAM_PAGES; page++)
	{
		for(y=0; y<VRAM_PAGEH; y++)
		{
			for(x=0; x<VRAM_PAGEW; x++)
			{
				b = VRAMblock[page][VRAM_SETXY(x,y)];
				if(b != 0)
				{
					if(!(b & 0x80))
					{
						rb = b;
						for(xx = x; xx < VRAM_PAGEW; xx++)
						{
							b = VRAMblock[page][VRAM_SETXY(xx,y)];
							if( b != rb )
								break;
						}
						high = xx;

						for(yy = y; yy < VRAM_PAGEH; yy++)
						{
							for(xx = x; xx < high; xx++)
							{
								b = VRAMblock[page][VRAM_SETXY(xx,yy)];

								if( b != rb )
									break;
							}
							if(xx != high)
								break;

							for(xx = x; xx < high; xx++)
							{
								VRAMblock[page][VRAM_SETXY(xx,yy)] |= 0x80;
							}
						}
						DB("  Occupied rectangle %dx%d, from page %d @ %d,%d\n", high-x,yy-y,page,x,y);
						n_rects++;
						area += (high-x) * (yy - y);
					}
				}
			}
		}
	}
	DB("  =%d rectangles, area covered = %d\n",n_rects,area);

	DB(" =========================\n");


	bPtr = (UBYTE *)VRAMblock;
	for(page=0; page<VRAM_PAGES; page++)
	{
		for(y=0; y<VRAM_PAGEH; y++)
		{
			for(x=0; x<VRAM_PAGEW; x++)
			{
				*bPtr &= 0x7f;
				bPtr++;
			}
		}
	}
}

/*****************************************************************************************/
int textureVRAMalloc(short w, short h)
{
	int		page, x,y, xx,yy;
	UBYTE	found;

	for(page=0; page<VRAM_PAGES; page++)
	{
		for(y=0; y<VRAM_PAGEH-h; y++)
		{
			for(x=0; x<VRAM_PAGEW-w; x++)
			{
				if (VRAMblock[page][VRAM_SETXY(x,y)]==0)
				{
					found = 0;
					for(yy=y; yy<y+h; yy++)
					{
						for(xx=x; xx<x+w; xx++)
						{
							found |= (VRAMblock[page][VRAM_SETXY(xx,yy)]);
						}
					}
					if (!found)
					{
						for(yy=y; yy<y+h; yy++)
						{
							for(xx=x; xx<x+w; xx++)
							{
								VRAMblock[page][VRAM_SETXY(xx,yy)] = 1;
							}
						}
						TMAPPRINTF("Allocated %dx%d, from page %d @ %d,%d\n", w,h,page,x,y);
						return (VRAM_SETPAGE(page)|VRAM_SETXY(x,y));
					}
				}
			}
		}
	}
	debugPrintf("VRAM alloc FAILED!\n");
	return NULL;
}
/*****************************************************************************************/
void textureVRAMfree(int handle, short w, short h)
{
	int		xx,yy;
	int		page, x,y;

	page = VRAM_GETPAGE(handle);
	x = VRAM_GETX(handle);
	y = VRAM_GETY(handle);
	TMAPPRINTF("Freed VRAM [0x%x] %dx%d, from page %d @ %d,%d\n", handle,w,h,page,x,y);
	for(yy=y; yy<y+h; yy++)
	{
		for(xx=x; xx<x+w; xx++)
		{
			VRAMblock[page][VRAM_SETXY(xx,yy)] = 0;
		}
	}
}
/*****************************************************************************************/
void textureVRAMinit()
{
	int		page;
	RECT	rect;
	UBYTE	r,g,b;

	memset((char *)VRAMblock, 0, sizeof(VRAMblock));
	memset((char *)VRAMpalBlock, 0, sizeof(VRAMpalBlock));
	memset((char *)VRAMpalHandle, 0, sizeof(VRAMpalHandle));
	memset((char *)VRAMpalette, 0, sizeof(VRAMpalette));
	rect.x = rect.y = 0;
	rect.w = 640;
	rect.h = 512;
	ClearImage(&rect,0,0,0);
	for(page=0; page<VRAM_PAGES; page++)
	{
		if ((page+page/VRAM_PAGECOLS) & 1)
		{
			r = g = b = 64;
		}
		else
		{
			r = g = b = 100;
		}
		rect.x = VRAM_STARTX+(page%VRAM_PAGECOLS)*64;
		//rect.x = 640+(page%VRAM_PAGECOLS)*64;
		rect.y = (page/VRAM_PAGECOLS)*256;
		rect.w = 64;
		rect.h = 256;
	  	ClearImage(&rect,r,g,b);
		DrawSync(0);
	}

	textureInit256ClutSpace();
}

/**************************************************************************
	FUNCTION:	textureInitVRAM()
	PURPOSE:	Initialise VRAM/texture handling
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void textureInitVRAM()
{
	int	loop;

	for(loop=0; loop<MAXTEXBANKS; loop++)
		texBank[loop] = NULL;
	textureVRAMinit();
}
/*****************************************************************************************/

USHORT textureAddCLUT256(USHORT *palette)
{
	int		pal,col,hnd, r,g,b, i;
	RECT	rect;
	int		checkSum;
	int		found;

	TMAPPRINTF("Adding 8-bit CLUT ...\n");

	for(col=0; col<256; col++)								// Mask out magenta + mark transparencies
	{
		r = (palette[col]>>10) & 31;
		g = (palette[col]>>5) & 31;
		b = palette[col] & 31;

		if ((r==31) && (g==0) && (b==31))
			palette[col] = 0x0000;
		else
			palette[col] |= 0x8000;
	}

	checkSum=0;
	for(i=0; i<256; i++)
	{
		checkSum^=(palette[i]<<(i&15));
	}

	TMAPPRINTF("Checksum = %x\n", checkSum);

	for(found=0; found<VRAM_256PALETTES; found++)
	{
		TMAPPRINTF("Matching with %x\n", VRAMpalette256chk[found]);

		if(VRAMpalette256chk[found]==checkSum)
		{
			VRAMpalette256ctr[found]++;
			TMAPPRINTF("An identical palette was already there. Using no. %d\n", found);
			return(VRAMpalette256ret[found]);
		}

		if(VRAMpalette256chk[found]==0)
			break;
	}
	// CLUT wasn't found in the list

	if(found==VRAM_256PALETTES)
	{
		DB("Out of 8 bit palettes!\n");
		CRASH;
	}

	TMAPPRINTF("Storing palette as no, %d\n", found);

	rect.x = VRAM_STARTX;		// Copy up the palette
	rect.y = found;
	rect.w = 256;
	rect.h = 1;
	LoadImage(&rect, (ULONG *)palette);

	VRAMpalette256ret[found]=getClut(rect.x,rect.y);
	VRAMpalette256ctr[found]=1;
	VRAMpalette256chk[found]=checkSum;

	return(VRAMpalette256ret[found]);
}

USHORT textureAddCLUT16(USHORT *palette)
{
	int		pal,col,hnd, r,g,b, i;
	RECT	rect;

	for(col=0; col<16; col++)								// Mask out magenta + mark transparencies
	{
		r = (palette[col]>>10) & 31;
		g = (palette[col]>>5) & 31;
		b = palette[col] & 31;

		if ((r==31) && (g==0) && (b==31))
			palette[col] = 0x0000;
		else
			palette[col] |= 0x8000;
	}

	if(modelctrl.brown)
	{
		for(col=0; col<16; col++)								// Shade everything
		{
			r = (palette[col]>>10) & 31;
			g = (palette[col]>>5) & 31;
			b = palette[col] & 31;

			i=(r+g+b)/3;

			i=31;
			r=(50*i)>>8;
			g=(10*i)>>8;
			b=(5*i)>>8;

			palette[col]&=0x8000;
			palette[col]|=(b|(g<<5)|(r<<10));
		}
	}

   	for(pal=0; pal<VRAM_PALETTES; pal++)					// Search for matching palette
   	{
		if (VRAMpalBlock[pal])
		{
	  		for(col=0; col<16; col++)
	  		{
	  			if (palette[col]!=VRAMpalette[pal][col])
	  				break;
	  		}
	  		if (col==16)									// Found match
			{
// Fred addition to fix hopefully the last bug in this file... Refcount's decced in "remove", & should be incced here.
				VRAMpalBlock[pal]++;

				hnd = VRAMpalHandle[pal/32];
				//fogpaletteno=0x8000+pal;
				return getClut((VRAM_CALCVRAMX(hnd)+16*(pal & 3)),(VRAM_CALCVRAMY(hnd)+((pal/4) & 7)));
			}
		}
	}

	for(pal=0; pal<VRAM_PALETTES; pal++)					// Search for new palette slot
	{
		if(VRAMpalBlock[pal]==0)
		{
			VRAMpalBlock[pal] = 1;
			hnd = VRAMpalHandle[pal/32];
			if (((pal & 31)==0) && (hnd==0))				// Is new area needed?
			{
				hnd = VRAMpalHandle[pal/32] = textureVRAMalloc(31,1);
//				VRAMblock[VRAM_GETPAGE(hnd)][VRAM_GETXY(hnd)+31] = 1;// THIS IS NOT NEEDED IF VRAM BORDERS ARE OFF
//debugPrintf("Allocated %dx%d, from page %d @ %d,%d\n", w,h,page,x,y);

// ?
				TMAPPRINTF("New palette area needed - allocated 0x%x : vram = %dx%d, from page %d @ %d,%d\n",
					hnd, 31,1,VRAM_GETPAGE(hnd),VRAM_GETX(hnd),VRAM_GETY(hnd));
			}
			rect.x = VRAM_CALCVRAMX(hnd)+16*(pal & 3);		// Copy up the palette
			rect.y = VRAM_CALCVRAMY(hnd)+((pal/4) & 7);
  //			debugPrintf("Placing palette @ %d,%d\n", rect.x,rect.y);
			rect.w = 16;
			rect.h = 1;
			LoadImage(&rect, (ULONG *)palette);
			for(col=0; col<16; col++)						// Local copy
				VRAMpalette[pal][col] = palette[col];

			VRAMpalCLUT[pal] = getClut(rect.x,rect.y);
			return VRAMpalCLUT[pal];

			//return getClut(rect.x,rect.y);
		}
	}
	debugPrintf("*** WARNING: No more palettes\n");
	return NULL;
}

void texturePrintStatus()
{
	int pal;
	int used;
	used = 0;
   	for(pal=0; pal<VRAM_PALETTES; pal++)					// Search for matching palette
   	{
		if (VRAMpalBlock[pal])
		{
			used++;
		}
	}
	psxPrintf("TEXTURE: Palette16's used: %d/%d\n",used,VRAM_PALETTES);
}

/*****************************************************************************************/
/*					  
USHORT textureAddCLUT16fog(USHORT *palette)
{
	int		pal,col,hnd, r,g,b;
	RECT	rect;

	for(col=0; col<16; col++)								// Mask out magenta + mark transparencies
	{
		r = (palette[col]>>10) & 31;
		g = (palette[col]>>5) & 31;
		b = palette[col] & 31;
		if ((r>20) && (g<10) && (b>20))	palette[col] = 0x0000;
		else						   	palette[col] |= 0x8000;
	}

   	for(pal=0; pal<VRAM_PALETTES; pal++)					// Search for matching palette
   	{
		if ((VRAMpalBlock[pal]&128)&&(pal==fogpalette))
		{
	  		for(col=0; col<16; col++)
	  		{
	  			if (palette[col]!=VRAMpalette[pal][col])
	  				break;
	  		}
	  		if (col==16)									// Found match
			{
				//if ((fogpalette!=0)&&(VRAMpalBlock[pal]!=1))break; //found but not for fogging
				hnd = VRAMpalHandle[pal/32];
				//fogpaletteno=0x8000+pal;
				return getClut((VRAM_CALCVRAMX(hnd)+16*(pal & 3)),(VRAM_CALCVRAMY(hnd)+((pal/4) & 7)));
			}
		}
	}

	for(pal=0; pal<VRAM_PALETTES; pal++)					// Search for new palette slot
	{
		if (VRAMpalBlock[pal]==0)
		{
			VRAMpalBlock[pal] = 128;
			//if (fogpalette){VRAMpalBlock[pal]=2;}
			hnd = VRAMpalHandle[pal/32];
			
			if (((pal & 31)==0) && (hnd==0))				// Is new area needed?
			{
				hnd = VRAMpalHandle[pal/32] = textureVRAMalloc(31,1);
				VRAMblock[VRAM_GETPAGE(hnd)][VRAM_GETXY(hnd)+31] = 1;// THIS IS NOT NEEDED IF VRAM BORDERS ARE OFF
				debugPrintf("New palette area needed - allocated 0x%x\n", VRAMpalHandle[hnd]);
			}
			rect.x = VRAM_CALCVRAMX(hnd)+16*(pal & 3);		// Copy up the palette
			rect.y = VRAM_CALCVRAMY(hnd)+((pal/4) & 7);
  //			debugPrintf("Placing palette @ %d,%d\n", rect.x,rect.y);
			rect.w = 16;
			rect.h = 1;
			LoadImage(&rect, (ULONG *)palette);
			for(col=0; col<16; col++)						// Local copy
				VRAMpalette[pal][col] = palette[col];
			
			printf("cm=%d\n",pal);
			
			//if (fogpalette)fogpaletteno=pal;
			VRAMpalCLUT[pal] = getClut(rect.x,rect.y);
			return VRAMpalCLUT[pal];


			//return getClut(rect.x,rect.y);
		}
	}
	debugPrintf("*** WARNING: No more palettes\n");
	return NULL;
}*/

/****************************************************************************************/
/*
void textureFogPaletteOn(void)
{
	fogpalette=YES;
}

void textureFogPaletteOff(void)
{
	fogpalette=NO;
}
*/

/*ULONG AverageOfClut(USHORT *clut){
//THIS ROUTINE APPEARS TO CONTAIN A VERY WEIRD BUG!
ULONG rr,gg,bb;
ULONG j;
ULONG col;
ULONG c;
	rr=0;
	gg=0;
	bb=0; //randomInt(250);
	for (j=0;j<16;j++){
		c=clut[j];
		rr=rr+((c) & 0x1f);
		gg=gg+((c>>5) & 0x1f);
		bb=bb+((c>>10) & 0x1f);
	}
 	rr=rr>>4;
 	gg=gg>>4;
 	bb=bb>>4;
 	col=(rr &31);
 	col|=((gg &31)<<5);
 	col|=((bb &31)<<10);
	return (col & 0x7fff);
}
*/
 
/*****************************************************************************************/

// note - the fogged palettes are now dynamically allocated, with free space in the
// array being marked by the clut pointers within the table being &8000
/*
#ifdef CLUT_FOGGING_CODE
USHORT textureAddCLUT16Fogged(u_short *clut,NSPRITE *nspr)
{
	ULONG r,g,b,t;
	ULONG rr,gg,bb;
	ULONG j;
	USHORT newclut[16];
	ULONG orig,fog;
	USHORT clutptr[16];    
	RGB	FogCol={0,0,0};
	ULONG x,y,w,h;
	UBYTE *image=(UBYTE *)(((ULONG *)nspr->image)+1);
	//UBYTE bit;
	ULONG col;
//	static int nFogPalettes=0;
	int nFogPalettes;

	w = nspr->w;
	h = nspr->h;
	//bit=image;
 	col=AverageOfClutAccurate(nspr);

 	FogCol.r=((col) &0x1f)<<3;
 	FogCol.g=((col>>5) &0x1f)<<3;
 	FogCol.b=((col>>10) &0x1f)<<3;

	fogpalette=YES;
	fogpalette=0xffff;


	for(nFogPalettes = 0; nFogPalettes < FOGLEVELS * VRAM_FOGPALETTES; nFogPalettes += FOGLEVELS)
	{
		if(	VRAMfogPalette[nFogPalettes] == 0x8000)
			break;
	}
	if(nFogPalettes >= FOGLEVELS * VRAM_FOGPALETTES)
	{
		DB("ERROR: Out of fog palettes\n");
		CRASH;
	}

// ok, we arrive here with "nFogPalettes" being the index number of (foglevels) clut shorts which aren't in use ATM
    for(fog=0; fog<FOGLEVELS; fog++)
	{
		orig=FOGLEVELS-fog-1;
		//orig=FOGLEVELS-fog;
		for(j=0; j<16; j++)
		{
	 		r= (clut[j] & 0x1f)<<3;
	 		g= ((clut[j] >>5) & 0x1f)<<3;
	  		b= ((clut[j] >>10) & 0x1f)<<3;
		   	t=0x8000;

			r|=r>>5;
			g|=g>>5;
			b|=b>>5;

			if((r>=0xf8)&&(g==0)&&(b>=0xf8)) // magenta
			{
				rr=r;
				gg=g;
				bb=b;
			}
			else
			{
				//rr= ((r * orig) + (FogCol.r * fog)) /3;
		   	  	//gg= ((g * orig) + (FogCol.g * fog)) /3;
				//bb= ((b * orig) + (FogCol.b * fog)) /3;

	   		   	if (orig==0)
				{
	   	   			rr= FogCol.r;
			   	  	gg= FogCol.g;
			   	   	bb= FogCol.b;
				}
				else
				{
					if(orig==1)
					{	
		   	   			rr= (FogCol.r*3+r)/4;
			   	  		gg= (FogCol.g*3+g)/4;
			   	   		bb= (FogCol.b*3+b)/4;
					}
					else
					{
		   	   			rr= ((r * orig) + (FogCol.r * fog)) /3;
		   	  			gg= ((g * orig) + (FogCol.g * fog)) /3;
		   	   			bb= ((b * orig) + (FogCol.b * fog)) /3;
					}
				}
			}

			rr=(rr)>>3;
		   	gg=(gg)>>3;																		  
		   	bb=(bb)>>3;

		   	newclut[j]=t|(rr)|(gg<<5)|(bb<<10);
		}

   		VRAMfogPalette[nFogPalettes+fog]=textureAddCLUT16((USHORT *)newclut);
    }

	return(0x8000+nFogPalettes);


	nFogPalettes+=FOGLEVELS;
//	nFogPalettes+=4;
// There're about 100 for the hub, and 20...40 on lesser levels
//	debugPrintf("nFogPalettes is now %d\n",nFogPalettes);

	if(nFogPalettes>=(FOGLEVELS*VRAM_FOGPALETTES))
	{
		DB("ERROR: Out of fog palettes\n");
		CRASH;
	}

	return(0x8000+(nFogPalettes-FOGLEVELS));

}
#endif
*/
/******************************************************************************************/

// returns "1" if the clut's reference count caused it to be actually freed,
// "0" if it didn't. (mod by fred to allow freeing of fogged cluts...

int textureRemoveCLUT16(USHORT clut)
{
	int	pal, area, found = 0;

// Normal, old fashioned CLUT16 removal
	for(pal=0; pal<VRAM_PALETTES; pal++)
	{
		if ((VRAMpalBlock[pal]) && (clut==VRAMpalCLUT[pal]))
		{
			if((--VRAMpalBlock[pal])<=0)						// Reference count = 0, free palette
			{
				VRAMpalBlock[pal] = 0;
				VRAMpalCLUT[pal] = 0;
				TMAPPRINTF("Freed palette #%d\n", pal);
				area = pal/32;									// Check other palettes in area
				found = 0;
				for(pal=area*32; pal<(area+1)*32; pal++)
					found |= VRAMpalBlock[pal];
				if (!found)
				{
					//RECT rect;

					TMAPPRINTF("Freed palette area 0x%x\n", VRAMpalHandle[area]);

					// added by Chris 
					//rect.x = VRAM_CALCVRAMX(VRAMpalHandle[area])+16*(pal & 3);		// Copy up the palette
					//rect.y = VRAM_CALCVRAMY(VRAMpalHandle[area])+((pal/4) & 7);
					//rect.w = 16;
					//rect.h = 1;
					//ClearImage(&rect, 100,0,0);

					textureVRAMfree(VRAMpalHandle[area],32,1);	// Free empty palette area
					VRAMpalHandle[area] = 0;
				}
				return 1;
			}
		}
	}
	return 0;
}

int textureRemoveCLUT256(USHORT clut)
{
	int pal;

	TMAPPRINTF("Removing 8 bit palette ...\n");

	for(pal=0; pal<VRAM_256PALETTES; pal++)
	{
		if(VRAMpalette256ret[pal]==clut)
		{
			if((--VRAMpalette256ctr[pal])<=0)
			{
				TMAPPRINTF("Freeing it\n");
				VRAMpalette256ctr[pal]=0;
				VRAMpalette256ret[pal]=0;
				VRAMpalette256chk[pal]=0;

				return 1;
			}
		}
	}

	return 0;
}


/*****************************************************************************************/

int textureSetSPRPointers(NSPRITE *pHeader)
{
	int n = 0;

	while(1)
	{
		pHeader[n].pal = (USHORT *)((ULONG)pHeader+((ULONG)pHeader[n].pal));
		pHeader[n].image = (UBYTE *)((ULONG)pHeader+((ULONG)pHeader[n].image));
		if(pHeader[n].flags&NLASTSPR)
			return(++n);
		n++;
	}
}

/*****************************************************************************************/

/*
ULONG AverageOfClutAccurate(NSPRITE *nspr)
{
	ULONG rrr,ggg,bbb;
	ULONG col;

	ULONG rr,gg,bb;
	ULONG j;
	ULONG c;
	USHORT *clut=nspr->pal;
	USHORT	ww;	//,hh;
	ULONG x,y;
	UBYTE *image=(UBYTE *)(((ULONG*)nspr->image)+1);
	UBYTE byte;
	ULONG total=0;
	ULONG cols[16];
	ULONG tPix,div;

#ifndef FOGTOSKY

	for (j=0;j<16;j++)
		cols[j]=0;

//	ww = (nspr->w+7)/8;
//	hh = (nspr->h+7)/8;

	ww = (nspr->w+7) & 0xfff8;
	ww = (ww-nspr->w)/2;
	
	for (y=0;y<nspr->h;y++)
	{
		for (x=0;x<nspr->w;x++)
		{
			total++;
			if(x&1)
			{
				byte=*image;
				byte=byte>>4;
			}
			else
			{
				byte=*image;
				byte=byte &15;
				image++;
			}
			cols[byte]++;			
		}
		image+= ww;	//(ww-nspr->w)/2;
	}
 //	printf("w=%d,h=%d,total=%d,0[%d],1[%d],2[%d],3[%d],4[%d],5[%d],6[%d],7[%d],8[%d],9[%d],
 //		10[%d],11[%d],12[%d],13[%d],14[%d],15[%d]\n",nspr->w,nspr->h,total,cols[0],cols[1],
 //		cols[2],cols[3],cols[4],cols[5],cols[6],cols[7],cols[8],cols[9],cols[10],cols[11],cols[12],cols[13],cols[14],cols[15]);

	rrr=0;
	ggg=0;
	bbb=0;
	tPix=0;

	for (j=0;j<16;j++)
	{
		c=clut[j];
		if((c&0x7fff)!=0x7c1f) // magenta
		{
			rrr=rrr+((c) & 0x1f)*cols[j];
			ggg=ggg+((c>>5) & 0x1f)*cols[j];
			bbb=bbb+((c>>10) & 0x1f)*cols[j];
		}
		else
			tPix+=cols[j];
	}

	div=(nspr->w*nspr->h)-tPix;

 	rrr=(rrr)/div;
 	ggg=(ggg)/div;
 	bbb=(bbb)/div;

//	printf("RGB =%d,%d,%d, NEW RGB=%d,%d,%d\n", rr,gg,bb,rrr,ggg,bbb);
	
#else

	// Fog to sky colour bodge CPW
	rrr=clscol.r/8;
	ggg=clscol.g/8;
	bbb=clscol.b/8;
#endif

 	col=(rrr &31);
 	col|=((ggg &31)<<5);
 	col|=((bbb &31)<<10);

	return (col & 0x7fff);
}
*/
/*****************************************************************************************/

void textureCreate(SPRITEX *pSprX, char *pBitMap, short *pCLUT, int x, int y)
{
	NSPRITE NSpr;

	NSpr.pal=pCLUT;
	NSpr.w=x;
	NSpr.h=y;
	NSpr.u=-(x/2);
	NSpr.v=-(y/2);
	NSpr.flags=NLASTSPR;
	NSpr.image=(pBitMap-4);	// stinky bodge

	textureDownLoad(&NSpr, pSprX);
}

void textureDownLoad(NSPRITE *nspr,SPRITEX *sprx)
{
	RECT	rect;
	USHORT	ww,hh;
	int		handle;
	ULONG col;
	int clutType;

	if(nspr->flags & NEIGHTBIT)
		sprx->clut = textureAddCLUT256(nspr->pal);
	else
		sprx->clut = textureAddCLUT16(nspr->pal);

	ww = (nspr->w+7)/8;
	hh = (nspr->h+7)/8;

	if(nspr->flags & NEIGHTBIT)
		ww*=2;

	handle = textureVRAMalloc(ww,hh);  
	rect.x = VRAM_CALCVRAMX(handle);
	rect.y = VRAM_CALCVRAMY(handle);
	rect.w = (nspr->w+3)/4;
	rect.h = nspr->h;

	if(nspr->flags & NEIGHTBIT)
	{
		rect.w*=2;	
	}

	LoadImage(&rect,((ULONG*)nspr->image)+1); 

	if(nspr->flags & NEIGHTBIT)
		clutType=1;
	else
		clutType=0;

	if(modelctrl.specialFX==99)
	{
		sprx->tpage = (getTPage(clutType,0,rect.x,rect.y) | ((SEMITRANS_ADD-1)<<5) );
		PRINTF ("changed tex\n");
	}
	else
		sprx->tpage = getTPage(clutType,0,rect.x,rect.y);


	sprx->x = VRAM_GETX(handle)*8;

	if(nspr->flags & NEIGHTBIT)
		sprx->x=sprx->x/2;	

	sprx->y = rect.y;
	sprx->image = 0;

//	sprx->image=AverageOfClut(nspr->pal);
	
	//sprx->image=(UBYTE *)AverageOfClutAccurate(nspr);  //ANO is this needed??!!

//	((TMD_P_TG4A*)p)->avgTexCol.r=((col) &0x1f)<<3; //face->rgb[0].code;
//	((TMD_P_TG4A*)p)->avgTexCol.g=((col>>5) &0x1f)<<3; //face->rgb[1].code;
 //	((TMD_P_TG4A*)p)->avgTexCol.b=((col>>10) &0x1f)<<3; //face->rgb[2].code;
	
   
	sprx->pal = nspr->pal; //0;
	sprx->w = nspr->w;
	sprx->h = nspr->h;
	sprx->u = nspr->u;
	sprx->v = nspr->v;
 	sprx->flags = nspr->flags; //20
   	sprx->u0 = sprx->x;
   	sprx->v0 = sprx->y;
   	sprx->u1 = sprx->x+nspr->w-1;
   	sprx->v1 = sprx->y;
   	sprx->u2 = sprx->x;
   	sprx->v2 = sprx->y+nspr->h-1;
   	sprx->u3 = sprx->x+nspr->w-1;
   	sprx->v3 = sprx->y+nspr->h-1;
   	sprx->tpage1 = sprx->tpage;
   	sprx->clut1  = sprx->clut;
	sprx->handle = handle;
}


/**************************************************************************
	FUNCTION:	textureLoadSPT()
	PURPOSE:	Load texture bank file as single sprite
	PARAMETERS:	Filename
	RETURNS:	Sprite info ptr
**************************************************************************/

SPRITEX *textureLoadSPT(UBYTE *name)
{
	ULONG	j;
	ULONG	count;
	NSPRITE	*sprptr;
	SPRITEX	*spritextest;

//	sprptr = (NSPRITE *)LoadFile(name);
	sprptr = (NSPRITE *)fileTempLoad(name,0);
	count = textureSetSPRPointers(sprptr);
	spritextest = (SPRITEX *)MALLOC(sizeof(SPRITEX)*count, name);
	for(j=0; j<count; j++)
		textureDownLoad(&sprptr[j], &spritextest[j]);
	FREE(sprptr);

	return spritextest;
}


/**************************************************************************
	FUNCTION:	textureUnload()
	PURPOSE:	Unload texture from VRAM
	PARAMETERS:	Sprite info ptr
	RETURNS:	
**************************************************************************/

void textureUnload(SPRITEX *sprX)
{
	RECT	rect;
	int		page;

	page = VRAM_GETPAGE(sprX->handle);
	rect.x = VRAM_CALCVRAMX(sprX->handle);
	rect.y = VRAM_CALCVRAMY(sprX->handle);
	rect.w = 2*((sprX->w+7)/8);
	rect.h = 8*((sprX->h+7)/8);

	if(sprX->flags & NEIGHTBIT)
		rect.w*=2;

	if ((page+page/VRAM_PAGECOLS) & 1)
		ClearImage(&rect, 64,64,64);
	else
		ClearImage(&rect, 100,100,100);

	if(sprX->flags & NEIGHTBIT)
		textureVRAMfree(sprX->handle, (sprX->w+7)/4,(sprX->h+7)/8);
	else
		textureVRAMfree(sprX->handle, (sprX->w+7)/8,(sprX->h+7)/8);


// tbd - check (clut & 8000) and free fogged 4 if set

	if(sprX->flags & NEIGHTBIT)
	{
		textureRemoveCLUT256(sprX->clut);
	}
	else
	{
		textureRemoveCLUT16(sprX->clut);
	}

	DrawSync(0);
}


/**************************************************************************
	FUNCTION:	textureLoadBank()
	PURPOSE:	Load texture bank
	PARAMETERS:	Filename
	RETURNS:	Ptr to texture bank info
**************************************************************************/

TextureBankType *textureLoadBank(char *sFile)
{
	ULONG			nTextures;
	ULONG			b,y;
 	TextureBankType	*pTmpData;
	const int 		tex=0;// 24 is beach ball texture 99 is normal ball
	char			str[100];
	int p;

	pTmpData = MALLOC(sizeof(TextureBankType),"TextureBankInfo");	

	for(y=0; y<MAXTEXBANKS; y++)
	{
		if (texBank[y]==NULL)
		{
			texBank[y] = pTmpData;
			break;
		}
	}
	if(y==MAXTEXBANKS)
	{
		debugPrintf("**** WARNING: OUT OF TEXTURE BANKS\n");
		CRASH;
	}
	// load SPR file
//	pTmpData->pNSprite=(NSPRITE *)LoadFile(sFile);
	pTmpData->pNSprite=(NSPRITE *)fileTempLoad(sFile,0);

	// set up pointers
	nTextures=textureSetSPRPointers(pTmpData->pNSprite);
	sprintf(str, "C/%s", sFile);
	pTmpData->CRC = (ULONG *)MALLOC(nTextures*4, str);
	for(y=0; y<nTextures; y++)
		pTmpData->CRC[y] = pTmpData->pNSprite[y].crc;
	sprintf(str, "U/%s", sFile);
	//pTmpData->pUsed=(ULONG *)MALLOC((nTextures>>3)+4, str);

	// Clear out used textures array
	for(y=0; y<MAXBANKLONGS; y++)
		pTmpData->pUsed[y]=0;

	pTmpData->pSpriteX=(SPRITEX *)MALLOC(sizeof(SPRITEX)*nTextures, sFile);
	memset((char *)pTmpData->pSpriteX, 0, sizeof(SPRITEX)*nTextures);
	pTmpData->nTextures=nTextures;
 	b=1<<(tex&31); // bit position
	y=tex>>5;	// dword number
	pTmpData->pUsed[y]|=b;

 	// bang it into VRAM
	textureDownLoad((pTmpData->pNSprite)+tex, (pTmpData->pSpriteX)+tex);
 		
	return pTmpData;
}


/**************************************************************************
	FUNCTION:	textureDownloadFromBank()
	PURPOSE:	Upload/find given texture in bank
	PARAMETERS:	Texture bank info ptr, index of texture
	RETURNS:	Sprite info ptr
**************************************************************************/

SPRITEX *textureDownloadFromBank(TextureBankType *bank, int idx)
{
	LONG	b,y;
	NSPRITE	*pNSprite = bank->pNSprite;

	b = 1<<(idx & 31);
	y = idx>>5;
	if (!(bank->pUsed[y] & b))
	{
		if (pNSprite!=NULL)
		{
			bank->pUsed[y] |= b;
			textureDownLoad(pNSprite+idx, (bank->pSpriteX)+idx);
		}
		else
			return NULL;
	}
	return ((bank->pSpriteX)+idx);
}


/**************************************************************************
	FUNCTION:	textureFindInBank()
	PURPOSE:	Find given texture in bank
	PARAMETERS:	Texture bank info ptr, Name of texture
	RETURNS:	Sprite info ptr
**************************************************************************/

SPRITEX *textureFindInBank(TextureBankType *bank, char *name)
{
	ULONG	numTextures = bank->nTextures;
	ULONG	crc, loop;

	crc = str2CRC(name);
	for(loop=0; loop<numTextures; loop++)
	{
		if (bank->CRC[loop] == crc)
			return textureDownloadFromBank(bank, loop);
	}
	return NULL;
}

// NOTE: At the moment, this assumes all the frames are consecutive in the file
// They normally will be.
SPRITEX *textureFindFramesAndGetNumber(char *sStr, int *nFrames)
{
	int f;
	char sFName[16];
	SPRITEX *pSpr1=NULL, *pSpr, *pLast=NULL;

	for(f=1; f<MAX_TEXTURE_FRAMES; f++)
	{
		sprintf(sFName, "%s%02d", sStr, f);
		TMAPPRINTF("Frame '%s'\n", sFName);

		pSpr=textureFindInAllBanks(sFName);
		if(!pSpr)
			break;

		if(!pSpr1)
			pSpr1=pSpr;
		else
		{
			if(pSpr!=(pLast+1))
			{
				// Frames are not sequential!
				DB("ERK! Frames not consecutive! (last=%p, this=%p)\n", pLast, pSpr);
			}
		}

		pLast=pSpr;
	}

	if(nFrames)
		*nFrames=f;

	return pSpr1;
}

/**************************************************************************
	FUNCTION:	textureDownloadBank()
	PURPOSE:	Download entire texture bank to VRAM
	PARAMETERS:	Texture bank info ptr
	RETURNS:	Sprite info ptr
**************************************************************************/

void textureDownloadBank(TextureBankType *bank)
{
	int	loop;

	for(loop=0; loop<bank->nTextures; loop++)
		textureDownloadFromBank(bank, loop);
}


/**************************************************************************
	FUNCTION:	textureFindInAllBanks()
	PURPOSE:	Find given texture in all loaded banks
	PARAMETERS:	Name of texture
	RETURNS:	Sprite info ptr
**************************************************************************/

SPRITEX *textureFindInAllBanks(char *name)
{
	int		loop;
	SPRITEX	*sprX = NULL;

	for(loop=0; loop<MAXTEXBANKS; loop++)
	{
		if (texBank[loop]!=NULL)
		{
			sprX = textureFindInBank(texBank[loop], name);
			if (sprX!=NULL)
				return sprX;
		}
	}

	//if(strcmp(name, "WHITE"))
	//DB("?");
	
//	DB("Cannot find texture '%s'\n", name);	// this is only really printed out for the anims now
	//sprX=texBank[0]->pSpriteX;// remember, 1st texture is always forced into VRAM

	return sprX; //gameInfo.white; THIS COULD BE A PROBLEM!!! ANO // Not any more CPW
}



SPRITEX *textureFindCRCInBank(TextureBankType *bank, ULONG crc)
{
	ULONG	numTextures = bank->nTextures;
	ULONG	loop;

	for(loop=0; loop<numTextures; loop++)
	{
		if (bank->CRC[loop] == crc)
			return textureDownloadFromBank(bank, loop);
	}
	return NULL;
}

SPRITEX *textureFindCRCInAllBanks(ULONG crc)
{
	int		loop;
	SPRITEX	*sprX = NULL;

	for(loop=0; loop<MAXTEXBANKS; loop++)
	{
		if (texBank[loop]!=NULL)
		{
			sprX = textureFindCRCInBank(texBank[loop], crc);
			if (sprX!=NULL)
				return sprX;
		}
	}
	return sprX;
}



/**************************************************************************
	FUNCTION:	textureDestroyBank()
	PURPOSE:	Free system RAM data for given texture bank
	PARAMETERS:	Texture bank info ptr
	RETURNS:	
**************************************************************************/

void textureDestroyBank(TextureBankType *bank)
{
	FREE(bank->pNSprite);
	bank->pNSprite = NULL;
}


/**************************************************************************
	FUNCTION:	textureUnloadBank()
	PURPOSE:	Unload given texture bank from VRAM
	PARAMETERS:	Texture bank info ptr
	RETURNS:	
**************************************************************************/

void textureUnloadBank(TextureBankType *bank)
{
	ULONG	loop, y,b, numTex;

	for(loop=0; loop<MAXTEXBANKS; loop++)
		if (texBank[loop]==bank)
			texBank[loop] = NULL;

	numTex = bank->nTextures;
	for(loop=0; loop<numTex; loop++)
	{
		b = 1<<(loop & 31);
		y = loop>>5;
		if (bank->pUsed[y] & b)
			textureUnload(bank->pSpriteX+loop);
	}

	FREE(bank->pSpriteX);
	//FREE(bank->pUsed);
	FREE(bank->CRC);
	FREE(bank);
}



void uploadTimToVRAM(ULONG *addr)
{
	RECT rect1;
	GsIMAGE tim1;

	/* get TIM data info. */	
	/* skip and pass file header*/
	GsGetTimInfo(addr+1, &tim1);	

	/* transfer pixel data to VRAM*/	
	rect1.x=tim1.px;
	rect1.y=tim1.py;
	rect1.w=tim1.pw;
	rect1.h=tim1.ph;

#if PALMODE == NO
	{
		LoadImage(&rect1,tim1.pixel);
	}
#endif
#if PALMODE == YES
	LoadImage(&rect1,tim1.pixel);
#endif


	/* if CLUT exists, transfer it to VRAM*/
	if((tim1.pmode>>3)&0x01)
	{
		rect1.x=tim1.cx;
		rect1.y=tim1.cy;
		rect1.w=tim1.cw;
		rect1.h=tim1.ch;
		LoadImage(&rect1,tim1.clut);
	}
}

/**************************************************************************
	FUNCTION:	shitCrapArseBollocks()
	PURPOSE:	None
	PARAMETERS:	
	RETURNS:
**************************************************************************/

void textureInit256ClutSpace(void)
{
	int		page, x,i;

	for(page=0; page<4; page++)
	{
		for(x=0; x<VRAM_PAGEW; x++)
		{
			for(i=0; i<(VRAM_256PALETTES/8); i++)
				VRAMblock[page][VRAM_SETXY(x,i)]=1;
		}
	}

	for(i=0; i<VRAM_256PALETTES; i++)
	{
		VRAMpalette256ctr[i]=0;
		VRAMpalette256chk[i]=0;
	}
}
