Index: cpp/frams/Makefile-SDK-files
===================================================================
--- cpp/frams/Makefile-SDK-files	(revision 954)
+++ cpp/frams/Makefile-SDK-files	(revision 955)
@@ -2,5 +2,5 @@
 
 # ALL_DIRS is later expanded by the shell, no spaces/newlines allowed, or it breaks
-ALL_DIRS={common,PrintFloat,frams,frams/canvas,frams/config,common/loggers,frams/genetics,frams/genetics/f0,frams/genetics/f1,frams/genetics/f2,frams/genetics/f3,frams/genetics/f4,frams/genetics/f5,frams/genetics/f6,frams/genetics/f7,frams/genetics/f8,frams/genetics/f9,frams/genetics/fn,frams/genetics/fF,frams/genetics/fT,frams/genetics/fB,frams/genetics/fH,frams/genetics/fL,frams/model,frams/neuro,frams/neuro/impl,frams/param,frams/test,frams/util,frams/vm/classes,common/virtfile,frams/_demos,frams/model/geometry,frams/_demos/geometry,frams/model/similarity,frams/model/similarity/hungarian,frams/model/similarity/SVD}
+ALL_DIRS={common,PrintFloat,frams,frams/canvas,frams/config,common/loggers,frams/genetics,frams/genetics/f0,frams/genetics/f1,frams/genetics/f2,frams/genetics/f3,frams/genetics/f4,frams/genetics/f5,frams/genetics/f6,frams/genetics/f7,frams/genetics/f8,frams/genetics/f9,frams/genetics/fn,frams/genetics/fF,frams/genetics/fT,frams/genetics/fB,frams/genetics/fH,frams/genetics/fL,frams/genetics/fS,frams/model,frams/neuro,frams/neuro/impl,frams/param,frams/test,frams/util,frams/vm/classes,common/virtfile,frams/_demos,frams/model/geometry,frams/_demos/geometry,frams/model/similarity,frams/model/similarity/hungarian,frams/model/similarity/SVD}
 
 GENMANF4=frams/genetics/f4/f4_oper.o
@@ -12,4 +12,5 @@
 GENMANFH=frams/genetics/fH/fH_oper.o frams/genetics/fH/fH_general.o
 GENMANFL=frams/genetics/fL/fL_oper.o frams/genetics/fL/fL_general.o frams/genetics/fL/fL_matheval.o
+GENMANFS=frams/genetics/fS/fS_oper.o frams/genetics/fS/fS_general.o
 
 CONVF1=frams/genetics/f1/f1_conv.o frams/genetics/geneprops.o
@@ -21,8 +22,9 @@
 CONVFH=frams/genetics/fH/fH_conv.o frams/genetics/fH/fH_general.o frams/param/mutableparam.o
 CONVFL=frams/genetics/fL/fL_conv.o frams/genetics/fL/fL_general.o frams/genetics/fL/fL_matheval.o
+CONVFS=frams/genetics/fS/fS_conv.o frams/genetics/fS/fS_general.o 
 
 # $(sort - remove duplicates
-GENOCONV_SDK_OBJS=$(sort frams/genetics/defgenoconv.o frams/vm/classes/genoobj.o frams/model/autoname.o $(CONVF1) $(CONVF4) $(CONVF9) $(CONVFF) $(CONVFN) $(CONVFB) $(CONVFH) $(CONVFL))
-GENMAN_SDK_OBJS=$(GENMAN_COMMON_OBJS) $(GENMANF4) $(GENMANF9) $(GENMANFF) $(GENMANFT) $(GENMANFN) $(GENMANFB) $(GENMANFH) $(GENMANFL)
+GENOCONV_SDK_OBJS=$(sort frams/genetics/defgenoconv.o frams/vm/classes/genoobj.o frams/model/autoname.o $(CONVF1) $(CONVF4) $(CONVF9) $(CONVFF) $(CONVFN) $(CONVFB) $(CONVFH) $(CONVFL) $(CONVFS))
+GENMAN_SDK_OBJS=$(GENMAN_COMMON_OBJS) $(GENMANF4) $(GENMANF9) $(GENMANFF) $(GENMANFT) $(GENMANFN) $(GENMANFB) $(GENMANFH) $(GENMANFL) $(GENMANFS)
 GENOCONV_AND_GENMAN_SDK_OBJS=$(sort $(GENOCONV_SDK_OBJS) $(GENMAN_SDK_OBJS))
 
Index: cpp/frams/_demos/f0_variants_test.cpp
===================================================================
--- cpp/frams/_demos/f0_variants_test.cpp	(revision 954)
+++ cpp/frams/_demos/f0_variants_test.cpp	(revision 955)
@@ -95,6 +95,6 @@
 Geno g(gen);
 printf("\nSource genotype: '%s'\n",g.getGenes().c_str());
-printf("                  ( format %c %s)\n",
-       g.getFormat(), g.getComment().c_str());
+printf("                  ( format %s %s)\n",
+       g.getFormat().c_str(), g.getComment().c_str());
 
 Model m(g);//.getConverted('0'));
Index: cpp/frams/_demos/genoconv_test.cpp
===================================================================
--- cpp/frams/_demos/genoconv_test.cpp	(revision 954)
+++ cpp/frams/_demos/genoconv_test.cpp	(revision 955)
@@ -66,5 +66,5 @@
 	{
 		name = "Test Converter #3";
-		in_format = 'z';
+		in_format = "multiply";
 		out_format = '1';
 		mapsupport = 1;
@@ -112,11 +112,11 @@
 void printGen(Geno &g)
 {
-	printf("Genotype:\n%s\nFormat: %c\nValid: %s\nComment: %s\n",
-		g.getGenes().c_str(), g.getFormat(), g.isValid() ? "yes" : "no", g.getComment().c_str());
-}
-
-static int goodWidthForFormat(int genotype_format)
-{
-	return genotype_format == '0' ? 45 : 15; // more space for long f0 lines
+	printf("Genotype:\n%s\nFormat: %s\nValid: %s\nComment: %s\n",
+	       g.getGenes().c_str(), g.getFormat().c_str(), g.isValid() ? "yes" : "no", g.getComment().c_str());
+}
+
+static int goodWidthForFormat(const SString& genotype_format)
+{
+	return genotype_format == "0" ? 45 : 15; // more space for long f0 lines
 }
 
@@ -154,5 +154,5 @@
 	else
 		src = "X";
-	char dst = (argc > 2) ? *argv[2] : '0';
+	SString dst = (argc > 2) ? *argv[2] : '0';
 	bool using_checkpoints = (argc > 3) ? (strcmp(argv[3], "checkpoints") == 0) : false;
 
@@ -162,5 +162,5 @@
 	MultiMap m;
 	Geno g2 = g1.getConverted(dst, &m, using_checkpoints);
-	printf("*** Converted to f%c:\n", dst);
+	printf("*** Converted to f%s:\n", dst.c_str());
 	printGen(g2);
 
@@ -168,7 +168,7 @@
 	{ // using Model with checkpoints
 		Model m1(g2, false, true);//true=using_checkpoints
-		printf("\nModel built from the converted f%c genotype has %d checkpoints\n", g2.getFormat(), m1.getCheckpointCount());
+		printf("\nModel built from the converted f%s genotype has %d checkpoints\n", g2.getFormat().c_str(), m1.getCheckpointCount());
 		Model m2(g1, false, true);//true=using_checkpoints
-		printf("Model built from the source f%c genotype has %d checkpoints\n", g1.getFormat(), m2.getCheckpointCount());
+		printf("Model built from the source f%s genotype has %d checkpoints\n", g1.getFormat().c_str(), m2.getCheckpointCount());
 		// accessing individual checkpoint models (if available)
 		if (m1.getCheckpointCount() > 0)
@@ -196,5 +196,5 @@
 
 		Model mod1(g1, 1);
-		printf("\nModel map for f%c genotype:\n", g1.getFormat());
+		printf("\nModel map for f%s genotype:\n", g1.getFormat().c_str());
 		ModelDisplayMap dm1(mod1);
 		dm1.print(goodWidthForFormat(g1.getFormat()));
@@ -203,5 +203,5 @@
 		mod1combined.print();
 		Model mod2(g2, 1);
-		printf("\nModel map for f%c genotype:\n", g2.getFormat());
+		printf("\nModel map for f%s genotype:\n", g2.getFormat().c_str());
 		ModelDisplayMap dm2(mod2);
 		dm2.print(goodWidthForFormat(g2.getFormat()));
Index: cpp/frams/_demos/genomanipulation.cpp
===================================================================
--- cpp/frams/_demos/genomanipulation.cpp	(revision 954)
+++ cpp/frams/_demos/genomanipulation.cpp	(revision 955)
@@ -258,5 +258,5 @@
 	if (gc) printf("found converter accepting f1: \"%s\"\n", gc->name);
 	SListTempl<GenoConverter*> found;
-	Geno::getConverters()->findConverters(&found, -1, '0');
+	Geno::getConverters()->findConverters(&found, Geno::UNKNOWN_FORMAT, '0');
 	printf("found %d converter(s) producing f0\n", found.size());
 }
@@ -281,6 +281,6 @@
 	Geno g(gen);
 	printf("\nSource genotype: '%s'\n", g.getGenes().c_str());
-	printf("                  ( format %c %s)\n",
-		g.getFormat(), g.getComment().c_str());
+	printf("                  ( format %s %s)\n",
+		g.getFormat().c_str(), g.getComment().c_str());
 
 	Model m(g);//.getConverted('0'));
Index: cpp/frams/_demos/genooper_test.cpp
===================================================================
--- cpp/frams/_demos/genooper_test.cpp	(revision 954)
+++ cpp/frams/_demos/genooper_test.cpp	(revision 955)
@@ -8,6 +8,6 @@
 void printGen(Geno &g)
 {
-	printf("Genotype: %s\nFormat: %c\nValid: %s\nComment: %s\n",
-		g.getGenes().c_str(), g.getFormat(), g.isValid() ? "yes" : "no", g.getComment().len() == 0 ? "(empty)" : g.getComment().c_str());
+	printf("Genotype: %s\nFormat: %s\nValid: %s\nComment: %s\n",
+	        g.getGenes().c_str(), g.getFormat().c_str(), g.isValid() ? "yes" : "no", g.getComment().len() == 0 ? "(empty)" : g.getComment().c_str());
 }
 
@@ -30,5 +30,5 @@
 	Geno gsrc(src, -1, "First");
 	printGenAndTitle(gsrc, "source genotype (gsrc)");
-	char format = gsrc.getFormat();
+	SString format = gsrc.getFormat();
 
 	Geno gmut = genetics.genman.mutate(gsrc);
@@ -41,5 +41,5 @@
 	printGenAndTitle(gsimplest, "simplest");
 
-	Geno ginvalid("IT'S REALLY WRONG", format);
+	Geno ginvalid("IT'S REALLY WRONG", format.c_str());
 	printGenAndTitle(ginvalid, "invalid");
 
Index: cpp/frams/_demos/multiline_f0_test.cpp
===================================================================
--- cpp/frams/_demos/multiline_f0_test.cpp	(revision 954)
+++ cpp/frams/_demos/multiline_f0_test.cpp	(revision 955)
@@ -27,6 +27,6 @@
 Geno g(gen);
 printf("\nSource genotype: '%s'\n",g.getGenes().c_str());
-printf("                  ( format %c %s)\n",
-       g.getFormat(), g.getComment().c_str());
+printf("                  ( format %s %s)\n",
+       g.getFormat().c_str(), g.getComment().c_str());
 
 Model m(g);//.getConverted('0'));
Index: cpp/frams/genetics/f4/f4_general.cpp
===================================================================
--- cpp/frams/genetics/f4/f4_general.cpp	(revision 954)
+++ cpp/frams/genetics/f4/f4_general.cpp	(revision 955)
@@ -1304,6 +1304,6 @@
 	unsigned int len;
 	// build in a SString, with initial size
-	SString out(strlen(buf) + 2000);
-	out = "";
+	SString out;
+	out.reserve(int(strlen(buf)) + 2000);
 
 	sprint(out);
Index: cpp/frams/genetics/genman.cpp
===================================================================
--- cpp/frams/genetics/genman.cpp	(revision 954)
+++ cpp/frams/genetics/genman.cpp	(revision 955)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -68,4 +68,7 @@
 #ifdef USE_GENMAN_fL
 #include "fL/fL_oper.h"
+#endif
+#ifdef USE_GENMAN_fS
+#include "fS/fS_oper.h"
 #endif
 
@@ -177,4 +180,7 @@
 	oper_fx_list.push_back(new Geno_fL);
 #endif
+#ifdef USE_GENMAN_fS
+	oper_fx_list.push_back(new GenoOper_fS);
+#endif
 
 	seloper = new int[oper_fx_list.size()]; //may result in a little overhead if some of the operators on the oper_fx_list concern the same genetic format
@@ -182,5 +188,5 @@
 	for (unsigned int i = 0; i < oper_fx_list.size(); i++)
 	{
-		if (operformats.find(oper_fx_list[i]->supported_format) != -1) continue;
+		if (findOperFormatIndex(oper_fx_list[i]->supported_format) != -1) continue;
 		string type = string("~") + oper_fx_list[i]->name;
 		int dup = 0;
@@ -188,15 +194,15 @@
 			if (oper_fx_list[i]->supported_format == oper_fx_list[j]->supported_format)
 			{
-			type += "~";
-			type += oper_fx_list[j]->name;
-			dup++;
+				type += "~";
+				type += oper_fx_list[j]->name;
+				dup++;
 			}
 		type = ssprintf("d 0 %d ", dup) + type;
-		string id = ssprintf("genoper_f%c", oper_fx_list[i]->supported_format);
-		string name = ssprintf("Operators for f%c", oper_fx_list[i]->supported_format);
+		string id = ssprintf("genoper_f%s", oper_fx_list[i]->supported_format.c_str());
+		string name = ssprintf("Operators for f%s", oper_fx_list[i]->supported_format.c_str());
 		seloper[selopercount] = 0;
-		operformats += oper_fx_list[i]->supported_format;
+		operformats += &oper_fx_list[i]->supported_format;
 		//printf("%x %s %s %s\n",&seloper[selopercount],(const char*)id,(const char*)type,(const char*)name);
-		seloperpar.addProperty(&seloper[selopercount++], id.c_str(), type.c_str(), name.c_str(), "", PARAM_READONLY*(dup == 0));
+		seloperpar.addProperty(&seloper[selopercount++], id.c_str(), type.c_str(), name.c_str(), "", PARAM_READONLY * (dup == 0));
 	}
 
@@ -214,4 +220,12 @@
 	for (unsigned int i = 0; i < oper_fx_list.size(); i++) delete oper_fx_list[i];
 	delete[] seloper;
+}
+
+int GenMan::findOperFormatIndex(const SString& format)
+{
+	for (int i = 0; i < operformats.size(); i++)
+		if (*operformats(i) == format)
+			return i;
+	return -1;
 }
 
@@ -265,8 +279,8 @@
 Geno GenMan::validate(const Geno& geny)
 {
-	char format = geny.getFormat();
+	SString format = geny.getFormat();
 	GenoOperators *gf = getOper_f(format);
 	if (gf == NULL)
-		return Geno(SString::empty(), -1, SString::empty(), SString::sprintf("GENOPER_NOOPER: Validate(): don't know how to handle genetic format %c", format));
+		return Geno("", Geno::INVALID_FORMAT, "", SString::sprintf("GENOPER_NOOPER: Validate(): don't know how to handle genetic format %s", format.c_str()));
 	char *g2 = strdup(geny.getGenes().c_str()); //copy for validation
 	int res = gf->validate(g2, geny.getName().c_str());
@@ -276,5 +290,5 @@
 		return Geno(sg2, format, geny.getName(), geny.getComment());
 	else
-		return Geno(SString::empty(), -1, SString::empty(), SString::sprintf("GENOPER_NOOPER: validate() for format %c returned invalid value", format));
+		return Geno("", Geno::INVALID_FORMAT, "", SString::sprintf("GENOPER_NOOPER: validate() for format %s returned invalid value", format.c_str()));
 }
 
@@ -283,12 +297,12 @@
 	float chg; //how many changes
 	int method; //mutation method
-	char format = g.getFormat();
+	SString format = g.getFormat();
 	GenoOperators *gf = getOper_f(format);
 	if (gf == NULL)
-		return Geno(SString::empty(), -1, SString::empty(), SString::sprintf("GENOPER_NOOPER: Mutate(): don't know how to handle genetic format %c", format));
+		return Geno("", Geno::INVALID_FORMAT, "", SString::sprintf("GENOPER_NOOPER: Mutate(): don't know how to handle genetic format %s", format.c_str()));
 	Geno gv = g;
 	bool canvalidate = true;
 	if (testValidity(gv, canvalidate) > 0 && canvalidate == false)
-		return Geno("", -1, "", "GENOPER_OPFAIL: Mutate(): cannot validate invalid source genotype");
+		return Geno("", Geno::INVALID_FORMAT, "", "GENOPER_OPFAIL: Mutate(): cannot validate invalid source genotype");
 	bool ok = false;
 	int pcount = count;
@@ -307,5 +321,5 @@
 				if (res > 0 && canvalidate == false) invalid_m++; else
 				{
-				validated_m++; ok = true;
+					validated_m++; ok = true;
 				}
 			if (ok) gv = G;
@@ -316,5 +330,5 @@
 		if (!ok && (count - pcount > GENMAN_REPEAT_FAILED))
 		{
-			logPrintf("GenMan", "Mutate", 2, "Tried " GENMAN_REPEAT_FAILED_STR "x and failed: %s", g.getGenes().c_str());
+			logPrintf("GenMan", "Mutate", LOG_WARN, "Tried " GENMAN_REPEAT_FAILED_STR "x and failed: %s", g.getGenes().c_str());
 			return Geno("", -1, "", "GENOPER_OPFAIL: Mutate() tried " GENMAN_REPEAT_FAILED_STR "x and failed");
 		}
@@ -332,9 +346,9 @@
 Geno GenMan::crossOver(const Geno& g1, const Geno& g2)
 {
-	char format = g1.getFormat();
-	if (format != g2.getFormat()) return Geno(SString::empty(), -1, SString::empty(), SString::sprintf("GENOPER_NOOPER: CrossOver(): does not work for parents with differing genetic formats (%c and %c)", format, g2.getFormat()));
+	SString format = g1.getFormat();
+	if (format != g2.getFormat()) return Geno("", Geno::INVALID_FORMAT, "", SString::sprintf("GENOPER_NOOPER: CrossOver(): does not work for parents with differing genetic formats (%s and %s)", format.c_str(), g2.getFormat().c_str()));
 	GenoOperators *gf = getOper_f(format);
 	if (gf == NULL)
-		return Geno(SString::empty(), -1, SString::empty(), SString::sprintf("GENOPER_NOOPER: CrossOver(): no operators found for genetic format %c", format));
+		return Geno("", Geno::INVALID_FORMAT, "", SString::sprintf("GENOPER_NOOPER: CrossOver(): no operators found for genetic format %s", format.c_str()));
 	Geno g1v = g1, g2v = g2;
 
@@ -343,8 +357,8 @@
 		bool canvalidate = true;
 		if (testValidity(g1v, canvalidate) > 0 && canvalidate == false)
-			return Geno("", -1, "", "GENOPER_OPFAIL: CrossOver(): cannot validate invalid source genotype #1");
+			return Geno("", Geno::INVALID_FORMAT, "", "GENOPER_OPFAIL: CrossOver(): cannot validate invalid source genotype #1");
 		canvalidate = true;
 		if (testValidity(g2v, canvalidate) > 0 && canvalidate == false)
-			return Geno("", -1, "", "GENOPER_OPFAIL: CrossOver(): cannot validate invalid source genotype #2");
+			return Geno("", Geno::INVALID_FORMAT, "", "GENOPER_OPFAIL: CrossOver(): cannot validate invalid source genotype #2");
 	}
 
@@ -373,5 +387,5 @@
 				if (res > 0 && canvalidate == false) invalid_xo++; else
 				{
-				validated_xo++; ok = true;
+					validated_xo++; ok = true;
 				}
 			if (ok) g1v = G;
@@ -383,6 +397,6 @@
 		if (!ok && (count - pcount > GENMAN_REPEAT_FAILED))
 		{
-			logPrintf("GenMan", "CrossOver", 2, "Tried " GENMAN_REPEAT_FAILED_STR "x and failed: %s and %s", g1.getGenes().c_str(), g2.getGenes().c_str());
-			return Geno("", -1, "", "GENOPER_OPFAIL: CrossOver() tried " GENMAN_REPEAT_FAILED_STR "x and failed");
+			logPrintf("GenMan", "CrossOver", LOG_WARN, "Tried " GENMAN_REPEAT_FAILED_STR "x and failed: %s and %s", g1.getGenes().c_str(), g2.getGenes().c_str());
+			return Geno("", Geno::INVALID_FORMAT, "", "GENOPER_OPFAIL: CrossOver() tried " GENMAN_REPEAT_FAILED_STR "x and failed");
 		}
 	}
@@ -398,5 +412,5 @@
 float GenMan::similarity(const Geno& g1, const Geno& g2)
 {
-	char format = g1.getFormat();
+	SString format = g1.getFormat();
 	if (format != g2.getFormat()) return GENOPER_NOOPER;
 	GenoOperators *gf = getOper_f(format);
@@ -406,5 +420,5 @@
 uint32_t GenMan::getStyle(const char *g, const Geno *G, int pos)
 {
-	char format = G->getFormat();
+	SString format = G->getFormat();
 	if (format == Geno::INVALID_FORMAT)
 		return GENSTYLE_RGBS(64, 64, 64, 0); // gray & "valid" (unknown format so we don't know what is valid and what is not)
@@ -423,5 +437,5 @@
 void GenMan::getFullStyle(const char *g, const Geno *G, uint32_t *styletab)
 {
-	char format = G->getFormat();
+	SString format = G->getFormat();
 	if (format == Geno::INVALID_FORMAT)
 	{
@@ -438,5 +452,5 @@
 		else if (!gf) styletab[pos] = GENSTYLE_CS(0, 0); //black & valid
 		else styletab[pos] = gf->style(geny.c_str(), posmapped);
-		//logPrintf("GenMan", "getFullStyle", 0, "%d  char='%c' (%d)  format=0x%08x", pos, g[pos], g[pos], styletab[pos]);
+		//logPrintf("GenMan", "getFullStyle", LOG_INFO, "%d  char='%c' (%d)  format=0x%08x", pos, g[pos], g[pos], styletab[pos]);
 	}
 }
@@ -473,15 +487,15 @@
 		color = GENGETCOLOR(styletab[i]);
 		if ((i != 0 && (color != prevcolor))) html += "</font>";
-		if ((style&GENSTYLE_INVALID) != (prevstyle&GENSTYLE_INVALID))
-		{
-			html += "<"; if (!(style&GENSTYLE_INVALID)) html += "/"; html += "u>";
-		}
-		if ((style&GENSTYLE_BOLD) != (prevstyle&GENSTYLE_BOLD))
-		{
-			html += "<"; if (!(style&GENSTYLE_BOLD)) html += "/"; html += "b>";
-		}
-		if ((style&GENSTYLE_ITALIC) != (prevstyle&GENSTYLE_ITALIC))
-		{
-			html += "<"; if (!(style&GENSTYLE_ITALIC)) html += "/"; html += "i>";
+		if ((style & GENSTYLE_INVALID) != (prevstyle & GENSTYLE_INVALID))
+		{
+			html += "<"; if (!(style & GENSTYLE_INVALID)) html += "/"; html += "u>";
+		}
+		if ((style & GENSTYLE_BOLD) != (prevstyle & GENSTYLE_BOLD))
+		{
+			html += "<"; if (!(style & GENSTYLE_BOLD)) html += "/"; html += "b>";
+		}
+		if ((style & GENSTYLE_ITALIC) != (prevstyle & GENSTYLE_ITALIC))
+		{
+			html += "<"; if (!(style & GENSTYLE_ITALIC)) html += "/"; html += "i>";
 		}
 		if ((i == 0 || (color != prevcolor)))
@@ -509,9 +523,9 @@
 }
 
-Geno GenMan::getSimplest(char format)
+Geno GenMan::getSimplest(const SString& format)
 {
 	GenoOperators *gf = getOper_f(format);
 	if (!gf) return Geno();
-	string info = "The simplest genotype of format f"; info += format;
+	string info = "The simplest genotype of format f"; info += format.c_str();
 	info += " for operators '"; info += gf->name; info += "'.";
 	return Geno(gf->getSimplest(), format, "Root", info.c_str());
@@ -520,5 +534,5 @@
 void GenMan::p_getsimplest(ExtValue *args, ExtValue *ret)
 {
-	int format = GenoObj::formatFromExtValue(args[0]);
+	SString format = GenoObj::formatFromExtValue(args[0]);
 	if (!getOper_f(format))
 		ret->setEmpty();
@@ -527,5 +541,5 @@
 }
 
-const char *GenMan::getOpName(char format)
+const char *GenMan::getOpName(const SString& format)
 {
 	GenoOperators *gf = getOper_f(format);
@@ -533,7 +547,7 @@
 }
 
-GenoOperators* GenMan::getOper_f(char format)
-{
-	int ind = operformats.find(format);
+GenoOperators* GenMan::getOper_f(const SString& format)
+{
+	int ind = findOperFormatIndex(format);
 	if (ind == -1) return NULL;
 	int which_oper_of_format = seloper[ind];
@@ -602,6 +616,6 @@
 		}
 		//      if (oper_fx_list[i]->similarity("","")!=GENOPER_NOOPER) l+=" similarity";
-		logPrintf("GenMan", "Report", 0, "format f%c (%s):%s",
-			oper_fx_list[i]->supported_format, oper_fx_list[i]->name.c_str(), l.c_str());
+		logPrintf("GenMan", "Report", LOG_INFO, "format f%s (%s):%s",
+			oper_fx_list[i]->supported_format.c_str(), oper_fx_list[i]->name.c_str(), l.c_str());
 	}
 }
Index: cpp/frams/genetics/genman.h
===================================================================
--- cpp/frams/genetics/genman.h	(revision 954)
+++ cpp/frams/genetics/genman.h	(revision 955)
@@ -52,12 +52,13 @@
 	string HTMLize(const char *g); //returns colored genotype in HTML.
 	string HTMLizeShort(const char *g); //returns colored genotype (abbreviated if needed) in HTML.
-	Geno getSimplest(char format); ///<returns pointer to the simplest genotype of \e format or empty Geno()
-	const char *getOpName(char format); ///<returns pointer to the active operator set for \e format
+	Geno getSimplest(const SString& format); ///<returns pointer to the simplest genotype of \e format or empty Geno()
+	const char *getOpName(const SString& format); ///<returns pointer to the active operator set for \e format
 	const vector<GenoOperators*>& GetOperators() const { return oper_fx_list; } ///<returns the list of available genetic operators
 private:
 	vector<GenoOperators*> oper_fx_list;
 	void saveLink(const string parent1, const string parent2, const string child, const float chg);
-	GenoOperators* getOper_f(char format);
+	GenoOperators* getOper_f(const SString& format);
 	string HTMLize(const char *g, bool shorten);
+	int findOperFormatIndex(const SString& format);
 public:
 	vector<GenoLink> GenoLinkList;
@@ -68,5 +69,5 @@
 	int valid_m, valid_xo, validated_m, validated_xo, invalid_m, invalid_xo, failed_m, failed_xo;
 	double mutchg, xochg;
-	SListTempl<char> operformats; //the list of supported_format, in the same order as in seloperpar
+	SListTempl<SString*> operformats; //the list of supported_format, in the same order as in seloperpar (raw SString* taken from oper_fx_list, not owned by operformats)
 	int* seloper; //fields for seloperpar
 	Param localpar, localstats;
Index: cpp/frams/genetics/geno.cpp
===================================================================
--- cpp/frams/genetics/geno.cpp	(revision 954)
+++ cpp/frams/genetics/geno.cpp	(revision 955)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -24,5 +24,8 @@
 }
 
-void Geno::init(const SString& genstring, char genformat, const SString& genname, const SString& comment)
+const SString Geno::INVALID_FORMAT = "invalid";
+const SString Geno::UNKNOWN_FORMAT = "";
+
+void Geno::init(const SString& genstring, const SString& genformat, const SString& genname, const SString& comment)
 {
 	refcount = 1;
@@ -35,12 +38,21 @@
 }
 
-void Geno::setGenesAndFormat(const SString& genstring, char genformat)
+static SString trimAndValidateFormat(const SString& input) //the new requirement for genotype format name: no whitespace
+{
+	SString format = trim(input);
+	if (format.len() == 0 || strContainsOneOf(format.c_str(), " \r\n\t"))
+		return Geno::INVALID_FORMAT;
+	return format;
+}
+
+void Geno::setGenesAndFormat(const SString& genstring, const SString& in_genformat)
 {
 	mapinshift = 0;
 	mapoutshift = 0;
 	SString gencopy(genstring);
-	if (genformat == -1)
+	SString genformat = in_genformat;
+	if (genformat == UNKNOWN_FORMAT)
 	{ // unknown format
-		genformat = '1';
+		genformat = "1";
 		if (genstring.charAt(0) == '/')
 		{
@@ -49,26 +61,24 @@
 			{
 			case '/':
-				genformat = genstring.charAt(2);
 				if ((end = genstring.indexOf('\n')) >= 0)
 				{
+					genformat = trimAndValidateFormat(genstring.substr(2, end - 2));
 					mapinshift = end + 1;
 					gencopy = genstring.substr(end + 1);
 					if ((end > 0) && (genstring[end - 1] == '\r')) end--;
 					error_end = end;
-					if (end != 3) genformat = INVALID_FORMAT;
 				}
 				else
 				{
-					if (genstring.len() != 3) genformat = INVALID_FORMAT;
-					gencopy = 0;
+					genformat = trimAndValidateFormat(genstring.substr(2));
+					gencopy = "";
 					mapinshift = genstring.len();
 				}
 				break;
 			case '*':
-				genformat = genstring.charAt(2);
 				if ((end = genstring.indexOf("*/")) >= 0)
 				{
+					genformat = trimAndValidateFormat(genstring.substr(2, end - 2));
 					error_end = end + 2;
-					if (end != 3) genformat = INVALID_FORMAT;
 					gencopy = genstring.substr(end + 2);
 					mapinshift = end + 2;
@@ -76,17 +86,16 @@
 				else
 				{
-					if (genstring.len() != 5) genformat = INVALID_FORMAT;
-					gencopy = 0;
+					genformat = trimAndValidateFormat(genstring.substr(2));
+					gencopy = "";
 					mapinshift = genstring.len();
 				}
 				break;
 			}
-			if (!isalnum(genformat)) genformat = INVALID_FORMAT;
 			if (genformat == INVALID_FORMAT)
 			{
 				SString cut;
-				if (error_end<0) error_end = genstring.len();
+				if (error_end < 0) error_end = genstring.len();
 				static const int MAX_ERROR = 20;
-				if (error_end>MAX_ERROR)
+				if (error_end > MAX_ERROR)
 					cut = genstring.substr(0, MAX_ERROR) + "...";
 				else
@@ -113,10 +122,18 @@
 }
 
+Geno::Geno(const char *genstring, const char* genformat, const char *genname, const char *comment)
+{
+	init(SString(genstring), SString(genformat), SString(genname), SString(comment));
+}
+
 Geno::Geno(const char *genstring, char genformat, const char *genname, const char *comment)
 {
-	init(SString(genstring), genformat, SString(genname), SString(comment));
-}
-
-Geno::Geno(const SString& genstring, char genformat, const SString& genname, const SString& comment)
+	SString genformat_string;
+	if (genformat > 0)
+		genformat_string = SString(&genformat, 1);
+	init(genstring, genformat_string, genname, comment);
+}
+
+Geno::Geno(const SString& genstring, const SString& genformat, const SString& genname, const SString& comment)
 {
 	init(genstring, genformat, genname, comment);
@@ -148,5 +165,5 @@
 Geno::Geno(const SString& src)
 {
-	init(src, -1, SString::empty(), SString::empty());
+	init(src, UNKNOWN_FORMAT, SString::empty(), SString::empty());
 }
 
@@ -161,5 +178,5 @@
 {
 	freeF0();
-	init(g, -1, SString::empty(), SString::empty());
+	init(g, UNKNOWN_FORMAT, SString::empty(), SString::empty());
 }
 
@@ -177,5 +194,5 @@
 {
 	SString out;
-	if (format != '1')
+	if (format != "1")
 	{
 		if (multiline)
@@ -183,8 +200,5 @@
 		else
 			out += "/*";
-		if (format == 0)
-			out += "invalid";
-		else
-			out += format;
+		out += format;
 		if (multiline)
 			out += "\n";
@@ -213,10 +227,10 @@
 SString Geno::getGenes(void) const { return gen; }
 SString Geno::getName(void) const { return name; }
-char Geno::getFormat(void) const { return format; }
+SString Geno::getFormat(void) const { return format; }
 SString Geno::getComment(void) const { return txt; }
 
 int ModelGenoValidator::testGenoValidity(Geno& g)
 {
-	if (g.getFormat() == '0')
+	if (g.getFormat() == "0")
 	{
 		Model mod(g);
@@ -226,5 +240,5 @@
 	{
 		bool converter_missing;
-		Geno f0geno = g.getConverted('0', NULL, false, &converter_missing);
+		Geno f0geno = g.getConverted("0", NULL, false, &converter_missing);
 		if (converter_missing)
 			return -1;//no result
@@ -243,26 +257,26 @@
 #ifdef WARN_VALIDATION_INCONSISTENCY
 		vector<int> results;
-		int first_result=-1;
+		int first_result = -1;
 		FOREACH(GenoValidator*, v, (*vals))
 		{
-			int r=v->testGenoValidity(*this);
-			if (first_result<0) first_result=r;
+			int r = v->testGenoValidity(*this);
+			if (first_result < 0) first_result = r;
 			results.push_back(r);
 		}
-		int N=vals->size();
-		for(int i=1;i<N;i++)
-			if (results[i]!=results[0])
+		int N = vals->size();
+		for (int i = 1; i < N; i++)
+			if (results[i] != results[0])
 			{
-			SString txt="Inconsistent validation results";
-			for(int i=0;i<N;i++)
-				txt+=SString::sprintf(" %d",results[i]);
-			txt+=" for genotype '";
-			txt+=getGene();
-			txt+="'";
-			logPrintf("Geno","validate",LOG_WARN,txt.c_str());
-			break;
+				SString txt = "Inconsistent validation results";
+				for (int i = 0; i < N; i++)
+					txt += SString::sprintf(" %d", results[i]);
+				txt += " for genotype '";
+				txt += getGene();
+				txt += "'";
+				logPrintf("Geno", "validate", LOG_WARN, txt.c_str());
+				break;
 			}
-		isvalid=first_result;
-		if (isvalid>=0)
+		isvalid = first_result;
+		if (isvalid >= 0)
 			return;
 #else
@@ -273,5 +287,5 @@
 	}
 	isvalid = 0;
-	logPrintf("Geno", "validate", LOG_WARN, "Wrong configuration? No genotype validators defined for genetic format 'f%c'.", format);
+	logPrintf("Geno", "validate", LOG_WARN, "Wrong configuration? No genotype validators defined for genetic format 'f%s'.", format.c_str());
 }
 
@@ -295,5 +309,5 @@
 }
 
-Geno Geno::getConverted(char otherformat, MultiMap *m, bool using_checkpoints, bool *converter_missing)
+Geno Geno::getConverted(SString otherformat, MultiMap *m, bool using_checkpoints, bool *converter_missing)
 {
 	if (otherformat == getFormat()) { if (converter_missing) *converter_missing = false; return *this; }
@@ -302,5 +316,5 @@
 	if (converters)
 	{
-		if ((otherformat == '0') && (!m) && (!using_checkpoints))
+		if ((otherformat == "0") && (!m) && (!using_checkpoints))
 		{
 			if (!f0gen)
@@ -317,5 +331,5 @@
 #endif
 	if (converter_missing) *converter_missing = true;
-	return (otherformat == getFormat()) ? *this : Geno(0, 0, 0, "GenConvManager not available");
+	return (otherformat == getFormat()) ? *this : Geno("", "", "", "GenConvManager not available");
 }
 
Index: cpp/frams/genetics/geno.h
===================================================================
--- cpp/frams/genetics/geno.h	(revision 954)
+++ cpp/frams/genetics/geno.h	(revision 955)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -34,5 +34,5 @@
 	SString gen;
 	SString name;
-	char format;
+	SString format;
 	SString txt;
 	int isvalid; ///< <0 -> unknown   >=0 -> value for "isValid"
@@ -44,5 +44,5 @@
 	int multiline;
 
-	void init(const SString& genstring, char genformat, const SString& genname, const SString& comment);
+	void init(const SString& genstring, const SString& genformat, const SString& genname, const SString& comment);
 	void validate(void);
 
@@ -55,5 +55,6 @@
 
 public:
-	static const char INVALID_FORMAT = '!';
+	static const SString INVALID_FORMAT;
+	static const SString UNKNOWN_FORMAT;
 	typedef SListTempl<GenoValidator*> Validators;
 
@@ -62,5 +63,7 @@
 	/// @param genformat genotype format
 	/// @param comment information about genotype (for genetic operators and "history")
-	Geno(const char *genstring = 0, char genformat = -1, const char *genname = 0, const char *comment = 0);
+	Geno(const char *genstring = 0, const char* genformat = 0, const char *genname = 0, const char *comment = 0);
+
+	Geno(const char *genstring, char genformat, const char *genname = 0, const char *comment = 0); //old style Geno, accepts char genoformat
 
 	/// create a genotype object from primitives
@@ -69,5 +72,5 @@
 	/// @param name genotype name, new name will generated if needed
 	/// @param comment information about genotype (for genetic operators and "history")
-	Geno(const SString& genstring, char genformat, const SString& genname, const SString& comment);
+	Geno(const SString& genstring, const SString& genformat, const SString& genname, const SString& comment);
 
 	/// create object from full string, containing optional format and comment information
@@ -89,6 +92,6 @@
 	void setString(const SString& genewithcomments);
 
-	/** @param genformat=-1 -> detect genotype format from genstring comment (like the constructor does), else specify the valid format in genformat and pure genes in genstring. */
-	void setGenesAndFormat(const SString& genstring, char genformat = -1);
+	/** @param genformat="" -> detect genotype format from genstring comment (like the constructor does), else specify the valid format in genformat and pure genes in genstring. */
+	void setGenesAndFormat(const SString& genstring, const SString& genformat = UNKNOWN_FORMAT);
 	/** g must be pure genes, without format. For the standard behavior use setGenesAndFormat() */
 	void setGenesAssumingSameFormat(const SString& g);
@@ -97,5 +100,5 @@
 	SString getName(void) const;
 	void setName(const SString&);
-	char getFormat(void) const;
+	SString getFormat(void) const;
 
 	SString getComment(void) const;
@@ -107,5 +110,5 @@
 	/// make converted version of the genotype.
 	/// @param converter_missing optional output parameter (ignored when NULL). Receives true if the conversion fails because of the lack of appropriate converter(s) (the returned Geno is always invalid in this case). Receives false if the genotype was converted by a converter or a converter chain (the returned Geno can be valid or invalid, depending on the converter's decision).
-	Geno getConverted(char otherformat, MultiMap *m = 0, bool using_checkpoints = false, bool *converter_missing = NULL);
+	Geno getConverted(SString otherformat, MultiMap *m = 0, bool using_checkpoints = false, bool *converter_missing = NULL);
 
 	/// @return -1 = before first char in the string
Index: cpp/frams/genetics/genoconv.cpp
===================================================================
--- cpp/frams/genetics/genoconv.cpp	(revision 954)
+++ cpp/frams/genetics/genoconv.cpp	(revision 955)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -57,7 +57,7 @@
 		pe->flags = 0;
 		std::string descr = "f";
-		descr += gk->in_format;
+		descr += gk->in_format.c_str();
 		descr += " --> f";
-		descr += gk->out_format;
+		descr += gk->out_format.c_str();
 		descr += "  :  ";
 		descr += gk->name;
@@ -101,5 +101,5 @@
 }
 
-GenoConverter *GenoConvManager::findConverters(SListTempl<GenoConverter*>* result, char in, char out, int enabled, char* name)
+GenoConverter *GenoConvManager::findConverters(SListTempl<GenoConverter*>* result, const SString& in, const SString& out, int enabled, char* name)
 {
 	GenoConverter *gk, *retval = 0;
@@ -107,6 +107,6 @@
 	for (; gk = (GenoConverter*)converters(i); i++)
 	{
-		if ((in != -1) && (in != gk->in_format)) continue;
-		if ((out != -1) && (out != gk->out_format)) continue;
+		if ((in != Geno::UNKNOWN_FORMAT) && (in != gk->in_format)) continue;
+		if ((out != Geno::UNKNOWN_FORMAT) && (out != gk->out_format)) continue;
 		if ((enabled != -1) && (enabled != gk->enabled)) continue;
 		if ((name) && (strcmp(name, gk->name))) continue;
@@ -123,5 +123,5 @@
 /// (can be NULL if you don't need this information)
 
-char *GenoConvManager::getPath(char in, char out, char *path, int maxlen, int *mapavailable)
+GenoConverter **GenoConvManager::getPath(const SString& in, const SString& out, GenoConverter **path, int maxlen, int *mapavailable)
 {
 	if (!maxlen) return 0;
@@ -132,5 +132,5 @@
 		if ((gk->enabled) && (gk->in_format == in))
 		{
-			*path = (char)i;
+			*path = gk;
 			if (gk->out_format == out)
 			{
@@ -142,5 +142,5 @@
 			{
 				int mapavail;
-				char *ret = getPath(gk->out_format, out, path + 1, maxlen - 1, &mapavail);
+				GenoConverter **ret = getPath(gk->out_format, out, path + 1, maxlen - 1, &mapavail);
 				if (ret)
 				{
@@ -155,29 +155,18 @@
 }
 
-char *GenoConvManager::getFormatPath(char in, char out, char *path, int maxlen, int *mapavailable)
-{
-	char *ret = getPath(in, out, path, maxlen, mapavailable);
-	if (ret)
-	{
-		for (char*t = path; t <= ret; t++)
-			*t = ((GenoConverter*)converters(*t))->out_format;
-	}
-	return ret;
-}
-
-Geno GenoConvManager::convert(Geno &in, char format, MultiMap *map, bool using_checkpoints, bool *converter_missing)
+Geno GenoConvManager::convert(Geno &in, SString format, MultiMap *map, bool using_checkpoints, bool *converter_missing)
 {
 	if (in.getFormat() == format) { if (converter_missing) *converter_missing = false; return in; }
-	char path[10];
+	GenoConverter *path[10];
 	int dep;
-	char *ret;
-	if (in.isInvalid()) { if (converter_missing) *converter_missing = false; return Geno("", 0, "", "invalid genotype cannot be converted"); }
+	GenoConverter **ret;
+	if (in.isInvalid()) { if (converter_missing) *converter_missing = false; return Geno("", Geno::INVALID_FORMAT, "", "invalid genotype cannot be converted"); }
 	int mapavail;
 	for (dep = 1; dep < (int)sizeof(path); dep++) //iterative deepening
 		if (ret = getPath(in.getFormat(), format, path, dep, &mapavail)) break;
-	if (!ret) { if (converter_missing) *converter_missing = true; return Geno("", 0, "", "converter not found"); }
+	if (!ret) { if (converter_missing) *converter_missing = true; return Geno("", Geno::INVALID_FORMAT, "", "converter not found"); }
 	if (converter_missing) *converter_missing = false;
 	if (!map) mapavail = 0;
-	char *t = path;
+	GenoConverter **t = path;
 	SString tmp;
 	tmp = in.getGenes();
@@ -186,10 +175,10 @@
 	for (; t <= ret; t++)
 	{
-		GenoConverter *gk = (GenoConverter*)converters(*t);
+		GenoConverter *gk = *t;
 		tmp = gk->convert(tmp, mapavail ? &tmpmap : 0, using_checkpoints);
 		if (!tmp.len())
 		{
-			string t = ssprintf("f%c->f%c conversion failed (%s)", gk->in_format, gk->out_format, gk->name);
-			return Geno(0, 0, 0, t.c_str());
+			string t = ssprintf("f%s->f%s conversion failed (%s)", gk->in_format.c_str(), gk->out_format.c_str(), gk->name);
+			return Geno("", Geno::INVALID_FORMAT, "", t.c_str());
 		}
 		if (mapavail)
Index: cpp/frams/genetics/genoconv.h
===================================================================
--- cpp/frams/genetics/genoconv.h	(revision 954)
+++ cpp/frams/genetics/genoconv.h	(revision 955)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -43,6 +43,6 @@
 public:
 	const char *name;	//< converter name (short)
-	char in_format,		//< input format, eg. '1'
-		out_format;	//< output format, eg. '0'
+	SString in_format,	//< input format, eg. "1"
+		out_format;	//< output format, eg. "0"
 	paInt enabled;	//< don't touch this! (used by configuration module)
 	paInt mapsupport; //< set to 1 if your converter supports genotype mapping
@@ -56,5 +56,5 @@
 	virtual ~GenoConverter() {}
 	/// Don't forget to set public fields in your constructor
-	GenoConverter() :name(""), in_format(-1), out_format('0'), enabled(1), mapsupport(0) {}
+	GenoConverter() :name(""), in_format(Geno::UNKNOWN_FORMAT), out_format("0"), enabled(1), mapsupport(0) {}
 };
 
@@ -77,5 +77,5 @@
 	/// make a genotype in other format. genotype will be invalid
 	/// if GenoConvManager cannot convert it.
-	Geno convert(Geno &in, char format, MultiMap *map = 0, bool using_checkpoints = false, bool *converter_missing = NULL);
+	Geno convert(Geno &in, SString format, MultiMap *map = 0, bool using_checkpoints = false, bool *converter_missing = NULL);
 	/// register GenoConverter, the added object will be automatically deleted when GenoConvManager is destructed (call removeConverter() if this is not desirable)
 	void addConverter(GenoConverter *conv);
@@ -83,10 +83,9 @@
 	void removeConverter(GenoConverter *conv);
 
-	char *getPath(char in, char out, char *path, int maxlen, int *mapavailable = 0);
-	char *getFormatPath(char in, char out, char *path, int maxlen, int *mapavailable = 0);
+	GenoConverter **getPath(const SString& in, const SString& out, GenoConverter **path, int maxlen, int *mapavailable = 0);
 	/// returns the list of converters meeting the specified criteria
 	/// pass result=0 if you only need one result (by return value)
 	/// default criteria values mean "don't care", pass anything else to narrow your search
-	GenoConverter *findConverters(SListTempl<GenoConverter*>* result = 0, char in = -1, char out = -1, int enabled = -1, char* name = 0);
+	GenoConverter *findConverters(SListTempl<GenoConverter*>* result = 0, const SString& in = Geno::UNKNOWN_FORMAT, const SString& out = Geno::UNKNOWN_FORMAT, int enabled = -1, char* name = 0);
 };
 
Index: cpp/frams/genetics/genooperators.cpp
===================================================================
--- cpp/frams/genetics/genooperators.cpp	(revision 954)
+++ cpp/frams/genetics/genooperators.cpp	(revision 955)
@@ -76,5 +76,5 @@
 }
 
-int GenoOperators::selectRandomProperty(Neuro *n)
+int GenoOperators::selectRandomNeuProperty(Neuro *n)
 {
 	int neuext = n->extraProperties().getPropCount(),
Index: cpp/frams/genetics/genooperators.h
===================================================================
--- cpp/frams/genetics/genooperators.h	(revision 954)
+++ cpp/frams/genetics/genooperators.h	(revision 955)
@@ -81,8 +81,8 @@
 public:
 	Param par;
-	char supported_format; ///<genotype format which is supported by this class ('6' for GenoOper_f6, 'F' for GenoOper_fF, etc.). Must be initialized in constructor
-	string name; ///<name of this set of genetic operators
+	SString supported_format; ///<genotype format which is supported by this class ("6" for GenoOper_f6, "Latent" for GenoOper_fLatent, etc.). Must be initialized in constructor.
+	string name; ///<short human-friendly name of this genetic representation/set of genetic operators
 	const char **mutation_method_names; ///<array of names for mutation methods. If initialized (by new const char*[]), must have entries for each method index returned by mutate(geno,chg,METHOD).  If initialized, it is automatically freed by this destructor.
-	GenoOperators() : par(empty_paramtab) { supported_format = 'x'; name = "Default"; mutation_method_names = NULL; setDefaults(); }
+	GenoOperators() : par(empty_paramtab) { supported_format = "x"; name = "Default"; mutation_method_names = NULL; setDefaults(); }
 
 	/**Used to perform initializations of Param parameters that are not handled by the Param itself
@@ -182,5 +182,5 @@
 	static int roulette(const double *probtab, const int count); 	///<returns random index according to probabilities in the \e probtab table or -1 if all probs are zero. \e count is the number of elements in \e probtab.
 	static bool getMinMaxDef(ParamInterface *p, int propindex, double &mn, double &mx, double &def); ///<perhaps a more useful (higher-level) way to obtain min/max/def info for integer and double properties. Returns true if min/max/def was really available (otherwise it is just invented).
-	static int selectRandomProperty(Neuro* n); ///<selects random property (either 0-based extraproperty of Neuro or 100-based property of its NeuroClass). -1 if Neuro has no properties.
+	static int selectRandomNeuProperty(Neuro* n); ///<selects random property (either 0-based extraproperty of Neuro or 100-based property of its NeuroClass). -1 if Neuro has no properties.
 	static double mutateNeuProperty(double current, Neuro *n, int propindex); ///<returns value \e current mutated for the property \e propindex of NeuroClass \e nc or for extraproperty (\e propindex - 100) of Neuro. Neuro is used as read-only. Give \e propindex == -1 to mutate connection weight (\e nc is then ignored).
 	static bool mutatePropertyNaive(ParamInterface &p, int propindex); ///<creep-mutate selected property. Returns true when success. mutateProperty() should be used instead of this function.
Index: cpp/frams/model/model.cpp
===================================================================
--- cpp/frams/model/model.cpp	(revision 954)
+++ cpp/frams/model/model.cpp	(revision 955)
@@ -333,5 +333,5 @@
 	f0warnposition = -1;
 	MultiMap *convmap = autobuildmaps ? new MultiMap() : NULL;
-	f0geno = (geno.getFormat() == '0') ? geno : geno.getConverted('0', convmap, using_checkpoints);
+	f0geno = (geno.getFormat() == "0") ? geno : geno.getConverted("0", convmap, using_checkpoints);
 	f0genoknown = 1;
 	if (f0geno.isInvalid())
@@ -437,5 +437,5 @@
 	if ((buildstatus != valid) && (buildstatus != building))
 	{
-		g = Geno(0, 0, 0, "invalid model");
+		g = Geno("", Geno::INVALID_FORMAT, "", "invalid model");
 		return;
 	}
Index: cpp/frams/util/sstring-simple.cpp
===================================================================
--- cpp/frams/util/sstring-simple.cpp	(revision 954)
+++ cpp/frams/util/sstring-simple.cpp	(revision 955)
@@ -22,11 +22,4 @@
 }
 
-SString::SString(int x)
-{
-	initEmpty();
-	if (x)
-		ensureSize(x + 1);
-}
-
 SString::SString(const char *t, int t_len)
 {
@@ -46,4 +39,10 @@
 	txt = from.txt; size = from.size; used = from.used;
 	from.txt = NULL; from.size = 0; from.used = 0;
+}
+
+SString::SString(char in)
+{
+	initEmpty();
+	append(&in, 1);
 }
 
@@ -117,5 +116,6 @@
 SString SString::operator+(const SString& s) const
 {
-	SString ret(len() + s.len());
+	SString ret;
+	ret.reserve(len() + s.len());
 	ret = *this;
 	ret += s;
Index: cpp/frams/util/sstring-simple.h
===================================================================
--- cpp/frams/util/sstring-simple.h	(revision 954)
+++ cpp/frams/util/sstring-simple.h	(revision 955)
@@ -27,11 +27,12 @@
 	SString(); ///< make an empty string
 	SString(const char*t, int t_len = -1); ///< make a string from char*
-	SString(int x); ///< string with initial buffer allocated for x characters
+	SString(int x) = delete; ///< disallow the former 'int' constructor (so the new 'char' version is not used through implicit conversion)
 	SString(const SString& from); ///< duplicate string
 	SString(SString&& from);///< move
+	SString(char in);
 	~SString();
 
 	void copyFrom(const char* ch, int chlen = -1); ///< copy string, length of -1 == unknown
-
+	
 	void* operator new(size_t s, void* mem){ return mem; }
 #ifdef _MSC_VER
@@ -43,4 +44,5 @@
 	int len() const { return used; } ///< get string length
 	void shrink(); ///< free unnecessary buffer
+	void reserve(int needed) { ensureSize(needed); } ///< like in std::string
 
 	/// after this call, you can modify sstring directly.
Index: cpp/frams/util/sstring.cpp
===================================================================
--- cpp/frams/util/sstring.cpp	(revision 954)
+++ cpp/frams/util/sstring.cpp	(revision 955)
@@ -127,9 +127,4 @@
 }
 
-SString::SString(int x)
-{
-	buf = new SBuf(x);
-}
-
 SString::SString(const char *t, int t_len)
 {
@@ -157,4 +152,10 @@
 		REF_UNLOCK;
 	}
+}
+
+SString::SString(char in)
+{
+	initEmpty();
+	copyFrom(&in, 1);
 }
 
Index: cpp/frams/util/sstring.h
===================================================================
--- cpp/frams/util/sstring.h	(revision 954)
+++ cpp/frams/util/sstring.h	(revision 955)
@@ -70,7 +70,8 @@
 	SString(); ///< make an empty string
 	SString(const char*t, int t_len = -1); ///< make a string from char*
-	SString(int x); ///< string with initial buffer size
+	SString(int x) = delete; ///< disallow the former 'int' constructor (so the new 'char' version is not used through implicit conversion)
 	SString(const SString& from); ///< duplicate string
 	SString(SString&& from); ///< move
+	SString(char in);
 	~SString();
 
@@ -86,4 +87,5 @@
 	int len() const { return buf->used; } ///< get string length
 	void shrink(); ///< free unnecessary buffer
+	void reserve(int needed) { ensureSize(needed); } ///< like in std::string
 
 	/// after this call, you can modify sstring directly.
Index: cpp/frams/vm/classes/genoobj.cpp
===================================================================
--- cpp/frams/vm/classes/genoobj.cpp	(revision 954)
+++ cpp/frams/vm/classes/genoobj.cpp	(revision 955)
@@ -86,6 +86,5 @@
 void GenoObj::get_format(ExtValue *ret)
 {
-	char format_as_string[2] = { getFormat(), 0 };
-	ret->setString(format_as_string);
+	ret->setString(getFormat());
 }
 
@@ -101,13 +100,14 @@
 }
 
-char GenoObj::formatFromExtValue(ExtValue& v)
+SString GenoObj::formatFromExtValue(ExtValue& v)
 {
 	if (v.getType() == TInt)
-		return v.getInt();
+	{
+		char ch = v.getInt();
+		return SString(&ch,1);
+	}
 	if (v.getType() == TString)
 	{
-		SString s = v.getString();
-		if (s.len() == 1)
-			return s.charAt(0);
+		return v.getString();
 	}
 	return Geno::INVALID_FORMAT;
@@ -198,5 +198,5 @@
 		SString n = vec->get(1) ? vec->get(1)->getString() : SString::empty();
 		SString c = vec->get(2) ? vec->get(2)->getString() : SString::empty();
-		*ret = makeDynamicObjectAndDecRef(new Geno(g, -1, n, c));
+		*ret = makeDynamicObjectAndDecRef(new Geno(g, Geno::UNKNOWN_FORMAT, n, c));
 	}
 	else
Index: cpp/frams/vm/classes/genoobj.h
===================================================================
--- cpp/frams/vm/classes/genoobj.h	(revision 954)
+++ cpp/frams/vm/classes/genoobj.h	(revision 955)
@@ -39,5 +39,5 @@
 	static Geno* fromObject(const ExtValue& v, bool warn = true);
 	static ParamInterface* getInterface();
-	static char formatFromExtValue(ExtValue& v);
+	static SString formatFromExtValue(ExtValue& v);
 
 	static Param& getStaticParam();
Index: cpp/frams/vm/framscript.l
===================================================================
--- cpp/frams/vm/framscript.l	(revision 954)
+++ cpp/frams/vm/framscript.l	(revision 955)
@@ -117,5 +117,6 @@
 {
 int len=src.len();
-SString ret((len*11)/10+10);
+SString ret;
+ret.reserve((len*11)/10+10);
 const char*t=src.c_str();
 while(len>0)
Index: cpp/frams/vm/framscript.y
===================================================================
--- cpp/frams/vm/framscript.y	(revision 954)
+++ cpp/frams/vm/framscript.y	(revision 955)
@@ -1514,5 +1514,6 @@
 SString s=val.getString();
 int len=s.len();
-SString ret((len*11)/10+10);
+SString ret;
+ret.reserve((len*11)/10+10);
 ret+='\"';
 const char*t=s.c_str();
