/*
 *  Project:         Glover PSX
 *  File:            loadlnd.c
 *  Description:     handles all file control
 *
 *  (c)1999 ISL.
 */

//#define TOKEN_Back PRINTF
#define TOKEN_PRINTF


#include "glover.h"
//#include "types.nh"
//#include "puzzles.h"
//#include "cameo.h"
//#include "bff_load.h"
//#include "water.h"


//extern USHORT			numberofsegments;


#define LOADLND_MAXMODELS 20
LOADEDMODEL loadlnd_models[LOADLND_MAXMODELS];
int loadlnd_nummodelsloaded=0;

int done_a_restart = 0;
int restart_ya;

TELEPORTDEFSTR *teleport_def;

//SCENERYPOS *current_scenery;

//int nme_positions_read = 0;

//char loadlnd_world_dir[12];


int loadlnd_allow_platforms = 1;
//int loadlnd_load_scape = 0;
#define MAX_WLD_ZONES 50

PLATFORM_DEFSTR current_platform;	// temporary platform used during level-parse process

PLATFORM_DEFSTR *loadlnd_PlatDefs = NULL;

int ParseTokenCollectable(short token,UBYTE **pdata);
int ParseTokenGeneric(short token,UBYTE **data);

int ParseTokenPlatformPath(short token,UBYTE **data);
int ParseTokenPlatformGeneric(short token,UBYTE **data);
int ParseTokenPlatformInteraction(short token,UBYTE **data);
int ParseTokenPlatformOrbit(short token,UBYTE **data);
int ParseTokenPlatformFeatures(short token,UBYTE **data);
int ParseTokenPlatformSpin(short token,UBYTE **data);
int ParseTokenBridge(short token,UBYTE **data);


//short ropeTag;
//short numRopeBridges;
//short numRestartGates;
//short spikeOrPlatformOrVent = 0;
//short numCamExtraSpheres;

//char	tempName[8];

//char	ianzIndicator[4]={"+*+-"};
//UBYTE	ianzCounter=0;

// UBYTE PRINTF4TOKENS=YES;	//turn on/off printf's in cameo stuff



/*	--------------------------------------------------------------------------------
	Function 	: EnsureModelLoaded()
	Purpose 	: Ensures that a model is loaded, & if not, adds it to the collection (see findobject in the n64 source)
	Info 		: Work-in-Progress by Fred
*/


//VECTOR lnd_offset;

LOADEDMODEL * EnsureModelLoaded(unsigned long crc, char *name,USHORT scale)
{
	unsigned int i;
	unsigned long rqd_crc;
	LOADEDMODEL *model;
//	char tempname[60];
	int gen;

	if(name)
		rqd_crc = str2CRC(name);
	else
		rqd_crc = crc;

	model = &loadlnd_models[0];

	for(i = 0; i < loadlnd_nummodelsloaded; i++, model++)
	{
		if(loadlnd_models[i].crc == rqd_crc)
			return model;
	}

	if(i >= LOADLND_MAXMODELS)
	{
		DB("Too Many Models!\n");
		CRASH;
	}

	model->psa = BFF_IsModelLoaded(name,&model->seg);
	gen = 0;

	if(!model->psa)
	{
		DB("Model not found %s\n",name);
		CRASH;
	}

//	PRINTF("number of segments = %d\n",numberofsegments);
	model->crc = rqd_crc;

	objectSetupSegmentSort(model->psa);

	model->psa->globalscale.vx=scale;
	model->psa->globalscale.vy=scale;
	model->psa->globalscale.vz=scale;

	loadlnd_nummodelsloaded++;
	return model;
}

void ResetParsePlatform()
{
	PLATFORM_DEFSTR *plat;
	plat = &current_platform;

	memset(plat,0,sizeof(PLATFORM_DEFSTR));
	plat->head.parentTag=-1; // because a tag of 0 is legal
}

void FlushParsePlatform()
{
	PLATFORM_DEFSTR *plat;
	int points;
	int size;

	//if(!current_platform.head.name[0])
	if(!current_platform.head.platID)
	{
		ResetParsePlatform();	// necessary to prevent platforms from picking up uwater terrains from lscape
		return;
	}

	if(!loadlnd_allow_platforms)
	{
		ResetParsePlatform();	// necessary to prevent platforms from picking up uwater terrains from lscape
		return;
	}

	if(current_platform.head.flags & PLATFLAG_EXITDOOR)	//platID==str2CRC("EXITPOS"))
	{
// MOBILE exit-doors are (will be) handled by the platform code
// (eg - the ones on the bosses. Possibly the AT3 whale, as well
		if(current_platform.head.n_points <= 1)
		{
			enemiesAddExitPortal(
				current_platform.points[0].pos.vx,
				current_platform.points[0].pos.vy,
				current_platform.points[0].pos.vz,
				current_platform.head.tag
				);
			ResetParsePlatform();
			return;
		}
	}
	if(current_platform.head.flags & PLATFLAG_HOOP)	//platID==str2CRC("HOOP"))	// get rid of those ugly green hoops for now
	{
		int ya;
		ya = restart_ya;

		enemiesAddHoop(
			current_platform.points[0].pos.vx,
			current_platform.points[0].pos.vy,
			current_platform.points[0].pos.vz,
			ya
			);

		ResetParsePlatform();
		return;
	}

//	if(current_platform.head.platID==str2CRC("TELEPORT"))
	if(current_platform.head.flags & PLATFLAG_TELEPORT)	//platID==str2CRC("TELEPORT"))
	{

		enemiesAddTeleport(
			&current_platform.points[0].pos,
			teleport_def,
			current_platform.head.tag
			);

		ResetParsePlatform();
		return;
	}

	if(!BFF_IsCRCModelLoaded(current_platform.head.platID, NULL))
	{
		DB("**Warning platform PSA not present (CRC %08x) **\n", current_platform.head.platID);
		ResetParsePlatform();
		return;
	}



	points = current_platform.head.n_points;
	if(points == 0)
		points = 1;

	size = sizeof(PLATFORM_HEADERSTR) + points * sizeof(PLATFORM_POINTSTR);
	plat = MALLOC(size,"PlatDef");
	memcpy(plat,&current_platform,size);

/*	if(plat->head.parentTag)
	{
		DB("tag= %d", plat->head.parentTag);
		CRASH;
	}
*/

	plat->head.next = loadlnd_PlatDefs;
	loadlnd_PlatDefs = plat;
	

//	printf("*Creating platform with flags = %d\n",plat->head.flags);

	platformCreatePlatform(plat);

	if(current_platform.head.terrain)
	{
//		DB("Setting Terrain Type to %d\n",current_platform.head.terrain);
		lscapeSetTerrainTag(current_platform.head.tag,current_platform.head.terrain);
	}

	ResetParsePlatform();
}

/*	--------------------------------------------------------------------------------
	Function 	: ReleaseLandscapeFile()
	Purpose 	: releases any models/memory that was loaded/mallocced by ParseLandscapeFile
	Info 		:
*/

int hub_tags[4] = {97,183,69,176};	// 3 crates, and the coop
int hub_tags2[3] = {136,15,164};	// 3 switches

void ReleaseLandscapeFile()
{
	int i;
	LOADEDMODEL *model;
	PLATFORM_DEFSTR *plat;

	puzzleFreePuzzles();
	cameoFreeCameos();
	enemiesFreeEnemies();
	windClearUpMessAfterwards();
    lightningClearUpMessAfterwards();

	model = &loadlnd_models[0];
	for(i = 0; i < loadlnd_nummodelsloaded; i++, model++)
	{
		//objectFree(model->psa,NULL);
		if(model->seg)
		{
			FREE(model->seg);
		}
	}

	for(plat = loadlnd_PlatDefs; plat;)
	{
		PLATFORM_DEFSTR *next;
		next = plat->head.next;
		FREE(plat);
		plat = next;
	}
	loadlnd_PlatDefs = NULL;

	platformInitialise();
	waterFlush();


	loadlnd_nummodelsloaded = 0;
}

// ============= Okeydokey, we've loaded the level =================
// Check the gamestate
// If it's a normal level, disable extra lives if they've been collected
// If it's a hub, adjust the whole thing, and choose an entrypoint

void loadlndEndFile()
{
	int i;
	GARIBPOS *gar;

	{
		int lev;

		GameState.maxhealth = 3;

		for(lev = 0; lev < 6; lev++)
		{
			if(   (GameState.lives_got[lev * 5])
				& (GameState.lives_got[lev * 5+1])
				& (GameState.lives_got[lev * 5+2])
				& 0x8000
			  )
			{
				GameState.maxhealth++;
			}
		}
	}

// Remove any extra lives that we've already collected
	if(level >= ATLANTIS1 && level <= SPACEBOSS2)
	{
		short bits;

		gar = &garibpositions[0];

		bits = GameState.lives_got[level - ATLANTIS1];

		for(i = 0; i < numberofgaribs; i++, gar++)
		{
			switch(gar->type)
			{
				case EXTRA_LIFE:
				{
					if(bits & 1)
					{
						gar->flags |= GARIBFLAG_COLLECTED;
						gar->flags &= ~GARIBFLAG_DISPLAY;
					}
					bits = bits >> 1;
				}
				break;
			}
		}
	}

// Read in the hub status
	if(level >= HUB1 && level <= HUB8)
	{
		int bits = 0;
		int bitcounter;

		weatherInitialise(WEATHER_BIRDY);


		garibscollected = 0;

		gar = &garibpositions[0];
		bitcounter = 0;

		for(i = 0; i < numberofgaribs; i++, gar++)
		{
			if(!(bitcounter & 31))
			{
				bits = GameState.hub_status[bitcounter>>5];
			}
			bitcounter++;

			if(bits & 1)
			{
				gar->flags |= GARIBFLAG_COLLECTED;
				gar->flags &= ~GARIBFLAG_DISPLAY;

				switch(gar->type)
				{
					case NORMAL_GARIB:
						garibscollected++;
						break;
				}
			}
			bits = bits >> 1;
		}


// 3 crates and the hubcoop
		for(i = 0; i < 4; i++)
		{
			DYNCOLLBOX *pBox;
			if(!(bitcounter & 31))
			{
				bits = GameState.hub_status[bitcounter>>5];
			}
			bitcounter++;

			loadlndFindPlatform(hub_tags[i], NULL, &pBox);
			if(bits & 1)
				DB("Removing hub crate %d\n",i);
			if(pBox)
			{
				DB("Hurrah, found ok\n");
				if(bits & 1)
				{
					pBox->active = 0;
				}
			}
			bits = bits >> 1;
		}

// tbd - the switches
		for(i = 0; i < 3; i++)
		{
			DYNCOLLBOX *pBox;
			PLATFORM_DEFSTR *def;

			if(!(bitcounter & 31))
			{
				bits = GameState.hub_status[bitcounter>>5];
			}
			bitcounter++;

			loadlndFindPlatform(hub_tags2[i], &def, &pBox);
			if(bits & 1)
				DB("Moving hub switch %d\n",i);
			if(pBox)
			{
				DB("Hurrah, switch found ok\n");
				if(bits & 1)
				{
					pBox->move.point = 1;
					pBox->move.waitTimer=pBox->pPlatDef->points[1].wait;
//					platformSetDynamicBox(pBox,
					platformMoveDynamicBox(pBox,
						def->points[1].pos.vx,
						def->points[1].pos.vy,
						def->points[1].pos.vz);

				}
			}
			bits = bits >> 1;
		}

// the mr tips
		for(i = 0; i < 12; i++)
		{
			if(!(bitcounter & 31))
			{
				bits = GameState.hub_status[bitcounter>>5];
			}
			bitcounter++;

			if(bits & 1)
			{
				enemyDimTip(i + 401);	// 401... 412
			}
			bits = bits >> 1;
		}


//If we're re-entering hub1, find & disable the intro camera pan
// (Bit of a stinking bodge, this, but it works, assuming no-one puts any more look-at-plat puzzles in the hub)

		if(level == HUB1
			&& (MrTipDimmed(401) || gameInfo.keyRecordFlag==PLAYBACK || gameInfo.keyRecordFlag==RECORD)
			)
		{
			PUZZLE *puzzle;

			for(puzzle = puzzles; puzzle; puzzle = puzzle->next)
			{
				if(puzzle->actions[0].type == ACTION_LOOK_AT_PLAT)
				{
					DB(">>> Disabling puzzle\n");
					puzzle->numtimes = 0;
					break;
				}
			}
		}


	}


		if(level == ATLANTIS1
			&& (gameInfo.keyRecordFlag==PLAYBACK || gameInfo.keyRecordFlag==RECORD)
			)
		{
			PUZZLE *puzzle;

			for(puzzle = puzzles; puzzle; puzzle = puzzle->next)
			{
				if(puzzle->actions[0].type == ACTION_LOOK_AT_PLAT)
				{
					DB(">>> Disabling puzzle\n");
					puzzle->numtimes = 0;
					break;
				}
			}
		}

	GloveCtrl.lives = GameState.lives;
	GloveCtrl.health = GameState.health;
	maxHealth = GameState.maxhealth;
	GloveCtrl.maxHealth = GameState.maxhealth;


// Let's be nice & start the ball off at full whack
	BallCtrl.health = 4;	//GameState.bhealth (ball has 4 health units)

	gameScore = GameState.score;
}

// Write the updated gamestate stuff into the structure...
void UpdateGamestate()
{
	int i;
	GARIBPOS *gar;
	if(level >= ATLANTIS1 && level <= SPACEBOSS2)
	{
		short bits;
		short orval;

		gar = &garibpositions[0];

		bits = 0;
		orval = 1;

		for(i = 0; i < numberofgaribs; i++, gar++)
		{
			switch(gar->type)
			{
				case EXTRA_LIFE:
				{
					if(gar->flags & GARIBFLAG_COLLECTED)
					{
						bits |= orval;
					}
					orval = orval << 1;
				}
				break;
			}
		}

		if(numGaribs == garibscollected)
		{
			DB("Setting the All-Garibs bit\n");
			bits |= 0x8000;
		}

		GameState.lives_got[level - ATLANTIS1] |= bits;	// retain the top bit, coz it's the "extra heart" bit.

	}

// Read in the hub status
	if(level >= HUB1 && level <= HUB8)
	{
		int bitcounter;


		if(gameCtrl.port_with_ball)
		{
			GameState.hub_ball_pos.vy = 0x7fff;
		}
		else
		{
			GameState.hub_ball_pos.vx = ballPos.vx>>12;
			GameState.hub_ball_pos.vy = ballPos.vy>>12;
			GameState.hub_ball_pos.vz = ballPos.vz>>12;
		}
		GameState.hub_ball_type   = BallCtrl.type;

		gar = &garibpositions[0];
		bitcounter = 0;

		for(i = 0; i < numberofgaribs; i++, gar++)
		{
			if(gar->flags & GARIBFLAG_COLLECTED)
			{
				GameState.hub_status[bitcounter>>5] |= (1<< (bitcounter & 31));
			}
			bitcounter++;
		}

		for(i = 0; i < 4; i++)
		{
			DYNCOLLBOX *pBox;
			loadlndFindPlatform(hub_tags[i], NULL, &pBox);



			if(pBox)
			{
				if(!pBox->active)
				{
					GameState.hub_status[bitcounter>>5] |= (1<< (bitcounter & 31));
//					DB("Flagging hub crate %d as dead\n",i);
				}
				else
				{
//					DB("Flagging hub crate %d as live\n",i);

				}
			}
			else
			{
//				DB("Couldn't find hub crate %d \n",i);
			}
			bitcounter++;
		}
		for(i = 0; i < 3; i++)
		{
			DYNCOLLBOX *pBox;
			loadlndFindPlatform(hub_tags2[i], NULL, &pBox);


//	pCollBox->move.point=pCollBox->pPlatDef->head.start_point;
//	pCollBox->move.waitTimer=pCollBox->pPlatDef->points[pCollBox->move.point].wait;
//	platformSetDynamicBox(pCollBox,
//		def->points[pCollBox->pPlatDef->head.start_point].pos.vx,
//		def->points[pCollBox->pPlatDef->head.start_point].pos.vy,
//		def->points[pCollBox->pPlatDef->head.start_point].pos.vz);

			if(pBox)
			{
				if(pBox->move.point)
				{
					GameState.hub_status[bitcounter>>5] |= (1<< (bitcounter & 31));
					DB("Flagging hub switch %d as done\n",i);
				}
				else
				{
					DB("Flagging hub switch %d as live\n",i);
				}
			}
			else
			{
				DB("Couldn't find hub switch %d \n",i);
			}
			bitcounter++;
		}


// the mr tips
		for(i = 0; i < 12; i++)
		{
/*
			if(bits & 1)
			{
				enemyDimTip(i + 401);	// 401... 412
			}
*/

			if(MrTipDimmed(i+401))
			{
				DB("Mr tip %d is dimmed\n",i+401);
				GameState.hub_status[bitcounter>>5] |= (1<< (bitcounter & 31));
			}
			bitcounter++;
		}
	}

// dirty little bodge to ensure you never leave a ball in the wayroom
	if(level == WAYROOM)
	{
		if(BallCtrl.enabled)
			GloveCtrl.ballWithHand = TRUE;
	}

	GameState.lives  = GloveCtrl.lives;
	GameState.health = GloveCtrl.health;
	GameState.bhealth= BallCtrl.health;
//	GameState.maxhealth = GloveCtrl.maxHealth;	// not relevant - it's calculated at level-load time now, with "all garibs got" being stored in the save data
	GameState.score  = gameScore;
}



/*	--------------------------------------------------------------------------------
	Function 	: ParseLandscapeFile()
	Purpose 	: loads in all the meshes that make up the landscape
	Parameters 	: filename
	Returns 	: success/failure
	Info 		:
*/
//void ParseLandscapeFile(int num)


void ParseLandscapeFile(char *filename)
{
	UBYTE		*data, *realData;
	int			j,ival;
	SHORT		token,nme,val[2];
//	GARIB		*garib;
	int i;
//	char tempname[60];

	pWld = NULL;	// nasty, but we need to detect whether the lscape's been loaded yet, And it definitely hasn't (see Gameflow)

	numGaribs = 0;
	numberofgaribs = 0;
//	numberofmrtips = 0;
//	numberofsceneries = 0;

	done_a_restart = 0;
	ball_at_hoop = 1;	// set to zero if there's a ball_start in the file

	gravity = 4096; // gravity, crushing me, crushing me, crushing me

	ResetParsePlatform();
	platformInitialise();
	windInitialise();
	lightningInitialise();

// beginning & end of the file-parse
	enemiesBeginFile();
	puzzleBeginFile();
	cameoBeginFile();
	waterFlush();

	if(!filename)
	{
		puzzleEndFile();
		cameoEndFile();
		FlushParsePlatform();	// note - before the NME flush, coz we turn portals into enemies
		platformEndFile();
		enemiesEndFile();
		loadlndEndFile();
		return;
	}
	Effects_LoadShapes();
	enemiesLoadShapes();


//	data=LoadFile(filename);
	data=fileTempLoad(filename,0);
	if(!data)
	{
		puzzleEndFile();
		cameoEndFile();
		FlushParsePlatform();	// note - before the NME flush, coz we turn portals into enemies
		platformEndFile();
		enemiesEndFile();
		loadlndEndFile();

		printf("*** No LEV file has been specified for this level! (%s) ***\n", filename);
		return;
	}

/*
	if(loadlnd_load_scape)
	{
// pWld's in gameflow, externned in lscape
		pWld=(PSAWORLD *)MALLOC(sizeof(PSAWORLD), "PSAWORLD");
		pWld->nZones = 0;
		pWld->pZones=(NEWMODEL **)MALLOC(sizeof(NEWMODEL **)*MAX_WLD_ZONES, "ZONES");
	}
*/
	realData = data;
	data += 4;

//	PRINTF("\nParsing landscape file %s   (please wait!)\n", data);
	data+=strlen(data)+1;

// this was in gameflow, but since it's needed for all the object loads, here seems better
	//textureFogPaletteOff();

	while((token = *((short *)data)) != TOKEN_END)
	{
		TOKEN_PRINTF("offset &%x token %d\n",data - realData, token);
		data += 2;

		if(ParseTokenCollectable(token,&data))
		{
			TOKEN_PRINTF(" Collectable Token\n");
			continue;
		}
		if(ParseTokenGeneric(token,&data))
		{
			TOKEN_PRINTF(" Generic Token\n");
			continue;
		}
		if(cameoParseTokenCameo(token,&data))
		{
			TOKEN_PRINTF(" Cameo Token\n");
			continue;
		}
		if(puzzleParseTokenPuzzle(token,&data))
		{
			TOKEN_PRINTF("Puzzle token!\n");
			continue;
		}
		if(ParseTokenBridge(token,&data))
		{
			TOKEN_PRINTF("Bridge token!\n");
			continue;
		}
		if(enemiesParseTokenEnemy(token,&data))
		{
			TOKEN_PRINTF("Enemy token!\n");
			continue;
		}

		if(ParseTokenPlatformPath(token,&data))
		{
			TOKEN_PRINTF("Platform PathToken\n");
			continue;
		}
		if(ParseTokenPlatformGeneric(token,&data))
		{
			TOKEN_PRINTF("Platform GenericToken\n");
			continue;
		}
		if(ParseTokenPlatformInteraction(token,&data))
		{
			TOKEN_PRINTF("Platform InteractionToken\n");
			continue;
		}
		if(ParseTokenPlatformOrbit(token,&data))
		{
			TOKEN_PRINTF("Platform OrbitToken\n");
			continue;
		}
		if(ParseTokenPlatformFeatures(token,&data))
		{
			TOKEN_PRINTF("Platform FeaturesToken\n");
			continue;
		}
		if(ParseTokenPlatformSpin(token,&data))
		{
			TOKEN_PRINTF("Platform SpinToken\n");
			continue;
		}

		switch(token)
		{
// ones which were originally at the bottom of the loop
		case TOKEN_PLATFORM:
		{
			int i;
			TOKEN_PRINTF("Platform Token\n");

			FlushParsePlatform();
/*			memcpy(&current_platform.head.name[0],data+4,8);
			for(i = 0; i < 8; i++)
			{
				if(current_platform.head.name[i] == '.')
					current_platform.head.name[i] = 0;
				if(current_platform.head.name[i] >= 'a' && current_platform.head.name[i] <= 'z')
					current_platform.head.name[i] += 'A'-'a';
			}
*/
			current_platform.head.platID=*((u_short *)(data+0));
			current_platform.head.platID|=(*((u_short *)(data+2)))<<16;

//			last_platform_name = data+4;
			data += 12;
			break;
		}

		case TOKEN_VENT:
		{
			int n_patterns;
			char *work;
			VENT *vent;

			work = data + 3*2+2*12;
			n_patterns = 0;
			while(*((short *)(work)) == TOKEN_VENT_PATTERN)
			{
				work += 6;
				n_patterns++;
			}

			vent = MALLOC(sizeof(VENTHEADER) + n_patterns * sizeof(VENTPATTERN),"vent");
			vent->head.n_patterns = n_patterns;

			vent->head.type = *(short *)(data);
			vent->head.tag = *(short *)(data+2);
			vent->head.plat_tag = *(short *)(data+4);

			vent->head.pos.vx = ( *(short *)(data+ 6))<<12;
			vent->head.pos.vy = (-*(short *)(data+10))<<12;
			vent->head.pos.vz = (-*(short *)(data+14))<<12;

			vent->head.vel.vx = ( *(short *)(data+18))<<12;
			vent->head.vel.vy = (-*(short *)(data+22))<<12;
			vent->head.vel.vz = (-*(short *)(data+26))<<12;

			work = data + 3*2+2*12;
			n_patterns = 0;
			while(*((short *)(work)) == TOKEN_VENT_PATTERN)
			{
				vent->pat[n_patterns].delay = *(short *)(work + 2);
				vent->pat[n_patterns].duration = *(short *)(work + 4);
				work += 6;
				n_patterns++;
			}

			enemiesAddVent(vent);

			TOKEN_PRINTF(" Vent Token plus %d Vent patterns\n",n_patterns);
//			printf(" Vent Token plus %d Vent patterns\n",n_patterns);

			data += 3*2 + 2*12;
			data += n_patterns * 6;
			break;
		}

// we won't get any of these unless scripted had an error
		case TOKEN_VENT_PATTERN:
//			TOKEN_PRINTF(" Vent pattern Token\n");
//			printf("vent pattern\n");
			data += 4;
			break;

		case TOKEN_DUMMY:
			break;

		default:
	   		PRINTF("&%8x: un-fill-ed-in token =&%8x\n",(data-realData-2),(int)token);VSync(0);
			ASSERT(0);
			break;
		}

	}

//	PRINTF("All data parsed freeing memory\n");

	FREE((UBYTE **)realData);

	puzzleEndFile();
	cameoEndFile();
	FlushParsePlatform();	// note - before the NME flush, coz we turn portals into enemies
	platformEndFile();
	enemiesEndFile();
	loadlndEndFile();

}

 
/*	--------------------------------------------------------------------------------
	Function 	: 
	Purpose 	: 
	Parameters 	: 
	Returns 	: 
	Info 		:
*/
//WATER water1; //temp //ano
int ParseTokenGeneric(short token,UBYTE **data)
{

// Fred's work in progress - once this is going, strip any duplicates out of our
// big top-level switch...
	switch(token)
	{
	case TOKEN_AMBIENT_SOUND:
		*data += 6;
		return YES;
	case TOKEN_SOUND:
		waterParseSound((void *)(*data));
		*data += 0x20;
		return YES;

	case TOKEN_LEVEL_GRAVITY:
		gravity = *(short *)(*data);	// well, that's easy (always assuming gravity's been saved out as a 4096-normalised float)
		*data += 4;
		return YES;

	case TOKEN_TIP:
	{
//		Commented out to be replaced with enemy type
/*		MRTIPS *mrt;
		if(numberofmrtips < MAX_MR_TIPS)
		{
			mrt = &mrtippositions[numberofmrtips];
			numberofmrtips++;
			mrt->x = *(short *)(*data);
			mrt->y = -*(short *)((*data)+4);
			mrt->z = -*(short *)((*data)+8);
			mrt->num = *(short *)((*data) + 12);
//			printf("Mr Tip at %d %d %d\n",mrt->x,mrt->y,mrt->z);
		}
*/

		enemiesParseTokenTip(data);


		*data += 0x0e;

		return YES;
	}
	case TOKEN_LIGHT:
		*data += 0x0e;
		return YES;
	case TOKEN_AMBIENT_LIGHT:
		*data += 0x06;
		return YES;

/*
		ballPos.vx=glovePos.vx=BallStartPos.vx=GloveStartPos.vx=0*4096;
		ballPos.vy=glovePos.vy=BallStartPos.vy=GloveStartPos.vy=-20*4096;
		ballPos.vz=glovePos.vz=BallStartPos.vz=GloveStartPos.vz=-400*4096;
*/

// default handstart comes from a restart-point psa? ("hoop.psa")
	case TOKEN_HAND_START:
		*data += 0x0c;
		return YES;

	case TOKEN_BALL_START:
	{

		VECTOR temp;
		temp.vx =(*(short *)(*data + 0x02))<<12;
		temp.vy =-(*(short *)(*data + 0x06))<<12;
		temp.vz =-(*(short *)(*data + 0x0a))<<12;
		ballPlaceAt(&temp);
		ball_at_hoop = 0;


//		PRINTF("ball start %d %d %d\n",*(short *)(*data + 0x02),-*(short *)(*data + 0x06),-*(short *)(*data + 0x0a));
//		PRINTF("       =  %d %d %d\n",ballPos.vx,ballPos.vy,ballPos.vz);

		*data += 0x0e;
		return YES;
	}


	case TOKEN_CAMERA_START:
		*data += 20;
		return YES;
	case TOKEN_FOG:

// there's an "enable" at (+0), which I'll ignore for now...

		SETRGBC(lscapeFogColour.r, (*(*data+1)),(*(*data+2)),(*(*data+3)), GPU_COM_F4);
		SETRGBC(clscol.r, (*(*data+1)),(*(*data+2)),(*(*data+3)), GPU_COM_F4);

		lscapeFogStartZ = *(short *)(*data + 4);
		lscapeFogEndZ   = lscapeFogStartZ + 500;
		lscapeFogFlatZ  = *(short *)(*data + 6);
		if(lscapeFogEndZ > lscapeFogFlatZ)
			lscapeFogFlatZ = lscapeFogEndZ;

		*data += 8;
		return YES;


	case TOKEN_CAMEO_CAM:
		*data += 6;
		return YES;

	case TOKEN_BACKGROUND:
/*
		if(numberofsceneries < MAX_SCENERIES)
		{
			SCENERYPOS *scenery;
			scenery = &sceneries[numberofsceneries];
			numberofsceneries++;

			scenery->pos.vx = 4096 * (int)( *(short *)(*data + 0x0C));
			scenery->pos.vy = 4096 * (int)(-*(short *)(*data + 0x10));
			scenery->pos.vz = 4096 * (int)(-*(short *)(*data + 0x14));
		}
*/
		*data += 0x18;
		return YES;



	case TOKEN_LANDSCAPE:
		TOKEN_PRINTF("Landscape Token\n");

		FlushParsePlatform();

		/*
		if(loadlnd_load_scape)
		{
			unsigned long crc;
			NEWMODEL *model;
			char tempname[8];
			int i;
			short x,y,z;
// 2token, 4crc, 8string, 4x4y4z position (ignore the position - the model should be adjusted to make it zero)
			crc  = *(short *)(*data);
			crc += (*(short *)(*data+2))<<16;

			memcpy(&tempname[0],*data+4,8);
			x = *(short *)(*data+12);
			y = -(*(short *)(*data+16));
			z = -(*(short *)(*data+20));


			for(i = 0; i < 8; i++)
			{
				if(tempname[i] == '.')
					tempname[i] = 0;
				if(tempname[i] >= 'a' && tempname[i] <= 'z')
					tempname[i] += 'A'-'a';
			}



//			model = BFF_IsCRCModelLoaded(crc,NULL);
			model = BFF_IsModelLoaded(tempname,NULL);
			if(!model)
			{
//				printf("landscape model not found. crc &%8x\n",crc);
//				printf("landscape model not found. name %s\n",tempname);
//				CRASH;
				char temp[40];
				int store[2];
				store[0] = modelctrl.halfgouraud;
				store[1] = fogpalette;

				modelctrl.halfgouraud = TRUE;
				fogpalette = 1;

				sprintf(temp,"WORLDS\\%s\\PLAT\\%s.PSA",WorldDirectoryNames[world],tempname);
				model = LoadPSA(temp);
				if(!model)
				{
					printf("landscape model not found. name %s\n",tempname);
					CRASH;
				}
				modelctrl.halfgouraud = store[0];
				fogpalette = store[1];

			}

// If the object ain't at zero
//			PRINTF("landscape model %s\n",tempname);

			if(x || y || z)
			{
				VERT *pVerts = model->world.meshdata->vertop;
				int nVerts = model->world.meshdata->vern;
				for(i=0; i<nVerts; i++)
				{
					pVerts[i].vx += x;
					pVerts[i].vy += y;
					pVerts[i].vz += z;
				}
				model->world.meshdata->boundminx += x;
				model->world.meshdata->boundminy += y;
				model->world.meshdata->boundminz += z;
				model->world.meshdata->boundmaxx += x;
				model->world.meshdata->boundmaxy += y;
				model->world.meshdata->boundmaxz += z;
			}

// (adjust coords here till I do pre-parsing of the lnd ?)
			if(pWld->nZones < MAX_WLD_ZONES)
			{
				pWld->pZones[pWld->nZones] = model;
				pWld->nZones++;
			}
		}
*/
		*data += 0x18;
		return YES;
	case TOKEN_LANDROT:
		*data += 0x0c;
		return YES;
	case TOKEN_LANDSCALE:
		*data += 0x0c;
		return YES;

	case TOKEN_WIND:
		// create wind
		{
			WIND_DEFSTR *pWind=windCreate();
			ASSERT(pWind);

			pWind->pos.vx=*(short *)(*data+0);
			pWind->pos.vy=-(*(short *)(*data+4)+*(short *)(*data +16));
			pWind->pos.vz=-(*(short *)(*data+8)+*(short *)(*data +20));

			pWind->size.vx=*(short *)(*data+12);
			pWind->size.vy=*(short *)(*data+16);
			pWind->size.vz=*(short *)(*data+20);

			pWind->dir.vx=*(short *)(*data+24);
			pWind->dir.vy=*(short *)(*data+28);
			pWind->dir.vz=*(short *)(*data+32);

			pWind->str=*(short *)(*data+36);
			pWind->dropOff=*(short *)(*data+40);
			pWind->active=*(short *)(*data+44);
			pWind->tag=*(short *)(*data+48);

			//DB("Successfully created wind. (Tag = %d)\n", pWind->tag);
			//DB("BBOX POS = (%d,%d,%d), \n", pWind->pos.vx, pWind->pos.vy, pWind->pos.vz);
			//DB("SIZE = (%d,%d,%d)\n", pWind->size.vx, pWind->size.vy, pWind->size.vz);
			//CRASH;

			*data += 12*3 + 4*4;
			return YES;
		}
	case TOKEN_WATER:
		waterParseToken(*data);
		*data += 0x3e;
		return YES;

	case TOKEN_BACKDROP:
		*data += 26;
		return YES;

	default:
		return NO;
	}

}


/*	--------------------------------------------------------------------------------
	Function 	: 
	Purpose 	: 
	Parameters 	: 
	Returns 	: 
	Info 		:
*/



int ParseTokenCollectable(short token,UBYTE **pdata)
{
	UBYTE *data;
	static SHORT GroupTag;
	static SHORT GroupActive;

	data = *pdata;

	switch(token)
	{
		case TOKEN_GARIBGROUP:
//			printf("&%8x: GARIBGROUP token\n",(data-realData-2));VSync(0);
//			printf(" GARIBGROUP token\n");
			GroupTag = *(SHORT *)data;
			GroupActive = *(SHORT *)(data+2);
			data+=0x04;
			*pdata = data;
			return YES;

		case TOKEN_GARIB:
		{
			GARIBPOS *gar;

			if(numberofgaribs < MAX_GARIBS)
			{
				gar = &garibpositions[numberofgaribs];
// note that while the file contains "ints", we can't read 'em in
// as ints, coz they're 2-aligned not 4-aligned.
				gar->x =  (*(short *)(data+0));
				gar->y = -(*(short *)(data+4));
				gar->z = -(*(short *)(data+8));
				gar->type = *(short *)(data+12);
				if(gar->type == SUPER_GARIB)
					gar->type = NORMAL_GARIB;

//				gar->num = numberofgaribs;
				if(GroupActive)
				{
					gar->flags = GARIBFLAG_DISPLAY;
				}
				else
				{
					gar->flags = 0;
				}
				gar->group = GroupTag;
				gar->shadowheight = 0x7fff;

				if(gar->type != EXTRA_LIFE)
				{
					numGaribs++;
				}
				numberofgaribs++;
			}

//			printf("&%8x: GARIB token xyz = %i %i %i\n",(data-realData-2),(int)gar->x,(int)gar->y,(int)gar->z);VSync(0);
//			printf("&%8x: GARIB token\n",(data-realData-2));VSync(0);
//			printf(" GARIB token\n");

			data+=0x10;
			*pdata = data;
			return YES;
		}



		default:
			return NO;
	}


	return NO;
}

/*	--------------------------------------------------------------------------------
	Function 	: 
	Purpose 	: 
	Parameters 	: 
	Returns 	: 
	Info 		:
*/
int ParseTokenPlatformInteraction(short token,UBYTE **data)
{
//	DB("TokenInteraction\n");

	switch(token)
	{
		case TOKEN_SPINTOMOVE:
			*data += 6;
			return YES;
		case TOKEN_MOVETOSPIN:
			*data += 18;
			return YES;
		case TOKEN_SPINTOSPIN:
			*data += 6;
			return YES;
		case TOKEN_PARENTTAG:
			current_platform.head.parentTag=*(short *)(*data+0);
			TOKEN_PRINTF("Parent Tag=%d\n", current_platform.head.parentTag);
			*data += 2;
			return YES;
		case TOKEN_MOVETARGET:
			*data += 14;
			return YES;
		case TOKEN_MOVEBYSPEED:
			*data += 12;
			return YES;
		case TOKEN_TAKESPIN:
			return YES;
		default:
			return NO;
	}

	return NO;
}

/*	--------------------------------------------------------------------------------
	Function 	: 
	Purpose 	: 
	Parameters 	: 
	Returns 	: 
	Info 		:
*/
int ParseTokenBridge(short token,UBYTE **data)
{
	switch(token)
	{
		case TOKEN_ROPEBRIDGE:
			*data += 4+2+4+8 + 7*4 + 4*6;
			return YES;

		case TOKEN_SINEBRIDGE:
			*data += 4*13 + 8;
			return YES;

		case TOKEN_ORBITBRIDGE:
			*data += 2+6 + 8 + 4*11;
			return YES;
		default:
			return NO;
	}

	return NO;
}

/*	--------------------------------------------------------------------------------
	Function 	: 
	Purpose 	: 
	Parameters 	: 
	Returns 	: 
	Info 		:
*/
int ParseTokenPlatformFeatures(short token,UBYTE **data)
{
	switch(token)
	{
		case TOKEN_RECALC:
			return YES;
		case TOKEN_MODGE:
			return YES;
		case TOKEN_COLL_OFFSET:
			*data += 4;
			return YES;
		case TOKEN_BUZZER:
		{
/*
			LIGHTNING_DEFHANDLE next, prev;
			short tag, flags;
			short p1tag, p2tag;
			RGBCD colour;
			VECTOR p1;
			VECTOR p2;
			short jagginess;
			short thickness;
*/
			LIGHTNING_DEFSTR *pL=lightningCreate();
			
			pL->tag=*(short *)((*data)+0);
			pL->pTag[0]=*(short *)((*data)+2);
			pL->pTag[1]=*(short *)((*data)+4);
			pL->flags=*(short *)((*data)+6);
			SETRGBC(pL->colour,	*(short *)((*data)+8), *(short *)((*data)+10),
								*(short *)((*data)+12), *(short *)((*data)+14));

			*data += 2+4+2+8+12+12+4+4;
			return YES;
		}
		case TOKEN_BUZZER_PATTERN:
			*data += 4;
			return YES;

		case TOKEN_SPARKLY:
			*data += 2;
			return YES;
		case TOKEN_NOCAMCOLLIDE:
			current_platform.head.flags|=PLATFLAG_NOCAMCOLLISION;
			return YES;
		case TOKEN_EXITDOOR:
//			current_platform.head.platID=str2CRC("EXITPOS");	// replace the dummy psa name
			current_platform.head.flags |= PLATFLAG_EXITDOOR;
			*data += 4;
			return YES;
		case TOKEN_CATAPULT:
			*data += 2+12+4+4+4;
			return YES;

		case TOKEN_CONVEYOR:
			*data += 12;
			return YES;
		case TOKEN_CURRENT:
			*data += 12+4;
			return YES;
		case TOKEN_TELEPORT:
			teleport_def = (void *)(*data);
			teleport_def->y = -teleport_def->y;
			teleport_def->z = -teleport_def->z;
//			current_platform.head.platID=str2CRC("TELEPORT");	// replace the dummy psa name
			current_platform.head.flags |= PLATFLAG_TELEPORT;
			*data += 4+2+2+12;
			return YES;

		case TOKEN_FAN:
			*data += 2+12+4*4;
			return YES;

		case TOKEN_MAGNET:
			current_platform.head.magnet.yes=TRUE;
			*data += 2+12+4*4;
			return YES;

// The restarts are done in order, so the first one's the starting point
		case TOKEN_RESTART:
//			current_platform.head.restart = 0;	// two bytes of "shadow", 4 of "rotaim"

			current_platform.head.flags |= PLATFLAG_IS_RESTART;
//			current_platform.head.platID=str2CRC("HOOP");	// replace the dummy psa name
			current_platform.head.flags |= PLATFLAG_HOOP;

//			{
//				DB("start data = %d %d %d\n",*(short *)(*data),*(short *)(*data+2),*(short *)(*data+4));
//			}
			restart_ya = *(short *)(*data+2);

// ok, it's back...
			if(!done_a_restart)
			{
				int x,y,z;
//				x = current_scenery->pos.vx;
//				y = current_scenery->pos.vy;
//				z = current_scenery->pos.vz;

				x = current_platform.points[0].pos.vx;
				y = current_platform.points[0].pos.vy;
				z = current_platform.points[0].pos.vz;

//				if(getHeightAt(x,y,z) != -1)
				if(getHeightAt(x,y,z) < 0x7fffffff)
				{

					GloveStartPos.vx = BallStartPos.vx = x;
					GloveStartPos.vy = BallStartPos.vy = y;
					GloveStartPos.vz = BallStartPos.vz = z;
					done_a_restart = 1;
				}
			}

			*data += 6;
			return YES;

		case TOKEN_CRUMBLE:
			current_platform.head.crumble.limit=*(short *)((*data)+0);
			current_platform.head.crumble.time=*(short *)((*data)+2);
			current_platform.head.crumble.grav=*(short *)((*data)+4);

			current_platform.head.flags|=PLATFLAG_CRUMBLE;
			*data += 2+2+4;
			return YES;
		case TOKEN_CRUMBLE_SOUND:
			current_platform.head.dest_sfx  = (*(short *)(*data + 0x00));
			current_platform.head.dest_pitch= (*(short *)(*data + 0x04));
			current_platform.head.dest_vol  = (*(short *)(*data + 0x02));
			*data += 6;
			return YES;
		case TOKEN_BOUNCE:
			*data += 6;
			return YES;
		case TOKEN_ELECTRIC:
			*data += 2;
			return YES;
		case TOKEN_PUSH:
			current_platform.head.confine.mass=*(short *)((*data)+2);
			*data += 2+3*4;
			return YES;
		case TOKEN_CONFINE:
		{
			// this now works, so leave it alone!
			int aX=*(short *)((*data)+12);
			int aY=*(short *)((*data)+16);
			int aZ=*(short *)((*data)+20);

			int pX=*(short *)((*data)+0);
			int pY=*(short *)((*data)+4);
			int pZ=*(short *)((*data)+8);

			if(aX==-1)
				aX=0;
			if(aY==-1)
				aY=0;
			if(aZ==-1)
				aZ=0;

			current_platform.head.confine.pX=pX;
			current_platform.head.confine.pY=pY;
			current_platform.head.confine.pZ=(pZ+aZ);

			current_platform.head.confine.aX=(pX-aX);
			current_platform.head.confine.aY=aY+pY;
			current_platform.head.confine.aZ=current_platform.head.confine.pZ-aZ;

			current_platform.head.confine.yes=YES;

			*data += 12*2;
			return YES;
		}
		default:
			return NO;
	}

	return NO;
}

/*	--------------------------------------------------------------------------------
	Function 	: 
	Purpose 	: 
	Parameters 	: 
	Returns 	: 
	Info 		:
*/
int ParseTokenPlatformOrbit(short token,UBYTE **data)
{
	switch(token)
	{
		case TOKEN_ORBIT_SOUND:
			*data += 6;
			return YES;
		case TOKEN_ORBIT_STOP_SOUND:
			*data += 6;
			return YES;
		case TOKEN_ORBIT:
			*data += 2+12+4;
			return YES;
		case TOKEN_ORBITPAUSE:
			*data += 4;
			return YES;
		case TOKEN_ORBITFLIP:
			*data += 6;
			return YES;
		default:
			return NO;
	}

	return NO;
}

/*	--------------------------------------------------------------------------------
	Function 	: 
	Purpose 	: 
	Parameters 	: 
	Returns 	: 
	Info 		:
*/
int ParseTokenPlatformSpin(short token,UBYTE **data)
{
	switch(token)
	{
		case TOKEN_SPIN_SOUND:
			current_platform.head.move_sfx  = (*(short *)(*data + 0x00));
			current_platform.head.move_pitch= (*(short *)(*data + 0x04));
			current_platform.head.move_vol  = (*(short *)(*data + 0x02));
			*data += 6;
			return YES;
		case TOKEN_SPIN_STOP_SOUND:
			current_platform.head.stop_sfx  = (*(short *)(*data + 0x00));
			current_platform.head.stop_pitch= (*(short *)(*data + 0x04));
			current_platform.head.stop_vol  = (*(short *)(*data + 0x02));
			*data += 6;
			return YES;
		case TOKEN_TILTY:
			*data += 16;
			return YES;
		case TOKEN_SPINPAUSE:
			current_platform.head.spinpause.yes=YES;
			current_platform.head.spinpause.times=*((short *)((*data) + 0));
			current_platform.head.spinpause.wait=*((short *)((*data) + 2));

			current_platform.head.spinpause.angle=4096/current_platform.head.spinpause.times;

			*data += 4;
			return YES;
		case TOKEN_SPINFLIP:
			current_platform.head.spinflip.yes=YES;
			current_platform.head.spinflip.wait=*((short *)((*data) + 0));
			current_platform.head.spinflip.angle=*((short *)((*data) + 2));

			*data += 6;
			return YES;
		case TOKEN_MAXROT:
			*data += 4;
			return YES;
		case TOKEN_SPIN:
			current_platform.head.spin.yes=YES;
			current_platform.head.spin.axis=*((short *)((*data) + 0));
			current_platform.head.spin.initrot=*((short *)((*data) + 2));
			current_platform.head.spin.rotspeed=*((short *)((*data) + 6));

			*data += 10;
			return YES;

		case TOKEN_SEESAW:
			current_platform.head.seesaw.yes=YES;
			current_platform.head.seesaw.axis=*((short *)(*data));
			current_platform.head.seesaw.inertia=4055;
			current_platform.head.seesaw.levelout=*((short *)((*data) + 2));
			current_platform.head.seesaw.maxrot=*((short *)((*data) + 0x6));
			current_platform.head.seesaw.initrot=0;
			current_platform.head.seesaw.maxspeed=1;
			current_platform.head.seesaw.mass=(*((short *)((*data) + 22)));

			if(current_platform.head.seesaw.mass>0)
				current_platform.head.seesaw.mass=16777216/current_platform.head.seesaw.mass;

			DB("TOKEN_SEESAW %d\n", current_platform.head.seesaw.maxrot);

			*data += 5*4 + 2*2;
			return YES;
		case TOKEN_TOPPLE:
			current_platform.head.topple.yes=YES;
			current_platform.head.topple.axis=*((short *)(*data));
			current_platform.head.topple.angle=*((short *)((*data) + 0xa));


			*data += 6*4 + 2*2;
			return YES;
		case TOKEN_LOOKATHAND:
			*data += 8;
			return YES;
		case TOKEN_LOOKATBALL:
			*data += 8;
			return YES;
		case TOKEN_PENDULUM:
			current_platform.head.pendulum.yes=YES;
			current_platform.head.pendulum.axis=*((short *)(*data));
			current_platform.head.pendulum.angle=*((short *)((*data) + 0x02));
			current_platform.head.pendulum.friction=0;
			current_platform.head.pendulum.headstart=*((short *)((*data) + 0xa)); // (is really swish factor)

			DB("TOKEN_PENDULUM: headstart = %d\n", current_platform.head.pendulum.headstart);

			*data += 3*4 + 2*2;
			return YES;
		case TOKEN_SYMMETRY:
			*data += 2;
			return YES;

		default:
			return NO;
	}

	return NO;
}

/*	--------------------------------------------------------------------------------
	Function 	: 
	Purpose 	: 
	Parameters 	: 
	Returns 	: 
	Info 		:
*/
int ParseTokenPlatformPath(short token,UBYTE **data)
{
// Fred mods again
	switch(token)
	{
		case TOKEN_MOVE_SOUND:
			current_platform.head.move_sfx  = (*(short *)(*data + 0x00));
			current_platform.head.move_pitch= (*(short *)(*data + 0x04));
			current_platform.head.move_vol  = (*(short *)(*data + 0x02));
			*data += 6;
			return YES;
		case TOKEN_STOP_SOUND:
			current_platform.head.stop_sfx  = (*(short *)(*data + 0x00));
			current_platform.head.stop_pitch= (*(short *)(*data + 0x04));
			current_platform.head.stop_vol  = (*(short *)(*data + 0x02));
			*data += 6;
			return YES;
		case TOKEN_LOOKATPOINT:
			*data += 8;
			return YES;
		case TOKEN_GOFORWARDS:
			*data += 16;
			return YES;

		case TOKEN_POS:
			current_platform.points[0].pos.vx = 4096 * (int)(*(short *)(*data + 0x00));
			current_platform.points[0].pos.vy = 4096 * (int)(-*(short *)(*data + 0x04));
			current_platform.points[0].pos.vz = 4096 * (int)(-*(short *)(*data + 0x08));
			current_platform.points[0].wait = 0;
			*data += 12;
			return YES;

		case TOKEN_POINT:
			current_platform.points[(int)current_platform.head.n_points].wait   = * ((short *)(*data + 0x00));	// -ves are special
			current_platform.points[(int)current_platform.head.n_points].pos.vx = 4096 * ((int)(*(short *)(*data + 0x02)));
			current_platform.points[(int)current_platform.head.n_points].pos.vy = 4096 * ((int)(-*(short *)(*data + 0x06)));
			current_platform.points[(int)current_platform.head.n_points].pos.vz = 4096 * ((int)(-*(short *)(*data + 0x0a)));
			current_platform.head.n_points++;
			*data += 14;
			return YES;

		case TOKEN_STARTPOINT:	// short, copies the point & the waittime into pos, & the current/next point numbers
			current_platform.head.start_point = *(short *)(*data+0);
			*data += 2;
			return YES;

		case TOKEN_SPEED:
//			current_platform.head.maxspeed = *(short *)(*data+0);
//			PRINTF("PLATFORM SPEED %d\n",current_platform.head.maxspeed);

			current_platform.head.maxspeed  = *((u_short *)(*data+0));
			current_platform.head.maxspeed |= (*((u_short *)(*data+2)))<<16;


			*data += 4;
			return YES;
		case TOKEN_ACCEL:
//			current_platform.head.accel = *(short *)(*data+0);
			current_platform.head.accel  = *((u_short *)(*data+0));
			current_platform.head.accel |= (*((u_short *)(*data+2)))<<16;
			*data += 4;
			return YES;
		default:
			return NO;
	}

	return NO;
}

/*	--------------------------------------------------------------------------------
	Function 	: 
	Purpose 	: 
	Parameters 	: 
	Returns 	: 
	Info 		:
*/
int ParseTokenPlatformGeneric(short token,UBYTE **data)
{
	switch(token)
	{
		case TOKEN_ANIMATE_PLATFORM:
			return YES;
		case TOKEN_FLOATS:
			return YES;
		case TOKEN_HEADSTART:
			*data += 2;
			return YES;

		case TOKEN_NOCOLLISION:
//			current_platform.head.nocollision = 1;
			current_platform.head.flags |= PLATFLAG_NOCOLLISION;
			return YES;

		case TOKEN_DESTRUCTIBLE:
		{
//			*data += 18;	// this might not actually be the case
			SHORT val;
			val = *((short *)(*data+0));
			if(val & 1)
				current_platform.head.flags |= PLATFLAG_DESTRUCTIBLE;
			if(val & 2)
				current_platform.head.flags |= PLATFLAG_DESTRUCT_BALL;
			if(val & 4)
				current_platform.head.flags |= PLATFLAG_DESTRUCT_NODUST;

			current_platform.head.destruct.nBits = *((short *)(*data+2));
			DB("nBits=%d\n", current_platform.head.destruct.nBits);

			if(current_platform.head.destruct.nBits>MAX_OBJDEBRIS)
				current_platform.head.destruct.nBits=MAX_OBJDEBRIS;

			//DB("nBits=%d\n", current_platform.head.destruct.nBits);

//			PRINTF("platform n_bits = %d\n",n_bits);
//			*data += (6 + n_bits *12);	// lies, all lies..

			//DB("Fragment %x, %s\n", *((u_long *)(*data+6)), (*data+10));
			//memcpy(current_platform.head.sFragName, (*data+10), 8);
			//current_platform.head.fragID=*((u_long *)(*data+6));

			DB("*data+6 = %x, *data+8 = %x\n", *((short *)(*data+6)), *((short *)(*data+8)));

			current_platform.head.destruct.fragID=*((USHORT *)(*data+6));
			current_platform.head.destruct.fragID|=(*((USHORT *)(*data+8)))<<16;

			DB("fragID= %x\n", current_platform.head.destruct.fragID);

			*data += 18;	// this might not actually be the case

			return YES;
		}

		case TOKEN_DESTROY_SOUND:
			current_platform.head.dest_sfx  = (*(short *)(*data + 0x00));
			current_platform.head.dest_pitch= (*(short *)(*data + 0x04));
			current_platform.head.dest_vol  = (*(short *)(*data + 0x02));
			*data += 6;
			return YES;
		case TOKEN_GRAVITY:
			return YES;
		case TOKEN_REVERSING:
			return YES;
		case TOKEN_TERRAIN:
//			DB("Token Terrain %d\n",(int)(*(USHORT *)(*data)));
			current_platform.head.terrain = *(USHORT *)(*data);
			*data += 2;
			return YES;

		case TOKEN_TAG:
			current_platform.head.tag = *(short *)(*data);
			*data += 2;
			return YES;

		case TOKEN_SPIKE:
			return YES;
		case TOKEN_SCALE:
			*data += 12;
			return YES;
		case TOKEN_STRETCH:
			*data += 16;
			return YES;
		default:
			return NO;
	}


	return NO;
}


void loadlndFindPlatform(SHORT tag, PLATFORM_DEFSTR **plat_ret,DYNCOLLBOX **box_ret)
{
	PLATFORM_DEFSTR *def;
	DYNCOLLBOX *ptr;

	for (def = loadlnd_PlatDefs; def; def = def->head.next)
	{
		if(def->head.tag == tag)
		{
			if(plat_ret)
				*plat_ret = def;
			if(!box_ret)
				return;

			for(ptr = dyncollList.head.next; ptr != &dyncollList.head; ptr = ptr->next)
			{
				if(ptr->pPlatDef == def)
				{
					*box_ret = ptr;
					return;
				}
			}
			*box_ret = NULL;
		}
	}

// this gets printed rather a lot & slows stuff down (!)
//	DB("WARNING: platform not found\n");

	if(plat_ret)
		*plat_ret = NULL;
	if(box_ret)
		*box_ret = NULL;
}

ENEMYPOS *loadlndFindEnemy(SHORT tag)
{
	ENEMYPOS *nme;

	for (nme = nme_list; nme; nme = nme->next)
	{
		if(nme->tag == tag)
		{
			return nme;
		}
	}
	return NULL;
}

