diff --git a/cockatrice/src/carddbparser/carddatabaseparser.h b/cockatrice/src/carddbparser/carddatabaseparser.h index a353db4c..5b232123 100644 --- a/cockatrice/src/carddbparser/carddatabaseparser.h +++ b/cockatrice/src/carddbparser/carddatabaseparser.h @@ -6,6 +6,8 @@ #include #include +#define COCKATRICE_XML_XSI_NAMESPACE "http://www.w3.org/2001/XMLSchema-instance" + class ICardDatabaseParser : public QObject { public: @@ -13,7 +15,11 @@ public: virtual bool getCanParseFile(const QString &name, QIODevice &device) = 0; virtual void parseFile(QIODevice &device) = 0; - virtual bool saveToFile(SetNameMap sets, CardNameMap cards, const QString &fileName) = 0; + virtual bool saveToFile(SetNameMap sets, + CardNameMap cards, + const QString &fileName, + const QString &sourceUrl = "unknown", + const QString &sourceVersion = "unknown") = 0; static void clearSetlist(); protected: diff --git a/cockatrice/src/carddbparser/cockatricexml3.cpp b/cockatrice/src/carddbparser/cockatricexml3.cpp index fae11637..5567426c 100644 --- a/cockatrice/src/carddbparser/cockatricexml3.cpp +++ b/cockatrice/src/carddbparser/cockatricexml3.cpp @@ -1,11 +1,15 @@ #include "cockatricexml3.h" +#include #include #include #include +#include #define COCKATRICE_XML3_TAGNAME "cockatrice_carddatabase" #define COCKATRICE_XML3_TAGVER 3 +#define COCKATRICE_XML3_SCHEMALOCATION \ + "https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v3/cards.xsd" bool CockatriceXml3Parser::getCanParseFile(const QString &fileName, QIODevice &device) { @@ -403,7 +407,11 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in return xml; } -bool CockatriceXml3Parser::saveToFile(SetNameMap sets, CardNameMap cards, const QString &fileName) +bool CockatriceXml3Parser::saveToFile(SetNameMap sets, + CardNameMap cards, + const QString &fileName, + const QString &sourceUrl, + const QString &sourceVersion) { QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { @@ -416,6 +424,15 @@ bool CockatriceXml3Parser::saveToFile(SetNameMap sets, CardNameMap cards, const xml.writeStartDocument(); xml.writeStartElement(COCKATRICE_XML3_TAGNAME); xml.writeAttribute("version", QString::number(COCKATRICE_XML3_TAGVER)); + xml.writeAttribute("xmlns:xsi", COCKATRICE_XML_XSI_NAMESPACE); + xml.writeAttribute("xsi:schemaLocation", COCKATRICE_XML3_SCHEMALOCATION); + + xml.writeStartElement("info"); + xml.writeTextElement("author", QCoreApplication::applicationName() + QString(" %1").arg(VERSION_STRING)); + xml.writeTextElement("createdAt", QDateTime::currentDateTimeUtc().toString(Qt::ISODate)); + xml.writeTextElement("sourceUrl", sourceUrl); + xml.writeTextElement("sourceVersion", sourceVersion); + xml.writeEndElement(); if (sets.count() > 0) { xml.writeStartElement("sets"); diff --git a/cockatrice/src/carddbparser/cockatricexml3.h b/cockatrice/src/carddbparser/cockatricexml3.h index 1d4b6c8f..b8330c88 100644 --- a/cockatrice/src/carddbparser/cockatricexml3.h +++ b/cockatrice/src/carddbparser/cockatricexml3.h @@ -14,7 +14,11 @@ public: ~CockatriceXml3Parser() override = default; bool getCanParseFile(const QString &name, QIODevice &device) override; void parseFile(QIODevice &device) override; - bool saveToFile(SetNameMap sets, CardNameMap cards, const QString &fileName) override; + bool saveToFile(SetNameMap sets, + CardNameMap cards, + const QString &fileName, + const QString &sourceUrl = "unknown", + const QString &sourceVersion = "unknown") override; private: void loadCardsFromXml(QXmlStreamReader &xml); diff --git a/cockatrice/src/carddbparser/cockatricexml4.cpp b/cockatrice/src/carddbparser/cockatricexml4.cpp index e8b2ba70..a4b89d6c 100644 --- a/cockatrice/src/carddbparser/cockatricexml4.cpp +++ b/cockatrice/src/carddbparser/cockatricexml4.cpp @@ -1,11 +1,15 @@ #include "cockatricexml4.h" +#include #include #include #include +#include #define COCKATRICE_XML4_TAGNAME "cockatrice_carddatabase" #define COCKATRICE_XML4_TAGVER 4 +#define COCKATRICE_XML4_SCHEMALOCATION \ + "https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v4/cards.xsd" bool CockatriceXml4Parser::getCanParseFile(const QString &fileName, QIODevice &device) { @@ -329,7 +333,11 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in return xml; } -bool CockatriceXml4Parser::saveToFile(SetNameMap sets, CardNameMap cards, const QString &fileName) +bool CockatriceXml4Parser::saveToFile(SetNameMap sets, + CardNameMap cards, + const QString &fileName, + const QString &sourceUrl, + const QString &sourceVersion) { QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { @@ -342,6 +350,15 @@ bool CockatriceXml4Parser::saveToFile(SetNameMap sets, CardNameMap cards, const xml.writeStartDocument(); xml.writeStartElement(COCKATRICE_XML4_TAGNAME); xml.writeAttribute("version", QString::number(COCKATRICE_XML4_TAGVER)); + xml.writeAttribute("xmlns:xsi", COCKATRICE_XML_XSI_NAMESPACE); + xml.writeAttribute("xsi:schemaLocation", COCKATRICE_XML4_SCHEMALOCATION); + + xml.writeStartElement("info"); + xml.writeTextElement("author", QCoreApplication::applicationName() + QString(" %1").arg(VERSION_STRING)); + xml.writeTextElement("createdAt", QDateTime::currentDateTimeUtc().toString(Qt::ISODate)); + xml.writeTextElement("sourceUrl", sourceUrl); + xml.writeTextElement("sourceVersion", sourceVersion); + xml.writeEndElement(); if (sets.count() > 0) { xml.writeStartElement("sets"); diff --git a/cockatrice/src/carddbparser/cockatricexml4.h b/cockatrice/src/carddbparser/cockatricexml4.h index 11f9ac59..d08ba872 100644 --- a/cockatrice/src/carddbparser/cockatricexml4.h +++ b/cockatrice/src/carddbparser/cockatricexml4.h @@ -14,7 +14,11 @@ public: ~CockatriceXml4Parser() override = default; bool getCanParseFile(const QString &name, QIODevice &device) override; void parseFile(QIODevice &device) override; - bool saveToFile(SetNameMap sets, CardNameMap cards, const QString &fileName) override; + bool saveToFile(SetNameMap sets, + CardNameMap cards, + const QString &fileName, + const QString &sourceUrl = "unknown", + const QString &sourceVersion = "unknown") override; private: QVariantHash loadCardPropertiesFromXml(QXmlStreamReader &xml); diff --git a/doc/carddatabase_v3/cards.xsd b/doc/carddatabase_v3/cards.xsd index c3e228b4..fe536428 100644 --- a/doc/carddatabase_v3/cards.xsd +++ b/doc/carddatabase_v3/cards.xsd @@ -11,6 +11,16 @@ + + + + + + + + + + diff --git a/doc/carddatabase_v4/cards.xsd b/doc/carddatabase_v4/cards.xsd index 18f43e44..62c2f2a8 100644 --- a/doc/carddatabase_v4/cards.xsd +++ b/doc/carddatabase_v4/cards.xsd @@ -57,6 +57,16 @@ + + + + + + + + + + diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index aa5081eb..658d688f 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -459,10 +459,10 @@ int OracleImporter::startImport() return setIndex; } -bool OracleImporter::saveToFile(const QString &fileName) +bool OracleImporter::saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion) { CockatriceXml4Parser parser; - return parser.saveToFile(sets, cards, fileName); + return parser.saveToFile(sets, cards, fileName, sourceUrl, sourceVersion); } void OracleImporter::clear() diff --git a/oracle/src/oracleimporter.h b/oracle/src/oracleimporter.h index 4a46f018..5d20af75 100644 --- a/oracle/src/oracleimporter.h +++ b/oracle/src/oracleimporter.h @@ -110,7 +110,7 @@ public: explicit OracleImporter(const QString &_dataDir, QObject *parent = nullptr); bool readSetsFromByteArray(const QByteArray &data); int startImport(); - bool saveToFile(const QString &fileName); + bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion); int importCardsFromSet(CardSetPtr currentSet, const QList &cards, bool skipSpecialNums = true); QList &getSets() { diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index e4c10a1d..f2407a6b 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -39,6 +39,7 @@ // Xz stream header: 0xFD + "7zXZ" #define XZ_SIGNATURE "\xFD\x37\x7A\x58\x5A" #define ALLSETS_URL_FALLBACK "https://www.mtgjson.com/files/AllPrintings.json" +#define MTGJSON_VERSION_URL "https://www.mtgjson.com/files/version.json" #ifdef HAS_LZMA #define ALLSETS_URL "https://www.mtgjson.com/files/AllPrintings.json.xz" @@ -331,14 +332,46 @@ bool LoadSetsPage::validatePage() wizard()->disableButtons(); setEnabled(false); + wizard()->setCardSourceUrl(setsFile.fileName()); + wizard()->setCardSourceVersion("unknown"); + readSetsFromByteArray(setsFile.readAll()); } return false; } +#include void LoadSetsPage::downloadSetsFile(QUrl url) { + wizard()->setCardSourceVersion("unknown"); + + QString urlString = url.toString(); + if (urlString == ALLSETS_URL || urlString == ALLSETS_URL_FALLBACK) { + QUrl versionUrl = QUrl::fromUserInput(MTGJSON_VERSION_URL); + QNetworkReply *versionReply = wizard()->nam->get(QNetworkRequest(versionUrl)); + connect(versionReply, &QNetworkReply::finished, [this, versionReply]() { + if (versionReply->error() == QNetworkReply::NoError) { + QByteArray jsonData = versionReply->readAll(); + QJsonParseError jsonError; + QJsonDocument jsonResponse = QJsonDocument::fromJson(jsonData, &jsonError); + + if (jsonError.error == QJsonParseError::NoError) { + QVariantMap jsonMap = jsonResponse.toVariant().toMap(); + QString versionString = jsonMap["version"].toString(); + if (versionString.isEmpty()) { + versionString = "unknown"; + } + wizard()->setCardSourceVersion(versionString); + } + } + + versionReply->deleteLater(); + }); + } + + wizard()->setCardSourceUrl(url.toString()); + QNetworkReply *reply = wizard()->nam->get(QNetworkRequest(url)); connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSetsFile())); @@ -597,7 +630,7 @@ bool SaveSetsPage::validatePage() return false; } - if (wizard()->importer->saveToFile(fileName)) { + if (wizard()->importer->saveToFile(fileName, wizard()->getCardSourceUrl(), wizard()->getCardSourceVersion())) { ok = true; } else { QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName)); diff --git a/oracle/src/oraclewizard.h b/oracle/src/oraclewizard.h index 89a90495..28f0d2e0 100644 --- a/oracle/src/oraclewizard.h +++ b/oracle/src/oraclewizard.h @@ -38,6 +38,22 @@ public: { return !tokensData.isEmpty(); } + void setCardSourceUrl(const QString &sourceUrl) + { + cardSourceUrl = sourceUrl; + } + void setCardSourceVersion(const QString &sourceVersion) + { + cardSourceVersion = sourceVersion; + } + const QString &getCardSourceUrl() const + { + return cardSourceUrl; + } + const QString &getCardSourceVersion() const + { + return cardSourceVersion; + } bool saveTokensToFile(const QString &fileName); public: @@ -50,6 +66,8 @@ private slots: private: QByteArray tokensData; + QString cardSourceUrl; + QString cardSourceVersion; protected: void changeEvent(QEvent *event) override; diff --git a/tests/carddatabase/CMakeLists.txt b/tests/carddatabase/CMakeLists.txt index 4ac15295..8d8d255e 100644 --- a/tests/carddatabase/CMakeLists.txt +++ b/tests/carddatabase/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(carddatabase_test ../../cockatrice/src/carddbparser/carddatabaseparser.cpp ../../cockatrice/src/carddbparser/cockatricexml3.cpp ../../cockatrice/src/carddbparser/cockatricexml4.cpp + ${VERSION_STRING_CPP} ) add_executable(filter_string_test filter_string_test.cpp @@ -17,6 +18,7 @@ add_executable(filter_string_test ../../cockatrice/src/carddbparser/carddatabaseparser.cpp ../../cockatrice/src/carddbparser/cockatricexml3.cpp ../../cockatrice/src/carddbparser/cockatricexml4.cpp + ${VERSION_STRING_CPP} ) if(NOT GTEST_FOUND) add_dependencies(carddatabase_test gtest) diff --git a/tests/carddatabase/filter_string_test.cpp b/tests/carddatabase/filter_string_test.cpp index ce6dbb6c..823ccc6c 100644 --- a/tests/carddatabase/filter_string_test.cpp +++ b/tests/carddatabase/filter_string_test.cpp @@ -1,5 +1,5 @@ #include "gtest/gtest.h" -#include "../cockatrice/src/filter_string.h" +#include "../../cockatrice/src/filter_string.h" #include "mocks.h" #include