Allow INCBIN regex to change, support INCGFX by default

This commit is contained in:
GriffinR
2026-04-10 01:23:26 -04:00
parent 061e1cc99f
commit 12436af1e3
10 changed files with 49 additions and 20 deletions

View File

@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project somewhat adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The MAJOR version number is bumped when there are **"Breaking Changes"** in the pret projects. For more on this, see [the manual page on breaking changes](https://huderlem.github.io/porymap/manual/breaking-changes.html).
## [Unreleased]
### Added
- Support `INCGFX` in addition to `INCBIN`. The regular expression for these statements is now available under `Project Settings`.
### Fixed
- Fix degraded image quality in exported timelapse gifs.
- Fix custom top-level data in the `encounters` object of `wild_encounters.json` being discarded if no `fields` data is present.

View File

@@ -244,6 +244,7 @@ enum ProjectIdentifier {
regex_music,
regex_encounter_types,
regex_terrain_types,
regex_incbin,
pals_output_extension,
tiles_output_extension,
};

View File

@@ -5,6 +5,7 @@
#include "log.h"
#include "orderedjson.h"
#include "orderedmap.h"
#include "regex.h"
#include <QString>
#include <QList>
@@ -73,6 +74,8 @@ public:
bool tryParseJsonFile(QJsonDocument *out, const QString &filepath, QString *error = nullptr);
bool tryParseOrderedJsonFile(poryjson::Json::object *out, const QString &filepath, QString *error = nullptr);
void setIncbinPattern(const QString& pattern);
static int getJsonLineNumber(const QString &filepath, const QString &searchText);
// Returns the 1-indexed line number for the definition of scriptLabel in the scripts file at filePath.
@@ -102,6 +105,10 @@ private:
QHash<QString, QString> fileCache;
QHash<QString, QStringList> errorMap;
QString incbinPattern = Regex::Pattern_INCBIN;
std::unique_ptr<QRegularExpression> incbinRegex = nullptr;
std::unique_ptr<QRegularExpression> incbinArrayRegex = nullptr;
// The maps of define names to values/expressions that are available while parsing C defines.
// As the parser reads and evaluates more defines it will update these maps accordingly.
QHash<QString, int> knownDefineValues;
@@ -140,7 +147,6 @@ private:
static const QRegularExpression re_poryScriptLabel;
static const QRegularExpression re_globalPoryScriptLabel;
static const QRegularExpression re_poryRawSection;
static const QString incbinRegexText;
};
#endif // PARSEUTIL_H

9
include/core/regex.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#ifndef REGEX_H
#define REGEX_H
namespace Regex {
constexpr auto Pattern_INCBIN = R"(INC(BIN|GFX)_\w+?\s*\(\s*\"(?<path>[^\"]*)\"[^\)]*\))";
};
#endif // REGEX_H

View File

@@ -5,6 +5,7 @@
#include "validator.h"
#include "utility.h"
#include "metatile.h"
#include "regex.h"
#include <QDir>
#include <QFile>
#include <QFormLayout>
@@ -133,6 +134,7 @@ const QMap<ProjectIdentifier, QPair<QString, QString>> ProjectConfig::defaultIde
{ProjectIdentifier::regex_music, {"regex_music", "\\b(SE|MUS)_"}},
{ProjectIdentifier::regex_encounter_types, {"regex_encounter_types", "\\bTILE_ENCOUNTER_"}},
{ProjectIdentifier::regex_terrain_types, {"regex_terrain_types", "\\bTILE_TERRAIN_"}},
{ProjectIdentifier::regex_incbin, {"regex_incbin", Regex::Pattern_INCBIN}},
// Other
{ProjectIdentifier::pals_output_extension, {"pals_output_extension", ".gbapal"}},
{ProjectIdentifier::tiles_output_extension, {"tiles_output_extension", ".4bpp.lz"}},

View File

@@ -16,7 +16,6 @@ const QRegularExpression ParseUtil::re_globalIncScriptLabel("\\b(?<label>[\\w_][
const QRegularExpression ParseUtil::re_poryScriptLabel("\\b(script)(\\((global|local)\\))?\\s*\\b(?<label>[\\w_][\\w\\d_]*)");
const QRegularExpression ParseUtil::re_globalPoryScriptLabel("\\b(script)(\\((global)\\))?\\s*\\b(?<label>[\\w_][\\w\\d_]*)");
const QRegularExpression ParseUtil::re_poryRawSection("\\b(raw)\\s*`(?<raw_script>[^`]*)");
const QString ParseUtil::incbinRegexText(R"(INCBIN_[US][0-9][0-9]?\s*\(\s*\"(?<path>[^\"]*)\"[^\)]*\))");
ParseUtil::ParseUtil() {
resetCDefines();
@@ -330,6 +329,15 @@ int ParseUtil::evaluatePostfix(const QList<Token> &postfix) {
return stack.size() ? stack.pop().value.toInt(nullptr, 0) : 0;
}
void ParseUtil::setIncbinPattern(const QString& pattern) {
this->incbinPattern = pattern;
// We need to regenerate regular expressions that depend on this pattern.
// We'll wait until the next time they're needed to do that.
this->incbinRegex = nullptr;
this->incbinArrayRegex = nullptr;
}
QString ParseUtil::readCIncbin(const QString &filename, const QString &label) {
return !label.isEmpty() ? readCIncbinMulti(filename).value(label) : QString();
}
@@ -340,9 +348,11 @@ QMap<QString, QString> ParseUtil::readCIncbinMulti(const QString &filename) {
this->file = filename;
this->text = loadTextFile(filename);
static const QRegularExpression regex(QString(R"((?<label>[\w]+)\s*(?:\[[^\]]*\])?\s*=\s*%1)").arg(this->incbinRegexText));
if (!this->incbinArrayRegex) {
this->incbinArrayRegex = std::make_unique<QRegularExpression>(QString(R"((?<label>[\w]+)\s*(?:\[[^\]]*\])?\s*=\s*%1)").arg(this->incbinPattern));
}
QRegularExpressionMatchIterator iter = regex.globalMatch(this->text);
QRegularExpressionMatchIterator iter = this->incbinArrayRegex->globalMatch(this->text);
while (iter.hasNext()) {
QRegularExpressionMatch match = iter.next();
QString label = match.captured("label");
@@ -382,8 +392,10 @@ QStringList ParseUtil::readCIncbinArray(const QString &filename, const QString &
}
// Extract incbin paths from the array
static const QRegularExpression re_incbin(this->incbinRegexText);
QRegularExpressionMatchIterator iter = re_incbin.globalMatch(arrayText);
if (!this->incbinRegex) {
this->incbinRegex = std::make_unique<QRegularExpression>(this->incbinPattern);
}
QRegularExpressionMatchIterator iter = this->incbinRegex->globalMatch(arrayText);
while (iter.hasNext()) {
paths.append(iter.next().captured("path"));
}

View File

@@ -172,8 +172,7 @@ bool RegionMap::loadLayout(poryjson::Json layoutJson) {
}
case LayoutFormat::CArray:
{
ParseUtil parser;
QString text = parser.readTextFile(fullPath(this->layout_path));
QString text = ParseUtil::readTextFile(fullPath(this->layout_path));
static const QRegularExpression re("(?<qual_1>static)?\\s?(?<qual_2>const)?\\s?(?<type>[A-Za-z0-9_]+)?\\s+(?<label>[A-Za-z0-9_]+)"
"(\\[(?<const_1>[A-Za-z0-9_]+)\\])(\\[(?<const_2>[A-Za-z0-9_]+)\\])(\\[(?<const_3>[A-Za-z0-9_]+)\\])\\s+=");

View File

@@ -32,7 +32,9 @@ int Project::num_pals_total = 13;
Project::Project(QObject *parent) :
QObject(parent)
{ }
{
this->parser.setIncbinPattern(projectConfig.getIdentifier(ProjectIdentifier::regex_incbin));
}
Project::~Project()
{

View File

@@ -337,17 +337,16 @@ bool Prefab::tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version,
return false;
}
ParseUtil parser;
QString content;
switch (version) {
case BaseGameVersion::pokeruby:
content = parser.readTextFile(":/text/prefabs_default_ruby.json");
content = ParseUtil::readTextFile(":/text/prefabs_default_ruby.json");
break;
case BaseGameVersion::pokefirered:
content = parser.readTextFile(":/text/prefabs_default_firered.json");
content = ParseUtil::readTextFile(":/text/prefabs_default_firered.json");
break;
case BaseGameVersion::pokeemerald:
content = parser.readTextFile(":/text/prefabs_default_emerald.json");
content = ParseUtil::readTextFile(":/text/prefabs_default_emerald.json");
break;
default:
content = QString();

View File

@@ -123,21 +123,17 @@ bool RegionMapEditor::saveRegionMapEntries() {
}
void buildEmeraldDefaults(poryjson::Json &json) {
ParseUtil parser;
QString emeraldDefault = parser.readTextFile(":/text/region_map_default_emerald.json");
QString emeraldDefault = ParseUtil::readTextFile(":/text/region_map_default_emerald.json");
json = poryjson::Json::parse(emeraldDefault);
}
void buildRubyDefaults(poryjson::Json &json) {
ParseUtil parser;
QString emeraldDefault = parser.readTextFile(":/text/region_map_default_ruby.json");
QString emeraldDefault = ParseUtil::readTextFile(":/text/region_map_default_ruby.json");
json = poryjson::Json::parse(emeraldDefault);
}
void buildFireredDefaults(poryjson::Json &json) {
ParseUtil parser;
QString fireredDefault = parser.readTextFile(":/text/region_map_default_firered.json");
QString fireredDefault = ParseUtil::readTextFile(":/text/region_map_default_firered.json");
json = poryjson::Json::parse(fireredDefault);
}