zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

XMLwrapper.cpp (22398B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   XMLwrapper.cpp - XML wrapper
      5   Copyright (C) 2003-2005 Nasca Octavian Paul
      6   Copyright (C) 2009-2009 Mark McCurry
      7   Author: Nasca Octavian Paul
      8           Mark McCurry
      9 
     10   This program is free software; you can redistribute it and/or
     11   modify it under the terms of the GNU General Public License
     12   as published by the Free Software Foundation; either version 2
     13   of the License, or (at your option) any later version.
     14 */
     15 
     16 #include "XMLwrapper.h"
     17 #include <cstring>
     18 #include <stdio.h>
     19 #include <stdlib.h>
     20 #include <cstdarg>
     21 #include <zlib.h>
     22 #include <iostream>
     23 #include <sstream>
     24 
     25 #include "globals.h"
     26 #include "Util.h"
     27 
     28 using namespace std;
     29 
     30 namespace zyn {
     31 
     32 int  xml_k   = 0;
     33 bool verbose = false;
     34 
     35 #if MXML_MAJOR_VERSION <= 3
     36 // Mimic datatypes present in mxml4 for compatibility
     37 typedef int mxml_ws_t;
     38 typedef int mxml_descend_t;
     39 // Mimic typenames present in mxml4 for compatibility
     40 constexpr int MXML_DESCEND_ALL = MXML_DESCEND;
     41 constexpr int MXML_DESCEND_NONE = MXML_NO_DESCEND;
     42 constexpr int MXML_TYPE_OPAQUE = MXML_OPAQUE;
     43 constexpr int MXML_TYPE_ELEMENT = MXML_ELEMENT;
     44 constexpr int MXML_TYPE_TEXT = MXML_TEXT;
     45 #endif
     46 
     47 const char *XMLwrapper_whitespace_callback(void*, mxml_node_t *node, mxml_ws_t where)
     48 {
     49 #if MXML_MAJOR_VERSION >= 4
     50     // New node types in MXL4
     51     if(mxmlGetDirective(node))  // "?xml" directive
     52         return "\n";
     53     else if(mxmlGetDeclaration(node))  // "!DOCTYPE" declaration
     54         return nullptr;
     55 #endif
     56 
     57     const char *name = mxmlGetElement(node);
     58     assert(name);
     59 
     60     if((where == MXML_WS_BEFORE_OPEN) && (!strcmp(name, "?xml")))
     61         return NULL;
     62     if((where == MXML_WS_BEFORE_CLOSE) && (!strcmp(name, "string")))
     63         return NULL;
     64 
     65     if((where == MXML_WS_BEFORE_OPEN) || (where == MXML_WS_BEFORE_CLOSE))
     66         /*	const char *tmp=node->value.element.name;
     67             if (tmp!=NULL) {
     68                 if ((strstr(tmp,"par")!=tmp)&&(strstr(tmp,"string")!=tmp)) {
     69                 printf("%s ",tmp);
     70                 if (where==MXML_WS_BEFORE_OPEN) xml_k++;
     71                 if (where==MXML_WS_BEFORE_CLOSE) xml_k--;
     72                 if (xml_k>=STACKSIZE) xml_k=STACKSIZE-1;
     73                 if (xml_k<0) xml_k=0;
     74                 printf("%d\n",xml_k);
     75                 printf("\n");
     76                 };
     77 
     78             };
     79             int i=0;
     80             for (i=1;i<xml_k;i++) tabs[i]='\t';
     81             tabs[0]='\n';tabs[i+1]='\0';
     82             if (where==MXML_WS_BEFORE_OPEN) return(tabs);
     83                 else return("\n");
     84         */
     85         return "\n";
     86     ;
     87 
     88     return 0;
     89 }
     90 
     91 #if MXML_MAJOR_VERSION <= 3
     92 // Wrapper, because int and mxml_ws_t are different types
     93 inline const char *XMLwrapper_whitespace_callback(mxml_node_t *node, int where)
     94 {
     95     return XMLwrapper_whitespace_callback(nullptr, node, where);
     96 }
     97 #endif
     98 
     99 //temporary const overload of mxmlFindElement
    100 const mxml_node_t *mxmlFindElement(const mxml_node_t *node,
    101                                    const mxml_node_t *top,
    102                                    const char *name,
    103                                    const char *attr,
    104                                    const char *value,
    105                                    mxml_descend_t descend)
    106 {
    107     return const_cast<const mxml_node_t *>(mxmlFindElement(
    108                                                const_cast<mxml_node_t *>(node),
    109                                                const_cast<mxml_node_t *>(top),
    110                                                name, attr, value, descend));
    111 }
    112 
    113 //temporary const overload of mxmlElementGetAttr
    114 const char *mxmlElementGetAttr(const mxml_node_t *node, const char *name)
    115 {
    116     return mxmlElementGetAttr(const_cast<mxml_node_t *>(node), name);
    117 }
    118 
    119 XMLwrapper::XMLwrapper()
    120 {
    121     minimal = true;
    122     SaveFullXml=false;
    123 
    124     node = tree = mxmlNewXML("1.0");
    125     assert(node);
    126     /*  for mxml 2.1f (and older)
    127         tree=mxmlNewElement(MXML_NO_PARENT,"?xml");
    128         mxmlElementSetAttr(tree,"version","1.0f");
    129         mxmlElementSetAttr(tree,"encoding","UTF-8");
    130     */
    131 
    132     mxml_node_t *doctype =
    133 #if MXML_MAJOR_VERSION <= 3
    134         mxmlNewElement(tree, "!DOCTYPE");
    135         mxmlElementSetAttr(doctype, "ZynAddSubFX-data", NULL);
    136 #else
    137         mxmlNewDeclaration(tree, "DOCTYPE ZynAddSubFX-data");
    138 #endif
    139     assert(doctype);
    140 
    141     node = root = addparams("ZynAddSubFX-data", 4,
    142                             "version-major", stringFrom<int>(
    143                                 version.get_major()).c_str(),
    144                             "version-minor", stringFrom<int>(
    145                                 version.get_minor()).c_str(),
    146                             "version-revision",
    147                             stringFrom<int>(version.get_revision()).c_str(),
    148                             "ZynAddSubFX-author", "Nasca Octavian Paul");
    149 
    150     //make the empty branch that will contain the information parameters
    151     info = addparams("INFORMATION", 0);
    152 
    153     //save zynaddsubfx specifications
    154     beginbranch("BASE_PARAMETERS");
    155     addpar("max_midi_parts", NUM_MIDI_PARTS);
    156     addpar("max_kit_items_per_instrument", NUM_KIT_ITEMS);
    157 
    158     addpar("max_system_effects", NUM_SYS_EFX);
    159     addpar("max_insertion_effects", NUM_INS_EFX);
    160     addpar("max_instrument_effects", NUM_PART_EFX);
    161 
    162     addpar("max_addsynth_voices", NUM_VOICES);
    163     endbranch();
    164 }
    165 
    166 void
    167 XMLwrapper::cleanup(void)
    168 {
    169     if(tree)
    170         mxmlDelete(tree);
    171 
    172     /* make sure freed memory is not referenced */
    173     tree = 0;
    174     node = 0;
    175     root = 0;
    176 }
    177 
    178 XMLwrapper::~XMLwrapper()
    179 {
    180     cleanup();
    181 }
    182 
    183 void XMLwrapper::setPadSynth(bool enabled)
    184 {
    185     /**@bug this might create multiple nodes when only one is needed*/
    186     mxml_node_t *oldnode = node;
    187     node = info;
    188     //Info storing
    189     addparbool("PADsynth_used", enabled);
    190     node = oldnode;
    191 }
    192 
    193 bool XMLwrapper::hasPadSynth() const
    194 {
    195     /**Right now this has a copied implementation of setparbool, so this should
    196      * be reworked as XMLwrapper evolves*/
    197     mxml_node_t *tmp = mxmlFindElement(tree,
    198                                        tree,
    199                                        "INFORMATION",
    200                                        NULL,
    201                                        NULL,
    202                                        MXML_DESCEND_ALL);
    203 
    204     mxml_node_t *parameter = mxmlFindElement(tmp,
    205                                              tmp,
    206                                              "par_bool",
    207                                              "name",
    208                                              "PADsynth_used",
    209                                              MXML_DESCEND_FIRST);
    210     if(parameter == NULL) //no information available
    211         return false;
    212 
    213     const char *strval = mxmlElementGetAttr(parameter, "value");
    214     if(strval == NULL) //no information available
    215         return false;
    216 
    217     if((strval[0] == 'Y') || (strval[0] == 'y'))
    218         return true;
    219     else
    220         return false;
    221 }
    222 
    223 
    224 /* SAVE XML members */
    225 
    226 int XMLwrapper::saveXMLfile(const string &filename, int compression) const
    227 {
    228     char *xmldata = getXMLdata();
    229     if(xmldata == NULL)
    230         return -2;
    231 
    232     int result      = dosavefile(filename.c_str(), compression, xmldata);
    233 
    234     free(xmldata);
    235     return result;
    236 }
    237 
    238 char *XMLwrapper::getXMLdata() const
    239 {
    240     xml_k = 0;
    241 
    242 #if MXML_MAJOR_VERSION <= 3
    243     char *xmldata = mxmlSaveAllocString(tree, XMLwrapper_whitespace_callback);
    244 #else
    245     mxml_options_t *options = mxmlOptionsNew();
    246     mxmlOptionsSetWhitespaceCallback(options, XMLwrapper_whitespace_callback, /*cbdata*/nullptr);
    247     char *xmldata = mxmlSaveAllocString(tree, options);
    248     mxmlOptionsDelete(options);
    249 #endif
    250 
    251 
    252     return xmldata;
    253 }
    254 
    255 
    256 int XMLwrapper::dosavefile(const char *filename,
    257                            int compression,
    258                            const char *xmldata) const
    259 {
    260     if(compression == 0) {
    261         FILE *file;
    262         file = fopen(filename, "w");
    263         if(file == NULL)
    264             return -1;
    265         fputs(xmldata, file);
    266         fclose(file);
    267     }
    268     else {
    269         if(compression > 9)
    270             compression = 9;
    271         if(compression < 1)
    272             compression = 1;
    273         char options[10];
    274         snprintf(options, 10, "wb%d", compression);
    275 
    276         gzFile gzfile;
    277         gzfile = gzopen(filename, options);
    278         if(gzfile == NULL)
    279             return -1;
    280         gzputs(gzfile, xmldata);
    281         gzclose(gzfile);
    282     }
    283 
    284     return 0;
    285 }
    286 
    287 
    288 
    289 void XMLwrapper::addpar(const string &name, int val)
    290 {
    291     addparams("par", 2, "name", name.c_str(), "value", stringFrom<int>(
    292                   val).c_str());
    293 }
    294 
    295 void XMLwrapper::addparreal(const string &name, float val)
    296 {
    297     union { float in; uint32_t out; } convert;
    298     char buf[11];
    299     convert.in = val;
    300     snprintf(buf, sizeof(buf), "0x%.8X", convert.out);
    301     addparams("par_real", 3, "name", name.c_str(), "value",
    302               stringFrom<float>(val).c_str(), "exact_value", buf);
    303 }
    304 
    305 void XMLwrapper::addparbool(const string &name, int val)
    306 {
    307     if(val != 0)
    308         addparams("par_bool", 2, "name", name.c_str(), "value", "yes");
    309     else
    310         addparams("par_bool", 2, "name", name.c_str(), "value", "no");
    311 }
    312 
    313 void XMLwrapper::addparstr(const string &name, const string &val)
    314 {
    315     mxml_node_t *element = mxmlNewElement(node, "string");
    316     mxmlElementSetAttr(element, "name", name.c_str());
    317     mxmlNewText(element, 0, val.c_str());
    318 }
    319 
    320 
    321 void XMLwrapper::beginbranch(const string &name)
    322 {
    323     if(verbose)
    324         cout << "beginbranch()" << name << endl;
    325     node = addparams(name.c_str(), 0);
    326 }
    327 
    328 void XMLwrapper::beginbranch(const string &name, int id)
    329 {
    330     if(verbose)
    331         cout << "beginbranch(" << id << ")" << name << endl;
    332     node = addparams(name.c_str(), 1, "id", stringFrom<int>(id).c_str());
    333 }
    334 
    335 void XMLwrapper::endbranch()
    336 {
    337     if(verbose)
    338         cout << "endbranch()" << node << "-" << mxmlGetElement(node)
    339              << " To "
    340              << mxmlGetParent(node) << "-" << mxmlGetElement(mxmlGetParent(node)) << endl;
    341     node = mxmlGetParent(node);
    342 }
    343 
    344 
    345 //workaround for memory leak
    346 const char *trimLeadingWhite(const char *c)
    347 {
    348     while(isspace(*c))
    349         ++c;
    350     return c;
    351 }
    352 
    353 /* LOAD XML members */
    354 
    355 int XMLwrapper::loadXMLfile(const string &filename)
    356 {
    357     cleanup();
    358 
    359     const char *xmldata = doloadfile(filename);
    360     if(xmldata == NULL)
    361         return -1;  //the file could not be loaded or uncompressed
    362 
    363 #if MXML_MAJOR_VERSION <= 3
    364     root = tree = mxmlLoadString(NULL, trimLeadingWhite(
    365                                            xmldata), MXML_OPAQUE_CALLBACK);
    366 #else
    367     mxml_options_t *options = mxmlOptionsNew();
    368     mxmlOptionsSetTypeValue(options, MXML_TYPE_OPAQUE);
    369     root = tree = mxmlLoadString(NULL, options, trimLeadingWhite(xmldata));
    370     mxmlOptionsDelete(options);
    371 #endif
    372 
    373     delete[] xmldata;
    374 
    375     if(tree == NULL)
    376         return -2;  //this is not XML
    377 
    378     node = root = mxmlFindElement(tree,
    379                                   tree,
    380                                   "ZynAddSubFX-data",
    381                                   NULL,
    382                                   NULL,
    383                                   MXML_DESCEND_ALL);
    384     if(root == NULL)
    385         return -3;  //the XML doesn't embbed zynaddsubfx data
    386 
    387     //fetch version information
    388     _fileversion.set_major(stringTo<int>(mxmlElementGetAttr(root, "version-major")));
    389     _fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor")));
    390     _fileversion.set_revision(
    391         stringTo<int>(mxmlElementGetAttr(root, "version-revision")));
    392 
    393     if(verbose)
    394         cout << "loadXMLfile() version: " << _fileversion << endl;
    395 
    396     return 0;
    397 }
    398 
    399 
    400 char *XMLwrapper::doloadfile(const string &filename) const
    401 {
    402     char  *xmldata = NULL;
    403     gzFile gzfile  = gzopen(filename.c_str(), "rb");
    404 
    405     if(gzfile != NULL) { //The possibly compressed file opened
    406         stringstream strBuf;             //reading stream
    407         const int    bufSize = 500;      //fetch size
    408         char fetchBuf[bufSize + 1];      //fetch buffer
    409         int  read = 0;                   //chars read in last fetch
    410 
    411         fetchBuf[bufSize] = 0; //force null termination
    412 
    413         while(bufSize == (read = gzread(gzfile, fetchBuf, bufSize)))
    414             strBuf << fetchBuf;
    415 
    416         fetchBuf[read] = 0; //Truncate last partial read
    417         strBuf << fetchBuf;
    418 
    419         gzclose(gzfile);
    420 
    421         //Place data in output format
    422         string tmp = strBuf.str();
    423         xmldata = new char[tmp.size() + 1];
    424         strncpy(xmldata, tmp.c_str(), tmp.size() + 1);
    425     }
    426 
    427     return xmldata;
    428 }
    429 
    430 bool XMLwrapper::putXMLdata(const char *xmldata)
    431 {
    432     cleanup();
    433 
    434     if(xmldata == NULL)
    435         return false;
    436 
    437 #if MXML_MAJOR_VERSION <= 3
    438     root = tree = mxmlLoadString(NULL, trimLeadingWhite(
    439                                            xmldata), MXML_OPAQUE_CALLBACK);
    440 #else
    441     mxml_options_t *options = mxmlOptionsNew();
    442     mxmlOptionsSetTypeValue(options, MXML_TYPE_OPAQUE);
    443     root = tree = mxmlLoadString(NULL, options, trimLeadingWhite(xmldata));
    444     mxmlOptionsDelete(options);
    445 #endif
    446     if(tree == NULL)
    447         return false;
    448 
    449     node = root = mxmlFindElement(tree,
    450                                   tree,
    451                                   "ZynAddSubFX-data",
    452                                   NULL,
    453                                   NULL,
    454                                   MXML_DESCEND_ALL);
    455     if(root == NULL)
    456         return false;
    457 
    458     //fetch version information
    459     _fileversion.set_major(stringTo<int>(mxmlElementGetAttr(root, "version-major")));
    460     _fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor")));
    461     _fileversion.set_revision(
    462         stringTo<int>(mxmlElementGetAttr(root, "version-revision")));
    463 
    464     return true;
    465 }
    466 
    467 
    468 
    469 int XMLwrapper::enterbranch(const string &name)
    470 {
    471     if(verbose)
    472         cout << "enterbranch() " << name << endl;
    473     mxml_node_t *tmp = mxmlFindElement(node, node,
    474                                        name.c_str(), NULL, NULL,
    475                                        MXML_DESCEND_FIRST);
    476     if(tmp == NULL)
    477         return 0;
    478 
    479     node = tmp;
    480     return 1;
    481 }
    482 
    483 int XMLwrapper::enterbranch(const string &name, int id)
    484 {
    485     if(verbose)
    486         cout << "enterbranch(" << id << ") " << name << endl;
    487     mxml_node_t *tmp = mxmlFindElement(node, node,
    488                                        name.c_str(), "id", stringFrom<int>(
    489                                            id).c_str(), MXML_DESCEND_FIRST);
    490     if(tmp == NULL)
    491         return 0;
    492 
    493     node = tmp;
    494     return 1;
    495 }
    496 
    497 
    498 void XMLwrapper::exitbranch()
    499 {
    500     if(verbose)
    501         cout << "exitbranch()" << node << "-" << mxmlGetElement(node)
    502              << " To "
    503              << mxmlGetParent(node) << "-" << mxmlGetElement(mxmlGetParent(node)) << endl;
    504     node = mxmlGetParent(node);
    505 }
    506 
    507 
    508 int XMLwrapper::getbranchid(int min, int max) const
    509 {
    510     int id = stringTo<int>(mxmlElementGetAttr(node, "id"));
    511     if((min == 0) && (max == 0))
    512         return id;
    513 
    514     if(id < min)
    515         id = min;
    516     else
    517     if(id > max)
    518         id = max;
    519 
    520     return id;
    521 }
    522 
    523 int XMLwrapper::getpar(const string &name, int defaultpar, int min,
    524                        int max) const
    525 {
    526     const mxml_node_t *tmp = mxmlFindElement(node,
    527                                              node,
    528                                              "par",
    529                                              "name",
    530                                              name.c_str(),
    531                                              MXML_DESCEND_FIRST);
    532 
    533     if(tmp == NULL)
    534         return defaultpar;
    535 
    536     const char *strval = mxmlElementGetAttr(tmp, "value");
    537     if(strval == NULL)
    538         return defaultpar;
    539 
    540     int val = stringTo<int>(strval);
    541     if(val < min)
    542         val = min;
    543     else
    544     if(val > max)
    545         val = max;
    546 
    547     return val;
    548 }
    549 
    550 int XMLwrapper::getpar127(const string &name, int defaultpar) const
    551 {
    552     return getpar(name, defaultpar, 0, 127);
    553 }
    554 
    555 int XMLwrapper::getparbool(const string &name, int defaultpar) const
    556 {
    557     const mxml_node_t *tmp = mxmlFindElement(node,
    558                                              node,
    559                                              "par_bool",
    560                                              "name",
    561                                              name.c_str(),
    562                                              MXML_DESCEND_FIRST);
    563 
    564     if(tmp == NULL)
    565         return defaultpar;
    566 
    567     const char *strval = mxmlElementGetAttr(tmp, "value");
    568     if(strval == NULL)
    569         return defaultpar;
    570 
    571     if((strval[0] == 'Y') || (strval[0] == 'y'))
    572         return 1;
    573     else
    574         return 0;
    575 }
    576 
    577 void XMLwrapper::getparstr(const string &name, char *par, int maxstrlen) const
    578 {
    579     ZERO(par, maxstrlen);
    580     mxml_node_t *tmp = mxmlFindElement(node,
    581                                        node,
    582                                        "string",
    583                                        "name",
    584                                         name.c_str(),
    585                                         MXML_DESCEND_FIRST);
    586 
    587     if(tmp == NULL)
    588         return;
    589     if(mxmlGetFirstChild(tmp) == NULL)
    590         return;
    591     if(mxmlGetType(mxmlGetFirstChild(tmp)) == MXML_TYPE_OPAQUE) {
    592         snprintf(par, maxstrlen, "%s", mxmlGetOpaque(mxmlGetFirstChild(tmp)));
    593         return;
    594     }
    595     if((mxmlGetType(mxmlGetFirstChild(tmp)) == MXML_TYPE_TEXT)
    596        && (mxmlGetFirstChild(tmp) != NULL)) {
    597         snprintf(par, maxstrlen, "%s", mxmlGetText(mxmlGetFirstChild(tmp),NULL));
    598         return;
    599     }
    600 }
    601 
    602 string XMLwrapper::getparstr(const string &name,
    603                              const std::string &defaultpar) const
    604 {
    605     mxml_node_t *tmp = mxmlFindElement(node,
    606                                        node,
    607                                        "string",
    608                                        "name",
    609                                        name.c_str(),
    610                                        MXML_DESCEND_FIRST);
    611 
    612     if((tmp == NULL) || (mxmlGetFirstChild(tmp) == NULL))
    613         return defaultpar;
    614 
    615     if(mxmlGetType(mxmlGetFirstChild(tmp)) == MXML_TYPE_OPAQUE
    616        && (mxmlGetOpaque(mxmlGetFirstChild(tmp)) != NULL))
    617         return mxmlGetOpaque(mxmlGetFirstChild(tmp));
    618 
    619     if(mxmlGetType(mxmlGetFirstChild(tmp)) == MXML_TYPE_TEXT
    620        && (mxmlGetText(mxmlGetFirstChild(tmp),NULL) != NULL))
    621         return mxmlGetText(mxmlGetFirstChild(tmp),NULL);
    622 
    623     return defaultpar;
    624 }
    625 
    626 bool XMLwrapper::hasparreal(const char *name) const
    627 {
    628     const mxml_node_t *tmp = mxmlFindElement(node,
    629                                              node,
    630                                              "par_real",
    631                                              "name",
    632                                              name,
    633                                              MXML_DESCEND_FIRST);
    634     return tmp != nullptr;
    635 }
    636 
    637 float XMLwrapper::getparreal(const char *name, float defaultpar) const
    638 {
    639     const mxml_node_t *tmp = mxmlFindElement(node,
    640                                              node,
    641                                              "par_real",
    642                                              "name",
    643                                              name,
    644                                              MXML_DESCEND_FIRST);
    645     if(tmp == NULL)
    646         return defaultpar;
    647 
    648     const char *strval = mxmlElementGetAttr(tmp, "exact_value");
    649     if (strval != NULL) {
    650         union { float out; uint32_t in; } convert;
    651         sscanf(strval+2, "%x", &convert.in);
    652         return convert.out;
    653     }
    654 
    655     strval = mxmlElementGetAttr(tmp, "value");
    656     if(strval == NULL)
    657         return defaultpar;
    658 
    659     return stringTo<float>(strval);
    660 }
    661 
    662 float XMLwrapper::getparreal(const char *name,
    663                              float defaultpar,
    664                              float min,
    665                              float max) const
    666 {
    667     float result = getparreal(name, defaultpar);
    668 
    669     if(result < min)
    670         result = min;
    671     else
    672     if(result > max)
    673         result = max;
    674     return result;
    675 }
    676 
    677 
    678 /** Private members **/
    679 
    680 mxml_node_t *XMLwrapper::addparams(const char *name, unsigned int params,
    681                                    ...) const
    682 {
    683     /**@todo make this function send out a good error message if something goes
    684      * wrong**/
    685     mxml_node_t *element = mxmlNewElement(node, name);
    686 
    687     if(params) {
    688         va_list variableList;
    689         va_start(variableList, params);
    690 
    691         const char *ParamName;
    692         const char *ParamValue;
    693         while(params--) {
    694             ParamName  = va_arg(variableList, const char *);
    695             ParamValue = va_arg(variableList, const char *);
    696             if(verbose)
    697                 cout << "addparams()[" << params << "]=" << name << " "
    698                      << ParamName << "=\"" << ParamValue << "\"" << endl;
    699             mxmlElementSetAttr(element, ParamName, ParamValue);
    700         }
    701         va_end(variableList);
    702     }
    703     return element;
    704 }
    705 
    706 XmlNode::XmlNode(std::string name_)
    707     :name(name_)
    708 {}
    709 
    710 std::string &XmlNode::operator[](std::string name)
    711 {
    712     //fetch an existing one
    713     for(auto &a:attrs)
    714         if(a.name == name)
    715             return a.value;
    716 
    717     //create a new one
    718     attrs.push_back({name, ""});
    719     return attrs[attrs.size()-1].value;
    720 }
    721 
    722 bool XmlNode::has(std::string name_)
    723 {
    724     //fetch an existing one
    725     for(auto &a:attrs)
    726         if(a.name == name_)
    727             return true;
    728     return false;
    729 }
    730 
    731 void XMLwrapper::add(const XmlNode &node_)
    732 {
    733     mxml_node_t *element = mxmlNewElement(node, node_.name.c_str());
    734     for(auto attr:node_.attrs)
    735         mxmlElementSetAttr(element, attr.name.c_str(),
    736                 attr.value.c_str());
    737 }
    738 
    739 std::vector<XmlNode> XMLwrapper::getBranch(void) const
    740 {
    741     std::vector<XmlNode> res;
    742     mxml_node_t *current = mxmlGetFirstChild(node);
    743     while(current) {
    744         if(mxmlGetType(current) == MXML_TYPE_ELEMENT) {
    745 #if MXML_MAJOR_VERSION >= 3
    746             XmlNode n(mxmlGetElement(current));
    747             int count = mxmlElementGetAttrCount(current);
    748             const char *name;
    749             const char *attrib;
    750             for(int i = 0; i < count; ++i) {
    751                 attrib = mxmlElementGetAttrByIndex(current, i, &name);
    752                 if(name)
    753                     n[name] = attrib;
    754             }
    755 #else
    756             auto elm = current->value.element;
    757             XmlNode n(elm.name);
    758             for(int i = 0; i < elm.num_attrs; ++i) {
    759                 auto &attr = elm.attrs[i];
    760                 n[attr.name] = attr.value;
    761             }
    762 #endif
    763             res.push_back(n);
    764         }
    765         current = mxmlWalkNext(current, node, MXML_DESCEND_NONE);
    766     }
    767     return res;
    768 }
    769 
    770 }