Browse Source

Add mod version validation

Ivan Savenko 4 months ago
parent
commit
5456e245a3
4 changed files with 40 additions and 17 deletions
  1. 1 0
      config/schemas/mod.json
  2. 10 0
      lib/json/JsonValidator.cpp
  3. 28 16
      lib/modding/CModVersion.cpp
  4. 1 1
      lib/modding/CModVersion.h

+ 1 - 0
config/schemas/mod.json

@@ -90,6 +90,7 @@
 		},
 		"version" : {
 			"type" : "string",
+			"format" : "version",
 			"description" : "Current mod version, up to 3 numbers, dot-separated. Format: A.B.C"
 		},
 		"changelog" : {

+ 10 - 0
lib/json/JsonValidator.cpp

@@ -19,6 +19,7 @@
 #include "../modding/CModHandler.h"
 #include "../texts/TextOperations.h"
 #include "../ScopeGuard.h"
+#include "modding/CModVersion.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -538,6 +539,14 @@ static std::string videoFile(const JsonNode & node)
 }
 #undef TEST_FILE
 
+static std::string version(const JsonNode & node)
+{
+	auto version = CModVersion::fromString(node.String());
+	if (version == CModVersion())
+		return "Failed to parse mod version: " + node.toCompactString() + ". Expected format X.Y.Z, where X, Y, Z are non-negative numbers";
+	return "";
+}
+
 JsonValidator::TValidatorMap createCommonFields()
 {
 	JsonValidator::TValidatorMap ret;
@@ -629,6 +638,7 @@ JsonValidator::TFormatMap createFormatMap()
 	ret["animationFile"] = animationFile;
 	ret["imageFile"]     = imageFile;
 	ret["videoFile"]     = videoFile;
+	ret["version"]     = version;
 
 	//TODO:
 	// uri-reference

+ 28 - 16
lib/modding/CModVersion.cpp

@@ -18,29 +18,41 @@ CModVersion CModVersion::GameVersion()
 	return CModVersion(VCMI_VERSION_MAJOR, VCMI_VERSION_MINOR, VCMI_VERSION_PATCH);
 }
 
-CModVersion CModVersion::fromString(std::string from)
+CModVersion CModVersion::fromString(const std::string & from)
 {
-	int major = Any;
-	int minor = Any;
-	int patch = Any;
+	std::vector<std::string> segments;
+	boost::split(segments, from, boost::is_any_of("."));
+
+	if (from.empty())
+		return CModVersion();
+
+	if (segments.size() > 3)
+		return CModVersion();
+
+	static const std::string whitelist = "1234567890.";
+
+	for (const auto & ch : from)
+		if (whitelist.find(ch) == std::string::npos)
+			return CModVersion();
+
 	try
 	{
-		auto pointPos = from.find('.');
-		major = std::stoi(from.substr(0, pointPos));
-		if(pointPos != std::string::npos)
-		{
-			from = from.substr(pointPos + 1);
-			pointPos = from.find('.');
-			minor = std::stoi(from.substr(0, pointPos));
-			if(pointPos != std::string::npos)
-				patch = std::stoi(from.substr(pointPos + 1));
-		}
+		int major = Any;
+		int minor = Any;
+		int patch = Any;
+
+		major = std::stoi(segments[0]);
+		if (segments.size() > 1)
+			minor = std::stoi(segments[1]);
+		if (segments.size() > 2)
+			patch = std::stoi(segments[2]);
+
+		return CModVersion(major, minor, patch);
 	}
-	catch(const std::invalid_argument &)
+	catch(const std::logic_error &)
 	{
 		return CModVersion();
 	}
-	return CModVersion(major, minor, patch);
 }
 
 std::string CModVersion::toString() const

+ 1 - 1
lib/modding/CModVersion.h

@@ -32,7 +32,7 @@ struct DLL_LINKAGE CModVersion
 	CModVersion(int mj, int mi, int p): major(mj), minor(mi), patch(p) {}
 
 	static CModVersion GameVersion();
-	static CModVersion fromString(std::string from);
+	static CModVersion fromString(const std::string & from);
 	std::string toString() const;
 
 	bool operator !=(const CModVersion & other) const;