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