#include "carddatabase.h" #include "settingscache.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include const int CardDatabase::versionNeeded = 3; const char* CardDatabase::TOKENS_SETNAME = "TK"; static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSet *set) { 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; } CardSet::CardSet(const QString &_shortName, const QString &_longName, const QString &_setType, const QDate &_releaseDate) : shortName(_shortName), longName(_longName), releaseDate(_releaseDate), setType(_setType) { updateSortKey(); } QString CardSet::getCorrectedShortName() const { // Because windows is horrible. QSet invalidFileNames; invalidFileNames << "CON" << "PRN" << "AUX" << "NUL" << "COM1" << "COM2" << "COM3" << "COM4" << "COM5" << "COM6" << "COM7" << "COM8" << "COM9" << "LPT1" << "LPT2" << "LPT3" << "LPT4" << "LPT5" << "LPT6" << "LPT7" << "LPT8" << "LPT9"; return invalidFileNames.contains(shortName) ? shortName + "_" : shortName; } void CardSet::setSortKey(unsigned int _sortKey) { sortKey = _sortKey; QSettings settings; settings.beginGroup("sets"); settings.beginGroup(shortName); settings.setValue("sortkey", sortKey); } void CardSet::updateSortKey() { QSettings settings; settings.beginGroup("sets"); settings.beginGroup(shortName); sortKey = settings.value("sortkey", 0).toInt(); } class SetList::CompareFunctor { public: inline bool operator()(CardSet *a, CardSet *b) const { return a->getSortKey() < b->getSortKey(); } }; void SetList::sortByKey() { qSort(begin(), end(), CompareFunctor()); } PictureToLoad::PictureToLoad(CardInfo *_card, bool _hq) : card(_card), setIndex(0), hq(_hq) { if (card) { sortedSets = card->getSets(); sortedSets.sortByKey(); } } bool PictureToLoad::nextSet() { if (setIndex == sortedSets.size() - 1) return false; ++setIndex; return true; } QString PictureToLoad::getSetName() const { if (setIndex < sortedSets.size()) return sortedSets[setIndex]->getCorrectedShortName(); else return QString(""); } CardSet *PictureToLoad::getCurrentSet() const { if (setIndex < sortedSets.size()) return sortedSets[setIndex]; else return 0; } PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent) : QObject(parent), _picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq), downloadRunning(false), loadQueueRunning(false) { connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection); networkManager = new QNetworkAccessManager(this); connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *))); } PictureLoader::~PictureLoader() { // This does not work with the destroyed() signal as this destructor is called after the main event loop is done. thread()->quit(); } void PictureLoader::processLoadQueue() { if (loadQueueRunning) return; loadQueueRunning = true; forever { mutex.lock(); if (loadQueue.isEmpty()) { mutex.unlock(); loadQueueRunning = false; return; } cardBeingLoaded = loadQueue.takeFirst(); mutex.unlock(); QString setName = cardBeingLoaded.getSetName(); QString correctedCardname = cardBeingLoaded.getCard()->getCorrectedName(); qDebug() << "Trying to load picture (set: " << setName << " card: " << correctedCardname << ")"; //The list of paths to the folders in which to search for images QList picsPaths = QList() << _picsPath + "/CUSTOM/" + correctedCardname; if(!setName.isEmpty()) { picsPaths << _picsPath + "/" + setName + "/" + correctedCardname << _picsPath + "/downloadedPics/" + setName + "/" + correctedCardname; } QImage image; QImageReader imgReader; imgReader.setDecideFormatFromContent(true); bool found = false; //Iterates through the list of paths, searching for images with the desired name with any QImageReader-supported extension for (int i = 0; i < picsPaths.length() && !found; i ++) { imgReader.setFileName(picsPaths.at(i)); if (imgReader.read(&image)) { qDebug() << "Picture found on disk (set: " << setName << " card: " << correctedCardname << ")"; emit imageLoaded(cardBeingLoaded.getCard(), image); found = true; break; } imgReader.setFileName(picsPaths.at(i) + ".full"); if (imgReader.read(&image)) { qDebug() << "Picture.full found on disk (set: " << setName << " card: " << correctedCardname << ")"; emit imageLoaded(cardBeingLoaded.getCard(), image); found = true; } } if (!found) { if (picDownload) { qDebug() << "Picture NOT found, trying to download (set: " << setName << " card: " << correctedCardname << ")"; cardsToDownload.append(cardBeingLoaded); cardBeingLoaded=0; if (!downloadRunning) startNextPicDownload(); } else { if (cardBeingLoaded.nextSet()) { qDebug() << "Picture NOT found and download disabled, moving to next set (newset: " << setName << " card: " << correctedCardname << ")"; mutex.lock(); loadQueue.prepend(cardBeingLoaded); cardBeingLoaded=0; mutex.unlock(); } else { qDebug() << "Picture NOT found, download disabled, no more sets to try: BAILING OUT (oldset: " << setName << " card: " << correctedCardname << ")"; emit imageLoaded(cardBeingLoaded.getCard(), QImage()); } } } } } QString PictureLoader::getPicUrl() { if (!picDownload) return QString(""); CardInfo *card = cardBeingDownloaded.getCard(); CardSet *set=cardBeingDownloaded.getCurrentSet(); QString picUrl = QString(""); // if sets have been defined for the card, they can contain custom picUrls if(set) { // first check if Hq is enabled and a custom Hq card url exists in cards.xml if(picDownloadHq) { picUrl = card->getCustomPicURLHq(set->getShortName()); if (!picUrl.isEmpty()) return picUrl; } // then, test for a custom, non-Hq card url in cards.xml picUrl = card->getCustomPicURL(set->getShortName()); if (!picUrl.isEmpty()) return picUrl; } // if a card has a muid, use the default url; if not, use the fallback int muid = set ? card->getMuId(set->getShortName()) : 0; if(muid) picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl(); else picUrl = picDownloadHq ? settingsCache->getPicUrlHqFallback() : settingsCache->getPicUrlFallback(); picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(muid))); if (set) { picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); } if (picUrl.contains("!name!") || picUrl.contains("!setcode!") || picUrl.contains("!setname!") || picUrl.contains("!cardid!")) { qDebug() << "Insufficient card data to download" << card->getName() << "Url:" << picUrl; return QString(""); } return picUrl; } void PictureLoader::startNextPicDownload() { if (cardsToDownload.isEmpty()) { cardBeingDownloaded = 0; downloadRunning = false; return; } downloadRunning = true; cardBeingDownloaded = cardsToDownload.takeFirst(); QString picUrl = getPicUrl(); if (picUrl.isEmpty()) { downloadRunning = false; picDownloadFailed(); } else { QUrl url(picUrl); QNetworkRequest req(url); qDebug() << "starting picture download:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url(); networkManager->get(req); } } void PictureLoader::picDownloadFailed() { if (cardBeingDownloaded.nextSet()) { qDebug() << "Picture NOT found, download failed, moving to next set (newset: " << cardBeingDownloaded.getSetName() << " card: " << cardBeingDownloaded.getCard()->getCorrectedName() << ")"; mutex.lock(); loadQueue.prepend(cardBeingDownloaded); mutex.unlock(); emit startLoadQueue(); } else { qDebug() << "Picture NOT found, download failed, no more sets to try: BAILING OUT (oldset: " << cardBeingDownloaded.getSetName() << " card: " << cardBeingDownloaded.getCard()->getCorrectedName() << ")"; cardBeingDownloaded = 0; emit imageLoaded(cardBeingDownloaded.getCard(), QImage()); } } void PictureLoader::picDownloadFinished(QNetworkReply *reply) { QString picsPath = _picsPath; if (reply->error()) { qDebug() << "Download failed:" << reply->errorString(); } const QByteArray &picData = reply->peek(reply->size()); //peek is used to keep the data in the buffer for use by QImageReader QImage testImage; QImageReader imgReader; imgReader.setDecideFormatFromContent(true); imgReader.setDevice(reply); QString extension = "." + imgReader.format(); //the format is determined prior to reading the QImageReader data into a QImage object, as that wipes the QImageReader buffer if (extension == ".jpeg") extension = ".jpg"; if (imgReader.read(&testImage)) { QString setName = cardBeingDownloaded.getSetName(); if(!setName.isEmpty()) { if (!QDir().mkpath(picsPath + "/downloadedPics/" + setName)) { qDebug() << picsPath + "/downloadedPics/" + setName + " could not be created."; return; } QFile newPic(picsPath + "/downloadedPics/" + setName + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + extension); if (!newPic.open(QIODevice::WriteOnly)) return; newPic.write(picData); newPic.close(); } emit imageLoaded(cardBeingDownloaded.getCard(), testImage); } else { picDownloadFailed(); } reply->deleteLater(); startNextPicDownload(); } void PictureLoader::loadImage(CardInfo *card) { QMutexLocker locker(&mutex); // avoid queueing the same card more than once if(card == cardBeingLoaded.getCard() || card == cardBeingDownloaded.getCard()) return; foreach(PictureToLoad pic, loadQueue) { if(pic.getCard() == card) return; } loadQueue.append(PictureToLoad(card)); emit startLoadQueue(); } void PictureLoader::setPicsPath(const QString &path) { QMutexLocker locker(&mutex); _picsPath = path; } void PictureLoader::setPicDownload(bool _picDownload) { QMutexLocker locker(&mutex); picDownload = _picDownload; } void PictureLoader::setPicDownloadHq(bool _picDownloadHq) { QMutexLocker locker(&mutex); picDownloadHq = _picDownloadHq; } CardInfo::CardInfo(CardDatabase *_db, const QString &_name, bool _isToken, const QString &_manacost, const QString &_cmc, const QString &_cardtype, const QString &_powtough, const QString &_text, const QStringList &_colors, int _loyalty, bool _cipt, int _tableRow, const SetList &_sets, const QStringMap &_customPicURLs, const QStringMap &_customPicURLsHq, MuidMap _muIds) : db(_db), name(_name), isToken(_isToken), sets(_sets), manacost(_manacost), cmc(_cmc), cardtype(_cardtype), powtough(_powtough), text(_text), colors(_colors), loyalty(_loyalty), customPicURLs(_customPicURLs), customPicURLsHq(_customPicURLsHq), muIds(_muIds), cipt(_cipt), tableRow(_tableRow) { pixmapCacheKey = QLatin1String("card_") + name; for (int i = 0; i < sets.size(); i++) sets[i]->append(this); } CardInfo::~CardInfo() { clearPixmapCache(); } QString CardInfo::getMainCardType() const { QString result = getCardType(); /* 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; } QString CardInfo::getCorrectedName() const { QString result = name; // Fire // Ice, Circle of Protection: Red, "Ach! Hans, Run!", Who/What/When/Where/Why, Question Elemental? return result.remove(" // ").remove(':').remove('"').remove('?').replace('/', ' '); } void CardInfo::addToSet(CardSet *set) { set->append(this); sets << set; } void CardInfo::loadPixmap(QPixmap &pixmap) { if(QPixmapCache::find(pixmapCacheKey, &pixmap)) return; pixmap = QPixmap(); if (getName().isEmpty()) { pixmap.load(settingsCache->getCardBackPicturePath()); return; } db->loadImage(this); } void CardInfo::imageLoaded(const QImage &image) { if (!image.isNull()) { QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(image)); emit pixmapUpdated(); } } void CardInfo::getPixmap(QSize size, QPixmap &pixmap) { QString key = QLatin1String("card_") + name + QLatin1Char('_') + QString::number(size.width()); if(QPixmapCache::find(key, &pixmap)) return; QPixmap bigPixmap; loadPixmap(bigPixmap); if (bigPixmap.isNull()) { if (!getName().isEmpty()) { pixmap = QPixmap(); // null return; } else { pixmap = QPixmap(size); pixmap.fill(Qt::transparent); QSvgRenderer svg(QString(":/back.svg")); QPainter painter(&pixmap); svg.render(&painter, QRectF(0, 0, size.width(), size.height())); } } else { pixmap = bigPixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } QPixmapCache::insert(key, pixmap); } void CardInfo::clearPixmapCache() { qDebug() << "Deleting pixmap for" << name; QPixmapCache::remove(pixmapCacheKey); } void CardInfo::clearPixmapCacheMiss() { QPixmap pixmap; if(!QPixmapCache::find(pixmapCacheKey, &pixmap)) return; if (pixmap.isNull()) clearPixmapCache(); } void CardInfo::updatePixmapCache() { qDebug() << "Updating pixmap cache for" << name; clearPixmapCache(); QPixmap tmp; loadPixmap(tmp); emit pixmapUpdated(); } QString CardInfo::simplifyName(const QString &name) { QString simpleName(name); // So Aetherling would work, but not Ætherling since 'Æ' would get replaced // with nothing. simpleName.replace("æ", "ae"); simpleName.replace("Æ", "AE"); // Replace Jötun Grunt with Jotun Grunt. simpleName = simpleName.normalized(QString::NormalizationForm_KD); // Replace dashes with spaces so that we can say "garruk the veil cursed" // instead of the unintuitive "garruk the veilcursed". simpleName = simpleName.replace("-", " "); simpleName.remove(QRegExp("[^a-zA-Z0-9 ]")); simpleName = simpleName.toLower(); return simpleName; } static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) { 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("set"); tmpSet=sets[i]->getShortName(); xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet))); tmpString = info->getCustomPicURL(tmpSet); if(!tmpString.isEmpty()) xml.writeAttribute("picURL", tmpString); tmpString = info->getCustomPicURLHq(tmpSet); if(!tmpString.isEmpty()) xml.writeAttribute("picURLHq", tmpString); xml.writeCharacters(tmpSet); xml.writeEndElement(); } const QStringList &colors = info->getColors(); for (int i = 0; i < colors.size(); i++) xml.writeTextElement("color", colors[i]); xml.writeTextElement("manacost", info->getManaCost()); xml.writeTextElement("cmc", info->getCmc()); xml.writeTextElement("type", info->getCardType()); if (!info->getPowTough().isEmpty()) xml.writeTextElement("pt", info->getPowTough()); xml.writeTextElement("tablerow", QString::number(info->getTableRow())); xml.writeTextElement("text", info->getText()); if (info->getMainCardType() == "Planeswalker") xml.writeTextElement("loyalty", QString::number(info->getLoyalty())); if (info->getCipt()) xml.writeTextElement("cipt", "1"); if (info->getIsToken()) xml.writeTextElement("token", "1"); xml.writeEndElement(); // card return xml; } CardDatabase::CardDatabase(QObject *parent) : QObject(parent), noCard(0), loadStatus(NotLoaded) { connect(settingsCache, SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged())); connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabase())); connect(settingsCache, SIGNAL(tokenDatabasePathChanged()), this, SLOT(loadTokenDatabase())); connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged())); connect(settingsCache, SIGNAL(picDownloadHqChanged()), this, SLOT(picDownloadHqChanged())); loadCardDatabase(); loadTokenDatabase(); pictureLoaderThread = new QThread; pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload(), settingsCache->getPicDownloadHq()); pictureLoader->moveToThread(pictureLoaderThread); connect(pictureLoader, SIGNAL(imageLoaded(CardInfo *, const QImage &)), this, SLOT(imageLoaded(CardInfo *, const QImage &))); pictureLoaderThread->start(QThread::LowPriority); noCard = new CardInfo(this); QPixmap tmp; noCard->loadPixmap(tmp); // cache pixmap for card back connect(settingsCache, SIGNAL(cardBackPicturePathChanged()), noCard, SLOT(updatePixmapCache())); } CardDatabase::~CardDatabase() { clear(); delete noCard; pictureLoader->deleteLater(); pictureLoaderThread->wait(); delete pictureLoaderThread; } void CardDatabase::clear() { QHashIterator setIt(sets); while (setIt.hasNext()) { setIt.next(); delete setIt.value(); } sets.clear(); QHashIterator i(cards); while (i.hasNext()) { i.next(); delete i.value(); } cards.clear(); // The pointers themselves were already deleted, so we don't delete them // again. simpleNameCards.clear(); } void CardDatabase::addCard(CardInfo *card) { cards.insert(card->getName(), card); simpleNameCards.insert(CardInfo::simplifyName(card->getName()), card); emit cardAdded(card); } void CardDatabase::removeCard(CardInfo *card) { cards.remove(card->getName()); simpleNameCards.remove(CardInfo::simplifyName(card->getName())); emit cardRemoved(card); } CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) { return getCardFromMap(cards, cardName, createIfNotFound); } CardInfo *CardDatabase::getCardBySimpleName(const QString &cardName, bool createIfNotFound) { QString simpleName = CardInfo::simplifyName(cardName); return getCardFromMap(simpleNameCards, simpleName, createIfNotFound); } CardSet *CardDatabase::getSet(const QString &setName) { if (sets.contains(setName)) return sets.value(setName); else { CardSet *newSet = new CardSet(setName); sets.insert(setName, newSet); return newSet; } } SetList CardDatabase::getSetList() const { SetList result; QHashIterator i(sets); while (i.hasNext()) { i.next(); result << i.value(); } return result; } void CardDatabase::clearPixmapCache() { // This also clears the cards in simpleNameCards since they point to the // same object. QHashIterator i(cards); while (i.hasNext()) { i.next(); i.value()->clearPixmapCache(); } if (noCard) noCard->clearPixmapCache(); } void CardDatabase::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); } sets.insert(shortName, new CardSet(shortName, longName, setType, releaseDate)); } } } void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) { while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::EndElement) break; if (xml.name() == "card") { QString name, manacost, cmc, type, pt, text; QStringList colors; QStringMap customPicURLs, customPicURLsHq; MuidMap muids; SetList sets; int tableRow = 0; int loyalty = 0; bool cipt = false; bool isToken = false; while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::EndElement) break; 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() == "set") { QXmlStreamAttributes attrs = xml.attributes(); QString setName = xml.readElementText(); sets.append(getSet(setName)); if (attrs.hasAttribute("muId")) { muids[setName] = attrs.value("muId").toString().toInt(); } if (attrs.hasAttribute("picURL")) { customPicURLs[setName] = attrs.value("picURL").toString(); } if (attrs.hasAttribute("picURLHq")) { customPicURLsHq[setName] = attrs.value("picURLHq").toString(); } } else if (xml.name() == "color") colors << xml.readElementText(); else if (xml.name() == "tablerow") tableRow = xml.readElementText().toInt(); else if (xml.name() == "cipt") cipt = (xml.readElementText() == "1"); else if (xml.name() == "loyalty") loyalty = xml.readElementText().toInt(); else if (xml.name() == "token") isToken = xml.readElementText().toInt(); } if (isToken == tokens) { addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, loyalty, cipt, tableRow, sets, customPicURLs, customPicURLsHq, muids)); } } } } CardInfo *CardDatabase::getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound) { if (cardName.isEmpty()) return noCard; else if (cardMap.contains(cardName)) return cardMap.value(cardName); else if (createIfNotFound) { CardInfo *newCard = new CardInfo(this, cardName, true); newCard->addToSet(getSet(CardDatabase::TOKENS_SETNAME)); cardMap.insert(cardName, newCard); return newCard; } else return 0; } LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) { QFile file(fileName); file.open(QIODevice::ReadOnly); if (!file.isOpen()) return FileError; QXmlStreamReader xml(&file); while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::StartElement) { if (xml.name() != "cockatrice_carddatabase") return Invalid; int version = xml.attributes().value("version").toString().toInt(); if (version < versionNeeded) { qDebug() << "loadFromFile(): Version too old: " << version; return VersionTooOld; } while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::EndElement) break; if (xml.name() == "sets") loadSetsFromXml(xml); else if (xml.name() == "cards") loadCardsFromXml(xml, tokens); } } } qDebug() << cards.size() << "cards in" << sets.size() << "sets loaded"; if (cards.isEmpty()) return NoCards; return Ok; } bool CardDatabase::saveToFile(const QString &fileName, bool tokens) { QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) return false; QXmlStreamWriter xml(&file); xml.setAutoFormatting(true); xml.writeStartDocument(); xml.writeStartElement("cockatrice_carddatabase"); xml.writeAttribute("version", QString::number(versionNeeded)); if (!tokens) { xml.writeStartElement("sets"); QHashIterator setIterator(sets); while (setIterator.hasNext()) xml << setIterator.next().value(); xml.writeEndElement(); // sets } xml.writeStartElement("cards"); QHashIterator cardIterator(cards); while (cardIterator.hasNext()) { CardInfo *card = cardIterator.next().value(); if (tokens == card->getIsToken()) { xml << card; } } xml.writeEndElement(); // cards xml.writeEndElement(); // cockatrice_carddatabase xml.writeEndDocument(); return true; } void CardDatabase::picDownloadChanged() { pictureLoader->setPicDownload(settingsCache->getPicDownload()); if (settingsCache->getPicDownload()) { QHashIterator cardIterator(cards); while (cardIterator.hasNext()) cardIterator.next().value()->clearPixmapCacheMiss(); } } void CardDatabase::picDownloadHqChanged() { pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq()); if (settingsCache->getPicDownloadHq()) { QHashIterator cardIterator(cards); while (cardIterator.hasNext()) cardIterator.next().value()->clearPixmapCacheMiss(); } } LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens) { LoadStatus tempLoadStatus = NotLoaded; if (!path.isEmpty()) tempLoadStatus = loadFromFile(path, tokens); if (tempLoadStatus == Ok) { SetList allSets; QHashIterator setsIterator(sets); while (setsIterator.hasNext()) allSets.append(setsIterator.next().value()); allSets.sortByKey(); for (int i = 0; i < allSets.size(); ++i) allSets[i]->setSortKey(i); emit cardListChanged(); } if (!tokens) { loadStatus = tempLoadStatus; qDebug() << "loadCardDatabase(): Status = " << loadStatus; } return tempLoadStatus; } void CardDatabase::loadCardDatabase() { loadCardDatabase(settingsCache->getCardDatabasePath(), false); } void CardDatabase::loadTokenDatabase() { loadCardDatabase(settingsCache->getTokenDatabasePath(), true); } QStringList CardDatabase::getAllColors() const { QSet colors; QHashIterator cardIterator(cards); while (cardIterator.hasNext()) { const QStringList &cardColors = cardIterator.next().value()->getColors(); if (cardColors.isEmpty()) colors.insert("X"); else for (int i = 0; i < cardColors.size(); ++i) colors.insert(cardColors[i]); } return colors.toList(); } QStringList CardDatabase::getAllMainCardTypes() const { QSet types; QHashIterator cardIterator(cards); while (cardIterator.hasNext()) types.insert(cardIterator.next().value()->getMainCardType()); return types.toList(); } void CardDatabase::cacheCardPixmaps(const QStringList &cardNames) { QPixmap tmp; for (int i = 0; i < cardNames.size(); ++i) getCard(cardNames[i])->loadPixmap(tmp); } void CardDatabase::loadImage(CardInfo *card) { pictureLoader->loadImage(card); } void CardDatabase::imageLoaded(CardInfo *card, QImage image) { card->imageLoaded(image); } void CardDatabase::picsPathChanged() { pictureLoader->setPicsPath(settingsCache->getPicsPath()); clearPixmapCache(); }