From 6ffcb58e5ed87b0e32a6b8bc259b18b101ad0aa4 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 14 Oct 2022 18:55:18 -0400 Subject: [PATCH] Convert JSON values by type, except events --- include/core/parseutil.h | 4 ++ include/project.h | 2 +- src/core/parseutil.cpp | 54 +++++++++++++++-- src/mainwindow.cpp | 4 +- src/project.cpp | 118 +++++++++++++++++++------------------ src/ui/prefab.cpp | 22 +++---- src/ui/regionmapeditor.cpp | 12 ++-- 7 files changed, 134 insertions(+), 82 deletions(-) diff --git a/include/core/parseutil.h b/include/core/parseutil.h index 5cb07366..0200d7ae 100644 --- a/include/core/parseutil.h +++ b/include/core/parseutil.h @@ -71,7 +71,11 @@ public: static QString removeLineComments(QString text, const QStringList &commentSymbols); static QStringList splitShellCommand(QStringView command); + static int gameStringToInt(QString gameString, bool * ok = nullptr); static bool gameStringToBool(QString gameString, bool * ok = nullptr); + static QString jsonToQString(QJsonValue value, bool * ok = nullptr); + static int jsonToInt(QJsonValue value, bool * ok = nullptr); + static bool jsonToBool(QJsonValue value, bool * ok = nullptr); private: QString root; diff --git a/include/project.h b/include/project.h index 0828284f..cdc81615 100644 --- a/include/project.h +++ b/include/project.h @@ -134,7 +134,7 @@ public: bool readSpeciesIconPaths(); QMap speciesToIconPath; - QMap getTopLevelMapFields(); + QSet getTopLevelMapFields(); bool loadMapData(Map*); bool readMapLayouts(); bool loadLayout(MapLayout *); diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index 63fbd84d..f765ff79 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -402,13 +402,17 @@ QMap ParseUtil::readNamedIndexCArray(const QString &filename, return map; } -bool ParseUtil::gameStringToBool(QString gameString, bool * ok) { +int ParseUtil::gameStringToInt(QString gameString, bool * ok) { if (ok) *ok = true; if (QString::compare(gameString, "TRUE", Qt::CaseInsensitive) == 0) - return true; + return 1; if (QString::compare(gameString, "FALSE", Qt::CaseInsensitive) == 0) - return false; - return gameString.toInt(ok) != 0; + return 0; + return gameString.toInt(ok); +} + +bool ParseUtil::gameStringToBool(QString gameString, bool * ok) { + return gameStringToInt(gameString, ok) != 0; } QMap> ParseUtil::readCStructs(const QString &filename, const QString &label, const QHash memberMap) { @@ -520,6 +524,48 @@ bool ParseUtil::ensureFieldsExist(const QJsonObject &obj, const QList & return true; } +// QJsonValues are strictly typed, and so will not attempt any implicit conversions. +// The below functions are for attempting to convert a JSON value read from the user's +// project to a QString, int, or bool (whichever Porymap expects). +QString ParseUtil::jsonToQString(QJsonValue value, bool * ok) { + if (ok) *ok = true; + switch (value.type()) + { + case QJsonValue::String: return value.toString(); + case QJsonValue::Double: return QString::number(value.toInt()); + case QJsonValue::Bool: return QString::number(value.toBool()); + default: break; + } + if (ok) *ok = false; + return QString(); +} + +int ParseUtil::jsonToInt(QJsonValue value, bool * ok) { + if (ok) *ok = true; + switch (value.type()) + { + case QJsonValue::String: return ParseUtil::gameStringToInt(value.toString(), ok); + case QJsonValue::Double: return value.toInt(); + case QJsonValue::Bool: return value.toBool(); + default: break; + } + if (ok) *ok = false; + return 0; +} + +bool ParseUtil::jsonToBool(QJsonValue value, bool * ok) { + if (ok) *ok = true; + switch (value.type()) + { + case QJsonValue::String: return ParseUtil::gameStringToBool(value.toString(), ok); + case QJsonValue::Double: return value.toInt() != 0; + case QJsonValue::Bool: return value.toBool(); + default: break; + } + if (ok) *ok = false; + return false; +} + int ParseUtil::getScriptLineNumber(const QString &filePath, const QString &scriptLabel) { if (scriptLabel.isEmpty()) return 0; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9d8d87e5..41d0a7a2 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1574,8 +1574,8 @@ void MainWindow::paste() { } QJsonArray metatilesArray = pasteObject["metatile_selection"].toArray(); QJsonArray collisionsArray = pasteObject["collision_selection"].toArray(); - int width = pasteObject["width"].toInt(); - int height = pasteObject["height"].toInt(); + int width = ParseUtil::jsonToInt(pasteObject["width"]); + int height = ParseUtil::jsonToInt(pasteObject["height"]); QList metatiles; QList> collisions; for (auto tile : metatilesArray) { diff --git a/src/project.cpp b/src/project.cpp index 0a3007ec..7632191e 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -142,36 +142,37 @@ void Project::setNewMapConnections(Map *map) { map->connections.clear(); } -static QMap defaultTopLevelMapFields { - {"id", true}, - {"name", true}, - {"layout", true}, - {"music", true}, - {"region_map_section", true}, - {"requires_flash", true}, - {"weather", true}, - {"map_type", true}, - {"show_map_name", true}, - {"battle_scene", true}, - {"connections", true}, - {"object_events", true}, - {"warp_events", true}, - {"coord_events", true}, - {"bg_events", true}, - {"shared_events_map", true}, - {"shared_scripts_map", true}, +const QSet defaultTopLevelMapFields = { + "id", + "name", + "layout", + "music", + "region_map_section", + "requires_flash", + "weather", + "map_type", + "show_map_name", + "battle_scene", + "connections", + "object_events", + "warp_events", + "coord_events", + "bg_events", + "shared_events_map", + "shared_scripts_map", + "test", }; -QMap Project::getTopLevelMapFields() { - QMap topLevelMapFields = defaultTopLevelMapFields; +QSet Project::getTopLevelMapFields() { + QSet topLevelMapFields = defaultTopLevelMapFields; if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { - topLevelMapFields.insert("allow_cycling", true); - topLevelMapFields.insert("allow_escaping", true); - topLevelMapFields.insert("allow_running", true); + topLevelMapFields.insert("allow_cycling"); + topLevelMapFields.insert("allow_escaping"); + topLevelMapFields.insert("allow_running"); } if (projectConfig.getFloorNumberEnabled()) { - topLevelMapFields.insert("floor_number", true); + topLevelMapFields.insert("floor_number"); } return topLevelMapFields; } @@ -190,24 +191,25 @@ bool Project::loadMapData(Map* map) { QJsonObject mapObj = mapDoc.object(); - map->song = mapObj["music"].toString(); - map->layoutId = mapObj["layout"].toString(); - map->location = mapObj["region_map_section"].toString(); - map->requiresFlash = mapObj["requires_flash"].toBool(); - map->weather = mapObj["weather"].toString(); - map->type = mapObj["map_type"].toString(); - map->show_location = mapObj["show_map_name"].toBool(); - map->battle_scene = mapObj["battle_scene"].toString(); + map->song = ParseUtil::jsonToQString(mapObj["music"]); + map->layoutId = ParseUtil::jsonToQString(mapObj["layout"]); + map->location = ParseUtil::jsonToQString(mapObj["region_map_section"]); + map->requiresFlash = ParseUtil::jsonToBool(mapObj["requires_flash"]); + map->weather = ParseUtil::jsonToQString(mapObj["weather"]); + map->type = ParseUtil::jsonToQString(mapObj["map_type"]); + map->show_location = ParseUtil::jsonToBool(mapObj["show_map_name"]); + map->battle_scene = ParseUtil::jsonToQString(mapObj["battle_scene"]); + if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { - map->allowBiking = mapObj["allow_cycling"].toBool(); - map->allowEscaping = mapObj["allow_escaping"].toBool(); - map->allowRunning = mapObj["allow_running"].toBool(); + map->allowBiking = ParseUtil::jsonToBool(mapObj["allow_cycling"]); + map->allowEscaping = ParseUtil::jsonToBool(mapObj["allow_escaping"]); + map->allowRunning = ParseUtil::jsonToBool(mapObj["allow_running"]); } if (projectConfig.getFloorNumberEnabled()) { - map->floorNumber = mapObj["floor_number"].toInt(); + map->floorNumber = ParseUtil::jsonToInt(mapObj["floor_number"]); } - map->sharedEventsMap = mapObj["shared_events_map"].toString(); - map->sharedScriptsMap = mapObj["shared_scripts_map"].toString(); + map->sharedEventsMap = ParseUtil::jsonToQString(mapObj["shared_events_map"]); + map->sharedScriptsMap = ParseUtil::jsonToQString(mapObj["shared_scripts_map"]); // Events map->events[EventGroup::Object].clear(); @@ -394,9 +396,9 @@ bool Project::loadMapData(Map* map) { for (int i = 0; i < connectionsArr.size(); i++) { QJsonObject connectionObj = connectionsArr[i].toObject(); MapConnection *connection = new MapConnection; - connection->direction = connectionObj["direction"].toString(); - connection->offset = QString::number(connectionObj["offset"].toInt()); - QString mapConstant = connectionObj["map"].toString(); + connection->direction = ParseUtil::jsonToQString(connectionObj["direction"]); + connection->offset = ParseUtil::jsonToInt(connectionObj["offset"]); + QString mapConstant = ParseUtil::jsonToQString(connectionObj["map"]); if (mapConstantsToMapNames.contains(mapConstant)) { connection->map_name = mapConstantsToMapNames.value(mapConstant); map->connections.append(connection); @@ -407,10 +409,10 @@ bool Project::loadMapData(Map* map) { } // Check for custom fields - QMap baseFields = this->getTopLevelMapFields(); + QSet baseFields = this->getTopLevelMapFields(); for (QString key : mapObj.keys()) { if (!baseFields.contains(key)) { - map->customHeaders.insert(key, mapObj[key].toString()); + map->customHeaders.insert(key, ParseUtil::jsonToQString(mapObj[key])); } } @@ -430,7 +432,7 @@ QString Project::readMapLayoutId(QString map_name) { } QJsonObject mapObj = mapDoc.object(); - return mapObj["layout"].toString(); + return ParseUtil::jsonToQString(mapObj["layout"]); } QString Project::readMapLocation(QString map_name) { @@ -446,7 +448,7 @@ QString Project::readMapLocation(QString map_name) { } QJsonObject mapObj = mapDoc.object(); - return mapObj["region_map_section"].toString(); + return ParseUtil::jsonToQString(mapObj["region_map_section"]); } void Project::setNewMapHeader(Map* map, int mapIndex) { @@ -518,7 +520,7 @@ bool Project::readMapLayouts() { return false; } - layoutsLabel = layoutsObj["layouts_table_label"].toString(); + layoutsLabel = ParseUtil::jsonToQString(layoutsObj["layouts_table_label"]); if (layoutsLabel.isNull()) { layoutsLabel = "gMapLayouts"; logWarn(QString("'layouts_table_label' value is missing from %1. Defaulting to %2") @@ -550,36 +552,36 @@ bool Project::readMapLayouts() { return false; } MapLayout *layout = new MapLayout(); - layout->id = layoutObj["id"].toString(); + layout->id = ParseUtil::jsonToQString(layoutObj["id"]); 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->name = ParseUtil::jsonToQString(layoutObj["name"]); 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(); + int lwidth = ParseUtil::jsonToInt(layoutObj["width"]); 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(); + int lheight = ParseUtil::jsonToInt(layoutObj["height"]); 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); if (useCustomBorderSize) { - int bwidth = layoutObj["border_width"].toInt(); + int bwidth = ParseUtil::jsonToInt(layoutObj["border_width"]); if (bwidth <= 0) { // 0 is an expected border width/height that should be handled, GF used it for the RS layouts in FRLG logWarn(QString("Invalid layout 'border_width' value '%1' on layout %2 in %3. Must be greater than 0. Using default (%4) instead.").arg(bwidth).arg(i).arg(layoutsFilepath).arg(DEFAULT_BORDER_WIDTH)); bwidth = DEFAULT_BORDER_WIDTH; } layout->border_width = QString::number(bwidth); - int bheight = layoutObj["border_height"].toInt(); + int bheight = ParseUtil::jsonToInt(layoutObj["border_height"]); if (bheight <= 0) { logWarn(QString("Invalid layout 'border_height' value '%1' on layout %2 in %3. Must be greater than 0. Using default (%4) instead.").arg(bheight).arg(i).arg(layoutsFilepath).arg(DEFAULT_BORDER_HEIGHT)); bheight = DEFAULT_BORDER_HEIGHT; @@ -589,22 +591,22 @@ bool Project::readMapLayouts() { layout->border_width = QString::number(DEFAULT_BORDER_WIDTH); layout->border_height = QString::number(DEFAULT_BORDER_HEIGHT); } - layout->tileset_primary_label = layoutObj["primary_tileset"].toString(); + layout->tileset_primary_label = ParseUtil::jsonToQString(layoutObj["primary_tileset"]); 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(); + layout->tileset_secondary_label = ParseUtil::jsonToQString(layoutObj["secondary_tileset"]); 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(); + layout->border_path = ParseUtil::jsonToQString(layoutObj["border_filepath"]); 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(); + layout->blockdata_path = ParseUtil::jsonToQString(layoutObj["blockdata_filepath"]); if (layout->blockdata_path.isEmpty()) { logError(QString("Missing 'blockdata_filepath' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); return false; @@ -1840,12 +1842,12 @@ bool Project::readMapGroups() { QStringList maps; QStringList groups; for (int groupIndex = 0; groupIndex < mapGroupOrder.size(); groupIndex++) { - QString groupName = mapGroupOrder.at(groupIndex).toString(); + QString groupName = ParseUtil::jsonToQString(mapGroupOrder.at(groupIndex)); QJsonArray mapNames = mapGroupsObj.value(groupName).toArray(); groupedMaps.append(QStringList()); groups.append(groupName); for (int j = 0; j < mapNames.size(); j++) { - QString mapName = mapNames.at(j).toString(); + QString mapName = ParseUtil::jsonToQString(mapNames.at(j)); mapGroups.insert(mapName, groupIndex); groupedMaps[groupIndex].append(mapName); maps.append(mapName); diff --git a/src/ui/prefab.cpp b/src/ui/prefab.cpp index e9e3b3b0..87bae518 100644 --- a/src/ui/prefab.cpp +++ b/src/ui/prefab.cpp @@ -46,14 +46,14 @@ void Prefab::loadPrefabs() { if (prefabObj.isEmpty()) continue; - int width = prefabObj["width"].toInt(); - int height = prefabObj["height"].toInt(); + int width = ParseUtil::jsonToInt(prefabObj["width"]); + int height = ParseUtil::jsonToInt(prefabObj["height"]); if (width <= 0 || height <= 0) continue; - QString name = prefabObj["name"].toString(); - QString primaryTileset = prefabObj["primary_tileset"].toString(); - QString secondaryTileset = prefabObj["secondary_tileset"].toString(); + QString name = ParseUtil::jsonToQString(prefabObj["name"]); + QString primaryTileset = ParseUtil::jsonToQString(prefabObj["primary_tileset"]); + QString secondaryTileset = ParseUtil::jsonToQString(prefabObj["secondary_tileset"]); MetatileSelection selection; selection.dimensions = QPoint(width, height); @@ -65,17 +65,17 @@ void Prefab::loadPrefabs() { QJsonArray metatiles = prefabObj["metatiles"].toArray(); for (int j = 0; j < metatiles.size(); j++) { QJsonObject metatileObj = metatiles[j].toObject(); - int x = metatileObj["x"].toInt(); - int y = metatileObj["y"].toInt(); + int x = ParseUtil::jsonToInt(metatileObj["x"]); + int y = ParseUtil::jsonToInt(metatileObj["y"]); if (x < 0 || x >= width || y < 0 || y >= height) continue; int index = y * width + x; - int metatileId = metatileObj["metatile_id"].toInt(); + int metatileId = ParseUtil::jsonToInt(metatileObj["metatile_id"]); if (metatileId < 0 || metatileId >= Project::getNumMetatilesTotal()) continue; - selection.metatileItems[index].metatileId = metatileObj["metatile_id"].toInt(); - selection.collisionItems[index].collision = metatileObj["collision"].toInt(); - selection.collisionItems[index].elevation = metatileObj["elevation"].toInt(); + selection.metatileItems[index].metatileId = ParseUtil::jsonToInt(metatileObj["metatile_id"]); + selection.collisionItems[index].collision = ParseUtil::jsonToInt(metatileObj["collision"]); + selection.collisionItems[index].elevation = ParseUtil::jsonToInt(metatileObj["elevation"]); selection.metatileItems[index].enabled = true; selection.collisionItems[index].enabled = true; } diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp index dc2c458e..8257231c 100644 --- a/src/ui/regionmapeditor.cpp +++ b/src/ui/regionmapeditor.cpp @@ -125,13 +125,13 @@ bool RegionMapEditor::loadRegionMapEntries() { for (auto entryRef : object["map_sections"].toArray()) { QJsonObject entryObject = entryRef.toObject(); - QString entryMapSection = entryObject["map_section"].toString(); + QString entryMapSection = ParseUtil::jsonToQString(entryObject["map_section"]); MapSectionEntry entry; - entry.name = entryObject["name"].toString(); - entry.x = entryObject["x"].toInt(); - entry.y = entryObject["y"].toInt(); - entry.width = entryObject["width"].toInt(); - entry.height = entryObject["height"].toInt(); + entry.name = ParseUtil::jsonToQString(entryObject["name"]); + entry.x = ParseUtil::jsonToInt(entryObject["x"]); + entry.y = ParseUtil::jsonToInt(entryObject["y"]); + entry.width = ParseUtil::jsonToInt(entryObject["width"]); + entry.height = ParseUtil::jsonToInt(entryObject["height"]); entry.valid = true; this->region_map_entries[entryMapSection] = entry; }