]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/compilers/aas/AASFile.cpp
hello world
[icculus/iodoom3.git] / neo / tools / compilers / aas / AASFile.cpp
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 "../../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "AASFile.h"
33 #include "AASFile_local.h"
34
35
36 /*
37 ===============================================================================
38
39         idReachability
40
41 ===============================================================================
42 */
43
44 /*
45 ================
46 Reachability_Write
47 ================
48 */
49 bool Reachability_Write( idFile *fp, idReachability *reach ) {
50         fp->WriteFloatString( "\t\t%d %d (%f %f %f) (%f %f %f) %d %d",
51                                 (int) reach->travelType, (int) reach->toAreaNum, reach->start.x, reach->start.y, reach->start.z,
52                                 reach->end.x, reach->end.y, reach->end.z, reach->edgeNum, (int) reach->travelTime );
53         return true;
54 }
55
56 /*
57 ================
58 Reachability_Read
59 ================
60 */
61 bool Reachability_Read( idLexer &src, idReachability *reach ) {
62         reach->travelType = src.ParseInt();
63         reach->toAreaNum = src.ParseInt();
64         src.Parse1DMatrix( 3, reach->start.ToFloatPtr() );
65         src.Parse1DMatrix( 3, reach->end.ToFloatPtr() );
66         reach->edgeNum = src.ParseInt();
67         reach->travelTime = src.ParseInt();
68         return true;
69 }
70
71 /*
72 ================
73 idReachability::CopyBase
74 ================
75 */
76 void idReachability::CopyBase( idReachability &reach ) {
77         travelType = reach.travelType;
78         toAreaNum = reach.toAreaNum;
79         start = reach.start;
80         end = reach.end;
81         edgeNum = reach.edgeNum;
82         travelTime = reach.travelTime;
83 }
84
85
86 /*
87 ===============================================================================
88
89         idReachability_Special
90
91 ===============================================================================
92 */
93
94 /*
95 ================
96 Reachability_Special_Write
97 ================
98 */
99 bool Reachability_Special_Write( idFile *fp, idReachability_Special *reach ) {
100         int i;
101         const idKeyValue *keyValue;
102
103         fp->WriteFloatString( "\n\t\t{\n" );
104         for ( i = 0; i < reach->dict.GetNumKeyVals(); i++ ) {
105                 keyValue = reach->dict.GetKeyVal( i );
106                 fp->WriteFloatString( "\t\t\t\"%s\" \"%s\"\n", keyValue->GetKey().c_str(), keyValue->GetValue().c_str() );
107         }
108         fp->WriteFloatString( "\t\t}\n" );
109
110         return true;
111 }
112
113 /*
114 ================
115 Reachability_Special_Read
116 ================
117 */
118 bool Reachability_Special_Read( idLexer &src, idReachability_Special *reach ) {
119         idToken key, value;
120
121         src.ExpectTokenString( "{" );
122         while( src.ReadToken( &key ) ) {
123                 if ( key == "}" ) {
124                         return true;
125                 }
126                 src.ExpectTokenType( TT_STRING, 0, &value );
127                 reach->dict.Set( key, value );
128         }
129         return false;
130 }
131
132 /*
133 ===============================================================================
134
135         idAASSettings
136
137 ===============================================================================
138 */
139
140 /*
141 ============
142 idAASSettings::idAASSettings
143 ============
144 */
145 idAASSettings::idAASSettings( void ) {
146         numBoundingBoxes = 1;
147         boundingBoxes[0] = idBounds( idVec3( -16, -16, 0 ), idVec3( 16, 16, 72 ) );
148         usePatches = false;
149         writeBrushMap = false;
150         playerFlood = false;
151         noOptimize = false;
152         allowSwimReachabilities = false;
153         allowFlyReachabilities = false;
154         fileExtension = "aas48";
155         // physics settings
156         gravity = idVec3( 0, 0, -1066 );
157         gravityDir = gravity;
158         gravityValue = gravityDir.Normalize();
159         invGravityDir = -gravityDir;
160         maxStepHeight = 14.0f;
161         maxBarrierHeight = 32.0f;
162         maxWaterJumpHeight = 20.0f;
163         maxFallHeight = 64.0f;
164         minFloorCos = 0.7f;
165         // fixed travel times
166         tt_barrierJump = 100;
167         tt_startCrouching = 100;
168         tt_waterJump = 100;
169         tt_startWalkOffLedge = 100;
170 }
171
172 /*
173 ============
174 idAASSettings::ParseBool
175 ============
176 */
177 bool idAASSettings::ParseBool( idLexer &src, bool &b ) {
178         if ( !src.ExpectTokenString( "=" ) ) {
179                 return false;
180         }
181         b = src.ParseBool();
182         return true;
183 }
184
185 /*
186 ============
187 idAASSettings::ParseInt
188 ============
189 */
190 bool idAASSettings::ParseInt( idLexer &src, int &i ) {
191         if ( !src.ExpectTokenString( "=" ) ) {
192                 return false;
193         }
194         i = src.ParseInt();
195         return true;
196 }
197
198 /*
199 ============
200 idAASSettings::ParseFloat
201 ============
202 */
203 bool idAASSettings::ParseFloat( idLexer &src, float &f ) {
204         if ( !src.ExpectTokenString( "=" ) ) {
205                 return false;
206         }
207         f = src.ParseFloat();
208         return true;
209 }
210
211 /*
212 ============
213 idAASSettings::ParseVector
214 ============
215 */
216 bool idAASSettings::ParseVector( idLexer &src, idVec3 &vec ) {
217         if ( !src.ExpectTokenString( "=" ) ) {
218                 return false;
219         }
220         return ( src.Parse1DMatrix( 3, vec.ToFloatPtr() ) != 0 );
221 }
222
223 /*
224 ============
225 idAASSettings::ParseBBoxes
226 ============
227 */
228 bool idAASSettings::ParseBBoxes( idLexer &src ) {
229         idToken token;
230         idBounds bounds;
231
232         numBoundingBoxes = 0;
233
234         if ( !src.ExpectTokenString( "{" ) ) {
235                 return false;
236         }
237         while( src.ReadToken( &token ) ) {
238                 if ( token == "}" ) {
239                         return true;
240                 }
241                 src.UnreadToken( &token );
242                 src.Parse1DMatrix( 3, bounds[0].ToFloatPtr() );
243                 if ( !src.ExpectTokenString( "-" ) ) {
244                         return false;
245                 }
246                 src.Parse1DMatrix( 3, bounds[1].ToFloatPtr() );
247
248                 boundingBoxes[numBoundingBoxes++] = bounds;
249         }
250         return false;
251 }
252
253 /*
254 ============
255 idAASSettings::FromParser
256 ============
257 */
258 bool idAASSettings::FromParser( idLexer &src ) {
259         idToken token;
260
261         if ( !src.ExpectTokenString( "{" ) ) {
262                 return false;
263         }
264
265         // parse the file
266         while ( 1 ) {
267                 if ( !src.ReadToken( &token ) ) {
268                         break;
269                 }
270
271                 if ( token == "}" ) {
272                         break;
273                 }
274
275                 if ( token == "bboxes" ) {
276                         if ( !ParseBBoxes( src ) ) { return false; }
277                 }
278                 else if ( token == "usePatches" ) {
279                         if ( !ParseBool( src, usePatches ) ) { return false; }
280                 }
281                 else if ( token == "writeBrushMap" ) {
282                         if ( !ParseBool( src, writeBrushMap ) ) { return false; }
283                 }
284                 else if ( token == "playerFlood" ) {
285                         if ( !ParseBool( src, playerFlood ) ) { return false; }
286                 }
287                 else if ( token == "allowSwimReachabilities" ) {
288                         if ( !ParseBool( src, allowSwimReachabilities ) ) { return false; }
289                 }
290                 else if ( token == "allowFlyReachabilities" ) {
291                         if ( !ParseBool( src, allowFlyReachabilities ) ) { return false; }
292                 }
293                 else if ( token == "fileExtension" ) {
294                         src.ExpectTokenString( "=" );
295                         src.ExpectTokenType( TT_STRING, 0, &token );
296                         fileExtension = token;
297                 }
298                 else if ( token == "gravity" ) {
299                         ParseVector( src, gravity );
300                         gravityDir = gravity;
301                         gravityValue = gravityDir.Normalize();
302                         invGravityDir = -gravityDir;
303                 }
304                 else if ( token == "maxStepHeight" ) {
305                         if ( !ParseFloat( src, maxStepHeight ) ) { return false; }
306                 }
307                 else if ( token == "maxBarrierHeight" ) {
308                         if ( !ParseFloat( src, maxBarrierHeight ) ) { return false; }
309                 }
310                 else if ( token == "maxWaterJumpHeight" ) {
311                         if ( !ParseFloat( src, maxWaterJumpHeight ) ) { return false; }
312                 }
313                 else if ( token == "maxFallHeight" ) {
314                         if ( !ParseFloat( src, maxFallHeight ) ) { return false; }
315                 }
316                 else if ( token == "minFloorCos" ) {
317                         if ( !ParseFloat( src, minFloorCos ) ) { return false; }
318                 }
319                 else if ( token == "tt_barrierJump" ) {
320                         if ( !ParseInt( src, tt_barrierJump ) ) { return false; }
321                 }
322                 else if ( token == "tt_startCrouching" ) {
323                         if ( !ParseInt( src, tt_startCrouching ) ) { return false; }
324                 }
325                 else if ( token == "tt_waterJump" ) {
326                         if ( !ParseInt( src, tt_waterJump ) ) { return false; }
327                 }
328                 else if ( token == "tt_startWalkOffLedge" ) {
329                         if ( !ParseInt( src, tt_startWalkOffLedge ) ) { return false; }
330                 }
331                 else {
332                         src.Error( "invalid token '%s'", token.c_str() );
333                 }
334         }
335
336         if ( numBoundingBoxes <= 0 ) {
337                 src.Error( "no valid bounding box" );
338         }
339
340         return true;
341 }
342
343 /*
344 ============
345 idAASSettings::FromFile
346 ============
347 */
348 bool idAASSettings::FromFile( const idStr &fileName ) {
349         idLexer src( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOSTRINGCONCAT );
350         idStr name;
351
352         name = fileName;
353
354         common->Printf( "loading %s\n", name.c_str() );
355
356         if ( !src.LoadFile( name ) ) {
357                 common->Error( "WARNING: couldn't load %s\n", name.c_str() );
358                 return false;
359         }
360
361         if ( !src.ExpectTokenString( "settings" ) ) {
362                 common->Error( "%s is not a settings file", name.c_str() );
363                 return false;
364         }
365
366         if ( !FromParser( src ) ) {
367                 common->Error( "failed to parse %s", name.c_str() );
368                 return false;
369         }
370
371         return true;
372 }
373
374 /*
375 ============
376 idAASSettings::FromDict
377 ============
378 */
379 bool idAASSettings::FromDict( const char *name, const idDict *dict ) {
380         idBounds bounds;
381
382         if ( !dict->GetVector( "mins", "0 0 0", bounds[ 0 ] ) ) {
383                 common->Error( "Missing 'mins' in entityDef '%s'", name );
384         }
385         if ( !dict->GetVector( "maxs", "0 0 0", bounds[ 1 ] ) ) {
386                 common->Error( "Missing 'maxs' in entityDef '%s'", name );
387         }
388
389         numBoundingBoxes = 1;
390         boundingBoxes[0] = bounds;
391
392         if ( !dict->GetBool( "usePatches", "0", usePatches ) ) {
393                 common->Error( "Missing 'usePatches' in entityDef '%s'", name );
394         }
395
396         if ( !dict->GetBool( "writeBrushMap", "0", writeBrushMap ) ) {
397                 common->Error( "Missing 'writeBrushMap' in entityDef '%s'", name );
398         }
399
400         if ( !dict->GetBool( "playerFlood", "0", playerFlood ) ) {
401                 common->Error( "Missing 'playerFlood' in entityDef '%s'", name );
402         }
403
404         if ( !dict->GetBool( "allowSwimReachabilities", "0", allowSwimReachabilities ) ) {
405                 common->Error( "Missing 'allowSwimReachabilities' in entityDef '%s'", name );
406         }
407
408         if ( !dict->GetBool( "allowFlyReachabilities", "0", allowFlyReachabilities ) ) {
409                 common->Error( "Missing 'allowFlyReachabilities' in entityDef '%s'", name );
410         }
411
412         if ( !dict->GetString( "fileExtension", "", fileExtension ) ) {
413                 common->Error( "Missing 'fileExtension' in entityDef '%s'", name );
414         }
415
416         if ( !dict->GetVector( "gravity", "0 0 -1066", gravity ) ) {
417                 common->Error( "Missing 'gravity' in entityDef '%s'", name );
418         }
419         gravityDir = gravity;
420         gravityValue = gravityDir.Normalize();
421         invGravityDir = -gravityDir;
422
423         if ( !dict->GetFloat( "maxStepHeight", "0", maxStepHeight ) ) {
424                 common->Error( "Missing 'maxStepHeight' in entityDef '%s'", name );
425         }
426
427         if ( !dict->GetFloat( "maxBarrierHeight", "0", maxBarrierHeight ) ) {
428                 common->Error( "Missing 'maxBarrierHeight' in entityDef '%s'", name );
429         }
430
431         if ( !dict->GetFloat( "maxWaterJumpHeight", "0", maxWaterJumpHeight ) ) {
432                 common->Error( "Missing 'maxWaterJumpHeight' in entityDef '%s'", name );
433         }
434
435         if ( !dict->GetFloat( "maxFallHeight", "0", maxFallHeight ) ) {
436                 common->Error( "Missing 'maxFallHeight' in entityDef '%s'", name );
437         }
438
439         if ( !dict->GetFloat( "minFloorCos", "0", minFloorCos ) ) {
440                 common->Error( "Missing 'minFloorCos' in entityDef '%s'", name );
441         }
442
443         if ( !dict->GetInt( "tt_barrierJump", "0", tt_barrierJump ) ) {
444                 common->Error( "Missing 'tt_barrierJump' in entityDef '%s'", name );
445         }
446
447         if ( !dict->GetInt( "tt_startCrouching", "0", tt_startCrouching ) ) {
448                 common->Error( "Missing 'tt_startCrouching' in entityDef '%s'", name );
449         }
450
451         if ( !dict->GetInt( "tt_waterJump", "0", tt_waterJump ) ) {
452                 common->Error( "Missing 'tt_waterJump' in entityDef '%s'", name );
453         }
454
455         if ( !dict->GetInt( "tt_startWalkOffLedge", "0", tt_startWalkOffLedge ) ) {
456                 common->Error( "Missing 'tt_startWalkOffLedge' in entityDef '%s'", name );
457         }
458
459         return true;
460 }
461
462
463 /*
464 ============
465 idAASSettings::WriteToFile
466 ============
467 */
468 bool idAASSettings::WriteToFile( idFile *fp ) const {
469         int i;
470
471         fp->WriteFloatString( "{\n" );
472         fp->WriteFloatString( "\tbboxes\n\t{\n" );
473         for ( i = 0; i < numBoundingBoxes; i++ ) {
474                 fp->WriteFloatString( "\t\t(%f %f %f)-(%f %f %f)\n", boundingBoxes[i][0].x, boundingBoxes[i][0].y,
475                                                 boundingBoxes[i][0].z, boundingBoxes[i][1].x, boundingBoxes[i][1].y, boundingBoxes[i][1].z );
476         }
477         fp->WriteFloatString( "\t}\n" );
478         fp->WriteFloatString( "\tusePatches = %d\n", usePatches );
479         fp->WriteFloatString( "\twriteBrushMap = %d\n", writeBrushMap );
480         fp->WriteFloatString( "\tplayerFlood = %d\n", playerFlood );
481         fp->WriteFloatString( "\tallowSwimReachabilities = %d\n", allowSwimReachabilities );
482         fp->WriteFloatString( "\tallowFlyReachabilities = %d\n", allowFlyReachabilities );
483         fp->WriteFloatString( "\tfileExtension = \"%s\"\n", fileExtension.c_str() );
484         fp->WriteFloatString( "\tgravity = (%f %f %f)\n", gravity.x, gravity.y, gravity.z );
485         fp->WriteFloatString( "\tmaxStepHeight = %f\n", maxStepHeight );
486         fp->WriteFloatString( "\tmaxBarrierHeight = %f\n", maxBarrierHeight );
487         fp->WriteFloatString( "\tmaxWaterJumpHeight = %f\n", maxWaterJumpHeight );
488         fp->WriteFloatString( "\tmaxFallHeight = %f\n", maxFallHeight );
489         fp->WriteFloatString( "\tminFloorCos = %f\n", minFloorCos );
490         fp->WriteFloatString( "\ttt_barrierJump = %d\n", tt_barrierJump );
491         fp->WriteFloatString( "\ttt_startCrouching = %d\n", tt_startCrouching );
492         fp->WriteFloatString( "\ttt_waterJump = %d\n", tt_waterJump );
493         fp->WriteFloatString( "\ttt_startWalkOffLedge = %d\n", tt_startWalkOffLedge );
494         fp->WriteFloatString( "}\n" );
495         return true;
496 }
497
498 /*
499 ============
500 idAASSettings::ValidForBounds
501 ============
502 */
503 bool idAASSettings::ValidForBounds( const idBounds &bounds ) const {
504         int i;
505
506         for ( i = 0; i < 3; i++ ) {
507                 if ( bounds[0][i] < boundingBoxes[0][0][i] ) {
508                         return false;
509                 }
510                 if ( bounds[1][i] > boundingBoxes[0][1][i] ) {
511                         return false;
512                 }
513         }
514         return true;
515 }
516
517 /*
518 ============
519 idAASSettings::ValidEntity
520 ============
521 */
522 bool idAASSettings::ValidEntity( const char *classname ) const {
523         idStr                   use_aas;
524         idVec3                  size;
525         idBounds                bounds;
526
527         if ( playerFlood ) {
528                 if ( !strcmp( classname, "info_player_start" ) || !strcmp( classname , "info_player_deathmatch" ) || !strcmp( classname, "func_teleporter" ) ) {
529                         return true;
530                 }
531         }
532
533         const idDeclEntityDef *decl = static_cast<const idDeclEntityDef *>( declManager->FindType( DECL_ENTITYDEF, classname, false ) );
534         if ( decl && decl->dict.GetString( "use_aas", NULL, use_aas ) && !fileExtension.Icmp( use_aas ) ) {
535                 if ( decl->dict.GetVector( "mins", NULL, bounds[0] ) ) {
536                         decl->dict.GetVector( "maxs", NULL, bounds[1] );
537                 } else if ( decl->dict.GetVector( "size", NULL, size ) ) {
538                         bounds[ 0 ].Set( size.x * -0.5f, size.y * -0.5f, 0.0f );
539                         bounds[ 1 ].Set( size.x * 0.5f, size.y * 0.5f, size.z );
540                 }
541
542                 if ( !ValidForBounds( bounds ) ) {
543                         common->Error( "%s cannot use %s\n", classname, fileExtension.c_str() );
544                 }
545
546                 return true;
547         }
548
549         return false;
550 }
551
552
553 /*
554 ===============================================================================
555
556         idAASFileLocal
557
558 ===============================================================================
559 */
560
561 #define AAS_LIST_GRANULARITY    1024
562 #define AAS_INDEX_GRANULARITY   4096
563 #define AAS_PLANE_GRANULARITY   4096
564 #define AAS_VERTEX_GRANULARITY  4096
565 #define AAS_EDGE_GRANULARITY    4096
566
567 /*
568 ================
569 idAASFileLocal::idAASFileLocal
570 ================
571 */
572 idAASFileLocal::idAASFileLocal( void ) {
573         planeList.SetGranularity( AAS_PLANE_GRANULARITY );
574         vertices.SetGranularity( AAS_VERTEX_GRANULARITY );
575         edges.SetGranularity( AAS_EDGE_GRANULARITY );
576         edgeIndex.SetGranularity( AAS_INDEX_GRANULARITY );
577         faces.SetGranularity( AAS_LIST_GRANULARITY );
578         faceIndex.SetGranularity( AAS_INDEX_GRANULARITY );
579         areas.SetGranularity( AAS_LIST_GRANULARITY );
580         nodes.SetGranularity( AAS_LIST_GRANULARITY );
581         portals.SetGranularity( AAS_LIST_GRANULARITY );
582         portalIndex.SetGranularity( AAS_INDEX_GRANULARITY );
583         clusters.SetGranularity( AAS_LIST_GRANULARITY );
584 }
585
586 /*
587 ================
588 idAASFileLocal::~idAASFileLocal
589 ================
590 */
591 idAASFileLocal::~idAASFileLocal( void ) {
592         int i;
593         idReachability *reach, *next;
594
595         for ( i = 0; i < areas.Num(); i++ ) {
596                 for ( reach = areas[i].reach; reach; reach = next ) {
597                         next = reach->next;
598                         delete reach;
599                 }
600         }
601 }
602
603 /*
604 ================
605 idAASFileLocal::Clear
606 ================
607 */
608 void idAASFileLocal::Clear( void ) {
609         planeList.Clear();
610         vertices.Clear();
611         edges.Clear();
612         edgeIndex.Clear();
613         faces.Clear();
614         faceIndex.Clear();
615         areas.Clear();
616         nodes.Clear();
617         portals.Clear();
618         portalIndex.Clear();
619         clusters.Clear();
620 }
621
622 /*
623 ================
624 idAASFileLocal::Write
625 ================
626 */
627 bool idAASFileLocal::Write( const idStr &fileName, unsigned int mapFileCRC ) {
628         int i, num;
629         idFile *aasFile;
630         idReachability *reach;
631
632         common->Printf( "[Write AAS]\n" );
633         common->Printf( "writing %s\n", fileName.c_str() );
634
635         name = fileName;
636         crc = mapFileCRC;
637
638         aasFile = fileSystem->OpenFileWrite( fileName, "fs_devpath" );
639         if ( !aasFile ) {
640                 common->Error( "Error opening %s", fileName.c_str() );
641                 return false;
642         }
643
644         aasFile->WriteFloatString( "%s \"%s\"\n\n", AAS_FILEID, AAS_FILEVERSION );
645         aasFile->WriteFloatString( "%u\n\n", mapFileCRC );
646
647         // write out the settings
648         aasFile->WriteFloatString( "settings\n" );
649         settings.WriteToFile( aasFile );
650
651         // write out planes
652         aasFile->WriteFloatString( "planes %d {\n", planeList.Num() );
653         for ( i = 0; i < planeList.Num(); i++ ) {
654                 aasFile->WriteFloatString( "\t%d ( %f %f %f %f )\n", i,
655                                 planeList[i].Normal().x, planeList[i].Normal().y, planeList[i].Normal().z, planeList[i].Dist() );
656         }
657         aasFile->WriteFloatString( "}\n" );
658
659         // write out vertices
660         aasFile->WriteFloatString( "vertices %d {\n", vertices.Num() );
661         for ( i = 0; i < vertices.Num(); i++ ) {
662                 aasFile->WriteFloatString( "\t%d ( %f %f %f )\n", i, vertices[i].x, vertices[i].y, vertices[i].z );
663         }
664         aasFile->WriteFloatString( "}\n" );
665
666         // write out edges
667         aasFile->WriteFloatString( "edges %d {\n", edges.Num() );
668         for ( i = 0; i < edges.Num(); i++ ) {
669                 aasFile->WriteFloatString( "\t%d ( %d %d )\n", i, edges[i].vertexNum[0], edges[i].vertexNum[1] );
670         }
671         aasFile->WriteFloatString( "}\n" );
672
673         // write out edgeIndex
674         aasFile->WriteFloatString( "edgeIndex %d {\n", edgeIndex.Num() );
675         for ( i = 0; i < edgeIndex.Num(); i++ ) {
676                 aasFile->WriteFloatString( "\t%d ( %d )\n", i, edgeIndex[i] );
677         }
678         aasFile->WriteFloatString( "}\n" );
679
680         // write out faces
681         aasFile->WriteFloatString( "faces %d {\n", faces.Num() );
682         for ( i = 0; i < faces.Num(); i++ ) {
683                 aasFile->WriteFloatString( "\t%d ( %d %d %d %d %d %d )\n", i, faces[i].planeNum, faces[i].flags,
684                                                 faces[i].areas[0], faces[i].areas[1], faces[i].firstEdge, faces[i].numEdges );
685         }
686         aasFile->WriteFloatString( "}\n" );
687
688         // write out faceIndex
689         aasFile->WriteFloatString( "faceIndex %d {\n", faceIndex.Num() );
690         for ( i = 0; i < faceIndex.Num(); i++ ) {
691                 aasFile->WriteFloatString( "\t%d ( %d )\n", i, faceIndex[i] );
692         }
693         aasFile->WriteFloatString( "}\n" );
694
695         // write out areas
696         aasFile->WriteFloatString( "areas %d {\n", areas.Num() );
697         for ( i = 0; i < areas.Num(); i++ ) {
698                 for ( num = 0, reach = areas[i].reach; reach; reach = reach->next ) {
699                         num++;
700                 }
701                 aasFile->WriteFloatString( "\t%d ( %d %d %d %d %d %d ) %d {\n", i, areas[i].flags, areas[i].contents,
702                                                 areas[i].firstFace, areas[i].numFaces, areas[i].cluster, areas[i].clusterAreaNum, num );
703                 for ( reach = areas[i].reach; reach; reach = reach->next ) {
704                         Reachability_Write( aasFile, reach );
705                         switch( reach->travelType ) {
706                                 case TFL_SPECIAL:
707                                         Reachability_Special_Write( aasFile, static_cast<idReachability_Special *>(reach) );
708                                         break;
709                         }
710                         aasFile->WriteFloatString( "\n" );
711                 }
712                 aasFile->WriteFloatString( "\t}\n" );
713         }
714         aasFile->WriteFloatString( "}\n" );
715
716         // write out nodes
717         aasFile->WriteFloatString( "nodes %d {\n", nodes.Num() );
718         for ( i = 0; i < nodes.Num(); i++ ) {
719                 aasFile->WriteFloatString( "\t%d ( %d %d %d )\n", i, nodes[i].planeNum, nodes[i].children[0], nodes[i].children[1] );
720         }
721         aasFile->WriteFloatString( "}\n" );
722
723         // write out portals
724         aasFile->WriteFloatString( "portals %d {\n", portals.Num() );
725         for ( i = 0; i < portals.Num(); i++ ) {
726                 aasFile->WriteFloatString( "\t%d ( %d %d %d %d %d )\n", i, portals[i].areaNum, portals[i].clusters[0],
727                                                 portals[i].clusters[1], portals[i].clusterAreaNum[0], portals[i].clusterAreaNum[1] );
728         }
729         aasFile->WriteFloatString( "}\n" );
730
731         // write out portalIndex
732         aasFile->WriteFloatString( "portalIndex %d {\n", portalIndex.Num() );
733         for ( i = 0; i < portalIndex.Num(); i++ ) {
734                 aasFile->WriteFloatString( "\t%d ( %d )\n", i, portalIndex[i] );
735         }
736         aasFile->WriteFloatString( "}\n" );
737
738         // write out clusters
739         aasFile->WriteFloatString( "clusters %d {\n", clusters.Num() );
740         for ( i = 0; i < clusters.Num(); i++ ) {
741                 aasFile->WriteFloatString( "\t%d ( %d %d %d %d )\n", i, clusters[i].numAreas, clusters[i].numReachableAreas,
742                                                         clusters[i].firstPortal, clusters[i].numPortals );
743         }
744         aasFile->WriteFloatString( "}\n" );
745
746         // close file
747         fileSystem->CloseFile( aasFile );
748
749         common->Printf( "done.\n" );
750
751         return true;
752 }
753
754 /*
755 ================
756 idAASFileLocal::ParseIndex
757 ================
758 */
759 bool idAASFileLocal::ParseIndex( idLexer &src, idList<aasIndex_t> &indexes ) {
760         int numIndexes, i;
761         aasIndex_t index;
762
763         numIndexes = src.ParseInt();
764         indexes.Resize( numIndexes );
765         if ( !src.ExpectTokenString( "{" ) ) {
766                 return false;
767         }
768         for ( i = 0; i < numIndexes; i++ ) {
769                 src.ParseInt();
770                 src.ExpectTokenString( "(" );
771                 index = src.ParseInt();
772                 src.ExpectTokenString( ")" );
773                 indexes.Append( index );
774         }
775         if ( !src.ExpectTokenString( "}" ) ) {
776                 return false;
777         }
778         return true;
779 }
780
781 /*
782 ================
783 idAASFileLocal::ParsePlanes
784 ================
785 */
786 bool idAASFileLocal::ParsePlanes( idLexer &src ) {
787         int numPlanes, i;
788         idPlane plane;
789         idVec4 vec;
790
791         numPlanes = src.ParseInt();
792         planeList.Resize( numPlanes );
793         if ( !src.ExpectTokenString( "{" ) ) {
794                 return false;
795         }
796         for ( i = 0; i < numPlanes; i++ ) {
797                 src.ParseInt();
798                 if ( !src.Parse1DMatrix( 4, vec.ToFloatPtr() ) ) {
799                         return false;
800                 }
801                 plane.SetNormal( vec.ToVec3() );
802                 plane.SetDist( vec[3] );
803                 planeList.Append( plane );
804         }
805         if ( !src.ExpectTokenString( "}" ) ) {
806                 return false;
807         }
808         return true;
809 }
810
811 /*
812 ================
813 idAASFileLocal::ParseVertices
814 ================
815 */
816 bool idAASFileLocal::ParseVertices( idLexer &src ) {
817         int numVertices, i;
818         idVec3 vec;
819
820         numVertices = src.ParseInt();
821         vertices.Resize( numVertices );
822         if ( !src.ExpectTokenString( "{" ) ) {
823                 return false;
824         }
825         for ( i = 0; i < numVertices; i++ ) {
826                 src.ParseInt();
827                 if ( !src.Parse1DMatrix( 3, vec.ToFloatPtr() ) ) {
828                         return false;
829                 }
830                 vertices.Append( vec );
831         }
832         if ( !src.ExpectTokenString( "}" ) ) {
833                 return false;
834         }
835         return true;
836 }
837
838 /*
839 ================
840 idAASFileLocal::ParseEdges
841 ================
842 */
843 bool idAASFileLocal::ParseEdges( idLexer &src ) {
844         int numEdges, i;
845         aasEdge_t edge;
846
847         numEdges = src.ParseInt();
848         edges.Resize( numEdges );
849         if ( !src.ExpectTokenString( "{" ) ) {
850                 return false;
851         }
852         for ( i = 0; i < numEdges; i++ ) {
853                 src.ParseInt();
854                 src.ExpectTokenString( "(" );
855                 edge.vertexNum[0] = src.ParseInt();
856                 edge.vertexNum[1] = src.ParseInt();
857                 src.ExpectTokenString( ")" );
858                 edges.Append( edge );
859         }
860         if ( !src.ExpectTokenString( "}" ) ) {
861                 return false;
862         }
863         return true;
864 }
865
866 /*
867 ================
868 idAASFileLocal::ParseFaces
869 ================
870 */
871 bool idAASFileLocal::ParseFaces( idLexer &src ) {
872         int numFaces, i;
873         aasFace_t face;
874
875         numFaces = src.ParseInt();
876         faces.Resize( numFaces );
877         if ( !src.ExpectTokenString( "{" ) ) {
878                 return false;
879         }
880         for ( i = 0; i < numFaces; i++ ) {
881                 src.ParseInt();
882                 src.ExpectTokenString( "(" );
883                 face.planeNum = src.ParseInt();
884                 face.flags = src.ParseInt();
885                 face.areas[0] = src.ParseInt();
886                 face.areas[1] = src.ParseInt();
887                 face.firstEdge = src.ParseInt();
888                 face.numEdges = src.ParseInt();
889                 src.ExpectTokenString( ")" );
890                 faces.Append( face );
891         }
892         if ( !src.ExpectTokenString( "}" ) ) {
893                 return false;
894         }
895         return true;
896 }
897
898 /*
899 ================
900 idAASFileLocal::ParseReachabilities
901 ================
902 */
903 bool idAASFileLocal::ParseReachabilities( idLexer &src, int areaNum ) {
904         int num, j;
905         aasArea_t *area;
906         idReachability reach, *newReach;
907         idReachability_Special *special;
908
909         area = &areas[areaNum];
910
911         num = src.ParseInt();
912         src.ExpectTokenString( "{" );
913         area->reach = NULL;
914         area->rev_reach = NULL;
915         area->travelFlags = AreaContentsTravelFlags( areaNum );
916         for ( j = 0; j < num; j++ ) {
917                 Reachability_Read( src, &reach );
918                 switch( reach.travelType ) {
919                         case TFL_SPECIAL:
920                                 newReach = special = new idReachability_Special();
921                                 Reachability_Special_Read( src, special );
922                                 break;
923                         default:
924                                 newReach = new idReachability();
925                                 break;
926                 }
927                 newReach->CopyBase( reach );
928                 newReach->fromAreaNum = areaNum;
929                 newReach->next = area->reach;
930                 area->reach = newReach;
931         }
932         src.ExpectTokenString( "}" );
933         return true;
934 }
935
936 /*
937 ================
938 idAASFileLocal::LinkReversedReachability
939 ================
940 */
941 void idAASFileLocal::LinkReversedReachability( void ) {
942         int i;
943         idReachability *reach;
944
945         // link reversed reachabilities
946         for ( i = 0; i < areas.Num(); i++ ) {
947                 for ( reach = areas[i].reach; reach; reach = reach->next ) {
948                         reach->rev_next = areas[reach->toAreaNum].rev_reach;
949                         areas[reach->toAreaNum].rev_reach = reach;
950                 }
951         }
952 }
953
954 /*
955 ================
956 idAASFileLocal::ParseAreas
957 ================
958 */
959 bool idAASFileLocal::ParseAreas( idLexer &src ) {
960         int numAreas, i;
961         aasArea_t area;
962
963         numAreas = src.ParseInt();
964         areas.Resize( numAreas );
965         if ( !src.ExpectTokenString( "{" ) ) {
966                 return false;
967         }
968         for ( i = 0; i < numAreas; i++ ) {
969                 src.ParseInt();
970                 src.ExpectTokenString( "(" );
971                 area.flags = src.ParseInt();
972                 area.contents = src.ParseInt();
973                 area.firstFace = src.ParseInt();
974                 area.numFaces = src.ParseInt();
975                 area.cluster = src.ParseInt();
976                 area.clusterAreaNum = src.ParseInt();
977                 src.ExpectTokenString( ")" );
978                 areas.Append( area );
979                 ParseReachabilities( src, i );
980         }
981         if ( !src.ExpectTokenString( "}" ) ) {
982                 return false;
983         }
984
985         LinkReversedReachability();
986
987         return true;
988 }
989
990 /*
991 ================
992 idAASFileLocal::ParseNodes
993 ================
994 */
995 bool idAASFileLocal::ParseNodes( idLexer &src ) {
996         int numNodes, i;
997         aasNode_t node;
998
999         numNodes = src.ParseInt();
1000         nodes.Resize( numNodes );
1001         if ( !src.ExpectTokenString( "{" ) ) {
1002                 return false;
1003         }
1004         for ( i = 0; i < numNodes; i++ ) {
1005                 src.ParseInt();
1006                 src.ExpectTokenString( "(" );
1007                 node.planeNum = src.ParseInt();
1008                 node.children[0] = src.ParseInt();
1009                 node.children[1] = src.ParseInt();
1010                 src.ExpectTokenString( ")" );
1011                 nodes.Append( node );
1012         }
1013         if ( !src.ExpectTokenString( "}" ) ) {
1014                 return false;
1015         }
1016         return true;
1017 }
1018
1019 /*
1020 ================
1021 idAASFileLocal::ParsePortals
1022 ================
1023 */
1024 bool idAASFileLocal::ParsePortals( idLexer &src ) {
1025         int numPortals, i;
1026         aasPortal_t portal;
1027
1028         numPortals = src.ParseInt();
1029         portals.Resize( numPortals );
1030         if ( !src.ExpectTokenString( "{" ) ) {
1031                 return false;
1032         }
1033         for ( i = 0; i < numPortals; i++ ) {
1034                 src.ParseInt();
1035                 src.ExpectTokenString( "(" );
1036                 portal.areaNum = src.ParseInt();
1037                 portal.clusters[0] = src.ParseInt();
1038                 portal.clusters[1] = src.ParseInt();
1039                 portal.clusterAreaNum[0] = src.ParseInt();
1040                 portal.clusterAreaNum[1] = src.ParseInt();
1041                 src.ExpectTokenString( ")" );
1042                 portals.Append( portal );
1043         }
1044         if ( !src.ExpectTokenString( "}" ) ) {
1045                 return false;
1046         }
1047         return true;
1048 }
1049
1050 /*
1051 ================
1052 idAASFileLocal::ParseClusters
1053 ================
1054 */
1055 bool idAASFileLocal::ParseClusters( idLexer &src ) {
1056         int numClusters, i;
1057         aasCluster_t cluster;
1058
1059         numClusters = src.ParseInt();
1060         clusters.Resize( numClusters );
1061         if ( !src.ExpectTokenString( "{" ) ) {
1062                 return false;
1063         }
1064         for ( i = 0; i < numClusters; i++ ) {
1065                 src.ParseInt();
1066                 src.ExpectTokenString( "(" );
1067                 cluster.numAreas = src.ParseInt();
1068                 cluster.numReachableAreas = src.ParseInt();
1069                 cluster.firstPortal = src.ParseInt();
1070                 cluster.numPortals = src.ParseInt();
1071                 src.ExpectTokenString( ")" );
1072                 clusters.Append( cluster );
1073         }
1074         if ( !src.ExpectTokenString( "}" ) ) {
1075                 return false;
1076         }
1077         return true;
1078 }
1079
1080 /*
1081 ================
1082 idAASFileLocal::FinishAreas
1083 ================
1084 */
1085 void idAASFileLocal::FinishAreas( void ) {
1086         int i;
1087
1088         for ( i = 0; i < areas.Num(); i++ ) {
1089                 areas[i].center = AreaReachableGoal( i );
1090                 areas[i].bounds = AreaBounds( i );
1091         }
1092 }
1093
1094 /*
1095 ================
1096 idAASFileLocal::Load
1097 ================
1098 */
1099 bool idAASFileLocal::Load( const idStr &fileName, unsigned int mapFileCRC ) {
1100         idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWPATHNAMES );
1101         idToken token;
1102         int depth;
1103         unsigned int c;
1104
1105         name = fileName;
1106         crc = mapFileCRC;
1107
1108         common->Printf( "[Load AAS]\n" );
1109         common->Printf( "loading %s\n", name.c_str() );
1110
1111         if ( !src.LoadFile( name ) ) {
1112                 return false;
1113         }
1114
1115         if ( !src.ExpectTokenString( AAS_FILEID ) ) {
1116                 common->Warning( "Not an AAS file: '%s'", name.c_str() );
1117                 return false;
1118         }
1119
1120         if ( !src.ReadToken( &token ) || token != AAS_FILEVERSION ) {
1121                 common->Warning( "AAS file '%s' has version %s instead of %s", name.c_str(), token.c_str(), AAS_FILEVERSION );
1122                 return false;
1123         }
1124
1125         if ( !src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) ) {
1126                 common->Warning( "AAS file '%s' has no map file CRC", name.c_str() );
1127                 return false;
1128         }
1129
1130         c = token.GetUnsignedLongValue();
1131         if ( mapFileCRC && c != mapFileCRC ) {
1132                 common->Warning( "AAS file '%s' is out of date", name.c_str() );
1133                 return false;
1134         }
1135
1136         // clear the file in memory
1137         Clear();
1138
1139         // parse the file
1140         while ( 1 ) {
1141                 if ( !src.ReadToken( &token ) ) {
1142                         break;
1143                 }
1144
1145                 if ( token == "settings" ) {
1146                         if ( !settings.FromParser( src ) ) { return false; }
1147                 }
1148                 else if ( token == "planes" ) {
1149                         if ( !ParsePlanes( src ) ) { return false; }
1150                 }
1151                 else if ( token == "vertices" ) {
1152                         if ( !ParseVertices( src ) ) { return false; }
1153                 }
1154                 else if ( token == "edges" ) {
1155                         if ( !ParseEdges( src ) ) { return false; }
1156                 }
1157                 else if ( token == "edgeIndex" ) {
1158                         if ( !ParseIndex( src, edgeIndex ) ) { return false; }
1159                 }
1160                 else if ( token == "faces" ) {
1161                         if ( !ParseFaces( src ) ) { return false; }
1162                 }
1163                 else if ( token == "faceIndex" ) {
1164                         if ( !ParseIndex( src, faceIndex ) ) { return false; }
1165                 }
1166                 else if ( token == "areas" ) {
1167                         if ( !ParseAreas( src ) ) { return false; }
1168                 }
1169                 else if ( token == "nodes" ) {
1170                         if ( !ParseNodes( src ) ) { return false; }
1171                 }
1172                 else if ( token == "portals" ) {
1173                         if ( !ParsePortals( src ) ) { return false; }
1174                 }
1175                 else if ( token == "portalIndex" ) {
1176                         if ( !ParseIndex( src, portalIndex ) ) { return false; }
1177                 }
1178                 else if ( token == "clusters" ) {
1179                         if ( !ParseClusters( src ) ) { return false; }
1180                 }
1181                 else {
1182                         src.Error( "idAASFileLocal::Load: bad token \"%s\"", token.c_str() );
1183                         return false;
1184                 }
1185         }
1186
1187         FinishAreas();
1188
1189         depth = MaxTreeDepth();
1190         if ( depth > MAX_AAS_TREE_DEPTH ) {
1191                 src.Error( "idAASFileLocal::Load: tree depth = %d", depth );
1192         }
1193
1194         common->Printf( "done.\n" );
1195
1196         return true;
1197 }
1198
1199 /*
1200 ================
1201 idAASFileLocal::MemorySize
1202 ================
1203 */
1204 int idAASFileLocal::MemorySize( void ) const {
1205         int size;
1206
1207         size = planeList.Size();
1208         size += vertices.Size();
1209         size += edges.Size();
1210         size += edgeIndex.Size();
1211         size += faces.Size();
1212         size += faceIndex.Size();
1213         size += areas.Size();
1214         size += nodes.Size();
1215         size += portals.Size();
1216         size += portalIndex.Size();
1217         size += clusters.Size();
1218         size += sizeof( idReachability_Walk ) * NumReachabilities();
1219
1220         return size;
1221 }
1222
1223 /*
1224 ================
1225 idAASFileLocal::PrintInfo
1226 ================
1227 */
1228 void idAASFileLocal::PrintInfo( void ) const {
1229         common->Printf( "%6d KB file size\n", MemorySize() >> 10 );
1230         common->Printf( "%6d areas\n", areas.Num() );
1231         common->Printf( "%6d max tree depth\n", MaxTreeDepth() );
1232         ReportRoutingEfficiency();
1233 }
1234
1235 /*
1236 ================
1237 idAASFileLocal::NumReachabilities
1238 ================
1239 */
1240 int idAASFileLocal::NumReachabilities( void ) const {
1241         int i, num;
1242         idReachability *reach;
1243
1244         num = 0;
1245         for ( i = 0; i < areas.Num(); i++ ) {
1246                 for ( reach = areas[i].reach; reach; reach = reach->next ) {
1247                         num++;
1248                 }
1249         }
1250         return num;
1251 }
1252
1253 /*
1254 ================
1255 idAASFileLocal::ReportRoutingEfficiency
1256 ================
1257 */
1258 void idAASFileLocal::ReportRoutingEfficiency( void ) const {
1259         int numReachableAreas, total, i, n;
1260
1261         numReachableAreas = 0;
1262         total = 0;
1263         for ( i = 0; i < clusters.Num(); i++ ) {
1264                 n = clusters[i].numReachableAreas;
1265                 numReachableAreas += n;
1266                 total += n * n;
1267         }
1268         total += numReachableAreas * portals.Num();
1269
1270         common->Printf( "%6d reachable areas\n", numReachableAreas );
1271         common->Printf( "%6d reachabilities\n", NumReachabilities() );
1272         common->Printf( "%6d KB max routing cache\n", ( total * 3 ) >> 10 );
1273 }
1274
1275 /*
1276 ================
1277 idAASFileLocal::DeleteReachabilities
1278 ================
1279 */
1280 void idAASFileLocal::DeleteReachabilities( void ) {
1281         int i;
1282         idReachability *reach, *nextReach;
1283
1284         for ( i = 0; i < areas.Num(); i++ ) {
1285                 for ( reach = areas[i].reach; reach; reach = nextReach ) {
1286                         nextReach = reach->next;
1287                         delete reach;
1288                 }
1289                 areas[i].reach = NULL;
1290                 areas[i].rev_reach = NULL;
1291         }
1292 }
1293
1294 /*
1295 ================
1296 idAASFileLocal::DeleteClusters
1297 ================
1298 */
1299 void idAASFileLocal::DeleteClusters( void ) {
1300         aasPortal_t portal;
1301         aasCluster_t cluster;
1302
1303         portals.Clear();
1304         portalIndex.Clear();
1305         clusters.Clear();
1306
1307         // first portal is a dummy
1308         memset( &portal, 0, sizeof( portal ) );
1309         portals.Append( portal );
1310
1311         // first cluster is a dummy
1312         memset( &cluster, 0, sizeof( portal ) );
1313         clusters.Append( cluster );
1314 }