#include "stdafx.h" #include "RuntimeParameterUtils.h" #include #include #include #include namespace { std::string TrimText(const std::string& text) { std::size_t start = 0; while (start < text.size() && std::isspace(static_cast(text[start]))) ++start; std::size_t end = text.size(); while (end > start && std::isspace(static_cast(text[end - 1]))) --end; return text.substr(start, end - start); } bool IsFiniteNumber(double value) { return std::isfinite(value) != 0; } std::string NormalizeTextValue(const std::string& text, unsigned maxLength) { std::string normalized; normalized.reserve(std::min(text.size(), maxLength)); for (unsigned char ch : text) { if (ch < 32 || ch > 126) continue; if (normalized.size() >= maxLength) break; normalized.push_back(static_cast(ch)); } return normalized; } } std::string MakeSafePresetFileStem(const std::string& presetName) { std::string trimmed = TrimText(presetName); std::string safe; safe.reserve(trimmed.size()); for (unsigned char ch : trimmed) { if (std::isalnum(ch)) safe.push_back(static_cast(std::tolower(ch))); else if (ch == ' ' || ch == '-' || ch == '_') { if (safe.empty() || safe.back() == '-') continue; safe.push_back('-'); } } while (!safe.empty() && safe.back() == '-') safe.pop_back(); return safe; } ShaderParameterValue DefaultValueForDefinition(const ShaderParameterDefinition& definition) { ShaderParameterValue value; switch (definition.type) { case ShaderParameterType::Float: value.numberValues = definition.defaultNumbers.empty() ? std::vector{ 0.0 } : definition.defaultNumbers; break; case ShaderParameterType::Vec2: value.numberValues = definition.defaultNumbers.size() == 2 ? definition.defaultNumbers : std::vector{ 0.0, 0.0 }; break; case ShaderParameterType::Color: value.numberValues = definition.defaultNumbers.size() == 4 ? definition.defaultNumbers : std::vector{ 1.0, 1.0, 1.0, 1.0 }; break; case ShaderParameterType::Boolean: value.booleanValue = definition.defaultBoolean; break; case ShaderParameterType::Enum: value.enumValue = definition.defaultEnumValue; break; case ShaderParameterType::Text: value.textValue = NormalizeTextValue(definition.defaultTextValue, definition.maxLength); break; case ShaderParameterType::Trigger: value.numberValues = { 0.0, -1000000.0 }; break; } return value; } bool NormalizeAndValidateParameterValue(const ShaderParameterDefinition& definition, const JsonValue& value, ShaderParameterValue& normalizedValue, std::string& error) { normalizedValue = DefaultValueForDefinition(definition); switch (definition.type) { case ShaderParameterType::Float: { if (!value.isNumber()) { error = "Expected numeric value for float parameter '" + definition.id + "'."; return false; } double number = value.asNumber(); if (!IsFiniteNumber(number)) { error = "Float parameter '" + definition.id + "' must be finite."; return false; } if (!definition.minNumbers.empty()) number = std::max(number, definition.minNumbers.front()); if (!definition.maxNumbers.empty()) number = std::min(number, definition.maxNumbers.front()); normalizedValue.numberValues = { number }; return true; } case ShaderParameterType::Vec2: case ShaderParameterType::Color: { std::vector numbers = JsonArrayToNumbers(value); const std::size_t expectedSize = definition.type == ShaderParameterType::Vec2 ? 2 : 4; if (numbers.size() != expectedSize) { error = "Expected array value of size " + std::to_string(expectedSize) + " for parameter '" + definition.id + "'."; return false; } for (std::size_t index = 0; index < numbers.size(); ++index) { if (!IsFiniteNumber(numbers[index])) { error = "Parameter '" + definition.id + "' contains a non-finite value."; return false; } if (index < definition.minNumbers.size()) numbers[index] = std::max(numbers[index], definition.minNumbers[index]); if (index < definition.maxNumbers.size()) numbers[index] = std::min(numbers[index], definition.maxNumbers[index]); } normalizedValue.numberValues = numbers; return true; } case ShaderParameterType::Boolean: if (!value.isBoolean()) { error = "Expected boolean value for parameter '" + definition.id + "'."; return false; } normalizedValue.booleanValue = value.asBoolean(); return true; case ShaderParameterType::Enum: { if (!value.isString()) { error = "Expected string value for enum parameter '" + definition.id + "'."; return false; } const std::string selectedValue = value.asString(); for (const ShaderParameterOption& option : definition.enumOptions) { if (option.value == selectedValue) { normalizedValue.enumValue = selectedValue; return true; } } error = "Enum parameter '" + definition.id + "' received unsupported option '" + selectedValue + "'."; return false; } case ShaderParameterType::Text: if (!value.isString()) { error = "Expected string value for text parameter '" + definition.id + "'."; return false; } normalizedValue.textValue = NormalizeTextValue(value.asString(), definition.maxLength); return true; case ShaderParameterType::Trigger: if (!value.isNumber() && !value.isBoolean()) { error = "Expected numeric or boolean value for trigger parameter '" + definition.id + "'."; return false; } normalizedValue.numberValues = { value.isNumber() ? std::max(0.0, std::floor(value.asNumber())) : 0.0, -1000000.0 }; return true; } return false; }