Refactor DataFileParser out of TileGenerator

master
Unknown 2018-04-14 09:02:22 +02:00
parent 685b136563
commit b0eb0675d7
6 changed files with 328 additions and 246 deletions

View File

@ -16,6 +16,8 @@ set(sources
ZlibDecompressor.h
Color.cpp
Color.h
DataFileParser.cpp
DataFileParser.h
Settings.cpp
Settings.h
BlockPos.cpp

View File

@ -46,4 +46,11 @@ inline Color &Color::operator=(const Color &c)
return *this;
}
struct HeightMapColor
{
HeightMapColor(int h0, Color c0, int h1, Color c1) : height{ h0, h1 }, color{ c0, c1 } {}
int height[2];
Color color[2];
};
#endif // COLOR_H

View File

@ -0,0 +1,244 @@
#include "DataFileParser.h"
#include <climits>
#include <fstream>
#include <iostream>
using namespace std;
DataFileParser::DataFileParser(int verboseReadColors, bool drawAlpha) :
verboseReadColors(verboseReadColors),
m_drawAlpha(drawAlpha)
{
};
DataFileParser::~DataFileParser()
= default;
void DataFileParser::parseDataFile(const std::string & fileName, const char * type, int depth)
{
if (depth > 100) {
throw std::runtime_error(std::string("Excessive inclusion depth of ") + type + " files - suspected recursion (i.e. cycle); current file: '" + fileName + "'");
}
if (depth == 0 && verboseReadColors >= 2) {
cout << "Checking for " << type << " file: " << fileName << std::endl;
}
ifstream in;
in.open(fileName.c_str(), ifstream::in);
if (!in.is_open()) {
throw std::runtime_error(std::string("Failed to open ") + type + " file '" + fileName + "'");
return;
}
if (verboseReadColors >= 1) {
cout << "Reading " << type << " file: " << fileName << std::endl;
}
parseDataStream(in, fileName, depth, type);
in.close();
}
void DataFileParser::parseDataStream(std::istream & in, const std::string & filename, int depth, const char * type)
{
string line;
int linenr = 0;
for (std::getline(in, line); in.good(); std::getline(in, line)) {
linenr++;
size_t comment = line.find_first_of('#');
if (comment != string::npos) {
line.erase(comment);
}
istringstream iline;
iline.str(line);
iline >> std::skipws;
string name;
iline >> name >> std::ws;
if (name.empty()) {
continue;
}
if (name == "@include") {
string includeFile;
getline(iline, includeFile);
size_t lastChar = includeFile.find_last_not_of(" \t\r\n");
if (lastChar != string::npos) {
includeFile.erase(lastChar + 1);
}
if (includeFile.empty()) {
std::cerr << filename << ":" << linenr << ": include filename missing in colors file (" << line << ")" << std::endl;
continue;
}
if (includeFile[0] != '/') {
string includePath = filename;
size_t offset = includePath.find_last_of('/');
if (offset != string::npos) {
includePath.erase(offset);
includePath.append("/").append(includeFile);
includeFile = includePath;
}
}
parseDataFile(includeFile, type, depth + 1);
}
else {
parseLine(line, name, iline, linenr, filename);
}
}
if (!in.eof()) {
std::cerr << filename << ": error reading colors file after line " << linenr << std::endl;
}
}
void ColorsFileParser::parseLine(const std::string & line, std::string name, std::istringstream & iline, int linenr, const std::string & filename)
{
iline >> std::ws >> std::skipws;
if (iline.good() && iline.peek() == '-') {
char c;
iline >> c >> std::ws;
if (iline.fail() || !iline.eof()) {
std::cerr << filename << ":" << linenr << ": bad line in colors file (" << line << ")" << std::endl;
return;
}
m_nodeColors.erase(name);
}
else {
int r, g, b, a, t, f;
std::string flags;
ColorEntry color;
iline >> r;
iline >> g;
iline >> b;
if (iline.fail()) {
std::cerr << filename << ":" << linenr << ": bad line in colors file (" << line << ")" << std::endl;
return;
}
a = 0xff;
iline >> std::ws;
if (iline.good() && isdigit(iline.peek())) {
iline >> a >> std::ws;
}
t = 0;
if (iline.good() && isdigit(iline.peek())) {
iline >> t >> std::ws;
}
if (iline.good() && !isdigit(iline.peek())) {
iline >> flags >> std::ws;
}
f = 0;
if (!iline.fail() && !flags.empty()) {
for (char & flag : flags) {
if (flag == ',') {
flag = ' ';
}
}
istringstream iflags(flags);
std::string flag;
iflags >> flag;
while (!iflags.fail()) {
if (flag == "ignore") {
f |= ColorEntry::FlagIgnore;
}
else if (flag == "air") {
f |= ColorEntry::FlagAir;
}
iflags >> flag;
}
}
color = ColorEntry(r, g, b, a, t, f);
if ((m_drawAlpha && a == 0xff) || (!m_drawAlpha && a != 0xff)) {
// If drawing alpha, and the colors file contains both
// an opaque entry and a non-opaque entry for a name, prefer
// the non-opaque entry
// If not drawing alpha, and the colors file contains both
// an opaque entry and a non-opaque entry for a name, prefer
// the opaque entry
// Otherwise, any later entry overrides any previous entry
auto it = m_nodeColors.find(name);
if (it != m_nodeColors.end()) {
if (m_drawAlpha && (a == 0xff && it->second.a != 0xff)) {
// drawing alpha: don't use opaque color to override
// non-opaque color
return;
}
if (!m_drawAlpha && (a != 0xff && it->second.a == 0xff)) {
// not drawing alpha: don't use non-opaque color to
// override opaque color
return;
}
}
}
m_nodeColors[name] = color;
}
}
void HeightMapColorsFileParser::parseLine(const std::string & line, std::string name, std::istringstream & iline, int linenr, const std::string & filename)
{
(void)name;
int height[2];
Color color[2];
iline.str(line); // Reset
for (int & i : height) {
iline >> std::ws;
char c = iline.peek();
iline >> i;
if (iline.fail()) {
std::string value;
iline.clear();
iline >> std::ws;
iline >> value >> std::ws;
if (!iline.fail()) {
if (value == "-oo" || (c == '-' && value == "oo")) {
i = INT_MIN;
}
else if (value == "oo" || value == "+oo") {
i = INT_MAX;
}
else {
iline.clear(ios::failbit); // Set to failed
break;
}
}
}
}
for (auto & i : color) {
int r, g, b;
iline >> r;
iline >> g;
iline >> b;
i = Color(r, g, b);
}
if (height[0] > height[1]) {
{
int tmp = height[0];
height[0] = height[1];
height[1] = tmp;
}
{
Color tmp = color[0];
color[0] = color[1];
color[1] = tmp;
}
}
iline >> std::ws;
if (iline.fail() || !iline.eof()) {
std::cerr << filename << ":" << linenr << ": bad line in heightmap colors file (" << line << ")" << std::endl;
return;
}
m_heightMapColors.push_back(HeightMapColor(height[0], color[0], height[1], color[1]));
}
void HeightMapNodesFileParser::parseLine(const std::string & line, std::string name, std::istringstream & iline, int linenr, const std::string & filename)
{
if (name == "-") {
iline >> std::ws >> name >> std::ws;
m_nodeColors.erase(name);
}
else {
m_nodeColors[name] = ColorEntry(0, 0, 0, 255, 1, 0); // Dummy entry - but must not be transparent
}
// Don't care if not at eof (== really eol). We might be reading a colors.txt file...
if (iline.fail()) {
std::cerr << filename << ":" << linenr << ": bad line in heightmap nodes file (" << line << ")" << std::endl;
return;
}
}

View File

@ -0,0 +1,63 @@
#pragma once
#include <istream>
#include <list>
#include <sstream>
#include <string>
#include <unordered_map>
#include "Color.h"
class DataFileParser
{
public:
using NodeColorMap = std::unordered_map<std::string, ColorEntry>;
using HeightMapColorList = std::list<HeightMapColor>;
DataFileParser(int verboseReadColors = 0, bool drawAlpha = false);
~DataFileParser();
void parseDataFile(const std::string &fileName, const char *type, int depth = 0);
void parseDataStream(std::istream &in, const std::string &filename, int depth, const char *type);
virtual void parseLine(const std::string &line, std::string name,
std::istringstream &iline, int linenr, const std::string &filename) = 0;
protected:
int verboseReadColors = 0;
bool m_drawAlpha = false;
};
class ColorsFileParser : public DataFileParser
{
public:
ColorsFileParser(int verboseReadColors = 0, bool drawAlpha = false) : DataFileParser(verboseReadColors, drawAlpha) {}
NodeColorMap getNodeColors() { return m_nodeColors; }
private:
const std::string type = "map colors";
void parseLine(const std::string &line, std::string name, std::istringstream &iline, int linenr, const std::string &filename) override;
NodeColorMap m_nodeColors;
};
class HeightMapColorsFileParser : public DataFileParser
{
public:
HeightMapColorsFileParser(int verboseReadColors = 0, bool drawAlpha = false) : DataFileParser(verboseReadColors, drawAlpha) {}
HeightMapColorList getHeightMapColors() { return m_heightMapColors; }
private:
void parseLine(const std::string &line, std::string name, std::istringstream &iline, int linenr, const std::string &filename) override;
HeightMapColorList m_heightMapColors;
};
class HeightMapNodesFileParser : public DataFileParser
{
public:
HeightMapNodesFileParser(int verboseReadColors = 0, bool drawAlpha = false) : DataFileParser(verboseReadColors, drawAlpha) {}
NodeColorMap getNodeColors() { return m_nodeColors; }
private:
void parseLine(const std::string &line, std::string name, std::istringstream &iline, int linenr, const std::string &filename) override;
NodeColorMap m_nodeColors;
};

View File

@ -16,7 +16,10 @@
#include <stdexcept>
#include <cerrno>
#include <cstring>
#include "config.h"
#include "DataFileParser.h"
#include "Settings.h"
#include "PlayerAttributes.h"
#include "TileGenerator.h"
@ -111,11 +114,6 @@ const ColorEntry *TileGenerator::NodeColorNotDrawn = &nodeColorNotDrawnObject;
const BlockPos TileGenerator::BlockPosLimitMin(MAPBLOCK_MIN, MAPBLOCK_MIN, MAPBLOCK_MIN);
const BlockPos TileGenerator::BlockPosLimitMax(MAPBLOCK_MAX, MAPBLOCK_MAX, MAPBLOCK_MAX);
struct HeightMapColor
{
int height[2];
Color color[2];
};
TileGenerator::TileGenerator():
verboseCoordinates(0),
@ -431,40 +429,23 @@ void TileGenerator::setMaxY(int y)
void TileGenerator::parseNodeColorsFile(const std::string &fileName)
{
m_nodeColors.clear();
parseDataFile(fileName, 0, "map colors", &TileGenerator::parseNodeColorsLine);
ColorsFileParser d(verboseReadColors, m_drawAlpha);
d.parseDataFile(fileName, "map colors");
m_nodeColors = d.getNodeColors();
}
void TileGenerator::parseHeightMapNodesFile(const std::string &fileName)
{
m_nodeColors.clear();
parseDataFile(fileName, 0, "heightmap nodes", &TileGenerator::parseHeightMapNodesLine);
HeightMapNodesFileParser p(verboseReadColors, m_drawAlpha);
p.parseDataFile(fileName, "heightmap nodes");
m_nodeColors = p.getNodeColors();
}
void TileGenerator::parseHeightMapColorsFile(const std::string &fileName)
{
m_heightMapColors.clear();
parseDataFile(fileName, 0, "heightmap colors", &TileGenerator::parseHeightMapColorsLine);
}
void TileGenerator::parseDataFile(const std::string &fileName, int depth, const char *type,
void (TileGenerator::*parseLine)(const std::string &line, std::string name,
istringstream &iline, int linenr, const std::string &filename))
{
if (depth > 100)
throw std::runtime_error(std::string("Excessive inclusion depth of ") + type + " files - suspected recursion (i.e. cycle); current file: '" + fileName + "'");
if (depth == 0 && verboseReadColors >= 2)
cout << "Checking for " << type << " file: " << fileName << std::endl;
ifstream in;
in.open(fileName.c_str(), ifstream::in);
if (!in.is_open()) {
throw std::runtime_error(std::string("Failed to open ") + type + " file '" + fileName + "'");
return;
}
if (verboseReadColors >= 1)
cout << "Reading " << type << " file: " << fileName << std::endl;
parseDataStream(in, fileName, depth, type, parseLine);
in.close();
HeightMapColorsFileParser p(verboseReadColors, m_drawAlpha);
p.parseDataFile(fileName, "heightmap colors");
m_heightMapColors = p.getHeightMapColors();
}
void TileGenerator::setBackend(std::string backend)
@ -559,202 +540,6 @@ void TileGenerator::generate(const std::string &input, const std::string &output
printUnknown();
}
void TileGenerator::parseDataStream(std::istream &in, const std::string &filename, int depth, const char *type,
void (TileGenerator::*parseLine)(const std::string &line, std::string name,
istringstream &iline, int linenr, const std::string &filename))
{
string line;
int linenr = 0;
for (std::getline(in,line); in.good(); std::getline(in,line)) {
linenr++;
size_t comment = line.find_first_of('#');
if (comment != string::npos)
line.erase(comment);
istringstream iline;
iline.str(line);
iline >> std::skipws;
string name;
iline >> name >> std::ws;
if (name.length() == 0)
continue;
if (name == "@include") {
string includeFile;
getline(iline,includeFile);
size_t lastChar = includeFile.find_last_not_of(" \t\r\n");
if (lastChar != string::npos)
includeFile.erase(lastChar + 1);
if (includeFile == "") {
std::cerr << filename << ":" << linenr << ": include filename missing in colors file (" << line << ")" << std::endl;
continue;
}
#if ! (MSDOS || __OS2__ || __NT__ || _WIN32)
// This same feature seems needlessly complicated on windows - so it is not supported
if (includeFile[0] != '/') {
string includePath = filename;
size_t offset = includePath.find_last_of('/');
if (offset != string::npos) {
includePath.erase(offset);
includeFile = includePath + '/' + includeFile;
}
}
#endif
parseDataFile(includeFile, depth + 1, type, parseLine);
}
else {
(this->*parseLine)(line, name, iline, linenr, filename);
}
}
if (!in.eof()) {
std::cerr << filename << ": error reading colors file after line " << linenr << std::endl;
}
}
void TileGenerator::parseNodeColorsLine(const std::string &line, std::string name, istringstream &iline, int linenr, const std::string &filename)
{
iline >> std::ws >> std::skipws;
if (iline.good() && iline.peek() == '-') {
char c;
iline >> c >> std::ws;
if (iline.fail() || !iline.eof()) {
std::cerr << filename << ":" << linenr << ": bad line in colors file (" << line << ")" << std::endl;
return;
}
m_nodeColors.erase(name);
}
else {
int r, g, b, a, t, f;
std::string flags;
ColorEntry color;
iline >> r;
iline >> g;
iline >> b;
if (iline.fail()) {
std::cerr << filename << ":" << linenr << ": bad line in colors file (" << line << ")" << std::endl;
return;
}
a = 0xff;
iline >> std::ws;
if (iline.good() && isdigit(iline.peek()))
iline >> a >> std::ws;
t = 0;
if (iline.good() && isdigit(iline.peek()))
iline >> t >> std::ws;
if (iline.good() && !isdigit(iline.peek()))
iline >> flags >> std::ws;
f = 0;
if (!iline.fail() && flags != "") {
for(size_t i = 0; i < flags.length(); i++) {
if (flags[i] == ',')
flags[i]= ' ';
}
istringstream iflags(flags);
std::string flag;
iflags >> flag;
while (!iflags.fail()) {
if (flag == "ignore")
f |= ColorEntry::FlagIgnore;
else if (flag == "air")
f |= ColorEntry::FlagAir;
iflags >> flag;
}
}
color = ColorEntry(r,g,b,a,t,f);
if ((m_drawAlpha && a == 0xff) || (!m_drawAlpha && a != 0xff)) {
// If drawing alpha, and the colors file contains both
// an opaque entry and a non-opaque entry for a name, prefer
// the non-opaque entry
// If not drawing alpha, and the colors file contains both
// an opaque entry and a non-opaque entry for a name, prefer
// the opaque entry
// Otherwise, any later entry overrides any previous entry
NodeColorMap::iterator it = m_nodeColors.find(name);
if (it != m_nodeColors.end()) {
if (m_drawAlpha && (a == 0xff && it->second.a != 0xff)) {
// drawing alpha: don't use opaque color to override
// non-opaque color
return;
}
if (!m_drawAlpha && (a != 0xff && it->second.a == 0xff)) {
// not drawing alpha: don't use non-opaque color to
// override opaque color
return;
}
}
}
m_nodeColors[name] = color;
}
}
void TileGenerator::parseHeightMapColorsLine(const std::string &line, std::string name, istringstream &iline, int linenr, const std::string &filename)
{
(void) name;
int height[2];
Color color[2];
iline.str(line); // Reset
for (int i = 0; i < 2; i++) {
iline >> std::ws;
char c = iline.peek();
iline >> height[i];
if (iline.fail()) {
std::string value;
iline.clear();
iline >> std::ws;
iline >> value >> std::ws;
if (!iline.fail()) {
if (value == "-oo" || (c == '-' && value=="oo"))
height[i] = INT_MIN;
else if (value == "oo" || value == "+oo")
height[i] = INT_MAX;
else {
iline.clear(ios::failbit); // Set to failed
break;
}
}
}
}
for (int i = 0; i < 2; i++) {
int r, g, b;
iline >> r;
iline >> g;
iline >> b;
color[i] = Color(r,g,b);
}
if (height[0] > height[1]) {
{
int tmp = height[0];
height[0] = height[1];
height[1] = tmp;
}
{
Color tmp = color[0];
color[0] = color[1];
color[1] = tmp;
}
}
iline >> std::ws;
if (iline.fail() || !iline.eof()) {
std::cerr << filename << ":" << linenr << ": bad line in heightmap colors file (" << line << ")" << std::endl;
return;
}
m_heightMapColors.push_back(HeightMapColor(height[0], color[0], height[1], color[1]));
}
void TileGenerator::parseHeightMapNodesLine(const std::string &line, std::string name, istringstream &iline, int linenr, const std::string &filename)
{
if (name == "-") {
iline >> std::ws >> name >> std::ws;
m_nodeColors.erase(name);
}
else {
m_nodeColors[name] = ColorEntry(0,0,0,255,1,0); // Dummy entry - but must not be transparent
}
// Don't care if not at eof (== really eol). We might be reading a colors.txt file...
if (iline.fail()) {
std::cerr << filename << ":" << linenr << ": bad line in heightmap nodes file (" << line << ")" << std::endl;
return;
}
}
std::string TileGenerator::getWorldDatabaseBackend(const std::string &input)
{
Settings world_mt(input + PATH_SEPARATOR + "world.mt");

View File

@ -61,12 +61,6 @@ private:
typedef std::unordered_map<int, std::string> NodeID2NameMap;
public:
struct HeightMapColor
{
HeightMapColor(int h0, Color c0, int h1, Color c1) : height{h0, h1}, color{c0, c1} {}
int height[2];
Color color[2];
};
typedef std::list<HeightMapColor> HeightMapColorList;
struct DrawObject {
void setCenter(const NodeCoord &c) { haveCenter = true; center = c; }
@ -197,19 +191,6 @@ private:
int borderLeft() const { return ((m_drawScale & DRAWSCALE_LEFT) ? SCALESIZE_VERT : 0) + (m_heightMap && (m_drawScale & DRAWHEIGHTSCALE_LEFT) ? HEIGHTSCALESIZE : 0); }
int borderRight() const { return ((m_drawScale & DRAWSCALE_RIGHT) ? SCALESIZE_VERT : 0) + (m_heightMap && (m_drawScale & DRAWHEIGHTSCALE_RIGHT) ? HEIGHTSCALESIZE : 0); }
void parseDataFile(const std::string &fileName, int depth, const char *type,
void (TileGenerator::*parseLine)(const std::string &line, std::string name,
std::istringstream &iline, int linenr, const std::string &filename));
void parseDataStream(std::istream &in, const std::string &filename, int depth, const char *type,
void (TileGenerator::*parseLine)(const std::string &line, std::string name,
std::istringstream &iline, int linenr, const std::string &filename));
void parseNodeColorsLine(const std::string &line, std::string name, std::istringstream &iline,
int linenr, const std::string &filename);
void parseHeightMapNodesLine(const std::string &line, std::string name, std::istringstream &iline,
int linenr, const std::string &filename);
void parseHeightMapColorsLine(const std::string &line, std::string name, std::istringstream &iline,
int linenr, const std::string &filename);
public:
static const BlockPos BlockPosLimitMin;
static const BlockPos BlockPosLimitMax;