mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 20:26:48 +00:00 
			
		
		
		
	Rewrite game settings manager
Removes the abhorrent dependency on Ogre for this code and improves the error handling.
This commit is contained in:
		
							parent
							
								
									6ea59c93ab
								
							
						
					
					
						commit
						27dc49a135
					
				
					 7 changed files with 139 additions and 121 deletions
				
			
		|  | @ -292,14 +292,10 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) | |||
|     else | ||||
|         throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed."); | ||||
| 
 | ||||
|     // load user settings if they exist, otherwise just load the default settings as user settings
 | ||||
|     // load user settings if they exist
 | ||||
|     const std::string settingspath = mCfgMgr.getUserConfigPath().string() + "/settings.cfg"; | ||||
|     if (boost::filesystem::exists(settingspath)) | ||||
|         settings.loadUser(settingspath); | ||||
|     else if (boost::filesystem::exists(localdefault)) | ||||
|         settings.loadUser(localdefault); | ||||
|     else if (boost::filesystem::exists(globaldefault)) | ||||
|         settings.loadUser(globaldefault); | ||||
| 
 | ||||
|     mFpsLevel = settings.getInt("fps", "HUD"); | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ | |||
| #define GAME_MWBASE_INPUTMANAGER_H | ||||
| 
 | ||||
| #include <string> | ||||
| #include <set> | ||||
| #include <vector> | ||||
| 
 | ||||
| namespace MWBase | ||||
| { | ||||
|  | @ -27,7 +29,7 @@ namespace MWBase | |||
| 
 | ||||
|             virtual void changeInputMode(bool guiMode) = 0; | ||||
| 
 | ||||
|             virtual void processChangedSettings(const std::vector< std::pair<std::string, std::string> >& changed) = 0; | ||||
|             virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed) = 0; | ||||
| 
 | ||||
|             virtual void setDragDrop(bool dragDrop) = 0; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| #define GAME_MWBASE_SOUNDMANAGER_H | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include <set> | ||||
| #include <boost/shared_ptr.hpp> | ||||
| 
 | ||||
| #include "../mwworld/ptr.hpp" | ||||
|  | @ -72,7 +72,7 @@ namespace MWBase | |||
| 
 | ||||
|             virtual ~SoundManager() {} | ||||
| 
 | ||||
|             virtual void processChangedSettings(const std::vector< std::pair<std::string, std::string> >& settings) = 0; | ||||
|             virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& settings) = 0; | ||||
| 
 | ||||
|             virtual void stopMusic() = 0; | ||||
|             ///< Stops music if it's playing
 | ||||
|  |  | |||
|  | @ -271,7 +271,7 @@ namespace MWBase | |||
|              */ | ||||
|             virtual std::string getGameSettingString(const std::string &id, const std::string &default_) = 0; | ||||
| 
 | ||||
|             virtual void processChangedSettings(const std::vector< std::pair<std::string, std::string> >& changed) = 0; | ||||
|             virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed) = 0; | ||||
| 
 | ||||
|             virtual void windowResized(int x, int y) = 0; | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| 
 | ||||
| #include <vector> | ||||
| #include <map> | ||||
| #include <set> | ||||
| 
 | ||||
| #include <components/esm/cellid.hpp> | ||||
| 
 | ||||
|  | @ -386,7 +387,7 @@ namespace MWBase | |||
|             virtual bool canPlaceObject (float cursorX, float cursorY) = 0; | ||||
|             ///< @return true if it is possible to place on object at specified cursor location
 | ||||
| 
 | ||||
|             virtual void processChangedSettings (const std::vector< std::pair<std::string, std::string> >& settings) = 0; | ||||
|             virtual void processChangedSettings (const std::set< std::pair<std::string, std::string> >& settings) = 0; | ||||
| 
 | ||||
|             virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; | ||||
|             virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const = 0; | ||||
|  |  | |||
|  | @ -1,95 +1,144 @@ | |||
| #include "settings.hpp" | ||||
| 
 | ||||
| #include <stdexcept> | ||||
| #include <boost/filesystem/path.hpp> | ||||
| #include <boost/filesystem/fstream.hpp> | ||||
| 
 | ||||
| #include <OgreResourceGroupManager.h> | ||||
| #include <OgreStringConverter.h> | ||||
| #include <OgreDataStream.h> | ||||
| 
 | ||||
| #include <components/files/constrainedfiledatastream.hpp> | ||||
| #include <boost/filesystem/fstream.hpp> | ||||
| #include <boost/filesystem/path.hpp> | ||||
| #include <boost/algorithm/string.hpp> | ||||
| 
 | ||||
| using namespace Settings; | ||||
| namespace Settings | ||||
| { | ||||
| 
 | ||||
| namespace bfs = boost::filesystem; | ||||
| 
 | ||||
| Ogre::ConfigFile Manager::mFile = Ogre::ConfigFile(); | ||||
| Ogre::ConfigFile Manager::mDefaultFile = Ogre::ConfigFile(); | ||||
| CategorySettingValueMap Manager::mDefaultSettings = CategorySettingValueMap(); | ||||
| CategorySettingValueMap Manager::mUserSettings = CategorySettingValueMap(); | ||||
| CategorySettingVector Manager::mChangedSettings = CategorySettingVector(); | ||||
| CategorySettingValueMap Manager::mNewSettings = CategorySettingValueMap(); | ||||
| 
 | ||||
| void Manager::loadUser (const std::string& file) | ||||
| 
 | ||||
| class SettingsFileParser | ||||
| { | ||||
|     Ogre::DataStreamPtr stream = openConstrainedFileDataStream(file.c_str()); | ||||
|     mFile.load(stream); | ||||
| } | ||||
| public: | ||||
|     SettingsFileParser() : mLine(0) {} | ||||
| 
 | ||||
| void Manager::loadDefault (const std::string& file) | ||||
| { | ||||
|     Ogre::DataStreamPtr stream = openConstrainedFileDataStream(file.c_str()); | ||||
|     mDefaultFile.load(stream); | ||||
| } | ||||
| 
 | ||||
| void Manager::saveUser(const std::string& file) | ||||
| { | ||||
|     bfs::ofstream fout((bfs::path(file))); | ||||
| 
 | ||||
|     Ogre::ConfigFile::SectionIterator seci = mFile.getSectionIterator(); | ||||
| 
 | ||||
|     while (seci.hasMoreElements()) | ||||
|     void loadSettingsFile (const std::string& file, CategorySettingValueMap& settings) | ||||
|     { | ||||
|         Ogre::String sectionName = seci.peekNextKey(); | ||||
| 
 | ||||
|         if (sectionName.length() > 0) | ||||
|             fout << '\n' << '[' << seci.peekNextKey() << ']' << '\n'; | ||||
| 
 | ||||
|         Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext(); | ||||
|         Ogre::ConfigFile::SettingsMultiMap::iterator i; | ||||
|         for (i = settings->begin(); i != settings->end(); ++i) | ||||
|         mFile = file; | ||||
|         boost::filesystem::ifstream stream; | ||||
|         stream.open(boost::filesystem::path(file)); | ||||
|         std::string currentCategory; | ||||
|         mLine = 0; | ||||
|         while (!stream.eof() && !stream.fail()) | ||||
|         { | ||||
|             fout << i->first.c_str() << " = " << i->second.c_str() << '\n'; | ||||
|         } | ||||
|             ++mLine; | ||||
|             std::string line; | ||||
|             std::getline( stream, line ); | ||||
| 
 | ||||
|         CategorySettingValueMap::iterator it = mNewSettings.begin(); | ||||
|         while (it != mNewSettings.end()) | ||||
|         { | ||||
|             if (it->first.first == sectionName) | ||||
|             size_t i = 0; | ||||
|             if (!skipWhiteSpace(i, line)) | ||||
|                 continue; | ||||
| 
 | ||||
|             if (line[i] == '#') // skip comment
 | ||||
|                 continue; | ||||
| 
 | ||||
|             if (line[i] == '[') | ||||
|             { | ||||
|                 fout << it->first.second << " = " << it->second << '\n'; | ||||
|                 mNewSettings.erase(it++); | ||||
|                 size_t end = line.find(']', i); | ||||
|                 if (end == std::string::npos) | ||||
|                     fail("unterminated category"); | ||||
| 
 | ||||
|                 currentCategory = line.substr(i+1, end - (i+1)); | ||||
|                 boost::algorithm::trim(currentCategory); | ||||
|                 i = end+1; | ||||
|             } | ||||
|             else | ||||
|                 ++it; | ||||
| 
 | ||||
|             if (!skipWhiteSpace(i, line)) | ||||
|                 continue; | ||||
| 
 | ||||
|             if (currentCategory.empty()) | ||||
|                 fail("empty category name"); | ||||
| 
 | ||||
|             size_t settingEnd = line.find('=', i); | ||||
|             if (settingEnd == std::string::npos) | ||||
|                 fail("unterminated setting name"); | ||||
| 
 | ||||
|             std::string setting = line.substr(i, (settingEnd-i)); | ||||
|             boost::algorithm::trim(setting); | ||||
| 
 | ||||
|             size_t valueBegin = settingEnd+1; | ||||
|             std::string value = line.substr(valueBegin); | ||||
|             boost::algorithm::trim(value); | ||||
| 
 | ||||
|             if (settings.insert(std::make_pair(std::make_pair(currentCategory, setting), value)).second == false) | ||||
|                 fail(std::string("duplicate setting: [" + currentCategory + "] " + setting)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     std::string category = ""; | ||||
|     for (CategorySettingValueMap::iterator it = mNewSettings.begin(); | ||||
|             it != mNewSettings.end(); ++it) | ||||
| private: | ||||
|     /// Increment i until it longer points to a whitespace character
 | ||||
|     /// in the string or has reached the end of the string.
 | ||||
|     /// @return false if we have reached the end of the string
 | ||||
|     bool skipWhiteSpace(size_t& i, std::string& str) | ||||
|     { | ||||
|         if (category != it->first.first) | ||||
|         while (i < str.size() && std::isspace(str[i], std::locale::classic())) | ||||
|         { | ||||
|             category = it->first.first; | ||||
|             fout << '\n' << '[' << category << ']' << '\n'; | ||||
|             ++i; | ||||
|         } | ||||
|         fout << it->first.second << " = " << it->second << '\n'; | ||||
|         return i < str.size(); | ||||
|     } | ||||
| 
 | ||||
|     fout.close(); | ||||
|     void fail(const std::string& message) | ||||
|     { | ||||
|         std::stringstream error; | ||||
|         error << "Error on line " << mLine << " in " << mFile << ":\n" << message; | ||||
|         throw std::runtime_error(error.str()); | ||||
|     } | ||||
| 
 | ||||
|     std::string mFile; | ||||
|     int mLine; | ||||
| }; | ||||
| 
 | ||||
| void Manager::loadDefault(const std::string &file) | ||||
| { | ||||
|     SettingsFileParser parser; | ||||
|     parser.loadSettingsFile(file, mDefaultSettings); | ||||
| } | ||||
| 
 | ||||
| std::string Manager::getString (const std::string& setting, const std::string& category) | ||||
| void Manager::loadUser(const std::string &file) | ||||
| { | ||||
|     if (mNewSettings.find(std::make_pair(category, setting)) != mNewSettings.end()) | ||||
|         return mNewSettings[std::make_pair(category, setting)]; | ||||
|     SettingsFileParser parser; | ||||
|     parser.loadSettingsFile(file, mUserSettings); | ||||
| } | ||||
| 
 | ||||
|     std::string defaultval = mDefaultFile.getSetting(setting, category, "NOTFOUND"); | ||||
|     std::string val = mFile.getSetting(setting, category, defaultval); | ||||
| void Manager::saveUser(const std::string &file) | ||||
| { | ||||
|     boost::filesystem::ofstream stream; | ||||
|     stream.open(boost::filesystem::path(file)); | ||||
|     std::string currentCategory; | ||||
|     for (CategorySettingValueMap::iterator it = mUserSettings.begin(); it != mUserSettings.end(); ++it) | ||||
|     { | ||||
|         if (it->first.first != currentCategory) | ||||
|         { | ||||
|             currentCategory = it->first.first; | ||||
|             stream << "\n[" << currentCategory << "]\n"; | ||||
|         } | ||||
|         stream << it->first.second << " = " << it->second << "\n"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     if (val == "NOTFOUND") | ||||
|         throw std::runtime_error("Trying to retrieve a non-existing setting: " + setting + " Make sure the settings-default.cfg file was properly installed."); | ||||
|     return val; | ||||
| std::string Manager::getString(const std::string &setting, const std::string &category) | ||||
| { | ||||
|     CategorySettingValueMap::key_type key = std::make_pair(category, setting); | ||||
|     CategorySettingValueMap::iterator it = mUserSettings.find(key); | ||||
|     if (it != mUserSettings.end()) | ||||
|         return it->second; | ||||
| 
 | ||||
|     it = mDefaultSettings.find(key); | ||||
|     if (it != mDefaultSettings.end()) | ||||
|         return it->second; | ||||
| 
 | ||||
|     throw std::runtime_error(std::string("Trying to retrieve a non-existing setting: ") + setting | ||||
|                              + ".\nMake sure the settings-default.cfg file file was properly installed."); | ||||
| } | ||||
| 
 | ||||
| float Manager::getFloat (const std::string& setting, const std::string& category) | ||||
|  | @ -107,51 +156,20 @@ bool Manager::getBool (const std::string& setting, const std::string& category) | |||
|     return Ogre::StringConverter::parseBool( getString(setting, category) ); | ||||
| } | ||||
| 
 | ||||
| void Manager::setString (const std::string& setting, const std::string& category, const std::string& value) | ||||
| void Manager::setString(const std::string &setting, const std::string &category, const std::string &value) | ||||
| { | ||||
|     CategorySetting s = std::make_pair(category, setting); | ||||
|     CategorySettingValueMap::key_type key = std::make_pair(category, setting); | ||||
| 
 | ||||
|     bool found=false; | ||||
|     try | ||||
|     CategorySettingValueMap::iterator found = mUserSettings.find(key); | ||||
|     if (found != mUserSettings.end()) | ||||
|     { | ||||
|         Ogre::ConfigFile::SettingsIterator it = mFile.getSettingsIterator(category); | ||||
|         while (it.hasMoreElements()) | ||||
|         { | ||||
|             Ogre::ConfigFile::SettingsMultiMap::iterator i = it.current(); | ||||
| 
 | ||||
|             if ((*i).first == setting) | ||||
|             { | ||||
|                 if ((*i).second != value) | ||||
|                 { | ||||
|                     mChangedSettings.push_back(std::make_pair(category, setting)); | ||||
|                     (*i).second = value; | ||||
|                 } | ||||
|                 found = true; | ||||
|             } | ||||
| 
 | ||||
|             it.getNext(); | ||||
|         } | ||||
|         if (found->second == value) | ||||
|             return; | ||||
|     } | ||||
|     catch (Ogre::Exception&) | ||||
|     {} | ||||
| 
 | ||||
|     if (!found) | ||||
|     { | ||||
|         if (mNewSettings.find(s) != mNewSettings.end()) | ||||
|         { | ||||
|             if (mNewSettings[s] != value) | ||||
|             { | ||||
|                 mChangedSettings.push_back(std::make_pair(category, setting)); | ||||
|                 mNewSettings[s] = value; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (mDefaultFile.getSetting(setting, category) != value) | ||||
|                 mChangedSettings.push_back(std::make_pair(category, setting)); | ||||
|             mNewSettings[s] = value; | ||||
|         } | ||||
|     } | ||||
|     mUserSettings[key] = value; | ||||
| 
 | ||||
|     mChangedSettings.insert(key); | ||||
| } | ||||
| 
 | ||||
| void Manager::setInt (const std::string& setting, const std::string& category, const int value) | ||||
|  | @ -159,12 +177,12 @@ void Manager::setInt (const std::string& setting, const std::string& category, c | |||
|     setString(setting, category, Ogre::StringConverter::toString(value)); | ||||
| } | ||||
| 
 | ||||
| void Manager::setFloat (const std::string& setting, const std::string& category, const float value) | ||||
| void Manager::setFloat (const std::string &setting, const std::string &category, const float value) | ||||
| { | ||||
|     setString(setting, category, Ogre::StringConverter::toString(value)); | ||||
| } | ||||
| 
 | ||||
| void Manager::setBool (const std::string& setting, const std::string& category, const bool value) | ||||
| void Manager::setBool(const std::string &setting, const std::string &category, const bool value) | ||||
| { | ||||
|     setString(setting, category, Ogre::StringConverter::toString(value)); | ||||
| } | ||||
|  | @ -175,3 +193,5 @@ const CategorySettingVector Manager::apply() | |||
|     mChangedSettings.clear(); | ||||
|     return vec; | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,12 +1,14 @@ | |||
| #ifndef COMPONENTS_SETTINGS_H | ||||
| #define COMPONENTS_SETTINGS_H | ||||
| 
 | ||||
| #include <OgreConfigFile.h> | ||||
| #include <set> | ||||
| #include <map> | ||||
| #include <string> | ||||
| 
 | ||||
| namespace Settings | ||||
| { | ||||
|     typedef std::pair < std::string, std::string > CategorySetting;  | ||||
|     typedef std::vector< std::pair<std::string, std::string> > CategorySettingVector; | ||||
|     typedef std::set< std::pair<std::string, std::string> > CategorySettingVector; | ||||
|     typedef std::map < CategorySetting, std::string > CategorySettingValueMap; | ||||
| 
 | ||||
|     ///
 | ||||
|  | @ -15,15 +17,12 @@ namespace Settings | |||
|     class Manager | ||||
|     { | ||||
|     public: | ||||
|         static Ogre::ConfigFile mFile; | ||||
|         static Ogre::ConfigFile mDefaultFile; | ||||
|         static CategorySettingValueMap mDefaultSettings; | ||||
|         static CategorySettingValueMap mUserSettings; | ||||
| 
 | ||||
|         static CategorySettingVector mChangedSettings; | ||||
|         ///< tracks all the settings that were changed since the last apply() call
 | ||||
| 
 | ||||
|         static CategorySettingValueMap mNewSettings; | ||||
|         ///< tracks all the settings that are in the default file, but not in user file yet
 | ||||
| 
 | ||||
|         void loadDefault (const std::string& file); | ||||
|         ///< load file as the default settings (can be overridden by user settings)
 | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue