Config: Add case-insensitive ini file parser

This commit is contained in:
Luis Caceres 2018-09-25 18:58:10 +01:00 committed by Jonas Kulla
parent bab22d87be
commit 43cb318862
5 changed files with 183 additions and 17 deletions

View File

@ -110,6 +110,7 @@ set(MAIN_HEADERS
src/flashable.h src/flashable.h
src/font.h src/font.h
src/input.h src/input.h
src/iniconfig.h
src/plane.h src/plane.h
src/scene.h src/scene.h
src/sprite.h src/sprite.h
@ -167,6 +168,7 @@ set(MAIN_SOURCE
src/filesystem.cpp src/filesystem.cpp
src/font.cpp src/font.cpp
src/input.cpp src/input.cpp
src/iniconfig.cpp
src/plane.cpp src/plane.cpp
src/scene.cpp src/scene.cpp
src/sprite.cpp src/sprite.cpp

View File

@ -89,6 +89,7 @@ HEADERS += \
src/flashable.h \ src/flashable.h \
src/font.h \ src/font.h \
src/input.h \ src/input.h \
src/iniconfig.h \
src/plane.h \ src/plane.h \
src/scene.h \ src/scene.h \
src/sprite.h \ src/sprite.h \
@ -145,6 +146,7 @@ SOURCES += \
src/filesystem.cpp \ src/filesystem.cpp \
src/font.cpp \ src/font.cpp \
src/input.cpp \ src/input.cpp \
src/iniconfig.cpp \
src/plane.cpp \ src/plane.cpp \
src/scene.cpp \ src/scene.cpp \
src/sprite.cpp \ src/sprite.cpp \

View File

@ -33,6 +33,7 @@
#include "debugwriter.h" #include "debugwriter.h"
#include "util.h" #include "util.h"
#include "sdl-util.h" #include "sdl-util.h"
#include "iniconfig.h"
#ifdef INI_ENCODING #ifdef INI_ENCODING
extern "C" { extern "C" {
@ -310,26 +311,32 @@ void Config::readGameINI()
return; return;
} }
po::options_description podesc;
podesc.add_options()
("Game.Title", po::value<std::string>())
("Game.Scripts", po::value<std::string>())
;
po::variables_map vm;
std::string iniFilename = execName + ".ini"; std::string iniFilename = execName + ".ini";
SDLRWStream iniFile(iniFilename.c_str(), "r"); SDLRWStream iniFile(iniFilename.c_str(), "r");
if (iniFile) if (iniFile)
{ {
try INIConfiguration ic;
if(ic.load(iniFile.stream()))
{ {
po::store(po::parse_config_file(iniFile.stream(), podesc, true), vm); GUARD_ALL( game.title = ic.getStringProperty("Game", "Title"); );
po::notify(vm); GUARD_ALL( game.scripts = ic.getStringProperty("Game", "Scripts"); );
strReplace(game.scripts, '\\', '/');
if (game.title.empty())
{
Debug() << iniFilename + ": Could not find Game.Title property";
} }
catch (po::error &error)
if (game.scripts.empty())
{ {
Debug() << iniFilename + ":" << error.what(); Debug() << iniFilename + ": Could not find Game.Scripts property";
}
}
else
{
Debug() << iniFilename + ": Failed to parse ini file";
} }
} }
else else
@ -337,11 +344,6 @@ void Config::readGameINI()
Debug() << "FAILED to open" << iniFilename; Debug() << "FAILED to open" << iniFilename;
} }
GUARD_ALL( game.title = vm["Game.Title"].as<std::string>(); );
GUARD_ALL( game.scripts = vm["Game.Scripts"].as<std::string>(); );
strReplace(game.scripts, '\\', '/');
#ifdef INI_ENCODING #ifdef INI_ENCODING
/* Can add more later */ /* Can add more later */
const char *languages[] = const char *languages[] =

114
src/iniconfig.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "iniconfig.h"
#include <algorithm>
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;
}

46
src/iniconfig.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef INICONFIG_H
#define INICONFIG_H
#include <iostream>
#include <map>
class INIConfiguration
{
class Section
{
friend class INIConfiguration;
struct Property
{
std::string m_Name;
std::string m_Value;
};
typedef std::map<std::string, Property> 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<std::string, Section> 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