DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

MapFile.cpp (24367B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #include "precompiled.h"
     30 #pragma hdrstop
     31 
     32 
     33 /*
     34 ===============
     35 FloatCRC
     36 ===============
     37 */
     38 ID_INLINE unsigned int FloatCRC( float f ) {
     39 	return *(unsigned int *)&f;
     40 }
     41 
     42 /*
     43 ===============
     44 StringCRC
     45 ===============
     46 */
     47 ID_INLINE unsigned int StringCRC( const char *str ) {
     48 	unsigned int i, crc;
     49 	const unsigned char *ptr;
     50 
     51 	crc = 0;
     52 	ptr = reinterpret_cast<const unsigned char*>(str);
     53 	for ( i = 0; str[i]; i++ ) {
     54 		crc ^= str[i] << (i & 3);
     55 	}
     56 	return crc;
     57 }
     58 
     59 /*
     60 =================
     61 ComputeAxisBase
     62 
     63 WARNING : special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere when x == 0
     64 rotation by (0,RotY,RotZ) assigns X to normal
     65 =================
     66 */
     67 static void ComputeAxisBase( const idVec3 &normal, idVec3 &texS, idVec3 &texT ) {
     68 	float RotY, RotZ;
     69 	idVec3 n;
     70 
     71 	// do some cleaning
     72 	n[0] = ( idMath::Fabs( normal[0] ) < 1e-6f ) ? 0.0f : normal[0];
     73 	n[1] = ( idMath::Fabs( normal[1] ) < 1e-6f ) ? 0.0f : normal[1];
     74 	n[2] = ( idMath::Fabs( normal[2] ) < 1e-6f ) ? 0.0f : normal[2];
     75 
     76 	RotY = -atan2( n[2], idMath::Sqrt( n[1] * n[1] + n[0] * n[0]) );
     77 	RotZ = atan2( n[1], n[0] );
     78 	// rotate (0,1,0) and (0,0,1) to compute texS and texT
     79 	texS[0] = -sin(RotZ);
     80 	texS[1] = cos(RotZ);
     81 	texS[2] = 0;
     82 	// the texT vector is along -Z ( T texture coorinates axis )
     83 	texT[0] = -sin(RotY) * cos(RotZ);
     84 	texT[1] = -sin(RotY) * sin(RotZ);
     85 	texT[2] = -cos(RotY);
     86 }
     87 
     88 /*
     89 =================
     90 idMapBrushSide::GetTextureVectors
     91 =================
     92 */
     93 void idMapBrushSide::GetTextureVectors( idVec4 v[2] ) const {
     94 	int i;
     95 	idVec3 texX, texY;
     96 
     97 	ComputeAxisBase( plane.Normal(), texX, texY );
     98 	for ( i = 0; i < 2; i++ ) {
     99 		v[i][0] = texX[0] * texMat[i][0] + texY[0] * texMat[i][1];
    100 		v[i][1] = texX[1] * texMat[i][0] + texY[1] * texMat[i][1];
    101 		v[i][2] = texX[2] * texMat[i][0] + texY[2] * texMat[i][1];
    102 		v[i][3] = texMat[i][2] + ( origin * v[i].ToVec3() );
    103 	}
    104 }
    105 
    106 /*
    107 =================
    108 idMapPatch::Parse
    109 =================
    110 */
    111 idMapPatch *idMapPatch::Parse( idLexer &src, const idVec3 &origin, bool patchDef3, float version ) {
    112 	float		info[7];
    113 	idDrawVert *vert;
    114 	idToken		token;
    115 	int			i, j;
    116 
    117 	if ( !src.ExpectTokenString( "{" ) ) {
    118 		return NULL;
    119 	}
    120 
    121 	// read the material (we had an implicit 'textures/' in the old format...)
    122 	if ( !src.ReadToken( &token ) ) {
    123 		src.Error( "idMapPatch::Parse: unexpected EOF" );
    124 		return NULL;
    125 	}
    126 
    127 	// Parse it
    128 	if (patchDef3) {
    129 		if ( !src.Parse1DMatrix( 7, info ) ) {
    130 			src.Error( "idMapPatch::Parse: unable to Parse patchDef3 info" );
    131 			return NULL;
    132 		}
    133 	} else {
    134 		if ( !src.Parse1DMatrix( 5, info ) ) {
    135 			src.Error( "idMapPatch::Parse: unable to parse patchDef2 info" );
    136 			return NULL;
    137 		}
    138 	}
    139 
    140 	idMapPatch *patch = new (TAG_IDLIB) idMapPatch( info[0], info[1] );
    141 
    142 	patch->SetSize( info[0], info[1] );
    143 	if ( version < 2.0f ) {
    144 		patch->SetMaterial( "textures/" + token );
    145 	} else {
    146 		patch->SetMaterial( token );
    147 	}
    148 
    149 	if ( patchDef3 ) {
    150 		patch->SetHorzSubdivisions( info[2] );
    151 		patch->SetVertSubdivisions( info[3] );
    152 		patch->SetExplicitlySubdivided( true );
    153 	}
    154 
    155 	if ( patch->GetWidth() < 0 || patch->GetHeight() < 0 ) {
    156 		src.Error( "idMapPatch::Parse: bad size" );
    157 		delete patch;
    158 		return NULL;
    159 	}
    160 
    161 	// these were written out in the wrong order, IMHO
    162 	if ( !src.ExpectTokenString( "(" ) ) {
    163 		src.Error( "idMapPatch::Parse: bad patch vertex data" );
    164 		delete patch;
    165 		return NULL;
    166 	}
    167 
    168 
    169 	for ( j = 0; j < patch->GetWidth(); j++ ) {
    170 		if ( !src.ExpectTokenString( "(" ) ) {
    171 			src.Error( "idMapPatch::Parse: bad vertex row data" );
    172 			delete patch;
    173 			return NULL;
    174 		}
    175 		for ( i = 0; i < patch->GetHeight(); i++ ) {
    176 			float v[5];
    177 
    178 			if ( !src.Parse1DMatrix( 5, v ) ) {
    179 				src.Error( "idMapPatch::Parse: bad vertex column data" );
    180 				delete patch;
    181 				return NULL;
    182 			}
    183 
    184 			vert = &((*patch)[i * patch->GetWidth() + j]);
    185 			vert->xyz[0] = v[0] - origin[0];
    186 			vert->xyz[1] = v[1] - origin[1];
    187 			vert->xyz[2] = v[2] - origin[2];
    188 			vert->SetTexCoord( v[3], v[4] );
    189 		}
    190 		if ( !src.ExpectTokenString( ")" ) ) {
    191 			delete patch;
    192 			src.Error( "idMapPatch::Parse: unable to parse patch control points" );
    193 			return NULL;
    194 		}
    195 	}
    196 
    197 	if ( !src.ExpectTokenString( ")" ) ) {
    198 		src.Error( "idMapPatch::Parse: unable to parse patch control points, no closure" );
    199 		delete patch;
    200 		return NULL;
    201 	}
    202 
    203 	// read any key/value pairs
    204 	while( src.ReadToken( &token ) ) {
    205 		if ( token == "}" ) {
    206 			src.ExpectTokenString( "}" );
    207 			break;
    208 		}
    209 		if ( token.type == TT_STRING ) {
    210 			idStr key = token;
    211 			src.ExpectTokenType( TT_STRING, 0, &token );
    212 			patch->epairs.Set( key, token );
    213 		}
    214 	}
    215 
    216 	return patch;
    217 }
    218 
    219 /*
    220 ============
    221 idMapPatch::Write
    222 ============
    223 */
    224 bool idMapPatch::Write( idFile *fp, int primitiveNum, const idVec3 &origin ) const {
    225 	int i, j;
    226 	const idDrawVert *v;
    227 
    228 	if ( GetExplicitlySubdivided() ) {
    229 		fp->WriteFloatString( "// primitive %d\n{\n patchDef3\n {\n", primitiveNum );
    230 		fp->WriteFloatString( "  \"%s\"\n  ( %d %d %d %d 0 0 0 )\n", GetMaterial(), GetWidth(), GetHeight(), GetHorzSubdivisions(), GetVertSubdivisions());
    231 	} else {
    232 		fp->WriteFloatString( "// primitive %d\n{\n patchDef2\n {\n", primitiveNum );
    233 		fp->WriteFloatString( "  \"%s\"\n  ( %d %d 0 0 0 )\n", GetMaterial(), GetWidth(), GetHeight());
    234 	}
    235 
    236 	fp->WriteFloatString( "  (\n" );
    237 	idVec2 st;
    238 	for ( i = 0; i < GetWidth(); i++ ) {
    239 		fp->WriteFloatString( "   ( " );
    240 		for ( j = 0; j < GetHeight(); j++ ) {
    241 			v = &verts[ j * GetWidth() + i ];
    242 			st = v->GetTexCoord();
    243 			fp->WriteFloatString( " ( %f %f %f %f %f )", v->xyz[0] + origin[0],
    244 								v->xyz[1] + origin[1], v->xyz[2] + origin[2], st[0], st[1] );
    245 		}
    246 		fp->WriteFloatString( " )\n" );
    247 	}
    248 	fp->WriteFloatString( "  )\n }\n}\n" );
    249 
    250 	return true;
    251 }
    252 
    253 /*
    254 ===============
    255 idMapPatch::GetGeometryCRC
    256 ===============
    257 */
    258 unsigned int idMapPatch::GetGeometryCRC() const {
    259 	int i, j;
    260 	unsigned int crc;
    261 
    262 	crc = GetHorzSubdivisions() ^ GetVertSubdivisions();
    263 	for ( i = 0; i < GetWidth(); i++ ) {
    264 		for ( j = 0; j < GetHeight(); j++ ) {
    265 			crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.x );
    266 			crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.y );
    267 			crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.z );
    268 		}
    269 	}
    270 
    271 	crc ^= StringCRC( GetMaterial() );
    272 
    273 	return crc;
    274 }
    275 
    276 /*
    277 =================
    278 idMapBrush::Parse
    279 =================
    280 */
    281 idMapBrush *idMapBrush::Parse( idLexer &src, const idVec3 &origin, bool newFormat, float version ) {
    282 	int i;
    283 	idVec3 planepts[3];
    284 	idToken token;
    285 	idList<idMapBrushSide*> sides;
    286 	idMapBrushSide	*side;
    287 	idDict epairs;
    288 
    289 	if ( !src.ExpectTokenString( "{" ) ) {
    290 		return NULL;
    291 	}
    292 
    293 	do {
    294 		if ( !src.ReadToken( &token ) ) {
    295 			src.Error( "idMapBrush::Parse: unexpected EOF" );
    296 			sides.DeleteContents( true );
    297 			return NULL;
    298 		}
    299 		if ( token == "}" ) {
    300 			break;
    301 		}
    302 
    303 		// here we may have to jump over brush epairs ( only used in editor )
    304 		do {
    305 			// if token is a brace
    306 			if ( token == "(" ) {
    307 				break;
    308 			}
    309 			// the token should be a key string for a key/value pair
    310 			if ( token.type != TT_STRING ) {
    311 				src.Error( "idMapBrush::Parse: unexpected %s, expected ( or epair key string", token.c_str() );
    312 				sides.DeleteContents( true );
    313 				return NULL;
    314 			}
    315 
    316 			idStr key = token;
    317 
    318 			if ( !src.ReadTokenOnLine( &token ) || token.type != TT_STRING ) {
    319 				src.Error( "idMapBrush::Parse: expected epair value string not found" );
    320 				sides.DeleteContents( true );
    321 				return NULL;
    322 			}
    323 
    324 			epairs.Set( key, token );
    325 
    326 			// try to read the next key
    327 			if ( !src.ReadToken( &token ) ) {
    328 				src.Error( "idMapBrush::Parse: unexpected EOF" );
    329 				sides.DeleteContents( true );
    330 				return NULL;
    331 			}
    332 		} while (1);
    333 
    334 		src.UnreadToken( &token );
    335 
    336 		side = new (TAG_IDLIB) idMapBrushSide();
    337 		sides.Append(side);
    338 
    339 		if ( newFormat ) {
    340 			if ( !src.Parse1DMatrix( 4, side->plane.ToFloatPtr() ) ) {
    341 				src.Error( "idMapBrush::Parse: unable to read brush side plane definition" );
    342 				sides.DeleteContents( true );
    343 				return NULL;
    344 			}
    345 		} else {
    346 			// read the three point plane definition
    347 			if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) ||
    348 				!src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) ||
    349 				!src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) {
    350 				src.Error( "idMapBrush::Parse: unable to read brush side plane definition" );
    351 				sides.DeleteContents( true );
    352 				return NULL;
    353 			}
    354 
    355 			planepts[0] -= origin;
    356 			planepts[1] -= origin;
    357 			planepts[2] -= origin;
    358 
    359 			side->plane.FromPoints( planepts[0], planepts[1], planepts[2] );
    360 		}
    361 
    362 		// read the texture matrix
    363 		// this is odd, because the texmat is 2D relative to default planar texture axis
    364 		if ( !src.Parse2DMatrix( 2, 3, side->texMat[0].ToFloatPtr() ) ) {
    365 			src.Error( "idMapBrush::Parse: unable to read brush side texture matrix" );
    366 			sides.DeleteContents( true );
    367 			return NULL;
    368 		}
    369 		side->origin = origin;
    370 		
    371 		// read the material
    372 		if ( !src.ReadTokenOnLine( &token ) ) {
    373 			src.Error( "idMapBrush::Parse: unable to read brush side material" );
    374 			sides.DeleteContents( true );
    375 			return NULL;
    376 		}
    377 
    378 		// we had an implicit 'textures/' in the old format...
    379 		if ( version < 2.0f ) {
    380 			side->material = "textures/" + token;
    381 		} else {
    382 			side->material = token;
    383 		}
    384 
    385 		// Q2 allowed override of default flags and values, but we don't any more
    386 		if ( src.ReadTokenOnLine( &token ) ) {
    387 			if ( src.ReadTokenOnLine( &token ) ) {
    388 				if ( src.ReadTokenOnLine( &token ) ) {
    389 				}
    390 			}
    391 		}
    392 	} while( 1 );
    393 
    394 	if ( !src.ExpectTokenString( "}" ) ) {
    395 		sides.DeleteContents( true );
    396 		return NULL;
    397 	}
    398 
    399 	idMapBrush *brush = new (TAG_IDLIB) idMapBrush();
    400 	for ( i = 0; i < sides.Num(); i++ ) {
    401 		brush->AddSide( sides[i] );
    402 	}
    403 
    404 	brush->epairs = epairs;
    405 
    406 	return brush;
    407 }
    408 
    409 /*
    410 =================
    411 idMapBrush::ParseQ3
    412 =================
    413 */
    414 idMapBrush *idMapBrush::ParseQ3( idLexer &src, const idVec3 &origin ) {
    415 	int i, shift[2], rotate;
    416 	float scale[2];
    417 	idVec3 planepts[3];
    418 	idToken token;
    419 	idList<idMapBrushSide*> sides;
    420 	idMapBrushSide	*side;
    421 	idDict epairs;
    422 
    423 	do {
    424 		if ( src.CheckTokenString( "}" ) ) {
    425 			break;
    426 		}
    427 
    428 		side = new (TAG_IDLIB) idMapBrushSide();
    429 		sides.Append( side );
    430 
    431 		// read the three point plane definition
    432 		if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) ||
    433 			!src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) ||
    434 			!src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) {
    435 			src.Error( "idMapBrush::ParseQ3: unable to read brush side plane definition" );
    436 			sides.DeleteContents( true );
    437 			return NULL;
    438 		}
    439 
    440 		planepts[0] -= origin;
    441 		planepts[1] -= origin;
    442 		planepts[2] -= origin;
    443 
    444 		side->plane.FromPoints( planepts[0], planepts[1], planepts[2] );
    445 
    446 		// read the material
    447 		if ( !src.ReadTokenOnLine( &token ) ) {
    448 			src.Error( "idMapBrush::ParseQ3: unable to read brush side material" );
    449 			sides.DeleteContents( true );
    450 			return NULL;
    451 		}
    452 
    453 		// we have an implicit 'textures/' in the old format
    454 		side->material = "textures/" + token;
    455 
    456 		// read the texture shift, rotate and scale
    457 		shift[0] = src.ParseInt();
    458 		shift[1] = src.ParseInt();
    459 		rotate = src.ParseInt();
    460 		scale[0] = src.ParseFloat();
    461 		scale[1] = src.ParseFloat();
    462 		side->texMat[0] = idVec3( 0.03125f, 0.0f, 0.0f );
    463 		side->texMat[1] = idVec3( 0.0f, 0.03125f, 0.0f );
    464 		side->origin = origin;
    465 		
    466 		// Q2 allowed override of default flags and values, but we don't any more
    467 		if ( src.ReadTokenOnLine( &token ) ) {
    468 			if ( src.ReadTokenOnLine( &token ) ) {
    469 				if ( src.ReadTokenOnLine( &token ) ) {
    470 				}
    471 			}
    472 		}
    473 	} while( 1 );
    474 
    475 	idMapBrush *brush = new (TAG_IDLIB) idMapBrush();
    476 	for ( i = 0; i < sides.Num(); i++ ) {
    477 		brush->AddSide( sides[i] );
    478 	}
    479 
    480 	brush->epairs = epairs;
    481 
    482 	return brush;
    483 }
    484 
    485 /*
    486 ============
    487 idMapBrush::Write
    488 ============
    489 */
    490 bool idMapBrush::Write( idFile *fp, int primitiveNum, const idVec3 &origin ) const {
    491 	int i;
    492 	idMapBrushSide *side;
    493 
    494 	fp->WriteFloatString( "// primitive %d\n{\n brushDef3\n {\n", primitiveNum );
    495 
    496 	// write brush epairs
    497 	for ( i = 0; i < epairs.GetNumKeyVals(); i++) {
    498 		fp->WriteFloatString( "  \"%s\" \"%s\"\n", epairs.GetKeyVal(i)->GetKey().c_str(), epairs.GetKeyVal(i)->GetValue().c_str());
    499 	}
    500 
    501 	// write brush sides
    502 	for ( i = 0; i < GetNumSides(); i++ ) {
    503 		side = GetSide( i );
    504 		fp->WriteFloatString( "  ( %f %f %f %f ) ", side->plane[0], side->plane[1], side->plane[2], side->plane[3] );
    505 		fp->WriteFloatString( "( ( %f %f %f ) ( %f %f %f ) ) \"%s\" 0 0 0\n",
    506 							side->texMat[0][0], side->texMat[0][1], side->texMat[0][2],
    507 								side->texMat[1][0], side->texMat[1][1], side->texMat[1][2],
    508 									side->material.c_str() );
    509 	}
    510 
    511 	fp->WriteFloatString( " }\n}\n" );
    512 
    513 	return true;
    514 }
    515 
    516 /*
    517 ===============
    518 idMapBrush::GetGeometryCRC
    519 ===============
    520 */
    521 unsigned int idMapBrush::GetGeometryCRC() const {
    522 	int i, j;
    523 	idMapBrushSide *mapSide;
    524 	unsigned int crc;
    525 
    526 	crc = 0;
    527 	for ( i = 0; i < GetNumSides(); i++ ) {
    528 		mapSide = GetSide(i);
    529 		for ( j = 0; j < 4; j++ ) {
    530 			crc ^= FloatCRC( mapSide->GetPlane()[j] );
    531 		}
    532 		crc ^= StringCRC( mapSide->GetMaterial() );
    533 	}
    534 
    535 	return crc;
    536 }
    537 
    538 /*
    539 ================
    540 idMapEntity::Parse
    541 ================
    542 */
    543 idMapEntity *idMapEntity::Parse( idLexer &src, bool worldSpawn, float version ) {
    544 	idToken	token;
    545 	idMapEntity *mapEnt;
    546 	idMapPatch *mapPatch;
    547 	idMapBrush *mapBrush;
    548 	bool worldent;
    549 	idVec3 origin;
    550 	double v1, v2, v3;
    551 
    552 	if ( !src.ReadToken(&token) ) {
    553 		return NULL;
    554 	}
    555 
    556 	if ( token != "{" ) {
    557 		src.Error( "idMapEntity::Parse: { not found, found %s", token.c_str() );
    558 		return NULL;
    559 	}
    560 
    561 	mapEnt = new (TAG_IDLIB) idMapEntity();
    562 
    563 	if ( worldSpawn ) {
    564 		mapEnt->primitives.Resize( 1024, 256 );
    565 	}
    566 
    567 	origin.Zero();
    568 	worldent = false;
    569 	do {
    570 		if ( !src.ReadToken(&token) ) {
    571 			src.Error( "idMapEntity::Parse: EOF without closing brace" );
    572 			return NULL;
    573 		}
    574 		if ( token == "}" ) {
    575 			break;
    576 		}
    577 
    578 		if ( token == "{" ) {
    579 			// parse a brush or patch
    580 			if ( !src.ReadToken( &token ) ) {
    581 				src.Error( "idMapEntity::Parse: unexpected EOF" );
    582 				return NULL;
    583 			}
    584 
    585 			if ( worldent ) {
    586 				origin.Zero();
    587 			}
    588 
    589 			// if is it a brush: brush, brushDef, brushDef2, brushDef3
    590 			if ( token.Icmpn( "brush", 5 ) == 0 ) {
    591 				mapBrush = idMapBrush::Parse( src, origin, ( !token.Icmp( "brushDef2" ) || !token.Icmp( "brushDef3" ) ), version );
    592 				if ( !mapBrush ) {
    593 					return NULL;
    594 				}
    595 				mapEnt->AddPrimitive( mapBrush );
    596 			}
    597 			// if is it a patch: patchDef2, patchDef3
    598 			else if ( token.Icmpn( "patch", 5 ) == 0 ) {
    599 				mapPatch = idMapPatch::Parse( src, origin, !token.Icmp( "patchDef3" ), version );
    600 				if ( !mapPatch ) {
    601 					return NULL;
    602 				}
    603 				mapEnt->AddPrimitive( mapPatch );
    604 			}
    605 			// assume it's a brush in Q3 or older style
    606 			else {
    607 				src.UnreadToken( &token );
    608 				mapBrush = idMapBrush::ParseQ3( src, origin );
    609 				if ( !mapBrush ) {
    610 					return NULL;
    611 				}
    612 				mapEnt->AddPrimitive( mapBrush );
    613 			}
    614 		} else {
    615 			idStr key, value;
    616 
    617 			// parse a key / value pair
    618 			key = token;
    619 			src.ReadTokenOnLine( &token );
    620 			value = token;
    621 
    622 			// strip trailing spaces that sometimes get accidentally
    623 			// added in the editor
    624 			value.StripTrailingWhitespace();
    625 			key.StripTrailingWhitespace();
    626 
    627 			mapEnt->epairs.Set( key, value );
    628 
    629 			if ( !idStr::Icmp( key, "origin" ) ) {
    630 				// scanf into doubles, then assign, so it is idVec size independent
    631 				v1 = v2 = v3 = 0;
    632 				sscanf( value, "%lf %lf %lf", &v1, &v2, &v3 );
    633 				origin.x = v1;
    634 				origin.y = v2;
    635 				origin.z = v3;
    636 			}
    637 			else if ( !idStr::Icmp( key, "classname" ) && !idStr::Icmp( value, "worldspawn" ) ) {
    638 				worldent = true;
    639 			}
    640 		}
    641 	} while( 1 );
    642 
    643 	return mapEnt;
    644 }
    645 
    646 /*
    647 ============
    648 idMapEntity::Write
    649 ============
    650 */
    651 bool idMapEntity::Write( idFile *fp, int entityNum ) const {
    652 	int i;
    653 	idMapPrimitive *mapPrim;
    654 	idVec3 origin;
    655 
    656 	fp->WriteFloatString( "// entity %d\n{\n", entityNum );
    657 
    658 	// write entity epairs
    659 	for ( i = 0; i < epairs.GetNumKeyVals(); i++) {
    660 		fp->WriteFloatString( "\"%s\" \"%s\"\n", epairs.GetKeyVal(i)->GetKey().c_str(), epairs.GetKeyVal(i)->GetValue().c_str());
    661 	}
    662 
    663 	epairs.GetVector( "origin", "0 0 0", origin );
    664 
    665 	// write pritimives
    666 	for ( i = 0; i < GetNumPrimitives(); i++ ) {
    667 		mapPrim = GetPrimitive( i );
    668 
    669 		switch( mapPrim->GetType() ) {
    670 			case idMapPrimitive::TYPE_BRUSH:
    671 				static_cast<idMapBrush*>(mapPrim)->Write( fp, i, origin );
    672 				break;
    673 			case idMapPrimitive::TYPE_PATCH:
    674 				static_cast<idMapPatch*>(mapPrim)->Write( fp, i, origin );
    675 				break;
    676 		}
    677 	}
    678 
    679 	fp->WriteFloatString( "}\n" );
    680 
    681 	return true;
    682 }
    683 
    684 /*
    685 ===============
    686 idMapEntity::RemovePrimitiveData
    687 ===============
    688 */
    689 void idMapEntity::RemovePrimitiveData() {
    690 	primitives.DeleteContents(true);
    691 }
    692 
    693 /*
    694 ===============
    695 idMapEntity::GetGeometryCRC
    696 ===============
    697 */
    698 unsigned int idMapEntity::GetGeometryCRC() const {
    699 	int i;
    700 	unsigned int crc;
    701 	idMapPrimitive	*mapPrim;
    702 
    703 	crc = 0;
    704 	for ( i = 0; i < GetNumPrimitives(); i++ ) {
    705 		mapPrim = GetPrimitive( i );
    706 
    707 		switch( mapPrim->GetType() ) {
    708 			case idMapPrimitive::TYPE_BRUSH:
    709 				crc ^= static_cast<idMapBrush*>(mapPrim)->GetGeometryCRC();
    710 				break;
    711 			case idMapPrimitive::TYPE_PATCH:
    712 				crc ^= static_cast<idMapPatch*>(mapPrim)->GetGeometryCRC();
    713 				break;
    714 		}
    715 	}
    716 
    717 	return crc;
    718 }
    719 
    720 /*
    721 ===============
    722 idMapFile::Parse
    723 ===============
    724 */
    725 bool idMapFile::Parse( const char *filename, bool ignoreRegion, bool osPath ) {
    726 	// no string concatenation for epairs and allow path names for materials
    727 	idLexer src( LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
    728 	idToken token;
    729 	idStr fullName;
    730 	idMapEntity *mapEnt;
    731 	int i, j, k;
    732 
    733 	name = filename;
    734 	name.StripFileExtension();
    735 	fullName = name;
    736 	hasPrimitiveData = false;
    737 
    738 	if ( !ignoreRegion ) {
    739 		// try loading a .reg file first
    740 		fullName.SetFileExtension( "reg" );
    741 		src.LoadFile( fullName, osPath );
    742 	}
    743 
    744 	if ( !src.IsLoaded() ) {
    745 		// now try a .map file
    746 		fullName.SetFileExtension( "map" );
    747 		src.LoadFile( fullName, osPath );
    748 		if ( !src.IsLoaded() ) {
    749 			// didn't get anything at all
    750 			return false;
    751 		}
    752 	}
    753 
    754 	version = OLD_MAP_VERSION;
    755 	fileTime = src.GetFileTime();
    756 	entities.DeleteContents( true );
    757 
    758 	if ( src.CheckTokenString( "Version" ) ) {
    759 		src.ReadTokenOnLine( &token );
    760 		version = token.GetFloatValue();
    761 	}
    762 
    763 	while( 1 ) {
    764 		mapEnt = idMapEntity::Parse( src, ( entities.Num() == 0 ), version );
    765 		if ( !mapEnt ) {
    766 			break;
    767 		}
    768 		entities.Append( mapEnt );
    769 	}
    770 
    771 	SetGeometryCRC();
    772 
    773 	// if the map has a worldspawn
    774 	if ( entities.Num() ) {
    775 
    776 		// "removeEntities" "classname" can be set in the worldspawn to remove all entities with the given classname
    777 		const idKeyValue *removeEntities = entities[0]->epairs.MatchPrefix( "removeEntities", NULL );
    778 		while ( removeEntities ) {
    779 			RemoveEntities( removeEntities->GetValue() );
    780 			removeEntities = entities[0]->epairs.MatchPrefix( "removeEntities", removeEntities );
    781 		}
    782 
    783 		// "overrideMaterial" "material" can be set in the worldspawn to reset all materials
    784 		idStr material;
    785 		if ( entities[0]->epairs.GetString( "overrideMaterial", "", material ) ) {
    786 			for ( i = 0; i < entities.Num(); i++ ) {
    787 				mapEnt = entities[i];
    788 				for ( j = 0; j < mapEnt->GetNumPrimitives(); j++ ) {
    789 					idMapPrimitive *mapPrimitive = mapEnt->GetPrimitive( j );
    790 					switch( mapPrimitive->GetType() ) {
    791 						case idMapPrimitive::TYPE_BRUSH: {
    792 							idMapBrush *mapBrush = static_cast<idMapBrush *>(mapPrimitive);
    793 							for ( k = 0; k < mapBrush->GetNumSides(); k++ ) {
    794 								mapBrush->GetSide( k )->SetMaterial( material );
    795 							}
    796 							break;
    797 						}
    798 						case idMapPrimitive::TYPE_PATCH: {
    799 							static_cast<idMapPatch *>(mapPrimitive)->SetMaterial( material );
    800 							break;
    801 						}
    802 					}
    803 				}
    804 			}
    805 		}
    806 
    807 		// force all entities to have a name key/value pair
    808 		if ( entities[0]->epairs.GetBool( "forceEntityNames" ) ) {
    809 			for ( i = 1; i < entities.Num(); i++ ) {
    810 				mapEnt = entities[i];
    811 				if ( !mapEnt->epairs.FindKey( "name" ) ) {
    812 					mapEnt->epairs.Set( "name", va( "%s%d", mapEnt->epairs.GetString( "classname", "forcedName" ), i ) );
    813 				}
    814 			}
    815 		}
    816 
    817 		// move the primitives of any func_group entities to the worldspawn
    818 		if ( entities[0]->epairs.GetBool( "moveFuncGroups" ) ) {
    819 			for ( i = 1; i < entities.Num(); i++ ) {
    820 				mapEnt = entities[i];
    821 				if ( idStr::Icmp( mapEnt->epairs.GetString( "classname" ), "func_group" ) == 0 ) {
    822 					entities[0]->primitives.Append( mapEnt->primitives );
    823 					mapEnt->primitives.Clear();
    824 				}
    825 			}
    826 		}
    827 	}
    828 
    829 	hasPrimitiveData = true;
    830 	return true;
    831 }
    832 
    833 /*
    834 ============
    835 idMapFile::Write
    836 ============
    837 */
    838 bool idMapFile::Write( const char *fileName, const char *ext, bool fromBasePath ) {
    839 	int i;
    840 	idStr qpath;
    841 	idFile *fp;
    842 
    843 	qpath = fileName;
    844 	qpath.SetFileExtension( ext );
    845 
    846 	idLib::common->Printf( "writing %s...\n", qpath.c_str() );
    847 
    848 	if ( fromBasePath ) {
    849 		fp = idLib::fileSystem->OpenFileWrite( qpath, "fs_basepath" );
    850 	}
    851 	else {
    852 		fp = idLib::fileSystem->OpenExplicitFileWrite( qpath );
    853 	}
    854 
    855 	if ( !fp ) {
    856 		idLib::common->Warning( "Couldn't open %s\n", qpath.c_str() );
    857 		return false;
    858 	}
    859 
    860 	fp->WriteFloatString( "Version %f\n", (float) CURRENT_MAP_VERSION );
    861 
    862 	for ( i = 0; i < entities.Num(); i++ ) {
    863 		entities[i]->Write( fp, i );
    864 	}
    865 
    866 	idLib::fileSystem->CloseFile( fp );
    867 
    868 	return true;
    869 }
    870 
    871 /*
    872 ===============
    873 idMapFile::SetGeometryCRC
    874 ===============
    875 */
    876 void idMapFile::SetGeometryCRC() {
    877 	int i;
    878 
    879 	geometryCRC = 0;
    880 	for ( i = 0; i < entities.Num(); i++ ) {
    881 		geometryCRC ^= entities[i]->GetGeometryCRC();
    882 	}
    883 }
    884 
    885 /*
    886 ===============
    887 idMapFile::AddEntity
    888 ===============
    889 */
    890 int idMapFile::AddEntity( idMapEntity *mapEnt ) {
    891 	int ret = entities.Append( mapEnt );
    892 	return ret;
    893 }
    894 
    895 /*
    896 ===============
    897 idMapFile::FindEntity
    898 ===============
    899 */
    900 idMapEntity *idMapFile::FindEntity( const char *name ) {
    901 	for ( int i = 0; i < entities.Num(); i++ ) {
    902 		idMapEntity *ent = entities[i];
    903 		if ( idStr::Icmp( ent->epairs.GetString( "name" ), name ) == 0 ) {
    904 			return ent;
    905 		}
    906 	}
    907 	return NULL;
    908 }
    909 
    910 /*
    911 ===============
    912 idMapFile::RemoveEntity
    913 ===============
    914 */
    915 void idMapFile::RemoveEntity( idMapEntity *mapEnt ) {
    916 	entities.Remove( mapEnt );
    917 	delete mapEnt;
    918 }
    919 
    920 /*
    921 ===============
    922 idMapFile::RemoveEntity
    923 ===============
    924 */
    925 void idMapFile::RemoveEntities( const char *classname ) {
    926 	for ( int i = 0; i < entities.Num(); i++ ) {
    927 		idMapEntity *ent = entities[i];
    928 		if ( idStr::Icmp( ent->epairs.GetString( "classname" ), classname ) == 0 ) {
    929 			delete entities[i];
    930 			entities.RemoveIndex( i );
    931 			i--;
    932 		}
    933 	}
    934 }
    935 
    936 /*
    937 ===============
    938 idMapFile::RemoveAllEntities
    939 ===============
    940 */
    941 void idMapFile::RemoveAllEntities() {
    942 	entities.DeleteContents( true );
    943 	hasPrimitiveData = false;
    944 }
    945 
    946 /*
    947 ===============
    948 idMapFile::RemovePrimitiveData
    949 ===============
    950 */
    951 void idMapFile::RemovePrimitiveData() {
    952 	for ( int i = 0; i < entities.Num(); i++ ) {
    953 		idMapEntity *ent = entities[i];
    954 		ent->RemovePrimitiveData();
    955 	}
    956 	hasPrimitiveData = false;
    957 }
    958 
    959 /*
    960 ===============
    961 idMapFile::NeedsReload
    962 ===============
    963 */
    964 bool idMapFile::NeedsReload() {
    965 	if ( name.Length() ) {
    966 		ID_TIME_T time = FILE_NOT_FOUND_TIMESTAMP;
    967 		if ( idLib::fileSystem->ReadFile( name, NULL, &time ) > 0 ) {
    968 			return ( time > fileTime );
    969 		}
    970 	}
    971 	return true;
    972 }