diff --git a/include/core/parseutil.h b/include/core/parseutil.h index 24efcbd8..9d7a6f40 100644 --- a/include/core/parseutil.h +++ b/include/core/parseutil.h @@ -48,8 +48,10 @@ public: QStringList readCArray(const QString &text, const QString &label); QMap readNamedIndexCArray(const QString &text, const QString &label); QString readCIncbin(const QString &text, const QString &label); + 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 = { }); QList getLabelMacros(const QList&, const QString&); QStringList getLabelValues(const QList&, const QString&); bool tryParseJsonFile(QJsonDocument *out, const QString &filepath); @@ -70,7 +72,6 @@ public: static QStringList splitShellCommand(QStringView command); static bool gameStringToBool(QString gameString); - static QMap> readCStructs(const QString &filePath, const QString &target = "", const QHash memberMap = { }); private: QString root; diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index 64ce33f0..36de8224 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -270,6 +270,30 @@ QString ParseUtil::readCIncbin(const QString &filename, const QString &label) { return path; } +QStringList ParseUtil::readCIncbinArray(const QString &filename, const QString &label) { + QStringList paths; + + if (label.isNull()) { + return paths; + } + + this->text = readTextFile(this->root + "/" + filename); + + // Get the text starting after the label all the way to the definition's end + QRegularExpression re(QString("\\b%1\\b(.*?)};").arg(label), QRegularExpression::DotMatchesEverythingOption); + QRegularExpressionMatch arrayMatch = re.match(this->text); + if (!arrayMatch.hasMatch()) + return paths; + + // Extract incbin paths from the array + re.setPattern("INCBIN_[US][0-9][0-9]?\\(\\s*\"([^\"]*)\"\\s*\\)"); + QRegularExpressionMatchIterator iter = re.globalMatch(arrayMatch.captured(1)); + while (iter.hasNext()) { + paths.append(iter.next().captured(1)); + } + return paths; +} + QMap ParseUtil::readCDefines(const QString &filename, const QStringList &prefixes, QMap allDefines) @@ -389,14 +413,15 @@ bool ParseUtil::gameStringToBool(QString gameString) { return num != 0; } -QMap> ParseUtil::readCStructs(const QString &filePath, const QString &target, 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; for (auto it = structs.begin(); it != structs.end(); it++) { QString structLabel = QString::fromStdString(it->first); - if (!target.isEmpty() && target != structLabel) continue; // Speed up parsing if only looking for a particular symbol + if (!label.isEmpty() && label != structLabel) continue; // Speed up parsing if only looking for a particular symbol QMap values; int i = 0; for (const fex::ArrayValue &v : it->second.values()) { diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp index 31096308..d8ad083a 100644 --- a/src/core/tileset.cpp +++ b/src/core/tileset.cpp @@ -169,6 +169,7 @@ bool Tileset::appendToHeaders(QString root, QString friendlyName, bool usingAsm) } // TODO: Interpet isSecondary to remove primary argument here and below +// TODO: friendlyName.toLower() is not the usual format for tileset folders bool Tileset::appendToGraphics(QString root, QString friendlyName, bool primary, bool usingAsm) { QString graphicsFile = root + "/" + (usingAsm ? projectConfig.getFilePath(ProjectFilePath::tilesets_graphics_asm) : projectConfig.getFilePath(ProjectFilePath::tilesets_graphics)); diff --git a/src/project.cpp b/src/project.cpp index d676d289..d9ecddfe 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -1153,7 +1153,7 @@ Tileset* Project::loadTileset(QString label, Tileset *tileset) { } } else { // Read C tileset header - QMap> structs = ParseUtil::readCStructs(this->root + "/" + projectConfig.getFilePath(ProjectFilePath::tilesets_headers), label); + QMap> structs = parser.readCStructs(projectConfig.getFilePath(ProjectFilePath::tilesets_headers), label); if (!structs.contains(label)) { return nullptr; } @@ -1519,12 +1519,26 @@ void Project::readTilesetPaths(Tileset* tileset) { tileset->metatiles_path = root + '/' + metatiles_values.value(0).section('"', 1, 1); if (!metatile_attrs_values.isEmpty()) tileset->metatile_attrs_path = root + '/' + metatile_attrs_values.value(0).section('"', 1, 1); - for (const auto &value : palettes_values) { + for (const auto &value : palettes_values) tileset->palettePaths.append(this->fixPalettePath(root + '/' + value.section('"', 1, 1))); - } } else { // Read C tileset data files - // TODO + const QString graphicsFile = projectConfig.getFilePath(ProjectFilePath::tilesets_graphics); + const QString metatilesFile = projectConfig.getFilePath(ProjectFilePath::tilesets_metatiles); + + const QString tilesImagePath = parser.readCIncbin(graphicsFile, tileset->tiles_label); + const QStringList palettePaths = parser.readCIncbinArray(graphicsFile, tileset->palettes_label); + const QString metatilesPath = parser.readCIncbin(metatilesFile, tileset->metatiles_label); + const QString metatileAttrsPath = parser.readCIncbin(metatilesFile, tileset->metatile_attrs_label); + + if (!tilesImagePath.isEmpty()) + tileset->tilesImagePath = this->fixGraphicPath(root + '/' + tilesImagePath); + if (!metatilesPath.isEmpty()) + tileset->metatiles_path = root + '/' + metatilesPath; + if (!metatileAttrsPath.isEmpty()) + tileset->metatile_attrs_path = root + '/' + metatileAttrsPath; + for (const auto &path : palettePaths) + tileset->palettePaths.append(this->fixPalettePath(root + '/' + path)); } // Construct the expected path of the tileset's graphics, in case Porymap couldn't find any paths. @@ -1934,13 +1948,13 @@ bool Project::readTilesetLabels() { this->tilesetLabels.insert("secondary", secondaryTilesets); this->tilesetLabelsOrdered.clear(); - QString filename = this->root + "/" + projectConfig.getFilePath(ProjectFilePath::tilesets_headers); - QFileInfo fileInfo(filename); + QString filename = projectConfig.getFilePath(ProjectFilePath::tilesets_headers); + QFileInfo fileInfo(this->root + "/" + filename); if (!fileInfo.exists() || !fileInfo.isFile()) { // If the tileset headers file is missing, the user may still have the old assembly format. this->usingAsmTilesets = true; - QString asm_filename = this->root + "/" + projectConfig.getFilePath(ProjectFilePath::tilesets_headers_asm); - QString text = parser.readTextFile(asm_filename); + QString asm_filename = projectConfig.getFilePath(ProjectFilePath::tilesets_headers_asm); + QString text = parser.readTextFile(this->root + "/" + asm_filename); if (text.isEmpty()) { logError(QString("Failed to read tileset labels from '%1' or '%2'.").arg(filename).arg(asm_filename)); return false; @@ -1959,7 +1973,7 @@ bool Project::readTilesetLabels() { filename = asm_filename; // For error reporting further down } else { this->usingAsmTilesets = false; - QMap> structs = ParseUtil::readCStructs(filename); + QMap> structs = parser.readCStructs(filename); QStringList labels = structs.keys(); for (const auto tilesetLabel : labels) { if (tilesetLabel.isEmpty()) continue; @@ -2525,8 +2539,8 @@ bool Project::readEventGraphics() { {14, "images"}, }; - QString filepath = root + "/" + projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx_info); - QMap> gfxInfos = ParseUtil::readCStructs(filepath, "", gfxInfoMemberMap); + QString filepath = projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx_info); + QMap> gfxInfos = parser.readCStructs(filepath, "", gfxInfoMemberMap); for (QString gfxName : gfxNames) { EventGraphics * eventGraphics = new EventGraphics;