Add ability to import metatiles from Advance Map (.bvd files)
This commit is contained in:
parent
b469cc047f
commit
3f88072981
8 changed files with 190 additions and 6 deletions
|
@ -477,6 +477,8 @@
|
|||
</property>
|
||||
<addaction name="actionImport_Primary_Tiles"/>
|
||||
<addaction name="actionImport_Secondary_Tiles"/>
|
||||
<addaction name="actionImport_Primary_Metatiles"/>
|
||||
<addaction name="actionImport_Secondary_Metatiles"/>
|
||||
<addaction name="actionChange_Metatiles_Count"/>
|
||||
<addaction name="actionChange_Palettes"/>
|
||||
<addaction name="separator"/>
|
||||
|
@ -505,12 +507,12 @@
|
|||
</action>
|
||||
<action name="actionImport_Primary_Tiles">
|
||||
<property name="text">
|
||||
<string>Import Primary Tiles...</string>
|
||||
<string>Import Primary Tiles Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Secondary_Tiles">
|
||||
<property name="text">
|
||||
<string>Import Secondary Tiles...</string>
|
||||
<string>Import Secondary Tiles Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionChange_Metatiles_Count">
|
||||
|
@ -549,6 +551,16 @@
|
|||
<string>Export Secondary Tiles Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Primary_Metatiles">
|
||||
<property name="text">
|
||||
<string>Import Primary Metatiles from Advance Map 1.92...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Secondary_Metatiles">
|
||||
<property name="text">
|
||||
<string>Import Secondary Metatiles from Advance Map 1.92...</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
@ -70,6 +70,7 @@ extern PorymapConfig porymapConfig;
|
|||
|
||||
enum BaseGameVersion {
|
||||
pokeruby,
|
||||
pokefirered,
|
||||
pokeemerald,
|
||||
};
|
||||
|
||||
|
|
15
include/core/metatileparser.h
Normal file
15
include/core/metatileparser.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef METATILEPARSER_H
|
||||
#define METATILEPARSER_H
|
||||
|
||||
#include "metatile.h"
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
class MetatileParser
|
||||
{
|
||||
public:
|
||||
MetatileParser();
|
||||
QList<Metatile*> *parse(QString filepath, bool *error, bool primaryTileset);
|
||||
};
|
||||
|
||||
#endif // METATILEPARSER_H
|
|
@ -79,6 +79,10 @@ private slots:
|
|||
|
||||
void on_actionExport_Secondary_Tiles_Image_triggered();
|
||||
|
||||
void on_actionImport_Primary_Metatiles_triggered();
|
||||
|
||||
void on_actionImport_Secondary_Metatiles_triggered();
|
||||
|
||||
private:
|
||||
void closeEvent(QCloseEvent*);
|
||||
void initMetatileSelector();
|
||||
|
@ -87,6 +91,7 @@ private:
|
|||
void initMetatileLayersItem();
|
||||
void drawSelectedTiles();
|
||||
void importTilesetTiles(Tileset*, bool);
|
||||
void importTilesetMetatiles(Tileset*, bool);
|
||||
void refresh();
|
||||
Ui::TilesetEditor *ui;
|
||||
History<MetatileHistoryItem*> metatileHistory;
|
||||
|
|
|
@ -22,6 +22,7 @@ SOURCES += src/core/block.cpp \
|
|||
src/core/map.cpp \
|
||||
src/core/maplayout.cpp \
|
||||
src/core/metatile.cpp \
|
||||
src/core/metatileparser.cpp \
|
||||
src/core/paletteparser.cpp \
|
||||
src/core/parseutil.cpp \
|
||||
src/core/tile.cpp \
|
||||
|
@ -67,6 +68,7 @@ HEADERS += include/core/block.h \
|
|||
include/core/mapconnection.h \
|
||||
include/core/maplayout.h \
|
||||
include/core/metatile.h \
|
||||
include/core/metatileparser.h \
|
||||
include/core/paletteparser.h \
|
||||
include/core/parseutil.h \
|
||||
include/core/tile.h \
|
||||
|
|
92
src/core/metatileparser.cpp
Normal file
92
src/core/metatileparser.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
#include "metatileparser.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "project.h"
|
||||
|
||||
MetatileParser::MetatileParser()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QList<Metatile*> *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<unsigned char>(in.at(0)) |
|
||||
(static_cast<unsigned char>(in.at(1)) << 8) |
|
||||
(static_cast<unsigned char>(in.at(2)) << 16) |
|
||||
(static_cast<unsigned char>(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<Metatile*> *metatiles = new QList<Metatile*>();
|
||||
for (int i = 0; i < numMetatiles; i++) {
|
||||
Metatile *metatile = new Metatile();
|
||||
QList<Tile> *tiles = new QList<Tile>();
|
||||
for (int j = 0; j < 8; j++) {
|
||||
int metatileOffset = 4 + i * metatileSize + j * 2;
|
||||
uint16_t word = static_cast<uint16_t>(
|
||||
static_cast<unsigned char>(in.at(metatileOffset)) |
|
||||
(static_cast<unsigned char>(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<unsigned char>(in.at(attrOffset)) |
|
||||
(static_cast<unsigned char>(in.at(attrOffset + 1)) << 8);
|
||||
metatile->behavior = value & 0xFF;
|
||||
metatile->layerType = (value & 0xF000) >> 12;
|
||||
metatile->tiles = tiles;
|
||||
metatiles->append(metatile);
|
||||
}
|
||||
|
||||
return metatiles;
|
||||
}
|
|
@ -124,7 +124,7 @@ QList<QRgb> PaletteParser::parseAdvanceMapPal(QString filepath, bool *error) {
|
|||
QFile file(filepath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
*error = true;
|
||||
logError(QString("Could not open Advance Map palette file '%1': ").arg(filepath) + file.errorString());
|
||||
logError(QString("Could not open Advance Map 1.92 palette file '%1': ").arg(filepath) + file.errorString());
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
|
@ -133,16 +133,16 @@ QList<QRgb> PaletteParser::parseAdvanceMapPal(QString filepath, bool *error) {
|
|||
|
||||
if (in.length() % 4 != 0) {
|
||||
*error = true;
|
||||
logError(QString("Advance Map 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()));
|
||||
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<QRgb>();
|
||||
}
|
||||
|
||||
QList<QRgb> palette;
|
||||
int i = 0;
|
||||
while (i < in.length()) {
|
||||
unsigned char blue = static_cast<unsigned char>(in.at(i));
|
||||
unsigned char red = static_cast<unsigned char>(in.at(i));
|
||||
unsigned char green = static_cast<unsigned char>(in.at(i + 1));
|
||||
unsigned char red = static_cast<unsigned char>(in.at(i + 2));
|
||||
unsigned char blue = static_cast<unsigned char>(in.at(i + 2));
|
||||
palette.append(qRgb(this->clampColorValue(red),
|
||||
this->clampColorValue(green),
|
||||
this->clampColorValue(blue)));
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "ui_tileseteditor.h"
|
||||
#include "log.h"
|
||||
#include "imageproviders.h"
|
||||
#include "metatileparser.h"
|
||||
#include "paletteparser.h"
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
@ -626,3 +627,59 @@ void TilesetEditor::on_actionExport_Secondary_Tiles_Image_triggered()
|
|||
image.save(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionImport_Primary_Metatiles_triggered()
|
||||
{
|
||||
this->importTilesetMetatiles(this->primaryTileset, true);
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionImport_Secondary_Metatiles_triggered()
|
||||
{
|
||||
this->importTilesetMetatiles(this->secondaryTileset, false);
|
||||
}
|
||||
|
||||
void TilesetEditor::importTilesetMetatiles(Tileset *tileset, bool primary)
|
||||
{
|
||||
QString descriptor = primary ? "primary" : "secondary";
|
||||
QString descriptorCaps = primary ? "Primary" : "Secondary";
|
||||
|
||||
QString filepath = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
QString("Import %1 Tileset Metatiles from Advance Map 1.92").arg(descriptorCaps),
|
||||
this->project->root,
|
||||
"Advance Map 1.92 Metatile Files (*.bvd)");
|
||||
if (filepath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MetatileParser parser;
|
||||
bool error = false;
|
||||
QList<Metatile*> *metatiles = parser.parse(filepath, &error, primary);
|
||||
if (error) {
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setText("Failed to import metatiles from Advance Map 1.92 .bvd file.");
|
||||
QString message = QString("The .bvd file could not be processed. View porymap.log for specific errors.");
|
||||
msgBox.setInformativeText(message);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.setIcon(QMessageBox::Icon::Critical);
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
\
|
||||
// TODO: This is crude because it makes a history entry for every newly-imported metatile.
|
||||
// Revisit this when tiles and num metatiles are added to tileset editory history.
|
||||
int metatileIdBase = primary ? 0 : Project::getNumMetatilesPrimary();
|
||||
for (int i = 0; i < metatiles->length(); i++) {
|
||||
if (i >= tileset->metatiles->length()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Metatile *prevMetatile = tileset->metatiles->at(i)->copy();
|
||||
MetatileHistoryItem *commit = new MetatileHistoryItem(static_cast<uint16_t>(metatileIdBase + i), prevMetatile, metatiles->at(i)->copy());
|
||||
metatileHistory.push(commit);
|
||||
}
|
||||
|
||||
tileset->metatiles = metatiles;
|
||||
this->refresh();
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue