Browse Source

Advance Json serializer
* added bool, numeric and string support
* added some enum support
* PoC implementation of logical id condition
* Refactoring

AlexVinS 9 years ago
parent
commit
ec760632a6

+ 31 - 1
lib/CTownHandler.cpp

@@ -526,7 +526,7 @@ void CTownHandler::loadClientData(CTown &town, const JsonNode & source)
 	    info.tavernVideo = source["tavernVideo"].String();
 	    info.tavernVideo = source["tavernVideo"].String();
 	else
 	else
 		info.tavernVideo = "TAVERN.BIK";
 		info.tavernVideo = "TAVERN.BIK";
-	//end of legacy assignment 
+	//end of legacy assignment
 
 
 	loadTownHall(town,   source["hallSlots"]);
 	loadTownHall(town,   source["hallSlots"]);
 	loadStructures(town, source["structures"]);
 	loadStructures(town, source["structures"]);
@@ -795,3 +795,33 @@ std::set<TFaction> CTownHandler::getAllowedFactions(bool withTown /*=true*/) con
 
 
 	return allowedFactions;
 	return allowedFactions;
 }
 }
+
+si32 CTownHandler::decodeFaction(const std::string & identifier)
+{
+	auto rawId = VLC->modh->identifiers.getIdentifier("core", "faction", identifier);
+	if(rawId)
+		return rawId.get();
+	else
+		return -1;
+}
+
+std::string CTownHandler::encodeFaction(const si32 index)
+{
+	return VLC->townh->factions[index]->identifier;
+}
+
+si32 CTownHandler::decodeBuilding(const std::string & identifier)
+{
+	//FIXME: CTownHandler::decodeBuilding
+	auto rawId = VLC->modh->identifiers.getIdentifier("core", "building", identifier); //???
+	if(rawId)
+		return rawId.get();
+	else
+		return -1;
+}
+
+std::string CTownHandler::encodeBuilding(const si32 index)
+{
+	 //FIXME: CTownHandler::encodeBuilding
+	 return "";
+}

+ 15 - 3
lib/CTownHandler.h

@@ -122,8 +122,8 @@ public:
 
 
 	std::string creatureBg120;
 	std::string creatureBg120;
 	std::string creatureBg130;
 	std::string creatureBg130;
-	
-	
+
+
 
 
 	std::vector<SPuzzleInfo> puzzleMap;
 	std::vector<SPuzzleInfo> puzzleMap;
 
 
@@ -142,7 +142,7 @@ public:
 	static std::vector<BattleHex> defaultMoatHexes();
 	static std::vector<BattleHex> defaultMoatHexes();
 
 
 	CFaction * faction;
 	CFaction * faction;
-	
+
 	std::vector<std::string> names; //names of the town instances
 	std::vector<std::string> names; //names of the town instances
 
 
 	/// level -> list of creatures on this tier
 	/// level -> list of creatures on this tier
@@ -282,6 +282,18 @@ public:
 	std::vector<bool> getDefaultAllowed() const override;
 	std::vector<bool> getDefaultAllowed() const override;
 	std::set<TFaction> getAllowedFactions(bool withTown = true) const;
 	std::set<TFaction> getAllowedFactions(bool withTown = true) const;
 
 
+	//json serialization helper
+	static si32 decodeFaction(const std::string & identifier);
+
+	//json serialization helper
+	static std::string encodeFaction(const si32 index);
+
+	//json serialization helper
+	static si32 decodeBuilding(const std::string & identifier);
+
+	//json serialization helper
+	static std::string encodeBuilding(const si32 index);
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
 		h & factions;
 		h & factions;

+ 238 - 199
lib/mapping/MapFormatJson.cpp

@@ -29,23 +29,15 @@
 
 
 namespace HeaderDetail
 namespace HeaderDetail
 {
 {
-	static const std::map<std::string, ui8> difficultyReverseMap =
-	{
-		{"", 1},
-		{"EASY", 0},
-		{"NORMAL", 1},
-		{"HARD", 2},
-		{"EXPERT", 3},
-		{"IMPOSSIBLE", 4}
-	};
+	static const ui8 difficultyDefault = 1;//normal
 
 
-	static const std::map<ui8, std::string> difficultyForwardMap =
+	static const std::vector<std::string> difficultyMap =
 	{
 	{
-		{0, "EASY"},
-		{1, "NORMAL"},
-		{2, "HARD"},
-		{3, "EXPERT"},
-		{4, "IMPOSSIBLE"}
+		"EASY",
+		"NORMAL",
+		"HARD",
+		"EXPERT",
+		"IMPOSSIBLE"
 	};
 	};
 }
 }
 
 
@@ -179,14 +171,218 @@ const int CMapFormatJson::VERSION_MINOR = 0;
 const std::string CMapFormatJson::HEADER_FILE_NAME = "header.json";
 const std::string CMapFormatJson::HEADER_FILE_NAME = "header.json";
 const std::string CMapFormatJson::OBJECTS_FILE_NAME = "objects.json";
 const std::string CMapFormatJson::OBJECTS_FILE_NAME = "objects.json";
 
 
+void CMapFormatJson::serializeAllowedFactions(JsonSerializeFormat & handler, std::set<TFaction> & value)
+{
+	//TODO: unify allowed factions with others - make them std::vector<bool>
+
+	std::vector<bool> temp;
+	temp.resize(VLC->townh->factions.size(), false);
+	auto standard = VLC->townh->getDefaultAllowed();
+
+    if(handler.saving)
+	{
+		for(auto faction : VLC->townh->factions)
+			if(faction->town && vstd::contains(value, faction->index))
+				temp[std::size_t(faction->index)] = true;
+	}
+
+	handler.serializeLIC("allowedFactions", &CTownHandler::decodeFaction, &CTownHandler::encodeFaction, standard, temp);
+
+	if(!handler.saving)
+	{
+		value.clear();
+		for (std::size_t i=0; i<temp.size(); i++)
+			if(temp[i])
+				value.insert(i);
+	}
+}
+
+void CMapFormatJson::serializeHeader(JsonSerializeFormat & handler)
+{
+	handler.serializeString("name", mapHeader->name);
+	handler.serializeString("description", mapHeader->description);
+	handler.serializeNumeric("heroLevelLimit", mapHeader->levelLimit);
+
+	//todo: support arbitrary percentage
+	handler.serializeNumericEnum("difficulty", HeaderDetail::difficultyMap, HeaderDetail::difficultyDefault, mapHeader->difficulty);
+
+	serializePlayerInfo(handler);
+}
+
+void CMapFormatJson::serializePlayerInfo(JsonSerializeFormat & handler)
+{
+	auto playersData = handler.enterStruct("players");
+
+	for(int player = 0; player < PlayerColor::PLAYER_LIMIT_I; player++)
+	{
+		PlayerInfo & info = mapHeader->players[player];
+
+		auto playerData = playersData.enterStruct(GameConstants::PLAYER_COLOR_NAMES[player]);
+
+		if(handler.saving)
+		{
+			if(!info.canAnyonePlay())
+				continue;
+		}
+		else
+		{
+			if(playerData.get().isNull())
+			{
+				info.canComputerPlay = false;
+				info.canHumanPlay = false;
+				continue;
+			}
+			info.canComputerPlay = true;
+		}
+
+		serializeAllowedFactions(handler, info.allowedFactions);
+
+		handler.serializeBoolEnum("canPlay", "PlayerOrAI", "AIOnly", info.canHumanPlay);
+
+		//mainTown
+		if(handler.saving)
+		{
+
+		}
+		else
+		{
+
+		}
+
+		handler.serializeBool("generateHeroAtMainTown", info.generateHeroAtMainTown);
+
+
+		//mainHero
+
+		//mainHeroPortrait
+
+		//mainCustomHeroName
+
+		//towns
+
+		//heroes
+		{
+			auto heroes = playersData.enterStruct("heroes");
+
+		}
+
+		if(!handler.saving)
+		{
+			//isFactionRandom indicates that player may select faction, depends on towns & heroes
+			// true if main town is random and generateHeroAtMainTown==true
+			// or main hero is random
+			//TODO: recheck mechanics
+			//	info.isFactionRandom =
+		}
+
+
+	}
+}
+
+void CMapFormatJson::readTeams(JsonDeserializer & handler)
+{
+	auto teams = handler.enterStruct("teams");
+	const JsonNode & src = teams.get();
+
+    if(src.getType() != JsonNode::DATA_VECTOR)
+	{
+		// No alliances
+		if(src.getType() != JsonNode::DATA_NULL)
+			logGlobal->errorStream() << "Invalid teams field type";
+
+		mapHeader->howManyTeams = 0;
+		for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
+		{
+			if(mapHeader->players[i].canComputerPlay || mapHeader->players[i].canHumanPlay)
+			{
+				mapHeader->players[i].team = TeamID(mapHeader->howManyTeams++);
+			}
+		}
+	}
+	else
+	{
+		const JsonVector & srcVector = src.Vector();
+		mapHeader->howManyTeams = srcVector.size();
+
+		for(int team = 0; team < mapHeader->howManyTeams; team++)
+		{
+			for(const JsonNode & playerData : srcVector[team].Vector())
+			{
+				PlayerColor player = PlayerColor(vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, playerData.String()));
+				if(player.isValidPlayer())
+				{
+					if(mapHeader->players[player.getNum()].canAnyonePlay())
+					{
+						mapHeader->players[player.getNum()].team = TeamID(team);
+					}
+				}
+			}
+		}
+
+		for(PlayerInfo & player : mapHeader->players)
+		{
+			if(player.canAnyonePlay() && player.team == TeamID::NO_TEAM)
+				player.team = TeamID(mapHeader->howManyTeams++);
+		}
+
+	}
+}
+
+
+void CMapFormatJson::writeTeams(JsonSerializer & handler)
+{
+	auto teams = handler.enterStruct("teams");
+	JsonNode & dest = teams.get();
+	std::vector<std::set<PlayerColor>> teamsData;
+
+	teamsData.resize(mapHeader->howManyTeams);
+
+	//get raw data
+	for(int idx = 0; idx < mapHeader->players.size(); idx++)
+	{
+		const PlayerInfo & player = mapHeader->players.at(idx);
+		int team = player.team.getNum();
+		if(vstd::iswithin(team, 0, mapHeader->howManyTeams-1) && player.canAnyonePlay())
+			teamsData.at(team).insert(PlayerColor(idx));
+	}
+
+	//remove single-member teams
+	vstd::erase_if(teamsData, [](std::set<PlayerColor> & elem) -> bool
+	{
+		return elem.size() <= 1;
+	});
+
+	//construct output
+	dest.setType(JsonNode::DATA_VECTOR);
+
+	for(const std::set<PlayerColor> & teamData : teamsData)
+	{
+		JsonNode team(JsonNode::DATA_VECTOR);
+		for(const PlayerColor & player : teamData)
+		{
+			JsonNode member(JsonNode::DATA_STRING);
+			member.String() = GameConstants::PLAYER_COLOR_NAMES[player.getNum()];
+			team.Vector().push_back(std::move(member));
+		}
+		dest.Vector().push_back(std::move(team));
+	}
+}
+
+void CMapFormatJson::serializeTriggeredEvents(JsonSerializeFormat & handler)
+{
+	handler.serializeString("victoryString", mapHeader->victoryMessage);
+	handler.serializeNumeric("victoryIconIndex", mapHeader->victoryIconIndex);
+
+	handler.serializeString("defeatString", mapHeader->defeatMessage);
+	handler.serializeNumeric("defeatIconIndex", mapHeader->defeatIconIndex);
+}
+
+
 void CMapFormatJson::readTriggeredEvents(JsonDeserializer & handler)
 void CMapFormatJson::readTriggeredEvents(JsonDeserializer & handler)
 {
 {
 	const JsonNode & input = handler.getCurrent();
 	const JsonNode & input = handler.getCurrent();
-	mapHeader->victoryMessage = input["victoryString"].String();
-	mapHeader->victoryIconIndex = input["victoryIconIndex"].Float();
 
 
-	mapHeader->defeatMessage = input["defeatString"].String();
-	mapHeader->defeatIconIndex = input["defeatIconIndex"].Float();
+	serializeTriggeredEvents(handler);
 
 
 	mapHeader->triggeredEvents.clear();
 	mapHeader->triggeredEvents.clear();
 
 
@@ -210,13 +406,11 @@ void CMapFormatJson::readTriggeredEvent(TriggeredEvent & event, const JsonNode &
 	event.trigger = EventExpression(source["condition"], JsonToCondition); // logical expression
 	event.trigger = EventExpression(source["condition"], JsonToCondition); // logical expression
 }
 }
 
 
-void CMapFormatJson::writeTriggeredEvents(JsonNode& output)
+void CMapFormatJson::writeTriggeredEvents(JsonSerializer & handler)
 {
 {
-	output["victoryString"].String() = mapHeader->victoryMessage;
-	output["victoryIconIndex"].Float() = mapHeader->victoryIconIndex;
+	JsonNode & output = handler.getCurrent();
 
 
-	output["defeatString"].String() = mapHeader->defeatMessage;
-	output["defeatIconIndex"].Float() = mapHeader->defeatIconIndex;
+	serializeTriggeredEvents(handler);
 
 
 	JsonMap & triggeredEvents = output["triggeredEvents"].Struct();
 	JsonMap & triggeredEvents = output["triggeredEvents"].Struct();
 
 
@@ -237,7 +431,6 @@ void CMapFormatJson::writeTriggeredEvent(const TriggeredEvent& event, JsonNode&
 	dest["condition"] = event.trigger.toJson(ConditionToJson);
 	dest["condition"] = event.trigger.toJson(ConditionToJson);
 }
 }
 
 
-
 ///CMapPatcher
 ///CMapPatcher
 CMapPatcher::CMapPatcher(JsonNode stream):
 CMapPatcher::CMapPatcher(JsonNode stream):
 	input(stream)
 	input(stream)
@@ -335,6 +528,22 @@ void CMapLoaderJson::readHeader()
 {
 {
 	//do not use map field here, use only mapHeader
 	//do not use map field here, use only mapHeader
 	JsonNode header = getFromArchive(HEADER_FILE_NAME);
 	JsonNode header = getFromArchive(HEADER_FILE_NAME);
+
+	int versionMajor = header["versionMajor"].Float();
+
+	if(versionMajor != VERSION_MAJOR)
+	{
+		logGlobal->errorStream() << "Unsupported map format version: " << versionMajor;
+		throw std::runtime_error("Unsupported map format version");
+	}
+
+	int versionMinor = header["versionMinor"].Float();
+
+	if(versionMinor > VERSION_MINOR)
+	{
+		logGlobal->traceStream() << "Too new map format revision: " << versionMinor << ". This map should work but some of map features may be ignored.";
+	}
+
 	JsonDeserializer handler(header);
 	JsonDeserializer handler(header);
 
 
 	mapHeader->version = EMapFormat::VCMI;//todo: new version field
 	mapHeader->version = EMapFormat::VCMI;//todo: new version field
@@ -354,13 +563,7 @@ void CMapLoaderJson::readHeader()
 		}
 		}
 	}
 	}
 
 
-	mapHeader->name = header["name"].String();
-	mapHeader->description = header["description"].String();
-
-	//todo: support arbitrary percentage
-
-	mapHeader->difficulty = HeaderDetail::difficultyReverseMap.at(header["difficulty"].String());
-	mapHeader->levelLimit = header["heroLevelLimit"].Float();
+	serializeHeader(handler);
 
 
 
 
 //	std::vector<bool> allowedHeroes;
 //	std::vector<bool> allowedHeroes;
@@ -368,99 +571,11 @@ void CMapLoaderJson::readHeader()
 
 
 	readTriggeredEvents(handler);
 	readTriggeredEvents(handler);
 
 
-	readPlayerInfo(handler);
 
 
 	readTeams(handler);
 	readTeams(handler);
 	//TODO: readHeader
 	//TODO: readHeader
 }
 }
 
 
-void CMapLoaderJson::readPlayerInfo(JsonDeserializer & handler)
-{
-	auto playersData = handler.enterStruct("players");
-
-	for(int player = 0; player < PlayerColor::PLAYER_LIMIT_I; player++)
-	{
-		PlayerInfo & info = mapHeader->players.at(player);
-
-		auto playerData = playersData.enterStruct(GameConstants::PLAYER_COLOR_NAMES[player]);
-
-		if(playerData.get().isNull())
-		{
-			info.canComputerPlay = false;
-			info.canHumanPlay = false;
-		}
-		else
-		{
-			//allowed factions
-
-		//	info.isFactionRandom =
-
-			info.canComputerPlay = true;
-			info.canHumanPlay = playerData.get()["canPlay"].String() != "AIOnly";
-
-			//placedHeroes
-
-			//mainTown
-
-			info.generateHeroAtMainTown = playerData.get()["generateHeroAtMainTown"].Bool();
-
-			//mainHero
-
-			//mainHeroPortrait
-
-			//mainCustomHeroName
-		}
-	}
-}
-
-void CMapLoaderJson::readTeams(JsonDeserializer & handler)
-{
-	auto teamsData = handler.enterStruct("teams");
-	const JsonNode & src = teamsData.get();
-
-    if(src.getType() != JsonNode::DATA_VECTOR)
-	{
-		// No alliances
-		if(src.getType() != JsonNode::DATA_NULL)
-			logGlobal->errorStream() << "Invalid teams field type";
-
-		mapHeader->howManyTeams = 0;
-		for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
-		{
-			if(mapHeader->players[i].canComputerPlay || mapHeader->players[i].canHumanPlay)
-			{
-				mapHeader->players[i].team = TeamID(mapHeader->howManyTeams++);
-			}
-		}
-	}
-	else
-	{
-		const JsonVector & srcVector = src.Vector();
-		mapHeader->howManyTeams = srcVector.size();
-
-		for(int team = 0; team < mapHeader->howManyTeams; team++)
-		{
-			for(const JsonNode & playerData : srcVector[team].Vector())
-			{
-				PlayerColor player = PlayerColor(vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, playerData.String()));
-				if(player.isValidPlayer())
-				{
-					if(mapHeader->players[player.getNum()].canAnyonePlay())
-					{
-						mapHeader->players[player.getNum()].team = TeamID(team);
-					}
-				}
-			}
-		}
-
-		for(PlayerInfo & player : mapHeader->players)
-		{
-			if(player.canAnyonePlay() && player.team == TeamID::NO_TEAM)
-				player.team = TeamID(mapHeader->howManyTeams++);
-		}
-
-	}
-}
 
 
 void CMapLoaderJson::readTerrainTile(const std::string& src, TerrainTile& tile)
 void CMapLoaderJson::readTerrainTile(const std::string& src, TerrainTile& tile)
 {
 {
@@ -775,20 +890,11 @@ void CMapSaverJson::writeHeader()
 		levels["underground"]["index"].Float() = 1;
 		levels["underground"]["index"].Float() = 1;
 	}
 	}
 
 
-	header["name"].String() = mapHeader->name;
-	header["description"].String() = mapHeader->description;
-
-
-	//todo: support arbitrary percentage
-
-	header["difficulty"].String() = HeaderDetail::difficultyForwardMap.at(mapHeader->difficulty);
-	header["heroLevelLimit"].Float() = mapHeader->levelLimit;
-
-	writeTriggeredEvents(header);
+	serializeHeader(handler);
 
 
-	writePlayerInfo(header);
+	writeTriggeredEvents(handler);
 
 
-	writeTeams(header);
+	writeTeams(handler);
 
 
 	//todo:	allowedHeroes;
 	//todo:	allowedHeroes;
 	//todo: placeholdedHeroes;
 	//todo: placeholdedHeroes;
@@ -796,73 +902,6 @@ void CMapSaverJson::writeHeader()
 	addToArchive(header, HEADER_FILE_NAME);
 	addToArchive(header, HEADER_FILE_NAME);
 }
 }
 
 
-void CMapSaverJson::writePlayerInfo(JsonNode & output)
-{
-	JsonNode & dest = output["players"];
-	dest.setType(JsonNode::DATA_STRUCT);
-
-	for(int player = 0; player < PlayerColor::PLAYER_LIMIT_I; player++)
-	{
-		const PlayerInfo & info = mapHeader->players[player];
-
-		if(info.canAnyonePlay())
-			writePlayerInfo(info, dest[GameConstants::PLAYER_COLOR_NAMES[player]]);
-	}
-}
-
-void CMapSaverJson::writePlayerInfo(const PlayerInfo & info, JsonNode & output)
-{
-	//allowed factions
-
-	output["canPlay"].String() = info.canHumanPlay ? "PlayerOrAI" : "AIOnly";
-
-	//mainTown
-	output["generateHeroAtMainTown"].Bool() = info.generateHeroAtMainTown;
-
-	//mainHero
-
-	//towns
-	//heroes
-}
-
-void CMapSaverJson::writeTeams(JsonNode& output)
-{
-	JsonNode & dest = output["teams"];
-	std::vector<std::set<PlayerColor>> teamsData;
-
-	teamsData.resize(mapHeader->howManyTeams);
-
-	//get raw data
-	for(int idx = 0; idx < mapHeader->players.size(); idx++)
-	{
-		const PlayerInfo & player = mapHeader->players.at(idx);
-		int team = player.team.getNum();
-		if(vstd::iswithin(team, 0, mapHeader->howManyTeams-1) && player.canAnyonePlay())
-			teamsData.at(team).insert(PlayerColor(idx));
-	}
-
-	//remove single-member teams
-	vstd::erase_if(teamsData, [](std::set<PlayerColor> & elem) -> bool
-	{
-		return elem.size() <= 1;
-	});
-
-	//construct output
-	dest.setType(JsonNode::DATA_VECTOR);
-
-	for(const std::set<PlayerColor> & teamData : teamsData)
-	{
-		JsonNode team(JsonNode::DATA_VECTOR);
-		for(const PlayerColor & player : teamData)
-		{
-			JsonNode member(JsonNode::DATA_STRING);
-			member.String() = GameConstants::PLAYER_COLOR_NAMES[player.getNum()];
-			team.Vector().push_back(std::move(member));
-		}
-		dest.Vector().push_back(std::move(team));
-	}
-}
-
 const std::string CMapSaverJson::writeTerrainTile(const TerrainTile & tile)
 const std::string CMapSaverJson::writeTerrainTile(const TerrainTile & tile)
 {
 {
 	using namespace TerrainDetail;
 	using namespace TerrainDetail;

+ 29 - 32
lib/mapping/MapFormatJson.h

@@ -22,6 +22,8 @@ struct TerrainTile;
 struct PlayerInfo;
 struct PlayerInfo;
 class CGObjectInstance;
 class CGObjectInstance;
 class AObjectTypeHandler;
 class AObjectTypeHandler;
+
+class JsonSerializeFormat;
 class JsonDeserializer;
 class JsonDeserializer;
 class JsonSerializer;
 class JsonSerializer;
 
 
@@ -44,6 +46,30 @@ protected:
 	 */
 	 */
 	CMapHeader * mapHeader;
 	CMapHeader * mapHeader;
 
 
+	void serializeAllowedFactions(JsonSerializeFormat & handler, std::set<TFaction> & value);
+
+	///common part of header saving/loading
+	void serializeHeader(JsonSerializeFormat & handler);
+
+	///player information saving/loading
+	void serializePlayerInfo(JsonSerializeFormat & handler);
+
+	/**
+	 * Reads team settings to header
+	 * @param input serialized header
+	 */
+	void readTeams(JsonDeserializer & handler);
+
+	/**
+	 * Saves team settings to header
+	 * @param output serialized header
+	 */
+	void writeTeams(JsonSerializer & handler);
+
+
+	///common part triggered events of saving/loading
+	void serializeTriggeredEvents(JsonSerializeFormat & handler);
+
 	/**
 	/**
 	 * Reads triggered events, including victory/loss conditions
 	 * Reads triggered events, including victory/loss conditions
 	 */
 	 */
@@ -52,7 +78,7 @@ protected:
 	/**
 	/**
 	 * Writes triggered events, including victory/loss conditions
 	 * Writes triggered events, including victory/loss conditions
 	 */
 	 */
-	void writeTriggeredEvents(JsonNode & output);
+	void writeTriggeredEvents(JsonSerializer & handler);
 
 
 	/**
 	/**
 	 * Reads one of triggered events
 	 * Reads one of triggered events
@@ -137,26 +163,15 @@ private:
 
 
 	si32 getIdentifier(const std::string & type, const std::string & name);
 	si32 getIdentifier(const std::string & type, const std::string & name);
 
 
-	/**
-	 * Reads complete map.
-	 */
-	void readMap();
-
 	/**
 	/**
 	 * Reads the map header.
 	 * Reads the map header.
 	 */
 	 */
 	void readHeader();
 	void readHeader();
 
 
 	/**
 	/**
-	 * Reads player information.
-	 */
-	void readPlayerInfo(JsonDeserializer & handler);
-
-	/**
-	 * Reads team settings to header
-	 * @param input serialized header
+	 * Reads complete map.
 	 */
 	 */
-	void readTeams(JsonDeserializer & handler);
+	void readMap();
 
 
 	void readTerrainTile(const std::string & src, TerrainTile & tile);
 	void readTerrainTile(const std::string & src, TerrainTile & tile);
 
 
@@ -205,24 +220,6 @@ private:
 	 */
 	 */
 	void writeHeader();
 	void writeHeader();
 
 
-	/**
-	 * Saves all players info to header
-	 * @param output serialized header
-	 */
-	void writePlayerInfo(JsonNode & output);
-
-	/**
-	 * Saves one player info
-	 * @param output empty object
-	 */
-	void writePlayerInfo(const PlayerInfo & info, JsonNode & output);
-
-	/**
-	 * Saves team settings to header
-	 * @param output serialized header
-	 */
-	void writeTeams(JsonNode & output);
-
 	/**
 	/**
 	 * Encodes one tile into string
 	 * Encodes one tile into string
 	 * @param tile tile to serialize
 	 * @param tile tile to serialize

+ 86 - 1
lib/serializer/JsonDeserializer.cpp

@@ -15,9 +15,94 @@
 #include "../JsonNode.h"
 #include "../JsonNode.h"
 
 
 JsonDeserializer::JsonDeserializer(JsonNode & root_):
 JsonDeserializer::JsonDeserializer(JsonNode & root_):
-	JsonSerializeFormat(root_)
+	JsonSerializeFormat(root_, false)
 {
 {
 
 
 }
 }
 
 
+void JsonDeserializer::serializeBool(const std::string & fieldName, bool & value)
+{
+	value = current->operator[](fieldName).Bool();
+}
+
+void JsonDeserializer::serializeBoolEnum(const std::string & fieldName, const std::string & trueValue, const std::string & falseValue, bool & value)
+{
+	const JsonNode & tmp = current->operator[](fieldName);
+
+	value = tmp.String() == trueValue;
+}
+
+void JsonDeserializer::serializeFloat(const std::string & fieldName, double & value)
+{
+	value = current->operator[](fieldName).Float();
+}
+
+void JsonDeserializer::serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value)
+{
+	const std::string & valueName = current->operator[](fieldName).String();
+
+	si32 rawValue = vstd::find_pos(enumMap, valueName);
+	if(rawValue < 0)
+		value = defaultValue;
+	else
+		value = rawValue;
+}
+
+void JsonDeserializer::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value)
+{
+	const JsonNode & field = current->operator[](fieldName);
+	if(field.isNull())
+		return;
+
+	const JsonVector & anyOf = field["anyOf"].Vector();
+	const JsonVector & allOf = field["allOf"].Vector();
+	const JsonVector & noneOf = field["noneOf"].Vector();
+
+
+	if(anyOf.empty() && allOf.empty())
+	{
+		//permissive mode
+		value = standard;
+	}
+	else
+	{
+		//restrictive mode
+		value.clear();
+		value.resize(standard.size(), false);
+
+		for(size_t index = 0; index < anyOf.size(); index++)
+		{
+			const std::string & identifier = anyOf[index].String();
+
+			si32 rawId = decoder(identifier);
+			if(rawId >= 0)
+				value[rawId] = true;
+		}
+
+
+		for(size_t index = 0; index < allOf.size(); index++)
+		{
+			const std::string & identifier = allOf[index].String();
+
+			si32 rawId = decoder(identifier);
+			if(rawId >=0)
+				value[rawId] = true;
+		}
+	}
+
+	for(size_t index = 0; index < noneOf.size(); index++)
+	{
+		const std::string & identifier = noneOf[index].String();
+
+		si32 rawId = decoder(identifier);
+		if(rawId >=0 )
+			value[rawId] = false;
+	}
+
+}
+
+void JsonDeserializer::serializeString(const std::string & fieldName, std::string & value)
+{
+	value = current->operator[](fieldName).String();
+}
 
 

+ 9 - 2
lib/serializer/JsonDeserializer.h

@@ -17,7 +17,14 @@ class JsonNode;
 class JsonDeserializer: public JsonSerializeFormat
 class JsonDeserializer: public JsonSerializeFormat
 {
 {
 public:
 public:
-	static const bool saving = false;
-
 	JsonDeserializer(JsonNode & root_);
 	JsonDeserializer(JsonNode & root_);
+
+	void serializeBool(const std::string & fieldName, bool & value) override;
+	void serializeBoolEnum(const std::string & fieldName, const std::string & trueValue, const std::string & falseValue, bool & value) override;
+	void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value) override;
+	void serializeString(const std::string & fieldName, std::string & value) override;
+
+protected:
+	void serializeFloat(const std::string & fieldName, double & value) override;
+	void serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value) override;
 };
 };

+ 2 - 1
lib/serializer/JsonSerializeFormat.cpp

@@ -66,7 +66,8 @@ JsonSerializeFormat * JsonStructSerializer::operator->()
 
 
 
 
 //JsonSerializeFormat
 //JsonSerializeFormat
-JsonSerializeFormat::JsonSerializeFormat(JsonNode & root_):
+JsonSerializeFormat::JsonSerializeFormat(JsonNode & root_, const bool saving_):
+	saving(saving_),
 	root(&root_),
 	root(&root_),
 	current(root)
 	current(root)
 {
 {

+ 54 - 4
lib/serializer/JsonSerializeFormat.h

@@ -36,27 +36,77 @@ private:
 	friend class JsonSerializeFormat;
 	friend class JsonSerializeFormat;
 };
 };
 
 
-class JsonSerializeFormat
+class JsonSerializeFormat: public boost::noncopyable
 {
 {
 public:
 public:
-	JsonSerializeFormat(JsonNode & root_);
+	///user-provided callback to resolve string identifier
+	///returns resolved identifier or -1 on error
+	typedef std::function<si32(const std::string &)> TDecoder;
+
+	///user-provided callback to get string identifier
+	///may assume that object index is valid
+	typedef std::function<std::string(si32)> TEncoder;
+
+	const bool saving;
+
+	JsonSerializeFormat() = delete;
 	virtual ~JsonSerializeFormat() = default;
 	virtual ~JsonSerializeFormat() = default;
 
 
 	JsonNode & getRoot()
 	JsonNode & getRoot()
 	{
 	{
-		return *root;
+		return * root;
 	};
 	};
 
 
 	JsonNode & getCurrent()
 	JsonNode & getCurrent()
 	{
 	{
-		return *current;
+		return * current;
 	};
 	};
 
 
 	JsonStructSerializer enterStruct(const std::string & fieldName);
 	JsonStructSerializer enterStruct(const std::string & fieldName);
 
 
+	virtual void serializeBool(const std::string & fieldName, bool & value) = 0;
+	virtual void serializeBoolEnum(const std::string & fieldName, const std::string & trueValue, const std::string & falseValue, bool & value) = 0;
+
+	/** @brief Restrictive serialization of Logical identifier condition (only "anyOf" used), full deserialization
+	 *
+	 * @param fieldName
+	 * @param decoder resolve callback, should report errors itself and do not throw
+	 * @param encoder encode callback, should report errors itself and do not throw
+	 * @param value target value, must be resized properly
+	 *
+	 */
+	virtual void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value) = 0;
+
+
+	template <typename T>
+	void serializeNumericEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const T defaultValue, T & value)
+	{
+		si32 temp = value;
+		serializeIntEnum(fieldName, enumMap, defaultValue, temp);
+		if(!saving)
+			value = temp;
+	};
+
+	template <typename T>
+	void serializeNumeric(const std::string & fieldName, T & value)
+	{
+		double temp = value;
+		serializeFloat(fieldName, temp);
+		if(!saving)
+			value = temp;
+	};
+
+	virtual void serializeString(const std::string & fieldName, std::string & value) = 0;
+
 protected:
 protected:
 	JsonNode * root;
 	JsonNode * root;
 	JsonNode * current;
 	JsonNode * current;
+
+	JsonSerializeFormat(JsonNode & root_, const bool saving_);
+
+	virtual void serializeFloat(const std::string & fieldName, double & value) = 0;
+
+	virtual void serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value) = 0;
 private:
 private:
 	friend class JsonStructSerializer;
 	friend class JsonStructSerializer;
 };
 };

+ 43 - 1
lib/serializer/JsonSerializer.cpp

@@ -15,9 +15,51 @@
 #include "../JsonNode.h"
 #include "../JsonNode.h"
 
 
 JsonSerializer::JsonSerializer(JsonNode & root_):
 JsonSerializer::JsonSerializer(JsonNode & root_):
-	JsonSerializeFormat(root_)
+	JsonSerializeFormat(root_, true)
 {
 {
 
 
 }
 }
 
 
+void JsonSerializer::serializeBool(const std::string & fieldName, bool & value)
+{
+	current->operator[](fieldName).Bool() = value;
+}
+
+void JsonSerializer::serializeBoolEnum(const std::string & fieldName, const std::string & trueValue, const std::string & falseValue, bool & value)
+{
+	current->operator[](fieldName).String() = value ? trueValue : falseValue;
+}
+
+void JsonSerializer::serializeFloat(const std::string & fieldName, double & value)
+{
+	current->operator[](fieldName).Float() = value;
+}
+
+void JsonSerializer::serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value)
+{
+	current->operator[](fieldName).String() = enumMap.at(value);
+}
+
+void JsonSerializer::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value)
+{
+	assert(standard.size() == value.size());
+	if(standard == value)
+		return;
+	auto & target = current->operator[](fieldName)["anyOf"].Vector();
+	for(si32 idx = 0; idx < value.size(); idx ++)
+	{
+		if(value[idx])
+		{
+			JsonNode val(JsonNode::DATA_STRING);
+			val.String() = encoder(idx);
+			target.push_back(std::move(val));
+		}
+	}
+}
+
+
+void JsonSerializer::serializeString(const std::string & fieldName, std::string & value)
+{
+	current->operator[](fieldName).String() = value;
+}
 
 

+ 7 - 2
lib/serializer/JsonSerializer.h

@@ -17,9 +17,14 @@ class JsonNode;
 class JsonSerializer: public JsonSerializeFormat
 class JsonSerializer: public JsonSerializeFormat
 {
 {
 public:
 public:
-	static const bool saving = true;
-
 	JsonSerializer(JsonNode & root_);
 	JsonSerializer(JsonNode & root_);
 
 
+	void serializeBool(const std::string & fieldName, bool & value) override;
+	void serializeBoolEnum(const std::string & fieldName, const std::string & trueValue, const std::string & falseValue, bool & value) override;
+	void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value) override;
+	void serializeString(const std::string & fieldName, std::string & value) override;
 
 
+protected:
+	void serializeFloat(const std::string & fieldName, double & value) override;
+	void serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value) override;
 };
 };

+ 3 - 2
test/CMapFormatTest.cpp

@@ -61,12 +61,13 @@ BOOST_GLOBAL_FIXTURE(CMapTestFixture);
 BOOST_AUTO_TEST_CASE(CMapFormatVCMI_Simple)
 BOOST_AUTO_TEST_CASE(CMapFormatVCMI_Simple)
 {
 {
 	logGlobal->info("CMapFormatVCMI_Simple start");
 	logGlobal->info("CMapFormatVCMI_Simple start");
+	BOOST_TEST_CHECKPOINT("CMapFormatVCMI_Simple start");
 	CMemoryBuffer serializeBuffer;
 	CMemoryBuffer serializeBuffer;
 	{
 	{
 		CMapSaverJson saver(&serializeBuffer);
 		CMapSaverJson saver(&serializeBuffer);
 		saver.saveMap(initialMap);
 		saver.saveMap(initialMap);
 	}
 	}
-
+	BOOST_TEST_CHECKPOINT("CMapFormatVCMI_Simple serialized");
 	#if 1
 	#if 1
 	{
 	{
 		auto path = VCMIDirs::get().userDataPath()/"test.vmap";
 		auto path = VCMIDirs::get().userDataPath()/"test.vmap";
@@ -79,7 +80,7 @@ BOOST_AUTO_TEST_CASE(CMapFormatVCMI_Simple)
 
 
 		logGlobal->infoStream() << "Test map has been saved to " << path;
 		logGlobal->infoStream() << "Test map has been saved to " << path;
 	}
 	}
-
+	BOOST_TEST_CHECKPOINT("CMapFormatVCMI_Simple saved");
 
 
 	#endif // 1
 	#endif // 1