
#include "precompiled.h"
#pragma hdrstop

#ifdef _XENON
#define PACIFIER_UPDATE session->PacifierUpdate()
#else
#define PACIFIER_UPDATE
#endif


/*
===============
FloatCRC
===============
*/
ID_INLINE unsigned int FloatCRC( float f ) {
	return *(unsigned int *)&f;
}

/*
===============
StringCRC
===============
*/
ID_INLINE unsigned int StringCRC( const char *str ) {
	unsigned int i, crc;
	const unsigned char *ptr;

	crc = 0;
	ptr = reinterpret_cast<const unsigned char*>(str);
	for ( i = 0; str[i]; i++ ) {
		crc ^= str[i] << (i & 3);
	}
	return crc;
}

/*
=================
ComputeAxisBase

WARNING : special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere when x == 0
rotation by (0,RotY,RotZ) assigns X to normal
=================
*/
static void ComputeAxisBase( const idVec3 &normal, idVec3 &texS, idVec3 &texT ) {
	float RotY, RotZ;
	idVec3 n;

	// do some cleaning
	n[0] = ( idMath::Fabs( normal[0] ) < 1e-6f ) ? 0.0f : normal[0];
	n[1] = ( idMath::Fabs( normal[1] ) < 1e-6f ) ? 0.0f : normal[1];
	n[2] = ( idMath::Fabs( normal[2] ) < 1e-6f ) ? 0.0f : normal[2];

	RotY = -idMath::ATan( n[2], idMath::Sqrt( n[1] * n[1] + n[0] * n[0]) );
	RotZ = idMath::ATan( n[1], n[0] );
	// rotate (0,1,0) and (0,0,1) to compute texS and texT
// RAVEN BEGIN
// jscott: routed thru idMath
	texS[0] = -idMath::Sin( RotZ );
	texS[1] = idMath::Cos( RotZ );
	texS[2] = 0;
	// the texT vector is along -Z ( T texture coorinates axis )
	texT[0] = -idMath::Sin( RotY ) * idMath::Cos( RotZ );
	texT[1] = -idMath::Sin( RotY ) * idMath::Sin( RotZ );
	texT[2] = -idMath::Cos( RotY );
// RAVEN END
}

// RAVEN BEGIN
// rhummer: Trying to debug why func_groups disappear when editing a map when they shouldn't.
idMapFile::~idMapFile( void ) 
{ 
	entities.DeleteContents( true ); 
	if ( cvarSystem->GetCVarBool( "developer" ) ) {
		common->Printf( "^2done with .map file ^0%s\n", name.c_str() );
	}
}
// RAVEN END

/*
=================
idMapBrushSide::GetTextureVectors
=================
*/
void idMapBrushSide::GetTextureVectors( idVec4 v[2] ) const {
	int i;
	idVec3 texX, texY;

	ComputeAxisBase( plane.Normal(), texX, texY );
	for ( i = 0; i < 2; i++ ) {
		v[i][0] = texX[0] * texMat[i][0] + texY[0] * texMat[i][1];
		v[i][1] = texX[1] * texMat[i][0] + texY[1] * texMat[i][1];
		v[i][2] = texX[2] * texMat[i][0] + texY[2] * texMat[i][1];
		v[i][3] = texMat[i][2] + ( origin * v[i].ToVec3() );
	}
}

/*
=================
idMapPatch::Parse
=================
*/
// RAVEN BEGIN
// jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files
idMapPatch *idMapPatch::Parse( Lexer &src, const idVec3 &origin, bool patchDef3, int version ) {
// RAVEN END
	
	TIME_THIS_SCOPE( __FUNCLINE__);	
	
	float		info[7];
	idDrawVert *vert;
	idToken		token;
	int			i, j;

	if ( !src.ExpectTokenString( "{" ) ) {
		return NULL;
	}

	// read the material (we had an implicit 'textures/' in the old format...)
	if ( !src.ReadToken( &token ) ) {
		src.Error( "idMapPatch::Parse: unexpected EOF" );
		return NULL;
	}

	// Parse it
	if (patchDef3) {
		if ( !src.Parse1DMatrix( 7, info ) ) {
			src.Error( "idMapPatch::Parse: unable to Parse patchDef3 info" );
			return NULL;
		}
	} else {
		if ( !src.Parse1DMatrix( 5, info ) ) {
			src.Error( "idMapPatch::Parse: unable to parse patchDef2 info" );
			return NULL;
		}
	}

	idMapPatch *patch = new idMapPatch( info[0], info[1] );
	patch->SetSize( info[0], info[1] );
	if ( version < 2 ) {
		patch->SetMaterial( "textures/" + token );
	} else {
		patch->SetMaterial( token );
	}

	if ( patchDef3 ) {
		patch->SetHorzSubdivisions( info[2] );
		patch->SetVertSubdivisions( info[3] );
		patch->SetExplicitlySubdivided( true );
	}

	if ( patch->GetWidth() < 0 || patch->GetHeight() < 0 ) {
		src.Error( "idMapPatch::Parse: bad size" );
		delete patch;
		return NULL;
	}

	// these were written out in the wrong order, IMHO
	if ( !src.ExpectTokenString( "(" ) ) {
		src.Error( "idMapPatch::Parse: bad patch vertex data" );
		delete patch;
		return NULL;
	}
	for ( j = 0; j < patch->GetWidth(); j++ ) {
		if ( !src.ExpectTokenString( "(" ) ) {
			src.Error( "idMapPatch::Parse: bad vertex row data" );
			delete patch;
			return NULL;
		}
		for ( i = 0; i < patch->GetHeight(); i++ ) {
			float v[5];

			if ( !src.Parse1DMatrix( 5, v ) ) {
				src.Error( "idMapPatch::Parse: bad vertex column data" );
				delete patch;
				return NULL;
			}

			vert = &((*patch)[i * patch->GetWidth() + j]);
			vert->xyz[0] = v[0] - origin[0];
			vert->xyz[1] = v[1] - origin[1];
			vert->xyz[2] = v[2] - origin[2];
			vert->st[0] = v[3];
			vert->st[1] = v[4];
		}
		if ( !src.ExpectTokenString( ")" ) ) {
			delete patch;
			src.Error( "idMapPatch::Parse: unable to parse patch control points" );
			return NULL;
		}
	}
	if ( !src.ExpectTokenString( ")" ) ||
			!src.ExpectTokenString( "}" ) ||
				!src.ExpectTokenString( "}" ) ) {
		src.Error( "idMapPatch::Parse: unable to parse patch control points, no closure" );
		delete patch;
		return NULL;
	}

	return patch;
}

/*
============
idMapPatch::Write
============
*/
bool idMapPatch::Write( idFile *fp, int primitiveNum, const idVec3 &origin ) const {
	int i, j;
	const idDrawVert *v;

	if ( GetExplicitlySubdivided() ) {
		fp->WriteFloatString( "// primitive %d\n{\n patchDef3\n {\n", primitiveNum );
		fp->WriteFloatString( "  \"%s\"\n  ( %d %d %d %d 0 0 0 )\n", GetMaterial(), GetWidth(), GetHeight(), GetHorzSubdivisions(), GetVertSubdivisions());
	} else {
		fp->WriteFloatString( "// primitive %d\n{\n patchDef2\n {\n", primitiveNum );
		fp->WriteFloatString( "  \"%s\"\n  ( %d %d 0 0 0 )\n", GetMaterial(), GetWidth(), GetHeight());
	}

	fp->WriteFloatString( "  (\n" );
	for ( i = 0; i < GetWidth(); i++ ) {
		fp->WriteFloatString( "   ( " );
		for ( j = 0; j < GetHeight(); j++ ) {
			v = &verts[ j * GetWidth() + i ];
			fp->WriteFloatString( " ( %f %f %f %f %f )", v->xyz[0] + origin[0],
								v->xyz[1] + origin[1], v->xyz[2] + origin[2], v->st[0], v->st[1] );
		}
		fp->WriteFloatString( " )\n" );
	}
	fp->WriteFloatString( "  )\n }\n}\n" );

	return true;
}

// RAVEN BEGIN
// rjohnson: added resolve for handling func_groups and other aspects.  Before, radiant would do this processing on a map destroying the original data
/*
===============
idMapPatch::AdjustOrigin
===============
*/
void idMapPatch::AdjustOrigin( idVec3 &delta ) {
	TranslateSelf( delta );
}
// RAVEN END

/*
===============
idMapPatch::GetGeometryCRC
===============
*/
unsigned int idMapPatch::GetGeometryCRC( void ) const {
	int i, j;
	unsigned int crc;

	crc = GetHorzSubdivisions() ^ GetVertSubdivisions();
	for ( i = 0; i < GetWidth(); i++ ) {
		for ( j = 0; j < GetHeight(); j++ ) {
			crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.x );
			crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.y );
			crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.z );
		}
	}

	crc ^= StringCRC( GetMaterial() );

	return crc;
}

/*
=================
idMapBrush::Parse
=================
*/
// RAVEN BEGIN
// jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files
idMapBrush *idMapBrush::Parse( Lexer &src, const idVec3 &origin, bool newFormat, int version ) {
// RAVEN END
	
	TIME_THIS_SCOPE( __FUNCLINE__);	
	
	int i;
	idVec3 planepts[3];
	idToken token;
	idList<idMapBrushSide*> sides;
	idMapBrushSide	*side;
	idDict epairs;

	if ( !src.ExpectTokenString( "{" ) ) {
		return NULL;
	}

	do {
		if ( !src.ReadToken( &token ) ) {
			src.Error( "idMapBrush::Parse: unexpected EOF" );
			sides.DeleteContents( true );
			return NULL;
		}
		if ( token == "}" ) {
			break;
		}

		// here we may have to jump over brush epairs ( only used in editor )
		do {
			// if token is a brace
			if ( token == "(" ) {
				break;
			}
			// the token should be a key string for a key/value pair
			if ( token.type != TT_STRING ) {
				src.Error( "idMapBrush::Parse: unexpected %s, expected ( or epair key string", token.c_str() );
				sides.DeleteContents( true );
				return NULL;
			}

			idStr key = token;

			if ( !src.ReadTokenOnLine( &token ) || token.type != TT_STRING ) {
				src.Error( "idMapBrush::Parse: expected epair value string not found" );
				sides.DeleteContents( true );
				return NULL;
			}

			epairs.Set( key, token );

			// try to read the next key
			if ( !src.ReadToken( &token ) ) {
				src.Error( "idMapBrush::Parse: unexpected EOF" );
				sides.DeleteContents( true );
				return NULL;
			}
		} while (1);

		src.UnreadToken( &token );

		side = new idMapBrushSide();
		sides.Append(side);

		if ( newFormat ) {
			if ( !src.Parse1DMatrix( 4, side->plane.ToFloatPtr() ) ) {
				src.Error( "idMapBrush::Parse: unable to read brush side plane definition" );
				sides.DeleteContents( true );
				return NULL;
			}
		} else {
			// read the three point plane definition
			if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) ||
				!src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) ||
				!src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) {
				src.Error( "idMapBrush::Parse: unable to read brush side plane definition" );
				sides.DeleteContents( true );
				return NULL;
			}

			planepts[0] -= origin;
			planepts[1] -= origin;
			planepts[2] -= origin;

			side->plane.FromPoints( planepts[0], planepts[1], planepts[2] );
		}

		// read the texture matrix
		// this is odd, because the texmat is 2D relative to default planar texture axis
		if ( !src.Parse2DMatrix( 2, 3, side->texMat[0].ToFloatPtr() ) ) {
			src.Error( "idMapBrush::Parse: unable to read brush side texture matrix" );
			sides.DeleteContents( true );
			return NULL;
		}
		side->origin = origin;
		
		// read the material
		if ( !src.ReadTokenOnLine( &token ) ) {
			src.Error( "idMapBrush::Parse: unable to read brush side material" );
			sides.DeleteContents( true );
			return NULL;
		}

		// we had an implicit 'textures/' in the old format...
		if ( version < 2 ) {
			side->material = "textures/" + token;
		} else {
			side->material = token;
		}

// RAVEN BEGIN
// jscott: make sure the material is properly parsed
		declManager->FindMaterial( token );
// RAVEN END

// RAVEN BEGIN
// jscott: removed these in later versions
		if( version < 3 ) {
// RAVEN END
			// Q2 allowed override of default flags and values, but we don't any more
			if ( src.ReadTokenOnLine( &token ) ) {
				if ( src.ReadTokenOnLine( &token ) ) {
					if ( src.ReadTokenOnLine( &token ) ) {
					}
				}
			}
		}
	} while( 1 );

	if ( !src.ExpectTokenString( "}" ) ) {
		sides.DeleteContents( true );
		return NULL;
	}

	idMapBrush *brush = new idMapBrush();
	for ( i = 0; i < sides.Num(); i++ ) {
		brush->AddSide( sides[i] );
	}

	brush->epairs = epairs;

	return brush;
}

/*
=================
idMapBrush::ParseQ3
=================
*/
// RAVEN BEGIN
// jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files
idMapBrush *idMapBrush::ParseQ3( Lexer &src, const idVec3 &origin ) {
// RAVEN END
	int i, shift[2], rotate;
	float scale[2];
	idVec3 planepts[3];
	idToken token;
	idList<idMapBrushSide*> sides;
	idMapBrushSide	*side;
	idDict epairs;

	do {
		if ( src.CheckTokenString( "}" ) ) {
			break;
		}

		side = new idMapBrushSide();
		sides.Append( side );

		// read the three point plane definition
		if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) ||
			!src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) ||
			!src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) {
			src.Error( "idMapBrush::ParseQ3: unable to read brush side plane definition" );
			sides.DeleteContents( true );
			return NULL;
		}

		planepts[0] -= origin;
		planepts[1] -= origin;
		planepts[2] -= origin;

		side->plane.FromPoints( planepts[0], planepts[1], planepts[2] );

		// read the material
		if ( !src.ReadTokenOnLine( &token ) ) {
			src.Error( "idMapBrush::ParseQ3: unable to read brush side material" );
			sides.DeleteContents( true );
			return NULL;
		}

		// we have an implicit 'textures/' in the old format
		side->material = "textures/" + token;

		// read the texture shift, rotate and scale
		shift[0] = src.ParseInt();
		shift[1] = src.ParseInt();
		rotate = src.ParseInt();
		scale[0] = src.ParseFloat();
		scale[1] = src.ParseFloat();
		side->texMat[0] = idVec3( 0.03125f, 0.0f, 0.0f );
		side->texMat[1] = idVec3( 0.0f, 0.03125f, 0.0f );
		side->origin = origin;
		
		// Q2 allowed override of default flags and values, but we don't any more
		if ( src.ReadTokenOnLine( &token ) ) {
			if ( src.ReadTokenOnLine( &token ) ) {
				if ( src.ReadTokenOnLine( &token ) ) {
				}
			}
		}
	} while( 1 );

	idMapBrush *brush = new idMapBrush();
	for ( i = 0; i < sides.Num(); i++ ) {
		brush->AddSide( sides[i] );
	}

	brush->epairs = epairs;

	return brush;
}

/*
============
idMapBrush::Write
============
*/
bool idMapBrush::Write( idFile *fp, int primitiveNum, const idVec3 &origin ) const {
	int i;
	idMapBrushSide *side;

	fp->WriteFloatString( "// primitive %d\n{\n brushDef3\n {\n", primitiveNum );

	// write brush epairs
	for ( i = 0; i < epairs.GetNumKeyVals(); i++) {
		fp->WriteFloatString( "  \"%s\" \"%s\"\n", epairs.GetKeyVal(i)->GetKey().c_str(), epairs.GetKeyVal(i)->GetValue().c_str());
	}

	// write brush sides
	for ( i = 0; i < GetNumSides(); i++ ) {
		side = GetSide( i );
		fp->WriteFloatString( "  ( %f %f %f %f ) ", side->plane[0], side->plane[1], side->plane[2], side->plane[3] );
// RAVEN BEGIN
		fp->WriteFloatString( "( ( %f %f %f ) ( %f %f %f ) ) \"%s\"\n",
// RAVEN END
							side->texMat[0][0], side->texMat[0][1], side->texMat[0][2],
								side->texMat[1][0], side->texMat[1][1], side->texMat[1][2],
									side->material.c_str() );
	}

	fp->WriteFloatString( " }\n}\n" );

	return true;
}

/*
===============
idMapBrush::GetGeometryCRC
===============
*/
unsigned int idMapBrush::GetGeometryCRC( void ) const {
	int i, j;
	idMapBrushSide *mapSide;
	unsigned int crc;

	crc = 0;
	for ( i = 0; i < GetNumSides(); i++ ) {
		mapSide = GetSide(i);
		for ( j = 0; j < 4; j++ ) {
			crc ^= FloatCRC( mapSide->GetPlane()[j] );
		}
		crc ^= StringCRC( mapSide->GetMaterial() );
	}

	return crc;
}

// RAVEN BEGIN
// rjohnson: added resolve for handling func_groups and other aspects.  Before, radiant would do this processing on a map destroying the original data

// This is taken from tools/radiant/EditorBrushPrimit.cpp
float SarrusDet(idVec3 a, idVec3 b, idVec3 c) {
	return (float)a[0] * (float)b[1] * (float)c[2] + (float)b[0] * (float)c[1] * (float)a[2] + (float)c[0] * (float)a[1] * (float)b[2] - (float)c[0] * (float)b[1] * (float)a[2] - (float)a[1] * (float)b[0] * (float)c[2] -	(float)a[0] * (float)b[2] * (float)c[1];
}

/*
===============
idMapBrush::AdjustOrigin
===============
*/
void idMapBrush::AdjustOrigin( idVec3 &delta ) {
	int				i;
	idMapBrushSide	*mapSide;

	for ( i = 0; i < GetNumSides(); i++ ) {
		mapSide = GetSide(i);

		mapSide->SetPlane( mapSide->GetPlane().Translate( delta ) );

		// This is taken from Face_MoveTexture_BrushPrimit() in tools/radiant/EditorBrushPrimit.cpp
		idVec3	texS, texT;
		float	tx, ty;
		idVec3	M[3];	// columns of the matrix .. easier that way
		float	det;
		idVec3	D[2];

		// compute plane axis base ( doesn't change with translation )
		ComputeAxisBase( mapSide->GetPlane().Normal(), texS, texT);

		// compute translation vector in plane axis base
		tx = DotProduct(delta, texS);
		ty = DotProduct(delta, texT);

		// fill the data vectors
		M[0][0] = tx;
		M[0][1] = 1.0f + tx;
		M[0][2] = tx;
		M[1][0] = ty;
		M[1][1] = ty;
		M[1][2] = 1.0f + ty;
		M[2][0] = 1.0f;
		M[2][1] = 1.0f;
		M[2][2] = 1.0f;

		idVec3	tm[2];
		mapSide->GetTextureMatrix( tm[0], tm[1] );

		D[0][0] = tm[0][2];
		D[0][1] = tm[0][0] + tm[0][2];
		D[0][2] = tm[0][1] + tm[0][2];
		D[1][0] = tm[1][2];
		D[1][1] = tm[1][0] + tm[1][2];
		D[1][2] = tm[1][1] + tm[1][2];

		// solve
		det = SarrusDet(M[0], M[1], M[2]);
		if ( det != 0. ) {
			tm[0][0] = SarrusDet(D[0], M[1], M[2]) / det;
			tm[0][1] = SarrusDet(M[0], D[0], M[2]) / det;
			tm[0][2] = SarrusDet(M[0], M[1], D[0]) / det;
			tm[1][0] = SarrusDet(D[1], M[1], M[2]) / det;
			tm[1][1] = SarrusDet(M[0], D[1], M[2]) / det;
			tm[1][2] = SarrusDet(M[0], M[1], D[1]) / det;
			mapSide->SetTextureMatrix(tm);
		}
	}
}
// RAVEN END

/*
================
idMapEntity::Parse
================
*/
// RAVEN BEGIN
// jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files
idMapEntity *idMapEntity::Parse( Lexer &src, bool worldSpawn, int version ) {
// RAVEN END
	
	TIME_THIS_SCOPE( __FUNCLINE__);	
	
	idToken	token;
	idMapEntity *mapEnt;
	idMapPatch *mapPatch;
	idMapBrush *mapBrush;
	bool worldent;
	idVec3 origin;
	double v1, v2, v3;

	if ( !src.ReadToken(&token) ) {
		return NULL;
	}

	if ( token != "{" ) {
		src.Error( "idMapEntity::Parse: { not found, found %s", token.c_str() );
		return NULL;
	}

	mapEnt = new idMapEntity();

	if ( worldSpawn ) {
		mapEnt->primitives.Resize( 1024, 256 );
	}

	origin.Zero();
	worldent = false;
	do {
		if ( !src.ReadToken(&token) ) {
			src.Error( "idMapEntity::Parse: EOF without closing brace" );
			return NULL;
		}
		if ( token == "}" ) {
			break;
		}

		if ( token == "{" ) {
			// parse a brush or patch
			if ( !src.ReadToken( &token ) ) {
				src.Error( "idMapEntity::Parse: unexpected EOF" );
				return NULL;
			}

			if ( worldent ) {
				origin.Zero();
			}

			// if is it a brush: brush, brushDef, brushDef2, brushDef3
			if ( token.Icmpn( "brush", 5 ) == 0 ) {
				mapBrush = idMapBrush::Parse( src, origin, ( !token.Icmp( "brushDef2" ) || !token.Icmp( "brushDef3" ) ), version );
				if ( !mapBrush ) {
					return NULL;
				}
				mapEnt->AddPrimitive( mapBrush );
			}
			// if is it a patch: patchDef2, patchDef3
			else if ( token.Icmpn( "patch", 5 ) == 0 ) {
				mapPatch = idMapPatch::Parse( src, origin, !token.Icmp( "patchDef3" ), version );
				if ( !mapPatch ) {
					return NULL;
				}
				mapEnt->AddPrimitive( mapPatch );
			}
			// assume it's a brush in Q3 or older style
			else {
				src.UnreadToken( &token );
				mapBrush = idMapBrush::ParseQ3( src, origin );
				if ( !mapBrush ) {
					return NULL;
				}
				mapEnt->AddPrimitive( mapBrush );
			}
		} else {
			idStr key, value;

			// parse a key / value pair
			key = token;
			src.ReadTokenOnLine( &token );
			value = token;

			// strip trailing spaces that sometimes get accidentally
			// added in the editor
			value.StripTrailingWhitespace();
			key.StripTrailingWhitespace();

			mapEnt->epairs.Set( key, value );

			if ( !idStr::Icmp( key, "origin" ) ) {
				// scanf into doubles, then assign, so it is idVec size independent
				v1 = v2 = v3 = 0;
				sscanf( value, "%lf %lf %lf", &v1, &v2, &v3 );
				origin.x = v1;
				origin.y = v2;
				origin.z = v3;
			}
			else if ( !idStr::Icmp( key, "classname" ) && !idStr::Icmp( value, "worldspawn" ) ) {
				worldent = true;
			}
		}
	} while( 1 );

	return mapEnt;
}

/*
============
idMapEntity::Write
============
*/
bool idMapEntity::Write( idFile *fp, int entityNum ) const {
	int i;
	idMapPrimitive *mapPrim;
	idVec3 origin;

	fp->WriteFloatString( "// entity %d\n{\n", entityNum );

	// write entity epairs
	for ( i = 0; i < epairs.GetNumKeyVals(); i++) {
		fp->WriteFloatString( "\"%s\" \"%s\"\n", epairs.GetKeyVal(i)->GetKey().c_str(), epairs.GetKeyVal(i)->GetValue().c_str());
	}

	epairs.GetVector( "origin", "0 0 0", origin );

	// write pritimives
	for ( i = 0; i < GetNumPrimitives(); i++ ) {
		mapPrim = GetPrimitive( i );

		switch( mapPrim->GetType() ) {
			case idMapPrimitive::TYPE_BRUSH:
				static_cast<idMapBrush*>(mapPrim)->Write( fp, i, origin );
				break;
			case idMapPrimitive::TYPE_PATCH:
				static_cast<idMapPatch*>(mapPrim)->Write( fp, i, origin );
				break;
		}
	}

	fp->WriteFloatString( "}\n" );

	return true;
}

/*
===============
idMapEntity::RemovePrimitiveData
===============
*/
void idMapEntity::RemovePrimitiveData() {
	primitives.DeleteContents(true);
}

/*
===============
idMapEntity::GetGeometryCRC
===============
*/
unsigned int idMapEntity::GetGeometryCRC( void ) const {
	int i;
	unsigned int crc;
	idMapPrimitive	*mapPrim;

	crc = 0;
	for ( i = 0; i < GetNumPrimitives(); i++ ) {
		mapPrim = GetPrimitive( i );

		switch( mapPrim->GetType() ) {
			case idMapPrimitive::TYPE_BRUSH:
				crc ^= static_cast<idMapBrush*>(mapPrim)->GetGeometryCRC();
				if ( epairs.GetString( "model" ) ) {
					crc ^= StringCRC( epairs.GetString( "model" ) );
				}
				break;
			case idMapPrimitive::TYPE_PATCH:
				crc ^= static_cast<idMapPatch*>(mapPrim)->GetGeometryCRC();
				if ( epairs.GetString( "model" ) ) {
					crc ^= StringCRC( epairs.GetString( "model" ) );
				}
				break;
		}
	}

	return crc;
}

/*
===============
idMapFile::Parse
===============
*/
bool idMapFile::Parse( const char *filename, bool ignoreRegion, bool osPath ) {
	// no string concatenation for epairs and allow path names for materials
// RAVEN BEGIN
// jsinger: Done this way to reduce the amount of code change.  The auto pointer will
// delete the lexer when this method exits
	idAutoPtr<Lexer> lexer(LexerFactory::MakeLexer( LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ));
	Lexer &src(*lexer);
// RAVEN END
	idToken token;
	idStr fullName;
	idMapEntity *mapEnt;
	
	TIME_THIS_SCOPE( __FUNCLINE__);	

	name = filename;
	name.StripFileExtension();
	fullName = name;
	hasPrimitiveData = false;

	if ( !ignoreRegion ) {
		// try loading a .reg file first
		fullName.SetFileExtension( "reg" );
		src.LoadFile( fullName, osPath );
	}

	if ( !src.IsLoaded() ) {

		PACIFIER_UPDATE;
		
		// now try a .map file
		fullName.SetFileExtension( "map" );
		src.LoadFile( fullName, osPath );

		if ( !src.IsLoaded() ) {
			// didn't get anything at all
			return false;
		}
	}

	version = OLD_MAP_VERSION;
	fileTime = src.GetFileTime();
	entities.DeleteContents( true );

	if ( src.CheckTokenString( "Version" ) ) {
		src.ReadTokenOnLine( &token );
		version = token.GetIntValue();
	}

	while( 1 ) {
		PACIFIER_UPDATE;
		mapEnt = idMapEntity::Parse( src, ( entities.Num() == 0 ), version );
		if ( !mapEnt ) {
			break;
		}
// RAVEN BEGIN
// rhummer: Check to see if there are func_groups in the map file.
		if ( !mHasFuncGroups && !idStr::Icmp( mapEnt->epairs.GetString( "classname" ), "func_group" ) ) {
			mHasFuncGroups = true;
		}
// RAVEN END
		entities.Append( mapEnt );
	}

	PACIFIER_UPDATE;
	ParseExport(filename, osPath);

	SetGeometryCRC();
// RAVEN BEGIN
// rhummer: Trying to debug why func_groups disappear when editing a map when they shouldn't.
	if ( cvarSystem->GetCVarBool( "developer" ) ) {
		common->Printf( "^2loaded .map file ^0%s\n", filename );
	}
	mHasBeenResolved = false;
// RAVEN END

	hasPrimitiveData = true;
	return true;
}

// RAVEN BEGIN
// rjohnson: added resolve for handling func_groups and other aspects.  Before, radiant would do this processing on a map destroying the original data
void idMapFile::Resolve( void )
{
	int i, j, k;
	idMapEntity *mapEnt;

	if ( !hasPrimitiveData ) {
		return;
	}

	// if the map has a worldspawn
	if ( entities.Num() ) {

		// "removeEntities" "classname" can be set in the worldspawn to remove all entities with the given classname
		const idKeyValue *removeEntities = entities[0]->epairs.MatchPrefix( "removeEntities", NULL );
		while ( removeEntities ) {
			RemoveEntities( removeEntities->GetValue() );
			removeEntities = entities[0]->epairs.MatchPrefix( "removeEntities", removeEntities );
		}

		// "overrideMaterial" "material" can be set in the worldspawn to reset all materials
		idStr material;
		if ( entities[0]->epairs.GetString( "overrideMaterial", "", material ) ) {
			for ( i = 0; i < entities.Num(); i++ ) {
				mapEnt = entities[i];
				for ( j = 0; j < mapEnt->GetNumPrimitives(); j++ ) {
					idMapPrimitive *mapPrimitive = mapEnt->GetPrimitive( j );
					switch( mapPrimitive->GetType() ) {
						case idMapPrimitive::TYPE_BRUSH: {
							idMapBrush *mapBrush = static_cast<idMapBrush *>(mapPrimitive);
							for ( k = 0; k < mapBrush->GetNumSides(); k++ ) {
								mapBrush->GetSide( k )->SetMaterial( material );
							}
							break;
						}
						case idMapPrimitive::TYPE_PATCH: {
							static_cast<idMapPatch *>(mapPrimitive)->SetMaterial( material );
							break;
						}
					}
				}
			}
		}

		// force all entities to have a name key/value pair
		if ( entities[0]->epairs.GetBool( "forceEntityNames" ) ) {
			for ( i = 1; i < entities.Num(); i++ ) {
				mapEnt = entities[i];
				if ( !mapEnt->epairs.FindKey( "name" ) ) {
					mapEnt->epairs.Set( "name", va( "%s%d", mapEnt->epairs.GetString( "classname", "forcedName" ), i ) );
				}
			}
		}

		// move the primitives of any func_group entities to the worldspawn
		for ( i = 1; i < entities.Num(); i++ ) {
			mapEnt = entities[i];
			if ( idStr::Icmp( mapEnt->epairs.GetString( "classname" ), "func_group" ) == 0 ) {
				idVec3	delta;
				
				mapEnt->epairs.GetVector( "origin", "0 0 0", delta );

				for( j = 0; j < mapEnt->primitives.Num(); j++ ) {
					idMapPrimitive	*mapPrim = mapEnt->primitives[j];

					mapPrim->AdjustOrigin( delta );
				}
				entities[0]->primitives.Append( mapEnt->primitives );
				mapEnt->primitives.Clear();
			}
		}
		RemoveEntities( "func_group" );
	}
// rhummer:
	mHasBeenResolved = true;
	if ( cvarSystem->GetCVarBool( "developer" ) ) {
		common->Printf( "^2idMapFile::Resolve has been run on ^0%s^2 so no func_groups exist.\n", name.c_str() );
	}
}

// rjohnson: added export
/*
============
idMapFile::Write
============
*/
bool idMapFile::Write( const char *fileName, const char *ext, bool fromBasePath, bool exportOnly ) {
	int i;
	idStr qpath;
	idFile *fp;
// RAVEN BEGIN
// rhummer: Used to verify func_groups didn't disappear somehow.
	bool funcGroupFound = false;
// RAVEN END

	qpath = fileName;
	qpath.SetFileExtension( ext );

	idLib::common->Printf( "writing %s...\n", qpath.c_str() );

	if ( fromBasePath ) {
		fp = idLib::fileSystem->OpenFileWrite( qpath, "fs_devpath" );
	}
	else {
		fp = idLib::fileSystem->OpenExplicitFileWrite( qpath );
	}

	if ( !fp ) {
		idLib::common->Warning( "Couldn't open %s\n", qpath.c_str() );
		return false;
	}

	fp->WriteFloatString( "Version %d\n", CURRENT_MAP_VERSION );

	if (exportOnly)
	{
		for ( i = 0; i < entities.Num(); i++ ) 
		{
			if (entities[i]->epairs.GetInt("export"))
			{
				entities[i]->Write( fp, i );
			}
		}
	}
	else
	{
		for ( i = 0; i < entities.Num(); i++ ) {
			entities[i]->Write( fp, i );
// RAVEN BEGIN
// rhummer: See if there are func_groups, there should be if there was during loading..
			if ( !idStr::Icmp( entities[i]->epairs.GetString( "classname" ), "func_group" ) ) {
				funcGroupFound = true;
			}
// RAVEN END
		}
	}

// RAVEN BEGIN
// rhummer: If this is true..something bad happened to cause all func_groups to go away between loading and saving.
	if ( mHasFuncGroups && !funcGroupFound && cvarSystem->GetCVarBool( "developer" ) ) {
		common->Warning( "^5Did not find any ^2func_groups^0 during save, but there were ^2func_groups^0 found during loading.  Was this intended?\n");
	}
// RAVEN END

	idLib::fileSystem->CloseFile( fp );

	return true;
}
// RAVEN END

/*
===============
idMapFile::SetGeometryCRC
===============
*/
void idMapFile::SetGeometryCRC( void ) {
	int i;

	geometryCRC = 0;
	for ( i = 0; i < entities.Num(); i++ ) {
		geometryCRC ^= entities[i]->GetGeometryCRC();
	}
}

/*
===============
idMapFile::AddEntity
===============
*/
int idMapFile::AddEntity( idMapEntity *mapEnt ) {
	int ret = entities.Append( mapEnt );
	return ret;
}

/*
===============
idMapFile::FindEntity
===============
*/
idMapEntity *idMapFile::FindEntity( const char *name ) {
	for ( int i = 0; i < entities.Num(); i++ ) {
		idMapEntity *ent = entities[i];
		if ( idStr::Icmp( ent->epairs.GetString( "name" ), name ) == 0 ) {
			return ent;
		}
	}
	return NULL;
}

/*
===============
idMapFile::RemoveEntity
===============
*/
void idMapFile::RemoveEntity( idMapEntity *mapEnt ) {
	entities.Remove( mapEnt );
	delete mapEnt;
}

/*
===============
idMapFile::RemoveEntity
===============
*/
void idMapFile::RemoveEntities( const char *classname ) {
	for ( int i = 0; i < entities.Num(); i++ ) {
		idMapEntity *ent = entities[i];
		if ( idStr::Icmp( ent->epairs.GetString( "classname" ), classname ) == 0 ) {
			delete entities[i];
			entities.RemoveIndex( i );
			i--;
		}
	}
}

/*
===============
idMapFile::RemoveAllEntities
===============
*/
void idMapFile::RemoveAllEntities() {
	entities.DeleteContents( true );
	hasPrimitiveData = false;
}

/*
===============
idMapFile::RemovePrimitiveData
===============
*/
void idMapFile::RemovePrimitiveData() {
	for ( int i = 0; i < entities.Num(); i++ ) {
		idMapEntity *ent = entities[i];
		ent->RemovePrimitiveData();
	}
	hasPrimitiveData = false;
// RAVEN BEGIN
// rhummer: debugging stuff..
	if ( cvarSystem->GetCVarBool( "developer" ) ) {
		common->Printf( "^2Removed all primitive data from ^0%s\n", name.c_str() );
	}
// RAVEN END
}

/*
===============
idMapFile::NeedsReload
===============
*/
bool idMapFile::NeedsReload() {
	if ( name.Length() ) {
		ID_TIME_T time = (ID_TIME_T)-1;
		if ( idLib::fileSystem->ReadFile( name, NULL, &time ) > 0 ) {
			return ( time > fileTime );
		}
	}
	return true;
}

bool idMapFile::WriteExport( const char *fileName, bool fromBasePath )
{
	int			i, j;
	idDict		newPairs;

	for ( i = 0; i < entities.Num(); i++ ) 
	{
		idMapEntity *ent = entities[i];

		if ( ent->epairs.GetInt("export") == 1 )
		{
/*			for ( j = 0; j < ent->epairs.GetNumKeyVals(); j++) 
			{
				const idKeyValue	*kv = ent->epairs.GetKeyVal(j);

				if (kv->key.CmpPrefix("export_") == 0)
				{
					ent->epairs.Delete(kv->key.c_str());
					j--;
					continue;
				}
			}
*/
			newPairs.Clear();

			for ( j = 0; j < ent->epairs.GetNumKeyVals(); j++) 
			{
				const idKeyValue	*kv = ent->epairs.GetKeyVal(j);

				if (kv->GetKey().CmpPrefix("export_") != 0)
				{
					char				newName[1024];

					sprintf(newName, "export_%s", kv->GetKey().c_str());

					if (!ent->epairs.GetString(newName, 0))
					{	// don't overwrite an existing export
						newPairs.Set (newName, kv->GetValue().c_str());
					}
				}
			}

			ent->epairs.Copy(newPairs);
		}
	}

	return Write(fileName, "export", fromBasePath, true);
}

bool idMapFile::ParseExport( const char *filename, bool osPath )
{
	// no string concatenation for epairs and allow path names for materials
// RAVEN BEGIN
// jsinger: Done this way to reduce the amount of code to change.
//          The auto pointer will delete the lexer when this method exits.
	idAutoPtr<Lexer>		lexer(LexerFactory::MakeLexer( LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ) );
	Lexer &src(*lexer);
// RAVEN END
	idToken		token;
	idStr		fullName;
	idMapEntity *mapEnt;
	int			i, j;
	int			numMerged, numNotMerged, numAdded, numRemoved;

	numMerged = numNotMerged = numAdded = numRemoved = 0;
	mHasExportEntities = false;
	
	fullName = filename;
	fullName.StripFileExtension();
	fullName.SetFileExtension( "export" );
	src.LoadFile( fullName, osPath );
	if ( !src.IsLoaded() ) {
		// didn't get anything at all
		return false;
	}

	version = OLD_MAP_VERSION;
	fileTime = src.GetFileTime();
	mExportEntities.DeleteContents( true );

	if ( src.CheckTokenString( "Version" ) ) {
		src.ReadTokenOnLine( &token );
		version = token.GetIntValue();
	}

	while( 1 ) {
		mapEnt = idMapEntity::Parse( src, false, version );
		if ( !mapEnt ) {
			break;
		}
		mExportEntities.Append( mapEnt );
	}

	if (mExportEntities.Num())
	{
		mHasExportEntities = true;
	}

	for(i=0;i<mExportEntities.Num();i++)
	{
		idMapEntity *ent = mExportEntities[i];
		idMapEntity *mapEnt;
		const char	*name;

		name = ent->epairs.GetString("name");
		mapEnt = FindEntity(name);

		if (!mapEnt)
		{	// check to see if we have an export_name
			name = ent->epairs.GetString("export_name", 0);
			if (!name || ent->epairs.GetInt("original") )
			{	// this is a new entity
				ent->epairs.SetInt("export", 2);			// 2 = when re-exporting, don't duplicated the export_ fields
				ent->epairs.SetInt("merged", 1);
				ent->epairs.SetInt("original", 0);			// 0 = now the entity in map is always treated as master
				entities.Append(ent);
				mExportEntities.RemoveIndex(i);
				i--;
				numAdded++;
				continue;
			}
			else
			{
				mapEnt = FindEntity(name);
			}
		}

		if (!mapEnt)
		{	// this export entity has been removed from the original map
			numNotMerged++;
		}
		else
		{	// exists in both, do a merge!
			mapEnt->epairs.SetInt("export", 2);			// 2 = when re-exporting, don't duplicated the export_ fields
			mapEnt->epairs.SetInt("merged", 1);
			for ( j = 0; j < ent->epairs.GetNumKeyVals(); j++) 
			{
				char				origName[1024];
				const idKeyValue	*exportKV = ent->epairs.GetKeyVal(j);
				const idKeyValue	*origKV, *mapKV;

				if (exportKV->GetKey().CmpPrefix("export_") != 0)
				{
					sprintf(origName, "export_%s", exportKV->GetKey().c_str());
					origKV = ent->epairs.FindKey(origName);
					mapKV = mapEnt->epairs.FindKey(exportKV->GetKey().c_str());

					if (origKV && mapKV)
					{	// keys exist in all spots, compare
						if (origKV->GetValue() == exportKV->GetValue())
						{	// export hasn't change
						}
						else if (mapKV->GetValue() == origKV->GetValue())
						{	// map is same as the orig, yet export has changed, so bring it over
							mapEnt->epairs.Set(exportKV->GetKey().c_str(), exportKV->GetValue().c_str());
						}
					}
					else if (origKV)
					{	// map has removed this key, so we shouldn't merge it
					}
					else if (mapKV)
					{	// map has added this key, so we'll ignore the exported value, which has also been added
					}
					else
					{	// this was added to the export, so merge it in
						mapEnt->epairs.Set(exportKV->GetKey().c_str(), exportKV->GetValue().c_str());
					}
				}
				else
				{
					mapEnt->epairs.Set(exportKV->GetKey().c_str(), exportKV->GetValue().c_str());
				}
			}
			numMerged++;
		}
	}

	// check for any entities that have been marked as exported in the original map but have been removed from the export file
	for(i=0;i<entities.Num();i++)
	{
		idMapEntity *ent = entities[i];

		if (ent->epairs.GetInt("export"))
		{
			if (!ent->epairs.GetInt("merged"))
			{
				entities.RemoveIndex(i);
				numRemoved++;
				i--;
			}
			else
			{
				ent->epairs.Delete("merged");
			}
		}
	}

	common->Printf("Export Merge: %d added, %d merged, %d not merged, %d removed\n", numAdded, numMerged, numNotMerged, numRemoved);

	mExportEntities.DeleteContents( true );

	return true;
}
