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