/* ZynAddSubFX - a software synthesizer XMLwrapper.cpp - XML wrapper Copyright (C) 2003-2005 Nasca Octavian Paul Copyright (C) 2009-2009 Mark McCurry Author: Nasca Octavian Paul Mark McCurry This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program 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 (version 2 or later) for more details. You should have received a copy of the GNU General Public License (version 2) along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "XMLwrapper.h" #include #include #include #include #include #include #include #include "globals.h" #include "Util.h" using namespace std; int xml_k = 0; bool verbose = false; const char *XMLwrapper_whitespace_callback(mxml_node_t *node, int where) { const char *name = node->value.element.name; if((where == MXML_WS_BEFORE_OPEN) && (!strcmp(name, "?xml"))) return NULL; if((where == MXML_WS_BEFORE_CLOSE) && (!strcmp(name, "string"))) return NULL; if((where == MXML_WS_BEFORE_OPEN) || (where == MXML_WS_BEFORE_CLOSE)) /* const char *tmp=node->value.element.name; if (tmp!=NULL) { if ((strstr(tmp,"par")!=tmp)&&(strstr(tmp,"string")!=tmp)) { printf("%s ",tmp); if (where==MXML_WS_BEFORE_OPEN) xml_k++; if (where==MXML_WS_BEFORE_CLOSE) xml_k--; if (xml_k>=STACKSIZE) xml_k=STACKSIZE-1; if (xml_k<0) xml_k=0; printf("%d\n",xml_k); printf("\n"); }; }; int i=0; for (i=1;i(mxmlFindElement( const_cast(node), const_cast(top), name, attr, value, descend)); } //temporary const overload of mxmlElementGetAttr const char *mxmlElementGetAttr(const mxml_node_t *node, const char *name) { return mxmlElementGetAttr(const_cast(node), name); } XMLwrapper::XMLwrapper() { version.Major = 2; version.Minor = 5; version.Revision = 2; minimal = true; node = tree = mxmlNewElement(MXML_NO_PARENT, "?xml version=\"1.0f\" encoding=\"UTF-8\"?"); /* for mxml 2.1f (and older) tree=mxmlNewElement(MXML_NO_PARENT,"?xml"); mxmlElementSetAttr(tree,"version","1.0f"); mxmlElementSetAttr(tree,"encoding","UTF-8"); */ mxml_node_t *doctype = mxmlNewElement(tree, "!DOCTYPE"); mxmlElementSetAttr(doctype, "ZynAddSubFX-data", NULL); node = root = addparams("ZynAddSubFX-data", 4, "version-major", stringFrom( version.Major).c_str(), "version-minor", stringFrom( version.Minor).c_str(), "version-revision", stringFrom(version.Revision).c_str(), "ZynAddSubFX-author", "Nasca Octavian Paul"); //make the empty branch that will contain the information parameters info = addparams("INFORMATION", 0); //save zynaddsubfx specifications beginbranch("BASE_PARAMETERS"); addpar("max_midi_parts", NUM_MIDI_PARTS); addpar("max_kit_items_per_instrument", NUM_KIT_ITEMS); addpar("max_system_effects", NUM_SYS_EFX); addpar("max_insertion_effects", NUM_INS_EFX); addpar("max_instrument_effects", NUM_PART_EFX); addpar("max_addsynth_voices", NUM_VOICES); endbranch(); } XMLwrapper::~XMLwrapper() { if(tree) mxmlDelete(tree); } void XMLwrapper::setPadSynth(bool enabled) { /**@bug this might create multiple nodes when only one is needed*/ mxml_node_t *oldnode = node; node = info; //Info storing addparbool("PADsynth_used", enabled); node = oldnode; } bool XMLwrapper::hasPadSynth() const { /**Right now this has a copied implementation of setparbool, so this should * be reworked as XMLwrapper evolves*/ mxml_node_t *tmp = mxmlFindElement(tree, tree, "INFORMATION", NULL, NULL, MXML_DESCEND); mxml_node_t *parameter = mxmlFindElement(tmp, tmp, "par_bool", "name", "PADsynth_used", MXML_DESCEND_FIRST); if(parameter == NULL) //no information availiable return false; const char *strval = mxmlElementGetAttr(parameter, "value"); if(strval == NULL) //no information available return false; if((strval[0] == 'Y') || (strval[0] == 'y')) return true; else return false; } /* SAVE XML members */ int XMLwrapper::saveXMLfile(const string &filename, int compression) const { char *xmldata = getXMLdata(); if(xmldata == NULL) return -2; int result = dosavefile(filename.c_str(), compression, xmldata); free(xmldata); return result; } char *XMLwrapper::getXMLdata() const { xml_k = 0; char *xmldata = mxmlSaveAllocString(tree, XMLwrapper_whitespace_callback); return xmldata; } int XMLwrapper::dosavefile(const char *filename, int compression, const char *xmldata) const { if(compression == 0) { FILE *file; file = fopen(filename, "w"); if(file == NULL) return -1; fputs(xmldata, file); fclose(file); } else { if(compression > 9) compression = 9; if(compression < 1) compression = 1; char options[10]; snprintf(options, 10, "wb%d", compression); gzFile gzfile; gzfile = gzopen(filename, options); if(gzfile == NULL) return -1; gzputs(gzfile, xmldata); gzclose(gzfile); } return 0; } void XMLwrapper::addpar(const string &name, int val) { addparams("par", 2, "name", name.c_str(), "value", stringFrom( val).c_str()); } void XMLwrapper::addparreal(const string &name, float val) { union { float in; uint32_t out; } convert; char buf[11]; convert.in = val; sprintf(buf, "0x%8X", convert.out); addparams("par_real", 3, "name", name.c_str(), "value", stringFrom(val).c_str(), "exact_value", buf); } void XMLwrapper::addparbool(const string &name, int val) { if(val != 0) addparams("par_bool", 2, "name", name.c_str(), "value", "yes"); else addparams("par_bool", 2, "name", name.c_str(), "value", "no"); } void XMLwrapper::addparstr(const string &name, const string &val) { mxml_node_t *element = mxmlNewElement(node, "string"); mxmlElementSetAttr(element, "name", name.c_str()); mxmlNewText(element, 0, val.c_str()); } void XMLwrapper::beginbranch(const string &name) { if(verbose) cout << "beginbranch()" << name << endl; node = addparams(name.c_str(), 0); } void XMLwrapper::beginbranch(const string &name, int id) { if(verbose) cout << "beginbranch(" << id << ")" << name << endl; node = addparams(name.c_str(), 1, "id", stringFrom(id).c_str()); } void XMLwrapper::endbranch() { if(verbose) cout << "endbranch()" << node << "-" << node->value.element.name << " To " << node->parent << "-" << node->parent->value.element.name << endl; node = node->parent; } //workaround for memory leak const char *trimLeadingWhite(const char *c) { while(isspace(*c)) ++c; return c; } /* LOAD XML members */ int XMLwrapper::loadXMLfile(const string &filename) { if(tree != NULL) mxmlDelete(tree); tree = NULL; const char *xmldata = doloadfile(filename); if(xmldata == NULL) return -1; //the file could not be loaded or uncompressed root = tree = mxmlLoadString(NULL, trimLeadingWhite( xmldata), MXML_OPAQUE_CALLBACK); delete[] xmldata; if(tree == NULL) return -2; //this is not XML node = root = mxmlFindElement(tree, tree, "ZynAddSubFX-data", NULL, NULL, MXML_DESCEND); if(root == NULL) return -3; //the XML doesnt embbed zynaddsubfx data //fetch version information version.Major = stringTo(mxmlElementGetAttr(root, "version-major")); version.Minor = stringTo(mxmlElementGetAttr(root, "version-minor")); version.Revision = stringTo(mxmlElementGetAttr(root, "version-revision")); if(verbose) cout << "loadXMLfile() version: " << version.Major << '.' << version.Minor << '.' << version.Revision << endl; return 0; } char *XMLwrapper::doloadfile(const string &filename) const { char *xmldata = NULL; gzFile gzfile = gzopen(filename.c_str(), "rb"); if(gzfile != NULL) { //The possibly compressed file opened stringstream strBuf; //reading stream const int bufSize = 500; //fetch size char fetchBuf[bufSize + 1]; //fetch buffer int read = 0; //chars read in last fetch fetchBuf[bufSize] = 0; //force null termination while(bufSize == (read = gzread(gzfile, fetchBuf, bufSize))) strBuf << fetchBuf; fetchBuf[read] = 0; //Truncate last partial read strBuf << fetchBuf; gzclose(gzfile); //Place data in output format string tmp = strBuf.str(); xmldata = new char[tmp.size() + 1]; strncpy(xmldata, tmp.c_str(), tmp.size() + 1); } return xmldata; } bool XMLwrapper::putXMLdata(const char *xmldata) { if(tree != NULL) mxmlDelete(tree); tree = NULL; if(xmldata == NULL) return false; root = tree = mxmlLoadString(NULL, trimLeadingWhite( xmldata), MXML_OPAQUE_CALLBACK); if(tree == NULL) return false; node = root = mxmlFindElement(tree, tree, "ZynAddSubFX-data", NULL, NULL, MXML_DESCEND); if(root == NULL) return false; return true; } int XMLwrapper::enterbranch(const string &name) { if(verbose) cout << "enterbranch() " << name << endl; mxml_node_t *tmp = mxmlFindElement(node, node, name.c_str(), NULL, NULL, MXML_DESCEND_FIRST); if(tmp == NULL) return 0; node = tmp; return 1; } int XMLwrapper::enterbranch(const string &name, int id) { if(verbose) cout << "enterbranch(" << id << ") " << name << endl; mxml_node_t *tmp = mxmlFindElement(node, node, name.c_str(), "id", stringFrom( id).c_str(), MXML_DESCEND_FIRST); if(tmp == NULL) return 0; node = tmp; return 1; } void XMLwrapper::exitbranch() { if(verbose) cout << "exitbranch()" << node << "-" << node->value.element.name << " To " << node->parent << "-" << node->parent->value.element.name << endl; node = node->parent; } int XMLwrapper::getbranchid(int min, int max) const { int id = stringTo(mxmlElementGetAttr(node, "id")); if((min == 0) && (max == 0)) return id; if(id < min) id = min; else if(id > max) id = max; return id; } int XMLwrapper::getpar(const string &name, int defaultpar, int min, int max) const { const mxml_node_t *tmp = mxmlFindElement(node, node, "par", "name", name.c_str(), MXML_DESCEND_FIRST); if(tmp == NULL) return defaultpar; const char *strval = mxmlElementGetAttr(tmp, "value"); if(strval == NULL) return defaultpar; int val = stringTo(strval); if(val < min) val = min; else if(val > max) val = max; return val; } int XMLwrapper::getpar127(const string &name, int defaultpar) const { return getpar(name, defaultpar, 0, 127); } int XMLwrapper::getparbool(const string &name, int defaultpar) const { const mxml_node_t *tmp = mxmlFindElement(node, node, "par_bool", "name", name.c_str(), MXML_DESCEND_FIRST); if(tmp == NULL) return defaultpar; const char *strval = mxmlElementGetAttr(tmp, "value"); if(strval == NULL) return defaultpar; if((strval[0] == 'Y') || (strval[0] == 'y')) return 1; else return 0; } void XMLwrapper::getparstr(const string &name, char *par, int maxstrlen) const { ZERO(par, maxstrlen); const mxml_node_t *tmp = mxmlFindElement(node, node, "string", "name", name.c_str(), MXML_DESCEND_FIRST); if(tmp == NULL) return; if(tmp->child == NULL) return; if(tmp->child->type == MXML_OPAQUE) { snprintf(par, maxstrlen, "%s", tmp->child->value.element.name); return; } if((tmp->child->type == MXML_TEXT) && (tmp->child->value.text.string != NULL)) { snprintf(par, maxstrlen, "%s", tmp->child->value.text.string); return; } } string XMLwrapper::getparstr(const string &name, const std::string &defaultpar) const { const mxml_node_t *tmp = mxmlFindElement(node, node, "string", "name", name.c_str(), MXML_DESCEND_FIRST); if((tmp == NULL) || (tmp->child == NULL)) return defaultpar; if((tmp->child->type == MXML_OPAQUE) && (tmp->child->value.element.name != NULL)) return tmp->child->value.element.name; if((tmp->child->type == MXML_TEXT) && (tmp->child->value.text.string != NULL)) return tmp->child->value.text.string; return defaultpar; } float XMLwrapper::getparreal(const char *name, float defaultpar) const { const mxml_node_t *tmp = mxmlFindElement(node, node, "par_real", "name", name, MXML_DESCEND_FIRST); if(tmp == NULL) return defaultpar; const char *strval = mxmlElementGetAttr(tmp, "exact_value"); if (strval != NULL) { union { float out; uint32_t in; } convert; sscanf(strval+2, "%x", &convert.in); return convert.out; } strval = mxmlElementGetAttr(tmp, "value"); if(strval == NULL) return defaultpar; return stringTo(strval); } float XMLwrapper::getparreal(const char *name, float defaultpar, float min, float max) const { float result = getparreal(name, defaultpar); if(result < min) result = min; else if(result > max) result = max; return result; } /** Private members **/ mxml_node_t *XMLwrapper::addparams(const char *name, unsigned int params, ...) const { /**@todo make this function send out a good error message if something goes * wrong**/ mxml_node_t *element = mxmlNewElement(node, name); if(params) { va_list variableList; va_start(variableList, params); const char *ParamName; const char *ParamValue; while(params--) { ParamName = va_arg(variableList, const char *); ParamValue = va_arg(variableList, const char *); if(verbose) cout << "addparams()[" << params << "]=" << name << " " << ParamName << "=\"" << ParamValue << "\"" << endl; mxmlElementSetAttr(element, ParamName, ParamValue); } va_end(variableList); } return element; } XmlNode::XmlNode(std::string name_) :name(name_) {} std::string &XmlNode::operator[](std::string name) { //fetch an existing one for(auto &a:attrs) if(a.name == name) return a.value; //create a new one attrs.push_back({name, ""}); return attrs[attrs.size()-1].value; } bool XmlNode::has(std::string name_) { //fetch an existing one for(auto &a:attrs) if(a.name == name_) return true; return false; } void XMLwrapper::add(const XmlNode &node_) { mxml_node_t *element = mxmlNewElement(node, node_.name.c_str()); for(auto attr:node_.attrs) mxmlElementSetAttr(element, attr.name.c_str(), attr.value.c_str()); } std::vector XMLwrapper::getBranch(void) const { std::vector res; mxml_node_t *current = node->child; while(current) { if(current->type == MXML_ELEMENT) { auto elm = current->value.element; XmlNode n(elm.name); for(int i=0; i