# HG changeset patch # User Matti Hamalainen # Date 1573150533 -7200 # Node ID a329f0216491b97516c5338fee7e39cae17e8a1e # Parent b1e75c65016d52012dcc4744ba687ebe4e8f941d Implement PLY file format parsing and extremely simplistic scene setup file format. Not finished yet. diff -r b1e75c65016d -r a329f0216491 Makefile --- a/Makefile Tue Nov 05 11:46:31 2019 +0200 +++ b/Makefile Thu Nov 07 20:15:33 2019 +0200 @@ -3,13 +3,23 @@ SDLGL_CFLAGS ?= $(shell pkg-config --cflags sdl2 gl glu) SDLGL_LIBS ?= $(shell pkg-config --libs sdl2 gl glu) -CFLAGS ?= -O3 -W -Wall -DGL_GLEXT_PROTOTYPES +CFLAGS ?= -O3 -W -Wall -DGL_GLEXT_PROTOTYPES -std=c++11 LDFLAGS ?= +CFLAGS += $(SDLGL_CFLAGS) +LDFLAGS += $(SDLGL_LIBS) + TARGETS = glxdragon$(BINEXT) -glxdragon$(BINEXT): glxdragon.cpp - $(CXX) $(CFLAGS) $(SDLGL_CFLAGS) -o $@ $< $(LDFLAGS) $(SDLGL_LIBS) + +%.o: %.cpp %.h + $(CXX) $(CFLAGS) -c -o $@ $< + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + +glxdragon$(BINEXT): glxdragon.o + $(CXX) -o $@ $+ $(LDFLAGS) clean: - $(RM) $(TARGETS) + $(RM) $(TARGETS) *.o diff -r b1e75c65016d -r a329f0216491 dmmodel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmmodel.cpp Thu Nov 07 20:15:33 2019 +0200 @@ -0,0 +1,709 @@ +// +// +// +#include "dmmodel.h" + + +static bool dmError(DMTextFileInfo &info, const std::string &msg) +{ + printf("ERROR: %s on line #%d: %s\n", + msg.c_str(), info.nline, info.line.c_str()); + return false; +} + + +static bool dmSyntaxError(DMTextFileInfo &info, const std::string &msg) +{ + printf("ERROR: Syntax error on line #%d: %s\n", + info.nline, msg.c_str()); + return false; +} + + +static bool dmReadLine(DMTextFileInfo &info) +{ + if (!std::getline(info.file, info.line)) + return false; + + info.nline++; + + info.line = dmStrTrim(info.line); + + return true; +} + + +static DMPLYPropType dmPLYParsePropType(const std::string &name) +{ + if (name == "list") + return PLY_TYPE_LIST; + else + if (name == "float" || name == "float32") + return PLY_TYPE_FLOAT; + else + if (name == "double" || name == "float64") + return PLY_TYPE_DOUBLE; + else + if (name == "uchar" || name == "uint8") + return PLY_TYPE_UINT8; + else + if (name == "int16") + return PLY_TYPE_INT16; + else + if (name == "uint16") + return PLY_TYPE_UINT16; + else + if (name == "int" || name == "int32") + return PLY_TYPE_INT32; + else + if (name == "uint" || name == "uint32") + return PLY_TYPE_UINT32; + else + return PLY_TYPE_NONE; +} + + +static bool dmPLYParsePropertyValueASCII(DMPLYFileInfo &info, const DMPLYPropType ptype, DMPLYPropValue &pval, size_t &pos) +{ + size_t len = 0; + std::string val = info.line.substr(pos); + + switch (ptype) + { + case PLY_TYPE_INT8: + case PLY_TYPE_INT16: + case PLY_TYPE_INT32: + pval.v_int = std::stoi(val, &len); + break; + + case PLY_TYPE_UINT8: + case PLY_TYPE_UINT16: + case PLY_TYPE_UINT32: + pval.v_uint = std::stoi(val, &len); + break; + + case PLY_TYPE_FLOAT: + pval.v_float = std::stof(val, &len); + break; + + case PLY_TYPE_DOUBLE: + pval.v_double = std::stod(val, &len); + break; + + default: + return dmError(info, + "Internal error, unimplemented PLY property type"); + } + + pos += len; + + return true; +} + + +static bool dmPLYParsePropertyASCII(DMPLYFileInfo &info, DMPLYFileProperty &prop, size_t &pos) +{ + switch (prop.type) + { + case PLY_TYPE_LIST: + prop.list_values.clear(); + + if (!dmPLYParsePropertyValueASCII(info, prop.list_num_type, prop.list_num_value, pos)) + return false; + + for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++) + { + DMPLYPropValue pval; + if (!dmPLYParsePropertyValueASCII(info, prop.list_values_type, pval, pos)) + return false; + + prop.list_values.push_back(pval); + } + + if (prop.list_values.size() != prop.list_num_value.v_uint) + { + return dmSyntaxError(info, + "Number of property list '"+ prop.name +" values not equal to number specified"); + } + return true; + + default: + return dmPLYParsePropertyValueASCII(info, prop.type, prop.value, pos); + } +} + + +static bool dmPLYParseElementASCII(DMPLYFileInfo &info, DMPLYFileElement &element) +{ + size_t nprop = 0, pos = 0; + + // Read one line + if (!dmReadLine(info)) + return false; + + // Skip empty lines + if (info.line.empty()) + return true; + + // Parse the properties + for (auto const prop : element.properties) + { + if (!dmPLYParsePropertyASCII(info, *prop, pos)) + return false; + + nprop++; + } + + if (nprop != element.properties.size()) + { + return dmSyntaxError(info, + "Expected N properties, got different number"); + } + + return true; +} + + +bool dmPLYReadPropertyValueBIN(DMPLYFileInfo &info, const DMPLYPropType ptype, DMPLYPropValue &pval) +{ + uint8_t tmp[8]; + uint16_t tmpU16; + int16_t tmpS16; + uint32_t tmpU32; + int32_t tmpS32; + + switch (ptype) + { + case PLY_TYPE_INT8: + info.file.read(reinterpret_cast(&tmp), 1); + pval.v_int = (int8_t) tmp[0]; + break; + + case PLY_TYPE_UINT8: + info.file.read(reinterpret_cast(&tmp), 1); + pval.v_uint = (uint8_t) tmp[0]; + break; + + case PLY_TYPE_INT16: + info.file.read(reinterpret_cast(&tmp), 2); + tmpS16 = + (tmp[0] << 8) | + (tmp[1]); + pval.v_int = tmpS16; + break; + + case PLY_TYPE_UINT16: + info.file.read(reinterpret_cast(&tmp), 2); + tmpU16 = + (tmp[0] << 8) | + (tmp[1]); + pval.v_uint = tmpU16; + break; + + case PLY_TYPE_INT32: + info.file.read(reinterpret_cast(&tmp), 4); + tmpS32 = + (tmp[0] << 24) | + (tmp[1] << 16) | + (tmp[2] << 8) | + (tmp[3]); + + pval.v_int = tmpS32; + break; + + case PLY_TYPE_UINT32: + info.file.read(reinterpret_cast(&tmp), 4); + tmpU32 = + (tmp[3] << 24) | + (tmp[2] << 16) | + (tmp[1] << 8) | + (tmp[0]); + + pval.v_uint = tmpU32; + break; + + case PLY_TYPE_FLOAT: + info.file.read(reinterpret_cast(&tmp), 4); + tmpU32 = + (tmp[0] << 24) | + (tmp[1] << 16) | + (tmp[2] << 8) | + (tmp[3]); + + pval.v_uint = tmpU32; + break; + + default: + return dmError(info, + "Internal error, unimplemented PLY property type"); + } + + return true; +} + + +bool dmPLYReadPropertyBIN(DMPLYFileInfo &info, DMPLYFileProperty &prop) +{ + switch (prop.type) + { + case PLY_TYPE_LIST: + prop.list_values.clear(); + + if (!dmPLYReadPropertyValueBIN(info, prop.list_num_type, prop.list_num_value)) + return false; + + for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++) + { + DMPLYPropValue pval; + if (!dmPLYReadPropertyValueBIN(info, prop.list_values_type, pval)) + return false; + + prop.list_values.push_back(pval); + } + + if (prop.list_values.size() != prop.list_num_value.v_uint) + { + return dmSyntaxError(info, + "Number of property list '"+ prop.name +" values not equal to number specified"); + } + return true; + + default: + return dmPLYReadPropertyValueBIN(info, prop.type, prop.value); + } +} + + +bool dmPLYReadElementBIN(DMPLYFileInfo &info, DMPLYFileElement &element) +{ + size_t nprop = 0; + + for (auto const prop : element.properties) + { + if (!dmPLYReadPropertyBIN(info, *prop)) + return false; + + nprop++; + } + + if (nprop != element.properties.size()) + { + return dmSyntaxError(info, + "Expected N properties, got different number"); + } + + return true; +} + + +bool DMModel::loadFromPLY(const std::string &filename) +{ + DMPLYFileInfo info; + + return loadFromPLY(filename, info); +} + + +bool DMModel::loadFromPLY(const std::string &filename, DMPLYFileInfo &info) +{ + info.filename = filename; + info.nline = info.state = 0; + info.file.open(info.filename.c_str(), std::fstream::in | std::fstream::binary); + + printf("INFO: Trying to read mesh from '%s'.\n", + info.filename.c_str()); + + if (!info.file.is_open()) + { + printf("ERROR: Unable to open file '%s'.\n", + info.filename.c_str()); + return false; + } + + // Parse the PLY header + while (info.state >= 0) + { + // Read one line + if (!dmReadLine(info)) + return false; + + // Skip empty lines + if (info.line.empty()) + continue; + + // Split key and value + std::vector tokens = dmStrSplit(info.line); + std::string &key = tokens[0]; + + if (info.state == 0 && key == "ply") + { + info.state = 1; + } + else + if (info.state > 0 && key == "end_header") + { + info.state = -1; + } + else + if (info.state == 1 && key == "format") + { + if (tokens.size() < 3) + { + return dmSyntaxError(info, + "Expected value for format"); + } + + info.format = PLY_FMT_UNKNOWN; + + if (tokens[1] == "ascii") + info.format = PLY_FMT_ASCII; + else + if (tokens[1] == "binary_little_endian") + info.format = PLY_FMT_BIN_LE; + else + if (tokens[1] == "binary_big_endian") + info.format = PLY_FMT_BIN_BE; + + if (info.format == PLY_FMT_UNKNOWN || + tokens[2] != "1.0") + { + printf("ERROR: Unknown or unsupported PLY file format '%s'.\n", + (tokens[1] +" "+ tokens[2]).c_str()); + return false; + } + + info.state = 2; + } + else + if ((info.state == 2 || info.state == 3) && key == "element") + { + if (tokens.size() < 3) + { + return dmSyntaxError(info, + "Expected a value for element key"); + } + + std::string &el_name = tokens[1], &el_value = tokens[2]; + + if (info.elem_map.count(el_name)) + { + return dmSyntaxError(info, + "Element '"+ el_name +"' has already been defined"); + } + + DMPLYFileElement &elem = info.elem_map[el_name]; + info.element = &elem; + info.elements.push_back(&elem); + + elem.name = el_name; + elem.value = std::stoi(el_value); + + info.state = 3; + } + else + if (info.state == 3 && key == "property") + { + if (tokens.size() < 3) + { + return dmSyntaxError(info, + "Expected value for property"); + } + + const std::string + &pr_name = tokens.back(), + &pr_typename = tokens.at(1); + + if (!info.element) + { + // Should not happen + return dmSyntaxError(info, + "No element defined for property '"+ pr_name +"'?"); + } + + // Check if this property has been already defined + if (info.element->prop_map.count(pr_name)) + { + return dmSyntaxError(info, + "Element '"+ info.element->name + + "' already has property '"+ pr_name +"' defined?"); + } + + // Parse property information + DMPLYPropType pr_type = dmPLYParsePropType(pr_typename); + if (pr_type == PLY_TYPE_NONE) + { + return dmSyntaxError(info, + "Invalid or unsupported property type '"+ pr_typename +"'"); + } + + DMPLYFileProperty &prop = info.element->prop_map[pr_name]; + info.element->properties.push_back(&prop); + + prop.name = pr_name; + prop.type = pr_type; + + if (pr_type == PLY_TYPE_LIST) + { + // List is a special case + if (tokens.size() < 5) + { + return dmSyntaxError(info, + "Expected more values for a list property (num_type, val_type, name)"); + } + + prop.list_num_type = dmPLYParsePropType(tokens.at(2)); + prop.list_values_type = dmPLYParsePropType(tokens.at(3)); + + if (prop.list_num_type == PLY_TYPE_NONE || + prop.list_values_type == PLY_TYPE_NONE) + { + return dmSyntaxError(info, + "Invalid or unsupported property type(s)"); + } + } + } + else + if (info.state > 0 // && (key == "comment" || key == "obj_info") + ) + { + // Ignore comments + // .. and unknown keys + } + else + { + return dmSyntaxError(info, + "Unexpected key '"+ key +"'"); + } + } + + // Check header data + DMPLYFileElement *elem; + DMPLYFileProperty *prop; + if (info.state != -1 || + (elem = info.checkElem(PLY_ELEM_FACE)) == 0 || + (prop = elem->checkProp(PLY_PROP_VERTEX_INDICES)) == 0 || + prop->type != PLY_TYPE_LIST || + (elem = info.checkElem(PLY_ELEM_VERTEX)) == 0 || + (prop = elem->checkProp("x")) == 0 || prop->type != PLY_TYPE_FLOAT || + (prop = elem->checkProp("y")) == 0 || prop->type != PLY_TYPE_FLOAT || + (prop = elem->checkProp("z")) == 0 || prop->type != PLY_TYPE_FLOAT + ) + { + printf("ERROR: PLY file did not contain expected information.\n"); + return false; + } + + nvertices = info.elem_map[PLY_ELEM_VERTEX].value; + nfaces = info.elem_map[PLY_ELEM_FACE].value; + + + if (nvertices < 3 || nfaces < 1) + { + printf("ERROR: Invalid nvertices (%d) and/or nfaces (%d).\n", + nvertices, nfaces); + return false; + } + + printf("INFO: Should have %d vertices, %d faces\n", + nvertices, nfaces); + + // Pre-allocate space + vertices.reserve(nvertices); + normals.reserve(nvertices); + faces.reserve(nfaces * 3); + + // Read the actual data (in order of the elements) + for (auto *element : info.elements) + for (int n = 0; n < element->value; n++) + { + switch (info.format) + { + case PLY_FMT_ASCII: + if (!dmPLYParseElementASCII(info, *element)) + return false; + break; + + default: + if (!dmPLYReadElementBIN(info, *element)) + return false; + break; + } + + // Check for specific elements + if (element->name == PLY_ELEM_FACE) + { + DMPLYFileProperty &prop = element->prop_map[PLY_PROP_VERTEX_INDICES]; + + if (prop.list_num_value.v_uint != 3) + { + return dmSyntaxError(info, + "Expected 3 vertices per face"); + } + + for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++) + { + faces.push_back(prop.list_values[n].v_uint); + } + } + else + if (element->name == PLY_ELEM_VERTEX) + { + DMVertex vert; + vert.x = element->prop_map["x"].value.v_float; + vert.y = element->prop_map["y"].value.v_float; + vert.z = element->prop_map["z"].value.v_float; + + vertices.push_back(vert); + + if (element->checkProp("nx") && + element->checkProp("ny") && + element->checkProp("nz")) + { + DMVertex normal; + normal.x = element->prop_map["nx"].value.v_float; + normal.y = element->prop_map["ny"].value.v_float; + normal.z = element->prop_map["nz"].value.v_float; + + normals.push_back(normal); + } + } + } + + printf("INFO: Found %ld vertices, %ld normals, %ld faces\n", + vertices.size(), + normals.size(), + faces.size() / 3); + + return true; +} + + +bool DMSimpleScene::loadInfo(const std::string &filename) +{ + DMTextFileInfo info; + + info.filename = filename; + info.nline = info.state = 0; + info.file.open(info.filename.c_str(), std::fstream::in | std::fstream::binary); + + printf("INFO: Trying to read scene data from '%s'.\n", + info.filename.c_str()); + + if (!info.file.is_open()) + { + printf("ERROR: Unable to open file '%s'.\n", + info.filename.c_str()); + return false; + } + + while (info.state >= 0) + { + // Read one line + if (!dmReadLine(info)) + { + info.state = -1; + break; + } + + // Skip empty lines and comments + if (info.line.empty() || + info.line[0] == '#' || + info.line[0] == ';') + continue; + + // Split key and values + std::vector tokens = dmStrSplit(info.line); + std::string &key = tokens[0]; + + if (key == "color") + { + DMColor color; + color.alpha = 0xff; + + if (tokens.size() == 2) + { + color.r = color.g = color.b = std::stoi(tokens[1], 0, 0); + } + else + if (tokens.size() == 4 || tokens.size() == 5) + { + color.r = std::stoi(tokens[1], 0, 0); + color.g = std::stoi(tokens[2], 0, 0); + color.b = std::stoi(tokens[3], 0, 0); + + if (tokens.size() == 5) + color.alpha = std::stoi(tokens[4], 0, 0); + } + else + { + return dmSyntaxError(info, + "Expected color values or []"); + } + + model.color = color; + } + else + if (key == "translate" || key == "rotate" || key == "scale" || + key == "camera_pos" || key == "camera_at") + { + DMVertex vec; + + if (tokens.size() == 2) + { + vec.x = vec.y = vec.z = std::stof(tokens[1]); + } + else + if (tokens.size() == 4) + { + vec.x = std::stof(tokens[1]); + vec.y = std::stof(tokens[2]); + vec.z = std::stof(tokens[3]); + } + else + { + return dmSyntaxError(info, + "Expected vector value for '"+ key +"'"); + } + + if (key == "translate") + model.translate = vec; + else + if (key == "rotate") + model.rotate = vec; + else + if (key == "scale") + model.scale = vec; + else + if (key == "camera_pos") + camera.pos = vec; + else + if (key == "camera_at") + camera.lookAt = vec; + } + else + if (key == "light") + { + if (tokens.size() == 4) + { + DMVertex pos; + pos.x = std::stof(tokens[1]); + pos.y = std::stof(tokens[2]); + pos.z = std::stof(tokens[3]); + } + else + { + return dmSyntaxError(info, + "Expected light definition as "); + } + } + else + { + return dmSyntaxError(info, + "Unexpected key '"+ key +"'"); + } + } + + model.printInfo(); + + return true; +} diff -r b1e75c65016d -r a329f0216491 dmmodel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmmodel.h Thu Nov 07 20:15:33 2019 +0200 @@ -0,0 +1,187 @@ +// +// +// +#ifndef DMMODEL_H +#define DMMODEL_H 1 + +#include "dmutil.h" +#include +#include +#include + + +#define PLY_PROP_VERTEX_INDICES "vertex_indices" +#define PLY_ELEM_FACE "face" +#define PLY_ELEM_VERTEX "vertex" + + +enum DMPLYFormat +{ + PLY_FMT_UNKNOWN, + PLY_FMT_ASCII, + PLY_FMT_BIN_LE, + PLY_FMT_BIN_BE +}; + + +enum DMPLYPropType +{ + PLY_TYPE_NONE, + PLY_TYPE_LIST, + + PLY_TYPE_UINT8, + PLY_TYPE_INT8, + PLY_TYPE_INT16, + PLY_TYPE_UINT16, + PLY_TYPE_INT32, + PLY_TYPE_UINT32, + PLY_TYPE_FLOAT, + PLY_TYPE_DOUBLE +}; + + +/* Structures + */ +union DMPLYPropValue +{ + double v_double; + float v_float; + unsigned int v_uint; + int v_int; +}; + + +struct DMPLYFileProperty +{ + std::string name; + DMPLYPropType + type, + list_num_type, + list_values_type; + + DMPLYPropValue value, list_num_value; + std::vector list_values; +}; + + +struct DMPLYFileElement +{ + int value; + std::string name; + + std::unordered_map prop_map; + std::vector properties; + + DMPLYFileProperty *checkProp(const std::string &prop) + { + if (prop_map.count(prop)) + return &prop_map[prop]; + else + return 0; + } +}; + + +struct DMTextFileInfo +{ + int nline, state; + std::string filename; + std::string line; + std::ifstream file; +}; + + +struct DMPLYFileInfo : DMTextFileInfo +{ + DMPLYFormat format; + + std::unordered_map elem_map; + std::vector elements; + DMPLYFileElement *element; + + DMPLYFileInfo() + { + element = 0; + format = PLY_FMT_UNKNOWN; + } + + DMPLYFileElement *checkElem(const std::string &elem) + { + if (elem_map.count(elem)) + return &elem_map[elem]; + else + return 0; + } +}; + + +struct DMColor +{ + int r, g, b, alpha; +}; + + +struct DMVertex +{ + float x, y, z; +}; + + +struct DMModel +{ + int nvertices, nfaces; + std::vector vertices, normals; + std::vector faces; + + DMColor color; + DMVertex translate, scale, rotate; + + unsigned int id_prog, id_fs, id_vs; + + bool loadFromPLY(const std::string &filename); + bool loadFromPLY(const std::string &filename, DMPLYFileInfo &info); + + void printInfo() + { + printf( + "MODEL: scale <%1.5f, %1.5f, %1.5f>\n" + "MODEL: translate <%1.5f, %1.5f, %1.5f>\n" + "MODEL: rotate <%1.5f, %1.5f, %1.5f>\n" + , + scale.x, scale.y, scale.z, + translate.x, translate.y, translate.z, + rotate.x, rotate.y, rotate.z); + } + + DMModel() + { + translate.x = translate.y = translate.z = 0; + rotate.x = rotate.y = rotate.z = 0; + scale.x = scale.y = scale.z = 1; + } +}; + + +struct DMLight +{ + DMVertex pos; + float ambient[4], diffuse[4], specular[4]; +}; + + +struct DMCamera +{ + DMVertex pos, lookAt; +}; + + +struct DMSimpleScene +{ + DMModel model; + DMCamera camera; + std::vector lights; + + bool loadInfo(const std::string &filename); +}; + +#endif diff -r b1e75c65016d -r a329f0216491 dmutil.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmutil.cpp Thu Nov 07 20:15:33 2019 +0200 @@ -0,0 +1,109 @@ +// +// +// +#include "dmutil.h" +#include + + +std::string dmStrLTrim(const std::string& str, const std::string& delim) +{ + return str.substr(str.find_first_not_of(delim)); +} + + +std::string dmStrRTrim(const std::string& str, const std::string& delim) +{ + return str.substr(0, str.find_last_not_of(delim)); +} + + +std::string dmStrTrim(const std::string& str, const std::string& delim) +{ + if (str.empty()) + return str; + + size_t start = str.find_first_not_of(delim); + return str.substr(start, str.find_last_not_of(delim) - start + 1); +} + + +std::vector dmStrSplit(const std::string& str, const std::string& delim) +{ + std::vector result; + size_t oldpos = 0, newpos; + + do + { + newpos = str.find_first_of(delim, oldpos); + std::string tmp = dmStrTrim(str.substr(oldpos, newpos - oldpos)); + + if (!tmp.empty()) + result.push_back(tmp); + + oldpos = newpos + 1; + } while (newpos != std::string::npos); + + return result; +} + + +std::string dmStrJoin(const std::vector &list, const std::string &delim) +{ + switch (list.size()) + { + case 0: + return ""; + + case 1: + return list[0]; + + default: + std::string result; + bool first = true; + for (const auto &elem : list) + { + if (!first) + result += delim; + else + first = false; + result += elem; + } + return result; + } +} + + +bool dmReadText(const std::string &filename, std::string &buf, const int maxSize) +{ + std::ifstream in(filename.c_str(), std::fstream::in); + + if (!in.is_open()) + { + printf("ERROR: Unable to open file '%s'.\n", + filename.c_str()); + return false; + } + + in.seekg(0, std::ios::end); + + if (in.tellg() > maxSize) + { + printf("ERROR: File '%s' is too large.\n", + filename.c_str()); + return false; + } + + buf.reserve(in.tellg()); + in.seekg(0, std::ios::beg); + + buf.assign((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + + return true; +} + + +bool dmFileExists(const std::string &filename, std::ios_base::openmode mode) +{ + std::ifstream infile(filename.c_str(), mode); + return infile.good(); +} diff -r b1e75c65016d -r a329f0216491 dmutil.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmutil.h Thu Nov 07 20:15:33 2019 +0200 @@ -0,0 +1,27 @@ +// +// +// +#ifndef DMUTIL_H +#define DMUTIL_H 1 + +#include +#include +#include +#include + + +#define SET_WHITESPACE "\t\n\v\f\r " + + +std::string dmStrLTrim(const std::string& str, const std::string& delim = SET_WHITESPACE); +std::string dmStrRTrim(const std::string& str, const std::string& delim = SET_WHITESPACE); +std::string dmStrTrim(const std::string& str, const std::string& delim = SET_WHITESPACE); + +std::vector dmStrSplit(const std::string& str, const std::string& delim = SET_WHITESPACE); +std::string dmStrJoin(const std::vector &list, const std::string &delim); + +bool dmReadText(const std::string &filename, std::string &buf, const int maxSize); +bool dmFileExists(const std::string &filename, std::ios_base::openmode mode = std::ios_base::in); + + +#endif