Index: cpp/frams/param/param.cpp
===================================================================
--- cpp/frams/param/param.cpp	(revision 648)
+++ cpp/frams/param/param.cpp	(revision 650)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -20,17 +20,20 @@
 { { "Empty", 1, 0, "Empty", }, { 0, 0, 0, }, };
 
-static void czytdotyldy(VirtFILE *f, SString &s)
+/** return: true if tilde was found, false if finished at EOF */
+static bool readUntilTilde(VirtFILE *f, SString &s)
 {
 	SString temp;
 	int z;
 	char last_char = 0;
+	bool tilde_found = false;
 	while ((z = f->Vgetc()) != EOF)
 	{
 		if (z == '~')
-			if (last_char != '\\') break;
+			if (last_char != '\\') { tilde_found = true; break; }
 		last_char = (char)z;
 		temp += last_char;
 	}
 	s = temp;
+	return tilde_found;
 }
 
@@ -72,15 +75,15 @@
 int ParamInterface::getMinMax(int prop, paInt& minumum, paInt& maximum, paInt &def)
 {
-	return getMinMax(type(prop),minumum,maximum,def);
+	return getMinMax(type(prop), minumum, maximum, def);
 }
 
 int ParamInterface::getMinMax(int prop, double& minumum, double& maximum, double& def)
 {
-	return getMinMax(type(prop),minumum,maximum,def);
+	return getMinMax(type(prop), minumum, maximum, def);
 }
 
 int ParamInterface::getMinMax(int prop, int& minumum, int& maximum, SString& def)
 {
-	return getMinMax(type(prop),minumum,maximum,def);
+	return getMinMax(type(prop), minumum, maximum, def);
 }
 
@@ -404,4 +407,18 @@
 }
 
+static void closingTildeError(ParamInterface *pi, VirtFILE *file, int field_index)
+{
+	SString fileinfo;
+	const char* fname = file->VgetPath();
+	if (fname != NULL)
+		fileinfo = SString::sprintf(" while reading from '%s'", fname);
+	SString field;
+	if (field_index >= 0)
+		field = SString::sprintf("'%s.%s'", pi->getName(), pi->id(field_index));
+	else
+		field = SString::sprintf("unknown property of '%s'", pi->getName());
+	logPrintf("ParamInterface", "load", LOG_WARN, "Closing '~' (tilde) not found in %s%s", field.c_str(), fileinfo.c_str());
+}
+
 int ParamInterface::load(VirtFILE* f, bool warn_unknown_fields, bool *abortable, int *linenum)
 {
@@ -415,6 +432,6 @@
 	vector<bool> seen;
 	seen.resize(getPropCount());
-	if ((i=findId("beforeLoad"))>=0)
-		call(i,NULL,NULL);
+	if ((i = findId("beforeLoad")) >= 0)
+		call(i, NULL, NULL);
 	while (((!abortable) || (!*abortable)) && loadSStringLine(f, buf))
 	{
@@ -426,21 +443,21 @@
 		p = strchr(p0, ':');
 		if (!p)
+		{
+			switch (unexpected_line)
 			{
-			switch(unexpected_line)
-				{
-				case 0:
-					logPrintf("ParamInterface", "load", LOG_WARN, "Ignored unexpected line %s while reading object '%s'", 
-					  linenum ?
-					  SString::sprintf("%d",*linenum).c_str()
+			case 0:
+				logPrintf("ParamInterface", "load", LOG_WARN, "Ignored unexpected line %s while reading object '%s'",
+					linenum ?
+					SString::sprintf("%d", *linenum).c_str()
 					: SString::sprintf("'%s'", p0).c_str(),
-					  getName());
-					break;
-				case 1:
-					logPrintf("ParamInterface", "load", LOG_WARN, "The following line(s) were also unexpected and were ignored");
-					break;
-				}
+					getName());
+				break;
+			case 1:
+				logPrintf("ParamInterface", "load", LOG_WARN, "The following line(s) were also unexpected and were ignored");
+				break;
+			}
 			unexpected_line++;
 			continue;
-			}
+		}
 		unexpected_line = 0;
 		p_len = (int)(p - p0);
@@ -449,17 +466,17 @@
 		{
 			if (seen[i])
+			{
+				SString fileinfo;
+				const char* fname = f->VgetPath();
+				if (fname != NULL)
 				{
-				SString fileinfo;
-				const char* fname=f->VgetPath();
-				if (fname!=NULL)
-					{
-					fileinfo=SString::sprintf(" while reading from '%s'",fname);
+					fileinfo = SString::sprintf(" while reading from '%s'", fname);
 					if (linenum)
-						fileinfo+=SString::sprintf(" (line %d)",*linenum);
-					}
-				logPrintf("ParamInterface", "load", LOG_WARN, "Multiple '%s.%s' fields found%s",getName(),id(i),fileinfo.c_str());
+						fileinfo += SString::sprintf(" (line %d)", *linenum);
 				}
+				logPrintf("ParamInterface", "load", LOG_WARN, "Multiple '%s.%s' fields found%s", getName(), id(i), fileinfo.c_str());
+			}
 			else
-				seen[i]=true;
+				seen[i] = true;
 			if (!(flags(i)&PARAM_DONTLOAD))
 			{
@@ -467,5 +484,6 @@
 				{
 					SString s;
-					czytdotyldy(f, s);
+					if (!readUntilTilde(f, s))
+						closingTildeError(this, f, i);
 					int lfcount = 1;
 					const char* tmp = s.c_str();
@@ -501,5 +519,6 @@
 		{ // eat unrecognized multiline field
 			SString s;
-			czytdotyldy(f, s);
+			if (!readUntilTilde(f, s))
+				closingTildeError(this, f, -1);
 			if (linenum)
 			{
@@ -516,6 +535,6 @@
 		}
 	}
-	if ((i=findId("afterLoad"))>=0)
-		call(i,NULL,NULL);
+	if ((i = findId("afterLoad")) >= 0)
+		call(i, NULL, NULL);
 	return fields_loaded;
 }
@@ -634,5 +653,5 @@
 	case 's': { SString t = v.getString(); return setString(i, t); }
 	case 'o':
-		if ((v.type!=TUnknown)&&(v.type!=TObj))
+		if ((v.type != TUnknown) && (v.type != TObj))
 			logPrintf("ParamInterface", "set", LOG_ERROR, "Setting object '%s.%s' from %s", getName(), id(i), v.typeAndValue().c_str());
 		else
@@ -724,39 +743,39 @@
 bool ParamInterface::isValidTypeDescription(const char* t)
 {
-if (t==NULL) return false;
-if (*t==0) return false;
-if (strchr("dfsoxp", *t)==NULL) return false;
-switch(*t)
+	if (t == NULL) return false;
+	if (*t == 0) return false;
+	if (strchr("dfsoxp", *t) == NULL) return false;
+	switch (*t)
 	{
 	case 'd':
-		{paInt a,b,c; if (getMinMax(t,a,b,c)==1) return false;}
+	{paInt a, b, c; if (getMinMax(t, a, b, c) == 1) return false; }
 		break;
 	case 'f':
-		{double a,b,c; if (getMinMax(t,a,b,c)==1) return false;}
-		break;
-	}
-return true;
+	{double a, b, c; if (getMinMax(t, a, b, c) == 1) return false; }
+		break;
+	}
+	return true;
 }
 
 SString ParamInterface::describeType(const char* type)
 {
-SString t;
-switch(type[0])
-	{
-	case 'd': t+="integer";
-	{paInt a,b,c; int n=getMinMax(type,a,b,c); if ((n>=2)&&(b>=a)) t+=SString::sprintf(" %d..%d",a,b); if (n>=3) t+=SString::sprintf(" (default %d)",c);}
-	break;
-	case 'f': t+="float";
-	{double a,b,c; int n=getMinMax(type,a,b,c); if ((n>=2)&&(b>=a)) t+=SString::sprintf(" %g..%g",a,b); if (n>=3) t+=SString::sprintf(" (default %g)",c);}
-	break;
-	case 's': t+="string";
-	{int a,b; SString c; int n=getMinMax(type,a,b,c); if ((n>=2)&&(b>0)) t+=SString::sprintf(", max %d chars",b); if (n>=3) t+=SString::sprintf(" (default \"%s\")",c.c_str());}
-	break;
-	case 'x': t+="untyped value"; break;
-	case 'p': t+="function"; break;
-	case 'o': t+="object"; if (type[1]) {t+=" of class "; t+=type+1;} break;
+	SString t;
+	switch (type[0])
+	{
+	case 'd': t += "integer";
+	{paInt a, b, c; int n = getMinMax(type, a, b, c); if ((n >= 2) && (b >= a)) t += SString::sprintf(" %d..%d", a, b); if (n >= 3) t += SString::sprintf(" (default %d)", c); }
+		break;
+	case 'f': t += "float";
+	{double a, b, c; int n = getMinMax(type, a, b, c); if ((n >= 2) && (b >= a)) t += SString::sprintf(" %g..%g", a, b); if (n >= 3) t += SString::sprintf(" (default %g)", c); }
+		break;
+	case 's': t += "string";
+	{int a, b; SString c; int n = getMinMax(type, a, b, c); if ((n >= 2) && (b > 0)) t += SString::sprintf(", max %d chars", b); if (n >= 3) t += SString::sprintf(" (default \"%s\")", c.c_str()); }
+		break;
+	case 'x': t += "untyped value"; break;
+	case 'p': t += "function"; break;
+	case 'o': t += "object"; if (type[1]) { t += " of class "; t += type + 1; } break;
 	default: return "unknown type";
 	}
-return t;
+	return t;
 }
 
@@ -766,15 +785,15 @@
 void SimpleAbstractParam::sanityCheck(int i)
 {
-	ParamEntry *pe=entry(i);
-
-	const char* t=pe->type;
-	const char* err=NULL;
+	ParamEntry *pe = entry(i);
+
+	const char* t = pe->type;
+	const char* err = NULL;
 
 	if (!isValidTypeDescription(t))
-		err="invalid type description";
-	if (*t=='p')
-	{
-		if (pe->fun1==NULL)
-			err="no procedure defined";
+		err = "invalid type description";
+	if (*t == 'p')
+	{
+		if (pe->fun1 == NULL)
+			err = "no procedure defined";
 	}
 	else
@@ -782,12 +801,12 @@
 		if (!(pe->flags & PARAM_READONLY))
 		{ //write access
-			if ((pe->fun2==NULL)&&(pe->offset==PARAM_ILLEGAL_OFFSET))
-				err="no field defined (GETONLY without PARAM_READONLY?)";
-		}
-	}
-	if (err!=NULL)
-		logPrintf("SimpleAbstractParam","sanityCheck", LOG_ERROR,
+			if ((pe->fun2 == NULL) && (pe->offset == PARAM_ILLEGAL_OFFSET))
+				err = "no field defined (GETONLY without PARAM_READONLY?)";
+		}
+	}
+	if (err != NULL)
+		logPrintf("SimpleAbstractParam", "sanityCheck", LOG_ERROR,
 		"Invalid ParamEntry for %s.%s (%s)", getName(), pe->id, err);
-}	
+}
 #endif
 
@@ -903,5 +922,5 @@
 	paInt mn = 0, mx = 0, de = 0;
 	int result = 0;
-	if (getMinMax(pe->type,mn,mx,de)>=2)
+	if (getMinMax(pe->type, mn, mx, de) >= 2)
 		if (mn <= mx) // else if mn>mx then the min/max constraint makes no sense and there is no checking
 		{
@@ -937,5 +956,5 @@
 	double mn = 0, mx = 0, de = 0;
 	int result = 0;
-	if (getMinMax(pe->type,mn,mx,de)>=2)
+	if (getMinMax(pe->type, mn, mx, de) >= 2)
 		if (mn <= mx) // else if mn>mx then the min/max constraint makes no sense and there is no checking
 		{
@@ -1010,19 +1029,19 @@
 	if (pe->flags&PARAM_READONLY) return PSET_RONLY;
 	if (pe->flags&PARAM_OBJECTSET)
-		{
-		ExtObject o=getObject(i);
+	{
+		ExtObject o = getObject(i);
 		Param tmp;
-		ParamInterface* oif=o.getParamInterface(tmp);
+		ParamInterface* oif = o.getParamInterface(tmp);
 		int ass;
-		if (oif && ((ass=oif->findId("assign"))>=0))
-			{
-			ExtValue arg=x;
-			oif->call(ass,&arg,&v);
-			}
+		if (oif && ((ass = oif->findId("assign")) >= 0))
+		{
+			ExtValue arg = x;
+			oif->call(ass, &arg, &v);
+		}
 		else
 			logPrintf("SimpleAbstractParam", "setObject", LOG_ERROR,
-				  "'%s.%s' is PARAM_OBJECTSET but no 'assign()' in %s", getName(), pe->id, o.interfaceName());
+			"'%s.%s' is PARAM_OBJECTSET but no 'assign()' in %s", getName(), pe->id, o.interfaceName());
 		return PSET_CHANGED;
-		}
+	}
 	ExtObject xcopy = x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
 	if (pe->fun2)
@@ -1122,5 +1141,5 @@
 	const char *value, *valstop;
 	SString tmpvalue;
-	bool parse_failed=false;
+	bool parse_failed = false;
 	if (poz >= s.len()) return fields_loaded;
 	t = s.c_str() + poz;
@@ -1178,5 +1197,5 @@
 		else
 #ifdef SAVE_SELECTED_NAMES
-			if ((i>=getPropCount()) || !(flags(i)&PARAM_CANOMITNAME))
+			if ((i >= getPropCount()) || !(flags(i)&PARAM_CANOMITNAME))
 #endif
 			{
@@ -1208,5 +1227,5 @@
 				id(i), getName(), (ret&PSET_HITMAX) ? "big" : "small");
 			if (ret&PSET_PARSEFAILED)
-				parse_failed=true;
+				parse_failed = true;
 			*(char*)valstop = remember;
 		}
Index: cpp/frams/param/param.h
===================================================================
--- cpp/frams/param/param.h	(revision 648)
+++ cpp/frams/param/param.h	(revision 650)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -118,5 +118,5 @@
 	ExtValue getById(const char* prop);
 
-	int setInt(int i, const char* str, bool strict=false);
+	int setInt(int i, const char* str, bool strict = false);
 	int setDouble(int i, const char* str);
 	virtual int setInt(int, paInt) = 0;		///< set long value, you can only use this for "d" type prop
@@ -128,5 +128,5 @@
 	int set(int, const ExtValue &);///< most universal set, can be used for every datatype
 
-	int set(int, const char*, bool strict=false);		///< oldstyle set, can convert string to long or double
+	int set(int, const char*, bool strict = false);		///< oldstyle set, can convert string to long or double
 
 	int setIntById(const char* prop, paInt);///< set long value, you can only use this for "d" type prop
@@ -157,6 +157,6 @@
 	/** return the human readable description of the given type */
 	static SString describeType(const char* type);
-	SString describeType(int i) {return describeType(type(i));}
-	
+	SString describeType(int i) { return describeType(type(i)); }
+
 	/** copy all property values from other ParamInterface object */
 	void copyFrom(ParamInterface *src);
@@ -176,6 +176,6 @@
 
 	static const char* SERIALIZATION_PREFIX;
-	static const int LOAD2_PARSE_FAILED=(1<<30); ///< this bit is set in return value from load2 if a parse error was detected while loading. usage: if (load2(...) & LOAD2_PARSE_FAILED) ...
-	static const int LOAD2_IGNORE_PARSE_FAILED=(~LOAD2_PARSE_FAILED); ///< bitmask to be used if the parsing error is to be ignored. usage: int number_of_loaded_fields=load2(...) & LOAD2_IGNORE_PARSE_FAILED;
+	static const int LOAD2_PARSE_FAILED = (1 << 30); ///< this bit is set in return value from load2 if a parse error was detected while loading. usage: if (load2(...) & LOAD2_PARSE_FAILED) ...
+	static const int LOAD2_IGNORE_PARSE_FAILED = (~LOAD2_PARSE_FAILED); ///< bitmask to be used if the parsing error is to be ignored. usage: int number_of_loaded_fields=load2(...) & LOAD2_IGNORE_PARSE_FAILED;
 
 	static bool isValidTypeDescription(const char* t);
