From b0eb0675d7d3dacd37d865bf447168b3dd4bd97e Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 14 Apr 2018 09:02:22 +0200 Subject: [PATCH] Refactor DataFileParser out of TileGenerator --- Minetestmapper/CMakeLists.txt | 2 + Minetestmapper/Color.h | 7 + Minetestmapper/DataFileParser.cpp | 244 ++++++++++++++++++++++++++++++ Minetestmapper/DataFileParser.h | 63 ++++++++ Minetestmapper/TileGenerator.cpp | 239 ++--------------------------- Minetestmapper/TileGenerator.h | 19 --- 6 files changed, 328 insertions(+), 246 deletions(-) create mode 100644 Minetestmapper/DataFileParser.cpp create mode 100644 Minetestmapper/DataFileParser.h diff --git a/Minetestmapper/CMakeLists.txt b/Minetestmapper/CMakeLists.txt index 32f9916..98df531 100644 --- a/Minetestmapper/CMakeLists.txt +++ b/Minetestmapper/CMakeLists.txt @@ -16,6 +16,8 @@ set(sources ZlibDecompressor.h Color.cpp Color.h + DataFileParser.cpp + DataFileParser.h Settings.cpp Settings.h BlockPos.cpp diff --git a/Minetestmapper/Color.h b/Minetestmapper/Color.h index 20a35e0..6c423a9 100644 --- a/Minetestmapper/Color.h +++ b/Minetestmapper/Color.h @@ -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 diff --git a/Minetestmapper/DataFileParser.cpp b/Minetestmapper/DataFileParser.cpp new file mode 100644 index 0000000..fffe25d --- /dev/null +++ b/Minetestmapper/DataFileParser.cpp @@ -0,0 +1,244 @@ +#include "DataFileParser.h" + +#include +#include +#include + +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; + } +} diff --git a/Minetestmapper/DataFileParser.h b/Minetestmapper/DataFileParser.h new file mode 100644 index 0000000..f0d5bee --- /dev/null +++ b/Minetestmapper/DataFileParser.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "Color.h" + +class DataFileParser +{ +public: + using NodeColorMap = std::unordered_map; + using HeightMapColorList = std::list; + + 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; +}; diff --git a/Minetestmapper/TileGenerator.cpp b/Minetestmapper/TileGenerator.cpp index 6179a12..4c99130 100644 --- a/Minetestmapper/TileGenerator.cpp +++ b/Minetestmapper/TileGenerator.cpp @@ -16,7 +16,10 @@ #include #include #include + + #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"); diff --git a/Minetestmapper/TileGenerator.h b/Minetestmapper/TileGenerator.h index d24bb28..3e09b32 100644 --- a/Minetestmapper/TileGenerator.h +++ b/Minetestmapper/TileGenerator.h @@ -61,12 +61,6 @@ private: typedef std::unordered_map 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 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;