diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index d7c4abe1..50e03e88 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -2931,7 +2931,7 @@ - + @@ -3222,9 +3222,9 @@ Open Config Folder - + - Import Map from Advance Map 1.92... + Import Layout from Advance Map 1.92... diff --git a/include/core/advancemapparser.h b/include/core/advancemapparser.h new file mode 100644 index 00000000..c3cb76cb --- /dev/null +++ b/include/core/advancemapparser.h @@ -0,0 +1,18 @@ +#ifndef ADVANCEMAPPARSER_H +#define ADVANCEMAPPARSER_H + +#include +#include +#include + +class Project; +class Layout; +class Metatile; + +namespace AdvanceMapParser { + Layout *parseLayout(const QString &filepath, bool *error, const Project *project); + QList parseMetatiles(const QString &filepath, bool *error, bool primaryTileset); + QList parsePalette(const QString &filepath, bool *error); +}; + +#endif // ADVANCEMAPPARSER_H diff --git a/include/core/mapparser.h b/include/core/mapparser.h deleted file mode 100644 index 4032154a..00000000 --- a/include/core/mapparser.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef MAPPARSER_H -#define MAPPARSER_H - -#include "maplayout.h" -#include "project.h" -#include -#include - -class MapParser -{ -public: - MapParser(); - Layout *parse(QString filepath, bool *error, Project *project); -}; - -#endif // MAPPARSER_H diff --git a/include/core/metatileparser.h b/include/core/metatileparser.h deleted file mode 100644 index b85e5b36..00000000 --- a/include/core/metatileparser.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#ifndef METATILEPARSER_H -#define METATILEPARSER_H - -#include "metatile.h" -#include - -namespace MetatileParser { - QList parse(QString filepath, bool *error, bool primaryTileset); -} - -#endif // METATILEPARSER_H diff --git a/include/core/parseutil.h b/include/core/parseutil.h index e3df8375..d184646b 100644 --- a/include/core/parseutil.h +++ b/include/core/parseutil.h @@ -5,6 +5,7 @@ #include "heallocation.h" #include "log.h" #include "orderedjson.h" +#include "orderedmap.h" #include #include @@ -57,7 +58,7 @@ public: QMap readCDefinesByRegex(const QString &filename, const QStringList ®exList); QMap readCDefinesByName(const QString &filename, const QStringList &names); QStringList readCDefineNames(const QString &filename, const QStringList ®exList); - QMap> readCStructs(const QString &, const QString & = "", const QHash = { }); + tsl::ordered_map> 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/lib/fex/parser.h b/include/lib/fex/parser.h index 6a6b9e43..c79b34a2 100644 --- a/include/lib/fex/parser.h +++ b/include/lib/fex/parser.h @@ -9,6 +9,7 @@ #include "array_value.h" #include "define_statement.h" #include "lexer.h" +#include "orderedmap.h" namespace fex { @@ -19,9 +20,9 @@ namespace fex std::vector Parse(std::vector tokens); std::vector ParseTopLevelArrays(std::vector tokens); - std::map ParseTopLevelObjects(std::vector tokens); + tsl::ordered_map ParseTopLevelObjects(std::vector tokens); - std::map ReadDefines(const std::string &filename, std::vector matching); + tsl::ordered_map ReadDefines(const std::string &filename, std::vector matching); private: int EvaluateExpression(std::vector tokens); diff --git a/include/mainwindow.h b/include/mainwindow.h index a8dbca1f..53889dff 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -194,7 +194,6 @@ private slots: void onNewMapGroupCreated(const QString &groupName); void onNewLayoutCreated(Layout *layout); void onMapLoaded(Map *map); - void importMapFromAdvanceMap1_92(); void onMapRulerStatusChanged(const QString &); void applyUserShortcuts(); void markMapEdited(); @@ -238,7 +237,7 @@ private slots: void on_action_Export_Map_Image_triggered(); void on_actionExport_Stitched_Map_Image_triggered(); void on_actionExport_Map_Timelapse_Image_triggered(); - void on_actionImport_Map_from_Advance_Map_1_92_triggered(); + void on_actionImport_Layout_from_Advance_Map_1_92_triggered(); void on_pushButton_AddConnection_clicked(); void on_button_OpenDiveMap_clicked(); diff --git a/include/project.h b/include/project.h index 19b757db..c1a36a80 100644 --- a/include/project.h +++ b/include/project.h @@ -189,7 +189,7 @@ public: void saveTilesetMetatiles(Tileset*); void saveTilesetTilesImage(Tileset*); void saveTilesetPalettes(Tileset*); - void appendTilesetLabel(QString label, QString isSecondaryStr); + void appendTilesetLabel(const QString &label, const QString &isSecondaryStr); bool readTilesetLabels(); bool readTilesetMetatileLabels(); bool readRegionMapSections(); diff --git a/include/ui/newlayoutdialog.h b/include/ui/newlayoutdialog.h index 49047cc7..c90a408b 100644 --- a/include/ui/newlayoutdialog.h +++ b/include/ui/newlayoutdialog.h @@ -20,7 +20,7 @@ class NewLayoutDialog : public QDialog public: explicit NewLayoutDialog(QWidget *parent = nullptr, Project *project = nullptr); ~NewLayoutDialog(); - void init(Layout *); + void copyFrom(const Layout &); void accept() override; signals: @@ -39,7 +39,6 @@ private: void saveSettings(); bool isExistingLayout() const; - void useLayoutSettings(Layout *mapLayout); private slots: //void on_comboBox_Layout_currentTextChanged(const QString &text);//TODO diff --git a/porymap.pro b/porymap.pro index 1cc9497c..1dfaf4b1 100644 --- a/porymap.pro +++ b/porymap.pro @@ -21,7 +21,8 @@ QMAKE_TARGET_BUNDLE_PREFIX = com.pret VERSION = 5.4.1 DEFINES += PORYMAP_VERSION=\\\"$$VERSION\\\" -SOURCES += src/core/block.cpp \ +SOURCES += src/core/advancemapparser.cpp \ + src/core/block.cpp \ src/core/bitpacker.cpp \ src/core/blockdata.cpp \ src/core/events.cpp \ @@ -32,9 +33,7 @@ SOURCES += src/core/block.cpp \ src/core/mapconnection.cpp \ src/core/mapheader.cpp \ src/core/maplayout.cpp \ - src/core/mapparser.cpp \ src/core/metatile.cpp \ - src/core/metatileparser.cpp \ src/core/network.cpp \ src/core/paletteutil.cpp \ src/core/parseutil.cpp \ @@ -127,7 +126,8 @@ SOURCES += src/core/block.cpp \ src/ui/updatepromoter.cpp \ src/ui/wildmonchart.cpp -HEADERS += include/core/block.h \ +HEADERS += include/core/advancemapparser.h \ + include/core/block.h \ include/core/bitpacker.h \ include/core/blockdata.h \ include/core/events.h \ @@ -139,9 +139,7 @@ HEADERS += include/core/block.h \ include/core/mapconnection.h \ include/core/mapheader.h \ include/core/maplayout.h \ - include/core/mapparser.h \ include/core/metatile.h \ - include/core/metatileparser.h \ include/core/network.h \ include/core/paletteutil.h \ include/core/parseutil.h \ diff --git a/src/core/advancemapparser.cpp b/src/core/advancemapparser.cpp new file mode 100644 index 00000000..6af7476e --- /dev/null +++ b/src/core/advancemapparser.cpp @@ -0,0 +1,217 @@ +#include "advancemapparser.h" +#include "log.h" +#include "project.h" +#include "maplayout.h" + +Layout *AdvanceMapParser::parseLayout(const QString &filepath, bool *error, const Project *project) +{ + QFile file(filepath); + if (!file.open(QIODevice::ReadOnly)) { + *error = true; + logError(QString("Could not open Advance Map 1.92 Map .map file '%1': ").arg(filepath) + file.errorString()); + return nullptr; + } + + QByteArray in = file.readAll(); + file.close(); + + if (in.length() < 20 || in.length() % 2 != 0) { + *error = true; + logError(QString("Advance Map 1.92 Map .map file '%1' is an unexpected size.").arg(filepath)); + return nullptr; + } + + int borderWidth = static_cast(in.at(16)); // 0 in RSE .map files + int borderHeight = static_cast(in.at(17)); // 0 in RSE .map files + int numBorderTiles = borderWidth * borderHeight; // 0 if RSE + + int mapDataOffset = 20 + (numBorderTiles * 2); // FRLG .map files store border metatile data after the header + int mapWidth = static_cast(in.at(0)) | + (static_cast(in.at(1)) << 8) | + (static_cast(in.at(2)) << 16) | + (static_cast(in.at(3)) << 24); + int mapHeight = static_cast(in.at(4)) | + (static_cast(in.at(5)) << 8) | + (static_cast(in.at(6)) << 16) | + (static_cast(in.at(7)) << 24); + int mapPrimaryTilesetNum = static_cast(in.at(8)) | + (static_cast(in.at(9)) << 8) | + (static_cast(in.at(10)) << 16) | + (static_cast(in.at(11)) << 24); + int mapSecondaryTilesetNum = static_cast(in.at(12)) | + (static_cast(in.at(13)) << 8) | + (static_cast(in.at(14)) << 16) | + (static_cast(in.at(15)) << 24); + + int numMetatiles = mapWidth * mapHeight; + int expectedFileSize = 20 + (numBorderTiles * 2) + (numMetatiles * 2); + if (in.length() != expectedFileSize) { + *error = true; + logError(QString(".map file is an unexpected size. Expected %1 bytes, but it has %2 bytes.").arg(expectedFileSize).arg(in.length())); + return nullptr; + } + + Blockdata blockdata; + for (int i = mapDataOffset; (i + 1) < in.length(); i += 2) { + uint16_t word = static_cast((in[i] & 0xff) + ((in[i + 1] & 0xff) << 8)); + blockdata.append(word); + } + + Blockdata border; + if (numBorderTiles != 0) { + for (int i = 20; (i + 1) < mapDataOffset; i += 2) { + uint16_t word = static_cast((in[i] & 0xff) + ((in[i + 1] & 0xff) << 8)); + border.append(word); + } + } + + Layout *mapLayout = new Layout(); + mapLayout->width = mapWidth; + mapLayout->height = mapHeight; + mapLayout->border_width = (borderWidth == 0) ? DEFAULT_BORDER_WIDTH : borderWidth; + mapLayout->border_height = (borderHeight == 0) ? DEFAULT_BORDER_HEIGHT : borderHeight; + + const QList tilesets = project->tilesetLabelsOrdered; + + if (mapPrimaryTilesetNum > tilesets.size()) + mapLayout->tileset_primary_label = project->getDefaultPrimaryTilesetLabel(); + else + mapLayout->tileset_primary_label = tilesets.at(mapPrimaryTilesetNum); + + if (mapSecondaryTilesetNum > tilesets.size()) + mapLayout->tileset_secondary_label = project->getDefaultSecondaryTilesetLabel(); + else + mapLayout->tileset_secondary_label = tilesets.at(mapSecondaryTilesetNum); + + mapLayout->blockdata = blockdata; + + if (!border.isEmpty()) { + mapLayout->border = border; + } + + return mapLayout; +} + +QList AdvanceMapParser::parseMetatiles(const QString &filepath, bool *error, bool primaryTileset) +{ + QFile file(filepath); + if (!file.open(QIODevice::ReadOnly)) { + *error = true; + logError(QString("Could not open Advance Map 1.92 Metatile .bvd file '%1': ").arg(filepath) + file.errorString()); + return { }; + } + + QByteArray in = file.readAll(); + file.close(); + + if (in.length() < 9 || in.length() % 2 != 0) { + *error = true; + logError(QString("Advance Map 1.92 Metatile .bvd file '%1' is an unexpected size.").arg(filepath)); + return { }; + } + + int projIdOffset = in.length() - 4; + int metatileSize = 16; + BaseGameVersion version; + if (in.at(projIdOffset + 0) == 'R' + && in.at(projIdOffset + 1) == 'S' + && in.at(projIdOffset + 2) == 'E' + && in.at(projIdOffset + 3) == ' ') { + // ruby and emerald are handled equally here. + version = BaseGameVersion::pokeemerald; + } else if (in.at(projIdOffset + 0) == 'F' + && in.at(projIdOffset + 1) == 'R' + && in.at(projIdOffset + 2) == 'L' + && in.at(projIdOffset + 3) == 'G') { + version = BaseGameVersion::pokefirered; + } else { + *error = true; + logError(QString("Detected unsupported game type from .bvd file. Last 4 bytes of file must be 'RSE ' or 'FRLG'.")); + return { }; + } + + int attrSize = Metatile::getDefaultAttributesSize(version); + int maxMetatiles = primaryTileset ? Project::getNumMetatilesPrimary() : Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary(); + int numMetatiles = static_cast(in.at(0)) | + (static_cast(in.at(1)) << 8) | + (static_cast(in.at(2)) << 16) | + (static_cast(in.at(3)) << 24); + if (numMetatiles > maxMetatiles) { + *error = true; + logError(QString(".bvd file contains data for %1 metatiles, but the maximum number of metatiles is %2.").arg(numMetatiles).arg(maxMetatiles)); + return { }; + } + if (numMetatiles < 1) { + *error = true; + logError(QString(".bvd file contains no data for metatiles.")); + return { }; + } + + int expectedFileSize = 4 + (metatileSize * numMetatiles) + (attrSize * numMetatiles) + 4; + if (in.length() != expectedFileSize) { + *error = true; + logError(QString(".bvd file is an unexpected size. Expected %1 bytes, but it has %2 bytes.").arg(expectedFileSize).arg(in.length())); + return { }; + } + + QList metatiles; + for (int i = 0; i < numMetatiles; i++) { + Metatile *metatile = new Metatile(); + QList tiles; + for (int j = 0; j < 8; j++) { + int metatileOffset = 4 + i * metatileSize + j * 2; + Tile tile(static_cast( + static_cast(in.at(metatileOffset)) | + (static_cast(in.at(metatileOffset + 1)) << 8))); + tiles.append(tile); + } + + // AdvanceMap .bvd files only contain 8 tiles of data per metatile. + // If the user has triple-layer metatiles enabled we need to fill the remaining 4 tiles ourselves. + if (projectConfig.tripleLayerMetatilesEnabled) { + Tile tile = Tile(); + for (int j = 0; j < 4; j++) + tiles.append(tile); + } + + int attrOffset = 4 + (numMetatiles * metatileSize) + (i * attrSize); + uint32_t attributes = 0; + for (int j = 0; j < attrSize; j++) + attributes |= static_cast(in.at(attrOffset + j)) << (8 * j); + metatile->setAttributes(attributes, version); + metatile->tiles = tiles; + metatiles.append(metatile); + } + + return metatiles; +} + +QList AdvanceMapParser::parsePalette(const QString &filepath, bool *error) { + QFile file(filepath); + if (!file.open(QIODevice::ReadOnly)) { + *error = true; + logError(QString("Could not open Advance Map 1.92 palette file '%1': ").arg(filepath) + file.errorString()); + return QList(); + } + + QByteArray in = file.readAll(); + file.close(); + + if (in.length() % 4 != 0) { + *error = true; + logError(QString("Advance Map 1.92 palette file '%1' had an unexpected format. File's length must be a multiple of 4, but the length is %2.").arg(filepath).arg(in.length())); + return QList(); + } + + QList palette; + int i = 0; + while (i < in.length()) { + unsigned char red = qMin(qMax(static_cast(in.at(i + 0)), 0u), 255u); + unsigned char green = qMin(qMax(static_cast(in.at(i + 1)), 0u), 255u); + unsigned char blue = qMin(qMax(static_cast(in.at(i + 2)), 0u), 255u); + palette.append(qRgb(red, green, blue)); + i += 4; + } + + return palette; +} diff --git a/src/core/mapparser.cpp b/src/core/mapparser.cpp deleted file mode 100644 index 3d4258bd..00000000 --- a/src/core/mapparser.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "mapparser.h" -#include "config.h" -#include "log.h" -#include "project.h" - -MapParser::MapParser() -{ -} - -Layout *MapParser::parse(QString filepath, bool *error, Project *project) -{ - QFile file(filepath); - if (!file.open(QIODevice::ReadOnly)) { - *error = true; - logError(QString("Could not open Advance Map 1.92 Map .map file '%1': ").arg(filepath) + file.errorString()); - return nullptr; - } - - QByteArray in = file.readAll(); - file.close(); - - if (in.length() < 20 || in.length() % 2 != 0) { - *error = true; - logError(QString("Advance Map 1.92 Map .map file '%1' is an unexpected size.").arg(filepath)); - return nullptr; - } - - int borderWidth = static_cast(in.at(16)); // 0 in RSE .map files - int borderHeight = static_cast(in.at(17)); // 0 in RSE .map files - int numBorderTiles = borderWidth * borderHeight; // 0 if RSE - - int mapDataOffset = 20 + (numBorderTiles * 2); // FRLG .map files store border metatile data after the header - int mapWidth = static_cast(in.at(0)) | - (static_cast(in.at(1)) << 8) | - (static_cast(in.at(2)) << 16) | - (static_cast(in.at(3)) << 24); - int mapHeight = static_cast(in.at(4)) | - (static_cast(in.at(5)) << 8) | - (static_cast(in.at(6)) << 16) | - (static_cast(in.at(7)) << 24); - int mapPrimaryTilesetNum = static_cast(in.at(8)) | - (static_cast(in.at(9)) << 8) | - (static_cast(in.at(10)) << 16) | - (static_cast(in.at(11)) << 24); - int mapSecondaryTilesetNum = static_cast(in.at(12)) | - (static_cast(in.at(13)) << 8) | - (static_cast(in.at(14)) << 16) | - (static_cast(in.at(15)) << 24); - - int numMetatiles = mapWidth * mapHeight; - int expectedFileSize = 20 + (numBorderTiles * 2) + (numMetatiles * 2); - if (in.length() != expectedFileSize) { - *error = true; - logError(QString(".map file is an unexpected size. Expected %1 bytes, but it has %2 bytes.").arg(expectedFileSize).arg(in.length())); - return nullptr; - } - - Blockdata blockdata; - for (int i = mapDataOffset; (i + 1) < in.length(); i += 2) { - uint16_t word = static_cast((in[i] & 0xff) + ((in[i + 1] & 0xff) << 8)); - blockdata.append(word); - } - - Blockdata border; - if (numBorderTiles != 0) { - for (int i = 20; (i + 1) < mapDataOffset; i += 2) { - uint16_t word = static_cast((in[i] & 0xff) + ((in[i + 1] & 0xff) << 8)); - border.append(word); - } - } - - Layout *mapLayout = new Layout(); - mapLayout->width = mapWidth; - mapLayout->height = mapHeight; - mapLayout->border_width = (borderWidth == 0) ? DEFAULT_BORDER_WIDTH : borderWidth; - mapLayout->border_height = (borderHeight == 0) ? DEFAULT_BORDER_HEIGHT : borderHeight; - - QList tilesets = project->tilesetLabelsOrdered; - - if (mapPrimaryTilesetNum > tilesets.size()) - mapLayout->tileset_primary_label = tilesets.at(0); - else - mapLayout->tileset_primary_label = tilesets.at(mapPrimaryTilesetNum); - - if (mapSecondaryTilesetNum > tilesets.size()) - mapLayout->tileset_secondary_label = tilesets.at(1); - else - mapLayout->tileset_secondary_label = tilesets.at(mapSecondaryTilesetNum); - - mapLayout->blockdata = blockdata; - - if (!border.isEmpty()) { - mapLayout->border = border; - } - - return mapLayout; -} diff --git a/src/core/metatileparser.cpp b/src/core/metatileparser.cpp deleted file mode 100644 index 104b757a..00000000 --- a/src/core/metatileparser.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "metatileparser.h" -#include "config.h" -#include "log.h" -#include "project.h" -#include - -QList MetatileParser::parse(QString filepath, bool *error, bool primaryTileset) -{ - QFile file(filepath); - if (!file.open(QIODevice::ReadOnly)) { - *error = true; - logError(QString("Could not open Advance Map 1.92 Metatile .bvd file '%1': ").arg(filepath) + file.errorString()); - return { }; - } - - QByteArray in = file.readAll(); - file.close(); - - if (in.length() < 9 || in.length() % 2 != 0) { - *error = true; - logError(QString("Advance Map 1.92 Metatile .bvd file '%1' is an unexpected size.").arg(filepath)); - return { }; - } - - int projIdOffset = in.length() - 4; - int metatileSize = 16; - BaseGameVersion version; - if (in.at(projIdOffset + 0) == 'R' - && in.at(projIdOffset + 1) == 'S' - && in.at(projIdOffset + 2) == 'E' - && in.at(projIdOffset + 3) == ' ') { - // ruby and emerald are handled equally here. - version = BaseGameVersion::pokeemerald; - } else if (in.at(projIdOffset + 0) == 'F' - && in.at(projIdOffset + 1) == 'R' - && in.at(projIdOffset + 2) == 'L' - && in.at(projIdOffset + 3) == 'G') { - version = BaseGameVersion::pokefirered; - } else { - *error = true; - logError(QString("Detected unsupported game type from .bvd file. Last 4 bytes of file must be 'RSE ' or 'FRLG'.")); - return { }; - } - - int attrSize = Metatile::getDefaultAttributesSize(version); - int maxMetatiles = primaryTileset ? Project::getNumMetatilesPrimary() : Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary(); - int numMetatiles = static_cast(in.at(0)) | - (static_cast(in.at(1)) << 8) | - (static_cast(in.at(2)) << 16) | - (static_cast(in.at(3)) << 24); - if (numMetatiles > maxMetatiles) { - *error = true; - logError(QString(".bvd file contains data for %1 metatiles, but the maximum number of metatiles is %2.").arg(numMetatiles).arg(maxMetatiles)); - return { }; - } - if (numMetatiles < 1) { - *error = true; - logError(QString(".bvd file contains no data for metatiles.")); - return { }; - } - - int expectedFileSize = 4 + (metatileSize * numMetatiles) + (attrSize * numMetatiles) + 4; - if (in.length() != expectedFileSize) { - *error = true; - logError(QString(".bvd file is an unexpected size. Expected %1 bytes, but it has %2 bytes.").arg(expectedFileSize).arg(in.length())); - return { }; - } - - QList metatiles; - for (int i = 0; i < numMetatiles; i++) { - Metatile *metatile = new Metatile(); - QList tiles; - for (int j = 0; j < 8; j++) { - int metatileOffset = 4 + i * metatileSize + j * 2; - Tile tile(static_cast( - static_cast(in.at(metatileOffset)) | - (static_cast(in.at(metatileOffset + 1)) << 8))); - tiles.append(tile); - } - - // AdvanceMap .bvd files only contain 8 tiles of data per metatile. - // If the user has triple-layer metatiles enabled we need to fill the remaining 4 tiles ourselves. - if (projectConfig.tripleLayerMetatilesEnabled) { - Tile tile = Tile(); - for (int j = 0; j < 4; j++) - tiles.append(tile); - } - - int attrOffset = 4 + (numMetatiles * metatileSize) + (i * attrSize); - uint32_t attributes = 0; - for (int j = 0; j < attrSize; j++) - attributes |= static_cast(in.at(attrOffset + j)) << (8 * j); - metatile->setAttributes(attributes, version); - metatile->tiles = tiles; - metatiles.append(metatile); - } - - return metatiles; -} diff --git a/src/core/paletteutil.cpp b/src/core/paletteutil.cpp index da281ce6..929336b2 100644 --- a/src/core/paletteutil.cpp +++ b/src/core/paletteutil.cpp @@ -1,4 +1,5 @@ #include "paletteutil.h" +#include "advancemapparser.h" #include "log.h" #include #include @@ -6,7 +7,6 @@ QList parsePal(QString filepath, bool *error); QList parseJASC(QString filepath, bool *error); -QList parseAdvanceMapPal(QString filepath, bool *error); QList parseAdobeColorTable(QString filepath, bool *error); QList parseTileLayerPro(QString filepath, bool *error); QList parseAdvancePaletteEditor(QString filepath, bool *error); @@ -81,7 +81,7 @@ QList parsePal(QString filepath, bool *error) { return parseJASC(filepath, error); } else { file.close(); - return parseAdvanceMapPal(filepath, error); + return AdvanceMapParser::parsePalette(filepath, error); } } @@ -152,38 +152,6 @@ QList parseJASC(QString filepath, bool *error) { return palette; } -QList parseAdvanceMapPal(QString filepath, bool *error) { - QFile file(filepath); - if (!file.open(QIODevice::ReadOnly)) { - *error = true; - logError(QString("Could not open Advance Map 1.92 palette file '%1': ").arg(filepath) + file.errorString()); - return QList(); - } - - QByteArray in = file.readAll(); - file.close(); - - if (in.length() % 4 != 0) { - *error = true; - logError(QString("Advance Map 1.92 palette file '%1' had an unexpected format. File's length must be a multiple of 4, but the length is %2.").arg(filepath).arg(in.length())); - return QList(); - } - - QList palette; - int i = 0; - while (i < in.length()) { - unsigned char red = static_cast(in.at(i)); - unsigned char green = static_cast(in.at(i + 1)); - unsigned char blue = static_cast(in.at(i + 2)); - palette.append(qRgb(clampColorValue(red), - clampColorValue(green), - clampColorValue(blue))); - i += 4; - } - - return palette; -} - QList parseAdobeColorTable(QString filepath, bool *error) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index 9664fdc7..1aff9cd5 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -593,13 +593,13 @@ bool ParseUtil::gameStringToBool(QString gameString, bool * ok) { return gameStringToInt(gameString, ok) != 0; } -QMap> ParseUtil::readCStructs(const QString &filename, const QString &label, const QHash memberMap) { +tsl::ordered_map> 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++) { + auto topLevelObjects = cParser.ParseTopLevelObjects(tokens); + tsl::ordered_map> structs; + for (auto it = topLevelObjects.begin(); it != topLevelObjects.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 @@ -617,9 +617,9 @@ QMap> ParseUtil::readCStructs(const QString &fi } i++; } - structMaps.insert(structLabel, values); + structs[structLabel] = values; } - return structMaps; + return structs; } QList ParseUtil::getLabelMacros(const QList &list, const QString &label) { diff --git a/src/lib/fex/parser.cpp b/src/lib/fex/parser.cpp index bb5c90a8..1c010528 100644 --- a/src/lib/fex/parser.cpp +++ b/src/lib/fex/parser.cpp @@ -337,9 +337,9 @@ namespace fex return DefineStatement(identifer, value); } - std::map Parser::ReadDefines(const std::string &filename, std::vector matching) + tsl::ordered_map Parser::ReadDefines(const std::string &filename, std::vector matching) { - std::map out; + tsl::ordered_map out; Lexer lexer; auto tokens = lexer.LexFile(filename); @@ -488,12 +488,12 @@ namespace fex return items; } - std::map Parser::ParseTopLevelObjects(std::vector tokens) + tsl::ordered_map Parser::ParseTopLevelObjects(std::vector tokens) { index_ = 0; tokens_ = std::move(tokens); - std::map items; + tsl::ordered_map items; while (index_ < tokens_.size()) { diff --git a/src/lib/fex/parser_util.cpp b/src/lib/fex/parser_util.cpp index 0f375b81..3a5d47f9 100644 --- a/src/lib/fex/parser_util.cpp +++ b/src/lib/fex/parser_util.cpp @@ -17,7 +17,7 @@ QStringList ParserUtil::ReadDefines(QString filename, QString prefix) fex::Parser parser; std::vector match_list = { prefix.toStdString() + ".*" }; - std::map defines = parser.ReadDefines(filepath.toStdString(), match_list); + tsl::ordered_map defines = parser.ReadDefines(filepath.toStdString(), match_list); QStringList out; for(auto const& define : defines) { @@ -39,7 +39,7 @@ QStringList ParserUtil::ReadDefinesValueSort(QString filename, QString prefix) fex::Parser parser; std::vector match_list = { prefix.toStdString() + ".*" }; - std::map defines = parser.ReadDefines(filepath.toStdString(), match_list); + tsl::ordered_map defines = parser.ReadDefines(filepath.toStdString(), match_list); QMultiMap defines_keyed_by_value; for (const auto& pair : defines) { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8c483f1f..7af3d100 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -14,7 +14,7 @@ #include "editcommands.h" #include "flowlayout.h" #include "shortcut.h" -#include "mapparser.h" +#include "advancemapparser.h" #include "prefab.h" #include "montabwidget.h" #include "imageexport.h" @@ -1634,7 +1634,7 @@ void MainWindow::on_actionNew_Tileset_triggered() { int index = insertTilesetLabel(&editor->project->secondaryTilesetLabels, createTilesetDialog->fullSymbolName); this->ui->comboBox_SecondaryTileset->insertItem(index, createTilesetDialog->fullSymbolName); } - insertTilesetLabel(&editor->project->tilesetLabelsOrdered, createTilesetDialog->fullSymbolName); + editor->project->tilesetLabelsOrdered.append(createTilesetDialog->fullSymbolName); QMessageBox msgBox(this); msgBox.setText("Successfully created tileset."); @@ -2712,20 +2712,14 @@ void MainWindow::on_actionExport_Map_Timelapse_Image_triggered() { showExportMapImageWindow(ImageExporterMode::Timelapse); } -void MainWindow::on_actionImport_Map_from_Advance_Map_1_92_triggered(){ - importMapFromAdvanceMap1_92(); -} - -void MainWindow::importMapFromAdvanceMap1_92() -{ - QString filepath = FileDialog::getOpenFileName(this, "Import Map from Advance Map 1.92", "", "Advance Map 1.92 Map Files (*.map)"); +void MainWindow::on_actionImport_Layout_from_Advance_Map_1_92_triggered() { + QString filepath = FileDialog::getOpenFileName(this, "Import Layout from Advance Map 1.92", "", "Advance Map 1.92 Map Files (*.map)"); if (filepath.isEmpty()) { return; } - MapParser parser; bool error = false; - Layout *mapLayout = parser.parse(filepath, &error, editor->project); + Layout *mapLayout = AdvanceMapParser::parseLayout(filepath, &error, editor->project); if (error) { QMessageBox msgBox(this); msgBox.setText("Failed to import map from Advance Map 1.92 .map file."); @@ -2734,11 +2728,13 @@ void MainWindow::importMapFromAdvanceMap1_92() msgBox.setDefaultButton(QMessageBox::Ok); msgBox.setIcon(QMessageBox::Icon::Critical); msgBox.exec(); + delete mapLayout; return; } - openNewMapDialog(); - this->newMapDialog->init(mapLayout); + openNewLayoutDialog(); + this->newLayoutDialog->copyFrom(*mapLayout); + delete mapLayout; } void MainWindow::showExportMapImageWindow(ImageExporterMode mode) { diff --git a/src/project.cpp b/src/project.cpp index 825c3c48..2f37e24a 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -1115,6 +1115,9 @@ bool Project::loadLayoutTilesets(Layout *layout) { return true; } +// TODO: We are parsing the tileset headers file whenever we load a tileset for the first time. +// At a minimum this means we're parsing the file three times per session (twice here for the first map's tilesets, once on launch in Project::readTilesetLabels). +// We can cache the header data instead and only parse it once on launch. Tileset* Project::loadTileset(QString label, Tileset *tileset) { auto memberMap = Tileset::getHeaderMemberMap(this->usingAsmTilesets); if (this->usingAsmTilesets) { @@ -1134,14 +1137,14 @@ Tileset* Project::loadTileset(QString label, Tileset *tileset) { tileset->metatile_attrs_label = values.value(memberMap.key("metatileAttributes")); } else { // Read C tileset header - const auto structs = parser.readCStructs(projectConfig.getFilePath(ProjectFilePath::tilesets_headers), label, memberMap); + auto structs = parser.readCStructs(projectConfig.getFilePath(ProjectFilePath::tilesets_headers), label, memberMap); if (!structs.contains(label)) { return nullptr; } if (tileset == nullptr) { tileset = new Tileset; } - const auto tilesetAttributes = structs[label]; + auto tilesetAttributes = structs[label]; tileset->name = label; tileset->is_secondary = ParseUtil::gameStringToBool(tilesetAttributes.value("isSecondary")); tileset->tiles_label = tilesetAttributes.value("tiles"); @@ -1579,7 +1582,7 @@ void Project::loadTilesetMetatiles(Tileset* tileset) { } QString Project::findMetatileLabelsTileset(QString label) { - for (QString tilesetName : this->tilesetLabelsOrdered) { + for (const QString &tilesetName : this->tilesetLabelsOrdered) { QString metatileLabelPrefix = Tileset::getMetatileLabelPrefix(tilesetName); if (label.startsWith(metatileLabelPrefix)) return tilesetName; @@ -2046,7 +2049,7 @@ QString Project::getDefaultSecondaryTilesetLabel() const { return defaultLabel; } -void Project::appendTilesetLabel(QString label, QString isSecondaryStr) { +void Project::appendTilesetLabel(const QString &label, const QString &isSecondaryStr) { bool ok; bool isSecondary = ParseUtil::gameStringToBool(isSecondaryStr, &ok); if (!ok) { @@ -2080,20 +2083,18 @@ bool Project::readTilesetLabels() { QRegularExpressionMatch match = iter.next(); appendTilesetLabel(match.captured("label"), match.captured("isSecondary")); } - this->primaryTilesetLabels.sort(); - this->secondaryTilesetLabels.sort(); - this->tilesetLabelsOrdered.sort(); filename = asm_filename; // For error reporting further down } else { this->usingAsmTilesets = false; const auto structs = parser.readCStructs(filename, "", Tileset::getHeaderMemberMap(this->usingAsmTilesets)); - const QStringList labels = structs.keys(); - // TODO: This is alphabetical, AdvanceMap import wants the vanilla order in tilesetLabelsOrdered - for (const auto &tilesetLabel : labels){ - appendTilesetLabel(tilesetLabel, structs[tilesetLabel].value("isSecondary")); + for (auto i = structs.cbegin(); i != structs.cend(); i++){ + appendTilesetLabel(i.key(), i.value().value("isSecondary")); } } + this->primaryTilesetLabels.sort(); + this->secondaryTilesetLabels.sort(); + bool success = true; if (this->secondaryTilesetLabels.isEmpty()) { logError(QString("Failed to find any secondary tilesets in %1").arg(filename)); @@ -2784,7 +2785,7 @@ bool Project::readEventGraphics() { }; QString filepath = projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx_info); - const auto gfxInfos = parser.readCStructs(filepath, "", gfxInfoMemberMap); + auto gfxInfos = parser.readCStructs(filepath, "", gfxInfoMemberMap); QMap picTables = parser.readCArrayMulti(projectConfig.getFilePath(ProjectFilePath::data_obj_event_pic_tables)); QMap graphicIncbins = parser.readCIncbinMulti(projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx)); @@ -2794,7 +2795,7 @@ bool Project::readEventGraphics() { if (!gfxInfos.contains(info_label)) continue; - const auto gfxInfoAttributes = gfxInfos[info_label]; + auto gfxInfoAttributes = gfxInfos[info_label]; auto eventGraphics = new EventGraphics; eventGraphics->inanimate = ParseUtil::gameStringToBool(gfxInfoAttributes.value("inanimate")); diff --git a/src/ui/newlayoutdialog.cpp b/src/ui/newlayoutdialog.cpp index 0f67545e..73822ba8 100644 --- a/src/ui/newlayoutdialog.cpp +++ b/src/ui/newlayoutdialog.cpp @@ -41,18 +41,27 @@ NewLayoutDialog::~NewLayoutDialog() delete ui; } -// Creating new map from AdvanceMap import +// Creating new layout from AdvanceMap import // TODO: Re-use for a "Duplicate Layout" option? -void NewLayoutDialog::init(Layout *layoutToCopy) { +void NewLayoutDialog::copyFrom(const Layout &layoutToCopy) { if (this->importedLayout) delete this->importedLayout; this->importedLayout = new Layout(); - this->importedLayout->blockdata = layoutToCopy->blockdata; - if (!layoutToCopy->border.isEmpty()) - this->importedLayout->border = layoutToCopy->border; + this->importedLayout->blockdata = layoutToCopy.blockdata; + if (!layoutToCopy.border.isEmpty()) + this->importedLayout->border = layoutToCopy.border; - useLayoutSettings(this->importedLayout); + this->settings->width = layoutToCopy.width; + this->settings->height = layoutToCopy.height; + this->settings->borderWidth = layoutToCopy.border_width; + this->settings->borderHeight = layoutToCopy.border_height; + this->settings->primaryTilesetLabel = layoutToCopy.tileset_primary_label; + this->settings->secondaryTilesetLabel = layoutToCopy.tileset_secondary_label; + + // Don't allow changes to the layout settings + ui->newLayoutForm->setSettings(*this->settings); + ui->newLayoutForm->setDisabled(true); } void NewLayoutDialog::saveSettings() { @@ -61,20 +70,6 @@ void NewLayoutDialog::saveSettings() { this->settings->name = ui->lineEdit_Name->text(); } -void NewLayoutDialog::useLayoutSettings(Layout *layout) { - if (!layout) return; - this->settings->width = layout->width; - this->settings->height = layout->height; - this->settings->borderWidth = layout->border_width; - this->settings->borderHeight = layout->border_height; - this->settings->primaryTilesetLabel = layout->tileset_primary_label; - this->settings->secondaryTilesetLabel = layout->tileset_secondary_label; - ui->newLayoutForm->setSettings(*this->settings); - - // Don't allow changes to the layout settings - ui->newLayoutForm->setDisabled(true); -} - bool NewLayoutDialog::validateLayoutID(bool allowEmpty) { QString id = ui->lineEdit_LayoutID->text(); diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index e709bed1..b6bf1735 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -2,7 +2,7 @@ #include "ui_tileseteditor.h" #include "log.h" #include "imageproviders.h" -#include "metatileparser.h" +#include "advancemapparser.h" #include "paletteutil.h" #include "imageexport.h" #include "config.h" @@ -978,7 +978,7 @@ void TilesetEditor::importTilesetMetatiles(Tileset *tileset, bool primary) } bool error = false; - QList metatiles = MetatileParser::parse(filepath, &error, primary); + QList metatiles = AdvanceMapParser::parseMetatiles(filepath, &error, primary); if (error) { QMessageBox msgBox(this); msgBox.setText("Failed to import metatiles from Advance Map 1.92 .bvd file.");