Add ability to import JASC and A-Map palettes into the tileset palette editor.
This commit is contained in:
parent
99a71afbfe
commit
851f8ec813
7 changed files with 393 additions and 8 deletions
|
@ -10,6 +10,9 @@
|
|||
<height>739</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
|
@ -1651,7 +1654,14 @@
|
|||
<addaction name="actionUndo"/>
|
||||
<addaction name="actionRedo"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuTools">
|
||||
<property name="title">
|
||||
<string>Tools</string>
|
||||
</property>
|
||||
<addaction name="actionImport_Palette"/>
|
||||
</widget>
|
||||
<addaction name="menuEdit"/>
|
||||
<addaction name="menuTools"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="actionUndo">
|
||||
|
@ -1661,6 +1671,12 @@
|
|||
<property name="shortcut">
|
||||
<string>Ctrl+Z</string>
|
||||
</property>
|
||||
<property name="shortcutContext">
|
||||
<enum>Qt::WindowShortcut</enum>
|
||||
</property>
|
||||
<property name="priority">
|
||||
<enum>QAction::NormalPriority</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRedo">
|
||||
<property name="text">
|
||||
|
@ -1670,6 +1686,11 @@
|
|||
<string>Ctrl+Y</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Palette">
|
||||
<property name="text">
|
||||
<string>Import Palette</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Tileset Editor</string>
|
||||
</property>
|
||||
|
@ -44,7 +47,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>272</width>
|
||||
<height>538</height>
|
||||
<height>539</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
|
@ -365,7 +368,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>366</height>
|
||||
<height>367</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
|
@ -458,7 +461,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>700</width>
|
||||
<height>20</height>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
|
|
23
include/core/paletteparser.h
Normal file
23
include/core/paletteparser.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef PALETTEPARSER_H
|
||||
#define PALETTEPARSER_H
|
||||
|
||||
#include <QList>
|
||||
#include <QRgb>
|
||||
#include <QString>
|
||||
|
||||
class PaletteParser
|
||||
{
|
||||
public:
|
||||
PaletteParser();
|
||||
QList<QRgb> parse(QString filepath, bool *error);
|
||||
private:
|
||||
QList<QRgb> parsePal(QString filepath, bool *error);
|
||||
QList<QRgb> parseJASC(QString filepath, bool *error);
|
||||
QList<QRgb> parseAdvanceMapPal(QString filepath, bool *error);
|
||||
QList<QRgb> parseAdobeColorTable(QString filepath, bool *error);
|
||||
QList<QRgb> parseTileLayerPro(QString filepath, bool *error);
|
||||
QList<QRgb> parseAdvancePaletteEditor(QString filepath, bool *error);
|
||||
int clampColorValue(int value);
|
||||
};
|
||||
|
||||
#endif // PALETTEPARSER_H
|
|
@ -43,7 +43,7 @@ private:
|
|||
void refreshColors();
|
||||
void refreshColor(int);
|
||||
void setColor(int);
|
||||
void commitEditHistory();
|
||||
void commitEditHistory(int paletteid);
|
||||
void setColorsFromHistory(PaletteHistoryItem*, int);
|
||||
|
||||
signals:
|
||||
|
@ -54,6 +54,7 @@ private slots:
|
|||
void on_spinBox_PaletteId_valueChanged(int arg1);
|
||||
void on_actionUndo_triggered();
|
||||
void on_actionRedo_triggered();
|
||||
void on_actionImport_Palette_triggered();
|
||||
};
|
||||
|
||||
#endif // PALETTEEDITOR_H
|
||||
|
|
|
@ -22,6 +22,7 @@ SOURCES += src/core/block.cpp \
|
|||
src/core/map.cpp \
|
||||
src/core/maplayout.cpp \
|
||||
src/core/metatile.cpp \
|
||||
src/core/paletteparser.cpp \
|
||||
src/core/parseutil.cpp \
|
||||
src/core/tile.cpp \
|
||||
src/core/tileset.cpp \
|
||||
|
@ -64,6 +65,7 @@ HEADERS += include/core/block.h \
|
|||
include/core/mapconnection.h \
|
||||
include/core/maplayout.h \
|
||||
include/core/metatile.h \
|
||||
include/core/paletteparser.h \
|
||||
include/core/parseutil.h \
|
||||
include/core/tile.h \
|
||||
include/core/tileset.h \
|
||||
|
|
278
src/core/paletteparser.cpp
Normal file
278
src/core/paletteparser.cpp
Normal file
|
@ -0,0 +1,278 @@
|
|||
#include "paletteparser.h"
|
||||
#include "log.h"
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
|
||||
PaletteParser::PaletteParser()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QList<QRgb> PaletteParser::parse(QString filepath, bool *error) {
|
||||
QFileInfo info(filepath);
|
||||
QString extension = info.completeSuffix();
|
||||
if (extension.isNull()) {
|
||||
logError(QString("Failed to parse palette file '%1' because it has an unrecognized extension '%2'").arg(filepath).arg(extension));
|
||||
*error = true;
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
extension = extension.toLower();
|
||||
if (extension == "pal") {
|
||||
return parsePal(filepath, error);
|
||||
} else if (extension == "act") {
|
||||
return parseAdobeColorTable(filepath, error);
|
||||
} else if (extension == "tpl") {
|
||||
return parseTileLayerPro(filepath, error);
|
||||
} else if (extension == "gpl") {
|
||||
return parseAdvancePaletteEditor(filepath, error);
|
||||
} else {
|
||||
logError(QString("Unsupported palette file. Supported formats are: .pal"));
|
||||
*error = true;
|
||||
}
|
||||
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QList<QRgb> PaletteParser::parsePal(QString filepath, bool *error) {
|
||||
QFile file(filepath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
*error = true;
|
||||
logError(QString("Could not open palette file '%1': ").arg(filepath) + file.errorString());
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
QString firstLine = in.readLine();
|
||||
if (firstLine == "JASC-PAL") {
|
||||
file.close();
|
||||
return parseJASC(filepath, error);
|
||||
} else {
|
||||
file.close();
|
||||
return parseAdvanceMapPal(filepath, error);
|
||||
}
|
||||
}
|
||||
|
||||
QList<QRgb> PaletteParser::parseJASC(QString filepath, bool *error) {
|
||||
QFile file(filepath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
*error = true;
|
||||
logError(QString("Could not open JASC palette file '%1': ").arg(filepath) + file.errorString());
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
if (in.readLine() != "JASC-PAL") {
|
||||
*error = true;
|
||||
logError(QString("JASC palette file '%1' had an unexpected format. First line must be 'JASC-PAL'.").arg(filepath));
|
||||
file.close();
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
if (in.readLine() != "0100") {
|
||||
*error = true;
|
||||
logError(QString("JASC palette file '%1' had an unexpected format. Second line must be '0100'.").arg(filepath));
|
||||
file.close();
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QString numColorsStr = in.readLine();
|
||||
bool numOk;
|
||||
int numColors = numColorsStr.toInt(&numOk);
|
||||
if (!numOk) {
|
||||
*error = true;
|
||||
logError(QString("JASC palette file '%1' had an unexpected format. Third line must be the number of colors.").arg(filepath));
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QList<QRgb> palette;
|
||||
QRegularExpression re("(?<red>\\d+)\\s(?<green>\\d+)\\s(?<blue>\\d+)");
|
||||
while (!in.atEnd() && numColors > 0) {
|
||||
numColors--;
|
||||
QString line = in.readLine();
|
||||
QRegularExpressionMatch match = re.match(line);
|
||||
if (match.hasMatch()) {
|
||||
QString redStr = match.captured("red");
|
||||
QString greenStr = match.captured("green");
|
||||
QString blueStr = match.captured("blue");
|
||||
bool redOk, greenOk, blueOk;
|
||||
int red = redStr.toInt(&redOk);
|
||||
int green = greenStr.toInt(&greenOk);
|
||||
int blue = blueStr.toInt(&blueOk);
|
||||
if (!redOk || !greenOk || !blueOk) {
|
||||
*error = true;
|
||||
logError(QString("JASC palette file '%1' had an unexpected format. Invalid color '%2'.").arg(filepath).arg(line));
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
palette.append(qRgb(this->clampColorValue(red),
|
||||
this->clampColorValue(green),
|
||||
this->clampColorValue(blue)));
|
||||
} else {
|
||||
*error = true;
|
||||
logError(QString("JASC palette file '%1' had an unexpected format. Invalid color '%2'.").arg(filepath).arg(line));
|
||||
file.close();
|
||||
return QList<QRgb>();
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
return palette;
|
||||
}
|
||||
|
||||
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());
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QByteArray in = file.readAll();
|
||||
file.close();
|
||||
|
||||
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()));
|
||||
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 green = static_cast<unsigned char>(in.at(i + 1));
|
||||
unsigned char red = static_cast<unsigned char>(in.at(i + 2));
|
||||
palette.append(qRgb(this->clampColorValue(red),
|
||||
this->clampColorValue(green),
|
||||
this->clampColorValue(blue)));
|
||||
i += 4;
|
||||
}
|
||||
|
||||
return palette;
|
||||
}
|
||||
|
||||
QList<QRgb> PaletteParser::parseAdobeColorTable(QString filepath, bool *error) {
|
||||
QFile file(filepath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
*error = true;
|
||||
logError(QString("Could not open Adobe Color Table palette file '%1': ").arg(filepath) + file.errorString());
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QByteArray in = file.readAll();
|
||||
file.close();
|
||||
|
||||
if (in.length() != 0x300) {
|
||||
*error = true;
|
||||
logError(QString("Adobe Color Table palette file '%1' had an unexpected format. File's length must be exactly 768, 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 red = static_cast<unsigned char>(in.at(i));
|
||||
unsigned char green = static_cast<unsigned char>(in.at(i + 1));
|
||||
unsigned char blue = static_cast<unsigned char>(in.at(i + 2));
|
||||
palette.append(qRgb(this->clampColorValue(red),
|
||||
this->clampColorValue(green),
|
||||
this->clampColorValue(blue)));
|
||||
i += 3;
|
||||
}
|
||||
|
||||
return palette;
|
||||
}
|
||||
|
||||
QList<QRgb> PaletteParser::parseTileLayerPro(QString filepath, bool *error) {
|
||||
QFile file(filepath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
*error = true;
|
||||
logError(QString("Could not open Tile Layer Pro palette file '%1': ").arg(filepath) + file.errorString());
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QByteArray in = file.readAll();
|
||||
file.close();
|
||||
|
||||
if (in.length() < 4 || in.at(0) != 'T' || in.at(1) != 'L' || in.at(2) != 'P' || in.at(3) != 0) {
|
||||
*error = true;
|
||||
logError(QString("Tile Layer Pro palette file '%1' had an unexpected format. The TLP header is missing.").arg(filepath).arg(in.length()));
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
if (in.length() != 0x304) {
|
||||
*error = true;
|
||||
logError(QString("Tile Layer Pro palette file '%1' had an unexpected format. File's length must be exactly 772, but the length is %2.").arg(filepath).arg(in.length()));
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QList<QRgb> palette;
|
||||
int i = 4;
|
||||
while (i < in.length()) {
|
||||
unsigned char red = static_cast<unsigned char>(in.at(i));
|
||||
unsigned char green = static_cast<unsigned char>(in.at(i + 1));
|
||||
unsigned char blue = static_cast<unsigned char>(in.at(i + 2));
|
||||
palette.append(qRgb(this->clampColorValue(red),
|
||||
this->clampColorValue(green),
|
||||
this->clampColorValue(blue)));
|
||||
i += 3;
|
||||
}
|
||||
|
||||
return palette;
|
||||
}
|
||||
|
||||
QList<QRgb> PaletteParser::parseAdvancePaletteEditor(QString filepath, bool *error) {
|
||||
QFile file(filepath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
*error = true;
|
||||
logError(QString("Could not open GPL palette file '%1': ").arg(filepath) + file.errorString());
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
if (in.readLine() != "[APE Palette]") {
|
||||
*error = true;
|
||||
logError(QString("GPL palette file '%1' had an unexpected format. First line must be '[APE Palette]'.").arg(filepath));
|
||||
file.close();
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
QList<QRgb> palette;
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine().trimmed();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
unsigned int raw = line.toUInt(&ok);
|
||||
if (!ok) {
|
||||
*error = true;
|
||||
logError(QString("GPL palette file '%1' had an unexpected format. Invalid color '%2'.").arg(filepath).arg(line));
|
||||
file.close();
|
||||
return QList<QRgb>();
|
||||
}
|
||||
|
||||
raw = ((raw & 0xFF)<< 8) | ((raw >> 8) & 0xFF);
|
||||
int red = (raw & 0x1F) * 8;
|
||||
int green = ((raw >> 5) & 0x1F) * 8;
|
||||
int blue = ((raw >> 10) & 0x1F) * 8;
|
||||
palette.append(qRgb(this->clampColorValue(red),
|
||||
this->clampColorValue(green),
|
||||
this->clampColorValue(blue)));
|
||||
}
|
||||
|
||||
file.close();
|
||||
return palette;
|
||||
}
|
||||
|
||||
int PaletteParser::clampColorValue(int value) {
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
}
|
||||
if (value > 255) {
|
||||
value = 255;
|
||||
}
|
||||
return value;
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
#include "paletteeditor.h"
|
||||
#include "ui_paletteeditor.h"
|
||||
#include "paletteparser.h"
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include "log.h"
|
||||
|
||||
PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset *secondaryTileset, QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
|
@ -108,7 +112,7 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
|
|||
this->initColorSliders();
|
||||
this->refreshColorSliders();
|
||||
this->refreshColors();
|
||||
this->commitEditHistory();
|
||||
this->commitEditHistory(this->ui->spinBox_PaletteId->value());
|
||||
}
|
||||
|
||||
PaletteEditor::~PaletteEditor()
|
||||
|
@ -194,18 +198,20 @@ void PaletteEditor::setColor(int colorIndex) {
|
|||
: this->secondaryTileset;
|
||||
(*tileset->palettes)[paletteNum][colorIndex] = qRgb(red, green, blue);
|
||||
this->refreshColor(colorIndex);
|
||||
this->commitEditHistory();
|
||||
this->commitEditHistory(paletteNum);
|
||||
emit this->changedPaletteColor();
|
||||
}
|
||||
|
||||
void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) {
|
||||
this->refreshColorSliders();
|
||||
this->refreshColors();
|
||||
if (!this->palettesHistory[paletteId].current()) {
|
||||
this->commitEditHistory(paletteId);
|
||||
}
|
||||
emit this->changedPalette(paletteId);
|
||||
}
|
||||
|
||||
void PaletteEditor::commitEditHistory() {
|
||||
int paletteId = this->ui->spinBox_PaletteId->value();
|
||||
void PaletteEditor::commitEditHistory(int paletteId) {
|
||||
QList<QRgb> colors;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
colors.append(qRgb(this->sliders[i][0]->value() * 8, this->sliders[i][1]->value() * 8, this->sliders[i][2]->value() * 8));
|
||||
|
@ -243,3 +249,54 @@ void PaletteEditor::setColorsFromHistory(PaletteHistoryItem *history, int palett
|
|||
this->refreshColors();
|
||||
emit this->changedPaletteColor();
|
||||
}
|
||||
|
||||
void PaletteEditor::on_actionImport_Palette_triggered()
|
||||
{
|
||||
QString filepath = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
QString("Import Tileset Palette"),
|
||||
this->project->root,
|
||||
"Palette Files (*.pal *.act *tpl *gpl)");
|
||||
if (filepath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PaletteParser parser;
|
||||
bool error = false;
|
||||
QList<QRgb> palette = parser.parse(filepath, &error);
|
||||
if (error) {
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setText("Failed to import palette.");
|
||||
QString message = QString("The palette 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;
|
||||
}
|
||||
|
||||
if (palette.length() < 16) {
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setText("Failed to import palette.");
|
||||
QString message = QString("The palette file has %1 colors, but it must have 16 colors.").arg(palette.length());
|
||||
msgBox.setInformativeText(message);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.setIcon(QMessageBox::Icon::Critical);
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
int paletteId = this->ui->spinBox_PaletteId->value();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (paletteId < Project::getNumPalettesPrimary()) {
|
||||
(*this->primaryTileset->palettes)[paletteId][i] = palette.at(i);
|
||||
} else {
|
||||
(*this->secondaryTileset->palettes)[paletteId][i] = palette.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
this->refreshColorSliders();
|
||||
this->refreshColors();
|
||||
this->commitEditHistory(paletteId);
|
||||
emit this->changedPaletteColor();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue