#include "paletteparser.h" #include "log.h" #include #include PaletteParser::PaletteParser() { } QList 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(); } 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(); } QList 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(); } 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 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(); } 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(); } 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(); } 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(); } QList palette; QRegularExpression re("(?\\d+)\\s(?\\d+)\\s(?\\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(); } 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(); } } file.close(); return palette; } QList 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(); } 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(); } QList palette; int i = 0; while (i < in.length()) { unsigned char blue = static_cast(in.at(i)); unsigned char green = static_cast(in.at(i + 1)); unsigned char red = static_cast(in.at(i + 2)); palette.append(qRgb(this->clampColorValue(red), this->clampColorValue(green), this->clampColorValue(blue))); i += 4; } return palette; } QList 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(); } 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(); } QList palette; int i = 0; while (i < in.length()) { unsigned char red = static_cast(in.at(i)); unsigned char green = static_cast(in.at(i + 1)); unsigned char blue = static_cast(in.at(i + 2)); palette.append(qRgb(this->clampColorValue(red), this->clampColorValue(green), this->clampColorValue(blue))); i += 3; } return palette; } QList 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(); } 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(); } 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(); } QList palette; int i = 4; while (i < in.length()) { unsigned char red = static_cast(in.at(i)); unsigned char green = static_cast(in.at(i + 1)); unsigned char blue = static_cast(in.at(i + 2)); palette.append(qRgb(this->clampColorValue(red), this->clampColorValue(green), this->clampColorValue(blue))); i += 3; } return palette; } QList 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(); } 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(); } QList 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(); } 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; }