Rework of the card database, xml format and oracle parser (#3511)

* CardDB: merge all card properties in a new structure

* Pre Json parser changes

 * Cockatrice: use qt's builtin json support
 * Move qt-json src dir from cockatrice to oracle
 * Add dummy cockatricexml4 parser (yet to be implemented)

* Implement a new parser and xml format

 * cockatricexml4: new xml parser following the "generic properties hash" pattern;
 * oracleimporter: refactor the parsing code to better adapt to cockatricexml4; rewrote split cards parsing
 * carddb: change "colors" from a stringlist to a string
 * carddb: move the getMainCardType() method to the cockatricexml3 parser
 *

* CardInfo: show all properties (stil missing: nice name + translation)

* Rework the "add related card" feature so that it doesn't change the card name in the carddb

Also, fix token count display

* Picture loader: Added support for transform cards

* Fix side information for flip cards

Mtgjson uses side a/b for flip cards, while scryfall doesn't

* Pictureloader: dynamic tag resolution from card properties

Examples old => new
* !cardid! => !set:muid!
* !uuid!   => !set:uuid!
* !collectornumber! => !set:num!
New examples:
 * !prop:type!
 * !prop:manacost!

* Start moving mtg-related property names to a specific file

* Clangify

* Fix tests

* Make gcc an happy puppy

* Revert "Make gcc an happy puppy"

This reverts commit 446ec5f27516c4d3b32dbfc79557f4827c5c5bdf.

* Some gcc fixes

* Share set list between different db parsers, so they won't overwrite one each other

* All glory to the hypnoclangifier!

* Fix test compilation

* Cleanup edited files in the prior PR. (#3519)

* Cleanup edited files in the prior PR.

Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>

* Fix includes

Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>

* Update carddatabase.h
This commit is contained in:
ctrlaltca 2019-01-24 00:17:10 +01:00 committed by Zach H
parent 19180243aa
commit ed70099e36
44 changed files with 1814 additions and 1360 deletions

View file

@ -0,0 +1,27 @@
#include "carddatabaseparser.h"
SetNameMap ICardDatabaseParser::sets;
void ICardDatabaseParser::clearSetlist()
{
sets.clear();
}
CardSetPtr ICardDatabaseParser::internalAddSet(const QString &setName,
const QString &longName,
const QString &setType,
const QDate &releaseDate)
{
if (sets.contains(setName)) {
return sets.value(setName);
}
CardSetPtr newSet = CardSet::newInstance(setName);
newSet->setLongName(longName);
newSet->setSetType(setType);
newSet->setReleaseDate(releaseDate);
sets.insert(setName, newSet);
emit addSet(newSet);
return newSet;
}

View file

@ -9,15 +9,27 @@
class ICardDatabaseParser : public QObject
{
public:
virtual ~ICardDatabaseParser()
{
}
~ICardDatabaseParser() override = default;
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 void clearSetlist() = 0;
static void clearSetlist();
protected:
/*
* A cached list of the available sets, needed to cross-reference sets from cards.
* Shared between all parsers
*/
static SetNameMap sets;
CardSetPtr internalAddSet(const QString &setName,
const QString &longName = "",
const QString &setType = "",
const QDate &releaseDate = QDate());
signals:
virtual void addCard(CardInfoPtr card) = 0;
virtual void addSet(CardSetPtr set) = 0;
};
Q_DECLARE_INTERFACE(ICardDatabaseParser, "ICardDatabaseParser")

View file

@ -61,30 +61,6 @@ void CockatriceXml3Parser::parseFile(QIODevice &device)
}
}
CardSetPtr CockatriceXml3Parser::internalAddSet(const QString &setName,
const QString &longName,
const QString &setType,
const QDate &releaseDate)
{
if (sets.contains(setName)) {
return sets.value(setName);
}
CardSetPtr newSet = CardSet::newInstance(setName);
newSet->setLongName(longName);
newSet->setSetType(setType);
newSet->setReleaseDate(releaseDate);
sets.insert(setName, newSet);
emit addSet(newSet);
return newSet;
}
void CockatriceXml3Parser::clearSetlist()
{
sets.clear();
}
void CockatriceXml3Parser::loadSetsFromXml(QXmlStreamReader &xml)
{
while (!xml.atEnd()) {
@ -120,6 +96,44 @@ void CockatriceXml3Parser::loadSetsFromXml(QXmlStreamReader &xml)
}
}
QString CockatriceXml3Parser::getMainCardType(QString &type)
{
QString result = type;
/*
Legendary Artifact Creature - Golem
Instant // Instant
*/
int pos;
if ((pos = result.indexOf('-')) != -1) {
result.remove(pos, result.length());
}
if ((pos = result.indexOf("")) != -1) {
result.remove(pos, result.length());
}
if ((pos = result.indexOf("//")) != -1) {
result.remove(pos, result.length());
}
result = result.simplified();
/*
Legendary Artifact Creature
Instant
*/
if ((pos = result.lastIndexOf(' ')) != -1) {
result = result.mid(pos + 1);
}
/*
Creature
Instant
*/
return result;
}
void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
{
while (!xml.atEnd()) {
@ -128,59 +142,77 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
}
if (xml.name() == "card") {
QString name, manacost, cmc, type, pt, text, loyalty;
QStringList colors;
QString name = QString("");
QString text = QString("");
QVariantHash properties = QVariantHash();
QString colors = QString("");
QList<CardRelation *> relatedCards, reverseRelatedCards;
QStringMap customPicURLs;
MuidMap muids;
QStringMap uuids, collectorNumbers, rarities;
SetList sets;
CardInfoPerSetMap sets = CardInfoPerSetMap();
int tableRow = 0;
bool cipt = false;
bool isToken = false;
bool upsideDown = false;
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
// variable - assigned properties
if (xml.name() == "name") {
name = xml.readElementText();
} else if (xml.name() == "manacost") {
manacost = xml.readElementText();
} else if (xml.name() == "cmc") {
cmc = xml.readElementText();
} else if (xml.name() == "type") {
type = xml.readElementText();
} else if (xml.name() == "pt") {
pt = xml.readElementText();
} else if (xml.name() == "text") {
text = xml.readElementText();
} else if (xml.name() == "color") {
colors.append(xml.readElementText());
} else if (xml.name() == "token") {
isToken = static_cast<bool>(xml.readElementText().toInt());
// generic properties
} else if (xml.name() == "manacost") {
properties.insert("manacost", xml.readElementText());
} else if (xml.name() == "cmc") {
properties.insert("cmc", xml.readElementText());
} else if (xml.name() == "type") {
QString type = xml.readElementText();
properties.insert("type", type);
properties.insert("maintype", getMainCardType(type));
} else if (xml.name() == "pt") {
properties.insert("pt", xml.readElementText());
} else if (xml.name() == "loyalty") {
properties.insert("loyalty", xml.readElementText());
// positioning info
} else if (xml.name() == "tablerow") {
tableRow = xml.readElementText().toInt();
} else if (xml.name() == "cipt") {
cipt = (xml.readElementText() == "1");
} else if (xml.name() == "upsidedown") {
upsideDown = (xml.readElementText() == "1");
// sets
} else if (xml.name() == "set") {
// NOTE: attributes must be read before readElementText()
QXmlStreamAttributes attrs = xml.attributes();
QString setName = xml.readElementText();
sets.append(internalAddSet(setName));
CardInfoPerSet setInfo(internalAddSet(setName));
if (attrs.hasAttribute("muId")) {
muids[setName] = attrs.value("muId").toString().toInt();
setInfo.setProperty("muid", attrs.value("muId").toString());
}
if (attrs.hasAttribute("muId")) {
uuids[setName] = attrs.value("uuId").toString();
setInfo.setProperty("uuid", attrs.value("uuId").toString());
}
if (attrs.hasAttribute("picURL")) {
customPicURLs[setName] = attrs.value("picURL").toString();
setInfo.setProperty("picurl", attrs.value("picURL").toString());
}
if (attrs.hasAttribute("num")) {
collectorNumbers[setName] = attrs.value("num").toString();
setInfo.setProperty("num", attrs.value("num").toString());
}
if (attrs.hasAttribute("rarity")) {
rarities[setName] = attrs.value("rarity").toString();
setInfo.setProperty("rarity", attrs.value("rarity").toString());
}
} else if (xml.name() == "color") {
colors << xml.readElementText();
sets.insert(setName, setInfo);
// relatd cards
} else if (xml.name() == "related" || xml.name() == "reverse-related") {
bool attach = false;
bool exclude = false;
@ -217,16 +249,6 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
} else {
relatedCards << relation;
}
} else if (xml.name() == "tablerow") {
tableRow = xml.readElementText().toInt();
} else if (xml.name() == "cipt") {
cipt = (xml.readElementText() == "1");
} else if (xml.name() == "upsidedown") {
upsideDown = (xml.readElementText() == "1");
} else if (xml.name() == "loyalty") {
loyalty = xml.readElementText();
} else if (xml.name() == "token") {
isToken = static_cast<bool>(xml.readElementText().toInt());
} else if (xml.name() != "") {
qDebug() << "[CockatriceXml3Parser] Unknown card property" << xml.name()
<< ", trying to continue anyway";
@ -234,9 +256,9 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
}
}
CardInfoPtr newCard = CardInfo::newInstance(
name, isToken, manacost, cmc, type, pt, text, colors, relatedCards, reverseRelatedCards, upsideDown,
loyalty, cipt, tableRow, sets, customPicURLs, muids, uuids, collectorNumbers, rarities);
properties.insert("colors", colors);
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
reverseRelatedCards, sets, cipt, tableRow, upsideDown);
emit addCard(newCard);
}
}
@ -266,38 +288,60 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
return xml;
}
xml.writeStartElement("card");
xml.writeTextElement("name", info->getName());
const SetList &sets = info->getSets();
QString tmpString;
QString tmpSet;
for (int i = 0; i < sets.size(); i++) {
xml.writeStartElement("card");
// variable - assigned properties
xml.writeTextElement("name", info->getName());
xml.writeTextElement("text", info->getText());
if (info->getIsToken()) {
xml.writeTextElement("token", "1");
}
// generic properties
xml.writeTextElement("manacost", info->getProperty("manacost"));
xml.writeTextElement("cmc", info->getProperty("cmc"));
xml.writeTextElement("type", info->getProperty("type"));
int colorSize = info->getColors().size();
for (int i = 0; i < colorSize; ++i) {
xml.writeTextElement("color", info->getColors().at(i));
}
tmpString = info->getProperty("pt");
if (!tmpString.isEmpty()) {
xml.writeTextElement("pt", tmpString);
}
tmpString = info->getProperty("loyalty");
if (!tmpString.isEmpty()) {
xml.writeTextElement("loyalty", tmpString);
}
// sets
const CardInfoPerSetMap sets = info->getSets();
for (CardInfoPerSet set : sets) {
xml.writeStartElement("set");
xml.writeAttribute("rarity", set.getProperty("rarity"));
xml.writeAttribute("muId", set.getProperty("muid"));
xml.writeAttribute("uuId", set.getProperty("uuid"));
tmpSet = sets[i]->getShortName();
xml.writeAttribute("rarity", info->getRarity(tmpSet));
xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet)));
xml.writeAttribute("uuId", info->getUuId(tmpSet));
tmpString = info->getCollectorNumber(tmpSet);
tmpString = set.getProperty("num");
if (!tmpString.isEmpty()) {
xml.writeAttribute("num", info->getCollectorNumber(tmpSet));
xml.writeAttribute("num", tmpString);
}
tmpString = info->getCustomPicURL(tmpSet);
tmpString = set.getProperty("picurl");
if (!tmpString.isEmpty()) {
xml.writeAttribute("picURL", tmpString);
}
xml.writeCharacters(tmpSet);
xml.writeCharacters(set.getPtr()->getShortName());
xml.writeEndElement();
}
const QStringList &colors = info->getColors();
for (int i = 0; i < colors.size(); i++) {
xml.writeTextElement("color", colors[i]);
}
// related cards
const QList<CardRelation *> related = info->getRelatedCards();
for (auto i : related) {
xml.writeStartElement("related");
@ -343,23 +387,12 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
xml.writeCharacters(i->getName());
xml.writeEndElement();
}
xml.writeTextElement("manacost", info->getManaCost());
xml.writeTextElement("cmc", info->getCmc());
xml.writeTextElement("type", info->getCardType());
if (!info->getPowTough().isEmpty()) {
xml.writeTextElement("pt", info->getPowTough());
}
// positioning
xml.writeTextElement("tablerow", QString::number(info->getTableRow()));
xml.writeTextElement("text", info->getText());
if (info->getMainCardType() == "Planeswalker") {
xml.writeTextElement("loyalty", info->getLoyalty());
}
if (info->getCipt()) {
xml.writeTextElement("cipt", "1");
}
if (info->getIsToken()) {
xml.writeTextElement("token", "1");
}
if (info->getUpsideDownArt()) {
xml.writeTextElement("upsidedown", "1");
}

View file

@ -11,27 +11,18 @@ class CockatriceXml3Parser : public ICardDatabaseParser
Q_INTERFACES(ICardDatabaseParser)
public:
CockatriceXml3Parser() = default;
~CockatriceXml3Parser() = default;
bool getCanParseFile(const QString &name, QIODevice &device);
void parseFile(QIODevice &device);
bool saveToFile(SetNameMap sets, CardNameMap cards, const QString &fileName);
void clearSetlist();
~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;
private:
/*
* A cached list of the available sets, needed to cross-reference sets from cards.
*/
SetNameMap sets;
CardSetPtr internalAddSet(const QString &setName,
const QString &longName = "",
const QString &setType = "",
const QDate &releaseDate = QDate());
void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml);
QString getMainCardType(QString &type);
signals:
void addCard(CardInfoPtr card);
void addSet(CardSetPtr set);
void addCard(CardInfoPtr card) override;
void addSet(CardSetPtr set) override;
};
#endif

View file

@ -0,0 +1,362 @@
#include "cockatricexml4.h"
#include <QDebug>
#include <QFile>
#include <QXmlStreamReader>
#define COCKATRICE_XML4_TAGNAME "cockatrice_carddatabase"
#define COCKATRICE_XML4_TAGVER 4
bool CockatriceXml4Parser::getCanParseFile(const QString &fileName, QIODevice &device)
{
qDebug() << "[CockatriceXml4Parser] Trying to parse: " << fileName;
if (!fileName.endsWith(".xml", Qt::CaseInsensitive)) {
qDebug() << "[CockatriceXml4Parser] Parsing failed: wrong extension";
return false;
}
QXmlStreamReader xml(&device);
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) {
if (xml.name() == COCKATRICE_XML4_TAGNAME) {
int version = xml.attributes().value("version").toString().toInt();
if (version == COCKATRICE_XML4_TAGVER) {
return true;
} else {
qDebug() << "[CockatriceXml4Parser] Parsing failed: wrong version" << version;
return false;
}
} else {
qDebug() << "[CockatriceXml4Parser] Parsing failed: wrong element tag" << xml.name();
return false;
}
}
}
return true;
}
void CockatriceXml4Parser::parseFile(QIODevice &device)
{
QXmlStreamReader xml(&device);
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) {
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
if (xml.name() == "sets") {
loadSetsFromXml(xml);
} else if (xml.name() == "cards") {
loadCardsFromXml(xml);
} else if (xml.name() != "") {
qDebug() << "[CockatriceXml4Parser] Unknown item" << xml.name() << ", trying to continue anyway";
xml.skipCurrentElement();
}
}
}
}
}
void CockatriceXml4Parser::loadSetsFromXml(QXmlStreamReader &xml)
{
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
if (xml.name() == "set") {
QString shortName, longName, setType;
QDate releaseDate;
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
if (xml.name() == "name") {
shortName = xml.readElementText();
} else if (xml.name() == "longname") {
longName = xml.readElementText();
} else if (xml.name() == "settype") {
setType = xml.readElementText();
} else if (xml.name() == "releasedate") {
releaseDate = QDate::fromString(xml.readElementText(), Qt::ISODate);
} else if (xml.name() != "") {
qDebug() << "[CockatriceXml4Parser] Unknown set property" << xml.name()
<< ", trying to continue anyway";
xml.skipCurrentElement();
}
}
internalAddSet(shortName, longName, setType, releaseDate);
}
}
}
QVariantHash CockatriceXml4Parser::loadCardPropertiesFromXml(QXmlStreamReader &xml)
{
QVariantHash properties = QVariantHash();
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
if (xml.name() != "") {
properties.insert(xml.name().toString(), xml.readElementText());
}
}
return properties;
}
void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
{
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
if (xml.name() == "card") {
QString name = QString("");
QString text = QString("");
QVariantHash properties = QVariantHash();
QList<CardRelation *> relatedCards, reverseRelatedCards;
CardInfoPerSetMap sets = CardInfoPerSetMap();
int tableRow = 0;
bool cipt = false;
bool isToken = false;
bool upsideDown = false;
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
// variable - assigned properties
if (xml.name() == "name") {
name = xml.readElementText();
} else if (xml.name() == "text") {
text = xml.readElementText();
} else if (xml.name() == "token") {
isToken = static_cast<bool>(xml.readElementText().toInt());
// generic properties
} else if (xml.name() == "prop") {
properties = loadCardPropertiesFromXml(xml);
// positioning info
} else if (xml.name() == "tablerow") {
tableRow = xml.readElementText().toInt();
} else if (xml.name() == "cipt") {
cipt = (xml.readElementText() == "1");
} else if (xml.name() == "upsidedown") {
upsideDown = (xml.readElementText() == "1");
// sets
} else if (xml.name() == "set") {
// NOTE: attributes but be read before readElementText()
QXmlStreamAttributes attrs = xml.attributes();
QString setName = xml.readElementText();
CardInfoPerSet setInfo(internalAddSet(setName));
for (QXmlStreamAttribute attr : attrs) {
setInfo.setProperty(attr.name().toString(), attr.value().toString());
}
sets.insert(setName, setInfo);
// relatd cards
} else if (xml.name() == "related" || xml.name() == "reverse-related") {
bool attach = false;
bool exclude = false;
bool variable = false;
int count = 1;
QXmlStreamAttributes attrs = xml.attributes();
QString cardName = xml.readElementText();
if (attrs.hasAttribute("count")) {
if (attrs.value("count").toString().indexOf("x=") == 0) {
variable = true;
count = attrs.value("count").toString().remove(0, 2).toInt();
} else if (attrs.value("count").toString().indexOf("x") == 0) {
variable = true;
} else {
count = attrs.value("count").toString().toInt();
}
if (count < 1) {
count = 1;
}
}
if (attrs.hasAttribute("attach")) {
attach = true;
}
if (attrs.hasAttribute("exclude")) {
exclude = true;
}
auto *relation = new CardRelation(cardName, attach, exclude, variable, count);
if (xml.name() == "reverse-related") {
reverseRelatedCards << relation;
} else {
relatedCards << relation;
}
} else if (xml.name() != "") {
qDebug() << "[CockatriceXml4Parser] Unknown card property" << xml.name()
<< ", trying to continue anyway";
xml.skipCurrentElement();
}
}
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
reverseRelatedCards, sets, cipt, tableRow, upsideDown);
emit addCard(newCard);
}
}
}
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSetPtr &set)
{
if (set.isNull()) {
qDebug() << "&operator<< set is nullptr";
return xml;
}
xml.writeStartElement("set");
xml.writeTextElement("name", set->getShortName());
xml.writeTextElement("longname", set->getLongName());
xml.writeTextElement("settype", set->getSetType());
xml.writeTextElement("releasedate", set->getReleaseDate().toString(Qt::ISODate));
xml.writeEndElement();
return xml;
}
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &info)
{
if (info.isNull()) {
qDebug() << "operator<< info is nullptr";
return xml;
}
QString tmpString;
xml.writeStartElement("card");
// variable - assigned properties
xml.writeTextElement("name", info->getName());
xml.writeTextElement("text", info->getText());
if (info->getIsToken()) {
xml.writeTextElement("token", "1");
}
// generic properties
xml.writeStartElement("prop");
for (QString propName : info->getProperties()) {
xml.writeTextElement(propName, info->getProperty(propName));
}
xml.writeEndElement();
// sets
for (CardInfoPerSet set : info->getSets()) {
xml.writeStartElement("set");
for (QString propName : set.getProperties()) {
xml.writeAttribute(propName, set.getProperty(propName));
}
xml.writeCharacters(set.getPtr()->getShortName());
xml.writeEndElement();
}
// related cards
const QList<CardRelation *> related = info->getRelatedCards();
for (auto i : related) {
xml.writeStartElement("related");
if (i->getDoesAttach()) {
xml.writeAttribute("attach", "attach");
}
if (i->getIsCreateAllExclusion()) {
xml.writeAttribute("exclude", "exclude");
}
if (i->getIsVariable()) {
if (1 == i->getDefaultCount()) {
xml.writeAttribute("count", "x");
} else {
xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount()));
}
} else if (1 != i->getDefaultCount()) {
xml.writeAttribute("count", QString::number(i->getDefaultCount()));
}
xml.writeCharacters(i->getName());
xml.writeEndElement();
}
const QList<CardRelation *> reverseRelated = info->getReverseRelatedCards();
for (auto i : reverseRelated) {
xml.writeStartElement("reverse-related");
if (i->getDoesAttach()) {
xml.writeAttribute("attach", "attach");
}
if (i->getIsCreateAllExclusion()) {
xml.writeAttribute("exclude", "exclude");
}
if (i->getIsVariable()) {
if (1 == i->getDefaultCount()) {
xml.writeAttribute("count", "x");
} else {
xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount()));
}
} else if (1 != i->getDefaultCount()) {
xml.writeAttribute("count", QString::number(i->getDefaultCount()));
}
xml.writeCharacters(i->getName());
xml.writeEndElement();
}
// positioning
xml.writeTextElement("tablerow", QString::number(info->getTableRow()));
if (info->getCipt()) {
xml.writeTextElement("cipt", "1");
}
if (info->getUpsideDownArt()) {
xml.writeTextElement("upsidedown", "1");
}
xml.writeEndElement(); // card
return xml;
}
bool CockatriceXml4Parser::saveToFile(SetNameMap sets, CardNameMap cards, const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
return false;
}
QXmlStreamWriter xml(&file);
xml.setAutoFormatting(true);
xml.writeStartDocument();
xml.writeStartElement(COCKATRICE_XML4_TAGNAME);
xml.writeAttribute("version", QString::number(COCKATRICE_XML4_TAGVER));
if (sets.count() > 0) {
xml.writeStartElement("sets");
for (CardSetPtr set : sets) {
xml << set;
}
xml.writeEndElement();
}
if (cards.count() > 0) {
xml.writeStartElement("cards");
for (CardInfoPtr card : cards) {
xml << card;
}
xml.writeEndElement();
}
xml.writeEndElement(); // cockatrice_carddatabase
xml.writeEndDocument();
return true;
}

View file

@ -0,0 +1,28 @@
#ifndef COCKATRICE_XML4_H
#define COCKATRICE_XML4_H
#include <QXmlStreamReader>
#include "carddatabaseparser.h"
class CockatriceXml4Parser : public ICardDatabaseParser
{
Q_OBJECT
Q_INTERFACES(ICardDatabaseParser)
public:
CockatriceXml4Parser() = default;
~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;
private:
QVariantHash loadCardPropertiesFromXml(QXmlStreamReader &xml);
void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml);
signals:
void addCard(CardInfoPtr card) override;
void addSet(CardSetPtr set) override;
};
#endif