2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
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.
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.
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
22 #include "iscriplib.h"
25 #include "ifiletypes.h"
27 #include "qerplugin.h"
30 #include "string/string.h"
32 #include "generic/constant.h"
34 #include "modulesystem/singletonmodule.h"
40 class MapDoom3Dependencies :
41 public GlobalRadiantModuleRef,
42 public GlobalFiletypesModuleRef,
43 public GlobalScripLibModuleRef,
44 public GlobalEntityClassManagerModuleRef,
45 public GlobalSceneGraphModuleRef,
46 public GlobalBrushModuleRef
48 PatchModuleRef m_patchDef2Doom3Module;
49 PatchModuleRef m_patchDoom3Module;
51 MapDoom3Dependencies() :
52 GlobalEntityClassManagerModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "entityclass" ) ),
53 GlobalBrushModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "brushtypes" ) ),
54 m_patchDef2Doom3Module( "def2doom3" ),
55 m_patchDoom3Module( "doom3" ){
57 BrushCreator& getBrushDoom3(){
58 return GlobalBrushModule::getTable();
60 PatchCreator& getPatchDoom3(){
61 return *m_patchDoom3Module.getTable();
63 PatchCreator& getPatchDef2Doom3(){
64 return *m_patchDef2Doom3Module.getTable();
68 class MapDoom3API : public TypeSystemRef, public MapFormat, public PrimitiveParser
70 MapDoom3Dependencies& m_dependencies;
72 typedef MapFormat Type;
73 STRING_CONSTANT( Name, "mapdoom3" );
74 INTEGER_CONSTANT( MapVersion, 2 );
76 MapDoom3API( MapDoom3Dependencies& dependencies ) : m_dependencies( dependencies ){
77 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "doom3 maps", "*.map" ) );
78 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "doom3 region", "*.reg" ) );
80 MapFormat* getTable(){
84 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
85 const char* primitive = tokeniser.getToken();
86 if ( primitive != 0 ) {
87 if ( string_equal( primitive, "patchDef3" ) ) {
88 return m_dependencies.getPatchDoom3().createPatch();
90 else if ( string_equal( primitive, "patchDef2" ) ) {
91 return m_dependencies.getPatchDef2Doom3().createPatch();
93 else if ( string_equal( primitive, "brushDef3" ) ) {
94 return m_dependencies.getBrushDoom3().createBrush();
98 Tokeniser_unexpectedError( tokeniser, primitive, "#doom3-primitive" );
101 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
102 Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
103 tokeniser.nextLine();
104 if ( !Tokeniser_parseToken( tokeniser, "Version" ) ) {
108 if ( !Tokeniser_getSize( tokeniser, version ) ) {
111 if ( version != MapVersion() ) {
112 globalErrorStream() << "Doom 3 map version " << MapVersion() << " supported, version is " << Unsigned( version ) << "\n";
115 tokeniser.nextLine();
116 Map_Read( root, tokeniser, entityTable, *this );
119 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream ) const {
120 TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
121 writer.writeToken( "Version" );
122 writer.writeInteger( MapVersion() );
124 Map_Write( root, traverse, writer, false );
129 typedef SingletonModule<
131 MapDoom3Dependencies,
132 DependenciesAPIConstructor<MapDoom3API, MapDoom3Dependencies>
136 MapDoom3Module g_MapDoom3Module;
139 class MapQuake4API : public TypeSystemRef, public MapFormat, public PrimitiveParser
141 MapDoom3Dependencies& m_dependencies;
143 typedef MapFormat Type;
144 STRING_CONSTANT( Name, "mapquake4" );
145 INTEGER_CONSTANT( MapVersion, 3 );
147 MapQuake4API( MapDoom3Dependencies& dependencies ) : m_dependencies( dependencies ){
148 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake4 maps", "*.map" ) );
149 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake4 region", "*.reg" ) );
151 MapFormat* getTable(){
155 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
156 const char* primitive = tokeniser.getToken();
157 if ( primitive != 0 ) {
158 if ( string_equal( primitive, "patchDef3" ) ) {
159 return m_dependencies.getPatchDoom3().createPatch();
161 else if ( string_equal( primitive, "patchDef2" ) ) {
162 return m_dependencies.getPatchDef2Doom3().createPatch();
164 else if ( string_equal( primitive, "brushDef3" ) ) {
165 return m_dependencies.getBrushDoom3().createBrush();
169 Tokeniser_unexpectedError( tokeniser, primitive, "#quake4-primitive" );
172 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
173 Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
174 tokeniser.nextLine();
175 if ( !Tokeniser_parseToken( tokeniser, "Version" ) ) {
179 if ( !Tokeniser_getSize( tokeniser, version ) ) {
182 if ( version != MapVersion() ) {
183 globalErrorStream() << "Quake 4 map version " << MapVersion() << " supported, version is " << Unsigned( version ) << "\n";
186 tokeniser.nextLine();
187 Map_Read( root, tokeniser, entityTable, *this );
190 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream ) const {
191 TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
192 writer.writeToken( "Version" );
193 writer.writeInteger( MapVersion() );
195 Map_Write( root, traverse, writer, false );
200 typedef SingletonModule<
202 MapDoom3Dependencies,
203 DependenciesAPIConstructor<MapQuake4API, MapDoom3Dependencies>
207 MapQuake4Module g_MapQuake4Module;
210 class MapDependencies :
211 public GlobalRadiantModuleRef,
212 public GlobalBrushModuleRef,
213 public GlobalPatchModuleRef,
214 public GlobalFiletypesModuleRef,
215 public GlobalScripLibModuleRef,
216 public GlobalEntityClassManagerModuleRef,
217 public GlobalSceneGraphModuleRef
221 GlobalBrushModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "brushtypes" ) ),
222 GlobalPatchModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "patchtypes" ) ),
223 GlobalEntityClassManagerModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "entityclass" ) ){
227 class MapQ3API : public TypeSystemRef, public MapFormat, public PrimitiveParser
229 mutable bool detectedFormat;
231 typedef MapFormat Type;
232 STRING_CONSTANT( Name, "mapq3" );
235 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake3 maps", "*.map", true, true, true ) );
236 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake3 region", "*.reg", true, true, true ) );
237 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake3 compiled maps", "*.bsp", false, true, false ) );
239 MapFormat* getTable(){
243 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
244 const char* primitive = tokeniser.getToken();
245 if ( primitive != 0 ) {
246 if ( string_equal( primitive, "patchDef2" ) ) {
247 return GlobalPatchModule::getTable().createPatch();
249 if ( GlobalBrushModule::getTable().useAlternativeTextureProjection() ) {
250 if ( string_equal( primitive, "brushDef" ) ) {
251 detectedFormat = true;
252 return GlobalBrushModule::getTable().createBrush();
254 else if ( !detectedFormat && string_equal( primitive, "(" ) ) {
255 detectedFormat = true;
257 Tokeniser_unexpectedError( tokeniser, primitive, "#quake3-switch-to-texdef" );
263 if ( string_equal( primitive, "(" ) ) {
264 detectedFormat = true;
265 tokeniser.ungetToken(); // (
266 return GlobalBrushModule::getTable().createBrush();
268 else if ( !detectedFormat && string_equal( primitive, "brushDef" ) ) {
269 detectedFormat = true;
271 Tokeniser_unexpectedError( tokeniser, primitive, "#quake3-switch-to-brush-primitives" );
277 Tokeniser_unexpectedError( tokeniser, primitive, "#quake3-primitive" );
281 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
282 detectedFormat = false;
284 Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
285 Map_Read( root, tokeniser, entityTable, *this );
288 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream ) const {
289 TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
290 Map_Write( root, traverse, writer, false );
295 typedef SingletonModule<MapQ3API, MapDependencies> MapQ3Module;
297 MapQ3Module g_MapQ3Module;
300 class MapQ1API : public TypeSystemRef, public MapFormat, public PrimitiveParser
303 typedef MapFormat Type;
304 STRING_CONSTANT( Name, "mapq1" );
307 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake maps", "*.map" ) );
308 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake region", "*.reg" ) );
310 MapFormat* getTable(){
314 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
315 const char* primitive = tokeniser.getToken();
316 if ( primitive != 0 ) {
317 if ( string_equal( primitive, "(" ) ) {
318 tokeniser.ungetToken(); // (
319 return GlobalBrushModule::getTable().createBrush();
323 Tokeniser_unexpectedError( tokeniser, primitive, "#quake-primitive" );
326 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
327 Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
328 Map_Read( root, tokeniser, entityTable, *this );
331 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream ) const {
332 TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
333 Map_Write( root, traverse, writer, true );
338 typedef SingletonModule<MapQ1API, MapDependencies> MapQ1Module;
340 MapQ1Module g_MapQ1Module;
343 class MapHalfLifeAPI : public TypeSystemRef, public MapFormat, public PrimitiveParser
346 typedef MapFormat Type;
347 STRING_CONSTANT( Name, "maphl" );
350 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "half-life maps", "*.map" ) );
351 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "half-life region", "*.reg" ) );
353 MapFormat* getTable(){
357 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
358 const char* primitive = tokeniser.getToken();
359 if ( primitive != 0 ) {
360 if ( string_equal( primitive, "(" ) ) {
361 tokeniser.ungetToken(); // (
362 return GlobalBrushModule::getTable().createBrush();
366 Tokeniser_unexpectedError( tokeniser, primitive, "#halflife-primitive" );
369 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
370 Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
371 Map_Read( root, tokeniser, entityTable, *this );
374 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream ) const {
375 TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
376 Map_Write( root, traverse, writer, true );
381 typedef SingletonModule<MapHalfLifeAPI, MapDependencies> MapHalfLifeModule;
383 MapHalfLifeModule g_MapHalfLifeModule;
386 class MapQ2API : public TypeSystemRef, public MapFormat, public PrimitiveParser
389 typedef MapFormat Type;
390 STRING_CONSTANT( Name, "mapq2" );
393 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake2 maps", "*.map" ) );
394 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake2 region", "*.reg" ) );
396 MapFormat* getTable(){
399 scene::Node& parsePrimitive( Tokeniser& tokeniser ) const {
400 const char* primitive = tokeniser.getToken();
401 if ( primitive != 0 ) {
402 if ( string_equal( primitive, "(" ) ) {
403 tokeniser.ungetToken(); // (
404 return GlobalBrushModule::getTable().createBrush();
408 Tokeniser_unexpectedError( tokeniser, primitive, "#quake2-primitive" );
411 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
412 Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
413 Map_Read( root, tokeniser, entityTable, *this );
416 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream ) const {
417 TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( outputStream );
418 Map_Write( root, traverse, writer, true );
423 typedef SingletonModule<MapQ2API, MapDependencies> MapQ2Module;
425 MapQ2Module g_MapQ2Module;
429 #define PARSE_ERROR "error parsing VMF"
431 inline void parseToken( Tokeniser& tokeniser, const char* token ){
432 ASSERT_MESSAGE( Tokeniser_parseToken( tokeniser, token ), "error parsing vmf: token not found: " << makeQuoted( token ) );
435 #include "generic/arrayrange.h"
438 typedef ArrayConstRange<VMFBlock> VMFBlockArrayRange;
445 VMFBlockArrayRange m_children;
446 typedef const VMFBlock Value;
448 VMFBlock( const char* name, VMFBlockArrayRange children = VMFBlockArrayRange( 0, 0 ) ) : m_name( name ), m_children( children ){
450 const char* name() const {
453 typedef Value* const_iterator;
454 const_iterator begin() const {
455 return m_children.first;
457 const_iterator end() const {
458 return m_children.last;
462 const VMFBlock c_vmfNormals( "normals" );
463 const VMFBlock c_vmfDistances( "distances" );
464 const VMFBlock c_vmfOffsets( "offsets" );
465 const VMFBlock c_vmfOffsetNormals( "offset_normals" );
466 const VMFBlock c_vmfAlphas( "alphas" );
467 const VMFBlock c_vmfTriangleTags( "triangle_tags" );
468 const VMFBlock c_vmfAllowedVerts( "allowed_verts" );
469 const VMFBlock c_vmfDispInfoChildren[] = { c_vmfNormals, c_vmfDistances, c_vmfOffsets, c_vmfOffsetNormals, c_vmfAlphas, c_vmfTriangleTags, c_vmfAllowedVerts };
470 const VMFBlock c_vmfDispInfo( "dispinfo", ARRAY_RANGE( c_vmfDispInfoChildren ) );
471 const VMFBlock c_vmfSideChildren[] = { c_vmfDispInfo };
472 const VMFBlock c_vmfSide( "side", ARRAY_RANGE( c_vmfSideChildren ) );
473 const VMFBlock c_vmfEditor( "editor" );
474 const VMFBlock c_vmfVersionInfo( "versioninfo" );
475 const VMFBlock c_vmfViewSettings( "viewsettings" );
476 const VMFBlock c_vmfCordon( "cordon" );
477 const VMFBlock c_vmfGroupChildren[] = { c_vmfEditor };
478 const VMFBlock c_vmfGroup( "group", ARRAY_RANGE( c_vmfGroupChildren ) );
479 const VMFBlock c_vmfCamera( "camera" );
480 const VMFBlock c_vmfCamerasChildren[] = { c_vmfCamera };
481 const VMFBlock c_vmfCameras( "cameras", ARRAY_RANGE( c_vmfCamerasChildren ) );
482 VMFBlock c_vmfVisGroup( "visgroup" );
483 VMFBlock c_vmfVisGroups( "visgroups", VMFBlockArrayRange( &c_vmfVisGroup, &c_vmfVisGroup + 1 ) );
484 const VMFBlock c_vmfSolidChildren[] = { c_vmfSide, c_vmfEditor };
485 const VMFBlock c_vmfSolid( "solid", ARRAY_RANGE( c_vmfSolidChildren ) );
486 const VMFBlock c_vmfConnections( "connections" );
487 const VMFBlock c_vmfEntityChildren[] = { c_vmfEditor, c_vmfSolid, c_vmfGroup, c_vmfConnections };
488 const VMFBlock c_vmfEntity( "entity", ARRAY_RANGE( c_vmfEntityChildren ) );
489 const VMFBlock c_vmfWorldChildren[] = { c_vmfEditor, c_vmfSolid, c_vmfGroup };
490 const VMFBlock c_vmfWorld( "world", ARRAY_RANGE( c_vmfWorldChildren ) );
491 const VMFBlock c_vmfRootChildren[] = { c_vmfVersionInfo, c_vmfViewSettings, c_vmfVisGroups, c_vmfWorld, c_vmfEntity, c_vmfCameras, c_vmfCordon };
492 const VMFBlock c_vmfRoot( "", ARRAY_RANGE( c_vmfRootChildren ) );
498 c_vmfVisGroup.m_children = VMFBlockArrayRange( &c_vmfVisGroup, &c_vmfVisGroup + 1 );
507 inline VMFBlock::const_iterator VMFBlock_find( const VMFBlock& block, const char* name ){
508 for ( VMFBlock::const_iterator i = block.begin(); i != block.end(); ++i )
510 if ( string_equal( name, ( *i ).name() ) ) {
517 void VMF_parseBlock( Tokeniser& tokeniser, const VMFBlock& block ){
520 const char* key = tokeniser.getToken();
521 if ( key == 0 || string_equal( key, "}" ) ) {
522 tokeniser.ungetToken();
525 CopiedString tmp( key );
526 tokeniser.nextLine();
527 const char* value = tokeniser.getToken();
528 tokeniser.nextLine();
529 if ( string_equal( value, "{" ) ) {
530 VMFBlock::const_iterator i = VMFBlock_find( block, tmp.c_str() );
531 ASSERT_MESSAGE( i != block.end(), "error parsing vmf block " << makeQuoted( block.name() ) << ": unknown block: " << makeQuoted( tmp.c_str() ) );
532 if ( string_equal( tmp.c_str(), "solid" ) ) {
535 else if ( string_equal( tmp.c_str(), "entity" ) || string_equal( tmp.c_str(), "world" ) ) {
538 VMF_parseBlock( tokeniser, *i );
539 parseToken( tokeniser, "}" );
540 tokeniser.nextLine();
549 void VMF_Read( scene::Node& root, Tokeniser& tokeniser, EntityCreator& entityTable ){
550 g_vmf_entities = g_vmf_brushes = 0;
551 VMF_parseBlock( tokeniser, c_vmfRoot );
552 globalOutputStream() << g_vmf_entities << " entities\n";
553 globalOutputStream() << g_vmf_brushes << " brushes\n";
556 class MapVMFAPI : public TypeSystemRef, public MapFormat
559 typedef MapFormat Type;
560 STRING_CONSTANT( Name, "mapvmf" );
563 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "vmf maps", "*.vmf" ) );
564 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "vmf region", "*.reg" ) );
566 MapFormat* getTable(){
570 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
571 Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
572 VMF_Read( root, tokeniser, entityTable );
575 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream ) const {
579 typedef SingletonModule<MapVMFAPI, MapDependencies> MapVMFModule;
581 MapVMFModule g_MapVMFModule;
585 extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules( ModuleServer& server ){
586 initialiseModule( server );
588 g_MapDoom3Module.selfRegister();
589 g_MapQuake4Module.selfRegister();
590 g_MapQ3Module.selfRegister();
591 g_MapQ1Module.selfRegister();
592 g_MapQ2Module.selfRegister();
593 g_MapHalfLifeModule.selfRegister();
594 g_MapVMFModule.selfRegister();