source: cpp/frams/param/param.cpp @ 334

Last change on this file since 334 was 334, checked in by Maciej Komosinski, 10 years ago

More specific error messages on failed deserialization (and the resulting value becomes null)

  • Property svn:eol-style set to native
File size: 25.0 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include <stdio.h>
6#include <ctype.h>
7
8#include "param.h"
9#include <frams/util/extvalue.h>
10#include "common/framsg.h"
11#include <frams/util/sstringutils.h>
12
13//#define SAVE_ALL_NAMES
14#define SAVE_SELECTED_NAMES
15#define WARN_MISSING_NAME
16
17char MakeCodeGuardHappy;
18
19ParamEntry empty_paramtab[] =
20{ { "Empty", 1, 0, "Empty", }, { 0, 0, 0, }, };
21
22static void czytdotyldy(VirtFILE *f, SString &s)
23{
24        SString temp;
25        int z;
26        char last_char = 0;
27        while ((z = fgetc(f)) != EOF)
28        {
29                if (z == '~')
30                        if (last_char != '\\') break;
31                last_char = (char)z;
32                temp += last_char;
33        }
34        s = temp;
35}
36
37static const char *strchrlimit(const char *t, int ch, const char *limit)
38{
39        if (limit < t) return NULL;
40        return (const char*)memchr((const void*)t, ch, limit - t);
41}
42
43void ParamInterface::copyFrom(ParamInterface *src)
44{
45        int n = getPropCount();
46        ExtValue v;
47        int j;
48        for (int i = 0; i < n; i++)
49                if ((!(flags(i)&PARAM_READONLY))
50                        && (*type(i) != 'p'))
51                {
52                j = src->findId(id(i));
53                if (j < 0) continue;
54                src->get(j, v);
55                set(i, v);
56                }
57}
58
59void ParamInterface::quickCopyFrom(ParamInterface *src)
60{
61        int n = getPropCount();
62        ExtValue v;
63        for (int i = 0; i < n; i++)
64                if ((!(flags(i)&PARAM_READONLY))
65                        && (*type(i) != 'p'))
66                {
67                src->get(i, v);
68                set(i, v);
69                }
70}
71
72int ParamInterface::getMinMax(int prop, paInt& minumum, paInt& maximum, paInt &def)
73{
74        const char* t = type(prop) + 1;
75        while (*t) if (*t == ' ') break; else t++;
76        return sscanf(t, PA_INT_SCANF " " PA_INT_SCANF " " PA_INT_SCANF, &minumum, &maximum, &def);
77}
78
79int ParamInterface::getMinMax(int prop, double& minumum, double& maximum, double& def)
80{
81        const char* t = type(prop) + 1;
82        while (*t) if (*t == ' ') break; else t++;
83        return sscanf(t, "%lg %lg %lg", &minumum, &maximum, &def);
84}
85
86int ParamInterface::getMinMax(int prop, int& minumum, int& maximum, SString& def)
87{
88        const char* t = type(prop) + 1;
89        while (*t) if (*t == ' ') break; else t++;
90        int ret = sscanf(t, "%d %d", &minumum, &maximum);
91        def = SString::empty();
92        if (ret == 2)
93        {
94                while (*t == ' ') t++;
95                for (int skip_fields = 2; skip_fields > 0; skip_fields--)
96                {
97                        while (*t) if (*t == ' ') break; else t++;
98                        while (*t == ' ') t++;
99                }
100                if (*t)
101                {
102                        const char* end = strchr(t, '~');
103                        if (!end)
104                                end = t + strlen(t);
105                        while ((end > t) && (end[-1] == ' ')) end--;
106                        def = SString(t, end - t);
107                }
108                return 3;
109        }
110        else
111                return ret;
112}
113
114void ParamInterface::setDefault()
115{
116        for (int i = 0; i < getPropCount(); i++)
117                setDefault(i);
118}
119
120void ParamInterface::setMin()
121{
122        for (int i = 0; i < getPropCount(); i++)
123                setMin(i);
124}
125
126void ParamInterface::setMax()
127{
128        for (int i = 0; i < getPropCount(); i++)
129                setMax(i);
130}
131
132void ParamInterface::setDefault(int i)
133{
134        const char *t = type(i);
135        switch (*t)
136        {
137        case 'f':
138        {
139                double mn = 0, mx = 0, def = 0;
140                if (getMinMax(i, mn, mx, def) < 3) def = mn;
141                setDouble(i, def);
142        }
143                break;
144        case 'd':
145        {
146                paInt mn = 0, mx = 0, def = 0;
147                if (getMinMax(i, mn, mx, def) < 3) def = mn;
148                setInt(i, def);
149        }
150                break;
151        case 's': case 'x':
152        {
153                int mn, mx; SString def;
154                getMinMax(i, mn, mx, def);
155                if (*t == 's')
156                        setString(i, def);
157                else
158                {
159                        if (def.len() > 0) setExtValue(i, ExtValue(def)); else setExtValue(i, ExtValue::empty());
160                }
161        }
162                break;
163        case 'o':
164                setObject(i, ExtObject::empty());
165                break;
166        }
167}
168
169void ParamInterface::setMin(int i)
170{
171        const char *t = type(i);
172        switch (*t)
173        {
174        case 'f':
175        {
176                double mn = 0, mx = 0, def = 0;
177                getMinMax(i, mn, mx, def);
178                setDouble(i, mn);
179        }
180                break;
181        case 'd':
182        {
183                paInt mn = 0, mx = 0, def = 0;
184                getMinMax(i, mn, mx, def);
185                setInt(i, mn);
186        }
187                break;
188        default: set(i, "");
189        }
190}
191
192void ParamInterface::setMax(int i)
193{
194        const char *t = type(i);
195        switch (*t)
196        {
197        case 'f':
198        {
199                double mn = 0, mx = 0, def = 0;
200                getMinMax(i, mn, mx, def);
201                setDouble(i, mx);
202        }
203                break;
204        case 'd':
205        {
206                paInt mn = 0, mx = 0, def = 0;
207                getMinMax(i, mn, mx, def);
208                setInt(i, mx);
209        }
210                break;
211        default: set(i, "");
212        }
213}
214
215SString ParamInterface::getStringById(const char*prop)
216{
217        int i = findId(prop); if (i >= 0) return getString(i); else return SString();
218}
219paInt ParamInterface::getIntById(const char*prop)
220{
221        int i = findId(prop); if (i >= 0) return getInt(i); else return 0;
222}
223double ParamInterface::getDoubleById(const char*prop)
224{
225        int i = findId(prop); if (i >= 0) return getDouble(i); else return 0;
226}
227ExtObject ParamInterface::getObjectById(const char*prop)
228{
229        int i = findId(prop); if (i >= 0) return getObject(i); else return ExtObject();
230}
231ExtValue ParamInterface::getExtValueById(const char*prop)
232{
233        int i = findId(prop); if (i >= 0) return getExtValue(i); else return ExtValue();
234}
235
236int ParamInterface::setIntById(const char* prop, paInt v)
237{
238        int i = findId(prop); if (i >= 0) return setInt(i, v); else return PSET_NOPROPERTY;
239}
240int ParamInterface::setDoubleById(const char* prop, double v)
241{
242        int i = findId(prop); if (i >= 0) return setDouble(i, v); else return PSET_NOPROPERTY;
243}
244int ParamInterface::setStringById(const char* prop, const SString &v)
245{
246        int i = findId(prop); if (i >= 0) return setString(i, v); else return PSET_NOPROPERTY;
247}
248int ParamInterface::setObjectById(const char* prop, const ExtObject &v)
249{
250        int i = findId(prop); if (i >= 0) return setObject(i, v); else return PSET_NOPROPERTY;
251}
252int ParamInterface::setExtValueById(const char* prop, const ExtValue &v)
253{
254        int i = findId(prop); if (i >= 0) return setExtValue(i, v); else return PSET_NOPROPERTY;
255}
256int ParamInterface::setById(const char* prop, const ExtValue &v)
257{
258        int i = findId(prop); if (i >= 0) return set(i, v); else return PSET_NOPROPERTY;
259}
260
261int ParamInterface::save(VirtFILE* f, const char* altname, bool force)
262{
263        const char *p;
264        SString ws;
265        int err = 0, i;
266        bool withname = false;
267        if ((altname == NULL) || (altname[0] != 0))
268        {
269                err |= (fputs(altname ? altname : getName(), f) == EOF);
270                err |= (fputs(":\n", f) == EOF);
271                withname = true;
272        }
273        for (i = 0; p = id(i); i++)
274                err |= saveprop(f, i, p, force);
275        if (withname)
276                err |= (fputs("\n", f) == EOF);
277        return err;
278}
279
280const char* ParamInterface::SERIALIZATION_PREFIX = "@Serialized:";
281
282int ParamInterface::saveprop(VirtFILE* f, int i, const char* p, bool force)
283{
284        if ((flags(i)&PARAM_DONTSAVE) && (!force)) return 0;
285        const char *typ = type(i);
286        if (*typ == 'p') return 0;
287
288        const char *t, *w;
289        SString ws;
290        int err = 0, cr;
291
292        err |= (fputs(p, f) == EOF); fputc(':', f);
293        cr = 0;
294        if ((*typ == 'x') || (*typ == 'o'))
295        {
296                ExtValue ex;
297                get(i, ex);
298                ws = SString(SERIALIZATION_PREFIX) + ex.serialize();
299        }
300        else
301                ws = get(i);
302        quoteTilde(ws);
303        w = ws;
304        if (ws.len() > 50) cr = 1;
305        else for (t = w; *t; t++) if ((*t == 10) || (*t == 13)) { cr = 1; break; }
306        if (cr) fputs("~\n", f);
307        err |= (fputs(w, f) == EOF);
308        err |= (fputs(cr ? "~\n" : "\n", f) == EOF);
309        return err;
310}
311
312
313int SimpleAbstractParam::isequal(int i, void* defdata)
314{ // defdata->member == object->member ?
315        void *backup = object;
316        switch (type(i)[0])
317        {
318        case 'd':
319        {
320                select(defdata);
321                paInt x = getInt(i);
322                select(backup);
323                return x == getInt(i);
324        }
325        case 'f':
326        {
327                select(defdata);
328                double x = getDouble(i);
329                select(backup);
330                return x == getDouble(i);
331        }
332        case 's':
333        {
334                select(defdata);
335                SString x = getString(i);
336                select(backup);
337                return x == getString(i);
338        }
339        }
340        return 1;
341}
342
343void SimpleAbstractParam::save2(SString& f, void *defdata, bool addcr, bool all_names)
344{ // defdata!=NULL -> does not save default values
345        const char *p;
346        int i;
347        int needlabel = 0;
348        int first = 1;
349        SString val;
350        SString t;
351        int fl;
352        // t+=SString(getName()); t+=':';
353        for (i = 0; p = id(i); i++)
354                if (!((fl = flags(i))&PARAM_DONTSAVE))
355                {
356                if (defdata && isequal(i, defdata))
357                        needlabel = 1;
358                else
359                {
360                        if (!first) t += ", ";
361#ifndef SAVE_ALL_NAMES
362#ifdef SAVE_SELECTED_NAMES
363                        if (needlabel || all_names || !(fl & PARAM_CANOMITNAME))
364#else
365                        if (needlabel)
366#endif
367#endif
368                        {
369                                t += p; t += "="; needlabel = 0;
370                        }
371                        if (type(i)[0] == 's')
372                        { // string - special case
373                                SString str = getString(i);
374                                if (strContainsOneOf(str, ", \\\n\r\t\""))
375                                {
376                                        t += "\"";
377                                        sstringQuote(str);
378                                        t += str;
379                                        t += "\"";
380                                }
381                                else
382                                        t += str;
383                        }
384                        else
385                                t += get(i);
386                        first = 0;
387                }
388                }
389        if (addcr)
390                t += "\n";
391        f += t;
392}
393
394int ParamInterface::load(VirtFILE* f, bool warn_unknown_fields, bool *abortable, int *linenum)
395{
396        SString buf;
397        int i;
398        const char *p, *p0;
399        int p_len;
400        bool loaded;
401        int fields_loaded = 0;
402        while (((!abortable) || (!*abortable)) && loadSStringLine(f, buf))
403        {
404                if (linenum) (*linenum)++;
405                const char* t = (const char*)buf;
406                p0 = t; while ((*p0 == ' ') || (*p0 == '\t')) p0++;
407                if (!*p0) break;
408                if (p0[0] == '#') continue;
409                p = strchr(p0, ':'); if (!p) continue;
410                p_len = (int)(p - p0);
411                loaded = false;
412                if (p_len && ((i = findIdn(p0, p_len)) >= 0))
413                {
414                        if (!(flags(i)&PARAM_DONTLOAD))
415                        {
416                                if (p0[p_len + 1] == '~')
417                                {
418                                        SString s;
419                                        czytdotyldy(f, s);
420                                        int lfcount = 1;
421                                        const char* tmp = s;
422                                        while (tmp)
423                                                if ((tmp = strchr(tmp, '\n')))
424                                                {
425                                                lfcount++; tmp++;
426                                                }
427                                        removeCR(s);
428                                        int ch; while ((ch = fgetc(f)) != EOF) if (ch == '\n') break;
429                                        unquoteTilde(s);
430                                        if (linenum && (flags(i)&PARAM_LINECOMMENT))
431                                                s = SString::sprintf("@line %d\n", *linenum + 1) + s;
432                                        set(i, (const char*)s);
433                                        if (linenum)
434                                                (*linenum) += lfcount;
435                                }
436                                else
437                                {
438                                        set(i, p0 + p_len + 1);
439                                }
440                                fields_loaded++;
441                                loaded = true;
442                        }
443                }
444                else if (warn_unknown_fields)
445                {
446                        SString name(p0, p_len);
447                        FMprintf("ParamInterface", "load", FMLV_WARN, "Unknown property '%s' while reading object '%s' (ignored)", (const char*)name, getName());
448                }
449
450                if ((!loaded) && (p0[p_len + 1] == '~'))
451                { // eat unrecognized multiline field
452                        SString s;
453                        czytdotyldy(f, s);
454                        if (linenum)
455                        {
456                                const char* tmp = s;
457                                int lfcount = 1;
458                                while (tmp)
459                                        if ((tmp = strchr(tmp, '\n')))
460                                        {
461                                        lfcount++; tmp++;
462                                        }
463                                (*linenum) += lfcount;
464                        }
465                        int ch; while ((ch = fgetc(f)) != EOF) if (ch == '\n') break;
466                }
467        }
468        return fields_loaded;
469}
470
471
472/*
473SString SimpleAbstractParam::getString(int i)
474{
475char *t;
476switch (*(t=type(i)))
477{
478case 'd':
479{
480for (i=atol(get(i));i>=0;i--) if (t) t=strchr(t+1,'~');
481if (t)
482{
483t++;
484char *t2=strchr(t,'~');
485if (!t2) t2=t+strlen(t);
486SString str;
487strncpy(str.directWrite(t2-t),t,t2-t);
488str.endWrite(t2-t);
489return str;
490}
491}
492}
493return get(i);
494}
495*/
496
497int ParamInterface::findId(const char* n)
498{
499        int i; const char *p;
500        for (i = 0; p = id(i); i++) if (!strcmp(n, p)) return i;
501        return -1;
502}
503
504int ParamInterface::findIdn(const char* naz, int n)
505{
506        int i; const char *p;
507        for (i = 0; p = id(i); i++) if ((!strncmp(naz, p, n)) && (!p[n])) return i;
508        return -1;
509}
510
511void ParamInterface::get(int i, ExtValue &ret)
512{
513        switch (type(i)[0])
514        {
515        case 'd':       ret.setInt(getInt(i)); break;
516        case 'f':       ret.setDouble(getDouble(i)); break;
517        case 's':       ret.setString(getString(i)); break;
518        case 'o':       ret.setObject(getObject(i)); break;
519        case 'x':       ret = getExtValue(i); break;
520        default: FMprintf("ParamInterface", "get", FMLV_ERROR, "'%s.%s' is not a field", getName(), id(i));
521        }
522}
523
524int ParamInterface::setInt(int i, const char* str)
525{
526        paInt value;
527        if (!ExtValue::parseInt(str, value, false, true))
528        {
529                paInt mn, mx, def;
530                if (getMinMax(i, mn, mx, def) >= 3)
531                        return setInt(i, def);
532                else
533                        return setInt(i, (paInt)0);
534        }
535        else
536                return setInt(i, value);
537}
538
539int ParamInterface::setDouble(int i, const char* str)
540{
541        double value;
542        if (!ExtValue::parseDouble(str, value, true))
543        {
544                double mn, mx, def;
545                if (getMinMax(i, mn, mx, def) >= 3)
546                        return setDouble(i, def);
547                else
548                        return setDouble(i, (double)0);
549        }
550        else
551                return setDouble(i, value);
552}
553
554int ParamInterface::set(int i, const ExtValue &v)
555{
556        switch (type(i)[0])
557        {
558        case 'd':
559                if ((v.type == TInt) || (v.type == TDouble)) return setInt(i, v.getInt());
560                else
561                {
562                        if (v.type == TObj)
563                        {
564                                FMprintf("ParamInterface", "set", FMLV_WARN, "Getting integer value from object reference (%s)", (const char*)v.getString());
565                                return 0;
566                        }
567                        else
568                                return setInt(i, (const char*)v.getString());
569                }
570        case 'f':
571                if ((v.type == TInt) || (v.type == TDouble)) return setDouble(i, v.getDouble());
572                else
573                {
574                        if (v.type == TObj)
575                        {
576                                FMprintf("ParamInterface", "set", FMLV_WARN, "Getting floating point value from object reference (%s)", (const char*)v.getString());
577                                return 0;
578                        }
579                        else
580                                return setDouble(i, (const char*)v.getString());
581                }
582        case 's': { SString t = v.getString(); return setString(i, t); }
583        case 'o': return setObject(i, v.getObject());
584        case 'x': return setExtValue(i, v);
585        default: FMprintf("ParamInterface", "set", FMLV_ERROR, "'%s.%s' is not a field", getName(), id(i));
586        }
587        return 0;
588}
589
590int ParamInterface::set(int i, const char *v)
591{
592        char typ = type(i)[0];
593        switch (typ)
594        {
595        case 'd': return setInt(i, v);
596        case 'f': return setDouble(i, v);
597        case 's': { SString t(v); return setString(i, t); }
598        case 'x': case 'o':
599        {
600                ExtValue e;
601                const char* after;
602                if (!strncmp(v, SERIALIZATION_PREFIX, strlen(SERIALIZATION_PREFIX)))
603                {
604                        after = e.deserialize(v + strlen(SERIALIZATION_PREFIX));
605                        if ((after == NULL) || (*after))
606                                {
607                                FMprintf("ParamInterface", "set", FMLV_WARN, "serialization format mismatch in %s.%s", (getName() ? getName() : "<Unknown>"), id(i));
608                                e.setEmpty();
609                                }
610                }
611                else if ((after = e.parseNumber(v)) && (*after == 0)) //consumed the whole string
612                {
613                        //OK!
614                }
615                else
616                {
617                        e.setString(SString(v));
618                }
619                if (typ == 'x')
620                        return setExtValue(i, e);
621                else
622                        return setObject(i, e.getObject());
623        }
624        }
625        return 0;
626}
627
628SString ParamInterface::getText(int i)
629{
630        const char *t;
631        if ((*(t = type(i))) == 'd')
632        {
633                paInt mn, mx, def;
634                int value = getInt(i);
635                if (getMinMax(i, mn, mx, def) >= 2)
636                {
637                        if (value > mx)
638                                return get(i);
639                        value -= mn;
640                }
641                if (value < 0) return get(i);
642                for (; value >= 0; value--) if (t) t = strchr(t + 1, '~');
643                if (t)
644                {
645                        t++;
646                        const char *t2 = strchr(t, '~');
647                        if (!t2) t2 = t + strlen(t);
648                        return SString(t, (int)(t2 - t));
649                }
650        }
651        return get(i);
652}
653
654SString ParamInterface::get(int i)
655{
656        switch (type(i)[0])
657        {
658        case 'd': return SString::valueOf(getInt(i));
659        case 'f': return SString::valueOf(getDouble(i));
660        case 's': return getString(i);
661        }
662        ExtValue v;
663        get(i, v);
664        return v.getString();
665}
666
667
668//////////////////////////////// PARAM ////////////////////////////////////
669
670#ifdef DEBUG
671void SimpleAbstractParam::sanityCheck(int i)
672{
673        ParamEntry *pe=entry(i);
674
675        const char* t=pe->type;
676        const char* err=NULL;
677
678        if (*t=='p')
679        {
680                if (pe->fun1==NULL)
681                        err="no procedure defined";
682        }
683        else
684        {
685                if (!(pe->flags & PARAM_READONLY))
686                { //write access
687                        if ((pe->fun2==NULL)&&(pe->offset==PARAM_ILLEGAL_OFFSET))
688                                err="no field defined (GETONLY without PARAM_READONLY?)";
689                }
690        }
691        if (err!=NULL)
692                FMprintf("SimpleAbstractParam","sanityCheck", FMLV_ERROR,
693                "Invalid ParamEntry for %s.%s (%s)", getName(), pe->id, err);
694}       
695#endif
696
697void *SimpleAbstractParam::getTarget(int i)
698{
699        return (void*)(((char*)object) + entry(i)->offset);
700        //return &(object->*(entry(i)->fldptr));
701}
702
703///////// get
704
705#ifdef DEBUG
706#define SANITY_CHECK(i) sanityCheck(i)
707#else
708#define SANITY_CHECK(i)
709#endif
710
711paInt SimpleAbstractParam::getInt(int i)
712{
713        SANITY_CHECK(i);
714        ExtValue v;
715        ParamEntry *pe = entry(i);
716        if (pe->fun1)
717        {
718                (*(void(*)(void*, ExtValue*))pe->fun1)(object, &v);
719                return v.getInt();
720        }
721        else
722        {
723                void *target = getTarget(i);
724                return *((paInt*)target);
725        }
726}
727
728double SimpleAbstractParam::getDouble(int i)
729{
730        SANITY_CHECK(i);
731        ExtValue v;
732        ParamEntry *pe = entry(i);
733        if (pe->fun1)
734        {
735                (*(void(*)(void*, ExtValue*))pe->fun1)(object, &v);
736                return v.getDouble();
737        }
738        else
739        {
740                void *target = getTarget(i);
741                return *((double*)target);
742        }
743}
744
745SString SimpleAbstractParam::getString(int i)
746{
747        SANITY_CHECK(i);
748        ExtValue v;
749        ParamEntry *pe = entry(i);
750        if (pe->fun1)
751        {
752                (*(void(*)(void*, ExtValue*))pe->fun1)(object, &v);
753                return v.getString();
754        }
755        else
756        {
757                void *target = getTarget(i);
758                return *((SString*)target);
759        }
760}
761
762ExtObject SimpleAbstractParam::getObject(int i)
763{
764        SANITY_CHECK(i);
765        ExtValue v;
766        ParamEntry *pe = entry(i);
767        if (pe->fun1)
768        {
769                (*(void(*)(void*, ExtValue*))pe->fun1)(object, &v);
770                return v.getObject();
771        }
772        else
773        {
774                void *target = getTarget(i);
775                return *((ExtObject*)target);
776        }
777}
778
779ExtValue SimpleAbstractParam::getExtValue(int i)
780{
781        SANITY_CHECK(i);
782        ExtValue v;
783        ParamEntry *pe = entry(i);
784        if (pe->fun1)
785        {
786                (*(void(*)(void*, ExtValue*))pe->fun1)(object, &v);
787                return v;
788        }
789        else
790        {
791                void *target = getTarget(i);
792                return *((ExtValue*)target);
793        }
794}
795
796
797//////// set
798
799int SimpleAbstractParam::setInt(int i, paInt x)
800{
801        SANITY_CHECK(i);
802        ExtValue v;
803        ParamEntry *pe = entry(i);
804        if (pe->flags&PARAM_READONLY) return PSET_RONLY;
805        paInt xcopy = x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
806        paInt mn = 0, mx = 0;
807        int result = 0;
808        const char* t = pe->type + 1;
809        while (*t) if (*t == ' ') break; else t++;
810        if (sscanf(t, PA_INT_SCANF " " PA_INT_SCANF, &mn, &mx) == 2)
811                if (mn <= mx) // else if mn>mx then the min/max constraint makes no sense and there is no checking
812                {
813                if (x < mn) { x = mn; result = PSET_HITMIN; }
814                else if (x > mx) { x = mx; result = PSET_HITMAX; }
815                }
816
817        if (pe->fun2)
818        {
819                v.setInt(x);
820                result |= (*(int(*)(void*, const ExtValue*))pe->fun2)(object, &v);
821        }
822        else
823        {
824                void *target = getTarget(i);
825                if (dontcheckchanges || (*((paInt*)target) != x))
826                {
827                        result |= PSET_CHANGED;
828                        *((paInt*)target) = x;
829                }
830        }
831        messageOnExceedRange(i, result, xcopy);
832        return result;
833}
834
835int SimpleAbstractParam::setDouble(int i, double x)
836{
837        SANITY_CHECK(i);
838        ExtValue v;
839        ParamEntry *pe = entry(i);
840        if (pe->flags&PARAM_READONLY) return PSET_RONLY;
841        double xcopy = x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
842        double mn = 0, mx = 0;
843        int result = 0;
844        const char* t = pe->type + 1;
845        while (*t) if (*t == ' ') break; else t++;
846        if (sscanf(t, "%lg %lg", &mn, &mx) == 2)
847                if (mn <= mx) // else if mn>mx then the min/max constraint makes no sense and there is no checking
848                {
849                if (x < mn) { x = mn; result = PSET_HITMIN; }
850                else if (x > mx) { x = mx; result = PSET_HITMAX; }
851                }
852
853        if (pe->fun2)
854        {
855                v.setDouble(x);
856                result |= (*(int(*)(void*, const ExtValue*))pe->fun2)(object, &v);
857        }
858        else
859        {
860                void *target = getTarget(i);
861                if (dontcheckchanges || (*((double*)target) != x))
862                {
863                        result |= PSET_CHANGED;
864                        *((double*)target) = x;
865                }
866        }
867        messageOnExceedRange(i, result, xcopy);
868        return result;
869}
870
871int SimpleAbstractParam::setString(int i, const SString& x)
872{
873        SANITY_CHECK(i);
874        ExtValue v;
875        SString vs;
876        const SString *xx = &x;
877        ParamEntry *pe = entry(i);
878        if (pe->flags&PARAM_READONLY) return PSET_RONLY;
879        SString xcopy = x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
880        const char* t = pe->type + 1;
881        while (*t) if (*t == ' ') break; else t++;
882        int mn = 0, mx = 0;
883        int result = 0;
884        if (sscanf(t, "%d %d", &mn, &mx) == 2) //using getMinMax would also get default value, which is not needed here
885        {
886                if ((x.len() > mx) && (mx > 0))
887                {
888                        vs = x.substr(0, mx);
889                        xx = &vs;
890                        result |= PSET_HITMAX;
891                }
892        }
893
894        if (pe->fun2)
895        {
896                v.setString(*xx);
897                result |= (*(int(*)(void*, const ExtValue*))pe->fun2)(object, &v);
898        }
899        else
900        {
901                void *target = getTarget(i);
902                if (dontcheckchanges || (!(*((SString*)target) == *xx)))
903                {
904                        result |= PSET_CHANGED;
905                        *((SString*)target) = *xx;
906                }
907        }
908        messageOnExceedRange(i, result, xcopy);
909        return result;
910}
911
912int SimpleAbstractParam::setObject(int i, const ExtObject& x)
913{
914        SANITY_CHECK(i);
915        ExtValue v;
916        ParamEntry *pe = entry(i);
917        if (pe->flags&PARAM_READONLY) return PSET_RONLY;
918        ExtObject xcopy = x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
919        if (pe->fun2)
920        {
921                v.setObject(x);
922                int result = (*(int(*)(void*, const ExtValue*))pe->fun2)(object, &v);
923                messageOnExceedRange(i, result, xcopy);
924                return result;
925        }
926        else
927        {
928                void *target = getTarget(i);
929                *((ExtObject*)target) = x;
930                return PSET_CHANGED;
931        }
932}
933
934int SimpleAbstractParam::setExtValue(int i, const ExtValue& x)
935{
936        SANITY_CHECK(i);
937        ParamEntry *pe = entry(i);
938        if (pe->flags&PARAM_READONLY) return PSET_RONLY;
939        ExtValue xcopy = x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
940        if (pe->fun2)
941        {
942                int result = (*(int(*)(void*, const ExtValue*))pe->fun2)(object, &x);
943                messageOnExceedRange(i, result, xcopy);
944                return result;
945        }
946        else
947        {
948                void *target = getTarget(i);
949                *((ExtValue*)target) = x;
950                return PSET_CHANGED;
951        }
952}
953
954void SimpleAbstractParam::call(int i, ExtValue *args, ExtValue *ret)
955{
956        SANITY_CHECK(i);
957        ParamEntry *pe = entry(i);
958        if (!pe) return;
959        if (pe->fun1 && (pe->type[0] == 'p'))
960                (*(void(*)(void*, ExtValue*, ExtValue*))pe->fun1)(object, args, ret);
961        else
962        {
963                FMprintf("SimpleAbstractParam", "call", FMLV_ERROR,
964                        (*pe->type != 'p') ? "'%s.%s' is not a function" : "Internal error - undefined function pointer for '%s.%s'", getName(), pe->id);
965                ret->setInvalid();
966        }
967}
968
969void SimpleAbstractParam::setDefault()
970{
971        bool save = dontcheckchanges;
972        dontcheckchanges = 1;
973        ParamInterface::setDefault();
974        dontcheckchanges = save;
975}
976
977void SimpleAbstractParam::setDefault(int i)
978{
979        bool save = dontcheckchanges;
980        dontcheckchanges = 1;
981        ParamInterface::setDefault(i);
982        dontcheckchanges = save;
983}
984
985// Returns the address of the beginning of the line.
986// len = line length (without \n).
987// 0 may mean the line with length=0 or the end of the SString.
988// poz is advanced to the beginning of the next line.
989// A typical loop: for(poz=0;poz<s.d;) {line=getline(s,poz,len);...
990static const char *getline(const SString &s, int &poz, int &len)
991{
992        const char *beg = (const char*)s + poz;
993        if (poz >= s.len()) { poz = s.len(); len = 0; return (const char*)s + s.len(); }
994        const char *lf = strchr(beg, '\n');
995        if (!lf) { lf = (const char*)s + s.len() - 1; poz = s.len(); }
996        else { poz = (int)(lf - (const char*)s) + 1; if (poz > s.len()) poz = s.len(); }
997        while (lf >= beg) if ((*lf == '\n') || (*lf == '\r')) lf--; else break;
998        len = (int)(lf - beg) + 1;
999        return beg;
1000}
1001
1002int ParamInterface::load2(const SString &s, int &poz)
1003{
1004        int i; // the index number of the parameter
1005        int tmpi;
1006        int len;
1007        int ret;
1008        int fields_loaded = 0;
1009        const char *t, *lin, *end;
1010        const char *equals_sign, *field_end, *next_field;
1011        char remember;
1012        const char *quote, *quote2;
1013        const char *value, *valstop;
1014        SString tmpvalue;
1015        if (poz >= s.len()) return fields_loaded;
1016        t = (const char*)s + poz;
1017
1018        lin = getline(s, poz, len); // all fields must be encoded in a single line
1019        if (!len) return fields_loaded; // empty line = end
1020        i = 0;
1021        end = lin + len;
1022        while (t < end)
1023        {
1024                // processing a single field
1025                // "p:name=field_value,  field_name=field_value  , name=value..."
1026                //                     ^ ^-t (after)           ^ ^_next_field
1027                //                     \_t (before)            \_field_end
1028                while (isspace(*t)) if (t < end) t++; else return fields_loaded;
1029
1030                field_end = strchrlimit(t, ',', end); if (!field_end) field_end = end;
1031                next_field = field_end;
1032                while ((field_end>t) && isblank(field_end[-1])) field_end--;
1033                quote = strchrlimit(t, '\"', field_end);
1034                if (quote)
1035                {
1036                        quote2 = skipQuoteString(quote + 1, end);
1037                        if (quote2 > field_end)
1038                        {
1039                                field_end = strchrlimit(quote2 + 1, ',', end);
1040                                if (!field_end) next_field = field_end = end;
1041                        }
1042                        equals_sign = strchrlimit(t, '=', quote);
1043                }
1044                else
1045                {
1046                        equals_sign = strchrlimit(t, '=', field_end);
1047                        quote2 = 0;
1048                }
1049                if (equals_sign == t) { t++; equals_sign = 0; }
1050                if (field_end == t)     // skip empty value
1051                {
1052                        t++; i++;
1053                        continue;
1054                }
1055                if (equals_sign) // have parameter name
1056                {
1057                        tmpi = findIdn(t, (int)(equals_sign - t));
1058                        i = tmpi;
1059                        if (tmpi < 0)
1060                        {
1061                                SString name(t, (int)(equals_sign - t));
1062                                FMprintf("Param", "load2", FMLV_WARN, "Unknown property '%s' while reading object '%s' (ignored)", (const char*)name, getName());
1063                        }
1064                        t = equals_sign + 1; // t=value
1065                }
1066#ifdef WARN_MISSING_NAME
1067                else
1068#ifdef SAVE_SELECTED_NAMES
1069                        if (!(flags(i)&PARAM_CANOMITNAME))
1070#endif
1071                        {
1072                        FMprintf("Param", "load2", FMLV_WARN, "Missing property name in '%s' (assuming '%s')",
1073                                getName(), id(i) ? id(i) : "unknown property?");
1074                        }
1075#endif
1076                if ((i >= 0) && id(i))
1077                {
1078                        value = t;
1079                        if (quote)
1080                        {
1081                                tmpvalue.copyFrom(quote + 1, (int)(quote2 - quote) - 1);
1082                                sstringUnquote(tmpvalue);
1083                                value = tmpvalue;
1084                                valstop = quote2;
1085                        }
1086                        else
1087                                if (field_end < end) valstop = field_end; else valstop = end;
1088
1089                        remember = *valstop;
1090                        *(char*)valstop = 0;
1091                        ret = set(i, value);
1092                        fields_loaded++;
1093                        if (ret&(PSET_HITMAX | PSET_HITMIN))
1094                                FMprintf("Param", "load2", FMLV_WARN, "Adjusted '%s' in '%s' (was too %s)",
1095                                id(i), getName(), (ret&PSET_HITMAX) ? "big" : "small");
1096                        *(char*)valstop = remember;
1097                }
1098
1099                if (i >= 0) i++;
1100#ifdef __CODEGUARD__
1101                if (next_field<end-1) t=next_field+1; else return fields_loaded;
1102#else
1103                t = next_field + 1;
1104#endif
1105        }
1106        return fields_loaded;
1107}
1108
1109int Param::grmember(int g, int a)
1110{
1111        if ((getGroupCount() < 2) && (!g))
1112                return (a < getPropCount()) ? a : -9999;
1113
1114        ParamEntry *e = entry(0);
1115        int x = 0, i = 0;
1116        for (; e->id; i++, e++)
1117        {
1118                if (e->group == g)
1119                        if (a == x) return i; else x++;
1120        }
1121        return -9999;
1122}
Note: See TracBrowser for help on using the repository browser.