Index: source/graphics/MapReader.cpp
===================================================================
--- source/graphics/MapReader.cpp	(revision 17650)
+++ source/graphics/MapReader.cpp	(working copy)
@@ -15,10 +15,14 @@
  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "precompiled.h"
 
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+
 #include "MapReader.h"
 
 #include "graphics/Camera.h"
 #include "graphics/CinemaManager.h"
 #include "graphics/Entity.h"
@@ -26,10 +30,11 @@
 #include "graphics/MapGenerator.h"
 #include "graphics/Patch.h"
 #include "graphics/Terrain.h"
 #include "graphics/TerrainTextureEntry.h"
 #include "graphics/TerrainTextureManager.h"
+#include "graphics/Unit.h"
 #include "lib/timer.h"
 #include "lib/external_libraries/libsdl.h"
 #include "maths/MathUtil.h"
 #include "ps/CLogger.h"
 #include "ps/Loader.h"
@@ -47,13 +52,10 @@
 #include "simulation2/components/ICmpPosition.h"
 #include "simulation2/components/ICmpTerrain.h"
 #include "simulation2/components/ICmpVisual.h"
 #include "simulation2/components/ICmpWaterManager.h"
 
-#include <boost/algorithm/string/predicate.hpp>
-
-
 CMapReader::CMapReader()
 	: xml_reader(0), m_PatchesPerSide(0), m_MapGen(0)
 {
 	cur_terrain_tex = 0;	// important - resets generator state
 
@@ -436,10 +438,11 @@ private:
 	int at_x, at_y, at_z;
 	int at_group, at_group2;
 	int at_angle;
 	int at_uid;
 	int at_seed;
+	int at_variation;
 
 	XMBElementList nodes; // children of root
 
 	// loop counters
 	size_t node_idx;
@@ -487,10 +490,11 @@ void CXMLReader::Init(const VfsPath& xml
 	AT(x); AT(y); AT(z);
 	AT(group); AT(group2);
 	AT(angle);
 	AT(uid);
 	AT(seed);
+	AT(variation);
 #undef AT
 #undef EL
 
 	XMBElement root = xmb_file.GetRoot();
 	ENSURE(xmb_file.GetElementString(root.GetNodeName()) == "Scenario");
@@ -978,10 +982,11 @@ int CXMLReader::ReadEntities(XMBElement
 		CStrW TemplateName;
 		int PlayerID = 0;
 		CFixedVector3D Position;
 		CFixedVector3D Orientation;
 		long Seed = -1;
+		std::set<CStr> actorSelections;
 
 		// Obstruction control groups.
 		entity_id_t ControlGroup = INVALID_ENTITY;
 		entity_id_t ControlGroup2 = INVALID_ENTITY;
 
@@ -1027,15 +1032,24 @@ int CXMLReader::ReadEntities(XMBElement
 			}
 			// <actor>
 			else if (element_name == el_actor)
 			{
 				XMBAttributeList attrs = setting.GetAttributes();
-				CStr seedStr = attrs.GetNamedItem(at_seed);
-				if (!seedStr.empty())
+				CStr variationStr = attrs.GetNamedItem(at_variation);
+
+				if (!variationStr.empty())
 				{
-					Seed = seedStr.ToLong();
-					ENSURE(Seed >= 0);
+					boost::algorithm::split(actorSelections, variationStr, boost::is_any_of("|"));
+				}
+				else
+				{
+					CStr seedStr = attrs.GetNamedItem(at_seed);
+					if (!seedStr.empty())
+					{
+						Seed = seedStr.ToLong();
+						ENSURE(Seed >= 0);
+					}
 				}
 			}
 			else
 				debug_warn(L"Invalid map XML data");
 		}
@@ -1072,13 +1086,18 @@ int CXMLReader::ReadEntities(XMBElement
 			}
 
 			CmpPtr<ICmpVisual> cmpVisual(sim, ent);
 			if (cmpVisual)
 			{
-				if (Seed != -1)
+				if (!actorSelections.empty())
+				{
+					CUnit* unit = cmpVisual->GetUnit();
+					if (unit)
+						unit->SetActorSelections(actorSelections);
+				}
+				else if (Seed != -1)
 					cmpVisual->SetActorSeed((u32)Seed);
-				// TODO: variation/selection strings
 			}
 
 			if (PlayerID == m_MapReader.m_PlayerID && (boost::algorithm::ends_with(TemplateName, L"civil_centre") || m_MapReader.m_StartingCameraTarget == INVALID_ENTITY))
 			{
 				// Focus on civil centre or first entity owned by player
Index: source/graphics/MapWriter.cpp
===================================================================
--- source/graphics/MapWriter.cpp	(revision 17650)
+++ source/graphics/MapWriter.cpp	(working copy)
@@ -15,20 +15,23 @@
  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "precompiled.h"
 
+#include <boost/algorithm/string/join.hpp>
+
 #include "Camera.h"
 #include "CinemaManager.h"
 #include "GameView.h"
 #include "LightEnv.h"
 #include "MapReader.h"
 #include "MapWriter.h"
 #include "Patch.h"
 #include "Terrain.h"
 #include "TerrainTextureEntry.h"
 #include "TerrainTextureManager.h"
+#include "Unit.h"
 
 #include "maths/MathUtil.h"
 #include "maths/NUSpline.h"
 #include "ps/CLogger.h"
 #include "ps/Loader.h"
@@ -385,19 +388,29 @@ void CMapWriter::WriteXML(const VfsPath&
 							XML_Attribute("group2", group2);
 					}
 				}
 
 				CmpPtr<ICmpVisual> cmpVisual(sim, ent);
+				CUnit* unit = cmpVisual->GetUnit();
 				if (cmpVisual)
 				{
 					u32 seed = cmpVisual->GetActorSeed();
-					if (seed != (u32)ent)
+					bool variationsAdded = false;
+					XML_Element("Actor");
+					if (unit)
 					{
-						XML_Element("Actor");
-						XML_Attribute("seed", seed);
+						const std::set<CStr>& actorSelections = unit->GetActorSelections();
+
+						if (!actorSelections.empty())
+						{
+							CStr actorSelectionsStr = boost::algorithm::join(unit->GetActorSelections(), "|");
+							XML_Attribute("variation", actorSelectionsStr.c_str());
+							variationsAdded = true;
+						}
 					}
-					// TODO: variation/selection strings
+					if (!variationsAdded && seed != (u32)ent)
+						XML_Attribute("seed", seed);
 				}
 			}
 		}
 
 		const std::map<CStrW, CCinemaPath>& paths = pCinema->GetAllPaths();
