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