Index: /cpp/frams/param/param.cpp
===================================================================
--- /cpp/frams/param/param.cpp	(revision 1183)
+++ /cpp/frams/param/param.cpp	(revision 1184)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2022  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -49,4 +49,28 @@
 	return (const char*)memchr((const void*)t, ch, limit - t);
 }
+
+
+Param2ParamCopy::Param2ParamCopy(ParamInterface& schema, const std::initializer_list<const char*> names)
+{
+	for (const char* n : names)
+	{
+		int i = schema.findId(n);
+		if (i >= 0)
+			fields.push_back(i);
+		else
+			logPrintf("Param2ParamCopy", "findId", LOG_CRITICAL, "Can't find '%s.%s'", schema.getName(), n);
+	}
+}
+
+void Param2ParamCopy::operator()(const ParamInterface& from, ParamInterface& to)
+{
+	ExtValue tmp;
+	for (int i : fields)
+	{
+		((ParamInterface&)from).get(i, tmp);
+		to.set(i, tmp);
+	}
+}
+
 
 void ParamInterface::copyFrom(ParamInterface *src)
Index: /cpp/frams/param/param.h
===================================================================
--- /cpp/frams/param/param.h	(revision 1183)
+++ /cpp/frams/param/param.h	(revision 1184)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2022  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -8,4 +8,5 @@
 #include <stdio.h>
 #include <stdint.h>
+#include <vector>
 #include <frams/util/sstring.h>
 #include <frams/util/sstringutils.h>
@@ -199,4 +200,18 @@
 };
 
+
+
+/** Copy fields between two compatible params. A Param2ParamCopy object can be static so the field names are only looked up once. */
+class Param2ParamCopy
+{
+	std::vector<int> fields;
+public:
+	Param2ParamCopy(ParamInterface& schema, const std::initializer_list<const char*> names);
+	void operator()(const ParamInterface& from, ParamInterface& to);
+};
+
+
+
+
 // implementations:
 
@@ -317,4 +332,8 @@
 };
 
+#ifdef _DEBUG
+#define CHECK_PARAMENTRY_COUNT // This could be the only version, because it is not significantly more costly than the other one. Now we have a _DEBUG (checking, slightly slower) version and a non-_DEBUG (i.e. release, non-checking, slightly faster) version. Since both versions are completely different, the non-_DEBUG (release) version is not verified at all during thorough _DEBUG testing.
+#endif
+
 class Param : public SimpleAbstractParam
 {
@@ -329,9 +348,42 @@
 		*/
 
+#ifdef CHECK_PARAMENTRY_COUNT
+	
+	template <int COUNT>
+	Param(ParamEntry (&t)[COUNT],void* o = 0, const char*n = 0) :SimpleAbstractParam(o, n), tab(t)
+	{
+		if (!n&&tab) myname = tab[0].name;
+		//printf("Param(ParamEntry t[%d]) %s\n",COUNT,myname?myname:"unknown name");
+		if (tab)
+			{
+			int in_array = COUNT-1-tab[0].group;
+			if (tab[0].flags != in_array)
+				printf("\nCHECK_PARAMENTRY_COUNT: %d items in ParamEntry[] array, declared %d (%s)\n\n",in_array, tab[0].flags, myname?myname:"unknown name");
+			if (in_array>0 && tab[COUNT-1].id != NULL) //in_array>0 -> completely empty paramtab can't end with the usual zero row because that's also its first row
+				printf("\nCHECK_PARAMENTRY_COUNT: last entry is not null (%s)\n\n",myname?myname:"unknown name");
+			}
+	}
+
+	template<typename T, typename std::enable_if_t<std::is_same<ParamEntry*,T>::value>* = nullptr > //SFINAE-fu because the normal ParamEntry* overload would be also called for ParamEntry[COUNT] argument
+	Param(T t, void* o = 0, const char*n = 0) :SimpleAbstractParam(o, n), tab(t)
+	{
+		//printf("Param(ParamEntry* t) %s\n",tab?tab[0].name:"");
+		if (!n&&tab) myname = tab[0].name;
+	}
+
+	Param() :SimpleAbstractParam(NULL, NULL), tab(NULL)
+	{
+		//printf("Param(NULL)\n");
+	}
+
+#else // CHECK_PARAMENTRY_COUNT
+	
 	Param(ParamEntry *t = 0, void* o = 0, const char*n = 0) :SimpleAbstractParam(o, n), tab(t)
 	{
 		if (!n&&tab) myname = tab[0].name;
 	}
-
+	
+#endif // CHECK_PARAMENTRY_COUNT
+	
 	Param(const Param& p) :SimpleAbstractParam(p.object, p.myname), tab(p.tab) {}
 	void operator=(const Param&p) { object = p.object; myname = p.myname; tab = p.tab; }
