]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - plugins/mapxml/xmlparse.cpp
Merge commit '830125fad042fad35dc029b6eb57c8156ad7e176'
[xonotic/netradiant.git] / plugins / mapxml / xmlparse.cpp
index f9093a5f01aee44214417fae947afd49743fe23f..044adb66ead780b0ea7a3d6e921c969cbf7dabf2 100644 (file)
+/*
+   Copyright (C) 2001-2006, William Joseph.
+   All Rights Reserved.
+
+   This file is part of GtkRadiant.
+
+   GtkRadiant is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   GtkRadiant is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GtkRadiant; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
 //
 // parses xml tree format into internal objects
 //
 
+#include "xmlparse.h"
 
-#include "plugin.h"
+#include <vector>
 
-void Patch_XMLParse(patchMesh_t *pPatch, xmlNodePtr surface)
-{
-  char *str, *content;
-  int i, j;
-
-  for(xmlNodePtr current = surface->children; current != NULL; current = current->next)
-  {
-    if(current->type != XML_ELEMENT_NODE) continue;
-    if(!strcmp((char *)current->name, "matrix"))
-    {
-      str = (char *)xmlGetProp(current, (xmlChar *)"width");
-      pPatch->width = atoi(str);
-      xmlFree(str);
-      str = (char *)xmlGetProp(current, (xmlChar *)"height");
-      pPatch->height = atoi(str);
-      xmlFree(str);
-
-      content = Q_StrDup((char *)current->children->content);
-
-      str = strtok(content, " \n\r\t\v\0");
-      for(i=0; i<pPatch->width; i++)
-      {
-        for(j=0; j<pPatch->height; j++)
-        {
-          pPatch->ctrl[i][j].xyz[0] = atof(str);
-          str = strtok(NULL, " \n\r\t\v\0");
-          pPatch->ctrl[i][j].xyz[1] = atof(str);
-          str = strtok(NULL, " \n\r\t\v\0");
-          pPatch->ctrl[i][j].xyz[2] = atof(str);
-          str = strtok(NULL, " \n\r\t\v\0");
-          pPatch->ctrl[i][j].st[0] = atof(str);
-          str = strtok(NULL, " \n\r\t\v\0");
-          pPatch->ctrl[i][j].st[1] = atof(str);
-          str = strtok(NULL, " \n\r\t\v\0");
-        }
-      }
-
-      delete [] content;
-    }
-    else if(!strcmp((char *)current->name, "shader")) {
-      pPatch->pShader = QERApp_Shader_ForName((char*)current->children->content);
-      pPatch->d_texture = pPatch->pShader->getTexture();
-    }
-  }
+#include "ientity.h"
+#include "ibrush.h"
+#include "ipatch.h"
+#include "ieclass.h"
+#include "eclasslib.h"
+
+#include "xml/xmlparser.h"
+#include "scenelib.h"
+#include "generic/reference.h"
+#include "generic/object.h"
+
+
+#define PARSE_ERROR "XML PARSE ERROR"
+
+
+inline XMLImporter* Node_getXMLImporter( scene::Node& node ){
+       return NodeTypeCast<XMLImporter>::cast( node );
 }
 
-void Face_XMLParse (face_t *face, xmlNodePtr surface)
-{
-  char *str, *content;
-  int i, j;
-
-  for(xmlNodePtr current = surface->children; current != NULL; current = current->next)
-  {
-    if(current->type != XML_ELEMENT_NODE) continue;
-    if(!strcmp((char *)current->name, "planepts"))
-    {
-      content = Q_StrDup((char *)current->children->content);
-
-      str = strtok(content, " \n\r\t\v\0");
-      for (i=0 ; i<3 ; i++)
-           {
-                   for (j=0 ; j<3 ; j++)
-                   {
-                           face->planepts[i][j] = atof(str);
-          str = strtok(NULL, " \n\r\t\v\0");
-                   }
-      }
-
-      delete [] content;
-    }
-    else if(!strcmp((char *)current->name, "texdef"))
-    {
-      content = Q_StrDup((char *)current->children->content);
-  
-      str = strtok(content, " \n\r\t\v\0");
-      face->texdef.shift[0] = atof(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->texdef.shift[1] = atof(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->texdef.rotate = atof(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->texdef.scale[0] = atof(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->texdef.scale[1] = atof(str);
-
-      delete [] content;
-    }
-    else if(!strcmp((char *)current->name, "bpmatrix"))
-    {
-      content = Q_StrDup((char *)current->children->content);
-  
-      str = strtok(content, " \n\r\t\v\0");
-      face->brushprimit_texdef.coords[0][0] = atof(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->brushprimit_texdef.coords[0][1] = atof(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->brushprimit_texdef.coords[0][2] = atof(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->brushprimit_texdef.coords[1][0] = atof(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->brushprimit_texdef.coords[1][1] = atof(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->brushprimit_texdef.coords[1][2] = atof(str);
-
-      delete [] content;
-    }
-    else if(!strcmp((char *)current->name, "flags"))
-    {
-      content = Q_StrDup((char *)current->children->content);
-
-      str = strtok(content, " \n\r\t\v\0");
-      face->texdef.contents = atoi(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->texdef.flags = atoi(str);
-      str = strtok(NULL, " \n\r\t\v\0");
-      face->texdef.value = atoi(str);
-
-      delete [] content;
-    }
-    else if(!strcmp((char *)current->name, "shader"))
-    {
-      face->texdef.SetName((char *)current->children->content);
-    }
-  }
+
+scene::Node& createPrimitive( const char* name ){
+       if ( string_equal( name, "brush" ) ) {
+               return GlobalBrushCreator().createBrush();
+       }
+       else if ( string_equal( name, "patch" ) ) {
+               return GlobalPatchCreator().createPatch();
+       }
+
+       ASSERT_MESSAGE( 0, PARSE_ERROR << ": primitive type not supported: \"" << name << "\"\n" );
+       scene::Node* node = 0;
+       return *node;
 }
 
-void Brush_XMLParse (brush_t *pBrush, xmlNodePtr primitive)
+class TreeXMLImporter : public XMLImporter
+{
+public:
+virtual TreeXMLImporter& child() = 0;
+};
+
+class SubPrimitiveImporter : public TreeXMLImporter
 {
-  face_t    *f;
-
-  for(xmlNodePtr current = primitive->children; current != NULL; current = current->next)
-  {
-    if(current->type != XML_ELEMENT_NODE) continue;
-    f = pBrush->brush_faces;
-    pBrush->brush_faces = Face_Alloc();
-    Face_XMLParse(pBrush->brush_faces, current);
-    pBrush->brush_faces->next = f;
-  }
+XMLImporter* m_importer;
+public:
+SubPrimitiveImporter( XMLImporter* importer ) : m_importer( importer ){
+}
+void pushElement( const XMLElement& element ){
+       m_importer->pushElement( element );
+}
+void popElement( const char* name ){
+       m_importer->popElement( name );
+}
+std::size_t write( const char* buffer, std::size_t length ){
+       return m_importer->write( buffer, length );
+}
+SubPrimitiveImporter& child(){
+       return *this;
 }
+};
 
-void Entity_XMLParse(entity_t *pEntity, xmlNodePtr entity)
+class PrimitiveImporter : public TreeXMLImporter
 {
-  brush_t *pBrush;
-
-  for(xmlNodePtr current = entity->children; current != NULL; current = current->next)
-  {
-    if(current->type != XML_ELEMENT_NODE) continue;
-    if(!strcmp((char *)current->name, "epair"))
-    {
-      char *key = (char *)xmlGetProp(current, (xmlChar *)"key");
-      char *value = (char *)xmlGetProp(current, (xmlChar *)"value");
-      SetKeyValue(pEntity, key, value);
-      xmlFree(key);
-      xmlFree(value);
-    }
-    else if(strcmp((char *)current->name, "brush") == 0)
-    {
-      pBrush = Brush_Alloc();
-      Brush_XMLParse(pBrush, current);
-      ((CPtrArray*)pEntity->pData)->Add(pBrush);
-    }
-    else if(strcmp((char *)current->name, "patch") == 0)
-    {
-      pBrush = Brush_Alloc();
-      pBrush->patchBrush = true;
-      pBrush->pPatch = Patch_Alloc();
-      pBrush->pPatch->pSymbiot = pBrush;
-      Patch_XMLParse(pBrush->pPatch, current);
-      ((CPtrArray*)pEntity->pData)->Add(pBrush);
-    }
-  }
+scene::Node& m_parent;
+XMLImporter* m_importer;
+char m_child[sizeof( SubPrimitiveImporter )];
+
+SubPrimitiveImporter& subprimitive(){
+       return *reinterpret_cast<SubPrimitiveImporter*>( m_child );
+}
+public:
+PrimitiveImporter( scene::Node& parent ) : m_parent( parent ), m_importer( 0 ){
 }
+void pushElement( const XMLElement& element ){
+       if ( string_equal( element.name(), "epair" ) ) {
+               ASSERT_MESSAGE( string_equal( element.name(), "epair" ), PARSE_ERROR );
+               Node_getEntity( m_parent )->setKeyValue( element.attribute( "key" ), element.attribute( "value" ) );
+       }
+       else
+       {
+               NodeSmartReference node( createPrimitive( element.name() ) );
+
+               m_importer = Node_getXMLImporter( node );
 
-void Map_XMLRead(CPtrArray *map, xmlNodePtr map_node)
+               constructor( subprimitive(), m_importer );
+
+               m_importer->pushElement( element );
+
+               Node_getTraversable( m_parent )->insert( node );
+       }
+}
+void popElement( const char* name ){
+       if ( string_equal( name, "epair" ) ) {
+       }
+       else
+       {
+               m_importer->popElement( name );
+
+               destructor( subprimitive() );
+               m_importer = 0;
+       }
+}
+std::size_t write( const char* buffer, std::size_t length ){
+       return m_importer->write( buffer, length );
+}
+TreeXMLImporter& child(){
+       return subprimitive();
+}
+};
+
+class EntityImporter : public TreeXMLImporter
 {
-  entity_t *pEntity;
-  xmlNodePtr current;
-
-  for(current = map_node->children; current != NULL; current = current->next)
-  {
-    if(current->type != XML_ELEMENT_NODE) continue;
-    pEntity = Entity_Alloc();
-    pEntity->pData = new CPtrArray;
-    Entity_XMLParse(pEntity, current);
-    map->Add(pEntity);
-  }
+scene::Node& m_parent;
+char m_node[sizeof( NodeSmartReference )];
+char m_child[sizeof( PrimitiveImporter )];
+EntityCreator& m_entityTable;
+
+NodeSmartReference& node(){
+       return *reinterpret_cast<NodeSmartReference*>( m_node );
+}
+PrimitiveImporter& primitive(){
+       return *reinterpret_cast<PrimitiveImporter*>( m_child );
 }
 
-// SPoG
-// temporarily copied from qe3.cpp
-// duplicate code starts here (note: g_strAppPath swapped for g_FuncTable.m_pfnGetQERPath())
+public:
+EntityImporter( scene::Node& parent, EntityCreator& entityTable ) : m_parent( parent ), m_entityTable( entityTable ){
+}
+void pushElement( const XMLElement& element ){
+       ASSERT_MESSAGE( string_equal( element.name(), "entity" ), PARSE_ERROR );
+       constructor( node(), NodeSmartReference( m_entityTable.createEntity( GlobalEntityClassManager().findOrInsert( "", true ) ) ) );
+       constructor( primitive(), makeReference( node().get() ) );
+}
+void popElement( const char* name ){
+       ASSERT_MESSAGE( string_equal( name, "entity" ), PARSE_ERROR );
+       NodeSmartReference entity( m_entityTable.createEntity( GlobalEntityClassManager().findOrInsert( Node_getEntity( node() )->getKeyValue( "classname" ), node_is_group( node() ) ) ) );
+
+       {
+               EntityCopyingVisitor visitor( *Node_getEntity( entity ) );
+               Node_getEntity( node() )->forEachKeyValue( visitor );
+       }
+
+       if ( Node_getTraversable( entity ) != 0 && !Node_getEntity( entity )->getEntityClass().fixedsize ) {
+               parentBrushes( node(), entity );
+       }
 
-void HandleXMLError( void* ctxt, const char* text, ... )
+       Node_getTraversable( m_parent )->insert( entity );
+
+       destructor( primitive() );
+       destructor( node() );
+}
+std::size_t write( const char* buffer, std::size_t length ){
+       return length;
+}
+TreeXMLImporter& child(){
+       return primitive();
+}
+};
+
+class MapDoom3Importer : public TreeXMLImporter
 {
-  va_list argptr;
-  static char buf[32768];
+scene::Node& m_root;
+char m_child[sizeof( EntityImporter )];
+EntityCreator& m_entityTable;
 
-  va_start (argptr,text);
-  vsprintf (buf, text, argptr);
-  Sys_FPrintf (SYS_ERR, "XML %s\n", buf);
-  va_end (argptr);
+EntityImporter& getEntity(){
+       return *reinterpret_cast<EntityImporter*>( m_child );
+}
+public:
+MapDoom3Importer( scene::Node& root, EntityCreator& entityTable ) : m_root( root ), m_entityTable( entityTable ){
+}
+void pushElement( const XMLElement& element ){
+       ASSERT_MESSAGE( string_equal( element.name(), "mapdoom3" ), PARSE_ERROR );
+       constructor( getEntity(), makeReference( m_root ), makeReference( m_entityTable ) );
+}
+void popElement( const char* name ){
+       ASSERT_MESSAGE( string_equal( name, "mapdoom3" ), PARSE_ERROR );
+       destructor( getEntity() );
+}
+std::size_t write( const char* data, std::size_t length ){
+       return length;
 }
+TreeXMLImporter& child(){
+       return getEntity();
+}
+};
 
-#define DTD_BUFFER_LENGTH 1024
-xmlDocPtr ParseXMLStream(IDataStream *stream, bool validate = false)
+class TreeXMLImporterStack : public XMLImporter
 {
-  xmlDocPtr doc = NULL;
-  bool wellFormed = false, valid = false;
-  int res, size = 1024;
-  char chars[1024];
-  xmlParserCtxtPtr ctxt;
-
-  // SPoG
-  // HACK: use AppPath to resolve DTD location
-  // do a buffer-safe string copy and concatenate
-  int i;
-  char* w;
-  const char* r;
-  char buf[DTD_BUFFER_LENGTH];
-
-  w = buf;
-  i = 0;
-  // copy
-  //assert(g_FuncTable.m_pfnGetQERPath() != NULL);
-  for(r = g_FuncTable.m_pfnGetQERPath(); i<DTD_BUFFER_LENGTH && *r != '\0'; i++, r++)  w[i] = *r;
-  // concatenate
-  for(r = "dtds/"; i<DTD_BUFFER_LENGTH && *r != '\0'; i++, r++)  w[i] = *r;
-  // terminate
-  w[i] = '\0';
-
-  if(i == DTD_BUFFER_LENGTH)
-  {
-    HandleXMLError(NULL, "ERROR: buffer overflow: DTD path length too large\n");
-    return NULL;
-  }
-
-  //if(validate)
-  //  xmlDoValidityCheckingDefaultValue = 1;
-  //else
-    xmlDoValidityCheckingDefaultValue = 0;
-
-  xmlSetGenericErrorFunc(NULL, HandleXMLError);
-
-  res = stream->Read(chars, 4);
-  if (res > 0)
-  {
-    ctxt = xmlCreatePushParserCtxt(NULL, NULL, chars, res, buf);
-
-    while ((res = stream->Read(chars, size)) > 0)
-    {
-      xmlParseChunk(ctxt, chars, res, 0);
-    }
-    xmlParseChunk(ctxt, chars, 0, 1);
-    doc = ctxt->myDoc;
-
-    wellFormed = (ctxt->wellFormed == 1);
-    valid = (ctxt->valid == 1);
-
-    xmlFreeParserCtxt(ctxt);
-  }
-
-  if(wellFormed && (!validate || (validate && valid)))
-    return doc;
-
-  if(doc != NULL)
-    xmlFreeDoc(doc);
-
-  return NULL;
+std::vector< Reference<TreeXMLImporter> > m_importers;
+public:
+TreeXMLImporterStack( TreeXMLImporter& importer ){
+       m_importers.push_back( makeReference( importer ) );
+}
+void pushElement( const XMLElement& element ){
+       m_importers.back().get().pushElement( element );
+       m_importers.push_back( makeReference( m_importers.back().get().child() ) );
+}
+void popElement( const char* name ){
+       m_importers.pop_back();
+       m_importers.back().get().popElement( name );
 }
+std::size_t write( const char* buffer, std::size_t length ){
+       return ( *( m_importers.end() - 2 ) ).get().write( buffer, length );
+}
+};
 
-// duplicate code ends here
 
-void Map_Read (IDataStream *in, CPtrArray *map)
-{
-  xmlDocPtr doc;
-
-  doc = ParseXMLStream(in, false ); // quick hack while dtd validation is broken
-
-  if(doc != NULL)
-  {
-    xmlNodePtr node=doc->children;
-    while(node != NULL && node->type != XML_ELEMENT_NODE) node=node->next;
-    if(node != NULL)
-      Map_XMLRead(map, node);
-  }
-  xmlFreeDoc(doc);
+void Map_Read( scene::Node& root, TextInputStream& in, EntityCreator& entityTable ){
+       XMLStreamParser parser( in );
+
+       MapDoom3Importer importer( root, entityTable );
+       TreeXMLImporterStack stack( importer );
+       parser.exportXML( stack );
 }