| [1505] | 1 | /* | 
|---|
|  | 2 | *   ORXONOX - the hottest 3D action shooter ever to exist | 
|---|
|  | 3 | *                    > www.orxonox.net < | 
|---|
|  | 4 | * | 
|---|
|  | 5 | * | 
|---|
|  | 6 | *   License notice: | 
|---|
|  | 7 | * | 
|---|
|  | 8 | *   This program is free software; you can redistribute it and/or | 
|---|
|  | 9 | *   modify it under the terms of the GNU General Public License | 
|---|
|  | 10 | *   as published by the Free Software Foundation; either version 2 | 
|---|
|  | 11 | *   of the License, or (at your option) any later version. | 
|---|
|  | 12 | * | 
|---|
|  | 13 | *   This program is distributed in the hope that it will be useful, | 
|---|
|  | 14 | *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
|  | 15 | *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
|  | 16 | *   GNU General Public License for more details. | 
|---|
|  | 17 | * | 
|---|
|  | 18 | *   You should have received a copy of the GNU General Public License | 
|---|
|  | 19 | *   along with this program; if not, write to the Free Software | 
|---|
|  | 20 | *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | 
|---|
|  | 21 | * | 
|---|
|  | 22 | *   Author: | 
|---|
|  | 23 | *      Fabian 'x3n' Landau | 
|---|
|  | 24 | *   Co-authors: | 
|---|
|  | 25 | *      ... | 
|---|
|  | 26 | * | 
|---|
|  | 27 | */ | 
|---|
|  | 28 |  | 
|---|
|  | 29 | /** | 
|---|
| [2171] | 30 | @file | 
|---|
| [1505] | 31 | @brief Implementation of the ConfigValueContainer class. | 
|---|
|  | 32 | */ | 
|---|
|  | 33 |  | 
|---|
|  | 34 | #include "ConfigValueContainer.h" | 
|---|
|  | 35 |  | 
|---|
| [3196] | 36 | #include "util/Convert.h" | 
|---|
| [1505] | 37 | #include "util/SubString.h" | 
|---|
| [6536] | 38 | #include "ConfigFileManager.h" | 
|---|
| [1505] | 39 | #include "Language.h" | 
|---|
|  | 40 |  | 
|---|
|  | 41 | namespace orxonox | 
|---|
|  | 42 | { | 
|---|
| [1791] | 43 | const unsigned int MAX_VECTOR_INDEX  = 255; // to avoid up to 4*10^9 vector entries in the config file after accidentally using a wrong argument | 
|---|
| [1784] | 44 |  | 
|---|
| [1505] | 45 | /** | 
|---|
| [6536] | 46 | @brief Initializes the ConfigValueContainer with default values. | 
|---|
| [1505] | 47 | */ | 
|---|
| [6536] | 48 | void ConfigValueContainer::init(ConfigFileType::Value type, Identifier* identifier, const std::string& sectionname, const std::string& varname) | 
|---|
| [1505] | 49 | { | 
|---|
|  | 50 | this->type_ = type; | 
|---|
|  | 51 | this->identifier_ = identifier; | 
|---|
| [1887] | 52 | this->sectionname_ = sectionname; | 
|---|
| [1505] | 53 | this->varname_ = varname; | 
|---|
| [1747] | 54 | this->callback_ = 0; | 
|---|
|  | 55 | this->bContainerIsNew_ = true; | 
|---|
|  | 56 | this->bDoInitialCallback_ = false; | 
|---|
|  | 57 | this->bAddedDescription_ = false; | 
|---|
| [6536] | 58 |  | 
|---|
|  | 59 | // Register containers for general settings | 
|---|
|  | 60 | if (this->type_ == ConfigFileType::Settings) | 
|---|
|  | 61 | SettingsConfigFile::getInstance().addConfigValueContainer(this); | 
|---|
| [1747] | 62 | } | 
|---|
| [1505] | 63 |  | 
|---|
| [1747] | 64 | /** | 
|---|
|  | 65 | @brief Does some special initialization for single config-values. | 
|---|
|  | 66 | */ | 
|---|
|  | 67 | void ConfigValueContainer::initValue(const MultiType& defvalue) | 
|---|
|  | 68 | { | 
|---|
| [1505] | 69 | this->value_ = defvalue; | 
|---|
|  | 70 | this->bIsVector_ = false; | 
|---|
|  | 71 |  | 
|---|
| [1747] | 72 | this->defvalueString_ = this->value_.getString(); | 
|---|
| [1505] | 73 | this->update(); | 
|---|
|  | 74 | } | 
|---|
|  | 75 |  | 
|---|
|  | 76 | /** | 
|---|
| [1747] | 77 | @brief Does some special initialization for vector config-values. | 
|---|
| [1505] | 78 | */ | 
|---|
| [1747] | 79 | void ConfigValueContainer::initVector() | 
|---|
| [1505] | 80 | { | 
|---|
|  | 81 | this->bIsVector_ = true; | 
|---|
|  | 82 |  | 
|---|
| [1747] | 83 | for (unsigned int i = 0; i < this->valueVector_.size(); i++) | 
|---|
| [1505] | 84 | { | 
|---|
| [6536] | 85 | ConfigFileManager::getInstance().getConfigFile(this->type_)->getOrCreateValue(this->sectionname_, this->varname_, i, this->valueVector_[i], this->value_.isType(MT_Type::String)); | 
|---|
| [1747] | 86 | this->defvalueStringVector_.push_back(this->valueVector_[i]); | 
|---|
|  | 87 | } | 
|---|
| [1505] | 88 |  | 
|---|
| [1747] | 89 | this->update(); | 
|---|
|  | 90 | } | 
|---|
| [1505] | 91 |  | 
|---|
| [1747] | 92 | /** | 
|---|
|  | 93 | @brief Destructor: Deletes the callback object if necessary. | 
|---|
|  | 94 | */ | 
|---|
|  | 95 | ConfigValueContainer::~ConfigValueContainer() | 
|---|
|  | 96 | { | 
|---|
|  | 97 | if (this->callback_) | 
|---|
|  | 98 | delete this->callback_; | 
|---|
| [6536] | 99 |  | 
|---|
|  | 100 | // Unregister general settings containers | 
|---|
|  | 101 | if (this->type_ == ConfigFileType::Settings && SettingsConfigFile::exists()) | 
|---|
|  | 102 | SettingsConfigFile::getInstance().removeConfigValueContainer(this); | 
|---|
| [1505] | 103 | } | 
|---|
|  | 104 |  | 
|---|
|  | 105 | /** | 
|---|
|  | 106 | @brief Assigns a new value to the config-value of all objects and writes the change into the config-file. | 
|---|
|  | 107 | @param input The new value | 
|---|
|  | 108 | @return True if the new value was successfully assigned | 
|---|
|  | 109 | */ | 
|---|
| [1747] | 110 | bool ConfigValueContainer::set(const MultiType& input) | 
|---|
| [1505] | 111 | { | 
|---|
|  | 112 | if (this->bIsVector_) | 
|---|
|  | 113 | { | 
|---|
| [1747] | 114 | return this->callFunctionWithIndex(&ConfigValueContainer::set, input); | 
|---|
| [1505] | 115 | } | 
|---|
|  | 116 | else | 
|---|
|  | 117 | { | 
|---|
|  | 118 | if (this->tset(input)) | 
|---|
|  | 119 | { | 
|---|
| [6536] | 120 | ConfigFileManager::getInstance().getConfigFile(this->type_)->setValue(this->sectionname_, this->varname_, input, this->value_.isType(MT_Type::String)); | 
|---|
| [1505] | 121 | return true; | 
|---|
|  | 122 | } | 
|---|
|  | 123 | } | 
|---|
|  | 124 | return false; | 
|---|
|  | 125 | } | 
|---|
|  | 126 |  | 
|---|
|  | 127 | /** | 
|---|
|  | 128 | @brief Assigns a new value to the config-value of all objects and writes the change into the config-file. | 
|---|
|  | 129 | @param index The index in the vector | 
|---|
|  | 130 | @param input The new value | 
|---|
|  | 131 | @return True if the new value was successfully assigned | 
|---|
|  | 132 | */ | 
|---|
| [1747] | 133 | bool ConfigValueContainer::set(unsigned int index, const MultiType& input) | 
|---|
| [1505] | 134 | { | 
|---|
|  | 135 | if (this->bIsVector_) | 
|---|
|  | 136 | { | 
|---|
|  | 137 | if (this->tset(index, input)) | 
|---|
|  | 138 | { | 
|---|
| [6536] | 139 | ConfigFileManager::getInstance().getConfigFile(this->type_)->setValue(this->sectionname_, this->varname_, index, input, this->value_.isType(MT_Type::String)); | 
|---|
| [1505] | 140 | return true; | 
|---|
|  | 141 | } | 
|---|
|  | 142 | } | 
|---|
|  | 143 | else | 
|---|
|  | 144 | { | 
|---|
| [8858] | 145 | orxout(user_error, context::config) << "Config-value '" << this->varname_ << "' in " << this->sectionname_ << " is not a vector." << endl; | 
|---|
| [1505] | 146 | } | 
|---|
|  | 147 | return false; | 
|---|
|  | 148 | } | 
|---|
|  | 149 |  | 
|---|
|  | 150 | /** | 
|---|
|  | 151 | @brief Assigns a new value to the config-value of all objects, but doesn't change the config-file (t stands for temporary). | 
|---|
|  | 152 | @param input The new value. If bIsVector_ then write "index value" | 
|---|
|  | 153 | @return True if the new value was successfully assigned | 
|---|
|  | 154 | */ | 
|---|
| [1747] | 155 | bool ConfigValueContainer::tset(const MultiType& input) | 
|---|
| [1505] | 156 | { | 
|---|
|  | 157 | if (this->bIsVector_) | 
|---|
|  | 158 | { | 
|---|
| [1747] | 159 | return this->callFunctionWithIndex(&ConfigValueContainer::tset, input); | 
|---|
| [1505] | 160 | } | 
|---|
|  | 161 | else | 
|---|
|  | 162 | { | 
|---|
| [1747] | 163 | this->value_ = input; | 
|---|
| [1505] | 164 |  | 
|---|
| [1747] | 165 | if (this->identifier_) | 
|---|
|  | 166 | this->identifier_->updateConfigValues(); | 
|---|
|  | 167 |  | 
|---|
|  | 168 | return true; | 
|---|
| [1505] | 169 | } | 
|---|
|  | 170 | } | 
|---|
|  | 171 |  | 
|---|
|  | 172 | /** | 
|---|
|  | 173 | @brief Assigns a new value to the config-value of all objects, but doesn't change the config-file (t stands for temporary). | 
|---|
|  | 174 | @param index The index in the vector | 
|---|
|  | 175 | @param input The new value | 
|---|
|  | 176 | @return True if the new value was successfully assigned | 
|---|
|  | 177 | */ | 
|---|
| [1747] | 178 | bool ConfigValueContainer::tset(unsigned int index, const MultiType& input) | 
|---|
| [1505] | 179 | { | 
|---|
|  | 180 | if (this->bIsVector_) | 
|---|
|  | 181 | { | 
|---|
|  | 182 | if (index > MAX_VECTOR_INDEX) | 
|---|
|  | 183 | { | 
|---|
| [8858] | 184 | orxout(user_error, context::config) << "Index " << index << " is too large." << endl; | 
|---|
| [1505] | 185 | return false; | 
|---|
|  | 186 | } | 
|---|
|  | 187 |  | 
|---|
|  | 188 | if (index >= this->valueVector_.size()) | 
|---|
|  | 189 | { | 
|---|
|  | 190 | for (unsigned int i = this->valueVector_.size(); i <= index; i++) | 
|---|
|  | 191 | { | 
|---|
| [1747] | 192 | this->valueVector_.push_back(MultiType()); | 
|---|
| [1505] | 193 | } | 
|---|
|  | 194 | } | 
|---|
|  | 195 |  | 
|---|
| [1747] | 196 | this->valueVector_[index] = input; | 
|---|
| [1505] | 197 |  | 
|---|
| [1747] | 198 | if (this->identifier_) | 
|---|
|  | 199 | this->identifier_->updateConfigValues(); | 
|---|
| [1505] | 200 |  | 
|---|
| [1747] | 201 | return true; | 
|---|
| [1505] | 202 | } | 
|---|
|  | 203 | else | 
|---|
|  | 204 | { | 
|---|
| [8858] | 205 | orxout(user_error, context::config) << "Config-value '" << this->varname_ << "' in " << this->sectionname_ << " is not a vector." << endl; | 
|---|
| [1747] | 206 | return false; | 
|---|
| [1505] | 207 | } | 
|---|
|  | 208 | } | 
|---|
|  | 209 |  | 
|---|
|  | 210 | /** | 
|---|
|  | 211 | @brief Adds a new entry to the end of the vector. | 
|---|
|  | 212 | @param input The new entry | 
|---|
|  | 213 | @return True if the new entry was successfully added | 
|---|
|  | 214 | */ | 
|---|
| [1747] | 215 | bool ConfigValueContainer::add(const MultiType& input) | 
|---|
| [1505] | 216 | { | 
|---|
|  | 217 | if (this->bIsVector_) | 
|---|
|  | 218 | return this->set(this->valueVector_.size(), input); | 
|---|
|  | 219 |  | 
|---|
| [8858] | 220 | orxout(user_error, context::config) << "Config-value '" << this->varname_ << "' in " << this->sectionname_ << " is not a vector." << endl; | 
|---|
| [1505] | 221 | return false; | 
|---|
|  | 222 | } | 
|---|
|  | 223 |  | 
|---|
|  | 224 | /** | 
|---|
|  | 225 | @brief Removes an existing entry from the vector. | 
|---|
|  | 226 | @param index The index of the entry | 
|---|
|  | 227 | @return True if the entry was removed | 
|---|
|  | 228 | */ | 
|---|
|  | 229 | bool ConfigValueContainer::remove(unsigned int index) | 
|---|
|  | 230 | { | 
|---|
|  | 231 | if (this->bIsVector_) | 
|---|
|  | 232 | { | 
|---|
|  | 233 | if (index < this->valueVector_.size()) | 
|---|
|  | 234 | { | 
|---|
|  | 235 | // Erase the entry from the vector, change (shift) all entries beginning with index in the config file, remove the last entry from the file | 
|---|
|  | 236 | this->valueVector_.erase(this->valueVector_.begin() + index); | 
|---|
|  | 237 | for (unsigned int i = index; i < this->valueVector_.size(); i++) | 
|---|
| [6536] | 238 | ConfigFileManager::getInstance().getConfigFile(this->type_)->setValue(this->sectionname_, this->varname_, i, this->valueVector_[i], this->value_.isType(MT_Type::String)); | 
|---|
|  | 239 | ConfigFileManager::getInstance().getConfigFile(this->type_)->deleteVectorEntries(this->sectionname_, this->varname_, this->valueVector_.size()); | 
|---|
| [1505] | 240 |  | 
|---|
|  | 241 | return true; | 
|---|
|  | 242 | } | 
|---|
| [8858] | 243 | orxout(user_error, context::config) << "Invalid vector-index." << endl; | 
|---|
| [1505] | 244 | } | 
|---|
|  | 245 |  | 
|---|
| [8858] | 246 | orxout(user_error, context::config) << "Config-value '" << this->varname_ << "' in " << this->sectionname_ << " is not a vector." << endl; | 
|---|
| [1505] | 247 | return false; | 
|---|
|  | 248 | } | 
|---|
|  | 249 |  | 
|---|
|  | 250 | /** | 
|---|
|  | 251 | @brief Sets the value of the variable back to the default value and resets the config-file entry. | 
|---|
|  | 252 | */ | 
|---|
|  | 253 | bool ConfigValueContainer::reset() | 
|---|
|  | 254 | { | 
|---|
|  | 255 | if (!this->bIsVector_) | 
|---|
|  | 256 | return this->set(this->defvalueString_); | 
|---|
|  | 257 | else | 
|---|
|  | 258 | { | 
|---|
|  | 259 | bool success = true; | 
|---|
|  | 260 | for (unsigned int i = 0; i < this->defvalueStringVector_.size(); i++) | 
|---|
|  | 261 | if (!this->set(i, this->defvalueStringVector_[i])) | 
|---|
|  | 262 | success = false; | 
|---|
| [6536] | 263 | ConfigFileManager::getInstance().getConfigFile(this->type_)->deleteVectorEntries(this->sectionname_, this->varname_, this->defvalueStringVector_.size()); | 
|---|
| [1505] | 264 | return success; | 
|---|
|  | 265 | } | 
|---|
|  | 266 | } | 
|---|
|  | 267 |  | 
|---|
|  | 268 | /** | 
|---|
|  | 269 | @brief Retrieves the configured value from the currently loaded config-file. | 
|---|
|  | 270 | */ | 
|---|
|  | 271 | void ConfigValueContainer::update() | 
|---|
|  | 272 | { | 
|---|
|  | 273 | if (!this->bIsVector_) | 
|---|
| [6536] | 274 | this->value_ = ConfigFileManager::getInstance().getConfigFile(this->type_)->getOrCreateValue(this->sectionname_, this->varname_, this->defvalueString_, this->value_.isType(MT_Type::String)); | 
|---|
| [1505] | 275 | else | 
|---|
|  | 276 | { | 
|---|
|  | 277 | this->valueVector_.clear(); | 
|---|
| [6536] | 278 | unsigned int vectorSize = ConfigFileManager::getInstance().getConfigFile(this->type_)->getVectorSize(this->sectionname_, this->varname_); | 
|---|
| [2662] | 279 | for (unsigned int i = 0; i < vectorSize; i++) | 
|---|
| [1505] | 280 | { | 
|---|
|  | 281 | if (i < this->defvalueStringVector_.size()) | 
|---|
|  | 282 | { | 
|---|
| [6536] | 283 | this->value_ = ConfigFileManager::getInstance().getConfigFile(this->type_)->getOrCreateValue(this->sectionname_, this->varname_, i, this->defvalueStringVector_[i], this->value_.isType(MT_Type::String)); | 
|---|
| [1505] | 284 | } | 
|---|
|  | 285 | else | 
|---|
|  | 286 | { | 
|---|
| [6536] | 287 | this->value_ = ConfigFileManager::getInstance().getConfigFile(this->type_)->getOrCreateValue(this->sectionname_, this->varname_, i, MultiType(), this->value_.isType(MT_Type::String)); | 
|---|
| [1505] | 288 | } | 
|---|
|  | 289 |  | 
|---|
|  | 290 | this->valueVector_.push_back(this->value_); | 
|---|
|  | 291 | } | 
|---|
|  | 292 | } | 
|---|
|  | 293 | } | 
|---|
|  | 294 |  | 
|---|
|  | 295 | /** | 
|---|
|  | 296 | @brief Calls the given function with parsed index and the parsed argument from the input string. | 
|---|
|  | 297 | @param function The function to call | 
|---|
|  | 298 | @param input The input string | 
|---|
|  | 299 | @return The returnvalue of the functioncall | 
|---|
|  | 300 | */ | 
|---|
| [1747] | 301 | bool ConfigValueContainer::callFunctionWithIndex(bool (ConfigValueContainer::* function) (unsigned int, const MultiType&), const std::string& input) | 
|---|
| [1505] | 302 | { | 
|---|
| [7284] | 303 | SubString token(input, " ", SubString::WhiteSpaces, true, '\\', false, '"', false, '\0', '\0', false, '\0'); | 
|---|
| [1505] | 304 | int index = -1; | 
|---|
|  | 305 | bool success = false; | 
|---|
|  | 306 |  | 
|---|
|  | 307 | if (token.size() > 0) | 
|---|
| [3196] | 308 | success = convertValue(&index, token[0]); | 
|---|
| [1505] | 309 |  | 
|---|
| [1791] | 310 | if (!success || index < 0 || index > (signed int)MAX_VECTOR_INDEX) | 
|---|
| [1505] | 311 | { | 
|---|
|  | 312 | if (!success) | 
|---|
|  | 313 | { | 
|---|
| [8858] | 314 | orxout(user_error, context::config) << "Config-value '" << this->varname_ << "' in " << this->sectionname_ << " is a vector." << endl; | 
|---|
| [1505] | 315 | } | 
|---|
|  | 316 | else | 
|---|
|  | 317 | { | 
|---|
| [8858] | 318 | orxout(user_error, context::config) << "Invalid vector-index." << endl; | 
|---|
| [1505] | 319 | } | 
|---|
|  | 320 | return false; | 
|---|
|  | 321 | } | 
|---|
|  | 322 |  | 
|---|
|  | 323 | if (token.size() >= 2) | 
|---|
|  | 324 | return (this->*function)(index, token.subSet(1).join()); | 
|---|
|  | 325 | else | 
|---|
|  | 326 | return (this->*function)(index, ""); | 
|---|
|  | 327 | } | 
|---|
|  | 328 |  | 
|---|
|  | 329 | /** | 
|---|
|  | 330 | @brief Adds a description to the config-value. | 
|---|
|  | 331 | @param description The description | 
|---|
|  | 332 | */ | 
|---|
| [1747] | 333 | ConfigValueContainer& ConfigValueContainer::description(const std::string& description) | 
|---|
| [1505] | 334 | { | 
|---|
|  | 335 | if (!this->bAddedDescription_) | 
|---|
|  | 336 | { | 
|---|
| [1887] | 337 | this->description_ = std::string("ConfigValueDescription::" + this->sectionname_ + "::" + this->varname_); | 
|---|
| [1505] | 338 | AddLanguageEntry(this->description_, description); | 
|---|
|  | 339 | this->bAddedDescription_ = true; | 
|---|
|  | 340 | } | 
|---|
| [1747] | 341 | return (*this); | 
|---|
| [1505] | 342 | } | 
|---|
|  | 343 |  | 
|---|
|  | 344 | /** | 
|---|
|  | 345 | @brief Returns the description of the config-value. | 
|---|
|  | 346 | @return The description | 
|---|
|  | 347 | */ | 
|---|
|  | 348 | const std::string& ConfigValueContainer::getDescription() const | 
|---|
|  | 349 | { | 
|---|
|  | 350 | return GetLocalisation(this->description_); | 
|---|
|  | 351 | } | 
|---|
|  | 352 | } | 
|---|