/***************************************************************************** * Eliot * Copyright (C) 2007 Olivier Teulière * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ #include "config.h" #include #ifdef HAVE_LIBCONFIG # define LIBCONFIG_STATIC # include #endif #ifdef WIN32 # include # include #else # if defined(HAVE_SYS_STAT_H) && defined(HAVE_SYS_TYPES_H) # include # include # endif #endif #include "settings.h" #include "game_exception.h" using namespace libconfig; INIT_LOGGER(game, Settings); Settings *Settings::m_instance = NULL; Settings & Settings::Instance() { if (m_instance == NULL) { m_instance = new Settings; } return *m_instance; } void Settings::Destroy() { delete m_instance; m_instance = NULL; } namespace { string getConfigFileName() { string fileName; #ifdef WIN32 char szPath[MAX_PATH]; // Get the AppData directory if (SHGetFolderPath(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, szPath) == S_OK) { fileName = szPath + string("\\eliot"); #if 1 strncpy(szPath, fileName.c_str(), MAX_PATH); // Try to create the directory // We don't care about the results CreateDirectory(fileName.c_str(), NULL); #endif } if (fileName != "") fileName += "\\"; fileName += "eliot.cfg"; #else // Follow the XDG Base Directory Specification (from freedesktop.org) // XXX: In fact we don't follow it to the letter, because the location // of the config file could be different when reading and writing. // But in the case of Eliot it's not very important (we don't try to // merge config files)... const char *configDir = getenv("XDG_CONFIG_HOME"); if (configDir != NULL) fileName = configDir; else { // Fallback to the default value: $HOME/.config configDir = getenv("HOME"); if (configDir) fileName = configDir + string("/.config"); } fileName += "/eliot"; #if defined(HAVE_SYS_STAT_H) && defined(HAVE_SYS_TYPES_H) // Create the directory if it doesn't exist struct stat sb; if (fileName != "" && stat(fileName.c_str(), &sb) == -1) { // Try to create the directory with mode 0700 if (mkdir(fileName.c_str(), S_IRWXU)) { // The directory could not be created. Too bad... // Saving the configuration file will definitely fail. } } #endif fileName += "/eliot.cfg"; #endif return fileName; } #ifdef HAVE_LIBCONFIG template void copySetting(const Config &srcConf, Config &dstConf, const char *path) { if (srcConf.exists(path)) { T t; srcConf.lookupValue(path, t); dstConf.lookup(path) = t; } } #endif } Settings::Settings() { #ifdef HAVE_LIBCONFIG m_fileName = ::getConfigFileName(); m_conf = new Config; // ============== General options ============== // ============== Training mode options ============== Setting &training = m_conf->getRoot().add("training", Setting::TypeGroup); // Number of search results kept in a search training.add("search-limit", Setting::TypeInt) = 100; // ============== Duplicate mode options ============== Setting &dupli = m_conf->getRoot().add("duplicate", Setting::TypeGroup); // Minimum number of players in a duplicate game needed to apply a "solo" bonus // (16 is the ODS value) dupli.add("solo-players", Setting::TypeInt) = 16; // Number of points granted for a solo (10 is the ODS value) dupli.add("solo-value", Setting::TypeInt) = 10; // If true, Eliot complains when the player does something illegal // If false, the word is accepted (with a score of 0) and the player does // not get a second chance dupli.add("reject-invalid", Setting::TypeBoolean) = true; // ============== Freegame mode options ============== Setting &freegame = m_conf->getRoot().add("freegame", Setting::TypeGroup); // If true, Eliot complains when the player does something illegal // If false, the word is accepted (with a score of 0) and the player does // not get a second chance. // Trying to change letters or to pass the turn in an incorrect way will // be rejected in any case. freegame.add("reject-invalid", Setting::TypeBoolean) = true; // ============== Arbitration mode options ============== Setting &arbitration = m_conf->getRoot().add("arbitration", Setting::TypeGroup); // Number of search results kept in a search arbitration.add("search-limit", Setting::TypeInt) = 100; // Try to read the values from the configuration file try { // We cannot call readFile() on m_conf, as it removes the previous // settings. So we create a temporary config, and copy the settings // one by one... Config tmpConf; tmpConf.readFile(m_fileName.c_str()); copySetting(tmpConf, *m_conf, "training.search-limit"); copySetting(tmpConf, *m_conf, "duplicate.solo-players"); copySetting(tmpConf, *m_conf, "duplicate.solo-value"); copySetting(tmpConf, *m_conf, "duplicate.reject-invalid"); copySetting(tmpConf, *m_conf, "freegame.reject-invalid"); copySetting(tmpConf, *m_conf, "arbitration.search-limit"); } catch (...) { // Ignore the exception } #endif } Settings::~Settings() { #ifdef HAVE_LIBCONFIG delete m_conf; #endif } void Settings::save() const { #ifdef HAVE_LIBCONFIG try { m_conf->writeFile(m_fileName.c_str()); } catch (FileIOException &e) { throw GameException("The configuration file cannot be written (" + m_fileName + ")"); } #endif } void Settings::setBool(const string &iName, bool iValue) { setValue(iName, iValue); } bool Settings::getBool(const string &iName) const { #ifdef HAVE_LIBCONFIG try { return m_conf->lookup(iName); } catch (SettingNotFoundException &e) { throw GameException("No such option: " + iName); } #else // Dummy implementation return true; #endif } void Settings::setInt(const string &iName, int iValue) { setValue(iName, iValue); } int Settings::getInt(const string &iName) const { #ifdef HAVE_LIBCONFIG try { return m_conf->lookup(iName); } catch (SettingNotFoundException &e) { throw GameException("No such option: " + iName); } #else // Dummy implementation if (iName == "training.search-limit") return 100; else if (iName == "duplicate.solo-players") return 16; else if (iName == "duplicate.solo-value") return 10; else if (iName == "arbitration.search-limit") return 100; return 0; #endif } template void Settings::setValue(const string &iName, T iValue) { #ifdef HAVE_LIBCONFIG try { m_conf->lookup(iName) = iValue; } catch (SettingNotFoundException &e) { #ifdef DEBUG throw GameException("No such option: " + iName); #endif } #endif }