Merge branch 'master' into divVerent/farplanedist-sky-fix
[xonotic/netradiant.git] / plugins / mapxml / xmlparse.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 //
23 // parses xml tree format into internal objects
24 //
25
26 #include "xmlparse.h"
27
28 #include <vector>
29
30 #include "ientity.h"
31 #include "ibrush.h"
32 #include "ipatch.h"
33 #include "ieclass.h"
34 #include "eclasslib.h"
35
36 #include "xml/xmlparser.h"
37 #include "scenelib.h"
38 #include "generic/reference.h"
39 #include "generic/object.h"
40
41
42 #define PARSE_ERROR "XML PARSE ERROR"
43
44
45 inline XMLImporter* Node_getXMLImporter( scene::Node& node ){
46         return NodeTypeCast<XMLImporter>::cast( node );
47 }
48
49
50 scene::Node& createPrimitive( const char* name ){
51         if ( string_equal( name, "brush" ) ) {
52                 return GlobalBrushCreator().createBrush();
53         }
54         else if ( string_equal( name, "patch" ) ) {
55                 return GlobalPatchCreator().createPatch();
56         }
57
58         ASSERT_MESSAGE( 0, PARSE_ERROR << ": primitive type not supported: \"" << name << "\"\n" );
59         scene::Node* node = 0;
60         return *node;
61 }
62
63 class TreeXMLImporter : public XMLImporter
64 {
65 public:
66 virtual TreeXMLImporter& child() = 0;
67 };
68
69 class SubPrimitiveImporter : public TreeXMLImporter
70 {
71 XMLImporter* m_importer;
72 public:
73 SubPrimitiveImporter( XMLImporter* importer ) : m_importer( importer ){
74 }
75 void pushElement( const XMLElement& element ){
76         m_importer->pushElement( element );
77 }
78 void popElement( const char* name ){
79         m_importer->popElement( name );
80 }
81 std::size_t write( const char* buffer, std::size_t length ){
82         return m_importer->write( buffer, length );
83 }
84 SubPrimitiveImporter& child(){
85         return *this;
86 }
87 };
88
89 class PrimitiveImporter : public TreeXMLImporter
90 {
91 scene::Node& m_parent;
92 XMLImporter* m_importer;
93 char m_child[sizeof( SubPrimitiveImporter )];
94
95 SubPrimitiveImporter& subprimitive(){
96         return *reinterpret_cast<SubPrimitiveImporter*>( m_child );
97 }
98 public:
99 PrimitiveImporter( scene::Node& parent ) : m_parent( parent ), m_importer( 0 ){
100 }
101 void pushElement( const XMLElement& element ){
102         if ( string_equal( element.name(), "epair" ) ) {
103                 ASSERT_MESSAGE( string_equal( element.name(), "epair" ), PARSE_ERROR );
104                 Node_getEntity( m_parent )->setKeyValue( element.attribute( "key" ), element.attribute( "value" ) );
105         }
106         else
107         {
108                 NodeSmartReference node( createPrimitive( element.name() ) );
109
110                 m_importer = Node_getXMLImporter( node );
111
112                 constructor( subprimitive(), m_importer );
113
114                 m_importer->pushElement( element );
115
116                 Node_getTraversable( m_parent )->insert( node );
117         }
118 }
119 void popElement( const char* name ){
120         if ( string_equal( name, "epair" ) ) {
121         }
122         else
123         {
124                 m_importer->popElement( name );
125
126                 destructor( subprimitive() );
127                 m_importer = 0;
128         }
129 }
130 std::size_t write( const char* buffer, std::size_t length ){
131         return m_importer->write( buffer, length );
132 }
133 TreeXMLImporter& child(){
134         return subprimitive();
135 }
136 };
137
138 class EntityImporter : public TreeXMLImporter
139 {
140 scene::Node& m_parent;
141 char m_node[sizeof( NodeSmartReference )];
142 char m_child[sizeof( PrimitiveImporter )];
143 EntityCreator& m_entityTable;
144
145 NodeSmartReference& node(){
146         return *reinterpret_cast<NodeSmartReference*>( m_node );
147 }
148 PrimitiveImporter& primitive(){
149         return *reinterpret_cast<PrimitiveImporter*>( m_child );
150 }
151
152 public:
153 EntityImporter( scene::Node& parent, EntityCreator& entityTable ) : m_parent( parent ), m_entityTable( entityTable ){
154 }
155 void pushElement( const XMLElement& element ){
156         ASSERT_MESSAGE( string_equal( element.name(), "entity" ), PARSE_ERROR );
157         constructor( node(), NodeSmartReference( m_entityTable.createEntity( GlobalEntityClassManager().findOrInsert( "", true ) ) ) );
158         constructor( primitive(), makeReference( node().get() ) );
159 }
160 void popElement( const char* name ){
161         ASSERT_MESSAGE( string_equal( name, "entity" ), PARSE_ERROR );
162         NodeSmartReference entity( m_entityTable.createEntity( GlobalEntityClassManager().findOrInsert( Node_getEntity( node() )->getKeyValue( "classname" ), node_is_group( node() ) ) ) );
163
164         {
165                 EntityCopyingVisitor visitor( *Node_getEntity( entity ) );
166                 Node_getEntity( node() )->forEachKeyValue( visitor );
167         }
168
169         if ( Node_getTraversable( entity ) != 0 && !Node_getEntity( entity )->getEntityClass().fixedsize ) {
170                 parentBrushes( node(), entity );
171         }
172
173         Node_getTraversable( m_parent )->insert( entity );
174
175         destructor( primitive() );
176         destructor( node() );
177 }
178 std::size_t write( const char* buffer, std::size_t length ){
179         return length;
180 }
181 TreeXMLImporter& child(){
182         return primitive();
183 }
184 };
185
186 class MapDoom3Importer : public TreeXMLImporter
187 {
188 scene::Node& m_root;
189 char m_child[sizeof( EntityImporter )];
190 EntityCreator& m_entityTable;
191
192 EntityImporter& getEntity(){
193         return *reinterpret_cast<EntityImporter*>( m_child );
194 }
195 public:
196 MapDoom3Importer( scene::Node& root, EntityCreator& entityTable ) : m_root( root ), m_entityTable( entityTable ){
197 }
198 void pushElement( const XMLElement& element ){
199         ASSERT_MESSAGE( string_equal( element.name(), "mapdoom3" ), PARSE_ERROR );
200         constructor( getEntity(), makeReference( m_root ), makeReference( m_entityTable ) );
201 }
202 void popElement( const char* name ){
203         ASSERT_MESSAGE( string_equal( name, "mapdoom3" ), PARSE_ERROR );
204         destructor( getEntity() );
205 }
206 std::size_t write( const char* data, std::size_t length ){
207         return length;
208 }
209 TreeXMLImporter& child(){
210         return getEntity();
211 }
212 };
213
214 class TreeXMLImporterStack : public XMLImporter
215 {
216 std::vector< Reference<TreeXMLImporter> > m_importers;
217 public:
218 TreeXMLImporterStack( TreeXMLImporter& importer ){
219         m_importers.push_back( makeReference( importer ) );
220 }
221 void pushElement( const XMLElement& element ){
222         m_importers.back().get().pushElement( element );
223         m_importers.push_back( makeReference( m_importers.back().get().child() ) );
224 }
225 void popElement( const char* name ){
226         m_importers.pop_back();
227         m_importers.back().get().popElement( name );
228 }
229 std::size_t write( const char* buffer, std::size_t length ){
230         return ( *( m_importers.end() - 2 ) ).get().write( buffer, length );
231 }
232 };
233
234
235 void Map_Read( scene::Node& root, TextInputStream& in, EntityCreator& entityTable ){
236         XMLStreamParser parser( in );
237
238         MapDoom3Importer importer( root, entityTable );
239         TreeXMLImporterStack stack( importer );
240         parser.exportXML( stack );
241 }