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_xml.h"
26 #include "ifilesystem.h"
29 #include "xml/xmlparser.h"
30 #include "generic/object.h"
31 #include "generic/reference.h"
32 #include "stream/stringstream.h"
33 #include "stream/textfilestream.h"
35 #include "eclasslib.h"
36 #include "modulesystem/moduleregistry.h"
39 #define PARSE_ERROR(elementName, name) makeQuoted(elementName) << " is not a valid child of " << makeQuoted(name)
46 IgnoreBreaks(const char* first, const char* last) : m_first(first), m_last(last)
51 template<typename TextOutputStreamType>
52 TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const IgnoreBreaks& ignoreBreaks)
54 for(const char* i = ignoreBreaks.m_first; i != ignoreBreaks.m_last; ++i)
67 class TreeXMLImporter : public TextOutputStream
70 virtual TreeXMLImporter& pushElement(const XMLElement& element) = 0;
71 virtual void popElement(const char* name) = 0;
74 template<typename Type>
77 char m_storage[sizeof(Type)];
81 return *reinterpret_cast<Type*>(m_storage);
83 const Type& get() const
85 return *reinterpret_cast<const Type*>(m_storage);
89 class BreakImporter : public TreeXMLImporter
92 BreakImporter(StringOutputStream& comment)
96 static const char* name()
100 TreeXMLImporter& pushElement(const XMLElement& element)
102 ERROR_MESSAGE(PARSE_ERROR(element.name(), name()));
105 void popElement(const char* elementName)
107 ERROR_MESSAGE(PARSE_ERROR(elementName, name()));
109 std::size_t write(const char* data, std::size_t length)
115 class AttributeImporter : public TreeXMLImporter
117 StringOutputStream& m_comment;
120 AttributeImporter(StringOutputStream& comment, EntityClass* entityClass, const XMLElement& element) : m_comment(comment)
122 const char* type = element.name();
123 const char* key = element.attribute("key");
124 const char* name = element.attribute("name");
125 const char* value = element.attribute("value");
127 ASSERT_MESSAGE(!string_empty(key), "key attribute not specified");
128 ASSERT_MESSAGE(!string_empty(name), "name attribute not specified");
130 if(string_equal(type, "flag"))
132 std::size_t bit = atoi(element.attribute("bit"));
133 ASSERT_MESSAGE(bit < MAX_FLAGS, "invalid flag bit");
134 ASSERT_MESSAGE(string_empty(entityClass->flagnames[bit]), "non-unique flag bit");
135 strcpy(entityClass->flagnames[bit], key);
141 EntityClass_insertAttribute(*entityClass, key, EntityClassAttribute(type, name, value));
146 TreeXMLImporter& pushElement(const XMLElement& element)
148 ERROR_MESSAGE(PARSE_ERROR(element.name(), "attribute"));
151 void popElement(const char* elementName)
153 ERROR_MESSAGE(PARSE_ERROR(elementName, "attribute"));
155 std::size_t write(const char* data, std::size_t length)
157 return m_comment.write(data, length);
161 bool attributeSupported(const char* name)
163 return string_equal(name, "real")
164 || string_equal(name, "integer")
165 || string_equal(name, "boolean")
166 || string_equal(name, "string")
167 || string_equal(name, "array")
168 || string_equal(name, "flag")
169 || string_equal(name, "real3")
170 || string_equal(name, "integer3")
171 || string_equal(name, "direction")
172 || string_equal(name, "angle")
173 || string_equal(name, "angles")
174 || string_equal(name, "color")
175 || string_equal(name, "target")
176 || string_equal(name, "targetname")
177 || string_equal(name, "sound")
178 || string_equal(name, "texture")
179 || string_equal(name, "model")
180 || string_equal(name, "skin")
181 || string_equal(name, "integer2");
184 typedef std::map<CopiedString, ListAttributeType> ListAttributeTypes;
186 bool listAttributeSupported(ListAttributeTypes& listTypes, const char* name)
188 return listTypes.find(name) != listTypes.end();
192 class ClassImporter : public TreeXMLImporter
194 EntityClassCollector& m_collector;
195 EntityClass* m_eclass;
196 StringOutputStream m_comment;
197 Storage<AttributeImporter> m_attribute;
198 ListAttributeTypes& m_listTypes;
201 ClassImporter(EntityClassCollector& collector, ListAttributeTypes& listTypes, const XMLElement& element) : m_collector(collector), m_listTypes(listTypes)
203 m_eclass = Eclass_Alloc();
204 m_eclass->free = &Eclass_Free;
206 const char* name = element.attribute("name");
207 ASSERT_MESSAGE(!string_empty(name), "name attribute not specified for class");
208 m_eclass->m_name = name;
210 const char* color = element.attribute("color");
211 ASSERT_MESSAGE(!string_empty(name), "color attribute not specified for class " << name);
212 string_parse_vector3(color, m_eclass->color);
213 eclass_capture_state(m_eclass);
215 const char* model = element.attribute("model");
216 if(!string_empty(model))
218 StringOutputStream buffer(256);
219 buffer << PathCleaned(model);
220 m_eclass->m_modelpath = buffer.c_str();
223 const char* type = element.name();
224 if(string_equal(type, "point"))
226 const char* box = element.attribute("box");
227 ASSERT_MESSAGE(!string_empty(box), "box attribute not found for class " << name);
228 m_eclass->fixedsize = true;
229 string_parse_vector(box, &m_eclass->mins.x(), &m_eclass->mins.x() + 6);
234 m_eclass->m_comments = m_comment.c_str();
235 m_collector.insert(m_eclass);
237 for(ListAttributeTypes::iterator i = m_listTypes.begin(); i != m_listTypes.end(); ++i)
239 m_collector.insert((*i).first.c_str(), (*i).second);
242 static const char* name()
246 TreeXMLImporter& pushElement(const XMLElement& element)
248 if(attributeSupported(element.name()) || listAttributeSupported(m_listTypes, element.name()))
250 constructor(m_attribute.get(), makeReference(m_comment), m_eclass, element);
251 return m_attribute.get();
255 ERROR_MESSAGE(PARSE_ERROR(element.name(), name()));
259 void popElement(const char* elementName)
261 if(attributeSupported(elementName) || listAttributeSupported(m_listTypes, elementName))
263 destructor(m_attribute.get());
267 ERROR_MESSAGE(PARSE_ERROR(elementName, name()));
270 std::size_t write(const char* data, std::size_t length)
272 return m_comment.write(data, length);
276 class ItemImporter : public TreeXMLImporter
279 ItemImporter(ListAttributeType& list, const XMLElement& element)
281 const char* name = element.attribute("name");
282 const char* value = element.attribute("value");
283 list.push_back(name, value);
285 TreeXMLImporter& pushElement(const XMLElement& element)
287 ERROR_MESSAGE(PARSE_ERROR(element.name(), "item"));
290 void popElement(const char* elementName)
292 ERROR_MESSAGE(PARSE_ERROR(elementName, "item"));
294 std::size_t write(const char* data, std::size_t length)
300 bool isItem(const char* name)
302 return string_equal(name, "item");
305 class ListAttributeImporter : public TreeXMLImporter
307 ListAttributeType* m_listType;
308 Storage<ItemImporter> m_item;
310 ListAttributeImporter(ListAttributeTypes& listTypes, const XMLElement& element)
312 const char* name = element.attribute("name");
313 m_listType = &listTypes[name];
315 TreeXMLImporter& pushElement(const XMLElement& element)
317 if(isItem(element.name()))
319 constructor(m_item.get(), makeReference(*m_listType), element);
324 ERROR_MESSAGE(PARSE_ERROR(element.name(), "list"));
328 void popElement(const char* elementName)
330 if(isItem(elementName))
332 destructor(m_item.get());
336 ERROR_MESSAGE(PARSE_ERROR(elementName, "list"));
339 std::size_t write(const char* data, std::size_t length)
345 bool classSupported(const char* name)
347 return string_equal(name, "group")
348 || string_equal(name, "point");
351 bool listSupported(const char* name)
353 return string_equal(name, "list");
356 class ClassesImporter : public TreeXMLImporter
358 EntityClassCollector& m_collector;
359 Storage<ClassImporter> m_class;
360 Storage<ListAttributeImporter> m_list;
361 ListAttributeTypes m_listTypes;
364 ClassesImporter(EntityClassCollector& collector) : m_collector(collector)
367 static const char* name()
371 TreeXMLImporter& pushElement(const XMLElement& element)
373 if(classSupported(element.name()))
375 constructor(m_class.get(), makeReference(m_collector), makeReference(m_listTypes), element);
376 return m_class.get();
378 else if(listSupported(element.name()))
380 constructor(m_list.get(), makeReference(m_listTypes), element);
385 ERROR_MESSAGE(PARSE_ERROR(element.name(), name()));
389 void popElement(const char* elementName)
391 if(classSupported(elementName))
393 destructor(m_class.get());
395 else if(listSupported(elementName))
397 destructor(m_list.get());
401 ERROR_MESSAGE(PARSE_ERROR(elementName, name()));
404 std::size_t write(const char* data, std::size_t length)
410 class EclassXMLImporter : public TreeXMLImporter
412 EntityClassCollector& m_collector;
413 Storage<ClassesImporter> m_classes;
416 EclassXMLImporter(EntityClassCollector& collector) : m_collector(collector)
419 static const char* name()
423 TreeXMLImporter& pushElement(const XMLElement& element)
425 if(string_equal(element.name(), ClassesImporter::name()))
427 constructor(m_classes.get(), makeReference(m_collector));
428 return m_classes.get();
432 ERROR_MESSAGE(PARSE_ERROR(element.name(), name()));
436 void popElement(const char* elementName)
438 if(string_equal(elementName, ClassesImporter::name()))
440 destructor(m_classes.get());
444 ERROR_MESSAGE(PARSE_ERROR(elementName, name()));
447 std::size_t write(const char* data, std::size_t length)
453 class TreeXMLImporterStack : public XMLImporter
455 std::vector< Reference<TreeXMLImporter> > m_importers;
457 TreeXMLImporterStack(TreeXMLImporter& importer)
459 m_importers.push_back(makeReference(importer));
461 void pushElement(const XMLElement& element)
463 m_importers.push_back(makeReference(m_importers.back().get().pushElement(element)));
465 void popElement(const char* name)
467 m_importers.pop_back();
468 m_importers.back().get().popElement(name);
470 std::size_t write(const char* buffer, std::size_t length)
472 return m_importers.back().get().write(buffer, length);
478 const char* GetExtension()
482 void ScanFile(EntityClassCollector& collector, const char *filename)
484 TextFileInputStream inputFile(filename);
485 if(!inputFile.failed())
487 XMLStreamParser parser(inputFile);
489 EclassXMLImporter importer(collector);
490 TreeXMLImporterStack stack(importer);
491 parser.exportXML(stack);
498 #include "modulesystem/singletonmodule.h"
500 class EntityClassXMLDependencies : public GlobalFileSystemModuleRef, public GlobalShaderCacheModuleRef
506 EntityClassScanner m_eclassxml;
508 typedef EntityClassScanner Type;
509 STRING_CONSTANT(Name, "xml");
513 m_eclassxml.scanFile = &ScanFile;
514 m_eclassxml.getExtension = &GetExtension;
516 EntityClassScanner* getTable()
522 typedef SingletonModule<EclassXMLAPI, EntityClassXMLDependencies> EclassXMLModule;
523 typedef Static<EclassXMLModule> StaticEclassXMLModule;
524 StaticRegisterModule staticRegisterEclassXML(StaticEclassXMLModule::instance());