Pārlūkot izejas kodu

Moved json files to new directory, split on per-class basis

Ivan Savenko 1 gadu atpakaļ
vecāks
revīzija
c3957c2c2a
100 mainītis faili ar 2136 papildinājumiem un 637 dzēšanām
  1. 0 1
      client/CMusicHandler.cpp
  2. 0 1
      client/CPlayerInterface.cpp
  3. 2 1
      client/gui/InterfaceObjectConfigurable.cpp
  4. 1 1
      client/gui/InterfaceObjectConfigurable.h
  5. 0 1
      client/mainmenu/CMainMenu.cpp
  6. 1 1
      client/mainmenu/CMainMenu.h
  7. 1 1
      client/render/CAnimation.cpp
  8. 1 1
      client/render/ColorFilter.cpp
  9. 3 2
      client/render/Colors.cpp
  10. 1 1
      client/render/Graphics.cpp
  11. 1 1
      client/renderSDL/CBitmapHanFont.cpp
  12. 1 1
      client/renderSDL/CTrueTypeFont.cpp
  13. 1 1
      client/renderSDL/SDLImage.cpp
  14. 1 0
      client/renderSDL/ScreenHandler.cpp
  15. 2 0
      launcher/jsonutils.cpp
  16. 2 1
      launcher/jsonutils.h
  17. 0 1
      launcher/modManager/cmodlist.cpp
  18. 0 1
      lib/BasicTypes.cpp
  19. 1 1
      lib/BattleFieldHandler.cpp
  20. 1 1
      lib/CArtHandler.cpp
  21. 1 1
      lib/CBonusTypeHandler.cpp
  22. 4 3
      lib/CConfigHandler.cpp
  23. 1 1
      lib/CConfigHandler.h
  24. 1 0
      lib/CCreatureHandler.cpp
  25. 0 1
      lib/CCreatureHandler.h
  26. 1 1
      lib/CHeroHandler.cpp
  27. 15 7
      lib/CMakeLists.txt
  28. 1 3
      lib/CSkillHandler.cpp
  29. 0 1
      lib/CStack.h
  30. 1 1
      lib/CTownHandler.cpp
  31. 1 1
      lib/GameSettings.cpp
  32. 1 2
      lib/LogicalExpression.h
  33. 1 1
      lib/ObstacleHandler.cpp
  34. 0 1
      lib/ResourceSet.cpp
  35. 1 1
      lib/RiverHandler.cpp
  36. 1 1
      lib/RoadHandler.cpp
  37. 1 1
      lib/ScriptHandler.h
  38. 1 1
      lib/TerrainHandler.cpp
  39. 1 0
      lib/bonuses/Bonus.cpp
  40. 2 5
      lib/bonuses/BonusEnum.cpp
  41. 1 2
      lib/bonuses/BonusList.cpp
  42. 1 1
      lib/bonuses/BonusParams.h
  43. 1 0
      lib/bonuses/Limiters.cpp
  44. 2 1
      lib/bonuses/Updaters.cpp
  45. 0 1
      lib/campaign/CampaignState.cpp
  46. 1 1
      lib/filesystem/AdapterLoaders.cpp
  47. 1 1
      lib/filesystem/Filesystem.cpp
  48. 0 1
      lib/filesystem/ResourcePath.cpp
  49. 1 0
      lib/gameState/CGameState.cpp
  50. 430 0
      lib/json/JsonNode.cpp
  51. 2 93
      lib/json/JsonNode.h
  52. 465 0
      lib/json/JsonParser.cpp
  53. 82 0
      lib/json/JsonParser.h
  54. 16 15
      lib/json/JsonRandom.cpp
  55. 0 0
      lib/json/JsonRandom.h
  56. 21 435
      lib/json/JsonUtils.cpp
  57. 104 0
      lib/json/JsonUtils.h
  58. 687 0
      lib/json/JsonValidator.cpp
  59. 49 0
      lib/json/JsonValidator.h
  60. 144 0
      lib/json/JsonWriter.cpp
  61. 35 0
      lib/json/JsonWriter.h
  62. 2 1
      lib/mapObjectConstructors/AObjectTypeHandler.cpp
  63. 1 1
      lib/mapObjectConstructors/AObjectTypeHandler.h
  64. 1 1
      lib/mapObjectConstructors/CBankInstanceConstructor.cpp
  65. 1 0
      lib/mapObjectConstructors/CBankInstanceConstructor.h
  66. 1 1
      lib/mapObjectConstructors/CObjectClassesHandler.cpp
  67. 2 1
      lib/mapObjectConstructors/CObjectClassesHandler.h
  68. 1 1
      lib/mapObjectConstructors/CommonConstructors.cpp
  69. 1 1
      lib/mapObjectConstructors/DwellingInstanceConstructor.cpp
  70. 2 0
      lib/mapObjectConstructors/DwellingInstanceConstructor.h
  71. 1 0
      lib/mapObjectConstructors/HillFortInstanceConstructor.h
  72. 1 0
      lib/mapObjectConstructors/ShipyardInstanceConstructor.h
  73. 1 0
      lib/mapObjects/CGHeroInstance.cpp
  74. 1 1
      lib/mapObjects/CObjectHandler.cpp
  75. 0 1
      lib/mapObjects/ObjectTemplate.cpp
  76. 1 0
      lib/mapping/CMapHeader.cpp
  77. 2 0
      lib/mapping/CMapHeader.h
  78. 1 0
      lib/mapping/CMapService.cpp
  79. 0 1
      lib/mapping/MapEditUtils.cpp
  80. 2 2
      lib/mapping/MapFormatJson.cpp
  81. 0 1
      lib/mapping/MapFormatJson.h
  82. 0 1
      lib/mapping/MapIdentifiersH3M.cpp
  83. 1 0
      lib/modding/CModHandler.cpp
  84. 1 1
      lib/modding/CModInfo.h
  85. 1 0
      lib/modding/ContentTypeHandler.cpp
  86. 1 1
      lib/modding/ContentTypeHandler.h
  87. 0 1
      lib/modding/IdentifierStorage.cpp
  88. 1 1
      lib/networkPacks/BattleChanges.h
  89. 2 2
      lib/networkPacks/EntityChanges.h
  90. 1 1
      lib/rewardable/Info.cpp
  91. 1 1
      lib/rewardable/Info.h
  92. 0 2
      lib/serializer/JsonDeserializer.cpp
  93. 0 2
      lib/serializer/JsonSerializeFormat.cpp
  94. 2 1
      lib/serializer/JsonSerializeFormat.h
  95. 0 2
      lib/serializer/JsonSerializer.cpp
  96. 1 2
      lib/serializer/JsonUpdater.cpp
  97. 1 1
      lib/spells/CSpellHandler.cpp
  98. 1 1
      lib/spells/CSpellHandler.h
  99. 1 1
      lib/spells/TargetCondition.cpp
  100. 1 0
      lib/spells/effects/Moat.cpp

+ 0 - 1
client/CMusicHandler.cpp

@@ -17,7 +17,6 @@
 #include "eventsSDL/InputHandler.h"
 #include "gui/CGuiHandler.h"
 
-#include "../lib/JsonNode.h"
 #include "../lib/GameConstants.h"
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/constants/StringConstants.h"

+ 0 - 1
client/CPlayerInterface.cpp

@@ -75,7 +75,6 @@
 #include "../lib/CTownHandler.h"
 #include "../lib/CondSh.h"
 #include "../lib/GameConstants.h"
-#include "../lib/JsonNode.h"
 #include "../lib/RoadHandler.h"
 #include "../lib/StartInfo.h"
 #include "../lib/TerrainHandler.h"

+ 2 - 1
client/gui/InterfaceObjectConfigurable.cpp

@@ -29,7 +29,8 @@
 #include "../windows/GUIClasses.h"
 #include "../windows/InfoWindows.h"
 
-#include "../../lib//constants/StringConstants.h"
+#include "../../lib/constants/StringConstants.h"
+#include "../../lib/json/JsonUtils.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/filesystem/ResourcePath.h"
 

+ 1 - 1
client/gui/InterfaceObjectConfigurable.h

@@ -14,7 +14,7 @@
 #include "TextAlignment.h"
 #include "../render/EFont.h"
 
-#include "../../lib/JsonNode.h"
+#include "../../lib/json/JsonNode.h"
 
 class CPicture;
 class CLabel;

+ 0 - 1
client/mainmenu/CMainMenu.cpp

@@ -45,7 +45,6 @@
 #include "../../CCallback.h"
 
 #include "../../lib/CGeneralTextHandler.h"
-#include "../../lib/JsonNode.h"
 #include "../../lib/campaign/CampaignHandler.h"
 #include "../../lib/serializer/CTypeList.h"
 #include "../../lib/filesystem/Filesystem.h"

+ 1 - 1
client/mainmenu/CMainMenu.h

@@ -10,7 +10,7 @@
 #pragma once
 
 #include "../windows/CWindowObject.h"
-#include "../../lib/JsonNode.h"
+#include "../../lib/json/JsonNode.h"
 #include "../../lib/LoadProgress.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 1 - 1
client/render/CAnimation.cpp

@@ -14,7 +14,7 @@
 
 #include "Graphics.h"
 #include "../../lib/filesystem/Filesystem.h"
-#include "../../lib/JsonNode.h"
+#include "../../lib/json/JsonUtils.h"
 #include "../renderSDL/SDLImage.h"
 
 std::shared_ptr<IImage> CAnimation::getFromExtraDef(std::string filename)

+ 1 - 1
client/render/ColorFilter.cpp

@@ -10,8 +10,8 @@
 #include "StdInc.h"
 #include "ColorFilter.h"
 
-#include "../../lib/JsonNode.h"
 #include "../../lib/Color.h"
+#include "../../lib/json/JsonNode.h"
 
 ColorRGBA ColorFilter::shiftColor(const ColorRGBA & in) const
 {

+ 3 - 2
client/render/Colors.cpp

@@ -10,7 +10,8 @@
 
 #include "StdInc.h"
 #include "Colors.h"
-#include "../../lib/JsonNode.h"
+
+#include "../../lib/json/JsonNode.h"
 
 const ColorRGBA Colors::YELLOW = { 229, 215, 123, ColorRGBA::ALPHA_OPAQUE };
 const ColorRGBA Colors::WHITE = { 255, 243, 222, ColorRGBA::ALPHA_OPAQUE };
@@ -48,4 +49,4 @@ std::optional<ColorRGBA> Colors::parseColor(std::string text)
 	}
 
 	return std::nullopt;
-}
+}

+ 1 - 1
client/render/Graphics.cpp

@@ -29,13 +29,13 @@
 
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/filesystem/CBinaryReader.h"
+#include "../../lib/json/JsonNode.h"
 #include "../lib/modding/CModHandler.h"
 #include "../lib/modding/ModScope.h"
 #include "CGameInfo.h"
 #include "../lib/VCMI_Lib.h"
 #include "../CCallback.h"
 #include "../lib/CGeneralTextHandler.h"
-#include "../lib/JsonNode.h"
 #include "../lib/vcmi_endian.h"
 #include "../lib/CStopWatch.h"
 #include "../lib/CHeroHandler.h"

+ 1 - 1
client/renderSDL/CBitmapHanFont.cpp

@@ -13,8 +13,8 @@
 #include "CBitmapFont.h"
 #include "SDL_Extensions.h"
 
-#include "../../lib/JsonNode.h"
 #include "../../lib/filesystem/Filesystem.h"
+#include "../../lib/json/JsonNode.h"
 #include "../../lib/TextOperations.h"
 #include "../../lib/Rect.h"
 

+ 1 - 1
client/renderSDL/CTrueTypeFont.cpp

@@ -15,8 +15,8 @@
 #include "../render/Colors.h"
 #include "../renderSDL/SDL_Extensions.h"
 
-#include "../../lib/JsonNode.h"
 #include "../../lib/TextOperations.h"
+#include "../../lib/json/JsonNode.h"
 #include "../../lib/filesystem/Filesystem.h"
 
 #include <SDL_ttf.h>

+ 1 - 1
client/renderSDL/SDLImage.cpp

@@ -18,7 +18,7 @@
 #include "../render/CDefFile.h"
 #include "../render/Graphics.h"
 
-#include "../../lib/JsonNode.h"
+#include "../../lib/json/JsonNode.h"
 
 #include <SDL_surface.h>
 

+ 1 - 0
client/renderSDL/ScreenHandler.cpp

@@ -12,6 +12,7 @@
 #include "ScreenHandler.h"
 
 #include "../../lib/CConfigHandler.h"
+#include "../../lib/constants/StringConstants.h"
 #include "../gui/CGuiHandler.h"
 #include "../eventsSDL/NotificationHandler.h"
 #include "../gui/WindowHandler.h"

+ 2 - 0
launcher/jsonutils.cpp

@@ -10,6 +10,8 @@
 #include "StdInc.h"
 #include "jsonutils.h"
 
+#include "../lib/json/JsonNode.h"
+
 static QVariantMap JsonToMap(const JsonMap & json)
 {
 	QVariantMap map;

+ 2 - 1
launcher/jsonutils.h

@@ -10,10 +10,11 @@
 #pragma once
 
 #include <QVariant>
-#include "../lib/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+class JsonNode;
+
 namespace JsonUtils
 {
 QVariant toVariant(const JsonNode & node);

+ 0 - 1
launcher/modManager/cmodlist.cpp

@@ -11,7 +11,6 @@
 #include "cmodlist.h"
 
 #include "../lib/CConfigHandler.h"
-#include "../../lib/JsonNode.h"
 #include "../../lib/filesystem/CFileInputStream.h"
 #include "../../lib/GameConstants.h"
 #include "../../lib/modding/CModVersion.h"

+ 0 - 1
lib/BasicTypes.cpp

@@ -13,7 +13,6 @@
 #include "VCMI_Lib.h"
 #include "GameConstants.h"
 #include "GameSettings.h"
-#include "JsonNode.h"
 #include "bonuses/BonusList.h"
 #include "bonuses/Bonus.h"
 #include "bonuses/IBonusBearer.h"

+ 1 - 1
lib/BattleFieldHandler.cpp

@@ -11,7 +11,7 @@
 
 #include <vcmi/Entity.h>
 #include "BattleFieldHandler.h"
-#include "JsonNode.h"
+#include "json/JsonUtils.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/CArtHandler.cpp

@@ -15,7 +15,7 @@
 #include "GameSettings.h"
 #include "mapObjects/MapObjects.h"
 #include "constants/StringConstants.h"
-
+#include "json/JsonUtils.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"
 #include "serializer/JsonSerializeFormat.h"

+ 1 - 1
lib/CBonusTypeHandler.cpp

@@ -13,12 +13,12 @@
 
 #include "CBonusTypeHandler.h"
 
-#include "JsonNode.h"
 #include "filesystem/Filesystem.h"
 
 #include "GameConstants.h"
 #include "CCreatureHandler.h"
 #include "CGeneralTextHandler.h"
+#include "json/JsonUtils.h"
 #include "spells/CSpellHandler.h"
 
 template class std::vector<VCMI_LIB_WRAP_NAMESPACE(CBonusType)>;

+ 4 - 3
lib/CConfigHandler.cpp

@@ -10,9 +10,10 @@
 #include "StdInc.h"
 #include "CConfigHandler.h"
 
-#include "../lib/filesystem/Filesystem.h"
-#include "../lib/GameConstants.h"
-#include "../lib/VCMIDirs.h"
+#include "filesystem/Filesystem.h"
+#include "GameConstants.h"
+#include "VCMIDirs.h"
+#include "json/JsonUtils.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/CConfigHandler.h

@@ -9,7 +9,7 @@
  */
 #pragma once
 
-#include "../lib/JsonNode.h"
+#include "json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 0
lib/CCreatureHandler.cpp

@@ -20,6 +20,7 @@
 #include "constants/StringConstants.h"
 #include "bonuses/Limiters.h"
 #include "bonuses/Updaters.h"
+#include "json/JsonUtils.h"
 #include "serializer/JsonDeserializer.h"
 #include "serializer/JsonUpdater.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"

+ 0 - 1
lib/CCreatureHandler.h

@@ -14,7 +14,6 @@
 #include "ConstTransitivePtr.h"
 #include "ResourceSet.h"
 #include "GameConstants.h"
-#include "JsonNode.h"
 #include "IHandlerBase.h"
 #include "Color.h"
 #include "filesystem/ResourcePath.h"

+ 1 - 1
lib/CHeroHandler.cpp

@@ -13,7 +13,6 @@
 #include "CGeneralTextHandler.h"
 #include "filesystem/Filesystem.h"
 #include "VCMI_Lib.h"
-#include "JsonNode.h"
 #include "constants/StringConstants.h"
 #include "battle/BattleHex.h"
 #include "CCreatureHandler.h"
@@ -24,6 +23,7 @@
 #include "BattleFieldHandler.h"
 #include "bonuses/Limiters.h"
 #include "bonuses/Updaters.h"
+#include "json/JsonUtils.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"
 #include "modding/IdentifierStorage.h"

+ 15 - 7
lib/CMakeLists.txt

@@ -61,6 +61,13 @@ set(lib_SRCS
 	filesystem/MinizipExtensions.cpp
 	filesystem/ResourcePath.cpp
 
+	json/JsonNode.cpp
+	json/JsonParser.cpp
+	json/JsonRandom.cpp
+	json/JsonUtils.cpp
+	json/JsonValidator.cpp
+	json/JsonWriter.cpp
+
 	gameState/CGameState.cpp
 	gameState/CGameStateCampaign.cpp
 	gameState/InfoAboutArmy.cpp
@@ -245,9 +252,6 @@ set(lib_SRCS
 	GameSettings.cpp
 	IGameCallback.cpp
 	IHandlerBase.cpp
-	JsonDetail.cpp
-	JsonNode.cpp
-	JsonRandom.cpp
 	LoadProgress.cpp
 	LogicalExpression.cpp
 	MetaString.cpp
@@ -399,6 +403,13 @@ set(lib_HEADERS
 	filesystem/MinizipExtensions.h
 	filesystem/ResourcePath.h
 
+	json/JsonNode.h
+	json/JsonParser.h
+	json/JsonRandom.h
+	json/JsonUtils.h
+	json/JsonValidator.h
+	json/JsonWriter.h
+
 	gameState/CGameState.h
 	gameState/CGameStateCampaign.h
 	gameState/EVictoryLossCheckResult.h
@@ -636,9 +647,6 @@ set(lib_HEADERS
 	IGameEventsReceiver.h
 	IHandlerBase.h
 	int3.h
-	JsonDetail.h
-	JsonNode.h
-	JsonRandom.h
 	Languages.h
 	LoadProgress.h
 	LogicalExpression.h
@@ -772,4 +780,4 @@ if(APPLE_IOS AND NOT USING_CONAN)
 		endif()
 		install(${INSTALL_TYPE} ${LINKED_LIB_REAL} LIBRARY DESTINATION ${LIB_DIR})
 	endforeach()
-endif()
+endif()

+ 1 - 3
lib/CSkillHandler.cpp

@@ -16,12 +16,10 @@
 
 #include "CGeneralTextHandler.h"
 #include "filesystem/Filesystem.h"
+#include "json/JsonUtils.h"
 #include "modding/IdentifierStorage.h"
 #include "modding/ModUtility.h"
 #include "modding/ModScope.h"
-
-#include "JsonNode.h"
-
 #include "constants/StringConstants.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 0 - 1
lib/CStack.h

@@ -9,7 +9,6 @@
  */
 
 #pragma once
-#include "JsonNode.h"
 #include "bonuses/Bonus.h"
 #include "bonuses/CBonusSystemNode.h"
 #include "CCreatureHandler.h" //todo: remove

+ 1 - 1
lib/CTownHandler.cpp

@@ -12,7 +12,6 @@
 
 #include "VCMI_Lib.h"
 #include "CGeneralTextHandler.h"
-#include "JsonNode.h"
 #include "constants/StringConstants.h"
 #include "CCreatureHandler.h"
 #include "CHeroHandler.h"
@@ -23,6 +22,7 @@
 #include "filesystem/Filesystem.h"
 #include "bonuses/Bonus.h"
 #include "bonuses/Propagators.h"
+#include "json/JsonUtils.h"
 #include "ResourceSet.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"

+ 1 - 1
lib/GameSettings.cpp

@@ -9,7 +9,7 @@
  */
 #include "StdInc.h"
 #include "GameSettings.h"
-#include "JsonNode.h"
+#include "json/JsonUtils.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 2
lib/LogicalExpression.h

@@ -9,8 +9,7 @@
  */
 #pragma once
 
-//FIXME: move some of code into .cpp to avoid this include?
-#include "JsonNode.h"
+#include "json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/ObstacleHandler.cpp

@@ -10,8 +10,8 @@
 #include "StdInc.h"
 #include "ObstacleHandler.h"
 #include "BattleFieldHandler.h"
+#include "json/JsonNode.h"
 #include "modding/IdentifierStorage.h"
-#include "JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 0 - 1
lib/ResourceSet.cpp

@@ -12,7 +12,6 @@
 #include "GameConstants.h"
 #include "ResourceSet.h"
 #include "constants/StringConstants.h"
-#include "JsonNode.h"
 #include "serializer/JsonSerializeFormat.h"
 #include "mapObjects/CObjectHandler.h"
 #include "VCMI_Lib.h"

+ 1 - 1
lib/RiverHandler.cpp

@@ -12,7 +12,7 @@
 #include "RiverHandler.h"
 #include "CGeneralTextHandler.h"
 #include "GameSettings.h"
-#include "JsonNode.h"
+#include "json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/RoadHandler.cpp

@@ -12,7 +12,7 @@
 #include "RoadHandler.h"
 #include "CGeneralTextHandler.h"
 #include "GameSettings.h"
-#include "JsonNode.h"
+#include "json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/ScriptHandler.h

@@ -13,7 +13,7 @@
 #if SCRIPTING_ENABLED
 #include <vcmi/scripting/Service.h>
 #include "IHandlerBase.h"
-#include "JsonNode.h"
+#include "json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/TerrainHandler.cpp

@@ -12,7 +12,7 @@
 #include "TerrainHandler.h"
 #include "CGeneralTextHandler.h"
 #include "GameSettings.h"
-#include "JsonNode.h"
+#include "json/JsonNode.h"
 #include "modding/IdentifierStorage.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 1 - 0
lib/bonuses/Bonus.cpp

@@ -26,6 +26,7 @@
 #include "../TerrainHandler.h"
 #include "../constants/StringConstants.h"
 #include "../battle/BattleInfo.h"
+#include "../json/JsonUtils.h"
 #include "../modding/ModUtility.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 2 - 5
lib/bonuses/BonusEnum.cpp

@@ -7,13 +7,10 @@
  * Full text of license available in license.txt file, in main folder
  *
  */
-
-
 #include "StdInc.h"
 
 #include "BonusEnum.h"
-
-#include "../JsonNode.h"
+#include "../json/JsonUtils.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -82,4 +79,4 @@ namespace BonusDuration
 	}
 }
 
-VCMI_LIB_NAMESPACE_END
+VCMI_LIB_NAMESPACE_END

+ 1 - 2
lib/bonuses/BonusList.cpp

@@ -10,8 +10,7 @@
 
 #include "StdInc.h"
 #include "CBonusSystemNode.h"
-
-#include "../JsonNode.h"
+#include "../json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/bonuses/BonusParams.h

@@ -12,7 +12,7 @@
 #include "Bonus.h"
 
 #include "../GameConstants.h"
-#include "../JsonNode.h"
+#include "../json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 0
lib/bonuses/Limiters.cpp

@@ -24,6 +24,7 @@
 #include "../TerrainHandler.h"
 #include "../constants/StringConstants.h"
 #include "../battle/BattleInfo.h"
+#include "../json/JsonUtils.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 2 - 1
lib/bonuses/Updaters.cpp

@@ -13,6 +13,7 @@
 #include "Updaters.h"
 #include "Limiters.h"
 
+#include "../json/JsonUtils.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../CStack.h"
 
@@ -208,4 +209,4 @@ std::shared_ptr<Bonus> OwnerUpdater::createUpdatedBonus(const std::shared_ptr<Bo
 	return updated;
 }
 
-VCMI_LIB_NAMESPACE_END
+VCMI_LIB_NAMESPACE_END

+ 0 - 1
lib/campaign/CampaignState.cpp

@@ -10,7 +10,6 @@
 #include "StdInc.h"
 #include "CampaignState.h"
 
-#include "../JsonNode.h"
 #include "../Point.h"
 #include "../filesystem/ResourcePath.h"
 #include "../VCMI_Lib.h"

+ 1 - 1
lib/filesystem/AdapterLoaders.cpp

@@ -10,8 +10,8 @@
 #include "StdInc.h"
 #include "AdapterLoaders.h"
 
-#include "../JsonNode.h"
 #include "Filesystem.h"
+#include "../json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/filesystem/Filesystem.cpp

@@ -16,10 +16,10 @@
 #include "CZipLoader.h"
 
 //For filesystem initialization
-#include "../JsonNode.h"
 #include "../GameConstants.h"
 #include "../VCMIDirs.h"
 #include "../CStopWatch.h"
+#include "../json/JsonNode.h"
 #include "../modding/ModScope.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 0 - 1
lib/filesystem/ResourcePath.cpp

@@ -11,7 +11,6 @@
 #include "ResourcePath.h"
 #include "FileInfo.h"
 
-#include "../JsonNode.h"
 #include "../serializer/JsonDeserializer.h"
 #include "../serializer/JsonSerializer.h"
 

+ 1 - 0
lib/gameState/CGameState.cpp

@@ -31,6 +31,7 @@
 #include "../campaign/CampaignState.h"
 #include "../constants/StringConstants.h"
 #include "../filesystem/ResourcePath.h"
+#include "../json/JsonUtils.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../mapObjectConstructors/DwellingInstanceConstructor.h"

+ 430 - 0
lib/json/JsonNode.cpp

@@ -0,0 +1,430 @@
+/*
+ * JsonNode.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 "JsonNode.h"
+
+#include "filesystem/Filesystem.h"
+#include "JsonParser.h"
+#include "JsonWriter.h"
+
+namespace
+{
+// to avoid duplicating const and non-const code
+template<typename Node>
+Node & resolvePointer(Node & in, const std::string & pointer)
+{
+	if(pointer.empty())
+		return in;
+	assert(pointer[0] == '/');
+
+	size_t splitPos = pointer.find('/', 1);
+
+	std::string entry = pointer.substr(1, splitPos - 1);
+	std::string remainer = splitPos == std::string::npos ? "" : pointer.substr(splitPos);
+
+	if(in.getType() == VCMI_LIB_WRAP_NAMESPACE(JsonNode)::JsonType::DATA_VECTOR)
+	{
+		if(entry.find_first_not_of("0123456789") != std::string::npos) // non-numbers in string
+			throw std::runtime_error("Invalid Json pointer");
+
+		if(entry.size() > 1 && entry[0] == '0') // leading zeros are not allowed
+			throw std::runtime_error("Invalid Json pointer");
+
+		auto index = boost::lexical_cast<size_t>(entry);
+
+		if (in.Vector().size() > index)
+			return in.Vector()[index].resolvePointer(remainer);
+	}
+	return in[entry].resolvePointer(remainer);
+}
+}
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+using namespace JsonDetail;
+
+class LibClasses;
+class CModHandler;
+
+static const JsonNode nullNode;
+
+JsonNode::JsonNode(JsonType Type)
+{
+	setType(Type);
+}
+
+JsonNode::JsonNode(const std::byte *data, size_t datasize)
+	:JsonNode(reinterpret_cast<const char*>(data), datasize)
+{}
+
+JsonNode::JsonNode(const char *data, size_t datasize)
+{
+	JsonParser parser(data, datasize);
+	*this = parser.parse("<unknown>");
+}
+
+JsonNode::JsonNode(const JsonPath & fileURI)
+{
+	auto file = CResourceHandler::get()->load(fileURI)->readAll();
+
+	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
+	*this = parser.parse(fileURI.getName());
+}
+
+JsonNode::JsonNode(const std::string & idx, const JsonPath & fileURI)
+{
+	auto file = CResourceHandler::get(idx)->load(fileURI)->readAll();
+	
+	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
+	*this = parser.parse(fileURI.getName());
+}
+
+JsonNode::JsonNode(const JsonPath & fileURI, bool &isValidSyntax)
+{
+	auto file = CResourceHandler::get()->load(fileURI)->readAll();
+
+	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
+	*this = parser.parse(fileURI.getName());
+	isValidSyntax = parser.isValid();
+}
+
+bool JsonNode::operator == (const JsonNode &other) const
+{
+	return data == other.data;
+}
+
+bool JsonNode::operator != (const JsonNode &other) const
+{
+	return !(*this == other);
+}
+
+JsonNode::JsonType JsonNode::getType() const
+{
+	return static_cast<JsonType>(data.index());
+}
+
+void JsonNode::setMeta(const std::string & metadata, bool recursive)
+{
+	meta = metadata;
+	if (recursive)
+	{
+		switch (getType())
+		{
+			break; case JsonType::DATA_VECTOR:
+			{
+				for(auto & node : Vector())
+				{
+					node.setMeta(metadata);
+				}
+			}
+			break; case JsonType::DATA_STRUCT:
+			{
+				for(auto & node : Struct())
+				{
+					node.second.setMeta(metadata);
+				}
+			}
+		}
+	}
+}
+
+void JsonNode::setType(JsonType Type)
+{
+	if (getType() == Type)
+		return;
+
+	//float<->int conversion
+	if(getType() == JsonType::DATA_FLOAT && Type == JsonType::DATA_INTEGER)
+	{
+		si64 converted = static_cast<si64>(std::get<double>(data));
+		data = JsonData(converted);
+		return;
+	}
+	else if(getType() == JsonType::DATA_INTEGER && Type == JsonType::DATA_FLOAT)
+	{
+		double converted = static_cast<double>(std::get<si64>(data));
+		data = JsonData(converted);
+		return;
+	}
+
+	//Set new node type
+	switch(Type)
+	{
+		break; case JsonType::DATA_NULL:    data = JsonData();
+		break; case JsonType::DATA_BOOL:    data = JsonData(false);
+		break; case JsonType::DATA_FLOAT:   data = JsonData(static_cast<double>(0.0));
+		break; case JsonType::DATA_STRING:  data = JsonData(std::string());
+		break; case JsonType::DATA_VECTOR:  data = JsonData(JsonVector());
+		break; case JsonType::DATA_STRUCT:  data = JsonData(JsonMap());
+		break; case JsonType::DATA_INTEGER: data = JsonData(static_cast<si64>(0));
+	}
+}
+
+bool JsonNode::isNull() const
+{
+	return getType() == JsonType::DATA_NULL;
+}
+
+bool JsonNode::isNumber() const
+{
+	return getType() == JsonType::DATA_INTEGER || getType() == JsonType::DATA_FLOAT;
+}
+
+bool JsonNode::isString() const
+{
+	return getType() == JsonType::DATA_STRING;
+}
+
+bool JsonNode::isVector() const
+{
+	return getType() == JsonType::DATA_VECTOR;
+}
+
+bool JsonNode::isStruct() const
+{
+	return getType() == JsonType::DATA_STRUCT;
+}
+
+bool JsonNode::containsBaseData() const
+{
+	switch(getType())
+	{
+	case JsonType::DATA_NULL:
+		return false;
+	case JsonType::DATA_STRUCT:
+		for(const auto & elem : Struct())
+		{
+			if(elem.second.containsBaseData())
+				return true;
+		}
+		return false;
+	default:
+		//other types (including vector) cannot be extended via merge
+		return true;
+	}
+}
+
+bool JsonNode::isCompact() const
+{
+	switch(getType())
+	{
+	case JsonType::DATA_VECTOR:
+		for(const JsonNode & elem : Vector())
+		{
+			if(!elem.isCompact())
+				return false;
+		}
+		return true;
+	case JsonType::DATA_STRUCT:
+		{
+			auto propertyCount = Struct().size();
+			if(propertyCount == 0)
+				return true;
+			else if(propertyCount == 1)
+				return Struct().begin()->second.isCompact();
+		}
+		return false;
+	default:
+		return true;
+	}
+}
+
+bool JsonNode::TryBoolFromString(bool & success) const
+{
+	success = true;
+	if(getType() == JsonNode::JsonType::DATA_BOOL)
+		return Bool();
+
+	success = getType() == JsonNode::JsonType::DATA_STRING;
+	if(success)
+	{
+		auto boolParamStr = String();
+		boost::algorithm::trim(boolParamStr);
+		boost::algorithm::to_lower(boolParamStr);
+		success = boolParamStr == "true";
+
+		if(success)
+			return true;
+		
+		success = boolParamStr == "false";
+	}
+	return false;
+}
+
+void JsonNode::clear()
+{
+	setType(JsonType::DATA_NULL);
+}
+
+bool & JsonNode::Bool()
+{
+	setType(JsonType::DATA_BOOL);
+	return std::get<bool>(data);
+}
+
+double & JsonNode::Float()
+{
+	setType(JsonType::DATA_FLOAT);
+	return std::get<double>(data);
+}
+
+si64 & JsonNode::Integer()
+{
+	setType(JsonType::DATA_INTEGER);
+	return std::get<si64>(data);
+}
+
+std::string & JsonNode::String()
+{
+	setType(JsonType::DATA_STRING);
+	return std::get<std::string>(data);
+}
+
+JsonVector & JsonNode::Vector()
+{
+	setType(JsonType::DATA_VECTOR);
+	return std::get<JsonVector>(data);
+}
+
+JsonMap & JsonNode::Struct()
+{
+	setType(JsonType::DATA_STRUCT);
+	return std::get<JsonMap>(data);
+}
+
+const bool boolDefault = false;
+bool JsonNode::Bool() const
+{
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_BOOL);
+
+	if (getType() == JsonType::DATA_BOOL)
+		return std::get<bool>(data);
+
+	return boolDefault;
+}
+
+const double floatDefault = 0;
+double JsonNode::Float() const
+{
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_INTEGER || getType() == JsonType::DATA_FLOAT);
+
+	if(getType() == JsonType::DATA_FLOAT)
+		return std::get<double>(data);
+
+	if(getType() == JsonType::DATA_INTEGER)
+		return static_cast<double>(std::get<si64>(data));
+
+	return floatDefault;
+}
+
+const si64 integerDefault = 0;
+si64 JsonNode::Integer() const
+{
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_INTEGER || getType() == JsonType::DATA_FLOAT);
+
+	if(getType() == JsonType::DATA_INTEGER)
+		return std::get<si64>(data);
+
+	if(getType() == JsonType::DATA_FLOAT)
+		return static_cast<si64>(std::get<double>(data));
+
+	return integerDefault;
+}
+
+const std::string stringDefault = std::string();
+const std::string & JsonNode::String() const
+{
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_STRING);
+
+	if (getType() == JsonType::DATA_STRING)
+		return std::get<std::string>(data);
+
+	return stringDefault;
+}
+
+const JsonVector vectorDefault = JsonVector();
+const JsonVector & JsonNode::Vector() const
+{
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_VECTOR);
+
+	if (getType() == JsonType::DATA_VECTOR)
+		return std::get<JsonVector>(data);
+
+	return vectorDefault;
+}
+
+const JsonMap mapDefault = JsonMap();
+const JsonMap & JsonNode::Struct() const
+{
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_STRUCT);
+
+	if (getType() == JsonType::DATA_STRUCT)
+		return std::get<JsonMap>(data);
+
+	return mapDefault;
+}
+
+JsonNode & JsonNode::operator[](const std::string & child)
+{
+	return Struct()[child];
+}
+
+const JsonNode & JsonNode::operator[](const std::string & child) const
+{
+	auto it = Struct().find(child);
+	if (it != Struct().end())
+		return it->second;
+	return nullNode;
+}
+
+JsonNode & JsonNode::operator[](size_t child)
+{
+	if (child >= Vector().size() )
+		Vector().resize(child + 1);
+	return Vector()[child];
+}
+
+const JsonNode & JsonNode::operator[](size_t child) const
+{
+	if (child < Vector().size() )
+		return Vector()[child];
+
+	return nullNode;
+}
+
+const JsonNode & JsonNode::resolvePointer(const std::string &jsonPointer) const
+{
+	return ::resolvePointer(*this, jsonPointer);
+}
+
+JsonNode & JsonNode::resolvePointer(const std::string &jsonPointer)
+{
+	return ::resolvePointer(*this, jsonPointer);
+}
+
+std::vector<std::byte> JsonNode::toBytes(bool compact) const
+{
+	std::string jsonString = toJson(compact);
+	auto dataBegin = reinterpret_cast<const std::byte*>(jsonString.data());
+	auto dataEnd = dataBegin + jsonString.size();
+	std::vector<std::byte> result(dataBegin, dataEnd);
+	return result;
+}
+
+std::string JsonNode::toJson(bool compact) const
+{
+	std::ostringstream out;
+	JsonWriter writer(out, compact);
+	writer.writeNode(*this);
+	return out.str();
+}
+
+VCMI_LIB_NAMESPACE_END

+ 2 - 93
lib/JsonNode.h → lib/json/JsonNode.h

@@ -8,8 +8,8 @@
  *
  */
 #pragma once
-#include "GameConstants.h"
-#include "filesystem/ResourcePath.h"
+
+#include "../filesystem/ResourcePath.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -127,97 +127,6 @@ public:
 	}
 };
 
-namespace JsonUtils
-{
-	DLL_LINKAGE std::shared_ptr<Bonus> parseBonus(const JsonVector & ability_vec);
-	DLL_LINKAGE std::shared_ptr<Bonus> parseBonus(const JsonNode & ability);
-	DLL_LINKAGE std::shared_ptr<Bonus> parseBuildingBonus(const JsonNode & ability, const FactionID & faction, const BuildingID & building, const std::string & description);
-	DLL_LINKAGE bool parseBonus(const JsonNode & ability, Bonus * placement);
-	DLL_LINKAGE std::shared_ptr<ILimiter> parseLimiter(const JsonNode & limiter);
-	DLL_LINKAGE CSelector parseSelector(const JsonNode &ability);
-	DLL_LINKAGE void resolveAddInfo(CAddInfo & var, const JsonNode & node);
-
-	/**
-	 * @brief recursively merges source into dest, replacing identical fields
-	 * struct : recursively calls this function
-	 * arrays : each entry will be merged recursively
-	 * values : value in source will replace value in dest
-	 * null   : if value in source is present but set to null it will delete entry in dest
-	 * @note this function will destroy data in source
-	 */
-	DLL_LINKAGE void merge(JsonNode & dest, JsonNode & source, bool ignoreOverride = false, bool copyMeta = false);
-
-	/**
-	 * @brief recursively merges source into dest, replacing identical fields
-	 * struct : recursively calls this function
-	 * arrays : each entry will be merged recursively
-	 * values : value in source will replace value in dest
-	 * null   : if value in source is present but set to null it will delete entry in dest
-	 * @note this function will preserve data stored in source by creating copy
-	 */
-	DLL_LINKAGE void mergeCopy(JsonNode & dest, JsonNode source, bool ignoreOverride = false, bool copyMeta = false);
-
-	/** @brief recursively merges descendant into copy of base node
-	* Result emulates inheritance semantic
-	*/
-	DLL_LINKAGE void inherit(JsonNode & descendant, const JsonNode & base);
-
-	/**
-	 * @brief construct node representing the common structure of input nodes
-	 * @param pruneEmpty - omit common properties whose intersection is empty
-	 * different types: null
-	 * struct: recursive intersect on common properties
-	 * other: input if equal, null otherwise
-	 */
-	DLL_LINKAGE JsonNode intersect(const JsonNode & a, const JsonNode & b, bool pruneEmpty = true);
-	DLL_LINKAGE JsonNode intersect(const std::vector<JsonNode> & nodes, bool pruneEmpty = true);
-
-	/**
-	 * @brief construct node representing the difference "node - base"
-	 * merging difference with base gives node
-	 */
-	DLL_LINKAGE JsonNode difference(const JsonNode & node, const JsonNode & base);
-
-	/**
-	 * @brief generate one Json structure from multiple files
-	 * @param files - list of filenames with parts of json structure
-	 */
-	DLL_LINKAGE JsonNode assembleFromFiles(const std::vector<std::string> & files);
-	DLL_LINKAGE JsonNode assembleFromFiles(const std::vector<std::string> & files, bool & isValid);
-
-	/// This version loads all files with same name (overridden by mods)
-	DLL_LINKAGE JsonNode assembleFromFiles(const std::string & filename);
-
-	/**
-	 * @brief removes all nodes that are identical to default entry in schema
-	 * @param node - JsonNode to minimize
-	 * @param schemaName - name of schema to use
-	 * @note for minimizing data must be valid against given schema
-	 */
-	DLL_LINKAGE void minimize(JsonNode & node, const std::string & schemaName);
-	/// opposed to minimize, adds all missing, required entries that have default value
-	DLL_LINKAGE void maximize(JsonNode & node, const std::string & schemaName);
-
-	/**
-	* @brief validate node against specified schema
-	* @param node - JsonNode to check
-	* @param schemaName - name of schema to use
-	* @param dataName - some way to identify data (printed in console in case of errors)
-	* @returns true if data in node fully compilant with schema
-	*/
-	DLL_LINKAGE bool validate(const JsonNode & node, const std::string & schemaName, const std::string & dataName);
-
-	/// get schema by json URI: vcmi:<name of file in schemas directory>#<entry in file, optional>
-	/// example: schema "vcmi:settings" is used to check user settings
-	DLL_LINKAGE const JsonNode & getSchema(const std::string & URI);
-
-	/// for easy construction of JsonNodes; helps with inserting primitives into vector node
-	DLL_LINKAGE JsonNode boolNode(bool value);
-	DLL_LINKAGE JsonNode floatNode(double value);
-	DLL_LINKAGE JsonNode stringNode(const std::string & value);
-	DLL_LINKAGE JsonNode intNode(si64 value);
-}
-
 namespace JsonDetail
 {
 	// conversion helpers for JsonNode::convertTo (partial template function instantiation is illegal in c++)

+ 465 - 0
lib/json/JsonParser.cpp

@@ -0,0 +1,465 @@
+/*
+ * JsonParser.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 "JsonParser.h"
+
+#include "../TextOperations.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+JsonParser::JsonParser(const char * inputString, size_t stringSize):
+	input(inputString, stringSize),
+	lineCount(1),
+	lineStart(0),
+	pos(0)
+{
+}
+
+JsonNode JsonParser::parse(const std::string & fileName)
+{
+	JsonNode root;
+
+	if (input.size() == 0)
+	{
+		error("File is empty", false);
+	}
+	else
+	{
+		if (!TextOperations::isValidUnicodeString(&input[0], input.size()))
+			error("Not a valid UTF-8 file", false);
+
+		extractValue(root);
+		extractWhitespace(false);
+
+		//Warn if there are any non-whitespace symbols left
+		if (pos < input.size())
+			error("Not all file was parsed!", true);
+	}
+
+	if (!errors.empty())
+	{
+		logMod->warn("File %s is not a valid JSON file!", fileName);
+		logMod->warn(errors);
+	}
+	return root;
+}
+
+bool JsonParser::isValid()
+{
+	return errors.empty();
+}
+
+bool JsonParser::extractSeparator()
+{
+	if (!extractWhitespace())
+		return false;
+
+	if ( input[pos] !=':')
+		return error("Separator expected");
+
+	pos++;
+	return true;
+}
+
+bool JsonParser::extractValue(JsonNode &node)
+{
+	if (!extractWhitespace())
+		return false;
+
+	switch (input[pos])
+	{
+		case '\"': return extractString(node);
+		case 'n' : return extractNull(node);
+		case 't' : return extractTrue(node);
+		case 'f' : return extractFalse(node);
+		case '{' : return extractStruct(node);
+		case '[' : return extractArray(node);
+		case '-' : return extractFloat(node);
+		default:
+		{
+			if (input[pos] >= '0' && input[pos] <= '9')
+				return extractFloat(node);
+			return error("Value expected!");
+		}
+	}
+}
+
+bool JsonParser::extractWhitespace(bool verbose)
+{
+	while (true)
+	{
+		while(pos < input.size() && static_cast<ui8>(input[pos]) <= ' ')
+		{
+			if (input[pos] == '\n')
+			{
+				lineCount++;
+				lineStart = pos+1;
+			}
+			pos++;
+		}
+		if (pos >= input.size() || input[pos] != '/')
+			break;
+
+		pos++;
+		if (pos == input.size())
+			break;
+		if (input[pos] == '/')
+			pos++;
+		else
+			error("Comments must consist of two slashes!", true);
+
+		while (pos < input.size() && input[pos] != '\n')
+			pos++;
+	}
+
+	if (pos >= input.size() && verbose)
+		return error("Unexpected end of file!");
+	return true;
+}
+
+bool JsonParser::extractEscaping(std::string &str)
+{
+	switch(input[pos])
+	{
+		break; case '\"': str += '\"';
+		break; case '\\': str += '\\';
+		break; case 'b': str += '\b';
+		break; case 'f': str += '\f';
+		break; case 'n': str += '\n';
+		break; case 'r': str += '\r';
+		break; case 't': str += '\t';
+		break; case '/': str += '/';
+		break; default: return error("Unknown escape sequence!", true);
+	}
+	return true;
+}
+
+bool JsonParser::extractString(std::string &str)
+{
+	if (input[pos] != '\"')
+		return error("String expected!");
+	pos++;
+
+	size_t first = pos;
+
+	while (pos != input.size())
+	{
+		if (input[pos] == '\"') // Correct end of string
+		{
+			str.append( &input[first], pos-first);
+			pos++;
+			return true;
+		}
+		if (input[pos] == '\\') // Escaping
+		{
+			str.append( &input[first], pos-first);
+			pos++;
+			if (pos == input.size())
+				break;
+			extractEscaping(str);
+			first = pos + 1;
+		}
+		if (input[pos] == '\n') // end-of-line
+		{
+			str.append( &input[first], pos-first);
+			return error("Closing quote not found!", true);
+		}
+		if(static_cast<unsigned char>(input[pos]) < ' ') // control character
+		{
+			str.append( &input[first], pos-first);
+			first = pos+1;
+			error("Illegal character in the string!", true);
+		}
+		pos++;
+	}
+	return error("Unterminated string!");
+}
+
+bool JsonParser::extractString(JsonNode &node)
+{
+	std::string str;
+	if (!extractString(str))
+		return false;
+
+	node.setType(JsonNode::JsonType::DATA_STRING);
+	node.String() = str;
+	return true;
+}
+
+bool JsonParser::extractLiteral(const std::string &literal)
+{
+	if (literal.compare(0, literal.size(), &input[pos], literal.size()) != 0)
+	{
+		while (pos < input.size() && ((input[pos]>'a' && input[pos]<'z')
+								   || (input[pos]>'A' && input[pos]<'Z')))
+			pos++;
+		return error("Unknown literal found", true);
+	}
+
+	pos += literal.size();
+	return true;
+}
+
+bool JsonParser::extractNull(JsonNode &node)
+{
+	if (!extractLiteral("null"))
+		return false;
+
+	node.clear();
+	return true;
+}
+
+bool JsonParser::extractTrue(JsonNode &node)
+{
+	if (!extractLiteral("true"))
+		return false;
+
+	node.Bool() = true;
+	return true;
+}
+
+bool JsonParser::extractFalse(JsonNode &node)
+{
+	if (!extractLiteral("false"))
+		return false;
+
+	node.Bool() = false;
+	return true;
+}
+
+bool JsonParser::extractStruct(JsonNode &node)
+{
+	node.setType(JsonNode::JsonType::DATA_STRUCT);
+	pos++;
+
+	if (!extractWhitespace())
+		return false;
+
+	//Empty struct found
+	if (input[pos] == '}')
+	{
+		pos++;
+		return true;
+	}
+
+	while (true)
+	{
+		if (!extractWhitespace())
+			return false;
+
+		std::string key;
+		if (!extractString(key))
+			return false;
+
+		// split key string into actual key and meta-flags
+		std::vector<std::string> keyAndFlags;
+		boost::split(keyAndFlags, key, boost::is_any_of("#"));
+		key = keyAndFlags[0];
+		// check for unknown flags - helps with debugging
+		std::vector<std::string> knownFlags = { "override" };
+		for(int i = 1; i < keyAndFlags.size(); i++)
+		{
+			if(!vstd::contains(knownFlags, keyAndFlags[i]))
+				error("Encountered unknown flag #" + keyAndFlags[i], true);
+		}
+
+		if (node.Struct().find(key) != node.Struct().end())
+			error("Duplicate element encountered!", true);
+
+		if (!extractSeparator())
+			return false;
+
+		if (!extractElement(node.Struct()[key], '}'))
+			return false;
+
+		// flags from key string belong to referenced element
+		for(int i = 1; i < keyAndFlags.size(); i++)
+			node.Struct()[key].flags.push_back(keyAndFlags[i]);
+
+		if (input[pos] == '}')
+		{
+			pos++;
+			return true;
+		}
+	}
+}
+
+bool JsonParser::extractArray(JsonNode &node)
+{
+	pos++;
+	node.setType(JsonNode::JsonType::DATA_VECTOR);
+
+	if (!extractWhitespace())
+		return false;
+
+	//Empty array found
+	if (input[pos] == ']')
+	{
+		pos++;
+		return true;
+	}
+
+	while (true)
+	{
+		//NOTE: currently 50% of time is this vector resizing.
+		//May be useful to use list during parsing and then swap() all items to vector
+		node.Vector().resize(node.Vector().size()+1);
+
+		if (!extractElement(node.Vector().back(), ']'))
+			return false;
+
+		if (input[pos] == ']')
+		{
+			pos++;
+			return true;
+		}
+	}
+}
+
+bool JsonParser::extractElement(JsonNode &node, char terminator)
+{
+	if (!extractValue(node))
+		return false;
+
+	if (!extractWhitespace())
+		return false;
+
+	bool comma = (input[pos] == ',');
+	if (comma )
+	{
+		pos++;
+		if (!extractWhitespace())
+			return false;
+	}
+
+	if (input[pos] == terminator)
+	{
+		//FIXME: MOD COMPATIBILITY: Too many of these right now, re-enable later
+		//if (comma)
+			//error("Extra comma found!", true);
+		return true;
+	}
+
+	if (!comma)
+		error("Comma expected!", true);
+
+	return true;
+}
+
+bool JsonParser::extractFloat(JsonNode &node)
+{
+	assert(input[pos] == '-' || (input[pos] >= '0' && input[pos] <= '9'));
+	bool negative=false;
+	double result=0;
+	si64 integerPart = 0;
+	bool isFloat = false;
+
+	if (input[pos] == '-')
+	{
+		pos++;
+		negative = true;
+	}
+
+	if (input[pos] < '0' || input[pos] > '9')
+		return error("Number expected!");
+
+	//Extract integer part
+	while (input[pos] >= '0' && input[pos] <= '9')
+	{
+		integerPart = integerPart*10+(input[pos]-'0');
+		pos++;
+	}
+
+	result = static_cast<double>(integerPart);
+
+	if (input[pos] == '.')
+	{
+		//extract fractional part
+		isFloat = true;
+		pos++;
+		double fractMult = 0.1;
+		if (input[pos] < '0' || input[pos] > '9')
+			return error("Decimal part expected!");
+
+		while (input[pos] >= '0' && input[pos] <= '9')
+		{
+			result = result + fractMult*(input[pos]-'0');
+			fractMult /= 10;
+			pos++;
+		}
+	}
+
+	if(input[pos] == 'e')
+	{
+		//extract exponential part
+		pos++;
+		isFloat = true;
+		bool powerNegative = false;
+		double power = 0;
+
+		if(input[pos] == '-')
+		{
+			pos++;
+			powerNegative = true;
+		}
+		else if(input[pos] == '+')
+		{
+			pos++;
+		}
+
+		if (input[pos] < '0' || input[pos] > '9')
+			return error("Exponential part expected!");
+
+		while (input[pos] >= '0' && input[pos] <= '9')
+		{
+			power = power*10 + (input[pos]-'0');
+			pos++;
+		}
+
+		if(powerNegative)
+			power = -power;
+
+		result *= std::pow(10, power);
+	}
+
+	if(isFloat)
+	{
+		if(negative)
+			result = -result;
+
+		node.setType(JsonNode::JsonType::DATA_FLOAT);
+		node.Float() = result;
+	}
+	else
+	{
+		if(negative)
+			integerPart = -integerPart;
+
+		node.setType(JsonNode::JsonType::DATA_INTEGER);
+		node.Integer() = integerPart;
+	}
+
+	return true;
+}
+
+bool JsonParser::error(const std::string &message, bool warning)
+{
+	std::ostringstream stream;
+	std::string type(warning?" warning: ":" error: ");
+
+	stream << "At line " << lineCount << ", position "<<pos-lineStart
+		   << type << message <<"\n";
+	errors += stream.str();
+
+	return warning;
+}
+
+VCMI_LIB_NAMESPACE_END

+ 82 - 0
lib/json/JsonParser.h

@@ -0,0 +1,82 @@
+/*
+ * JsonParser.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 "JsonNode.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+//Tiny string class that uses const char* as data for speed, members are private
+//for ease of debugging and some compatibility with std::string
+class constString
+{
+	const char *data;
+	const size_t datasize;
+
+public:
+	constString(const char * inputString, size_t stringSize):
+		data(inputString),
+		datasize(stringSize)
+	{
+	}
+
+	inline size_t size() const
+	{
+		return datasize;
+	};
+
+	inline const char& operator[] (size_t position)
+	{
+		assert (position < datasize);
+		return data[position];
+	}
+};
+
+//Internal class for string -> JsonNode conversion
+class JsonParser
+{
+	std::string errors;     // Contains description of all encountered errors
+	constString input;      // Input data
+	ui32 lineCount; // Currently parsed line, starting from 1
+	size_t lineStart;       // Position of current line start
+	size_t pos;             // Current position of parser
+
+	//Helpers
+	bool extractEscaping(std::string &str);
+	bool extractLiteral(const std::string &literal);
+	bool extractString(std::string &string);
+	bool extractWhitespace(bool verbose = true);
+	bool extractSeparator();
+	bool extractElement(JsonNode &node, char terminator);
+
+	//Methods for extracting JSON data
+	bool extractArray(JsonNode &node);
+	bool extractFalse(JsonNode &node);
+	bool extractFloat(JsonNode &node);
+	bool extractNull(JsonNode &node);
+	bool extractString(JsonNode &node);
+	bool extractStruct(JsonNode &node);
+	bool extractTrue(JsonNode &node);
+	bool extractValue(JsonNode &node);
+
+	//Add error\warning message to list
+	bool error(const std::string &message, bool warning=false);
+
+public:
+	JsonParser(const char * inputString, size_t stringSize);
+
+	/// do actual parsing. filename is name of file that will printed to console if any errors were found
+	JsonNode parse(const std::string & fileName);
+
+	/// returns true if parsing was successful
+	bool isValid();
+};
+
+VCMI_LIB_NAMESPACE_END

+ 16 - 15
lib/JsonRandom.cpp → lib/json/JsonRandom.cpp

@@ -13,21 +13,22 @@
 
 #include <vstd/StringUtils.h>
 
-#include "JsonNode.h"
-#include "CRandomGenerator.h"
-#include "constants/StringConstants.h"
-#include "VCMI_Lib.h"
-#include "CArtHandler.h"
-#include "CCreatureHandler.h"
-#include "CCreatureSet.h"
-#include "spells/CSpellHandler.h"
-#include "CSkillHandler.h"
-#include "CHeroHandler.h"
-#include "IGameCallback.h"
-#include "gameState/CGameState.h"
-#include "mapObjects/IObjectInterface.h"
-#include "modding/IdentifierStorage.h"
-#include "modding/ModScope.h"
+#include "JsonUtils.h"
+
+#include "../CRandomGenerator.h"
+#include "../constants/StringConstants.h"
+#include "../VCMI_Lib.h"
+#include "../CArtHandler.h"
+#include "../CCreatureHandler.h"
+#include "../CCreatureSet.h"
+#include "../spells/CSpellHandler.h"
+#include "../CSkillHandler.h"
+#include "../CHeroHandler.h"
+#include "../IGameCallback.h"
+#include "../gameState/CGameState.h"
+#include "../mapObjects/IObjectInterface.h"
+#include "../modding/IdentifierStorage.h"
+#include "../modding/ModScope.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 0 - 0
lib/JsonRandom.h → lib/json/JsonRandom.h


+ 21 - 435
lib/JsonNode.cpp → lib/json/JsonUtils.cpp

@@ -1,5 +1,5 @@
 /*
- * JsonNode.cpp, part of VCMI engine
+ * JsonUtils.cpp, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
  *
@@ -9,437 +9,25 @@
  */
 
 #include "StdInc.h"
-#include "JsonNode.h"
-
-#include "ScopeGuard.h"
-
-#include "bonuses/BonusParams.h"
-#include "bonuses/Bonus.h"
-#include "bonuses/Limiters.h"
-#include "bonuses/Propagators.h"
-#include "bonuses/Updaters.h"
-#include "filesystem/Filesystem.h"
-#include "modding/IdentifierStorage.h"
-#include "VCMI_Lib.h" //for identifier resolution
-#include "CGeneralTextHandler.h"
-#include "JsonDetail.h"
-#include "constants/StringConstants.h"
-#include "battle/BattleHex.h"
-
-namespace
-{
-// to avoid duplicating const and non-const code
-template<typename Node>
-Node & resolvePointer(Node & in, const std::string & pointer)
-{
-	if(pointer.empty())
-		return in;
-	assert(pointer[0] == '/');
-
-	size_t splitPos = pointer.find('/', 1);
-
-	std::string entry = pointer.substr(1, splitPos - 1);
-	std::string remainer = splitPos == std::string::npos ? "" : pointer.substr(splitPos);
-
-	if(in.getType() == VCMI_LIB_WRAP_NAMESPACE(JsonNode)::JsonType::DATA_VECTOR)
-	{
-		if(entry.find_first_not_of("0123456789") != std::string::npos) // non-numbers in string
-			throw std::runtime_error("Invalid Json pointer");
-
-		if(entry.size() > 1 && entry[0] == '0') // leading zeros are not allowed
-			throw std::runtime_error("Invalid Json pointer");
-
-		auto index = boost::lexical_cast<size_t>(entry);
-
-		if (in.Vector().size() > index)
-			return in.Vector()[index].resolvePointer(remainer);
-	}
-	return in[entry].resolvePointer(remainer);
-}
-}
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-using namespace JsonDetail;
-
-class LibClasses;
-class CModHandler;
+#include "JsonUtils.h"
+
+#include "JsonValidator.h"
+
+#include "../ScopeGuard.h"
+#include "../bonuses/BonusParams.h"
+#include "../bonuses/Bonus.h"
+#include "../bonuses/Limiters.h"
+#include "../bonuses/Propagators.h"
+#include "../bonuses/Updaters.h"
+#include "../filesystem/Filesystem.h"
+#include "../modding/IdentifierStorage.h"
+#include "../VCMI_Lib.h" //for identifier resolution
+#include "../CGeneralTextHandler.h"
+#include "../constants/StringConstants.h"
+#include "../battle/BattleHex.h"
 
 static const JsonNode nullNode;
 
-JsonNode::JsonNode(JsonType Type)
-{
-	setType(Type);
-}
-
-JsonNode::JsonNode(const std::byte *data, size_t datasize)
-	:JsonNode(reinterpret_cast<const char*>(data), datasize)
-{}
-
-JsonNode::JsonNode(const char *data, size_t datasize)
-{
-	JsonParser parser(data, datasize);
-	*this = parser.parse("<unknown>");
-}
-
-JsonNode::JsonNode(const JsonPath & fileURI)
-{
-	auto file = CResourceHandler::get()->load(fileURI)->readAll();
-
-	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
-	*this = parser.parse(fileURI.getName());
-}
-
-JsonNode::JsonNode(const std::string & idx, const JsonPath & fileURI)
-{
-	auto file = CResourceHandler::get(idx)->load(fileURI)->readAll();
-	
-	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
-	*this = parser.parse(fileURI.getName());
-}
-
-JsonNode::JsonNode(const JsonPath & fileURI, bool &isValidSyntax)
-{
-	auto file = CResourceHandler::get()->load(fileURI)->readAll();
-
-	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
-	*this = parser.parse(fileURI.getName());
-	isValidSyntax = parser.isValid();
-}
-
-bool JsonNode::operator == (const JsonNode &other) const
-{
-	return data == other.data;
-}
-
-bool JsonNode::operator != (const JsonNode &other) const
-{
-	return !(*this == other);
-}
-
-JsonNode::JsonType JsonNode::getType() const
-{
-	return static_cast<JsonType>(data.index());
-}
-
-void JsonNode::setMeta(const std::string & metadata, bool recursive)
-{
-	meta = metadata;
-	if (recursive)
-	{
-		switch (getType())
-		{
-			break; case JsonType::DATA_VECTOR:
-			{
-				for(auto & node : Vector())
-				{
-					node.setMeta(metadata);
-				}
-			}
-			break; case JsonType::DATA_STRUCT:
-			{
-				for(auto & node : Struct())
-				{
-					node.second.setMeta(metadata);
-				}
-			}
-		}
-	}
-}
-
-void JsonNode::setType(JsonType Type)
-{
-	if (getType() == Type)
-		return;
-
-	//float<->int conversion
-	if(getType() == JsonType::DATA_FLOAT && Type == JsonType::DATA_INTEGER)
-	{
-		si64 converted = static_cast<si64>(std::get<double>(data));
-		data = JsonData(converted);
-		return;
-	}
-	else if(getType() == JsonType::DATA_INTEGER && Type == JsonType::DATA_FLOAT)
-	{
-		double converted = static_cast<double>(std::get<si64>(data));
-		data = JsonData(converted);
-		return;
-	}
-
-	//Set new node type
-	switch(Type)
-	{
-		break; case JsonType::DATA_NULL:    data = JsonData();
-		break; case JsonType::DATA_BOOL:    data = JsonData(false);
-		break; case JsonType::DATA_FLOAT:   data = JsonData(static_cast<double>(0.0));
-		break; case JsonType::DATA_STRING:  data = JsonData(std::string());
-		break; case JsonType::DATA_VECTOR:  data = JsonData(JsonVector());
-		break; case JsonType::DATA_STRUCT:  data = JsonData(JsonMap());
-		break; case JsonType::DATA_INTEGER: data = JsonData(static_cast<si64>(0));
-	}
-}
-
-bool JsonNode::isNull() const
-{
-	return getType() == JsonType::DATA_NULL;
-}
-
-bool JsonNode::isNumber() const
-{
-	return getType() == JsonType::DATA_INTEGER || getType() == JsonType::DATA_FLOAT;
-}
-
-bool JsonNode::isString() const
-{
-	return getType() == JsonType::DATA_STRING;
-}
-
-bool JsonNode::isVector() const
-{
-	return getType() == JsonType::DATA_VECTOR;
-}
-
-bool JsonNode::isStruct() const
-{
-	return getType() == JsonType::DATA_STRUCT;
-}
-
-bool JsonNode::containsBaseData() const
-{
-	switch(getType())
-	{
-	case JsonType::DATA_NULL:
-		return false;
-	case JsonType::DATA_STRUCT:
-		for(const auto & elem : Struct())
-		{
-			if(elem.second.containsBaseData())
-				return true;
-		}
-		return false;
-	default:
-		//other types (including vector) cannot be extended via merge
-		return true;
-	}
-}
-
-bool JsonNode::isCompact() const
-{
-	switch(getType())
-	{
-	case JsonType::DATA_VECTOR:
-		for(const JsonNode & elem : Vector())
-		{
-			if(!elem.isCompact())
-				return false;
-		}
-		return true;
-	case JsonType::DATA_STRUCT:
-		{
-			auto propertyCount = Struct().size();
-			if(propertyCount == 0)
-				return true;
-			else if(propertyCount == 1)
-				return Struct().begin()->second.isCompact();
-		}
-		return false;
-	default:
-		return true;
-	}
-}
-
-bool JsonNode::TryBoolFromString(bool & success) const
-{
-	success = true;
-	if(getType() == JsonNode::JsonType::DATA_BOOL)
-		return Bool();
-
-	success = getType() == JsonNode::JsonType::DATA_STRING;
-	if(success)
-	{
-		auto boolParamStr = String();
-		boost::algorithm::trim(boolParamStr);
-		boost::algorithm::to_lower(boolParamStr);
-		success = boolParamStr == "true";
-
-		if(success)
-			return true;
-		
-		success = boolParamStr == "false";
-	}
-	return false;
-}
-
-void JsonNode::clear()
-{
-	setType(JsonType::DATA_NULL);
-}
-
-bool & JsonNode::Bool()
-{
-	setType(JsonType::DATA_BOOL);
-	return std::get<bool>(data);
-}
-
-double & JsonNode::Float()
-{
-	setType(JsonType::DATA_FLOAT);
-	return std::get<double>(data);
-}
-
-si64 & JsonNode::Integer()
-{
-	setType(JsonType::DATA_INTEGER);
-	return std::get<si64>(data);
-}
-
-std::string & JsonNode::String()
-{
-	setType(JsonType::DATA_STRING);
-	return std::get<std::string>(data);
-}
-
-JsonVector & JsonNode::Vector()
-{
-	setType(JsonType::DATA_VECTOR);
-	return std::get<JsonVector>(data);
-}
-
-JsonMap & JsonNode::Struct()
-{
-	setType(JsonType::DATA_STRUCT);
-	return std::get<JsonMap>(data);
-}
-
-const bool boolDefault = false;
-bool JsonNode::Bool() const
-{
-	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_BOOL);
-
-	if (getType() == JsonType::DATA_BOOL)
-		return std::get<bool>(data);
-
-	return boolDefault;
-}
-
-const double floatDefault = 0;
-double JsonNode::Float() const
-{
-	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_INTEGER || getType() == JsonType::DATA_FLOAT);
-
-	if(getType() == JsonType::DATA_FLOAT)
-		return std::get<double>(data);
-
-	if(getType() == JsonType::DATA_INTEGER)
-		return static_cast<double>(std::get<si64>(data));
-
-	return floatDefault;
-}
-
-const si64 integerDefault = 0;
-si64 JsonNode::Integer() const
-{
-	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_INTEGER || getType() == JsonType::DATA_FLOAT);
-
-	if(getType() == JsonType::DATA_INTEGER)
-		return std::get<si64>(data);
-
-	if(getType() == JsonType::DATA_FLOAT)
-		return static_cast<si64>(std::get<double>(data));
-
-	return integerDefault;
-}
-
-const std::string stringDefault = std::string();
-const std::string & JsonNode::String() const
-{
-	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_STRING);
-
-	if (getType() == JsonType::DATA_STRING)
-		return std::get<std::string>(data);
-
-	return stringDefault;
-}
-
-const JsonVector vectorDefault = JsonVector();
-const JsonVector & JsonNode::Vector() const
-{
-	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_VECTOR);
-
-	if (getType() == JsonType::DATA_VECTOR)
-		return std::get<JsonVector>(data);
-
-	return vectorDefault;
-}
-
-const JsonMap mapDefault = JsonMap();
-const JsonMap & JsonNode::Struct() const
-{
-	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_STRUCT);
-
-	if (getType() == JsonType::DATA_STRUCT)
-		return std::get<JsonMap>(data);
-
-	return mapDefault;
-}
-
-JsonNode & JsonNode::operator[](const std::string & child)
-{
-	return Struct()[child];
-}
-
-const JsonNode & JsonNode::operator[](const std::string & child) const
-{
-	auto it = Struct().find(child);
-	if (it != Struct().end())
-		return it->second;
-	return nullNode;
-}
-
-JsonNode & JsonNode::operator[](size_t child)
-{
-	if (child >= Vector().size() )
-		Vector().resize(child + 1);
-	return Vector()[child];
-}
-
-const JsonNode & JsonNode::operator[](size_t child) const
-{
-	if (child < Vector().size() )
-		return Vector()[child];
-
-	return nullNode;
-}
-
-const JsonNode & JsonNode::resolvePointer(const std::string &jsonPointer) const
-{
-	return ::resolvePointer(*this, jsonPointer);
-}
-
-JsonNode & JsonNode::resolvePointer(const std::string &jsonPointer)
-{
-	return ::resolvePointer(*this, jsonPointer);
-}
-
-std::vector<std::byte> JsonNode::toBytes(bool compact) const
-{
-	std::string jsonString = toJson(compact);
-	auto dataBegin = reinterpret_cast<const std::byte*>(jsonString.data());
-	auto dataEnd = dataBegin + jsonString.size();
-	std::vector<std::byte> result(dataBegin, dataEnd);
-	return result;
-}
-
-std::string JsonNode::toJson(bool compact) const
-{
-	std::ostringstream out;
-	JsonWriter writer(out, compact);
-	writer.writeNode(*this);
-	return out.str();
-}
-
-///JsonUtils
-
 static void loadBonusSubtype(BonusSubtypeID & subtype, BonusType type, const JsonNode & node)
 {
 	if (node.isNull())
@@ -1145,7 +733,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
 	if (!value->isNull())
 	{
 		//ALL_CREATURES old propagator compatibility
-		if(value->String() == "ALL_CREATURES") 
+		if(value->String() == "ALL_CREATURES")
 		{
 			logMod->warn("ALL_CREATURES propagator is deprecated. Use GLOBAL_EFFECT propagator with CREATURES_ONLY limiter");
 			b->addLimiter(std::make_shared<CreatureLevelLimiter>());
@@ -1182,7 +770,7 @@ CSelector JsonUtils::parseSelector(const JsonNode & ability)
 		CSelector base = Selector::none;
 		for(const auto & andN : value->Vector())
 			base = base.Or(parseSelector(andN));
-		
+
 		ret = ret.And(base);
 	}
 
@@ -1192,7 +780,7 @@ CSelector JsonUtils::parseSelector(const JsonNode & ability)
 		CSelector base = Selector::none;
 		for(const auto & andN : value->Vector())
 			base = base.Or(parseSelector(andN));
-		
+
 		ret = ret.And(base.Not());
 	}
 
@@ -1237,7 +825,7 @@ CSelector JsonUtils::parseSelector(const JsonNode & ability)
 	else if(src)
 		ret = ret.And(Selector::sourceTypeSel(*src));
 
-	
+
 	value = &ability["targetSourceType"];
 	if(value->isString())
 	{
@@ -1649,5 +1237,3 @@ DLL_LINKAGE JsonNode JsonUtils::intNode(si64 value)
 	node.Integer() = value;
 	return node;
 }
-
-VCMI_LIB_NAMESPACE_END

+ 104 - 0
lib/json/JsonUtils.h

@@ -0,0 +1,104 @@
+/*
+ * JsonUtils.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 "JsonNode.h"
+#include "../GameConstants.h"
+
+namespace JsonUtils
+{
+	DLL_LINKAGE std::shared_ptr<Bonus> parseBonus(const JsonVector & ability_vec);
+	DLL_LINKAGE std::shared_ptr<Bonus> parseBonus(const JsonNode & ability);
+	DLL_LINKAGE std::shared_ptr<Bonus> parseBuildingBonus(const JsonNode & ability, const FactionID & faction, const BuildingID & building, const std::string & description);
+	DLL_LINKAGE bool parseBonus(const JsonNode & ability, Bonus * placement);
+	DLL_LINKAGE std::shared_ptr<ILimiter> parseLimiter(const JsonNode & limiter);
+	DLL_LINKAGE CSelector parseSelector(const JsonNode &ability);
+	DLL_LINKAGE void resolveAddInfo(CAddInfo & var, const JsonNode & node);
+
+	/**
+	 * @brief recursively merges source into dest, replacing identical fields
+	 * struct : recursively calls this function
+	 * arrays : each entry will be merged recursively
+	 * values : value in source will replace value in dest
+	 * null   : if value in source is present but set to null it will delete entry in dest
+	 * @note this function will destroy data in source
+	 */
+	DLL_LINKAGE void merge(JsonNode & dest, JsonNode & source, bool ignoreOverride = false, bool copyMeta = false);
+
+	/**
+	 * @brief recursively merges source into dest, replacing identical fields
+	 * struct : recursively calls this function
+	 * arrays : each entry will be merged recursively
+	 * values : value in source will replace value in dest
+	 * null   : if value in source is present but set to null it will delete entry in dest
+	 * @note this function will preserve data stored in source by creating copy
+	 */
+	DLL_LINKAGE void mergeCopy(JsonNode & dest, JsonNode source, bool ignoreOverride = false, bool copyMeta = false);
+
+	/** @brief recursively merges descendant into copy of base node
+	* Result emulates inheritance semantic
+	*/
+	DLL_LINKAGE void inherit(JsonNode & descendant, const JsonNode & base);
+
+	/**
+	 * @brief construct node representing the common structure of input nodes
+	 * @param pruneEmpty - omit common properties whose intersection is empty
+	 * different types: null
+	 * struct: recursive intersect on common properties
+	 * other: input if equal, null otherwise
+	 */
+	DLL_LINKAGE JsonNode intersect(const JsonNode & a, const JsonNode & b, bool pruneEmpty = true);
+	DLL_LINKAGE JsonNode intersect(const std::vector<JsonNode> & nodes, bool pruneEmpty = true);
+
+	/**
+	 * @brief construct node representing the difference "node - base"
+	 * merging difference with base gives node
+	 */
+	DLL_LINKAGE JsonNode difference(const JsonNode & node, const JsonNode & base);
+
+	/**
+	 * @brief generate one Json structure from multiple files
+	 * @param files - list of filenames with parts of json structure
+	 */
+	DLL_LINKAGE JsonNode assembleFromFiles(const std::vector<std::string> & files);
+	DLL_LINKAGE JsonNode assembleFromFiles(const std::vector<std::string> & files, bool & isValid);
+
+	/// This version loads all files with same name (overridden by mods)
+	DLL_LINKAGE JsonNode assembleFromFiles(const std::string & filename);
+
+	/**
+	 * @brief removes all nodes that are identical to default entry in schema
+	 * @param node - JsonNode to minimize
+	 * @param schemaName - name of schema to use
+	 * @note for minimizing data must be valid against given schema
+	 */
+	DLL_LINKAGE void minimize(JsonNode & node, const std::string & schemaName);
+	/// opposed to minimize, adds all missing, required entries that have default value
+	DLL_LINKAGE void maximize(JsonNode & node, const std::string & schemaName);
+
+	/**
+	* @brief validate node against specified schema
+	* @param node - JsonNode to check
+	* @param schemaName - name of schema to use
+	* @param dataName - some way to identify data (printed in console in case of errors)
+	* @returns true if data in node fully compilant with schema
+	*/
+	DLL_LINKAGE bool validate(const JsonNode & node, const std::string & schemaName, const std::string & dataName);
+
+	/// get schema by json URI: vcmi:<name of file in schemas directory>#<entry in file, optional>
+	/// example: schema "vcmi:settings" is used to check user settings
+	DLL_LINKAGE const JsonNode & getSchema(const std::string & URI);
+
+	/// for easy construction of JsonNodes; helps with inserting primitives into vector node
+	DLL_LINKAGE JsonNode boolNode(bool value);
+	DLL_LINKAGE JsonNode floatNode(double value);
+	DLL_LINKAGE JsonNode stringNode(const std::string & value);
+	DLL_LINKAGE JsonNode intNode(si64 value);
+}

+ 687 - 0
lib/json/JsonValidator.cpp

@@ -0,0 +1,687 @@
+/*
+ * JsonValidator.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 "JsonValidator.h"
+
+#include "JsonUtils.h"
+
+#include "../VCMI_Lib.h"
+#include "../filesystem/Filesystem.h"
+#include "../modding/ModScope.h"
+#include "../modding/CModHandler.h"
+#include "../ScopeGuard.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+//TODO: integer support
+
+static const std::unordered_map<std::string, JsonNode::JsonType> stringToType =
+{
+	{"null",   JsonNode::JsonType::DATA_NULL},
+	{"boolean", JsonNode::JsonType::DATA_BOOL},
+	{"number", JsonNode::JsonType::DATA_FLOAT},
+	{"string",  JsonNode::JsonType::DATA_STRING},
+	{"array",  JsonNode::JsonType::DATA_VECTOR},
+	{"object",  JsonNode::JsonType::DATA_STRUCT}
+};
+
+namespace
+{
+	namespace Common
+	{
+		std::string emptyCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			// check is not needed - e.g. incorporated into another check
+			return "";
+		}
+
+		std::string notImplementedCheck(Validation::ValidationData & validator,
+										const JsonNode & baseSchema,
+										const JsonNode & schema,
+										const JsonNode & data)
+		{
+			return "Not implemented entry in schema";
+		}
+
+		std::string schemaListCheck(Validation::ValidationData & validator,
+									const JsonNode & baseSchema,
+									const JsonNode & schema,
+									const JsonNode & data,
+									const std::string & errorMsg,
+									const std::function<bool(size_t)> & isValid)
+		{
+			std::string errors = "<tested schemas>\n";
+			size_t result = 0;
+
+			for(const auto & schemaEntry : schema.Vector())
+			{
+				std::string error = check(schemaEntry, data, validator);
+				if (error.empty())
+				{
+					result++;
+				}
+				else
+				{
+					errors += error;
+					errors += "<end of schema>\n";
+				}
+			}
+			if (isValid(result))
+				return "";
+			else
+				return validator.makeErrorMessage(errorMsg) + errors;
+		}
+
+		std::string allOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass all schemas", [&](size_t count)
+			{
+				return count == schema.Vector().size();
+			});
+		}
+
+		std::string anyOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass any schema", [&](size_t count)
+			{
+				return count > 0;
+			});
+		}
+
+		std::string oneOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass exactly one schema", [&](size_t count)
+			{
+				return count == 1;
+			});
+		}
+
+		std::string notCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			if (check(schema, data, validator).empty())
+				return validator.makeErrorMessage("Successful validation against negative check");
+			return "";
+		}
+
+		std::string enumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			for(const auto & enumEntry : schema.Vector())
+			{
+				if (data == enumEntry)
+					return "";
+			}
+			return validator.makeErrorMessage("Key must have one of predefined values");
+		}
+
+		std::string typeCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			const auto & typeName = schema.String();
+			auto it = stringToType.find(typeName);
+			if(it == stringToType.end())
+			{
+				return validator.makeErrorMessage("Unknown type in schema:" + typeName);
+			}
+
+			JsonNode::JsonType type = it->second;
+
+			//FIXME: hack for integer values
+			if(data.isNumber() && type == JsonNode::JsonType::DATA_FLOAT)
+				return "";
+
+			if(type != data.getType() && data.getType() != JsonNode::JsonType::DATA_NULL)
+				return validator.makeErrorMessage("Type mismatch! Expected " + schema.String());
+			return "";
+		}
+
+		std::string refCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			std::string URI = schema.String();
+			//node must be validated using schema pointed by this reference and not by data here
+			//Local reference. Turn it into more easy to handle remote ref
+			if (boost::algorithm::starts_with(URI, "#"))
+			{
+				const std::string name = validator.usedSchemas.back();
+				const std::string nameClean = name.substr(0, name.find('#'));
+				URI = nameClean + URI;
+			}
+			return check(URI, data, validator);
+		}
+
+		std::string formatCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			auto formats = Validation::getKnownFormats();
+			std::string errors;
+			auto checker = formats.find(schema.String());
+			if (checker != formats.end())
+			{
+				if (data.isString())
+				{
+					std::string result = checker->second(data);
+					if (!result.empty())
+						errors += validator.makeErrorMessage(result);
+				}
+				else
+				{
+					errors += validator.makeErrorMessage("Format value must be string: " + schema.String());
+				}
+			}
+			else
+				errors += validator.makeErrorMessage("Unsupported format type: " + schema.String());
+			return errors;
+		}
+	}
+
+	namespace String
+	{
+		std::string maxLengthCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			if (data.String().size() > schema.Float())
+				return validator.makeErrorMessage((boost::format("String is longer than %d symbols") % schema.Float()).str());
+			return "";
+		}
+
+		std::string minLengthCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			if (data.String().size() < schema.Float())
+				return validator.makeErrorMessage((boost::format("String is shorter than %d symbols") % schema.Float()).str());
+			return "";
+		}
+	}
+
+	namespace Number
+	{
+
+		std::string maximumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			if (baseSchema["exclusiveMaximum"].Bool())
+			{
+				if (data.Float() >= schema.Float())
+					return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
+			}
+			else
+			{
+				if (data.Float() >  schema.Float())
+					return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
+			}
+			return "";
+		}
+
+		std::string minimumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			if (baseSchema["exclusiveMinimum"].Bool())
+			{
+				if (data.Float() <= schema.Float())
+					return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
+			}
+			else
+			{
+				if (data.Float() <  schema.Float())
+					return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
+			}
+			return "";
+		}
+
+		std::string multipleOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			double result = data.Float() / schema.Float();
+			if (floor(result) != result)
+				return validator.makeErrorMessage((boost::format("Value is not divisible by %d") % schema.Float()).str());
+			return "";
+		}
+	}
+
+	namespace Vector
+	{
+		std::string itemEntryCheck(Validation::ValidationData & validator, const JsonVector & items, const JsonNode & schema, size_t index)
+		{
+			validator.currentPath.emplace_back();
+			validator.currentPath.back().Float() = static_cast<double>(index);
+			auto onExit = vstd::makeScopeGuard([&]()
+			{
+				validator.currentPath.pop_back();
+			});
+
+			if (!schema.isNull())
+				return check(schema, items[index], validator);
+			return "";
+		}
+
+		std::string itemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			std::string errors;
+			for (size_t i=0; i<data.Vector().size(); i++)
+			{
+				if (schema.getType() == JsonNode::JsonType::DATA_VECTOR)
+				{
+					if (schema.Vector().size() > i)
+						errors += itemEntryCheck(validator, data.Vector(), schema.Vector()[i], i);
+				}
+				else
+				{
+					errors += itemEntryCheck(validator, data.Vector(), schema, i);
+				}
+			}
+			return errors;
+		}
+
+		std::string additionalItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			std::string errors;
+			// "items" is struct or empty (defaults to empty struct) - validation always successful
+			const JsonNode & items = baseSchema["items"];
+			if (items.getType() != JsonNode::JsonType::DATA_VECTOR)
+				return "";
+
+			for (size_t i=items.Vector().size(); i<data.Vector().size(); i++)
+			{
+				if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
+					errors += itemEntryCheck(validator, data.Vector(), schema, i);
+				else if(!schema.isNull() && !schema.Bool())
+					errors += validator.makeErrorMessage("Unknown entry found");
+			}
+			return errors;
+		}
+
+		std::string minItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			if (data.Vector().size() < schema.Float())
+				return validator.makeErrorMessage((boost::format("Length is smaller than %d") % schema.Float()).str());
+			return "";
+		}
+
+		std::string maxItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			if (data.Vector().size() > schema.Float())
+				return validator.makeErrorMessage((boost::format("Length is bigger than %d") % schema.Float()).str());
+			return "";
+		}
+
+		std::string uniqueItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			if (schema.Bool())
+			{
+				for (auto itA = schema.Vector().begin(); itA != schema.Vector().end(); itA++)
+				{
+					auto itB = itA;
+					while (++itB != schema.Vector().end())
+					{
+						if (*itA == *itB)
+							return validator.makeErrorMessage("List must consist from unique items");
+					}
+				}
+			}
+			return "";
+		}
+	}
+
+	namespace Struct
+	{
+		std::string maxPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			if (data.Struct().size() > schema.Float())
+				return validator.makeErrorMessage((boost::format("Number of entries is bigger than %d") % schema.Float()).str());
+			return "";
+		}
+
+		std::string minPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			if (data.Struct().size() < schema.Float())
+				return validator.makeErrorMessage((boost::format("Number of entries is less than %d") % schema.Float()).str());
+			return "";
+		}
+
+		std::string uniquePropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			for (auto itA = data.Struct().begin(); itA != data.Struct().end(); itA++)
+			{
+				auto itB = itA;
+				while (++itB != data.Struct().end())
+				{
+					if (itA->second == itB->second)
+						return validator.makeErrorMessage("List must consist from unique items");
+				}
+			}
+			return "";
+		}
+
+		std::string requiredCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			std::string errors;
+			for(const auto & required : schema.Vector())
+			{
+				if (data[required.String()].isNull())
+					errors += validator.makeErrorMessage("Required entry " + required.String() + " is missing");
+			}
+			return errors;
+		}
+
+		std::string dependenciesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			std::string errors;
+			for(const auto & deps : schema.Struct())
+			{
+				if (!data[deps.first].isNull())
+				{
+					if (deps.second.getType() == JsonNode::JsonType::DATA_VECTOR)
+					{
+						JsonVector depList = deps.second.Vector();
+						for(auto & depEntry : depList)
+						{
+							if (data[depEntry.String()].isNull())
+								errors += validator.makeErrorMessage("Property " + depEntry.String() + " required for " + deps.first + " is missing");
+						}
+					}
+					else
+					{
+						if (!check(deps.second, data, validator).empty())
+							errors += validator.makeErrorMessage("Requirements for " + deps.first + " are not fulfilled");
+					}
+				}
+			}
+			return errors;
+		}
+
+		std::string propertyEntryCheck(Validation::ValidationData & validator, const JsonNode &node, const JsonNode & schema, const std::string & nodeName)
+		{
+			validator.currentPath.emplace_back();
+			validator.currentPath.back().String() = nodeName;
+			auto onExit = vstd::makeScopeGuard([&]()
+			{
+				validator.currentPath.pop_back();
+			});
+
+			// there is schema specifically for this item
+			if (!schema.isNull())
+				return check(schema, node, validator);
+			return "";
+		}
+
+		std::string propertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			std::string errors;
+
+			for(const auto & entry : data.Struct())
+				errors += propertyEntryCheck(validator, entry.second, schema[entry.first], entry.first);
+			return errors;
+		}
+
+		std::string additionalPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		{
+			std::string errors;
+			for(const auto & entry : data.Struct())
+			{
+				if (baseSchema["properties"].Struct().count(entry.first) == 0)
+				{
+					// try generic additionalItems schema
+					if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
+						errors += propertyEntryCheck(validator, entry.second, schema, entry.first);
+
+					// or, additionalItems field can be bool which indicates if such items are allowed
+					else if(!schema.isNull() && !schema.Bool()) // present and set to false - error
+						errors += validator.makeErrorMessage("Unknown entry found: " + entry.first);
+				}
+			}
+			return errors;
+		}
+	}
+
+	namespace Formats
+	{
+		bool testFilePresence(const std::string & scope, const ResourcePath & resource)
+		{
+			std::set<std::string> allowedScopes;
+			if(scope != ModScope::scopeBuiltin() && !scope.empty()) // all real mods may have dependencies
+			{
+				//NOTE: recursive dependencies are not allowed at the moment - update code if this changes
+				bool found = true;
+				allowedScopes = VLC->modh->getModDependencies(scope, found);
+
+				if(!found)
+					return false;
+
+				allowedScopes.insert(ModScope::scopeBuiltin()); // all mods can use H3 files
+			}
+			allowedScopes.insert(scope); // mods can use their own files
+
+			for(const auto & entry : allowedScopes)
+			{
+				if (CResourceHandler::get(entry)->existsResource(resource))
+					return true;
+			}
+			return false;
+		}
+
+		#define TEST_FILE(scope, prefix, file, type) \
+			if (testFilePresence(scope, ResourcePath(prefix + file, type))) \
+				return ""
+
+		std::string testAnimation(const std::string & path, const std::string & scope)
+		{
+			TEST_FILE(scope, "Sprites/", path, EResType::ANIMATION);
+			TEST_FILE(scope, "Sprites/", path, EResType::JSON);
+			return "Animation file \"" + path + "\" was not found";
+		}
+
+		std::string textFile(const JsonNode & node)
+		{
+			TEST_FILE(node.meta, "", node.String(), EResType::JSON);
+			return "Text file \"" + node.String() + "\" was not found";
+		}
+
+		std::string musicFile(const JsonNode & node)
+		{
+			TEST_FILE(node.meta, "Music/", node.String(), EResType::SOUND);
+			TEST_FILE(node.meta, "", node.String(), EResType::SOUND);
+			return "Music file \"" + node.String() + "\" was not found";
+		}
+
+		std::string soundFile(const JsonNode & node)
+		{
+			TEST_FILE(node.meta, "Sounds/", node.String(), EResType::SOUND);
+			return "Sound file \"" + node.String() + "\" was not found";
+		}
+
+		std::string defFile(const JsonNode & node)
+		{
+			return testAnimation(node.String(), node.meta);
+		}
+
+		std::string animationFile(const JsonNode & node)
+		{
+			return testAnimation(node.String(), node.meta);
+		}
+
+		std::string imageFile(const JsonNode & node)
+		{
+			TEST_FILE(node.meta, "Data/", node.String(), EResType::IMAGE);
+			TEST_FILE(node.meta, "Sprites/", node.String(), EResType::IMAGE);
+			if (node.String().find(':') != std::string::npos)
+				return testAnimation(node.String().substr(0, node.String().find(':')), node.meta);
+			return "Image file \"" + node.String() + "\" was not found";
+		}
+
+		std::string videoFile(const JsonNode & node)
+		{
+			TEST_FILE(node.meta, "Video/", node.String(), EResType::VIDEO);
+			return "Video file \"" + node.String() + "\" was not found";
+		}
+
+		#undef TEST_FILE
+	}
+
+	Validation::TValidatorMap createCommonFields()
+	{
+		Validation::TValidatorMap ret;
+
+		ret["format"] =  Common::formatCheck;
+		ret["allOf"] = Common::allOfCheck;
+		ret["anyOf"] = Common::anyOfCheck;
+		ret["oneOf"] = Common::oneOfCheck;
+		ret["enum"]  = Common::enumCheck;
+		ret["type"]  = Common::typeCheck;
+		ret["not"]   = Common::notCheck;
+		ret["$ref"]  = Common::refCheck;
+
+		// fields that don't need implementation
+		ret["title"] = Common::emptyCheck;
+		ret["$schema"] = Common::emptyCheck;
+		ret["default"] = Common::emptyCheck;
+		ret["description"] = Common::emptyCheck;
+		ret["definitions"] = Common::emptyCheck;
+		return ret;
+	}
+
+	Validation::TValidatorMap createStringFields()
+	{
+		Validation::TValidatorMap ret = createCommonFields();
+		ret["maxLength"] = String::maxLengthCheck;
+		ret["minLength"] = String::minLengthCheck;
+
+		ret["pattern"] = Common::notImplementedCheck;
+		return ret;
+	}
+
+	Validation::TValidatorMap createNumberFields()
+	{
+		Validation::TValidatorMap ret = createCommonFields();
+		ret["maximum"]    = Number::maximumCheck;
+		ret["minimum"]    = Number::minimumCheck;
+		ret["multipleOf"] = Number::multipleOfCheck;
+
+		ret["exclusiveMaximum"] = Common::emptyCheck;
+		ret["exclusiveMinimum"] = Common::emptyCheck;
+		return ret;
+	}
+
+	Validation::TValidatorMap createVectorFields()
+	{
+		Validation::TValidatorMap ret = createCommonFields();
+		ret["items"]           = Vector::itemsCheck;
+		ret["minItems"]        = Vector::minItemsCheck;
+		ret["maxItems"]        = Vector::maxItemsCheck;
+		ret["uniqueItems"]     = Vector::uniqueItemsCheck;
+		ret["additionalItems"] = Vector::additionalItemsCheck;
+		return ret;
+	}
+
+	Validation::TValidatorMap createStructFields()
+	{
+		Validation::TValidatorMap ret = createCommonFields();
+		ret["additionalProperties"]  = Struct::additionalPropertiesCheck;
+		ret["uniqueProperties"]      = Struct::uniquePropertiesCheck;
+		ret["maxProperties"]         = Struct::maxPropertiesCheck;
+		ret["minProperties"]         = Struct::minPropertiesCheck;
+		ret["dependencies"]          = Struct::dependenciesCheck;
+		ret["properties"]            = Struct::propertiesCheck;
+		ret["required"]              = Struct::requiredCheck;
+
+		ret["patternProperties"] = Common::notImplementedCheck;
+		return ret;
+	}
+
+	Validation::TFormatMap createFormatMap()
+	{
+		Validation::TFormatMap ret;
+		ret["textFile"]      = Formats::textFile;
+		ret["musicFile"]     = Formats::musicFile;
+		ret["soundFile"]     = Formats::soundFile;
+		ret["defFile"]       = Formats::defFile;
+		ret["animationFile"] = Formats::animationFile;
+		ret["imageFile"]     = Formats::imageFile;
+		ret["videoFile"]     = Formats::videoFile;
+
+		return ret;
+	}
+}
+
+namespace Validation
+{
+	std::string ValidationData::makeErrorMessage(const std::string &message)
+	{
+		std::string errors;
+		errors += "At ";
+		if (!currentPath.empty())
+		{
+			for(const JsonNode &path : currentPath)
+			{
+				errors += "/";
+				if (path.getType() == JsonNode::JsonType::DATA_STRING)
+					errors += path.String();
+				else
+					errors += std::to_string(static_cast<unsigned>(path.Float()));
+			}
+		}
+		else
+			errors += "<root>";
+		errors += "\n\t Error: " + message + "\n";
+		return errors;
+	}
+
+	std::string check(const std::string & schemaName, const JsonNode & data)
+	{
+		ValidationData validator;
+		return check(schemaName, data, validator);
+	}
+
+	std::string check(const std::string & schemaName, const JsonNode & data, ValidationData & validator)
+	{
+		validator.usedSchemas.push_back(schemaName);
+		auto onscopeExit = vstd::makeScopeGuard([&]()
+		{
+			validator.usedSchemas.pop_back();
+		});
+		return check(JsonUtils::getSchema(schemaName), data, validator);
+	}
+
+	std::string check(const JsonNode & schema, const JsonNode & data, ValidationData & validator)
+	{
+		const TValidatorMap & knownFields = getKnownFieldsFor(data.getType());
+		std::string errors;
+		for(const auto & entry : schema.Struct())
+		{
+			auto checker = knownFields.find(entry.first);
+			if (checker != knownFields.end())
+				errors += checker->second(validator, schema, entry.second, data);
+			//else
+			//	errors += validator.makeErrorMessage("Unknown entry in schema " + entry.first);
+		}
+		return errors;
+	}
+
+	const TValidatorMap & getKnownFieldsFor(JsonNode::JsonType type)
+	{
+		static const TValidatorMap commonFields = createCommonFields();
+		static const TValidatorMap numberFields = createNumberFields();
+		static const TValidatorMap stringFields = createStringFields();
+		static const TValidatorMap vectorFields = createVectorFields();
+		static const TValidatorMap structFields = createStructFields();
+
+		switch (type)
+		{
+			case JsonNode::JsonType::DATA_FLOAT:
+			case JsonNode::JsonType::DATA_INTEGER:
+				return numberFields;
+			case JsonNode::JsonType::DATA_STRING: return stringFields;
+			case JsonNode::JsonType::DATA_VECTOR: return vectorFields;
+			case JsonNode::JsonType::DATA_STRUCT: return structFields;
+			default: return commonFields;
+		}
+	}
+
+	const TFormatMap & getKnownFormats()
+	{
+		static TFormatMap knownFormats = createFormatMap();
+		return knownFormats;
+	}
+
+} // Validation namespace
+
+VCMI_LIB_NAMESPACE_END

+ 49 - 0
lib/json/JsonValidator.h

@@ -0,0 +1,49 @@
+/*
+ * JsonValidator.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 "JsonNode.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+
+//Internal class for Json validation. Mostly compilant with json-schema v4 draft
+namespace Validation
+{
+	/// struct used to pass data around during validation
+	struct ValidationData
+	{
+		/// path from root node to current one.
+		/// JsonNode is used as variant - either string (name of node) or as float (index in list)
+		std::vector<JsonNode> currentPath;
+
+		/// Stack of used schemas. Last schema is the one used currently.
+		/// May contain multiple items in case if remote references were found
+		std::vector<std::string> usedSchemas;
+
+		/// generates error message
+		std::string makeErrorMessage(const std::string &message);
+	};
+
+	using TFormatValidator = std::function<std::string(const JsonNode &)>;
+	using TFormatMap = std::unordered_map<std::string, TFormatValidator>;
+	using TFieldValidator = std::function<std::string(ValidationData &, const JsonNode &, const JsonNode &, const JsonNode &)>;
+	using TValidatorMap = std::unordered_map<std::string, TFieldValidator>;
+
+	/// map of known fields in schema
+	const TValidatorMap & getKnownFieldsFor(JsonNode::JsonType type);
+	const TFormatMap & getKnownFormats();
+
+	std::string check(const std::string & schemaName, const JsonNode & data);
+	std::string check(const std::string & schemaName, const JsonNode & data, ValidationData & validator);
+	std::string check(const JsonNode & schema, const JsonNode & data, ValidationData & validator);
+}
+
+VCMI_LIB_NAMESPACE_END

+ 144 - 0
lib/json/JsonWriter.cpp

@@ -0,0 +1,144 @@
+/*
+ * JsonWriter.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 "JsonWriter.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+template<typename Iterator>
+void JsonWriter::writeContainer(Iterator begin, Iterator end)
+{
+	if (begin == end)
+		return;
+
+	prefix += '\t';
+
+	writeEntry(begin++);
+
+	while (begin != end)
+	{
+		out << (compactMode ? ", " : ",\n");
+		writeEntry(begin++);
+	}
+
+	out << (compactMode ? "" : "\n");
+	prefix.resize(prefix.size()-1);
+}
+
+void JsonWriter::writeEntry(JsonMap::const_iterator entry)
+{
+	if(!compactMode)
+	{
+		if (!entry->second.meta.empty())
+			out << prefix << " // " << entry->second.meta << "\n";
+		if(!entry->second.flags.empty())
+			out << prefix << " // flags: " << boost::algorithm::join(entry->second.flags, ", ") << "\n";
+		out << prefix;
+	}
+	writeString(entry->first);
+	out << " : ";
+	writeNode(entry->second);
+}
+
+void JsonWriter::writeEntry(JsonVector::const_iterator entry)
+{
+	if(!compactMode)
+	{
+		if (!entry->meta.empty())
+			out << prefix << " // " << entry->meta << "\n";
+		if(!entry->flags.empty())
+			out << prefix << " // flags: " << boost::algorithm::join(entry->flags, ", ") << "\n";
+		out << prefix;
+	}
+	writeNode(*entry);
+}
+
+void JsonWriter::writeString(const std::string &string)
+{
+	static const std::string escaped = "\"\\\b\f\n\r\t/";
+
+	static const std::array<char, 8> escaped_code = {'\"', '\\', 'b', 'f', 'n', 'r', 't', '/'};
+
+	out <<'\"';
+	size_t pos = 0;
+	size_t start = 0;
+	for (; pos<string.size(); pos++)
+	{
+		//we need to check if special character was been already escaped
+		if((string[pos] == '\\')
+			&& (pos+1 < string.size())
+			&& (std::find(escaped_code.begin(), escaped_code.end(), string[pos+1]) != escaped_code.end()) )
+		{
+			pos++; //write unchanged, next simbol also checked
+		}
+		else
+		{
+			size_t escapedPos = escaped.find(string[pos]);
+
+			if (escapedPos != std::string::npos)
+			{
+				out.write(string.data()+start, pos - start);
+				out << '\\' << escaped_code[escapedPos];
+				start = pos+1;
+			}
+		}
+
+	}
+	out.write(string.data()+start, pos - start);
+	out <<'\"';
+}
+
+void JsonWriter::writeNode(const JsonNode &node)
+{
+	bool originalMode = compactMode;
+	if(compact && !compactMode && node.isCompact())
+		compactMode = true;
+
+	switch(node.getType())
+	{
+		break; case JsonNode::JsonType::DATA_NULL:
+			out << "null";
+
+		break; case JsonNode::JsonType::DATA_BOOL:
+			if (node.Bool())
+				out << "true";
+			else
+				out << "false";
+
+		break; case JsonNode::JsonType::DATA_FLOAT:
+			out << node.Float();
+
+		break; case JsonNode::JsonType::DATA_STRING:
+			writeString(node.String());
+
+		break; case JsonNode::JsonType::DATA_VECTOR:
+			out << "[" << (compactMode ? " " : "\n");
+			writeContainer(node.Vector().begin(), node.Vector().end());
+			out << (compactMode ? " " : prefix) << "]";
+
+		break; case JsonNode::JsonType::DATA_STRUCT:
+			out << "{" << (compactMode ? " " : "\n");
+			writeContainer(node.Struct().begin(), node.Struct().end());
+			out << (compactMode ? " " : prefix) << "}";
+
+		break; case JsonNode::JsonType::DATA_INTEGER:
+			out << node.Integer();
+	}
+
+	compactMode = originalMode;
+}
+
+JsonWriter::JsonWriter(std::ostream & output, bool compact)
+	: out(output), compact(compact)
+{
+}
+
+VCMI_LIB_NAMESPACE_END

+ 35 - 0
lib/json/JsonWriter.h

@@ -0,0 +1,35 @@
+/*
+ * JsonWriter.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 "JsonNode.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class JsonWriter
+{
+	//prefix for each line (tabulation)
+	std::string prefix;
+	std::ostream & out;
+	//sets whether compact nodes are written in single-line format
+	bool compact;
+	//tracks whether we are currently using single-line format
+	bool compactMode = false;
+public:
+	template<typename Iterator>
+	void writeContainer(Iterator begin, Iterator end);
+	void writeEntry(JsonMap::const_iterator entry);
+	void writeEntry(JsonVector::const_iterator entry);
+	void writeString(const std::string & string);
+	void writeNode(const JsonNode & node);
+	JsonWriter(std::ostream & output, bool compact);
+};
+
+VCMI_LIB_NAMESPACE_END

+ 2 - 1
lib/mapObjectConstructors/AObjectTypeHandler.cpp

@@ -13,8 +13,9 @@
 
 #include "IObjectInfo.h"
 #include "../CGeneralTextHandler.h"
-#include "../modding/IdentifierStorage.h"
 #include "../VCMI_Lib.h"
+#include "../json/JsonUtils.h"
+#include "../modding/IdentifierStorage.h"
 #include "../mapObjects/CGObjectInstance.h"
 #include "../mapObjects/ObjectTemplate.h"
 

+ 1 - 1
lib/mapObjectConstructors/AObjectTypeHandler.h

@@ -9,9 +9,9 @@
  */
 #pragma once
 
+#include "../constants/EntityIdentifiers.h"
 #include "RandomMapInfo.h"
 #include "SObjectSounds.h"
-#include "../JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/mapObjectConstructors/CBankInstanceConstructor.cpp

@@ -10,7 +10,7 @@
 #include "StdInc.h"
 #include "CBankInstanceConstructor.h"
 
-#include "../JsonRandom.h"
+#include "../json/JsonRandom.h"
 #include "../CGeneralTextHandler.h"
 #include "../IGameCallback.h"
 #include "../CRandomGenerator.h"

+ 1 - 0
lib/mapObjectConstructors/CBankInstanceConstructor.h

@@ -14,6 +14,7 @@
 
 #include "../CCreatureSet.h"
 #include "../ResourceSet.h"
+#include "../json/JsonNode.h"
 #include "../mapObjects/CBank.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 1 - 1
lib/mapObjectConstructors/CObjectClassesHandler.cpp

@@ -12,12 +12,12 @@
 
 #include "../filesystem/Filesystem.h"
 #include "../filesystem/CBinaryReader.h"
+#include "../json/JsonUtils.h"
 #include "../VCMI_Lib.h"
 #include "../GameConstants.h"
 #include "../constants/StringConstants.h"
 #include "../CGeneralTextHandler.h"
 #include "../GameSettings.h"
-#include "../JsonNode.h"
 #include "../CSoundBase.h"
 
 #include "../mapObjectConstructors/CBankInstanceConstructor.h"

+ 2 - 1
lib/mapObjectConstructors/CObjectClassesHandler.h

@@ -9,8 +9,9 @@
  */
 #pragma once
 
+#include "../constants/EntityIdentifiers.h"
 #include "../IHandlerBase.h"
-#include "../JsonNode.h"
+#include "../json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/mapObjectConstructors/CommonConstructors.cpp

@@ -14,7 +14,7 @@
 #include "../CHeroHandler.h"
 #include "../CTownHandler.h"
 #include "../IGameCallback.h"
-#include "../JsonRandom.h"
+#include "../json/JsonRandom.h"
 #include "../constants/StringConstants.h"
 #include "../TerrainHandler.h"
 #include "../VCMI_Lib.h"

+ 1 - 1
lib/mapObjectConstructors/DwellingInstanceConstructor.cpp

@@ -12,7 +12,7 @@
 
 #include "../CCreatureHandler.h"
 #include "../CGeneralTextHandler.h"
-#include "../JsonRandom.h"
+#include "../json/JsonRandom.h"
 #include "../VCMI_Lib.h"
 #include "../mapObjects/CGDwelling.h"
 #include "../modding/IdentifierStorage.h"

+ 2 - 0
lib/mapObjectConstructors/DwellingInstanceConstructor.h

@@ -10,6 +10,8 @@
 #pragma once
 
 #include "CDefaultObjectTypeHandler.h"
+
+#include "../json/JsonNode.h"
 #include "../mapObjects/CGDwelling.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 1 - 0
lib/mapObjectConstructors/HillFortInstanceConstructor.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "CDefaultObjectTypeHandler.h"
+#include "../json/JsonNode.h"
 #include "../mapObjects/MiscObjects.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 1 - 0
lib/mapObjectConstructors/ShipyardInstanceConstructor.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "CDefaultObjectTypeHandler.h"
+#include "../json/JsonNode.h"
 #include "../mapObjects/MiscObjects.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 1 - 0
lib/mapObjects/CGHeroInstance.cpp

@@ -32,6 +32,7 @@
 #include "../StartInfo.h"
 #include "CGTownInstance.h"
 #include "../campaign/CampaignState.h"
+#include "../json/JsonUtils.h"
 #include "../pathfinder/TurnInfo.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"

+ 1 - 1
lib/mapObjects/CObjectHandler.cpp

@@ -13,7 +13,7 @@
 
 #include "CGObjectInstance.h"
 #include "../filesystem/ResourcePath.h"
-#include "../JsonNode.h"
+#include "../json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 0 - 1
lib/mapObjects/ObjectTemplate.cpp

@@ -16,7 +16,6 @@
 #include "../GameConstants.h"
 #include "../constants/StringConstants.h"
 #include "../CGeneralTextHandler.h"
-#include "../JsonNode.h"
 #include "../TerrainHandler.h"
 
 #include "../mapObjectConstructors/CRewardableConstructor.h"

+ 1 - 0
lib/mapping/CMapHeader.cpp

@@ -15,6 +15,7 @@
 #include "../VCMI_Lib.h"
 #include "../CTownHandler.h"
 #include "../CGeneralTextHandler.h"
+#include "../json/JsonUtils.h"
 #include "../modding/CModHandler.h"
 #include "../CHeroHandler.h"
 #include "../Languages.h"

+ 2 - 0
lib/mapping/CMapHeader.h

@@ -10,6 +10,8 @@
 
 #pragma once
 
+#include "../constants/EntityIdentifiers.h"
+#include "../constants/Enumerations.h"
 #include "../constants/VariantIdentifier.h"
 #include "../modding/CModInfo.h"
 #include "../LogicalExpression.h"

+ 1 - 0
lib/mapping/CMapService.cpp

@@ -10,6 +10,7 @@
 #include "StdInc.h"
 #include "CMapService.h"
 
+#include "../json/JsonUtils.h"
 #include "../filesystem/Filesystem.h"
 #include "../filesystem/CBinaryReader.h"
 #include "../filesystem/CCompressedStream.h"

+ 0 - 1
lib/mapping/MapEditUtils.cpp

@@ -12,7 +12,6 @@
 #include "MapEditUtils.h"
 
 #include "../filesystem/Filesystem.h"
-#include "../JsonNode.h"
 #include "../TerrainHandler.h"
 #include "CMap.h"
 #include "CMapOperation.h"

+ 2 - 2
lib/mapping/MapFormatJson.cpp

@@ -13,7 +13,7 @@
 
 #include "../filesystem/CInputStream.h"
 #include "../filesystem/COutputStream.h"
-#include "../JsonDetail.h"
+#include "../json/JsonWriter.h"
 #include "CMap.h"
 #include "MapFormat.h"
 #include "../ArtifactUtils.h"
@@ -1201,7 +1201,7 @@ CMapSaverJson::~CMapSaverJson() = default;
 void CMapSaverJson::addToArchive(const JsonNode & data, const std::string & filename)
 {
 	std::ostringstream out;
-	JsonWriter writer(out);
+	JsonWriter writer(out, false);
 	writer.writeNode(data);
 	out.flush();
 

+ 0 - 1
lib/mapping/MapFormatJson.h

@@ -11,7 +11,6 @@
 #pragma once
 
 #include "CMapService.h"
-#include "../JsonNode.h"
 
 #include "../filesystem/CZipSaver.h"
 #include "../filesystem/CZipLoader.h"

+ 0 - 1
lib/mapping/MapIdentifiersH3M.cpp

@@ -11,7 +11,6 @@
 #include "StdInc.h"
 #include "MapIdentifiersH3M.h"
 
-#include "../JsonNode.h"
 #include "../VCMI_Lib.h"
 #include "../CTownHandler.h"
 #include "../CHeroHandler.h"

+ 1 - 0
lib/modding/CModHandler.cpp

@@ -25,6 +25,7 @@
 #include "../ScriptHandler.h"
 #include "../constants/StringConstants.h"
 #include "../filesystem/Filesystem.h"
+#include "../json/JsonUtils.h"
 #include "../spells/CSpellHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 1 - 1
lib/modding/CModInfo.h

@@ -9,7 +9,7 @@
  */
 #pragma once
 
-#include "../JsonNode.h"
+#include "../json/JsonNode.h"
 #include "ModVerificationInfo.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 1 - 0
lib/modding/ContentTypeHandler.cpp

@@ -31,6 +31,7 @@
 #include "../ScriptHandler.h"
 #include "../constants/StringConstants.h"
 #include "../TerrainHandler.h"
+#include "../json/JsonUtils.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../rmg/CRmgTemplateStorage.h"
 #include "../spells/CSpellHandler.h"

+ 1 - 1
lib/modding/ContentTypeHandler.h

@@ -9,7 +9,7 @@
  */
 #pragma once
 
-#include "../JsonNode.h"
+#include "../json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 0 - 1
lib/modding/IdentifierStorage.cpp

@@ -13,7 +13,6 @@
 #include "CModHandler.h"
 #include "ModScope.h"
 
-#include "../JsonNode.h"
 #include "../VCMI_Lib.h"
 #include "../constants/StringConstants.h"
 #include "../spells/CSpellHandler.h"

+ 1 - 1
lib/networkPacks/BattleChanges.h

@@ -9,7 +9,7 @@
  */
 #pragma once
 
-#include "JsonNode.h"
+#include "../json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 2 - 2
lib/networkPacks/EntityChanges.h

@@ -9,9 +9,9 @@
  */
 #pragma once
 
-#include <vcmi/Metatype.h>
+#include "../json/JsonNode.h"
 
-#include "../JsonNode.h"
+#include <vcmi/Metatype.h>
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/rewardable/Info.cpp

@@ -17,7 +17,7 @@
 
 #include "../CGeneralTextHandler.h"
 #include "../IGameCallback.h"
-#include "../JsonRandom.h"
+#include "../json/JsonRandom.h"
 #include "../mapObjects/IObjectInterface.h"
 #include "../modding/IdentifierStorage.h"
 #include "../CRandomGenerator.h"

+ 1 - 1
lib/rewardable/Info.h

@@ -10,7 +10,7 @@
 
 #pragma once
 
-#include "../JsonNode.h"
+#include "../json/JsonNode.h"
 #include "../mapObjectConstructors/IObjectInfo.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 0 - 2
lib/serializer/JsonDeserializer.cpp

@@ -10,8 +10,6 @@
 #include "StdInc.h"
 #include "JsonDeserializer.h"
 
-#include "../JsonNode.h"
-
 #include <vstd/StringUtils.h>
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 0 - 2
lib/serializer/JsonSerializeFormat.cpp

@@ -10,8 +10,6 @@
 #include "StdInc.h"
 #include "JsonSerializeFormat.h"
 
-#include "../JsonNode.h"
-
 VCMI_LIB_NAMESPACE_BEGIN
 
 //JsonSerializeHelper

+ 2 - 1
lib/serializer/JsonSerializeFormat.h

@@ -9,7 +9,8 @@
  */
 #pragma once
 
-#include "../JsonNode.h"
+#include "../constants/IdentifierBase.h"
+#include "../json/JsonNode.h"
 #include "../modding/IdentifierStorage.h"
 #include "../modding/ModScope.h"
 #include "../VCMI_Lib.h"

+ 0 - 2
lib/serializer/JsonSerializer.cpp

@@ -10,8 +10,6 @@
 #include "StdInc.h"
 #include "JsonSerializer.h"
 
-#include "../JsonNode.h"
-
 VCMI_LIB_NAMESPACE_BEGIN
 
 JsonSerializer::JsonSerializer(const IInstanceResolver * instanceResolver_, JsonNode & root_):

+ 1 - 2
lib/serializer/JsonUpdater.cpp

@@ -10,10 +10,9 @@
 #include "StdInc.h"
 #include "JsonUpdater.h"
 
-#include "../JsonNode.h"
-
 #include "../bonuses/CBonusSystemNode.h"
 #include "../bonuses/Bonus.h"
+#include "../json/JsonUtils.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/spells/CSpellHandler.cpp

@@ -25,7 +25,7 @@
 #include "../battle/BattleInfo.h"
 #include "../battle/CBattleInfoCallback.h"
 #include "../battle/Unit.h"
-
+#include "../json/JsonUtils.h"
 #include "../mapObjects/CGHeroInstance.h" //todo: remove
 #include "../serializer/CSerializer.h"
 #include "../modding/IdentifierStorage.h"

+ 1 - 1
lib/spells/CSpellHandler.h

@@ -13,7 +13,6 @@
 #include <vcmi/spells/Spell.h>
 #include <vcmi/spells/Service.h>
 #include <vcmi/spells/Magic.h>
-#include "../JsonNode.h"
 #include "../IHandlerBase.h"
 #include "../ConstTransitivePtr.h"
 #include "../int3.h"
@@ -21,6 +20,7 @@
 #include "../battle/BattleHex.h"
 #include "../bonuses/Bonus.h"
 #include "../filesystem/ResourcePath.h"
+#include "../json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/spells/TargetCondition.cpp

@@ -17,7 +17,7 @@
 #include "../battle/Unit.h"
 #include "../bonuses/BonusParams.h"
 #include "../bonuses/BonusList.h"
-
+#include "../json/JsonUtils.h"
 #include "../modding/IdentifierStorage.h"
 #include "../modding/ModUtility.h"
 #include "../serializer/JsonSerializeFormat.h"

+ 1 - 0
lib/spells/effects/Moat.cpp

@@ -18,6 +18,7 @@
 #include "../../bonuses/Limiters.h"
 #include "../../battle/IBattleState.h"
 #include "../../battle/CBattleInfoCallback.h"
+#include "../../json/JsonUtils.h"
 #include "../../serializer/JsonSerializeFormat.h"
 #include "../../networkPacks/PacksForClient.h"
 #include "../../networkPacks/PacksForClientBattle.h"

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels