From 7782b35f6e3e05152f5fdd35fb51de81cdea2a74 Mon Sep 17 00:00:00 2001 From: RickyRister Date: Wed, 4 Mar 2026 16:46:21 -0800 Subject: [PATCH] [DeckList] Refactor load from plaintext to take normalizer as param --- cockatrice/CMakeLists.txt | 1 + .../deck_loader/card_name_normalizer.cpp | 45 +++++++++++++++++++ .../deck_loader/card_name_normalizer.h | 15 +++++++ .../libcockatrice/deck_list/deck_list.cpp | 43 ++++-------------- .../libcockatrice/deck_list/deck_list.h | 6 ++- 5 files changed, 73 insertions(+), 37 deletions(-) create mode 100644 cockatrice/src/interface/deck_loader/card_name_normalizer.cpp create mode 100644 cockatrice/src/interface/deck_loader/card_name_normalizer.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index d675b809d..c7d136950 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -19,6 +19,7 @@ set(cockatrice_SOURCES src/client/settings/card_counter_settings.cpp src/client/settings/shortcut_treeview.cpp src/client/settings/shortcuts_settings.cpp + src/interface/deck_loader/card_name_normalizer.cpp src/interface/deck_loader/card_node_function.cpp src/interface/deck_loader/deck_file_format.cpp src/interface/deck_loader/deck_loader.cpp diff --git a/cockatrice/src/interface/deck_loader/card_name_normalizer.cpp b/cockatrice/src/interface/deck_loader/card_name_normalizer.cpp new file mode 100644 index 000000000..216d15ce4 --- /dev/null +++ b/cockatrice/src/interface/deck_loader/card_name_normalizer.cpp @@ -0,0 +1,45 @@ +#include "card_name_normalizer.h" + +#include "libcockatrice/card/database/card_database_manager.h" + +QString CardNameNormalizer::operator()(const QString &cardNameString) const +{ + QString cardName = cardNameString; + + // Regex for advanced card parsing + static const QRegularExpression reSplitCard(R"( ?\/\/ ?)"); + static const QRegularExpression reBrace(R"( ?[\[\{][^\]\}]*[\]\}] ?)"); // not nested + static const QRegularExpression reRoundBrace(R"(^\([^\)]*\) ?)"); // () are only matched at start of string + static const QRegularExpression reDigitBrace(R"( ?\(\d*\) ?)"); // () are matched if containing digits + static const QRegularExpression reBraceDigit( + R"( ?\([\dA-Z]+\) *\d+$)"); // () are matched if containing setcode then a number + static const QRegularExpression reDoubleFacedMarker(R"( ?\(Transform\) ?)"); + + static const QHash differences{{QRegularExpression("’"), "'"}, + {QRegularExpression("Æ"), "Ae"}, + {QRegularExpression("æ"), "ae"}, + {QRegularExpression(" ?[|/]+ ?"), " // "}}; + + // Handle advanced card types + if (cardName.contains(reSplitCard)) { + cardName = cardName.split(reSplitCard).join(" // "); + } + + if (cardName.contains(reDoubleFacedMarker)) { + QStringList faces = cardName.split(reDoubleFacedMarker); + cardName = faces.first().trimmed(); + } + + // Remove unnecessary characters + cardName.remove(reBrace); + cardName.remove(reRoundBrace); // I'll be entirely honest here, these are split to accommodate just three cards + cardName.remove(reDigitBrace); // from un-sets that have a word in between round braces at the end + cardName.remove(reBraceDigit); // very specific format with the set code in () and collectors number after + + // Normalize characters + for (auto diff = differences.constBegin(); diff != differences.constEnd(); ++diff) { + cardName.replace(diff.key(), diff.value()); + } + + return cardName; +} \ No newline at end of file diff --git a/cockatrice/src/interface/deck_loader/card_name_normalizer.h b/cockatrice/src/interface/deck_loader/card_name_normalizer.h new file mode 100644 index 000000000..716e7da80 --- /dev/null +++ b/cockatrice/src/interface/deck_loader/card_name_normalizer.h @@ -0,0 +1,15 @@ +#ifndef COCKATRICE_CARD_NAME_NORMALIZER_H +#define COCKATRICE_CARD_NAME_NORMALIZER_H + +#include + +/** + * Functor that normalizes the raw card name parsed during a plaintext deck import into the card name that Cockatrice + * uses. + */ +struct CardNameNormalizer +{ + QString operator()(const QString &cardNameString) const; +}; + +#endif // COCKATRICE_CARD_NAME_NORMALIZER_H diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp index 3d1070f15..e3e7b41c0 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp @@ -199,9 +199,12 @@ bool DeckList::saveToFile_Native(QIODevice *device) const * * @param in The text to load * @param preserveMetadata If true, don't clear the existing metadata + * @param cardNameNormalizer Function that takes the parsed card name string in the text and * @return False if the input was empty, true otherwise. */ -bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) +bool DeckList::loadFromStream_Plain(QTextStream &in, + bool preserveMetadata, + const std::function &cardNameNormalizer) { const QRegularExpression reCardLine(R"(^\s*[\w\[\(\{].*$)", QRegularExpression::UseUnicodePropertiesOption); const QRegularExpression reEmpty("^\\s*$"); @@ -213,23 +216,11 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) // Regex for advanced card parsing const QRegularExpression reMultiplier(R"(^[xX\(\[]*(\d+)[xX\*\)\]]* ?(.+))"); - const QRegularExpression reSplitCard(R"( ?\/\/ ?)"); - const QRegularExpression reBrace(R"( ?[\[\{][^\]\}]*[\]\}] ?)"); // not nested - const QRegularExpression reRoundBrace(R"(^\([^\)]*\) ?)"); // () are only matched at start of string - const QRegularExpression reDigitBrace(R"( ?\(\d*\) ?)"); // () are matched if containing digits - const QRegularExpression reBraceDigit( - R"( ?\([\dA-Z]+\) *\d+$)"); // () are matched if containing setcode then a number - const QRegularExpression reDoubleFacedMarker(R"( ?\(Transform\) ?)"); // Regex for extracting set code and collector number with attached symbols const QRegularExpression reHyphenFormat(R"(\((\w{3,})\)\s+(\w{3,})-(\d+[^\w\s]*))"); const QRegularExpression reRegularFormat(R"(\((\w{3,})\)\s+(\d+[^\w\s]*))"); - const QHash differences{{QRegularExpression("’"), QString("'")}, - {QRegularExpression("Æ"), QString("Ae")}, - {QRegularExpression("æ"), QString("ae")}, - {QRegularExpression(" ?[|/]+ ?"), QString(" // ")}}; - cleanList(preserveMetadata); auto inputs = in.readAll().trimmed().split('\n'); @@ -355,26 +346,8 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) cardName = match.captured(2); } - // Handle advanced card types - if (cardName.contains(reSplitCard)) { - cardName = cardName.split(reSplitCard).join(" // "); - } - - if (cardName.contains(reDoubleFacedMarker)) { - QStringList faces = cardName.split(reDoubleFacedMarker); - cardName = faces.first().trimmed(); - } - - // Remove unnecessary characters - cardName.remove(reBrace); - cardName.remove(reRoundBrace); // I'll be entirely honest here, these are split to accommodate just three cards - cardName.remove(reDigitBrace); // from un-sets that have a word in between round braces at the end - cardName.remove(reBraceDigit); // very specific format with the set code in () and collectors number after - - // Normalize names - for (auto diff = differences.constBegin(); diff != differences.constEnd(); ++diff) { - cardName.replace(diff.key(), diff.value()); - } + // Normalize the card name + cardName = cardNameNormalizer(cardName); // Determine the zone (mainboard/sideboard) QString zoneName = sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; @@ -387,10 +360,10 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) return true; } -bool DeckList::loadFromFile_Plain(QIODevice *device) +bool DeckList::loadFromFile_Plain(QIODevice *device, const std::function &cardNameNormalizer) { QTextStream in(device); - return loadFromStream_Plain(in, false); + return loadFromStream_Plain(in, false, cardNameNormalizer); } bool DeckList::saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h index 91e56fc9f..a96adeb38 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h @@ -206,8 +206,10 @@ public: /// @name Serialization (Plain text) ///@{ - bool loadFromStream_Plain(QTextStream &stream, bool preserveMetadata); - bool loadFromFile_Plain(QIODevice *device); + bool loadFromStream_Plain(QTextStream &stream, + bool preserveMetadata, + const std::function &cardNameNormalizer); + bool loadFromFile_Plain(QIODevice *device, const std::function &cardNameNormalizer); bool saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const; bool saveToFile_Plain(QIODevice *device, bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false) const;