diff --git a/include/core/parseutil.h b/include/core/parseutil.h index 9d7a6f40..96d56c27 100644 --- a/include/core/parseutil.h +++ b/include/core/parseutil.h @@ -51,7 +51,7 @@ public: QStringList readCIncbinArray(const QString &filename, const QString &label); QMap readCDefines(const QString &filename, const QStringList &prefixes, QMap = { }); QStringList readCDefinesSorted(const QString&, const QStringList&, const QMap& = { }); - QMap> readCStructs(const QString &, const QString & = "", const QHash = { }); + QMap> readCStructs(const QString &, const QString & = "", const QHash = { }); QList getLabelMacros(const QList&, const QString&); QStringList getLabelValues(const QList&, const QString&); bool tryParseJsonFile(QJsonDocument *out, const QString &filepath); diff --git a/include/core/tileset.h b/include/core/tileset.h index b210f8d4..f3ba70ba 100644 --- a/include/core/tileset.h +++ b/include/core/tileset.h @@ -37,6 +37,7 @@ public: static QList> getBlockPalettes(Tileset*, Tileset*, bool useTruePalettes = false); static QList getPalette(int, Tileset*, Tileset*, bool useTruePalettes = false); static bool metatileIsValid(uint16_t metatileId, Tileset *, Tileset *); + static QHash getHeaderMemberMap(bool usingAsm); static QString getExpectedDir(QString tilesetName, bool isSecondary); QString getExpectedDir(); bool appendToHeaders(QString root, QString friendlyName, bool usingAsm); diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index 36de8224..485154bc 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -413,16 +413,17 @@ bool ParseUtil::gameStringToBool(QString gameString) { return num != 0; } -QMap> ParseUtil::readCStructs(const QString &filename, const QString &label, const QHash memberMap) { +QMap> ParseUtil::readCStructs(const QString &filename, const QString &label, const QHash memberMap) { QString filePath = this->root + "/" + filename; auto cParser = fex::Parser(); auto tokens = fex::Lexer().LexFile(filePath.toStdString()); auto structs = cParser.ParseTopLevelObjects(tokens); - QMap> structMaps; + QMap> structMaps; for (auto it = structs.begin(); it != structs.end(); it++) { QString structLabel = QString::fromStdString(it->first); + if (structLabel.isEmpty()) continue; if (!label.isEmpty() && label != structLabel) continue; // Speed up parsing if only looking for a particular symbol - QMap values; + QHash values; int i = 0; for (const fex::ArrayValue &v : it->second.values()) { if (v.type() == fex::ArrayValue::Type::kValuePair) { @@ -430,7 +431,7 @@ QMap> ParseUtil::readCStructs(const QString &fil QString value = QString::fromStdString(v.pair().second->string_value()); values.insert(key, value); } else { - // For backwards compatibility with structs that don't specify member names. + // For compatibility with structs that don't specify member names. if (memberMap.contains(i)) values.insert(memberMap.value(i), QString::fromStdString(v.string_value())); } diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp index 0434a2fe..96344f55 100644 --- a/src/core/tileset.cpp +++ b/src/core/tileset.cpp @@ -255,3 +255,23 @@ QString Tileset::getExpectedDir(QString tilesetName, bool isSecondary) const QString basePath = projectConfig.getFilePath(ProjectFilePath::data_tilesets_folders) + category + "/"; return basePath + tilesetName.replace("gTileset_", "").replace(re, "\\1_\\2").toLower(); } + +// Get the expected positions of the members in struct Tileset. +// Used when parsing asm tileset data, or C tileset data that's missing initializers. +QHash Tileset::getHeaderMemberMap(bool usingAsm) +{ + // The asm header has a padding field that needs to be skipped + int paddingOffset = usingAsm ? 1 : 0; + + // The position of metatileAttributes changes between games + bool isPokefirered = (projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered); + int metatileAttrPosition = (isPokefirered ? 6 : 5) + paddingOffset; + + auto map = QHash(); + map.insert(1, "isSecondary"); + map.insert(2 + paddingOffset, "tiles"); + map.insert(3 + paddingOffset, "palettes"); + map.insert(4 + paddingOffset, "metatiles"); + map.insert(metatileAttrPosition, "metatileAttributes"); + return map; +} diff --git a/src/project.cpp b/src/project.cpp index 59056a24..8cd96c34 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -1131,6 +1131,7 @@ bool Project::loadLayoutTilesets(MapLayout *layout) { } Tileset* Project::loadTileset(QString label, Tileset *tileset) { + auto memberMap = Tileset::getHeaderMemberMap(this->usingAsmTilesets); if (this->usingAsmTilesets) { // Read asm tileset header. Backwards compatibility const QStringList values = parser.getLabelValues(parser.parseAsm(projectConfig.getFilePath(ProjectFilePath::tilesets_headers_asm)), label); @@ -1140,27 +1141,22 @@ Tileset* Project::loadTileset(QString label, Tileset *tileset) { if (tileset == nullptr) { tileset = new Tileset; } - // Skips 0 (isCompressed), 2 (padding), and 6/7 (callback) tileset->name = label; - tileset->is_secondary = values.value(1); - tileset->tiles_label = values.value(3); - tileset->palettes_label = values.value(4); - tileset->metatiles_label = values.value(5); - if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered) { - tileset->metatile_attrs_label = values.value(7); - } else { - tileset->metatile_attrs_label = values.value(6); - } + tileset->is_secondary = values.value(memberMap.key("isSecondary")); + tileset->tiles_label = values.value(memberMap.key("tiles")); + tileset->palettes_label = values.value(memberMap.key("palettes")); + tileset->metatiles_label = values.value(memberMap.key("metatiles")); + tileset->metatile_attrs_label = values.value(memberMap.key("metatileAttributes")); } else { // Read C tileset header - QMap> structs = parser.readCStructs(projectConfig.getFilePath(ProjectFilePath::tilesets_headers), label); + const auto structs = parser.readCStructs(projectConfig.getFilePath(ProjectFilePath::tilesets_headers), label, memberMap); if (!structs.contains(label)) { return nullptr; } if (tileset == nullptr) { tileset = new Tileset; } - QMap tilesetAttributes = structs[label]; + const auto tilesetAttributes = structs[label]; tileset->name = label; tileset->is_secondary = tilesetAttributes.value("isSecondary"); tileset->tiles_label = tilesetAttributes.value("tiles"); @@ -1967,7 +1963,7 @@ bool Project::readTilesetLabels() { filename = asm_filename; // For error reporting further down } else { this->usingAsmTilesets = false; - QMap> structs = parser.readCStructs(filename); + const auto structs = parser.readCStructs(filename, "", Tileset::getHeaderMemberMap(this->usingAsmTilesets)); QStringList labels = structs.keys(); for (const auto tilesetLabel : labels) { if (tilesetLabel.isEmpty()) continue; @@ -1975,7 +1971,7 @@ bool Project::readTilesetLabels() { this->tilesetLabels["secondary"].append(tilesetLabel); else this->tilesetLabels["primary"].append(tilesetLabel); - this->tilesetLabelsOrdered.append(tilesetLabel); + this->tilesetLabelsOrdered.append(tilesetLabel); // TODO: This is alphabetical, AdvanceMap import wants the vanilla order } } @@ -2534,7 +2530,7 @@ bool Project::readEventGraphics() { }; QString filepath = projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx_info); - QMap> gfxInfos = parser.readCStructs(filepath, "", gfxInfoMemberMap); + const auto gfxInfos = parser.readCStructs(filepath, "", gfxInfoMemberMap); for (QString gfxName : gfxNames) { EventGraphics * eventGraphics = new EventGraphics; @@ -2542,7 +2538,7 @@ bool Project::readEventGraphics() { if (!gfxInfos.contains(info_label)) continue; - QMapgfxInfoAttributes = gfxInfos[info_label]; + const auto gfxInfoAttributes = gfxInfos[info_label]; eventGraphics->inanimate = ParseUtil::gameStringToBool(gfxInfoAttributes.value("inanimate")); QString pic_label = gfxInfoAttributes.value("images");