diff --git a/loader/include/Geode/utils/JsonValidation.hpp b/loader/include/Geode/utils/JsonValidation.hpp index 54dab08d..0156cf3c 100644 --- a/loader/include/Geode/utils/JsonValidation.hpp +++ b/loader/include/Geode/utils/JsonValidation.hpp @@ -140,12 +140,10 @@ namespace geode { return *this; } - template <matjson::Type T> - JsonMaybeValue& is() { - if (this->isError()) return *this; - self().m_hasValue = jsonConvertibleTo(self().m_json.type(), T); - m_inferType = false; - return *this; + template <class T> + bool is() { + if (this->isError()) return false; + return self().m_json.template is<T>(); } template <class T> diff --git a/loader/include/Geode/utils/cocos.hpp b/loader/include/Geode/utils/cocos.hpp index 2b1dcdeb..48e15999 100644 --- a/loader/include/Geode/utils/cocos.hpp +++ b/loader/include/Geode/utils/cocos.hpp @@ -816,8 +816,30 @@ namespace geode::cocos { static_cast<GLubyte>(hexValue >> 0 & 0xff)}; } - GEODE_DLL Result<cocos2d::ccColor3B> cc3bFromHexString(std::string const& hexValue); - GEODE_DLL Result<cocos2d::ccColor4B> cc4bFromHexString(std::string const& hexValue); + /** + * Parse a ccColor3B from a hexadecimal string. The string may not contain + * a leading '#' + * @param hexValue The string to parse into a color + * @param permissive If true, strings like "f" are considered valid + * representations of the color white. Useful for UIs that allow entering + * a hex color. Empty strings evaluate to pure white + * @returns A ccColor3B if it could be succesfully parsed, or an error + * indicating the failure reason + */ + GEODE_DLL Result<cocos2d::ccColor3B> cc3bFromHexString(std::string const& hexValue, bool permissive = false); + /** + * Parse a ccColor4B from a hexadecimal string. The string may not contain + * a leading '#' + * @param hexValue The string to parse into a color + * @param requireAlpha Require the alpha component to be passed. If false, + * alpha defaults to 255 + * @param permissive If true, strings like "f" are considered valid + * representations of the color white. Useful for UIs that allow entering + * a hex color. Empty strings evaluate to pure white + * @returns A ccColor4B if it could be succesfully parsed, or an error + * indicating the failure reason + */ + GEODE_DLL Result<cocos2d::ccColor4B> cc4bFromHexString(std::string const& hexValue, bool requireAlpha = false, bool permissive = false); GEODE_DLL std::string cc3bToHexString(cocos2d::ccColor3B const& color); GEODE_DLL std::string cc4bToHexString(cocos2d::ccColor4B const& color); diff --git a/loader/src/loader/Setting.cpp b/loader/src/loader/Setting.cpp index e202944b..531a358f 100644 --- a/loader/src/loader/Setting.cpp +++ b/loader/src/loader/Setting.cpp @@ -15,7 +15,21 @@ template<class T> static void parseCommon(T& sett, JsonMaybeObject& obj) { obj.has("name").into(sett.name); obj.has("description").into(sett.description); - obj.has("default").into(sett.defaultValue); + if (auto defValue = obj.needs("default")) { + // Platform-specific default value + if (defValue.template is<matjson::Object>()) { + auto def = defValue.obj(); + if (auto plat = def.has(PlatformID::toShortString(GEODE_PLATFORM_TARGET, true))) { + plat.into(sett.defaultValue); + } + else { + defValue.into(sett.defaultValue); + } + } + else { + defValue.into(sett.defaultValue); + } + } } Result<BoolSetting> BoolSetting::parse(JsonMaybeObject& obj) { diff --git a/loader/src/ui/nodes/ColorPickPopup.cpp b/loader/src/ui/nodes/ColorPickPopup.cpp index 2238387e..afdb9e0b 100644 --- a/loader/src/ui/nodes/ColorPickPopup.cpp +++ b/loader/src/ui/nodes/ColorPickPopup.cpp @@ -213,7 +213,7 @@ void ColorPickPopup::textChanged(CCTextInputNode* input) { switch (input->getTag()) { case TAG_HEX_INPUT: { - if (auto color = cc3bFromHexString(input->getString())) { + if (auto color = cc3bFromHexString(input->getString(), true)) { m_color.r = color.unwrap().r; m_color.g = color.unwrap().g; m_color.b = color.unwrap().b; diff --git a/loader/src/utils/cocos.cpp b/loader/src/utils/cocos.cpp index 7ebc56d3..0db65f29 100644 --- a/loader/src/utils/cocos.cpp +++ b/loader/src/utils/cocos.cpp @@ -114,8 +114,8 @@ ccColor4B matjson::Serialize<ccColor4B>::from_json(matjson::Value const& json) { return color; } -Result<ccColor3B> geode::cocos::cc3bFromHexString(std::string const& hexValue) { - if (hexValue.empty()) { +Result<ccColor3B> geode::cocos::cc3bFromHexString(std::string const& hexValue, bool permissive) { + if (permissive && hexValue.empty()) { return Ok(ccc3(255, 255, 255)); } if (hexValue.size() > 6) { @@ -142,21 +142,34 @@ Result<ccColor3B> geode::cocos::cc3bFromHexString(std::string const& hexValue) { } break; case 2: { + if (!permissive) { + return Err("Invalid hex pattern, expected RGB or RRGGBB"); + } auto num = static_cast<uint8_t>(numValue); return Ok(ccc3(num, num, num)); } break; case 1: { + if (!permissive) { + return Err("Invalid hex pattern, expected RGB or RRGGBB"); + } auto num = static_cast<uint8_t>(numValue) * 17; return Ok(ccc3(num, num, num)); } break; - default: return Err("Invalid hex size, expected 1, 2, 3, or 6"); + default: { + if (permissive) { + return Err("Invalid hex pattern, expected R, RR, RGB, or RRGGBB"); + } + else { + return Err("Invalid hex pattern, expected RGB or RRGGBB"); + } + } } } -Result<ccColor4B> geode::cocos::cc4bFromHexString(std::string const& hexValue) { - if (hexValue.empty()) { +Result<ccColor4B> geode::cocos::cc4bFromHexString(std::string const& hexValue, bool requireAlpha, bool permissive) { + if (permissive && hexValue.empty()) { return Ok(ccc4(255, 255, 255, 255)); } if (hexValue.size() > 8) { @@ -177,6 +190,9 @@ Result<ccColor4B> geode::cocos::cc4bFromHexString(std::string const& hexValue) { } break; case 6: { + if (requireAlpha) { + return Err("Alpha component is required, got only RRGGBB"); + } auto r = static_cast<uint8_t>((numValue & 0xFF0000) >> 16); auto g = static_cast<uint8_t>((numValue & 0x00FF00) >> 8); auto b = static_cast<uint8_t>((numValue & 0x0000FF)); @@ -192,6 +208,9 @@ Result<ccColor4B> geode::cocos::cc4bFromHexString(std::string const& hexValue) { } break; case 3: { + if (requireAlpha) { + return Err("Alpha component is required, got only RGB"); + } auto r = static_cast<uint8_t>(((numValue & 0xF00) >> 8) * 17); auto g = static_cast<uint8_t>(((numValue & 0x0F0) >> 4) * 17); auto b = static_cast<uint8_t>(((numValue & 0x00F)) * 17); @@ -199,16 +218,38 @@ Result<ccColor4B> geode::cocos::cc4bFromHexString(std::string const& hexValue) { } break; case 2: { + if (!permissive) { + return Err("Invalid hex pattern, expected RGBA or RRGGBBAA"); + } + if (requireAlpha) { + return Err("Alpha component is required, specify full RRGGBBAA"); + } auto num = static_cast<uint8_t>(numValue); return Ok(ccc4(num, num, num, 255)); } break; case 1: { + if (!permissive) { + return Err("Invalid hex pattern, expected RGBA or RRGGBBAA"); + } + if (requireAlpha) { + return Err("Alpha component is required, specify full RGBA"); + } auto num = static_cast<uint8_t>(numValue) * 17; return Ok(ccc4(num, num, num, 255)); } break; - default: return Err("Invalid hex size, expected 1, 2, 3, 4, 6, or 8"); + default: { + if (requireAlpha) { + return Err("Invalid hex pattern, expected RGBA or RRGGBBAA"); + } + else if (permissive) { + return Err("Invalid hex pattern, expected R, RR, RGB, RGBA, RRGGBB, or RRGGBBAA"); + } + else { + return Err("Invalid hex pattern, expected RGB, RGBA, RRGGBB, or RRGGBBAA"); + } + } } }