]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/mapq3/plugin.cpp
Merge branch 'NateEag-master-patch-12920' into 'master'
[xonotic/netradiant.git] / plugins / mapq3 / plugin.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "iscriplib.h"
23 #include "ibrush.h"
24 #include "ipatch.h"
25 #include "ifiletypes.h"
26 #include "ieclass.h"
27 #include "qerplugin.h"
28
29 #include "scenelib.h"
30 #include "string/string.h"
31 #include "stringio.h"
32 #include "generic/constant.h"
33
34 #include "modulesystem/singletonmodule.h"
35
36 #include "parse.h"
37 #include "write.h"
38
39
40 class MapDoom3Dependencies :
41         public GlobalRadiantModuleRef,
42         public GlobalFiletypesModuleRef,
43         public GlobalScripLibModuleRef,
44         public GlobalEntityClassManagerModuleRef,
45         public GlobalSceneGraphModuleRef,
46         public GlobalBrushModuleRef
47 {
48 PatchModuleRef m_patchDef2Doom3Module;
49 PatchModuleRef m_patchDoom3Module;
50 public:
51 MapDoom3Dependencies() :
52         GlobalEntityClassManagerModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "entityclass" ) ),
53         GlobalBrushModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "brushtypes" ) ),
54         m_patchDef2Doom3Module( "def2doom3" ),
55         m_patchDoom3Module( "doom3" ){
56 }
57
58 BrushCreator& getBrushDoom3(){
59         return GlobalBrushModule::getTable();
60 }
61
62 PatchCreator& getPatchDoom3(){
63         return *m_patchDoom3Module.getTable();
64 }
65
66 PatchCreator& getPatchDef2Doom3(){
67         return *m_patchDef2Doom3Module.getTable();
68 }
69 };
70
71 class MapDoom3API : public TypeSystemRef, public MapFormat, public PrimitiveParser
72 {
73 MapDoom3Dependencies& m_dependencies;
74 public:
75 typedef MapFormat Type;
76
77 STRING_CONSTANT( Name, "mapdoom3" );
78
79 UINT_CONSTANT( MapVersion, 2 );
80
81 MapDoom3API( MapDoom3Dependencies& dependencies ) : m_dependencies( dependencies ){
82         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "doom3 maps", "*.map" ) );
83         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "doom3 region", "*.reg" ) );
84 }
85
86 MapFormat* getTable(){
87         return this;
88 }
89
90 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
91         const char* primitive = tokeniser.getToken();
92         if ( primitive != 0 ) {
93                 if ( string_equal( primitive, "patchDef3" ) ) {
94                         return m_dependencies.getPatchDoom3().createPatch();
95                 }
96                 else if ( string_equal( primitive, "patchDef2" ) ) {
97                         return m_dependencies.getPatchDef2Doom3().createPatch();
98                 }
99                 else if ( string_equal( primitive, "brushDef3" ) ) {
100                         return m_dependencies.getBrushDoom3().createBrush();
101                 }
102         }
103
104         Tokeniser_unexpectedError( tokeniser, primitive, "#doom3-primitive" );
105         return g_nullNode;
106 }
107 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
108         Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
109         tokeniser.nextLine();
110         if ( !Tokeniser_parseToken( tokeniser, "Version" ) ) {
111                 return;
112         }
113         std::size_t version;
114         if ( !Tokeniser_getSize( tokeniser, version ) ) {
115                 return;
116         }
117         if ( version != MapVersion() ) {
118                 globalErrorStream() << "Doom 3 map version " << MapVersion() << " supported, version is " << Unsigned( version ) << "\n";
119                 return;
120         }
121         tokeniser.nextLine();
122         Map_Read( root, tokeniser, entityTable, *this );
123         tokeniser.release();
124 }
125
126 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream, bool writeComments ) const {
127         TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
128         writer.writeToken( "Version" );
129         writer.writeInteger( MapVersion() );
130         writer.nextLine();
131         Map_Write( root, traverse, writer, false, writeComments );
132         writer.release();
133 }
134 };
135
136 typedef SingletonModule<
137         MapDoom3API,
138         MapDoom3Dependencies,
139         DependenciesAPIConstructor<MapDoom3API, MapDoom3Dependencies>
140         >
141 MapDoom3Module;
142
143 MapDoom3Module g_MapDoom3Module;
144
145
146 class MapQuake4API : public TypeSystemRef, public MapFormat, public PrimitiveParser
147 {
148 MapDoom3Dependencies& m_dependencies;
149 public:
150 typedef MapFormat Type;
151
152 STRING_CONSTANT( Name, "mapquake4" );
153
154 UINT_CONSTANT( MapVersion, 3 );
155
156 MapQuake4API( MapDoom3Dependencies& dependencies ) : m_dependencies( dependencies ){
157         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake4 maps", "*.map" ) );
158         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake4 region", "*.reg" ) );
159 }
160
161 MapFormat* getTable(){
162         return this;
163 }
164
165 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
166         const char* primitive = tokeniser.getToken();
167         if ( primitive != 0 ) {
168                 if ( string_equal( primitive, "patchDef3" ) ) {
169                         return m_dependencies.getPatchDoom3().createPatch();
170                 }
171                 else if ( string_equal( primitive, "patchDef2" ) ) {
172                         return m_dependencies.getPatchDef2Doom3().createPatch();
173                 }
174                 else if ( string_equal( primitive, "brushDef3" ) ) {
175                         return m_dependencies.getBrushDoom3().createBrush();
176                 }
177         }
178
179         Tokeniser_unexpectedError( tokeniser, primitive, "#quake4-primitive" );
180         return g_nullNode;
181 }
182 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
183         Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
184         tokeniser.nextLine();
185         if ( !Tokeniser_parseToken( tokeniser, "Version" ) ) {
186                 return;
187         }
188         std::size_t version;
189         if ( !Tokeniser_getSize( tokeniser, version ) ) {
190                 return;
191         }
192         if ( version != MapVersion() ) {
193                 globalErrorStream() << "Quake 4 map version " << MapVersion() << " supported, version is " << Unsigned( version ) << "\n";
194                 return;
195         }
196         tokeniser.nextLine();
197         Map_Read( root, tokeniser, entityTable, *this );
198         tokeniser.release();
199 }
200
201 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream, bool writeComments ) const {
202         TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
203         writer.writeToken( "Version" );
204         writer.writeInteger( MapVersion() );
205         writer.nextLine();
206         Map_Write( root, traverse, writer, false, writeComments );
207         writer.release();
208 }
209 };
210
211 typedef SingletonModule<
212         MapQuake4API,
213         MapDoom3Dependencies,
214         DependenciesAPIConstructor<MapQuake4API, MapDoom3Dependencies>
215         >
216 MapQuake4Module;
217
218 MapQuake4Module g_MapQuake4Module;
219
220
221 class MapDependencies :
222         public GlobalRadiantModuleRef,
223         public GlobalBrushModuleRef,
224         public GlobalPatchModuleRef,
225         public GlobalFiletypesModuleRef,
226         public GlobalScripLibModuleRef,
227         public GlobalEntityClassManagerModuleRef,
228         public GlobalSceneGraphModuleRef
229 {
230 public:
231 MapDependencies() :
232         GlobalBrushModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "brushtypes" ) ),
233         GlobalPatchModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "patchtypes" ) ),
234         GlobalEntityClassManagerModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "entityclass" ) ){
235 }
236 };
237
238 class MapQ3API : public TypeSystemRef, public MapFormat, public PrimitiveParser
239 {
240 mutable bool detectedFormat;
241 public:
242 typedef MapFormat Type;
243
244 STRING_CONSTANT( Name, "mapq3" );
245
246 MapQ3API(){
247         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake3 maps", "*.map", true, true, true ) );
248         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake3 region", "*.reg", true, true, true ) );
249         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake3 compiled maps", "*.bsp", false, true, false ) );
250 }
251
252 MapFormat* getTable(){
253         return this;
254 }
255
256 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
257         const char* primitive = tokeniser.getToken();
258         if ( primitive != 0 ) {
259                 if ( string_equal( primitive, "patchDef2" ) ) {
260                         return GlobalPatchModule::getTable().createPatch();
261                 }
262                 if ( GlobalBrushModule::getTable().useAlternativeTextureProjection() ) {
263                         if ( string_equal( primitive, "brushDef" ) ) {
264                                 detectedFormat = true;
265                                 return GlobalBrushModule::getTable().createBrush();
266                         }
267                         else if ( !detectedFormat && string_equal( primitive, "(" ) ) {
268                                 detectedFormat = true;
269                                 wrongFormat = true;
270                                 Tokeniser_unexpectedError( tokeniser, primitive, "#quake3-switch-to-texdef" );
271                                 return g_nullNode;
272                         }
273                 }
274                 else
275                 {
276                         if ( string_equal( primitive, "(" ) ) {
277                                 detectedFormat = true;
278                                 tokeniser.ungetToken(); // (
279                                 return GlobalBrushModule::getTable().createBrush();
280                         }
281                         else if ( !detectedFormat && string_equal( primitive, "brushDef" ) ) {
282                                 detectedFormat = true;
283                                 wrongFormat = true;
284                                 Tokeniser_unexpectedError( tokeniser, primitive, "#quake3-switch-to-brush-primitives" );
285                                 return g_nullNode;
286                         }
287                 }
288         }
289
290         Tokeniser_unexpectedError( tokeniser, primitive, "#quake3-primitive" );
291         return g_nullNode;
292 }
293
294 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
295         detectedFormat = false;
296         wrongFormat = false;
297         Tokeniser &tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
298         Map_Read( root, tokeniser, entityTable, *this );
299         tokeniser.release();
300 }
301
302 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream, bool writeComments ) const {
303         TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
304         Map_Write( root, traverse, writer, false, writeComments );
305         writer.release();
306 }
307 };
308
309 typedef SingletonModule<MapQ3API, MapDependencies> MapQ3Module;
310
311 MapQ3Module g_MapQ3Module;
312
313
314 class MapQ1API : public TypeSystemRef, public MapFormat, public PrimitiveParser
315 {
316 public:
317 typedef MapFormat Type;
318
319 STRING_CONSTANT( Name, "mapq1" );
320
321 MapQ1API(){
322         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake maps", "*.map" ) );
323         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake region", "*.reg" ) );
324 }
325
326 MapFormat* getTable(){
327         return this;
328 }
329
330 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
331         const char* primitive = tokeniser.getToken();
332         if ( primitive != 0 ) {
333                 if ( string_equal( primitive, "(" ) ) {
334                         tokeniser.ungetToken(); // (
335                         return GlobalBrushModule::getTable().createBrush();
336                 }
337         }
338
339         Tokeniser_unexpectedError( tokeniser, primitive, "#quake-primitive" );
340         return g_nullNode;
341 }
342 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
343         Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
344         Map_Read( root, tokeniser, entityTable, *this );
345         tokeniser.release();
346 }
347
348 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream, bool writeComments ) const {
349         TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
350         Map_Write( root, traverse, writer, true, writeComments );
351         writer.release();
352 }
353 };
354
355 typedef SingletonModule<MapQ1API, MapDependencies> MapQ1Module;
356
357 MapQ1Module g_MapQ1Module;
358
359
360 class MapHalfLifeAPI : public TypeSystemRef, public MapFormat, public PrimitiveParser
361 {
362 public:
363 typedef MapFormat Type;
364
365 STRING_CONSTANT( Name, "maphl" );
366
367 MapHalfLifeAPI(){
368         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "half-life maps", "*.map" ) );
369         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "half-life region", "*.reg" ) );
370 }
371
372 MapFormat* getTable(){
373         return this;
374 }
375
376 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
377         const char* primitive = tokeniser.getToken();
378         if ( primitive != 0 ) {
379                 if ( string_equal( primitive, "(" ) ) {
380                         tokeniser.ungetToken(); // (
381                         return GlobalBrushModule::getTable().createBrush();
382                 }
383         }
384
385         Tokeniser_unexpectedError( tokeniser, primitive, "#halflife-primitive" );
386         return g_nullNode;
387 }
388
389 void readGraph( scene::Node &root, TextInputStream &inputStream, EntityCreator &entityTable ) const {
390         Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
391         Map_Read( root, tokeniser, entityTable, *this );
392         tokeniser.release();
393 }
394
395 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream, bool writeComments ) const {
396         TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
397         Map_Write( root, traverse, writer, true, writeComments );
398         writer.release();
399 }
400 };
401
402 typedef SingletonModule<MapHalfLifeAPI, MapDependencies> MapHalfLifeModule;
403
404 MapHalfLifeModule g_MapHalfLifeModule;
405
406
407 class MapQ2API : public TypeSystemRef, public MapFormat, public PrimitiveParser
408 {
409 public:
410 typedef MapFormat Type;
411
412 STRING_CONSTANT( Name, "mapq2" );
413
414 MapQ2API(){
415         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake2 maps", "*.map" ) );
416         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake2 region", "*.reg" ) );
417 }
418
419 MapFormat* getTable(){
420         return this;
421 }
422 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
423         const char* primitive = tokeniser.getToken();
424         if ( primitive != 0 ) {
425                 if ( string_equal( primitive, "(" ) ) {
426                         tokeniser.ungetToken(); // (
427                         return GlobalBrushModule::getTable().createBrush();
428                 }
429         }
430
431         Tokeniser_unexpectedError( tokeniser, primitive, "#quake2-primitive" );
432         return g_nullNode;
433 }
434 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
435         Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
436         Map_Read( root, tokeniser, entityTable, *this );
437         tokeniser.release();
438 }
439
440 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream, bool writeComments ) const {
441         TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
442         Map_Write( root, traverse, writer, true, writeComments );
443         writer.release();
444 }
445 };
446
447 typedef SingletonModule<MapQ2API, MapDependencies> MapQ2Module;
448
449 MapQ2Module g_MapQ2Module;
450
451
452 const char *PARSE_ERROR = "error parsing VMF";
453
454 inline void parseToken( Tokeniser& tokeniser, const char* token ){
455         ASSERT_MESSAGE( Tokeniser_parseToken( tokeniser, token ), "error parsing vmf: token not found: " << makeQuoted( token ) );
456 }
457
458 #include "generic/arrayrange.h"
459
460 class VMFBlock;
461
462 typedef ArrayConstRange<VMFBlock> VMFBlockArrayRange;
463
464
465 class VMFBlock
466 {
467 public:
468 const char* m_name;
469 VMFBlockArrayRange m_children;
470 typedef const VMFBlock Value;
471
472 VMFBlock( const char* name, VMFBlockArrayRange children = VMFBlockArrayRange( 0, 0 ) ) : m_name( name ), m_children( children ){
473 }
474 const char* name() const {
475         return m_name;
476 }
477 typedef Value* const_iterator;
478
479 const_iterator begin() const {
480         return m_children.first;
481 }
482
483 const_iterator end() const {
484         return m_children.last;
485 }
486 };
487
488 const VMFBlock c_vmfNormals( "normals" );
489 const VMFBlock c_vmfDistances( "distances" );
490 const VMFBlock c_vmfOffsets( "offsets" );
491 const VMFBlock c_vmfOffsetNormals( "offset_normals" );
492 const VMFBlock c_vmfAlphas( "alphas" );
493 const VMFBlock c_vmfTriangleTags( "triangle_tags" );
494 const VMFBlock c_vmfAllowedVerts( "allowed_verts" );
495 const VMFBlock c_vmfDispInfoChildren[] = { c_vmfNormals, c_vmfDistances, c_vmfOffsets, c_vmfOffsetNormals, c_vmfAlphas, c_vmfTriangleTags, c_vmfAllowedVerts };
496 const VMFBlock c_vmfDispInfo( "dispinfo", ARRAY_RANGE( c_vmfDispInfoChildren ) );
497 const VMFBlock c_vmfSideChildren[] = { c_vmfDispInfo };
498 const VMFBlock c_vmfSide( "side", ARRAY_RANGE( c_vmfSideChildren ) );
499 const VMFBlock c_vmfEditor( "editor" );
500 const VMFBlock c_vmfVersionInfo( "versioninfo" );
501 const VMFBlock c_vmfViewSettings( "viewsettings" );
502 const VMFBlock c_vmfCordon( "cordon" );
503 const VMFBlock c_vmfGroupChildren[] = { c_vmfEditor };
504 const VMFBlock c_vmfGroup( "group", ARRAY_RANGE( c_vmfGroupChildren ) );
505 const VMFBlock c_vmfCamera( "camera" );
506 const VMFBlock c_vmfCamerasChildren[] = { c_vmfCamera };
507 const VMFBlock c_vmfCameras( "cameras", ARRAY_RANGE( c_vmfCamerasChildren ) );
508 VMFBlock c_vmfVisGroup( "visgroup" );
509 VMFBlock c_vmfVisGroups( "visgroups", VMFBlockArrayRange( &c_vmfVisGroup, &c_vmfVisGroup + 1 ) );
510 const VMFBlock c_vmfSolidChildren[] = { c_vmfSide, c_vmfEditor };
511 const VMFBlock c_vmfSolid( "solid", ARRAY_RANGE( c_vmfSolidChildren ) );
512 const VMFBlock c_vmfConnections( "connections" );
513 const VMFBlock c_vmfEntityChildren[] = { c_vmfEditor, c_vmfSolid, c_vmfGroup, c_vmfConnections };
514 const VMFBlock c_vmfEntity( "entity", ARRAY_RANGE( c_vmfEntityChildren ) );
515 const VMFBlock c_vmfWorldChildren[] = { c_vmfEditor, c_vmfSolid, c_vmfGroup };
516 const VMFBlock c_vmfWorld( "world", ARRAY_RANGE( c_vmfWorldChildren ) );
517 const VMFBlock c_vmfRootChildren[] = { c_vmfVersionInfo, c_vmfViewSettings, c_vmfVisGroups, c_vmfWorld, c_vmfEntity, c_vmfCameras, c_vmfCordon };
518 const VMFBlock c_vmfRoot( "", ARRAY_RANGE( c_vmfRootChildren ) );
519
520 class VMFInit
521 {
522 public:
523 VMFInit(){
524         c_vmfVisGroup.m_children = VMFBlockArrayRange( &c_vmfVisGroup, &c_vmfVisGroup + 1 );
525 }
526 };
527
528 VMFInit g_VMFInit;
529
530 int g_vmf_entities;
531 int g_vmf_brushes;
532
533 inline VMFBlock::const_iterator VMFBlock_find( const VMFBlock& block, const char* name ){
534         for ( VMFBlock::const_iterator i = block.begin(); i != block.end(); ++i )
535         {
536                 if ( string_equal( name, ( *i ).name() ) ) {
537                         return i;
538                 }
539         }
540         return block.end();
541 }
542
543 void VMF_parseBlock( Tokeniser& tokeniser, const VMFBlock& block ){
544         for (;; )
545         {
546                 const char* key = tokeniser.getToken();
547                 if ( key == 0 || string_equal( key, "}" ) ) {
548                         tokeniser.ungetToken();
549                         break;
550                 }
551                 CopiedString tmp( key );
552                 tokeniser.nextLine();
553                 const char* value = tokeniser.getToken();
554                 tokeniser.nextLine();
555                 if ( string_equal( value, "{" ) ) {
556                         VMFBlock::const_iterator i = VMFBlock_find( block, tmp.c_str() );
557                         ASSERT_MESSAGE( i != block.end(), "error parsing vmf block " << makeQuoted( block.name() ) << ": unknown block: " << makeQuoted( tmp.c_str() ) );
558                         if ( string_equal( tmp.c_str(), "solid" ) ) {
559                                 ++g_vmf_brushes;
560                         }
561                         else if ( string_equal( tmp.c_str(), "entity" ) || string_equal( tmp.c_str(), "world" ) ) {
562                                 ++g_vmf_entities;
563                         }
564                         VMF_parseBlock( tokeniser, *i );
565                         parseToken( tokeniser, "}" );
566                         tokeniser.nextLine();
567                 }
568                 else
569                 {
570                         // was a pair
571                 }
572         }
573 }
574
575 void VMF_Read( scene::Node& root, Tokeniser& tokeniser, EntityCreator& entityTable ){
576         g_vmf_entities = g_vmf_brushes = 0;
577         VMF_parseBlock( tokeniser, c_vmfRoot );
578         globalOutputStream() << g_vmf_entities << " entities\n";
579         globalOutputStream() << g_vmf_brushes << " brushes\n";
580 }
581
582 class MapVMFAPI : public TypeSystemRef, public MapFormat
583 {
584 public:
585 typedef MapFormat Type;
586
587 STRING_CONSTANT( Name, "mapvmf" );
588
589 MapVMFAPI(){
590         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "vmf maps", "*.vmf" ) );
591         GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "vmf region", "*.reg" ) );
592 }
593
594 MapFormat* getTable(){
595         return this;
596 }
597
598 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
599         Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
600         VMF_Read( root, tokeniser, entityTable );
601         tokeniser.release();
602 }
603
604 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream, bool writeComments ) const {
605 }
606 };
607
608 typedef SingletonModule<MapVMFAPI, MapDependencies> MapVMFModule;
609
610 MapVMFModule g_MapVMFModule;
611
612
613 extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules( ModuleServer& server ){
614         initialiseModule( server );
615
616         g_MapDoom3Module.selfRegister();
617         g_MapQuake4Module.selfRegister();
618         g_MapQ3Module.selfRegister();
619         g_MapQ1Module.selfRegister();
620         g_MapQ2Module.selfRegister();
621         g_MapHalfLifeModule.selfRegister();
622         g_MapVMFModule.selfRegister();
623 }