
#include "glover.h"



#define BFF_CorrectPointer(header,pointer) {if(pointer) (pointer) = ADD2POINTER((header),((int)(pointer)));}
ULONG NPercentAcrossX(ULONG x,ULONG w,ULONG per,UBYTE tiled);

BFF_Header *generic_bff;

// Turn the offsets & spritex numbers in a BFF PSA's TMD data into pointers


// link the bits of the "tmd" header that are common to bff'd PSA's and Glovescape objects

int Link_BFF_ObjectHeader(BFF_PSAHeader *header, NEWOBJECT *obj, SPRITEX **sprs)
{
	NEWTMD *tmd;
// adjust the object
	BFF_CorrectPointer(header,obj->scalekeys);
	BFF_CorrectPointer(header,obj->movekeys);
	BFF_CorrectPointer(header,obj->rotatekeys);
	BFF_CorrectPointer(header,obj->parent);
	BFF_CorrectPointer(header,obj->child);
	BFF_CorrectPointer(header,obj->next);
	BFF_CorrectPointer(header,obj->meshdata);

// adjust the tmd
	tmd = obj->meshdata;

	BFF_CorrectPointer(header,tmd->vertop);
	BFF_CorrectPointer(header,tmd->nortop);
	BFF_CorrectPointer(header,tmd->primtop);
	BFF_CorrectPointer(header,tmd->sorttable[0]);
	BFF_CorrectPointer(header,tmd->sorttable[1]);
	BFF_CorrectPointer(header,tmd->sorttable[2]);
	BFF_CorrectPointer(header,tmd->sorttable[3]);
	return 1;
}
int Link_BFF_Object(BFF_PSAHeader *header, NEWOBJECT *obj, SPRITEX **sprs)
{
	NEWTMD *tmd;
	TMD_P_TF4A	*op;
	int i;
	SPRITEX *spr;
//	int want_fog;
	int transp;
	int semiflag;

	tmd = obj->meshdata;

// adjust the primitives
//	printf("  tmd has %d prims\n",tmd->primn);

	op = (void *)tmd->primtop;
	for(i = 0; i < tmd->primn; i++)
	{
		switch(op->cd & (0xff-2))
		{
// there's an "if fogged" to deal with here in the CLUT bit
		case GPU_COM_TG4:
			transp = ((TMD_P_TG4A*)op)->tpage;	// a combo of (32 + 64);
			if(transp)
				semiflag = 2;
			else
				semiflag = 0;

			spr = sprs[(int)((TMD_P_TG4A *)op)->spr];
			((TMD_P_TG4A*)op)->spr    = (SPRITE *)spr;

			((TMD_P_TG4A*)op)->tpage |= spr->tpage;

//			want_fog = ((TMD_P_TG4A*)op)->clut;



//#ifdef CLUT_FOGGING_CODE
//			if(!want_fog && (spr->clut & 0x8000))	// The model printer doesn't know about fogging
//				((TMD_P_TG4A*)op)->clut = VRAMfogPalette[spr->clut & 0x7fff];
//			else
//#endif
  			((TMD_P_TG4A*)op)->clut = spr->clut;
 	 		((TMD_P_TG4A*)op)->tu0=NPercentAcrossX(spr->x,spr->w,((TMD_P_TG4A*)op)->tu0,0);
 	 		((TMD_P_TG4A*)op)->tv0=NPercentAcrossX(spr->y,spr->h,((TMD_P_TG4A*)op)->tv0,0);
 	 		((TMD_P_TG4A*)op)->tu1=NPercentAcrossX(spr->x,spr->w,((TMD_P_TG4A*)op)->tu1,0);
 	 		((TMD_P_TG4A*)op)->tv1=NPercentAcrossX(spr->y,spr->h,((TMD_P_TG4A*)op)->tv1,0);
 	 		((TMD_P_TG4A*)op)->tu2=NPercentAcrossX(spr->x,spr->w,((TMD_P_TG4A*)op)->tu2,0);
 	 		((TMD_P_TG4A*)op)->tv2=NPercentAcrossX(spr->y,spr->h,((TMD_P_TG4A*)op)->tv2,0);
 	 		((TMD_P_TG4A*)op)->tu3=NPercentAcrossX(spr->x,spr->w,((TMD_P_TG4A*)op)->tu3,0);
 	 		((TMD_P_TG4A*)op)->tv3=NPercentAcrossX(spr->y,spr->h,((TMD_P_TG4A*)op)->tv3,0);
//			((TMD_P_TG4A*)op)->tileduvs=(0xe2<<24)|((spr->x>>3)<<10)|((spr->y>>3)<<15)|((0x20-(spr->w>>3)))|((0x20-(spr->h>>3))<<5);

// ick - we could do with loading the tmap into the BFF compiler & generating this stuff in advance
/*
			if(want_fog)
			{
				int r,g,b;
				unsigned int col;
				col=(ULONG)spr->image;	//AverageOfClutAccurate(spr);
				
				r=(col&0x1f)<<3;
				g=((col>>5)&0x1f)<<3;
				b=((col>>10)&0x1f)<<3;

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

				r*=(((TMD_P_TG4A*)op)->r0+((TMD_P_TG4A*)op)->r1+((TMD_P_TG4A*)op)->r2+((TMD_P_TG4A*)op)->r3)/4;
				g*=(((TMD_P_TG4A*)op)->g0+((TMD_P_TG4A*)op)->g1+((TMD_P_TG4A*)op)->g2+((TMD_P_TG4A*)op)->g3)/4;
				b*=(((TMD_P_TG4A*)op)->g0+((TMD_P_TG4A*)op)->b1+((TMD_P_TG4A*)op)->b2+((TMD_P_TG4A*)op)->b3)/4;

				r=r>>7;
				g=g>>7;
				b=b>>7;

				if(r>255) r=255;
				if(g>255) g=255;
				if(b>255) b=255;

				((TMD_P_TG4A*)op)->avgTexCol.r=r;	
				((TMD_P_TG4A*)op)->avgTexCol.g=g;
				((TMD_P_TG4A*)op)->avgTexCol.b=b;	

				((TMD_P_TG4A*)op)->avgTexCol.code=GPU_COM_F4|semiflag; 
			}
*/
			if(((TMD_P_TG4A*)op)->code2 == 0x80)	// water detection
			{
				if(!pWaterSprites)
					pWaterSprites=textureFindFrames("WATER");
//				((TMD_P_TG4A*)p)->code2=128;// 128 = anim, 8 = no. of frames

	 			(((TMD_P_TG4A*)op)->tu0)-=spr->u0;
	 			(((TMD_P_TG4A*)op)->tv0)-=spr->v0;

	 			(((TMD_P_TG4A*)op)->tu1)-=spr->u0;
	 			(((TMD_P_TG4A*)op)->tv1)-=spr->v0;

	 			(((TMD_P_TG4A*)op)->tu2)-=spr->u0;
	 			(((TMD_P_TG4A*)op)->tv2)-=spr->v0;

	 			(((TMD_P_TG4A*)op)->tu3)-=spr->u0;
	 			(((TMD_P_TG4A*)op)->tv3)-=spr->v0;
			}

			op = ADD2POINTER(op,sizeof(TMD_P_TG4A));
			break;

		case GPU_COM_TG3:
			transp = ((TMD_P_TG3A*)op)->tpage;	// a combo of (32 + 64);
			if(transp)
				semiflag = 2;
			else
				semiflag = 0;

			spr = sprs[(int)((TMD_P_TG3A *)op)->spr];
			((TMD_P_TG3A*)op)->spr    = (SPRITE *)spr;
			((TMD_P_TG3A*)op)->tpage |= spr->tpage;

//			want_fog = ((TMD_P_TG4A*)op)->clut;

//#ifdef CLUT_FOGGING_CODE
//			if(!want_fog && (spr->clut & 0x8000))	// The model printer doesn't know about fogging
//				((TMD_P_TG3A*)op)->clut = VRAMfogPalette[spr->clut & 0x7fff];
//			else
//#endif
  			((TMD_P_TG3A*)op)->clut = spr->clut;

 	 		((TMD_P_TG3A*)op)->tu0=NPercentAcrossX(spr->x,spr->w,((TMD_P_TG3A*)op)->tu0,0);
 	 		((TMD_P_TG3A*)op)->tv0=NPercentAcrossX(spr->y,spr->h,((TMD_P_TG3A*)op)->tv0,0);
 	 		((TMD_P_TG3A*)op)->tu1=NPercentAcrossX(spr->x,spr->w,((TMD_P_TG3A*)op)->tu1,0);
 	 		((TMD_P_TG3A*)op)->tv1=NPercentAcrossX(spr->y,spr->h,((TMD_P_TG3A*)op)->tv1,0);
 	 		((TMD_P_TG3A*)op)->tu2=NPercentAcrossX(spr->x,spr->w,((TMD_P_TG3A*)op)->tu2,0);
			((TMD_P_TG3A*)op)->tv2=NPercentAcrossX(spr->y,spr->h,((TMD_P_TG3A*)op)->tv2,0);

//			((TMD_P_TG3A*)op)->tileduvs=(0xe2<<24)|((spr->x>>3)<<10)|((spr->y>>3)<<15)|((0x20-(spr->w>>3)))|((0x20-(spr->h>>3))<<5);

// ick - we could do with loading the tmap into the BFF compiler & generating this stuff in advance
/*
			if(want_fog)
			{
				int r,g,b;
				unsigned int col;

				col=(ULONG)spr->image;	//AverageOfClutAccurate(spr);
				
				r=(col&0x1f)<<3;
				g=((col>>5)&0x1f)<<3;
				b=((col>>10)&0x1f)<<3;

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

				r*=(((TMD_P_TG3A*)op)->r0+((TMD_P_TG3A*)op)->r1+((TMD_P_TG3A*)op)->r2)/3;
				g*=(((TMD_P_TG3A*)op)->g0+((TMD_P_TG3A*)op)->g1+((TMD_P_TG3A*)op)->g2)/3;
				b*=(((TMD_P_TG3A*)op)->g0+((TMD_P_TG3A*)op)->b1+((TMD_P_TG3A*)op)->b2)/3;

				r=r>>7;
				g=g>>7;
				b=b>>7;

				if(r>255) r=255;
				if(g>255) g=255;
				if(b>255) b=255;

				((TMD_P_TG3A*)op)->avgTexCol.r=r;	
				((TMD_P_TG3A*)op)->avgTexCol.g=g;
				((TMD_P_TG3A*)op)->avgTexCol.b=b;	

				((TMD_P_TG3A*)op)->avgTexCol.code=GPU_COM_F3|semiflag; 
			}
*/

			if(((TMD_P_TG3A*)op)->code2 == 0x80)	// water detection
			{
				if(!pWaterSprites)
					pWaterSprites=textureFindFrames("WATER");

	 			(((TMD_P_TG3A*)op)->tu0)-=spr->u0;
	 			(((TMD_P_TG3A*)op)->tv0)-=spr->v0;

	 			(((TMD_P_TG3A*)op)->tu1)-=spr->u0;
	 			(((TMD_P_TG3A*)op)->tv1)-=spr->v0;

	 			(((TMD_P_TG3A*)op)->tu2)-=spr->u0;
	 			(((TMD_P_TG3A*)op)->tv2)-=spr->v0;
			}

			op = ADD2POINTER(op,sizeof(TMD_P_TG3A));

			break;

		case GPU_COM_TF3:
			spr = sprs[(int)((TMD_P_TF3A *)op)->spr];
			((TMD_P_TF3A*)op)->spr    = (SPRITE *)spr;
			((TMD_P_TF3A*)op)->tpage |= spr->tpage;
//			want_fog = ((TMD_P_TG3A*)op)->clut;
//#ifdef CLUT_FOGGING_CODE
//			if(!want_fog && (spr->clut & 0x8000))	// The model printer doesn't know about fogging
//				((TMD_P_TF3A*)op)->clut = VRAMfogPalette[spr->clut & 0x7fff];
//			else
//#endif
  			((TMD_P_TF3A*)op)->clut = spr->clut;

 	 		((TMD_P_TF3A*)op)->tu0=NPercentAcrossX(spr->x,spr->w,((TMD_P_TF3A*)op)->tu0,0);
 	 		((TMD_P_TF3A*)op)->tv0=NPercentAcrossX(spr->y,spr->h,((TMD_P_TF3A*)op)->tv0,0);
 	 		((TMD_P_TF3A*)op)->tu1=NPercentAcrossX(spr->x,spr->w,((TMD_P_TF3A*)op)->tu1,0);
 	 		((TMD_P_TF3A*)op)->tv1=NPercentAcrossX(spr->y,spr->h,((TMD_P_TF3A*)op)->tv1,0);
 	 		((TMD_P_TF3A*)op)->tu2=NPercentAcrossX(spr->x,spr->w,((TMD_P_TF3A*)op)->tu2,0);
 	 		((TMD_P_TF3A*)op)->tv2=NPercentAcrossX(spr->y,spr->h,((TMD_P_TF3A*)op)->tv2,0);

//			((TMD_P_TF3A*)op)->tileduvs=(0xe2<<24)|((spr->x>>3)<<10)|((spr->y>>3)<<15)|((0x20-(spr->w>>3)))|((0x20-(spr->h>>3))<<5);
			op = ADD2POINTER(op,sizeof(TMD_P_TF3A));
			break;

		case GPU_COM_TF4:
		case GPU_COM_TF4SPR:
//			((TMD_P_TF4A*)p)
			spr = sprs[(int)((TMD_P_TF4A *)op)->spr];
			((TMD_P_TF4A*)op)->spr    = (SPRITE *)spr;
			((TMD_P_TF4A*)op)->tpage |= spr->tpage;
//			want_fog = ((TMD_P_TF4A*)op)->clut;
//#ifdef CLUT_FOGGING_CODE
//			if(!want_fog && (spr->clut & 0x8000))	// The model printer doesn't know about fogging
//				((TMD_P_TF4A*)op)->clut = VRAMfogPalette[spr->clut & 0x7fff];
//			else
//#endif
  			((TMD_P_TF4A*)op)->clut = spr->clut;

 	 		((TMD_P_TF4A*)op)->tu0=NPercentAcrossX(spr->x,spr->w,((TMD_P_TF4A*)op)->tu0,0);
 	 		((TMD_P_TF4A*)op)->tv0=NPercentAcrossX(spr->y,spr->h,((TMD_P_TF4A*)op)->tv0,0);
 	 		((TMD_P_TF4A*)op)->tu1=NPercentAcrossX(spr->x,spr->w,((TMD_P_TF4A*)op)->tu1,0);
 	 		((TMD_P_TF4A*)op)->tv1=NPercentAcrossX(spr->y,spr->h,((TMD_P_TF4A*)op)->tv1,0);
 	 		((TMD_P_TF4A*)op)->tu2=NPercentAcrossX(spr->x,spr->w,((TMD_P_TF4A*)op)->tu2,0);
 	 		((TMD_P_TF4A*)op)->tv2=NPercentAcrossX(spr->y,spr->h,((TMD_P_TF4A*)op)->tv2,0);
 	 		((TMD_P_TF4A*)op)->tu3=NPercentAcrossX(spr->x,spr->w,((TMD_P_TF4A*)op)->tu3,0);
 	 		((TMD_P_TF4A*)op)->tv3=NPercentAcrossX(spr->y,spr->h,((TMD_P_TF4A*)op)->tv3,0);

//			((TMD_P_TF4A*)op)->tileduvs=(0xe2<<24)|((spr->x>>3)<<10)|((spr->y>>3)<<15)|((0x20-(spr->w>>3)))|((0x20-(spr->h>>3))<<5);
			op = ADD2POINTER(op,sizeof(TMD_P_TF4A));

			break;


		case GPU_COM_G4:
//			((TMD_P_F4G*)p)
			op = ADD2POINTER(op,sizeof(TMD_P_F4G));
			break;
		case GPU_COM_G3:
			op = ADD2POINTER(op,sizeof(TMD_P_F3G));
//			((TMD_P_F3G*)p)
			break;
	 	case GPU_COM_F4:
			op = ADD2POINTER(op,sizeof(TMD_P_F4));
//			((TMD_P_F4*)p)
			break;
  	 	case GPU_COM_F3:
			op = ADD2POINTER(op,sizeof(TMD_P_F3));
//			((TMD_P_F3*)p)
			break;
		default:
			PRINTF("ermmm... what kinda prim's that then? %s\n",tmd->name);
			break;
		}
	}
	return 1;
}

int Link_BFF_GlovescapeObject(BFF_PSAHeader *header, NEWOBJECT *obj, SPRITEX **sprs)
{


#ifdef NEW_GLOVER_LANDSCAPE
	NEWTMD *tmd;
	int i;
	SPRITEX *spr;
	SPRITEX *pWSpr;//=textureFindFrames("WATER");


	union
	{
		TMD_PTYPE_TG3 glv_gt3;
		TMD_PTYPE_TG4 glv_gt4;
		TMD_PTYPE_GENERIC glv_gen;
	}*u;

//	DB("glove obj 1\n");
	tmd = obj->meshdata;
	u = (void *)tmd->primtop;

// adjust the primitives

	for(i = 0; i < tmd->primn; i++)
	{
		spr=NULL;

//		DB("prim %d/%d\n",i,tmd->primn);
		switch(u->glv_gen.primtype & 1)
		{
		case PTYPE_TG4:
			spr = sprs[(int)(u->glv_gt4.spr)];

			if(spr && (spr->flags & NCOLOURKEY))
			{
//				DB("ck\n");
				u->glv_gt4.primtype|=128;			// set special part (if not already set)
				u->glv_gen.flags|=PMASK_COLOURKEY;	// colour key flag
			}

			if(u->glv_gen.flags & PMASK_TEXANIM)
				spr++;

			u->glv_gt4.spr    = (SPRITEX *)spr;
			u->glv_gt4.tpage |= spr->tpage;

  			u->glv_gt4.clut = spr->clut;
 	 		u->glv_gt4.tu0 = NPercentAcrossX(spr->x, spr->w, u->glv_gt4.tu0,0);
 	 		u->glv_gt4.tv0 = NPercentAcrossX(spr->y, spr->h, u->glv_gt4.tv0,0);
 	 		u->glv_gt4.tu1 = NPercentAcrossX(spr->x, spr->w, u->glv_gt4.tu1,0);
 	 		u->glv_gt4.tv1 = NPercentAcrossX(spr->y, spr->h, u->glv_gt4.tv1,0);
 	 		u->glv_gt4.tu2 = NPercentAcrossX(spr->x, spr->w, u->glv_gt4.tu2,0);
 	 		u->glv_gt4.tv2 = NPercentAcrossX(spr->y, spr->h, u->glv_gt4.tv2,0);
 	 		u->glv_gt4.tu3 = NPercentAcrossX(spr->x, spr->w, u->glv_gt4.tu3,0);
 	 		u->glv_gt4.tv3 = NPercentAcrossX(spr->y, spr->h, u->glv_gt4.tv3,0);

			if(u->glv_gen.flags & PMASK_TEXANIM)
			{
	 			u->glv_gt4.tu0-=spr->u0;
	 			u->glv_gt4.tv0-=spr->v0;

				u->glv_gt4.tu1-=spr->u0;
	 			u->glv_gt4.tv1-=spr->v0;

	 			u->glv_gt4.tu2-=spr->u0;
	 			u->glv_gt4.tv2-=spr->v0;

	 			u->glv_gt4.tu3-=spr->u0;
	 			u->glv_gt4.tv3-=spr->v0;
			}

			if((u->glv_gen.flags & PMASK_UVANIM) || (u->glv_gen.flags & PMASK_TEXANIM))
			{
				u = ADD2POINTER(u,sizeof(TMD_PTYPE_STG4));
			}
			else
			{
				u = ADD2POINTER(u,sizeof(TMD_PTYPE_TG4));
			}

			break;

		case PTYPE_TG3:
			spr = sprs[(int)(u->glv_gt3.spr)];

			if(spr && (spr->flags & NCOLOURKEY))
			{
//				DB("ck\n");
				u->glv_gt3.primtype|=128;			// set special part (if not already set)
				u->glv_gt3.flags|=PMASK_COLOURKEY;	// colour key flag
			}

			if(u->glv_gen.flags & PMASK_TEXANIM)
				spr++;

			u->glv_gt3.spr    = (SPRITEX *)spr;
			u->glv_gt3.tpage |= spr->tpage;

  			u->glv_gt3.clut = spr->clut;

 	 		u->glv_gt3.tu0 = NPercentAcrossX(spr->x,spr->w,u->glv_gt3.tu0,0);
 	 		u->glv_gt3.tv0 = NPercentAcrossX(spr->y,spr->h,u->glv_gt3.tv0,0);
 	 		u->glv_gt3.tu1 = NPercentAcrossX(spr->x,spr->w,u->glv_gt3.tu1,0);
 	 		u->glv_gt3.tv1 = NPercentAcrossX(spr->y,spr->h,u->glv_gt3.tv1,0);
 	 		u->glv_gt3.tu2 = NPercentAcrossX(spr->x,spr->w,u->glv_gt3.tu2,0);
			u->glv_gt3.tv2 = NPercentAcrossX(spr->y,spr->h,u->glv_gt3.tv2,0);

			if(u->glv_gen.flags & PMASK_TEXANIM)
			{
	 			u->glv_gt4.tu0-=spr->u0;
	 			u->glv_gt4.tv0-=spr->v0;

				u->glv_gt4.tu1-=spr->u0;
	 			u->glv_gt4.tv1-=spr->v0;

	 			u->glv_gt4.tu2-=spr->u0;
	 			u->glv_gt4.tv2-=spr->v0;
			}

			if((u->glv_gen.flags & PMASK_UVANIM) || (u->glv_gen.flags & PMASK_TEXANIM))
			{
				u = ADD2POINTER(u,sizeof(TMD_PTYPE_STG3));
			}
			else
			{
				u = ADD2POINTER(u,sizeof(TMD_PTYPE_TG3));
			}
			break;

		case PTYPE_TG4 + PTYPENUMBER_SPECIAL:	// not done yet
//			DB("unknown p4type\n");
			switch(u->glv_gen.flags)
			{
			}
			break;

		case PTYPE_TG3 + PTYPENUMBER_SPECIAL:	// not done yet
//			DB("unknown p3type\n");
			switch(u->glv_gen.flags)
			{
			}
			break;
		}

	}
#endif
//	DB("glove obj end\n");

	return 1;
}






ULONG NPercentAcrossX(ULONG x,ULONG w,ULONG per,UBYTE tiled)
{
	ULONG calc;
	ULONG result;

	calc=(w*per)>>8; 
 	result=calc+x;
 	if (result>255)
		result=8;
 	return result;
}

SPRITEX mirror;
// convert the offsets in a BFF "PSA" object into pointers.
// Find the textures, & call the mesh linker
void BFF_Link_PSA(BFF_PSAHeader *header)
{
	NEWMODEL *ret;
	NEWOBJECT *objptr;
	BFF_PSATexture *tex;
	BFF_PSAObject *obj;
	SPRITEX **spritexes;
	int i;

	ret = ADD2POINTER(header,header->model_offset);
	tex = ADD2POINTER(header,header->texturedef_offset);
	obj = ADD2POINTER(header,header->objectdef_offset);

	spritexes = MALLOC(header->n_textures * 4,"spritexes");	// temp malloc, no more mallocs in this routine, fortunately

	for(i = 0; i < header->n_textures;i++)
	{
		spritexes[i] = textureFindCRCInAllBanks(tex[i].name_crc);

		if(!spritexes[i])
		{
// You can get names from crc's by logging the BFF compiler's output.
			PRINTF("TEX %i (crc %8x) not found in shape (sorry, no names!)\n",i,tex[i].name_crc);
		}
		ASSERT(spritexes[i]);

	}

//	printf("  %d objects\n",header->n_objects);

	for(i = 0; i < header->n_objects;i++)
	{
//		DB("  pointerising object %d (offset %d)\n",i,obj[i].object_offset);
		objptr = ADD2POINTER(header,obj[i].object_offset);
		Link_BFF_ObjectHeader(header,objptr,spritexes);
		Link_BFF_Object(header,objptr,spritexes);
// Uncomment this line if you get the texture asserts
//		DB("psa object %s\n",objptr->meshdata->name);
	}

	FREE(spritexes);
}

void BFF_Link_GlovescapePSA(BFF_PSAHeader *header)
{
	NEWMODEL *ret;
	NEWOBJECT *objptr;
	BFF_PSATexture *tex;
	BFF_PSAObject *obj;
	SPRITEX **spritexes;
	int i;

	ret = ADD2POINTER(header,header->model_offset);
	tex = ADD2POINTER(header,header->texturedef_offset);
	obj = ADD2POINTER(header,header->objectdef_offset);

	spritexes = MALLOC(header->n_textures * 4,"spritexes");	// temp malloc, no more mallocs in this routine, fortunately

	for(i = 0; i < header->n_textures;i++)
	{
		spritexes[i] = textureFindCRCInAllBanks(tex[i].name_crc);
		if(!spritexes[i])
		{
// You can get names from crc's by logging the BFF compiler's output.
			PRINTF("TEX %i (crc %8x) not found in shape (sorry, no names!)\n",i,tex[i].name_crc);
		}
		ASSERT(spritexes[i]);


	}

//	printf("  %d objects\n",header->n_objects);

	for(i = 0; i < header->n_objects;i++)
	{
//		DB("  pointerising glovescape %d (offset %d)\n",i,obj[i].object_offset);
		objptr = ADD2POINTER(header,obj[i].object_offset);
		Link_BFF_ObjectHeader(header,objptr,spritexes);
		Link_BFF_GlovescapeObject(header,objptr,spritexes);
// Uncomment this line if you get the texture asserts
//		DB("lnd object %s\n",objptr->meshdata->name);
	}

//	printf("done\n");
	FREE(spritexes);
}




NEWMODEL * BFF_IsModelLoaded(char *modelname, objectSegDataType **seg)
{
	BFF_PSAHeader *header;
	NEWMODEL *ret;

	header = (BFF_PSAHeader *)BFF_FindObject(BFF_PSA_ID,str2CRC(modelname));

	if(!header)
		header = (BFF_PSAHeader *)BFF_FindObject(BFF_GLOVSCAPE_PSA_ID,str2CRC(modelname));

	if(!header)
		return NULL;
	ret = ADD2POINTER(header,header->model_offset);
	if(seg)
	{
		if(!header->seg_offset)
			*seg = NULL;
		else
			*seg = ADD2POINTER(header,header->seg_offset);

	}
	return ret;
}

NEWMODEL * BFF_IsCRCModelLoaded(unsigned long crc, objectSegDataType **seg)
{
	BFF_PSAHeader *header;
	NEWMODEL *ret;

	header = (BFF_PSAHeader *)BFF_FindObject(BFF_PSA_ID,crc);

	if(!header)
		header = (BFF_PSAHeader *)BFF_FindObject(BFF_GLOVSCAPE_PSA_ID,crc);
	
	
	if(!header)
		return NULL;
	ret = ADD2POINTER(header,header->model_offset);
	if(seg)
	{
		if(!header->seg_offset)
			*seg = NULL;
		else
			*seg = ADD2POINTER(header,header->seg_offset);

	}
	return ret;
}

// ===================================================================================

void BFF_Link_WLD(BFF_WorldHeader *header)
{
	int i;

//	unsigned long crc;
	header->world.pZones = ADD2POINTER(header,(int)(header->world.pZones));


	for(i = 0; i < header->world.nZones; i++)
	{
//		printf("wldobj %d\n",i);
//		crc = (unsigned long)(header->world.pZones[i]);
//		header->world.pZones[i] = BFF_IsCRCModelLoaded(crc, NULL);
		header->world.pZones[i].u.model = BFF_IsCRCModelLoaded(header->world.pZones[i].u.crc, NULL);
		ASSERT(header->world.pZones[i].u.model);

		if(i == 0)
		{
			world_base_y = header->world.pZones[i].u.model->world.meshdata->boundmaxy;
		}
		else
		{
			if(world_base_y < header->world.pZones[i].u.model->world.meshdata->boundmaxy)
				world_base_y = header->world.pZones[i].u.model->world.meshdata->boundmaxy;
		}

	}
	world_base_y = (world_base_y+100) << 12;
}
// ===================================================================================
void BFF_Link_Coll(BFF_CollHeader *header)
{
	int i;
	COLLBOX	*boxes;
	void    **pointers;
	int value;

	if(header->red_mesh_offset)
	{
		boxes = ADD2POINTER(header,header->base_mesh_offset+4);	// 1st int is the number of collboxes
		pointers = ADD2POINTER(header,header->red_mesh_offset);

		WorldBFFcoll = header;

		reducedCollBox = ADD2POINTER(header,header->red_mesh_offset);

		for(i = 0; i < header->n_pointers; i++)
		{
			value = (ULONG)pointers[i];
			if(value == -1)
			{
				pointers[i] = NULL;
			}
			else
			{
				if(!(value & 0x80000000))	// single
				{
					pointers[i] = &boxes[value & 0x7fffffff];
				}
				else	// multi
				{
					value = (ULONG)(&pointers[value]);
					value |= 0x80000000;
					pointers[i] = (void *)value;
				}
			}
		}
	}
}

// ===================================================================================
#define MAX_BFF_FILES 4
BFF_Header *BFF_Pointers[MAX_BFF_FILES] = {NULL,NULL,NULL,NULL};
int BFF_Lens[MAX_BFF_FILES];

BFF_Header *BFF_FindObject(int id, unsigned long crc)
{
	int i;
	BFF_Header *work;
	for(i = 0; i < MAX_BFF_FILES; i++)
	{
		if(BFF_Pointers[i])
		{
//			printf(" scanning bff %d",i);
			work = BFF_Pointers[i];
			for(;;)
			{
				if(work->id == id && (crc == 0 || work->crc == crc))
				{
//					printf("\n");
					return work;
				}
				if(work->len & 0x80000000)
					break;
				work = ADD2POINTER(work,work->len);
			}
//			printf("\n");
		}
	}
	return NULL;	// yes there are occasions when we need to try to find things that aren't there
}
BFF_Header *BFF_FindNamedObject(int id, char *name)
{
	int i;
	unsigned long crc;

	BFF_Header *work;

	crc = str2CRC(name);
	for(i = 0; i < MAX_BFF_FILES; i++)
	{
		if(BFF_Pointers[i])
		{
//			printf(" scanning bff %d",i);
			work = BFF_Pointers[i];
			for(;;)
			{

//				printf(".");
				if(work->id == id && (crc == 0 || work->crc == crc))
				{
//					printf("\n");
					return work;
				}
				if(work->len & 0x80000000)
					break;
				work = ADD2POINTER(work,work->len);
			}
//			printf("\n");
		}
	}
//	printf("null\n");
	return 0;	// yes there are occasions when we need to try to find things that aren't there
}

BFF_Header *BFF_LoadFile(char *filename)
{
	BFF_Header *addr;
	BFF_Header *work;
	BFF_Header *ending;
	int i;
	int lastfilelength;

	addr = (void *)fileLoad(filename, &lastfilelength);
	if(!addr)
		return NULL;

	ending = ADD2POINTER(addr, lastfilelength);

	work = addr;

// Scan through, & insert the EOF marker
	for(;;)
	{
		if((void *)ADD2POINTER(work,work->len) >= (void *)ending)
		{
			work->len |= 0x80000000;	// mark as the last one
			break;
		}
		work = ADD2POINTER(work,work->len);
	}


// Mark the BFF as being available for searching.
	for(i = 0; i < MAX_BFF_FILES; i++)
	{
		if(!BFF_Pointers[i])
		{
			BFF_Pointers[i] = addr;
			BFF_Lens[i] = lastfilelength;
			break;
		}
	}

// Link stuff
	work = addr;
	for(;;)
	{
/*
		DB("linking len = %d class (%c%c%c%d)\n",
					(int)work->len,
					((char *)(&work->id))[0],
					((char *)(&work->id))[1],
					((char *)(&work->id))[2],
					(int)(((char *)(&work->id))[3])
				);
*/
		switch(work->id)
		{
			case BFF_PSA_ID:
//				printf("Linking BFF PSA\n");
				BFF_Link_PSA((BFF_PSAHeader *)work);
				break;

			case BFF_GLOVSCAPE_PSA_ID:
				BFF_Link_GlovescapePSA((BFF_PSAHeader *)work);
				break;

// ".raw", and in the case of the landscape, ".dat"
			case BFF_COLL_ID:
				BFF_Link_Coll((BFF_CollHeader *)work);
				break;
			case BFF_WLD_ID:
				BFF_Link_WLD((BFF_WorldHeader *)work);
				break;

			case BFF_TIP_ID_ENG:
			case BFF_TIP_ID_GER:
			case BFF_TIP_ID_FRE:
			case BFF_TIP_ID_ITA:
				DB("Tip data block\n");
				break;

			default:
				PRINTF("Unknown or out-of-date BFF class %d (%c%c%c%d)\n",
					work->id,
					((char *)(&work->id))[0],
					((char *)(&work->id))[1],
					((char *)(&work->id))[2],
					(int)(((char *)(&work->id))[3])
					);
				CRASH;
		}

		if(work->len & 0x80000000)
			break;
		work = ADD2POINTER(work,work->len);
//		DB("ok\n");
	}
//	DB("linking all done\n");

	return addr;
}

void BFF_ReleaseFile(BFF_Header *header)
{
	int i;
	if(!header)
		return;
	for(i = 0; i < MAX_BFF_FILES; i++)
	{
		if(BFF_Pointers[i] == header)
		{
			BFF_Pointers[i] = NULL;
			break;
		}
	}
	FREE(header);
}

int BFF_IsInBFF(void *addr)
{
	void *base;
	int i;

	for(i = 0; i < MAX_BFF_FILES; i++)
	{
		base = BFF_Pointers[i];
		if(base)
		{
			if(base <= addr && ADD2POINTER(base,BFF_Lens[i]) > addr)
				return 1;
			break;
		}
	}
	return 0;
}


