// This file is a part of the Framsticks GDK library. // Copyright (C) 2002-2013 Szymon Ulatowski. See LICENSE.txt for details. // Refer to http://www.framsticks.com/ for further information. #include #include #include "param.h" #include #include "common/framsg.h" #include //#define SAVE_ALL_NAMES #define SAVE_SELECTED_NAMES #define WARN_MISSING_NAME char MakeCodeGuardHappy; ParamEntry empty_paramtab[]= { {"Empty",1,0,"Empty",}, {0,0,0,}, }; static void czytdotyldy(VirtFILE *f,SString &s) { SString temp; int z; char last_char=0; while((z=fgetc(f))!=EOF) { if (z=='~') if (last_char!='\\') break; last_char=(char)z; temp+=last_char; } s=temp; } static const char *strchrlimit(const char *t,int ch,const char *limit) { int n=limit-t; for (;(n>0)&&*t;t++,n--) if (*t==ch) return t; return 0; } void ParamInterface::copyFrom(ParamInterface *src) { int n=getPropCount(); ExtValue v; int j; for(int i=0;ifindId(id(i)); if (j<0) continue; src->get(j,v); set(i,v); } } void ParamInterface::quickCopyFrom(ParamInterface *src) { int n=getPropCount(); ExtValue v; for(int i=0;iget(i,v); set(i,v); } } int ParamInterface::getMinMax(int prop,long& minumum,long& maximum,long &def) { const char* t=type(prop)+1; while(*t) if (*t==' ') break; else t++; return sscanf(t,"%ld %ld %ld",&minumum,&maximum,&def); } int ParamInterface::getMinMax(int prop,double& minumum,double& maximum,double& def) { const char* t=type(prop)+1; while(*t) if (*t==' ') break; else t++; return sscanf(t,"%lg %lg %lg",&minumum,&maximum,&def); } void ParamInterface::setDefault(bool numericonly) { int n=getPropCount(); for(int i=0;i=0) return getString(i); else return SString();} long ParamInterface::getIntById(const char*prop) {int i=findId(prop); if (i>=0) return getInt(i); else return 0;} double ParamInterface::getDoubleById(const char*prop) {int i=findId(prop); if (i>=0) return getDouble(i); else return 0;} ExtObject ParamInterface::getObjectById(const char*prop) {int i=findId(prop); if (i>=0) return getObject(i); else return ExtObject();} ExtValue ParamInterface::getExtValueById(const char*prop) {int i=findId(prop); if (i>=0) return getExtValue(i); else return ExtValue();} int ParamInterface::setIntById(const char* prop,long v) {int i=findId(prop); if (i>=0) return setInt(i,v); else return PSET_NOPROPERTY;} int ParamInterface::setDoubleById(const char* prop,double v) {int i=findId(prop); if (i>=0) return setDouble(i,v); else return PSET_NOPROPERTY;} int ParamInterface::setStringById(const char* prop,const SString &v) {int i=findId(prop); if (i>=0) return setString(i,v); else return PSET_NOPROPERTY;} int ParamInterface::setObjectById(const char* prop,const ExtObject &v) {int i=findId(prop); if (i>=0) return setObject(i,v); else return PSET_NOPROPERTY;} int ParamInterface::setExtValueById(const char* prop,const ExtValue &v) {int i=findId(prop); if (i>=0) return setExtValue(i,v); else return PSET_NOPROPERTY;} int ParamInterface::setById(const char* prop,const ExtValue &v) {int i=findId(prop); if (i>=0) return set(i,v); else return PSET_NOPROPERTY;} int ParamInterface::save(VirtFILE* f,const SString* altname,bool force) { const char *p; SString ws; int err=0,i; bool withname=false; if ((!altname)||(altname->len())) { err|=(fputs(altname?((const char*)(*altname)):getName(),f)==EOF); err|=(fputs(":\n",f)==EOF); withname=true; } for (i=0;p=id(i);i++) err|=saveprop(f,i,p,force); if (withname) err|=(fputs("\n",f)==EOF); return err; } const char* ParamInterface::SERIALIZATION_PREFIX="@Serialized:"; int ParamInterface::saveprop(VirtFILE* f,int i,const char* p,bool force) { if ((flags(i)&PARAM_DONTSAVE)&&(!force)) return 0; const char *typ=type(i); if ((*typ=='p')||(*typ=='o')) return 0; const char *t,*w; SString ws; int err=0,cr; err|=(fputs(p,f)==EOF); fputc(':',f); cr=0; if (*typ=='x') { ExtValue ex; get(i,ex); ws=SString(SERIALIZATION_PREFIX)+ex.serialize(); } else ws=get(i); quoteTilde(ws); w=ws; if (ws.len()>50) cr=1; else for (t=w;*t;t++) if ((*t==10)||(*t==13)) {cr=1; break;} if (cr) fputs("~\n",f); err|=(fputs(w,f)==EOF); err|=(fputs(cr ? "~\n" : "\n",f)==EOF); return err; } int SimpleAbstractParam::isequal(int i,void* defdata) { // defdata->member == object->member ? void *backup=object; switch(type(i)[0]) { case 'd': { select(defdata); long x=getInt(i); select(backup); return x==getInt(i); } case 'f': { select(defdata); double x=getDouble(i); select(backup); return x==getDouble(i); } case 's': { select(defdata); SString x=getString(i); select(backup); return x==getString(i); } } return 1; } void SimpleAbstractParam::save2(SString& f,void *defdata,bool addcr,bool all_names) { // defdata!=NULL -> nie zapisuje wartosci domyslnych const char *p; int i; int needlabel=0; int first=1; SString val; SString t; int fl; // t+=SString(getName()); t+=':'; for (i=0;p=id(i);i++) if (!((fl=flags(i))&PARAM_DONTSAVE)) { if (defdata && isequal(i,defdata)) needlabel=1; else { if (!first) t+=", "; #ifndef SAVE_ALL_NAMES #ifdef SAVE_SELECTED_NAMES if (needlabel || all_names || !(fl & PARAM_CANOMITNAME)) #else if (needlabel) #endif #endif { t+=p; t+="="; needlabel=0; } if (type(i)[0]=='s') { // string - special case SString str=getString(i); if (strContainsOneOf(str,", \\\n\r\t\"")) { t+="\""; sstringQuote(str); t+=str; t+="\""; } else t+=str; } else t+=get(i); first=0; } } if (addcr) t+="\n"; f+=t; } void ParamInterface::load(VirtFILE* f) { SString buf; int i; const char *p,*p0; int p_len; bool loaded; while(loadSStringLine(f,buf)) { const char* t=(const char*)buf; p0=t; while ((*p0==' ')||(*p0=='\t')) p0++; if (!*p0) break; p=strchr(p0,':'); if (!p) continue; p_len=p-p0; loaded=false; if (p_len&&((i=findIdn(p0,p_len))>=0)&&(!(flags(i)&PARAM_DONTLOAD))) { if (p0[p_len+1]=='~') { SString s; czytdotyldy(f,s); removeCR(s); int ch; while((ch=fgetc(f))!=EOF) if (ch=='\n') break; unquoteTilde(s); set(i,(const char*)s); } else { set(i,p0+p_len+1); } loaded=true; } if ((!loaded) && (p0[p_len+1]=='~')) { // eat unrecognized multiline field SString s; czytdotyldy(f,s); int ch; while((ch=fgetc(f))!=EOF) if (ch=='\n') break; } } } /* SString SimpleAbstractParam::getString(int i) { char *t; switch (*(t=type(i))) { case 'd': { for (i=atol(get(i));i>=0;i--) if (t) t=strchr(t+1,'~'); if (t) { t++; char *t2=strchr(t,'~'); if (!t2) t2=t+strlen(t); SString str; strncpy(str.directWrite(t2-t),t,t2-t); str.endWrite(t2-t); return str; } } } return get(i); } */ int ParamInterface::findId(const char* n) { int i; const char *p; for (i=0;p=id(i);i++) if (!strcmp(n,p)) return i; return -1; } int ParamInterface::findIdn(const char* naz,int n) { int i; const char *p; for (i=0;p=id(i);i++) if ((!strncmp(naz,p,n))&&(!p[n])) return i; return -1; } void ParamInterface::get(int i,ExtValue &ret) { switch(type(i)[0]) { case 'd': ret.setInt(getInt(i)); break; case 'f': ret.setDouble(getDouble(i)); break; case 's': ret.setString(getString(i)); break; case 'o': ret.setObject(getObject(i)); break; case 'x': ret=getExtValue(i); break; default: FMprintf("ParamInterface","get",FMLV_ERROR,"'%s.%s' is not a field",getName(),id(i)); } } static bool stringIsNumeric(const char* str) {// /-?.?[0-9]+/ if (!str) return false; if (*str=='-') str++; if (*str=='.') str++; return isdigit(*str)!=0; } int ParamInterface::setInt(int i,const char* str) { if (!stringIsNumeric(str)) { long a,b,c; if (getMinMax(i,a,b,c)>=3) return setInt(i,c); else return setInt(i,(long)0); } else return setInt(i,atol(str)); } int ParamInterface::setDouble(int i,const char* str) { if (!stringIsNumeric(str)) { double a,b,c; if (getMinMax(i,a,b,c)>=3) return setDouble(i,c); else return setDouble(i,(double)0); } else return setDouble(i,atof(str)); } int ParamInterface::set(int i,const ExtValue &v) { switch(type(i)[0]) { case 'd': if ((v.type==TInt)||(v.type==TDouble)) return setInt(i,v.getInt()); else return setInt(i,(const char*)v.getString()); case 'f': if ((v.type==TInt)||(v.type==TDouble)) return setDouble(i,v.getDouble()); else return setDouble(i,(const char*)v.getString()); case 's': { SString t=v.getString(); return setString(i,t); } case 'o': return setObject(i,v.getObject()); case 'x': return setExtValue(i,v); default: FMprintf("ParamInterface","get",FMLV_ERROR,"'%s.%s' is not a field",getName(),id(i)); } return 0; } int ParamInterface::set(int i,const char *v) { switch(type(i)[0]) { case 'd': return setInt(i,v); case 'f': return setDouble(i,v); case 's': { SString t(v); return setString(i,t); } case 'x': { ExtValue e; const char* after; if (!strncmp(v,SERIALIZATION_PREFIX,strlen(SERIALIZATION_PREFIX))) { after=e.deserialize(v+strlen(SERIALIZATION_PREFIX)); if ((after==NULL)||(*after)) FMprintf("ParamInterface","set",FMLV_WARN,"serialization format mismatch in %s.%s",(getName()?getName():""),id(i)); } else if ((after=e.parseNumber(v))&&(*after==0)) //consumed the whole string { //OK! } else { e.setString(SString(v)); } return setExtValue(i,e); } } return 0; } SString ParamInterface::getText(int i) { const char *t; if ((*(t=type(i)))=='d') { for (int j=getInt(i);j>=0;j--) if (t) t=strchr(t+1,'~'); if (t) { t++; const char *t2=strchr(t,'~'); if (!t2) t2=t+strlen(t); return SString(t,t2-t); } } return get(i); } SString ParamInterface::get(int i) { switch(type(i)[0]) { case 'd': return SString::valueOf(getInt(i)); case 'f': return SString::valueOf(getDouble(i)); case 's': return getString(i); } ExtValue v; get(i,v); return v.getString(); } //////////////////////////////// PARAM //////////////////////////////////// void *SimpleAbstractParam::getTarget(int i) { return (void*)(((char*)object)+entry(i)->offset); //return &(object->*(entry(i)->fldptr)); } ///////// get long SimpleAbstractParam::getInt(int i) { ExtValue v; ParamEntry *pe=entry(i); if (pe->fun1) { (*(void(*)(void*,ExtValue*))pe->fun1)(object,&v); return v.getInt(); } else { void *target=getTarget(i); return *((long*)target); } } double SimpleAbstractParam::getDouble(int i) { ExtValue v; ParamEntry *pe=entry(i); if (pe->fun1) { (*(void(*)(void*,ExtValue*))pe->fun1)(object,&v); return v.getDouble(); } else { void *target=getTarget(i); return *((double*)target); } } SString SimpleAbstractParam::getString(int i) { ExtValue v; ParamEntry *pe=entry(i); if (pe->fun1) { (*(void(*)(void*,ExtValue*))pe->fun1)(object,&v); return v.getString(); } else { void *target=getTarget(i); return *((SString*)target); } } ExtObject SimpleAbstractParam::getObject(int i) { ExtValue v; ParamEntry *pe=entry(i); if (pe->fun1) { (*(void(*)(void*,ExtValue*))pe->fun1)(object,&v); return v.getObject(); } else { void *target=getTarget(i); return *((ExtObject*)target); } } ExtValue SimpleAbstractParam::getExtValue(int i) { ExtValue v; ParamEntry *pe=entry(i); if (pe->fun1) { (*(void(*)(void*,ExtValue*))pe->fun1)(object,&v); return v; } else { void *target=getTarget(i); return *((ExtValue*)target); } } //////// set int SimpleAbstractParam::setInt(int i,long x) { ExtValue v; ParamEntry *pe=entry(i); if (pe->flags&PARAM_READONLY) return PSET_RONLY; long xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below long a=0,b=0; int result=0; const char* t=pe->type+1; while(*t) if (*t==' ') break; else t++; if (sscanf(t,"%ld %ld",&a,&b)==2) if (a<=b) // jezeli maxb) {x=b; result=PSET_HITMAX;} } if (pe->fun2) { v.setInt(x); result |= (*(int(*)(void*,const ExtValue*))pe->fun2)(object,&v); } else { void *target=getTarget(i); if (dontcheckchanges || (*((long*)target)!=x)) { result |= PSET_CHANGED; *((long*)target)=x; } } messageOnExceedRange(i,result,xcopy); return result; } int SimpleAbstractParam::setDouble(int i,double x) { ExtValue v; ParamEntry *pe=entry(i); if (pe->flags&PARAM_READONLY) return PSET_RONLY; double xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below double a=0,b=0; int result=0; const char* t=pe->type+1; while(*t) if (*t==' ') break; else t++; if (sscanf(t,"%lg %lg",&a,&b)==2) if (a<=b) // jezeli maxb) {x=b; result=PSET_HITMAX;} } if (pe->fun2) { v.setDouble(x); result |= (*(int(*)(void*,const ExtValue*))pe->fun2)(object,&v); } else { void *target=getTarget(i); if (dontcheckchanges || (*((double*)target)!=x)) { result|=PSET_CHANGED; *((double*)target)=x; } } messageOnExceedRange(i,result,xcopy); return result; } int SimpleAbstractParam::setString(int i,const SString& x) { ExtValue v; SString vs; const SString *xx=&x; ParamEntry *pe=entry(i); if (pe->flags&PARAM_READONLY) return PSET_RONLY; SString xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below const char* t=pe->type+1; while(*t) if (*t==' ') break; else t++; long a=0,b=0; int result=0; if (sscanf(t,"%ld %ld",&a,&b)==2) { if ((x.len()>b)&&(b>0)) { vs=x.substr(0,b); xx=&vs; result|=PSET_HITMAX; } } if (pe->fun2) { v.setString(*xx); result |= (*(int(*)(void*,const ExtValue*))pe->fun2)(object,&v); } else { void *target=getTarget(i); if (dontcheckchanges || (!(*((SString*)target) == *xx))) { result|=PSET_CHANGED; *((SString*)target)=x; } } messageOnExceedRange(i,result,xcopy); return result; } int SimpleAbstractParam::setObject(int i,const ExtObject& x) { ExtValue v; ParamEntry *pe=entry(i); if (pe->flags&PARAM_READONLY) return PSET_RONLY; ExtObject xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below if (pe->fun2) { v.setObject(x); int result=(*(int(*)(void*,const ExtValue*))pe->fun2)(object,&v); messageOnExceedRange(i,result,xcopy); return result; } else { void *target=getTarget(i); *((ExtObject*)target)=x; return PSET_CHANGED; } } int SimpleAbstractParam::setExtValue(int i,const ExtValue& x) { ParamEntry *pe=entry(i); if (pe->flags&PARAM_READONLY) return PSET_RONLY; ExtValue xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below if (pe->fun2) { int result=(*(int(*)(void*,const ExtValue*))pe->fun2)(object,&x); messageOnExceedRange(i,result,xcopy); return result; } else { void *target=getTarget(i); *((ExtValue*)target)=x; return PSET_CHANGED; } } void SimpleAbstractParam::call(int i,ExtValue *args,ExtValue *ret) { ParamEntry *pe=entry(i); if (!pe) return; if (pe->fun1 && (pe->type[0]=='p')) (*(void(*)(void*,ExtValue*,ExtValue*))pe->fun1)(object,args,ret); else { FMprintf("SimpleAbstractParam","call",FMLV_ERROR, (*pe->type!='p')?"'%s.%s' is not a function":"Internal error - undefined function pointer for '%s.%s'",getName(),pe->id); } } void SimpleAbstractParam::setDefault(bool numericonly) { bool save=dontcheckchanges; dontcheckchanges=1; ParamInterface::setDefault(numericonly); dontcheckchanges=save; } void SimpleAbstractParam::setDefault(int i,bool numericonly) { bool save=dontcheckchanges; dontcheckchanges=1; ParamInterface::setDefault(i,numericonly); dontcheckchanges=save; } // zwraca adres poczatku linii // len = dlugosc linii (bez \n) // 0 moze oznaczac linie dlugosci 0 lub koniec SStringa // poz jest przesuwane na poczatek nastepnej linii // typowa petla: for(poz=0;poz=s.len()) {poz=s.len(); len=0; return (const char*)s+s.len();} const char *lf=strchr(beg,'\n'); if (!lf) { lf=(const char*)s+s.len()-1; poz=s.len(); } else {poz=(lf-(const char*)s)+1; if (poz>s.len()) poz=s.len();} while (lf>=beg) if ((*lf=='\n')||(*lf=='\r')) lf--; else break; len=lf-beg+1; return beg; } void ParamInterface::load2(const SString &s,int &poz) { int i; // numer akt. parametru int tmpi; int len; int ret; const char *t,*lin,*end; const char *rownasie,*przecinek; char remember; const char *quote,*quote2; const char *value,*valstop; SString tmpvalue; if (poz>=s.len()) return; t=(const char*)s+poz; // na razie wszystko musi byc w jednej linii... lin=getline(s,poz,len); if (!len) return; // pusta linia = koniec i=0; end=lin+len; while(tprzecinek) { przecinek=strchrlimit(quote2+1,',',end); if (!przecinek) przecinek=end; } rownasie=strchrlimit(t,'=',quote); } else { rownasie=strchrlimit(t,'=',przecinek); quote2=0; } if (rownasie==t) { t++; rownasie=0; } if (przecinek==t) // skip empty value { t++; i++; continue; } if (rownasie) // have parameter name { tmpi=findIdn(t,rownasie-t); i=tmpi; if (tmpi<0) FMprintf("Param","load2",FMLV_WARN,"Unknown property name for '%s' (ignored)",getName()); t=rownasie+1; // t=value } #ifdef WARN_MISSING_NAME else #ifdef SAVE_SELECTED_NAMES if (!(flags(i)&PARAM_CANOMITNAME)) #endif { FMprintf("Param","load2",FMLV_WARN,"Missing property name in '%s' (assuming '%s')", getName(),id(i)?id(i):"unknown property?"); } #endif if ((i>=0)&&id(i)) { value=t; if (quote) { tmpvalue.copyFrom(quote+1,quote2-quote-1); sstringUnquote(tmpvalue); value=tmpvalue; valstop=quote2; } else if (przecinek=0) i++; #ifdef __CODEGUARD__ if (przecinekid;i++,e++) { if (e->group==g) if (a==x) return i; else x++; } return -9999; }