porymap/src/core/regionmap.cpp

358 lines
12 KiB
C++
Raw Normal View History

#include "regionmap.h"
2019-01-14 00:27:28 +00:00
#include "log.h"
#include "config.h"
#include <QByteArray>
#include <QFile>
#include <QDebug>
#include <QRegularExpression>
#include <QImage>
2019-01-28 18:47:20 +00:00
#include <math.h>
void RegionMap::init(Project *pro) {
QString path = pro->root;
2019-01-05 04:04:14 +00:00
this->project = pro;
2019-01-14 00:27:28 +00:00
QSize dimensions = porymapConfig.getRegionMapDimensions();
img_width_ = dimensions.width();
img_height_ = dimensions.height();
layout_width_ = img_width_ - this->padLeft - this->padRight;
layout_height_ = img_height_ - this->padTop - this->padBottom;
2019-01-14 00:27:28 +00:00
region_map_bin_path = path + "/graphics/pokenav/region_map_map.bin";
region_map_png_path = path + "/graphics/pokenav/region_map.png";
region_map_entries_path = path + "/src/data/region_map/region_map_entries.h";
2019-01-05 04:04:14 +00:00
region_map_layout_bin_path = path + "/graphics/pokenav/region_map_section_layout.bin";
2019-01-14 00:27:28 +00:00
city_map_tiles_path = path + "/graphics/pokenav/zoom_tiles.png";
readBkgImgBin();
2019-01-05 04:04:14 +00:00
readLayout();
}
2019-01-28 18:47:20 +00:00
void RegionMap::save() {
logInfo("Saving region map data.");
2019-03-25 04:10:57 +00:00
saveTileImages();
2019-01-28 18:47:20 +00:00
saveBkgImgBin();
saveLayout();
porymapConfig.setRegionMapDimensions(this->img_width_, this->img_height_);
}
2019-03-25 04:10:57 +00:00
void RegionMap::saveTileImages() {
QFile backgroundTileFile(pngPath());
if (backgroundTileFile.open(QIODevice::ReadOnly)) {
QByteArray imageData = backgroundTileFile.readAll();
QImage pngImage = QImage::fromData(imageData);
this->region_map_png_path = project->root + "/graphics/pokenav/region_map.png";
pngImage.save(pngPath());
}
QFile cityTileFile(cityTilesPath());
if (cityTileFile.open(QIODevice::ReadOnly)) {
QByteArray imageData = cityTileFile.readAll();
QImage cityTilesImage = QImage::fromData(imageData);
this->city_map_tiles_path = project->root + "/graphics/pokenav/zoom_tiles.png";
cityTilesImage.save(cityTilesPath());
}
}
void RegionMap::readBkgImgBin() {
QFile binFile(region_map_bin_path);
if (!binFile.open(QIODevice::ReadOnly)) return;
QByteArray mapBinData = binFile.readAll();
binFile.close();
2019-01-14 00:27:28 +00:00
if (mapBinData.size() < img_height_ * img_width_) {
logError(QString("The region map tilemap at %1 is too small.").arg(region_map_bin_path));
return;
}
for (int m = 0; m < img_height_; m++) {
for (int n = 0; n < img_width_; n++) {
2019-01-14 00:27:28 +00:00
RegionMapSquare square;
square.tile_img_id = mapBinData.at(n + m * img_width_ * 2);
map_squares.append(square);
}
}
}
void RegionMap::saveBkgImgBin() {
2019-01-28 18:47:20 +00:00
QByteArray data(pow(img_width_ * 2, 2),0);
for (int m = 0; m < img_height_; m++) {
for (int n = 0; n < img_width_; n++) {
data[n + m * img_width_ * 2] = map_squares[n + m * img_width_].tile_img_id;
}
}
QFile file(region_map_bin_path);
if (!file.open(QIODevice::WriteOnly)) return;
file.write(data);
file.close();
}
2019-01-05 04:04:14 +00:00
void RegionMap::readLayout() {
QFile file(region_map_entries_path);
if (!file.open(QIODevice::ReadOnly)) return;
2019-01-05 04:04:14 +00:00
QString line;
2019-01-05 04:04:14 +00:00
QMap<QString, QString> *qmap = new QMap<QString, QString>;
QTextStream in(&file);
2019-01-22 20:06:49 +00:00
in.setCodec("UTF-8");
while (!in.atEnd()) {
line = in.readLine();
2019-01-05 04:04:14 +00:00
if (line.startsWith("static const u8")) {
QRegularExpression reBefore("sMapName_(.*)\\[");
QRegularExpression reAfter("_\\(\"(.*)\"");
QString const_name = reBefore.match(line).captured(1);
QString full_name = reAfter.match(line).captured(1);
2019-01-14 00:27:28 +00:00
sMapNames.append(const_name);
sMapNamesMap.insert(const_name, full_name);
2019-01-05 04:04:14 +00:00
} else if (line.contains("MAPSEC")) {
QRegularExpression reBefore("\\[(.*)\\]");
QRegularExpression reAfter("{(.*)}");
QStringList entry = reAfter.match(line).captured(1).remove(" ").split(",");
QString mapsec = reBefore.match(line).captured(1);
QString insertion = entry[4].remove("sMapName_");
qmap->insert(mapsec, sMapNamesMap.value(insertion));
mapSecToMapEntry[mapsec] = {
// x y width height name
entry[0].toInt(), entry[1].toInt(), entry[2].toInt(), entry[3].toInt(), insertion
};
}
}
2019-01-05 04:04:14 +00:00
file.close();
project->mapSecToMapHoverName = qmap;
2019-01-05 04:04:14 +00:00
QFile binFile(region_map_layout_bin_path);
if (!binFile.open(QIODevice::ReadOnly)) return;
QByteArray mapBinData = binFile.readAll();
binFile.close();
2019-01-28 18:47:20 +00:00
for (int y = 0; y < layout_height_; y++) {
for (int x = 0; x < layout_width_; x++) {
int i = img_index_(x,y);
uint8_t id = static_cast<uint8_t>(mapBinData.at(layout_index_(x,y)));
map_squares[i].secid = id;
QString secname = project->mapSectionValueToName.value(id);
2019-01-05 04:04:14 +00:00
if (secname != "MAPSEC_NONE") map_squares[i].has_map = true;
map_squares[i].mapsec = secname;
2019-01-28 18:47:20 +00:00
map_squares[i].map_name = sMapNamesMap.value(mapSecToMapEntry.value(secname).name);
map_squares[i].x = x;
map_squares[i].y = y;
}
}
}
void RegionMap::saveLayout() {
2019-01-05 04:04:14 +00:00
QString entries_text;
QString layout_text;
entries_text += "#ifndef GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n";
entries_text += "#define GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n\n";
2019-01-14 00:27:28 +00:00
for (auto sName : sMapNames) {
entries_text += "static const u8 sMapName_" + sName + "[] = _(\"" + sMapNamesMap.value(sName) + "\");\n";
}
2019-01-05 04:04:14 +00:00
entries_text += "\nconst struct RegionMapLocation gRegionMapEntries[] = {\n";
for (auto sec : project->mapSectionNameToValue.keys()) {
if (!mapSecToMapEntry.contains(sec)) continue;
2019-01-05 04:04:14 +00:00
struct RegionMapEntry entry = mapSecToMapEntry.value(sec);
entries_text += " [" + sec + "] = {" + QString::number(entry.x) + ", " + QString::number(entry.y) + ", "
2019-01-14 00:27:28 +00:00
+ QString::number(entry.width) + ", " + QString::number(entry.height) + ", sMapName_" + entry.name + "},\n";
}
2019-01-05 04:04:14 +00:00
entries_text += "};\n\n#endif // GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n";
2019-01-05 04:04:14 +00:00
project->saveTextFile(region_map_entries_path, entries_text);
QByteArray data;
for (int m = 0; m < layout_height_; m++) {
for (int n = 0; n < layout_width_; n++) {
int i = img_index_(n,m);
data.append(map_squares[i].secid);
}
}
QFile bfile(region_map_layout_bin_path);
if (!bfile.open(QIODevice::WriteOnly)) return;
bfile.write(data);
bfile.close();
}
2019-01-14 00:27:28 +00:00
void RegionMap::saveOptions(int id, QString sec, QString name, int x, int y) {
resetSquare(id);
int index = getMapSquareIndex(x + this->padLeft, y + this->padTop);
2019-01-05 18:02:14 +00:00
if (!sec.isEmpty()) {
2019-03-25 04:10:57 +00:00
// Validate the input section name.
if (!project->mapSectionNameToValue.contains(sec)) {
sec = "MAPSEC_NONE";
name = QString();
}
2019-01-28 18:47:20 +00:00
this->map_squares[index].has_map = sec == "MAPSEC_NONE" ? false : true;
this->map_squares[index].secid = static_cast<uint8_t>(project->mapSectionNameToValue.value(sec));
2019-01-14 00:27:28 +00:00
this->map_squares[index].mapsec = sec;
if (!name.isEmpty()) {
this->map_squares[index].map_name = name;
this->project->mapSecToMapHoverName->insert(sec, name);
QString sName = fix_case(sec);
sMapNamesMap.insert(sName, name);
2019-01-14 00:27:28 +00:00
if (!mapSecToMapEntry.keys().contains(sec)) {
sMapNames.append(sName);
2019-01-28 18:47:20 +00:00
struct RegionMapEntry entry = {x, y, 1, 1, sName};
2019-01-14 00:27:28 +00:00
mapSecToMapEntry.insert(sec, entry);
}
}
this->map_squares[index].x = x;
this->map_squares[index].y = y;
this->map_squares[index].duplicated = false;
2019-01-05 18:02:14 +00:00
}
2019-01-05 04:04:14 +00:00
}
2019-01-14 00:27:28 +00:00
void RegionMap::resetSquare(int index) {
this->map_squares[index].mapsec = "MAPSEC_NONE";
this->map_squares[index].map_name = QString();
this->map_squares[index].has_map = false;
this->map_squares[index].secid = static_cast<uint8_t>(project->mapSectionNameToValue.value("MAPSEC_NONE"));
2019-01-14 00:27:28 +00:00
this->map_squares[index].has_city_map = false;
this->map_squares[index].city_map_name = QString();
this->map_squares[index].duplicated = false;
}
void RegionMap::clearLayout() {
for (int i = 0; i < map_squares.size(); i++)
resetSquare(i);
}
void RegionMap::clearImage() {
for (int i = 0; i < map_squares.size(); i++)
this->map_squares[i].tile_img_id = 0x00;
2019-01-14 00:27:28 +00:00
}
void RegionMap::replaceSectionId(unsigned oldId, unsigned newId) {
for (auto &square : map_squares) {
if (square.secid == oldId) {
square.has_map = false;
square.secid = newId;
QString secname = project->mapSectionValueToName.value(newId);
if (secname != "MAPSEC_NONE") square.has_map = true;
square.mapsec = secname;
square.map_name = sMapNamesMap.value(mapSecToMapEntry.value(secname).name);
}
}
}
2019-01-14 00:27:28 +00:00
void RegionMap::resize(int newWidth, int newHeight) {
QVector<RegionMapSquare> new_squares;
for (int y = 0; y < newHeight; y++) {
for (int x = 0; x < newWidth; x++) {
RegionMapSquare square;
if (x < img_width_ && y < img_height_) {
square = map_squares[getMapSquareIndex(x, y)];
} else if (x < newWidth - this->padRight && y < newHeight - this->padBottom) {
2019-01-14 00:27:28 +00:00
square.tile_img_id = 0;
square.x = x;
square.y = y;
square.mapsec = "MAPSEC_NONE";
} else {
square.tile_img_id = 0;
}
new_squares.append(square);
}
}
this->map_squares = new_squares;
this->img_width_ = newWidth;
this->img_height_ = newHeight;
this->layout_width_ = newWidth - this->padLeft - this->padRight;
this->layout_height_ = newHeight - this->padTop - this->padBottom;
2019-01-14 00:27:28 +00:00
}
2019-01-28 18:47:20 +00:00
QVector<uint8_t> RegionMap::getTiles() {
QVector<uint8_t> tileIds;
for (auto square : map_squares) {
tileIds.append(square.tile_img_id);
}
return tileIds;
}
void RegionMap::setTiles(QVector<uint8_t> tileIds) {
if (tileIds.size() != map_squares.size()) return;
int i = 0;
for (uint8_t tileId : tileIds) {
map_squares[i].tile_img_id = tileId;
i++;
}
}
// Layout coords to image index.
int RegionMap::img_index_(int x, int y) {
return ((x + this->padLeft) + (y + this->padTop) * img_width_);
}
// Layout coords to layout index.
int RegionMap::layout_index_(int x, int y) {
return (x + y * layout_width_);
}
int RegionMap::width() {
return this->img_width_;
}
int RegionMap::height() {
return this->img_height_;
}
QSize RegionMap::imgSize() {
return QSize(img_width_ * 8, img_height_ * 8);
}
unsigned RegionMap::getTileId(int x, int y) {
return map_squares.at(x + y * img_width_).tile_img_id;
}
QString RegionMap::pngPath() {
return this->region_map_png_path;
}
2019-03-25 04:10:57 +00:00
void RegionMap::setTemporaryPngPath(QString path) {
this->region_map_png_path = path;
}
2019-01-28 18:47:20 +00:00
QString RegionMap::cityTilesPath() {
return this->city_map_tiles_path;
}
2019-03-25 04:10:57 +00:00
void RegionMap::setTemporaryCityTilesPath(QString path) {
this->city_map_tiles_path = path;
}
2019-01-28 18:47:20 +00:00
// From x, y of image.
int RegionMap::getMapSquareIndex(int x, int y) {
int index = (x + y * img_width_);
return index < map_squares.length() ? index : 0;
}
// For turning a MAPSEC_NAME into a unique identifier sMapName-style variable.
// CAPS_WITH_UNDERSCORE to CamelCase
QString RegionMap::fix_case(QString caps) {
bool big = true;
QString camel;
for (auto ch : caps.remove(QRegularExpression("({.*})")).remove("MAPSEC")) {
if (ch == '_' || ch == ' ') {
big = true;
continue;
}
if (big) {
camel += ch.toUpper();
big = false;
}
else camel += ch.toLower();
}
return camel;
}