//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================

#include "sysexternal.h"
#include "cmodel_engine.h"

// UNDONE: Abstract the texture/material lookup stuff and all of this goes away
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
extern IMaterialSystem *materialSystemInterface;

#include "vphysics_interface.h"
#include "sys_dll.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"


IPhysicsSurfaceProps *physprop = NULL;
IPhysicsCollision	 *physcollision = NULL;

// local forward declarations
void CollisionBSPData_LoadTextures( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadTexinfo( CCollisionBSPData *pBSPData, CUtlVector<unsigned short> &map_texinfo );
void CollisionBSPData_LoadLeafs( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadLeafs( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadLeafBrushes( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadPlanes( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadBrushes( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadBrushSides( CCollisionBSPData *pBSPData, CUtlVector<unsigned short> &map_texinfo );
void CollisionBSPData_LoadSubmodels( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadNodes( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadAreas( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadAreaPortals( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadVisibility( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadEntityString( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadPhysics( CCollisionBSPData *pBSPData );
void CollisionBSPData_LoadDispInfo( CCollisionBSPData *pBSPData );


//=============================================================================
//
// Initialization/Destruction Functions
//


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CollisionBSPData_Init( CCollisionBSPData *pBSPData )
{
	pBSPData->numleafs = 1;
	pBSPData->map_vis = NULL;
	pBSPData->numareas = 1;
	pBSPData->numclusters = 1;
	pBSPData->map_nullname = "**empty**";
	pBSPData->numtextures = 0;

	return true;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_Destroy( CCollisionBSPData *pBSPData )
{
	for ( int i = 0; i < pBSPData->numcmodels; i++ )
	{
		physcollision->VCollideUnload( &pBSPData->map_cmodels[i].vcollisionData );
	}

	// free displacement data
	DispCollTrees_FreeLeafList( pBSPData );
	DispCollTrees_Free( g_pDispCollTrees );
	g_pDispCollTrees = NULL;
	g_DispCollTreeCount = 0;

	if ( pBSPData->map_planes.Base() )
	{
		free( pBSPData->map_planes.Base() );
		pBSPData->map_planes.Detach();
	}

	if ( pBSPData->map_texturenames )
	{
		free( pBSPData->map_texturenames );
		pBSPData->map_texturenames = NULL;
	}

	if ( pBSPData->map_surfaces.Base() )
	{
		free( pBSPData->map_surfaces.Base() );
		pBSPData->map_surfaces.Detach();
	}

	if ( pBSPData->map_areaportals.Base() )
	{
		free( pBSPData->map_areaportals.Base() );
		pBSPData->map_areaportals.Detach();
	}

	if ( pBSPData->portalopen.Base() )
	{
		free( pBSPData->portalopen.Base() );
		pBSPData->portalopen.Detach();
	}

	if ( pBSPData->map_areas.Base() )
	{
		free( pBSPData->map_areas.Base() );
		pBSPData->map_areas.Detach();
	}

	if ( pBSPData->map_entitystring.Base() )
	{
		free( pBSPData->map_entitystring.Base() );
		pBSPData->map_entitystring.Detach();
	}

	if ( pBSPData->map_brushes.Base() )
	{
		free( pBSPData->map_brushes.Base() );
		pBSPData->map_brushes.Detach();
	}

	if ( pBSPData->map_cmodels.Base() )
	{
		free( pBSPData->map_cmodels.Base() );
		pBSPData->map_cmodels.Detach();
	}

	if ( pBSPData->map_leafbrushes.Base() )
	{
		free( pBSPData->map_leafbrushes.Base() );
		pBSPData->map_leafbrushes.Detach();
	}

	if ( pBSPData->map_leafs.Base() )
	{
		free( pBSPData->map_leafs.Base() );
		pBSPData->map_leafs.Detach();
	}

	if ( pBSPData->map_nodes.Base() )
	{
		free( pBSPData->map_nodes.Base() );
		pBSPData->map_nodes.Detach();
	}

	if ( pBSPData->map_brushsides.Base() )
	{
		free( pBSPData->map_brushsides.Base() );
		pBSPData->map_brushsides.Detach();
	}

	if ( pBSPData->map_vis )
	{
		free( pBSPData->map_vis );
		pBSPData->map_vis = NULL;
	}

	pBSPData->numplanes = 0;
	pBSPData->numbrushsides = 0;
	pBSPData->emptyleaf = pBSPData->solidleaf =0;
	pBSPData->numnodes = 0;
	pBSPData->numleafs = 0;
	pBSPData->numbrushes = 0;
	pBSPData->numleafbrushes = 0;
	pBSPData->numareas = 0;
	pBSPData->numtextures = 0;
	pBSPData->floodvalid = 0;
	pBSPData->numareaportals = 0;
	pBSPData->numclusters = 0;
	pBSPData->numcmodels = 0;
	pBSPData->numvisibility = 0;
	pBSPData->numentitychars = 0;
	pBSPData->numportalopen = 0;
	pBSPData->map_name[0] = 0;
	pBSPData->map_rootnode = NULL;
}

//-----------------------------------------------------------------------------
// Returns the collision tree associated with the ith displacement
//-----------------------------------------------------------------------------

CDispCollTree* CollisionBSPData_GetCollisionTree( int i )
{
	if ((i < 0) || (i >= g_DispCollTreeCount))
		return 0;

	return &g_pDispCollTrees[i];
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LinkPhysics( void )
{
	//
	// initialize the physics surface properties -- if necessary!
	//
	if( !physprop )
	{
		physprop = ( IPhysicsSurfaceProps* )physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL );
		physcollision = ( IPhysicsCollision* )physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );

		if ( !physprop || !physcollision )
		{
			Sys_Error( "CollisionBSPData_PreLoad: Can't link physics" );
		}
	}
}


//=============================================================================
//
// Loading Functions
//

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_PreLoad( CCollisionBSPData *pBSPData )
{
	// initialize the collision bsp data
	CollisionBSPData_Init( pBSPData ); 
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CollisionBSPData_Load( const char *pName, CCollisionBSPData *pBSPData )
{
	// This is a table that maps texinfo references to csurface_t
	// It is freed after the map has been loaded
	CUtlVector<unsigned short> 	map_texinfo;

	// copy map name
	strcpy( pBSPData->map_name, pName );

	//
	// load bsp file data
	//
	CollisionBSPData_LoadTextures( pBSPData );
	CollisionBSPData_LoadTexinfo( pBSPData, map_texinfo );
	CollisionBSPData_LoadLeafs( pBSPData );
	CollisionBSPData_LoadLeafBrushes( pBSPData );
	CollisionBSPData_LoadPlanes( pBSPData );
	CollisionBSPData_LoadBrushes( pBSPData );
	CollisionBSPData_LoadBrushSides( pBSPData, map_texinfo );
	CollisionBSPData_LoadSubmodels( pBSPData );
	CollisionBSPData_LoadNodes( pBSPData );
	CollisionBSPData_LoadAreas( pBSPData );
	CollisionBSPData_LoadAreaPortals( pBSPData );
	CollisionBSPData_LoadVisibility( pBSPData );
	CollisionBSPData_LoadEntityString( pBSPData );
	CollisionBSPData_LoadPhysics( pBSPData );
    CollisionBSPData_LoadDispInfo( pBSPData );

	return true;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadTextures( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_TEXDATA );

	CMapLoadHelper lhStringData( LUMP_TEXDATA_STRING_DATA );
	const char *pStringData = ( const char * )lhStringData.LumpBase();

	CMapLoadHelper lhStringTable( LUMP_TEXDATA_STRING_TABLE );
	if( lhStringTable.LumpSize() % sizeof( int ) )
		Sys_Error( "CMod_LoadTextures: funny lump size");
	int *pStringTable = ( int * )lhStringTable.LumpBase();

	dtexdata_t	*in;
	int			i, count;
	bool		found;
	IMaterial	*material;

	in = (dtexdata_t *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
	{
		Sys_Error( "CMod_LoadTextures: funny lump size");
	}
	count = lh.LumpSize() / sizeof(*in);
	if (count < 1)
	{
		Sys_Error( "Map with no textures");
	}
	if (count > MAX_MAP_TEXDATA)
	{
		Sys_Error( "Map has too many textures");
	}

	int nSize = count * sizeof(csurface_t);
	pBSPData->map_surfaces.Attach( count, (csurface_t*)malloc( nSize ) );
	memset( pBSPData->map_surfaces.Base(), 0, nSize );

	pBSPData->numtextures = count;

	pBSPData->map_texturenames = (char *)malloc( lhStringData.LumpSize() * sizeof(char) );
	memcpy( pBSPData->map_texturenames, pStringData, lhStringData.LumpSize() );
 
	for ( i=0 ; i<count ; i++, in++ )
	{
		const char *pInName = &pStringData[pStringTable[in->nameStringTableID]];
		int index = pInName - pStringData;
		
		csurface_t *out = &pBSPData->map_surfaces[i];
		out->name = &pBSPData->map_texturenames[index];
		out->surfaceProps = 0;
		out->flags = 0;

		material = materialSystemInterface->FindMaterial( pBSPData->map_surfaces[i].name, &found, true );
		if ( found )
		{
			IMaterialVar *var;
			bool varFound;
			var = material->FindVar( "$surfaceprop", &varFound, false );
			if ( varFound )
			{
				const char *pProps = var->GetStringValue();
				pBSPData->map_surfaces[i].surfaceProps = physprop->GetSurfaceIndex( pProps );
			}
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadTexinfo( CCollisionBSPData *pBSPData, 
									CUtlVector<unsigned short> &map_texinfo )
{
	CMapLoadHelper lh( LUMP_TEXINFO );

	texinfo_t	*in;
	unsigned short	out;
	int			i, count;

	in = (texinfo_t *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
		Sys_Error( "CollisionBSPData_LoadTexinfo: funny lump size");
	count = lh.LumpSize() / sizeof(*in);
	if (count < 1)
		Sys_Error( "Map with no texinfo");
	if (count > MAX_MAP_TEXINFO)
		Sys_Error( "Map has too many surfaces");

	map_texinfo.RemoveAll();
	map_texinfo.EnsureCapacity( count );

	for ( i=0 ; i<count ; i++, in++ )
	{
		out = in->texdata;
		
		if ( out >= pBSPData->numtextures )
			out = 0;

		// HACKHACK: Copy this over for the whole material!!!
		pBSPData->map_surfaces[out].flags |= in->flags;
		map_texinfo.AddToTail(out);
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadLeafs( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_LEAFS );

	int			i;
	dleaf_t 	*in;
	int			count;
	
	in = (dleaf_t *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
	{
		Sys_Error( "CollisionBSPData_LoadLeafs: funny lump size");
	}

	count = lh.LumpSize() / sizeof(*in);

	if (count < 1)
	{
		Sys_Error( "Map with no leafs");
	}

	// need to save space for box planes
	if (count > MAX_MAP_PLANES)
	{
		Sys_Error( "Map has too many planes");
	}

	// Need an extra one for the emptyleaf below, another extra one
	// needed for CM_InitBoxHull
	int nSize = (count + 2) * sizeof(cleaf_t);
	pBSPData->map_leafs.Attach( count + 2, (cleaf_t*)malloc( nSize ) );
	memset( pBSPData->map_leafs.Base(), 0, nSize );

	pBSPData->numleafs = count;
	pBSPData->numclusters = 0;

	for ( i=0 ; i<count ; i++, in++ )
	{
		cleaf_t	*out = &pBSPData->map_leafs[i];	
		out->contents = LittleLong (in->contents);
		out->cluster = LittleShort (in->cluster);
		out->area = LittleShort (in->area);
		out->firstleafbrush = (unsigned short)LittleShort (in->firstleafbrush);
		out->numleafbrushes = (unsigned short)LittleShort (in->numleafbrushes);

		out->m_pDisplacements = NULL;

		if (out->cluster >= pBSPData->numclusters)
		{
			pBSPData->numclusters = out->cluster + 1;
		}
	}

	if (pBSPData->map_leafs[0].contents != CONTENTS_SOLID)
	{
		Sys_Error( "Map leaf 0 is not CONTENTS_SOLID");
	}

	pBSPData->solidleaf = 0;
	pBSPData->emptyleaf = pBSPData->numleafs;
	memset( &pBSPData->map_leafs[pBSPData->emptyleaf], 0, sizeof(pBSPData->map_leafs[pBSPData->emptyleaf]) );
	pBSPData->numleafs++;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadLeafBrushes( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_LEAFBRUSHES );

	int			i;
	unsigned short 	*in;
	int			count;
	
	in = (unsigned short *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
	{
		Sys_Error( "CMod_LoadLeafBrushes: funny lump size");
	}

	count = lh.LumpSize() / sizeof(*in);
	if (count < 1)
	{
		Sys_Error( "Map with no planes");
	}

	// need to save space for box planes
	if (count > MAX_MAP_LEAFBRUSHES)
	{
		Sys_Error( "Map has too many leafbrushes");
	}

	// Extra one added for CM_InitBoxHull
	pBSPData->map_leafbrushes.Attach( count + 1, (unsigned short*)malloc( ( count + 1 ) * sizeof(unsigned short) ) );
	pBSPData->numleafbrushes = count;

	for ( i=0 ; i<count ; i++, in++)
	{
		pBSPData->map_leafbrushes[i] = (unsigned short)LittleShort (*in);
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadPlanes( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_PLANES );

	int			i, j;
	dplane_t 	*in;
	int			count;
	int			bits;
	
	in = (dplane_t *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
	{
		Sys_Error( "CollisionBSPData_LoadPlanes: funny lump size");
	}

	count = lh.LumpSize() / sizeof(*in);

	if (count < 1)
	{
		Sys_Error( "Map with no planes");
	}

	// need to save space for box planes
	if (count > MAX_MAP_PLANES)
	{
		Sys_Error( "Map has too many planes");
	}

	// Add extra room for CM_InitBoxHull
	int nSize = ( count + 12 ) * sizeof(cplane_t);
	pBSPData->map_planes.Attach( count + 12, (cplane_t*)malloc( nSize ) );
	memset( pBSPData->map_planes.Base(), 0, nSize );

	pBSPData->numplanes = count;

	for ( i=0 ; i<count ; i++, in++)
	{
		cplane_t *out = &pBSPData->map_planes[i];	
		bits = 0;
		for (j=0 ; j<3 ; j++)
		{
			out->normal[j] = LittleFloat (in->normal[j]);
			if (out->normal[j] < 0)
			{
				bits |= 1<<j;
			}
		}

		out->dist = LittleFloat (in->dist);
		out->type = LittleLong (in->type);
		out->signbits = bits;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadBrushes( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_BRUSHES );

	dbrush_t	*in;
	int			i, count;
	
	in = (dbrush_t *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
	{
		Sys_Error( "CMod_LoadBrushes: funny lump size");
	}

	count = lh.LumpSize() / sizeof(*in);
	if (count > MAX_MAP_BRUSHES)
	{
		Sys_Error( "Map has too many brushes");
	}

	// Extra one for CM_InitBoxHull
	int nSize = ( count + 1 ) * sizeof(cbrush_t);
	pBSPData->map_brushes.Attach( count+1, (cbrush_t*)malloc( nSize ) );
	memset( pBSPData->map_brushes.Base(), 0, nSize );

	pBSPData->numbrushes = count;

	for (i=0 ; i<count ; i++, in++)
	{
		cbrush_t *out = &pBSPData->map_brushes[i];
		out->firstbrushside = LittleLong(in->firstside);
		out->numsides = LittleLong(in->numsides);
		out->contents = LittleLong(in->contents);
		out->next = NULL;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadBrushSides( CCollisionBSPData *pBSPData, CUtlVector<unsigned short> &map_texinfo )
{
	CMapLoadHelper lh( LUMP_BRUSHSIDES );

	int				i, j;
	dbrushside_t 	*in;
	int				count;
	int				num;

	in = (dbrushside_t *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
	{
		Sys_Error( "CMod_LoadBrushSides: funny lump size");
	}

	count = lh.LumpSize() / sizeof(*in);

	// need to save space for box planes
	if (count > MAX_MAP_BRUSHSIDES)
	{
		Sys_Error( "Map has too many planes");
	}

	// Extra 6 for CM_InitBoxHull
	int nSize = ( count + 6 ) * sizeof(cbrushside_t);
	pBSPData->map_brushsides.Attach( count + 6, (cbrushside_t*)malloc( nSize ) );
	memset( pBSPData->map_brushsides.Base(), 0, nSize );

	pBSPData->numbrushsides = count;

	for ( i=0 ; i<count ; i++, in++)
	{
		cbrushside_t *out = &pBSPData->map_brushsides[i];

		num = (unsigned short)LittleShort (in->planenum);
		out->plane = &pBSPData->map_planes[num];
		j = LittleShort (in->texinfo);
		if (j >= map_texinfo.Size())
		{
			Sys_Error( "Bad brushside texinfo");
		}
		
		// BUGBUG: Why is vbsp writing out -1 as the texinfo id?  (TEXINFO_NODE ?)
		if ( j < 0 )
		{
			out->surface = &nullsurface;
		}
		else
		{
			out->surface = &pBSPData->map_surfaces[map_texinfo[j]];
		}

		out->bBevel = FALSE;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadSubmodels( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_MODELS );

	dmodel_t	*in;
	int			i, j, count;

	in = (dmodel_t *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
		Sys_Error("CMod_LoadSubmodels: funny lump size");
	count = lh.LumpSize() / sizeof(*in);

	if (count < 1)
		Sys_Error( "Map with no models" );
	if (count > MAX_MAP_MODELS)
		Sys_Error( "Map has too many models" );

	int nSize = count * sizeof(cmodel_t);
	pBSPData->map_cmodels.Attach( count, (cmodel_t*)malloc( nSize ) );
	memset( pBSPData->map_cmodels.Base(), 0, nSize );
	pBSPData->numcmodels = count;

	for ( i=0 ; i<count ; i++, in++ )
	{
		cmodel_t *out = &pBSPData->map_cmodels[i];

		for (j=0 ; j<3 ; j++)
		{	// spread the mins / maxs by a pixel
			out->mins[j] = LittleFloat (in->mins[j]) - 1;
			out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
			out->origin[j] = LittleFloat (in->origin[j]);
		}
		out->headnode = LittleLong (in->headnode);
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadNodes( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_NODES );

	dnode_t		*in;
	int			child;
	int			i, j, count;
	
	in = (dnode_t *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
		Sys_Error( "CollisionBSPData_LoadNodes: funny lump size");
	count = lh.LumpSize() / sizeof(*in);

	if (count < 1)
		Sys_Error( "Map has no nodes");
	if (count > MAX_MAP_NODES)
		Sys_Error( "Map has too many nodes");

	// 6 extra for box hull
	int nSize = ( count + 6 ) * sizeof(cnode_t);
	pBSPData->map_nodes.Attach( count + 6, (cnode_t*)malloc( nSize ) );
	memset( pBSPData->map_nodes.Base(), 0, nSize );

	pBSPData->numnodes = count;
	pBSPData->map_rootnode = pBSPData->map_nodes.Base();

	for (i=0 ; i<count ; i++, in++)
	{
		cnode_t	*out = &pBSPData->map_nodes[i];
		out->plane = &pBSPData->map_planes[ LittleLong(in->planenum) ];
		for (j=0 ; j<2 ; j++)
		{
			child = LittleLong (in->children[j]);
			out->children[j] = child;
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadAreas( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_AREAS );

	int			i;
	darea_t 	*in;
	int			count;

	in = (darea_t *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
	{
		Sys_Error( "CMod_LoadAreas: funny lump size");
	}

	count = lh.LumpSize() / sizeof(*in);
	if (count > MAX_MAP_AREAS)
	{
		Sys_Error( "Map has too many areas");
	}

	int nSize = count * sizeof(carea_t);
	pBSPData->map_areas.Attach( count, (carea_t*)malloc( nSize ) );
	memset( pBSPData->map_areas.Base(), 0, nSize );

	pBSPData->numareas = count;

	for ( i=0 ; i<count ; i++, in++)
	{
		carea_t	*out = &pBSPData->map_areas[i];
		out->numareaportals = LittleLong (in->numareaportals);
		out->firstareaportal = LittleLong (in->firstareaportal);
		out->floodvalid = 0;
		out->floodnum = 0;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadAreaPortals( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_AREAPORTALS );

	dareaportal_t 	*in;
	int				count;

	in = (dareaportal_t *)lh.LumpBase();
	if (lh.LumpSize() % sizeof(*in))
	{
		Sys_Error( "CMod_LoadAreaPortals: funny lump size");
	}
		   
	count = lh.LumpSize() / sizeof(*in);
	if (count > MAX_MAP_AREAPORTALS)
	{
		Sys_Error( "Map has too many area portals");
	}

	// Need to add one more in owing to 1-based instead of 0-based data!
	++count;

	pBSPData->numportalopen = count;
	pBSPData->portalopen.Attach( count, (qboolean*)malloc( pBSPData->numportalopen * sizeof(qboolean) ) );
	for ( int i=0; i < pBSPData->numportalopen; i++ )
	{
		pBSPData->portalopen[i] = false;
	}

	pBSPData->numareaportals = count;
	int nSize = count * sizeof(dareaportal_t);
	pBSPData->map_areaportals.Attach( count, (dareaportal_t*)malloc( nSize ) );
	memset( pBSPData->map_areaportals.Base(), 0, nSize );

	Assert( nSize >= lh.LumpSize() ); 
	memcpy( pBSPData->map_areaportals.Base(), in, lh.LumpSize() );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadVisibility( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_VISIBILITY );

	int		i;

	pBSPData->numvisibility = lh.LumpSize();
	if (lh.LumpSize() > MAX_MAP_VISIBILITY)
		Sys_Error( "Map has too large visibility lump");

	int visDataSize = lh.LumpSize();
	if ( visDataSize == 0 )
	{
		pBSPData->map_vis = NULL;
	}
	else
	{
		pBSPData->map_vis = (dvis_t *) malloc( visDataSize );
		memcpy( pBSPData->map_vis, lh.LumpBase(), visDataSize );

		pBSPData->map_vis->numclusters = LittleLong (pBSPData->map_vis->numclusters);
		for (i=0 ; i<pBSPData->map_vis->numclusters ; i++)
		{
			pBSPData->map_vis->bitofs[i][0] = LittleLong (pBSPData->map_vis->bitofs[i][0]);
			pBSPData->map_vis->bitofs[i][1] = LittleLong (pBSPData->map_vis->bitofs[i][1]);
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadEntityString( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_ENTITIES );

	pBSPData->numentitychars = lh.LumpSize();
	if (lh.LumpSize() > MAX_MAP_ENTSTRING)
		Sys_Error( "Map has too large entity lump");

	pBSPData->map_entitystring.Attach( pBSPData->numentitychars, (char*)malloc( lh.LumpSize() ) );
	memcpy( pBSPData->map_entitystring.Base(), lh.LumpBase(), lh.LumpSize() );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadPhysics( CCollisionBSPData *pBSPData )
{
	CMapLoadHelper lh( LUMP_PHYSCOLLIDE );

	if ( !lh.LumpSize() )
		return;

	byte *ptr = lh.LumpBase();
	byte *basePtr = ptr;

	dphysmodel_t physModel;

	// physics data is variable length.  The last physmodel is a NULL pointer
	// with modelIndex -1, dataSize -1
	do
	{
		memcpy( &physModel, ptr, sizeof(physModel) );
		ptr += sizeof(physModel);

		if ( physModel.dataSize > 0 )
		{
			cmodel_t *pModel = &pBSPData->map_cmodels[ physModel.modelIndex ];
			physcollision->VCollideLoad( &pModel->vcollisionData, physModel.solidCount, (const char *)ptr, physModel.dataSize + physModel.keydataSize );
			ptr += physModel.dataSize;
			ptr += physModel.keydataSize;
		}
		
		// avoid infinite loop on badly formed file
		if ( (int)(ptr - basePtr) > lh.LumpSize() )
			break;

	} while ( physModel.dataSize > 0 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionBSPData_LoadDispInfo( CCollisionBSPData *pBSPData )
{
	// How many displacements in the map?
	int coreDispCount = CMapLoadHelper::LumpSize( LUMP_DISPINFO ) / sizeof( ddispinfo_t );
	if( coreDispCount == 0 )
		return;	

    //
    // get the vertex data
    //
 	CMapLoadHelper lhv( LUMP_VERTEXES );
	dvertex_t *pVerts = ( dvertex_t* )lhv.LumpBase();
	if( lhv.LumpSize() % sizeof( dvertex_t ) )
		Sys_Error( "CMod_LoadDispInfo: bad vertex lump size!" );

    //
    // get the edge data
    //
 	CMapLoadHelper lhe( LUMP_EDGES );
    dedge_t *pEdges = ( dedge_t* )lhe.LumpBase();
    if( lhe.LumpSize() % sizeof( dedge_t ) )
        Sys_Error( "CMod_LoadDispInfo: bad edge lump size!" );

    //
    // get surf edges data
    //
 	CMapLoadHelper lhs( LUMP_SURFEDGES );
    int *pSurfEdges = ( int* )lhs.LumpBase();
    if( lhs.LumpSize() % sizeof( int ) )
        Sys_Error( "CMod_LoadDispInfo: bad surf edge lump size!" );

    //
    // get face data
    //
 	CMapLoadHelper lhf( LUMP_FACES );
    dface_t *pFaces = ( dface_t* )lhf.LumpBase();
    if( lhf.LumpSize() % sizeof( dface_t ) )
        Sys_Error( "CMod_LoadDispInfo: bad face lump size!" );
    int faceCount = lhf.LumpSize() / sizeof( dface_t );

	dface_t *pFaceList = pFaces;
	if (!pFaceList)
		return;

    //
    // get texinfo data
    //
 	CMapLoadHelper lhti( LUMP_TEXINFO );
    texinfo_t *pTexinfoList = ( texinfo_t* )lhti.LumpBase();
    if( lhti.LumpSize() % sizeof( texinfo_t ) )
        Sys_Error( "CMod_LoadDispInfo: bad texinfo lump size!" );

	// allocate displacement collision trees
	g_DispCollTreeCount = coreDispCount;
	g_pDispCollTrees = DispCollTrees_Alloc( g_DispCollTreeCount );

	// Build the inverse mapping from disp index to face
	int nMemSize = coreDispCount * sizeof(unsigned short);
	unsigned short *pDispIndexToFaceIndex = (unsigned short*)stackalloc( nMemSize );
	memset( pDispIndexToFaceIndex, 0xFF, nMemSize );
	
	int i;
    for( i = 0; i < faceCount; ++i, ++pFaces )
    {
        // check face for displacement data
        if( pFaces->dispinfo == -1 )
            continue;

        // get the current displacement build surface
		if( pFaces->dispinfo >= coreDispCount )
			continue;

		pDispIndexToFaceIndex[pFaces->dispinfo] = (unsigned short)i;
    }

	// Load one dispinfo from disk at a time and set it up.
	int iCurVert = 0;
	int iCurTri = 0;
	CDispVert tempVerts[MAX_DISPVERTS];
	CDispTri  tempTris[MAX_DISPTRIS];

	for ( i = 0; i < coreDispCount; ++i )
	{
		// Find the face associated with this dispinfo
		unsigned short nFaceIndex = pDispIndexToFaceIndex[i];
		if (nFaceIndex == 0xFFFF)
			continue;

		// Load up the dispinfo and create the CCoreDispInfo from it.
		ddispinfo_t dispInfo;
		CMapLoadHelper::LoadLumpElement( LUMP_DISPINFO, i, sizeof(ddispinfo_t), &dispInfo );

		// Read in the vertices.
		int nVerts = NUM_DISP_POWER_VERTS( dispInfo.power );
		CMapLoadHelper::LoadLumpData( LUMP_DISP_VERTS, iCurVert * sizeof(CDispVert), nVerts*sizeof(CDispVert), tempVerts );
		iCurVert += nVerts;
		
		// Read in the tris.
		int nTris = NUM_DISP_POWER_TRIS( dispInfo.power );
		CMapLoadHelper::LoadLumpData( LUMP_DISP_TRIS, iCurTri * sizeof( CDispTri ), nTris*sizeof( CDispTri), tempTris );
		iCurTri += nTris;

		CCoreDispInfo coreDisp;
		CCoreDispSurface *pDispSurf = coreDisp.GetSurface();
		pDispSurf->SetPointStart( dispInfo.startPosition );
		pDispSurf->SetContents( dispInfo.contents );
	
		coreDisp.InitDispInfo( dispInfo.power, dispInfo.minTess, dispInfo.smoothingAngle, tempVerts, tempTris );

		// Hook the disp surface to the face
		pFaces = &pFaceList[ nFaceIndex ];
		pDispSurf->SetHandle( nFaceIndex );

		// get points
		if( pFaces->numedges > 4 )
			continue;

		Vector surfPoints[4];
		pDispSurf->SetPointCount( pFaces->numedges );
		int j;
		for( j = 0; j < pFaces->numedges; j++ )
		{
			int eIndex = pSurfEdges[pFaces->firstedge+j];
			if( eIndex < 0 )
			{
				VectorCopy( pVerts[pEdges[-eIndex].v[1]].point, surfPoints[j] );
			}
			else
			{
				VectorCopy( pVerts[pEdges[eIndex].v[0]].point, surfPoints[j] );
			}
		}

		for( j = 0; j < 4; j++ )
		{
			pDispSurf->SetPoint( j, surfPoints[j] );
		}

		pDispSurf->FindSurfPointStartIndex();
		pDispSurf->AdjustSurfPointData();

		//
		// generate the collision displacement surfaces
		//
		CDispCollTree *pDispTree = &g_pDispCollTrees[i];
		pDispTree->SetPower( 0 );

		//
		// check for null faces, should have been taken care of in vbsp!!!
		//
		int pointCount = pDispSurf->GetPointCount();
		if( pointCount != 4 )
			continue;

		coreDisp.Create();

		// new collision
		pDispTree->Create( &coreDisp );	

		// Surface props.
		texinfo_t *pTex = &pTexinfoList[pFaces->texinfo];
		bool bFound = false;
		if( pTex->texdata >= 0 )
		{
			IMaterial *pMaterial = materialSystemInterface->FindMaterial( pBSPData->map_surfaces[pTex->texdata].name, &bFound, true );
			if ( bFound )
			{
				IMaterialVar *pVar;
				bool bVarFound;
				pVar = pMaterial->FindVar( "$surfaceprop", &bVarFound, false );
				if ( bVarFound )
				{
					const char *pProps = pVar->GetStringValue();
					pDispTree->SetSurfaceProps( physprop->GetSurfaceIndex( pProps ) );
				}

				pVar = pMaterial->FindVar( "$surfaceprop2", &bVarFound, false );
				if ( bVarFound )
				{
					const char *pProps = pVar->GetStringValue();
					pDispTree->SetSurfaceProps2( physprop->GetSurfaceIndex( pProps ) );
				}
			}
		}
	}
}


//=============================================================================
//
// Collision Count Functions
//

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CollisionCounts_Init( CCollisionCounts *pCounts )
{
	pCounts->m_PointContents = 0;
	pCounts->m_Traces = 0;
	pCounts->m_BrushTraces = 0;
	pCounts->m_DispTraces = 0;
	pCounts->m_Stabs = 0;
}
