diff --git a/include/project.h b/include/project.h index 2b467ee5..e4f6840b 100644 --- a/include/project.h +++ b/include/project.h @@ -210,6 +210,7 @@ public: QString findMetatileLabelsTileset(QString label); void setImportExportPath(QString filename); + static QString getExistingFilepath(QString filepath); void applyParsedLimits(); static int getNumTilesPrimary(); diff --git a/include/scripting.h b/include/scripting.h index ce435236..7fd2a170 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -55,7 +55,7 @@ public: static QJSValue version(QList versionNums); static QJSValue dimensions(int width, int height); static QJSValue position(int x, int y); - static QImage getImage(QString filepath); + static const QImage * getImage(const QString &filepath, bool useCache); static QJSValue dialogInput(QJSValue input, bool selectedOk); private: diff --git a/src/core/events.cpp b/src/core/events.cpp index bd74ec94..1b9605fb 100644 --- a/src/core/events.cpp +++ b/src/core/events.cpp @@ -151,10 +151,8 @@ void Event::setIcons() { } // Try to load custom icon - QFileInfo info(customIconPath); - if (info.isRelative()) { - customIconPath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + customIconPath); - } + QString validPath = Project::getExistingFilepath(customIconPath); + if (!validPath.isEmpty()) customIconPath = validPath; // Otherwise allow it to fail with the original path const QPixmap customIcon = QPixmap(customIconPath); if (customIcon.isNull()) { // Custom icon failed to load, use the default icon. diff --git a/src/editor.cpp b/src/editor.cpp index 1cd80291..014fa489 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -2278,22 +2278,20 @@ void Editor::setCollisionTabSpinBoxes(uint16_t collision, uint16_t elevation) { // Custom collision graphics may be provided by the user. void Editor::setCollisionGraphics() { - QString customPath = projectConfig.getCollisionSheetPath(); + QString filepath = projectConfig.getCollisionSheetPath(); QImage imgSheet; - if (customPath.isEmpty()) { + if (filepath.isEmpty()) { // No custom collision image specified, use the default. imgSheet = this->defaultCollisionImgSheet; } else { // Try to load custom collision image - QFileInfo info(customPath); - if (info.isRelative()) { - customPath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + customPath); - } - imgSheet = QImage(customPath); + QString validPath = Project::getExistingFilepath(filepath); + if (!validPath.isEmpty()) filepath = validPath; // Otherwise allow it to fail with the original path + imgSheet = QImage(filepath); if (imgSheet.isNull()) { // Custom collision image failed to load, use default - logWarn(QString("Failed to load custom collision image '%1', using default.").arg(customPath)); + logWarn(QString("Failed to load custom collision image '%1', using default.").arg(filepath)); imgSheet = this->defaultCollisionImgSheet; } } diff --git a/src/project.cpp b/src/project.cpp index beec9308..3370dacd 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -2747,6 +2747,20 @@ void Project::setImportExportPath(QString filename) this->importExportPath = QFileInfo(filename).absolutePath(); } +// If the provided filepath is an absolute path to an existing file, return filepath. +// If not, and the provided filepath is a relative path from the project dir to an existing file, return the relative path. +// Otherwise return empty string. +QString Project::getExistingFilepath(QString filepath) { + if (filepath.isEmpty() || QFile::exists(filepath)) + return filepath; + + filepath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + filepath); + if (QFile::exists(filepath)) + return filepath; + + return QString(); +} + // The values of some config fields can limit the values of other config fields // (for example, metatile attributes size limits the metatile attribute masks). // Others depend on information in the project (for example the default metatile ID diff --git a/src/scriptapi/scripting.cpp b/src/scriptapi/scripting.cpp index 109677f0..3ef50ba2 100644 --- a/src/scriptapi/scripting.cpp +++ b/src/scriptapi/scripting.cpp @@ -48,22 +48,20 @@ Scripting::Scripting(MainWindow *mainWindow) { void Scripting::loadModules(QStringList moduleFiles) { for (QString filepath : moduleFiles) { - QJSValue module = this->engine->importModule(filepath); - if (module.isError()) { - QString relativePath = QDir::cleanPath(userConfig.getProjectDir() + QDir::separator() + filepath); - module = this->engine->importModule(relativePath); - if (tryErrorJS(module)) { - QMessageBox messageBox(this->mainWindow); - messageBox.setText("Failed to load script"); - messageBox.setInformativeText(QString("An error occurred while loading custom script file '%1'").arg(filepath)); - messageBox.setDetailedText(getMostRecentError()); - messageBox.setIcon(QMessageBox::Warning); - messageBox.addButton(QMessageBox::Ok); - messageBox.exec(); - continue; - } - } + QString validPath = Project::getExistingFilepath(filepath); + if (!validPath.isEmpty()) filepath = validPath; // Otherwise allow it to fail with the original path + QJSValue module = this->engine->importModule(filepath); + if (tryErrorJS(module)) { + QMessageBox messageBox(this->mainWindow); + messageBox.setText("Failed to load script"); + messageBox.setInformativeText(QString("An error occurred while loading custom script file '%1'").arg(filepath)); + messageBox.setDetailedText(getMostRecentError()); + messageBox.setIcon(QMessageBox::Warning); + messageBox.addButton(QMessageBox::Ok); + messageBox.exec(); + continue; + } logInfo(QString("Successfully loaded custom script file '%1'").arg(filepath)); this->modules.append(module); } @@ -377,11 +375,22 @@ QJSEngine *Scripting::getEngine() { return instance->engine; } -QImage Scripting::getImage(QString filepath) { - const QImage * image = instance->imageCache.value(filepath, nullptr); - if (!image) { - image = new QImage(filepath); - instance->imageCache.insert(filepath, image); +const QImage * Scripting::getImage(const QString &inputFilepath, bool useCache) { + if (inputFilepath.isEmpty()) + return nullptr; + + const QImage * image; + if (useCache) { + // Try to retrieve image from the cache + image = instance->imageCache.value(inputFilepath, nullptr); + if (image) return image; } - return QImage(*image); + + const QString filepath = Project::getExistingFilepath(inputFilepath); + if (filepath.isEmpty()) + return nullptr; + + image = new QImage(filepath); + instance->imageCache.insert(inputFilepath, image); + return image; } diff --git a/src/ui/encountertabledelegates.cpp b/src/ui/encountertabledelegates.cpp index dc975c14..7824087e 100644 --- a/src/ui/encountertabledelegates.cpp +++ b/src/ui/encountertabledelegates.cpp @@ -21,9 +21,7 @@ void SpeciesComboDelegate::paint(QPainter *painter, const QStyleOptionViewItem & if (path.isEmpty()) { path = this->project->speciesToIconPath.value(species); } else { - QFileInfo info(path); - if (info.isRelative()) - path = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + path); + path = Project::getExistingFilepath(path); } QImage img(path); diff --git a/src/ui/overlay.cpp b/src/ui/overlay.cpp index 38e2b4de..4642afa1 100644 --- a/src/ui/overlay.cpp +++ b/src/ui/overlay.cpp @@ -202,11 +202,12 @@ bool Overlay::addPath(QList xCoords, QList yCoords, QString borderColo } bool Overlay::addImage(int x, int y, QString filepath, bool useCache, int width, int height, int xOffset, int yOffset, qreal hScale, qreal vScale, QList palette, bool setTransparency) { - QImage image = useCache ? Scripting::getImage(filepath) : QImage(filepath); - if (image.isNull()) { + const QImage * baseImage = Scripting::getImage(filepath, useCache); + if (!baseImage || baseImage->isNull()) { logError(QString("Failed to load image '%1'").arg(filepath)); return false; } + QImage image = *baseImage; int fullWidth = image.width(); int fullHeight = image.height(); diff --git a/src/ui/prefab.cpp b/src/ui/prefab.cpp index 9ad154f2..c5ad7f9b 100644 --- a/src/ui/prefab.cpp +++ b/src/ui/prefab.cpp @@ -28,14 +28,13 @@ void Prefab::loadPrefabs() { ParseUtil parser; QJsonDocument prefabDoc; - QFileInfo info(filepath); - if (info.isRelative()) { - filepath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + filepath); - } - if (!QFile::exists(filepath) || !parser.tryParseJsonFile(&prefabDoc, filepath)) { + + QString validPath = Project::getExistingFilepath(filepath); + if (validPath.isEmpty() || !parser.tryParseJsonFile(&prefabDoc, validPath)) { logError(QString("Failed to read prefab data from %1").arg(filepath)); return; } + filepath = validPath; QJsonArray prefabs = prefabDoc.array(); if (prefabs.size() == 0) {