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" ){
58 BrushCreator& getBrushDoom3(){
59 return GlobalBrushModule::getTable();
62 PatchCreator& getPatchDoom3(){
63 return *m_patchDoom3Module.getTable();
66 PatchCreator& getPatchDef2Doom3(){
67 return *m_patchDef2Doom3Module.getTable();
71 class MapDoom3API : public TypeSystemRef, public MapFormat, public PrimitiveParser
73 MapDoom3Dependencies& m_dependencies;
75 typedef MapFormat Type;
77 STRING_CONSTANT( Name, "mapdoom3" );
79 UINT_CONSTANT( MapVersion, 2 );
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" ) );
86 MapFormat* getTable(){
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();
96 else if ( string_equal( primitive, "patchDef2" ) ) {
97 return m_dependencies.getPatchDef2Doom3().createPatch();
99 else if ( string_equal( primitive, "brushDef3" ) ) {
100 return m_dependencies.getBrushDoom3().createBrush();
104 Tokeniser_unexpectedError( tokeniser, primitive, "#doom3-primitive" );
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" ) ) {
114 if ( !Tokeniser_getSize( tokeniser, version ) ) {
117 if ( version != MapVersion() ) {
118 globalErrorStream() << "Doom 3 map version " << MapVersion() << " supported, version is " << Unsigned( version ) << "\n";
121 tokeniser.nextLine();
122 Map_Read( root, tokeniser, entityTable, *this );
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() );
131 Map_Write( root, traverse, writer, false, writeComments );
136 typedef SingletonModule<
138 MapDoom3Dependencies,
139 DependenciesAPIConstructor<MapDoom3API, MapDoom3Dependencies>
143 MapDoom3Module g_MapDoom3Module;
146 class MapQuake4API : public TypeSystemRef, public MapFormat, public PrimitiveParser
148 MapDoom3Dependencies& m_dependencies;
150 typedef MapFormat Type;
152 STRING_CONSTANT( Name, "mapquake4" );
154 UINT_CONSTANT( MapVersion, 3 );
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" ) );
161 MapFormat* getTable(){
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();
171 else if ( string_equal( primitive, "patchDef2" ) ) {
172 return m_dependencies.getPatchDef2Doom3().createPatch();
174 else if ( string_equal( primitive, "brushDef3" ) ) {
175 return m_dependencies.getBrushDoom3().createBrush();
179 Tokeniser_unexpectedError( tokeniser, primitive, "#quake4-primitive" );
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" ) ) {
189 if ( !Tokeniser_getSize( tokeniser, version ) ) {
192 if ( version != MapVersion() ) {
193 globalErrorStream() << "Quake 4 map version " << MapVersion() << " supported, version is " << Unsigned( version ) << "\n";
196 tokeniser.nextLine();
197 Map_Read( root, tokeniser, entityTable, *this );
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() );
206 Map_Write( root, traverse, writer, false, writeComments );
211 typedef SingletonModule<
213 MapDoom3Dependencies,
214 DependenciesAPIConstructor<MapQuake4API, MapDoom3Dependencies>
218 MapQuake4Module g_MapQuake4Module;
221 class MapDependencies :
222 public GlobalRadiantModuleRef,
223 public GlobalBrushModuleRef,
224 public GlobalPatchModuleRef,
225 public GlobalFiletypesModuleRef,
226 public GlobalScripLibModuleRef,
227 public GlobalEntityClassManagerModuleRef,
228 public GlobalSceneGraphModuleRef
232 GlobalBrushModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "brushtypes" ) ),
233 GlobalPatchModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "patchtypes" ) ),
234 GlobalEntityClassManagerModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "entityclass" ) ){
238 class MapQ3API : public TypeSystemRef, public MapFormat, public PrimitiveParser
240 mutable bool detectedFormat;
242 typedef MapFormat Type;
244 STRING_CONSTANT( Name, "mapq3" );
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 ) );
252 MapFormat* getTable(){
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();
262 if ( GlobalBrushModule::getTable().useAlternativeTextureProjection() ) {
263 if ( string_equal( primitive, "brushDef" ) ) {
264 detectedFormat = true;
265 return GlobalBrushModule::getTable().createBrush();
267 else if ( !detectedFormat && string_equal( primitive, "(" ) ) {
268 detectedFormat = true;
270 Tokeniser_unexpectedError( tokeniser, primitive, "#quake3-switch-to-texdef" );
276 if ( string_equal( primitive, "(" ) ) {
277 detectedFormat = true;
278 tokeniser.ungetToken(); // (
279 return GlobalBrushModule::getTable().createBrush();
281 else if ( !detectedFormat && string_equal( primitive, "brushDef" ) ) {
282 detectedFormat = true;
284 Tokeniser_unexpectedError( tokeniser, primitive, "#quake3-switch-to-brush-primitives" );
290 Tokeniser_unexpectedError( tokeniser, primitive, "#quake3-primitive" );
294 void readGraph( scene::Node& root, TextInputStream& inputStream, EntityCreator& entityTable ) const {
295 detectedFormat = false;
297 Tokeniser &tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( inputStream );
298 Map_Read( root, tokeniser, entityTable, *this );
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 );
309 typedef SingletonModule<MapQ3API, MapDependencies> MapQ3Module;
311 MapQ3Module g_MapQ3Module;
314 class MapQ1API : public TypeSystemRef, public MapFormat, public PrimitiveParser
317 typedef MapFormat Type;
319 STRING_CONSTANT( Name, "mapq1" );
322 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake maps", "*.map" ) );
323 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake region", "*.reg" ) );
326 MapFormat* getTable(){
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();
339 Tokeniser_unexpectedError( tokeniser, primitive, "#quake-primitive" );
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 );
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 );
355 typedef SingletonModule<MapQ1API, MapDependencies> MapQ1Module;
357 MapQ1Module g_MapQ1Module;
360 class MapHalfLifeAPI : public TypeSystemRef, public MapFormat, public PrimitiveParser
363 typedef MapFormat Type;
365 STRING_CONSTANT( Name, "maphl" );
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" ) );
372 MapFormat* getTable(){
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();
385 Tokeniser_unexpectedError( tokeniser, primitive, "#halflife-primitive" );
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 );
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 );
402 typedef SingletonModule<MapHalfLifeAPI, MapDependencies> MapHalfLifeModule;
404 MapHalfLifeModule g_MapHalfLifeModule;
407 class MapQ2API : public TypeSystemRef, public MapFormat, public PrimitiveParser
410 typedef MapFormat Type;
412 STRING_CONSTANT( Name, "mapq2" );
415 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake2 maps", "*.map" ) );
416 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "quake2 region", "*.reg" ) );
419 MapFormat* getTable(){
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();
431 Tokeniser_unexpectedError( tokeniser, primitive, "#quake2-primitive" );
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 );
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 );
447 typedef SingletonModule<MapQ2API, MapDependencies> MapQ2Module;
449 MapQ2Module g_MapQ2Module;
452 const char *PARSE_ERROR = "error parsing VMF";
454 inline void parseToken( Tokeniser& tokeniser, const char* token ){
455 ASSERT_MESSAGE( Tokeniser_parseToken( tokeniser, token ), "error parsing vmf: token not found: " << makeQuoted( token ) );
458 #include "generic/arrayrange.h"
462 typedef ArrayConstRange<VMFBlock> VMFBlockArrayRange;
469 VMFBlockArrayRange m_children;
470 typedef const VMFBlock Value;
472 VMFBlock( const char* name, VMFBlockArrayRange children = VMFBlockArrayRange( 0, 0 ) ) : m_name( name ), m_children( children ){
474 const char* name() const {
477 typedef Value* const_iterator;
479 const_iterator begin() const {
480 return m_children.first;
483 const_iterator end() const {
484 return m_children.last;
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 ) );
524 c_vmfVisGroup.m_children = VMFBlockArrayRange( &c_vmfVisGroup, &c_vmfVisGroup + 1 );
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 )
536 if ( string_equal( name, ( *i ).name() ) ) {
543 void VMF_parseBlock( Tokeniser& tokeniser, const VMFBlock& block ){
546 const char* key = tokeniser.getToken();
547 if ( key == 0 || string_equal( key, "}" ) ) {
548 tokeniser.ungetToken();
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" ) ) {
561 else if ( string_equal( tmp.c_str(), "entity" ) || string_equal( tmp.c_str(), "world" ) ) {
564 VMF_parseBlock( tokeniser, *i );
565 parseToken( tokeniser, "}" );
566 tokeniser.nextLine();
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";
582 class MapVMFAPI : public TypeSystemRef, public MapFormat
585 typedef MapFormat Type;
587 STRING_CONSTANT( Name, "mapvmf" );
590 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "vmf maps", "*.vmf" ) );
591 GlobalFiletypesModule::getTable().addType( Type::Name(), Name(), filetype_t( "vmf region", "*.reg" ) );
594 MapFormat* getTable(){
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 );
604 void writeGraph( scene::Node& root, GraphTraversalFunc traverse, TextOutputStream& outputStream, bool writeComments ) const {
608 typedef SingletonModule<MapVMFAPI, MapDependencies> MapVMFModule;
610 MapVMFModule g_MapVMFModule;
613 extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules( ModuleServer& server ){
614 initialiseModule( server );
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();