diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 68838759d..566c2fdac 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -405,6 +405,7 @@ set(lib_HEADERS filesystem/ResourcePath.h json/JsonBonus.h + json/JsonFormatException.h json/JsonNode.h json/JsonParser.h json/JsonRandom.h diff --git a/lib/json/JsonBonus.h b/lib/json/JsonBonus.h index c994d63e5..dd2b8fa12 100644 --- a/lib/json/JsonBonus.h +++ b/lib/json/JsonBonus.h @@ -14,6 +14,11 @@ VCMI_LIB_NAMESPACE_BEGIN +struct Bonus; +class ILimiter; +class CSelector; +class CAddInfo; + namespace JsonUtils { DLL_LINKAGE std::shared_ptr parseBonus(const JsonVector & ability_vec); diff --git a/lib/json/JsonFormatException.h b/lib/json/JsonFormatException.h new file mode 100644 index 000000000..2d385029d --- /dev/null +++ b/lib/json/JsonFormatException.h @@ -0,0 +1,20 @@ +/* + * JsonFormatException.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 + +VCMI_LIB_NAMESPACE_BEGIN + +class JsonFormatException : public std::runtime_error +{ +public: + using runtime_error::runtime_error; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/json/JsonNode.cpp b/lib/json/JsonNode.cpp index 4d78131ae..bb759f72c 100644 --- a/lib/json/JsonNode.cpp +++ b/lib/json/JsonNode.cpp @@ -79,8 +79,12 @@ JsonNode::JsonNode(const std::string & string) {} JsonNode::JsonNode(const std::byte *data, size_t datasize) + :JsonNode(data, datasize, JsonParsingSettings()) +{} + +JsonNode::JsonNode(const std::byte *data, size_t datasize, const JsonParsingSettings & parserSettings) { - JsonParser parser(reinterpret_cast(data), datasize); + JsonParser parser(reinterpret_cast(data), datasize, parserSettings); *this = parser.parse(""); } @@ -88,7 +92,7 @@ JsonNode::JsonNode(const JsonPath & fileURI) { auto file = CResourceHandler::get()->load(fileURI)->readAll(); - JsonParser parser(reinterpret_cast(file.first.get()), file.second); + JsonParser parser(reinterpret_cast(file.first.get()), file.second, JsonParsingSettings()); *this = parser.parse(fileURI.getName()); } @@ -96,7 +100,7 @@ JsonNode::JsonNode(const JsonPath & fileURI, const std::string & idx) { auto file = CResourceHandler::get(idx)->load(fileURI)->readAll(); - JsonParser parser(reinterpret_cast(file.first.get()), file.second); + JsonParser parser(reinterpret_cast(file.first.get()), file.second, JsonParsingSettings()); *this = parser.parse(fileURI.getName()); } @@ -104,7 +108,7 @@ JsonNode::JsonNode(const JsonPath & fileURI, bool &isValidSyntax) { auto file = CResourceHandler::get()->load(fileURI)->readAll(); - JsonParser parser(reinterpret_cast(file.first.get()), file.second); + JsonParser parser(reinterpret_cast(file.first.get()), file.second, JsonParsingSettings()); *this = parser.parse(fileURI.getName()); isValidSyntax = parser.isValid(); } diff --git a/lib/json/JsonNode.h b/lib/json/JsonNode.h index 1aa60cfac..e4e3d787f 100644 --- a/lib/json/JsonNode.h +++ b/lib/json/JsonNode.h @@ -17,10 +17,23 @@ class JsonNode; using JsonMap = std::map; using JsonVector = std::vector; -struct Bonus; -class CSelector; -class CAddInfo; -class ILimiter; +struct JsonParsingSettings +{ + enum class JsonFormatMode + { + JSON, // strict implementation of json format + JSONC, // json format that also allows comments that start from '//' + //JSON5 // TODO? + }; + + JsonFormatMode mode = JsonFormatMode::JSONC; + + /// Maximum depth of elements + uint32_t maxDepth = 30; + + /// If set to true, parser will throw on any encountered error + bool strict = false; +}; class DLL_LINKAGE JsonNode { @@ -58,6 +71,7 @@ public: /// Create tree from Json-formatted input explicit JsonNode(const std::byte * data, size_t datasize); + explicit JsonNode(const std::byte * data, size_t datasize, const JsonParsingSettings & parserSettings); /// Create tree from JSON file explicit JsonNode(const JsonPath & fileURI); diff --git a/lib/json/JsonParser.cpp b/lib/json/JsonParser.cpp index b7fb570ec..2fb31725c 100644 --- a/lib/json/JsonParser.cpp +++ b/lib/json/JsonParser.cpp @@ -11,12 +11,14 @@ #include "StdInc.h" #include "JsonParser.h" +#include "JsonFormatException.h" #include "../TextOperations.h" VCMI_LIB_NAMESPACE_BEGIN -JsonParser::JsonParser(const char * inputString, size_t stringSize): +JsonParser::JsonParser(const char * inputString, size_t stringSize, const JsonParsingSettings & settings): input(inputString, stringSize), + settings(settings), lineCount(1), lineStart(0), pos(0) @@ -105,9 +107,13 @@ bool JsonParser::extractWhitespace(bool verbose) } pos++; } + if (pos >= input.size() || input[pos] != '/') break; + if (settings.mode == JsonParsingSettings::JsonFormatMode::JSON) + error("Comments are not permitted in json!", true); + pos++; if (pos == input.size()) break; @@ -346,9 +352,8 @@ bool JsonParser::extractElement(JsonNode &node, char terminator) if (input[pos] == terminator) { - //FIXME: MOD COMPATIBILITY: Too many of these right now, re-enable later - //if (comma) - //error("Extra comma found!", true); + if (comma) + error("Extra comma found!", true); return true; } @@ -456,6 +461,9 @@ bool JsonParser::extractFloat(JsonNode &node) bool JsonParser::error(const std::string &message, bool warning) { + if (settings.strict) + throw JsonFormatException(message); + std::ostringstream stream; std::string type(warning?" warning: ":" error: "); diff --git a/lib/json/JsonParser.h b/lib/json/JsonParser.h index 5ab544e41..8bae22450 100644 --- a/lib/json/JsonParser.h +++ b/lib/json/JsonParser.h @@ -16,6 +16,8 @@ VCMI_LIB_NAMESPACE_BEGIN //Internal class for string -> JsonNode conversion class JsonParser { + const JsonParsingSettings settings; + std::string errors; // Contains description of all encountered errors std::string_view input; // Input data ui32 lineCount; // Currently parsed line, starting from 1 @@ -44,7 +46,7 @@ class JsonParser bool error(const std::string &message, bool warning=false); public: - JsonParser(const char * inputString, size_t stringSize); + JsonParser(const char * inputString, size_t stringSize, const JsonParsingSettings & settings); /// do actual parsing. filename is name of file that will printed to console if any errors were found JsonNode parse(const std::string & fileName); diff --git a/lib/spells/effects/Moat.h b/lib/spells/effects/Moat.h index 536cf4bfd..c30130743 100644 --- a/lib/spells/effects/Moat.h +++ b/lib/spells/effects/Moat.h @@ -14,6 +14,8 @@ VCMI_LIB_NAMESPACE_BEGIN +struct Bonus; + namespace spells { namespace effects