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