
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_Streaming_System.cpp
// Author : Andy Tate
// Date   : 2-Feb-2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

// RJT NOTE: Required for GCC builds as the file list generation doesn't account for VC project exclusion from build
#ifdef ASURA_USE_STREAMING

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header...
#include "Streaming/Asura_Streaming_System.h"

// Asura independent header-files...
#include "Camera/Asura_Camera.h"
#include "ChunkSystem/Asura_Chunk_Entity.h"
#include "ChunkSystem/Asura_Chunk_MetaMusic.h"
//#ifdef ASURA_USE_AXON
//#include "ChunkSystem/Axon_Chunk_LocationSystem.h"
//#endif
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
#include "Core/Memory/Asura_MemoryBank.h"
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_Timers.h"
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "Debugging/Asura_CPUProfile.h"
#include "Entity/PhysicalObject/Asura_ServerEntity_PhysicalObject.h"
#include "FileAccess/Asura_FileCache.h"
#include "Debugging/Asura_Logfile.h"
#include "Rendering/Asura_Module.h"
#include "Sound/Asura_Sound.h"
#include "SpecialFX/Asura_Skybox.h"
#include "SpecialFX/Asura_SpecialFX.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"
#include "Core/Handlers/Asura_Handler_System.h"
#include "Streaming/Asura_Streaming_ZoneDataState.h"

// Asura platform header-files...

// Game independent header-files...
#include "Sound/Asura_ProjectSoundIDs.h"  // project-supplied file

// Game platform header-files...

// Tools header-files...


////////////////////////////////////////////////////////////////////////////////
// Constant declarations...
////////////////////////////////////////////////////////////////////////////////

// Pre-defined name and hash-IDs...
const char*			Asura_Streaming_System::s_szPermanentDataName	= "PermanentData";
const Asura_Hash_ID	Asura_Streaming_System::s_uPermanentDataHash	= Asura_GetHashID(Asura_Streaming_System::s_szPermanentDataName);

const char*			Asura_Streaming_System::s_szCommonDataName		= "CommonData";
const Asura_Hash_ID	Asura_Streaming_System::s_uCommonDataHash		= Asura_GetHashID(Asura_Streaming_System::s_szCommonDataName);

const char*			Asura_Streaming_System::s_szSkyboxDataName		= "SkyboxData";
const Asura_Hash_ID	Asura_Streaming_System::s_uSkyboxDataHash		= Asura_GetHashID(Asura_Streaming_System::s_szSkyboxDataName);

// The end-of-frame update constants...
DEBUGVAR float	dbg_fEndOfFrameUpdateDuration	= 0.200f;
DEBUGVAR float	dbg_fEndOfFrameUpdateProportion	= 0.250f;

// MGH 20/6/2006: Bump the position upwards a bit to make sure that ground level
// positions are well within the corresponding module.
DEBUGVAR float  dbg_fPositionMoveUpDistance = 0.5f;

////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////

// Flags...
volatile	bool	Asura_Streaming_System::s_bSuspended			= true;
volatile	bool	Asura_Streaming_System::s_bUpdating				= false;
bool	Asura_Streaming_System::s_bAllowLODs			= true;
bool	Asura_Streaming_System::s_bShouldQueueMessages	= false;
bool	Asura_Streaming_System::s_bForceBlockingUpdate	= false;
bool	Asura_Streaming_System::s_bInLowDetailZone		= false;
bool	Asura_Streaming_System::s_bMissionHasPriority	= true;

u_int	Asura_Streaming_System::s_uFrameNumberLastZoneLoad = 0;

// Storage data...
Asura_Streaming_ZoneHandler*	Asura_Streaming_System::s_pxMissionHandler	= NULL;
Asura_Streaming_ZoneHandler*	Asura_Streaming_System::s_pxZoneHandler		= NULL;

// Current zone data...
Asura_Guid		Asura_Streaming_System::s_uControllerEntityGuid	= ASURA_GUID_UNREGISTERED;
Asura_Vector_3	Asura_Streaming_System::s_xCurrentPosition(0.0f, 0.0f, 0.0f);

// Memory-bank indices...
u_int	Asura_Streaming_System::s_uPermanentDataIndex	= 0;
u_int	Asura_Streaming_System::s_uCommonDataIndex		= 1;
u_int	Asura_Streaming_System::s_uSkyboxDataIndex		= 2;

u_int	Asura_Streaming_System::s_uNumDataIndices;

#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
// Memory-banks...
Asura_MemoryBank*	Asura_Streaming_System::s_pxPermanentMemoryBank	= NULL;
Asura_MemoryBank*	Asura_Streaming_System::s_pxCommonMemoryBank	= NULL;
Asura_MemoryBank*	Asura_Streaming_System::s_pxSkyboxMemoryBank	= NULL;
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

// Loading handlers...
u_char*						Asura_Streaming_System::s_pucFileCacheMemory	= NULL;
Asura_FileCache*			Asura_Streaming_System::s_pxFileCache			= NULL;

float						Asura_Streaming_System::s_fJobQueueUpdateTime	= 0.0f;
float						Asura_Streaming_System::s_fJobQueueFrameTime	= 0.0f;
Asura_Streaming_JobQueue	Asura_Streaming_System::s_xJobQueue;


////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::GetResetGroupFromDataIndex
// Author       : Andy Tate
// Date         : 27-Mar-2006
//
// Description  : Finds the texture reset-group corresponding to the specified
//				  data-index.
//
// Parameters   : The data-index to be used.
//
// Return value : The reset-group corresponding to the data-index.
////////////////////////////////////////////////////////////////////////////////

u_int Asura_Streaming_System::GetResetGroupFromDataIndex (const u_int uDataIndex)
{
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	// Which memory-bank are we checking?
	if(uDataIndex == s_uPermanentDataIndex)
	{
		return(ASURA_TEXTURE_LOADED_PERMANENT);
	}
	else if(uDataIndex == s_uCommonDataIndex)
	{
		return(ASURA_TEXTURE_RESETTABLE_GROUP_COMMON);
	}
	else if(uDataIndex == s_uSkyboxDataIndex)
	{
		return(ASURA_TEXTURE_RESETTABLE_STREAMING_SKYBOX);
	}
	else
	{
		u_int	uResetGroup	= (s_pxZoneHandler == NULL)
							? ASURA_TEXTURE_NOT_LOADED
							: s_pxZoneHandler->GetResetGroupFromDataIndex(uDataIndex);

		// None of the standard ones - what about a zone memory-bank?
		if(uResetGroup == ASURA_TEXTURE_NOT_LOADED)
		{
			// No - what about a mission one?
			uResetGroup	= (s_pxMissionHandler == NULL)
						? ASURA_TEXTURE_NOT_LOADED
						: s_pxMissionHandler->GetResetGroupFromDataIndex(uDataIndex);

			if(uResetGroup == ASURA_TEXTURE_NOT_LOADED)
			{
				Asura_Assert(false, "Unexpected data-index for reset-group fetch.");
				return(ASURA_TEXTURE_RESETTABLE_STREAMING_GROUP01);
			}
			else
			{
				return(uResetGroup);
			}
		}
		else
		{
			return(uResetGroup);
		}
	}
#else
	// Just use the first reset-group...
	return(ASURA_TEXTURE_RESETTABLE_STREAMING_GROUP01);
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
}

/// End of function 'Asura_Streaming_System::GetResetGroupFromDataIndex'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::UpdateConnections
// Author       : Andy Tate
// Date         : 6-Apr-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::UpdateConnections (const Asura_Vector_3* pxCurrentPosition,
												const bool bBlocking)
{
	// Do we have zone handler?
	if(s_pxZoneHandler != NULL)
	{
		Asura_Streaming_Zone*	pxCurrentZone	= s_pxZoneHandler->GetCurrentZone();

		Asura_String			xCurrentZoneDirectory;
		Asura_String			xCurrentZoneExtension;

		Asura_String			xCurrentZoneFullPath(pxCurrentZone->GetFilename());

		// Separate the filename of current zone into it's constituent parts...
		xCurrentZoneFullPath.SplitFilename(&xCurrentZoneDirectory, NULL, &xCurrentZoneExtension);

		// Make sure that all of the zones connected to the current one are present...
		for(u_int u = 0; u < pxCurrentZone->GetNumConnection(); u++)
		{
			const Asura_Streaming_Connection*	pxConnection		= pxCurrentZone->GetConnection(u);
			const Asura_Hash_ID					uConnectedZoneHash	= pxConnection->GetToZoneHashID();
			Asura_Streaming_Zone*				pxConnectedZone		= s_pxZoneHandler->FindZone(uConnectedZoneHash);

			// Does this zone already exist?
			if(pxConnectedZone == NULL)
			{
				Asura_String	xConnectedZoneName(pxConnection->GetToZoneName());
				Asura_String	xConnectedZoneFilename(xCurrentZoneDirectory);

				// No - we'd better create it then as we'll soon need it...
				xConnectedZoneFilename	+= "\\";
				xConnectedZoneFilename	+= xConnectedZoneName;
				xConnectedZoneFilename	+= ".";
				xConnectedZoneFilename	+= xCurrentZoneExtension;

				pxConnectedZone			 = CreateZone(ZONE_DATA, xConnectedZoneFilename);
			}

			// Create the appropriate data-states...
			if((pxConnectedZone->GetZoneDataState(Asura_Streaming_Zone::HIGH_DETAIL_DATA) == NULL) &&
			   !pxConnection->IsLowDetail() &&
			   (!pxConnection->GetUseBoxForLoading() || ((pxCurrentPosition != NULL) && pxConnection->GetBoxForLoading().Contains(*pxCurrentPosition))))
			{
				pxConnectedZone->CreateDataState(Asura_Streaming_Zone::HIGH_DETAIL_DATA);
			}

#if !defined(ASURA_TOOLS)
			if(s_bAllowLODs &&
			   pxConnectedZone->IsEnvironmentAllowed() &&
			   (pxConnectedZone->GetZoneDataState(Asura_Streaming_Zone::LOW_DETAIL_DATA) == NULL))
			{
				pxConnectedZone->CreateDataState(Asura_Streaming_Zone::LOW_DETAIL_DATA);
			}
#endif	// !defined(ASURA_TOOLS)
		}
	}
}

/// End of function 'Asura_Streaming_System::UpdateConnections'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::CheckInLowDetailZone
// Author       : Andy Tate
// Date         : 6-Jul-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_System::CheckInLowDetailZone (const Asura_Streaming_Zone& xCurrentZone,
												   const Asura_Vector_3& xCurrentPosition)
{
#ifndef ASURA_TOOLS
	// Check each connection in-turn...
	for(u_int u = 0; u < xCurrentZone.GetNumConnection(); u++)
	{
		const Asura_Streaming_Connection*	pxConnection		= xCurrentZone.GetConnection(u);
		const Asura_Hash_ID					uConnectedZoneHash	= pxConnection->GetToZoneHashID();
		const Asura_Streaming_Zone*			pxConnectedZone		= s_pxZoneHandler->FindZone(uConnectedZoneHash);

		// Do we have a low-detail ONLY zone?
		if(pxConnectedZone != NULL)
		{
			Asura_Streaming_ZoneDataState*	pxHighDetailDataState	= pxConnectedZone->GetZoneDataState(Asura_Streaming_Zone::HIGH_DETAIL_DATA);
			Asura_Streaming_ZoneDataState*	pxLowDetailDataState	= pxConnectedZone->GetZoneDataState(Asura_Streaming_Zone::LOW_DETAIL_DATA);

			if((pxHighDetailDataState != NULL) &&
			   (pxLowDetailDataState != NULL) &&
			   !pxHighDetailDataState->IsEmpty() &&
			   !pxHighDetailDataState->IsFull() &&
			   !pxHighDetailDataState->IsNotPresent() &&
			   !pxHighDetailDataState->IsUnused() &&
			   pxLowDetailDataState->IsFull())
			{
				// Yes - is our position within this zone?
				if(pxConnectedZone->ContainsPosition(xCurrentPosition, Asura_Streaming_Zone::LOW_DETAIL_DATA, NULL, true))
				{
					return(true);
				}
			}
		}
	}
#endif //#ifndef ASURA_TOOLS

	// Not in any low-detail zones...
	return(false);
}

/// End of function 'Asura_Streaming_System::CheckInLowDetailZone'. ///



////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::Initialise
// Author       : Andy Tate
// Date         : 3-Mar-2006
//
// Description  : Handles the initialisation of the streaming system.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::Initialise (void)
{
	// Initialise the variables...
	s_bSuspended	= false;
	s_bUpdating		= false; 
	s_uFrameNumberLastZoneLoad = 0;
	s_bShouldQueueMessages = false;

	// Allocate the file-cache...
	s_pucFileCacheMemory	= Platform_AllocateFileCacheMemory(AUSRA_STREAMING_FILE_CACHE_SIZE);
	s_pxFileCache			= new Asura_FileCache ("Streaming File-Cache", AUSRA_STREAMING_FILE_CACHE_SIZE, s_pucFileCacheMemory, AUSRA_STREAMING_NUM_FILE_CACHE_SECTIONS, false);

	Asura_FileAccess::SetFileCacheForStreaming(s_pxFileCache);

#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	// Initialise the memory-banks...
	s_pxPermanentMemoryBank			= new Asura_MemoryBank("Permanent", ASURA_STREAMING_PERMANENT_RESOURCE_SIZE);
	s_pxCommonMemoryBank			= new Asura_MemoryBank("Common", ASURA_STREAMING_COMMON_RESOURCE_SIZE);
	s_pxSkyboxMemoryBank			= new Asura_MemoryBank("Skybox", ASURA_STREAMING_SKYBOX_RESOURCE_SIZE);

#if defined(ASURA_MEMORYBANK_ALLOW_STATS)
	// Initialise the memory-bank stats...
	s_pxPermanentMemoryBank->CreateStats(ASURA_MAX_RESOURCE_TYPES, Asura_ResourceAllocator::GetResourceNameArray());
	s_pxCommonMemoryBank->CreateStats(ASURA_MAX_RESOURCE_TYPES, Asura_ResourceAllocator::GetResourceNameArray());
	s_pxSkyboxMemoryBank->CreateStats(ASURA_MAX_RESOURCE_TYPES, Asura_ResourceAllocator::GetResourceNameArray());
#endif	// defined(ASURA_MEMORYBANK_ALLOW_STATS)

	// Set the memory-bank to use...
	Asura_ResourceAllocator::SetCurrentMemoryBank(s_pxPermanentMemoryBank);
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

	// Reset the timers...
	s_fJobQueueUpdateTime	= 0.0f;
	s_fJobQueueFrameTime	= 0.0f;
}

/// End of function 'Asura_Streaming_System::Initialise'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::Shutdown
// Author       : Andy Tate
// Date         : 3-Mar-2006
//
// Description  : Handles the shutdown of the streaming system.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::Shutdown (void)
{
	// Destroy the hash-trees...
	DestroyAllZones(MISSION_DATA);
	DestroyAllZones(ZONE_DATA);

	// Destroy the handlers...
	delete(s_pxZoneHandler);
	delete(s_pxMissionHandler);

	s_pxZoneHandler		= NULL;
	s_pxMissionHandler	= NULL;

	// ...and the remaining data...
	DestroyData(SKYBOX_DATA);
	DestroyData(COMMON_DATA);
	DestroyData(PERMANENT_DATA);

#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	// Remove our memory-bank usage...
	ResetCurrentMemoryBank();

	// Destroy the memory-banks..
	delete(s_pxSkyboxMemoryBank);
	delete(s_pxCommonMemoryBank);
	delete(s_pxPermanentMemoryBank);

	s_pxSkyboxMemoryBank	= NULL;
	s_pxCommonMemoryBank	= NULL;
	s_pxPermanentMemoryBank	= NULL;
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

	// Remove the file-cache...
	Asura_FileAccess::SetFileCacheForStreaming(NULL);

	if(s_pucFileCacheMemory != NULL)
	{
		Platform_DeallocateFileCacheMemory(s_pucFileCacheMemory);
		s_pucFileCacheMemory	= NULL;
	}

	delete(s_pxFileCache);
	s_pxFileCache	= NULL;

	// Reset the variables...
	s_bSuspended	= true;
	s_bUpdating		= false; 
}

/// End of function 'Asura_Streaming_System::Shutdown'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::AllocateZones
// Author       : Andy Tate
// Date         : 3-Apr-2006
//
// Description  : Goes through ALL of the memory-banks and attempts to 
//				  re-initialise each one in-turn.
//
// Parameters   : The number of high and low detail memory-banks, along with the
//				  number of mission memory-banks AND a pointer to the function
//				  to be used to create the individual memory-banks.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::AllocateZones (const u_int* puNumZoneDataIndices,
											const u_int* puNumMissionDataIndices)
{
	u_int	auFirstZoneDataIndices[Asura_Streaming_Zone::MAX_DATA_STATES + 1];
	u_int	auFirstMissionDataIndices[Asura_Streaming_Zone::MAX_DATA_STATES + 1];

	// Deallocate any existing zones...
	DeallocateZones();

	// Create the mission handler...
	if(puNumMissionDataIndices == NULL)
	{
		for(u_int u = 0; u <= Asura_Streaming_Zone::MAX_DATA_STATES; u++)
		{
			auFirstMissionDataIndices[u]	= s_uNumDataIndices;
		}

		s_pxMissionHandler	= NULL;
	}
	else
	{
		// Calculate the data-indices for the missions...
		auFirstMissionDataIndices[0]	= s_uNumDataIndices;
		for(u_int u = 1; u <= Asura_Streaming_Zone::MAX_DATA_STATES; u++)
		{
			auFirstMissionDataIndices[u]	= auFirstMissionDataIndices[u-1] + puNumMissionDataIndices[u-1];
		}

		s_pxMissionHandler	= new Asura_Streaming_ZoneHandler (0, false, s_uCommonDataHash, puNumMissionDataIndices, auFirstMissionDataIndices);
		s_uNumDataIndices	= auFirstMissionDataIndices[Asura_Streaming_Zone::MAX_DATA_STATES];
	}

	// Create the zone handler...
	if(puNumZoneDataIndices == NULL)
	{
		for(u_int u = 0; u <= Asura_Streaming_Zone::MAX_DATA_STATES; u++)
		{
			auFirstZoneDataIndices[u]	= s_uNumDataIndices;
		}

		s_pxZoneHandler	= NULL;
	}
	else
	{
		// Calculate the data-indices for the zones...
		auFirstZoneDataIndices[0]	= s_uNumDataIndices;
		for(u_int u = 1; u <= Asura_Streaming_Zone::MAX_DATA_STATES; u++)
		{
			auFirstZoneDataIndices[u]	= auFirstZoneDataIndices[u-1] + puNumZoneDataIndices[u-1];
		}

		s_pxZoneHandler		= new Asura_Streaming_ZoneHandler (auFirstMissionDataIndices[Asura_Streaming_Zone::MAX_DATA_STATES], true, s_uCommonDataHash, puNumZoneDataIndices, auFirstZoneDataIndices);
		s_uNumDataIndices	= auFirstZoneDataIndices[Asura_Streaming_Zone::MAX_DATA_STATES];
	}
}

/// End of function 'Asura_Streaming_System::AllocateZones'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::DeallocateZones
// Author       : Andy Tate
// Date         : 3-Apr-2006
//
// Description  : Goes through ALL of the memory-banks and attempts to 
//				  deallocate each one in-turn.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::DeallocateZones (void)
{
	// Destroy the handlers...
	delete(s_pxMissionHandler);
	delete(s_pxZoneHandler);

	// Reset the variables...
	s_pxMissionHandler	= NULL;
	s_pxZoneHandler		= NULL;

	s_uNumDataIndices	= s_uSkyboxDataIndex + 1;
}

/// End of function 'Asura_Streaming_System::AllocateZones'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::CreateData
// Author       : Andy Tate
// Date         : 24-Mar-2006
//
// Description  : Creates the specified type of data.
//
// Parameters	: The type of data to be created.
//
// Return value : "true" if created successfully, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_System::CreateData (const DataType eDataType)
{
	// What sort of data are we to create?
	switch(eDataType)
	{
		case PERMANENT_DATA:
		{
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
			Asura_ResourceAllocator::SetCurrentMemoryBank(s_pxPermanentMemoryBank);
			s_pxPermanentMemoryBank->Reset();
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

			Asura_Handler_System::CreateAllHandlerData(s_uPermanentDataHash, s_uPermanentDataIndex, s_szPermanentDataName, ASURA_HASH_ID_UNSET);
			Asura_Handler_System::SetAllHandlerData(s_uPermanentDataHash, s_uPermanentDataIndex);
			return(true);
		}

		case COMMON_DATA:
		{
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
			Asura_ResourceAllocator::SetCurrentMemoryBank(s_pxCommonMemoryBank);
			s_pxCommonMemoryBank->Reset();
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

			Asura_Handler_System::CreateAllHandlerData(s_uCommonDataHash, s_uCommonDataIndex, s_szCommonDataName, s_uPermanentDataHash);
			Asura_Handler_System::SetAllHandlerData(s_uCommonDataHash, s_uCommonDataIndex);
			return(true);
		}

		case SKYBOX_DATA:
		{
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
			Asura_ResourceAllocator::SetCurrentMemoryBank(s_pxSkyboxMemoryBank);
			s_pxSkyboxMemoryBank->Reset();
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

			Asura_Handler_System::CreateAllHandlerData(s_uSkyboxDataHash, s_uSkyboxDataIndex, s_szSkyboxDataName, s_uPermanentDataHash);
			Asura_Handler_System::SetAllHandlerData(s_uSkyboxDataHash, s_uSkyboxDataIndex);
			return(true);
		}

		default:
		{
			Asura_Assert(false, "Cannot create data using this data-type. Is it a zone?");
			return(false);
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_System::CreateData'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::DestroyData
// Author       : Andy Tate
// Date         : 20-Mar-2006
//
// Description  : Destroys the specified type of data.
//
// Parameters	: The type of data to be destroyed.
//
// Return value : "true" if destroyed successfully, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_System::DestroyData (const DataType eDataType)
{
	// What sort of data are we to create?
	switch(eDataType)
	{
		case PERMANENT_DATA:
		{
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
			Asura_ResourceAllocator::SetCurrentMemoryBank(s_pxPermanentMemoryBank);
			s_pxPermanentMemoryBank->SetAllowDeletion(true);
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

			Asura_Handler_System::SetAllHandlerData(s_uPermanentDataHash, s_uPermanentDataIndex);
			Asura_Handler_System::DestroyAllHandlerData(s_uPermanentDataHash, s_uPermanentDataIndex);
			return(true);
		}

		case COMMON_DATA:
		{
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
			Asura_ResourceAllocator::SetCurrentMemoryBank(s_pxCommonMemoryBank);
			s_pxCommonMemoryBank->SetAllowDeletion(true);
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

			Asura_Handler_System::SetAllHandlerData(s_uCommonDataHash, s_uCommonDataIndex);
			Asura_Handler_System::DestroyAllHandlerData(s_uCommonDataHash, s_uCommonDataIndex);

			// ART - 05/04/06 - Nasty hack to remove PFX templates since the
			// PFX system doesn't current have a concept of templates stored
			// on a per-handler basis...
			Asura_PFX_System::Reset();
			return(true);
		}

		case SKYBOX_DATA:
		{
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
			Asura_ResourceAllocator::SetCurrentMemoryBank(s_pxSkyboxMemoryBank);
			s_pxSkyboxMemoryBank->SetAllowDeletion(true);
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

			Asura_Handler_System::SetAllHandlerData(s_uSkyboxDataHash, s_uSkyboxDataIndex);
			Asura_Handler_System::DestroyAllHandlerData(s_uSkyboxDataHash, s_uSkyboxDataIndex);
			return(true);
		}

		default:
		{
			Asura_Assert(false, "Cannot create data using this data-type. Is it a zone?");
			return(false);
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_System::DestroyData'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::CreateZone
// Author       : Andy Tate
// Date         : 2-Feb-2006
//
// Description  : Creates a streaming zone of the specified filename.
//
// Parameters   : The type of data to create and pointers to the name and
//				  filename we're to use for this zone.
//
// Return value : A pointer to the zone created, NULL if we can't create the
//				  zone for some reason.
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_Zone* Asura_Streaming_System::CreateZone (const DataType eDataType,
														  const char* szFilename)
{
	// What sort of data are we creating?
	switch(eDataType)
	{
		case ZONE_DATA:
		{
			return((s_pxZoneHandler == NULL) ? false : s_pxZoneHandler->CreateZone(szFilename, true));
		}

		case MISSION_DATA:
		{
			return((s_pxMissionHandler == NULL) ? false : s_pxMissionHandler->CreateZone(szFilename, false));
		}

		default:
		{
			// Invalid data-type...
			Asura_Assert(false, "Invalid data-type when creating this zone.");
			return(NULL);
		}
	}	// End of switch.
}

/// End of function 'Asura_Streaming_System::CreateZone'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::DestroyZone
// Author       : Andy Tate
// Date         : 2-Feb-2006
//
// Description  : Handles the destruction of the specified zone.
//
// Parameters   : The type of data to destroy and the hash-ID for this zone.
//
// Return value : "true" if destroyed successfully, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_System::DestroyZone (const DataType eDataType,
										  const Asura_Hash_ID uHash)
{
	// What sort of data are we creating?
	switch(eDataType)
	{
		case ZONE_DATA:
		{
			return((s_pxZoneHandler == NULL) ? false : s_pxZoneHandler->DestroyZone(uHash));
		}

		case MISSION_DATA:
		{
			return((s_pxMissionHandler == NULL) ? false : s_pxMissionHandler->DestroyZone(uHash));
		}

		default:
		{
			// Invalid data-type...
			Asura_Assert(false, "Invalid data-type when destroy a zone.");
			return(false);
		}
	}	// End of switch.
}

/// End of function 'Asura_Streaming_System::DestroyZone'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::DestroyAllZones
// Author       : Andy Tate
// Date         : 20-Mar-2006
//
// Description  : Loops through ALL of the high and low-detail zones and destroys
//				  them.
//
// Parameters	: The type of data to destroy.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::DestroyAllZones (const DataType eDataType)
{
	// Ensure that we've finished any outstanding jobs first...
	//Asura_Assert(!s_bSuspended, "Streaming system is suspended - cannot destroy all zones.");

	// JG - 15/08/06: Rather than just asserting that things are about to go wrong, let's wait here until the streaming system gets resumed. 
	// I'm manually creating a mutual exclusion between UpdateAll and Suspend, so if the main thread is updating the streaming jobs we are 
	// NOT allowed to suspend until we have tided up everything. As a side affect you sometimes could get a slightly longer green flashing light
	// before the PSP effectively goes into sleep, but I guess that is better than a crash

	// Wait here until we exit suspended state
	while(s_bSuspended)
	{
		Asura_Thread_System::DelayCurrentThread(0.001f, Asura_Thread_System::DELAY_ALLOW_CALLBACKS); 
	}
	// Flag the start of the updating stage, don't suspend now please
	s_bUpdating	= true; 
	
	s_xJobQueue.UpdateAll();
	
	// Flag the end of the updating stage
	s_bUpdating = false; 

	// What sort of data are we creating?
	switch(eDataType)
	{
		case ZONE_DATA:
		{
			// Do we have anything to destroy?
			if(s_pxZoneHandler != NULL)
			{
				s_pxZoneHandler->DestroyAllZones();
			}
			break;
		}

		case MISSION_DATA:
		{
			// Do we have anything to destroy?
			if(s_pxMissionHandler != NULL)
			{
				s_pxMissionHandler->DestroyAllZones();
			}
			break;
		}

		default:
		{
			Asura_Assert(false, "Cannot find a zone-tree to use when destroying a zone.");
			break;
		}
	}	// End of switch.
}

/// End of function 'Asura_Streaming_System::DestroyAllZones'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::ReloadZone
// Author       : Andy Tate
// Date         : 26-May-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_System::ReloadZone (const DataType eDataType,
										 const Asura_Hash_ID uHash,
										 const bool bBlocking)
{
	Asura_Streaming_ZoneHandler*	pxZoneHandler;

	// What sort of data are we creating?
	switch(eDataType)
	{
		case ZONE_DATA:
		{
			pxZoneHandler	= s_pxZoneHandler;
			break;
		}

		case MISSION_DATA:
		{
			pxZoneHandler	= s_pxMissionHandler;
			break;
		}

		default:
		{
			pxZoneHandler	= NULL;
			break;
		}
	}	// End of switch.

	// Do we have an appropriate handler?
	if(pxZoneHandler == NULL)
	{
		Asura_Assert(false, "Cannot find a zone-handler to use when reloading a zone.");
		return(false);
	}
	else
	{
		Asura_Streaming_Zone*	pxZone	= pxZoneHandler->FindZone(uHash);

		// Did we find our zone?
		if(pxZone == NULL)
		{
			Asura_Assert(false, "Cannot find zone to reload.");
			return(false);
		}
		else
		{
			// Reload this zone...
			for(u_int u = 0; u < Asura_Streaming_Zone::MAX_DATA_STATES; u++)
			{
				const Asura_Streaming_Zone::DataState	eDataState	= static_cast<Asura_Streaming_Zone::DataState>(u);

				// Reload this data-state...
				pxZoneHandler->ReloadZone(eDataState, *pxZone, bBlocking);
			}

			return(true);
		}
	}
}

/// End of function 'Asura_Streaming_System::ReloadZone'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::DestroySavedMissionEntities
// Author       : Andy Tate
// Date         : 2-May-2006
//
// Description  : Destroys ALL of the entities in ALL missions.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::DestroySavedMissionEntities (void)
{
	// Do we have any missions?
	if(s_pxMissionHandler != NULL)
	{
		s_pxMissionHandler->DestroySavedEntities();
	}
}

/// End of function 'Asura_Streaming_System::DestroySavedMissionEntities'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::AddToZoneUniqueIndexHashTree
// Author       : Martin G. Harris
// Date         : 15-Nov-2006
//
// Description  : 
//  Function to allow projects to pre-fill the unique index hash tree.
//  This allows them to ensure that zones always have the same unique index
//  (which is used to modify GUIDs). Hence allowing such things as messaging between zones.
//
// Return value : the unique index (or possibly 0 if failed)
////////////////////////////////////////////////////////////////////////////////
int Asura_Streaming_System::AddToZoneUniqueIndexHashTree(const Asura_Hash_ID uHash)
{
	if (s_pxZoneHandler)
	{
		s_pxZoneHandler->AddToUniqueIndexHashTree(uHash);
		return s_pxZoneHandler->GetUniqueIndexForZone(uHash);
	}
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::GetZoneHandlerUniqueIndexForZone
// Author       : Martin G. Harris
// Date         : 21-Nov-2006
//
// Description  : 
// Look up the unique indices in the tree for the zone handler.
//
// Return value : the unique index (or possibly 0 if failed)
////////////////////////////////////////////////////////////////////////////////
int Asura_Streaming_System::GetZoneHandlerUniqueIndexForZone(const Asura_Hash_ID uHash)
{
	if (s_pxZoneHandler)
	{
		return s_pxZoneHandler->GetUniqueIndexForZone(uHash);
	}
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::GetZoneToBeCreatedIn
// Author       : Andy Tate
// Date         : 19-Apr-2006
//
// Description  : Gets the current zone, first checking the missions and only if
//				  no match is found here do we check the zones themselves.
//
// Return value : A pointer to the zone found.
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_Zone* Asura_Streaming_System::GetZoneToBeCreatedIn (void)
{
	Asura_Streaming_Zone*	pxMission	= (s_pxMissionHandler == NULL)
										? NULL
										: s_pxMissionHandler->GetCurrentZone();
	Asura_Streaming_Zone*	pxZone		= (s_pxZoneHandler == NULL)
										? NULL
										: s_pxZoneHandler->GetCurrentZone();

	// AJG - 14/09/06 - The original behaviour was to use the zone if there was no mission, or if we were loading a zone.
	//					Now we can choose which has priority we may reverse that logic.
	//					The original behaviour occurs if GetMissionHasPriority() returns true.
	Asura_Streaming_Zone* pxPriority = (GetMissionHasPriority()) ? pxMission : pxZone;
	Asura_Streaming_Zone* pxSecondary = (GetMissionHasPriority()) ? pxZone : pxMission;

	// Did we find one of the priority type?
	if(pxPriority == NULL)
	{
		// No - use the secondary...
		return(pxSecondary);
	}
	else
	{
		// Yes - but do we need to use it?
		if((pxSecondary == NULL) || pxSecondary->IsDataFull())
		{
			// Yes - use the priority type...
			return(pxPriority);
		}
		else
		{
			// No - we're loading something of the secondary type, so use that instead...
			return(pxSecondary);
		}
	}
}

/// End of function 'Asura_Streaming_System::GetZoneToBeCreatedIn'. ///

///////////////////////////////////////////////////////////////////////////////
// Function		: Asura_Streaming_System::GetContainingZoneHash
// Author       : Martin G. Harris
// Date         : 22-May-2006
//
// Description  : Returns the hash of the current zone,
//				  however if it is a mission then return an unset value.
//
///////////////////////////////////////////////////////////////////////////////

// MGH 22/5/2006: Return the hash for the current zone, but only if it isn't a mission.
Asura_Hash_ID Asura_Streaming_System::GetContainingZoneHash(const Asura_Hash_ID uHash)
{
	if (s_pxMissionHandler)
	{
		if (s_pxMissionHandler->FindZone(uHash))
		{
			// It is a mission. Return unset value.
			return ASURA_HASH_ID_UNSET;
		}
	}

	// It is a zone, so we can return the hash.
	return uHash;
}


#if defined(ASURA_USE_STREAMING) && !defined(ASURA_TOOLS)

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::MakeCurrentMasterZoneGuid
// Author       : Andy Tate
// Date         : 20-Apr-2006
//
// Description  : Makes the GUID to be used based on the current zone.
//
// Parameters   : The initial GUID to be set.
//
// Return value : The GUID after the current zone has been applied.
////////////////////////////////////////////////////////////////////////////////

Asura_Guid Asura_Streaming_System::MakeCurrentMasterZoneGuid (const Asura_Guid uGuid)
{
	// Do we need to bother converting the GUID?
	if((uGuid >= ASURA_GUID_TOOLSCREATEDENTITY_START) && (uGuid <= ASURA_GUID_TOOLSCREATEDENTITY_END))
	{
		Asura_Streaming_Zone*	pxMission	= (s_pxMissionHandler == NULL)
											? NULL
											: s_pxMissionHandler->GetCurrentZone();
		Asura_Streaming_Zone*	pxZone		= (s_pxZoneHandler == NULL)
											? NULL
											: s_pxZoneHandler->GetCurrentZone();

		// AJG - 14/09/06 - Same adjustment as in GetZoneToBeCreatedIn. This may not actually be needed here, as loading a tools-created guid should imply exactly one out of
		//					the current zone and the current mission should be in the process of loading, but the logic is the same so the same adjustment makes sense.
		if (GetMissionHasPriority())
		{
			// Do we find a mission?
			if(pxMission == NULL)
			{
				// No - what about a zone?
				if(pxZone == NULL)
				{
					// No - we don't know how modify the GUID...
					return(uGuid);
				}
				else
				{
					const int	iUniqueIndex	= s_pxZoneHandler->GetUniqueIndexForZone(*pxZone);

					// Yes - apply the streaming mods to the GUID...
					return(uGuid + ASURA_GUID_STREAMING_ENTITY_START + (ASURA_GUID_STREAMING_ZONE_MULTIPLE * iUniqueIndex));
				}
			}
			else
			{
				// Yes - but do we need to use it?
				if((pxZone == NULL) || pxZone->IsDataFull())
				{
					const int	iUniqueIndex	= s_pxMissionHandler->GetUniqueIndexForZone(*pxMission);

					// Yes - apply the streaming mods to the GUID...
					return(uGuid + ASURA_GUID_STREAMING_ENTITY_START + (ASURA_GUID_STREAMING_ZONE_MULTIPLE * iUniqueIndex));
				}
				else
				{
					const int	iUniqueIndex	= s_pxZoneHandler->GetUniqueIndexForZone(*pxZone);

					// No - apply the streaming mods to the GUID...
					return(uGuid + ASURA_GUID_STREAMING_ENTITY_START + (ASURA_GUID_STREAMING_ZONE_MULTIPLE * iUniqueIndex));
				}
			}
		}
		else
		{
			// Do we find a zone?
			if(pxZone == NULL)
			{
				// No - what about a mission?
				if(pxMission == NULL)
				{
					// No - we don't know how modify the GUID...
					return(uGuid);
				}
				else
				{
					const int	iUniqueIndex	= s_pxMissionHandler->GetUniqueIndexForZone(*pxMission);

					// Yes - apply the streaming mods to the GUID...
					return(uGuid + ASURA_GUID_STREAMING_ENTITY_START + (ASURA_GUID_STREAMING_ZONE_MULTIPLE * iUniqueIndex));
				}
			}
			else
			{
				// Yes - but do we need to use it?
				if((pxMission == NULL) || pxMission->IsDataFull())
				{
					const int	iUniqueIndex	= s_pxZoneHandler->GetUniqueIndexForZone(*pxZone);

					// Yes - apply the streaming mods to the GUID...
					return(uGuid + ASURA_GUID_STREAMING_ENTITY_START + (ASURA_GUID_STREAMING_ZONE_MULTIPLE * iUniqueIndex));
				}
				else
				{
					const int	iUniqueIndex	= s_pxMissionHandler->GetUniqueIndexForZone(*pxMission);

					// No - apply the streaming mods to the GUID...
					return(uGuid + ASURA_GUID_STREAMING_ENTITY_START + (ASURA_GUID_STREAMING_ZONE_MULTIPLE * iUniqueIndex));
				}
			}
		}
	}
	else
	{
		return(uGuid);
	}
}

/// End of function 'Asura_Streaming_System::MakeCurrentMasterZoneGuid'. ///

#endif	// defined(ASURA_USE_STREAMING) && !defined(ASURA_TOOLS)

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::FindMasterZone
// Author       : Andy Tate
// Date         : 19-Apr-2006
//
// Description  : Finds the zone matching the specified hash-ID, first checking
//				  the missions and only if no match is found here do we check
//				  the zones themselves.
//
// Parameters	: The hash-ID to check for.
//
// Return value : A pointer to the zone found.
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_Zone* Asura_Streaming_System::FindMasterZone (const Asura_Hash_ID uHash)
{
	Asura_Streaming_Zone*	pxMission	= (s_pxMissionHandler == NULL)
										? NULL
										: s_pxMissionHandler->FindZone(uHash);
	Asura_Streaming_Zone*	pxZone		= (s_pxZoneHandler == NULL)
										? NULL
										: s_pxZoneHandler->FindZone(uHash);

	// AJG - 14/09/06 - Same adjustment as in GetZoneToBeCreatedIn.
	if (GetMissionHasPriority())
	{
		// Do we find a mission?
		if(pxMission == NULL)
		{
			// No - use this zone...
			return(pxZone);
		}
		else
		{
			// Yes - use this mission...
			Asura_Assert((pxZone == NULL), "Found both a mission and a zone with this hash-ID - returning mission.");
			return(pxMission);
		}
	}
	else
	{
		// Do we find a zone?
		if(pxZone == NULL)
		{
			// No - use the mission...
			return(pxMission);
		}
		else
		{
			// Yes - use this zone...
			Asura_Assert((pxMission == NULL), "Found both a mission and a zone with this hash-ID - returning zone.");
			return(pxZone);
		}
	}
}

/// End of function 'Asura_Streaming_System::FindMasterZone'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::CreateEmptyZone
// Author       : Andy Tate
// Date         : 20-Apr-2006
//
// Description  : Creates a set of empty data for the specified mission.
//
// Parameters   : The type of data to create and the hash-ID for the zone.
//
// Return value : "true" if successful, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_System::CreateEmptyZone (const DataType eDataType,
											  const Asura_Hash_ID uHash)
{
	Asura_Streaming_ZoneHandler*	pxZoneHandler;

	// What sort of data are we creating?
	switch(eDataType)
	{
		case ZONE_DATA:
		{
			pxZoneHandler	= s_pxZoneHandler;
			break;
		}

		case MISSION_DATA:
		{
			pxZoneHandler	= s_pxMissionHandler;
			break;
		}

		default:
		{
			pxZoneHandler	= NULL;
			break;
		}
	}	// End of switch.

	// Do we have a zone-handler?
	if(pxZoneHandler == NULL)
	{
		Asura_Assert(false, "Cannot create an empty zone since we have no zone-handler.");
		return(false);
	}
	else
	{
		Asura_Streaming_Zone*	pxZone	= pxZoneHandler->FindZone(uHash);

		// Did we find a zone to use?
		if(pxZone == NULL)
		{
			Asura_Assert(false, "Cannot find zone to create empty data for.");
			return(false);
		}
		else
		{
			// Remove the low-detail data...
#if !defined(ASURA_TOOLS)
			pxZoneHandler->CommenceDestruction(Asura_Streaming_Zone::LOW_DETAIL_DATA, *pxZone, true);
#endif	// !defined(ASURA_TOOLS)

			// Attempt to create an empty zone...
			if(pxZoneHandler->CommenceEmptyCreation(Asura_Streaming_Zone::HIGH_DETAIL_DATA, *pxZone, true))
			{
				// Wait for zone creation to be completed...
				do
				{
					pxZoneHandler->Update(NULL, true);

				} while(!pxZoneHandler->AreAllZonesFull());

				// Return success...
				return(true);
			}
			else
			{
				Asura_Assertf(false, ("Failed to create empty zone \"%s\".", pxZone->GetFilename()));
				return(false);
			}
		}
	}
}

/// End of function 'Asura_Streaming_System::CreateEmptyZone'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::LoadZone
// Author       : Andy Tate
// Date         : 31-Mar-2006
//
// Description  : Loads the data for the specified zone.
//
// Parameters   : The type of data to load, the hash-ID for the zone and a flag
//				  to say whether we recurse connections when loading.
//
// Return value : "true" if successful, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_System::LoadZone (const DataType eDataType,
									   const Asura_Hash_ID uHash,
									   const bool bRecurse)
{
	Asura_Streaming_ZoneHandler*	pxZoneHandler;

	// What sort of data are we creating?
	switch(eDataType)
	{
		case ZONE_DATA:
		{
			pxZoneHandler	= s_pxZoneHandler;
			break;
		}

		case MISSION_DATA:
		{
			pxZoneHandler	= s_pxMissionHandler;
			break;
		}

		default:
		{
			pxZoneHandler	= NULL;
			break;
		}
	}	// End of switch.

	// Do we have a zone handler?
	if(pxZoneHandler == NULL)
	{
		Asura_Assert(false, "Cannot load a zone since we have no zone-handler.");
		return(false);
	}
	else
	{
		Asura_Streaming_Zone*	pxZone	= pxZoneHandler->FindZone(uHash);

		// Did we find a zone to use?
		if(pxZone == NULL)
		{
			Asura_Assert(false, "Cannot find zone to load data into.");
			return(false);
		}
		else
		{
#if defined(ASURA_TOOLS)
			// Set the current zone...
			pxZoneHandler->SetCurrentZone(uHash);
#else
			// Load the low-detail data-state...
			if(s_bAllowLODs && pxZone->IsEnvironmentAllowed())
			{
				pxZoneHandler->CommenceLoad(Asura_Streaming_Zone::LOW_DETAIL_DATA, *pxZone, true);
			}
#endif	// !defined(ASURA_TOOLS)

			// Load the high-detail data-state...
			if(pxZoneHandler->CommenceLoad(Asura_Streaming_Zone::HIGH_DETAIL_DATA, *pxZone, true))
			{
				// Wait for zone-loading to be completed...
				do
				{
					// Do we need to update connections between zones?
					if(bRecurse)
					{
						UpdateConnections(NULL, true);
					}

					// Update the zones...
					pxZoneHandler->Update(NULL, true);

				} while(!pxZoneHandler->AreAllZonesFull());

				// Set this as the current zone...
				pxZoneHandler->SetCurrentZone(uHash);
				return(true);
			}
			else
			{
				Asura_Assertf(false, ("Failed to load zone \"%s\".", pxZone->GetFilename()));
				return(false);
			}
		}
	}
}

/// End of function 'Asura_Streaming_System::LoadZone'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::Update
// Author       : Andy Tate
// Date         : 6-Apr-2006
//
// Description  : Updates the connections between zones, creating and destroying
//				  zones where necessary and initiating loading as needed.
//
// Parameters	: "true" if we're to recurse connections, "false" if not.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::Update (const bool bRecurse)
{
	ASURA_CPU_PROFILE__START_LABEL(ASURA_CPU_PROFILE__STREAMING_UPDATE, "Asura_Streaming_System::Update");
	{
		// Higher priority should be updated second, so that's the current zone after the update
		if (GetMissionHasPriority())
		{
			UpdateZoneHandler(bRecurse);
			UpdateMissionHandler();
		}
		else
		{
			UpdateMissionHandler();
			UpdateZoneHandler(bRecurse);
		}
	}
	ASURA_CPU_PROFILE__END(ASURA_CPU_PROFILE__STREAMING_UPDATE);
}

/// End of function 'Asura_Streaming_System::Update'. ///


// AJG - 14/09/06 - Separated update stages, so we can swap the order easily.

void Asura_Streaming_System::UpdateZoneHandler(bool bRecurse)
{
		// Do we have any zones?
		if(s_pxZoneHandler != NULL)
		{
			// Are we to recurse connections?
			if(bRecurse)
			{
				Asura_Streaming_Zone*						pxCurrentZone	= s_pxZoneHandler->GetCurrentZone();
				const Asura_ServerEntity_PhysicalObject*	pxEntity		= (s_uControllerEntityGuid == ASURA_GUID_UNREGISTERED)
																			? NULL
																			: Asura_ServerEntity_PhysicalObject::FindEntity(s_uControllerEntityGuid);

				// Set the position...
				s_xCurrentPosition	= (pxEntity == NULL)
									? *(Asura_Camera::GetPosition())
									: pxEntity->GetPosition();

				if (pxEntity)
				{
					// MGH 20/6/2006: Move upwards slightly in case the ground position
					// is very close to the module boundary.
					s_xCurrentPosition.y -= dbg_fPositionMoveUpDistance;
				}

				// Check whether we're in a low-detail zone...
				s_bInLowDetailZone	= CheckInLowDetailZone(*pxCurrentZone, s_xCurrentPosition);

				// Has our position changed zones?
				if(!pxCurrentZone->ContainsPosition(s_xCurrentPosition))
				{
					Asura_Streaming_Zone*	pxNewZone	= NULL;

					// Yes - are we in any of the connected zones then?
					for(u_int u = 0; u < pxCurrentZone->GetNumConnection(); u++)
					{
						const Asura_Streaming_Connection*	pxConnection		= pxCurrentZone->GetConnection(u);
						const Asura_Hash_ID					uConnectedZoneHash	= pxConnection->GetToZoneHashID();
						Asura_Streaming_Zone*				pxConnectedZone		= s_pxZoneHandler->FindZone(uConnectedZoneHash);

						// Is our position inside this zone?
						if((pxConnectedZone != NULL) && pxConnectedZone->ContainsPosition(s_xCurrentPosition))
						{
							// Yes - set this as our current zone...
							pxNewZone	= pxConnectedZone;
						}
					}

					// Did we change zones?
					if(pxNewZone == NULL)
					{
						Asura_Assert(!pxCurrentZone->IsEnvironmentAllowed() || pxCurrentZone->ContainsPosition(s_xCurrentPosition), "Failed to find a zone to change to.");
					}
					else
					{
						Asura_OutputToDebugger(("Changing to zone \"%s\"...\n", pxNewZone->GetFilename()));
						s_pxZoneHandler->SetCurrentZone(pxNewZone->GetHashID());
					}
				}

				// Update the connections...
				UpdateConnections(&s_xCurrentPosition, false);
			}

			// ...before updating the zones themselves...
			s_pxZoneHandler->Update(&s_xCurrentPosition, false);
		}
}


void Asura_Streaming_System::UpdateMissionHandler()
{
	// Do we have any missions?
		if(s_pxMissionHandler != NULL)
		{
			s_pxMissionHandler->Update(NULL, false);
		}
	}


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::UpdateEntities
// Author       : Andy Tate
// Date         : 31-Mar-2006
//
// Description  : Updates ALL of the entities associated with the streaming-system.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::UpdateEntities (void)
{
	// Begin the entity render...
	Asura_Entity_System::BeginUpdate();

	// Higher priority should update second
	if (GetMissionHasPriority())
	{
		if(s_pxZoneHandler != NULL)
		{
			s_pxZoneHandler->UpdateEntitiesForAllZones();
		}

		if(s_pxMissionHandler != NULL)
		{
			s_pxMissionHandler->UpdateEntitiesForAllZones();
		}
	}
	else
	{
		if(s_pxMissionHandler != NULL)
		{
			s_pxMissionHandler->UpdateEntitiesForAllZones();
		}

		if(s_pxZoneHandler != NULL)
		{
			s_pxZoneHandler->UpdateEntitiesForAllZones();
		}
	}

	Asura_Entity_System::EndUpdate();
}

/// End of function 'Asura_Streaming_System::UpdateEntities'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::PreRenderUpdate
// Author       : Andy Tate
// Date         : 4-Apr-2006
//
// Description  : Performs the pre-render update of ALL of the streaming-zones.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::PreRenderUpdate (void)
{
	// Higher priority should update second
	if (GetMissionHasPriority())
	{
		if( s_pxZoneHandler != NULL )
		{
			s_pxZoneHandler->PreModuleUpdateUpdateForAllZones();
		}

		if( s_pxMissionHandler != NULL )
		{
			s_pxMissionHandler->PreModuleUpdateUpdateForAllZones();		
		}
	}
	else
	{
		if( s_pxMissionHandler != NULL )
		{
			s_pxMissionHandler->PreModuleUpdateUpdateForAllZones();		
		}

		if( s_pxZoneHandler != NULL )
		{
			s_pxZoneHandler->PreModuleUpdateUpdateForAllZones();
		}
	}

	//Module system update now takes care of all zones in one go. RAR 11/04/2006
	Asura_Module_System::Update();
	
	// Higher priority should update second
	if (GetMissionHasPriority())
	{
		// Update each zone...
		if(s_pxZoneHandler != NULL)
		{
			s_pxZoneHandler->PreRenderUpdateForAllZones();
		}

		if(s_pxMissionHandler != NULL)
		{
			s_pxMissionHandler->PreRenderUpdateForAllZones();
		}
	}
	else
	{
		// Update each zone...
		if(s_pxMissionHandler != NULL)
		{
			s_pxMissionHandler->PreRenderUpdateForAllZones();
		}

		if(s_pxZoneHandler != NULL)
		{
			s_pxZoneHandler->PreRenderUpdateForAllZones();
		}
	}
}

/// End of function 'Asura_Streaming_System::PreRenderUpdate'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::EndOfFrameUpdate
// Author       : Andy Tate
// Date         : 15-May-2006
//
// Description  : Performs the end-of-frame update for the streaming-system.
//				  This keeps updating the job-queue for as long as we're allowed,
//				  however if the cumulative effect of the update isn't enough
//				  then we will be allowed to take an extra frame.
//
// Parameters   : The time-allowed this frame for the update AND the expected
//				  frame-rate in case we need to hang around for longer.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::EndOfFrameUpdate (const float fTimeAllowed,
											   const float fFrameDuration)
{
	// Are we able to update?
	if(!s_bSuspended)
	{
		ASURA_CPU_PROFILE__START_LABEL(ASURA_CPU_PROFILE__STREAMING_ENDOFFRAME, "Asura_Streaming_System::EndOfFrameUpdate");
		{
			// Do we need to update everything in this frame?
			if(s_bForceBlockingUpdate)
			{
				// Wait here until we exit suspended state
				while(s_bSuspended)
				{
					Asura_Thread_System::DelayCurrentThread(0.001f, Asura_Thread_System::DELAY_ALLOW_CALLBACKS); 
				}
				// Flag the start of the updating stage, don't suspend now please
				s_bUpdating	= true; 
				
				s_xJobQueue.UpdateAll();
				
				// Flag the end of the updating stage
				s_bUpdating = false; 
			}
			else
			{
				float		fTimeLeft	= fTimeAllowed;

			const float	fStartTime	= Asura_Timers::GetTimeInFrame();
				const bool	bFinished	= s_xJobQueue.Update(fTimeLeft);
			const float	fEndTime	= Asura_Timers::GetTimeInFrame();
			const float	fTimeTaken	= fEndTime - fStartTime;

			// Update the timers...
			s_fJobQueueUpdateTime	+= fTimeTaken;
			s_fJobQueueFrameTime	+= Asura_Timers::GetFrameTime();

			// Do we have more to update?
			if(!bFinished)
			{
				// Yes - is it time to do a longer update?
				if(s_fJobQueueFrameTime >= dbg_fEndOfFrameUpdateDuration)
				{
					const float	fFrameTime	= dbg_fEndOfFrameUpdateDuration * dbg_fEndOfFrameUpdateProportion;

					// Yes - but do we need to bother?
					if(s_fJobQueueUpdateTime < fFrameTime)
					{
						// Allow another whole frame's worth of update...
							fTimeLeft	= fFrameDuration;
							s_xJobQueue.Update(fTimeLeft);
					}

					// Update the timers...
					s_fJobQueueUpdateTime	= 0.0f;
					s_fJobQueueFrameTime	= 0.0f;
				}
			}
		}
		}
		ASURA_CPU_PROFILE__END(ASURA_CPU_PROFILE__STREAMING_ENDOFFRAME);
	}
}

/// End of function 'Asura_Streaming_System::EndOfFrameUpdate'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::Suspend
// Author       : Andy Tate
// Date         : 9-May-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::Suspend (void)
{
	// JG - 15/08/06: Check that we are not updating, if we are WAIT here until we have finished updating
	// WARNING: as this method is going to be called from a callback on the PSP we can not allow to be interrupted by a callback during this wait,
	// or we could go into a recursive series of calls
	while (s_bUpdating)
	{
		Asura_Thread_System::DelayCurrentThread(0.001f, Asura_Thread_System::DELAY_DONT_ALLOW_CALLBACKS); 
	}

	// Set the sleep flag...
	Asura_Assert(!s_bSuspended, "Streaming system is already asleep.");
	s_bSuspended	= true;

	// Remove any memory we have allocated...
	if(s_pucFileCacheMemory != NULL)
	{
		if(s_pxFileCache != NULL)
		{
			s_pxFileCache->ResetMemory();
		}

		Platform_DeallocateFileCacheMemory(s_pucFileCacheMemory);
		s_pucFileCacheMemory	= NULL;
	}
}

/// End of function 'Asura_Streaming_System::Suspend'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::Resume
// Author       : Andy Tate
// Date         : 9-May-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::Resume (void)
{
	// Set the sleep flag...
	Asura_Assert(s_bSuspended, "Streaming system is already awake.");
	s_bSuspended	= false;

	// Do we already have memory for the file-cache?
	if(s_pucFileCacheMemory == NULL)
	{
		// Allocate the memory for file-cache...
		s_pucFileCacheMemory	= Platform_AllocateFileCacheMemory(AUSRA_STREAMING_FILE_CACHE_SIZE);

		s_pxFileCache->SetMemory(AUSRA_STREAMING_FILE_CACHE_SIZE, s_pucFileCacheMemory);
	}
	else
	{
		Asura_Assert(false, "Streaming file-cache already has memory.");
	}
}

/// End of function 'Asura_Streaming_System::Resume'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::RenderEntities
// Author       : Andy Tate
// Date         : 31-Mar-2006
//
// Description  : Renders ALL of the entities associated with the streaming-system.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::RenderEntities (void)
{
	// Begin the entity render...
	Asura_Entity_System::BeginRender();

	// Higher priority should update second
	if (GetMissionHasPriority())
	{
		if(s_pxZoneHandler != NULL)
		{
			s_pxZoneHandler->RenderEntitiesForAllZones();
		}

		if(s_pxMissionHandler != NULL)
		{
			s_pxMissionHandler->RenderEntitiesForAllZones();
		}
	}
	else
	{
		if(s_pxMissionHandler != NULL)
		{
			s_pxMissionHandler->RenderEntitiesForAllZones();
		}

		if(s_pxZoneHandler != NULL)
		{
			s_pxZoneHandler->RenderEntitiesForAllZones();
		}
	}

	Asura_Entity_System::EndRender();
}

/// End of function 'Asura_Streaming_System::RenderEntities'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::RenderEnvironment
// Author       : Andy Tate
// Date         : 31-Mar-2006
//
// Description  : Renders all of the environment-zones associated with the
//				  streaming-system.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::RenderEnvironment (void)
{
	// Render each zone in-turn...
	if(s_pxZoneHandler != NULL)
	{
		s_pxZoneHandler->RenderEnvironmentForAllZones();
	}
}

/// End of function 'Asura_Streaming_System::RenderEnvironment'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::RenderSkybox
// Author       : Andy Tate
// Date         : 31-Mar-2006
//
// Description  : Renders the skybox used by the streaming-system.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::RenderSkybox (void)
{
	// Set the appropriate handlers...
	Asura_Handler_System::SetAllHandlerData(s_uSkyboxDataHash, s_uSkyboxDataIndex);

	// Render the skybox...
	Asura_Skybox::Render();
}

/// End of function 'Asura_Streaming_System::RenderSkybox'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::RenderSpecialFX
// Author       : Andy Tate
// Date         : 31-Mar-2006
//
// Description  : Renders ALL of the special-effects associated with the
//				  streaming-system.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::RenderSpecialFX (void)
{
	// Only needs to be rendered once - not once per zone...
	Asura_SpecialFX_System::Render();
	// Special FX rendering has been split so that the cutscene bars/subtitles are in a new function
	// Render2DEffects is called here to keep the old behaviour , but this may not be the best place for it.
	// RAR 22/09/2006
	Asura_SpecialFX_System::Render2DEffects();

}

/// End of function 'Asura_Streaming_System::RenderSpecialFX'. ///



///////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::ResetPeriodicClientUpdateList
// Author       : Martin G. Harris
// Date         : 27-Apr-2006
///////////////////////////////////////////////////////////////////////////////
void Asura_Streaming_System::ResetPeriodicClientUpdateList()
{
	// Higher priority should update second
	if (GetMissionHasPriority())
	{
		if (s_pxZoneHandler)
		{
			s_pxZoneHandler->ResetPeriodicClientUpdateListForAllZones();
		}

		if (s_pxMissionHandler)
		{
			s_pxMissionHandler->ResetPeriodicClientUpdateListForAllZones();
		}
	}
	else
	{
		if (s_pxMissionHandler)
		{
			s_pxMissionHandler->ResetPeriodicClientUpdateListForAllZones();
		}

		if (s_pxZoneHandler)
		{
			s_pxZoneHandler->ResetPeriodicClientUpdateListForAllZones();
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::FinishPeriodicClientUpdates
// Author       : Martin G. Harris
// Date         : 27-Apr-2006
///////////////////////////////////////////////////////////////////////////////
void Asura_Streaming_System::FinishPeriodicClientUpdates()
{
	// Higher priority should update second
	if (GetMissionHasPriority())
	{
		if (s_pxZoneHandler)
		{
			Asura_Streaming_Zone* pxCurrentZone = s_pxZoneHandler->GetCurrentZone();
			Asura_Assert(pxCurrentZone, "Null current zone");
			s_pxZoneHandler->FinishPeriodicClientUpdatesForAllZones();
			if (pxCurrentZone)
			{
				s_pxZoneHandler->SetCurrentZone(pxCurrentZone->GetHashID());
			}
		}

		if (s_pxMissionHandler)
		{
			Asura_Streaming_Zone* pxCurrentMission = s_pxMissionHandler->GetCurrentZone();
			Asura_Assert(pxCurrentMission, "Null current mission");
			if (pxCurrentMission)
			{
				s_pxMissionHandler->FinishPeriodicClientUpdatesForAllZones();
				s_pxMissionHandler->SetCurrentZone(pxCurrentMission->GetHashID());
			}
		}
	}
	else
	{
		if (s_pxMissionHandler)
		{
			Asura_Streaming_Zone* pxCurrentMission = s_pxMissionHandler->GetCurrentZone();
			s_pxMissionHandler->FinishPeriodicClientUpdatesForAllZones();
			s_pxMissionHandler->SetCurrentZone(pxCurrentMission->GetHashID());
		}

		if (s_pxZoneHandler)
		{
			Asura_Streaming_Zone* pxCurrentZone = s_pxZoneHandler->GetCurrentZone();
			Asura_Assert(pxCurrentZone, "Null current zone");
			s_pxZoneHandler->FinishPeriodicClientUpdatesForAllZones();
			if (pxCurrentZone)
			{
				s_pxZoneHandler->SetCurrentZone(pxCurrentZone->GetHashID());
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::DoSomePeriodicClientUpdates
// Author       : Martin G. Harris
// Date         : 27-Apr-2006
///////////////////////////////////////////////////////////////////////////////
void Asura_Streaming_System::DoSomePeriodicClientUpdates()
{
	// Higher priority should update second
	if (GetMissionHasPriority())
	{
		if (s_pxZoneHandler)
		{
			Asura_Streaming_Zone* pxCurrentZone = s_pxZoneHandler->GetCurrentZone();
			Asura_Assert(pxCurrentZone, "Null current zone");
			s_pxZoneHandler->DoSomePeriodicClientUpdatesForAllZones();
			if (pxCurrentZone)
			{
				s_pxZoneHandler->SetCurrentZone(pxCurrentZone->GetHashID());
			}
		}

		if (s_pxMissionHandler)
		{
			Asura_Streaming_Zone* pxCurrentMission = s_pxMissionHandler->GetCurrentZone();
			Asura_Assert(pxCurrentMission, "Null current mission");
			if (pxCurrentMission)
			{
				s_pxMissionHandler->DoSomePeriodicClientUpdatesForAllZones();
				s_pxMissionHandler->SetCurrentZone(pxCurrentMission->GetHashID());
			}
		}
	}
	else
	{
		if (s_pxMissionHandler)
		{
			Asura_Streaming_Zone* pxCurrentMission = s_pxMissionHandler->GetCurrentZone();
			s_pxMissionHandler->DoSomePeriodicClientUpdatesForAllZones();
			s_pxMissionHandler->SetCurrentZone(pxCurrentMission->GetHashID());
		}

		if (s_pxZoneHandler)
		{
			Asura_Streaming_Zone* pxCurrentZone = s_pxZoneHandler->GetCurrentZone();
			Asura_Assert(pxCurrentZone, "Null current zone");
			s_pxZoneHandler->DoSomePeriodicClientUpdatesForAllZones();
			if (pxCurrentZone)
			{
				s_pxZoneHandler->SetCurrentZone(pxCurrentZone->GetHashID());
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::ResetConstantClientUpdateList
// Author       : Martin G. Harris
// Date         : 27-Apr-2006
///////////////////////////////////////////////////////////////////////////////
void Asura_Streaming_System::ResetConstantClientUpdateList()
{
	// Higher priority should update second
	if (GetMissionHasPriority())
	{
		if (s_pxZoneHandler)
		{
			s_pxZoneHandler->ResetConstantClientUpdateListForAllZones();
		}

		if (s_pxMissionHandler)
		{
			s_pxMissionHandler->ResetConstantClientUpdateListForAllZones();
		}
	}
	else
	{
		if (s_pxMissionHandler)
		{
			s_pxMissionHandler->ResetConstantClientUpdateListForAllZones();
		}

		if (s_pxZoneHandler)
		{
			s_pxZoneHandler->ResetConstantClientUpdateListForAllZones();
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::DoConstantClientUpdates
// Author       : Martin G. Harris
// Date         : 27-Apr-2006
///////////////////////////////////////////////////////////////////////////////
void Asura_Streaming_System::DoConstantClientUpdates()
{
	// Higher priority should update second
	if (GetMissionHasPriority())
	{
		if (s_pxZoneHandler)
		{
			Asura_Streaming_Zone* pxCurrentZone = s_pxZoneHandler->GetCurrentZone();
			Asura_Assert(pxCurrentZone, "Null current zone");
			s_pxZoneHandler->DoConstantClientUpdatesForAllZones();
			if (pxCurrentZone)
			{
				s_pxZoneHandler->SetCurrentZone(pxCurrentZone->GetHashID());
			}
		}

		if (s_pxMissionHandler)
		{
			Asura_Streaming_Zone* pxCurrentMission = s_pxMissionHandler->GetCurrentZone();
			Asura_Assert(pxCurrentMission, "Null current mission");
			if (pxCurrentMission)
			{
				s_pxMissionHandler->DoConstantClientUpdatesForAllZones();
				s_pxMissionHandler->SetCurrentZone(pxCurrentMission->GetHashID());
			}
		}
	}
	else
	{
		if (s_pxMissionHandler)
		{
			Asura_Streaming_Zone* pxCurrentMission = s_pxMissionHandler->GetCurrentZone();
			s_pxMissionHandler->DoConstantClientUpdatesForAllZones();
			s_pxMissionHandler->SetCurrentZone(pxCurrentMission->GetHashID());
		}

		if (s_pxZoneHandler)
		{
			Asura_Streaming_Zone* pxCurrentZone = s_pxZoneHandler->GetCurrentZone();
			Asura_Assert(pxCurrentZone, "Null current zone");
			s_pxZoneHandler->DoConstantClientUpdatesForAllZones();
			if (pxCurrentZone)
			{
				s_pxZoneHandler->SetCurrentZone(pxCurrentZone->GetHashID());
			}
		}
	}
}



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::MakeArrayOfZones
// Author       : Andy Tate
// Date         : 16-Jun-2006
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_Zone** Asura_Streaming_System::MakeArrayOfZones (const DataType eDataType,
																 int& iNumZones)
{
	// What sort of zone are we allocating?
	switch(eDataType)
	{
		case ZONE_DATA:
		{
			return((s_pxZoneHandler == NULL) ? NULL : s_pxZoneHandler->MakeArrayOfZones(iNumZones));
		}

		case MISSION_DATA:
		{
			return((s_pxMissionHandler == NULL) ? NULL : s_pxMissionHandler->MakeArrayOfZones(iNumZones));
		}

		default:
		{
			Asura_Assert(false, "Unexpected data-type when making array of zones.");
			return(NULL);
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_System::MakeArrayOfZones'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::GetNumZones
// Author       : Andy Tate
// Date         : 7-Aug-2006
////////////////////////////////////////////////////////////////////////////////

u_int Asura_Streaming_System::GetNumZones (const DataType eDataType)
{
	// What sort of memory-bank are we allocating?
	switch(eDataType)
	{
		case ZONE_DATA:
		{
			return((s_pxZoneHandler == NULL) ? 0 : s_pxZoneHandler->GetNumZones());
		}

		case MISSION_DATA:
		{
			return((s_pxMissionHandler == NULL) ? 0 : s_pxMissionHandler->GetNumZones());
		}

		default:
		{
			Asura_Assert(false, "Unexpected data-type when getting the number of zones.");
			return(0);
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_System::GetNumZones'. ///



#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::AllocateMemoryBanks
// Author       : Andy Tate
// Date         : 19-Apr-2006
//
// Description  : Goes through ALL of the memory-banks and attempts to 
//				  re-initialise each one in-turn.
//
// Parameters   : A pointer to the memory-bank allocation function.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::AllocateMemoryBanks (const DataType eDataType,
												  Asura_Streaming_ZoneHandler::MemoryBankAllocationFunction* pfnCreateMemoryBank)
{
	// What sort of memory-bank are we allocating?
	switch(eDataType)
	{
		case ZONE_DATA:
		{
			const char*	aszFormatText[Asura_Streaming_Zone::MAX_DATA_STATES] =
			{
				"High-detail zone %u",
				"Low-detail zone %u",
			};

			// Allocate the appropriate memory-banks...
			s_pxZoneHandler->AllocateMemoryBanks(aszFormatText, pfnCreateMemoryBank);
			break;
		}

		case MISSION_DATA:
		{
			const char*	aszFormatText[Asura_Streaming_Zone::MAX_DATA_STATES] =
			{
				"Mission %u",
				NULL,
			};

			// Allocate the appropriate memory-banks...
			s_pxMissionHandler->AllocateMemoryBanks(aszFormatText, pfnCreateMemoryBank);
			break;
		}

		default:
		{
			Asura_Assert(false, "Unexpected data-type for memory-bank allocation.");
			break;
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_System::AllocateMemoryBanks'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::DeallocateMemoryBanks
// Author       : Andy Tate
// Date         : 19-Apr-2006
//
// Description  : Goes through ALL of the memory-banks and attempts to 
//				  deallocate each one in-turn.
//
// Parameters   : The type of zone to deallocate memory-banks for.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::DeallocateMemoryBanks (const DataType eDataType)
{
	// What sort of memory-bank are we allocating?
	switch(eDataType)
	{
		case ZONE_DATA:
		{
			if(s_pxZoneHandler != NULL)
			{
				s_pxZoneHandler->DeallocateMemoryBanks();
			}
			break;
		}

		case MISSION_DATA:
		{
			if(s_pxMissionHandler != NULL)
			{
				s_pxMissionHandler->DeallocateMemoryBanks();
			}
			break;
		}

		default:
		{
			Asura_Assert(false, "Unexpected data-type for memory-bank deallocation.");
			break;
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_System::DeallocateMemoryBanks'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::SetCurrentMemoryBank
// Author       : Andy Tate
// Date         : 3-Mar-2006
//
// Description  : Resets the specified memory-bank.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::ResetCurrentMemoryBank (void)
{
	// Reset the memory-bank to use...
	Asura_ResourceAllocator::SetCurrentMemoryBank(NULL);
}

/// End of function 'Asura_Streaming_System::SetCurrentMemoryBank'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::GetMemoryBankFromDataIndex
// Author       : Andy Tate
// Date         : 24-Mar-2006
//
// Description  : Returns the memory-bank appropriate to the specified data-index.
//
// Parameters   : The data-index to use for the memory-bank.
//
// Return value : A pointer to the memory-bank found, NULL if index out-of-range.
////////////////////////////////////////////////////////////////////////////////

Asura_MemoryBank* Asura_Streaming_System::GetMemoryBankFromDataIndex (const u_int uDataIndex)
{
	// Which memory-bank are we checking?
	if(uDataIndex == s_uPermanentDataIndex)
	{
		return(s_pxPermanentMemoryBank);
	}
	else if(uDataIndex == s_uCommonDataIndex)
	{
		return(s_pxCommonMemoryBank);
	}
	else if(uDataIndex == s_uSkyboxDataIndex)
	{
		return(s_pxSkyboxMemoryBank);
	}
	else
	{
		Asura_MemoryBank*	pxMemoryBank	= s_pxZoneHandler->GetMemoryBankFromDataIndex(uDataIndex);

		// None of the standard ones - what about a zone memory-bank?
		if(pxMemoryBank == NULL)
		{
			// No - what about a mission one?
			pxMemoryBank	= s_pxMissionHandler->GetMemoryBankFromDataIndex(uDataIndex);

			if(pxMemoryBank == NULL)
			{
				Asura_Assert(false, "Unexpected data-index for memory-bank fetch.");
				return(NULL);
			}
			else
			{
				return(pxMemoryBank);
			}
		}
		else
		{
			return(pxMemoryBank);
		}
	}
}

/// End of function 'Asura_Streaming_System::GetMemoryBankFromDataIndex'. ///

#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_System::SaveMissionDataToBuffer
// Author       : Anthony Gregson
// Date         : 03/05/06
//
// Description  : Saves the current mission data to a save block
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_System::SaveMissionDataToBuffer(Asura_SaveBlock_Base& xSaveBlock)
{
	// AJG - 03/05/06 - Assume the mission is what last set up the handlers. This may be unsafe, but is all the system allows for now.
	if (!s_pxMissionHandler || !s_pxMissionHandler->GetCurrentZone()) { return; }
	//Save any delayed messages
	Asura_Chunk_StaticMessages_InGame::Create(xSaveBlock);

	s_pxMissionHandler->GetCurrentZone()->SaveEntitiesToBuffer(xSaveBlock);
	
	//save meta music state
	Asura_Chunk_MetaMusic_Ingame_State::Create( xSaveBlock );

//#ifdef ASURA_USE_AXON
//    Axon_Chunk_LocationSystem::Create( xSaveBlock );
//#ifdef ASURA_TOOLS
//    Axon_Chunk_LocationSystem_ToolsData::Create( xSaveBlock );
//#endif
//#endif
}

/// End of function 'Asura_Streaming_System::SaveMissionDataToBuffer'. ///

////////////////////
// MarkZoneLoaded //
////////////////////
// Mark that a zone has finished loading.
void Asura_Streaming_System::MarkZoneLoaded()
{
	s_uFrameNumberLastZoneLoad = Asura_Timers::GetFrameNumber();
}

#endif	// ASURA_USE_STREAMING

/// End of file. ///
