1 /*
2  * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
3  * Copyright (C) 2011 Pete Shorthose
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  * ---------------------------------------------------------------------------
19  *
20  *    This is the gx_head system interface
21  *
22  * ----------------------------------------------------------------------------
23  */
24 
25 #include "engine.h"               // NOLINT
26 
27 #include <sys/stat.h>
28 
29 namespace gx_system {
30 
check_mtime(const std::string & filename,time_t & mtime)31 static bool check_mtime(const std::string& filename, time_t& mtime) {
32     struct stat st;
33     if (stat(filename.c_str(), &st) != 0) {
34 	mtime = 0;
35 	return false;
36     }
37     time_t t = max(st.st_mtime, st.st_ctime);
38     if (t == mtime) {
39 	return true;
40     }
41     mtime = t;
42     return false;
43 }
44 
45 /****************************************************************
46  ** JsonWriter
47  */
48 
JsonWriter(ostream * o,bool enable_newlines)49 JsonWriter::JsonWriter(ostream *o, bool enable_newlines)
50     : os(o),
51       first(true),
52       deferred_nl(enable_newlines ? 0 : -1),
53       indent("") {}
54 
~JsonWriter()55 JsonWriter::~JsonWriter() {
56     close();
57 }
58 
reset()59 void JsonWriter::reset() {
60     os->flush();
61     first = true;
62     if (deferred_nl == 1) {
63 	deferred_nl = 0;
64     }
65     indent.clear();
66 }
67 
close()68 void JsonWriter::close() {
69     if (is_closed()) {
70 	return;
71     }
72     if (deferred_nl == 1) {
73 	*os << endl;
74     }
75     os = 0;
76 }
77 
komma()78 inline void JsonWriter::komma() {
79     if (first)
80         first = false;
81     else if (deferred_nl == 0)
82         *os << ", ";
83     else
84         *os << ",";
85     flush();
86 }
87 
space()88 inline void JsonWriter::space() {
89     if (first)
90         first = false;
91     else if (deferred_nl == 0)
92         *os << " ";
93     flush();
94 }
95 
iplus()96 inline void JsonWriter::iplus() {
97     indent += "  ";
98 }
99 
iminus()100 inline void JsonWriter::iminus() {
101     if (!indent.empty()) {
102         indent = indent.substr(0, indent.size() - 2);
103     }
104 }
105 
fp_sanitize(T v)106 template<class T> static inline T fp_sanitize(T v) {
107     switch (fpclassify(v)) {
108     case FP_NORMAL: return v;
109     case FP_NAN: assert(false); return 1e50;
110     case FP_INFINITE: assert(false); return (v < 0 ? -1e50 : 1e50);
111     case FP_SUBNORMAL: return 0;
112     }
113     return v;
114 }
115 
write(float v,bool nl)116 void JsonWriter::write(float v, bool nl) {
117     komma();
118     *os << fp_sanitize(v);
119     snl(nl);
120 }
121 
write(double v,bool nl)122 void JsonWriter::write(double v, bool nl) {
123     komma();
124     *os << fp_sanitize(v);
125     snl(nl);
126 }
127 
write(int i,bool nl)128 void JsonWriter::write(int i, bool nl) {
129     komma();
130     *os << i;
131     snl(nl);
132 }
133 
write(unsigned int i,bool nl)134 void JsonWriter::write(unsigned int i, bool nl) {
135     komma();
136     *os << i;
137     snl(nl);
138 }
139 
write_lit(const string & s,bool nl)140 void JsonWriter::write_lit(const string& s, bool nl) {
141     komma();
142     *os << s;
143     snl(nl);
144 }
145 
write(const char * p,bool nl)146 void JsonWriter::write(const char* p, bool nl) {
147     if (p) {
148 	komma();
149 	*os << '"';
150 	for (; *p; p++) {
151 	    switch (*p) {
152 	    case '\\': case '"': *os << '\\'; break;
153 	    case '\b': *os << '\\'; *os << 'b'; continue;       // NOLINT
154 	    case '\f': *os << '\\'; *os << 'f'; continue;       // NOLINT
155 	    case '\n': *os << '\\'; *os << 'n'; continue;       // NOLINT
156 	    case '\r': *os << '\\'; *os << 'r'; continue;       // NOLINT
157 	    case '\t': *os << '\\'; *os << 't'; continue;       // NOLINT
158 	    }
159 	    *os << *p;
160 	}
161 	*os << '"';
162     } else {
163 	write_null();
164     }
165     snl(nl);
166 }
167 
begin_object(bool nl)168 void JsonWriter::begin_object(bool nl) {
169     komma();
170     *os << '{';
171     snl(nl);
172     first = true;
173     iplus();
174 }
175 
end_object(bool nl)176 void JsonWriter::end_object(bool nl) {
177     iminus();
178     flush();
179     first = false;
180     *os << '}';
181     snl(nl);
182 }
183 
begin_array(bool nl)184 void JsonWriter::begin_array(bool nl) {
185     komma();
186     *os << '[';
187     snl(nl);
188     first = true;
189     iplus();
190 }
191 
end_array(bool nl)192 void JsonWriter::end_array(bool nl) {
193     iminus();
194     flush();
195     first = false;
196     *os << ']';
197     snl(nl);
198 }
199 
write_key(const char * p,bool nl)200 void JsonWriter::write_key(const char* p, bool nl) {
201     write(p, nl);
202     *os << ": ";
203     first = true;
204 }
205 
write_key(const string & p,bool nl)206 void JsonWriter::write_key(const string& p, bool nl) {
207     write(p, nl);
208     *os << ": ";
209     first = true;
210 }
211 
212 // called before output of next element
flush()213 void JsonWriter::flush() {
214     if (deferred_nl == 1) {
215         *os << endl;
216         deferred_nl = false;
217         *os << indent;
218     }
219 }
220 
221 
222 /****************************************************************
223  ** class JsonStringWriter
224  */
225 
send_notify_begin(const char * method)226 void JsonStringWriter::send_notify_begin(const char *method) {
227     begin_object();
228     write_key("jsonrpc");
229     write("2.0");
230     write_key("method");
231     write(method);
232     write_key("params");
233     begin_array();
234 }
235 
send_notify_end()236 void JsonStringWriter::send_notify_end() {
237     end_array();
238     end_object();
239 }
240 
241 
242 /****************************************************************
243  ** JsonParser
244  */
245 
JsonException(const Glib::ustring & desc)246 JsonException::JsonException(const Glib::ustring& desc) {
247     what_str = "Json parse error: " + desc;
248 }
249 
reset()250 void JsonParser::reset() {
251     depth = 0;
252     cur_tok = no_token;
253     str.clear();
254     nl = false;
255     next_depth = 0;
256     next_tok = no_token;
257     next_str.clear();
258     next_pos = 0;
259 }
260 
JsonParser(istream * i)261 JsonParser::JsonParser(istream* i)
262     : is(i),
263       depth(0),
264       cur_tok(no_token),
265       str(),
266       nl(false),
267       next_depth(0),
268       next_tok(no_token),
269       next_str(),
270       next_pos(0) {
271 }
272 
~JsonParser()273 JsonParser::~JsonParser() {
274     close();
275 }
276 
close()277 void JsonParser::close() {
278     if (is_closed()) {
279 	return;
280     }
281     is = 0;
282 }
283 
get_token_name(token tok)284 const char* JsonParser::get_token_name(token tok) {
285     switch (tok) {
286     case no_token: return "no_token";
287     case end_token: return "end_token";
288     case begin_object: return "begin_object";
289     case end_object: return "end_object";
290     case begin_array: return "begin_array";
291     case end_array: return "end_array";
292     case value_string: return "value_string";
293     case value_number: return "value_number";
294     case value_key: return "value_key";
295     case value_null: return "value_null";
296     case value_bool: return "value_bool";
297     case value_false: return "value_false";
298     case value_true: return "value_true";
299     default: assert(false); return 0;
300     }
301 }
302 
throw_unexpected(token expect)303 void JsonParser::throw_unexpected(token expect) {
304     ostringstream b;
305     b << "unexpected token: " << get_token_name(cur_tok)
306       << " (expected: " << get_token_name(expect) << ")"
307       << endl;
308     //cerr << b.str() << endl; assert(false);
309     throw JsonException(b.str().c_str());
310 }
311 
unicode2utf8(unsigned int input)312 const char* unicode2utf8(unsigned int input) {
313     const int maskbits   = 0x3F;
314     const int maskbyte   = 0x80;
315     const int mask2bytes = 0xC0;
316     const int mask3bytes = 0xE0;
317     static char result[4];
318     int n = 0;
319     // 0xxxxxxx
320     if (input < 0x80) {
321         result[n++] = static_cast<char>(input);
322 
323     // 110xxxxx 10xxxxxx
324     } else if (input < 0x800) {
325         result[n++] = (static_cast<char>(mask2bytes | (input >> 6)));
326         result[n++] = (static_cast<char>(maskbyte | (input & maskbits)));
327 
328     // 1110xxxx 10xxxxxx 10xxxxxx
329     } else {
330         result[n++] = (static_cast<char>(mask3bytes | (input >> 12)));
331         result[n++] = (static_cast<char>(maskbyte | ((input >> 6) & maskbits)));
332         result[n++] = (static_cast<char>(maskbyte | (input & maskbits)));
333     }
334     result[n++] = '\0';
335     return result;
336 }
337 
readcode()338 const char* JsonParser::readcode() {
339     int code = 0;
340     for (int i = 0; i < 4; i++) {
341         int n = is->get();
342         if (!is->good())
343             throw JsonExceptionEOF("eof");
344         if ('0' <= n && n <= '9')
345             n = n - '0';
346         else
347             n = 10 + (toupper(n) - 'A');
348         code = code * 16 + n;
349     }
350     return unicode2utf8(code);
351 }
352 
readstring()353 string JsonParser::readstring() {
354     ostringstream os("");
355     char c;
356     do {
357         is->get(c);
358         if (!is->good())
359             return "";
360         if (c == '\\') {
361             is->get(c);
362             if (!is->good())
363                 return "";
364             switch (c) {
365             case 'b': os << '\b'; break;
366             case 'f': os << '\f'; break;
367             case 'n': os << '\n'; break;
368             case 'r': os << '\r'; break;
369             case 't': os << '\t'; break;
370 	    case '"': os << '"'; break;
371             case 'u': os << readcode(); break;
372             default: is->get(c); os << c; break;
373             }
374         } else if (c == '"') {
375             return os.str();
376         } else {
377             os << c;
378         }
379     } while (true);
380 }
381 
readnumber(char c)382 string JsonParser::readnumber(char c) {
383     ostringstream os("");
384     static int count_dn = 0;
385     do {
386         os << c;
387         c = is->peek();
388         switch (c) {
389         case '+': case '-': case '0': case '1': case '2': case '3': case '4':
390         case '5': case '6': case '7': case '8': case '9': case 'e': case 'E':
391         case '.':
392             break;
393         // read denormal value
394         case 'n': case 'a': case 'i': case 'f':
395 			++count_dn;
396 			if (count_dn >=3) {
397 				gx_print_warning("JsonParser", Glib::ustring::compose("DENORMAL VALUE DETECTED in %1", log_tok));
398 				count_dn = 0;
399 			}
400 			break;
401         default:
402             return os.str();
403         }
404         is->get(c);
405     } while (is->good());
406     return "";
407 }
408 
read_value_token(char c)409 JsonParser::token JsonParser::read_value_token(char c) {
410     ostringstream os("");
411     do {
412         os << c;
413         c = is->peek();
414 	if (c < 'a' || c > 'z') {
415 	    break;
416         }
417         is->get(c);
418     } while (is->good());
419     next_str = os.str();
420     if (next_str == "null") {
421 	return value_null;
422     }
423     if (next_str == "true") {
424 	return value_true;
425     }
426     if (next_str == "false") {
427 	return value_false;
428     }
429     return no_token;
430 }
431 
read_next()432 void JsonParser::read_next() {
433     if (next_tok == end_token)
434         return;
435     if (next_tok != no_token and next_depth == 0) {
436         next_tok = end_token;
437         return;
438     }
439     char c;
440     nl = false;
441     while (true) {
442         do {
443             is->get(c);
444             if (!is->good())
445                 throw JsonExceptionEOF("eof");
446             if (c == '\n')
447                 nl = true;
448         } while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
449 	next_pos = is->tellg();
450         switch (c) {
451         case '[': next_tok = begin_array; next_depth++; break;
452 
453         case ']': next_tok = end_array; next_depth--; break;
454 
455         case '{': next_tok = begin_object; next_depth++; break;
456 
457         case '}': next_tok = end_object; next_depth--; break;
458 
459         case ',': continue;
460 
461         case '"':
462             next_str = log_tok = readstring();
463              *is >> c;
464             if (!is->good())
465                 throw JsonExceptionEOF("eof");
466             if (c == ':') {
467                 next_tok = value_key;
468             } else {
469                 is->unget();
470                 next_tok = value_string;
471             }
472             break;
473 
474         case '-': case '0': case '1': case '2': case '3': case '4':
475         case '5': case '6': case '7': case '8': case '9':
476             next_str = readnumber(c);
477             next_tok = value_number;
478             break;
479         // read denormal value
480 		case 'n': case 'a': case 'i': case 'f':
481             next_str = readnumber(c);
482             next_tok = value_number;
483             break;
484 
485         default:
486 	    next_tok = read_value_token(c);
487 	    if (next_tok == no_token) {
488 		throw JsonException("bad token");
489 	    }
490 	    break;
491         }
492         break;
493     }
494 }
495 
next(token expect)496 JsonParser::token JsonParser::next(token expect) {
497     if (cur_tok != end_token) {
498         if (next_tok == no_token)
499             read_next();
500         depth = next_depth;
501         cur_tok = next_tok;
502         str = next_str;
503         if (next_tok != end_token)
504             read_next();
505     }
506     if (expect != no_token)
507         check_expect(expect);
508     return cur_tok;
509 }
510 
read_kv(const char * key,float & v)511 bool JsonParser::read_kv(const char *key, float& v) {
512     if (str == key) {
513 	next(value_number);
514 	v = current_value_float();
515 	return true;
516     } else {
517 	return false;
518     }
519 }
520 
read_kv(const char * key,double & v)521 bool JsonParser::read_kv(const char *key, double& v) {
522     if (str == key) {
523 	next(value_number);
524 	v = current_value_double();
525 	return true;
526     } else {
527 	return false;
528     }
529 }
530 
read_kv(const char * key,int & i)531 bool JsonParser::read_kv(const char *key, int& i) {
532     if (str == key) {
533 	next(value_number);
534 	i = current_value_int();
535 	return true;
536     } else {
537 	return false;
538     }
539 }
540 
read_kv(const char * key,unsigned int & i)541 bool JsonParser::read_kv(const char *key, unsigned int& i) {
542     if (str == key) {
543 	next(value_number);
544 	i = current_value_uint();
545 	return true;
546     } else {
547 	return false;
548     }
549 }
550 
read_kv(const char * key,string & s)551 bool JsonParser::read_kv(const char *key, string& s) {
552     if (str == key) {
553 	next(value_string);
554 	s = current_value();
555 	return true;
556     } else {
557 	return false;
558     }
559 }
560 
read_kv(const char * key,Glib::ustring & s)561 bool JsonParser::read_kv(const char *key, Glib::ustring& s) {
562     if (str == key) {
563 	next(value_string);
564 	s = current_value();
565 	return true;
566     } else {
567 	return false;
568     }
569 }
570 
set_streampos(streampos pos)571 void JsonParser::set_streampos(streampos pos) {
572     is->seekg(pos);
573     depth = 0;
574     cur_tok = no_token;
575     nl = false;
576     next_depth = 0;
577     next_tok = no_token;
578 }
579 
copy_object(JsonWriter & jw)580 void JsonParser::copy_object(JsonWriter& jw) {
581     int curdepth = depth;
582     do {
583         switch (next()) {
584         case begin_object:
585             jw.begin_object(nl);
586             break;
587         case end_object:
588             jw.end_object(nl);
589             break;
590         case begin_array:
591             jw.begin_array(nl);
592             break;
593         case end_array:
594             jw.end_array(nl);
595             break;
596         case value_string:
597             jw.write(current_value(), nl);
598             break;
599         case value_number:
600             jw.write_lit(current_value(), nl);
601             break;
602         case value_key:
603             jw.write_key(current_value().c_str(), nl);
604             break;
605         default:
606             throw JsonException("unexpected token");
607         }
608     } while (curdepth != depth);
609 }
610 
skip_object()611 void JsonParser::skip_object() {
612     int curdepth = depth;
613     do {
614         if (next() == end_token) {
615             throw JsonException("unexpected eof");
616         }
617     } while (curdepth != depth);
618 }
619 
JsonSubParser(JsonParser & jp,streampos pos)620 JsonSubParser::JsonSubParser(JsonParser& jp, streampos pos)
621     : JsonParser() {
622     set_stream(jp.get_stream());
623     position = get_stream()->tellg();
624     set_streampos(pos);
625 }
626 
~JsonSubParser()627 JsonSubParser::~JsonSubParser() {
628     get_stream()->seekg(position);
629 }
630 
631 /****************************************************************
632  ** class SettingsFileHeader
633  */
634 
635 const string SettingsFileHeader::gx_version = GX_VERSION;
636 
read(JsonParser & jp)637 void SettingsFileHeader::read(JsonParser& jp) {
638     jp.next(JsonParser::value_string);
639     if (jp.current_value() != "gx_head_file_version") {
640         throw JsonException("invalid gx_head file header");
641     }
642     jp.next(JsonParser::begin_array);
643     jp.next(JsonParser::value_number);
644     file_major = jp.current_value_int();
645     jp.next(JsonParser::value_number);
646     file_minor = jp.current_value_int();
647     jp.next(JsonParser::value_string);
648     file_gx_version = jp.current_value();
649     jp.next(JsonParser::end_array);
650 }
651 
write(JsonWriter & jw)652 void SettingsFileHeader::write(JsonWriter& jw) {
653     jw.write("gx_head_file_version");
654     jw.begin_array();
655     jw.write(major); // major format version
656     jw.write(minor); // minor format version
657     jw.write(gx_version);
658     jw.end_array(true);
659 }
660 
write_current_major_minor(JsonWriter & jw)661 void SettingsFileHeader::write_current_major_minor(JsonWriter& jw) {
662     jw.begin_array();
663     jw.write(major);
664     jw.write(minor);
665     jw.end_array();
666 }
667 
write_major_minor(JsonWriter & jw)668 void SettingsFileHeader::write_major_minor(JsonWriter& jw) {
669     jw.begin_array();
670     jw.write(file_major);
671     jw.write(file_minor);
672     jw.end_array();
673 }
674 
read_major_minor(JsonParser & jp)675 void SettingsFileHeader::read_major_minor(JsonParser& jp) {
676     jp.next(JsonParser::begin_array);
677     jp.next(JsonParser::value_number);
678     file_major = jp.current_value_int();
679     jp.next(JsonParser::value_number);
680     file_minor = jp.current_value_int();
681     jp.next(JsonParser::end_array);
682 }
683 
make_empty_settingsfile(const string & name)684 bool SettingsFileHeader::make_empty_settingsfile(const string& name) {
685     ofstream os(name.c_str());
686     if (!os.good()) {
687 	return false;
688     }
689     JsonWriter jw(&os);
690     jw.begin_array();
691     SettingsFileHeader::write(jw);
692     jw.end_array(true);
693     jw.close();
694     os.close();
695     return true;
696 }
697 
698 
699 /****************************************************************
700  ** class StateFile
701  */
702 
703 class JsonReader: public JsonParser {
704 public:
JsonReader(istream * is)705     JsonReader(istream* is) : JsonParser(is) {}
706     ~JsonReader();
707     void close();
708 };
709 
~JsonReader()710 JsonReader::~JsonReader() {
711     close();
712 }
713 
close()714 void JsonReader::close() {
715     if (is_closed()) {
716 	return;
717     }
718     next(JsonParser::end_array);
719     next(JsonParser::end_token);
720     JsonParser::close();
721 }
722 
create_reader()723 JsonParser *StateFile::create_reader() {
724     if (is) {
725 	is->seekg(0);
726     } else {
727 	check_mtime(filename, mtime);
728 	is = new ifstream(filename.c_str());
729     }
730     JsonReader *jp = new JsonReader(is);
731     jp->next(JsonParser::begin_array);
732     header.read(*jp);
733     if (header.is_major_diff()) {
734 	if (header.get_major() == 0) {
735 	    gx_print_info(_("recall settings"), _("loading converted state"));
736 	} else {
737 	    gx_print_warning(
738 		_("recall settings"),
739 		boost::format(_("major version mismatch in %1%: found %2%, expected %3%"))
740 		% filename % header.get_major() % static_cast<int>(SettingsFileHeader::major));
741 	}
742     }
743     return jp;
744 }
745 
set_filename(const string & fn)746 void StateFile::set_filename(const string& fn) {
747     filename = fn;
748     delete is;
749     is = 0;
750 }
751 
ensure_is_current()752 void StateFile::ensure_is_current() {
753     if (filename.empty() || !mtime) {
754 	return;
755     }
756     if (check_mtime(filename, mtime)) {
757 	return;
758     }
759     delete is;
760     is = 0;
761 }
762 
763 
764 class ModifyState: public JsonWriter {
765 private:
766     string filename;
767     string tmpfile;
768     ofstream os;
769 public:
770     ModifyState(const string& name);
771     ~ModifyState();
772     virtual void close();
773 };
774 
ModifyState(const string & name)775 ModifyState::ModifyState(const string& name)
776     : filename(name),
777       tmpfile(filename + "_tmp"),
778       os(tmpfile.c_str()) {
779     set_stream(&os);
780     begin_array();
781     SettingsFileHeader::write(*this);
782 }
783 
~ModifyState()784 ModifyState::~ModifyState() {
785     close();
786 }
787 
close()788 void ModifyState::close() {
789     if (is_closed()) {
790 	return;
791     }
792     end_array(true);
793     JsonWriter::close();
794     os.close();
795     if (!os.good()) {
796 	gx_print_error(_("save preset"),
797 		       boost::format(_("couldn't write %1%")) % tmpfile);
798     } else {
799 	int rc = rename(tmpfile.c_str(), filename.c_str());
800 	if (rc != 0) {
801 	    gx_print_error(_("save preset"),
802 			   boost::format(_("couldn't rename %1% to %2%"))
803 			   % tmpfile % filename);
804 	}
805     }
806 }
807 
808 class ModifyStatePreservePreset: public ModifyState {
809 private:
810     ifstream is;
811     JsonParser jp;
812 public:
813     ModifyStatePreservePreset(const string& name, bool *preserve_preset);
814     ~ModifyStatePreservePreset();
815 };
816 
ModifyStatePreservePreset(const string & name,bool * preserve_preset)817 ModifyStatePreservePreset::ModifyStatePreservePreset(const string& name, bool *preserve_preset)
818     : ModifyState(name),
819       is(name.c_str()),
820       jp(&is) {
821     bool copied = false;
822     if (is.good()) {
823 	try {
824 	    jp.next(JsonParser::begin_array);
825 	    SettingsFileHeader header;
826 	    header.read(jp);
827 	    while (jp.peek() != JsonParser::end_array) {
828 		jp.next(JsonParser::value_string);
829 		if (jp.current_value() == "current_preset") {
830 		    write(jp.current_value());
831 		    jp.copy_object(*this);
832 		    copied = true;
833 		} else {
834 		    jp.skip_object();
835 		}
836 	    }
837 	} catch(JsonException& e) {
838 	    // just ignore
839 	}
840     }
841     if (!copied) {
842 	*preserve_preset = false;
843     }
844 }
845 
~ModifyStatePreservePreset()846 ModifyStatePreservePreset::~ModifyStatePreservePreset() {
847 }
848 
create_writer(bool * preserve_preset)849 JsonWriter *StateFile::create_writer(bool *preserve_preset) {
850     JsonWriter *jw;
851     if (*preserve_preset) {
852 	jw = new ModifyStatePreservePreset(filename, preserve_preset);
853     } else {
854 	jw = new ModifyState(filename);
855     }
856     delete is;
857     is = 0;
858     return jw;
859 }
860 
861 
862 /****************************************************************
863  ** class PresetFile
864  */
865 
PresetFile()866 PresetFile::PresetFile()
867     : filename(),
868       is(0),
869       mtime(),
870       header(),
871       entries(),
872       name(),
873       tp(),
874       flags() {
875 }
876 
readJSON_remote(JsonParser & jp)877 void PresetFile::readJSON_remote(JsonParser& jp) {
878     entries.clear();
879     flags = 0;
880     name = "";
881     tp = PresetFile::PRESET_FILE;
882     jp.next(JsonParser::begin_object);
883     while (jp.peek() != JsonParser::end_object) {
884 	jp.next(JsonParser::value_key);
885 	if (jp.current_value() == "name") {
886 	    jp.next(JsonParser::value_string);
887 	    name = jp.current_value();
888 	} else if (jp.current_value() == "type") {
889 	    jp.next(JsonParser::value_string);
890 	    if (jp.current_value() == "scratch") {
891 		tp = PresetFile::PRESET_SCRATCH;
892 	    } else if (jp.current_value() == "factory") {
893 		tp = PresetFile::PRESET_FACTORY;
894 	    } else if (jp.current_value() == "file") {
895 		tp = PresetFile::PRESET_FILE;
896 	    }
897 	} else if (jp.current_value() == "mutable") {
898 	    jp.skip_object();
899 	} else if (jp.current_value() == "flag_invalid") {
900 	    flags |= PRESET_FLAG_INVALID;
901 	} else if (jp.current_value() == "flag_readonly") {
902 	    flags |= PRESET_FLAG_READONLY;
903 	} else if (jp.current_value() == "flag_versiondiff") {
904 	    flags |= PRESET_FLAG_VERSIONDIFF;
905 	} else if (jp.current_value() == "presets") {
906 	    jp.next(JsonParser::begin_array);
907 	    while (jp.peek() != JsonParser::end_array) {
908 		jp.next(JsonParser::value_string);
909 		entries.push_back(Position(jp.current_value(), 0));
910 	    }
911 	    jp.next(JsonParser::end_array);
912 	} else {
913 	    gx_print_warning(
914 		"PresetFile", Glib::ustring::compose("%1: unknown remote key: %2", name, jp.current_value()));
915 	}
916     }
917     jp.next(JsonParser::end_object);
918 }
919 
writeJSON_remote(gx_system::JsonWriter & jw)920 void PresetFile::writeJSON_remote(gx_system::JsonWriter& jw) {
921     jw.begin_object();
922     jw.write_key("name");
923     jw.write(name);
924     jw.write_key("mutable");
925     jw.write(is_mutable());
926     jw.write_key("type");
927     switch (tp) {
928     case gx_system::PresetFile::PRESET_SCRATCH: jw.write("scratch"); break;
929     case gx_system::PresetFile::PRESET_FACTORY: jw.write("factory"); break;
930     case gx_system::PresetFile::PRESET_FILE: jw.write("file"); break;
931     default: jw.write("unknown"); break;
932     }
933     if (flags & gx_system::PRESET_FLAG_INVALID) {
934 	jw.write_key("flag_invalid");
935     }
936     if (flags & gx_system::PRESET_FLAG_READONLY) {
937 	jw.write_key("flag_readonly");
938     }
939     if (flags & gx_system::PRESET_FLAG_VERSIONDIFF) {
940 	jw.write_key("flag_versiondiff");
941     }
942     jw.write_key("presets");
943     jw.begin_array();
944     for (int i = 0; i < size(); i++) {
945 	jw.write(entries[i].name);
946     }
947     jw.end_array();
948     jw.end_object();
949 }
950 
set_factory(const Glib::ustring & name_,const std::string & path)951 bool PresetFile::set_factory(const Glib::ustring& name_, const std::string& path) {
952     check_mtime(path, mtime);
953     if (mtime == 0) {
954 	gx_print_error(
955 	    _("open factory preset"),
956 	    boost::format(_("couldn't open %1%")) % path);
957 	return false;
958     }
959     name = name_;
960     filename = path;
961     tp = PRESET_FACTORY;
962     flags = 0;
963     header.set_to_current();
964     return true;
965 }
966 
fail()967 bool PresetFile::fail() {
968     try {
969 	reopen();
970     } catch (gx_system::JsonException& e) {
971 	gx_print_error(filename.c_str(), _("parse error"));
972 	return true;
973     }
974     return is->fail();
975 }
976 
open_file(const Glib::ustring & name_,const std::string & path,int tp_,int flags_)977 bool PresetFile::open_file(const Glib::ustring& name_, const std::string& path, int tp_, int flags_) {
978     name = name_;
979     filename = path;
980     tp = tp_;
981     flags = flags_;
982     if (fail()) {
983 	set_flag(PRESET_FLAG_INVALID, true);
984 	return false;
985     }
986     set_flag(PRESET_FLAG_INVALID, false);
987     check_flags();
988     return true;
989 }
990 
create_file(const Glib::ustring & name_,const std::string & path,int tp_,int flags_)991 bool PresetFile::create_file(const Glib::ustring& name_, const std::string& path, int tp_, int flags_) {
992     name = name_;
993     filename = path;
994     tp = tp_;
995     flags = flags_;
996     bool res = SettingsFileHeader::make_empty_settingsfile(path);
997     if (res) {
998 	header.set_to_current();
999 	check_mtime(path, mtime);
1000     } else {
1001 	gx_print_error(
1002 	    _("create preset bank"),
1003 	    boost::format(_("couldn't create %1%")) % path);
1004     }
1005     return res;
1006 }
1007 
check_flags()1008 void PresetFile::check_flags() {
1009     set_flag(PRESET_FLAG_READONLY, access(filename.c_str(), W_OK) != 0);
1010     set_flag(PRESET_FLAG_VERSIONDIFF, !header.is_current());
1011 }
1012 
readJSON(const std::string & dirpath,JsonParser & jp,bool * mtime_diff)1013 bool PresetFile::readJSON(const std::string& dirpath, JsonParser &jp, bool *mtime_diff) {
1014     jp.next(gx_system::JsonParser::begin_array);
1015     jp.next(gx_system::JsonParser::value_string);
1016     name = jp.current_value();
1017     jp.next(gx_system::JsonParser::value_string);
1018     filename = Glib::build_filename(dirpath, jp.current_value());
1019     jp.next(gx_system::JsonParser::value_number);
1020     tp = jp.current_value_int();
1021     jp.next(gx_system::JsonParser::value_number);
1022     flags = jp.current_value_int();
1023     header.read_major_minor(jp);
1024     jp.next(gx_system::JsonParser::value_number);
1025     mtime = jp.current_value_int();
1026     jp.next(gx_system::JsonParser::end_array);
1027     if (!check_mtime(filename, mtime)) {
1028 	*mtime_diff = true;
1029 	if (mtime == 0) {
1030 	    gx_print_error(filename.c_str(), _("not found"));
1031 	    return false;
1032 	}
1033 	try {
1034 	    open();
1035 	} catch (gx_system::JsonException& e) {
1036 	    set_flag(PRESET_FLAG_INVALID, true);
1037 	    gx_print_error(filename.c_str(), _("parse error"));
1038 	    return false;
1039 	}
1040 	set_flag(PRESET_FLAG_INVALID, false);
1041 	check_flags();
1042     }
1043     return true;
1044 }
1045 
writeJSON(JsonWriter & jw)1046 void PresetFile::writeJSON(JsonWriter& jw) {
1047     assert(tp == PRESET_FILE || tp == PRESET_SCRATCH);
1048     jw.begin_array();
1049     jw.write(name);
1050     jw.write(Gio::File::create_for_path(filename)->get_basename());
1051     jw.write(tp);
1052     jw.write(flags);
1053     header.write_major_minor(jw);
1054     jw.write(static_cast<int>(mtime));
1055     jw.end_array(true);
1056 }
1057 
open()1058 void PresetFile::open() {
1059     close();
1060     entries.clear();
1061     if (filename.empty()) {
1062 	return;
1063     }
1064     check_mtime(filename, mtime);
1065     is = new ifstream(filename.c_str());
1066     JsonParser jp(is);
1067     jp.next(JsonParser::begin_array);
1068     header.read(jp);
1069     while (jp.peek() == JsonParser::value_string) {
1070 	jp.next();
1071 	if (jp.current_value() == "midi_controller") { //FIXME there should be a file signature
1072 	    if (jp.peek() == JsonParser::begin_array) {
1073 		entries.clear();
1074 		is->setstate(istream::failbit);
1075 		gx_print_error(
1076 		    _("open preset"),
1077 		    boost::format(_("%1% is a state file, not a preset file")) % filename);
1078 		throw JsonException(_("This is a state file, not a preset file"));
1079 	    }
1080 	}
1081 	streampos pos = jp.get_streampos();
1082 	entries.push_back(Position(jp.current_value(), pos));
1083 	jp.skip_object();
1084     }
1085     jp.next(JsonParser::end_array);
1086     jp.next(JsonParser::end_token);
1087 }
1088 
size()1089 int PresetFile::size() {
1090     try {
1091 	reopen();
1092     } catch (gx_system::JsonException& e) {
1093 	gx_print_error(filename.c_str(), _("parse error"));
1094     }
1095     return entries.size();
1096 }
1097 
ensure_is_current()1098 bool PresetFile::ensure_is_current() {
1099     if (filename.empty() || check_mtime(filename, mtime)) {
1100 	return true;
1101     }
1102     if (!mtime) {
1103 	return true;
1104     }
1105     close();
1106     return false;
1107 }
1108 
is_newer(time_t m)1109 bool PresetFile::is_newer(time_t m) {
1110     check_mtime(filename, mtime);
1111     return mtime >= m;
1112 }
1113 
open(const std::string & fname)1114 void PresetFile::open(const std::string& fname) {
1115     filename = fname;
1116     open();
1117 }
1118 
get_header()1119 const SettingsFileHeader& PresetFile::get_header() {
1120     reopen();
1121     return header;
1122 }
1123 
fill_names(vector<Glib::ustring> & l)1124 void PresetFile::fill_names(vector<Glib::ustring>& l) {
1125     reopen();
1126     for (vector<Position>::const_iterator i = entries.begin(); i != entries.end(); ++i) {
1127 	l.push_back(i->name);
1128     }
1129 }
1130 
get_name(int n)1131 const Glib::ustring& PresetFile::get_name(int n) {
1132     reopen();
1133     return entries.at(n).name;
1134 }
1135 
get_index(const Glib::ustring & name)1136 int PresetFile::get_index(const Glib::ustring& name) {
1137     try {
1138 	reopen();
1139     } catch (gx_system::JsonException& e) {
1140 	// will be reported elsewhere
1141 	return -1;
1142     }
1143     for (int i = 0; i < size(); i++) {
1144 	if (name == entries[i].name) {
1145 	    return i;
1146 	}
1147     }
1148     return -1;
1149 }
1150 
create_reader(int n)1151 JsonParser *PresetFile::create_reader(int n) {
1152     reopen();
1153     JsonParser *jp = new JsonParser(is);
1154     jp->set_streampos(entries.at(n).pos);
1155     return jp;
1156 }
1157 
PresetTransformer(string fname,istream * is_)1158 PresetTransformer::PresetTransformer(string fname, istream* is_)
1159     : JsonWriter(),
1160       filename(fname),
1161       tmpfile(filename + "_tmp"),
1162       os(tmpfile.c_str()),
1163       is(is_),
1164       jp(is_),
1165       header() {
1166     set_stream(&os);
1167     if (!is->fail()) {
1168 	is->seekg(0);
1169 	jp.next(JsonParser::begin_array);
1170 	header.read(jp);
1171     }
1172     begin_array();
1173     header.write(*this);
1174 }
1175 
~PresetTransformer()1176 PresetTransformer::~PresetTransformer() {
1177     // JsonParser destructor will only run JsonParser::close()
1178     close();
1179 }
1180 
abort()1181 void PresetTransformer::abort() {
1182     if (is_closed()) {
1183 	return;
1184     }
1185     JsonWriter::close();
1186     delete is;
1187     is = 0;
1188     os.close();
1189     remove(tmpfile.c_str());
1190 }
1191 
close_nocheck()1192 void PresetTransformer::close_nocheck() {
1193     end_array(true);
1194     JsonWriter::close();
1195     delete is;
1196     is = 0;
1197     os.close();
1198     if (!os.good()) {
1199 	gx_print_error(_("save preset"),
1200 		       boost::format(_("couldn't write %1%")) % tmpfile);
1201 	return;
1202     }
1203     int rc = rename(tmpfile.c_str(), filename.c_str());
1204     if (rc != 0) {
1205 	gx_print_error(_("save preset"),
1206 		       boost::format(_("couldn't rename %1% to %2%"))
1207 		       % tmpfile % filename);
1208     }
1209 }
1210 
close()1211 void PresetTransformer::close() {
1212     if (is_closed()) {
1213 	return;
1214     }
1215     if (!is->fail()) {
1216 	jp.next(JsonParser::end_array);
1217 	jp.next(JsonParser::end_token);
1218     }
1219     close_nocheck();
1220 }
1221 
1222 class ModifyPreset: public PresetTransformer {
1223 public:
1224     ModifyPreset(string filename, istream* is, const Glib::ustring& presname);
1225     ~ModifyPreset();
1226     void close();
1227     void copy_object();
1228 };
1229 
~ModifyPreset()1230 ModifyPreset::~ModifyPreset() {
1231     close();
1232 }
1233 
ModifyPreset(string fname,istream * is,const Glib::ustring & presname)1234 ModifyPreset::ModifyPreset(string fname, istream* is, const Glib::ustring& presname)
1235     : PresetTransformer(fname, is) {
1236     if (!is->fail()) {
1237 	while (jp.peek() != JsonParser::end_array) {
1238 	    jp.next(JsonParser::value_string);
1239 	    if (jp.current_value() == presname) {
1240 		return;
1241 	    } else {
1242 		write(jp.current_value());
1243 		jp.copy_object(*this);
1244 	    }
1245 	}
1246     }
1247 }
1248 
close()1249 void ModifyPreset::close() {
1250     if (is_closed()) {
1251 	return;
1252     }
1253     if (!is->fail()) {
1254 	while (jp.peek() != JsonParser::end_array) {
1255 	    jp.next(JsonParser::value_string);
1256 	    write(jp.current_value());
1257 	    jp.copy_object(*this);
1258 	}
1259     }
1260     PresetTransformer::close();
1261 }
1262 
copy_object()1263 void ModifyPreset::copy_object() {
1264     jp.copy_object(*this);
1265 }
1266 
create_writer(const Glib::ustring & name)1267 JsonWriter *PresetFile::create_writer(const Glib::ustring& name) {
1268     reopen();
1269     ModifyPreset *jw = new ModifyPreset(filename, is, name);
1270     jw->write(name);
1271     if (!is->fail()) {
1272 	if (jw->jp.peek() != JsonParser::end_array) {
1273 	    jw->jp.skip_object(); // we are replacing a setting
1274 	}
1275     }
1276     is = 0;
1277     return jw;
1278 }
1279 
create_writer_at(const Glib::ustring & pos,const Glib::ustring & name)1280 JsonWriter *PresetFile::create_writer_at(const Glib::ustring& pos, const Glib::ustring& name) {
1281     reopen();
1282     ModifyPreset *jw = new ModifyPreset(filename, is, pos);
1283     jw->write(name);
1284     is = 0;
1285     return jw;
1286 }
1287 
create_transformer()1288 PresetTransformer *PresetFile::create_transformer() {
1289     reopen();
1290     PresetTransformer *tr = new PresetTransformer(filename, is);
1291     is = 0;
1292     return tr;
1293 }
1294 
erase(const Glib::ustring & name)1295 bool PresetFile::erase(const Glib::ustring& name) {
1296     reopen();
1297     if (get_index(name) < 0) {
1298 	return false;
1299     }
1300     ModifyPreset jw(filename, is, name);
1301     is = 0;
1302     jw.jp.skip_object();
1303     return true;
1304 }
1305 
clear()1306 bool PresetFile::clear() {
1307     if (!SettingsFileHeader::make_empty_settingsfile(filename)) {
1308 	return false;
1309     }
1310     open(filename);
1311     return true;
1312 }
1313 
rename(const Glib::ustring & name,Glib::ustring newname)1314 bool PresetFile::rename(const Glib::ustring& name, Glib::ustring newname) {
1315     reopen();
1316     if (get_index(name) < 0) {
1317 	return false;
1318     }
1319     ModifyPreset jw(filename, is, name);
1320     is = 0;
1321     jw.write(newname);
1322     jw.jp.copy_object(jw);
1323     return true;
1324 }
1325 
set_name(const Glib::ustring & n,const std::string & newfile)1326 bool PresetFile::set_name(const Glib::ustring& n, const std::string& newfile) {
1327     if (!Gio::File::create_for_path(filename)->move(Gio::File::create_for_path(newfile))) {
1328 	gx_print_error(_("rename bank"),
1329 		       boost::format(_("couldn't move to %1%")) % newfile);
1330 	return false;
1331     }
1332     name = n;
1333     filename = newfile;
1334     return true;
1335 }
1336 
remove_file()1337 bool PresetFile::remove_file() {
1338     if (!Gio::File::create_for_path(filename)->remove()) {
1339 	gx_print_error(_("remove bank"),
1340 		       boost::format(_("couldn't remove %1%")) % filename);
1341 	return false;
1342     }
1343     filename = "";
1344     return true;
1345 }
1346 
begin()1347 PresetFile::iterator PresetFile::begin() {
1348     if (flags & PRESET_FLAG_INVALID) {
1349 	return entries.end();
1350     }
1351     try {
1352 	reopen();
1353     } catch (gx_system::JsonException& e) {
1354 	gx_print_error(filename.c_str(), _("parse error"));
1355     }
1356     return entries.begin();
1357 }
1358 
1359 
1360 /****************************************************************
1361  ** class PresetBanks
1362  */
1363 
1364 static const char *std_presetname_postfix = ".gx";
1365 
PresetBanks()1366 PresetBanks::PresetBanks()
1367     : banklist(), filepath(), mtime(), preset_dir() {
1368 }
1369 
~PresetBanks()1370 PresetBanks::~PresetBanks() {
1371     for (iterator i = begin(); i != end(); ++i) {
1372 	delete *i;
1373     }
1374 }
1375 
readJSON_remote(gx_system::JsonParser & jp)1376 void PresetBanks::readJSON_remote(gx_system::JsonParser& jp) {
1377     for (iterator i = begin(); i != end(); ++i) {
1378 	delete *i;
1379     }
1380     banklist.clear();
1381     jp.next(gx_system::JsonParser::begin_array);
1382     while (jp.peek() != gx_system::JsonParser::end_array) {
1383 	gx_system::PresetFile *pf = new gx_system::PresetFile;
1384 	pf->readJSON_remote(jp);
1385 	banklist.push_back(pf);
1386     }
1387     jp.next(gx_system::JsonParser::end_array);
1388 }
1389 
check_reparse()1390 bool PresetBanks::check_reparse() {
1391     if (check_mtime(filepath, mtime)) {
1392 	bool reload = false;
1393 	for (iterator i = begin(); i != end(); ++i) {
1394 	    int tp = i->get_type();
1395 	    if (tp == PresetFile::PRESET_FILE || tp == PresetFile::PRESET_SCRATCH) {
1396 		if (!i->ensure_is_current()) {
1397 		    try {
1398 			i->reopen();
1399 			i->set_flag(PRESET_FLAG_INVALID, false);
1400 		    } catch (gx_system::JsonException& e) {
1401 			i->set_flag(PRESET_FLAG_INVALID, true);
1402 			// no message, we already know the error
1403 		    }
1404 		    i->check_flags();
1405 		    reload = true;
1406 		}
1407 	    }
1408 	}
1409 	return reload;
1410     }
1411     for (bl_type::iterator i = banklist.begin(); i != banklist.end();) {
1412 	int tp = (*i)->get_type();
1413 	if (tp == PresetFile::PRESET_FILE || tp == PresetFile::PRESET_SCRATCH) {
1414 	    bl_type::iterator j = i;
1415 	    ++i;
1416 	    delete *j;
1417 	    banklist.erase(j);
1418 	} else {
1419 	    ++i;
1420 	}
1421     }
1422     parse_bank_list(banklist.begin());
1423     return true;
1424 }
1425 
parse(const std::string & bank_path,const std::string & preset_dir_,const std::string & factory_dir,const char * scratchpad_name,const char * scratchpad_file)1426 void PresetBanks::parse(const std::string& bank_path, const std::string& preset_dir_,
1427 			const std::string& factory_dir, const char* scratchpad_name,
1428 			const char* scratchpad_file) {
1429     filepath = bank_path;
1430     preset_dir = preset_dir_;
1431     banklist.clear();
1432     parse_bank_list(banklist.end());
1433     collect_lost_banks(scratchpad_name, scratchpad_file);
1434     parse_factory_list(factory_dir);
1435 }
1436 
make_valid_utf8(Glib::ustring & s)1437 void PresetBanks::make_valid_utf8(Glib::ustring& s) {
1438    Glib::ustring::iterator i;
1439    while (!s.validate(i)) {
1440        Glib::ustring::iterator j = i;
1441        s.replace(i,++j,1,'?');
1442    }
1443    if (s.empty()) {
1444        s = "?";
1445    }
1446 }
1447 
add_preset_postfix(const std::string & filename)1448 std::string PresetBanks::add_preset_postfix(const std::string& filename) {
1449     return filename + std_presetname_postfix;
1450 }
1451 
strip_preset_postfix(std::string & name)1452 bool PresetBanks::strip_preset_postfix(std::string& name) {
1453     if (name.compare(name.size()-3, 3, std_presetname_postfix) != 0) {
1454 	return false;
1455     }
1456     name = name.substr(0, name.size()-3);
1457     return true;
1458 }
1459 
make_bank_unique(Glib::ustring & name,std::string * file)1460 void PresetBanks::make_bank_unique(Glib::ustring& name, std::string *file) {
1461     int n = 1;
1462     Glib::ustring t = name;
1463     while (true) {
1464 	if (file) {
1465 	    *file = add_preset_postfix(Glib::build_filename(preset_dir, encode_filename(name)));
1466 	}
1467 	if (!has_entry(name)) {
1468 	    if (!file || !Gio::File::create_for_path(*file)->query_exists()) {
1469 		return;
1470 	    }
1471 	}
1472 	name = t + "-" + gx_system::to_string(n);
1473 	n += 1;
1474     }
1475 }
1476 
has_file(const std::string & file) const1477 bool PresetBanks::has_file(const std::string& file) const {
1478     for (bl_type::const_iterator i = banklist.begin(); i != banklist.end(); ++i) {
1479 	if ((*i)->get_filename() == file) {
1480 	    return true;
1481 	}
1482     }
1483     return false;
1484 }
1485 
collect_lost_banks(const char * scratchpad_name,const char * scratchpad_file)1486 void PresetBanks::collect_lost_banks(const char* scratchpad_name, const char* scratchpad_file) {
1487     Glib::RefPtr<Gio::FileEnumerator> en = Gio::File::create_for_path(
1488 	preset_dir)->enumerate_children(G_FILE_ATTRIBUTE_STANDARD_NAME);
1489     while (true) {
1490 	Glib::RefPtr<Gio::FileInfo> fi = en->next_file();
1491 	if (!fi) {
1492 	    break;
1493 	}
1494 	std::string n = fi->get_name();
1495 	if (n.size() <= 3 || n.substr(n.size()-3) != std_presetname_postfix) {
1496 	    continue;
1497 	}
1498 	std::string path = Glib::build_filename(preset_dir, n);
1499 	if (has_file(path)) {
1500 	    continue;
1501 	}
1502 	PresetFile *f = new PresetFile();
1503 	if (n == scratchpad_file) {
1504 	    Glib::ustring nm = scratchpad_name;
1505 	    make_bank_unique(nm);
1506 	    f->open_file(nm, path, PresetFile::PRESET_SCRATCH, 0);
1507 	} else {
1508 	    strip_preset_postfix(n);
1509 	    Glib::ustring nm = decode_filename(n);
1510 	    make_valid_utf8(nm);
1511 	    make_bank_unique(nm);
1512 	    f->open_file(nm, path, PresetFile::PRESET_FILE, 0);
1513 	}
1514 	insert(f);
1515     }
1516 }
1517 
1518 
insert(PresetFile * f,int position)1519 void PresetBanks::insert(PresetFile* f, int position) {
1520     std::list<PresetFile*>::iterator i = banklist.begin();
1521     for (; position > 0 && i != banklist.end(); ++i, --position);
1522     banklist.insert(i, f);
1523     save();
1524 }
1525 
check_save()1526 void PresetBanks::check_save() {
1527     for (iterator i = begin(); i != end(); ++i) {
1528 	int tp = i->get_type();
1529 	if (tp == PresetFile::PRESET_FILE || tp == PresetFile::PRESET_SCRATCH) {
1530 	    if (i->is_newer(mtime)) {
1531 		save();
1532 		return;
1533 	    }
1534 	}
1535     }
1536 }
1537 
save()1538 void PresetBanks::save() {
1539     if (filepath.empty()) { //FIXME remote operation hack
1540 	return;
1541     }
1542     std::string tmpfile = filepath + "_tmp";
1543     ofstream os(tmpfile.c_str());
1544     gx_system::JsonWriter jw(&os);
1545     jw.begin_array(true);
1546     for (iterator i = begin(); i != end(); ++i) {
1547 	int tp = i->get_type();
1548 	if (tp == PresetFile::PRESET_FILE || tp == PresetFile::PRESET_SCRATCH) {
1549 	    i->writeJSON(jw);
1550 	}
1551     }
1552     jw.end_array(true);
1553     jw.close();
1554     os.close();
1555     if (!os.good()) {
1556 	gx_print_error(_("save banklist"),
1557 		       boost::format(_("couldn't write %1%")) % tmpfile);
1558     } else {
1559 	int rc = ::rename(tmpfile.c_str(), filepath.c_str());
1560 	if (rc != 0) {
1561 	    gx_print_error(_("save banklist"),
1562 			   boost::format(_("couldn't rename %1% to %2%"))
1563 			   % tmpfile % filepath);
1564 	}
1565     }
1566     check_mtime(filepath, mtime);
1567 }
1568 
parse_factory_list(const std::string & path)1569 void PresetBanks::parse_factory_list(const std::string& path) {
1570     ifstream is(Glib::build_filename(path, "dirlist.js").c_str());
1571     if (is.fail()) {
1572 	gx_print_error(_("Presets"), _("factory preset list not found"));
1573 	return;
1574     }
1575     gx_system::JsonParser jp(&is);
1576     PresetFile *f = 0;
1577     try {
1578 	jp.next(gx_system::JsonParser::begin_array);
1579 	while (jp.peek() != gx_system::JsonParser::end_array) {
1580 	    jp.next(gx_system::JsonParser::begin_array);
1581 	    jp.next(gx_system::JsonParser::value_string);
1582 	    string name = jp.current_value();
1583 	    jp.next(gx_system::JsonParser::value_string);
1584 	    string fname = Glib::build_filename(path, jp.current_value());
1585 	    PresetFile *f = new PresetFile();
1586 	    try {
1587 		if (f->set_factory(name, fname)) {
1588 		    banklist.push_back(f);
1589 		} else {
1590 		    delete f;
1591 		}
1592 	    } catch (gx_system::JsonException& e) {
1593 		delete f;
1594 		gx_print_error(fname.c_str(), _("not found or parse error"));
1595 	    }
1596 	    f = 0;
1597 	    jp.next(gx_system::JsonParser::end_array);
1598 	}
1599 	jp.next(gx_system::JsonParser::end_array);
1600 	jp.next(gx_system::JsonParser::end_token);
1601     } catch (gx_system::JsonException& e) {
1602 	delete f;
1603     }
1604     jp.close();
1605     is.close();
1606 }
1607 
parse_bank_list(bl_type::iterator pos)1608 void PresetBanks::parse_bank_list(bl_type::iterator pos) {
1609     ifstream is(filepath.c_str());
1610     if (is.fail()) {
1611 	gx_print_error(
1612 	    _("Presets"), boost::format(_("banks not found: '%1%'")) % filepath);
1613 	return;
1614     }
1615     gx_system::JsonParser jp(&is);
1616     bool mtime_diff = false;
1617     PresetFile *f = 0;
1618     try {
1619 	jp.next(gx_system::JsonParser::begin_array);
1620 	while (jp.peek() != gx_system::JsonParser::end_array) {
1621 	    f = new PresetFile();
1622 	    if (!f->readJSON(preset_dir, jp, &mtime_diff)) {
1623 		delete f;
1624 	    } else {
1625 		banklist.insert(pos, f);
1626 	    }
1627 	    f = 0;
1628 	}
1629 	jp.next(gx_system::JsonParser::end_array);
1630 	jp.next(gx_system::JsonParser::end_token);
1631     } catch (gx_system::JsonException& e) {
1632 	delete f;
1633 	gx_print_error(filepath.c_str(), _("parse error"));
1634     }
1635     jp.close();
1636     is.close();
1637     if (mtime_diff) {
1638 	save();
1639     } else {
1640 	check_mtime(filepath, mtime);
1641     }
1642 }
1643 
get_file(const Glib::ustring & bank) const1644 PresetFile *PresetBanks::get_file(const Glib::ustring& bank) const {
1645     for (bl_type::const_iterator i = banklist.begin(); i != banklist.end(); ++i) {
1646 	if ((*i)->get_name() == bank) {
1647 	    return *i;
1648 	}
1649     }
1650     return 0;
1651 }
1652 
get_index(const Glib::ustring & bank) const1653 int PresetBanks::get_index(const Glib::ustring& bank) const {
1654     int n = 0;
1655     for (bl_type::const_iterator i = banklist.begin(); i != banklist.end(); ++i) {
1656 	if ((*i)->get_name() == bank) {
1657 	    return n;
1658 	}
1659 	n += 1;
1660     }
1661     return -1;
1662 }
1663 
rename(const Glib::ustring & oldname,const Glib::ustring & newname,const std::string & newfile)1664 bool PresetBanks::rename(const Glib::ustring& oldname, const Glib::ustring& newname, const std::string& newfile) {
1665     PresetFile *f = get_file(oldname);
1666     if (!f) {
1667 	return false;
1668     }
1669     if (!f->set_name(newname, newfile)) {
1670 	return false;
1671     }
1672     save();
1673     return true;
1674 }
1675 
remove(const Glib::ustring & bank)1676 bool PresetBanks::remove(const Glib::ustring& bank) {
1677     PresetFile *f = get_file(bank);
1678     if (!f) {
1679 	return false;
1680     }
1681     if (!f->remove_file()) {
1682 	return false;
1683     }
1684     banklist.remove(f);
1685     delete f;
1686     save();
1687     return true;
1688 }
1689 
reorder(const std::vector<Glib::ustring> & neworder)1690 void PresetBanks::reorder(const std::vector<Glib::ustring>& neworder) {
1691     bl_type::iterator j = banklist.begin();
1692     for (std::vector<Glib::ustring>::const_iterator i = neworder.begin(); i != neworder.end(); ++i) {
1693 	assert(j != banklist.end());
1694 	if (*i == (*j)->get_name()) {
1695 	    ++j;
1696 	} else {
1697 	    for (bl_type::iterator k = j; k != banklist.end(); ++k) {
1698 		if (*i == (*k)->get_name()) {
1699 		    banklist.splice(j, banklist, k);
1700 		    break;
1701 		}
1702 	    }
1703 	}
1704     }
1705     save();
1706 }
1707 
get_name(int n)1708 Glib::ustring PresetBanks::get_name(int n) {
1709 	for (iterator i = begin(); i != end(); ++i, --n) {
1710 	if (n == 0) {
1711 	    return i->get_name();
1712 	}
1713     }
1714     return "";
1715 }
1716 
1717 /****************************************************************
1718  ** class GxSettingsBase
1719  */
1720 
~AbstractStateIO()1721 AbstractStateIO::~AbstractStateIO() {}
~AbstractPresetIO()1722 AbstractPresetIO::~AbstractPresetIO() {}
1723 
1724 // seq_ may not yet be initialized, only use address!
GxSettingsBase(gx_engine::EngineControl & seq_)1725 GxSettingsBase::GxSettingsBase(gx_engine::EngineControl& seq_)
1726     : state_io(),
1727       preset_io(),
1728       statefile(),
1729       banks(),
1730       current_bank(),
1731       current_name(),
1732       seq(seq_),
1733       selection_changed(),
1734       presetlist_changed() {
1735 }
1736 
~GxSettingsBase()1737 GxSettingsBase::~GxSettingsBase() {
1738 }
1739 
loadsetting(PresetFile * p,const Glib::ustring & name)1740 bool GxSettingsBase::loadsetting(PresetFile *p, const Glib::ustring& name) {
1741     try {
1742 	if (p) {
1743 	    JsonParser *jp = p->create_reader(name);
1744 	    preset_io->read_preset(*jp, p->get_header());
1745 	    seq.wait_ramp_down_finished();
1746 	    preset_io->commit_preset();
1747 	    delete jp;
1748 	    gx_print_info(
1749 		_("loaded preset"),
1750 		boost::format(_("%1% from file %2%")) % name % p->get_filename());
1751 
1752 	} else {
1753 	    JsonParser *jp = statefile.create_reader();
1754 	    state_io->read_state(*jp, statefile.get_header());
1755 	    seq.wait_ramp_down_finished();
1756 	    state_io->commit_state();
1757 	    delete jp;
1758 	    gx_print_info(
1759 		_("loaded state"),
1760 		boost::format(_("from file %1%")) % statefile.get_filename());
1761 	}
1762 	return seq.update_module_lists();
1763     } catch(JsonException& e) {
1764 	if (p) {
1765 	    gx_print_error(
1766 		_("load preset"),
1767 		boost::format(_("error loading %1% from file %2%")) % name % p->get_filename());
1768 	} else {
1769 	    gx_print_warning(
1770 		_("load state"),
1771 		boost::format(_("error loading state from file %1%"))
1772 		% statefile.get_filename());
1773 	}
1774 	return false;
1775     }
1776 }
1777 
load_preset(PresetFile * pf,const Glib::ustring & name)1778 void GxSettingsBase::load_preset(PresetFile* pf, const Glib::ustring& name) {
1779     PresetFile *p = get_current_bank_file();
1780     if (p && p->has_entry(current_name) && p->get_type() == gx_system::PresetFile::PRESET_SCRATCH) {
1781         JsonWriter *jw = 0;
1782         try {
1783             jw = p->create_writer(current_name);
1784             preset_io->write_preset(*jw);
1785         } catch(JsonException& e) {
1786             gx_print_warning(
1787                 _("save preset"),
1788                 boost::format(_("parse error in %1%"))
1789                 % p->get_filename());
1790         }
1791         delete jw;
1792     }
1793     if (!pf->has_entry(name)) {
1794         gx_print_error(_("open preset"), Glib::ustring::compose("bank %1 does not contain preset %2", pf->get_name(), name));
1795         pf = 0;
1796     }
1797     if (!pf) {
1798         if (setting_is_preset()) {
1799             current_bank = "";
1800             current_name = "";
1801             selection_changed();
1802         }
1803         return;
1804     }
1805     current_bank = pf->get_name();
1806     current_name = name;
1807     seq.start_ramp_down();
1808     bool modules_changed = loadsetting(pf, name);
1809     seq.start_ramp_up();
1810     // if no modules changed either there was no change (then
1811     // rack_changed should not be set anyhow) or the modules
1812     // could not be installed because jack is not initialized.
1813     // In that case there is still a rack change left to be
1814     // done
1815     if (modules_changed) {
1816         seq.clear_rack_changed();
1817     }
1818     selection_changed();
1819 }
1820 
loadstate()1821 void GxSettingsBase::loadstate() {
1822     current_bank = current_name = "";
1823     seq.start_ramp_down();
1824     bool modules_changed = loadsetting(0, current_name);
1825     seq.start_ramp_up();
1826     if (modules_changed) { // see comment in load_preset()
1827 	seq.clear_rack_changed();
1828     }
1829     presetlist_changed();
1830     selection_changed();
1831 }
1832 
set_source_to_state()1833 void GxSettingsBase::set_source_to_state() {
1834     current_bank = current_name = "";
1835     selection_changed();
1836 }
1837 
save_to_state(bool preserve_preset)1838 void GxSettingsBase::save_to_state(bool preserve_preset) {
1839     gx_print_info("write state",boost::format("%2% [%1%]")
1840 			     % preserve_preset % statefile.get_filename());
1841     JsonWriter *jw = statefile.create_writer(&preserve_preset);
1842     state_io->write_state(*jw, preserve_preset);
1843     delete jw;
1844 #if 0
1845     if (!preserve_preset && setting_is_preset()) {
1846 	set_source_to_state();
1847 	presetlist_changed();
1848     }
1849 #endif
1850 }
1851 
append(PresetFile & pf,const Glib::ustring & src,PresetFile & pftgt,const Glib::ustring & name)1852 void GxSettingsBase::append(PresetFile& pf, const Glib::ustring& src, PresetFile& pftgt, const Glib::ustring& name) {
1853     JsonWriter *jw = 0;
1854     JsonParser *jp = 0;
1855     try {
1856 	jp = pf.create_reader(src);
1857 	jw = pftgt.create_writer(name);
1858 	jp->copy_object(*jw);
1859     } catch(JsonException& e) {
1860 	gx_print_warning(
1861 	    _("save preset"),
1862 	    boost::format(_("parse error in %1%"))
1863 	    % pf.get_filename());
1864     }
1865     delete jp;
1866     delete jw;
1867     presetlist_changed();
1868 }
1869 
insert_before(PresetFile & pf,const Glib::ustring & src,PresetFile & pftgt,const Glib::ustring & pos,const Glib::ustring & name)1870 void GxSettingsBase::insert_before(PresetFile& pf, const Glib::ustring& src, PresetFile& pftgt, const Glib::ustring& pos, const Glib::ustring& name) {
1871     JsonWriter *jw = 0;
1872     JsonParser *jp = 0;
1873     try {
1874 	jp = pf.create_reader(src);
1875 	jw = pftgt.create_writer_at(pos, name);
1876 	jp->copy_object(*jw);
1877 	jw->write(pos);
1878 	dynamic_cast<ModifyPreset*>(jw)->copy_object();
1879     } catch(JsonException& e) {
1880 	gx_print_warning(
1881 	    _("save preset"),
1882 	    boost::format(_("parse error in %1%"))
1883 	    % pf.get_filename());
1884     }
1885     delete jp;
1886     delete jw;
1887     presetlist_changed();
1888 }
1889 
insert_after(PresetFile & pf,const Glib::ustring & src,PresetFile & pftgt,const Glib::ustring & pos,const Glib::ustring & name)1890 void GxSettingsBase::insert_after(PresetFile& pf, const Glib::ustring& src, PresetFile& pftgt, const Glib::ustring& pos, const Glib::ustring& name) {
1891     int i = pftgt.get_index(pos) + 1;
1892     if (i >= pftgt.size()) {
1893 	append(pf, src, pftgt, name);
1894     } else {
1895 	insert_before(pf, src, pftgt, pftgt.get_name(i), name);
1896     }
1897 }
1898 
save(PresetFile & pf,const Glib::ustring & name)1899 void GxSettingsBase::save(PresetFile& pf, const Glib::ustring& name) {
1900     bool newentry = (pf.get_index(name) < 0);
1901     JsonWriter *jw = 0;
1902     try {
1903 	jw = pf.create_writer(name);
1904 	preset_io->write_preset(*jw);
1905     } catch(JsonException& e) {
1906 	gx_print_warning(
1907 	    _("save preset"),
1908 	    boost::format(_("parse error in %1%"))
1909 	    % pf.get_filename());
1910     }
1911     delete jw;
1912     if (newentry) {
1913 	presetlist_changed();
1914     }
1915     if (!setting_is_preset()
1916 	|| (setting_is_preset() && current_name != name)) {
1917 	current_name = name;
1918 	current_bank = pf.get_name();
1919 	presetlist_changed();
1920 	selection_changed();
1921     }
1922 }
1923 
reorder_preset(PresetFile & pf,const std::vector<Glib::ustring> & neworder)1924 void GxSettingsBase::reorder_preset(PresetFile& pf, const std::vector<Glib::ustring>& neworder) {
1925     PresetTransformer *jw = 0;
1926     try {
1927 	jw = pf.create_transformer();
1928 	for (std::vector<Glib::ustring>::const_iterator i = neworder.begin(); i != neworder.end(); ++i) {
1929 	    JsonParser *jp = pf.create_reader(*i);
1930 	    jw->write(*i);
1931 	    jp->copy_object(*jw);
1932 	    delete jp;
1933 	}
1934 	jw->close_nocheck();
1935     } catch(JsonException& e) {
1936 	gx_print_error(
1937 	    _("reorder presetfile"),
1938 	    boost::format(_("parse error in %1%"))
1939 	    % pf.get_filename());
1940 	jw->abort();
1941     }
1942     delete jw;
1943     pf.close();
1944     presetlist_changed();
1945 }
1946 
erase_preset(PresetFile & pf,const Glib::ustring & name)1947 void GxSettingsBase::erase_preset(PresetFile& pf, const Glib::ustring& name) {
1948     try {
1949 	pf.erase(name);
1950     } catch(JsonException& e) {
1951 	gx_print_warning(
1952 	    _("delete preset"),
1953 	    boost::format(_("parse error in %1%"))
1954 	    % pf.get_filename());
1955     }
1956     if (pf.get_name() == current_bank && name == current_name) {
1957 	set_source_to_state();
1958     }
1959     presetlist_changed();
1960 }
1961 
convert_preset(PresetFile & pf)1962 bool GxSettingsBase::convert_preset(PresetFile& pf) {
1963     seq.start_ramp_down();
1964     bool preset_preset = false;
1965     JsonWriter *sw = statefile.create_writer(&preset_preset);
1966     state_io->write_state(*sw, preset_preset);
1967     delete sw;
1968     seq.wait_ramp_down_finished();
1969     PresetTransformer *jw = 0;
1970     bool res = true;
1971     try {
1972 	jw = pf.create_transformer();
1973 	while (jw->jp.peek() != JsonParser::end_array) {
1974 	    jw->jp.next(JsonParser::value_string);
1975 	    jw->write(jw->jp.current_value());
1976 	    preset_io->copy_preset(jw->jp, jw->header, *jw);
1977 	}
1978 	pf.set_flag(PRESET_FLAG_VERSIONDIFF, false);
1979     } catch(JsonException& e) {
1980 	gx_print_warning(
1981 	    _("convert presetfile"),
1982 	    boost::format(_("parse error in %1%"))
1983 	    % pf.get_filename());
1984 	res = false;
1985     }
1986     delete jw;
1987     JsonParser *sp = statefile.create_reader();
1988     state_io->read_state(*sp, statefile.get_header());
1989     state_io->commit_state();
1990     delete sp;
1991     seq.start_ramp_up();
1992     if (res) {
1993 	presetlist_changed();
1994     }
1995     return res;
1996 }
1997 
rename_bank(const Glib::ustring & oldname,const Glib::ustring & newname,const std::string & newfile)1998 bool GxSettingsBase::rename_bank(const Glib::ustring& oldname, const Glib::ustring& newname, const std::string& newfile) {
1999     if (!banks.rename(oldname, newname, newfile)) {
2000 	return false;
2001     }
2002     if (setting_is_preset() && oldname == current_bank) {
2003 	current_bank = newname;
2004 	presetlist_changed();
2005 	selection_changed();
2006     } else {
2007 	presetlist_changed();
2008     }
2009     return true;
2010 }
2011 
remove_bank(const Glib::ustring & bank)2012 bool GxSettingsBase::remove_bank(const Glib::ustring& bank) {
2013     if (!banks.remove(bank)) {
2014 	return false;
2015     }
2016     if (bank == current_bank) {
2017 	set_source_to_state();
2018 	presetlist_changed();
2019 	selection_changed();
2020     } else {
2021 	presetlist_changed();
2022     }
2023     return true;
2024 }
2025 
rename_preset(PresetFile & pf,const Glib::ustring & oldname,const Glib::ustring & newname)2026 bool GxSettingsBase::rename_preset(PresetFile& pf, const Glib::ustring& oldname, const Glib::ustring& newname) {
2027     if (!pf.rename(oldname, newname)) {
2028 	return false;
2029     }
2030     presetlist_changed();
2031     if (setting_is_preset() && current_bank == pf.get_name()) {
2032 	if (current_name == oldname) {
2033 	    current_name = newname;
2034 	    selection_changed();
2035 	}
2036     }
2037     return true;
2038 }
2039 
2040 } /* end of gx_system namespace */
2041