[DeckList] Extract deck-hash encoding helpers (#7321)

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-09-19 10:35:04 +02:00 committed by GitHub
parent c45cb8ac32
commit dce9efcaa3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -7,6 +7,47 @@
static constexpr int MAX_DECK_SIZE = 1e5;
namespace
{
/**
* @brief Expands card nodes into one lowercase name entry per copy.
*
* @param nodes The card nodes to expand.
* @param prefix Optional prefix prepended to every entry (e.g. "SB:" for the sideboard).
* @return One entry per copy, in node order.
*/
QStringList cardNodesToCopies(const QList<const DecklistCardNode *> &nodes, const QString &prefix = {})
{
QStringList result;
for (auto node : nodes) {
for (int i = 0; i < node->getNumber(); ++i) {
result.append(prefix + node->getName().toLower());
}
}
return result;
}
/**
* @brief Packs the first five bytes of a SHA-1 digest into a compact base-32 number.
*
* The bytes are placed in most-significant-byte-first order (byte 0 shifted by 32,
* byte 4 unshifted) so decks that only differ in their low-order hash bytes still
* produce distinct identifiers.
*
* @return The 8-character base-32 representation of the packed number.
*/
QString encodeDeckHash(const QByteArray &digest)
{
quint64 number = 0;
for (int i = 0; i < 5; ++i) {
number |= static_cast<quint64>(static_cast<unsigned char>(digest[i])) << (32 - 8 * i);
}
return QString::number(number, 32).rightJustified(8, '0');
}
} // namespace
DecklistNodeTree::DecklistNodeTree() : root(new InnerDecklistNode())
{
}
@ -83,25 +124,11 @@ QString DecklistNodeTree::computeDeckHash() const
auto mainDeckNodes = getCardNodes({DECK_ZONE_MAIN});
auto sideDeckNodes = getCardNodes({DECK_ZONE_SIDE});
static auto nodesToCardList = [](const QList<const DecklistCardNode *> &nodes, const QString &prefix = {}) {
QStringList result;
for (auto node : nodes) {
for (int i = 0; i < node->getNumber(); ++i) {
result.append(prefix + node->getName().toLower());
}
}
return result;
};
QStringList cardList = nodesToCardList(mainDeckNodes) + nodesToCardList(sideDeckNodes, "SB:");
QStringList cardList = cardNodesToCopies(mainDeckNodes) + cardNodesToCopies(sideDeckNodes, "SB:");
cardList.sort();
QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1);
quint64 number = (((quint64)(unsigned char)deckHashArray[0]) << 32) +
(((quint64)(unsigned char)deckHashArray[1]) << 24) +
(((quint64)(unsigned char)deckHashArray[2] << 16)) +
(((quint64)(unsigned char)deckHashArray[3]) << 8) + (quint64)(unsigned char)deckHashArray[4];
return QString::number(number, 32).rightJustified(8, '0');
return encodeDeckHash(deckHashArray);
}
void DecklistNodeTree::write(QXmlStreamWriter *xml) const