#include "regionmap.h" #include "regionmapeditor.h" #include "paletteutil.h" #include "project.h" #include "log.h" #include "config.h" #include "regionmapeditcommands.h" #include #include #include #include #include #include using std::make_shared; static bool ensureRegionMapFileExists(QString filepath) { if (!QFile::exists(filepath)) { logError(QString("Region map file does not exist: %1").arg(filepath)); return false; } return true; } RegionMap::RegionMap(Project *project) { this->project = project; } bool RegionMap::loadMapData(poryjson::Json data) { poryjson::Json::object mapObject = data.object_items(); this->alias = mapObject["alias"].string_value(); poryjson::Json tilemapJson = mapObject["tilemap"]; poryjson::Json layoutJson = mapObject["layout"]; this->tilemap.clear(); this->layout_layers.clear(); this->layouts.clear(); loadTilemap(tilemapJson); loadLayout(layoutJson); } int RegionMap::tilemapBytes() { // bytes per tile multiplier int multiplier = 1; switch (tilemap_format) { case TilemapFormat::Plain: multiplier = 1; break; case TilemapFormat::BPP_4: multiplier = 2; break; case TilemapFormat::BPP_8: multiplier = 2; break; } return tilemapSize() * multiplier; } bool RegionMap::loadTilemap(poryjson::Json tilemapJson) { bool errored = false; poryjson::Json::object tilemapObject = tilemapJson.object_items(); this->tilemap_width = tilemapObject["width"].int_value(); this->tilemap_height = tilemapObject["height"].int_value(); QString tilemapFormat = tilemapObject["format"].string_value(); QMap formatsMap = { {"plain", TilemapFormat::Plain}, {"4bpp", TilemapFormat::BPP_4}, {"8bpp", TilemapFormat::BPP_8} }; this->tilemap_format = formatsMap[tilemapFormat]; this->tileset_path = tilemapObject["tileset_path"].string_value(); this->tilemap_path = tilemapObject["tilemap_path"].string_value(); if (tilemapObject.contains("palette")) { this->palette_path = tilemapObject["palette"].string_value(); } QFile tilemapFile(fullPath(this->tilemap_path)); if (!tilemapFile.open(QIODevice::ReadOnly)) { logError(QString("Failed to open region map tilemap file %1.").arg(tilemap_path)); return false; } if (tilemapFile.size() < tilemapBytes()) { logError(QString("The region map tilemap at %1 is too small.").arg(tilemap_path)); return false; } QByteArray newTilemap = tilemapFile.readAll(); this->setTilemap(newTilemap); tilemapFile.close(); return !errored; } bool RegionMap::loadLayout(poryjson::Json layoutJson) { if (layoutJson.is_null()) { this->layout_format = LayoutFormat::None; return true; } // TODO: reset other values here this->layout_constants.clear(); poryjson::Json::object layoutObject = layoutJson.object_items(); QString layoutFormat = layoutObject["format"].string_value(); QMap layoutFormatMap = { {"binary", LayoutFormat::Binary}, {"C array", LayoutFormat::CArray} }; this->layout_format = layoutFormatMap[layoutFormat]; this->layout_path = layoutObject["path"].string_value(); this->layout_width = layoutObject["width"].int_value(); this->layout_height = layoutObject["height"].int_value(); this->offset_left = layoutObject["offset_left"].int_value(); this->offset_top = layoutObject["offset_top"].int_value(); bool errored = false; switch (this->layout_format) { case LayoutFormat::Binary: { // TODO: only one layer supported for binary layouts (change or no?) QFile binFile(fullPath(this->layout_path)); if (!binFile.open(QIODevice::ReadOnly)) { logError(QString("Failed to read region map layout binary file %1").arg(this->layout_path)); return false; } QByteArray mapBinData = binFile.readAll(); binFile.close(); if (mapBinData.size() != this->layout_width * this->layout_height) { logError("Region map layout file size does not match given dimensions for " + this->alias); return false; } // for layouts with only a single layer, it is called main this->layout_layers.append("main"); QList layout; for (int y = 0; y < this->layout_height; y++) { for (int x = 0; x < this->layout_width; x++) { int bin_index = x + y * this->layout_width; uint8_t square_section_id = mapBinData.at(bin_index); QString square_section_name = project->mapSectionValueToName.value(square_section_id); LayoutSquare square; square.map_section = square_section_name; square.has_map = (square_section_name != "MAPSEC_NONE" && !square_section_name.isEmpty()); square.x = x; square.y = y; layout.append(square); } } setLayout("main", layout); break; } case LayoutFormat::CArray: { // TODO: pokeruby / non-layered style C array or just an array of mapsections ParseUtil parser; QString text = parser.readTextFile(fullPath(this->layout_path)); QRegularExpression re("(?static)?\\s?(?const)?\\s?(?[A-Za-z0-9_]+)?\\s+(?