#include "metatileparser.h" #include "config.h" #include "log.h" #include "project.h" MetatileParser::MetatileParser() { } 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 nullptr; } 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 nullptr; } int projIdOffset = in.length() - 4; int metatileSize = 16; int attrSize; 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; attrSize = 2; } else { *error = true; logError(QString("Detected unsupported game type from .bvd file. Last 4 bytes of file must be 'RSE '.")); return nullptr; } 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 nullptr; } if (numMetatiles < 1) { *error = true; logError(QString(".bvd file contains no data for metatiles.")); return nullptr; } 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 nullptr; } QList *metatiles = new QList(); for (int i = 0; i < numMetatiles; i++) { Metatile *metatile = new Metatile(); QList *tiles = new QList(); for (int j = 0; j < 8; j++) { int metatileOffset = 4 + i * metatileSize + j * 2; uint16_t word = static_cast( static_cast(in.at(metatileOffset)) | (static_cast(in.at(metatileOffset + 1)) << 8)); Tile tile(word & 0x3ff, (word >> 10) & 1, (word >> 11) & 1, (word >> 12) & 0xf); tiles->append(tile); } int attrOffset = 4 + (numMetatiles * metatileSize) + (i * attrSize); int value = static_cast(in.at(attrOffset)) | (static_cast(in.at(attrOffset + 1)) << 8); metatile->behavior = value & 0xFF; metatile->layerType = (value & 0xF000) >> 12; metatile->tiles = tiles; metatiles->append(metatile); } return metatiles; }