File indexing completed on 2024-05-12 04:03:07

0001 /*
0002     SPDX-FileCopyrightText: 2011 Julian Helfferich <julian.helfferich@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "levelloader.h"
0008 
0009 // own
0010 #include "globals.h"
0011 #include "kbreakout_debug.h"
0012 // KF
0013 // These can be removed when KConfig style levelsets are no longer supported
0014 #include <KConfig>
0015 #include <KConfigGroup>
0016 // Qt
0017 #include <QDomDocument>
0018 #include <QFile>
0019 #include <QStandardPaths>
0020 
0021 LevelLoader::LevelLoader(QObject *parent)
0022     : QObject(parent)
0023 {
0024     m_level = 0;
0025     m_levelset = nullptr;
0026 }
0027 
0028 LevelLoader::~LevelLoader()
0029 {
0030     delete m_levelset;
0031 }
0032 
0033 int LevelLoader::level() const
0034 {
0035     return m_level;
0036 }
0037 
0038 void LevelLoader::setLevel(int level)
0039 {
0040     m_level = level;
0041 }
0042 
0043 QString LevelLoader::levelset() const
0044 {
0045     return m_levelname;
0046 }
0047 
0048 void LevelLoader::setLevelset(const QString &levelname)
0049 {
0050     if (levelname == m_levelname) {
0051         return;
0052     }
0053     m_levelname = levelname;
0054 
0055     // Loading document model
0056     // Locating the path in the filesystem
0057     QString path = QStringLiteral("levelsets/%1.levelset").arg(m_levelname);
0058     path = QStandardPaths::locate(QStandardPaths::AppDataLocation, path);
0059     // --
0060 
0061     delete m_levelset;
0062 
0063     m_levelset = new QDomDocument(m_levelname);
0064     QFile file(path);
0065     if (!file.open(QIODevice::ReadOnly)) {
0066         qCCritical(KBREAKOUT_General) << "Can't open file " << path;
0067     }
0068 
0069     const QDomDocument::ParseResult parseResult = m_levelset->setContent(&file);
0070     if (!parseResult) {
0071         file.close();
0072         // Testing whether levelset is of old KConfig style
0073         KConfig kconfigfile(path, KConfig::SimpleConfig);
0074         if (kconfigfile.hasGroup(QStringLiteral("level1"))) {
0075             // Levelset is in KConfig style
0076             m_oldstyle = true;
0077             qCCritical(KBREAKOUT_General) << "Warning: Using deprecated KConfig-levelset. Please change to XML-Style.\n";
0078         } else {
0079             qCCritical(KBREAKOUT_General) << "Can't read levelset from " << path << "\nError: " << parseResult.errorMessage <<
0080                                              " in Line " << parseResult.errorLine << ", Column " << parseResult.errorColumn;
0081         }
0082     } else {
0083         // Successfully loaded QDom-style levelset
0084         m_oldstyle = false;
0085     }
0086     file.close();
0087     // --
0088 }
0089 
0090 void LevelLoader::loadLevel()
0091 {
0092     // Check if levelset is of KConfig-type
0093     if (m_oldstyle) {
0094         loadOldStyleLevel();
0095         return;
0096     }
0097     // Selecting the correct level
0098     m_level++;
0099 
0100     if (m_levelset == nullptr) {
0101         qCCritical(KBREAKOUT_General) << "Error: No levelset specified";
0102         return;
0103     }
0104 
0105     QDomElement levels = m_levelset->documentElement();
0106     QDomNode node = levels.firstChild();
0107     for (int i = 1; i < m_level; i++) {
0108         node = node.nextSibling();
0109     }
0110     // --
0111 
0112     // Load level information
0113     if (node.isNull() || node.toElement().tagName() != QLatin1String("Level")) {
0114         // Level not found or no more levels
0115         return;
0116     }
0117 
0118     QDomAttr attribute;
0119     QDomElement level = node.toElement();
0120     if (level.isNull()) {
0121         qCCritical(KBREAKOUT_General) << "Invalid Levelset " << m_levelname << ": Can't read level information";
0122     }
0123     attribute = level.attributeNode(QStringLiteral("Name"));
0124     QString levelName;
0125     if (!attribute.isNull()) {
0126         levelName = level.attributeNode(QStringLiteral("Name")).value();
0127     }
0128     node = node.firstChild();
0129     // --
0130 
0131     // Load bricks and gifts
0132     m_lineNumber = 0;
0133     while (!node.isNull()) {
0134         QDomElement info = node.toElement();
0135         if (info.isNull()) {
0136             qCCritical(KBREAKOUT_General) << "Invalid levelset " << m_levelname << ": Can't read level information.";
0137         }
0138 
0139         if (info.tagName() == QLatin1String("Line")) {
0140             // Load one line of bricks
0141             loadLine(info);
0142         } else if (info.tagName() == QLatin1String("Gift")) {
0143             // Load one gift type
0144             loadGift(info);
0145         } else {
0146             qCCritical(KBREAKOUT_General) << "Invalid tag name " << info.tagName() << " has occurred in level "
0147                                           << levelName << " in levelset " << m_levelname;
0148         }
0149 
0150         node = node.nextSibling();
0151     }
0152 }
0153 
0154 void LevelLoader::loadLine(QDomElement lineNode)
0155 {
0156     // Reading the line number
0157     QDomAttr attribute = lineNode.attributeNode(QStringLiteral("Number"));
0158     QDomElement attributeNode = lineNode.firstChildElement(QStringLiteral("Number"));
0159     if (!attribute.isNull()) {
0160         m_lineNumber = attribute.value().toInt();
0161     } else if (!attributeNode.isNull()) {
0162         m_lineNumber = attributeNode.text().toInt();
0163     } else {
0164         // Standard line numbering: load next line
0165         m_lineNumber++;
0166     }
0167 
0168     // Reading the brick information
0169     attribute = lineNode.attributeNode(QStringLiteral("Bricks"));
0170     attributeNode = lineNode.firstChildElement(QStringLiteral("Bricks"));
0171     QString line;
0172     if (!attribute.isNull()) {
0173         line = attribute.value();
0174     } else if (!attributeNode.isNull()) {
0175         line = attributeNode.text();
0176     } else {
0177         line = lineNode.text();
0178     }
0179 
0180     if (line.size() > WIDTH) {
0181         qCCritical(KBREAKOUT_General) << "Invalid levelset " << m_levelname << ": too many bricks in line "
0182                                       << m_lineNumber;
0183     }
0184 
0185     Q_EMIT newLine(line, m_lineNumber);
0186 }
0187 
0188 void LevelLoader::loadGift(QDomElement giftNode)
0189 {
0190     bool nodeTextRead = false;
0191     // Reading the brick type
0192     QDomAttr attribute = giftNode.attributeNode(QStringLiteral("Type"));
0193     QDomElement attributeNode = giftNode.firstChildElement(QStringLiteral("Type"));
0194     QString giftType;
0195     if (!attribute.isNull()) {
0196         giftType = attribute.value();
0197     } else if (!attributeNode.isNull()) {
0198         giftType = attributeNode.text();
0199         nodeTextRead = true;
0200     } else {
0201         giftType = giftNode.text();
0202         nodeTextRead = true;
0203     }
0204 
0205     // Reading number of gifts to be distributed. If not specified one gift is placed.
0206     attribute = giftNode.attributeNode(QStringLiteral("Count"));
0207     attributeNode = giftNode.firstChildElement(QStringLiteral("Count"));
0208     int times = 1;
0209     bool ok = true;
0210     if (!attribute.isNull()) {
0211         times = attribute.value().toInt(&ok);
0212     } else if (!attributeNode.isNull()) {
0213         times = attributeNode.text().toInt(&ok);
0214         nodeTextRead = true;
0215     } else if (!nodeTextRead) {
0216         times = giftNode.text().toInt(&ok);
0217         if (!ok) {
0218             times = 1;
0219         }
0220     }
0221 
0222     // If only one brick to be placed: see if position is given
0223     QString position;
0224     if (times == 1) {
0225         attribute = giftNode.attributeNode(QStringLiteral("Position"));
0226         attributeNode = giftNode.firstChildElement(QStringLiteral("Position"));
0227         if (!attribute.isNull()) {
0228             position = attribute.value();
0229         } else if (!attributeNode.isNull()) {
0230             position = attributeNode.text();
0231             nodeTextRead = true;
0232         } else if (!nodeTextRead && giftNode.text().contains(QLatin1Char(','))) {
0233             position = giftNode.text();
0234             nodeTextRead = true;
0235         }
0236     }
0237 
0238     Q_EMIT newGift(giftType, times, position);
0239 }
0240 
0241 void LevelLoader::loadOldStyleLevel()
0242 {
0243     // Selecting the correct level
0244     m_level++;
0245 
0246     // Loading the levelset
0247     QString path = QStringLiteral("levelsets/%1.levelset").arg(m_levelname);
0248     path = QStandardPaths::locate(QStandardPaths::AppDataLocation, path);
0249     KConfig file(path, KConfig::SimpleConfig);
0250 
0251     QString levelName(QLatin1String("level") + QString::number(m_level));
0252 
0253     if (!file.hasGroup(levelName)) {
0254         // No more levels or no levels found
0255         return;
0256     }
0257 
0258     // Loading level information
0259     KConfigGroup lvl = file.group(levelName);
0260 
0261     // add bricks
0262 
0263     int y = 1;
0264     QString key(QLatin1String("line") + QString::number(y));
0265 
0266     while (lvl.hasKey(key)) {
0267         // one line of bricks to be converted
0268         QString line = lvl.readEntry(key, "error");
0269         if (line == QLatin1String("error")) {
0270             qCCritical(KBREAKOUT_General) << "Something strange happened!!\n";
0271             return;
0272         }
0273 
0274         qCDebug(KBREAKOUT_General) << line;
0275 
0276         if (line.size() > WIDTH) {
0277             qCCritical(KBREAKOUT_General) << "Invalid file: too many bricks\n";
0278         }
0279 
0280         Q_EMIT newLine(line, y);
0281 
0282         ++y;
0283         key = QLatin1String("line") + QString::number(y);
0284     }
0285 
0286     // add gifts
0287 
0288     for (int i = 0; i < GIFT_TYPES_COUNT; ++i) {
0289         key = GIFT_TYPES[i];
0290         if (!lvl.hasKey(key)) {
0291             continue;
0292         }
0293 
0294         QString line = lvl.readEntry(key, "error");
0295         if (line == QLatin1String("error")) {
0296             qCCritical(KBREAKOUT_General) << "Impossible reading " << m_level << ":" << key;
0297             return;
0298         }
0299         bool ok;
0300         int times = line.toInt(&ok);
0301         if (!ok) {
0302             qCCritical(KBREAKOUT_General) << m_levelname << ":" << key << " invalid number!!";
0303             continue;
0304         }
0305 
0306         Q_EMIT newGift(key, times, QString());
0307     }
0308 }
0309 
0310 #include "moc_levelloader.cpp"