Got a bit distracted from the zones thing then because I decided I wanted to have text-based configuration files for storing game options. Since I will probably always want this in games, I decided to build a static library to deal with nice and simple option files. It presents this interface:
#ifndef config_H#define config_H#include #include #include #include #pragma comment(lib,"config.lib")class configuration{private: bool error(const std::string &m,const std::string &p=""); enum type { eof,err,eq,text }; typedef std::map map_type; int skip(std::istream &is); type next(std::istream &is,std::string &token); bool match(bool get,std::istream &is,std::string &token,type t); bool process_line(const std::string &line); template<class T> bool string_to_T(const std::string &s,T &i); map_type map; std::string err_str,path_str; type last_type;public: configuration(); configuration(const std::string &path); bool fail() const; bool open(const std::string &path); std::string error_text() const; enum result { no_key,invalid_value,ok }; result operator()(const std::string &key,std::string &value); template<class T> result operator()(const std::string &key,T &value);};template<class T> bool configuration::string_to_T(const std::string &s,T &i){ std::istringstream is(s); is >> i; if(is.fail()) return false; std::string x; is >> x; return(x=="");}template<class T> configuration::result configuration::operator()(const std::string &key,T &value){ std::string s; if((*this)(key,s)!=ok) return no_key; return(string_to_T(s,value) ? ok : invalid_value);}#endif
It can then cope with .cfg files like:
; this is a commentkey=valueanother_key = "a string value" ; this is another commentnumber = 10 real_number=-23.0
and so on. The groovy little template and stringstream stuff means you can query the configuration for a value of any type that std::stringstream can deal with converting and it will tell you if:
a) the key doesn't exist
b) the value is invalid for the type
So you can do something like:
bool f(){ configuration con("x.cfg"); if(con.fail()) { std::cerr << con.error_text() << "\n"; return false; } std::string s; if(con("key",s)==configuration::ok) std::cout << s << std::endl; float f; configuration::result r=con("real_number",f); if(r==configuration::no_key) std::cout << "real_number not in the file\n"; if(r==configuration::invalid_value) std::cout << "value not a float\n"; if(r==configuration::ok) std::cout << f << "\n"; return true;}
Nice and simple but pretty robust. In case anyone cares, the dummy >> into a std::string at the end of the template conversion method is to prevent something like "123x" from returning a valid int (123) and leaving "x" in the stringstream.
Udo has now been updated to use the above so I can manually edit the resolution and turn the sound on and off with notepad. Woohoo.
Should probably stop spending evenings on details like this and get on with the game really.