diff --git a/Global.h b/Global.h index 4089f4425..43523e3d1 100644 --- a/Global.h +++ b/Global.h @@ -55,7 +55,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); # define VCMI_UNIX # define VCMI_XDG # ifdef __ANDROID__ -# define VCMI_ANDROID +# define VCMI_ANDROID # endif #elif defined(__FreeBSD_kernel__) || defined(__FreeBSD__) # define VCMI_UNIX @@ -285,7 +285,7 @@ std::ostream & operator<<(std::ostream & out, const std::vector & container) namespace vstd { - + // combine hashes. Present in boost but not in std template inline void hash_combine(std::size_t& seed, const T& v) @@ -293,7 +293,7 @@ namespace vstd std::hash hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); } - + //returns true if container c contains item i template bool contains(const Container & c, const Item &i) @@ -505,7 +505,7 @@ namespace vstd void erase_if(std::set &setContainer, Predicate pred) { auto itr = setContainer.begin(); - auto endItr = setContainer.end(); + auto endItr = setContainer.end(); while(itr != endItr) { auto tmpItr = itr++; @@ -519,7 +519,7 @@ namespace vstd void erase_if(std::map &container, Predicate pred) { auto itr = container.begin(); - auto endItr = container.end(); + auto endItr = container.end(); while(itr != endItr) { auto tmpItr = itr++; @@ -554,7 +554,7 @@ namespace vstd return vf(lhs) < vf(rhs); }); } - + //Returns iterator to the element for which the value of ValueFunction is maximal template auto maxElementByFun(const ForwardRange& rng, ValueFunction vf) -> decltype(std::begin(rng)) @@ -627,7 +627,7 @@ namespace vstd { if(index < r.size()) return r[index]; - + return defaultValue; } @@ -668,12 +668,12 @@ namespace vstd boost::sort(vec); vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); } - + template void concatenate(std::vector &dest, const std::vector &src) { dest.reserve(dest.size() + src.size()); - dest.insert(dest.end(), src.begin(), src.end()); + dest.insert(dest.end(), src.begin(), src.end()); } template diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index ef5e1e9b6..be534cff8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -104,6 +104,10 @@ set(lib_SRCS Connection.cpp NetPacksLib.cpp + serializer/JsonSerializer.cpp + serializer/JsonDeserializer.cpp + serializer/JsonSerializeFormat.cpp + registerTypes/RegisterTypes.cpp registerTypes/TypesClientPacks1.cpp registerTypes/TypesClientPacks2.cpp diff --git a/lib/VCMI_lib.cbp b/lib/VCMI_lib.cbp index e68ce99a8..946c88d88 100644 --- a/lib/VCMI_lib.cbp +++ b/lib/VCMI_lib.cbp @@ -152,6 +152,7 @@ + @@ -315,6 +316,12 @@ + + + + + + diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index 5fe5bb28b..ba227a96f 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -23,8 +23,31 @@ #include "../mapObjects/CObjectClassesHandler.h" #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGTownInstance.h" - #include "../StringConstants.h" +#include "../serializer/JsonDeserializer.h" +#include "../serializer/JsonSerializer.h" + +namespace HeaderDetail +{ + static const std::map difficultyReverseMap = + { + {"", 1}, + {"EASY", 0}, + {"NORMAL", 1}, + {"HARD", 2}, + {"EXPERT", 3}, + {"IMPOSSIBLE", 4} + }; + + static const std::map difficultyForwardMap = + { + {0, "EASY"}, + {1, "NORMAL"}, + {2, "HARD"}, + {3, "EXPERT"}, + {4, "IMPOSSIBLE"} + }; +} namespace TriggeredEventsDetail { @@ -46,7 +69,7 @@ namespace TriggeredEventsDetail auto pos = vstd::find_pos(conditionNames, conditionName); - event.condition = EventCondition::EWinLoseType(pos)); + event.condition = EventCondition::EWinLoseType(pos); if (node.Vector().size() > 1) { const JsonNode & data = node.Vector()[1]; @@ -272,7 +295,7 @@ std::unique_ptr CMapLoaderJson::loadMapHeader() return std::move(mapHeader); } -const JsonNode CMapLoaderJson::readJson(const std::string & archiveFilename) +const JsonNode CMapLoaderJson::getFromArchive(const std::string & archiveFilename) { ResourceID resource(archiveFilename, EResType::TEXT); @@ -306,93 +329,89 @@ void CMapLoaderJson::readMap() void CMapLoaderJson::readHeader() { //do not use map field here, use only mapHeader - const JsonNode header = readJson(HEADER_FILE_NAME); + JsonNode header = getFromArchive(HEADER_FILE_NAME); + JsonDeserializer handler(header); mapHeader->version = EMapFormat::VCMI;//todo: new version field //todo: multilevel map load support - const JsonNode levels = header["mapLevels"]; - mapHeader->height = levels["surface"]["height"].Float(); - mapHeader->width = levels["surface"]["width"].Float(); - mapHeader->twoLevel = !levels["underground"].isNull(); + { + auto levels = handler.enterStruct("mapLevels"); + + { + auto surface = levels.enterStruct("surface"); + mapHeader->height = surface.get()["height"].Float(); + mapHeader->width = surface.get()["width"].Float(); + } + { + auto underground = levels.enterStruct("underground"); + mapHeader->twoLevel = !underground.get().isNull(); + } + } mapHeader->name = header["name"].String(); mapHeader->description = header["description"].String(); //todo: support arbitrary percentage - static const std::map difficultyMap = - { - {"", 1}, - {"EASY", 0}, - {"NORMAL", 1}, - {"HARD", 2}, - {"EXPERT", 3}, - {"IMPOSSIBLE", 4} - }; - - mapHeader->difficulty = difficultyMap.at(header["difficulty"].String()); - mapHeader->levelLimit = header["levelLimit"].Float(); + mapHeader->difficulty = HeaderDetail::difficultyReverseMap.at(header["difficulty"].String()); + mapHeader->levelLimit = header["heroLevelLimit"].Float(); // std::vector allowedHeroes; // std::vector placeholdedHeroes; readTriggeredEvents(header); - readPlayerInfo(header); - readTeams(header); + + readPlayerInfo(handler); + + readTeams(handler); //TODO: readHeader } -void CMapLoaderJson::readPlayerInfo(const JsonNode & input) +void CMapLoaderJson::readPlayerInfo(JsonDeserializer & handler) { - const JsonNode & src = input["players"]; - int howManyTeams = 0; + auto playersData = handler.enterStruct("players"); for(int player = 0; player < PlayerColor::PLAYER_LIMIT_I; player++) { PlayerInfo & info = mapHeader->players.at(player); - const JsonNode & playerSrc = src[GameConstants::PLAYER_COLOR_NAMES[player]]; + auto playerData = playersData.enterStruct(GameConstants::PLAYER_COLOR_NAMES[player]); - if(playerSrc.isNull()) + if(playerData.get().isNull()) { info.canComputerPlay = false; info.canHumanPlay = false; } else { - readPlayerInfo(info, playerSrc); + //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 } } - mapHeader->howManyTeams = howManyTeams; } -void CMapLoaderJson::readPlayerInfo(PlayerInfo& info, const JsonNode& input) +void CMapLoaderJson::readTeams(JsonDeserializer & handler) { - //allowed factions - -// info.isFactionRandom = - - info.canComputerPlay = true; - info.canHumanPlay = input["canPlay"].String() != "AIOnly"; - - //placedHeroes - - //mainTown - - info.generateHeroAtMainTown = input["generateHeroAtMainTown"].Bool(); - - //mainHero - - //mainHeroPortrait - - //mainCustomHeroName -} - -void CMapLoaderJson::readTeams(const JsonNode& input) -{ - const JsonNode & src = input["teams"]; + auto teamsData = handler.enterStruct("teams"); + const JsonNode & src = teamsData.get(); if(src.getType() != JsonNode::DATA_VECTOR) { @@ -566,12 +585,12 @@ void CMapLoaderJson::readTerrainLevel(const JsonNode& src, const int index) void CMapLoaderJson::readTerrain() { { - const JsonNode surface = readJson("surface_terrain.json"); + const JsonNode surface = getFromArchive("surface_terrain.json"); readTerrainLevel(surface, 0); } if(map->twoLevel) { - const JsonNode underground = readJson("underground_terrain.json"); + const JsonNode underground = getFromArchive("underground_terrain.json"); readTerrainLevel(underground, 1); } @@ -642,7 +661,7 @@ void CMapLoaderJson::readObjects() std::vector> loaders;//todo: optimize MapObjectLoader memory layout - const JsonNode data = readJson(OBJECTS_FILE_NAME); + const JsonNode data = getFromArchive(OBJECTS_FILE_NAME); //get raw data for(const auto & p : data.Struct()) @@ -703,6 +722,8 @@ void CMapSaverJson::saveMap(const std::unique_ptr& map) void CMapSaverJson::writeHeader() { JsonNode header; + JsonSerializer handler(header); + header["versionMajor"].Float() = VERSION_MAJOR; header["versionMinor"].Float() = VERSION_MINOR; @@ -724,17 +745,9 @@ void CMapSaverJson::writeHeader() //todo: support arbitrary percentage - static const std::map difficultyMap = - { - {0, "EASY"}, - {1, "NORMAL"}, - {2, "HARD"}, - {3, "EXPERT"}, - {4, "IMPOSSIBLE"} - }; - header["difficulty"].String() = difficultyMap.at(map->difficulty); - header["levelLimit"].Float() = map->levelLimit; + header["difficulty"].String() = HeaderDetail::difficultyForwardMap.at(map->difficulty); + header["heroLevelLimit"].Float() = map->levelLimit; writeTriggeredEvents(header); @@ -789,17 +802,15 @@ void CMapSaverJson::writeTeams(JsonNode& output) { const PlayerInfo & player = map->players.at(idx); int team = player.team.getNum(); - if(vstd::isbetween(team, 0, map->howManyTeams-1) && player.canAnyonePlay()) + if(vstd::iswithin(team, 0, map->howManyTeams-1) && player.canAnyonePlay()) teamsData.at(team).insert(PlayerColor(idx)); } -//just an optimization but breaks test -#if 0 + //remove single-member teams vstd::erase_if(teamsData, [](std::set & elem) -> bool { return elem.size() <= 1; }); -#endif //construct output dest.setType(JsonNode::DATA_VECTOR); diff --git a/lib/mapping/MapFormatJson.h b/lib/mapping/MapFormatJson.h index de3fabb45..2289529a1 100644 --- a/lib/mapping/MapFormatJson.h +++ b/lib/mapping/MapFormatJson.h @@ -22,6 +22,8 @@ struct TerrainTile; struct PlayerInfo; class CGObjectInstance; class AObjectTypeHandler; +class JsonDeserializer; +class JsonSerializer; class DLL_LINKAGE CMapFormatJson { @@ -149,18 +151,13 @@ private: /** * Reads player information. */ - void readPlayerInfo(const JsonNode & input); - - /** - * Reads one player information. - */ - void readPlayerInfo(PlayerInfo & info, const JsonNode & input); + void readPlayerInfo(JsonDeserializer & handler); /** * Reads team settings to header * @param input serialized header */ - void readTeams(const JsonNode & input); + void readTeams(JsonDeserializer & handler); void readTerrainTile(const std::string & src, TerrainTile & tile); @@ -173,7 +170,7 @@ private: */ void readObjects(); - const JsonNode readJson(const std::string & archiveFilename); + const JsonNode getFromArchive(const std::string & archiveFilename); CInputStream * buffer; std::shared_ptr ioApi; diff --git a/lib/serializer/JsonDeserializer.cpp b/lib/serializer/JsonDeserializer.cpp new file mode 100644 index 000000000..5f1737434 --- /dev/null +++ b/lib/serializer/JsonDeserializer.cpp @@ -0,0 +1,23 @@ +/* + * JsonDeserializer.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + + +#include "StdInc.h" +#include "JsonDeserializer.h" + +#include "../JsonNode.h" + +JsonDeserializer::JsonDeserializer(JsonNode & root_): + JsonSerializeFormat(root_) +{ + +} + + diff --git a/lib/serializer/JsonDeserializer.h b/lib/serializer/JsonDeserializer.h new file mode 100644 index 000000000..91bbae0bc --- /dev/null +++ b/lib/serializer/JsonDeserializer.h @@ -0,0 +1,23 @@ +/* + * JsonDeserializer.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "JsonSerializeFormat.h" + +class JsonNode; + +class JsonDeserializer: public JsonSerializeFormat +{ +public: + static const bool saving = false; + + JsonDeserializer(JsonNode & root_); +}; diff --git a/lib/serializer/JsonSerializeFormat.cpp b/lib/serializer/JsonSerializeFormat.cpp new file mode 100644 index 000000000..68d7c0e71 --- /dev/null +++ b/lib/serializer/JsonSerializeFormat.cpp @@ -0,0 +1,81 @@ +/* + * JsonSerializeFormat.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + + +#include "StdInc.h" +#include "JsonSerializeFormat.h" + +#include "../JsonNode.h" + +//JsonStructSerializer +JsonStructSerializer::JsonStructSerializer(JsonStructSerializer&& other): + restoreState(false), + owner(other.owner), + parentNode(other.parentNode), + thisNode(other.thisNode) +{ + +} + +JsonStructSerializer::~JsonStructSerializer() +{ + if(restoreState) + owner.current = parentNode; +} + +JsonStructSerializer::JsonStructSerializer(JsonSerializeFormat& owner_, const std::string& fieldName): + restoreState(true), + owner(owner_), + parentNode(owner.current), + thisNode(&(parentNode->operator[](fieldName))) +{ + owner.current = thisNode; +} + +JsonStructSerializer::JsonStructSerializer(JsonStructSerializer & parent, const std::string & fieldName): + restoreState(true), + owner(parent.owner), + parentNode(parent.thisNode), + thisNode(&(parentNode->operator[](fieldName))) +{ + owner.current = thisNode; +} + + +JsonStructSerializer JsonStructSerializer::enterStruct(const std::string & fieldName) +{ + return JsonStructSerializer(*this, fieldName); +} + +JsonNode& JsonStructSerializer::get() +{ + return *thisNode; +} + +JsonSerializeFormat * JsonStructSerializer::operator->() +{ + return &owner; +} + + +//JsonSerializeFormat +JsonSerializeFormat::JsonSerializeFormat(JsonNode & root_): + root(&root_), + current(root) +{ + +} + +JsonStructSerializer JsonSerializeFormat::enterStruct(const std::string & fieldName) +{ + JsonStructSerializer res(*this, fieldName); + + return res; +} diff --git a/lib/serializer/JsonSerializeFormat.h b/lib/serializer/JsonSerializeFormat.h new file mode 100644 index 000000000..d6d27e470 --- /dev/null +++ b/lib/serializer/JsonSerializeFormat.h @@ -0,0 +1,58 @@ +/* + * JsonSerializeFormat.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +class JsonNode; + +class JsonSerializeFormat; + +class JsonStructSerializer: public boost::noncopyable +{ +public: + JsonStructSerializer(JsonStructSerializer && other); + virtual ~JsonStructSerializer(); + + JsonStructSerializer enterStruct(const std::string & fieldName); + + JsonNode & get(); + + JsonSerializeFormat * operator->(); +private: + JsonStructSerializer(JsonSerializeFormat & owner_, const std::string & fieldName); + JsonStructSerializer(JsonStructSerializer & parent, const std::string & fieldName); + + bool restoreState; + JsonSerializeFormat & owner; + JsonNode * parentNode; + JsonNode * thisNode; + friend class JsonSerializeFormat; +}; + +class JsonSerializeFormat +{ +public: + JsonSerializeFormat(JsonNode & root_); + virtual ~JsonSerializeFormat() = default; + + JsonNode & getRoot() + { + return *root; + }; + + JsonStructSerializer enterStruct(const std::string & fieldName); + +protected: + JsonNode * root; + JsonNode * current; +private: + friend class JsonStructSerializer; +}; + diff --git a/lib/serializer/JsonSerializer.cpp b/lib/serializer/JsonSerializer.cpp new file mode 100644 index 000000000..21755321c --- /dev/null +++ b/lib/serializer/JsonSerializer.cpp @@ -0,0 +1,23 @@ +/* + * JsonSerializer.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + + +#include "StdInc.h" +#include "JsonSerializer.h" + +#include "../JsonNode.h" + +JsonSerializer::JsonSerializer(JsonNode & root_): + JsonSerializeFormat(root_) +{ + +} + + diff --git a/lib/serializer/JsonSerializer.h b/lib/serializer/JsonSerializer.h new file mode 100644 index 000000000..0f4a9e925 --- /dev/null +++ b/lib/serializer/JsonSerializer.h @@ -0,0 +1,25 @@ +/* + * JsonSerializer.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "JsonSerializeFormat.h" + +class JsonNode; + +class JsonSerializer: public JsonSerializeFormat +{ +public: + static const bool saving = true; + + JsonSerializer(JsonNode & root_); + + +};