Index: /cpp/gdk/3dobject.cpp
===================================================================
--- /cpp/gdk/3dobject.cpp	(revision 104)
+++ /cpp/gdk/3dobject.cpp	(revision 104)
@@ -0,0 +1,574 @@
+#include "3d.h"
+#include "3dobject.h"
+#include "param.h"
+#include "collectionobj.h"
+
+ParamEntry* Pt3D_Ext::getStaticParamtab()
+{
+#define FIELDSTRUCT Pt3D_Ext
+static ParamEntry paramtab[]=
+{
+{"XYZ",1,18,"XYZ","3D vector"},
+
+{"x",0,PARAM_NOSTATIC,"x","f",FIELD(p.x),},
+{"y",0,PARAM_NOSTATIC,"y","f",FIELD(p.y),},
+{"z",0,PARAM_NOSTATIC,"z","f",FIELD(p.z),},
+{"new",0,0,"create new XYZ object","p oXYZ(f x,f y,f z)",PROCEDURE(p_new),"3D vectors objects can be also created using the (x,y,z) notation, i.e. var v=(1,2,3) is the same as var v=XYZ.new(1,2,3);",},
+{"newFromVector",0,0,"create new XYZ object","p oXYZ(oVector)",PROCEDURE(p_newFromVector),"used for deserialization"},
+{"clone",0,PARAM_NOSTATIC,"create new XYZ object copying the coordinates","p oXYZ()",PROCEDURE(p_clone),"Note: copying object references does not create new objects. Use clone() if a new object is needed.\n\nExample:\nvar o1=(1,2,3), o2=o1, o3=o1.clone();\no1.y=9999;\n//o2 is now (1,9999,3) but o3 is still (1,2,3)",},
+{"set",0,PARAM_NOSTATIC,"set (copy) coordinates from another XYZ object","p(oXYZ)",PROCEDURE(p_set),},
+{"set3",0,PARAM_NOSTATIC,"set individual 3 coordinates","p(f x,f y,f z)",PROCEDURE(p_set3),},
+{"add",0,PARAM_NOSTATIC,"add","p(oXYZ)",PROCEDURE(p_addvec),"Note: it does not return a new object, just modifies the existing one"},
+{"sub",0,PARAM_NOSTATIC,"subtract","p(oXYZ)",PROCEDURE(p_subvec),"Note: it does not return a new object, just modifies the existing one"},
+{"scale",0,PARAM_NOSTATIC,"multiply by scalar","p(f)",PROCEDURE(p_scale),},
+{"length",0,PARAM_READONLY+PARAM_NOSTATIC,"length","f",GETONLY(length),},
+{"normalize",0,PARAM_NOSTATIC,"normalize","p()",PROCEDURE(p_normalize),"scales the vector length to 1.0"},
+{"toString",0,PARAM_READONLY+PARAM_NOSTATIC,"textual form","s",GETONLY(toString),},
+{"toVector",0,PARAM_READONLY+PARAM_NOSTATIC,"vector of [x,y,z]","oVector",GETONLY(toVector),},
+{"rotate",0,PARAM_NOSTATIC,"rotate using Orient object","p(oOrient)",PROCEDURE(p_rotate),},
+{"revRotate",0,PARAM_NOSTATIC,"reverse rotate using Orient object","p(oOrient)",PROCEDURE(p_revrotate),},
+{"get",0,PARAM_NOSTATIC,"get one of coordinates","p f(d index)",PROCEDURE(p_get),"this function makes the XYZ objects \"indexable\" (so you can use [] for accessing subsequent fields, like in Vector)",},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+return paramtab;
+}
+
+void Pt3D_Ext::p_new(ExtValue *args,ExtValue *ret)
+{
+*ret=makeDynamicObject(new Pt3D_Ext(args[2].getDouble(),args[1].getDouble(),args[0].getDouble()));
+}
+
+static double doubleFromVec(VectorObject *vec,int i)
+{
+if (i>=vec->data.size()) return 0;
+ExtValue *v=(ExtValue*)vec->data.get(i);
+if (v)
+	return v->getDouble();
+return 0;
+}
+
+static Pt3D pt3DFromVec(VectorObject* v,int offset=0)
+{
+return Pt3D(doubleFromVec(v,offset),doubleFromVec(v,offset+1),doubleFromVec(v,offset+2));
+}
+
+void Pt3D_Ext::p_newFromVector(ExtValue *args,ExtValue *ret)
+{
+VectorObject *vec=VectorObject::fromObject(args->getObject());
+if (vec)
+	*ret=makeDynamicObject(new Pt3D_Ext(pt3DFromVec(vec)));
+else
+	ret->setEmpty();
+}
+
+void Pt3D_Ext::p_clone(ExtValue *args,ExtValue *ret)
+{
+*ret=makeDynamicObject(new Pt3D_Ext(p.x,p.y,p.z));
+}
+
+void Pt3D_Ext::p_set3(ExtValue *args,ExtValue *ret)
+{
+p.x=args[2].getDouble();
+p.y=args[1].getDouble();
+p.z=args[0].getDouble();
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_set(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *other=fromObject(args[0]);
+if (other)
+	p=other->p;
+ret->setEmpty();
+}
+
+void Pt3D_Ext::get_length(ExtValue *ret)
+{
+ret->setDouble(p.length());
+}
+
+void Pt3D_Ext::get_toString(ExtValue *ret)
+{
+SString s="(";
+ExtValue v;
+v.setDouble(p.x); s+=v.getString();
+s+=",";
+v.setDouble(p.y); s+=v.getString();
+s+=",";
+v.setDouble(p.z); s+=v.getString();
+s+=")";
+ret->setString(s);
+}
+
+static void add3Coords(VectorObject* vec,const Pt3D& p)
+{
+vec->data+=new ExtValue(p.x);
+vec->data+=new ExtValue(p.y);
+vec->data+=new ExtValue(p.z);
+}
+
+void Pt3D_Ext::get_toVector(ExtValue *ret)
+{
+VectorObject *vec=new VectorObject;
+add3Coords(vec,p);
+ret->setObject(ExtObject(&VectorObject::par,vec));
+}
+
+void Pt3D_Ext::p_addvec(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *other=fromObject(args[0]);
+if (other)
+	p+=other->p;
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_subvec(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *other=fromObject(args[0]);
+if (other)
+	p-=other->p;
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_scale(ExtValue *args,ExtValue *ret)
+{
+double d=args[0].getDouble();
+p.x*=d; p.y*=d; p.z*=d;
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_normalize(ExtValue *args,ExtValue *ret)
+{
+p.normalize();
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_rotate(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *o=Orient_Ext::fromObject(args[0]);
+if (o)
+	{
+	Pt3D tmp=p;
+	o->o.transform(p,tmp);
+	}
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_revrotate(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *o=Orient_Ext::fromObject(args[0]);
+if (o)
+	{
+	Pt3D tmp=p;
+	o->o.revTransform(p,tmp);
+	}
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_get(ExtValue *args,ExtValue *ret)
+{
+int index=args->getInt();
+if ((index<0)||(index>2))
+	ret->setEmpty();
+else
+	ret->setDouble((&p.x)[index]);
+}
+
+Param& Pt3D_Ext::getStaticParam()
+{
+#ifdef __CODEGUARD__
+static Pt3D_Ext static_pt3dobj;
+static Param static_pt3dparam(getStaticParamtab(),&static_pt3dobj);
+#else
+static Param static_pt3dparam(getStaticParamtab());
+#endif
+return static_pt3dparam;
+}
+
+Pt3D_Ext* Pt3D_Ext::fromObject(const ExtValue& v)
+{
+if (v.type!=TObj) return 0;
+const ExtObject& o=v.getObject();
+return (Pt3D_Ext*)o.getTarget(getStaticParam().getName());
+}
+
+ParamInterface* Pt3D_Ext::getInterface() {return &getStaticParam();}
+
+ExtObject Pt3D_Ext::makeStaticObject(Pt3D* p)
+{return ExtObject(&getStaticParam(),((char*)p)+(((char*)&p->x)-((char*)&((Pt3D_Ext*)p)->p.x)));}
+
+ExtObject Pt3D_Ext::makeDynamicObject(Pt3D_Ext* p)
+{return ExtObject(&getStaticParam(),p);}
+
+//////////////////////////////////////
+
+ParamEntry* Orient_Ext::getStaticParamtab()
+{
+#define FIELDSTRUCT Orient_Ext
+static ParamEntry paramtab[]=
+{
+{"Orient",1,27,"Orient","3D orientation, stored as 3x3 matrix."},
+
+{"xx",1,PARAM_NOSTATIC,"orientation.x.x","f",FIELD(o.x.x),},
+{"xy",1,PARAM_NOSTATIC,"orientation.x.y","f",FIELD(o.x.y),},
+{"xz",1,PARAM_NOSTATIC,"orientation.x.z","f",FIELD(o.x.z),},
+{"yx",1,PARAM_NOSTATIC,"orientation.y.x","f",FIELD(o.y.x),},
+{"yy",1,PARAM_NOSTATIC,"orientation.y.y","f",FIELD(o.y.y),},
+{"yz",1,PARAM_NOSTATIC,"orientation.y.z","f",FIELD(o.y.z),},
+{"zx",1,PARAM_NOSTATIC,"orientation.z.x","f",FIELD(o.z.x),},
+{"zy",1,PARAM_NOSTATIC,"orientation.z.y","f",FIELD(o.z.y),},
+{"zz",1,PARAM_NOSTATIC,"orientation.z.z","f",FIELD(o.z.z),},
+
+{"x",0,PARAM_NOSTATIC,"x vector","oXYZ",GETONLY(x),},
+{"y",0,PARAM_NOSTATIC,"y vector","oXYZ",GETONLY(y),},
+{"z",0,PARAM_NOSTATIC,"z vector","oXYZ",GETONLY(z),},
+
+{"new",0,0,"create new Orient object","p oOrient()",PROCEDURE(p_new),},
+{"newFromVector",0,0,"create new Orient object","p oOrient(oVector)",PROCEDURE(p_newFromVector),},
+{"toVector",0,PARAM_READONLY+PARAM_NOSTATIC,"vector representation","oVector",GETONLY(toVector),"for serialization"},
+{"clone",0,PARAM_NOSTATIC,"create new Orient object","p oOrient()",PROCEDURE(p_clone),},
+{"set",0,PARAM_NOSTATIC,"copy from another Orient object","p(oOrient)",PROCEDURE(p_set),},
+{"reset",0,PARAM_NOSTATIC,"set identity matrix","p()",PROCEDURE(p_reset),},
+{"rotate3",0,PARAM_NOSTATIC,"rotate around 3 axes","p(f x,f y,f z)",PROCEDURE(p_rotate3),},
+{"rotate",0,PARAM_NOSTATIC,"rotate using Orient object","p(oOrient)",PROCEDURE(p_rotate),},
+{"revRotate",0,PARAM_NOSTATIC,"reverse rotate using Orient object","p(oOrient)",PROCEDURE(p_revrotate),},
+{"lookAt",0,PARAM_NOSTATIC,"calculate rotation from 2 vectors","p(oXYZ direction,oXYZ up)",PROCEDURE(p_lookat),},
+{"normalize",0,PARAM_NOSTATIC,"normalize","p()",PROCEDURE(p_normalize),},
+{"between2",0,PARAM_NOSTATIC,"interpolate orientation","p(oOrient,oOrient,f amount)",PROCEDURE(p_between2),"The calling Orient receives the orientation interpolated from 2 input orientations.\nExample:\n"
+	"var o1=Orient.new(), o2=Orient.new(), o3=Orient.new();\n"
+	"o2.rotate3(0,Math.pi/2,0);\n"
+	"o3.between2(o1,o2,0); // o3 equals o2\n"
+	"o3.between2(o1,o2,1); // o3 equals o1\n"
+	"o3.between2(o1,o2,0.5); // o3 is halfway between o1 and o2\n"},
+{"betweenOV",0,PARAM_NOSTATIC,"interpolate orientation","p(oOrient,oXYZ,f amount)",PROCEDURE(p_betweenOV),"Like between2(), but the second Orient is composed of the supplied XYZ vector (X component) and Y Z vectors from the calling object.\n"
+	"Example:\n"
+	"var o=Orient.new();\n"
+	"o.betweenOV(o,(0,1,0),1); //no change, o remains 100 010 001\n"
+	"o.betweenOV(o,(0,1,0),0.9); //o is slightly rotated towards (0,1,0)\n"
+	"o.betweenOV(o,(0,1,0),0); //o is completely transformed, o.x=(0,1,0)\n"
+	},
+{"localToWorld",0,PARAM_NOSTATIC,"transform coordinates","p XYZ(oXYZ point,oXYZ center)",PROCEDURE(p_localToWorld),},
+{"worldToLocal",0,PARAM_NOSTATIC,"transform coordinates","p XYZ(oXYZ point,oXYZ center)",PROCEDURE(p_worldToLocal),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+return paramtab;
+}
+
+void Orient_Ext::p_new(ExtValue *args,ExtValue *ret)
+{
+*ret=makeDynamicObject(new Orient_Ext());
+}
+
+void Orient_Ext::p_newFromVector(ExtValue *args,ExtValue *ret)
+{
+VectorObject *vec=VectorObject::fromObject(args->getObject());
+if (vec)
+	*ret=makeDynamicObject(new Orient_Ext(Orient(pt3DFromVec(vec,0),pt3DFromVec(vec,3),pt3DFromVec(vec,6))));
+else
+	ret->setEmpty();
+}
+
+void Orient_Ext::get_toVector(ExtValue *ret)
+{
+VectorObject *vec=new VectorObject;
+add3Coords(vec,o.x);
+add3Coords(vec,o.y);
+add3Coords(vec,o.z);
+ret->setObject(ExtObject(&VectorObject::par,vec));
+}
+
+void Orient_Ext::p_clone(ExtValue *args,ExtValue *ret)
+{
+*ret=makeDynamicObject(new Orient_Ext(o));
+}
+
+void Orient_Ext::p_set(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *other=fromObject(args[0]);
+if (other)
+	o=other->o;
+ret->setEmpty();
+}
+
+void Orient_Ext::p_reset(ExtValue *args,ExtValue *ret)
+{
+o=Orient_1;
+ret->setEmpty();
+}
+
+void Orient_Ext::get_x(PARAMGETARGS)
+{
+*ret=Pt3D_Ext::makeStaticObject(&o.x);
+}
+
+void Orient_Ext::get_y(PARAMGETARGS)
+{
+*ret=Pt3D_Ext::makeStaticObject(&o.y);
+}
+
+void Orient_Ext::get_z(PARAMGETARGS)
+{
+*ret=Pt3D_Ext::makeStaticObject(&o.z);
+}
+
+void Orient_Ext::p_lookat(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *dir=Pt3D_Ext::fromObject(args[1]),*up=Pt3D_Ext::fromObject(args[0]);
+if (dir&&up)
+	o.lookAt(dir->p,up->p);
+ret->setEmpty();
+}
+
+void Orient_Ext::p_rotate3(ExtValue *args,ExtValue *ret)
+{
+Pt3D p(args[2].getDouble(),args[1].getDouble(),args[0].getDouble());
+o.rotate(p);
+ret->setEmpty();
+}
+
+void Orient_Ext::p_rotate(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *obj=Orient_Ext::fromObject(args[0]);
+if (!obj)
+	{
+	Orient tmp=o;
+	obj->o.transform(o,tmp);
+	}
+ret->setEmpty();
+}
+
+void Orient_Ext::p_revrotate(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *obj=Orient_Ext::fromObject(args[0]);
+if (obj)
+	{
+	Orient tmp=o;
+	obj->o.revTransform(o,tmp);
+	}
+ret->setEmpty();
+}
+
+void Orient_Ext::p_normalize(ExtValue *args,ExtValue *ret)
+{
+o.normalize();
+ret->setEmpty();
+}
+
+void Orient_Ext::p_between2(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *o1=Orient_Ext::fromObject(args[2]);
+Orient_Ext *o2=Orient_Ext::fromObject(args[1]);
+if (o1&&o2)
+	{
+	double q1=args[0].getDouble(),q2=1.0-q1;
+	o.x.x=q1*o1->o.x.x+q2*o2->o.x.x;
+	o.x.y=q1*o1->o.x.y+q2*o2->o.x.y;
+	o.x.z=q1*o1->o.x.z+q2*o2->o.x.z;
+	o.y.x=q1*o1->o.y.x+q2*o2->o.y.x;
+	o.y.y=q1*o1->o.y.y+q2*o2->o.y.y;
+	o.y.z=q1*o1->o.y.z+q2*o2->o.y.z;
+	o.z.x=q1*o1->o.z.x+q2*o2->o.z.x;
+	o.z.y=q1*o1->o.z.y+q2*o2->o.z.y;
+	o.z.z=q1*o1->o.z.z+q2*o2->o.z.z;
+	o.normalize();
+	}
+ret->setEmpty();
+}
+
+void Orient_Ext::p_betweenOV(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *o1=Orient_Ext::fromObject(args[2]);
+Pt3D_Ext *p2=Pt3D_Ext::fromObject(args[1]);
+if (o1&&p2)
+	{
+	double q1=args[0].getDouble(),q2=1.0-q1;
+	o.x.x=q1*o1->o.x.x+q2*p2->p.x;
+	o.x.y=q1*o1->o.x.y+q2*p2->p.y;
+	o.x.z=q1*o1->o.x.z+q2*p2->p.z;
+	o.normalize();
+	}
+ret->setEmpty();
+}
+
+void Orient_Ext::p_localToWorld(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *center,*point;
+point=Pt3D_Ext::fromObject(args[1]);
+center=Pt3D_Ext::fromObject(args[0]);
+if (center && point)
+	{
+	Pt3D d;
+	Pt3D src=point->p;
+	o.transform(d,src);
+	d+=center->p;
+	*ret=Pt3D_Ext::makeDynamicObject(new Pt3D_Ext(d));
+	}
+else
+	ret->setEmpty();
+}
+
+void Orient_Ext::p_worldToLocal(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *center,*point;
+point=Pt3D_Ext::fromObject(args[1]);
+center=Pt3D_Ext::fromObject(args[0]);
+if (center && point)
+	{
+	Pt3D d;
+	Pt3D src=point->p;
+	d-=center->p;
+	o.revTransform(d,src);
+	*ret=Pt3D_Ext::makeDynamicObject(new Pt3D_Ext(d));
+	}
+else
+	ret->setEmpty();
+}
+
+Param& Orient_Ext::getStaticParam()
+{
+#ifdef __CODEGUARD__
+static Orient_Ext static_orientobj;
+static Param static_orientparam(getStaticParamtab(),&static_orientobj);
+#else
+static Param static_orientparam(getStaticParamtab());
+#endif
+return static_orientparam;
+}
+
+Orient_Ext* Orient_Ext::fromObject(const ExtValue& v)
+{
+if (v.type!=TObj) return 0;
+const ExtObject& o=v.getObject();
+return (Orient_Ext*)o.getTarget(getStaticParam().getName());
+}
+
+ParamInterface* Orient_Ext::getInterface() {return &getStaticParam();}
+
+ExtObject Orient_Ext::makeStaticObject(Orient* o)
+{return ExtObject(&getStaticParam(),((char*)o)+(((char*)&o->x)-((char*)&((Orient_Ext*)o)->o.x)));}
+
+ExtObject Orient_Ext::makeDynamicObject(Orient_Ext* p)
+{return ExtObject(&getStaticParam(),p);}
+
+ParamEntry* ReferenceObj::getStaticParamtab()
+{
+#define FIELDSTRUCT ReferenceObj
+static ParamEntry paramtab[]=
+{
+{"Ref",1,5,"Ref","Reference objects. Useful for returning things from functions.\n\nExample:\nvar x=111;\nsquare(&x);// '&' creates the Reference object\nSimulator.print(x);//x is now 12321\n\nfunction square(r)\n{r.value=r.value*r.value;}\n//square receives the Reference objects and changes its 'value' field"},
+
+{"value",0,PARAM_NOSTATIC,"value","x",GETSET(value),},
+{"newS",0,0,"create new reference","p",PROCEDURE(p_newS),"(for internal use only) use &variablename to create Ref objects.",},
+{"newO",0,0,"create new reference","p",PROCEDURE(p_newO),"(for internal use only) use &variablename to create Ref objects.",},
+{"copyFrom",0,0,"copy the reference","p(oRef)",PROCEDURE(p_copyFrom),"make the reference point to the same target,"},
+{"toString",0,PARAM_READONLY+PARAM_NOSTATIC,"textual form","s",GETONLY(toString),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+return paramtab;
+}
+
+Param& ReferenceObj::getStaticParam()
+{
+#ifdef __CODEGUARD__
+static ReferenceObj static_referenceobj;
+static Param static_refobjectparam(getStaticParamtab(),&static_referenceobj);
+#else
+static Param static_refobjectparam(getStaticParamtab());
+#endif
+return static_refobjectparam;
+}
+
+void ReferenceObj::p_newS(ExtValue *args,ExtValue *ret)
+{
+*ret=makeDynamicObject(new ReferenceObj((ExtValue*)args->getInt()));
+}
+
+void ReferenceObj::p_newO(ExtValue *args,ExtValue *ret)
+{
+if (args[0].type==TInt)
+	*ret=makeDynamicObject(new ReferenceObj(args[1].getObject(),args[0].getInt()));
+else
+	*ret=makeDynamicObject(new ReferenceObj(args[1].getObject(),args[0].getString()));
+}
+
+void ReferenceObj::p_copyFrom(ExtValue *args,ExtValue *ret)
+{
+ReferenceObj* other=fromObject(args[0]);
+if (other)
+	{
+	value=other->value;
+	obj=other->obj;
+	prop=other->prop;
+	}
+}
+
+void ReferenceObj::get_toString(ExtValue *ret)
+{
+SString s="(";
+static SListTempl<ReferenceObj*> trace;
+if (trace.find(this)>=0)
+	s+="...";
+else
+	{
+	trace+=this;
+	if (value)
+		s+=value->getString();
+	else
+		{
+		ExtValue v;
+		Param tmp_param;
+		ParamInterface *pi=obj.getParamInterface(tmp_param);
+		pi->get(prop,v);
+		s+=v.getString();
+		}
+	trace-=this;
+	}
+s+=")";
+ret->setString(s);
+}
+
+void ReferenceObj::get_value(ExtValue *ret)
+{
+if (value)
+	*ret=*value;
+else
+	{
+	Param tmp_param;
+	ParamInterface *pi=obj.getParamInterface(tmp_param);
+	pi->get(prop,*ret);
+	}
+}
+
+int ReferenceObj::set_value(const ExtValue *val)
+{
+if (value)
+	*value=*val;
+else
+	{
+	Param tmp_param;
+	ParamInterface *pi=obj.getParamInterface(tmp_param);
+	pi->set(prop,*val);
+	}
+return PSET_CHANGED;
+}
+
+ReferenceObj::ReferenceObj(const ExtObject &o,const SString &p)
+	:value(0),obj(o)
+{
+Param tmp_param;
+ParamInterface *pi=obj.getParamInterface(tmp_param);
+prop=pi->findId(p);
+}
+
+ExtObject ReferenceObj::makeDynamicObject(ReferenceObj* r)
+{return ExtObject(&getStaticParam(),r);}
+
+ReferenceObj* ReferenceObj::fromObject(const ExtValue& v)
+{
+if (v.type!=TObj) return 0;
+const ExtObject& o=v.getObject();
+return (ReferenceObj*)o.getTarget(getStaticParam().getName());
+}
Index: /cpp/gdk/3dobject.h
===================================================================
--- /cpp/gdk/3dobject.h	(revision 104)
+++ /cpp/gdk/3dobject.h	(revision 104)
@@ -0,0 +1,105 @@
+#ifndef _3DOBJECT_H_
+#define _3DOBJECT_H_
+
+#include "3d.h"
+#include "extvalue.h"
+
+class Pt3D_Ext: public DestrBase
+{
+  public:
+Pt3D p;
+
+Pt3D_Ext(double x,double y,double z):p(x,y,z) {}
+Pt3D_Ext():p(0,0,0) {}
+Pt3D_Ext(const Pt3D &i):p(i) {}
+#define STATRICKCLASS Pt3D_Ext
+PARAMPROCDEF(p_new);
+PARAMPROCDEF(p_newFromVector);
+PARAMPROCDEF(p_clone);
+PARAMGETDEF(length);
+PARAMPROCDEF(p_addvec);
+PARAMPROCDEF(p_subvec);
+PARAMPROCDEF(p_scale);
+PARAMPROCDEF(p_rotate);
+PARAMPROCDEF(p_revrotate);
+PARAMPROCDEF(p_normalize);
+PARAMGETDEF(toString);
+PARAMGETDEF(toVector);
+PARAMPROCDEF(p_set);
+PARAMPROCDEF(p_set3);
+PARAMPROCDEF(p_get);
+#undef STATRICKCLASS
+
+static ParamInterface* getInterface();
+static ExtObject makeStaticObject(Pt3D* p);
+static ExtObject makeDynamicObject(Pt3D_Ext* p);
+static Pt3D_Ext* fromObject(const ExtValue& v);
+static Param& getStaticParam();
+static ParamEntry* getStaticParamtab();
+};
+
+class Orient_Ext: public DestrBase
+{
+  public:
+Orient o;
+
+Orient_Ext():o(Orient_1) {}
+Orient_Ext(const Orient& other):o(other) {}
+#define STATRICKCLASS Orient_Ext
+PARAMPROCDEF(p_new);
+PARAMPROCDEF(p_newFromVector);
+PARAMPROCDEF(p_clone);
+PARAMGETDEF(toString);
+PARAMGETDEF(toVector);
+PARAMGETDEF(x);
+PARAMGETDEF(y);
+PARAMGETDEF(z);
+PARAMPROCDEF(p_rotate3);
+PARAMPROCDEF(p_rotate);
+PARAMPROCDEF(p_revrotate);
+PARAMPROCDEF(p_lookat);
+PARAMPROCDEF(p_normalize);
+PARAMPROCDEF(p_between2);
+PARAMPROCDEF(p_betweenOV);
+PARAMPROCDEF(p_set);
+PARAMPROCDEF(p_reset);
+PARAMPROCDEF(p_localToWorld);
+PARAMPROCDEF(p_worldToLocal);
+#undef STATRICKCLASS
+
+static ParamInterface* getInterface();
+static ExtObject makeStaticObject(Orient* p);
+static ExtObject makeDynamicObject(Orient_Ext* p);
+static Orient_Ext* fromObject(const ExtValue& v);
+static Param& getStaticParam();
+static ParamEntry* getStaticParamtab();
+};
+
+class ReferenceObj: public DestrBase
+{
+  public:
+ExtValue *value;
+ExtObject obj;
+int prop;
+
+ReferenceObj(ExtValue *val):value(val) {}
+ReferenceObj() {}
+ReferenceObj(const ExtObject &o,int p):value(0),obj(o),prop(p) {}
+ReferenceObj(const ExtObject &o,const SString &p);
+#define STATRICKCLASS ReferenceObj
+PARAMPROCDEF(p_newS);
+PARAMPROCDEF(p_newO);
+PARAMPROCDEF(p_copyFrom);
+PARAMGETDEF(toString);
+PARAMGETDEF(value);
+PARAMSETDEF(value);
+#undef STATRICKCLASS
+
+static ParamInterface* getInterface();
+static ExtObject makeDynamicObject(ReferenceObj* r);
+static ReferenceObj* fromObject(const ExtValue& v);
+static Param& getStaticParam();
+static ParamEntry* getStaticParamtab();
+};
+
+#endif
Index: /cpp/gdk/Makefile
===================================================================
--- /cpp/gdk/Makefile	(revision 103)
+++ /cpp/gdk/Makefile	(revision 104)
@@ -14,5 +14,5 @@
 #   defassign-f0_neuroconn.h f0def.xml neurocls-library.h neurocls-factory.h model.def
 
-TARGETS=convtest gdktest genotest neurotest loadertest f0def.xml model.def
+TARGETS=convtest gdktest genotest neurotest loadertest serialtest f0def.xml model.def
 
 all: $(TARGETS)
@@ -28,5 +28,6 @@
 GDKOBJS=list.o advlist.o param.o sstring.o 3d.o model.o modelparts.o errmanager.o \
 	neurolibrary.o geno.o genoconv.o extvalue.o framsg.o callbacks.o syntparam.o \
-	multirange.o multimap.o paramtabobj.o defgenoconv.o sstringutils.o paramobj.o rndutil.o \
+	multirange.o multimap.o paramtabobj.o defgenoconv.o sstringutils.o paramobj.o \
+	rndutil.o collectionobj.o hashtable.o stderrors.o 3dobject.o \
 	$(CONVMODULES)
 
@@ -58,4 +59,9 @@
 loadertest: libgdk.a $(LOADERTESTOBJS)
 	g++ $(LOADERTESTOBJS) -L. -lgdk -o $@
+
+SERIALTESTOBJS=serialtest.o virtfile.o stdiofile-autoinit.o stdiofile.o nonstd_stdio.o
+
+serialtest: libgdk.a $(SERIALTESTOBJS)
+	g++ $(SERIALTESTOBJS) -L. -lgdk -o $@
 
 neurocls-library.h: f0.def neurocls-library.m4
@@ -103,102 +109,138 @@
 # DO NOT DELETE
 
-3d.o: nonstd.h 3d.h framsg.h
+3d.o: nonstd_math.h framsg.h 3d.h
+3dobject.o: 3d.h 3dobject.h extvalue.h sstring.h param.h list.h nonstd.h
+3dobject.o: statrick.h virtfile.h framsg.h nonstd_stl.h threads.h
+3dobject.o: collectionobj.h hashtable.h
 advlist.o: advlist.h list.h nonstd.h callbacks.h statrick.h
 callbacks.o: callbacks.h list.h nonstd.h statrick.h
+collectionobj.o: collectionobj.h param.h sstring.h list.h nonstd.h statrick.h
+collectionobj.o: virtfile.h framsg.h extvalue.h nonstd_stl.h threads.h
+collectionobj.o: hashtable.h 3d.h nonstd_math.h stderrors.h sstringutils.h
 conv_f1.o: conv_f1.h genoconv.h geno.h sstring.h extvalue.h param.h list.h
-conv_f1.o: nonstd.h statrick.h virtfile.h model.h modelparts.h 3d.h
-conv_f1.o: sstringutils.h syntparam.h usertags.h paramtabobj.h advlist.h
-conv_f1.o: callbacks.h framsg.h multirange.h multimap.h
+conv_f1.o: nonstd.h statrick.h virtfile.h framsg.h nonstd_stl.h threads.h
+conv_f1.o: model.h nonstd_math.h modelparts.h 3d.h sstringutils.h syntparam.h
+conv_f1.o: usertags.h paramtabobj.h advlist.h callbacks.h multirange.h
+conv_f1.o: multimap.h
 convtest.o: genoconv.h geno.h sstring.h extvalue.h param.h list.h nonstd.h
-convtest.o: statrick.h virtfile.h model.h modelparts.h 3d.h sstringutils.h
-convtest.o: syntparam.h usertags.h paramtabobj.h advlist.h callbacks.h
-convtest.o: multimap.h multirange.h conv_f1.h printconvmap.h stdouterr.h
-convtest.o: errmanager.h framsg.h
+convtest.o: statrick.h virtfile.h framsg.h nonstd_stl.h threads.h model.h
+convtest.o: nonstd_math.h modelparts.h 3d.h sstringutils.h syntparam.h
+convtest.o: usertags.h paramtabobj.h advlist.h callbacks.h multimap.h
+convtest.o: multirange.h conv_f1.h printconvmap.h stdouterr.h errmanager.h
 defgenoconv.o: defgenoconv.h genoconv.h geno.h sstring.h extvalue.h param.h
-defgenoconv.o: list.h nonstd.h statrick.h virtfile.h gen-config.h conv_f1.h
-defgenoconv.o: model.h modelparts.h 3d.h sstringutils.h syntparam.h
-defgenoconv.o: usertags.h paramtabobj.h advlist.h callbacks.h
-errmanager.o: errmanager.h list.h nonstd.h sstring.h framsg.h
+defgenoconv.o: list.h nonstd.h statrick.h virtfile.h framsg.h nonstd_stl.h
+defgenoconv.o: threads.h
+errmanager.o: errmanager.h list.h nonstd.h sstring.h framsg.h threads.h
 extvalue.o: extvalue.h sstring.h param.h list.h nonstd.h statrick.h
-extvalue.o: virtfile.h framsg.h
-framsg.o: nonstd.h framsg.h
-gdktest.o: model.h modelparts.h 3d.h genoconv.h geno.h sstring.h extvalue.h
-gdktest.o: param.h list.h nonstd.h statrick.h virtfile.h sstringutils.h
-gdktest.o: syntparam.h usertags.h paramtabobj.h advlist.h callbacks.h
-gdktest.o: defgenoconv.h stdouterr.h errmanager.h framsg.h
+extvalue.o: virtfile.h framsg.h nonstd_stl.h threads.h sstringutils.h
+extvalue.o: collectionobj.h hashtable.h 3d.h 3dobject.h
+framsg.o: framsg.h nonstd_stdio.h
+fullprops.o: stdiofile.h virtfile.h sstring.h nonstd_dir.h model.h
+fullprops.o: nonstd_math.h modelparts.h 3d.h genoconv.h geno.h extvalue.h
+fullprops.o: param.h list.h nonstd.h statrick.h framsg.h nonstd_stl.h
+fullprops.o: threads.h sstringutils.h syntparam.h usertags.h paramtabobj.h
+fullprops.o: advlist.h callbacks.h defgenoconv.h stdouterr.h errmanager.h
+gdktest.o: stdiofile.h virtfile.h sstring.h nonstd_dir.h model.h
+gdktest.o: nonstd_math.h modelparts.h 3d.h genoconv.h geno.h extvalue.h
+gdktest.o: param.h list.h nonstd.h statrick.h framsg.h nonstd_stl.h threads.h
+gdktest.o: sstringutils.h syntparam.h usertags.h paramtabobj.h advlist.h
+gdktest.o: callbacks.h defgenoconv.h stdouterr.h errmanager.h
 genoconv.o: nonstd.h genoconv.h geno.h sstring.h extvalue.h param.h list.h
-genoconv.o: statrick.h virtfile.h multimap.h multirange.h
+genoconv.o: statrick.h virtfile.h framsg.h nonstd_stl.h threads.h multimap.h
+genoconv.o: multirange.h
 geno.o: geno.h sstring.h extvalue.h param.h list.h nonstd.h statrick.h
-geno.o: virtfile.h genoconv.h model.h modelparts.h 3d.h sstringutils.h
-geno.o: syntparam.h usertags.h paramtabobj.h advlist.h callbacks.h
+geno.o: virtfile.h framsg.h nonstd_stl.h threads.h genoconv.h model.h
+geno.o: nonstd_math.h modelparts.h 3d.h sstringutils.h syntparam.h usertags.h
+geno.o: paramtabobj.h advlist.h callbacks.h
 genotest.o: geno.h sstring.h extvalue.h param.h list.h nonstd.h statrick.h
-genotest.o: virtfile.h genoconv.h stdiofile.h sstringutils.h defgenoconv.h
+genotest.o: virtfile.h framsg.h nonstd_stl.h threads.h genoconv.h stdiofile.h
+genotest.o: nonstd_dir.h sstringutils.h defgenoconv.h
 genotypeloader.o: genotypeloader.h sstring.h multiparamload.h param.h list.h
-genotypeloader.o: nonstd.h statrick.h virtfile.h
+genotypeloader.o: nonstd.h statrick.h virtfile.h framsg.h extvalue.h
+genotypeloader.o: nonstd_stl.h threads.h
+hashtable.o: hashtable.h sstring.h
 list.o: list.h nonstd.h
 loadertest.o: genotypeloader.h sstring.h multiparamload.h param.h list.h
-loadertest.o: nonstd.h statrick.h virtfile.h
-model.o: nonstd.h model.h modelparts.h 3d.h genoconv.h geno.h sstring.h
-model.o: extvalue.h param.h list.h statrick.h virtfile.h sstringutils.h
-model.o: syntparam.h usertags.h paramtabobj.h advlist.h callbacks.h framsg.h
-model.o: multimap.h multirange.h errmanager.h
+loadertest.o: nonstd.h statrick.h virtfile.h framsg.h extvalue.h nonstd_stl.h
+loadertest.o: threads.h
+model.o: nonstd_math.h model.h modelparts.h 3d.h genoconv.h geno.h sstring.h
+model.o: extvalue.h param.h list.h nonstd.h statrick.h virtfile.h framsg.h
+model.o: nonstd_stl.h threads.h sstringutils.h syntparam.h usertags.h
+model.o: paramtabobj.h advlist.h callbacks.h multimap.h multirange.h
+model.o: errmanager.h
 modelparts.o: modelparts.h 3d.h genoconv.h geno.h sstring.h extvalue.h
-modelparts.o: param.h list.h nonstd.h statrick.h virtfile.h sstringutils.h
-modelparts.o: syntparam.h usertags.h paramtabobj.h model.h advlist.h
-modelparts.o: callbacks.h neurolibrary.h multirange.h defassign-f0_neuro.h
+modelparts.o: param.h list.h nonstd.h statrick.h virtfile.h framsg.h
+modelparts.o: nonstd_stl.h threads.h sstringutils.h syntparam.h usertags.h
+modelparts.o: paramtabobj.h model.h nonstd_math.h advlist.h callbacks.h
+modelparts.o: neurolibrary.h multirange.h defassign-f0_neuro.h
 modelparts.o: defassign-f0_part.h defassign-f0_joint.h f0classes.h
 modelparts.o: defassign-f0_neuroconn.h
 multimap.o: multimap.h multirange.h list.h nonstd.h
 multiparamload.o: multiparamload.h param.h sstring.h list.h nonstd.h
-multiparamload.o: statrick.h virtfile.h framsg.h
-multirange.o: multirange.h list.h nonstd.h
+multiparamload.o: statrick.h virtfile.h framsg.h extvalue.h nonstd_stl.h
+multiparamload.o: threads.h
+multirange.o: multirange.h list.h nonstd.h nonstd_stl.h
 neurofactory.o: neurofactory.h modelparts.h 3d.h genoconv.h geno.h sstring.h
 neurofactory.o: extvalue.h param.h list.h nonstd.h statrick.h virtfile.h
-neurofactory.o: sstringutils.h syntparam.h usertags.h paramtabobj.h
-neurofactory.o: neuroimpl.h model.h advlist.h callbacks.h framsg.h
-neurofactory.o: neuroimplfiles.h neuroimpl-simple.h neuroimpl-channels.h
-neurofactory.o: neuroimpl-fuzzy.h neuroimpl-body-gdk.h neurocls-factory.h
-neuroimpl-channels.o: neuroimpl-channels.h neuroimpl.h model.h modelparts.h
-neuroimpl-channels.o: 3d.h genoconv.h geno.h sstring.h extvalue.h param.h
-neuroimpl-channels.o: list.h nonstd.h statrick.h virtfile.h sstringutils.h
-neuroimpl-channels.o: syntparam.h usertags.h paramtabobj.h advlist.h
-neuroimpl-channels.o: callbacks.h framsg.h
-neuroimpl.o: neuroimpl.h model.h modelparts.h 3d.h genoconv.h geno.h
-neuroimpl.o: sstring.h extvalue.h param.h list.h nonstd.h statrick.h
-neuroimpl.o: virtfile.h sstringutils.h syntparam.h usertags.h paramtabobj.h
-neuroimpl.o: advlist.h callbacks.h framsg.h neurofactory.h
-neuroimpl-fuzzy.o: neuroimpl-fuzzy.h neuroimpl.h model.h modelparts.h 3d.h
-neuroimpl-fuzzy.o: genoconv.h geno.h sstring.h extvalue.h param.h list.h
-neuroimpl-fuzzy.o: nonstd.h statrick.h virtfile.h sstringutils.h syntparam.h
-neuroimpl-fuzzy.o: usertags.h paramtabobj.h advlist.h callbacks.h framsg.h
+neurofactory.o: framsg.h nonstd_stl.h threads.h sstringutils.h syntparam.h
+neurofactory.o: usertags.h paramtabobj.h neuroimpl.h model.h nonstd_math.h
+neurofactory.o: advlist.h callbacks.h neurolibrary.h
+neuroimpl-channels.o: neuroimpl-channels.h neuroimpl.h model.h nonstd_math.h
+neuroimpl-channels.o: modelparts.h 3d.h genoconv.h geno.h sstring.h
+neuroimpl-channels.o: extvalue.h param.h list.h nonstd.h statrick.h
+neuroimpl-channels.o: virtfile.h framsg.h nonstd_stl.h threads.h
+neuroimpl-channels.o: sstringutils.h syntparam.h usertags.h paramtabobj.h
+neuroimpl-channels.o: advlist.h callbacks.h
+neuroimpl.o: neuroimpl.h model.h nonstd_math.h modelparts.h 3d.h genoconv.h
+neuroimpl.o: geno.h sstring.h extvalue.h param.h list.h nonstd.h statrick.h
+neuroimpl.o: virtfile.h framsg.h nonstd_stl.h threads.h sstringutils.h
+neuroimpl.o: syntparam.h usertags.h paramtabobj.h advlist.h callbacks.h
+neuroimpl.o: neurofactory.h rndutil.h
+neuroimpl-fuzzy.o: neuroimpl-fuzzy.h neuroimpl.h model.h nonstd_math.h
+neuroimpl-fuzzy.o: modelparts.h 3d.h genoconv.h geno.h sstring.h extvalue.h
+neuroimpl-fuzzy.o: param.h list.h nonstd.h statrick.h virtfile.h framsg.h
+neuroimpl-fuzzy.o: nonstd_stl.h threads.h sstringutils.h syntparam.h
+neuroimpl-fuzzy.o: usertags.h paramtabobj.h advlist.h callbacks.h
 neuroimpl-fuzzy.o: neuroimpl-fuzzy-f0.h
-neuroimpl-fuzzy-f0.o: neuroimpl-fuzzy-f0.h sstring.h nonstd.h
-neuroimpl-simple.o: neuroimpl-simple.h neuroimpl.h model.h modelparts.h 3d.h
-neuroimpl-simple.o: genoconv.h geno.h sstring.h extvalue.h param.h list.h
-neuroimpl-simple.o: nonstd.h statrick.h virtfile.h sstringutils.h syntparam.h
-neuroimpl-simple.o: usertags.h paramtabobj.h advlist.h callbacks.h framsg.h
+neuroimpl-fuzzy-f0.o: neuroimpl-fuzzy-f0.h sstring.h nonstd_stl.h
+neuroimpl-simple.o: neuroimpl-simple.h neuroimpl.h model.h nonstd_math.h
+neuroimpl-simple.o: modelparts.h 3d.h genoconv.h geno.h sstring.h extvalue.h
+neuroimpl-simple.o: param.h list.h nonstd.h statrick.h virtfile.h framsg.h
+neuroimpl-simple.o: nonstd_stl.h threads.h sstringutils.h syntparam.h
+neuroimpl-simple.o: usertags.h paramtabobj.h advlist.h callbacks.h
 neurolibrary.o: neurolibrary.h advlist.h list.h nonstd.h callbacks.h
-neurolibrary.o: statrick.h param.h sstring.h virtfile.h modelparts.h 3d.h
-neurolibrary.o: genoconv.h geno.h extvalue.h sstringutils.h syntparam.h
-neurolibrary.o: usertags.h paramtabobj.h neurocls-library.h
+neurolibrary.o: statrick.h param.h sstring.h virtfile.h framsg.h modelparts.h
+neurolibrary.o: 3d.h genoconv.h geno.h extvalue.h nonstd_stl.h threads.h
+neurolibrary.o: sstringutils.h syntparam.h usertags.h paramtabobj.h
 neurotest.o: geno.h sstring.h extvalue.h param.h list.h nonstd.h statrick.h
-neurotest.o: virtfile.h genoconv.h stdiofile.h sstringutils.h defgenoconv.h
-neurotest.o: neuroimpl.h model.h modelparts.h 3d.h syntparam.h usertags.h
-neurotest.o: paramtabobj.h advlist.h callbacks.h framsg.h neurofactory.h
+neurotest.o: virtfile.h framsg.h nonstd_stl.h threads.h genoconv.h
+neurotest.o: stdiofile.h nonstd_dir.h sstringutils.h defgenoconv.h
+neurotest.o: neuroimpl.h model.h nonstd_math.h modelparts.h 3d.h syntparam.h
+neurotest.o: usertags.h paramtabobj.h advlist.h callbacks.h neurofactory.h
 neurotest.o: stdouterr.h errmanager.h
-param.o: param.h sstring.h list.h nonstd.h statrick.h virtfile.h extvalue.h
-param.o: framsg.h sstringutils.h
+nonstd_stdio.o: nonstd_stdio.h
+param.o: param.h sstring.h list.h nonstd.h statrick.h virtfile.h framsg.h
+param.o: extvalue.h nonstd_stl.h threads.h sstringutils.h
 paramobj.o: paramobj.h param.h sstring.h list.h nonstd.h statrick.h
-paramobj.o: virtfile.h extvalue.h
+paramobj.o: virtfile.h framsg.h extvalue.h nonstd_stl.h threads.h
 paramtabobj.o: paramtabobj.h param.h sstring.h list.h nonstd.h statrick.h
-paramtabobj.o: virtfile.h
+paramtabobj.o: virtfile.h framsg.h
 printconvmap.o: printconvmap.h sstring.h multimap.h multirange.h list.h
 printconvmap.o: nonstd.h
-sstring.o: sstring.h nonstd.h
+rndutil.o: rndutil.h nonstd.h nonstd_math.h
+serialtest.o: collectionobj.h param.h sstring.h list.h nonstd.h statrick.h
+serialtest.o: virtfile.h framsg.h extvalue.h nonstd_stl.h threads.h
+serialtest.o: hashtable.h 3d.h
+sstring.o: sstring.h nonstd_stl.h extvalue.h param.h list.h nonstd.h
+sstring.o: statrick.h virtfile.h framsg.h threads.h
 sstringutils.o: sstringutils.h sstring.h virtfile.h framsg.h
-stdiofile-autoinit.o: stdiofile.h virtfile.h sstring.h
-stdiofile.o: stdiofile.h virtfile.h sstring.h nonstd.h nonstd_io.h
+stderrors.o: stderrors.h list.h nonstd.h sstring.h sstringutils.h virtfile.h
+stderrors.o: framsg.h
+stdiofile-autoinit.o: stdiofile.h virtfile.h sstring.h nonstd_dir.h
+stdiofile.o: stdiofile.h virtfile.h sstring.h nonstd_dir.h nonstd_stdio.h
+stdiofile.o: framsg.h
 stdouterr.o: stdouterr.h errmanager.h list.h nonstd.h sstring.h framsg.h
+stdouterr.o: threads.h virtfile.h
 syntparam.o: nonstd.h syntparam.h param.h sstring.h list.h statrick.h
-syntparam.o: virtfile.h paramobj.h
+syntparam.o: virtfile.h framsg.h paramobj.h
 virtfile.o: virtfile.h
Index: /cpp/gdk/callbacks.h
===================================================================
--- /cpp/gdk/callbacks.h	(revision 103)
+++ /cpp/gdk/callbacks.h	(revision 104)
@@ -39,5 +39,5 @@
 void (*fun)(void*,long);
   public:
-FunctionCallbackNode(void (*f)(void*,long),void *d):fun(f),userdata(d) {}
+FunctionCallbackNode(void (*f)(void*,long),void *d):userdata(d),fun(f) {}
 void action(long calldata) {(*fun)(userdata,calldata);}
 int equals(CallbackNode*);
@@ -51,5 +51,5 @@
 void (*fun)(void*,void*,long);
   public:
-StatrickCallbackNode(void *o,void (*f)(void*,void*,long),void *d):object(o),fun(f),userdata(d) {}
+StatrickCallbackNode(void *o,void (*f)(void*,void*,long),void *d):object(o),userdata(d),fun(f) {}
 void action(long calldata) {(*fun)(object,userdata,calldata);}
 int equals(CallbackNode*);
Index: /cpp/gdk/collectionobj.cpp
===================================================================
--- /cpp/gdk/collectionobj.cpp	(revision 104)
+++ /cpp/gdk/collectionobj.cpp	(revision 104)
@@ -0,0 +1,354 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "collectionobj.h"
+#include "nonstd_math.h" //sqrt in borland
+#include "stderrors.h"
+#include "nonstd_stl.h"
+#include "sstringutils.h"
+
+#define FIELDSTRUCT VectorObject
+ParamEntry vector_paramtab[]=
+{
+{"Vector",1,11,"Vector","Vector is 1-dimensional array, indexed by integer value (starting from 0). "
+ "Multidimensional arrays can be simulated by putting other Vector objects into the Vector.\n"
+ "Example:\nvar v=Vector.new();\nv.add(123); v.add(\"string\");",},
+{"clear",0,PARAM_NOSTATIC,"clear data","p()",PROCEDURE(p_clear),},
+{"size",0,PARAM_READONLY+PARAM_NOSTATIC,"element count","d",GETONLY(size),},
+{"remove",0,PARAM_NOSTATIC,"remove at position","p(d position)",PROCEDURE(p_remove),},
+{"get",0,PARAM_NOSTATIC,"get value at position","p x(d position)",PROCEDURE(p_get),},
+{"set",0,PARAM_NOSTATIC,"set value at position","p(d position,x value)",PROCEDURE(p_set),},
+{"add",0,PARAM_NOSTATIC,"append at the end","p(x value)",PROCEDURE(p_add),},
+{"find",0,PARAM_NOSTATIC,"find","p d(x value)",PROCEDURE(p_find),"returns the element index or -1 if not found"},
+{"avg",0,PARAM_READONLY+PARAM_NOSTATIC,"average","f",GETONLY(avg)},
+{"stdev",0,PARAM_READONLY+PARAM_NOSTATIC,"standard deviation","f",GETONLY(stdev),"=sqrt(sum((element[i]-avg)^2)/(size-1)) which is estimated population std.dev. from sample std.dev."},
+{"toString",0,PARAM_READONLY+PARAM_NOSTATIC,"textual form","s",GETONLY(toString),},
+{"new",0,0,"create new Vector","p oVector()",STATICPROCEDURE(p_new),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT DictionaryObject
+ParamEntry dictionary_paramtab[]=
+{
+{"Dictionary",1,9,"Dictionary","Dictionary associates stored values with string keys "
+ "(\"key\" is the first argument in get/set/remove functions). Integer \"key\" can be "
+ "used to enumerate all elements (the sequence of elements is not preserved).\n"
+ "Example:\nvar d=Dictionary.new(); d.set(\"name\",\"John\"); d.set(\"age\",44);\n"
+ "var i;\nfor(i=0;i<d.size;i++) Simulator.print(d.getKey(i)+\" is \"+d.get(i));",},
+{"clear",0,PARAM_NOSTATIC,"clear data","p()",PROCEDURE(p_clear),},
+{"size",0,PARAM_NOSTATIC,"element count","d",GETONLY(size),},
+{"remove",0,PARAM_NOSTATIC,"remove named or indexed element","p(x key)",PROCEDURE(p_remove),},
+{"get",0,PARAM_NOSTATIC,"get named or indexed element","p x(x key)",PROCEDURE(p_get),},
+{"getKey",0,PARAM_NOSTATIC,"get a key of the indexed element","p s(d index)",PROCEDURE(p_getKey),},
+{"set",0,PARAM_NOSTATIC,"set named or indexed element","p(x key,x value)",PROCEDURE(p_set),},
+{"find",0,PARAM_NOSTATIC,"find","p s(x value)",PROCEDURE(p_find),"returns the element key or null if not found"},
+{"new",0,0,"create new Dictionary","p oDictionary()",STATICPROCEDURE(p_new),},
+{"toString",0,PARAM_READONLY+PARAM_NOSTATIC,"textual form","s",GETONLY(toString),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+
+Param VectorObject::par(vector_paramtab);
+Param DictionaryObject::par(dictionary_paramtab);
+
+/////////////////////////////////////////
+
+VectorObject::VectorObject(Pt3D &pt)
+	:readonly(0),owndata(1)
+{
+set(0,ExtValue(pt.x));
+set(1,ExtValue(pt.y));
+set(2,ExtValue(pt.z));
+}
+
+void VectorObject::clear()
+{
+if (owndata)
+for(int i=data.size()-1;i>=0;i--)
+	{
+	ExtValue *v=(ExtValue*)data.get(i);
+	if (v) delete v;
+	}
+//nie tak spektakularny jak rasowe bugi roku ale tez idiotyczny :>
+//wszystko przez to ze poczatkowo bylo tylko uzywane w destruktorze...
+data.clear();
+}
+
+void VectorObject::p_remove(PARAMPROCARGS)
+{
+if (readonly) return;
+int i=args->getInt();
+if ((i<0)||(i>=data.size())) return;
+ExtValue *v=(ExtValue*)data.get(i);
+if (v) delete v;
+data-=i;
+}
+
+void VectorObject::set(int i,const ExtValue& val)
+{
+int oldsize=data.size();
+if (i<0) return;
+ExtValue *v=(ExtValue*)data.get(i);
+if (v) delete v;
+data.set(i,new ExtValue(val));
+i--;
+while(i>=oldsize)
+	{
+	data.set(i,0);
+	i--;
+	}
+}
+
+void VectorObject::p_get(PARAMPROCARGS)
+{
+int i=args->getInt();
+if (listIndexCheck(&data,i,"VectorObject","get"))
+	{
+	ExtValue *v=get(i);
+	if (v)
+		{
+		*ret=*v;
+		return;
+		}
+	}
+*ret=ExtValue();
+}
+
+void VectorObject::get_avg(ExtValue* ret)
+{
+if (!data.size()) {ret->setEmpty(); return;}
+double s=0.0;
+for(int i=data.size()-1;i>=0;i--)
+	s+=((ExtValue*)data.get(i))->getDouble();
+s/=data.size();
+ret->setDouble(s);
+}
+
+SString VectorObject::serialize() const
+{
+SString out="[";
+	{
+	for(int i=0;i<data.size();i++)
+		{
+		ExtValue* v=(ExtValue*)data.get(i);
+		if (i) out+=",";
+		if (v)
+			out+=v->serialize();
+		else
+			out+="null";
+		}
+	}
+out+="]";
+//sprintf(out.directAppend(20),"<Vector@%p>",this);out.endAppend();
+return out;
+}
+
+static ThreadSingleton<SList> tostring_trace;
+
+void VectorObject::get_toString(ExtValue* ret)
+{
+SString out="[";
+//static SListTempl<VectorObject*> trace;
+if (tostring_trace.getref().find(this)>=0)
+	out+="...";
+else
+	{
+	tostring_trace.getref()+=this;
+	for(int i=0;i<data.size();i++)
+		{
+		ExtValue* v=(ExtValue*)data.get(i);
+		if (i) out+=",";
+		if (v)
+			out+=v->getString();
+		else
+			out+="null";
+		}
+	tostring_trace.getref()-=this;
+	}
+out+="]";
+ret->setString(out);
+}
+
+void VectorObject::get_stdev(ExtValue* ret)
+{
+if (!data.size()) {ret->setEmpty(); return;}
+get_avg(ret);
+double a=ret->getDouble();
+double s=0.0;
+for(int i=data.size()-1;i>=0;i--)
+	{
+	double d=a-((ExtValue*)data.get(i))->getDouble();
+	s+=d*d;
+	}
+ret->setDouble(sqrt(s/max(1,data.size()-1)));
+}
+
+void VectorObject::p_find(PARAMPROCARGS)
+{
+short i;
+for(i=0;i<data.size();i++)
+	{
+	if ((*args)==(*get(i)))
+		{ret->setInt(i);return;}
+	}
+ret->setInt(-1);
+}
+
+VectorObject* VectorObject::fromObject(const ExtObject& o)
+{
+return (VectorObject*)o.getTarget(par.getName());
+}
+
+/////////////////////////////
+
+void DictionaryObject::clear()
+{
+for(HashEntryIterator it(hash);it.isValid();)
+	{
+        ExtValue *v=(ExtValue*)hash.remove(it);
+	if (v) delete v;
+	}
+hash.clear();
+hash.init();
+}
+
+void DictionaryObject::p_find(PARAMPROCARGS)
+{
+for(HashEntryIterator it(hash);it.isValid();it++)
+	{
+	if ((*args)==(*((ExtValue*)it->value)))
+		{
+		ret->setString(it->key);
+		return;
+		}
+	}
+ret->setEmpty();
+}
+
+HashEntryIterator* DictionaryObject::getIndexIterator(int i)
+{
+if (i<0) return 0;
+if (i>=hash.getSize()) return 0;
+
+if ((!it.isValid())||(it_index>i))
+	{
+	it=HashEntryIterator(hash);
+	it_index=0;
+	}
+while(it.isValid())
+	{
+	if (it_index==i)
+		return &it;
+	it_index++;
+	it++;
+	}
+return 0;
+}
+
+void DictionaryObject::p_remove(PARAMPROCARGS)
+{
+if ((args->type==TInt)||(args->type==TDouble))
+	{
+	HashEntryIterator* iter=getIndexIterator(args->getInt());
+	if (iter)
+		{
+		ExtValue *oldval=(ExtValue*)hash.remove(*iter);
+		if (oldval) {*ret=*oldval; delete oldval;} else *ret=ExtValue();
+		}
+	}
+else
+	{
+	ExtValue *oldval=(ExtValue*)hash.remove(args[0].getString());
+	if (oldval) {*ret=*oldval; delete oldval;} else *ret=ExtValue();
+	}
+}
+
+void DictionaryObject::p_get(PARAMPROCARGS)
+{
+if ((args->type==TInt)||(args->type==TDouble))
+	{
+	HashEntryIterator* iter=getIndexIterator(args->getInt());
+	if (iter && (*iter)->value)
+		{
+		*ret=*((ExtValue*)(*iter)->value);
+		return;
+		}
+	}
+else
+	{
+	ExtValue *val=(ExtValue*)hash.get(args[0].getString());
+	if (val)
+		{
+		*ret=*val;
+		return;
+		}
+	}
+*ret=ExtValue();
+}
+
+void DictionaryObject::p_getKey(PARAMPROCARGS)
+{
+HashEntryIterator* iter=getIndexIterator(args->getInt());
+if (iter)
+	{
+	*ret=(*iter)->key;
+	return;
+	}
+*ret=ExtValue();
+}
+
+void DictionaryObject::p_set(PARAMPROCARGS)
+{
+ExtValue *newval=(args[0].getType()==TUnknown)?0:new ExtValue(args[0]);
+ExtValue *oldval=(ExtValue*)hash.put(args[1].getString(),newval);
+if (oldval) {*ret=*oldval; delete oldval;} else *ret=ExtValue();
+}
+
+SString DictionaryObject::serialize() const
+{
+SString out="{";
+	{
+	for(HashEntryIterator it(hash);it.isValid();)
+		{
+		out+="\"";
+		SString q=it->key; sstringQuote(q);
+		out+=q;
+		out+="\":";
+		out+=((ExtValue*)it->value)->serialize();
+		it++;
+		if (it.isValid()) out+=",";
+		}
+	}
+out+="}";
+return out;
+}
+
+void DictionaryObject::get_toString(ExtValue* ret)
+{
+SString out="{";
+//static SListTempl<DictionaryObject*> trace;
+if (tostring_trace.getref().find(this)>=0)
+	out+="...";
+else
+	{
+	tostring_trace.getref()+=this;
+	for(HashEntryIterator it(hash);it.isValid();)
+		{
+		out+=it->key;
+		out+=":";
+		out+=((ExtValue*)it->value)->getString();
+		it++;
+		if (it.isValid()) out+=",";
+		}
+	tostring_trace.getref()-=this;
+	}
+out+="}";
+ret->setString(out);
+}
+
+DictionaryObject* DictionaryObject::fromObject(const ExtObject& o)
+{
+return (DictionaryObject*)o.getTarget(par.getName());
+}
Index: /cpp/gdk/collectionobj.h
===================================================================
--- /cpp/gdk/collectionobj.h	(revision 104)
+++ /cpp/gdk/collectionobj.h	(revision 104)
@@ -0,0 +1,78 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _COLLECTIONOBJ_H_
+#define _COLLECTIONOBJ_H_
+
+#include "param.h"
+#include "extvalue.h"
+#include "hashtable.h"
+#include "3d.h"
+
+/** object collection, indexed by int */
+class VectorObject: public DestrBase
+{
+  public:
+SList data;
+int readonly:1, owndata:1;
+void clear();
+ExtValue *get(int i) {return (ExtValue*)data.get(i);}
+void set(int i,const ExtValue& val);
+
+static Param par;
+VectorObject(Pt3D& pt);
+VectorObject():readonly(0),owndata(1) {}
+~VectorObject() {clear();}
+#define STATRICKCLASS VectorObject
+PARAMPROCDEF(p_clear) {if (readonly) return; clear();}
+PARAMGETDEF(size) {arg1->setInt(data.size());}
+PARAMGETDEF(avg);
+PARAMGETDEF(stdev);
+PARAMPROCDEF(p_remove);
+PARAMPROCDEF(p_get);
+PARAMPROCDEF(p_find);
+PARAMPROCDEF(p_set) {if (!readonly) set(arg1[1].getInt(),arg1[0]);}
+PARAMPROCDEF(p_add) {if (readonly) return; /*ExtValue tmp; get_toString(&tmp); printf("%s += %s",(const char*)tmp.getString(),(const char*)arg1[0].getString());*/ data+=new ExtValue(arg1[0]); /*get_toString(&tmp); printf(" -> %s\n",(const char*)tmp.getString());*/ arg2->setInt(data.size()-1);}
+PARAMGETDEF(toString);
+#undef STATRICKCLASS
+static void p_new(void*,ExtValue*args,ExtValue*ret)
+	{ret->setObject(ExtObject(&par,new VectorObject));}
+SString serialize() const;
+ExtObject makeObject() {return ExtObject(&par,this);}
+
+static VectorObject* fromObject(const ExtObject& o);
+};
+
+/** object collection, indexed by name */
+class DictionaryObject: public DestrBase
+{
+  public:
+HashTable hash;
+HashEntryIterator it;
+int it_index;
+
+void clear();
+HashEntryIterator* getIndexIterator(int i);
+
+static Param par;
+DictionaryObject():it(hash),it_index(-1) {}
+~DictionaryObject() {clear();}
+#define STATRICKCLASS DictionaryObject
+PARAMPROCDEF(p_clear) {clear();}
+PARAMGETDEF(size) {arg1->setInt(hash.getSize());}
+PARAMPROCDEF(p_remove);
+PARAMPROCDEF(p_get);
+PARAMPROCDEF(p_getKey);
+PARAMPROCDEF(p_set);
+PARAMPROCDEF(p_find);
+PARAMGETDEF(toString);
+#undef STATRICKCLASS
+SString serialize() const;
+static void p_new(void*,ExtValue*args,ExtValue*ret)
+	{ret->setObject(ExtObject(&par,new DictionaryObject));} //NOWY KANDYDAT NA BUGA ROKU!!!
+static DictionaryObject* fromObject(const ExtObject& v);
+ExtObject makeObject() {return ExtObject(&par,this);}
+};
+
+#endif
Index: /cpp/gdk/conv_f1.cpp
===================================================================
--- /cpp/gdk/conv_f1.cpp	(revision 103)
+++ /cpp/gdk/conv_f1.cpp	(revision 104)
@@ -261,5 +261,9 @@
 lastNeuroClassParam();
 if (neuro_cls_param)
-	neuro_cls_param->setById(name,ExtValue(value));
+	{
+	ExtValue e(value);
+	const ExtValue &re(e);
+	neuro_cls_param->setById(name,re);
+	}
 }
 
Index: /cpp/gdk/errmanager.cpp
===================================================================
--- /cpp/gdk/errmanager.cpp	(revision 103)
+++ /cpp/gdk/errmanager.cpp	(revision 104)
@@ -5,10 +5,11 @@
 #include "errmanager.h"
 
-ErrorManager globalErrorManager;
 
 void FramMessage(const char *o,const char *m,const char *bl,int w)
 {
-globalErrorManager.send(o,m,bl,w);
+ErrorManager::instance.getref().send(o,m,bl,w);
 }
+
+ThreadSingleton<ErrorManager> ErrorManager::instance;
 
 void ErrorManager::send(int level,const char *o,const char *m,const char *bl,int w)
@@ -76,5 +77,5 @@
 {
 if (isEnabled()) return;
-globalErrorManager.add(this);
+ErrorManager::instance.getref().add(this);
 }
 
@@ -82,5 +83,5 @@
 {
 if (!isEnabled()) return;
-globalErrorManager.remove(this);
+ErrorManager::instance.getref().remove(this);
 }
 
Index: /cpp/gdk/errmanager.h
===================================================================
--- /cpp/gdk/errmanager.h	(revision 103)
+++ /cpp/gdk/errmanager.h	(revision 104)
@@ -9,4 +9,5 @@
 #include "sstring.h"
 #include "framsg.h"
+#include "threads.h"
 
 class ErrorHandlerBase;
@@ -26,7 +27,7 @@
 	{send(handlers.size()-1,o,m,bl,w);}
 ~ErrorManager() {removeAll();}
+
+static ThreadSingleton<ErrorManager> instance;
 };
-
-extern ErrorManager globalErrorManager;
 
 ////////////////////////////////////////
@@ -51,5 +52,5 @@
 void disable();
 
-ErrorHandlerBase(int opts=0):options(opts),mgr(0)
+ErrorHandlerBase(int opts=0):mgr(0),options(opts)
 	{if (!(options&DontEnable)) enable();}
 ~ErrorHandlerBase()
@@ -87,4 +88,17 @@
 };
 
+class ErrorRedirector: public ErrorHandlerBase
+{
+	ErrorManager *other_mgr;
+public:
+	ErrorRedirector(ErrorManager *om)
+		:ErrorHandlerBase(),other_mgr(om) {}
+
+	void handle(const char *o,const char *m,const char *bl,int w)
+		{
+		other_mgr->send(o,m,bl,w);
+		}
+};
+
 #endif
 
Index: /cpp/gdk/extvalue.cpp
===================================================================
--- /cpp/gdk/extvalue.cpp	(revision 103)
+++ /cpp/gdk/extvalue.cpp	(revision 104)
@@ -5,10 +5,15 @@
 #include "extvalue.h"
 #include "param.h"
+#include "sstringutils.h"
 #include <math.h>
+#include <ctype.h>
+#include "collectionobj.h"
+#include "3dobject.h"
 
 SString ExtObject::toString() const
 {
 if (isEmpty()) return SString("<empty object>");
-ParamInterface *p=getParamInterface();
+Param tmp_param;
+ParamInterface *p=getParamInterface(tmp_param);
 int tostr=p->findId("toString");
 if (tostr>=0)
@@ -25,4 +30,113 @@
 	return tmp;
 	}
+}
+
+ThreadSingleton<ExtObject::Serialization> ExtObject::serialization;
+
+void ExtObject::Serialization::begin()
+{
+if (level==0)
+	refs.clear();
+level++;
+}
+
+int ExtObject::Serialization::add(const ExtObject &o)
+{
+if (o.isEmpty()) return -1;
+for(int i=0;i<refs.size();i++)
+	{
+	ExtObject& r=refs[i];
+	if (r==o) return i;
+	}
+refs.push_back(o);
+return -1;
+}
+
+void ExtObject::Serialization::replace(const ExtObject& o,const ExtObject& other)
+{
+if (o.isEmpty()) return;
+for(int i=0;i<refs.size();i++)
+	{
+	ExtObject& r=refs[i];
+	if (r==o)
+		{
+		r=other;
+		return;
+		}
+	}
+}
+
+void ExtObject::Serialization::remove(const ExtObject& o)
+{
+if (o.isEmpty()) return;
+for(int i=0;i<refs.size();i++)
+	{
+	ExtObject& r=refs[i];
+	if (o==r) refs.erase(refs.begin()+i);
+	}
+}
+
+const ExtObject* ExtObject::Serialization::get(int ref)
+{
+if (ref<0) return NULL;
+if (ref>=refs.size()) return NULL;
+return &refs[ref];
+}
+
+void ExtObject::Serialization::end()
+{
+level--;
+if (level==0)
+	refs.clear();
+}
+
+SString ExtObject::serialize_inner() const
+{
+int ref=serialization.getref().add(*this);
+if (ref>=0)
+	{
+	SString ret;
+	sprintf(ret.directAppend(20),"^%d",ref);ret.endAppend();
+	return ret;
+	}
+
+if (isEmpty()) return SString();
+VectorObject *vec=VectorObject::fromObject(*this);
+if (vec)
+	return vec->serialize();
+DictionaryObject *dic=DictionaryObject::fromObject(*this);
+if (dic)
+	return dic->serialize();
+Param tmp_param;
+ParamInterface *p=getParamInterface(tmp_param);
+int m=p->findId("toVector");
+if (m<0) 
+	m=p->findId("toDictionary");
+if (m>=0)
+	{
+	ExtObject o(p->getObject(m));
+	SString ret=SString(interfaceName())+o.serialize();
+	return ret;
+	}
+m=p->findId("toString");
+if (m>=0)
+	{
+	SString str=p->getString(m);
+	sstringQuote(str);
+	SString ret=SString(interfaceName())+"\""+str+"\"";
+	return ret;
+	}
+
+SString ret=interfaceName();
+sprintf(ret.directAppend(30),"<%p>",object?object:paraminterface);ret.endAppend();
+return ret;
+}
+
+SString ExtObject::serialize() const
+{
+serialization.getref().begin();
+SString ret=serialize_inner();
+serialization.getref().end();
+return ret;
 }
 
@@ -297,4 +411,257 @@
 }
 
+SString ExtValue::serialize() const
+{
+switch(type)
+	{
+	case TString:
+		{
+		SString q=sdata();
+		sstringQuote(q);
+		return SString("\"")+q+SString("\"");
+		}
+	case TInt:
+		{
+		SString tmp;
+		sprintf(tmp.directAppend(20),"%d",idata());
+		tmp.endAppend();
+		return tmp;
+		}
+	case TDouble:
+		{
+		SString tmp;
+		sprintf(tmp.directAppend(20),"%.15g",ddata());
+		tmp.endAppend();
+		if ((!strchr(tmp,'.'))&&(!strchr(tmp,'e'))) tmp+=".0";
+		return tmp;
+		}
+	case TObj:
+		return odata().serialize();
+	case TInvalid:
+		return SString("undefined");
+	default:
+		return SString("null");
+	}
+}
+
+//returns the first character after the parsed number or NULL if not a number
+const char* ExtValue::parseNumber(const char* in)
+{
+if (isdigit(*in)||((*in=='-')&&(isdigit(in[1]))))
+	{
+	const char* p=in;
+	if (*p=='-') p++;
+	while(isdigit(*p)) p++;
+	bool fp=false;
+	if ((*p=='.') && isdigit(p[1]))
+		{
+		p++;
+		while(isdigit(*p)) p++;
+		fp=true;
+		}
+	if (((*p=='e')||(*p=='E')) && (isdigit(p[1]) || (((p[1]=='-') || (p[1]=='+')) && isdigit(p[2]))))
+		{
+		p++;
+		if ((*p=='-')||(*p=='+')) p++;
+		while(isdigit(*p)) p++;
+		fp=true;
+		}
+
+	if (fp)
+		{
+		setDouble(atof(in));
+		return p;
+		}
+	else
+		{
+		setInt(atol(in));
+		return p;
+		}
+	}
+return NULL;
+}
+
+PtrListTempl<ParamInterface*> ExtValue::deserializable_classes;
+
+void ExtValue::initDeserializableClasses()
+{
+deserializable_classes+=&Pt3D_Ext::getStaticParam();
+deserializable_classes+=&Orient_Ext::getStaticParam();
+}
+
+ParamInterface *ExtValue::findDeserializableClass(const char* name)
+{
+FOREACH(ParamInterface*,cls,deserializable_classes)
+	if (!strcmp(cls->getName(),name))
+		return cls;
+return NULL;
+}
+
+static const char* skipWord(const char* in)
+{
+while(isalpha(*in))
+	in++;
+return in;
+}
+
+//returns the first character after the parsed portion or NULL if invalid format
+const char* ExtValue::deserialize_inner(const char* in)
+{
+const char* ret=parseNumber(in);
+if (ret)
+	return ret;
+else if (*in=='\"')
+	{
+	ret=skipQuoteString(in+1,NULL);
+	SString s(in+1,ret-(in+1));
+	sstringUnquote(s);
+	setString(s);
+	if (*ret=='\"')
+		return ret+1;
+	else
+		return NULL;
+	}
+else if (*in=='[')
+	{
+	VectorObject *vec=new VectorObject;
+	ExtObject o(&VectorObject::par,vec);
+	ExtObject::serialization.getref().add(o);
+	const char* p=in+1;
+	ExtValue tmp;
+	while(*p)
+		{
+		if (*p==']') {p++;break;}
+		ret=tmp.deserialize(p);
+		if (ret)
+			{
+			vec->data+=new ExtValue(tmp);
+			p=ret;
+			if (*p==',') p++;
+			}
+		else
+			{
+			p=NULL;
+			break;
+			}
+		}
+	setObject(o);
+	return p;
+	}
+else if (*in=='{')
+	{
+	DictionaryObject *dic=new DictionaryObject;
+	ExtObject o(&DictionaryObject::par,dic);
+	ExtObject::serialization.getref().add(o);
+	const char* p=in+1;
+	ExtValue args[2]/*={value,key}*/, dummy_ret;
+	while(*p)
+		{
+		if (*p=='}') {p++;break;}
+		ret=args[1].deserialize(p);
+		if ((!ret)||(args[1].getType()!=TString)) {p=NULL;break;}
+		p=ret;
+		if (*p!=':') {p=NULL;break;}
+		p++;
+		ret=args[0].deserialize(p);
+		if (!ret) {p=NULL;break;}
+		p=ret;
+		dic->p_set(args,&dummy_ret);
+		if (*p==',') p++;
+		}
+	setObject(o);
+	return p;
+	}
+else if (!strncmp(in,"null",4))
+	{
+	setEmpty();
+	return in+4;
+	}
+else if (!strncmp(in,"undefined",9))
+	{
+	setInvalid();
+	return in+9;
+	}
+else if (*in=='<')
+	{ //unserializable object
+	setInvalid();
+	while(*in)
+		if (*in=='>')
+			return in+1;
+		else in++;
+	return in;
+	}
+else if (*in=='^')
+	{
+	in++;
+	ExtValue ref;
+	ret=ref.parseNumber(in);
+	if (ret && (ref.getType()==TInt))
+		{
+		const ExtObject* o=ExtObject::serialization.getref().get(ref.getInt());
+		if (o)
+			{
+			setObject(*o);
+			return ret;
+			}
+		}
+	return NULL;
+	}
+else if ((ret=skipWord(in))&&(ret!=in))
+	{
+	SString clsname(in,ret-in);
+	ExtValue tmp;
+	ret=tmp.deserialize(ret);
+	ParamInterface *cls=findDeserializableClass(clsname);
+	if (cls && (tmp.getType()!=TUnknown) && (tmp.getType()!=TInvalid))
+		{
+		VectorObject *vec=VectorObject::fromObject(tmp.getObject());
+		if (vec)
+			{
+			int m=cls->findId("newFromVector");
+			if (m>=0)
+				{
+				cls->call(m,&tmp,this);
+				ExtObject::serialization.getref().replace(tmp.getObject(),getObject());
+				return ret;
+				}
+			}
+		DictionaryObject *dic=DictionaryObject::fromObject(tmp.getObject());
+		if (dic)
+			{
+			int m=cls->findId("newFromDictionary");
+			if (m>=0)
+				{
+				cls->call(m,&tmp,this);
+				ExtObject::serialization.getref().replace(tmp.getObject(),getObject());
+				return ret;
+				}
+			}
+		if (tmp.getType()==TString)
+			{
+			int m=cls->findId("newFromString");
+			if (m>=0)
+				{
+				cls->call(m,&tmp,this);
+				ExtObject::serialization.getref().replace(tmp.getObject(),getObject());
+				return ret;
+				}
+			}
+		ExtObject::serialization.getref().remove(tmp.getObject());
+		setEmpty();
+		}
+	FMprintf("ExtValue","deserialize",FMLV_WARN,"object of class \"%s\" could not be deserialized",(const char*)clsname);
+	return ret;
+	}
+return NULL;
+}
+
+const char* ExtValue::deserialize(const char* in)
+{
+ExtObject::serialization.getref().begin();
+const char* ret=deserialize_inner(in);
+ExtObject::serialization.getref().end();
+return ret;
+}
+
 ExtObject ExtValue::getObject() const
 {
@@ -307,4 +674,4 @@
 if (getType()!=TObj) return ExtValue((long)getType());
 ExtObject& o=odata();
-return ExtValue(SString(o.isEmpty()?"":o.getParamInterface()->getName()));
-}
+return ExtValue(SString(o.isEmpty()?"":o.interfaceName()));
+}
Index: /cpp/gdk/extvalue.h
===================================================================
--- /cpp/gdk/extvalue.h	(revision 103)
+++ /cpp/gdk/extvalue.h	(revision 104)
@@ -8,4 +8,6 @@
 #include "sstring.h"
 #include "param.h"
+#include "nonstd_stl.h"
+#include "threads.h"
 
 #define EXTVALUEUNION
@@ -48,16 +50,20 @@
 void* operator new(size_t s) {return malloc(sizeof(ExtObject));}
 void operator delete(void* mem) {free(mem);}
-
-ParamInterface *getParamInterface() const  {if(subtype&2){param->select(object);return param;} return paraminterface;}
-const char* interfaceName() const {if (isEmpty()) return "Empty"; return getParamInterface()->getName();}
+///@param tmp_param can be used for temporary storage, the result ParamInterface* is only valid for as long as tmp_param is valid
+ParamInterface *getParamInterface(Param &tmp_param) const  {if(subtype&2){tmp_param.setParamTab(param->getParamTab());tmp_param.select(object);return &tmp_param;} return paraminterface;}
+const char* interfaceName() const {if (isEmpty()) return "Empty"; return (subtype&2)?param->getName():paraminterface->getName();}
+bool matchesInterfaceName(ParamInterface* pi) const {return !strcmp(interfaceName(),pi->getName());}
 void* getTarget() const {return (subtype&1)?dbobject:object;}
 void* getTarget(const char* classname) const {return strcmp(interfaceName(),classname)?0:getTarget();}
+void setEmpty() {decref();subtype=0;param=NULL;object=NULL;}
 int isEmpty() const {return !param;}
 ExtObject(const ExtObject& src)      {src.incref();copyFrom(src);}
 void operator=(const ExtObject& src) {src.incref();decref();copyFrom(src);}
 
-int operator==(const ExtObject& src) {if (object!=src.object) return 0; return (object==0)?(param==src.param):1;}
+int operator==(const ExtObject& src) const {if (object!=src.object) return 0; return (object==0)?(!strcmp(param->getName(),src.param->getName())):1;}
 
 SString toString() const;
+SString serialize_inner() const;
+SString serialize() const;
 
 ExtObject(Param *p,void *o):subtype(2),object(o),param(p){}
@@ -67,6 +73,20 @@
 
 ~ExtObject(){decref();}
+
+class Serialization
+{
+std::vector<ExtObject> refs;
+int level;
+  public:
+void begin();
+void end();
+int add(const ExtObject& o);
+void replace(const ExtObject& o,const ExtObject& other);
+void remove(const ExtObject& o);
+const ExtObject* get(int ref);
 };
 
+static ThreadSingleton<Serialization> serialization;
+};
 
 class ExtValue
@@ -124,5 +144,12 @@
 double getDouble() const;
 SString getString() const;
+SString serialize() const;
 ExtObject getObject() const;
+const char* parseNumber(const char* in);
+const char* deserialize(const char* in);//< @return first character after the succesfully parsed string or NULL if failed
+const char* deserialize_inner(const char* in);
+static PtrListTempl<ParamInterface*> deserializable_classes;
+static ParamInterface *findDeserializableClass(const char* name);
+static void initDeserializableClasses();
 
 ExtValue getExtType();
Index: /cpp/gdk/framsg.cpp
===================================================================
--- /cpp/gdk/framsg.cpp	(revision 103)
+++ /cpp/gdk/framsg.cpp	(revision 104)
@@ -11,12 +11,10 @@
 #endif
 
-static const char *warn="[warn] ";
-static const char *err="[ERROR] ";
+char* MSG_LEVEL[]={"DEBUG","INFO","WARN","ERROR","CRITICAL"};
 
 /* TODO integracja z error managerem 
 void FramMessage(const char *o,const char *m,const char *bl,int w)
 {
-  const char *lvl="";
-  if (w==1) lvl=warn; else if (w>1) lvl=err;
+  const char *lvl=MSG_LEVEL[w+1];
 #ifdef SHP
   //jesli chcemy zeby nawet w trybie release wysylal komunikaty:
Index: /cpp/gdk/framsg.h
===================================================================
--- /cpp/gdk/framsg.h	(revision 103)
+++ /cpp/gdk/framsg.h	(revision 104)
@@ -4,7 +4,9 @@
 #include <stdarg.h>
 
+extern char* MSG_LEVEL[];
+
 void FMprintf(const char *o,const char *m,int w,const char *bl, ...);
 void FMprintf(const char *o,const char *m,int w,const char *bl,va_list va);
-void FMprintf(const char *bl,...); //dla leniwych - zastepuje printf
+void FMprintf(const char *bl,...); //for lazy people - instead of printf
 void FramMessage(const char *o,const char *m,const char *bl,int w);
 
@@ -17,10 +19,10 @@
 /*
 w: weight (importance) of a message
--1:debugging information, not needed for final users
-0: information
-1: warning or corrected error
-2: uncorrected error. can cause malfunction
-3: serious error, causes side effects. user should save what can be
-   saved and restart the application
+
+-1: debugging information, not needed for final users
+ 0: information
+ 1: warning or corrected error
+ 2: uncorrected error. can cause malfunction
+ 3: serious error, causes side effects. user should save what can be saved and restart the application
 */
 
Index: /cpp/gdk/gdktest.cpp
===================================================================
--- /cpp/gdk/gdktest.cpp	(revision 103)
+++ /cpp/gdk/gdktest.cpp	(revision 104)
@@ -28,5 +28,5 @@
        "    ########################################\n",title);
 }
-void printProperties(ParamInterface &pi)
+void printProperties(Param &pi)
 {
 printf(" #        id                      type  name        group (%d properties)\n",pi.getPropCount());
@@ -39,5 +39,7 @@
 }
 
-void changeOneProperty(ParamInterface &pi)
+#define PRINT_PROPERTIES(p) {Param tmp_param(p); printProperties(tmp_param);}
+
+void changeOneProperty(Param &pi)
 {
 if (pi.getPropCount()<=0) return;
@@ -54,4 +56,6 @@
 }
 
+#define CHANGE_ONE_PROPERTY(p) {Param tmp_param(p); changeOneProperty(tmp_param);}
+
 void moreAboutPart(Part* p)
 {
@@ -61,5 +65,5 @@
        "and should be handled with care, because they influence other elements of the model.\n\n"
        " [this data is provided by Part::properties() ]\n");
-printProperties(p->properties());
+PRINT_PROPERTIES(p->properties());
 printf("\nHowever, there is a subset of properties which may be modified more freely.\n"
        "Properties on this list are related only to this part and can be changed\n"
@@ -67,5 +71,5 @@
        "will be silently repaired.\n"
        "\n [this data is provided by Part::extraProperties() ]\n");
-printProperties(p->extraProperties());
+PRINT_PROPERTIES(p->extraProperties());
 printf("\nThis set of properties can vary from release to release,\n"
        "but can be safely accessed by using extraProperties() call.\n"
@@ -74,5 +78,5 @@
        "Now we will try to change some of properties:\n\n");
 p->getModel().open();
-changeOneProperty(p->extraProperties());
+CHANGE_ONE_PROPERTY(p->extraProperties());
 p->getModel().close();
 printf("\nLet's see f0... (check out part #%d !)\n\n%s\n", p->refno, (const char*)p->getModel().getF0Geno().getGene());
@@ -127,5 +131,5 @@
 {
 printf("Similarly as with Part, the full list of properties comes first:\n\n");
-printProperties(j->properties());
+CHANGE_ONE_PROPERTY(j->properties());
 printf("\nActually, there are two kinds of Joints: delta and absolute.\n"
        "For this object, Joint::isDelta() returns %d, so this is the %s Joint.\n",
@@ -147,8 +151,8 @@
        "The other properties are available from Joint::extraProperties()\n"
        "and at the moment are defined as follows:\n\n");
-printProperties(j->extraProperties());
+PRINT_PROPERTIES(j->extraProperties());
 printf("\nThey can be changed just like Part's extra properties:\n");
 j->getModel().open();
-changeOneProperty(j->extraProperties());
+CHANGE_ONE_PROPERTY(j->extraProperties());
 j->getModel().close();
 printf("And after that we have this genotype:\n\n%s\n", (const char*)j->getModel().getF0Geno().getGene());
@@ -161,10 +165,10 @@
 printf("Basic features of Neuro object are similar to those of Part and Joint.\n"
        "We can request a property list:\n\n");
-printProperties(n->properties());
+PRINT_PROPERTIES(n->properties());
 printf("\n...and extra properties (which are designed to be always valid and easy to change):\n\n");
-printProperties(n->extraProperties());
+PRINT_PROPERTIES(n->extraProperties());
 printf("\nAs usual, we will change something:\n");
 n->getModel().open();
-changeOneProperty(n->extraProperties());
+CHANGE_ONE_PROPERTY(n->extraProperties());
 n->getModel().close();
 printf("Each neuron can have any number of inputs = weighted connections\n with other neurons.\n"
Index: /cpp/gdk/genoconv.cpp
===================================================================
--- /cpp/gdk/genoconv.cpp	(revision 103)
+++ /cpp/gdk/genoconv.cpp	(revision 104)
@@ -32,7 +32,6 @@
 {
 if (i>=gcm->converters.size()) return 0;
-static char t[20];
-sprintf(t,"genkonw%d",i);
-return t;
+sprintf(tmp_id,"genkonw%d",i);
+return tmp_id;
 }
 
@@ -174,5 +173,5 @@
 {
 if (in.getFormat()==format) return in;
-static char path[10];
+char path[10];
 int dep;
 char *ret;
Index: /cpp/gdk/genoconv.h
===================================================================
--- /cpp/gdk/genoconv.h	(revision 103)
+++ /cpp/gdk/genoconv.h	(revision 104)
@@ -21,4 +21,5 @@
 GenoConvManager *gcm;
 std::vector<std::string> gcnames;
+char tmp_id[20];
 void freetab();
 public:
Index: /cpp/gdk/genotypeloader.cpp
===================================================================
--- /cpp/gdk/genotypeloader.cpp	(revision 103)
+++ /cpp/gdk/genotypeloader.cpp	(revision 104)
@@ -22,5 +22,5 @@
 void MiniGenotypeLoader::init()
 {
-addClass(&genotype_param);
+addObject(&genotype_param);
 breakOn(MultiParamLoader::OnError + MultiParamLoader::AfterObject);
 }
@@ -29,5 +29,5 @@
 {
 genotype_object.clear();
-if ((go()==AfterObject)&&(getClass()==&genotype_param))
+if ((go()==AfterObject)&&(getObject().matchesInterfaceName(&genotype_param)))
 	return &genotype_object;
 else
Index: /cpp/gdk/hashtable.cpp
===================================================================
--- /cpp/gdk/hashtable.cpp	(revision 104)
+++ /cpp/gdk/hashtable.cpp	(revision 104)
@@ -0,0 +1,195 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "hashtable.h"
+
+int HashTable::hash(const char *s)
+{
+int h=0;
+char c;
+while(c=*(s++))
+    h=31*h+c;
+return h&0x7fffffff;
+}
+
+void HashTable::init(int initsize,float lo)
+{
+size=initsize;
+load=lo;
+threshold=(int)(load*size);
+tab=(HashEntry**)calloc(size,sizeof(HashEntry*));
+count=0;
+sync=0;
+}
+
+void HashTable::rehash(int newsize)
+{
+if (newsize==size) return;
+HashEntry **newtab=(HashEntry**)calloc(newsize,sizeof(HashEntry*));
+HashEntry **te=tab,*e,*ne;
+int i;
+for (int s=size;s>0;s--,te++)
+    for (e=*te;e;)
+	{
+	    ne=e; e=e->next;
+	    i=ne->hash%newsize;
+	    ne->next=newtab[i];
+	    newtab[i]=ne;
+	}
+free(tab);
+tab=newtab;
+size=newsize;
+threshold=int(load*size);
+sync++;
+}
+
+void HashTable::clear()
+{
+HashEntry *e,**te,*next;
+int n;
+for (n=size,te=tab;n>0;n--,te++)
+	for (e=*te;e;e=next)
+		{
+		next=e->next;
+		delete e;
+		}
+if (tab) free(tab);
+tab=0; size=0;
+sync++;
+}
+
+HashTable::~HashTable()
+{
+clear();
+}
+
+void* HashTable::put(const SString& key,void *value)
+{
+int h=hash(key);
+int i=h%size;
+for (HashEntry *e=tab[i];e;e=e->next)
+    {
+    if (e->key==key)
+	{
+	void *v=e->value;
+	e->value=value;
+	return v;
+	}
+    }
+if (count>=threshold) { rehash(2*size+1); i=h%size; }
+HashEntry *e=new HashEntry(h,key,value);
+e->next=tab[i];
+tab[i]=e;
+count++;
+sync++;
+return 0;
+}
+
+void* HashTable::remove(const SString& key)
+{
+int i=hash(key)%size;
+HashEntry **ptr=tab+i,*e;
+for (;e=*ptr;ptr=&e->next)
+    {
+    if (e->key==key)
+	{
+	*ptr=e->next;
+	void *v=e->value;
+	delete e;
+	count--;
+	sync++;
+	return v;
+	}
+    }
+return 0;
+}
+
+void* HashTable::remove(HashEntryIterator& it)
+{
+if (!it.entry) return 0;
+HashEntry **ptr=tab+it.hashindex,*e;
+for (;e=*ptr;ptr=&e->next)
+    {
+    if (e == it.entry)
+	{
+	it++;
+	*ptr=e->next;
+	void *v=e->value;
+	delete e;
+	count--;
+	sync++;
+	it.sync++;
+	return v;
+	}
+    }
+   return NULL; 
+}
+
+void* HashTable::get(const SString& key,int *reallygot)
+{
+int i=hash(key)%size;
+for (HashEntry *e=tab[i];e;e=e->next)
+	if (e->key==key) {if (reallygot) *reallygot=1; return e->value;}
+return 0;
+}
+
+/////////
+
+void HashEntryIterator::findNext()
+{
+while (hashindex < (ht->size-1))
+	{
+	hashindex++;
+	if (entry=ht->tab[hashindex]) return;
+	}
+}
+
+
+void HashEntryIterator::operator++()
+{
+if (entry) entry=entry->next;
+if (!entry) findNext();
+}
+
+//////////////////////////
+
+void HashTable::debugprint()
+{
+printf("HashTable: %d/%d (max %d)\n",count,size,threshold);
+HashEntry *e,**te;
+int n;
+for (n=0,te=tab;n<size;n++,te++)
+	if (e=*te)
+	{
+	printf(" %d:",n);
+	for (;e;e=e->next)
+		printf(" (%x)%s=%p",e->hash,(const char*)e->key,e->value);
+	printf("\n");
+	}
+}
+
+/**
+stats[0]=buckets used
+stats[1]=min keys in bucket
+stats[2]=avg keys in bucket
+stats[3]=max keys in bucket
+ */
+void HashTable::getstats(float *stats)
+{
+HashEntry *e,**te;
+int used=0, ma=0, mi=count;
+int c,n;
+for (n=size,te=tab;n>0;n--,te++)
+	{
+	c=0;
+	for (e=*te;e;e=e->next) c++;
+	if (c>ma) ma=c;
+	if ((c<mi)&&(c>0)) mi=c;
+	if (c>0) used++;
+	}
+stats[0]=used;
+stats[1]=(mi==count)?0:mi;
+stats[2]=(float)count/(float)used;
+stats[3]=ma;
+}
Index: /cpp/gdk/hashtable.h
===================================================================
--- /cpp/gdk/hashtable.h	(revision 104)
+++ /cpp/gdk/hashtable.h	(revision 104)
@@ -0,0 +1,84 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _HASHTABLE_H_
+#define _HASHTABLE_H_
+
+#include "sstring.h"
+
+class HashEntry
+{
+public:
+int hash;
+SString key;
+void *value;
+HashEntry *next;
+
+HashEntry(int h,const SString& k,void *v):hash(h),key(k),value(v),next(0){}
+};
+
+class HashEntryIterator;
+
+class HashTable
+{
+friend class HashEntryIterator;
+HashEntry **tab;
+int size;
+int count;
+int threshold;
+float load;
+long sync;
+
+int hash(const char *s);
+void rehash(int newsize);
+public:
+
+HashTable(int initsize,float lo) {init(initsize,lo);}
+HashTable(int initsize) {init(initsize,0.75);}
+HashTable() {init();}
+~HashTable();
+
+/** always use init() after clear() ! */
+void clear();
+void init(int initsize=11,float lo=0.75);
+
+int getSize() {return count;}
+void* put(const SString& key,void *value);
+void* get(const SString& key, int *reallygot=0);
+void* remove(const SString& key);
+/** can be used inside iteration loop:
+    for(HashEntryIterator it(hashtable);it;) hashtable.remove(it);
+    \note iterator is "iterated" to the next entry when the current one is removed (no "it++"!)
+ */
+void* remove(HashEntryIterator& it);
+
+void debugprint();
+void getstats(float *);
+};
+
+/** for(HashEntryIterator it(hashtable);it;it++)
+      {
+      ... it->value
+      ... it->key
+      }
+ */
+class HashEntryIterator
+{
+void findNext();
+  public:
+const HashTable *ht;
+int hashindex;
+HashEntry *entry;
+int sync;
+  HashEntryIterator(const HashTable&t):ht(&t),hashindex(0),entry(t.tab[0]),sync(ht->sync)
+	{if (!entry) findNext();}
+  HashEntryIterator() {}
+void operator++();
+void operator++(int) {operator++();}
+HashEntry* operator->() {return entry;}
+bool isValid() {return (entry&&(sync==ht->sync))?1:0;}
+};
+
+
+#endif
Index: /cpp/gdk/list.h
===================================================================
--- /cpp/gdk/list.h	(revision 103)
+++ /cpp/gdk/list.h	(revision 104)
@@ -57,5 +57,5 @@
 SListTempl(const SListTempl<T>& src):have(0),used(0),mem(0),pos(0)
 	{(*this)=src;}
-SListTempl():have(0),used(0),mem(0),pos(0)
+SListTempl():have(0),used(0),pos(0),mem(0)
 	{}
 ~SListTempl()
Index: /cpp/gdk/model.cpp
===================================================================
--- /cpp/gdk/model.cpp	(revision 103)
+++ /cpp/gdk/model.cpp	(revision 104)
@@ -382,9 +382,9 @@
 SString gen;
 
-static Param modelparam(f0_model_paramtab);
-static Param partparam(f0_part_paramtab);
-static Param jointparam(f0_joint_paramtab);
-static Param neuroparam(f0_neuro_paramtab);
-static Param connparam(f0_neuroconn_paramtab);
+Param modelparam(f0_model_paramtab);
+Param partparam(f0_part_paramtab);
+Param jointparam(f0_joint_paramtab);
+Param neuroparam(f0_neuro_paramtab);
+Param connparam(f0_neuroconn_paramtab);
 
 static Part defaultpart;
@@ -563,5 +563,5 @@
 if (!strncmp(t,"p:",2))
 	{
-	static Param partparam(f0_part_paramtab);
+	Param partparam(f0_part_paramtab);
 	Part *p=new Part();
 	partparam.select(p);
@@ -576,5 +576,5 @@
 if (!strncmp(t,"m:",2))
 	{
-	static Param modelparam(f0_model_paramtab);
+	Param modelparam(f0_model_paramtab);
 	modelparam.select(this);
 	pos+=2;
@@ -584,5 +584,5 @@
 else if (!strncmp(t,"j:",2))
 	{
-	static Param jointparam(f0_joint_paramtab);
+	Param jointparam(f0_joint_paramtab);
 	Joint *j=new Joint();
 	jointparam.select(j);
@@ -610,5 +610,5 @@
 else if (!strncmp(t,"n:",2)) // neuro (or the old neuro object as the special case)
 	{
-	static Param neuroparam(f0_neuro_paramtab);
+	Param neuroparam(f0_neuro_paramtab);
 	Neuro *nu=new Neuro();
 	neuroparam.select(nu);
@@ -674,5 +674,5 @@
 else if (!strncmp(t,"c:",2)) // add input
 	{
-	static Param ncparam(f0_neuroconn_paramtab);
+	Param ncparam(f0_neuroconn_paramtab);
 	NeuroConn c;
 	ncparam.select(&c);
@@ -696,5 +696,5 @@
 	{
 	// we always use old layout for "ni:"
-	static Param neuroitemparam(f0_neuroitem_paramtab);
+	Param neuroitemparam(f0_neuroitem_paramtab);
 	Neuro *nu=new Neuro();
 	neuroitemparam.select(nu);
@@ -1085,11 +1085,10 @@
 }
 
-const Geno& Model::getF0Geno()
-{
-static Geno invalidgeno("",'0',"","invalid");
+const Geno Model::getF0Geno()
+{
 if (buildstatus==building)
 	FMprintf("Model","getGeno",FMLV_WARN,"model was not completed - missing close()");
 if (buildstatus!=valid)
-	return invalidgeno;
+	return Geno("",'0',"","invalid");
 if (!f0genoknown)
 	{
Index: /cpp/gdk/model.h
===================================================================
--- /cpp/gdk/model.h	(revision 103)
+++ /cpp/gdk/model.h	(revision 104)
@@ -212,5 +212,5 @@
     don't use between open()-close()
 */
-const Geno &getF0Geno();
+const Geno getF0Geno();
 
 /// make f0 genotype from current construction (low level version of getF0Geno)
Index: /cpp/gdk/modelparts.cpp
===================================================================
--- /cpp/gdk/modelparts.cpp	(revision 103)
+++ /cpp/gdk/modelparts.cpp	(revision 104)
@@ -97,13 +97,12 @@
 		       int _prefinputs,int _prefoutput,int _preflocation,
 		       int *_vectordata,bool own_vd,int vhints)
-	:longname(_props->id),
-	 name(_props->name),
-	 description(_description),
+	:ownedvectordata(own_vd),
+	 name(_props->name),longname(_props->id),description(_description),
 	 props(_props),
 	 prefinputs(_prefinputs),
 	 prefoutput(_prefoutput),
 	 preflocation(_preflocation),
-	 vectordata(_vectordata),ownedvectordata(own_vd),
-	 visualhints(vhints),impl(0),active(1),genactive(0)
+	 vectordata(_vectordata),
+	 visualhints(vhints),impl_count(0),/*impl(0),*/active(1),genactive(0)
 {}
 
@@ -114,8 +113,10 @@
 
 NeuroClass::NeuroClass()
-	:name("Invalid"),props(empty_paramtab),
+	:ownedvectordata(0),
+	 name("Invalid"),
+	 props(empty_paramtab),
 	 prefinputs(0), prefoutput(0),
-	 preflocation(0), vectordata(0), ownedvectordata(0),
-	 impl(0), active(1), genactive(0)
+	 preflocation(0), vectordata(0), 
+	 visualhints(0),impl_count(0), /*impl(0),*/ active(1), genactive(0)
 {}
 
@@ -176,5 +177,5 @@
 
 Neuro::Neuro(double _state,double _inertia,double _force,double _sigmo)
-	:state(_state),PartBase(getDefaultStyle())
+	:PartBase(getDefaultStyle()),state(_state)
 #ifdef MODEL_V1_COMPATIBLE
 ,inertia(_inertia),force(_force),sigmo(_sigmo)
@@ -187,4 +188,5 @@
 myclass=0;
 knownclass=1;
+part_refno=-1; joint_refno=-1;
 }
 
@@ -204,4 +206,5 @@
 #endif
 flags=0;
+part_refno=-1; joint_refno=-1;
 }
 
@@ -614,8 +617,8 @@
 ///////////////////////////////////////
 
-const SString& Part::getDefaultStyle()
-{static SString s("part"); return s;}
-const SString& Joint::getDefaultStyle()
-{static SString s("joint"); return s;}
+SString Part::getDefaultStyle()
+{return SString("part");}
+SString Joint::getDefaultStyle()
+{return SString("joint");}
 /*
 const SString& Neuro::getDefaultStyle()
@@ -624,6 +627,6 @@
 {static SString s("neuroitem"); return s;}
 */
-const SString& Neuro::getDefaultStyle()
-{static SString s("neuro"); return s;}
+SString Neuro::getDefaultStyle()
+{return SString("neuro");}
 
 Part::Part():PartBase(getDefaultStyle())
@@ -776,46 +779,32 @@
 };
 
-static Param staticparam;
-
-ParamInterface &Part::extraProperties()
-{
-staticparam.setParamTab(f0_part_xtra_paramtab);
-staticparam.select(this);
-return staticparam;
-}
-
-ParamInterface &Joint::extraProperties()
-{
-staticparam.setParamTab(f0_joint_xtra_paramtab);
-staticparam.select(this);
-return staticparam;
-}
-
-ParamInterface &Neuro::extraProperties()
-{
-staticparam.setParamTab(f0_neuro_xtra_paramtab);
-staticparam.select(this);
-return staticparam;
-}
-
-ParamInterface &Part::properties()
-{
-staticparam.setParamTab(f0_part_paramtab);
-staticparam.select(this);
-return staticparam;
-}
-
-ParamInterface &Joint::properties()
-{
-staticparam.setParamTab(usedelta?f0_joint_paramtab:f0_nodeltajoint_paramtab);
-staticparam.select(this);
-return staticparam;
-}
-
-ParamInterface &Neuro::properties()
-{
-staticparam.setParamTab(f0_neuro_paramtab);
-staticparam.select(this);
-return staticparam;
+Param Part::extraProperties()
+{
+return Param(f0_part_xtra_paramtab,this);
+}
+
+Param Joint::extraProperties()
+{
+return Param(f0_joint_xtra_paramtab,this);
+}
+
+Param Neuro::extraProperties()
+{
+return Param(f0_neuro_xtra_paramtab,this);
+}
+
+Param Part::properties()
+{
+return Param(f0_part_paramtab,this);
+}
+
+Param Joint::properties()
+{
+return Param(usedelta?f0_joint_paramtab:f0_nodeltajoint_paramtab,this);
+}
+
+Param Neuro::properties()
+{
+return Param(f0_neuro_paramtab,this);
 }
 
Index: /cpp/gdk/modelparts.h
===================================================================
--- /cpp/gdk/modelparts.h	(revision 103)
+++ /cpp/gdk/modelparts.h	(revision 104)
@@ -35,5 +35,5 @@
 PartBase(const SString& s):vis_style(s),mapped(0) {}
 ~PartBase();
-static const SString& getDefaultStyle(){static SString s("none"); return s;}
+static SString getDefaultStyle(){return SString("none");}
 MultiRange *mapped;
 enum PartBaseFlags { Selected=1 };
@@ -68,8 +68,7 @@
 {
 friend class Model;
-static const SString& getDefaultStyle();
+static SString getDefaultStyle();
 Part(double _mass,double _size,double _density,double _friction,double _ingest,double _assim)
-	:mass(_mass),size(_size),density(_density),friction(_friction),ingest(_ingest),assim(_assim),
-	PartBase(getDefaultStyle())
+	:PartBase(getDefaultStyle()),mass(_mass),size(_size),density(_density),friction(_friction),ingest(_ingest),assim(_assim)
 	{}
 public:
@@ -78,6 +77,6 @@
 Orient o;  ///< orientation in 3d space (rotation matrix)
 /// ParamInterface object is preferred way to get/set other properties.
-ParamInterface &extraProperties();
-ParamInterface &properties();
+Param extraProperties();
+Param properties();
 long refno;
 Pt3D rot;
@@ -104,7 +103,7 @@
 {
 friend class Model;
-static const SString& getDefaultStyle();
+SString getDefaultStyle();
 Joint(double _stamina,double _stif,double _rotstif,double _d)
-	:stamina(_stamina),stif(_stif),rotstif(_rotstif),PartBase(getDefaultStyle())
+	:PartBase(getDefaultStyle()),stamina(_stamina),stif(_stif),rotstif(_rotstif)
 	{d=Pt3D(_d,0,0);}
 public:
@@ -144,6 +143,6 @@
 
 /// ParamInterface object is preferred way to get/set other properties.
-ParamInterface &extraProperties();
-ParamInterface &properties();
+Param extraProperties();
+Param properties();
 
 // do not touch these:
@@ -182,5 +181,6 @@
 long visualhints;
 
-void *impl;
+//void *impl;
+int impl_count;
 bool active;
 int genactive;
@@ -290,5 +290,5 @@
 {
 friend class Model;
-static const SString& getDefaultStyle();
+static SString getDefaultStyle();
 
 struct NInput { Neuro *n; double weight; SString *info;
@@ -307,6 +307,6 @@
 public:
 enum NeuroFlags { HoldState=2 };
-ParamInterface &properties();
-ParamInterface &extraProperties();
+Param properties();
+Param extraProperties();
 
 void setInputInfo(int i,const SString& name,const SString &value);
Index: /cpp/gdk/multiparamload.cpp
===================================================================
--- /cpp/gdk/multiparamload.cpp	(revision 103)
+++ /cpp/gdk/multiparamload.cpp	(revision 104)
@@ -19,4 +19,29 @@
 breakcond=OnError;
 emptyparam.setParamTab(empty_paramtab);
+}
+
+int MultiParamLoader::findObject(const ExtObject &o)
+{
+for(int i=0;i<objects.size();i++)
+	if ((*objects(i))==o)
+		return i;
+return -1;
+}
+
+void MultiParamLoader::removeObject(const ExtObject &o)
+{
+int i=findObject(o);
+if (i>=0)
+	{
+	delete objects(i);
+	objects-=i;
+	}
+}
+
+void MultiParamLoader::clearObjects()
+{
+FOREACH(ExtObject*,o,objects)
+	delete o;
+objects.clear();
 }
 
@@ -102,7 +127,9 @@
 while (!finished())
 	{
-	if ((status==BeforeObject) || ((status==BeforeUnknown)&&lastclass))
-		{
-		lastclass->load(file);
+	if ((status==BeforeObject) || ((status==BeforeUnknown) && !lastobject.isEmpty()))
+		{
+		Param tmp_param;
+		ParamInterface *pi=lastobject.getParamInterface(tmp_param);
+		pi->load(file);
 		if ((status!=Finished) && maybeBreak(AfterObject))
 			break;
@@ -155,10 +182,10 @@
 		lastunknown=0;
 		lastunknown.append(t,linlen-1);
-		lastclass=0;
-		FOREACH(ParamInterface*,pi,params)
-			{
-			if (!strcmp(pi->getName(),lastunknown)) { lastclass=pi; break; }
-			}
-			if (lastclass)
+		lastobject.setEmpty();
+		FOREACH(ExtObject*,o,objects)
+			{
+			if (!strcmp(o->interfaceName(),lastunknown)) {lastobject=*o; break;}
+			}
+			if (!lastobject.isEmpty())
 				{
 				if (maybeBreak(BeforeObject))
@@ -244,6 +271,8 @@
 }
 
-int MultiParamLoader::loadObjectNow(ParamInterface *pi)
-{
+int MultiParamLoader::loadObjectNow(const ExtObject& o)
+{
+Param tmp_param;
+ParamInterface *pi=o.getParamInterface(tmp_param);
 pi->load(file);
 status=AfterObject;
Index: /cpp/gdk/multiparamload.h
===================================================================
--- /cpp/gdk/multiparamload.h	(revision 103)
+++ /cpp/gdk/multiparamload.h	(revision 104)
@@ -9,4 +9,5 @@
 #include "param.h"
 #include "virtfile.h"
+#include "extvalue.h"
 
 /** This is the general "framsticks-format" file parser for loading multiple objects.
@@ -37,13 +38,12 @@
 SListTempl<VirtFILE*> filestack;
 char ownfile;
-SList params;
+PtrListTempl<ExtObject*> objects;
 int status;
 SString lasterror, lastcomment, lastunknown;
-ParamInterface *lastclass;
+ExtObject lastobject;
 int breakcond;
 Param emptyparam;
 
 void init();
-void load();
 
 int maybeBreak(int cond)
@@ -58,8 +58,9 @@
 MultiParamLoader(const char* filename) {init(); load(filename);}
 
-~MultiParamLoader() {abort();}
+~MultiParamLoader() {abort();clearObjects();}
 
 void reset();
 
+void load(); //< use previously opened file
 void load(VirtFILE *f);
 void load(const char* filename);
@@ -69,7 +70,10 @@
     Note that registered classes will only work when the record name matches the class name, otherwise breakOn(BeforeUnknown) must be used and then getClassName() to check for the expected record.
  */
-void addClass(ParamInterface *pi) {params+=pi;}
-void removeClass(ParamInterface *pi) {params-=pi;}
-void clearClasses() {params.clear();}
+void addObject(ParamInterface *pi) {objects+=new ExtObject(pi);}
+void removeObject(ParamInterface *pi) {removeObject(ExtObject(pi));}
+void addObject(const ExtObject &o) {objects+=new ExtObject(o);}
+void removeObject(const ExtObject &o);
+int findObject(const ExtObject &o);
+void clearObjects();
 
 /** To be used in the main loop: while(event=loader.go()) { ... }
@@ -107,10 +111,11 @@
 
 /** Can be used BeforeObject and AfterObject */
-ParamInterface *getClass() {return lastclass;}
+ExtObject &getObject() {return lastobject;}
 /** Can be used BeforeUnknown, BeforeObject, AfterObject */
-const SString& getClassName() {return lastunknown;}
-const void setClassName(SString n) {lastunknown=n;}
+const SString& getObjectName() {return lastunknown;}
+const void setObjectName(SString n) {lastunknown=n;}
 /** Unknown object will be loaded if you set its class BeforeUnknown */
-void setClass(ParamInterface *pi) {lastclass=pi;}
+void setObject(ParamInterface *pi) {lastobject=ExtObject(pi);}
+void setObject(const ExtObject& o) {lastobject=o;}
 /** Can be used OnComment */
 const SString& getComment() {return lastcomment;}
@@ -118,7 +123,8 @@
 const SString& getError() {return lasterror;}
 /** Can be used BeforeObject and BeforeUnknown */
-int loadObjectNow(ParamInterface *pi);
+int loadObjectNow(ParamInterface *pi) {return loadObjectNow(ExtObject(pi));}
+int loadObjectNow(const ExtObject &o);
 /** Can be used BeforeObject */
-int loadObjectNow() {return loadObjectNow(getClass());}
+int loadObjectNow() {return loadObjectNow(getObject());}
 /** Can be used BeforeObject and BeforeUnknown.
     Object data will not be loaded. */
Index: /cpp/gdk/neurofactory.cpp
===================================================================
--- /cpp/gdk/neurofactory.cpp	(revision 103)
+++ /cpp/gdk/neurofactory.cpp	(revision 104)
@@ -7,7 +7,18 @@
 #include "param.h"
 #include "neuroimpl.h"
+#include "neurolibrary.h"
+
 #include NEURO_IMPL_FILES
 
-int NeuroFactory::used=0;
+NeuroImpl* NeuroFactory::getImplementation(NeuroClass *nc)
+{
+if (nc!=NULL)
+	{
+	std::map<NeuroClass*,NeuroImpl*>::iterator it=impl.find(nc);
+	if (it!=impl.end())
+		return it->second;
+	}
+return NULL;
+}
 
 NeuroImpl* NeuroFactory::createNeuroImpl(NeuroClass *nc)
@@ -15,5 +26,5 @@
 if (!nc) return 0;
 if (!nc->active) return 0;
-NeuroImpl* ni=(NeuroImpl*)nc->impl;
+NeuroImpl* ni=getImplementation(nc);
 if (!ni) return 0;
 ni=ni->makeNew();
@@ -31,6 +42,27 @@
 NeuroImpl* NeuroFactory::setImplementation(NeuroClass *nc,NeuroImpl *ni,bool deleteold)
 {
-NeuroImpl* old_ni=(NeuroImpl*)nc->impl;
-nc->impl=ni;
+if (nc==NULL) return NULL;
+std::map<NeuroClass*,NeuroImpl*>::iterator it=impl.find(nc);
+NeuroImpl* old_ni=NULL;
+if (it==impl.end())
+	{
+	if (ni!=NULL)
+		{
+		impl[nc]=ni;
+		nc->impl_count++;
+		}
+	return NULL;
+	}
+else
+	{
+	old_ni=it->second;
+	if (ni)
+		it->second=ni;
+	else
+		{
+		impl.erase(it);
+		nc->impl_count--;
+		}
+	}
 if (deleteold && old_ni) delete old_ni;
 return old_ni;
@@ -39,21 +71,33 @@
 #include NEURO_CLS_FACTORY
 
-void NeuroFactory::setImplementation()
+void NeuroFactory::setStandardImplementation()
 {
-if (!used)
-	{
-	SETIMPLEMENTATION
-	}
-used++;
+SETIMPLEMENTATION
 }
 
 void NeuroFactory::freeImplementation()
 {
-used--;
-if (!used)
-	{
-	for(int i=0;i<Neuro::getClassCount();i++)
-		setImplementation(Neuro::getClass(i),0);
-	}
+for(int i=0;i<Neuro::getClassCount();i++)
+	setImplementation(Neuro::getClass(i),0);
 }
 
+void NeuroFactory::removeUnimplemented()
+{
+SString removed;
+for(int i=0;i<Neuro::getClassCount();i++)
+	{
+	NeuroClass *nc=Neuro::getClass(i);
+	if (nc->impl_count==0)
+		{
+		removed+=nc->getName();
+		removed+=" ";
+		NeuroLibrary::staticlibrary.classes-=i;	
+		i--;
+		delete nc;
+		}
+	}
+if (removed.len())
+	FMprintf("NeuroFactory","removeUninmplemented",FMLV_INFO,
+	 "Removed Neuro classes: %s",(const char*)removed);
+}
+
Index: /cpp/gdk/neurofactory.h
===================================================================
--- /cpp/gdk/neurofactory.h	(revision 103)
+++ /cpp/gdk/neurofactory.h	(revision 104)
@@ -7,4 +7,5 @@
 
 #include "modelparts.h"
+#include <map>
 
 class NeuroImpl;
@@ -12,14 +13,16 @@
 class NeuroFactory
 {
+std::map<NeuroClass*,NeuroImpl*> impl;
 public:
-static NeuroImpl* setImplementation(NeuroClass *nc,NeuroImpl *ni,bool deleteold=1);
-static NeuroImpl* setImplementation(const SString& classname,NeuroImpl *ni,bool deleteold=1);
-static class NeuroImpl* createNeuroImpl(Neuro *n)
+NeuroImpl* setImplementation(NeuroClass *nc,NeuroImpl *ni,bool deleteold=1);
+NeuroImpl* setImplementation(const SString& classname,NeuroImpl *ni,bool deleteold=1);
+NeuroImpl* getImplementation(NeuroClass *nc);
+class NeuroImpl* createNeuroImpl(Neuro *n)
 	{NeuroClass *nc=n->getClass(); if (!nc) return 0; return createNeuroImpl(nc);}
-static class NeuroImpl* createNeuroImpl(NeuroClass *nc);
+class NeuroImpl* createNeuroImpl(NeuroClass *nc);
 
-static int used;
-static void setImplementation();
-static void freeImplementation();
+void setStandardImplementation();
+void freeImplementation();
+void removeUnimplemented();
 };
 
Index: /cpp/gdk/neuroimpl.cpp
===================================================================
--- /cpp/gdk/neuroimpl.cpp	(revision 103)
+++ /cpp/gdk/neuroimpl.cpp	(revision 104)
@@ -32,16 +32,11 @@
 #undef FIELDSTRUCT
 
-NeuroNetConfig::NeuroNetConfig()
+NeuroNetConfig::NeuroNetConfig(NeuroFactory *fac)
 	:par(nncfg_paramtab,this),
 	 randominit(0.01),
 	 nnoise(0),
-	 touchrange(1)
+	 touchrange(1),
+	 factory(fac)
 {}
-
-NeuroNetConfig& NeuroNetConfig::getGlobalConfig()
-{
-static NeuroNetConfig globalconfig;
-return globalconfig;
-}
 
 /////////////////////////////////////////////////////////////////
@@ -74,5 +69,5 @@
 for (i=0;n=mod.getNeuro(i);i++)
 	{
-	ni=NeuroFactory::createNeuroImpl(n);
+	ni=conf.factory->createNeuroImpl(n);
 	n->userdata[mytags_id]=ni;
 	if (!ni) { errorcount++;
@@ -205,10 +200,10 @@
 		chstate=chnewstate;
 	neuro->state=newstate;
-	if (NeuroNetConfig::getGlobalConfig().nnoise>0.0)
+	if (owner->getConfig().nnoise>0.0)
 		{
-		neuro->state+=RndGen.GaussStd()*NeuroNetConfig::getGlobalConfig().nnoise;
+		neuro->state+=RndGen.GaussStd()*owner->getConfig().nnoise;
 		if (channels>1)
 			for(int i=0;i<chstate.size();i++)
-				chstate(0)+=RndGen.GaussStd()*NeuroNetConfig::getGlobalConfig().nnoise;
+				chstate(0)+=RndGen.GaussStd()*owner->getConfig().nnoise;
 		}
 	}
@@ -272,5 +267,5 @@
 void NeuroImpl::readParam()
 {
-static Param par;
+Param par;
 if (!paramentries) return;
 par.setParamTab(paramentries);
Index: /cpp/gdk/neuroimpl.h
===================================================================
--- /cpp/gdk/neuroimpl.h	(revision 103)
+++ /cpp/gdk/neuroimpl.h	(revision 104)
@@ -17,9 +17,11 @@
 
 class Creature;
+class NeuroLibrary;
+class NeuroFactory;
 
 class NeuroNetConfig
 {
 public:
-NeuroNetConfig();
+NeuroNetConfig(NeuroFactory *fac);
 
 Param par;
@@ -28,5 +30,6 @@
 double touchrange;
 
-static NeuroNetConfig& getGlobalConfig();
+NeuroFactory *factory;
+//static NeuroNetConfig& getGlobalConfig();
 };
 
@@ -40,5 +43,5 @@
   public:
 
-NeuroSignals(NeuroImpl *n):owner(n),cr(0) {}
+NeuroSignals(NeuroImpl *n):cr(0),owner(n) {}
 
 #define STATRICKCLASS NeuroSignals
@@ -74,5 +77,6 @@
 static double getStateFromNeuro(Neuro *n);
 int getErrorCount() {return errorcount;}
-NeuroNetImpl(Model& model, NeuroNetConfig& conf = NeuroNetConfig::getGlobalConfig()
+NeuroNetConfig &getConfig() {return config;}
+NeuroNetImpl(Model& model, NeuroNetConfig& conf
 #ifdef NEURO_SIGNALS
 , ChannelSpace *ch=0
@@ -198,16 +202,16 @@
 /** don't access directly */
 double newstate;
+NeuroNetImpl *owner;
+/** will be used by readParam() method, if not null  */
+ParamEntry *paramentries; // no extra properties if ==0
 
 #ifdef NEURO_SIGNALS
 NeuroSignals sigs;
-#endif
-NeuroNetImpl *owner;
 ExtObject sigs_obj;
+#endif
 
 /** "virtual constructor" - NeuroFactory uses this method to create the proper implementation object.
     subclasses must return new object here. */
 virtual NeuroImpl* makeNew() {return 0;} // 
-/** will be used by readParam() method, if not null  */
-ParamEntry *paramentries; // no extra properties if ==0
 /** read additional properties from "moredata" field of the originating Neuro */
 void readParam();
@@ -247,5 +251,5 @@
 void validateNeuroState(double& st) {if (st<=-1e10) st=-1e10; else if (st>1e10) st=1e10;}
 
-NeuroImpl():owner(0),neuro(0),newstate(0),paramentries(0),simorder(1),status(BeforeInit),channels(1),fields_param(0),fields_object(0)
+NeuroImpl():simorder(1),channels(1),fields_param(0),fields_object(0),status(BeforeInit),neuro(0),newstate(0),owner(0),paramentries(0)
 #ifdef NEURO_SIGNALS
 ,sigs(this),sigs_obj(&NeuroSignals::getStaticParam(),&sigs)
Index: /cpp/gdk/neurolibrary.cpp
===================================================================
--- /cpp/gdk/neurolibrary.cpp	(revision 103)
+++ /cpp/gdk/neurolibrary.cpp	(revision 104)
@@ -25,4 +25,10 @@
 if (i<0) return 0;
 return getClass(i);
+}
+
+SString NeuroLibrary::getClassName(int classindex)
+{
+NeuroClass *cl=getClass(classindex);
+return cl? cl->getName() : SString();
 }
 
Index: /cpp/gdk/neurolibrary.h
===================================================================
--- /cpp/gdk/neurolibrary.h	(revision 103)
+++ /cpp/gdk/neurolibrary.h	(revision 104)
@@ -22,4 +22,5 @@
 int findClassIndex(const SString& classname, bool activeonly=1);
 NeuroClass* getClass(int classindex) {return (NeuroClass*)classes(classindex);}
+SString getClassName(int classindex);
 int getClassCount() {return classes.size();}
 void removeClass(int i);
Index: /cpp/gdk/neurotest.cpp
===================================================================
--- /cpp/gdk/neurotest.cpp	(revision 103)
+++ /cpp/gdk/neurotest.cpp	(revision 104)
@@ -80,6 +80,8 @@
 if (!m.getNeuroCount()) {puts("no neural network");return 1;}
 printf("%d neurons,",m.getNeuroCount());
-NeuroFactory::setImplementation();
-NeuroNetImpl *nn=new NeuroNetImpl(m);
+NeuroFactory neurofac;
+neurofac.setStandardImplementation();
+NeuroNetConfig nn_config(&neurofac);
+NeuroNetImpl *nn=new NeuroNetImpl(m,nn_config);
 int i; Neuro *n;
 if (!nn->getErrorCount()) printf(" no errors\n");
@@ -138,4 +140,4 @@
 	nn->simulateNeuroNet();
 	}
-NeuroFactory::freeImplementation();
+neurofac.freeImplementation();
 }
Index: /cpp/gdk/param.cpp
===================================================================
--- /cpp/gdk/param.cpp	(revision 103)
+++ /cpp/gdk/param.cpp	(revision 104)
@@ -231,4 +231,6 @@
 }
 
+const char* ParamInterface::SERIALIZATION_PREFIX="@Serialized:";
+
 int ParamInterface::saveprop(VirtFILE* f,int i,const char* p,bool force)
 {
@@ -243,5 +245,12 @@
 err|=(fputs(p,f)==EOF); fputc(':',f);
 cr=0;
-ws=get(i);
+if (*typ=='x')
+	{
+	ExtValue ex;
+	get(i,ex);
+	ws=SString(SERIALIZATION_PREFIX)+ex.serialize();
+	}
+else
+	ws=get(i);
 quoteTilde(ws);
 w=ws;
@@ -497,8 +506,14 @@
 	{
 	ExtValue e;
-	if (isdigit(*v)||((*v=='-')&&isdigit(v[1])))
-		{
-		if (strchr(v,'.')) e.setDouble(atof(v));
-		else e.setInt(atol(v));
+	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():"<Unknown>"),id(i));
+		}
+	else if ((after=e.parseNumber(v))&&(*after==0)) //consumed the whole string
+		{
+		//OK!
 		}
 	else
@@ -566,5 +581,5 @@
 long SimpleAbstractParam::getInt(int i)
 {
-static ExtValue v;
+ExtValue v;
 ParamEntry *pe=entry(i);
 if (pe->fun1)
@@ -582,5 +597,5 @@
 double SimpleAbstractParam::getDouble(int i)
 {
-static ExtValue v;
+ExtValue v;
 ParamEntry *pe=entry(i);
 if (pe->fun1)
@@ -598,5 +613,5 @@
 SString SimpleAbstractParam::getString(int i)
 {
-static ExtValue v;
+ExtValue v;
 ParamEntry *pe=entry(i);
 if (pe->fun1)
@@ -614,5 +629,5 @@
 ExtObject SimpleAbstractParam::getObject(int i)
 {
-static ExtValue v;
+ExtValue v;
 ParamEntry *pe=entry(i);
 if (pe->fun1)
@@ -630,5 +645,5 @@
 ExtValue SimpleAbstractParam::getExtValue(int i)
 {
-static ExtValue v;
+ExtValue v;
 ParamEntry *pe=entry(i);
 if (pe->fun1)
@@ -649,8 +664,8 @@
 int SimpleAbstractParam::setInt(int i,long x)
 {
-static ExtValue v;
+ExtValue v;
 ParamEntry *pe=entry(i);
 if (pe->flags&PARAM_READONLY) return PSET_RONLY;
-long xcopy=x; //only needed for helpful printed messages: retain original, requested value of x because it may be changed below
+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;
@@ -684,8 +699,8 @@
 int SimpleAbstractParam::setDouble(int i,double x)
 {
-static ExtValue v;
+ExtValue v;
 ParamEntry *pe=entry(i);
 if (pe->flags&PARAM_READONLY) return PSET_RONLY;
-double xcopy=x; //only needed for helpful printed messages: retain original, requested value of x because it may be changed below
+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;
@@ -719,10 +734,10 @@
 int SimpleAbstractParam::setString(int i,const SString& x)
 {
-static ExtValue v;
-static SString vs;
+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 helpful printed messages: retain original, requested value of x because it may be changed below
+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++;
@@ -759,8 +774,8 @@
 int SimpleAbstractParam::setObject(int i,const ExtObject& x)
 {
-static ExtValue v;
+ExtValue v;
 ParamEntry *pe=entry(i);
 if (pe->flags&PARAM_READONLY) return PSET_RONLY;
-ExtObject xcopy=x; //only needed for helpful printed messages: retain original, requested value of x because it may be changed below
+ExtObject xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
 if (pe->fun2)
 	{
Index: /cpp/gdk/param.h
===================================================================
--- /cpp/gdk/param.h	(revision 103)
+++ /cpp/gdk/param.h	(revision 104)
@@ -151,4 +151,6 @@
 void load(VirtFILE*);
 void load2(const SString &,int &);
+
+static const char* SERIALIZATION_PREFIX;
 };
 
@@ -221,5 +223,6 @@
 	@param n Param's name
 */
-SimpleAbstractParam(void* o=0,const char*n=0):object(o),myname(n),dontcheckchanges(0) {}
+SimpleAbstractParam(void* o=0,const char*n=0):myname(n),dontcheckchanges(0),object(o) {}
+void setDontCheckChanges(bool x) {dontcheckchanges=x;}
 
 void select(void *o) {object=o;}
Index: /cpp/gdk/serialtest.cpp
===================================================================
--- /cpp/gdk/serialtest.cpp	(revision 104)
+++ /cpp/gdk/serialtest.cpp	(revision 104)
@@ -0,0 +1,93 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "collectionobj.h"
+
+void printIndent(int indent)
+{
+for(int i=0;i<indent;i++)
+	putchar(' ');
+}
+
+struct Trace
+{
+ExtObject object;
+Trace *previous;
+bool isAlreadyVisited(const ExtObject& o)
+	{
+	if (object==o) return true;
+	if (previous) return previous->isAlreadyVisited(o);
+	return false;
+	}
+};
+
+void print(ExtValue* v,int indent=0,Trace *prev_trace=NULL)
+{
+printIndent(indent);
+if (!v)
+	{puts("null"); return;}
+
+switch(v->getType())
+	{
+	case TUnknown: puts("null"); return;
+	case TInt: printf("int:"); break;
+	case TDouble: printf("double:"); break;
+	case TString: printf("string:"); break;
+	case TObj:
+		{
+		Trace next_trace={v->getObject(),prev_trace};
+		printf("%s object:",v->getObject().interfaceName());
+		if (prev_trace && prev_trace->isAlreadyVisited(next_trace.object))
+			{
+			printf(" already visited - breaking recursion\n");
+			return;
+			}
+		VectorObject *vec=VectorObject::fromObject(v->getObject());
+		if (vec)
+			{
+			printf("\n");
+			for(int i=0;i<vec->data.size();i++)
+				print((ExtValue*)vec->data(i),indent+3,&next_trace);
+			return;
+			}
+		DictionaryObject *dict=DictionaryObject::fromObject(v->getObject());
+		if (dict)
+			{
+			printf("\n");
+			for(HashEntryIterator it(dict->hash);it.isValid();it++)
+				{
+				printIndent(indent+3);
+				printf("key \"%s\"\n",(const char*)it->key);
+				print((ExtValue*)it->value,indent+6,&next_trace);
+				}
+			return;
+			}
+		}
+	}
+puts((const char*)v->getString());
+}
+
+int main(int argc,char*argv[])
+{
+const char* in= (argc>1) ? argv[1] : "[1,2,3,null,{\"x\":3.4,\"y\":[7,77,777]},4]";
+
+// note: this is not an example of how to read a param field, but only a special case where the input data was
+// previously identified as "serialized" and the "@Serialized:" prefix was already removed
+
+printf("DESERIALIZATION TEST\n"
+       "input string = %s\n",in);
+ExtValue v;
+const char* ret=v.deserialize(in);
+if (ret==NULL)
+	{
+	printf("failed!\n");
+	return 1;
+	}
+else
+	{
+	printf("OK, consumed %d of %d characters\n\n",ret-in,strlen(in));
+	print(&v);
+	return 0;
+	}
+}
Index: /cpp/gdk/sstring.cpp
===================================================================
--- /cpp/gdk/sstring.cpp	(revision 103)
+++ /cpp/gdk/sstring.cpp	(revision 104)
@@ -7,4 +7,14 @@
 #include "nonstd_stl.h"
 #include "extvalue.h"
+
+#ifdef MULTITHREADED
+#include <pthread.h>
+static pthread_mutex_t sstring_ref_lock;
+#define REF_LOCK pthread_mutex_lock(&sstring_ref_lock);
+#define REF_UNLOCK pthread_mutex_unlock(&sstring_ref_lock)
+#else
+#define REF_LOCK 
+#define REF_UNLOCK 
+#endif
 
 static int guessMemSize(int request, int memhint)
@@ -96,5 +106,7 @@
 SString::~SString()
 {
+REF_LOCK;
 detach();
+REF_UNLOCK;
 }
 
@@ -102,4 +114,5 @@
 {
 buf=new SBuf(x);
+memhint=-1;
 }
 
@@ -113,6 +126,14 @@
 SString::SString(const SString &from)
 {
-buf=from.buf;
-if (buf->size) buf->refcount++;
+if (from.buf==&SBuf::empty())
+	buf=&SBuf::empty();
+else
+	{
+	REF_LOCK;
+	buf=from.buf;
+	if (buf->size)
+		buf->refcount++;
+	REF_UNLOCK;
+	}
 memhint=-1;
 }
@@ -163,5 +184,7 @@
 {
 if (ensuresize<0) ensuresize=len();
+REF_LOCK;
 detachCopy(ensuresize);
+REF_UNLOCK;
 appending=buf->used;
 return buf->txt;
@@ -176,5 +199,7 @@
 char *SString::directAppend(int maxappend)
 {
+REF_LOCK;
 detachCopy(buf->used+maxappend);
+REF_UNLOCK;
 appending=buf->used;
 return buf->txt+appending;
@@ -208,5 +233,7 @@
 {
 if (!count) return;
+REF_LOCK;
 detachCopy(buf->used+count);
+REF_UNLOCK;
 buf->append(txt,count);
 }
@@ -230,5 +257,7 @@
 if (!ch) chlen=0;
 else if (chlen<0) chlen=strlen(ch);
+REF_LOCK;
 detachEmpty(chlen);
+REF_UNLOCK;
 memcpy(buf->txt,ch,chlen);
 buf->txt[chlen]=0; buf->used=chlen;
@@ -243,7 +272,9 @@
 {
 if (s.buf==buf) return;
+REF_LOCK;
 detach();
 buf=s.buf;
 if (buf->size) buf->refcount++;
+REF_UNLOCK;
 }
 ///////////////////////////////////////
@@ -306,23 +337,23 @@
 }
 
-const SString& SString::valueOf(int i)
-{
-static SString t;
+SString SString::valueOf(int i)
+{
+SString t;
 sprintf(t.directWrite(20),"%d",i); t.endWrite();
 return t;
 }
-const SString& SString::valueOf(long i)
-{
-static SString t;
+SString SString::valueOf(long i)
+{
+SString t;
 sprintf(t.directWrite(20),"%d",i); t.endWrite();
 return t;
 }
-const SString& SString::valueOf(double d)
-{
-static SString t;
+SString SString::valueOf(double d)
+{
+SString t;
 sprintf(t.directWrite(20),"%g",d); t.endWrite();
 return t;
 }
-const SString& SString::valueOf(const SString& s)
+SString SString::valueOf(const SString& s)
 {
 return s;
Index: /cpp/gdk/sstring.h
===================================================================
--- /cpp/gdk/sstring.h	(revision 103)
+++ /cpp/gdk/sstring.h	(revision 104)
@@ -157,8 +157,8 @@
 char charAt(int pos) const {return buf->txt[pos];}
 
-static const SString& valueOf(int);
-static const SString& valueOf(long);
-static const SString& valueOf(double);
-static const SString& valueOf(const SString&); //tylko do kompletu zeby mozna uzyc tej funkcji nie martwiac sie o typ argumentu
+static SString valueOf(int);
+static SString valueOf(long);
+static SString valueOf(double);
+static SString valueOf(const SString&); //tylko do kompletu zeby mozna uzyc tej funkcji nie martwiac sie o typ argumentu
 static SString valueOf(const ExtValue&); //tylko do kompletu zeby mozna uzyc tej funkcji nie martwiac sie o typ argumentu
 static SString valueOf(const ExtObject&); //tylko do kompletu zeby mozna uzyc tej funkcji nie martwiac sie o typ argumentu
Index: /cpp/gdk/stderrors.cpp
===================================================================
--- /cpp/gdk/stderrors.cpp	(revision 104)
+++ /cpp/gdk/stderrors.cpp	(revision 104)
@@ -0,0 +1,34 @@
+// 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 "stderrors.h"
+#include "sstringutils.h"
+#include "framsg.h"
+
+bool listIndexCheck(SList* list,int index,char* msgobj,char* msgfun)
+{
+	int size=list->size();
+if ((index<0)||(index>=size))
+	{
+	if (size>0) 
+		FMprintf(msgobj,msgfun,FMLV_ERROR,"Invalid index %d (allowed range is 0..%d)",index,size-1);
+	else
+		FMprintf(msgobj,msgfun,FMLV_ERROR,"Invalid index %d (this list is empty)",index);
+	return 0;
+	}
+return 1;
+}
+
+SString stringCheck(SString& in,char* msgobj,char* msgfun,char* msg,SString (*checker)(SString& in))
+{
+if (!checker)
+	checker=trim;
+SString corrected=checker(in);
+if (corrected!=in)
+	{
+	SString msg2=SString(msg)+": \"%s\" (adjusted to \"%s\")";
+	FMprintf(msgobj,msgfun,FMLV_WARN,(const char*)msg2,(const char*)in,(const char*)corrected);
+	}
+return corrected;
+}
Index: /cpp/gdk/stderrors.h
===================================================================
--- /cpp/gdk/stderrors.h	(revision 104)
+++ /cpp/gdk/stderrors.h	(revision 104)
@@ -0,0 +1,14 @@
+// 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.
+
+#ifndef _STDERRORS_H_
+#define _STDERRORS_H_
+
+#include "list.h"
+#include "sstring.h"
+
+bool listIndexCheck(SList* list,int index,char* msgobj,char* msgfun);
+SString stringCheck(SString& in,char* msgobj,char* msgfun,char* msg,SString (*checker)(SString& in)=0);
+
+#endif
Index: /cpp/gdk/stdouterr.cpp
===================================================================
--- /cpp/gdk/stdouterr.cpp	(revision 103)
+++ /cpp/gdk/stdouterr.cpp	(revision 104)
@@ -1,4 +1,4 @@
 // This file is a part of the Framsticks GDK library.
-// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Copyright (C) 2002-2013  Szymon Ulatowski.  See LICENSE.txt for details.
 // Refer to http://www.framsticks.com/ for further information.
 
@@ -12,13 +12,12 @@
 void StdoutErrorHandler::handle(const char *o,const char *m,const char *bl,int w)
 {
-static char* level[]={"DEBUG","INFO","WARN","ERROR","CRITICAL"};
 if (w<-1) w=-1; else if (w>3) w=3;
 #ifdef SHP
-AppLog("[%s] %s::%s - %s\n",level[w+1],o,m,bl);
+AppLog("[%s] %s::%s - %s\n",MSG_LEVEL[w+1],o,m,bl);
 #else
 if (file)
-	file->printf("[%s] %s::%s - %s\n",level[w+1],o,m,bl);
+	file->printf("[%s] %s::%s - %s\n",MSG_LEVEL[w+1],o,m,bl);
 else
-	printf("[%s] %s::%s - %s\n",level[w+1],o,m,bl);
+	printf("[%s] %s::%s - %s\n",MSG_LEVEL[w+1],o,m,bl);
 #endif
 }
Index: /cpp/gdk/threads.h
===================================================================
--- /cpp/gdk/threads.h	(revision 104)
+++ /cpp/gdk/threads.h	(revision 104)
@@ -0,0 +1,60 @@
+#ifndef _THREADS_H_
+#define _THREADS_H_
+
+#ifdef MULTITHREADED
+
+#include <pthread.h>
+
+template<class T> class ThreadSingleton
+{
+pthread_key_t mt_key;
+
+  public:
+
+ThreadSingleton()
+ {
+ pthread_key_create(&mt_key,&destructor);
+ }
+
+static void destructor(void* o)
+ {
+ if (o)
+	 delete (T*)o;
+ }
+
+T* set(T* new_o)
+ {
+ T* o=(T*)pthread_getspecific(mt_key);
+ pthread_setspecific(mt_key,new_o);
+ return o;
+ }
+
+T* get()
+ {
+ T* o=(T*)pthread_getspecific(mt_key);
+ if (!o)
+	 {
+	 o=new T();
+	 pthread_setspecific(mt_key,o);
+	 }
+ return o;
+ }
+
+T& getref() {return *get();}
+};
+
+#else
+
+template<class T> class ThreadSingleton
+{
+T object;
+
+  public:
+
+T* get() {return &object;}
+T& getref() {return object;}
+};
+
+#endif
+
+#endif
