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 "eclass_fgd.h"
24 #include "debugging/debugging.h"
28 #include "ifilesystem.h"
29 #include "iscriplib.h"
30 #include "qerplugin.h"
32 #include "string/string.h"
33 #include "eclasslib.h"
36 #include "stream/stringstream.h"
37 #include "moduleobservers.h"
39 #include "stream/textfilestream.h"
43 typedef std::map<const char*, EntityClass*, RawStringLessNoCase> EntityClasses;
44 EntityClasses g_EntityClassFGD_classes;
45 typedef std::map<const char*, EntityClass*, RawStringLess> BaseClasses;
46 BaseClasses g_EntityClassFGD_bases;
47 EntityClass *g_EntityClassFGD_bad = 0;
48 typedef std::map<CopiedString, ListAttributeType> ListAttributeTypes;
49 ListAttributeTypes g_listTypesFGD;
53 void EntityClassFGD_clear()
55 for(BaseClasses::iterator i = g_EntityClassFGD_bases.begin(); i != g_EntityClassFGD_bases.end(); ++i)
57 (*i).second->free((*i).second);
59 g_EntityClassFGD_bases.clear();
60 g_listTypesFGD.clear();
63 EntityClass* EntityClassFGD_insertUniqueBase(EntityClass* entityClass)
65 std::pair<BaseClasses::iterator, bool> result = g_EntityClassFGD_bases.insert(BaseClasses::value_type(entityClass->name(), entityClass));
68 globalErrorStream() << "duplicate base class: " << makeQuoted(entityClass->name()) << "\n";
69 //eclass_capture_state(entityClass);
70 //entityClass->free(entityClass);
72 return (*result.first).second;
75 EntityClass* EntityClassFGD_insertUnique(EntityClass* entityClass)
77 EntityClassFGD_insertUniqueBase(entityClass);
78 std::pair<EntityClasses::iterator, bool> result = g_EntityClassFGD_classes.insert(EntityClasses::value_type(entityClass->name(), entityClass));
81 globalErrorStream() << "duplicate entity class: " << makeQuoted(entityClass->name()) << "\n";
82 eclass_capture_state(entityClass);
83 entityClass->free(entityClass);
85 return (*result.first).second;
88 void EntityClassFGD_forEach(EntityClassVisitor& visitor)
90 for(EntityClasses::iterator i = g_EntityClassFGD_classes.begin(); i != g_EntityClassFGD_classes.end(); ++i)
92 visitor.visit((*i).second);
96 inline bool EntityClassFGD_parseToken(Tokeniser& tokeniser, const char* token)
98 return string_equal(tokeniser.getToken(), token);
101 #define PARSE_ERROR "error parsing entity class definition"
103 void EntityClassFGD_parseSplitString(Tokeniser& tokeniser, CopiedString& string)
105 StringOutputStream buffer(256);
108 buffer << tokeniser.getToken();
109 if(!string_equal(tokeniser.getToken(), "+"))
111 tokeniser.ungetToken();
112 string = buffer.c_str();
118 void EntityClassFGD_parseClass(Tokeniser& tokeniser, bool fixedsize, bool isBase)
120 EntityClass* entityClass = Eclass_Alloc();
121 entityClass->free = &Eclass_Free;
122 entityClass->fixedsize = fixedsize;
123 entityClass->inheritanceResolved = false;
124 entityClass->mins = Vector3(-8, -8, -8);
125 entityClass->maxs = Vector3(8, 8, 8);
129 const char* property = tokeniser.getToken();
130 if(string_equal(property, "="))
134 else if(string_equal(property, "base"))
136 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
139 const char* base = tokeniser.getToken();
140 if(string_equal(base, ")"))
144 else if(!string_equal(base, ","))
146 entityClass->m_parent.push_back(base);
150 else if(string_equal(property, "size"))
152 entityClass->sizeSpecified = true;
153 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
154 Tokeniser_getFloat(tokeniser, entityClass->mins.x());
155 Tokeniser_getFloat(tokeniser, entityClass->mins.y());
156 Tokeniser_getFloat(tokeniser, entityClass->mins.z());
157 const char* token = tokeniser.getToken();
158 if(string_equal(token, ","))
160 Tokeniser_getFloat(tokeniser, entityClass->maxs.x());
161 Tokeniser_getFloat(tokeniser, entityClass->maxs.y());
162 Tokeniser_getFloat(tokeniser, entityClass->maxs.z());
163 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
167 entityClass->maxs = entityClass->mins;
168 vector3_negate(entityClass->mins);
169 ASSERT_MESSAGE(string_equal(token, ")"), "");
172 else if(string_equal(property, "color"))
174 entityClass->colorSpecified = true;
175 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
176 Tokeniser_getFloat(tokeniser, entityClass->color.x());
177 entityClass->color.x() /= 256.0;
178 Tokeniser_getFloat(tokeniser, entityClass->color.y());
179 entityClass->color.y() /= 256.0;
180 Tokeniser_getFloat(tokeniser, entityClass->color.z());
181 entityClass->color.z() /= 256.0;
182 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
184 else if(string_equal(property, "iconsprite"))
186 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
187 StringOutputStream buffer(256);
188 buffer << PathCleaned(tokeniser.getToken());
189 entityClass->m_modelpath = buffer.c_str();
190 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
192 else if(string_equal(property, "sprite")
193 || string_equal(property, "decal")
195 || string_equal(property, "overlay")
196 || string_equal(property, "light")
197 || string_equal(property, "keyframe")
198 || string_equal(property, "animator")
199 || string_equal(property, "quadbounds"))
201 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
202 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
205 else if(string_equal(property, "sphere")
206 || string_equal(property, "sweptplayerhull")
207 || string_equal(property, "studio")
208 || string_equal(property, "studioprop")
209 || string_equal(property, "lightprop")
210 || string_equal(property, "lightcone")
211 || string_equal(property, "sidelist"))
213 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
214 if(string_equal(tokeniser.getToken(), ")"))
216 tokeniser.ungetToken();
218 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
220 else if(string_equal(property, "line")
221 || string_equal(property, "cylinder"))
223 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
225 tokeniser.getToken();
227 tokeniser.getToken();
229 tokeniser.getToken();
232 if(string_equal(tokeniser.getToken(), ")"))
234 tokeniser.ungetToken();
238 tokeniser.getToken();
240 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
242 else if(string_equal(property, "wirebox"))
244 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
246 tokeniser.getToken();
247 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ","), PARSE_ERROR);
249 tokeniser.getToken();
250 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
252 else if(string_equal(property, "halfgridsnap"))
257 ERROR_MESSAGE(PARSE_ERROR);
261 entityClass->m_name = tokeniser.getToken();
265 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ":"), PARSE_ERROR);
267 EntityClassFGD_parseSplitString(tokeniser, entityClass->m_comments);
270 tokeniser.nextLine();
272 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "["), PARSE_ERROR);
274 tokeniser.nextLine();
278 CopiedString key = tokeniser.getToken();
279 if(string_equal(key.c_str(), "]"))
281 tokeniser.nextLine();
285 if(string_equal_nocase(key.c_str(), "input")
286 || string_equal_nocase(key.c_str(), "output"))
288 const char* name = tokeniser.getToken();
289 if(!string_equal(name, "("))
291 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
293 tokeniser.getToken();
294 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
295 const char* descriptionSeparator = tokeniser.getToken();
296 if(string_equal(descriptionSeparator, ":"))
298 CopiedString description;
299 EntityClassFGD_parseSplitString(tokeniser, description);
303 tokeniser.ungetToken();
305 tokeniser.nextLine();
310 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
311 CopiedString type = tokeniser.getToken();
312 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
314 if(string_equal_nocase(type.c_str(), "flags"))
316 EntityClassAttribute attribute;
318 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "="), PARSE_ERROR);
319 tokeniser.nextLine();
320 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "["), PARSE_ERROR);
321 tokeniser.nextLine();
324 const char* flag = tokeniser.getToken();
325 if(string_equal(flag, "]"))
327 tokeniser.nextLine();
332 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ":"), PARSE_ERROR);
334 tokeniser.getToken();
336 const char* defaultSeparator = tokeniser.getToken();
337 if(string_equal(defaultSeparator, ":"))
339 tokeniser.getToken();
341 const char* descriptionSeparator = tokeniser.getToken();
342 if(string_equal(descriptionSeparator, ":"))
344 EntityClassFGD_parseSplitString(tokeniser, attribute.m_description);
348 tokeniser.ungetToken();
354 tokeniser.ungetToken();
358 tokeniser.nextLine();
360 EntityClass_insertAttribute(*entityClass, key.c_str(), attribute);
362 else if(string_equal_nocase(type.c_str(), "choices"))
364 EntityClassAttribute attribute;
366 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ":"), PARSE_ERROR);
367 attribute.m_name = tokeniser.getToken();
368 const char* valueSeparator = tokeniser.getToken();
369 if(string_equal(valueSeparator, ":"))
371 const char* value = tokeniser.getToken();
372 if(!string_equal(value, ":"))
374 attribute.m_value = value;
378 tokeniser.ungetToken();
381 const char* descriptionSeparator = tokeniser.getToken();
382 if(string_equal(descriptionSeparator, ":"))
384 EntityClassFGD_parseSplitString(tokeniser, attribute.m_description);
388 tokeniser.ungetToken();
394 tokeniser.ungetToken();
396 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "="), PARSE_ERROR);
397 tokeniser.nextLine();
398 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "["), PARSE_ERROR);
399 tokeniser.nextLine();
401 StringOutputStream listTypeName(64);
402 listTypeName << entityClass->m_name.c_str() << "_" << attribute.m_name.c_str();
403 attribute.m_type = listTypeName.c_str();
405 ListAttributeType& listType = g_listTypesFGD[listTypeName.c_str()];
409 const char* value = tokeniser.getToken();
410 if(string_equal(value, "]"))
412 tokeniser.nextLine();
417 CopiedString tmp(value);
418 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ":"), PARSE_ERROR);
419 const char* name = tokeniser.getToken();
420 listType.push_back(name, tmp.c_str());
422 tokeniser.nextLine();
425 for(ListAttributeType::const_iterator i = listType.begin(); i != listType.end(); ++i)
427 if(string_equal(attribute.m_value.c_str(), (*i).first.c_str()))
429 attribute.m_value = (*i).second.c_str();
433 EntityClass_insertAttribute(*entityClass, key.c_str(), attribute);
435 else if(string_equal_nocase(type.c_str(), "decal"))
438 else if(string_equal_nocase(type.c_str(), "string")
439 || string_equal_nocase(type.c_str(), "integer")
440 || string_equal_nocase(type.c_str(), "studio")
441 || string_equal_nocase(type.c_str(), "sprite")
442 || string_equal_nocase(type.c_str(), "color255")
443 || string_equal_nocase(type.c_str(), "target_source")
444 || string_equal_nocase(type.c_str(), "target_destination")
445 || string_equal_nocase(type.c_str(), "sound")
447 || string_equal_nocase(type.c_str(), "angle")
448 || string_equal_nocase(type.c_str(), "origin")
449 || string_equal_nocase(type.c_str(), "float")
450 || string_equal_nocase(type.c_str(), "node_dest")
451 || string_equal_nocase(type.c_str(), "filterclass")
452 || string_equal_nocase(type.c_str(), "vector")
453 || string_equal_nocase(type.c_str(), "sidelist")
454 || string_equal_nocase(type.c_str(), "material")
455 || string_equal_nocase(type.c_str(), "vecline")
456 || string_equal_nocase(type.c_str(), "axis")
457 || string_equal_nocase(type.c_str(), "npcclass")
458 || string_equal_nocase(type.c_str(), "target_name_or_class")
459 || string_equal_nocase(type.c_str(), "pointentityclass")
460 || string_equal_nocase(type.c_str(), "scene"))
462 if(!string_equal(tokeniser.getToken(), "readonly"))
464 tokeniser.ungetToken();
467 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ":"), PARSE_ERROR);
468 const char* attributeType = "string";
469 if(string_equal_nocase(type.c_str(), "studio"))
471 attributeType = "model";
474 EntityClassAttribute attribute;
475 attribute.m_type = attributeType;
476 attribute.m_name = tokeniser.getToken();
478 const char* defaultSeparator = tokeniser.getToken();
479 if(string_equal(defaultSeparator, ":"))
481 const char* value = tokeniser.getToken();
482 if(!string_equal(value, ":"))
484 attribute.m_value = value;
488 tokeniser.ungetToken();
492 const char* descriptionSeparator = tokeniser.getToken();
493 if(string_equal(descriptionSeparator, ":"))
495 EntityClassFGD_parseSplitString(tokeniser, attribute.m_description);
499 tokeniser.ungetToken();
505 tokeniser.ungetToken();
507 EntityClass_insertAttribute(*entityClass, key.c_str(), attribute);
511 ERROR_MESSAGE("unknown key type: " << makeQuoted(type.c_str()));
513 tokeniser.nextLine();
518 EntityClassFGD_insertUniqueBase(entityClass);
522 EntityClassFGD_insertUnique(entityClass);
526 void EntityClassFGD_loadFile(const char* filename);
528 void EntityClassFGD_parse(TextInputStream& inputStream, const char* path)
530 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(inputStream);
532 tokeniser.nextLine();
536 const char* blockType = tokeniser.getToken();
541 if(string_equal(blockType, "@SolidClass"))
543 EntityClassFGD_parseClass(tokeniser, false, false);
545 else if(string_equal(blockType, "@BaseClass"))
547 EntityClassFGD_parseClass(tokeniser, false, true);
549 else if(string_equal(blockType, "@PointClass")
551 || string_equal(blockType, "@KeyFrameClass")
552 || string_equal(blockType, "@MoveClass")
553 || string_equal(blockType, "@FilterClass")
554 || string_equal(blockType, "@NPCClass"))
556 EntityClassFGD_parseClass(tokeniser, true, false);
559 else if(string_equal(blockType, "@include"))
561 StringOutputStream includePath(256);
562 includePath << StringRange(path, path_get_filename_start(path));
563 includePath << tokeniser.getToken();
564 EntityClassFGD_loadFile(includePath.c_str());
566 else if(string_equal(blockType, "@mapsize"))
568 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
570 tokeniser.getToken();
571 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ","), PARSE_ERROR);
573 tokeniser.getToken();
574 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
578 ERROR_MESSAGE("unknown block type: " << makeQuoted(blockType));
586 void EntityClassFGD_loadFile(const char* filename)
588 TextFileInputStream file(filename);
591 globalOutputStream() << "parsing entity classes from " << makeQuoted(filename) << "\n";
593 EntityClassFGD_parse(file, filename);
597 EntityClass* EntityClassFGD_findOrInsert(const char *name, bool has_brushes)
599 ASSERT_NOTNULL(name);
601 if(string_empty(name))
603 return g_EntityClassFGD_bad;
606 EntityClasses::iterator i = g_EntityClassFGD_classes.find(name);
607 if(i != g_EntityClassFGD_classes.end()
608 //&& string_equal((*i).first, name)
614 EntityClass* e = EntityClass_Create_Default(name, has_brushes);
615 return EntityClassFGD_insertUnique(e);
618 const ListAttributeType* EntityClassFGD_findListType(const char *name)
620 ListAttributeTypes::iterator i = g_listTypesFGD.find(name);
621 if(i != g_listTypesFGD.end())
630 void EntityClassFGD_resolveInheritance(EntityClass* derivedClass)
632 if(derivedClass->inheritanceResolved == false)
634 derivedClass->inheritanceResolved = true;
635 for(StringList::iterator j = derivedClass->m_parent.begin(); j != derivedClass->m_parent.end(); ++j)
637 BaseClasses::iterator i = g_EntityClassFGD_bases.find((*j).c_str());
638 if(i == g_EntityClassFGD_bases.end())
640 globalErrorStream() << "failed to find entityDef " << makeQuoted((*j).c_str()) << " inherited by " << makeQuoted(derivedClass->m_name.c_str()) << "\n";
644 EntityClass* parentClass = (*i).second;
645 EntityClassFGD_resolveInheritance(parentClass);
646 if(!derivedClass->colorSpecified)
648 derivedClass->colorSpecified = parentClass->colorSpecified;
649 derivedClass->color = parentClass->color;
651 if(!derivedClass->sizeSpecified)
653 derivedClass->sizeSpecified = parentClass->sizeSpecified;
654 derivedClass->mins = parentClass->mins;
655 derivedClass->maxs = parentClass->maxs;
658 for(EntityClassAttributes::iterator k = parentClass->m_attributes.begin(); k != parentClass->m_attributes.end(); ++k)
660 EntityClass_insertAttribute(*derivedClass, (*k).first.c_str(), (*k).second);
667 class EntityClassFGD : public ModuleObserver
669 std::size_t m_unrealised;
670 ModuleObservers m_observers;
672 EntityClassFGD() : m_unrealised(3)
677 if(--m_unrealised == 0)
679 StringOutputStream filename(256);
680 filename << GlobalRadiant().getGameToolsPath() << GlobalRadiant().getGameName() << "/halflife.fgd";
681 EntityClassFGD_loadFile(filename.c_str());
684 for(EntityClasses::iterator i = g_EntityClassFGD_classes.begin(); i != g_EntityClassFGD_classes.end(); ++i)
686 EntityClassFGD_resolveInheritance((*i).second);
687 if((*i).second->fixedsize && string_empty((*i).second->m_modelpath.c_str()))
689 if(!(*i).second->sizeSpecified)
691 globalErrorStream() << "size not specified for entity class: " << makeQuoted((*i).second->m_name.c_str()) << '\n';
693 if(!(*i).second->colorSpecified)
695 globalErrorStream() << "color not specified for entity class: " << makeQuoted((*i).second->m_name.c_str()) << '\n';
701 for(BaseClasses::iterator i = g_EntityClassFGD_bases.begin(); i != g_EntityClassFGD_bases.end(); ++i)
703 eclass_capture_state((*i).second);
707 m_observers.realise();
712 if(++m_unrealised == 1)
714 m_observers.unrealise();
715 EntityClassFGD_clear();
718 void attach(ModuleObserver& observer)
720 m_observers.attach(observer);
722 void detach(ModuleObserver& observer)
724 m_observers.detach(observer);
728 EntityClassFGD g_EntityClassFGD;
730 void EntityClassFGD_attach(ModuleObserver& observer)
732 g_EntityClassFGD.attach(observer);
734 void EntityClassFGD_detach(ModuleObserver& observer)
736 g_EntityClassFGD.detach(observer);
739 void EntityClassFGD_realise()
741 g_EntityClassFGD.realise();
743 void EntityClassFGD_unrealise()
745 g_EntityClassFGD.unrealise();
748 void EntityClassFGD_construct()
750 // start by creating the default unknown eclass
751 g_EntityClassFGD_bad = EClass_Create("UNKNOWN_CLASS", Vector3(0.0f, 0.5f, 0.0f), "");
753 EntityClassFGD_realise();
756 void EntityClassFGD_destroy()
758 EntityClassFGD_unrealise();
760 g_EntityClassFGD_bad->free(g_EntityClassFGD_bad);
763 class EntityClassFGDDependencies : public GlobalFileSystemModuleRef, public GlobalShaderCacheModuleRef, public GlobalRadiantModuleRef
767 class EntityClassFGDAPI
769 EntityClassManager m_eclassmanager;
771 typedef EntityClassManager Type;
772 STRING_CONSTANT(Name, "halflife");
776 EntityClassFGD_construct();
778 m_eclassmanager.findOrInsert = &EntityClassFGD_findOrInsert;
779 m_eclassmanager.findListType = &EntityClassFGD_findListType;
780 m_eclassmanager.forEach = &EntityClassFGD_forEach;
781 m_eclassmanager.attach = &EntityClassFGD_attach;
782 m_eclassmanager.detach = &EntityClassFGD_detach;
783 m_eclassmanager.realise = &EntityClassFGD_realise;
784 m_eclassmanager.unrealise = &EntityClassFGD_unrealise;
786 GlobalRadiant().attachGameToolsPathObserver(g_EntityClassFGD);
787 GlobalRadiant().attachGameNameObserver(g_EntityClassFGD);
791 GlobalRadiant().detachGameNameObserver(g_EntityClassFGD);
792 GlobalRadiant().detachGameToolsPathObserver(g_EntityClassFGD);
794 EntityClassFGD_destroy();
796 EntityClassManager* getTable()
798 return &m_eclassmanager;
802 #include "modulesystem/singletonmodule.h"
803 #include "modulesystem/moduleregistry.h"
805 typedef SingletonModule<EntityClassFGDAPI, EntityClassFGDDependencies> EntityClassFGDModule;
806 typedef Static<EntityClassFGDModule> StaticEntityClassFGDModule;
807 StaticRegisterModule staticRegisterEntityClassFGD(StaticEntityClassFGDModule::instance());