From d2e662ddbb518b147de76ca3f613e046d2912db5 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 12:35:34 -0400 Subject: [PATCH 01/20] added support for components in resource pack descriptions. Signed-off-by: Caden Miller --- .../mod/tasks/LocalResourcePackParseTask.cpp | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 73cbf891c..f78d3b7b3 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -186,7 +186,35 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) auto pack_obj = Json::requireObject(json_doc.object(), "pack", {}); pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - pack.setDescription(Json::ensureString(pack_obj, "description", "")); + + // description could either be string, or array of dictionaries + auto desc_val = pack_obj.value("description"); + + if (desc_val.isString()) { + pack.setDescription(Json::ensureString(pack_obj, "description", "")); + } else if (desc_val.isArray()) { + + // rebuild the description from the dictionaries without colors + QString build_desc; + auto arr = desc_val.toArray(); + + for(const QJsonValue& dict : arr) { + // must be an object, for a dictionary + if (!dict.isObject()) throw Json::JsonException("Invalid value description."); + + auto obj = dict.toObject(); + auto val = obj.value(obj.keys()[0]); + + // value must be a string type + if (!val.isString()) throw Json::JsonException("Invalid value description type."); + + build_desc.append(val.toString()); + }; + + pack.setDescription(build_desc); + } else { + throw Json::JsonException("Invalid description type."); + } } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); return false; From 093d09efe36dbe1b18e4ae0ccb48150ef2f9f3ab Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:03:33 -0400 Subject: [PATCH 02/20] fix style, and use qWarning instead of throw. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index f78d3b7b3..22f5fa163 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -189,9 +189,13 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); + if (desc_val.isUndefined()) { + qWarning() << "No resource pack description found."; + return false; + } if (desc_val.isString()) { - pack.setDescription(Json::ensureString(pack_obj, "description", "")); + pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { // rebuild the description from the dictionaries without colors @@ -200,20 +204,27 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) for(const QJsonValue& dict : arr) { // must be an object, for a dictionary - if (!dict.isObject()) throw Json::JsonException("Invalid value description."); + if (!dict.isObject()) + { + qWarning() << "Invalid component object."; + continue; + } auto obj = dict.toObject(); auto val = obj.value(obj.keys()[0]); - // value must be a string type - if (!val.isString()) throw Json::JsonException("Invalid value description type."); + if (!val.isString()) + { + qWarning() << "Invalid text description type in components."; + continue; + } build_desc.append(val.toString()); }; pack.setDescription(build_desc); } else { - throw Json::JsonException("Invalid description type."); + qWarning() << "Invalid description type."; } } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); From 04aa0155bf181dcd17236ef5baa3b91a4b9df449 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:45:30 -0400 Subject: [PATCH 03/20] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: d2e662ddbb518b147de76ca3f613e046d2912db5 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 22f5fa163..7a2fd7a86 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -221,7 +221,6 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) build_desc.append(val.toString()); }; - pack.setDescription(build_desc); } else { qWarning() << "Invalid description type."; From 05f4214cc5d3a9005c0d259ca185303792aa2fa7 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:50:13 -0400 Subject: [PATCH 04/20] fix clang-format failing --- .../minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 7a2fd7a86..b696adea5 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -202,10 +202,9 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) QString build_desc; auto arr = desc_val.toArray(); - for(const QJsonValue& dict : arr) { + for (const QJsonValue& dict : arr) { // must be an object, for a dictionary - if (!dict.isObject()) - { + if (!dict.isObject()) { qWarning() << "Invalid component object."; continue; } @@ -213,8 +212,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) auto obj = dict.toObject(); auto val = obj.value(obj.keys()[0]); - if (!val.isString()) - { + if (!val.isString()) { qWarning() << "Invalid text description type in components."; continue; } From ef1dc2afaccc681fdfd53513ac134b36a5d943cc Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:50:59 -0400 Subject: [PATCH 05/20] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: 05f4214cc5d3a9005c0d259ca185303792aa2fa7 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index b696adea5..eaef76a6c 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -197,7 +197,6 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) if (desc_val.isString()) { pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { - // rebuild the description from the dictionaries without colors QString build_desc; auto arr = desc_val.toArray(); From 1261908ef7465e000390c9c12133171f4e73302f Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:55:55 -0400 Subject: [PATCH 06/20] remove space for clang-format Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index eaef76a6c..1f535695b 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -194,7 +194,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) return false; } - if (desc_val.isString()) { + if (desc_val.isString()) { pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { // rebuild the description from the dictionaries without colors From 7a7c3015f47cba94eb34deba6748dc8555d8e457 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 20:25:49 -0400 Subject: [PATCH 07/20] fix not properly reading json text. The text now displays properly in the GUI of Prism. Signed-off-by: cullvox --- .../minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 1f535695b..4c30c1204 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -209,14 +209,8 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) } auto obj = dict.toObject(); - auto val = obj.value(obj.keys()[0]); - if (!val.isString()) { - qWarning() << "Invalid text description type in components."; - continue; - } - - build_desc.append(val.toString()); + build_desc.append(Json::ensureString(obj, "text", {})); }; pack.setDescription(build_desc); } else { From 58bd6d929f20e43c38cb372e431d1e1240d736b6 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sun, 10 Sep 2023 23:37:26 -0400 Subject: [PATCH 08/20] added formatting with colors. This is somewhat dirty implementation and I will clean it up soon. Using the HTML rich text features of Qt, made it much easier to understand what was needed. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 170 +++++++++++++++--- 1 file changed, 143 insertions(+), 27 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 4c30c1204..e6dde4ba0 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -178,9 +178,143 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta -bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) +struct TextFormat { + QString color = "#000000"; + bool bold = false; + bool italic = false; + bool underlined = false; + bool strikethrough = false; + + TextFormat() = default; + ~TextFormat() = default; + + void clear() { + color = "#00000"; + bold = false; + italic = false; + underlined = false; + strikethrough = false; + } +}; + +QString textColorToHexColor(const QString& color) { + static const std::unordered_map str_to_hex = { + { "black", "#000000" }, + { "dark_blue", "#0000AA" }, + { "dark_green", "#00AA00" }, + { "dark_aqua", "#00AAAA" }, + { "dark_red", "#AA0000" }, + { "dark_purple", "#AA00AA" }, + { "gold", "#FFAA00" }, + { "gray", "#AAAAAA" }, + { "dark_gray", "#555555" }, + { "blue", "#5555FF" }, + { "green", "#55FF55" }, + { "aqua", "#55FFFF" }, + { "red", "#FF5555" }, + { "light_purple", "#FF55FF" }, + { "yellow", "#FFFF55" }, + { "white", "#FFFFFF" }, + }; + + auto it = str_to_hex.find(color); + return (it != str_to_hex.end()) ? it->second : QString("#000000"); // return black if color not found +} + +bool readFormat(const QJsonObject& obj, TextFormat& format) { + auto text = obj.value("text"); + auto color = obj.value("color"); + auto bold = obj.value("bold"); + auto italic = obj.value("italic"); + auto underlined = obj.value("underlined"); + auto strikethrough = obj.value("strikethrough"); + auto extra = obj.value("extra"); + + if (color.isString()) { + format.color = textColorToHexColor(color.toString()); + } + if (bold.isBool()) + format.bold = bold.toBool(); + if (italic.isBool()) + format.italic = italic.toBool(); + if (underlined.isBool()) + format.underlined = underlined.toBool(); + if (strikethrough.isBool()) + format.strikethrough = strikethrough.toBool(); + + return true; +} + +void appendBeginFormat(TextFormat& format, QString& toAppend) { + toAppend.append(""); + if (format.bold) + toAppend.append(""); + if (format.italic) + toAppend.append(""); + if (format.underlined) + toAppend.append(""); + if (format.strikethrough) + toAppend.append(""); +} + +void appendEndFormat(TextFormat& format, QString& toAppend) { + toAppend.append(""); + if (format.bold) + toAppend.append(""); + if (format.italic) + toAppend.append(""); + if (format.underlined) + toAppend.append(""); + if (format.strikethrough) + toAppend.append(""); +} + +bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat = nullptr); + +bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) { + + for (const QJsonValue& val : arr) { + processComponent(val, result, parentFormat); + } + + return true; +} + +bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) { + if (value.isString()) { + result.append(value.toString()); + } else if (value.isObject()) { + auto obj = value.toObject(); + + TextFormat format{}; + if (parentFormat) + format = *parentFormat; + + if (!readFormat(obj, format)) + return false; + + appendBeginFormat(format, result); + + auto text = obj.value("text"); + if (text.isString()) + result.append(text.toString()); + + auto extra = obj.value("extra"); + if (extra.isArray()) + if (!processComponentList(extra.toArray(), result, &format)) + return false; + + appendEndFormat(format, result); + } + + return true; +} + +// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta +bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { + + try { auto json_doc = QJsonDocument::fromJson(raw_data); auto pack_obj = Json::requireObject(json_doc.object(), "pack", {}); @@ -189,33 +323,15 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); - if (desc_val.isUndefined()) { - qWarning() << "No resource pack description found."; - return false; - } - - if (desc_val.isString()) { + + if (desc_val.isString()) pack.setDescription(desc_val.toString()); - } else if (desc_val.isArray()) { - // rebuild the description from the dictionaries without colors - QString build_desc; - auto arr = desc_val.toArray(); - - for (const QJsonValue& dict : arr) { - // must be an object, for a dictionary - if (!dict.isObject()) { - qWarning() << "Invalid component object."; - continue; - } - - auto obj = dict.toObject(); - - build_desc.append(Json::ensureString(obj, "text", {})); - }; - pack.setDescription(build_desc); - } else { - qWarning() << "Invalid description type."; + else if (desc_val.isArray()) { + QString desc{}; + processComponentList(desc_val.toArray(), desc); + pack.setDescription(desc); } + } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); return false; From fbe4043651bebaca399b9f4499a2097670e485b6 Mon Sep 17 00:00:00 2001 From: cullvox Date: Mon, 11 Sep 2023 01:34:53 -0400 Subject: [PATCH 09/20] fix formatting and add more proper errors. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 129 +++++++++++------- 1 file changed, 80 insertions(+), 49 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index e6dde4ba0..ca1a5455c 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -184,45 +184,60 @@ struct TextFormat { bool italic = false; bool underlined = false; bool strikethrough = false; - - TextFormat() = default; - ~TextFormat() = default; - - void clear() { - color = "#00000"; - bold = false; - italic = false; - underlined = false; - strikethrough = false; - } }; -QString textColorToHexColor(const QString& color) +bool textColorToHexColor(const QString& text_color, QString& result) { - static const std::unordered_map str_to_hex = { - { "black", "#000000" }, - { "dark_blue", "#0000AA" }, - { "dark_green", "#00AA00" }, - { "dark_aqua", "#00AAAA" }, - { "dark_red", "#AA0000" }, - { "dark_purple", "#AA00AA" }, - { "gold", "#FFAA00" }, - { "gray", "#AAAAAA" }, - { "dark_gray", "#555555" }, - { "blue", "#5555FF" }, - { "green", "#55FF55" }, - { "aqua", "#55FFFF" }, - { "red", "#FF5555" }, - { "light_purple", "#FF55FF" }, - { "yellow", "#FFFF55" }, - { "white", "#FFFFFF" }, + static const QHash text_to_hex = { + { "black", "#000000" }, { "dark_blue", "#0000AA" }, { "dark_green", "#00AA00" }, { "dark_aqua", "#00AAAA" }, + { "dark_red", "#AA0000" }, { "dark_purple", "#AA00AA" }, { "gold", "#FFAA00" }, { "gray", "#AAAAAA" }, + { "dark_gray", "#555555" }, { "blue", "#5555FF" }, { "green", "#55FF55" }, { "aqua", "#55FFFF" }, + { "red", "#FF5555" }, { "light_purple", "#FF55FF" }, { "yellow", "#FFFF55" }, { "white", "#FFFFFF" }, }; - auto it = str_to_hex.find(color); - return (it != str_to_hex.end()) ? it->second : QString("#000000"); // return black if color not found + auto it = text_to_hex.find(text_color); + + if (it == text_to_hex.end()) { + qWarning() << "Invalid color string in component!"; + result = "#000000"; // return black if color not found + return false; + } + + result = it.value(); + return true; } -bool readFormat(const QJsonObject& obj, TextFormat& format) { +bool getBoolOrFromText(const QJsonValue& val, bool& result) +{ + if (val.isUndefined()) { + result = false; + return true; + } + + if (val.isBool()) { + result = val.toBool(); + return true; + } else if (val.isString()) { + auto bool_str = val.toString(); + + if (bool_str == "true") { + result = true; + return true; + } else if (bool_str == "false") { + result = false; + return true; + } else { + qWarning() << "Invalid bool value in component!"; + return false; + } + } else { + qWarning() << "Invalid type where bool expected!"; + return false; + } +} + +bool readFormat(const QJsonObject& obj, TextFormat& format) +{ auto text = obj.value("text"); auto color = obj.value("color"); auto bold = obj.value("bold"); @@ -232,21 +247,30 @@ bool readFormat(const QJsonObject& obj, TextFormat& format) { auto extra = obj.value("extra"); if (color.isString()) { - format.color = textColorToHexColor(color.toString()); - } - if (bold.isBool()) - format.bold = bold.toBool(); - if (italic.isBool()) - format.italic = italic.toBool(); - if (underlined.isBool()) - format.underlined = underlined.toBool(); - if (strikethrough.isBool()) - format.strikethrough = strikethrough.toBool(); + // colors can either be a hex code or one of a few text colors + auto col_str = color.toString(); - return true; + const QRegularExpression hex_expression("^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"); + const auto hex_match = hex_expression.match(col_str); + + if (hex_match.hasMatch()) { + format.color = color.toString(); + } else { + if (!textColorToHexColor(color.toString(), format.color)) { + qWarning() << "Invalid color type in component!"; + return false; + } + } + } + + return getBoolOrFromText(bold, format.bold) && + getBoolOrFromText(italic, format.italic) && + getBoolOrFromText(underlined, format.underlined) && + getBoolOrFromText(strikethrough, format.strikethrough); } -void appendBeginFormat(TextFormat& format, QString& toAppend) { +void appendBeginFormat(TextFormat& format, QString& toAppend) +{ toAppend.append(""); if (format.bold) toAppend.append(""); @@ -275,7 +299,8 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) { for (const QJsonValue& val : arr) { - processComponent(val, result, parentFormat); + if (!processComponent(val, result, parentFormat)) + return false; } return true; @@ -306,6 +331,9 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare return false; appendEndFormat(format, result); + } else { + qWarning() << "Invalid component type!"; + return false; } return true; @@ -324,12 +352,15 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); - if (desc_val.isString()) + if (desc_val.isString()) { pack.setDescription(desc_val.toString()); - else if (desc_val.isArray()) { - QString desc{}; - processComponentList(desc_val.toArray(), desc); + } else if (desc_val.isArray()) { + QString desc; + if (!processComponentList(desc_val.toArray(), desc)) + return false; pack.setDescription(desc); + } else { + return false; } } catch (Json::JsonException& e) { From b16085f66c9083a1ec6d0feb08b96ce9173baccd Mon Sep 17 00:00:00 2001 From: cullvox Date: Mon, 11 Sep 2023 02:05:05 -0400 Subject: [PATCH 10/20] attempt to fix clang-format and ubuntu build. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index ca1a5455c..c2aef06d5 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -26,6 +26,7 @@ #include #include +#include namespace ResourcePackUtils { @@ -236,7 +237,7 @@ bool getBoolOrFromText(const QJsonValue& val, bool& result) } } -bool readFormat(const QJsonObject& obj, TextFormat& format) +bool readFormat(const QJsonObject& obj, TextFormat& format) { auto text = obj.value("text"); auto color = obj.value("color"); @@ -247,7 +248,7 @@ bool readFormat(const QJsonObject& obj, TextFormat& format) auto extra = obj.value("extra"); if (color.isString()) { - // colors can either be a hex code or one of a few text colors + // colors can either be a hex code or one of a few text colors auto col_str = color.toString(); const QRegularExpression hex_expression("^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"); @@ -263,13 +264,11 @@ bool readFormat(const QJsonObject& obj, TextFormat& format) } } - return getBoolOrFromText(bold, format.bold) && - getBoolOrFromText(italic, format.italic) && - getBoolOrFromText(underlined, format.underlined) && - getBoolOrFromText(strikethrough, format.strikethrough); + return getBoolOrFromText(bold, format.bold) && getBoolOrFromText(italic, format.italic) && + getBoolOrFromText(underlined, format.underlined) && getBoolOrFromText(strikethrough, format.strikethrough); } -void appendBeginFormat(TextFormat& format, QString& toAppend) +void appendBeginFormat(TextFormat& format, QString& toAppend) { toAppend.append(""); if (format.bold) @@ -282,7 +281,8 @@ void appendBeginFormat(TextFormat& format, QString& toAppend) toAppend.append(""); } -void appendEndFormat(TextFormat& format, QString& toAppend) { +void appendEndFormat(TextFormat& format, QString& toAppend) +{ toAppend.append(""); if (format.bold) toAppend.append(""); @@ -296,7 +296,8 @@ void appendEndFormat(TextFormat& format, QString& toAppend) { bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat = nullptr); -bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) { +bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) +{ for (const QJsonValue& val : arr) { if (!processComponent(val, result, parentFormat)) @@ -306,7 +307,8 @@ bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* pa return true; } -bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) { +bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) +{ if (value.isString()) { result.append(value.toString()); } else if (value.isObject()) { @@ -340,9 +342,8 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare } // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta -bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { - - +bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) +{ try { auto json_doc = QJsonDocument::fromJson(raw_data); auto pack_obj = Json::requireObject(json_doc.object(), "pack", {}); @@ -351,7 +352,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); - + if (desc_val.isString()) { pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { From df88ccd4190ad2f605efe96b2b1503bc0ce0d5c8 Mon Sep 17 00:00:00 2001 From: cullvox Date: Mon, 11 Sep 2023 15:49:01 -0400 Subject: [PATCH 11/20] clean up and add review suggestions, links open Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 133 ++++++------------ .../mod/tasks/LocalResourcePackParseTask.h | 3 + 2 files changed, 43 insertions(+), 93 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index c2aef06d5..5b6f6fee0 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -185,87 +185,23 @@ struct TextFormat { bool italic = false; bool underlined = false; bool strikethrough = false; + bool is_linked = false; + QString link_url = ""; }; -bool textColorToHexColor(const QString& text_color, QString& result) -{ - static const QHash text_to_hex = { - { "black", "#000000" }, { "dark_blue", "#0000AA" }, { "dark_green", "#00AA00" }, { "dark_aqua", "#00AAAA" }, - { "dark_red", "#AA0000" }, { "dark_purple", "#AA00AA" }, { "gold", "#FFAA00" }, { "gray", "#AAAAAA" }, - { "dark_gray", "#555555" }, { "blue", "#5555FF" }, { "green", "#55FF55" }, { "aqua", "#55FFFF" }, - { "red", "#FF5555" }, { "light_purple", "#FF55FF" }, { "yellow", "#FFFF55" }, { "white", "#FFFFFF" }, - }; - - auto it = text_to_hex.find(text_color); - - if (it == text_to_hex.end()) { - qWarning() << "Invalid color string in component!"; - result = "#000000"; // return black if color not found - return false; - } - - result = it.value(); - return true; -} - -bool getBoolOrFromText(const QJsonValue& val, bool& result) -{ - if (val.isUndefined()) { - result = false; - return true; - } - - if (val.isBool()) { - result = val.toBool(); - return true; - } else if (val.isString()) { - auto bool_str = val.toString(); - - if (bool_str == "true") { - result = true; - return true; - } else if (bool_str == "false") { - result = false; - return true; - } else { - qWarning() << "Invalid bool value in component!"; - return false; - } - } else { - qWarning() << "Invalid type where bool expected!"; - return false; - } -} - bool readFormat(const QJsonObject& obj, TextFormat& format) { - auto text = obj.value("text"); - auto color = obj.value("color"); - auto bold = obj.value("bold"); - auto italic = obj.value("italic"); - auto underlined = obj.value("underlined"); - auto strikethrough = obj.value("strikethrough"); - auto extra = obj.value("extra"); + format.color = Json::ensureString(obj, "color"); + format.bold = Json::ensureBoolean(obj, "bold", false); + format.italic = Json::ensureBoolean(obj, "italic", false); + format.underlined = Json::ensureBoolean(obj, "underlined", false); + format.strikethrough = Json::ensureBoolean(obj, "strikethrough", false); - if (color.isString()) { - // colors can either be a hex code or one of a few text colors - auto col_str = color.toString(); + auto click_event = Json::ensureObject(obj, "clickEvent"); + format.is_linked = Json::ensureBoolean(click_event, "open_url", false); + format.link_url = Json::ensureString(click_event, "value"); - const QRegularExpression hex_expression("^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"); - const auto hex_match = hex_expression.match(col_str); - - if (hex_match.hasMatch()) { - format.color = color.toString(); - } else { - if (!textColorToHexColor(color.toString(), format.color)) { - qWarning() << "Invalid color type in component!"; - return false; - } - } - } - - return getBoolOrFromText(bold, format.bold) && getBoolOrFromText(italic, format.italic) && - getBoolOrFromText(underlined, format.underlined) && getBoolOrFromText(strikethrough, format.strikethrough); + return true; } void appendBeginFormat(TextFormat& format, QString& toAppend) @@ -279,35 +215,36 @@ void appendBeginFormat(TextFormat& format, QString& toAppend) toAppend.append(""); if (format.strikethrough) toAppend.append(""); + if (format.is_linked) + toAppend.append(""); } void appendEndFormat(TextFormat& format, QString& toAppend) { - toAppend.append(""); - if (format.bold) - toAppend.append(""); + if (format.is_linked) + toAppend.append(""); + if (format.strikethrough) + toAppend.append(""); if (format.italic) toAppend.append(""); if (format.underlined) toAppend.append(""); - if (format.strikethrough) - toAppend.append(""); + if (format.bold) + toAppend.append(""); + toAppend.append(""); } -bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat = nullptr); - -bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) +bool processComponentList(const QJsonArray& arr, QString& result) { - for (const QJsonValue& val : arr) { - if (!processComponent(val, result, parentFormat)) + if (!processComponent(val, result)) return false; } return true; } -bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) +bool processComponent(const QJsonValue& value, QString& result) { if (value.isString()) { result.append(value.toString()); @@ -315,8 +252,6 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare auto obj = value.toObject(); TextFormat format{}; - if (parentFormat) - format = *parentFormat; if (!readFormat(obj, format)) return false; @@ -329,7 +264,7 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare auto extra = obj.value("extra"); if (extra.isArray()) - if (!processComponentList(extra.toArray(), result, &format)) + if (!processComponentList(extra.toArray(), result)) return false; appendEndFormat(format, result); @@ -342,6 +277,7 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare } // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta +// https://minecraft.fandom.com/wiki/Raw_JSON_text_format#Plain_Text bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { try { @@ -350,20 +286,31 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - // description could either be string, or array of dictionaries + // description could be many things according to minecraft auto desc_val = pack_obj.value("description"); + QString desc; if (desc_val.isString()) { - pack.setDescription(desc_val.toString()); + desc = desc_val.toString(); } else if (desc_val.isArray()) { - QString desc; if (!processComponentList(desc_val.toArray(), desc)) return false; - pack.setDescription(desc); + } else if (desc_val.isObject()) { + if (!processComponent(desc_val, desc)) + return false; + } else if (desc_val.isBool()) { + desc = desc_val.toBool() ? "true" : "false"; + } else if (desc_val.isDouble()) { + desc = QString::number(desc_val.toDouble()); } else { + qWarning() << "Invalid description type!"; return false; } + qInfo() << desc; + + pack.setDescription(desc); + } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); return false; diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 5199bf3f0..ed3317643 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -34,6 +34,9 @@ bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); + +bool processComponent(const QJsonValue& value, QString& result); +bool processComponentList(const QJsonArray& value, QString& result); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data); From a4e65305139dc5cdc7ad246b3203175a21262050 Mon Sep 17 00:00:00 2001 From: cullvox Date: Tue, 12 Sep 2023 21:34:42 -0400 Subject: [PATCH 12/20] added tests, fixed issues with overriding/format In the documentation it states that child values can override the parent values. Originally this code did not support that but now it does. Also added in testing inspired by the previous tests. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 217 ++++++++++-------- .../mod/tasks/LocalResourcePackParseTask.h | 4 +- tests/CMakeLists.txt | 3 + tests/MetaComponentParse_test.cpp | 108 +++++++++ .../MetaComponentParse/component_basic.json | 4 + .../component_with_extra.json | 18 ++ .../component_with_format.json | 13 ++ .../component_with_link.json | 12 + .../component_with_mixed.json | 40 ++++ 9 files changed, 321 insertions(+), 98 deletions(-) create mode 100644 tests/MetaComponentParse_test.cpp create mode 100644 tests/testdata/MetaComponentParse/component_basic.json create mode 100644 tests/testdata/MetaComponentParse/component_with_extra.json create mode 100644 tests/testdata/MetaComponentParse/component_with_format.json create mode 100644 tests/testdata/MetaComponentParse/component_with_link.json create mode 100644 tests/testdata/MetaComponentParse/component_with_mixed.json diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 5b6f6fee0..31cfdca04 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -179,96 +179,138 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -struct TextFormat { - QString color = "#000000"; - bool bold = false; - bool italic = false; - bool underlined = false; - bool strikethrough = false; - bool is_linked = false; - QString link_url = ""; -}; +struct TextFormatter { + // left is value, right is if the value was explicitly written + QPair color = { "#000000", false }; + QPair bold = { false, false }; + QPair italic = { false, false }; + QPair underlined = { false, false }; + QPair strikethrough = { false, false }; + QPair is_linked = { false, false }; + QPair link_url = { "", false }; -bool readFormat(const QJsonObject& obj, TextFormat& format) -{ - format.color = Json::ensureString(obj, "color"); - format.bold = Json::ensureBoolean(obj, "bold", false); - format.italic = Json::ensureBoolean(obj, "italic", false); - format.underlined = Json::ensureBoolean(obj, "underlined", false); - format.strikethrough = Json::ensureBoolean(obj, "strikethrough", false); + void setColor(const QString& new_color, bool written) { color = { new_color, written}; } + void setBold(bool new_bold, bool written) { bold = { new_bold, written}; } + void setItalic(bool new_italic, bool written) { italic = { new_italic, written}; } + void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written}; } + void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written}; } + void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written}; } + void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written}; } - auto click_event = Json::ensureObject(obj, "clickEvent"); - format.is_linked = Json::ensureBoolean(click_event, "open_url", false); - format.link_url = Json::ensureString(click_event, "value"); - - return true; -} - -void appendBeginFormat(TextFormat& format, QString& toAppend) -{ - toAppend.append(""); - if (format.bold) - toAppend.append(""); - if (format.italic) - toAppend.append(""); - if (format.underlined) - toAppend.append(""); - if (format.strikethrough) - toAppend.append(""); - if (format.is_linked) - toAppend.append(""); -} - -void appendEndFormat(TextFormat& format, QString& toAppend) -{ - if (format.is_linked) - toAppend.append(""); - if (format.strikethrough) - toAppend.append(""); - if (format.italic) - toAppend.append(""); - if (format.underlined) - toAppend.append(""); - if (format.bold) - toAppend.append(""); - toAppend.append(""); -} - -bool processComponentList(const QJsonArray& arr, QString& result) -{ - for (const QJsonValue& val : arr) { - if (!processComponent(val, result)) - return false; + void overrideFrom(const TextFormatter& child) + { + if (child.color.second) + color.first = child.color.first; + if (child.bold.second) + bold.first = child.bold.first; + if (child.italic.second) + italic.first = child.italic.first; + if (child.underlined.second) + underlined.first = child.underlined.first; + if (child.strikethrough.second) + strikethrough.first = child.strikethrough.first; + if (child.is_linked.second) + is_linked.first = child.is_linked.first; + if (child.link_url.second) + link_url.first = child.link_url.first; } - return true; -} + QString format(QString text) + { + if (text.isEmpty()) + return QString(); -bool processComponent(const QJsonValue& value, QString& result) + QString result; + + if (color.first != "#000000") + result.append(""); + if (bold.first) + result.append(""); + if (italic.first) + result.append(""); + if (underlined.first) + result.append(""); + if (strikethrough.first) + result.append(""); + if (is_linked.first) + result.append(""); + + result.append(text); + + if (is_linked.first) + result.append(""); + if (strikethrough.first) + result.append(""); + if (underlined.first) + result.append(""); + if (italic.first) + result.append(""); + if (bold.first) + result.append(""); + if (color.first != "#000000") + result.append(""); + + return result; + } + + bool readFormat(const QJsonObject& obj) + { + setColor(Json::ensureString(obj, "color", "#000000"), obj.contains("color")); + setBold(Json::ensureBoolean(obj, "bold", false), obj.contains("bold")); + setItalic(Json::ensureBoolean(obj, "italic", false), obj.contains("italic")); + setUnderlined(Json::ensureBoolean(obj, "underlined", false), obj.contains("underlined")); + setStrikethrough(Json::ensureBoolean(obj, "strikethrough", false), obj.contains("strikethrough")); + + auto click_event = Json::ensureObject(obj, "clickEvent"); + setIsLinked(Json::ensureBoolean(click_event, "open_url", false), click_event.contains("open_url")); + setLinkURL(Json::ensureString(click_event, "value"), click_event.contains("value")); + + return true; + } +}; + +bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat) { + TextFormatter formatter; + + if (parentFormat) + formatter = *parentFormat; + if (value.isString()) { - result.append(value.toString()); - } else if (value.isObject()) { + result.append(formatter.format(value.toString())); + } else if (value.isBool()) { + result.append(formatter.format(value.toBool() ? "true" : "false")); + } else if (value.isDouble()) { + result.append(formatter.format(QString::number(value.toDouble()))); + } else if (value.isObject()) { auto obj = value.toObject(); - TextFormat format{}; - - if (!readFormat(obj, format)) + if (not formatter.readFormat(obj)) return false; - - appendBeginFormat(format, result); - - auto text = obj.value("text"); - if (text.isString()) - result.append(text.toString()); + // override the parent format with our new one + TextFormatter mixed; + if (parentFormat) + mixed = *parentFormat; + + mixed.overrideFrom(formatter); + + result.append(mixed.format(Json::ensureString(obj, "text"))); + + // process any 'extra' children with this format auto extra = obj.value("extra"); - if (extra.isArray()) - if (!processComponentList(extra.toArray(), result)) - return false; + if (not extra.isUndefined()) + return processComponent(extra, result, &mixed); - appendEndFormat(format, result); - } else { + } else if (value.isArray()) { + auto array = value.toArray(); + + for (const QJsonValue& current : array) { + if (not processComponent(current, result, parentFormat)) { + return false; + } + } + } else { qWarning() << "Invalid component type!"; return false; } @@ -286,29 +328,12 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - // description could be many things according to minecraft auto desc_val = pack_obj.value("description"); - - QString desc; - if (desc_val.isString()) { - desc = desc_val.toString(); - } else if (desc_val.isArray()) { - if (!processComponentList(desc_val.toArray(), desc)) - return false; - } else if (desc_val.isObject()) { - if (!processComponent(desc_val, desc)) - return false; - } else if (desc_val.isBool()) { - desc = desc_val.toBool() ? "true" : "false"; - } else if (desc_val.isDouble()) { - desc = QString::number(desc_val.toDouble()); - } else { - qWarning() << "Invalid description type!"; + QString desc{}; + if (not processComponent(desc_val, desc)) return false; - } qInfo() << desc; - pack.setDescription(desc); } catch (Json::JsonException& e) { diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index ed3317643..7e893979a 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -35,8 +35,8 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Ful bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); -bool processComponent(const QJsonValue& value, QString& result); -bool processComponentList(const QJsonArray& value, QString& result); +struct TextFormatter; +bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat = nullptr); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a26a49fec..8690c078e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -56,3 +56,6 @@ ecm_add_test(Index_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}: ecm_add_test(Version_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME Version) + +ecm_add_test(MetaComponentParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test + TEST_NAME MetaComponentParse) \ No newline at end of file diff --git a/tests/MetaComponentParse_test.cpp b/tests/MetaComponentParse_test.cpp new file mode 100644 index 000000000..77b49b478 --- /dev/null +++ b/tests/MetaComponentParse_test.cpp @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include + +class MetaComponentParseTest : public QObject { + Q_OBJECT + + void doTest(QString name) + { + QString source = QFINDTESTDATA("testdata/MetaComponentParse"); + + QString comp_rp = FS::PathCombine(source, name); + + QFile file; + file.setFileName(comp_rp); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + QString data = file.readAll(); + file.close(); + + QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8()); + QJsonObject obj = doc.object(); + + QJsonValue description_json = obj.value("description"); + QJsonValue expected_json = obj.value("expected_output"); + + QVERIFY(description_json.isUndefined() == false); + QVERIFY(expected_json.isString() == true); + + QString expected = expected_json.toString(); + + QString processed; + bool valid = ResourcePackUtils::processComponent(description_json, processed); + + QVERIFY(processed == expected); + QVERIFY(valid == true); + } + +private slots: + void test_parseComponentBasic() + { + doTest("component_basic.json"); + } + + void test_parseComponentWithFormat() + { + doTest("component_with_format.json"); + } + + void test_parseComponentWithExtra() + { + doTest("component_with_extra.json"); + } + + void test_parseComponentWithLink() + { + doTest("component_with_link.json"); + } + + void test_parseComponentWithMixed() + { + doTest("component_with_mixed.json"); + } +}; + +QTEST_GUILESS_MAIN(MetaComponentParseTest) + +#include "MetaComponentParse_test.moc" diff --git a/tests/testdata/MetaComponentParse/component_basic.json b/tests/testdata/MetaComponentParse/component_basic.json new file mode 100644 index 000000000..5cfdeeeab --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_basic.json @@ -0,0 +1,4 @@ +{ + "description": [{"text": "Hello, Component!"}], + "expected_output": "Hello, Component!" +} diff --git a/tests/testdata/MetaComponentParse/component_with_extra.json b/tests/testdata/MetaComponentParse/component_with_extra.json new file mode 100644 index 000000000..49397556d --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_extra.json @@ -0,0 +1,18 @@ +{ + "description": [ + { + "text": "Hello, ", + "color": "red", + "bold": true, + "italic": true, + "extra": [ + { + "extra": "Component!", + "bold": false, + "italic": false + } + ] + } + ], + "expected_output": "Hello, Component!" +} \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_format.json b/tests/testdata/MetaComponentParse/component_with_format.json new file mode 100644 index 000000000..000de20cb --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_format.json @@ -0,0 +1,13 @@ +{ + "description": [ + { + "text": "Hello, Component!", + "color": "blue", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true + } + ], + "expected_output": "Hello, Component!" +} \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_link.json b/tests/testdata/MetaComponentParse/component_with_link.json new file mode 100644 index 000000000..e190cc5e8 --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_link.json @@ -0,0 +1,12 @@ +{ + "description": [ + { + "text": "Hello, Component!", + "clickEvent": { + "open_url": true, + "value": "https://google.com" + } + } + ], + "expected_output": "Hello, Component!" +} diff --git a/tests/testdata/MetaComponentParse/component_with_mixed.json b/tests/testdata/MetaComponentParse/component_with_mixed.json new file mode 100644 index 000000000..7e65169af --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_mixed.json @@ -0,0 +1,40 @@ +{ + "description": [ + { + "text": "The quick ", + "color": "blue", + "italic": true + }, + { + "text": "brown fox ", + "color": "#873600", + "bold": true, + "underlined": true, + "extra": { + "text": "jumped over ", + "color": "blue", + "bold": false, + "underlined": false, + "italic": true, + "strikethrough": true + } + }, + { + "text": "the lazy dog's back. ", + "color": "green", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true, + "extra": [ + { + "text": "1234567890 ", + "color": "black", + "strikethrough": false, + "extra": "How vexingly quick daft zebras jump!" + } + ] + } + ], + "expected_output": "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" +} From ddf0c28b1b0b6617b0308405b4d5eefd20ff48b2 Mon Sep 17 00:00:00 2001 From: cullvox Date: Tue, 12 Sep 2023 21:45:29 -0400 Subject: [PATCH 13/20] clang-format fixes --- .../mod/tasks/LocalResourcePackParseTask.cpp | 22 ++++++------ .../mod/tasks/LocalResourcePackParseTask.h | 1 - tests/MetaComponentParse_test.cpp | 35 +++++-------------- 3 files changed, 19 insertions(+), 39 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 31cfdca04..8e209a416 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -189,18 +189,18 @@ struct TextFormatter { QPair is_linked = { false, false }; QPair link_url = { "", false }; - void setColor(const QString& new_color, bool written) { color = { new_color, written}; } - void setBold(bool new_bold, bool written) { bold = { new_bold, written}; } - void setItalic(bool new_italic, bool written) { italic = { new_italic, written}; } - void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written}; } - void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written}; } - void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written}; } - void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written}; } + void setColor(const QString& new_color, bool written) { color = { new_color, written }; } + void setBold(bool new_bold, bool written) { bold = { new_bold, written }; } + void setItalic(bool new_italic, bool written) { italic = { new_italic, written }; } + void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written }; } + void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written }; } + void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written }; } + void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written }; } void overrideFrom(const TextFormatter& child) { if (child.color.second) - color.first = child.color.first; + color.first = child.color.first; if (child.bold.second) bold.first = child.bold.first; if (child.italic.second) @@ -282,7 +282,7 @@ bool processComponent(const QJsonValue& value, QString& result, const TextFormat result.append(formatter.format(value.toBool() ? "true" : "false")); } else if (value.isDouble()) { result.append(formatter.format(QString::number(value.toDouble()))); - } else if (value.isObject()) { + } else if (value.isObject()) { auto obj = value.toObject(); if (not formatter.readFormat(obj)) @@ -294,7 +294,7 @@ bool processComponent(const QJsonValue& value, QString& result, const TextFormat mixed = *parentFormat; mixed.overrideFrom(formatter); - + result.append(mixed.format(Json::ensureString(obj, "text"))); // process any 'extra' children with this format @@ -310,7 +310,7 @@ bool processComponent(const QJsonValue& value, QString& result, const TextFormat return false; } } - } else { + } else { qWarning() << "Invalid component type!"; return false; } diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 7e893979a..945499a91 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -34,7 +34,6 @@ bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); - struct TextFormatter; bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat = nullptr); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); diff --git a/tests/MetaComponentParse_test.cpp b/tests/MetaComponentParse_test.cpp index 77b49b478..1b1e2ce3e 100644 --- a/tests/MetaComponentParse_test.cpp +++ b/tests/MetaComponentParse_test.cpp @@ -33,11 +33,11 @@ * limitations under the License. */ -#include -#include #include #include #include +#include +#include #include @@ -76,31 +76,12 @@ class MetaComponentParseTest : public QObject { QVERIFY(valid == true); } -private slots: - void test_parseComponentBasic() - { - doTest("component_basic.json"); - } - - void test_parseComponentWithFormat() - { - doTest("component_with_format.json"); - } - - void test_parseComponentWithExtra() - { - doTest("component_with_extra.json"); - } - - void test_parseComponentWithLink() - { - doTest("component_with_link.json"); - } - - void test_parseComponentWithMixed() - { - doTest("component_with_mixed.json"); - } + private slots: + void test_parseComponentBasic() { doTest("component_basic.json"); } + void test_parseComponentWithFormat() { doTest("component_with_format.json"); } + void test_parseComponentWithExtra() { doTest("component_with_extra.json"); } + void test_parseComponentWithLink() { doTest("component_with_link.json"); } + void test_parseComponentWithMixed() { doTest("component_with_mixed.json"); } }; QTEST_GUILESS_MAIN(MetaComponentParseTest) From e1dda6c005dab379a5b93a2a9279205895b71060 Mon Sep 17 00:00:00 2001 From: cullvox Date: Tue, 12 Sep 2023 21:50:33 -0400 Subject: [PATCH 14/20] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: ddf0c28b1b0b6617b0308405b4d5eefd20ff48b2 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 8e209a416..5e53244ff 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -333,7 +333,6 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) if (not processComponent(desc_val, desc)) return false; - qInfo() << desc; pack.setDescription(desc); } catch (Json::JsonException& e) { From ee7016fa542e3ca92bdd691bd2e168d9d43a02c2 Mon Sep 17 00:00:00 2001 From: cullvox Date: Wed, 13 Sep 2023 14:28:56 -0400 Subject: [PATCH 15/20] use clang-format Signed-off-by: cullvox Hello, Component!" + ], + "expected_output": "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_format.json b/tests/testdata/MetaComponentParse/component_with_format.json index 000de20cb..00dfc7daf 100644 --- a/tests/testdata/MetaComponentParse/component_with_format.json +++ b/tests/testdata/MetaComponentParse/component_with_format.json @@ -1,13 +1,13 @@ { - "description": [ - { - "text": "Hello, Component!", - "color": "blue", - "bold": true, - "italic": true, - "underlined": true, - "strikethrough": true - } - ], - "expected_output": "Hello, Component!" + "description": [ + { + "text": "Hello, Component!", + "color": "blue", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true + } + ], + "expected_output": "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_link.json b/tests/testdata/MetaComponentParse/component_with_link.json index e190cc5e8..b1e34c7d6 100644 --- a/tests/testdata/MetaComponentParse/component_with_link.json +++ b/tests/testdata/MetaComponentParse/component_with_link.json @@ -1,12 +1,12 @@ { - "description": [ - { - "text": "Hello, Component!", - "clickEvent": { - "open_url": true, - "value": "https://google.com" - } - } - ], - "expected_output": "Hello, Component!" + "description": [ + { + "text": "Hello, Component!", + "clickEvent": { + "open_url": true, + "value": "https://google.com" + } + } + ], + "expected_output": "Hello, Component!" } diff --git a/tests/testdata/MetaComponentParse/component_with_mixed.json b/tests/testdata/MetaComponentParse/component_with_mixed.json index 7e65169af..7c8c5b032 100644 --- a/tests/testdata/MetaComponentParse/component_with_mixed.json +++ b/tests/testdata/MetaComponentParse/component_with_mixed.json @@ -1,40 +1,41 @@ { - "description": [ - { - "text": "The quick ", - "color": "blue", - "italic": true - }, - { - "text": "brown fox ", - "color": "#873600", - "bold": true, - "underlined": true, - "extra": { - "text": "jumped over ", - "color": "blue", - "bold": false, - "underlined": false, - "italic": true, - "strikethrough": true - } - }, - { - "text": "the lazy dog's back. ", - "color": "green", - "bold": true, - "italic": true, - "underlined": true, - "strikethrough": true, - "extra": [ + "description": [ { - "text": "1234567890 ", - "color": "black", - "strikethrough": false, - "extra": "How vexingly quick daft zebras jump!" + "text": "The quick ", + "color": "blue", + "italic": true + }, + { + "text": "brown fox ", + "color": "#873600", + "bold": true, + "underlined": true, + "extra": { + "text": "jumped over ", + "color": "blue", + "bold": false, + "underlined": false, + "italic": true, + "strikethrough": true + } + }, + { + "text": "the lazy dog's back. ", + "color": "green", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true, + "extra": [ + { + "text": "1234567890 ", + "color": "black", + "strikethrough": false, + "extra": "How vexingly quick daft zebras jump!" + } + ] } - ] - } - ], - "expected_output": "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" + ], + "expected_output": + "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" } From e96d0b6cafb0a0c17e7f5baef1bfbafe4a7fcb07 Mon Sep 17 00:00:00 2001 From: cullvox Date: Wed, 13 Sep 2023 14:31:04 -0400 Subject: [PATCH 16/20] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: ee7016fa542e3ca92bdd691bd2e168d9d43a02c2 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 5e53244ff..fd28aa0b7 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -272,7 +272,6 @@ struct TextFormatter { bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat) { TextFormatter formatter; - if (parentFormat) formatter = *parentFormat; From c81689d39322d5ea5c7ed0073d73e4d8d2446b33 Mon Sep 17 00:00:00 2001 From: cullvox Date: Fri, 15 Sep 2023 20:41:21 -0400 Subject: [PATCH 17/20] fixes html elide issue with InfoFrame --- launcher/ui/widgets/InfoFrame.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 1f03f9eaf..0162b4000 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include "InfoFrame.h" #include "ui_InfoFrame.h" @@ -274,12 +275,31 @@ void InfoFrame::setDescription(QString text) } QString labeltext; labeltext.reserve(300); - if (finaltext.length() > 290) { + + + // elide rich text by getting characters without formatting + const int maxCharacterElide = 290; + QTextDocument doc; + doc.setHtml(text); + + if (doc.characterCount() > maxCharacterElide) { + ui->descriptionLabel->setOpenExternalLinks(false); - ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); + ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. m_description = text; - // This allows injecting HTML here. - labeltext.append("" + finaltext.left(287) + "..."); + + const QString elidedPostfix = "..."; + + // move the cursor to the character elide, doesn't see html + QTextCursor cursor(&doc); + cursor.movePosition(QTextCursor::End); + cursor.setPosition(maxCharacterElide, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + + // insert the post fix at the cursor + cursor.insertHtml(elidedPostfix); + + labeltext.append(doc.toHtml()); QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler); } else { ui->descriptionLabel->setTextFormat(Qt::TextFormat::AutoText); From 4053229544ca2d80cd569f4d67365f81ed64353d Mon Sep 17 00:00:00 2001 From: cullvox Date: Fri, 15 Sep 2023 20:55:34 -0400 Subject: [PATCH 18/20] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: c81689d39322d5ea5c7ed0073d73e4d8d2446b33 Signed-off-by: cullvox --- launcher/ui/widgets/InfoFrame.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 0162b4000..6423e88d6 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -36,8 +36,9 @@ #include #include -#include #include +#include +#include #include "InfoFrame.h" #include "ui_InfoFrame.h" @@ -276,16 +277,14 @@ void InfoFrame::setDescription(QString text) QString labeltext; labeltext.reserve(300); - // elide rich text by getting characters without formatting const int maxCharacterElide = 290; QTextDocument doc; doc.setHtml(text); if (doc.characterCount() > maxCharacterElide) { - ui->descriptionLabel->setOpenExternalLinks(false); - ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. + ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. m_description = text; const QString elidedPostfix = "..."; @@ -298,7 +297,7 @@ void InfoFrame::setDescription(QString text) // insert the post fix at the cursor cursor.insertHtml(elidedPostfix); - + labeltext.append(doc.toHtml()); QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler); } else { From 01e98a6ce810ed1e8169d2252dca5cec39f8d335 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 16 Sep 2023 10:20:24 +0300 Subject: [PATCH 19/20] simplify the raw json parsing Signed-off-by: Trial97 Fixed Tests Signed-off-by: Trial97 --- .../mod/tasks/LocalResourcePackParseTask.cpp | 214 +++++++----------- .../mod/tasks/LocalResourcePackParseTask.h | 3 +- launcher/ui/widgets/InfoFrame.cpp | 6 +- tests/MetaComponentParse_test.cpp | 10 +- .../component_with_extra.json | 7 +- .../component_with_format.json | 2 +- .../component_with_link.json | 2 +- .../component_with_mixed.json | 24 +- 8 files changed, 104 insertions(+), 164 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index fd28aa0b7..d9d26b9c2 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -179,142 +179,85 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -struct TextFormatter { - // left is value, right is if the value was explicitly written - QPair color = { "#000000", false }; - QPair bold = { false, false }; - QPair italic = { false, false }; - QPair underlined = { false, false }; - QPair strikethrough = { false, false }; - QPair is_linked = { false, false }; - QPair link_url = { "", false }; - - void setColor(const QString& new_color, bool written) { color = { new_color, written }; } - void setBold(bool new_bold, bool written) { bold = { new_bold, written }; } - void setItalic(bool new_italic, bool written) { italic = { new_italic, written }; } - void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written }; } - void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written }; } - void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written }; } - void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written }; } - - void overrideFrom(const TextFormatter& child) - { - if (child.color.second) - color.first = child.color.first; - if (child.bold.second) - bold.first = child.bold.first; - if (child.italic.second) - italic.first = child.italic.first; - if (child.underlined.second) - underlined.first = child.underlined.first; - if (child.strikethrough.second) - strikethrough.first = child.strikethrough.first; - if (child.is_linked.second) - is_linked.first = child.is_linked.first; - if (child.link_url.second) - link_url.first = child.link_url.first; - } - - QString format(QString text) - { - if (text.isEmpty()) - return QString(); - - QString result; - - if (color.first != "#000000") - result.append(""); - if (bold.first) - result.append(""); - if (italic.first) - result.append(""); - if (underlined.first) - result.append(""); - if (strikethrough.first) - result.append(""); - if (is_linked.first) - result.append(""); - - result.append(text); - - if (is_linked.first) - result.append(""); - if (strikethrough.first) - result.append(""); - if (underlined.first) - result.append(""); - if (italic.first) - result.append(""); - if (bold.first) - result.append(""); - if (color.first != "#000000") - result.append(""); - - return result; - } - - bool readFormat(const QJsonObject& obj) - { - setColor(Json::ensureString(obj, "color", "#000000"), obj.contains("color")); - setBold(Json::ensureBoolean(obj, "bold", false), obj.contains("bold")); - setItalic(Json::ensureBoolean(obj, "italic", false), obj.contains("italic")); - setUnderlined(Json::ensureBoolean(obj, "underlined", false), obj.contains("underlined")); - setStrikethrough(Json::ensureBoolean(obj, "strikethrough", false), obj.contains("strikethrough")); - - auto click_event = Json::ensureObject(obj, "clickEvent"); - setIsLinked(Json::ensureBoolean(click_event, "open_url", false), click_event.contains("open_url")); - setLinkURL(Json::ensureString(click_event, "value"), click_event.contains("value")); - - return true; - } -}; - -bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat) +QString buildStyle(const QJsonObject& obj) { - TextFormatter formatter; - if (parentFormat) - formatter = *parentFormat; - - if (value.isString()) { - result.append(formatter.format(value.toString())); - } else if (value.isBool()) { - result.append(formatter.format(value.toBool() ? "true" : "false")); - } else if (value.isDouble()) { - result.append(formatter.format(QString::number(value.toDouble()))); - } else if (value.isObject()) { - auto obj = value.toObject(); - - if (not formatter.readFormat(obj)) - return false; - - // override the parent format with our new one - TextFormatter mixed; - if (parentFormat) - mixed = *parentFormat; - - mixed.overrideFrom(formatter); - - result.append(mixed.format(Json::ensureString(obj, "text"))); - - // process any 'extra' children with this format - auto extra = obj.value("extra"); - if (not extra.isUndefined()) - return processComponent(extra, result, &mixed); - - } else if (value.isArray()) { - auto array = value.toArray(); - - for (const QJsonValue& current : array) { - if (not processComponent(current, result, parentFormat)) { - return false; - } + QStringList styles; + if (auto color = Json::ensureString(obj, "color"); !color.isEmpty()) { + styles << QString("color: %1;").arg(color); + } + if (obj.contains("bold")) { + QString weight = "normal"; + if (Json::ensureBoolean(obj, "bold", false)) { + weight = "bold"; } - } else { - qWarning() << "Invalid component type!"; - return false; + styles << QString("font-weight: %1;").arg(weight); + } + if (obj.contains("italic")) { + QString style = "normal"; + if (Json::ensureBoolean(obj, "italic", false)) { + style = "italic"; + } + styles << QString("font-style: %1;").arg(style); } - return true; + return styles.isEmpty() ? "" : QString("style=\"%1\"").arg(styles.join(" ")); +} + +QString processComponent(const QJsonArray& value, bool strikethrough, bool underline) +{ + QString result; + for (auto current : value) + result += processComponent(current, strikethrough, underline); + return result; +} + +QString processComponent(const QJsonObject& obj, bool strikethrough, bool underline) +{ + underline = Json::ensureBoolean(obj, "underlined", underline); + strikethrough = Json::ensureBoolean(obj, "strikethrough", strikethrough); + + QString result = Json::ensureString(obj, "text"); + if (underline) { + result = QString("%1").arg(result); + } + if (strikethrough) { + result = QString("%1").arg(result); + } + // the extra needs to be a array + result += processComponent(Json::ensureArray(obj, "extra"), strikethrough, underline); + if (auto style = buildStyle(obj); !style.isEmpty()) { + result = QString("%2").arg(style, result); + } + if (obj.contains("clickEvent")) { + auto click_event = Json::ensureObject(obj, "clickEvent"); + auto action = Json::ensureString(click_event, "action"); + auto value = Json::ensureString(click_event, "value"); + if (action == "open_url" && !value.isEmpty()) { + result = QString("%2").arg(value, result); + } + } + return result; +} + +QString processComponent(const QJsonValue& value, bool strikethrough, bool underline) +{ + if (value.isString()) { + return value.toString(); + } + if (value.isBool()) { + return value.toBool() ? "true" : "false"; + } + if (value.isDouble()) { + return QString::number(value.toDouble()); + } + if (value.isArray()) { + return processComponent(value.toArray(), strikethrough, underline); + } + if (value.isObject()) { + return processComponent(value.toObject(), strikethrough, underline); + } + qWarning() << "Invalid component type!"; + return {}; } // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta @@ -327,12 +270,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - auto desc_val = pack_obj.value("description"); - QString desc{}; - if (not processComponent(desc_val, desc)) - return false; - - pack.setDescription(desc); + pack.setDescription(processComponent(pack_obj.value("description"))); } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 945499a91..97bf7b2ba 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -34,8 +34,7 @@ bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); -struct TextFormatter; -bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat = nullptr); +QString processComponent(const QJsonValue& value, bool strikethrough = false, bool underline = false); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data); diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 6423e88d6..2cf3e7a93 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -287,8 +287,6 @@ void InfoFrame::setDescription(QString text) ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. m_description = text; - const QString elidedPostfix = "..."; - // move the cursor to the character elide, doesn't see html QTextCursor cursor(&doc); cursor.movePosition(QTextCursor::End); @@ -296,7 +294,7 @@ void InfoFrame::setDescription(QString text) cursor.removeSelectedText(); // insert the post fix at the cursor - cursor.insertHtml(elidedPostfix); + cursor.insertHtml("..."); labeltext.append(doc.toHtml()); QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler); @@ -335,7 +333,7 @@ void InfoFrame::setLicense(QString text) if (finaltext.length() > 290) { ui->licenseLabel->setOpenExternalLinks(false); ui->licenseLabel->setTextFormat(Qt::TextFormat::RichText); - m_description = text; + m_license = text; // This allows injecting HTML here. labeltext.append("" + finaltext.left(287) + "..."); QObject::connect(ui->licenseLabel, &QLabel::linkActivated, this, &InfoFrame::licenseEllipsisHandler); diff --git a/tests/MetaComponentParse_test.cpp b/tests/MetaComponentParse_test.cpp index 1b1e2ce3e..9979a9fa6 100644 --- a/tests/MetaComponentParse_test.cpp +++ b/tests/MetaComponentParse_test.cpp @@ -64,16 +64,14 @@ class MetaComponentParseTest : public QObject { QJsonValue description_json = obj.value("description"); QJsonValue expected_json = obj.value("expected_output"); - QVERIFY(description_json.isUndefined() == false); - QVERIFY(expected_json.isString() == true); + QVERIFY(!description_json.isUndefined()); + QVERIFY(expected_json.isString()); QString expected = expected_json.toString(); - QString processed; - bool valid = ResourcePackUtils::processComponent(description_json, processed); + QString processed = ResourcePackUtils::processComponent(description_json); - QVERIFY(processed == expected); - QVERIFY(valid == true); + QCOMPARE(processed, expected); } private slots: diff --git a/tests/testdata/MetaComponentParse/component_with_extra.json b/tests/testdata/MetaComponentParse/component_with_extra.json index e26b2abff..887becdbe 100644 --- a/tests/testdata/MetaComponentParse/component_with_extra.json +++ b/tests/testdata/MetaComponentParse/component_with_extra.json @@ -7,12 +7,15 @@ "italic": true, "extra": [ { - "extra": "Component!", + "extra": [ + "Component!" + ], "bold": false, "italic": false } ] } ], - "expected_output": "Hello, Component!" + "expected_output": + "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_format.json b/tests/testdata/MetaComponentParse/component_with_format.json index 00dfc7daf..1078886a6 100644 --- a/tests/testdata/MetaComponentParse/component_with_format.json +++ b/tests/testdata/MetaComponentParse/component_with_format.json @@ -9,5 +9,5 @@ "strikethrough": true } ], - "expected_output": "Hello, Component!" + "expected_output": "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_link.json b/tests/testdata/MetaComponentParse/component_with_link.json index b1e34c7d6..188c004cd 100644 --- a/tests/testdata/MetaComponentParse/component_with_link.json +++ b/tests/testdata/MetaComponentParse/component_with_link.json @@ -3,7 +3,7 @@ { "text": "Hello, Component!", "clickEvent": { - "open_url": true, + "action": "open_url", "value": "https://google.com" } } diff --git a/tests/testdata/MetaComponentParse/component_with_mixed.json b/tests/testdata/MetaComponentParse/component_with_mixed.json index 7c8c5b032..661fc1a3e 100644 --- a/tests/testdata/MetaComponentParse/component_with_mixed.json +++ b/tests/testdata/MetaComponentParse/component_with_mixed.json @@ -10,14 +10,16 @@ "color": "#873600", "bold": true, "underlined": true, - "extra": { - "text": "jumped over ", - "color": "blue", - "bold": false, - "underlined": false, - "italic": true, - "strikethrough": true - } + "extra": [ + { + "text": "jumped over ", + "color": "blue", + "bold": false, + "underlined": false, + "italic": true, + "strikethrough": true + } + ] }, { "text": "the lazy dog's back. ", @@ -31,11 +33,13 @@ "text": "1234567890 ", "color": "black", "strikethrough": false, - "extra": "How vexingly quick daft zebras jump!" + "extra": [ + "How vexingly quick daft zebras jump!" + ] } ] } ], "expected_output": - "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" + "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" } From 0e41ceffc4a2b8842e44a8bd1dc027944ce27471 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 16 Sep 2023 19:18:58 +0300 Subject: [PATCH 20/20] removed missed header Signed-off-by: Trial97 --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index d9d26b9c2..c5d899123 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -26,7 +26,6 @@ #include #include -#include namespace ResourcePackUtils {