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