/*
	This file is part of Warzone 2100.
	Copyright (C) 1999-2004  Eidos Interactive
	Copyright (C) 2005-2007  Warzone Resurrection Project

	Warzone 2100 is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	Warzone 2100 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Warzone 2100; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
 * Levels.c
 *
 * Control the data loading for game levels
 *
 */

#include <ctype.h>

// levLoadData printf's
#define DEBUG_GROUP0
#include "lib/framework/frame.h"
#include "init.h"
#include "objects.h"
#include "hci.h"
#include "levels.h"
#include "mission.h"
#include "levelint.h"
#include "game.h"
#include "lighting.h"
#include "lib/ivis_common/piestate.h"
#include "data.h"
#include "lib/ivis_common/ivi.h"

//#ifdef DEBUG
#include "lib/script/script.h"
#include "scripttabs.h"
//#endif



// minimum type number for a type instruction
#define MULTI_TYPE_START	10


// block ID number start for the current level data (as opposed to a dataset)
#define CURRENT_DATAID		LEVEL_MAXFILES

static	char	currentLevelName[32];

// the current level descriptions
LEVEL_DATASET	*psLevels;

// the currently loaded data set
LEVEL_DATASET	*psBaseData;
LEVEL_DATASET	*psCurrLevel;

// dummy level data for single WRF loads
LEVEL_DATASET	sSingleWRF;

// return values from the lexer
STRING *pLevToken;
SDWORD levVal;
SDWORD levelLoadType;
// modes for the parser
enum
{
	LP_START,		// no input received
	LP_LEVEL,		// level token received
	LP_LEVELDONE,	// defined a level waiting for players/type/data
	LP_PLAYERS,		// players token received
	LP_TYPE,		// type token received
	LP_DATASET,		// dataset token received
	LP_WAITDATA,	// defining level data, waiting for data token
	LP_DATA,		// data token received
	LP_GAME,		// game token received
};

/*// the current data file to parse
static UBYTE	*pDataFile;
static SDWORD	dataFileSize;

// the current position in the data file
static UBYTE	*pDataPtr;
static SDWORD	levLine;

// the token buffer
#define TOKEN_MAX	255
static STRING	aTokenBuff[TOKEN_MAX];
*/

// initialise the level system
BOOL levInitialise(void)
{
	psLevels = NULL;
	psBaseData = NULL;
	psCurrLevel = NULL;

	return TRUE;
}

SDWORD getLevelLoadType(void)
{
	return levelLoadType;
}

// shutdown the level system
void levShutDown(void)
{
	LEVEL_DATASET	*psNext;
	SDWORD			i;

	while (psLevels)
	{
		FREE(psLevels->pName);
		for(i=0; i<LEVEL_MAXFILES; i++)
		{
			if (psLevels->apDataFiles[i] != NULL)
			{
				FREE(psLevels->apDataFiles[i]);
			}
		}
		psNext = psLevels->psNext;
		FREE(psLevels);
		psLevels = psNext;
	}
}


// error report function for the level parser
void levError(STRING *pError)
{
	char	*pText;
	int		line;

	levGetErrorData(&line, &pText);

#ifdef DEBUG
	ASSERT( FALSE, "Level File parse error:\n%s at line %d text %s\n", pError, line, pText );
#else
	debug( LOG_ERROR, "Level File parse error:\n%s at line %d text %s\n", pError, line, pText );
#endif
}

// find the level dataset
BOOL levFindDataSet(STRING *pName, LEVEL_DATASET **ppsDataSet)
{
	LEVEL_DATASET	*psNewLevel;

	for(psNewLevel = psLevels; psNewLevel; psNewLevel = psNewLevel->psNext)
	{
		if (psNewLevel->pName != NULL)
		{
			if (strcmp(psNewLevel->pName, pName) == 0)
			{
				*ppsDataSet = psNewLevel;
				return TRUE;
			}
		}
	}

	return FALSE;
}

// parse a level description data file
BOOL levParse(char *pBuffer, SDWORD size, int datadir)
{
	SDWORD			token, state, currData=0;
	LEVEL_DATASET	*psDataSet = NULL;
	LEVEL_DATASET	*psFoundData;

	levSetInputBuffer(pBuffer, size);

	state = LP_START;
	token = lev_lex();
	while (token != 0)
	{
		switch (token)
		{
		case LTK_LEVEL:
		case LTK_CAMPAIGN:
		case LTK_CAMSTART:
		case LTK_CAMCHANGE:
		case LTK_EXPAND:
		case LTK_BETWEEN:
		case LTK_MKEEP:
		case LTK_MCLEAR:
        case LTK_EXPAND_LIMBO:
        case LTK_MKEEP_LIMBO:
			if (state == LP_START || state == LP_WAITDATA)
			{
				// start a new level data set
				psDataSet = MALLOC(sizeof(LEVEL_DATASET));
				if (!psDataSet)
				{
					levError("Out of memory");
					return FALSE;
				}
				memset(psDataSet, 0, sizeof(LEVEL_DATASET));
				psDataSet->players = 1;
				psDataSet->game = -1;
				psDataSet->dataDir = datadir;
				LIST_ADDEND(psLevels, psDataSet, LEVEL_DATASET);
				currData = 0;

				// set the dataset type
				switch (token)
				{
				case LTK_LEVEL:
					psDataSet->type = LDS_COMPLETE;
					break;
				case LTK_CAMPAIGN:
					psDataSet->type = LDS_CAMPAIGN;
					break;
				case LTK_CAMSTART:
					psDataSet->type = LDS_CAMSTART;
					break;
				case LTK_BETWEEN:
					psDataSet->type = LDS_BETWEEN;
					break;
				case LTK_MKEEP:
					psDataSet->type = LDS_MKEEP;
					break;
				case LTK_CAMCHANGE:
					psDataSet->type = LDS_CAMCHANGE;
					break;
				case LTK_EXPAND:
					psDataSet->type = LDS_EXPAND;
					break;
				case LTK_MCLEAR:
					psDataSet->type = LDS_MCLEAR;
					break;
				case LTK_EXPAND_LIMBO:
					psDataSet->type = LDS_EXPAND_LIMBO;
					break;
				case LTK_MKEEP_LIMBO:
					psDataSet->type = LDS_MKEEP_LIMBO;
					break;
				default:
					ASSERT( FALSE,"eh?" );
					break;
				}
			}
			else
			{
				levError("Syntax Error");
				return FALSE;
			}
			state = LP_LEVEL;
			break;
		case LTK_PLAYERS:
			if (state == LP_LEVELDONE &&
				(psDataSet->type == LDS_COMPLETE || psDataSet->type >= MULTI_TYPE_START))
			{
				state = LP_PLAYERS;
			}
			else
			{
				levError("Syntax Error");
				return FALSE;
			}
			break;
		case LTK_TYPE:
			if (state == LP_LEVELDONE && psDataSet->type == LDS_COMPLETE)
			{
				state = LP_TYPE;
			}
			else
			{
				levError("Syntax Error");
				return FALSE;
			}
			break;
		case LTK_INTEGER:
			if (state == LP_PLAYERS)
			{
				psDataSet->players = (SWORD)levVal;
			}
			else if (state == LP_TYPE)
			{
				if (levVal < MULTI_TYPE_START)
				{
					levError("invalid type number");
					return FALSE;
				}

				psDataSet->type = (SWORD)levVal;
			}
			else
			{
				levError("Syntax Error");
				return FALSE;
			}
			state = LP_LEVELDONE;
			break;
		case LTK_DATASET:
			if (state == LP_LEVELDONE && psDataSet->type != LDS_COMPLETE)
			{
				state = LP_DATASET;
			}
			else
			{
				levError("Syntax Error");
				return FALSE;
			}
			break;
		case LTK_DATA:
			if (state == LP_WAITDATA)
			{
				state = LP_DATA;
			}
			else if (state == LP_LEVELDONE)
			{
				if (psDataSet->type == LDS_CAMSTART ||
					psDataSet->type == LDS_MKEEP
					||psDataSet->type == LDS_CAMCHANGE ||
					psDataSet->type == LDS_EXPAND ||
					psDataSet->type == LDS_MCLEAR ||
                    psDataSet->type == LDS_EXPAND_LIMBO ||
                    psDataSet->type == LDS_MKEEP_LIMBO
					)
				{
					levError("Missing dataset command");
					return FALSE;
				}
				state = LP_DATA;
			}
			else
			{
				levError("Syntax Error");
				return FALSE;
			}
			break;
		case LTK_GAME:
			if ((state == LP_WAITDATA || state == LP_LEVELDONE) &&
				psDataSet->game == -1 && psDataSet->type != LDS_CAMPAIGN)
			{
				state = LP_GAME;
			}
			else
			{
				levError("Syntax Error");
				return FALSE;
			}
			break;
		case LTK_IDENT:
			if (state == LP_LEVEL)
			{
				if (psDataSet->type == LDS_CAMCHANGE)
				{
					// campaign change dataset - need to find the full data set
					if (!levFindDataSet(pLevToken, &psFoundData))
					{
						levError("Cannot find full data set for camchange");
						return FALSE;
					}

					if (psFoundData->type != LDS_CAMSTART)
					{
						levError("Invalid data set name for cam change");
						return FALSE;
					}
					psFoundData->psChange = psDataSet;
				}
				// store the level name
				psDataSet->pName = MALLOC(strlen(pLevToken) + 1);
				if (!psDataSet->pName)
				{
					levError("Out of memory");
					return FALSE;
				}
				strcpy(psDataSet->pName, pLevToken);
				state = LP_LEVELDONE;
			}
			else if (state == LP_DATASET)
			{
				// find the dataset
				if (!levFindDataSet(pLevToken, &psDataSet->psBaseData))
				{
					levError("Unknown dataset");
					return FALSE;
				}
				state = LP_WAITDATA;
			}
			else
			{
				levError("Syntax Error");
				return FALSE;
			}
			break;
		case LTK_STRING:
			if (state == LP_DATA || state == LP_GAME)
			{
				if (currData >= LEVEL_MAXFILES)
				{
					levError("Too many data files");
					return FALSE;
				}

				// note the game index if necessary
				if (state == LP_GAME)
				{
					psDataSet->game = (SWORD)currData;
				}

				// store the data name
				psDataSet->apDataFiles[currData] = MALLOC(strlen(pLevToken) + 1);
				if (!psDataSet->apDataFiles[currData])
				{
					levError("Out of memory");
					return FALSE;
				}
				resToLower(pLevToken);
				strcpy(psDataSet->apDataFiles[currData], pLevToken);

				currData += 1;
				state = LP_WAITDATA;
			}
			else
			{
				levError("Syntax Error");
				return FALSE;
			}
			break;
		default:
			levError("Unexpected token");
			break;
		}

		// get the next token
		token = lev_lex();
	}

	if (state != LP_WAITDATA || currData == 0)
	{
		levError("Unexpected end of file");
		return FALSE;
	}

	return TRUE;
}


// free the data for the current mission
BOOL levReleaseMissionData(void)
{
	SDWORD i;

	// release old data if any was loaded
	if (psCurrLevel != NULL)
	{
		if (!stageThreeShutDown())
		{
			return FALSE;
		}

		if ((psCurrLevel->type == LDS_COMPLETE ||
			 psCurrLevel->type >= MULTI_TYPE_START) && psCurrLevel->game == -1)
		{
			BLOCK_RESET(psMissionHeap);
		}

		// free up the old data
		for(i=LEVEL_MAXFILES-1; i >= 0; i--)
		{
			if (i == psCurrLevel->game)
			{
				BLOCK_RESET(psMissionHeap);
				if (psCurrLevel->psBaseData == NULL)
				{
					if (!stageTwoShutDown())
					{
						return FALSE;
					}
				}
			}
			else// if (psCurrLevel->apDataFiles[i])
			{

				resReleaseBlockData(i + CURRENT_DATAID);
			}
		}
		if (psCurrLevel->type == LDS_BETWEEN)
		{
			BLOCK_RESET(psMissionHeap);
		}
	}
	return TRUE;
}


// free the currently loaded dataset
BOOL levReleaseAll(void)
{
	SDWORD i;

	// release old data if any was loaded
	if (psCurrLevel != NULL)
	{
		if (!levReleaseMissionData())
		{
			return FALSE;
		}

		// release the game data
		if (psCurrLevel->psBaseData != NULL)
		{
			if (!stageTwoShutDown())
			{
				return FALSE;
			}
		}


		if (psCurrLevel->psBaseData)
		{
			for(i=LEVEL_MAXFILES-1; i >= 0; i--)
			{
				if (psCurrLevel->psBaseData->apDataFiles[i])
				{
					resReleaseBlockData(i);
				}
			}
		}

		if (!stageOneShutDown())
		{
			return FALSE;
		}

		BLOCK_RESET(psGameHeap);
	}

	psCurrLevel=NULL;

	return TRUE;
}

// load up a single wrf file
BOOL levLoadSingleWRF(STRING *pName)
{
	// free the old data
	levReleaseAll();

	// create the dummy level data
	memset(&sSingleWRF, 0, sizeof(LEVEL_DATASET));
	sSingleWRF.pName = pName;

	// load up the WRF
	if (!stageOneInitialise())
	{
		return FALSE;
	}
	BLOCK_RESET(psGameHeap);
	memSetBlockHeap(psGameHeap);
	// load the data
	debug(LOG_WZ, "levLoadSingleWRF: Loading %s ...", pName);
	if (!resLoad(pName, 0,
				 DisplayBuffer, displayBufferSize,
				 psGameHeap))
	{
		return FALSE;
	}

	BLOCK_RESET(psMissionHeap);
	memSetBlockHeap(psMissionHeap);

	if (!stageThreeInitialise())
	{
		return FALSE;
	}

	psCurrLevel = &sSingleWRF;

	return TRUE;
}


// load up the base data set for a level (used by savegames)
BOOL levLoadBaseData(STRING *pName)
{
	LEVEL_DATASET	*psNewLevel, *psBaseData;
	SDWORD			i;

	debug(LOG_WZ, "Loading base data for level %s", pName);

	// find the level dataset
	if (!levFindDataSet(pName, &psNewLevel))
	{
		debug( LOG_ERROR, "levLoadBaseData: couldn't find level data" );
		abort();
		return FALSE;
	}

	if (psNewLevel->type != LDS_CAMSTART &&
		psNewLevel->type != LDS_MKEEP
		&& psNewLevel->type != LDS_EXPAND &&
		psNewLevel->type != LDS_MCLEAR &&
        psNewLevel->type != LDS_EXPAND_LIMBO &&
        psNewLevel->type != LDS_MKEEP_LIMBO
		)
	{
		debug( LOG_ERROR, "levLoadBaseData: incorect level type" );
		abort();
		return FALSE;
	}

	// clear all the old data
	levReleaseAll();

	// basic game data is loaded in the game heap
	memSetBlockHeap(psGameHeap);

	// initialise
	BLOCK_RESET(psGameHeap);
	if (!stageOneInitialise())
	{
		return FALSE;
	}

	// load up the base dataset
	psBaseData = psNewLevel->psBaseData;
	for(i=0; i<LEVEL_MAXFILES; i++)
	{
		if (psBaseData->apDataFiles[i])
		{
			// load the data
			debug(LOG_WZ, "levLoadBaseData: Loading %s", psBaseData->apDataFiles[i]);
			if (!resLoad(psBaseData->apDataFiles[i], i,
						 DisplayBuffer, displayBufferSize,
						 psGameHeap))
			{
				return FALSE;
			}
		}
	}

	psCurrLevel = psNewLevel;

	return TRUE;
}

char *getLevelName( void )
{
	return(currentLevelName);
}


// load up the data for a level
BOOL levLoadData(STRING *pName, STRING *pSaveName, SDWORD saveType)
{
	LEVEL_DATASET	*psNewLevel, *psBaseData, *psChangeLevel;
	SDWORD			i;
	BLOCK_HEAP		*psCurrHeap;
    BOOL            bCamChangeSaveGame;

	debug(LOG_WZ, "Loading level %s", pName);
	// reset fog
//	fogStatus = 0;
//	pie_EnableFog(FALSE);//removed, always set by script or save game

	levelLoadType = saveType;

	// find the level dataset
	if (!levFindDataSet(pName, &psNewLevel))
	{
		debug( LOG_NEVER, "levLoadData: dataset %s not found - trying to load as WRF", pName );
		return levLoadSingleWRF(pName);
	}

	/* Keep a copy of the present level name */
	strcpy(currentLevelName,pName);

    bCamChangeSaveGame = FALSE;
    if (pSaveName AND saveType == GTYPE_SAVE_START)
    {
        if (psNewLevel->psChange != NULL)
        {
            bCamChangeSaveGame = TRUE;
        }
    }

	// select the change dataset if there is one
    psChangeLevel = NULL;
	if (((psNewLevel->psChange != NULL) && (psCurrLevel != NULL)) OR bCamChangeSaveGame)
	{
        //store the level name
		debug( LOG_WZ, "levLoadData: Found CAMCHANGE dataset\n" );
        psChangeLevel = psNewLevel;
		psNewLevel = psNewLevel->psChange;
	}

	// ensure the correct dataset is loaded
	if (psNewLevel->type == LDS_CAMPAIGN)
	{
		debug( LOG_ERROR, "levLoadData: Cannot load a campaign dataset (%s)", psNewLevel->pName );
		return FALSE;
	}
	else
	{
		if (psCurrLevel != NULL)
		{
			if ((psCurrLevel->psBaseData != psNewLevel->psBaseData) ||
				(psCurrLevel->type < LDS_NONE && psNewLevel->type  >= LDS_NONE) ||
				(psCurrLevel->type >= LDS_NONE && psNewLevel->type  < LDS_NONE))
			{
				// there is a dataset loaded but it isn't the correct one
				debug( LOG_WZ, "levLoadData: Incorrect base dataset loaded - levReleaseAll()\n" );
				levReleaseAll();	// this sets psCurrLevel to NULL
			}
		}

		// setup the correct dataset to load if necessary
		if (psCurrLevel == NULL)
		{
#ifdef DEBUG_GROUP0
			if (psNewLevel->psBaseData != NULL)
			{
				debug( LOG_NEVER, "levLoadData: Setting base dataset to load: %s\n", psNewLevel->psBaseData->pName );
			}
#endif
			psBaseData = psNewLevel->psBaseData;
		}
		else
		{
			debug( LOG_NEVER, "levLoadData: No base dataset to load\n" );
			psBaseData = NULL;
		}
	}

	rebuildSearchPath(psNewLevel->dataDir, FALSE);

	// reset the old mission data if necessary
	if (psCurrLevel != NULL)
	{
		debug( LOG_NEVER, "levLoadData: reseting old mission data\n" );
		if (!gameReset())
		{
			return FALSE;
		}
		if (!levReleaseMissionData())
		{
			return FALSE;
		}
	}

	// need to free the current map and droids etc for a save game
	if ((psBaseData == NULL) &&
		(pSaveName != NULL))
	{
		if (!saveGameReset())
		{
			return FALSE;
		}
	}


	// basic game data is loaded in the game heap
	debug( LOG_NEVER, "levLoadData: Setting game heap\n" );
	memSetBlockHeap(psGameHeap);

	// initialise if necessary
	if (psNewLevel->type == LDS_COMPLETE || //psNewLevel->type >= MULTI_TYPE_START ||
		psBaseData != NULL)
	{
		debug( LOG_NEVER, "levLoadData: reset game heap\n" );
		BLOCK_RESET(psGameHeap);
		if (!stageOneInitialise())
		{
			return FALSE;
		}
	}

	// load up a base dataset if necessary
	if (psBaseData != NULL)
	{
		debug( LOG_NEVER, "levLoadData: loading base dataset %s\n", psBaseData->pName );
		for(i=0; i<LEVEL_MAXFILES; i++)
		{
			if (psBaseData->apDataFiles[i])
			{
				// load the data
				debug(LOG_WZ, "levLoadData: Loading %s ...", psBaseData->apDataFiles[i]);
				if (!resLoad(psBaseData->apDataFiles[i], i,
							 DisplayBuffer, displayBufferSize,
							 psGameHeap))
				{
					return FALSE;
				}
			}
		}
	}
	if (psNewLevel->type == LDS_CAMCHANGE)
	{
		if (!campaignReset())
		{
			return FALSE;
		}
	}
	if (psNewLevel->game == -1)  //no .gam file to load - BETWEEN missions (for Editor games only)
	{
		ASSERT( psNewLevel->type == LDS_BETWEEN,
			"levLoadData: only BETWEEN missions do not need a .gam file" );
		debug( LOG_NEVER, "levLoadData: no .gam file for level: BETWEEN mission\n" );
		if (pSaveName != NULL)
		{
			if (psBaseData != NULL)
			{
				if (!stageTwoInitialise())
				{
					return FALSE;
				}
			}

			debug( LOG_NEVER, "levLoadData: setting map heap\n" );
			BLOCK_RESET(psMapHeap);
			memSetBlockHeap(psMapHeap);

            //set the mission type before the saveGame data is loaded
			if (saveType == GTYPE_SAVE_MIDMISSION)
			{
				debug( LOG_NEVER, "levLoadData: init mission stuff\n" );
				if (!startMissionSave(psNewLevel->type))
				{
					return FALSE;
				}

				debug( LOG_NEVER, "levLoadData: dataSetSaveFlag\n" );
				dataSetSaveFlag();
			}

			debug( LOG_NEVER, "levLoadData: loading savegame: %s\n", pSaveName );
			if (!loadGame(pSaveName, FALSE, TRUE,TRUE))
			{
				return FALSE;
			}

			if (!newMapInitialise())
			{
				return FALSE;
			}
		}

		if ((pSaveName == NULL) ||
			(saveType == GTYPE_SAVE_START))
		{
			debug( LOG_NEVER, "levLoadData: start mission - no .gam\n" );
			if (!startMission(psNewLevel->type, NULL))
			{
				return FALSE;
			}
		}

		debug( LOG_NEVER, "levLoadData: setting mission heap\n" );
		BLOCK_RESET(psMissionHeap);
		memSetBlockHeap(psMissionHeap);
	}

    //we need to load up the save game data here for a camchange
    if (bCamChangeSaveGame)
    {
		debug( LOG_NEVER, "levLoadData: no .gam file for level: BETWEEN mission\n" );
		if (pSaveName != NULL)
		{
			if (psBaseData != NULL)
			{
				if (!stageTwoInitialise())
				{
					return FALSE;
				}
			}

			debug( LOG_NEVER, "levLoadData: setting map heap\n" );
			BLOCK_RESET(psMapHeap);
			memSetBlockHeap(psMapHeap);

			debug( LOG_NEVER, "levLoadData: loading savegame: %s\n", pSaveName );
			if (!loadGame(pSaveName, FALSE, TRUE,TRUE))
			{
				return FALSE;
			}

    		if (!campaignReset())
	    	{
		    	return FALSE;
		    }

            //we now need to go to the next level
            //psNewLevel = psChangeLevel;
            //psChangeLevel = NULL;

            //stageTwoShutDown??
            //block_reset??
        }
    }


	// load the new data
	debug( LOG_NEVER, "levLoadData: loading mission dataset: %s\n", psNewLevel->pName );
	psCurrHeap = memGetBlockHeap();
	for(i=0; i<LEVEL_MAXFILES; i++)
	{
		if (psNewLevel->game == i)
		{
			// do some more initialising if necessary
			if (psNewLevel->type == LDS_COMPLETE || psNewLevel->type >= MULTI_TYPE_START ||
				(psBaseData != NULL AND !bCamChangeSaveGame))
			{
iV_Reset(FALSE);//unload font, to avoid crash on 8th load... ajl 15/sep/99
				if (!stageTwoInitialise())
				{
					return FALSE;
				}

				debug( LOG_NEVER, "levLoadData: setting map heap\n" );
				BLOCK_RESET(psMapHeap);
				memSetBlockHeap(psMapHeap);
				psCurrHeap = psMapHeap;
			}

			// missions with a seperate map have to use the mission heap now
			if ((psNewLevel->type == LDS_MKEEP
				 ||psNewLevel->type == LDS_MCLEAR
                 ||psNewLevel->type == LDS_MKEEP_LIMBO
				  ) &&
				pSaveName == NULL)
			{
				debug( LOG_NEVER, "levLoadData: setting mission heap\n" );
				BLOCK_RESET(psMissionHeap);
				memSetBlockHeap(psMissionHeap);
				psCurrHeap = psMissionHeap;
			}

			// load a savegame if there is one - but not if already done so
			if (pSaveName != NULL AND !bCamChangeSaveGame)
			{
				// make sure the map gets loaded into the right heap
				debug( LOG_NEVER, "levLoadData: setting map heap\n" );
				BLOCK_RESET(psMapHeap);
				memSetBlockHeap(psMapHeap);
				psCurrHeap = psMapHeap;

                //set the mission type before the saveGame data is loaded
				if (saveType == GTYPE_SAVE_MIDMISSION)
				{
					debug( LOG_NEVER, "levLoadData: init mission stuff\n" );
					if (!startMissionSave(psNewLevel->type))
					{
						return FALSE;
					}

					debug( LOG_NEVER, "levLoadData: dataSetSaveFlag\n" );
					dataSetSaveFlag();
				}

				debug( LOG_NEVER, "levLoadData: loading save game %s\n", pSaveName );
				if (!loadGame(pSaveName, FALSE, TRUE,TRUE))
				{
					return FALSE;
				}

/*				if (saveType == GTYPE_SAVE_START)
				{
					// do not load any more data
					break;
				}*/
			}

			if ((pSaveName == NULL) ||
				(saveType == GTYPE_SAVE_START))
			{
				// load the game
				debug(LOG_WZ, "Loading scenario file %s", psNewLevel->apDataFiles[i]);
				switch (psNewLevel->type)
				{
				case LDS_COMPLETE:
				case LDS_CAMSTART:
					debug(LOG_WZ, "levLoadData: LDS_COMPLETE / LDS_CAMSTART");
					//if (!startMission(MISSION_CAMPSTART, psNewLevel->apDataFiles[i]))
					if (!startMission(LDS_CAMSTART, psNewLevel->apDataFiles[i]))
					{
						return FALSE;
					}
					break;
				case LDS_BETWEEN:
					debug(LOG_WZ, "levLoadData: LDS_BETWEEN");
					if (!startMission(LDS_BETWEEN, psNewLevel->apDataFiles[i]))
					{
						return FALSE;
					}
					break;

				case LDS_MKEEP:
					debug(LOG_WZ, "levLoadData: LDS_MKEEP");
					//if (!startMission(MISSION_OFFKEEP, psNewLevel->apDataFiles[i]))
					if (!startMission(LDS_MKEEP, psNewLevel->apDataFiles[i]))
					{
						return FALSE;
					}
					break;
				case LDS_CAMCHANGE:
					debug(LOG_WZ, "levLoadData: LDS_CAMCHANGE");
					//if (!startMission(MISSION_CAMPSTART, psNewLevel->apDataFiles[i]))
					if (!startMission(LDS_CAMCHANGE, psNewLevel->apDataFiles[i]))
					{
						return FALSE;
					}
					break;

				case LDS_EXPAND:
					debug(LOG_WZ, "levLoadData: LDS_EXPAND");
					//if (!startMission(MISSION_CAMPEXPAND, psNewLevel->apDataFiles[i]))
					if (!startMission(LDS_EXPAND, psNewLevel->apDataFiles[i]))
					{
						return FALSE;
					}
					break;
				case LDS_EXPAND_LIMBO:
					debug(LOG_WZ, "levLoadData: LDS_LIMBO");
					//if (!startMission(MISSION_CAMPEXPAND, psNewLevel->apDataFiles[i]))
					if (!startMission(LDS_EXPAND_LIMBO, psNewLevel->apDataFiles[i]))
					{
						return FALSE;
					}
					break;

				case LDS_MCLEAR:
					debug(LOG_WZ, "levLoadData: LDS_MCLEAR");
					//if (!startMission(MISSION_OFFCLEAR, psNewLevel->apDataFiles[i]))
					if (!startMission(LDS_MCLEAR, psNewLevel->apDataFiles[i]))
					{
						return FALSE;
					}
					break;
				case LDS_MKEEP_LIMBO:
					debug(LOG_WZ, "levLoadData: LDS_MKEEP_LIMBO");
					debug( LOG_NEVER, "MKEEP_LIMBO\n" );
					//if (!startMission(MISSION_OFFKEEP, psNewLevel->apDataFiles[i]))
					if (!startMission(LDS_MKEEP_LIMBO, psNewLevel->apDataFiles[i]))
					{
						return FALSE;
					}
					break;
				default:
					ASSERT( psNewLevel->type >= MULTI_TYPE_START,
						"levLoadData: Unexpected mission type" );
					debug(LOG_WZ, "levLoadData: default (MULTIPLAYER)");
					//if (!startMission(MISSION_CAMPSTART, psNewLevel->apDataFiles[i]))
					if (!startMission(LDS_CAMSTART, psNewLevel->apDataFiles[i]))
					{
						return FALSE;
					}
					break;
				}
			}

			// set the view position if necessary
			if ((pSaveName != NULL)
			    || ((psNewLevel->type != LDS_BETWEEN)
				&& (psNewLevel->type != LDS_EXPAND)
				&& (psNewLevel->type != LDS_EXPAND_LIMBO)
				))
			{
				if (!newMapInitialise())
				{
					return FALSE;
				}
			}

			// set the mission heap now if it isn't already being used
			if (memGetBlockHeap() != psMissionHeap)
			{
				debug( LOG_NEVER, "levLoadData: setting mission heap\n" );
				BLOCK_RESET(psMissionHeap);
				memSetBlockHeap(psMissionHeap);
			}
			psCurrHeap = psMissionHeap;
		}
		else if (psNewLevel->apDataFiles[i])
		{
			// load the data
			debug(LOG_WZ, "levLoadData: Loading %s", psNewLevel->apDataFiles[i]);
			if (!resLoad(psNewLevel->apDataFiles[i], i + CURRENT_DATAID,
						 DisplayBuffer, displayBufferSize,
						 psCurrHeap))
			{
				return FALSE;
			}
		}
	}

	dataClearSaveFlag();

	// set the mission heap now if it isn't already being used
	if (memGetBlockHeap() != psMissionHeap)
	{
		debug( LOG_NEVER, "levLoadData: setting mission heap\n" );
		BLOCK_RESET(psMissionHeap);
		memSetBlockHeap(psMissionHeap);
		psCurrHeap = psMissionHeap;
	}


	//if (pSaveName != NULL && saveType == GTYPE_SAVE_MIDMISSION)
    if (pSaveName != NULL)
	{
		//load MidMission Extras
		if (!loadMissionExtras(pSaveName, psNewLevel->type))
		{
			return FALSE;
		}
    }

    if (pSaveName != NULL && saveType == GTYPE_SAVE_MIDMISSION)
    {
		//load script stuff
		// load the event system state here for a save game
		debug( LOG_NEVER, "levLoadData: loading script system state\n" );
		if (!loadScriptState(pSaveName))
		{
			return FALSE;
		}
	}

	if (!stageThreeInitialise())
	{
		return FALSE;
	}

//want to test with release build too
//#ifdef DEBUG
    //this enables us to to start cam2/cam3 without going via a save game and get the extra droids
    //in from the script-controlled Transporters
    if (!pSaveName AND psNewLevel->type == LDS_CAMSTART)
    {
        eventFireCallbackTrigger((TRIGGER_TYPE)CALL_NO_REINFORCEMENTS_LEFT);
    }
//#endif

    //restore the level name for comparisons on next mission load up
    if (psChangeLevel == NULL)
    {
        psCurrLevel = psNewLevel;
    }
    else
    {
        psCurrLevel = psChangeLevel;
    }


    return TRUE;
}
