#include #include "game.h" class ConfigBuilder{ public: ConfigData collectedData; void parseFile(const string& fname); void readConfig(); void dumpInternalValues(); private: map internalValues; string& getString(const configKey& key); char getChar(const configKey& key); int getInt(const configKey& key); nsGraphics::RGBAcolor getColor(const configKey& key); void getList(const configKey& key, vector&); }; void ConfigBuilder::dumpInternalValues(){ for(const auto& ite : internalValues){ cerr << ite.first << " -> " << ite.second << endl; } } void trimSpaces(string& str){ str.erase(0, str.find_first_not_of(' ')); } /* * WARNING : This implementation of YAML is not meant to detect and report errors in a non YAML-compliant file */ void ConfigBuilder::parseFile(const string& fname) { ifstream file(fname); if(!file.is_open())throw runtime_error("Error while opening config.yml. Check file location ?"); vector keyParts; unsigned listIndex; while (!file.eof()) { string line; getline(file, line); auto match = line.find('#'); if (match != string::npos)line.erase(match); if (line.empty())continue; unsigned currentIndent = 0; while (line[currentIndent] == ' ')++currentIndent; if(line[currentIndent]=='-'){ string value = line.substr(currentIndent+1); trimSpaces(value); string fullKey; for (unsigned i = 0; i < currentIndent; ++i) { fullKey.append(keyParts[i]); fullKey.append("."); } // lists are just treated as sections with key 0,1,2... fullKey.append(to_string((listIndex))); ++listIndex; internalValues[fullKey] = value; }else{ match = line.find(':'); if (match == string::npos)throw runtime_error("Invalid line : " + line); string key = line.substr(0, match); string value = line.substr(match + 1); trimSpaces(key); trimSpaces(value); if (value.empty()) { keyParts.resize(currentIndent); keyParts.push_back(key); listIndex = 0; } else { string fullKey; for (unsigned i = 0; i < currentIndent; ++i) { fullKey.append(keyParts[i]); fullKey.append("."); } fullKey.append(key); internalValues[fullKey] = value; } } } file.close(); } void ConfigBuilder::readConfig() { vector tmp; getList("grid", tmp); // we are essentially going to translate a line-oriented config to a column-oriented grid unsigned maxSize = 0; for(string& s : tmp){ if(s.size()>maxSize)maxSize = s.size(); } collectedData.grid.resize(maxSize); for(string& s : tmp){ unsigned i=0; for(;i& toPopulate) { size_t i=0; string fullKey = key+".0"; if(!internalValues.contains(fullKey))throw runtime_error("Non-existent list key requested : "+key); do{ toPopulate.push_back(internalValues.at(fullKey)); ++i; fullKey = key+"."+to_string(i); }while(internalValues.contains(key+"."+to_string(i))); } nsGraphics::RGBAcolor ConfigBuilder::getColor(const configKey& key) { // switch do not work with strings, and I don't want to implement a constexpr hash function string colorStr = getString(key); if (colorStr == "black")return nsGraphics::KBlack; else if (colorStr == "white")return nsGraphics::KWhite; else if (colorStr == "red")return nsGraphics::KRed; else if (colorStr == "lime")return nsGraphics::KLime; else if (colorStr == "blue")return nsGraphics::KBlue; else if (colorStr == "yellow")return nsGraphics::KYellow; else if (colorStr == "cyan")return nsGraphics::KCyan; else if (colorStr == "magenta")return nsGraphics::KMagenta; else if (colorStr == "silver")return nsGraphics::KSilver; else if (colorStr == "gray")return nsGraphics::KGray; else if (colorStr == "maroon")return nsGraphics::KMaroon; else if (colorStr == "olive")return nsGraphics::KOlive; else if (colorStr == "green")return nsGraphics::KGreen; else if (colorStr == "purple")return nsGraphics::KPurple; else if (colorStr == "teal")return nsGraphics::KTeal; else if (colorStr == "navy")return nsGraphics::KNavy; else throw runtime_error("Invalid color string : "+colorStr); } /** * * @return false if there was an error */ bool Game::reloadConfig() { map strValues; ConfigBuilder builder; bool parsed = false; try{ builder.parseFile("config.yml"); parsed = true; builder.readConfig(); }catch(runtime_error& e){ if(parsed)cerr << "An error occured while reading the configuration :" << endl; else cerr << "An error occured while parsing the configuration :" << endl; cerr << e.what() << endl; if(parsed){ cerr << "Parsed keys :" << endl; builder.dumpInternalValues(); } cerr << "(The old configuration was kept in memory)" << endl; return false; } confData = builder.collectedData; return true; }