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