diff --git a/CMakeLists.txt b/CMakeLists.txt index d9b4e51..554e694 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,7 @@ set(MAIN_HEADERS src/flashable.h src/font.h src/input.h + src/iniconfig.h src/plane.h src/scene.h src/sprite.h @@ -167,6 +168,7 @@ set(MAIN_SOURCE src/filesystem.cpp src/font.cpp src/input.cpp + src/iniconfig.cpp src/plane.cpp src/scene.cpp src/sprite.cpp diff --git a/mkxp.pro b/mkxp.pro index 0a4aa56..6f6540c 100644 --- a/mkxp.pro +++ b/mkxp.pro @@ -89,6 +89,7 @@ HEADERS += \ src/flashable.h \ src/font.h \ src/input.h \ + src/iniconfig.h \ src/plane.h \ src/scene.h \ src/sprite.h \ @@ -145,6 +146,7 @@ SOURCES += \ src/filesystem.cpp \ src/font.cpp \ src/input.cpp \ + src/iniconfig.cpp \ src/plane.cpp \ src/scene.cpp \ src/sprite.cpp \ diff --git a/src/config.cpp b/src/config.cpp index e98e781..f15a0bc 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -33,6 +33,7 @@ #include "debugwriter.h" #include "util.h" #include "sdl-util.h" +#include "iniconfig.h" #ifdef INI_ENCODING extern "C" { @@ -357,26 +358,32 @@ void Config::readGameINI() return; } - po::options_description podesc; - podesc.add_options() - ("Game.Title", po::value()) - ("Game.Scripts", po::value()) - ; - - po::variables_map vm; std::string iniFilename = execName + ".ini"; SDLRWStream iniFile(iniFilename.c_str(), "r"); if (iniFile) { - try + INIConfiguration ic; + if(ic.load(iniFile.stream())) { - po::store(po::parse_config_file(iniFile.stream(), podesc, true), vm); - po::notify(vm); + GUARD_ALL( game.title = ic.getStringProperty("Game", "Title"); ); + GUARD_ALL( game.scripts = ic.getStringProperty("Game", "Scripts"); ); + + strReplace(game.scripts, '\\', '/'); + + if (game.title.empty()) + { + Debug() << iniFilename + ": Could not find Game.Title property"; + } + + if (game.scripts.empty()) + { + Debug() << iniFilename + ": Could not find Game.Scripts property"; + } } - catch (po::error &error) + else { - Debug() << iniFilename + ":" << error.what(); + Debug() << iniFilename + ": Failed to parse ini file"; } } else @@ -384,11 +391,6 @@ void Config::readGameINI() Debug() << "FAILED to open" << iniFilename; } - GUARD_ALL( game.title = vm["Game.Title"].as(); ); - GUARD_ALL( game.scripts = vm["Game.Scripts"].as(); ); - - strReplace(game.scripts, '\\', '/'); - #ifdef INI_ENCODING /* Can add more later */ const char *languages[] = diff --git a/src/iniconfig.cpp b/src/iniconfig.cpp new file mode 100644 index 0000000..f3c0485 --- /dev/null +++ b/src/iniconfig.cpp @@ -0,0 +1,114 @@ +#include "iniconfig.h" + +#include + +std::string toLowerCase(const std::string& str) +{ + std::string lower = str; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + return lower; +} + +std::string trim(const std::string& str, const std::string& chars = "\t\n\v\f\r ") +{ + std::string trimmed = str; + trimmed.erase(trimmed.find_last_not_of(chars) + 1); + trimmed.erase(0, trimmed.find_first_not_of(chars)); + return trimmed; +} + +INIConfiguration::Section::Section (const std::string& sname) : m_Name (sname), m_PropertyMap() +{ +} + +bool INIConfiguration::Section::getStringProperty (const std::string& name, std::string& outPropStr) const +{ + try + { + outPropStr = m_PropertyMap.at(toLowerCase(name)).m_Value; + return true; + } + catch (std::out_of_range& oorexcept) + { + return false; + } +} + +bool INIConfiguration::load (std::istream& is) +{ + if (!is.good()) + { + return false; + } + + std::string currSectionName; + + std::string line; + std::getline (is, line); + + while (!is.eof() && !is.bad()) + { + if (line[0] == '[') + { + currSectionName = line.substr (1, line.find_last_of (']') - 1); + } + else if (line[0] != '#' && line.length() > 2) + { + int crloc = line.length() - 1; + + if (crloc >= 0 && line[crloc] == '\r') //check for Windows-style newline + line.resize (crloc); //and correct + + size_t equalsPos = line.find_first_of ("="); + + if (equalsPos != std::string::npos) + { + std::string key = line.substr (0, equalsPos); + std::string val = line.substr (equalsPos + 1); + + addProperty (currSectionName, key , val); + } + } + + std::getline (is, line); + } + + if (is.bad()) + { + return false; + } + + return true; +} + + +std::string INIConfiguration::getStringProperty(const std::string& sname, const std::string& name, const std::string& def) const +{ + auto sectionIt = m_SectionMap.find(toLowerCase(sname)); + + if (sectionIt != m_SectionMap.end()) + { + std::string prop; + + if(sectionIt->second.getStringProperty(name, prop)) + { + return prop; + } + } + + return def; +} + +void INIConfiguration::addProperty (const std::string& sname, const std::string& name, const std::string& val) +{ + if (m_SectionMap.find (toLowerCase(sname)) == m_SectionMap.end()) + { + m_SectionMap.emplace (toLowerCase(sname), Section (sname)); + } + + Section::Property p; + p.m_Name = trim(name); + p.m_Value = trim(val); + + m_SectionMap.at (toLowerCase(sname)).m_PropertyMap[toLowerCase(p.m_Name)] = p; +} diff --git a/src/iniconfig.h b/src/iniconfig.h new file mode 100644 index 0000000..2907df5 --- /dev/null +++ b/src/iniconfig.h @@ -0,0 +1,46 @@ +#ifndef INICONFIG_H +#define INICONFIG_H + +#include +#include + +class INIConfiguration +{ + class Section + { + friend class INIConfiguration; + + struct Property + { + std::string m_Name; + std::string m_Value; + }; + + typedef std::map property_map; + public: + Section (const Section& s) = default; + Section (Section&& s) = default; + + bool getStringProperty (const std::string& name, std::string& outPropStr) const; + + private: + explicit Section (const std::string& name); + + std::string m_Name; + property_map m_PropertyMap; + }; + + typedef std::map section_map; +public: + bool load (std::istream& inStream); + + std::string getStringProperty(const std::string& sname, const std::string& name, const std::string& def = "") const; + +protected: + void addProperty (const std::string& sname, const std::string& name, const std::string& val); + +private: + section_map m_SectionMap; +}; + +#endif // INICONFIG_H