From efd49cd544a39dbc59e5093797b7bf824b38f03f Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 27 Sep 2022 20:17:55 -0400 Subject: [PATCH] Read tileset headers from C data --- include/config.h | 3 + include/core/parseutil.h | 3 +- include/core/tileset.h | 9 +- include/project.h | 7 +- src/config.cpp | 9 +- src/core/parseutil.cpp | 17 ++- src/core/tileset.cpp | 92 ++++++++------ src/mainwindow.cpp | 42 +++---- src/project.cpp | 256 ++++++++++++++++++++++----------------- src/scriptapi/apimap.cpp | 14 +-- src/ui/newmappopup.cpp | 10 +- 11 files changed, 261 insertions(+), 201 deletions(-) diff --git a/include/config.h b/include/config.h index a8e9ffb8..2f670582 100644 --- a/include/config.h +++ b/include/config.h @@ -154,6 +154,9 @@ enum ProjectFilePath { tilesets_headers, tilesets_graphics, tilesets_metatiles, + tilesets_headers_asm, + tilesets_graphics_asm, + tilesets_metatiles_asm, data_obj_event_gfx_pointers, data_obj_event_gfx_info, data_obj_event_pic_tables, diff --git a/include/core/parseutil.h b/include/core/parseutil.h index 2486caec..24efcbd8 100644 --- a/include/core/parseutil.h +++ b/include/core/parseutil.h @@ -69,7 +69,8 @@ public: static QString removeLineComments(QString text, const QStringList &commentSymbols); static QStringList splitShellCommand(QStringView command); - static QMap> readCStructs(const QString &filePath, const QHash memberMap = { }); + static bool gameStringToBool(QString gameString); + static QMap> readCStructs(const QString &filePath, const QString &target = "", const QHash memberMap = { }); private: QString root; diff --git a/include/core/tileset.h b/include/core/tileset.h index ea89c602..31213f82 100644 --- a/include/core/tileset.h +++ b/include/core/tileset.h @@ -15,14 +15,11 @@ public: public: QString name; - QString is_compressed; QString is_secondary; - QString padding; QString tiles_label; QString palettes_label; QString metatiles_label; QString metatiles_path; - QString callback_label; QString metatile_attrs_label; QString metatile_attrs_path; QString tilesImagePath; @@ -41,9 +38,9 @@ public: static QList getPalette(int, Tileset*, Tileset*, bool useTruePalettes = false); static bool metatileIsValid(uint16_t metatileId, Tileset *, Tileset *); - bool appendToHeaders(QString headerFile, QString friendlyName); - bool appendToGraphics(QString graphicsFile, QString friendlyName, bool primary); - bool appendToMetatiles(QString metatileFile, QString friendlyName, bool primary); + bool appendToHeaders(QString root, QString friendlyName, bool usingAsm); + bool appendToGraphics(QString root, QString friendlyName, bool primary, bool usingAsm); + bool appendToMetatiles(QString root, QString friendlyName, bool primary, bool usingAsm); }; #endif // TILESET_H diff --git a/include/project.h b/include/project.h index 9cca1936..59f7a798 100644 --- a/include/project.h +++ b/include/project.h @@ -81,6 +81,7 @@ public: ParseUtil parser; QFileSystemWatcher fileWatcher; QMap modifiedFileTimestamps; + bool usingAsmTilesets; void set_root(QString); @@ -105,7 +106,7 @@ public: Tileset* loadTileset(QString, Tileset *tileset = nullptr); Tileset* getTileset(QString, bool forceLoad = false); QMap tilesetLabels; - QList tilesetLabelsOrdered; + QStringList tilesetLabelsOrdered; Blockdata readBlockdata(QString); bool loadBlockdata(MapLayout*); @@ -143,6 +144,8 @@ public: void loadTilesetTiles(Tileset*, QImage); void loadTilesetMetatiles(Tileset*); void loadTilesetMetatileLabels(Tileset*); + void loadTilesetPalettes(Tileset*); + void readTilesetPaths(Tileset* tileset); void saveLayoutBlockdata(Map*); void saveLayoutBorder(Map*); @@ -164,7 +167,7 @@ public: QString defaultSong; QStringList getVisibilities(); - QMap getTilesetLabels(); + bool readTilesetLabels(); bool readTilesetProperties(); bool readMaxMapDataSize(); bool readRegionMapSections(); diff --git a/src/config.cpp b/src/config.cpp index 671b84c6..85be7793 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -27,9 +27,12 @@ const QMap> defaultPaths = { {ProjectFilePath::json_wild_encounters, { "json_wild_encounters", "src/data/wild_encounters.json"}}, {ProjectFilePath::json_region_map_entries, { "json_region_map_entries", "src/data/region_map/region_map_sections.json"}}, {ProjectFilePath::json_region_porymap_cfg, { "json_region_porymap_cfg", "src/data/region_map/porymap_config.json"}}, - {ProjectFilePath::tilesets_headers, { "tilesets_headers", "data/tilesets/headers.inc"}}, - {ProjectFilePath::tilesets_graphics, { "tilesets_graphics", "data/tilesets/graphics.inc"}}, - {ProjectFilePath::tilesets_metatiles, { "tilesets_metatiles", "data/tilesets/metatiles.inc"}}, + {ProjectFilePath::tilesets_headers, { "tilesets_headers", "src/data/tilesets/headers.h"}}, + {ProjectFilePath::tilesets_graphics, { "tilesets_graphics", "src/data/tilesets/graphics.h"}}, + {ProjectFilePath::tilesets_metatiles, { "tilesets_metatiles", "src/data/tilesets/metatiles.h"}}, + {ProjectFilePath::tilesets_headers_asm, { "tilesets_headers_asm", "data/tilesets/headers.inc"}}, + {ProjectFilePath::tilesets_graphics_asm, { "tilesets_graphics_asm", "data/tilesets/graphics.inc"}}, + {ProjectFilePath::tilesets_metatiles_asm, { "tilesets_metatiles_asm", "data/tilesets/metatiles.inc"}}, {ProjectFilePath::data_obj_event_gfx_pointers, { "data_obj_event_gfx_pointers", "src/data/object_events/object_event_graphics_info_pointers.h"}}, {ProjectFilePath::data_obj_event_gfx_info, { "data_obj_event_gfx_info", "src/data/object_events/object_event_graphics_info.h"}}, {ProjectFilePath::data_obj_event_pic_tables, { "data_obj_event_pic_tables", "src/data/object_events/object_event_pic_tables.h"}}, diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index 1e34dafa..64ce33f0 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -378,12 +378,25 @@ QMap ParseUtil::readNamedIndexCArray(const QString &filename, return map; } -QMap> ParseUtil::readCStructs(const QString &filePath, const QHash memberMap) { +bool ParseUtil::gameStringToBool(QString gameString) { + if (QString::compare(gameString, "TRUE", Qt::CaseInsensitive) == 0) + return true; + if (QString::compare(gameString, "FALSE", Qt::CaseInsensitive) == 0) + return false; + bool ok; + int num = gameString.toInt(&ok); + if (!ok) logWarn(QString("Unable to convert string '%1' to bool, will be interpreted as 'FALSE'").arg(gameString)); + return num != 0; +} + +QMap> ParseUtil::readCStructs(const QString &filePath, const QString &target, const QHash memberMap) { 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 QMap values; int i = 0; for (const fex::ArrayValue &v : it->second.values()) { @@ -398,7 +411,7 @@ QMap> ParseUtil::readCStructs(const QString &fil } i++; } - structMaps.insert(QString::fromStdString(it->first), values); + structMaps.insert(structLabel, values); } return structMaps; } diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp index 9b85bfee..38c090ed 100644 --- a/src/core/tileset.cpp +++ b/src/core/tileset.cpp @@ -10,14 +10,11 @@ Tileset::Tileset(const Tileset &other) : name(other.name), - is_compressed(other.is_compressed), is_secondary(other.is_secondary), - padding(other.padding), tiles_label(other.tiles_label), palettes_label(other.palettes_label), metatiles_label(other.metatiles_label), metatiles_path(other.metatiles_path), - callback_label(other.callback_label), metatile_attrs_label(other.metatile_attrs_label), metatile_attrs_path(other.metatile_attrs_path), tilesImagePath(other.tilesImagePath), @@ -34,14 +31,11 @@ Tileset::Tileset(const Tileset &other) Tileset &Tileset::operator=(const Tileset &other) { name = other.name; - is_compressed = other.is_compressed; is_secondary = other.is_secondary; - padding = other.padding; tiles_label = other.tiles_label; palettes_label = other.palettes_label; metatiles_label = other.metatiles_label; metatiles_path = other.metatiles_path; - callback_label = other.callback_label; metatile_attrs_label = other.metatile_attrs_label; metatile_attrs_path = other.metatile_attrs_path; tilesImagePath = other.tilesImagePath; @@ -132,26 +126,33 @@ QList Tileset::getPalette(int paletteId, Tileset *primaryTileset, Tileset return paletteTable; } -bool Tileset::appendToHeaders(QString headerFile, QString friendlyName){ - QFile file(headerFile); +bool Tileset::appendToHeaders(QString root, QString friendlyName, bool usingAsm) { + QString headersFile = root + "/" + (usingAsm ? projectConfig.getFilePath(ProjectFilePath::tilesets_headers_asm) + : projectConfig.getFilePath(ProjectFilePath::tilesets_headers)); + QFile file(headersFile); if (!file.open(QIODevice::WriteOnly | QIODevice::Append)) { - logError(QString("Could not write to file \"%1\"").arg(headerFile)); + logError(QString("Could not write to file \"%1\"").arg(headersFile)); return false; } - QString dataString = "\n\t.align 2\n"; - dataString.append(QString("%1::\n").arg(this->name)); - dataString.append(QString("\t.byte %1 @ is compressed\n").arg(this->is_compressed)); - dataString.append(QString("\t.byte %1 @ is secondary\n").arg(this->is_secondary)); - dataString.append(QString("\t.2byte %1\n").arg(this->padding)); - dataString.append(QString("\t.4byte gTilesetTiles_%1\n").arg(friendlyName)); - dataString.append(QString("\t.4byte gTilesetPalettes_%1\n").arg(friendlyName)); - dataString.append(QString("\t.4byte gMetatiles_%1\n").arg(friendlyName)); - if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered) { - dataString.append("\t.4byte NULL\n"); - dataString.append(QString("\t.4byte gMetatileAttributes_%1\n").arg(friendlyName)); + QString dataString = "\n"; + if (usingAsm) { + dataString.append("\t.align 2\n"); + dataString.append(QString("%1::\n").arg(this->name)); + dataString.append("\t.byte TRUE @ is compressed\n"); + dataString.append(QString("\t.byte %1 @ is secondary\n").arg(this->is_secondary)); + dataString.append("\t.2byte 0 @ padding\n"); + dataString.append(QString("\t.4byte gTilesetTiles_%1\n").arg(friendlyName)); + dataString.append(QString("\t.4byte gTilesetPalettes_%1\n").arg(friendlyName)); + dataString.append(QString("\t.4byte gMetatiles_%1\n").arg(friendlyName)); + if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered) { + dataString.append("\t.4byte NULL @ animation callback\n"); + dataString.append(QString("\t.4byte gMetatileAttributes_%1\n").arg(friendlyName)); + } else { + dataString.append(QString("\t.4byte gMetatileAttributes_%1\n").arg(friendlyName)); + dataString.append("\t.4byte NULL @ animation callback\n"); + } } else { - dataString.append(QString("\t.4byte gMetatileAttributes_%1\n").arg(friendlyName)); - dataString.append("\t.4byte NULL\n"); + // TODO } file.write(dataString.toUtf8()); file.flush(); @@ -159,7 +160,10 @@ bool Tileset::appendToHeaders(QString headerFile, QString friendlyName){ return true; } -bool Tileset::appendToGraphics(QString graphicsFile, QString friendlyName, bool primary) { +// TODO: Interpet isSecondary to remove primary argument here and below +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)); QString primaryString = primary ? "primary" : "secondary"; QString basePath = projectConfig.getFilePath(ProjectFilePath::data_tilesets_folders); QFile file(graphicsFile); @@ -167,23 +171,30 @@ bool Tileset::appendToGraphics(QString graphicsFile, QString friendlyName, bool logError(QString("Could not write to file \"%1\"").arg(graphicsFile)); return false; } - QString dataString = "\n\t.align 2\n"; - dataString.append(QString("gTilesetPalettes_%1::\n").arg(friendlyName)); - for(int i = 0; i < Project::getNumPalettesTotal(); ++i) { - QString paletteString = QString("%1.gbapal").arg(i, 2, 10, QLatin1Char('0')); - dataString.append(QString("\t.incbin \"%1%2/%3/palettes/%4\"\n").arg(basePath, primaryString, friendlyName.toLower(), paletteString)); + QString dataString = "\n"; + if (usingAsm) { + dataString.append("\t.align 2\n"); + dataString.append(QString("gTilesetPalettes_%1::\n").arg(friendlyName)); + for(int i = 0; i < Project::getNumPalettesTotal(); ++i) { + QString paletteString = QString("%1.gbapal").arg(i, 2, 10, QLatin1Char('0')); + dataString.append(QString("\t.incbin \"%1%2/%3/palettes/%4\"\n").arg(basePath, primaryString, friendlyName.toLower(), paletteString)); + } + dataString.append("\n\t.align 2\n"); + dataString.append(QString("gTilesetTiles_%1::\n").arg(friendlyName)); + dataString.append(QString("\t.incbin \"%1%2/%3/tiles.4bpp.lz\"\n").arg(basePath, primaryString, friendlyName.toLower())); + } else { + // TODO } - dataString.append("\n\t.align 2\n"); - dataString.append(QString("gTilesetTiles_%1::\n").arg(friendlyName)); - dataString.append(QString("\t.incbin \"%1%2/%3/tiles.4bpp.lz\"\n").arg(basePath, primaryString, friendlyName.toLower())); file.write(dataString.toUtf8()); file.flush(); file.close(); return true; } -bool Tileset::appendToMetatiles(QString metatileFile, QString friendlyName, bool primary) { +bool Tileset::appendToMetatiles(QString root, QString friendlyName, bool primary, bool usingAsm) { + QString metatileFile = root + "/" + (usingAsm ? projectConfig.getFilePath(ProjectFilePath::tilesets_metatiles_asm) + : projectConfig.getFilePath(ProjectFilePath::tilesets_metatiles)); QString primaryString = primary ? "primary" : "secondary"; QString basePath = projectConfig.getFilePath(ProjectFilePath::data_tilesets_folders); QFile file(metatileFile); @@ -191,12 +202,17 @@ bool Tileset::appendToMetatiles(QString metatileFile, QString friendlyName, bool logError(QString("Could not write to file \"%1\"").arg(metatileFile)); return false; } - QString dataString = "\n\t.align 1\n"; - dataString.append(QString("gMetatiles_%1::\n").arg(friendlyName)); - dataString.append(QString("\t.incbin \"%1%2/%3/metatiles.bin\"\n").arg(basePath, primaryString, friendlyName.toLower())); - dataString.append(QString("\n\t.align 1\n")); - dataString.append(QString("gMetatileAttributes_%1::\n").arg(friendlyName)); - dataString.append(QString("\t.incbin \"%1%2/%3/metatile_attributes.bin\"\n").arg(basePath, primaryString, friendlyName.toLower())); + QString dataString = "\n"; + if (usingAsm) { + dataString.append("\t.align 1\n"); + dataString.append(QString("gMetatiles_%1::\n").arg(friendlyName)); + dataString.append(QString("\t.incbin \"%1%2/%3/metatiles.bin\"\n").arg(basePath, primaryString, friendlyName.toLower())); + dataString.append(QString("\n\t.align 1\n")); + dataString.append(QString("gMetatileAttributes_%1::\n").arg(friendlyName)); + dataString.append(QString("\t.incbin \"%1%2/%3/metatile_attributes.bin\"\n").arg(basePath, primaryString, friendlyName.toLower())); + } else { + // TODO + } file.write(dataString.toUtf8()); file.flush(); file.close(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ff507326..478bae9a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -787,14 +787,14 @@ void MainWindow::displayMapProperties() { ui->comboBox_Song->setCurrentText(map->song); ui->comboBox_Location->setCurrentText(map->location); - ui->checkBox_Visibility->setChecked(map->requiresFlash.toInt() > 0 || map->requiresFlash == "TRUE"); + ui->checkBox_Visibility->setChecked(ParseUtil::gameStringToBool(map->requiresFlash)); ui->comboBox_Weather->setCurrentText(map->weather); ui->comboBox_Type->setCurrentText(map->type); ui->comboBox_BattleScene->setCurrentText(map->battle_scene); - ui->checkBox_ShowLocation->setChecked(map->show_location.toInt() > 0 || map->show_location == "TRUE"); - ui->checkBox_AllowRunning->setChecked(map->allowRunning.toInt() > 0 || map->allowRunning == "TRUE"); - ui->checkBox_AllowBiking->setChecked(map->allowBiking.toInt() > 0 || map->allowBiking == "TRUE"); - ui->checkBox_AllowEscapeRope->setChecked(map->allowEscapeRope.toInt() > 0 || map->allowEscapeRope == "TRUE"); + ui->checkBox_ShowLocation->setChecked(ParseUtil::gameStringToBool(map->show_location)); + ui->checkBox_AllowRunning->setChecked(ParseUtil::gameStringToBool(map->allowRunning)); + ui->checkBox_AllowBiking->setChecked(ParseUtil::gameStringToBool(map->allowBiking)); + ui->checkBox_AllowEscapeRope->setChecked(ParseUtil::gameStringToBool(map->allowEscapeRope)); ui->spinBox_FloorNumber->setValue(map->floorNumber); // Custom fields table. @@ -940,6 +940,7 @@ bool MainWindow::loadDataStructures() { && project->readTrainerTypes() && project->readMetatileBehaviors() && project->readTilesetProperties() + && project->readTilesetLabels() && project->readMaxMapDataSize() && project->readHealLocations() && project->readMiscellaneousConstants() @@ -972,16 +973,10 @@ bool MainWindow::loadProjectCombos() { ui->comboBox_Song->addItems(project->songNames); ui->comboBox_Location->clear(); ui->comboBox_Location->addItems(project->mapSectionValueToName.values()); - - QMap tilesets = project->getTilesetLabels(); - if (tilesets.isEmpty()) { - return false; - } - ui->comboBox_PrimaryTileset->clear(); - ui->comboBox_PrimaryTileset->addItems(tilesets.value("primary")); + ui->comboBox_PrimaryTileset->addItems(project->tilesetLabels.value("primary")); ui->comboBox_SecondaryTileset->clear(); - ui->comboBox_SecondaryTileset->addItems(tilesets.value("secondary")); + ui->comboBox_SecondaryTileset->addItems(project->tilesetLabels.value("secondary")); ui->comboBox_Weather->clear(); ui->comboBox_Weather->addItems(project->weatherNames); ui->comboBox_BattleScene->clear(); @@ -1199,7 +1194,7 @@ void MainWindow::onNewMapCreated() { sortMapList(); setMap(newMapName, true); - if (newMap->isFlyable == "TRUE") { + if (ParseUtil::gameStringToBool(newMap->isFlyable)) { addNewEvent(EventType::HealLocation); editor->project->saveHealLocationStruct(newMap); editor->save();// required @@ -1284,8 +1279,8 @@ void MainWindow::on_actionNew_Tileset_triggered() { msgBox.exec(); return; } - QMap tilesets = this->editor->project->getTilesetLabels(); - if(tilesets.value("primary").contains(createTilesetDialog->fullSymbolName) || tilesets.value("secondary").contains(createTilesetDialog->fullSymbolName)) { + if (editor->project->tilesetLabels.value("primary").contains(createTilesetDialog->fullSymbolName) + || editor->project->tilesetLabels.value("secondary").contains(createTilesetDialog->fullSymbolName)) { logError(QString("Could not create tileset \"%1\", the symbol \"%2\" already exists.").arg(createTilesetDialog->friendlyName, createTilesetDialog->fullSymbolName)); QMessageBox msgBox(this); msgBox.setText("Failed to add new tileset."); @@ -1333,26 +1328,25 @@ void MainWindow::on_actionNew_Tileset_triggered() { } newSet.palettes[0][1] = qRgb(255,0,255); newSet.palettePreviews[0][1] = qRgb(255,0,255); - newSet.is_compressed = "TRUE"; - newSet.padding = "0"; editor->project->saveTilesetTilesImage(&newSet); editor->project->saveTilesetMetatiles(&newSet); editor->project->saveTilesetMetatileAttributes(&newSet); editor->project->saveTilesetPalettes(&newSet); //append to tileset specific files + newSet.appendToHeaders(editor->project->root, createTilesetDialog->friendlyName, editor->project->usingAsmTilesets); + newSet.appendToGraphics(editor->project->root, createTilesetDialog->friendlyName, !createTilesetDialog->isSecondary, editor->project->usingAsmTilesets); + newSet.appendToMetatiles(editor->project->root, createTilesetDialog->friendlyName, !createTilesetDialog->isSecondary, editor->project->usingAsmTilesets); - newSet.appendToHeaders(editor->project->root + "/" + projectConfig.getFilePath(ProjectFilePath::tilesets_headers), createTilesetDialog->friendlyName); - newSet.appendToGraphics(editor->project->root + "/" + projectConfig.getFilePath(ProjectFilePath::tilesets_graphics), createTilesetDialog->friendlyName, !createTilesetDialog->isSecondary); - newSet.appendToMetatiles(editor->project->root + "/" + projectConfig.getFilePath(ProjectFilePath::tilesets_metatiles), createTilesetDialog->friendlyName, !createTilesetDialog->isSecondary); if(!createTilesetDialog->isSecondary) { this->ui->comboBox_PrimaryTileset->addItem(createTilesetDialog->fullSymbolName); + editor->project->tilesetLabels["primary"].append(createTilesetDialog->fullSymbolName); + } else { this->ui->comboBox_SecondaryTileset->addItem(createTilesetDialog->fullSymbolName); + editor->project->tilesetLabels["secondary"].append(createTilesetDialog->fullSymbolName); } - - // hydrate tileset labels. - this->editor->project->getTilesetLabels(); + editor->project->tilesetLabelsOrdered.append(createTilesetDialog->fullSymbolName); QMessageBox msgBox(this); msgBox.setText("Successfully created tileset."); diff --git a/src/project.cpp b/src/project.cpp index 380002a3..d676d289 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -1131,26 +1131,42 @@ bool Project::loadLayoutTilesets(MapLayout *layout) { } Tileset* Project::loadTileset(QString label, Tileset *tileset) { - const QStringList values = parser.getLabelValues(parser.parseAsm(projectConfig.getFilePath(ProjectFilePath::tilesets_headers)), label); - if (values.isEmpty()) { - return nullptr; - } - if (tileset == nullptr) { - tileset = new Tileset; - } - tileset->name = label; - tileset->is_compressed = values.value(0); - tileset->is_secondary = values.value(1); - tileset->padding = values.value(2); - tileset->tiles_label = values.value(3); - tileset->palettes_label = values.value(4); - tileset->metatiles_label = values.value(5); - if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered) { - tileset->callback_label = values.value(6); - tileset->metatile_attrs_label = values.value(7); + if (this->usingAsmTilesets) { + // Read asm tileset header. Backwards compatibility + const QStringList values = parser.getLabelValues(parser.parseAsm(projectConfig.getFilePath(ProjectFilePath::tilesets_headers_asm)), label); + if (values.isEmpty()) { + return nullptr; + } + 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); + } } else { - tileset->metatile_attrs_label = values.value(6); - tileset->callback_label = values.value(7); + // Read C tileset header + QMap> structs = ParseUtil::readCStructs(this->root + "/" + projectConfig.getFilePath(ProjectFilePath::tilesets_headers), label); + if (!structs.contains(label)) { + return nullptr; + } + if (tileset == nullptr) { + tileset = new Tileset; + } + QMap tilesetAttributes = structs[label]; + tileset->name = label; + tileset->is_secondary = tilesetAttributes.value("isSecondary"); + tileset->tiles_label = tilesetAttributes.value("tiles"); + tileset->palettes_label = tilesetAttributes.value("palettes"); + tileset->metatiles_label = tilesetAttributes.value("metatiles"); + tileset->metatile_attrs_label = tilesetAttributes.value("metatileAttributes"); } loadTilesetAssets(tileset); @@ -1329,15 +1345,15 @@ void Project::saveMap(Map *map) { mapObj["layout"] = map->layout->id; mapObj["music"] = map->song; mapObj["region_map_section"] = map->location; - mapObj["requires_flash"] = map->requiresFlash.toInt() > 0 || map->requiresFlash == "TRUE"; + mapObj["requires_flash"] = ParseUtil::gameStringToBool(map->requiresFlash); mapObj["weather"] = map->weather; mapObj["map_type"] = map->type; if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { - mapObj["allow_cycling"] = map->allowBiking.toInt() > 0 || map->allowBiking == "TRUE"; - mapObj["allow_escaping"] = map->allowEscapeRope.toInt() > 0 || map->allowEscapeRope == "TRUE"; - mapObj["allow_running"] = map->allowRunning.toInt() > 0 || map->allowRunning == "TRUE"; + mapObj["allow_cycling"] = ParseUtil::gameStringToBool(map->allowBiking); + mapObj["allow_escaping"] = ParseUtil::gameStringToBool(map->allowEscapeRope); + mapObj["allow_running"] = ParseUtil::gameStringToBool(map->allowRunning); } - mapObj["show_map_name"] = map->show_location.toInt() > 0 || map->show_location == "TRUE"; + mapObj["show_map_name"] = ParseUtil::gameStringToBool(map->show_location); if (projectConfig.getFloorNumberEnabled()) { mapObj["floor_number"] = map->floorNumber; } @@ -1469,56 +1485,10 @@ void Project::saveAllDataStructures() { } void Project::loadTilesetAssets(Tileset* tileset) { - QString category = (tileset->is_secondary == "TRUE") ? "secondary" : "primary"; if (tileset->name.isNull()) { return; } - QRegularExpression re("([a-z])([A-Z0-9])"); - QString tilesetName = tileset->name; - QString basePath = root + "/" + projectConfig.getFilePath(ProjectFilePath::data_tilesets_folders) + category + "/"; - QString dir_path = basePath + tilesetName.replace("gTileset_", "").replace(re, "\\1_\\2").toLower(); - - const QList graphics = parser.parseAsm(projectConfig.getFilePath(ProjectFilePath::tilesets_graphics)); - const QStringList tiles_values = parser.getLabelValues(graphics, tileset->tiles_label); - const QStringList palettes_values = parser.getLabelValues(graphics, tileset->palettes_label); - - QString tiles_path; - if (!tiles_values.isEmpty()) { - tiles_path = root + '/' + tiles_values.value(0).section('"', 1, 1); - } else { - tiles_path = dir_path + "/tiles.4bpp"; - if (tileset->is_compressed == "TRUE") { - tiles_path += ".lz"; - } - } - - if (!palettes_values.isEmpty()) { - for (const auto &value : palettes_values) { - tileset->palettePaths.append(this->fixPalettePath(root + '/' + value.section('"', 1, 1))); - } - } else { - QString palettes_dir_path = dir_path + "/palettes"; - for (int i = 0; i < 16; i++) { - tileset->palettePaths.append(palettes_dir_path + '/' + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".pal"); - } - } - - const QList metatiles_macros = parser.parseAsm(projectConfig.getFilePath(ProjectFilePath::tilesets_metatiles)); - const QStringList metatiles_values = parser.getLabelValues(metatiles_macros, tileset->metatiles_label); - if (!metatiles_values.isEmpty()) { - tileset->metatiles_path = root + '/' + metatiles_values.value(0).section('"', 1, 1); - } else { - tileset->metatiles_path = dir_path + "/metatiles.bin"; - } - const QStringList metatile_attrs_values = parser.getLabelValues(metatiles_macros, tileset->metatile_attrs_label); - if (!metatile_attrs_values.isEmpty()) { - tileset->metatile_attrs_path = root + '/' + metatile_attrs_values.value(0).section('"', 1, 1); - } else { - tileset->metatile_attrs_path = dir_path + "/metatile_attributes.bin"; - } - - tiles_path = fixGraphicPath(tiles_path); - tileset->tilesImagePath = tiles_path; + this->readTilesetPaths(tileset); QImage image; if (QFile::exists(tileset->tilesImagePath)) { image = QImage(tileset->tilesImagePath); @@ -1528,8 +1498,59 @@ void Project::loadTilesetAssets(Tileset* tileset) { this->loadTilesetTiles(tileset, image); this->loadTilesetMetatiles(tileset); this->loadTilesetMetatileLabels(tileset); + this->loadTilesetPalettes(tileset); +} - // palettes +void Project::readTilesetPaths(Tileset* tileset) { + // Parse the tileset data files to try and get explicit file paths for this tileset's assets + if (this->usingAsmTilesets) { + // Read asm tileset data files. Backwards compatibility + const QList graphics = parser.parseAsm(projectConfig.getFilePath(ProjectFilePath::tilesets_graphics_asm)); + const QList metatiles_macros = parser.parseAsm(projectConfig.getFilePath(ProjectFilePath::tilesets_metatiles_asm)); + + const QStringList tiles_values = parser.getLabelValues(graphics, tileset->tiles_label); + const QStringList palettes_values = parser.getLabelValues(graphics, tileset->palettes_label); + const QStringList metatiles_values = parser.getLabelValues(metatiles_macros, tileset->metatiles_label); + const QStringList metatile_attrs_values = parser.getLabelValues(metatiles_macros, tileset->metatile_attrs_label); + + if (!tiles_values.isEmpty()) + tileset->tilesImagePath = this->fixGraphicPath(root + '/' + tiles_values.value(0).section('"', 1, 1)); + if (!metatiles_values.isEmpty()) + 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) { + tileset->palettePaths.append(this->fixPalettePath(root + '/' + value.section('"', 1, 1))); + } + } else { + // Read C tileset data files + // TODO + } + + // Construct the expected path of the tileset's graphics, in case Porymap couldn't find any paths. + // This will be used for e.g. gTileset_General, which has paths specified in graphics.c instead + QRegularExpression re("([a-z])([A-Z0-9])"); + QString tilesetName = tileset->name; + QString category = ParseUtil::gameStringToBool(tileset->is_secondary) ? "secondary" : "primary"; + QString basePath = root + "/" + projectConfig.getFilePath(ProjectFilePath::data_tilesets_folders) + category + "/"; + QString defaultPath = basePath + tilesetName.replace("gTileset_", "").replace(re, "\\1_\\2").toLower(); + + // Try to set defaults + if (tileset->tilesImagePath.isEmpty()) + tileset->tilesImagePath = defaultPath + "/tiles.png"; + if (tileset->metatiles_path.isEmpty()) + tileset->metatiles_path = defaultPath + "/metatiles.bin"; + if (tileset->metatile_attrs_path.isEmpty()) + tileset->metatile_attrs_path = defaultPath + "/metatile_attributes.bin"; + if (tileset->palettePaths.isEmpty()) { + QString palettes_dir_path = defaultPath + "/palettes/"; + for (int i = 0; i < 16; i++) { + tileset->palettePaths.append(palettes_dir_path + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".pal"); + } + } +} + +void Project::loadTilesetPalettes(Tileset* tileset) { QList> palettes; QList> palettePreviews; for (int i = 0; i < tileset->palettePaths.length(); i++) { @@ -1643,7 +1664,7 @@ void Project::loadTilesetMetatileLabels(Tileset* tileset) { for (QString labelName : labels.keys()) { int metatileId = labels[labelName]; // subtract Project::num_tiles_primary from secondary metatiles - Metatile *metatile = Tileset::getMetatile(metatileId - (tileset->is_secondary == "TRUE" ? Project::num_tiles_primary : 0), tileset, nullptr); + Metatile *metatile = Tileset::getMetatile(metatileId - (ParseUtil::gameStringToBool(tileset->is_secondary) ? Project::num_tiles_primary : 0), tileset, nullptr); if (metatile) { metatile->label = labelName.replace(tilesetPrefix, ""); } else { @@ -1905,46 +1926,61 @@ Project::DataQualifiers Project::getDataQualifiers(QString text, QString label) return qualifiers; } -QMap Project::getTilesetLabels() { - QMap allTilesets; +bool Project::readTilesetLabels() { QStringList primaryTilesets; QStringList secondaryTilesets; - allTilesets.insert("primary", primaryTilesets); - allTilesets.insert("secondary", secondaryTilesets); - QList tilesetLabelsOrdered; + this->tilesetLabels.clear(); + this->tilesetLabels.insert("primary", primaryTilesets); + this->tilesetLabels.insert("secondary", secondaryTilesets); + this->tilesetLabelsOrdered.clear(); - QString filename = projectConfig.getFilePath(ProjectFilePath::tilesets_headers); - QString headers_text = parser.readTextFile(root + "/" + filename); - if (headers_text.isEmpty()) { - logError(QString("Failed to read tileset labels from %1.").arg(filename)); - return QMap(); - } - - QRegularExpression re("(?