Warning, file /games/kmuddy/plugins/mapper/filefilters/cmapfilefilterxml.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /***************************************************************************
0002                                cmapfilefilterxml.cpp
0003                              -------------------
0004     begin                : Tue Nov 19 2002
0005     copyright            : (C) 2002 by Kmud Developer Team
0006     email                : kmud-devel@kmud.de
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *                                                                         *
0011  *   This program is free software; you can redistribute it and/or modify  *
0012  *   it under the terms of the GNU General Public License as published by  *
0013  *   the Free Software Foundation; either version 2 of the License, or     *
0014  *   (at your option) any later version.                                   *
0015  *                                                                         *
0016  ***************************************************************************/
0017 
0018 #include "cmapfilefilterxml.h"
0019 
0020 #include <kmessagebox.h>
0021 #include <KLocalizedString>
0022 #include <kzip.h>
0023 
0024 #include <qfile.h>
0025 #include <qdom.h>
0026 #include <qfileinfo.h>
0027 #include <QDebug>
0028 
0029 #include <set>
0030 
0031 #include "../cmaproom.h"
0032 #include "../cmappath.h"
0033 #include "../cmaptext.h"
0034 #include "../cmapmanager.h"
0035 #include "../cmaplevel.h"
0036 #include "../cmapelementutil.h"
0037 #include "../cmappluginbase.h"
0038 
0039 CMapFileFilterXML::CMapFileFilterXML(CMapManager *manager) : CMapFileFilterBase(manager)
0040 {
0041 }
0042 
0043 CMapFileFilterXML::~CMapFileFilterXML()
0044 {
0045 }
0046 
0047 /** This returns name of the import/export filter. This should be kept small
0048   * @return The Name of the filter */
0049 QString CMapFileFilterXML::getName(void)
0050 {
0051     return "KMuddy map xml filles (*.mapxml)";
0052 }
0053 
0054 /** This returns a discription of the import/export filter
0055   * @return The discription */
0056 QString CMapFileFilterXML::getDescription(void)
0057 {
0058     return "KMuddy save/load filter";
0059 }
0060 
0061 /** This returns the extension  of the filename that will be loaded,created
0062   * @return The exstension */
0063 QString CMapFileFilterXML::getExtension(void)
0064 {
0065     return ".mapxml";
0066 }
0067 
0068 /** This returns the pattern extension of the filename that will be loaded,created
0069       * @return The exstension */
0070 QString CMapFileFilterXML::getPatternExtension(void)
0071 {
0072     return "*" + getExtension();
0073 }
0074 
0075 #include <iostream>
0076 
0077 /** This method should be reimplemented if this is a to be a export filter. It
0078   * is called to save the map data
0079   * @param url The url of the file to be saved
0080   * @return 0, The file was saved succesfully
0081   * @return -1, The file could not be created
0082   */
0083 int CMapFileFilterXML::saveData(const QString &filename)
0084 {
0085     // Create the archive
0086     KZip zip(filename); 
0087     if ( !zip.open( QIODevice::WriteOnly ) )
0088     {
0089 
0090         return -1;
0091     }
0092 
0093     zip.setCompression(KZip::DeflateCompression);
0094 
0095     // Create the xml map file
0096     QString result = saveXMLFile();
0097     if (!result.isEmpty())
0098     {
0099         qDebug() << "Write map.xml : " << result.size();
0100         zip.writeFile("map.xml", result.toLocal8Bit());
0101         qDebug() << "Done write";       
0102 
0103     }
0104     
0105     zip.close();
0106     
0107     return result.isEmpty() ? -1 : 0;
0108 }
0109 
0110 QString CMapFileFilterXML::saveXMLFile()
0111 {
0112   for (CMapPluginBase *plugin : m_mapManager->getPluginList())
0113     plugin->saveAboutToStart();
0114 
0115     // Create XML Document and add root node
0116     QDomDocument doc ("kmudmap");
0117     QDomElement root = doc.createElement ("kmudmap");
0118     doc.appendChild(root);
0119 
0120     // Write version
0121     QDomElement version = doc.createElement("Version");
0122     version.setAttribute("Major",1);
0123     version.setAttribute("Minor",0);
0124     root.appendChild(version);
0125 
0126     // Write main header
0127     
0128     // Write the zone
0129     CMapZone *rootZone = m_mapManager->getZone();
0130     saveZone(&doc,&root,rootZone);
0131 
0132     // Write Path Header
0133     QDomElement paths = doc.createElement("Paths");
0134     root.appendChild(paths);
0135 
0136     // Write Links Header
0137     QDomElement links = doc.createElement("Links");
0138     root.appendChild(links);
0139 
0140     // Write Paths
0141     saveZoneLinks(&doc,&paths,&links,m_mapManager->getZone());
0142 
0143     // Write Speedwalk list
0144 
0145     // return the result
0146         return doc.toString();
0147 }
0148 
0149 /**
0150  * This method is used to save the zone and all of it's sub elements
0151  * @param doc The document being the elemnts are saved too 
0152  * @param rootNode The XML node to save the zone to
0153  * @param zone The zone to save
0154  */
0155 void CMapFileFilterXML::saveZone(QDomDocument *doc,QDomNode *rootNode,CMapZone *zone)
0156 {
0157     // Save Zone
0158     QDomElement zoneProperties = doc->createElement("Zone");
0159     zone->saveQDomElement(doc,&zoneProperties);
0160     savePluginPropertiesForElement(zone,doc,&zoneProperties);   
0161     
0162     for (unsigned int idx = 0; idx < zone->levelCount(); ++idx)
0163     {
0164                 CMapLevel *level = zone->getLevel(idx);
0165         QDomElement levelProperties = doc->createElement("Level");
0166         levelProperties.setAttribute("ID",level->getLevelID());
0167         levelProperties.setAttribute("Number",level->getNumber());
0168         levelProperties.setAttribute("Name",level->getName());
0169         levelProperties.setAttribute("NumRooms",level->getRoomList()->count());
0170         levelProperties.setAttribute("NumTexts",level->getTextList()->count());
0171         
0172         // Save Rooms
0173         foreach (CMapRoom* room, *level->getRoomList())
0174         {
0175             QDomElement roomProperties = doc->createElement("Room");
0176             room->saveQDomElement(doc,&roomProperties);
0177             savePluginPropertiesForElement(room,doc,&roomProperties);
0178             levelProperties.appendChild(roomProperties);
0179         }
0180         
0181         // Save Texts
0182         foreach (CMapText* text, *level->getTextList())
0183         {
0184             QDomElement textProperties = doc->createElement("Text");
0185             text->saveQDomElement(doc,&textProperties);
0186             savePluginPropertiesForElement(text,doc,&textProperties);           
0187             levelProperties.appendChild(textProperties);    
0188         }
0189 
0190         zoneProperties.appendChild(levelProperties);
0191     }
0192 
0193     rootNode->appendChild(zoneProperties);
0194 }
0195 
0196 /**
0197   * This method is used to save all the paths in a zone
0198   * @param doc The document being the paths are saved too
0199   * @param rootNode The XML node to save the paths to
0200   * @param zone The zone to save
0201   */
0202 void CMapFileFilterXML::saveZoneLinks(QDomDocument *doc,QDomElement *pathsNode,QDomElement *linksNode,CMapZone *zone)
0203 {
0204   if (zone == nullptr)
0205     return;
0206 
0207   std::set<CMapPath *> saved; // this ensures that we don't save bi-dir paths twice
0208   for (unsigned int idx = 0; idx < zone->levelCount(); ++idx)
0209   {
0210     CMapLevel *level = zone->getLevel(idx);
0211     foreach (CMapRoom *room, *level->getRoomList())
0212     {
0213       foreach (CMapPath *path, *room->getPathList())
0214       {
0215         if (saved.count(path)) continue;
0216         QDomElement pathElement = doc->createElement("Path");
0217         path->saveQDomElement(doc,&pathElement);
0218         savePluginPropertiesForElement(path,doc,&pathElement);
0219         pathsNode->appendChild(pathElement);
0220         saved.insert(path);
0221       }
0222     }
0223 
0224     foreach (CMapText *text, *level->getTextList())
0225     {
0226       CMapElement *element = text->getLinkElement();
0227       if (element)
0228       {
0229         QDomElement linkElement = doc->createElement("Link");
0230 
0231         linkElement.setAttribute("SrcType",text->getElementType());
0232         linkElement.setAttribute("SrcLevel",text->getLevel()->getLevelID());
0233         linkElement.setAttribute("SrcID",text->getTextID());
0234         linkElement.setAttribute("DestType",element->getElementType());
0235         linkElement.setAttribute("DestLevel",element->getLevel()->getLevelID());
0236         if (element->getElementType()==ROOM)
0237         {
0238           linkElement.setAttribute("DestID",((CMapRoom *)element)->getRoomID());
0239           linkElement.setAttribute("LabelPos",(int)((CMapRoom *)element)->getLabelPosition());
0240         }
0241 
0242         linksNode->appendChild(linkElement);
0243       }
0244     }
0245   }
0246 }
0247 
0248 /** This method should be reimplemeted if this is to be a import filter. It is
0249   * called to load the map data
0250   * @param url The url of the file to be loaded
0251   * @return  0 , The file was loaded without problems
0252   *         -1 , Could not open the file
0253   *         -2 , If the file is corrupt
0254   *         -4 , Wrong version
0255   */
0256 int CMapFileFilterXML::loadData(const QString &filename)
0257 {
0258     KZip zip(filename);
0259     if ( !zip.open( QIODevice::ReadOnly ) )
0260     {
0261         return -1;
0262     }
0263 
0264     int result = -1;
0265     const KArchiveDirectory* dir = zip.directory();
0266     const KArchiveEntry *e = dir->entry("map.xml");
0267     if (e->isFile())
0268     {
0269         const KArchiveFile* mapFile = (KArchiveFile*)e;
0270 
0271         if (mapFile)
0272         {
0273             QByteArray arr( mapFile->data() );
0274             result = loadXMLData(arr);
0275         }
0276     }
0277 
0278     zip.close();
0279 
0280     return result;
0281 }
0282   
0283 /** This method should be reimplemeted if this is to be a import filter. It is
0284   * called to load the map data
0285   * @param url The url of the file to be loaded
0286   * @return  0 , The file was loaded without problems
0287   *         -1 , Could not open the file
0288   *         -2 , If the file is corrupt
0289   *         -4 , Wrong version
0290   */
0291 //int CMapFileFilterXML::loadXMLData(QString filename)
0292 int CMapFileFilterXML::loadXMLData(const QByteArray & buffer)
0293 {
0294   // TODO_jp : Make sure Zone ID and level ID max value is set corretly
0295   QDomDocument doc ("map");  
0296 
0297   if (!doc.setContent( buffer))
0298   {
0299     qDebug() << "Unable to open the map file, not a valid xml document";
0300     // file.close();
0301     return -1;
0302   }
0303 
0304   for (CMapPluginBase *plugin : m_mapManager->getPluginList())
0305     plugin->loadAboutToStart();
0306 
0307   QDomElement docElem = doc.documentElement();
0308 
0309   // Check that this version of the file can be loaded
0310   QDomElement e = readChildElement(&docElem,"Version");
0311   if (!e.isNull())
0312   {
0313     QString major = e.attribute("Major","");
0314     QString minor = e.attribute("Minor","");
0315     
0316     if (major != "1" || minor != "0")
0317     {
0318        //TODO_jp : Output error message
0319       qDebug() << "This version can't be loaded";
0320       return -4;
0321     }
0322   }
0323   else
0324   {
0325     //TODO_jp : Output error message
0326     qDebug() << "Unable to find version";    
0327     return -2;
0328   }
0329 
0330   // Find Root Zone
0331   QDomElement rootZoneNode = readChildElement(&docElem,"Zone");
0332   if (rootZoneNode.isNull())
0333   {
0334     //TODO_jp : Output error message
0335     qDebug() << "Unable to find root zone";
0336     return -2;
0337   }
0338 
0339   // Load Root Zone
0340   int errorZone =loadZone(&rootZoneNode);
0341   
0342   if (errorZone!=0)
0343     return errorZone;
0344 
0345   // Find Paths
0346   QDomElement pathsNode = readChildElement(&docElem,"Paths");
0347   if (pathsNode.isNull())
0348   {
0349     //TODO_jp : Output error message
0350     qDebug() << "Unable to find paths";
0351     return -2;
0352   }
0353 
0354   // Load Paths
0355   int errorPath = loadPaths(&pathsNode);
0356 
0357   if (errorPath!=0)
0358   {
0359     return errorPath;
0360   }
0361 
0362   // Find Links
0363   QDomElement linksNode = readChildElement(&docElem,"Links");
0364   if (pathsNode.isNull())
0365   {
0366     //TODO_jp : Output error message
0367     qDebug() << "Unable to find links";
0368     return -2;
0369   }
0370 
0371   int errorLinks = loadLinks(&linksNode);
0372   if (errorLinks)
0373     return errorLinks;
0374 
0375   // Return no error
0376   return 0;
0377 }
0378 
0379 /** This method is used to load all of the links
0380   * @param pathsNode The XML node to load the links from
0381   * @return  0 , The file was loaded without problems
0382   *         -2 , If the file is corrupt
0383   */
0384 int CMapFileFilterXML::loadLinks(QDomElement *pathsNode)
0385 {
0386     QDomNode n = pathsNode->firstChild();
0387     while (!n.isNull() )
0388     {
0389         QDomElement e = n.toElement();
0390 
0391         if (e.isNull() )
0392         {
0393             qDebug() << "Unable to find link element ";
0394             return -2;
0395         }
0396 
0397         if (e.tagName()=="Link")
0398         {
0399             
0400             int srcLevelID = e.attribute("SrcLevel","-2").toInt();
0401             int destLevelID = e.attribute("DestLevel","-2").toInt();
0402 
0403             if (srcLevelID == -2 || destLevelID == -2)
0404             {
0405                 qDebug() << "Unable to find link end points";
0406                 return -2;              
0407             }
0408 
0409             CMapLevel *srcLevel = m_mapManager->findLevel(srcLevelID);
0410             CMapLevel *destLevel = m_mapManager->findLevel(destLevelID);
0411 
0412 
0413             int textID = e.attribute("SrcID","-2").toInt();
0414             int destID = e.attribute("DestID","-2").toInt();
0415             int labelPos = e.attribute("LabelPos","0").toInt();
0416             int srcTyp = e.attribute("SrcType","-2").toInt();
0417             int destTyp = e.attribute("DestType","-2").toInt();
0418             
0419 
0420             if (textID == -2 || destID == -2 || destTyp == -2 || srcTyp == -2)
0421             {
0422                 qDebug() << "Unable to find link end points";
0423                 return -2;
0424             }
0425 
0426             if (srcTyp == (int)TEXT)
0427             {
0428                 CMapText *text = srcLevel->findText(textID);
0429                 if (destTyp==(int)ROOM)
0430                 {
0431                     CMapRoom *destElement = destLevel->findRoom(destID);
0432                     destElement->setLabelPosition((CMapRoom::labelPosTyp)labelPos,text);
0433                 }
0434             }            
0435         }
0436 
0437         n = n.nextSibling();
0438     }
0439 
0440     return 0;
0441 }
0442 
0443 
0444 /** This method is used to load all of the paths
0445   * @param pathsNode The XML node to load the paths from
0446   * @return  0 , The file was loaded without problems
0447   *         -2 , If the file is corrupt
0448   */  
0449 int CMapFileFilterXML::loadPaths(QDomElement *pathsNode)
0450 {
0451   bool first = true;
0452   QDomNode n;
0453   do {
0454     n = first ? pathsNode->firstChild() : n.nextSibling();
0455     if (n.isNull()) break;
0456     first = false;
0457     QDomElement e = n.toElement();
0458 
0459     if (e.tagName() != "Path") continue;
0460 
0461     int srcLevelID = e.attribute("SrcLevel","-2").toInt();
0462     int destLevelID = e.attribute("DestLevel","-2").toInt();
0463 
0464     if (srcLevelID == -2 || destLevelID == -2)
0465     {
0466       qDebug() << "Unable to find path end points";
0467       continue;
0468     }
0469     CMapLevel *srcLevel = m_mapManager->findLevel(srcLevelID);
0470     CMapLevel *destLevel = m_mapManager->findLevel(destLevelID);
0471 
0472     int srcRoomID = e.attribute("SrcRoom","-2").toInt();
0473     int destRoomID = e.attribute("DestRoom","-2").toInt();
0474 
0475     if (destRoomID == -2 || srcRoomID == -2)
0476     {
0477       qDebug() << "Unable to find path end points";
0478       continue;
0479     }
0480 
0481     CMapRoom *srcRoom = srcLevel->findRoom(srcRoomID);
0482     CMapRoom *destRoom = destLevel->findRoom(destRoomID);
0483 
0484     if (srcRoom==nullptr || destRoom==nullptr)
0485     {               
0486       qDebug() << "Src or Dest room is NULL while creating path";
0487       continue;
0488     }
0489 
0490     directionTyp srcDir = (directionTyp)e.attribute("SrcDir","0").toInt();
0491     directionTyp destDir = (directionTyp)e.attribute("DestDir","0").toInt();
0492     QString specialCmd = e.attribute("SpecialCmd", QString());
0493 
0494     if (srcRoom->getPathTarget(srcDir, specialCmd))
0495     {
0496       qDebug() << "Duplicate path, ignoring";
0497       continue;
0498     }
0499 
0500     CMapPath *path = m_mapManager->createPath(srcRoom,srcDir,destRoom,destDir,false,false);
0501     path->loadQDomElement(&e);
0502     loadPluginPropertiesForElement(path,&e);
0503   } while (!n.isNull());
0504   qDebug() << "loadPaths Here 4";   
0505 
0506   return 0;
0507 }
0508 
0509 /**
0510   * This method is used to load the zone and all of it's sub elememnts
0511   * @param zoneNode The XML node to load the zone from
0512   * @param intoLevel The level to create the zone in
0513   * @return  0 , The file was loaded without problems
0514   *         -2 , If the file is corrupt
0515   */  
0516 int CMapFileFilterXML::loadZone(QDomElement *zoneNode)
0517 {
0518   // Wipe the default level.
0519   while (m_mapManager->getZone()->levelCount())
0520     delete m_mapManager->getZone()->firstLevel();
0521 
0522   QDomNode n = zoneNode->firstChild();
0523   while (!n.isNull() )
0524   {
0525     QDomElement e = n.toElement();
0526 
0527     if (e.isNull() )
0528     {
0529       qDebug() << "Unable to find element ";
0530       return -2;
0531     }
0532 
0533     if (e.tagName()=="Level")
0534     {
0535       CMapLevel *level = m_mapManager->createLevel(UP);
0536       QString id = e.attribute("ID","-2");
0537       if (id=="-2")
0538       {
0539         qDebug() << "Unable to find level ID";
0540         return -2;
0541       }
0542       level->setLevelID(id.toInt());
0543       level->setName(e.attribute("Name", ""));
0544 
0545       QDomNode n2 = e.firstChild();
0546       while (!n2.isNull() )
0547       {
0548         QDomElement e2 = n2.toElement();
0549 
0550         if (e2.isNull() )
0551         {
0552           qDebug() << "Unable to find element ";
0553           return -2;
0554         }
0555 
0556         int x1 = e2.attribute("X",QString::number(-1)).toInt();
0557         int y1 = e2.attribute("Y",QString::number(-1)).toInt();         
0558 
0559         if (x1==-1 || y1==-1)
0560         {
0561           qDebug() << "Unable to find pos ";
0562           return -2;
0563         }
0564 
0565         if (e2.tagName()=="Room")
0566         {
0567           CMapRoom *room = CMapElementUtil::createRoom(m_mapManager, QPoint(x1, y1),level);
0568           if (!room) {
0569             qWarning()<<"NO ROOM AT "<<x1<<"/"<<y1<<" on level "<<id<<" WHEN LOADING ROOM #"<<e2.attribute("RoomID",QString::number(-1)).toInt();
0570             n2 = n2.nextSibling();
0571             continue;
0572           }
0573 
0574           room->loadQDomElement(&e2);
0575           loadPluginPropertiesForElement(room,&e2);
0576         }
0577         else if (e2.tagName()=="Text")
0578         {
0579           CMapText *text = CMapElementUtil::createText(m_mapManager, QPoint (x1,y1),level,"");
0580           if (!text) {
0581             qDebug() << "Unable to create text";
0582             n2 = n2.nextSibling();
0583             continue;
0584           }
0585           text->loadQDomElement(&e2);
0586           loadPluginPropertiesForElement(text,&e2);
0587         }
0588         else
0589         {
0590           qDebug() << "Unknown Type :  " << e2.tagName();
0591         }
0592 
0593         n2 = n2.nextSibling();
0594       }
0595     }
0596 
0597 
0598     n = n.nextSibling();
0599   }
0600 
0601   return 0;
0602 }
0603 
0604 /**
0605  * This method is used to read a child XML object of a XML object
0606  * @param parent The parent XML object
0607  * @param key The name of the child
0608  * @return The child node
0609  */
0610 QDomElement CMapFileFilterXML::readChildElement(QDomElement *parent,QString key)
0611 {
0612     QDomElement e;
0613     
0614     // Find Root Zone
0615     QDomNode n = parent->namedItem(key);
0616     if (n.isNull())
0617     {
0618         e.clear();
0619         return e;
0620     }
0621 
0622     e = n.toElement();
0623 
0624     return e;
0625 }
0626 
0627 /**
0628  * This method is used to save element properties that are stored in plugins
0629  * @param element The element being saved
0630  * @param doc The XML document
0631  * @param elProperties The xml properties of the element
0632  */
0633 void CMapFileFilterXML::savePluginPropertiesForElement(CMapElement *element,QDomDocument *doc,QDomElement *elProperties)
0634 {
0635     typedef QMap<QString, QString> EntryMap;
0636     
0637     QDomElement pluginsNode = doc->createElement ("plugins");
0638 
0639 
0640   for (CMapPluginBase *plugin : m_mapManager->getPluginList())
0641   {
0642     QDomElement pNode = doc->createElement(plugin->tagName());
0643     KMemConfig pluginProperties;
0644     plugin->saveElementProperties(element,&pluginProperties);
0645 
0646     EntryMap entries = pluginProperties.entryMap("Properties");
0647     for (EntryMap::ConstIterator it = entries.constBegin(); it != entries.constEnd(); ++it)
0648     {
0649       pNode.setAttribute(it.key(),it.value());
0650     }
0651 
0652     pluginsNode.appendChild(pNode);
0653   }
0654 
0655   elProperties->appendChild(pluginsNode);
0656 }
0657 
0658 /**
0659  * This method is used to load element properties that are stored in plugins
0660  * @param element The Element being loaded
0661  * @param elProperties the xml properties of the element
0662  */
0663 void CMapFileFilterXML::loadPluginPropertiesForElement(CMapElement *element,QDomElement *elProperties)
0664 {
0665     QDomElement pluginsNode = readChildElement(elProperties,"plugins");
0666     if (!pluginsNode.isNull())
0667     {
0668         QDomNode n = pluginsNode.firstChild();
0669         while (!n.isNull() )
0670         {
0671 
0672             QDomElement e = n.toElement();
0673 
0674             if (!e.isNull() )
0675             {
0676                           for (CMapPluginBase *plugin : m_mapManager->getPluginList())
0677                 {
0678                     if (plugin->tagName()==e.tagName())
0679                     {
0680                         KMemConfig pluginProperties;
0681 
0682                         QDomNamedNodeMap attribs = e.attributes();
0683 
0684                         for (int i=0; i<attribs.length();i++)
0685                         {
0686                             QDomNode n2 = attribs.item(i);
0687 
0688                             qDebug() << "Attrib " << n2.nodeName() << " = " << n2.nodeValue();
0689                             pluginProperties.group("Properties").writeEntry(n2.nodeName(),n2.nodeValue());
0690                             
0691                         }
0692                         
0693                         plugin->loadElementProperties(element,&pluginProperties);
0694                         break;  
0695                     }
0696                 }
0697             }
0698 
0699             n = n.nextSibling();
0700         }
0701     }
0702 }