From 79c74b8814d640e8550a178cde29e28c024b00f8 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Tue, 11 Feb 2020 18:34:08 -0600 Subject: [PATCH] Validate layouts --- include/core/parseutil.h | 1 + include/mainwindow.h | 2 +- include/project.h | 2 +- src/core/parseutil.cpp | 10 ++++++ src/mainwindow.cpp | 28 ++++++++++------ src/project.cpp | 72 ++++++++++++++++++++++++++++++++++++---- 6 files changed, 96 insertions(+), 19 deletions(-) diff --git a/include/core/parseutil.h b/include/core/parseutil.h index df2492a6..38157c31 100644 --- a/include/core/parseutil.h +++ b/include/core/parseutil.h @@ -52,6 +52,7 @@ public: QList* getLabelMacros(QList*, QString); QStringList* getLabelValues(QList*, QString); bool tryParseJsonFile(QJsonDocument *out, QString filepath); + bool ensureFieldsExist(QJsonObject obj, QList fields); private: QString root; diff --git a/include/mainwindow.h b/include/mainwindow.h index d4ff8eca..7224f485 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -186,7 +186,7 @@ private: bool setMap(QString, bool scrollTreeView = false); void redrawMapScene(); - void loadDataStructures(); + bool loadDataStructures(); void populateMapList(); void sortMapList(); QString getExistingDirectory(QString); diff --git a/include/project.h b/include/project.h index f2194e83..20458dbe 100644 --- a/include/project.h +++ b/include/project.h @@ -100,7 +100,7 @@ public: QMap getTopLevelMapFields(); bool loadMapData(Map*); - void readMapLayouts(); + bool readMapLayouts(); void loadMapLayout(Map*); void loadMapTilesets(Map*); void loadTilesetAssets(Tileset*); diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index 8d683c7a..0182f9b0 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -410,3 +410,13 @@ bool ParseUtil::tryParseJsonFile(QJsonDocument *out, QString filepath) { *out = jsonDoc; return true; } + +bool ParseUtil::ensureFieldsExist(QJsonObject obj, QList fields) { + for (QString field : fields) { + if (!obj.contains(field)) { + logError(QString("JSON object is missing field '%1'.").arg(field)); + return false; + } + } + return true; +} diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2b913435..468ef0de 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -281,17 +281,20 @@ bool MainWindow::openProject(QString dir) { if (!already_open) { editor->project = new Project; editor->project->set_root(dir); - setWindowTitle(editor->project->getProjectTitle()); - loadDataStructures(); - populateMapList(); - success = setMap(getDefaultMap(), true); + success = loadDataStructures(); + if (success) { + populateMapList(); + success = setMap(getDefaultMap(), true); + } } else { - setWindowTitle(editor->project->getProjectTitle()); - loadDataStructures(); - populateMapList(); + success = loadDataStructures(); + if (success) { + populateMapList(); + } } if (success) { + setWindowTitle(editor->project->getProjectTitle()); this->statusBar()->showMessage(QString("Opened project %1").arg(nativeDir)); } else { this->statusBar()->showMessage(QString("Failed to open project %1").arg(nativeDir)); @@ -301,8 +304,7 @@ bool MainWindow::openProject(QString dir) { } bool MainWindow::isProjectOpen() { - return (editor && editor != nullptr) - && (editor->project && editor->project != nullptr); + return editor != nullptr && editor->project != nullptr; } QString MainWindow::getDefaultMap() { @@ -593,9 +595,12 @@ void MainWindow::on_checkBox_AllowEscapeRope_clicked(bool checked) } } -void MainWindow::loadDataStructures() { +bool MainWindow::loadDataStructures() { Project *project = editor->project; - project->readMapLayouts(); + bool success = project->readMapLayouts(); + if (!success) { + return false; + } project->readRegionMapSections(); project->readItemNames(); project->readFlagNames(); @@ -625,6 +630,7 @@ void MainWindow::loadDataStructures() { ui->comboBox_Weather->addItems(*project->weatherNames); ui->comboBox_BattleScene->addItems(*project->mapBattleScenes); ui->comboBox_Type->addItems(*project->mapTypes); + return true; } void MainWindow::populateMapList() { diff --git a/src/project.cpp b/src/project.cpp index f173bd3a..c3bded77 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -404,7 +404,7 @@ void Project::loadMapLayout(Map* map) { loadMapBorder(map); } -void Project::readMapLayouts() { +bool Project::readMapLayouts() { mapLayouts.clear(); mapLayoutsTable.clear(); @@ -412,24 +412,83 @@ void Project::readMapLayouts() { QJsonDocument layoutsDoc; if (!parser.tryParseJsonFile(&layoutsDoc, layoutsFilepath)) { logError(QString("Failed to read map layouts from %1").arg(layoutsFilepath)); - return; + return false; } QJsonObject layoutsObj = layoutsDoc.object(); - layoutsLabel = layoutsObj["layouts_table_label"].toString(); - QJsonArray layouts = layoutsObj["layouts"].toArray(); + if (layouts.size() == 0) { + logError(QString("'layouts' array is missing from %1.").arg(layoutsFilepath)); + return false; + } + + layoutsLabel = layoutsObj["layouts_table_label"].toString(); + if (layoutsLabel.isNull()) { + layoutsLabel = "gMapLayouts"; + logWarn(QString("'layouts_table_label' value is missing from %1. Defaulting to %2") + .arg(layoutsFilepath) + .arg(layoutsLabel)); + } + + QList requiredFields = QList{ + "id", + "name", + "width", + "height", + "primary_tileset", + "secondary_tileset", + "border_filepath", + "blockdata_filepath", + }; for (int i = 0; i < layouts.size(); i++) { QJsonObject layoutObj = layouts[i].toObject(); + if (!parser.ensureFieldsExist(layoutObj, requiredFields)) { + logError(QString("Layout %1 is missing field(s) in %2.").arg(i).arg(layoutsFilepath)); + return false; + } MapLayout *layout = new MapLayout(); layout->id = layoutObj["id"].toString(); + if (layout->id.isEmpty()) { + logError(QString("Missing 'id' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); + return false; + } layout->name = layoutObj["name"].toString(); - layout->width = QString::number(layoutObj["width"].toInt()); - layout->height = QString::number(layoutObj["height"].toInt()); + if (layout->name.isEmpty()) { + logError(QString("Missing 'name' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); + return false; + } + int lwidth = layoutObj["width"].toInt(); + if (lwidth <= 0) { + logError(QString("Invalid layout 'width' value '%1' on layout %2 in %3. Must be greater than 0.").arg(lwidth).arg(i).arg(layoutsFilepath)); + return false; + } + layout->width = QString::number(lwidth); + int lheight = layoutObj["height"].toInt(); + if (lheight <= 0) { + logError(QString("Invalid layout 'height' value '%1' on layout %2 in %3. Must be greater than 0.").arg(lheight).arg(i).arg(layoutsFilepath)); + return false; + } + layout->height = QString::number(lheight); layout->tileset_primary_label = layoutObj["primary_tileset"].toString(); + if (layout->tileset_primary_label.isEmpty()) { + logError(QString("Missing 'primary_tileset' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); + return false; + } layout->tileset_secondary_label = layoutObj["secondary_tileset"].toString(); + if (layout->tileset_secondary_label.isEmpty()) { + logError(QString("Missing 'secondary_tileset' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); + return false; + } layout->border_path = layoutObj["border_filepath"].toString(); + if (layout->border_path.isEmpty()) { + logError(QString("Missing 'border_filepath' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); + return false; + } layout->blockdata_path = layoutObj["blockdata_filepath"].toString(); + if (layout->border_path.isEmpty()) { + logError(QString("Missing 'blockdata_filepath' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); + return false; + } mapLayouts.insert(layout->id, layout); mapLayoutsTable.append(layout->id); } @@ -439,6 +498,7 @@ void Project::readMapLayouts() { mapLayoutsMaster.detach(); mapLayoutsTableMaster = mapLayoutsTable; mapLayoutsTableMaster.detach(); + return true; } void Project::saveMapLayouts() {