/////////////////////////////////////////////////////////////////////////////////////////
//
//	FILE:			Tools_Shapes.cpp
//
//	DESCRIPTION:	Tools side management of shapes in Asura
//
//	AUTHOR:			Richard M. Smith (5 September 2002)
//
/////////////////////////////////////////////////////////////////////////////////////////


//////////////
// INCLUDES //
//////////////

#include "Core/Asura.h"

#include "Shapes/Tools_Shapes.h"

#include "Animation/Asura_Hierarchy.h"
#include "Debugging/Asura_Assert.h"
#include "ChunkSystem/Asura_Chunk_Mesh.h"
#include "Core/Asura_HashTree.h"
#include "Core/Asura_List.h"
#include "Core/Asura_String.h"
#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_UserInput.h"
#include "Cutscene/Asura_Cutscene_System.h"
#include "Cutscene/Asura_Cutscene.h"
#include "Entity/Asura_Entity.h"
#include "Entity/PhysicalObject/Asura_ServerEntity_PhysicalObject.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Key_Combination.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Overlay_System.h"
#include "Misc/Tools_Modal_Overlay.h"
#include "Misc/Tools_IniFile.h"
#include "Rendering/Asura_Module.h"
#include "SpecialFX/Asura_Fragment_System.h"
#include "SpecialFX/Asura_Skybox.h"

#include "PC/FileSystem/Tools_FileSystem.h"

#include "Load&Save/LevelExport/Tools_Level_Export.h"

#include "Mesh/Asura_Mesh.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Shapes/Asura_Shape_System.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Lighting/Tools_LightSets.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Load&Save/Tools_AsuraFileContents.h"
#include "Load&Save/Tools_BatchProcessing.h"

#include "ChunkSystem/Asura_Chunk_Loaders.h"
#include "ChunkSystem/Asura_Huffman.h"
#include "PC/Tools_PC_Export.h"
#include "PSP/Tools_PSP_Export.h"
#include "PS2/PS2Strip/PS2Strip_ToolsInterface.h"
#include "PS3/Tools_PS3_Export.h"
#include "Wii/Tools_Wii_Export.h"
#include "Xbox360/Tools_Xbox360_Export.h"
#include "ChunkSystem/Asura_Saveblock.h"
#include "ChunkSystem/Asura_Chunk_Shape.h"
#include "ChunkSystem/Asura_Chunk_Fragment.h"

#ifdef ASURA_USE_BOTANICALS
#include "Botanicals/Asura_Botanicals_TemplateManager.h"
#endif

/////////////
// DEFINES //
/////////////
#define	MAX_LINES_IN_SHAPE_TABLE_LIST	( 20 )


////////////////////
// STATIC MEMBERS //
////////////////////
Tools_ShapeTable* 			Tools_ShapeTable::s_pxThis = 0;

Tools_ShapeTableElement*	Tools_ShapeTable::s_pxTable						= NULL;
int							Tools_ShapeTable::s_iNumElementsInTable			= 0;

Tools_ShapeTableElement*	Tools_ShapeTable::s_pxExportShapes				= NULL;
int							Tools_ShapeTable::s_iNumExportShapes			= 0;

Asura_Hash_ID				Tools_ShapeTable::s_uDefaultShapeID = ASURA_HASH_ID_UNSET;


bool						Tools_ShapeTable::s_bCloseShapeTable			= false;
Tools_ShapeTable_Overlay*	Tools_ShapeTable::s_pxOverlay					= NULL;
Tools_ShapeTableElement*	Tools_ShapeTable::s_pxShapeTableSelection		= NULL;

char**						Tools_ShapeTable_Overlay::s_pszListBoxStrings	= NULL;
char**						Tools_ShapeTable_Overlay::s_pszAlphabetisedList = NULL;
int							Tools_ShapeTable_Overlay::s_iNumberOfEntriesInList = 0;
int							Tools_ShapeTable_Overlay::s_iListBoxOffset		= 0;
int							Tools_ShapeTable_Overlay::s_iListBoxSelection	= 0;
int	*						Tools_ShapeTable_Overlay::s_aiMappings			= 0;



/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTableElement::Tools_ShapeTableElement
// PARAMETERS	Zero terminated string containing the filename of the new shape to add
//				Optional linked list head to add this element to
//				Optional reference to the list len integer
// RETURNS		Default (Constructor)
// DESCRIPTION	Creates a new element for the shape table
// AUTHOR		Richard M. Smith (5 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
Tools_ShapeTableElement::Tools_ShapeTableElement( const char* szShapeFile, Tools_ShapeTableElement*& pxList, int& iListLen )
{
	m_bIncludeInExport = true;
	m_bUsedInGame = true;
	Asura_String	xFullFileName		= szShapeFile;

// Convert the string that we will work with to lower case...
	strlwr( xFullFileName.GetString() );

// Convert all slashes to forward slashes...
	{
		char*	pcPtr = xFullFileName.GetString();

		while( '\0' != *pcPtr )
		{
			if( '\\' == *pcPtr )
			{
				*pcPtr = '/';
			}

			pcPtr++;
		}
	}

	Asura_String	xHashingFileName;
	Asura_String	xShapeName;

// Get the relevant strings...
	char* szRelativePath	= strstr( xFullFileName.GetString(), "objects" );
	char* szObjectsRelativePath = szRelativePath;
	Asura_Assert( NULL != szObjectsRelativePath, "Serious error - Object not in 'objects' directory" );

	if( NULL != szObjectsRelativePath )
	{
		xHashingFileName = szObjectsRelativePath;
		szObjectsRelativePath += strlen( "objects" ) + 1;	// Add one for the slash...
		xShapeName = szObjectsRelativePath;
	}
	else
	{
		xHashingFileName	= xFullFileName.GetString();
		xShapeName			= xFullFileName.GetString();
	}

// Convert the filename to a HASH value
	m_uHashID	= Asura_GetHashID( xHashingFileName.GetString() );
	Asura_Assert( ASURA_HASH_ID_UNSET != m_uHashID, "Error - Hash value equals the 'UNSET' value" );

// Check if this is already in the table...
	Tools_ShapeTableElement*	pxAlreadyExistsInTable = Tools_ShapeTable::FindElementByHashID( m_uHashID, pxList );
	Asura_Assert( NULL == pxAlreadyExistsInTable, "Element already in table" );

	if( NULL != pxAlreadyExistsInTable )
	{
		delete this;
		return;
	}

// Store the filename and shape names
	strcpy( m_szFile, szRelativePath );
	strcpy( m_szShapeName, xShapeName.GetString() );
	_splitpath( xFullFileName.GetString(), NULL, NULL, m_szMeshName, NULL );

// There is no following link yet
	m_pxNext = NULL;

	m_uReferenceCount = 0;

	//get the modified time of the file for a checksum
	FILETIME xModifiedTime;

	char szFullShapePath[MAX_PATH];
	Asura_FileAccess::PlatformGetNetworkFilename(szShapeFile,szFullShapePath,MAX_PATH);
	if(Tools_FileSystem::GetModifiedTime(szFullShapePath,&xModifiedTime))
	{
		m_uChecksum = xModifiedTime.dwLowDateTime;
	}
	else
	{
		Asura_Assertf(0,("Failed to get modified time for %s",szShapeFile));
		m_uChecksum = 0;
	}


// add this to the end of the table
	Tools_ShapeTable::AddElement( this, pxList, iListLen );
	
} // End Function 'Tools_ShapeTableElement::Tools_ShapeTableElement'




Tools_ShapeTableElement::~Tools_ShapeTableElement()
{
#if _DEBUG
	Tools_ShapeTableElement*	pxList = Tools_ShapeTable::s_pxExportShapes;

	while( NULL != pxList )
	{
		if( this == pxList )
		{
			Asura_Assert( false, "Error - This is in the export list" );
		}

		pxList = pxList->m_pxNext;
	}

#endif _DEBUG
}










/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::Initialise
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Performs any necessary initialisation of the shape table
// AUTHOR		Richard M. Smith (5 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::Initialise( void )
{
    // Catch data being added before this is initialised.
    Asura_Assert( !s_iNumElementsInTable && !s_iNumExportShapes, "" );

	s_pxTable				= NULL;
	s_iNumElementsInTable	= 0;

	s_pxExportShapes		= NULL;
	s_iNumExportShapes		= 0;

	Tools_ShapeTable_Overlay::s_pszListBoxStrings	= NULL;
	Tools_ShapeTable_Overlay::s_iListBoxOffset		= 0;
	Tools_ShapeTable_Overlay::s_iListBoxSelection	= 0;

} // End Function 'Tools_ShapeTable::Initialise'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::Shutdown
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Shuts down the shape table
// AUTHOR		Richard M. Smith (5 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::Shutdown( void )
{
	while( NULL != s_pxTable )
	{
		RemoveElement( s_pxTable );
	}

	Asura_Assert( 0 == s_iNumElementsInTable, "Error - Shutdown failed" );

	while( NULL != s_pxExportShapes )
	{
		RemoveElement( s_pxExportShapes, s_pxExportShapes, s_iNumExportShapes );
	}

	Asura_Assert( 0 == s_iNumExportShapes, "Error - Shutdown failed" );

} // End Function 'Tools_ShapeTable::Shutdown'




class STB_Header
{
public:
	char	szID[4];
	int		iNumElements;
};

/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::BuildShapeTable
// PARAMETERS	None
// DESCRIPTION	Generates shape table from shapes used by entities etc
// AUTHOR		RAR 01/03/2004
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::BuildShapeTable( void )
{

#ifdef ASURA_TOOLS
	//get a list of shapes used
	List<Asura_Hash_ID> xShapeList;
	BuildListOfUsedShapes(xShapeList,xShapeList);

	Asura_HashTree xAddedTree;


	//add all the shapes in the list
	for(LIF<Asura_Hash_ID> xShapeLIF(&xShapeList);!xShapeLIF.done();xShapeLIF.next())
	{
		Asura_Hash_ID uHash = xShapeLIF();
		if(uHash == ASURA_HASH_ID_UNSET) continue;

		//dealt with this shape already?
		int iJunkIndex;
		if(xAddedTree.FindNodeIndexByHash(uHash,true,iJunkIndex))
		{
			continue;
		}

		//get the filename for this shape and add it
		const char* pszFilename = Asura_HashedFilename_System::GetFilename(uHash,false);
		if(!pszFilename)
		{
			//Hmm , couldn't find it...
			//Leave the asserts about this to Tools_Entity_PhysicalObject , since it can
			//provide some more useful information (the names of the objects that use
			//the missing shapes)
			continue;
		}

		//add this shape to the shape table
		AddShapeByFilename(pszFilename,false);
	}


#endif ASURA_TOOLS

} // End Function 'Tools_ShapeTable::LoadTable'




/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::SelectShape
// PARAMETERS	None
// RETURNS		The Hash ID of the selected shape
// DESCRIPTION	Puts the shape table on screen so the user can select from or add a shape
//				to the shape table
// AUTHOR		Richard M. Smith (5 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
Asura_Hash_ID Tools_ShapeTable::SelectShape( Asura_Hash_ID uInitiallySelectedHashID )
{
// Put the modal shape table overlay up...

        s_uDefaultShapeID = uInitiallySelectedHashID;
    

// (RMS) I need a loop here so that I can destroy one version of the overlay and then
// recreate it when a shape is added or removed from the table.
	do
	{
		Asura_Assert( NULL == s_pxOverlay, "Error - Old overlay not deleted!" );
		s_pxOverlay = NULL;

		s_pxOverlay = new Tools_ShapeTable_Overlay();
		Asura_Assert( NULL != s_pxOverlay, "Out of mem" );

		s_pxOverlay->DoModal();

		delete s_pxOverlay;
		s_pxOverlay = NULL;

		if (Tools_ShapeTable_Overlay::s_aiMappings)
		{	
			delete[] Tools_ShapeTable_Overlay::s_aiMappings;
			Tools_ShapeTable_Overlay::s_aiMappings = NULL;
		}
	}
	while( false == s_bCloseShapeTable );
	

	if( NULL != s_pxShapeTableSelection )
	{
		//set default shape , so it gets selected by default when the overlay is next shown
		s_uDefaultShapeID = s_pxShapeTableSelection->m_uHashID;
		return s_pxShapeTableSelection->m_uHashID;
	}
	return ASURA_HASH_ID_UNSET;

} // End Function 'Tools_ShapeTable::SelectShape'





// RICHARDS	/////////////////////////////////////////////////////////////////////////////////////////
// RICHARDS	// FUNCTION		Tools_ShapeTable::RemoveNonCommonShapes
// RICHARDS	// PARAMETERS	None
// RICHARDS	// RETURNS		None
// RICHARDS	// DESCRIPTION	Removes all elements in the table whose reference count is not
// RICHARDS	//				ASURA_SHAPE_REFERENCE_COUNT_DO_NOT_DELETE
// RICHARDS	// AUTHOR		Richard M. Smith (13 September 2002)
// RICHARDS	/////////////////////////////////////////////////////////////////////////////////////////
// RICHARDS	void Tools_ShapeTable::RemoveNonCommonShapes( void )
// RICHARDS	{
// RICHARDS		Tools_ShapeTableElement*	pxCurr = s_pxTable;
// RICHARDS		
// RICHARDS		while( NULL != pxCurr )
// RICHARDS		{
// RICHARDS			if( ASURA_SHAPE_REFERENCE_COUNT_DO_NOT_DELETE != pxCurr->m_uReferenceCount )
// RICHARDS			{
// RICHARDS				RemoveElement( pxCurr );
// RICHARDS	
// RICHARDS			// Start again at the beginning of the table...
// RICHARDS				pxCurr = s_pxTable;
// RICHARDS			}
// RICHARDS			else
// RICHARDS			{
// RICHARDS				pxCurr = pxCurr->m_pxNext;
// RICHARDS			}
// RICHARDS		}
// RICHARDS	
// RICHARDS	} // End Function 'Tools_ShapeTable::RemoveNonCommonShapes'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::ListBoxCallBack
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Call back for the dialog list box
// AUTHOR		Richard M. Smith (9 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::ListBoxCallBack( void )
{
// RICHARDS		int	iCurrentSelectionIndex = s_iListBoxOffset + s_iListBoxSelection;

// (RMS) Can do stuff here like preview....eventually!!!...maybe

} // End Function 'Tools_ShapeTable::ListBoxCallBack'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::OkaySelected
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Exit function from the overlay when OK was selected
// AUTHOR		Richard M. Smith (9 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::OkaySelected( void )
{
	int	iCurrentSelectionIndex = Tools_ShapeTable_Overlay::s_iListBoxOffset + Tools_ShapeTable_Overlay::s_iListBoxSelection;


/*
// If there are no shapes then act as if cancel was selected...
	// Now never happens, as there's always 'none'
	if( 0 == s_iNumElementsInTable )
	{
		CancelSelected();
		return;
	}
*/

// What did you choose...what did you choose???
	Asura_Assert( iCurrentSelectionIndex < s_iNumElementsInTable+1, "Error - Invalid selection" );
	
	Tools_ShapeTable::s_pxShapeTableSelection = NULL;

	if (Tools_ShapeTable_Overlay::s_aiMappings[iCurrentSelectionIndex]>0)
	{
		Tools_ShapeTable::s_pxShapeTableSelection = GetElementByIndex( Tools_ShapeTable_Overlay::s_aiMappings[iCurrentSelectionIndex]-1 );
	Asura_Assert( NULL != Tools_ShapeTable::s_pxShapeTableSelection, "Invalid selection" );
	}

// Indicate that we need to break out of the overlay loop
	Tools_ShapeTable::s_bCloseShapeTable = true;

} // End Function 'Tools_ShapeTable::OkaySelected'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::CancelSelected
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Exit function from the overlay when CANCEL was selected
// AUTHOR		Richard M. Smith (9 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::CancelSelected( void )
{
// Indicate that we need to break out of the overlay loop
	Tools_ShapeTable::s_bCloseShapeTable = true;

} // End Function 'Tools_ShapeTable::CancelSelected'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::AddShapeByRequester
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Allows the user to add a new shape to the shape table
// AUTHOR		Richard M. Smith (6 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::AddShapeByRequester( void )
{
	//don't allow shapes to be added without access to the shape table file
	if(!Tools_SaveAsura::CanSaveShapes())
	{
		Message_Overlay xOverlay("The .shp and .stb files must be writeable for shapes to be added.");
		xOverlay.DoModal();
		return;		
	}
	static Asura_String s_xDefaultPath = "Objects";
		
	Tools_FileSystem_DialogInfo	xFileSelector;

	xFileSelector.DefaultExtension		= ".ASR";
	xFileSelector.DefaultFilename		= "";
	xFileSelector.DefaultPath			= s_xDefaultPath.GetString();
	xFileSelector.FileFilter			= "Object Files\0*.ASR;\0";
	xFileSelector.WindowTitle			= "Load Object(s)...";

// RICHARDS		xFileSelector.AllowMultipleSelect	= false;
	xFileSelector.AllowMultipleSelect	= true;

	Asura_String xName = Tools_FileSystem::GetLoadFilename( xFileSelector );

	if(!xName.IsNull())
	{
		//remember the most recent directory selected
		xName.SplitFilename(&s_xDefaultPath,0,0);
	}

	bool bShapeAdded = false;
	while( !xName.IsNull() )
	{
		Asura_Hash_ID uHash = AddShapeByFilename( xName.GetString() );
		//set he default shape to the one just loaded
		if(uHash != ASURA_HASH_ID_UNSET)
		{
			s_uDefaultShapeID = uHash;
			bShapeAdded = true;
		}

		xName = Tools_FileSystem::GetNextLoadFilename();
	}

	if(bShapeAdded) Asura_Fragment_System::LoadFragments();

} // End Function 'Tools_ShapeTable::AddShapeByRequester'


/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::RepairShapeTable()
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Checks all the shapes in the shape table are loaded into the shape system
//				Any shapes that aren't in the shape system are loaded
// AUTHOR		Pete Ellacott (Aug 2003)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::RepairShapeTable()
{
	// Iterate through all the enteries in the shape table
	Tools_ShapeTableElement*	pxNext = s_pxTable;
	
	while( NULL != pxNext )
	{
		// Find the next element now, incase we have to delete the current element
		Tools_ShapeTableElement *pxCurr = pxNext;
		pxNext = pxCurr->m_pxNext;
	
		// Is the shape in the shape system ?	
		if ( NULL == Asura_Shape_System::GetShape( pxCurr->m_uHashID ) )
		{
			// This shape is in the shape table, but not in the shape system :o(
			// Remove it and re add it.
			Asura_String xszShapePath = pxCurr->m_szFile;
			RemoveElement( pxCurr );
			AddShapeByFilename( xszShapePath.GetString() );
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::AddShapeByFilename
// PARAMETERS	None
// RETURNS		Hash ID of the element in the table or UNSET on error
// DESCRIPTION	Allows the user to add a new shape to the shape table
// AUTHOR		Richard M. Smith (6 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
Asura_Hash_ID Tools_ShapeTable::AddShapeByFilename( const char* szFilename, bool bShowOverlays )
{
	return AddShapeByFilenameInternal(szFilename,s_pxTable,s_iNumElementsInTable,bShowOverlays);
}

Asura_Hash_ID Tools_ShapeTable::AddShapeByFilenameInternal( const char* szFilename,Tools_ShapeTableElement*& pxList,int& iListLength,bool bShowOverlays )
{
	Asura_String xName = szFilename;
	_strlwr( xName.GetString() );

	{
		char*	szStart = strstr( xName.GetString(), "objects" );

		if(!szStart)
		{
			Asura_Assert( NULL != szStart, "File not in 'objects' folder");
			return ASURA_HASH_ID_UNSET;
		}
	}


	Tools_ShapeTableElement*	pxExistingElement = FindElementByFilename( xName.GetString(),pxList);
	
	if( NULL != pxExistingElement )
	{
	// Already exists in table
		return pxExistingElement->m_uHashID;
	}

	
	Tools_ShapeTableElement* pxElement = new Tools_ShapeTableElement( xName.GetString(),pxList,iListLength );


// Do we need to load this shape?
	if( NULL == Asura_Shape_System::GetShape( pxElement->m_uHashID ) )
	{
		//Check the asura file to make sure it is in fact a shape file. RAR 02/05/03
		if(!IsValidShapeFile(szFilename,bShowOverlays))
		{
			RemoveElement( pxElement,pxList,iListLength );
			return ASURA_HASH_ID_UNSET;
		}

		
		char	szMeshName[ _MAX_PATH ];
		_splitpath( pxElement->m_szFile, NULL, NULL, szMeshName, NULL );		

		
		int iMeshesBeforeLoad = Asura_Mesh_System::GetNumberOfMeshes();
		
		Asura_Shape* pxNewShape = Asura_Shape_System::NewShape( pxElement->m_uHashID );


		if( pxNewShape->CreateShape( pxElement->m_szFile, szMeshName ) )
		{
		// Successfully created the shape
			Asura_Texture_Management::LoadTextures();
		}
		else
		{
		// Failed to create the shape
			Asura_Shape_System::DeleteShape(pxNewShape);
			RemoveElement( pxElement,pxList,iListLength);



		// Delete the meshes that were just loaded (so that we don't get 'Mesh already loaded' errors)
			while(Asura_Mesh_System::GetNumberOfMeshes()>iMeshesBeforeLoad)
			{
				Asura_Mesh*		pxMesh = NULL;

				pxMesh = Asura_Mesh_System::GetMesh( Asura_Mesh_System::GetNumberOfMeshes() - 1 );

				if( NULL != pxMesh )
				{
					Asura_Mesh_System::DeleteMesh( pxMesh );
				}
			}
			return ASURA_HASH_ID_UNSET;
		}
	}

	return pxElement->m_uHashID;
	

} // End Function 'Tools_ShapeTable::AddShapeByFilenameInternal'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::RemoveShape
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Allows the user to try and remove a shape from the shape table
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::RemoveShape( void )
{
	#if 0 //doesn't matter any more. RAR 26/02/2004
	if(!Tools_SaveAsura::CanSaveEntity() || !Tools_SaveAsura::CanSaveCutscenes())
	{
		//Don't allow shapes to be removed unless the user has both the cutscene and entity files.
		//While these files don't actually need to be modified , this makes sure that we don't
		//end up removing shapes that are currently being placed by someone else that does have access
		//to these files. RAR 27/03/03
		YesNo_Overlay xOverlay("Warning.\nYou haven't got both the cutscene and entity files checked out , so it may not be safe to remove the shape.\nContinue anyway?.");
		if(!xOverlay.DoModal()) return;
	}
	#endif
	

	if (s_iNumElementsInTable==0)
	{
		return;
	}

// Which one is currently selected?
	int	iCurrentSelectionIndex = Tools_ShapeTable_Overlay::s_iListBoxOffset + Tools_ShapeTable_Overlay::s_iListBoxSelection;

	// Handle the new 'none' option, which will be at the top of the list if there's at least one
	// element present
	if (iCurrentSelectionIndex<=0)
	{
		return;
	}

	Asura_Assert( iCurrentSelectionIndex >= 0 && iCurrentSelectionIndex < (s_iNumElementsInTable+1) , "Invalid index" );

	if( iCurrentSelectionIndex >= 0 &&
		iCurrentSelectionIndex < s_iNumElementsInTable )
	{
	// Get the element
		Tools_ShapeTableElement* pxElement = GetElementByIndex( Tools_ShapeTable_Overlay::s_aiMappings[iCurrentSelectionIndex]-1 );

		Asura_Assert( NULL != pxElement, "No element found" );
		
		if( NULL != pxElement )
		{
		// Check the reference count...
			if( pxElement->m_uReferenceCount > 0 )
			{
				Tools_ShapeTable_WarningDlg	xWarning("Cannot remove shape that is still referenced");
					
				xWarning.DoModal();
			}
			else
			{
			// Check if this is in the export shapes...
				bool	bIsExportShape = false;

				if( FindElementByHashID( pxElement->m_uHashID, s_pxExportShapes ) )
				{
					bIsExportShape = true;
				}

				if( false == bIsExportShape )
				{
				// Delete the relevant mesh (so that we don't get 'Mesh already loaded' errors)
					Asura_Mesh*		pxMesh;
					Asura_String	xMeshName = pxElement->m_szMeshName;
					pxMesh = Asura_Mesh_System::GetMesh( xMeshName );

					if( NULL == pxMesh )
					{
					// Get the most recently added mesh instead then...
						if( Asura_Mesh_System::GetNumberOfMeshes() > 0 )
						{
							pxMesh = Asura_Mesh_System::GetMesh( Asura_Mesh_System::GetNumberOfMeshes() - 1 );
						}
					}

				//Remove this mesh , and any meshes used as LOD models by it
					if(IsSafeToDeleteMesh(pxMesh))
					{
						while(pxMesh)
						{
							Asura_Mesh* pxMeshToDelete = pxMesh;
							pxMesh = pxMesh->m_pxNextLowerMesh;
							Asura_Mesh_System::DeleteMesh( pxMeshToDelete );
						}
					}

				// Remove the shape from the shape system
					Asura_Shape* pxShape = Asura_Shape_System::GetShape( pxElement->m_uHashID );
					Asura_Assert( NULL != pxShape, "Error - Did not find shape in shape system" );
					Asura_Shape_System::DeleteShape(pxShape);
				}

			// Finally remove from the shape table
				RemoveElement( pxElement );
			}
		}
	}

} // End Function 'Tools_ShapeTable::RemoveShape'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::FindElementByFilename
// PARAMETERS	Filename to search for
//				Optional list to search
// RETURNS		Pointer to the element with matching hash ID or NULL if not found
// DESCRIPTION	See returns
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
Tools_ShapeTableElement* Tools_ShapeTable::FindElementByFilename( const char* szFilename, Tools_ShapeTableElement* pxList )
{
	Tools_ShapeTableElement*	pxCurr = pxList;

	Asura_String	xSearchString = szFilename;

// Convert all slashes to forward slashes...
	{
		char*	pcPtr = xSearchString.GetString();

		while( '\0' != *pcPtr )
		{
			if( '\\' == *pcPtr )
			{
				*pcPtr = '/';
			}

			pcPtr++;
		}
	}
	//and make sure its lower case
	xSearchString.MakeLowercase();

	char*	szRelativePath = strstr( xSearchString.GetString(), "objects" );

	Asura_Assert( NULL != szRelativePath, "Error: All objects must be in the objects directory!" );

	if( NULL == szRelativePath )
	{
		return NULL;
	}

	while( NULL != pxCurr )
	{
		if( 0 == strcmp( szRelativePath, pxCurr->m_szFile ) )
		{
		// Match!
			return pxCurr;
		}

		pxCurr = pxCurr->m_pxNext;
	}

	return NULL;

} // End Function 'Tools_ShapeTable::FindElementByFilename'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::AddElement
// PARAMETERS	New shape table element
// RETURNS		None
// DESCRIPTION	Adds the specified shape table element to the end of the linked list
//				that forms the table
// AUTHOR		Richard M. Smith (5 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::AddElement( Tools_ShapeTableElement* pxNewElement, Tools_ShapeTableElement*& pxList, int& iListLen )
{
	Asura_Assert( NULL != pxNewElement, "Error - What element?" );

	if( NULL == pxList )
	{
		Asura_Assert( 0 == iListLen, "Error - Element count is wrong" );
		pxList = pxNewElement;
		iListLen++;
	}
	else
	{
		Asura_Assert( iListLen > 0, "Error - Element count is wrong" );

	// Find the last link
		Tools_ShapeTableElement* pxLastLink = pxList;
		
		while( NULL != pxLastLink->m_pxNext )
		{
			pxLastLink = pxLastLink->m_pxNext;
		}

	// Add this new one after that then
		pxLastLink->m_pxNext = pxNewElement;
		iListLen++;
	}

} // End Function 'Tools_ShapeTable::AddElement'






/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::RemoveElement
// PARAMETERS	Element to find and remove
//				Optional list header
//				Optional list length integer
// RETURNS		None
// DESCRIPTION	Removes the specified shape table element from the linked list that
//				forms the table
// AUTHOR		Richard M. Smith (5 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::RemoveElement( Tools_ShapeTableElement* pxElementToRemove, Tools_ShapeTableElement*& pxList, int& iListLen )
{
	Asura_Assert( NULL != pxElementToRemove, "Error - What element?" );
	Asura_Assert( NULL != pxList, "Error - What list?" );

// First check if the one to remove is the first one...
	if( pxElementToRemove == pxList )
	{
		pxList = pxList->m_pxNext;
	}
	else
	{
	// Find the link that preceeds the element to remove
		Tools_ShapeTableElement* pxPreviousLink	= pxList;

		while( pxPreviousLink->m_pxNext != pxElementToRemove &&
				NULL != pxPreviousLink )
		{
			pxPreviousLink = pxPreviousLink->m_pxNext;
		}

		Asura_Assert( NULL != pxPreviousLink, "Error - Element to remove is NOT in the shape table!" );
		if( NULL != pxPreviousLink )
		{
			Asura_Assert( pxPreviousLink->m_pxNext == pxElementToRemove, "Error - Wrong element found" );
			pxPreviousLink->m_pxNext = pxElementToRemove->m_pxNext;
		}
	}

	delete pxElementToRemove;
	iListLen--;

} // End Function 'Tools_ShapeTable::RemoveElement'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::UpdateReferenceCounts
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Loops over all physical object entities and counts the references to
//				each shape in the table
// AUTHOR		Richard M. Smith (5 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::UpdateReferenceCounts( void )
{
	ClearReferenceCount();

	//get a list of shapes used
	List<Asura_Hash_ID> xExportShapeList;
	List<Asura_Hash_ID> xNonExportShapeList;
	BuildListOfUsedShapes(xExportShapeList,xNonExportShapeList);

	//update reference counts for them
	for(LIF<Asura_Hash_ID> xShapeLIF(&xExportShapeList);!xShapeLIF.done();xShapeLIF.next())
	{
		if(xShapeLIF()!=ASURA_HASH_ID_UNSET)
		{
			IncrementReference( xShapeLIF(),true);
		}
	}
	for(LIF<Asura_Hash_ID> xShapeLIF(&xNonExportShapeList);!xShapeLIF.done();xShapeLIF.next())
	{
		if(xShapeLIF()!=ASURA_HASH_ID_UNSET)
		{
			IncrementReference( xShapeLIF(),false);
		}
	}

} // End Function 'Tools_ShapeTable::UpdateReferenceCounts'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::ClearReferenceCount
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Set the reference count of all elements in the shape table to zero
// AUTHOR		Richard M. Smith (5 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::ClearReferenceCount( void )
{
	Tools_ShapeTableElement* pxElement = s_pxTable;

	while( NULL != pxElement )
	{
		pxElement->m_uReferenceCount = 0;
		pxElement->m_bUsedInGame = false;
		pxElement = pxElement->m_pxNext;
	}

} // End Function 'Tools_ShapeTable::ClearReferenceCount'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::IncrementReference
// PARAMETERS	Hash ID of the shape to incrememnt the reference count for
// RETURNS		None
// DESCRIPTION	Increment the reference count of the shape with the specified hash ID
// AUTHOR		Richard M. Smith (5 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::IncrementReference( const Asura_Hash_ID uShapeHashID, bool bUsedInGame /*= true*/ )
{
	Tools_ShapeTableElement* pxElement = FindElementByHashID( uShapeHashID );

#ifdef ASURA_ASSERT
    if (NULL == pxElement)
    {
        const char* szFilename = Asura_HashedFilename_System::GetFilename(uShapeHashID);
        Asura_Assertf( NULL != pxElement, ("Error - Shape with specified hash ID (0x%08x %s) not found in table", uShapeHashID, szFilename) );
    }
#endif

	if( NULL != pxElement )
	{
		pxElement->m_uReferenceCount++;
		if(bUsedInGame) pxElement->m_bUsedInGame = bUsedInGame;
	}

} // End Function 'Tools_ShapeTable::IncrementReference'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::RemoveZeroReferenceCountElements
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Loops over all elements in the table and removes those with a zero
//				reference count
// AUTHOR		Richard M. Smith (11 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::RemoveZeroReferenceCountElements( void )
{
	UpdateReferenceCounts();

	Tools_ShapeTableElement*	pxCurr = s_pxTable;
	Tools_ShapeTableElement*	pxNext;

	while( NULL != pxCurr )
	{
		pxNext = pxCurr->m_pxNext;

	// Get the element
		Tools_ShapeTableElement* pxElement = pxCurr;

	// Check the reference count...
		if( 0 == pxElement->m_uReferenceCount )
		{
		// If this is not one of the common export shapes then delete the mesh and shape...
			if( false == IsCommonExportShape( pxElement->m_uHashID ) )
			{
			// Delete the relevant mesh (so that we don't get 'Mesh already loaded' errors)
				Asura_Mesh*		pxMesh;
				Asura_String	xMeshName = pxElement->m_szMeshName;
				pxMesh = Asura_Mesh_System::GetMesh( xMeshName );

				if(IsSafeToDeleteMesh(pxMesh))
				{
					while(pxMesh)
					{
						Asura_Mesh* pxMeshToDelete = pxMesh;
						pxMesh = pxMesh->m_pxNextLowerMesh;
						Asura_Mesh_System::DeleteMesh( pxMeshToDelete );
					}
				}

			// Remove the shape from the shape system
				Asura_Shape* pxShape = Asura_Shape_System::GetShape( pxElement->m_uHashID );
				Asura_Assert( NULL != pxShape, "Error - Did not find shape in shape system" );
				Asura_Shape_System::DeleteShape(pxShape);
			}

		// Finally remove from the shape table
			RemoveElement( pxElement );
		}

		pxCurr = pxNext;
	}

} // End Function 'Tools_ShapeTable::RemoveZeroReferenceCountElements'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::FindElementByHashID
// PARAMETERS	Hash ID of the shape to find
//				Optional list to seach
// RETURNS		Pointer to the matching element in the table or NULL if not found
// DESCRIPTION	Find a table element by hash ID
// AUTHOR		Richard M. Smith (5 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
Tools_ShapeTableElement* Tools_ShapeTable::FindElementByHashID( const Asura_Hash_ID uShapeHashID, Tools_ShapeTableElement* pxList )
{
	Tools_ShapeTableElement* pxElement = pxList;

	while( NULL != pxElement )
	{
		if( uShapeHashID == pxElement->m_uHashID )
		{
			break;
		}

		pxElement = pxElement->m_pxNext;
	}

	return pxElement;

} // End Function 'Tools_ShapeTable::FindElementByHashID'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::GetElementByIndex
// PARAMETERS	Index of element to return
// RETURNS		Pointer to the element at the specified index or NULL on error
// DESCRIPTION	See returns 
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
Tools_ShapeTableElement* Tools_ShapeTable::GetElementByIndex( int iIndex, Tools_ShapeTableElement* pxList, int iListLen )
{
	Asura_Assert( iIndex >= 0 && iIndex < iListLen, "Invalid index" );

	if( iIndex < 0 ||
		iIndex >= iListLen )
	{
		return NULL;
	}

	Tools_ShapeTableElement* pxCurr = pxList;

	while( NULL != pxCurr )
	{
		if( 0 == iIndex )
		{
			return pxCurr;
		}

		pxCurr = pxCurr->m_pxNext;
		iIndex--;
	}

	return NULL;

} // End Function 'Tools_ShapeTable::GetElementByIndex'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::LookupObjectMeshName
// PARAMETERS	Hash ID of element to look up
// RETURNS		Mesh name of the shape with the specified hash ID or NULL if not found
// DESCRIPTION	See returns 
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
const char* Tools_ShapeTable::LookupObjectMeshName( Asura_Hash_ID uShapeID )
{
	Tools_ShapeTableElement* pxElement = FindElementByHashID( uShapeID );
	if(!pxElement) pxElement = FindElementByHashID(uShapeID,s_pxExportShapes);
	//Removing this assert in favour of letting the caller deal with it , since it doesn't
	//help with tracking down the problem at all. RAR 09/05/03
	//Asura_Assert( NULL != pxElement, "Element not found" );

	if( NULL != pxElement )
	{
		return pxElement->m_szMeshName;
	}

	return NULL;

} // End Function 'Tools_ShapeTable::LookupObjectMeshName'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::LookupObjectFileName
// PARAMETERS	Hash ID of element to look up
// RETURNS		File name of the shape with the specified hash ID or NULL if not found
// DESCRIPTION	See returns 
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
const char* Tools_ShapeTable::LookupObjectFileName( Asura_Hash_ID uShapeID )
{
	Tools_ShapeTableElement* pxElement = FindElementByHashID( uShapeID );
	if(!pxElement) pxElement = FindElementByHashID(uShapeID,s_pxExportShapes);
	
	//Removing this assert in favour of letting the caller deal with it , since it doesn't
	//help with tracking down the problem at all. RAR 09/05/03
	//Asura_Assert( NULL != pxElement, "Element not found" );

	if( NULL != pxElement )
	{
		return pxElement->m_szFile;
	}

	return NULL;

} // End Function 'Tools_ShapeTable::LookupObjectFileName'


/////////////////////////////////////////////////////////////////////////////////////////
Asura_Mesh* Tools_ShapeTable::GetMeshByIndex(unsigned int uIndex, bool bCommonShapes /*= false*/ )
{
	const char* pszMeshName = GetMeshNameByIndex(uIndex,bCommonShapes);

	return pszMeshName ? Asura_Mesh_System::GetMesh(pszMeshName) : 0;

}

const char* Tools_ShapeTable::GetMeshNameByIndex(unsigned int uIndex, bool bCommonShapes /*= false*/ )
{
	unsigned int	uNumElements;

	if( false == bCommonShapes )
	{
		uNumElements = (unsigned int)s_iNumElementsInTable;
	}
	else
	{
		uNumElements = (unsigned int)s_iNumExportShapes;
	}

	Asura_Assert( uIndex < uNumElements, "(RAR) Invalid index passed to Tools_ShapeTable::GetMeshNameByIndex" );

	//get the tools shape table entry from the index number
	const Tools_ShapeTableElement*	pxElement = 0;
	if( false == bCommonShapes )
	{
		pxElement = GetElementByIndex( uIndex );
	}
	else
	{
		pxElement = GetElementByIndex( uIndex, s_pxExportShapes, s_iNumExportShapes );
	}

	if(!pxElement) return 0;
	Asura_Assert( ASURA_HASH_ID_UNSET != pxElement->m_uHashID, "Error - Invalid entry in tools shape table" );

	return pxElement->m_szMeshName;

}

Asura_Mesh* Tools_ShapeTable::GetMeshByHashID(Asura_Hash_ID uShapeID)
{
	const char* szMeshName = LookupObjectMeshName(uShapeID);
	if(!szMeshName) return 0;
	return Asura_Mesh_System::GetMesh(szMeshName);
}

Asura_Hash_ID Tools_ShapeTable::GetHashIDByIndex(unsigned int uIndex, bool bCommonShapes /*= false*/ )
{
	unsigned int	uNumElements;

	if( false == bCommonShapes )
	{
		uNumElements = (unsigned int)s_iNumElementsInTable;
	}
	else
	{
		uNumElements = (unsigned int)s_iNumExportShapes;
	}

	Asura_Assert( uIndex < uNumElements, "(RAR) Invalid index passed to Tools_ShapeTable::GetMeshNameByIndex" );

	//get the tools shape table entry from the index number
	const Tools_ShapeTableElement*	pxElement = 0;
	if( false == bCommonShapes )
	{
		pxElement = GetElementByIndex( uIndex );
	}
	else
	{
		pxElement = GetElementByIndex( uIndex, s_pxExportShapes, s_iNumExportShapes );
	}

	if(!pxElement) return ASURA_HASH_ID_UNSET;
	Asura_Assert( ASURA_HASH_ID_UNSET != pxElement->m_uHashID, "Error - Invalid entry in tools shape table" );

	return pxElement->m_uHashID;
}



/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::ExportShape
// PARAMETERS	Index of the shape to export
//				Handle of the file to export to
//				A string pointer to set to the shape's filename
//				Optional flag whether this is a request to export a common shape (default false)
// RETURNS		TRUE on success; FALSE otherwise
// DESCRIPTION	Prepares to export a single shape to the specified platform
// AUTHOR		Richard M. Smith (13 August 2002)
/////////////////////////////////////////////////////////////////////////////////////////
bool Tools_ShapeTable::ExportShape( Tools_Level_Export::EXPORT_TYPE eExportType, unsigned int uIndex,
										HANDLE pSaveHandle, const char*& szShapeFilename, bool bCommonShapes )
{
	Asura_Assert(s_pxThis,"Tools_ShapeTable not allocated");
	unsigned int	uNumElements;

	if( false == bCommonShapes )
	{
		uNumElements = (unsigned int)s_iNumElementsInTable;
	}
	else
	{
		uNumElements = (unsigned int)s_iNumExportShapes;
	}

	Asura_Assert( uIndex < uNumElements, "(RMS) Invalid index passed to Tools_ShapeTable::ExportShapePS2" );
	Asura_Assert( NULL != pSaveHandle, "(RMS) Invalid save handle passed to Tools_ShapeTable::ExportShapePS2" );

	bool bSuccess = false;

	if( false == bCommonShapes )
	{
		if( uIndex < uNumElements &&
			NULL != pSaveHandle )
		{
			const Tools_ShapeTableElement*	pxElement = GetElementByIndex( uIndex );


			Asura_Assert( ASURA_HASH_ID_UNSET != pxElement->m_uHashID, "Error - Invalid entry in tools shape table" );
			//Asura_Assert( pxElement->m_uReferenceCount > 0, "Error - Entry in tools shape table has no references?" );
			Asura_Assert( NULL != pxElement->m_szFile, "Error - Entry in tools shape table has no filename?" );

			if( NULL != pxElement->m_szFile )
			{
				szShapeFilename = pxElement->m_szFile;

						bSuccess = ExportShapeTableEntryMain( eExportType, pxElement, pSaveHandle );
					}
			if(bSuccess)
			{
				s_pxThis->ExportProjectShapeData(eExportType,pxElement,pSaveHandle);
			}
		}
	}
	else
	{
	// Common shape export...
		Tools_ShapeTableElement* pxElement = GetElementByIndex( uIndex, s_pxExportShapes, s_iNumExportShapes );

		if( NULL == pxElement )
		{
			return false;
		}

		Asura_Assert( ASURA_HASH_ID_UNSET != pxElement->m_uHashID, "Error - Invalid entry in tools shape table" );
		Asura_Assert( NULL != pxElement->m_szFile, "Error - Entry in tools shape table has no filename?" );

	// Export this shape...
		if( NULL != pSaveHandle &&
			NULL != pxElement->m_szFile )
		{
			szShapeFilename = pxElement->m_szFile;

					bSuccess = ExportShapeTableEntryMain( eExportType, pxElement, pSaveHandle );
			if(bSuccess)
			{
				s_pxThis->ExportProjectShapeData(eExportType,pxElement,pSaveHandle);
			}
		}
	}

	return bSuccess;

} // End Function 'Tools_ShapeTable::ExportShape'

bool Tools_ShapeTable::ExportShapeFile(Tools_Level_Export::EXPORT_TYPE eExportType, const char* pszFilename,HANDLE pSaveHandle)
{
			return ExportShapeFileMain(eExportType,pszFilename,NULL,pSaveHandle);
	}

bool Tools_ShapeTable::ExportShapeTableEntryMain( Tools_Level_Export::EXPORT_TYPE eExportType, const Tools_ShapeTableElement* pxElement, HANDLE pSaveHandle )
{
	//get the mesh name from the filename
	Asura_String xFilename = pxElement->m_szFile;
	Asura_String xMeshName;
	xFilename.SplitFilename(NULL,&xMeshName,NULL);

	return ExportShapeFileMain(eExportType,pxElement->m_szFile,xMeshName.GetString(),pSaveHandle);

} // End Function 'Tools_ShapeTable::ExportForPC'

bool Tools_ShapeTable::ExportShapeFileMain(Tools_Level_Export::EXPORT_TYPE eExportType,const char* szFilename,const char* szSingleMeshName, HANDLE pSaveHandle)
{
	char szFileToLoad[MAX_PATH];
	Asura_FileAccess::PlatformGetNetworkFilename(szFilename,szFileToLoad,MAX_PATH);

	Asura_String xExtendedFilename = szFilename;
	Asura_String xMeshName;
	xExtendedFilename.SplitFilename(NULL,&xMeshName,NULL);

    // Set some default progress text.
    if( Tools_Level_Export::IsLevelExport() )
    {
        Asura_String xStatus = "Adding shape \"";
        xStatus += xMeshName;
        xStatus += "\"...";

		Tools_Level_Export::UpdateStatus( Tools_Level_Export::EXPORT_STAGE_ADDING_SHAPES, xStatus.GetString(), -1 );
    }

	//read and decompress the file
	Tools_ReadAsuraFileOutput xReadFileOutput;
	char* buffer = Tools_FileSystem::ReadAsuraFile(szFileToLoad, &xReadFileOutput);


	if(!buffer)
	{
		switch(xReadFileOutput.m_uErrorCode)
	{
		case TOOLS_READASURAFILE_FAILED_TO_OPEN_FILE :
			Tools_Level_Export::AddMessage( Tools_Level_Export::MT_ERROR, "Failed to open %s for reading.", szFileToLoad );
        return false;

		case TOOLS_READASURAFILE_NOT_ASURA_FILE :
			Tools_Level_Export::AddMessage( Tools_Level_Export::MT_ERROR, "\"%s\" isn't an asura file.", szFileToLoad );
			return false;
	
		case TOOLS_READASURAFILE_ERROR_READING_FILE :
		    Tools_Level_Export::AddMessage( Tools_Level_Export::MT_ERROR, "I/O error while reading \"%s\".", szFileToLoad );
            return false;

		default :
			Tools_Level_Export::AddMessage( Tools_Level_Export::MT_ERROR, "Error while reading \"%s\".", szFileToLoad );
		return false;
	}
	}

	
	
	const char* chunkBuffer = buffer;
	
    // Get cur file ptr pos.
	#if defined ASURA_ASSERT
		const DWORD uFilePtrPosBeforeWrite =
	#endif
		SetFilePointer( pSaveHandle, 0, NULL, FILE_CURRENT );

	List<Asura_String> xMeshList;
					  
    unsigned uTotalNumBytesWritten = 0;
	const Asura_Chunk_Header* chunk = (Asura_Chunk_Header*) chunkBuffer;
	while(chunk->Valid())
	{
		Asura_Assert(chunk->Size,"");


		if(!Tools_Level_Export::ShouldExportBigEndian())
		{
			switch( chunk->ID )
			{
				case ASURA_CHUNK_MESH_LOD:
				case ASURA_CHUNK_MESH_MISC:
				case ASURA_CHUNK_MESH_NORMALS:
				case ASURA_CHUNK_MESH_POLYGONFLAGS:
				case ASURA_CHUNK_MESH_POLYGONS:
				case ASURA_CHUNK_MESH_SMOOTHINGGROUP:
				case ASURA_CHUNK_MESH_TEXTUREINFO:
				case ASURA_CHUNK_SHAPEDATA ://this chunk gets created later instead
					break;
				case ASURA_CHUNK_MESH_VERTICES:
				{
					//add the mesh name to the list
					const char* pcBuffer = (const char*) chunk;
					pcBuffer += sizeof(Asura_Chunk_Mesh_Vertices);				
					xMeshList.add_entry(pcBuffer);
					//(and then skip this chunk)
					break;
				}


				default:
				{
					//check chunk filter. RAR 23/05/2005
					if(!Tools_Level_Export::ShouldChunkBeExported_Static(chunk->ID))
					{
						break;
					}
					
					const Asura_Chunk_Header* pxChunkToWrite = chunk;
					
					char* pcModifedChunkBuffer = 0;
            
					#ifdef ASURA_USE_MATERIAL_RESPONSE_SYSTEM
					//Just enabled the modify chunk code for material response system at the moment , because
					//I'm not entirely sure whether the PS2 material fiddling will work correctly when called here.
					//Its probably okay, but I don't want to try and debug that for now to be sure.
					//RAR 12/04/2006
					if(Tools_Level_Export::ModifyChunkBeforeOutput_Static(szFilename,pxChunkToWrite,&pcModifedChunkBuffer))
					{
						pxChunkToWrite = (const Asura_Chunk_Header*) pcModifedChunkBuffer;
						Asura_Assert(pxChunkToWrite,"");
					}
					#endif

					
					DWORD bytesWritten;
					WriteFile( pSaveHandle, pxChunkToWrite,pxChunkToWrite->Size, &bytesWritten, NULL );

					if( bytesWritten != static_cast<DWORD>(pxChunkToWrite->Size) )
					{
						Tools_Level_Export::AddMessage( Tools_Level_Export::MT_ERROR, "I/O error." );
						Asura_Deallocate( buffer );
						return false;
					}

					uTotalNumBytesWritten += pxChunkToWrite->Size;
					delete [] pcModifedChunkBuffer;

					break;
				}
			}
		}
		else
		{
			// Special handling for Gamecube. Add name for mesh vertices as per above,
			// but fragments must be parsed and re-exported (rather than copied directly)
			Asura_SaveBlock xSaveBlock;
			switch( chunk->ID )
			{
				case ASURA_CHUNK_MESH_VERTICES:
				{
					//add the mesh name to the list
					const char* pcBuffer = (const char*) chunk;
					pcBuffer += sizeof(Asura_Chunk_Mesh_Vertices);				
					xMeshList.add_entry(pcBuffer);
					//(and then skip this chunk)
					break;
				}
				case ASURA_CHUNK_FRAGMENT:
				{
					Asura_Hash_ID* puBuffer = (Asura_Hash_ID*)((Asura_Chunk_Fragment*)chunk + 1);
					Asura_FragmentType* pxFragType = Asura_Fragment_System::GetFragmentType(*puBuffer);
					Asura_Assert(pxFragType, "Could not find fragment type");
					if(pxFragType)
					{
						Asura_Chunk_Fragment::Create(pxFragType, &xSaveBlock);
					}
					break;
				}
				default:	break;
			}
			int iBytesWritten = xSaveBlock.WriteToFile(pSaveHandle);
			if(iBytesWritten == -1)
			{
				Tools_Level_Export::AddMessage( Tools_Level_Export::MT_ERROR, "I/O error." );
				return false;
			}
			uTotalNumBytesWritten += iBytesWritten;
		}

		//advance to the next chunk
		chunkBuffer += chunk->Size;
		chunk = (Asura_Chunk_Header*) chunkBuffer;
	}

    // Get new file ptr pos.
    #if defined ASURA_ASSERT
		const DWORD uFilePtrPosAfterWrite =
	#endif
		SetFilePointer( pSaveHandle, 0, NULL, FILE_CURRENT );

    Asura_Assert( uFilePtrPosAfterWrite - uFilePtrPosBeforeWrite == uTotalNumBytesWritten, "" );

	delete [] buffer;

//	if(pxShape && pxShape->IsNeverRendered())
//	{
		//Exporting a shape that is only used as a collision mesh.
		//So can skip the rest of the export
		//RAR 01/12/2004
//		return true;
//	}


	// KJL - 05/05/2003 15:03:59 - add platform specific render mesh info

	//aiMaterialIDs contains a list of all material IDs in the order they are encountered
	//in the file. This can be used for converting the IDs back to indeces
	const int iMaxMaterialIDs = 100;
	int aiMaterialIDs[iMaxMaterialIDs];
	int iNumMaterialIDs = 0;

	//Iterate through each mesh in the file to work out the conversion form material IDs to material
	//indeces. 
	bool bLoadFileAttempted = false;
	for(LIF<Asura_String> xMeshLIF(&xMeshList);!xMeshLIF.done();xMeshLIF.next())
	{
		const char* szMeshName = xMeshLIF().GetString();
		Asura_Mesh* pxMesh = Asura_Mesh_System::GetMesh(szMeshName);

		if (!pxMesh)
		{
			Asura_Assertf(pxMesh, ("Shape not already loaded: <%s>",szMeshName));
			if(!bLoadFileAttempted)
			{
				bLoadFileAttempted = true;
				LoadAsuraFile(szFilename);
				pxMesh = Asura_Mesh_System::GetMesh(szMeshName);
			}
		}
		// if you still haven't got a mesh, just give up
		if (!pxMesh) return false;

		if(pxMesh->m_iSourceFileType != AsuraFile_Shapes && pxMesh->m_iSourceFileType != AsuraFile_Fragment)
		{
			Asura_Assertf(0,("The shape mesh '%s' wasn't loaded from a shape or fragment file.\nCheck Logfiles\\file_contents.txt to see where the mesh has been loaded from , since the wrong one has probably been found.",szMeshName));
		}

		if(pxMesh->TextureDataValid)
		{
			for(int i=0;i<pxMesh->NumberOfPolygons;i++)
			{
				int iMaterialID = pxMesh->TextureDataPtr[i].m_iMaterialID;
				if(iMaterialID == ASURA_MATERIAL_NONE) continue;
				
				//This material ID been encountered yet?
				int iMaterialIndex = 0;
				for(;iMaterialIndex<iNumMaterialIDs;iMaterialIndex++)
				{
					if(aiMaterialIDs[iMaterialIndex] == iMaterialID)
					{
						break;
					}
				}

				if(iMaterialIndex == iNumMaterialIDs)
				{
					//new material
					aiMaterialIDs[iNumMaterialIDs++] = iMaterialID;
				}

			}
		}
	}

    List<Asura_Mesh*> xRenderMeshList;
	
	//Now go through the meshes a second time , exporting those that are needed
	for(LIF<Asura_String> xMeshLIF(&xMeshList);!xMeshLIF.done();xMeshLIF.next())
	{
		const char* szMeshName = xMeshLIF().GetString();
		Asura_Mesh* pxMainMesh = Asura_Mesh_System::GetMesh(szMeshName);

		if (!pxMainMesh) return false;
		
		if(pxMainMesh->HasDetailMeshName())
		{
			//ignore detail meshes for now , they weill be done with the main mesh
			continue;
		}

		//Determine whether to export this shape (and get the hash id to do it under)
		Asura_Hash_ID uMainExportHash = ASURA_HASH_ID_UNSET;
		const char* szMainExportName = 0;
		if(szSingleMeshName)
		{
			if(!stricmp(szSingleMeshName,szMeshName))
			{
				uMainExportHash = Asura_GetHashID(szFilename);
				szMainExportName = szFilename;
			}
		}
		else
		{
			uMainExportHash = Asura_GetHashID(szMeshName);
			szMainExportName = szMeshName;
		}

		if(uMainExportHash != ASURA_HASH_ID_UNSET)
		{
			//get the Ausra_Shape for this (I think they should always exist now...) RAR 23/03/2005
			Asura_Shape* pxShape = Asura_Shape_System::GetShape(uMainExportHash);
			Asura_Assert(pxShape,"Expectnig to find Asura_Shape for exported shape");
			if(!pxShape) continue;

			Asura_Shape_RenderData* pxRenderData = pxShape->GetRenderData();
			if( pxRenderData )
			{
                xRenderMeshList.add_entry( pxRenderData->GetMesh() );
            }

            // Big endian exports need the texture and material chunks adding
            // manually.
            if( Tools_Level_Export::ShouldExportBigEndian() )
			{
				Asura_SaveBlock xSaveBlock;

                if( xRenderMeshList.size() )
                {
                    Asura_Chunk_SaveBlock_Stream xStream(&xSaveBlock);
					Tools_SaveAsura::Static_AddMeshesAndTexturesToSaveBlock( xRenderMeshList, xStream, NULL, AsuraFile_Shapes );
				}

				int iBytesWritten = xSaveBlock.WriteToFile(pSaveHandle);
				if(iBytesWritten == -1)
				{
					Tools_Level_Export::AddMessage( Tools_Level_Export::MT_ERROR, "I/O error." );
					return false;
				}
			}

			//add render data if needed
			if( !pxShape->IsNeverRendered() )
			{
				//Export the mesh and all its detail levels.
				for(int i=0;i<pxMainMesh->GetNumberOfDetailLevels();i++)
				{
					int iDetail;
					//Export the meshes highest detail level first for PS2 , and lowest detail
					//first for the rest.
					if(eExportType == Tools_Level_Export::Export_PS2)
					{
						iDetail = i;
					}
					else
					{
						iDetail = pxMainMesh->GetNumberOfDetailLevels() - (i+1);
					}
					
					
					Asura_Mesh* pxMesh = pxMainMesh->GetDetailMeshFromIndex(iDetail);
					Asura_Assert(pxMesh,"");

					//determine the material index used by this mesh
					int iMeshMaterialIndex = -1;
					if(pxMesh->TextureDataValid)
					{
						bool bUsesMultipleTextures = false;
						for(int i=0;i<pxMesh->NumberOfPolygons;i++)
						{
							int iMaterialID = pxMesh->TextureDataPtr[i].m_iMaterialID;
							if(iMaterialID == ASURA_MATERIAL_NONE) continue;
							
							//This material ID been encountered yet?
							int iMaterialIndex = 0;
							for(;iMaterialIndex<iNumMaterialIDs;iMaterialIndex++)
							{
								if(aiMaterialIDs[iMaterialIndex] == iMaterialID)
								{
									break;
								}
							}
							Asura_Assert(iMaterialIndex != iNumMaterialIDs,"All materials should have been found by now");

							if(iMeshMaterialIndex == -1)
							{
								//found the material index for this mesh
								iMeshMaterialIndex = iMaterialIndex;
							}
							else if(iMeshMaterialIndex != iMaterialIndex)
							{
								//more than one texture used in this mesh
								bUsesMultipleTextures = true;
							}
						}
						Asura_Assertf(!bUsesMultipleTextures,("Shape %s in %s uses more than one material",pxMesh->Name.GetString(),szFilename));
					}
					if(iMeshMaterialIndex == ASURA_MATERIAL_NONE)
					{
						//PS2 gets a fake material instead ,so use materail index 0.
						//RAR 07/09/2004

						if(Tools_Level_Export::Export_PS2  == eExportType)
						{
							iMeshMaterialIndex = 0;
						}
					}

					const Asura_Hash_ID uMaterialResponseHashID = pxMesh->TextureDataValid && pxMesh->TextureDataPtr
																? pxMesh->TextureDataPtr[0].m_auMaterialResponseHashID[0]
																: ASURA_HASH_ID_UNSET;

					//get the hash ID for this detail level
					Asura_Hash_ID uExportHash = (pxMainMesh==pxMesh) ? uMainExportHash : Asura_GetHashID(pxMesh->Name.GetString());
					//const char* szExportName = (pxMainMesh==pxMesh) ? szMainExportName : pxMesh->Name.GetString();

                    bool bExportedOK = false;

					switch(eExportType)
					{
						case Tools_Level_Export::Export_PC :
						{
							bExportedOK = Tools_PC_Export::ExportObject(pSaveHandle,pxMesh,uExportHash,iMeshMaterialIndex,uMaterialResponseHashID);
							break;
						}

						case Tools_Level_Export::Export_PSP :
						{
							bExportedOK = Tools_PSP_Export::ExportObject(pSaveHandle,pxMesh,uExportHash,iMeshMaterialIndex);
							break;
						}

						case Tools_Level_Export::Export_PS2:
						{
                            bExportedOK = PS2Strip::ExportObject( pSaveHandle, pxMesh, szMainExportName );
							break;
						}

						case Tools_Level_Export::Export_PS3:
						{
							bExportedOK = true;
							Asura_Assertf( false, ("Object renderer no longer supported - will not render on PS3. Use Object Hierarchy. Mesh name is %s", pxMesh->Name.GetString() ));
							Tools_Level_Export::AddMessage( Tools_Level_Export::MT_WARNING, "Object renderer no longer supported - will not render on PS3. Use Object Hierarchy. Mesh name is %s", pxMesh->Name.GetString() );
							break;
						}

						case Tools_Level_Export::Export_Xbox360:
						{
							Tools_Level_Export::AddMessage( Tools_Level_Export::MT_WARNING, "Object renderer no longer supported - will not render on PS3. Use Object Hierarchy. Mesh name is %s", pxMesh->Name.GetString() );
							bExportedOK = Tools_Xbox360_Export::ExportObject (pSaveHandle, pxMesh, uExportHash, iMeshMaterialIndex, uMaterialResponseHashID);
							break;
						}

						case Tools_Level_Export::Export_Wii:
						{
							bExportedOK	= Tools_Wii_Export::ExportShape(pSaveHandle,pxMesh,uExportHash,iMeshMaterialIndex);
							break;
						}

						default:
						{
							Asura_Assert( false, "Unhandled platform!" );
							break;
						}
					}

                    if( !bExportedOK ) { return false; }
				}
			}
			
			//add chunks for Asura_Shape
			{ 
				Asura_SaveBlock xSaveBlock;
				pxShape->SaveShapeChunks( &xSaveBlock, xRenderMeshList );
                xSaveBlock.WriteToFile( pSaveHandle );
			}
		}
	} 

    // Ok.
    return true;
}




/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::LoadExportShapes
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Attempts to load the txt file that lists any and all shapes required to
//				be added to the export
// AUTHOR		Richard M. Smith (11 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::LoadExportShapes( void )
{
	//load common export shapes
	LoadExportShapesFromFile(Tools_SaveAsura::GetAllLevelsExportFilename());

	//load level specific shapes
	LoadExportShapesFromFile(Tools_SaveAsura::GetLevelSpecificExportFilename());

	LoadExportShapesFromFile(Tools_Level_Export::GetCommonExportFile());

} // End Function 'Tools_ShapeTable::LoadExportShapes'


bool Tools_ShapeTable::LoadExportShapesFromFile(const char* pszTextFilename)
{
	List<Asura_String> xFilenameList;
	if(!ListShapesInTextFile(pszTextFilename,xFilenameList)) return false;

	for(LIF<Asura_String> xFilenameLIF(&xFilenameList);!xFilenameLIF.done();xFilenameLIF.next())
	{
		LoadExportShape(xFilenameLIF().GetString());		
	}
	return true;
}

bool Tools_ShapeTable::EnableShapesForExport(const char* pszTextFilename,bool bEnableEntityShapesAsWell /*= false*/)
{
	List<Asura_String> xFilenameList;
	if(!ListShapesInTextFile(pszTextFilename,xFilenameList)) return false;

	for(LIF<Asura_String> xFilenameLIF(&xFilenameList);!xFilenameLIF.done();xFilenameLIF.next())
	{
		EnableShapeForExportByFilename(xFilenameLIF().GetString(),bEnableEntityShapesAsWell);
	}
	return true;
}

bool Tools_ShapeTable::DisableShapesForExport(const char* pszFilename,bool bDisableEntityShapesAsWell /*= false*/)
{
	List<Asura_String> xFilenameList;
	if(!ListShapesInTextFile(pszFilename,xFilenameList)) return false;

	for(LIF<Asura_String> xFilenameLIF(&xFilenameList);!xFilenameLIF.done();xFilenameLIF.next())
	{
		// JEB - 01 June 2006 14:20:54 - Honor the bDisableEntityShapesAsWell argument
		DisableShapeForExportByFilename(xFilenameLIF().GetString(), bDisableEntityShapesAsWell);
	}
	return true;
}

bool Tools_ShapeTable::LoadExportShape(const char* pszFilename)
{
	Asura_Hash_ID uShapeHash = AddShapeByFilenameInternal(pszFilename,s_pxExportShapes,s_iNumExportShapes,false);
	//Force the shape to be enabled , so we don't get different behaviour according to whether the shape
	//was already loaded.
	if(uShapeHash != ASURA_HASH_ID_UNSET) EnableShapeForExportByHashID(uShapeHash);

	return uShapeHash != ASURA_HASH_ID_UNSET;
}

bool Tools_ShapeTable::EnableShapeForExportByFilename(const char* pszFilename,bool bEnableEntityShapesAsWell /*= false*/)
{
	//update entity shapes if desired
	if(bEnableEntityShapesAsWell)
	{
		Tools_ShapeTableElement* pxEntityElement = FindElementByFilename(pszFilename,s_pxTable);
		if(pxEntityElement) pxEntityElement->m_bIncludeInExport = true;
	}
	
	//check for entry in 'common' shapes
	Tools_ShapeTableElement* pxElement = FindElementByFilename(pszFilename,s_pxExportShapes);
	if(!pxElement) return false;

	pxElement->m_bIncludeInExport = true;
	return true;
}

bool Tools_ShapeTable::EnableShapeForExportByHashID(Asura_Hash_ID uShapeHash,bool bEnableEntityShapesAsWell /*= false*/)
{
	//update entity shapes if desired
	if(bEnableEntityShapesAsWell)
	{
		Tools_ShapeTableElement* pxEntityElement = FindElementByHashID(uShapeHash,s_pxTable);
		if(pxEntityElement) 
			pxEntityElement->m_bIncludeInExport = true;
	}

	//check for entry in 'common' shapes
	Tools_ShapeTableElement* pxElement = FindElementByHashID(uShapeHash,s_pxExportShapes);
	if(!pxElement) return false;

	pxElement->m_bIncludeInExport = true;
	return true;
}

bool Tools_ShapeTable::DisableShapeForExportByFilename(const char* pszFilename,bool bDisableEntityShapesAsWell /*= false*/)
{
	//update entity shapes if desired
	if(bDisableEntityShapesAsWell)
	{
		Tools_ShapeTableElement* pxEntityElement = FindElementByFilename(pszFilename,s_pxTable);
		if(pxEntityElement) 
			pxEntityElement->m_bIncludeInExport = false;
	}

	//check for entry in 'common' shapes
	Tools_ShapeTableElement* pxElement = FindElementByFilename(pszFilename,s_pxExportShapes);
	if(!pxElement) return false;

	pxElement->m_bIncludeInExport = false;
	return true;
}

bool Tools_ShapeTable::DisableShapeForExportByHashID(Asura_Hash_ID uShapeHash,bool bDisableEntityShapesAsWell /*= false*/)
{
	//update entity shapes if desired
	if(bDisableEntityShapesAsWell)
	{
		Tools_ShapeTableElement* pxEntityElement = FindElementByHashID(uShapeHash,s_pxTable);
		// JEB - 01 June 2006 14:22:30 - Changed m_bIncludeInExport assignment from true to false
		if(pxEntityElement) pxEntityElement->m_bIncludeInExport = false;
	}

	//check for entry in 'common' shapes
	Tools_ShapeTableElement* pxElement = FindElementByHashID(uShapeHash,s_pxExportShapes);
	if(!pxElement) return false;

	pxElement->m_bIncludeInExport = false;
	return true;
}


bool Tools_ShapeTable::ListShapesInTextFile(const char* pszFilename,List<Asura_String>& xOutputFilenameList)
{
	Asura_Assert(pszFilename,"");

	Tools_IniFile xTextFile(pszFilename);
	if(!xTextFile.LoadIniFile()) return false;//failed to laod

	//search for shapes section
	if(!xTextFile.GetIniTextSection("Shapes")) return false;

	//add all entries in this section to output list
	while(xTextFile.GetNextIniTextLine())
	{
		Asura_String xFilename;
		xTextFile.GetString(xFilename);

		xOutputFilenameList.add_entry(xFilename);
	}

	return true;
}


// RICHARDS	/////////////////////////////////////////////////////////////////////////////////////////
// RICHARDS	// FUNCTION		Tools_ShapeTable::UnloadExportShapes
// RICHARDS	// PARAMETERS	None
// RICHARDS	// RETURNS		None
// RICHARDS	// DESCRIPTION	Removes any and all shapes that were added added to the export from the
// RICHARDS	//				txt files
// RICHARDS	// AUTHOR		Richard M. Smith (11 September 2002)
// RICHARDS	/////////////////////////////////////////////////////////////////////////////////////////
// RICHARDS	void Tools_ShapeTable::UnloadExportShapes( void )
// RICHARDS	{
// RICHARDS		Tools_ShapeTableElement*	pxElement = s_pxTable;
// RICHARDS		Tools_ShapeTableElement*	pxNext;
// RICHARDS	
// RICHARDS		while( NULL != pxElement )
// RICHARDS		{
// RICHARDS			pxNext = pxElement->m_pxNext;
// RICHARDS	
// RICHARDS			if( ASURA_SHAPE_REFERENCE_COUNT_DO_NOT_DELETE == pxElement->m_uReferenceCount )
// RICHARDS			{
// RICHARDS			// Delete the relevant mesh (so that we don't get 'Mesh already loaded' errors)
// RICHARDS				Asura_Mesh*		pxMesh;
// RICHARDS				Asura_String	xMeshName = pxElement->m_szMeshName;
// RICHARDS				pxMesh = Asura_Mesh_System::GetMesh( xMeshName );
// RICHARDS				Asura_Mesh_System::DeleteMesh( pxMesh );
// RICHARDS	
// RICHARDS			// Remove the shape from the shape system
// RICHARDS				Asura_Shape* pxShape = Asura_Shape_System::GetShape( pxElement->m_uHashID );
// RICHARDS				Asura_Assert( NULL != pxShape, "Error - Did not find shape in shape system" );
// RICHARDS				delete pxShape;
// RICHARDS	
// RICHARDS			// Finally remove from the shape table
// RICHARDS				RemoveElement( pxElement );
// RICHARDS			}
// RICHARDS	
// RICHARDS			pxElement = pxNext;
// RICHARDS		}
// RICHARDS	
// RICHARDS	} // End Function 'Tools_ShapeTable::UnloadExportShapes'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::GetShapesFromTextFile
// PARAMETERS	The open file to search for shapes in
// RETURNS		None
// DESCRIPTION	Attempts to locate a "[SHAPES]" section in the specified file and then
//				load the shapes specified thereafter
// AUTHOR		Richard M. Smith (11 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable::GetShapesFromTextFile( FILE* pFile )
{
#define	MAX_LINE_LEN	( _MAX_PATH )

	char	szLine[ MAX_LINE_LEN ];

// First search for the "[SHAPES]" header
	while( NULL != fgets( szLine, MAX_LINE_LEN, pFile ) )
	{
	// Convert the input string to all lower case
		strlwr( szLine );

		if( strstr( szLine, "[shapes]" ) )
		{
		// Found the [SHAPES] header
			while( NULL != fgets( szLine, MAX_LINE_LEN, pFile ) )
			{
			// Remove the carriage return at the end of the line if present...
				char*	szEndOfLineChar = strstr( szLine, "\n" );
				if( szEndOfLineChar )
				{
					*szEndOfLineChar = '\0';
				}

				if( strlen( szLine ) )
				{
				// Check if we have reached the end of the shapes section...
					if( strstr( szLine, "[" ) ||
						strstr( szLine, "]" ) )
					{
						return;
					}
				// Check the file exists first
					if( CheckFileNameFromTextFile( szLine, MAX_LINE_LEN ) )
					{
						Asura_String xName = szLine;

					// Check if this shape is already in the "common export shape" list - in case the same shape appears more than
					// once in the same text file...or perhaps it appears both in the common_export.txt and LEVEL_export.txt file
						Tools_ShapeTableElement*	pxExistingElement = FindElementByFilename( xName.GetString(), s_pxExportShapes );

						if( NULL == pxExistingElement )
						{
							Tools_ShapeTableElement* pxElement = new Tools_ShapeTableElement( xName.GetString(), s_pxExportShapes, s_iNumExportShapes );

						// Do we need to load this shape?
							if( NULL == Asura_Shape_System::GetShape( pxElement->m_uHashID ) )
							{
								int iMeshesBeforeLoad = Asura_Mesh_System::GetNumberOfMeshes();
						
							// We need to load the shape now!
								Asura_Shape* pxNewShape = Asura_Shape_System::NewShape( pxElement->m_uHashID );

								char	szMeshName[ _MAX_PATH ];
								_splitpath( pxElement->m_szFile, NULL, NULL, szMeshName, NULL );

								if( pxNewShape->CreateShape( pxElement->m_szFile, szMeshName ) )
								{
								// Successfully created the shape
									Asura_Texture_Management::LoadTextures();
								}
								else
								{
								// Failed to create the shape
									Asura_Shape_System::DeleteShape(pxNewShape);
									RemoveElement( pxElement, s_pxExportShapes, s_iNumExportShapes );

								// Delete the relevant mesh (so that we don't get 'Mesh already loaded' errors)
									Asura_Mesh*		pxMesh = NULL;

								// Get the most recently added mesh...
									while(Asura_Mesh_System::GetNumberOfMeshes()>iMeshesBeforeLoad)
									{
										pxMesh = Asura_Mesh_System::GetMesh( Asura_Mesh_System::GetNumberOfMeshes() - 1 );
									
										if( NULL != pxMesh )
										{
											Asura_Mesh_System::DeleteMesh( pxMesh );
										}
									}
								}
							}
						}
					}
				}
			}

		// We're done here
			break;
		}
	}

#undef MAX_LINE_LEN

} // End Function 'Tools_ShapeTable::GetShapesFromTextFile'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::CheckFileNameFromTextFile
// PARAMETERS	Filename to check
// RETURNS		TRUE if the file name is valid and exists; FALSE otherwise
// DESCRIPTION	Converts the input filename to a full path
// AUTHOR		Richard M. Smith (11 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
bool Tools_ShapeTable::CheckFileNameFromTextFile( char* szFilename, int iStringLen )
{
	Asura_String xInput = szFilename;

	#ifdef ASURA_PC_ALLOW_LOAD_FROM_NETWORK
	//get path to network version if required. RAR 28/03/03
	Asura_FileAccess::PlatformGetNetworkFilename(xInput.GetString(),szFilename,iStringLen);
	#else
	if( NULL == _fullpath( szFilename, xInput.GetString(), iStringLen ) )
	{
		return false;
	}
	#endif


	FILE*	pFile;

	pFile = fopen( szFilename, "rb" );

	if( NULL == pFile )
	{
		return false;
	}

	fclose( pFile );

	strlwr( szFilename );

	return true;

} // End Function 'Tools_ShapeTable::CheckFileNameFromTextFile'









/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::IsCommonExportShape
// PARAMETERS	Hash ID of the shape to test
// RETURNS		TRUE if the specified shape is one of the "common export" shapes; FALSE
//				otherwise
// DESCRIPTION	See returns
// AUTHOR		Richard M. Smith (17 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
bool Tools_ShapeTable::IsCommonExportShape( Asura_Hash_ID uShapeHashID )
{
	Tools_ShapeTableElement* pxElement = FindElementByHashID( uShapeHashID, s_pxExportShapes );

	if( NULL == pxElement )
	{
		return false;
	}
	else
	{
		return true;
	}

} // End Function 'Tools_ShapeTable::IsCommonExportShape'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable::IsInMainShapeTable
// PARAMETERS	Hash ID of the shape to test
// RETURNS		TRUE if the specified shape is in the main shape table; FALSE otherwise
// DESCRIPTION	See returns
// AUTHOR		Richard M. Smith (17 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
bool Tools_ShapeTable::IsInMainShapeTable( Asura_Hash_ID uShapeHashID )
{
	Tools_ShapeTableElement* pxElement = FindElementByHashID( uShapeHashID );

	if( NULL == pxElement )
	{
		return false;
	}
	else
	{
		return true;
	}

} // End Function 'Tools_ShapeTable::IsInMainShapeTable'



bool Tools_ShapeTable::IsValidShapeFile(const char* pszFilename,Asura_String* pxErrorMessage)
{
	char szErrorMessage[200];

	Tools_AsuraFileContents xFileContents;
	if(!xFileContents.LoadAsuraFile(pszFilename))
	{
		//failed to load file
		if(pxErrorMessage)
		{
			
			Asura_PrintfToBfr(szErrorMessage,"Failed to load %s.",200,true,pszFilename);
			*pxErrorMessage = szErrorMessage;
		}
		return false;
	}

	if(xFileContents.GetSkinList()->size())
	{
		//is a skin file
		if(pxErrorMessage)
		{
			Asura_PrintfToBfr(szErrorMessage,"%s is a skin file.",200,true,pszFilename);
			*pxErrorMessage = szErrorMessage;
		}
		return false;
	}

	if(xFileContents.GetAnimList()->size())
	{
		//is an anim file
		if(pxErrorMessage)
		{
			Asura_PrintfToBfr(szErrorMessage,"%s is an anim file.",200,true,pszFilename);
			*pxErrorMessage = szErrorMessage;
		}
		
		return false;
	}

	//check the mesh names
	Asura_String xFilename = pszFilename;
	Asura_String xMeshName;
	xFilename.SplitFilename(0,&xMeshName,0);

	bool bFoundMesh = false;
	for(CLIF<Asura_String> xMeshLIF(xFileContents.GetMeshList());!xMeshLIF.done();xMeshLIF.next())
	{
		if(!xMeshName.Compare(xMeshLIF()))
		{
			bFoundMesh = true;
			break;
		}
	}

	if(!bFoundMesh)
	{
		if(pxErrorMessage)
		{
			Asura_PrintfToBfr(szErrorMessage,"Failed to find mesh named %s in %s.",200,true,xMeshName.GetString(),pszFilename);
			*pxErrorMessage = szErrorMessage;
		}

		return false;
	}

	//apears to be okay
	return true;
}

bool Tools_ShapeTable::IsValidShapeFile(const char* pszFilename,bool bShowErrorMessages)
{
	Asura_String xErrorMessage;
	bool bOkay = IsValidShapeFile(pszFilename,bShowErrorMessages ? &xErrorMessage : 0);
	if(!bOkay && bShowErrorMessages)
	{
		ShowMessageOverlay("Error.\n%s",xErrorMessage.GetString());
	}
	return bOkay;
}



//////////////////////////////
// Tools_ShapeTable_Overlay //
//////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable_Overlay::Tools_ShapeTable_Overlay
// PARAMETERS	None
// RETURNS		Default for constructor
// DESCRIPTION	Creates the Shape table overlay
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
Tools_ShapeTable_Overlay::Tools_ShapeTable_Overlay( void )
:Tools_Modal_Overlay( "The Shape Table" )
{
	Tools_ShapeTable::UpdateReferenceCounts();

	Tools_ShapeTable::s_bCloseShapeTable = false;
	Tools_ShapeTable_Overlay::s_iListBoxOffset = 0;
	Tools_ShapeTable_Overlay::s_iListBoxSelection = 0;

} // End Function 'Tools_ShapeTable_Overlay::Tools_ShapeTable_Overlay'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable_Overlay::~Tools_ShapeTable_Overlay
// PARAMETERS	None
// RETURNS		Default for destructor
// DESCRIPTION	Destroys the Shape table overlay
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
Tools_ShapeTable_Overlay::~Tools_ShapeTable_Overlay( void )
{
} // End Function 'Tools_ShapeTable_Overlay::~Tools_ShapeTable_Overlay'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable_Overlay::DoModal
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Executes the shape table overlay
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable_Overlay::DoModal( void )
{
	ShowOverlay();

// We can now delete the array of string pointers used by the list box
	delete [] s_pszListBoxStrings;
	s_pszListBoxStrings = NULL;
	for (int i=0;i<s_iNumberOfEntriesInList;i++)
	{
		delete[] s_pszAlphabetisedList[i];
	}
	delete [] s_pszAlphabetisedList;
	s_pszAlphabetisedList = NULL;
	s_iNumberOfEntriesInList = 0;

} // End Function 'Tools_ShapeTable_Overlay::DoModal'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable_Overlay::InitialiseOverlay
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Called by the base constructor
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable_Overlay::InitialiseOverlay( class Asura_Overlay* pxOverlay )
{
	Asura_Dialog* pxNewDialog;

	Asura_Vector_2 xOffset = Asura_Vector_2( 1.0f, 3.0f );

// Add the button to allow a new shape to be added to the shape table...
	pxNewDialog = new Asura_Function_Dialog( "Load New Shape", xOffset, Tools_ShapeTable::AddShapeByRequester, &pxOverlay->m_bShouldClose );
	pxNewDialog->SetHelpText("Load one or more shape files and add them to the shape list.");
	pxOverlay->AddDialog( pxNewDialog );
	xOffset.y += 1.5f;

	pxNewDialog = new Asura_Function_Dialog( "Remove Shape", xOffset, Tools_ShapeTable::RemoveShape, &pxOverlay->m_bShouldClose );
	pxNewDialog->SetHelpText("Remove the selected shape from the shape list (if it isn't used anywhere).");
	pxOverlay->AddDialog( pxNewDialog );
	xOffset.y += 1.5f;


// Add the list of shapes already in the table...
	Asura_Assert( NULL == s_pszListBoxStrings, "Error - Old string list not destroyed" );

	int	iNumElementsInList = 0;
	int iDefaultIndex = -1;//index of default shape to select

	if( Tools_ShapeTable::s_iNumElementsInTable > 0 )
	{
		s_pszListBoxStrings = new char*[ Tools_ShapeTable::s_iNumElementsInTable+1 ];

		Tools_ShapeTableElement*	pxCurrent	= Tools_ShapeTable::s_pxTable;
		int							iIndex		= 0;

		s_pszListBoxStrings[ 0 ] = "[None]";

		while( NULL != pxCurrent )
		{
			Asura_Assert( iIndex < Tools_ShapeTable::s_iNumElementsInTable, "Error - Invalid index" );
			s_pszListBoxStrings[ iIndex+1 ] = pxCurrent->m_szShapeName;

			//see if this is the default shape , if so take its index. RAR 04/04/03
			if(pxCurrent->m_uHashID == Tools_ShapeTable::s_uDefaultShapeID)
			{
				iDefaultIndex = iIndex+1;
			}
			iIndex++;

			pxCurrent = pxCurrent->m_pxNext;
		}

		iNumElementsInList = Tools_ShapeTable::s_iNumElementsInTable+1;
	}
	else
	{
		s_pszListBoxStrings = new char*[ 1 ];

		s_pszListBoxStrings[ 0 ] = "<No shapes>";

		iNumElementsInList = 1;
	}

	int	iNumLinesInList = min( iNumElementsInList, MAX_LINES_IN_SHAPE_TABLE_LIST );

	xOffset.y += 1.0f;
	
	s_pszAlphabetisedList = new char*[iNumElementsInList];
	s_iNumberOfEntriesInList = iNumElementsInList;
	s_aiMappings = new int[iNumElementsInList];
	bool *abDone = new bool[iNumElementsInList];
	for (int i=0;i<iNumElementsInList;i++)
	{
		abDone[i] = false;
	}
	for (int i=0;i<iNumElementsInList;i++)
	{
		int iEarliestName = -1;
		for (int j=0;j<iNumElementsInList;j++)
		{
			if (abDone[j])
				continue;
			if (iEarliestName == -1 ||
				stricmp(s_pszListBoxStrings[j], s_pszListBoxStrings[iEarliestName]) < 0)
				iEarliestName = j;
		}
		Asura_Assert(!abDone[iEarliestName], "");
		abDone[iEarliestName] = true;
		s_pszAlphabetisedList[i] = new char[strlen(s_pszListBoxStrings[iEarliestName]) + 1];
		strcpy(s_pszAlphabetisedList[i], s_pszListBoxStrings[iEarliestName]);
		s_aiMappings[i] = iEarliestName;
	}
#ifdef ASURA_ASSERT
	for (int i=0;i<iNumElementsInList;i++)
	{
		Asura_Assert(abDone[i], "");
	}
#endif
	delete [] abDone;

	//Now need to convert the index of the default shape to take into account the remapping. RAR 04/04/03
	if(iDefaultIndex>=0)
	{
		for (int i=0;i<iNumElementsInList;i++)
		{
			if(s_aiMappings[i] == iDefaultIndex)
			{
				iDefaultIndex = i;
				break;
			}
		}

		s_iListBoxSelection = iDefaultIndex;
	}
	else
	{
		// Handle the new 'None' option
		s_iListBoxSelection = 0;
	}
	
		//set the offset and selection values so they add up to this index
		s_iListBoxOffset = 0;
		Asura_List_Dialog::AdjustListSelectionAndOffset(&s_iListBoxOffset, &s_iListBoxSelection, iNumLinesInList, iNumElementsInList);
	


	pxNewDialog = new Asura_List_Dialog( xOffset, s_pszAlphabetisedList, &s_iListBoxOffset, &s_iListBoxSelection, iNumLinesInList, iNumElementsInList, Tools_ShapeTable::ListBoxCallBack );

	pxOverlay->AddDialog( pxNewDialog );
	xOffset.y += ( iNumLinesInList * 1.2f );
	xOffset.y += 1.0f;

// We need an OK and CANCEL now...
	pxNewDialog = new Asura_FunctionWithKeybinding_Dialog( "OK", xOffset, Tools_ShapeTable::OkaySelected, ASURA_KEYID_ENTER, &pxOverlay->m_bShouldClose );
	pxOverlay->AddDialog( pxNewDialog );

	xOffset.x += 6.0f;

	pxNewDialog = new Asura_FunctionWithKeybinding_Dialog( "Cancel", xOffset, Tools_ShapeTable::CancelSelected, ASURA_KEYID_ESCAPE, &pxOverlay->m_bShouldClose );
	pxOverlay->AddDialog( pxNewDialog );

} // End Function 'Tools_ShapeTable_Overlay::InitialiseOverlay'















/////////////////////////////////
// Tools_ShapeTable_WarningDlg //
/////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable_WarningDlg::Tools_ShapeTable_WarningDlg
// PARAMETERS	None
// RETURNS		Default for constructor
// DESCRIPTION	Creates a warning table overlay
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
Tools_ShapeTable_WarningDlg::Tools_ShapeTable_WarningDlg( const char* szWarningMsg )
:Tools_Modal_Overlay("Warning")
,s_szWarning( szWarningMsg )
{
} // End Function 'Tools_ShapeTable_WarningDlg::Tools_ShapeTable_WarningDlg'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable_WarningDlg::~Tools_ShapeTable_WarningDlg
// PARAMETERS	None
// RETURNS		Default for destructor
// DESCRIPTION	Destroys the Shape table warning message overlay
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
Tools_ShapeTable_WarningDlg::~Tools_ShapeTable_WarningDlg( void )
{
} // End Function 'Tools_ShapeTable_WarningDlg::~Tools_ShapeTable_WarningDlg'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable_WarningDlg::DoModal
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Executes the shape table overlay
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable_WarningDlg::DoModal( void )
{
	ShowOverlay();

	s_szWarning = NULL;

} // End Function 'Tools_ShapeTable_WarningDlg::DoModal'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tools_ShapeTable_WarningDlg::InitialiseOverlay
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Called by the base constructor
// AUTHOR		Richard M. Smith (10 September 2002)
/////////////////////////////////////////////////////////////////////////////////////////
void Tools_ShapeTable_WarningDlg::InitialiseOverlay( class Asura_Overlay* pxOverlay )
{
	Asura_Dialog* pxNewDialog;

	Asura_Vector_2 xOffset = Asura_Vector_2( 1.0f, 3.0f );

// Add the button to allow a new shape to be added to the shape table...
	pxNewDialog = new Asura_Function_Dialog( s_szWarning, xOffset, NULL, &pxOverlay->m_bShouldClose );
	pxOverlay->AddDialog( pxNewDialog );
	xOffset.y += 1.5f;

} // End Function 'Tools_ShapeTable_WarningDlg::InitialiseOverlay'

bool Tools_ShapeTable::IsSafeToDeleteMesh(const Asura_Mesh* pxMesh)
{
	//make sure the mesh isn't being used by any module
	for(int i=0;i<Asura_Module_System::GetNumberOfModules();i++)
	{
		if(Asura_Module_System::GetMeshForModule(i) == pxMesh)
		{
			return false;
		}
	}
	//or any skin
	for(int i=0;i<Asura_Animation_System::GetNumberOfSkins();i++)
	{
		Asura_Hierarchy_Skin* pxSkin = Asura_Animation_System::GetSkin(i);
		if(pxSkin->GetMesh() == pxMesh)
		{
			return false;
		}
	}

	return true;
}


static void ListShapesCallback(Asura_Hash_ID uShapeHash,void* pList)
{
	Asura_Assert(pList,"");
	List<Asura_Hash_ID>* pxShapeList = (List<Asura_Hash_ID>*) pList;

	if(uShapeHash)
	{
		pxShapeList->add_entry(uShapeHash);
	}
}

void Tools_ShapeTable::BuildListOfUsedShapes(List<Asura_Hash_ID>& xExportShapeList,List<Asura_Hash_ID>& xNonExportShapeList)
{
// Loop over all entities
	for(Asura_Classified_List_It<Asura_Entity_Server> xIt(Asura_Entity_Server::GetList());!xIt.Done();xIt.Next())
	{
		Asura_Entity_Server* pxEntity = xIt.GetCurrent();
		//ignore entities marked for destruction
		if(pxEntity->IsDestroyed()) continue;

		//ick ,need to se if its a physical object to determine the merge with environment flag.
		//may need some other virtual function to handle it in the future
		bool bMergedIntoEnv = false;
		Asura_ServerEntity_PhysicalObject* pxPhysicalEntity = Asura_ServerEntity_PhysicalObject::FindEntity(pxEntity->GetGuid());
		if(pxPhysicalEntity)
		{
			bMergedIntoEnv = pxPhysicalEntity->IsMergedWithEnvironment();
		}

		//add any shapes used to the appropriate list
		if(bMergedIntoEnv || !pxEntity->ShouldIncludeInLevelExport())
		{
			pxEntity->TraverseUsedShapes(ListShapesCallback,&xNonExportShapeList);	
		}
		else
		{
			pxEntity->TraverseUsedShapes(ListShapesCallback,&xExportShapeList);	
		}

	}


	//get a list of shapes used in cutscenes
	for(int i=0;i<Asura_Cutscene_System::GetNumberOfCutscenes();i++)
	{
		Asura_Cutscene* pxCutscene = Asura_Cutscene_System::GetCutsceneByIndex(i);
		Asura_Assert(pxCutscene,"");

		pxCutscene->ListShapesUsed(xExportShapeList);
	}


#ifdef ASURA_USE_BOTANICALS
	// Botanicals
	for(int i=0;i<Asura_Botanicals_TemplateManager::GetNumTemplates();i++)
	{
		Asura_Botanicals_Template *pxTemplate=Asura_Botanicals_TemplateManager::GetTemplate(i);		
		// Add the botanical shapes to the list of shapes not to export!
		xNonExportShapeList.add_entry(pxTemplate->uShapeID);
	}	
#endif
}




bool Tools_ShapeTable::ShouldExportShape(Asura_Hash_ID uShapeHashID)
{
	Tools_ShapeTableElement* pxElement = FindElementByHashID(uShapeHashID,s_pxExportShapes);
	if(pxElement && pxElement->m_bIncludeInExport && pxElement->m_bUsedInGame) return true;

	pxElement = FindElementByHashID(uShapeHashID,s_pxTable);	
	if(pxElement && pxElement->m_bIncludeInExport && pxElement->m_bUsedInGame) return true;
	
	return false;
}

bool Tools_ShapeTable::ShouldExportShapeByIndex(u_int uIndex,bool bCommonShapes)
{
	unsigned int	uNumElements;

	if( false == bCommonShapes )
	{
		uNumElements = (unsigned int)s_iNumElementsInTable;
	}
	else
	{
		uNumElements = (unsigned int)s_iNumExportShapes;
	}

	Asura_Assert( uIndex < uNumElements, "(RAR) Invalid index passed to Tools_ShapeTable::GetMeshNameByIndex" );

	//get the tools shape table entry from the index number
	const Tools_ShapeTableElement*	pxElement = 0;
	if( false == bCommonShapes )
	{
		pxElement = GetElementByIndex( uIndex );
	}
	else
	{
		pxElement = GetElementByIndex( uIndex, s_pxExportShapes, s_iNumExportShapes );
	}

	if(!pxElement) return false;

	return pxElement->m_bIncludeInExport && pxElement->m_bUsedInGame;
}


u_int Tools_ShapeTable::GetChecksumForShape(Asura_Hash_ID uShapeHashID)
{
	Tools_ShapeTableElement* pxElement = FindElementByHashID(uShapeHashID,s_pxExportShapes);
	if(pxElement) return pxElement->m_uChecksum;

	pxElement = FindElementByHashID(uShapeHashID,s_pxTable);	
	if(pxElement) return pxElement->m_uChecksum;

	return 0;
}


#ifdef ASURA_USING_ALL_LEVEL_EXPORT

// DF - Function used to exclude shapes for the common export from the level export
// Flag all common export shapes to export = true, then disable those files from the
// common and level lists of shapes 
// ( possible discrepancy if Common_Export is changed after one export and before another without shutting down editor )
void Tools_ShapeTable::ExcludeCommonShapesFromLevelExport(void)
{
	// DF - Enable all the common shapes for export
	Tools_ShapeTableElement* pxShapeElement = s_pxExportShapes;
	while ( pxShapeElement != NULL )
	{
		EnableShapeForExportByFilename(pxShapeElement->m_szFile);
		pxShapeElement = pxShapeElement->m_pxNext;
	}

	DisableShapesForExport( Tools_Level_Export::GetCommonExportFile(), true);
}

// DF - Function used to include only shapes for the common export
// Flag all common export shapes to export = false, then ebable those files from the
// common and level lists of shapes 
void Tools_ShapeTable::ExcludeNonCommonShapesFromCommonExport(void)
{
	// DF - Disable all the common shapes for export
	Tools_ShapeTableElement* pxShapeElement = s_pxExportShapes;
	while ( pxShapeElement != NULL )
	{
		DisableShapeForExportByFilename(pxShapeElement->m_szFile);
		pxShapeElement = pxShapeElement->m_pxNext;
	}

	EnableShapesForExport( Tools_Level_Export::GetCommonExportFile());
}

#endif // ASURA_USING_ALL_LEVEL_EXPORT

////////////////////////////////////// End of file //////////////////////////////////////

