1 /* $Id: wopsettings.cpp,v 1.81.2.3 2006/01/24 10:08:10 chfreund Exp $ */
2 
3 #include  "global.hpp"
4 #include  "constants.hpp"
5 #include  "wopsettings.hpp"
6 #include  "spriteset.hpp"
7 #include  "string.hpp"
8 
9 #include  <cstdlib>
10 #include  <cstdio>
11 
12 #ifdef WIN32
13 #define NO_HOME_PATH_EXPANSION
14 #endif
15 
16 #ifndef NO_HOME_PATH_EXPANSION
17 #include  <pwd.h>
18 #endif
19 
20 /**********************************************************/
21 // definition of some standard attributes
22 
23 // STD_ATTR : standard setting
24 //   - explicit assignment by "="
25 //   - comma separation of parameters possible
26 #define  STD_ATTR  (Setting::EXPLICIT_ASSIGNMENT | \
27                     Setting::COMMA_SEPARATED)
28 // STD_ATTR_MAND : standard setting, but mandatory
29 #define  STD_ATTR_MAND  (STD_ATTR | Setting::MANDATORY)
30 // STD_ATTR_CLO : standard settings, command line only
31 #define  STD_ATTR_CLO   (STD_ATTR | Setting::COMMAND_LINE_ONLY)
32 // STD_ATTR_CFO : standard settings, config file only
33 #define  STD_ATTR_CFO   (STD_ATTR | Setting::CONFIG_FILE_ONLY)
34 
35 /**********************************************************
36  * definition of settings and string maps
37  **********************************************************/
38 
39 // the default configuration file is defined as a macro,
40 // since it is also used in the help string of the setting
41 
42 // commented out on 15.Jan 2005 (uwe)
43 //// #if defined(__APPLE__) || defined(WIN32)
44 //// #define  DEFAULT_WOP_CONFIG_FILE  "woprc.txt"
45 //// #else
46 //// #define  DEFAULT_WOP_CONFIG_FILE  ".woprc"
47 //// #endif
48 
49 // Here the standard search paths for the config file are set,
50 // INCLUDING their names.
51 // This list also defines the hierarchy of the settings' usage.
52 // First the command line is parsed and the specified settings are
53 // accepted. Then one by one config file from the list defined in
54 // m_configPaths is read, and the settings are AUGMENTED with the
55 // ones in that file, NOT OVERWRITTEN. This alows for example the
56 // definition of a standard system-wide configuration file and a
57 // userspecific configuration file by setting:
58 //
59 //     const char* WopSettings::m_configPaths[] = {
60 //         "~/.woprc", "/usr/local/config/sys_woprc", NULL };
61 //
62 // The list must be terminated by a 0x0 pointer as last entry.
63 //
64 // So the settings are read in the following hierarchy:
65 //
66 //  HIGHEST PRIORITY
67 //     -> command line options
68 //     -> options in configuration file, that has been
69 //        specified optionally in the commandline using
70 //        the setting "config=<path/filename>"
71 //     -> m_configPaths[0]
72 //             :
73 //             :
74 //  LOWEST PRIORITY
75 //
76 const char* WopSettings::m_configPaths[] = {
77 #ifdef __APPLE__
78 	"~/woprc.txt",
79 #else
80 	"~/.woprc",   // home directory
81 	"/usr/local/etc/woprc", // system wide
82 #endif
83 	0x0
84 };
85 
86 /**********************************************************/
87 
88 // definition of the basic settings
89 SettingDef WopSettings::m_basicSettings[] = {
90 	// the members are:
91 	//  (1) ID string of the setting
92 	//  (2) alternative ID string (optional, can also be set to NULL)
93 	//  (3) parameter type of the setting (Setting::[INT,BOOL,FLOAT,STRING])
94 	//  (4) minimal number of parameters for the setting
95 	//  (5) maximal number of parameters for the setting (-1 means unlimited)
96 	//  (6) attributes for the setting (as default use STD_ATTR, for more
97 	//      details read documentation of class SettingDef in settings.hpp
98 	//  (7) if the new setting is a server setting, that should be sent to
99 	//      the clients, add its IDstring to m_serverSettingsIDs
100 	//
101 	// try to limit line length of the help to maximal 60 characters   |<------ 60 characters, including built in indent ------>|
102 	// mode -> SettingStringMap<int> WopSettings::m_mode_StringMap[]
103 	SettingDef( "mode",    "m",  Setting::STRING, 1, 1, STD_ATTR_MAND, "program mode [server,client,replay]", 0 ),
104 	SettingDef( "gamemode","gm", Setting::STRING, 1, 1, STD_ATTR,      "game mode (server only) [deathmatch,teamplay]", 0 ),
105 	SettingDef( "data",    "d",  Setting::STRING, 1, 1, STD_ATTR,      "path of data directory\n    "
106 	                                                                   "default: \"data\", then /usr/local/games/wop/data", 0 ),
107 	SettingDef( "server",  "s",  Setting::STRING, 1, 1, STD_ATTR,      "server address. default: localhost", 0 ),
108 	SettingDef( "width",   "w",  Setting::INT,    1, 1, STD_ATTR,      "width of the game map: > 0 (server only)", 0 ),
109 	SettingDef( "height",  "h",  Setting::INT,    1, 1, STD_ATTR,      "height of the game map: > 0 (server only)", 0 ),
110 	SettingDef( "theme",   0x0,  Setting::STRING, 1, 1, STD_ATTR,      "name of the map theme (effect on server only)\n    "
111 	                                                                   "possible theme names are specified in file "THEME_CONFIG"\n    "
112 	                                                                   "in the data directory (default: \"standard\").\n    "
113 	                                                                   "To get a list of the available themes simply specify a\n    "
114 	                                                                   "non-valid theme, e.g. theme=?", 0 ),
115 	// view -> SettingStringMap<int> WopSettings::m_view_StringMap[]
116 	SettingDef( "view",    "v",  Setting::STRING, 1, 1, STD_ATTR,      "view mode: [fullscreen,window], default: window", 0 ),
117 	SettingDef( "quiet",   "q",  Setting::BOOL  , 0, 1, STD_ATTR,      "no sound output: [yes,no], default: no", 0 ),
118 	SettingDef( "config",  "c",  Setting::STRING, 1, 1, STD_ATTR_CLO,  "path of the configuration file", 0 ),
119 	SettingDef( "name",    "n",  Setting::STRING, 1, 1, STD_ATTR,      "the name of the player", 0 ),
120 	SettingDef( "color",   "c",  Setting::INT,    3, 3, STD_ATTR,      "the color of the player's avatar, specified in the\n    "
121 	                                                                   "three color components RED, GREEN, BLUE in this order.\n    "
122 	                                                                   "The RGB values must be in range [0,255]" ),
123 	SettingDef( "team",    "t",  Setting::INT,    1, 1, STD_ATTR,      "the team number (0 <= team <= 19, only considered in \n    "
124 	                                                                   "team mode)", 0 ),
125 	SettingDef( "keyboard","kb", Setting::STRING, 1, 1, STD_ATTR,      "Keyboard layout (de/us)" ),
126 	SettingDef( "nballs",  "nb", Setting::INT,    1, 1, STD_ATTR,      "number of balls in the game", 0 ),
127 	SettingDef( "ngoals",  "ng", Setting::INT,    1, 1, STD_ATTR,      "number of goals in the game", 0 ),
128 	SettingDef( "szprob",   0x0, Setting::REAL,   1, 1, STD_ATTR,      "probability for the creation of SkwoermZones [0.0, 1.0]", 0 ),
129 	SettingDef( "debug",   "db", Setting::BOOL,   0, 1, STD_ATTR,      "debug mode: [yes,no], default: no", 0 ),
130 	SettingDef( "musicdir", 0x0, Setting::STRING, 1, 1, STD_ATTR,      "path of a directory containing sound files of type\n    "
131 	                                                                   "*.wav, *.mp3, *.ogg, that are played randomly during\n    "
132 	                                                                   "the game.", 0 ),
133 	SettingDef( "bots",     0x0, Setting::STRING, 1,-1, STD_ATTR,      "list of bot names" ),
134 	SettingDef( "replaylog",0x0, Setting::STRING, 1, 1, STD_ATTR,      "name of replay log file. This setting also activates\n    "
135 	                                                                   "the replay logging (server only). To view the replay\n    "
136 	                                                                   "in the specified file choose \"mode=replay\"", 0 ),
137 	SettingDef( "keyhelp",  0x0, Setting::BOOL, 1, -1, STD_ATTR_CLO,   "prints the long list of possible key configurations on\n    "
138 	                                                                   "the terminal.", 0 ),
139 	// settings for keyboard configuration
140 	SettingDef( "key_left",           0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "left key (configuration file only)", 10 ),
141 	SettingDef( "key_right",          0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "right key (configuration file only)", 10 ),
142 	SettingDef( "key_down",           0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "down key (configuration file only)", 10 ),
143 	SettingDef( "key_up",             0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "up key (configuration file only)", 10 ),
144 	SettingDef( "key_shoot",          0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "shoot key (configuration file only)", 10 ),
145 	SettingDef( "key_jump",           0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "jump key (configuration file only)", 10 ),
146 	SettingDef( "key_dig",            0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "dig key (configuration file only)", 10 ),
147 	SettingDef( "key_jet",            0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "jet key (configuration file only)", 10 ),
148 	SettingDef( "key_rope_on",        0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "rope on key (configuration file only)", 10 ),
149 	SettingDef( "key_rope_off",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "rope off key (configuration file only)", 10 ),
150 	SettingDef( "key_rope_in",        0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "rope in key (configuration file only)", 10 ),
151 	SettingDef( "key_rope_out",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "rope out key (configuration file only)", 10 ),
152 	SettingDef( "key_rope_rel",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "rope rel key (configuration file only)", 10 ),
153 	SettingDef( "key_screenshot",     0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "screenshot key (configuration file only)", 10 ),
154 	SettingDef( "key_debug",          0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "debug key (configuration file only)", 10 ),
155 	SettingDef( "key_dec_vol",        0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "decrease volume key (configuration file only)", 10 ),
156 	SettingDef( "key_inc_vol",        0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "increase volume key (configuration file only)", 10 ),
157 	SettingDef( "key_toggle_jukebox", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "toggle jukebox key (configuration file only)", 10 ),
158 	SettingDef( "key_weapon_1",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 1 key (configuration file only)", 10 ),
159 	SettingDef( "key_weapon_2",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 2 key (configuration file only)", 10 ),
160 	SettingDef( "key_weapon_3",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 3 key (configuration file only)", 10 ),
161 	SettingDef( "key_weapon_4",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 4 key (configuration file only)", 10 ),
162 	SettingDef( "key_weapon_5",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 5 key (configuration file only)", 10 ),
163 	SettingDef( "key_weapon_6",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 6 key (configuration file only)", 10 ),
164 	SettingDef( "key_weapon_7",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 7 key (configuration file only)", 10 ),
165 	SettingDef( "key_weapon_8",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 8 key (configuration file only)", 10 ),
166 	SettingDef( "key_weapon_9",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 9 key (configuration file only)", 10 ),
167 	SettingDef( "key_weapon_0",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 0 key (configuration file only)", 10 ),
168 	SettingDef( "key_spawn",          0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "spawn key (configuration file only)", 10 ),
169 	SettingDef( "key_next_weapon",    0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "next weapon key (configuration file only)", 10 ),
170 	SettingDef( "key_prev_weapon",    0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "previous weapon key (configuration file only)", 10 ),
171 	SettingDef( "key_insert_replay_bookmark",
172 	                                  0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "insert replay bookmark key (configuration file only)", 10 ),
173 	SettingDef( "key_change_focus",   0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "change focus key (configuration file only)", 10 ),
174 	SettingDef( "key_toggle_fullscreen",
175 	                                  0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "toggle fullscreen key (configuration file only)", 10 ),
176 	SettingDef( "key_show_fps",       0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "show frames per second key (configuration file only)", 10 ),
177 	SettingDef( "key_limit_fps",      0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "limit frames per second key (configuration file only)", 10 ),
178 	// empty SettingDef() for termination, do not remove
179 	SettingDef()
180 };
181 
182 // map for the setting "mode"
183 SettingStringMap<int> WopSettings::
184 m_mode_StringMap[NUMBER_OF_PROGRAM_MODES+1] = {
185 	SettingStringMap<int>( "server", SERVER ),
186 	SettingStringMap<int>( "client", CLIENT ),
187 	SettingStringMap<int>( "replay", REPLAY ),
188 	SettingStringMap<int>()
189 };
190 
191 // map for the setting "view"
192 SettingStringMap<int> WopSettings::
193 m_view_StringMap[NUMBER_OF_VIEW_MODES+1] = {
194 	SettingStringMap<int>( "fullscreen", FULLSCREEN ),
195 	SettingStringMap<int>( "window",     WINDOW ),
196 	SettingStringMap<int>()
197 };
198 
199 // map for the setting "view"
200 SettingStringMap<int> WopSettings::
201 m_gamemode_StringMap[NUMBER_OF_GAME_MODES+1] = {
202 	SettingStringMap<int>( "deathmatch", DEATHMATCH ),
203 	SettingStringMap<int>( "teamplay",   TEAMPLAY ),
204 	SettingStringMap<int>()
205 };
206 
207 // primary IDs of the settings, that are fixed on server side only
208 // and sent to all clients.
209 // IMPORTANT: All server settings must be present as objects on the
210 // server, since we have this Client-Server-Join-or--Start-Game-Feature.
211 // This creation of not explicitly set setting objects is done by
212 // method WopSettings::enforcePresenceOfServerSettings and is not
213 // adapted automatically to new entries in array m_serverSettingsIDs.
214 // Therefore, whenever you add a new server setting, also add its
215 // "instantiation" in WopSettings::enforcePresenceOfServerSettings.
216 const char* WopSettings::m_serverSettingsIDs[] = {
217 	"gamemode",
218 	"width", "height",
219 	"nballs", "ngoals",
220 	"theme",
221 	NULL
222 };
223 
224 
225 /**********************************************************
226  * default values
227  **********************************************************/
228 
229 // IMPORTANT: Do not use paths containing '~' for HOME as
230 //            default paths. '~' is only expanded, if specified
231 //            in command line or configuration file.
232 const int    WopSettings::m_DefaultMode        = CLIENT;
233 const int    WopSettings::m_DefaultGameMode    = DEATHMATCH;
234 const char   WopSettings::m_DefaultData[]      = "data";
235 const char   WopSettings::m_DefaultServer[]    = "localhost";
236 const int    WopSettings::m_DefaultWidth       = 1000;
237 const int    WopSettings::m_DefaultHeight      = 1000;
238 // RANDOM_CIRCLES_WOP_MAP_THEME defined in constants.hpp
239 const char   WopSettings::m_DefaultTheme[]     = "standard"; // RANDOM_CIRCLES_WOP_MAP_THEME;
240 const int    WopSettings::m_DefaultView        = WINDOW;
241 const bool   WopSettings::m_DefaultQuiet       = false;
242 #if defined(__APPLE__) || defined(WIN32)
243 const char   WopSettings::m_DefaultConfig[]    = "woprc.txt";
244 #else
245 const char   WopSettings::m_DefaultConfig[]    = ".woprc";
246 #endif
247 const char   WopSettings::m_DefaultName[]      = "The Dude";
248 const Uint32 WopSettings::m_DefaultPlayerColor = 0x00ffffff;
249 const int    WopSettings::m_DefaultTeamID      = 0;
250 const char   WopSettings::m_DefaultKeyboard[]  = "us";
251 const int    WopSettings::m_DefaultNumBalls    = 2;
252 const int    WopSettings::m_DefaultNumGoals    = 2;
253 const real   WopSettings::m_DefaultSZProb      = 0.01;
254 const bool   WopSettings::m_DefaultDebug       = false;
255 
256 // some additional definitions, e.g. for range control
257  #define MIN_MAP_WIDTH    100
258 #define  MAX_MAP_WIDTH   2000
259 #define  MIN_MAP_HEIGHT   100
260 #define  MAX_MAP_HEIGHT  2000
261 //#define  MIN_GAME_ID        0
262 
263 /**********************************************************/
264 
265 #ifndef  FUNCTION_ERROR
266 #define  FUNCTION_ERROR(fns)  "\033[31m" << fns << ":\033[0m\n >> "
267 #endif // FUNCTION_ERROR
268 #ifndef  CALLED_FROM
269 #define  CALLED_FROM(fns)  "    called from \033[36m" << fns << "\033[0m\n"
270 #endif // CALLED_FROM
271 
272 /**********************************************************/
273 
274 WopSettings* WopSettings::m_settings = 0;
275 
276 /**********************************************************/
277 
WopSettings(const bool verbose)278 WopSettings::WopSettings( const bool verbose )
279            : SettingDataBase( verbose ),
280              m_writeConfigFile( false ),
281              m_pickySuccess( false )
282 {}
283 
284 /**********************************************************/
285 
getInstance()286 WopSettings* WopSettings::getInstance()
287 {
288 	if( NULL ==  m_settings &&
289 		NULL == (m_settings = NEW WopSettings()) ) {
290 		cerr << FUNCTION_ERROR("WopSettings::getInstance")
291 		     << "could not create setting object\n";
292 	}
293 	return m_settings;
294 }
295 
296 /**********************************************************/
297 
deleteInstance()298 void WopSettings::deleteInstance()
299 {
300 	delete m_settings;
301 	m_settings = NULL;
302 }
303 
304 /**********************************************************/
305 
~WopSettings()306 WopSettings::~WopSettings()
307 {
308 /* Deactivated writing of config file in the destructor,
309  * since this will be triggered in the settings GUI from
310  * now on (19.01.2005, uwe)
311  *
312 	if( m_writeConfigFile ) {
313 		Setting *setting = getSetting( "config" );
314 		String path( setting ? setting->getString() : NULL );
315 		writeConfigFile( path );
316 	}
317  *
318  */
319 }
320 
321 /**********************************************************/
322 
getMode(const bool exitOnError) const323 int WopSettings::getMode( const bool exitOnError ) const
324 {
325 	int mode = m_DefaultMode;
326 	// note that "mode" is a mandatory setting, and that we
327 	// have passed already SettingDataBase::finalCheck
328 	// ==> setting "mode" is present
329 	if( false == mapStringParameter("mode", m_mode_StringMap, mode) ) {
330 		Setting *setting = getSetting( "mode" );
331 		if( setting ) {
332 			cerr << FUNCTION_ERROR("WopSettings::getMode")
333 			     << "\"" << setting->getString()
334 			     << "\" is not a valid program mode\n     the list of "
335 			        "options follows\n\n";
336 			// print help strings only of level 0
337 			printHelp( 0, 0 );
338 		}
339 		if( exitOnError )  exit( 1 );
340 	}
341 
342 	return mode;
343 }
344 
345 /**********************************************************/
346 
getGameMode() const347 int WopSettings::getGameMode() const
348 {
349 	int gamemode = m_DefaultGameMode;
350 	Setting *setting = getSetting( "gamemode" );
351 	if( setting != NULL ) {
352 		ASSERT( mapStringParameter("gamemode",
353 		                           m_gamemode_StringMap,
354 		                           gamemode),
355 		        "WopSettings::getGameMode: \"%s\" is not a valid "
356 		        "game mode.\n", setting->getString() );
357 	}
358 
359 	return gamemode;
360 }
361 
362 /**********************************************************/
363 
getData() const364 const char* WopSettings::getData() const
365 {
366 	const char *data = m_DefaultData;
367 	Setting *setting = getSetting( "data" );
368 	if( setting ) {
369 		ASSERT( true == setting->expandPathString(), "could not expand "
370 		        "data path \"%s\"\n", setting->getString() );
371 		data = setting->getString();
372 	}
373 	return (const char*)data;
374 }
375 
376 /**********************************************************/
377 
getServer() const378 const char* WopSettings::getServer() const
379 {
380 	const char *server = m_DefaultServer;
381 	Setting *setting   = getSetting( "server" );
382 	if( setting )  server = setting->getString();
383 	return (const char*)server;
384 }
385 
386 /**********************************************************/
387 
getWidth() const388 int WopSettings::getWidth() const
389 {
390 	int      width   = m_DefaultWidth;
391 	Setting *setting = getSetting( "width" );
392 	if( setting ) {
393 		width = setting->getInt();
394 		if( width < MIN_MAP_WIDTH || width > MAX_MAP_WIDTH ) {
395 			cerr << FUNCTION_ERROR("WopSettings::getWidth")
396 			     << "width (given as " << width << ") must be in "
397 			        "range [" << MIN_MAP_WIDTH << ", " << MAX_MAP_WIDTH
398 			     << "]\n";
399 			exit( 1 );
400 		}
401 	}
402 	return width;
403 }
404 
405 /**********************************************************/
406 
getHeight() const407 int WopSettings::getHeight() const
408 {
409 	int      height  = m_DefaultHeight;
410 	Setting *setting = getSetting( "height" );
411 	if( setting ) {
412 		height = setting->getInt();
413 		if( height < MIN_MAP_HEIGHT || height > MAX_MAP_HEIGHT ) {
414 			cerr << FUNCTION_ERROR("WopSettings::getHeight")
415 			     << "heigth (given as " << height << ") must be in "
416 			        "range [" << MIN_MAP_HEIGHT << ", " << MAX_MAP_HEIGHT
417 			     << "]\n";
418 			exit( 1 );
419 		}
420 	}
421 	return height;
422 }
423 
424 /**********************************************************/
425 
getTheme() const426 const char* WopSettings::getTheme() const
427 {
428 	const Setting* setting = getSetting( "theme" );
429 	if( setting )  return setting->getString();
430 
431 	return m_DefaultTheme;
432 }
433 
434 /**********************************************************/
435 
getView() const436 int WopSettings::getView() const
437 {
438 	int view = m_DefaultView;
439 	if( NULL  != getSetting("view") &&
440 	    false == mapStringParameter("view", m_view_StringMap, view) ) {
441 		cerr << FUNCTION_ERROR("WopSettings::getView")
442 		     << "\"" << getSetting("view")->getString()
443 		     << "\" is not a valid view\n     the list of options"
444 		        " follows\n\n";
445 		// print help strings only of level 0
446 		printHelp( 0, 0 );
447 		exit( 1 );
448 	}
449 	return view;
450 }
451 
452 /**********************************************************/
453 
getQuiet() const454 bool WopSettings::getQuiet() const
455 {
456 	bool quiet = m_DefaultQuiet;
457 	Setting *setting = getSetting( "quiet" );
458 	if( setting ) {
459 		// quiet should be also usable as a simple flag
460 		// -> no parameter is interpreted as "true"
461 		quiet =   setting->getNumParameters() > 0
462 		         ? setting->getBool()
463 		         : true;
464 	// if the setting "quiet" is not specified explicitly,
465 	// the server runs in quiet mode explicitely
466 	} else {
467 		if( SERVER == getMode() )  quiet = true;
468 	}
469 	return quiet;
470 }
471 
472 /**********************************************************/
473 
getName() const474 const char* WopSettings::getName() const
475 {
476 	const char *name = m_DefaultName;
477 	Setting *setting = getSetting( "name" );
478 	if( setting )  name = setting->getString();
479 	return (const char*) name;
480 }
481 
482 /**********************************************************/
483 
getPlayerColor() const484 Uint32 WopSettings::getPlayerColor() const
485 {
486 	Uint32 color = m_DefaultPlayerColor;
487 	Setting* setting = getSetting( "color" );
488 	if( setting ) {
489 		color = (Uint32)(TPLCOL_RGB(setting->getInt(0),
490 		                            setting->getInt(1),
491 		                            setting->getInt(2)));
492 	}
493 	return color;
494 }
495 
496 /**********************************************************/
497 
getTeamID() const498 int WopSettings::getTeamID() const
499 {
500 	Setting *setting = getSetting( "team" );
501 	if( setting ) {
502 		ASSERT(    setting->getInt() >= 0
503 		        && setting->getInt() <  MAX_NUMBER_OF_PLAYERS,
504 		        "invalid team number (%i): 0 <= team < %i\n",
505 		        setting->getInt(), MAX_NUMBER_OF_PLAYERS );
506 		return setting->getInt();
507 	} else {
508 		return m_DefaultTeamID;
509 	}
510 }
511 
512 /**********************************************************/
513 
getKeyboard() const514 const char* WopSettings::getKeyboard() const
515 {
516 	const char *keyboard = m_DefaultKeyboard;
517 	Setting *setting   = getSetting( "keyboard" );
518 	if( setting )  keyboard = setting->getString();
519 	return (const char*) keyboard;
520 }
521 
522 /**********************************************************/
523 
getNumBalls() const524 int WopSettings::getNumBalls() const
525 {
526 	Setting *setting = getSetting( "nballs" );
527 	if( setting ) {
528 		ASSERT( setting->getInt() >= 0,
529 		        "the number of balls must be >= 0, using "
530 		        "default %d\n",m_DefaultNumBalls  );
531 		return setting->getInt();
532 	} else {
533 		return m_DefaultNumBalls;
534 	}
535 }
536 
537 /**********************************************************/
538 
getNumGoals() const539 int WopSettings::getNumGoals() const
540 {
541 	Setting *setting = getSetting( "ngoals" );
542 	if( setting ) {
543 		ASSERT( setting->getInt() >= 0,
544 		        "the number of goals must be >= 0, using "
545 		        "default %d\n",m_DefaultNumGoals  );
546 		return setting->getInt();
547 	} else {
548 		return m_DefaultNumGoals;
549 	}
550 }
551 
552 /**********************************************************/
553 
getSZProb() const554 real WopSettings::getSZProb() const
555 {
556 	Setting *setting = getSetting( "szprob" );
557 	if( setting ) {
558 		ASSERT( setting->getReal() >= 0.0 && setting->getReal() <= 1.0,
559 		        "the skwoerm zone probability must be between "
560 		        "0 and 1, using default %f\n", m_DefaultSZProb  );
561 		return setting->getReal();
562 	} else {
563 		return m_DefaultSZProb;
564 	}
565 }
566 
567 /**********************************************************/
568 
getDebug() const569 bool WopSettings::getDebug() const
570 {
571 	bool debug = m_DefaultDebug;
572 	Setting *setting = getSetting( "debug" );
573 	if( setting ) {
574 		// debug should be also usable as a simple flag
575 		// -> no parameter is interpreted as "true"
576 		debug =   setting->getNumParameters() > 0
577 		         ? setting->getBool()
578 		         : true;
579 	}
580 	return debug;
581 }
582 
583 /**********************************************************/
584 
getBots(const Sint32 i) const585 const char* WopSettings::getBots( const Sint32 i ) const
586 {
587 	Setting *setting = getSetting( "bots" );
588 	return (setting && i >= 0 && i < setting->getNumParameters())
589 	       ? setting->getString( i ) : 0x0;
590 }
591 
592 /**********************************************************/
593 
getReplayFileName(String & path) const594 bool WopSettings::getReplayFileName( String& path ) const
595 {
596 	Setting *setting = getSetting( "replaylog" );
597 	if( setting ) {
598 		path = setting->getString();
599 		return expandPath( path );
600 	}
601 
602 	return false;
603 }
604 
605 /**********************************************************/
606 
getMinWidth() const607 int WopSettings::getMinWidth()  const { return MIN_MAP_WIDTH;  }
getMaxWidth() const608 int WopSettings::getMaxWidth()  const { return MAX_MAP_WIDTH;  }
getMinHeight() const609 int WopSettings::getMinHeight() const { return MIN_MAP_HEIGHT; }
getMaxHeight() const610 int WopSettings::getMaxHeight() const { return MIN_MAP_HEIGHT; }
611 
612 /**********************************************************/
613 
getMusicDir(String & path) const614 bool WopSettings::getMusicDir( String& path ) const
615 {
616 	Setting *setting = getSetting( "musicdir" );
617 	if( setting ) {
618 		path = setting->getString();
619 		return expandPath( path );
620 	}
621 
622 	return false;
623 }
624 
625 /**********************************************************/
626 
setGameMode(const int mode)627 bool WopSettings::setGameMode( const int mode )
628 {
629 	int i = 0;
630 	while( m_gamemode_StringMap[i].m_String ) {
631 		if( m_gamemode_StringMap[i].m_Value == mode ) {
632 			if( !CHECK(setValue(m_basicSettings, "gamemode",
633 			                    &m_gamemode_StringMap[i].m_String),
634 			           "WopSettings::setGameMode: failed setting "
635 			           "the game mode to \"%s\"\n",
636 			           m_gamemode_StringMap[i].m_String) ) {
637 				return false;
638 			}
639 			return true;
640 		}
641 	}
642 	return CHECK( false, "WopSettings::setGameMode: %d is not"
643 	              "a valid game mode\n", mode );
644 }
645 
646 /**********************************************************/
647 
setData(const char * const path)648 bool WopSettings::setData( const char* const path )
649 {
650 	return
651 	CHECK( setValue(m_basicSettings, "data", (char**)&path),
652 	       "WopSettings::setData: could not set data path "
653 	       "to %s\n", path );
654 }
655 
656 /**********************************************************/
657 
setServer(const char * const server)658 bool WopSettings::setServer( const char* const server )
659 {
660 	return
661 	CHECK( setValue(m_basicSettings, "server", (char**)&server),
662 	       "WopSettings::setServer: could not set server address "
663 	       "to \"%s\"", server );
664 }
665 
666 /**********************************************************/
667 
setWidth(const int width)668 bool WopSettings::setWidth( const int width )
669 {
670 	return
671 	CHECK( setValue(m_basicSettings, "width", &width),
672 	       "WopSettings::setTeam: could not set map width to %d",
673 	       width );
674 }
675 
676 /**********************************************************/
677 
setHeight(const int height)678 bool WopSettings::setHeight( const int height )
679 {
680 	return
681 	CHECK( setValue(m_basicSettings, "height", &height),
682 	       "WopSettings::setTeam: could not set map height to %d",
683 	       height );
684 }
685 
686 /**********************************************************/
687 
setName(const char * const name)688 bool WopSettings::setName( const char* const name )
689 {
690 	return
691 	CHECK( setValue(m_basicSettings, "name", (char**)&name),
692 	       "WopSettings::setName: could not set player name "
693 	       "to \"%s\"", name );
694 }
695 
696 /**********************************************************/
697 
setPlayerColor(const Uint32 color)698 bool WopSettings::setPlayerColor( const Uint32 color )
699 {
700 	int rgb[3] = { TPLCOL_RED( color ),
701 	               TPLCOL_GREEN( color ),
702 	               TPLCOL_BLUE( color ) };
703 	return
704 	CHECK( setValue(m_basicSettings, "color", (int*)rgb, -1, 3),
705 	       "WopSettings::setColor: could not set player color "
706 	       "to (%d,%d,%d)", rgb[0], rgb[1], rgb[2] );
707 }
708 
709 /**********************************************************/
710 
setTeamID(const int teamID)711 bool WopSettings::setTeamID( const int teamID )
712 {
713 	return
714 	CHECK( setValue(m_basicSettings, "team", &teamID),
715 	       "WopSettings::setTeam: could not set team ID to %d",
716 	       teamID );
717 }
718 
719 /**********************************************************/
720 
setKey(const char * name,const char * value)721 bool WopSettings::setKey( const char* name, const char* value ) {
722 	return
723 	CHECK( setValue(m_basicSettings, name, (char**)&value),
724 	       "WopSettings::setKey: could not set key %s "
725 	       "to value \"%s\"", name, value );
726 }
727 
728 /**********************************************************/
729 
730 #define NUM_KEYBOARD_LAYOUTS 2
731 
setKeyboard(const char * const layout)732 bool WopSettings::setKeyboard( const char* const layout )
733 {
734 	const char *layouts[NUM_KEYBOARD_LAYOUTS] = {"us", "de"};
735 
736 	for( int i = 0; i < NUM_KEYBOARD_LAYOUTS; i++ ) {
737 		if( !strcmp(layouts[i], layout) ) {
738 			return
739 			CHECK( setValue(m_basicSettings, "keyboard", (char**)&layout),
740 			       "WopSettings::setKeyboard: could not set keyboard "
741 			       "layout to \"%s\"", layout );
742 		}
743 	}
744 	return
745 	CHECK( false, "WopSettings::setKeyboard: \"%s\" is not "
746 	       "a valid keyboard layout", layout );
747 }
748 
749 #undef NUM_KEYBOARD_LAYOUTS
750 
751 /**********************************************************/
752 
setNumBalls(const int nballs)753 bool WopSettings::setNumBalls( const int nballs )
754 {
755 	return
756 	CHECK( setValue(m_basicSettings, "nballs", &nballs),
757 	       "WopSettings::setNumBalls: could not set number "
758 	       "of balls to %d", nballs );
759 }
760 
761 /**********************************************************/
762 
setNumGoals(const int ngoals)763 bool WopSettings::setNumGoals( const int ngoals )
764 {
765 	return
766 	CHECK( setValue(m_basicSettings, "nballs", &ngoals),
767 	       "WopSettings::setNumGoals: could not set number "
768 	       "of goals to %d", ngoals );
769 }
770 
771 /**********************************************************/
772 
isAvailableTheme(const String & name) const773 bool WopSettings::isAvailableTheme( const String& name ) const
774 {
775 	const char fn[] = "WopSettings::isAvailableTheme:";
776 	PointerVector<String> themes;
777 
778 	ASSERT( getAvailableThemes(themes),
779 	        "%s could not build list of theme names\n", fn );
780 
781 	for( int i = 0; i < themes.getSize(); i++ ) {
782 		if( *themes[i] == name )  return true;
783 	}
784 
785 	return false;
786 }
787 
788 /**********************************************************/
789 
790 bool WopSettings::
getAvailableThemes(PointerVector<String> & themes) const791 getAvailableThemes( PointerVector<String>& themes ) const
792 {
793 	const char fn[] = "WopSettings::getAvailableThemes:";
794 
795 	themes.reset();
796 
797 	String themePath( getData() );
798 	themePath += '/';
799 	themePath += THEME_CONFIG_PATH;
800 	themePath += '/';
801 	themePath += THEME_CONFIG;
802 
803 	FILE* file = fopen( themePath, "rt" );
804 	ASSERT( file, "%s could not open theme file \"%s\"\n",
805 	        themePath.getString() );
806 
807 	char          character = EOF ^ 0x1;
808 	const char *const  key  = SETTING_DB_DIRECTIVE_SECTION_IN;
809 	const int     keyLength = strlen(key);
810 	int           lineIndex = 1;
811 
812 	while( character != EOF ) {
813 		// start scanning of line
814 		bool section_enter = true;
815 		for( int i = 0; i < keyLength; i++ ) {
816 			character = fgetc( file );
817 			if( EOF == character || character != key[i] ) {
818 				section_enter = false;
819 				break;
820 			}
821 		}
822 		// We found the complete string "key" at top of the current
823 		// line. The position of "file" is the first character after
824 		// the directive "key".
825 		if( section_enter ) {
826 			// search start of theme string
827 			// NOTE: Due to the for-loop above we did not yet read
828 			// the first character after SETTING_DB_DIRECTIVE_SECTION_IN
829 			while( EOF != (character = fgetc(file)) ) {
830 				if( ! CHECK(character != '\n',
831 				            "%s no section identifier in file \"%s\","
832 				            " line %d\n", fn, themePath.getString(),
833 				            lineIndex) ) {
834 					fclose( file );
835 					return false;
836 				}
837 				if( character != ' ' && character != '\t' )  break;
838 			}
839 			// build the theme name string
840 			String *name = NEW String;
841 			while( character != EOF  && character != ' '  &&
842 			       character != '\t' && character != '\n'    ) {
843 				*name += character;
844 				character = fgetc( file );
845 			}
846 			// add theme name to list
847 			themes.append( name );
848 		}
849 
850 		// search the next line start
851 		if( character != '\n' ) {
852 			while( EOF != (character = fgetc(file))
853 			       && character != '\n' );
854 		}
855 
856 		lineIndex++;
857 	}
858 
859 	// add the circle theme by hand
860 	themes.append( NEW String(RANDOM_CIRCLES_WOP_MAP_THEME) );
861 
862 	fclose( file );
863 
864 	return true;
865 }
866 
867 /**********************************************************/
868 
869 void WopSettings::
printAvailableThemes(ostream & out,const char * const head,const char * const tail) const870 printAvailableThemes( ostream&          out,
871                       const char *const head,
872 		              const char *const tail  ) const
873 {
874 	const char fn[] = "WopSettings::printAvailableThemes:";
875 
876 	PointerVector<String> themes;
877 
878 	ASSERT( getAvailableThemes(themes),
879 	        "%s could not build list of theme names\n", fn );
880 
881 	for( int i = 0; i < themes.getSize(); i++ ) {
882 		if( head )  out << head;
883 		out << *themes[i];
884 		if( tail )  out << tail;
885 	}
886 }
887 
888 /**********************************************************/
889 
890 void WopSettings::
serializeServerSettings(Uint8 * & bufferPointer)891 serializeServerSettings( Uint8*& bufferPointer )
892 {
893 	const Setting* setting = NULL;
894 
895 	LOG(2) INFO( "WopSettings::serializeServerSettings\n" );
896 
897 	for( int i = 0; m_serverSettingsIDs[i]; i++ ) {
898 		if( NULL != (setting = getSetting(m_serverSettingsIDs[i])) ) {
899 			LOG(3) INFO( "\"%s\": -> present\n", m_serverSettingsIDs[i] );
900 			// write a 1 to signalize, that a setting follows
901 			Serialize<Uint8>::serialize( (Uint8)1, bufferPointer );
902 			// serialize the setting
903 			setting->serialize( bufferPointer );
904 		} else {
905 			LOG(3) INFO( "\"%s\": -> absent\n", m_serverSettingsIDs[i] );
906 		}
907 	}
908 	// terminate the serialization with a 0
909 	Serialize<Uint8>::serialize( (Uint8)0, bufferPointer );
910 }
911 
912 /**********************************************************/
913 
914 void WopSettings::
deserializeServerSettings(Uint8 * & bufferPointer)915 deserializeServerSettings( Uint8*& bufferPointer )
916 {
917 	const char fn[]  = "WopSettings::deserializeServerSettings";
918 	Setting* setting = NULL;
919 	Uint8    flag    = 0;
920 
921 	LOG(2) INFO( "%s\n", fn );
922 
923 	Serialize<Uint8>::deserialize( bufferPointer, flag );
924 	while( flag ) {
925 		// create new setting object, if necessary
926 		ASSERT( !setting && NULL != (setting = new Setting(m_verbose)),
927 		        "%s: could not create new Setting object\n", fn );
928 		// deserialize the setting
929 		ASSERT( setting->deserialize(bufferPointer, m_basicSettings),
930 		        "%s: server setting \"%s\" could not be deserialized "
931 		        "properly\n", fn, setting->getIDString() );
932 		// integrate the deserialized setting
933 		// Return value "false" means that the local setting
934 		// object has not been used and inserted in the list
935 		// of settings and can be reused
936 		if( integrateSetting(setting, true, NULL) ) {
937 			setting = NULL;
938 		}
939 		Serialize<Uint8>::deserialize( bufferPointer, flag );
940 	}
941 
942 	// if the last auxiliary setting has not been integrated
943 	// in the list of settings, delete it
944 	delete setting;
945 }
946 
947 /**********************************************************/
948 
serialize(Uint8 * & bufferPointer) const949 void WopSettings::serialize( Uint8*& bufferPointer ) const
950 {
951 	ASSERT( false, "WopSettings::serialize not implemented.\n"
952 	        "       Did you want to call WopSettings::"
953 	        "serializeServerSettings instead?\n" );
954 }
955 
956 /**********************************************************/
957 
deserialize(Uint8 * & bufferPointer)958 void WopSettings::deserialize( Uint8*& bufferPointer )
959 {
960 	ASSERT( false, "WopSettings::deserialize not implemented.\n"
961 	        "       Did you want to call WopSettings::"
962 	        "deserializeServerSettings instead?\n" );
963 }
964 
965 /**********************************************************/
966 
getSerializeBufferSize() const967 Uint32 WopSettings::getSerializeBufferSize() const
968 {
969 	Uint32 size = sizeof(Uint8);
970 	const Setting* setting = NULL;
971 
972 	for( int i = 0; m_serverSettingsIDs[i]; i++ ) {
973 		if( NULL != (setting = getSetting(m_serverSettingsIDs[i])) ) {
974 			size += setting->getSerializeBufferSize();
975 			size += sizeof(Uint8);
976 		}
977 	}
978 
979 	return size;
980 }
981 
982 /**********************************************************/
983 
enforcePresenceOfServerSettings()984 bool WopSettings::enforcePresenceOfServerSettings()
985 {
986 	bool result = true;
987 
988 	if( !getSetting("gamemode") ) {
989 		result = result && setGameMode( getGameMode() ); }
990 	if( !getSetting("width") ) {
991 		result = result && setWidth( getWidth() ); }
992 	if( !getSetting("height") ) {
993 		result = result && setHeight( getHeight() ); }
994 	if( !getSetting("nballs") ) {
995 		result = result && setNumBalls( getNumBalls() ); }
996 	if( !getSetting("ngoals") ) {
997 		result = result && setNumGoals( getNumGoals() ); }
998 
999 	return result;
1000 }
1001 
1002 /**********************************************************
1003  * read settings form command line AND configuration file
1004  **********************************************************/
1005 
readSettings(const char ** const CommandLine,const int nargs)1006 bool WopSettings::readSettings( const char** const CommandLine,
1007                                 const int          nargs )
1008 {
1009 	const char fn[]   = "WopSettings::readSettings";
1010 	bool pickySuccess = true;
1011 
1012 	// read command line (SettingDataBase::readSettings)
1013 	LOG(1) INFO( "%s: reading command line options\n", fn );
1014 	    // read settings from command line
1015 	if( false == SettingDataBase::readSettings(
1016 	                 m_basicSettings,    // class internal setting definitions
1017 	                 CommandLine, nargs, // command line and # argumentes
1018 	                 true,               // overwrite == true
1019 	                 false,              // expectDashesInCommandLine == false
1020 	                 &pickySuccess) ||   // get more details about the success
1021 	    // stop also, if there happened only small errors
1022 	    false == pickySuccess )
1023 	{
1024 		return false;
1025 	}
1026 
1027 	String configfile;
1028 	int    rank = 0;
1029 	// read the configuration files in the ranking order
1030 	while( getConfigFilePath(configfile, rank) ) {
1031 		LOG(1) INFO( "%s: checking presence of \"%s\"\n",
1032 		             fn, configfile.getString() );
1033 		// check, whether the configuration file is available
1034 		FILE* configFile = fopen( (char*)configfile, "r" );
1035 		if( configFile == NULL ) {
1036 			// If the file, that was specified in the command
1037 			// line cannot be opened, abort.
1038 			if( !CHECK( rank > 0, "%s: cannot open \"%s\"\n",
1039 			            fn, configfile.getString() ) )  return false;
1040 			// otherwise only print out a warning
1041 			LOG(1) INFO( "%s: \"%s\" not available\n",
1042 			             fn, configfile.getString() );
1043 			continue;
1044 		}
1045 		fclose( configFile );
1046 		// now we are sure that we can open the configuration file
1047 		LOG(1) INFO( "%s: reading options in \"%s\"\n",
1048 		             fn, configfile.getString() );
1049 		// read configuration file, DO NOT overwrite settings, since
1050 		// the command line has got a higher priority ( 3rd param == false)
1051 		if( ! SettingDataBase::
1052 		      readSettings(m_basicSettings, configfile,
1053 		                   false, &pickySuccess) ||
1054 	        // stop also, if there happened only small errors
1055 		    ! pickySuccess )
1056 		{
1057 			return false;
1058 		}
1059 	}
1060 
1061 	// check, if every mandatory setting is present
1062 	if( false == finalCheck(m_basicSettings) )  return false;
1063 
1064 	// check if data directory is present by opening the theme path
1065 	String themesConfPath( getData() );
1066 	themesConfPath += '/';
1067 	themesConfPath += THEME_CONFIG_PATH;
1068 	themesConfPath += '/';
1069 	themesConfPath += THEME_CONFIG;
1070 	FILE* themesConfFile = fopen( themesConfPath, "r" );
1071 	ASSERT ( themesConfFile,
1072 	         "Could not open data directory (%s).\n"
1073 	         "Make sure that you:\n"
1074 	         " - downloaded the data package\n"
1075 	         " - set the \"data\" option in the command line "
1076 	         "or the configuration file\n", getData() );
1077 	fclose( themesConfFile );
1078 
1079 	// remove pure server settings from a client
1080 // 	if( getMode() == CLIENT ) {
1081 // 		for( int i = 0; m_serverSettingsIDs[i]; i++ ) {
1082 // 			Setting *setting = getSetting( m_serverSettingsIDs[i] );
1083 // 			if( setting ) {
1084 // 				LOG(1) INFO( "%s: setting \"%s\" ignored on client\n",
1085 // 				             fn, m_serverSettingsIDs[i] );
1086 // 				delSetting( m_serverSettingsIDs[i] );
1087 // 			}
1088 // 		}
1089 // 	}
1090 
1091 	// check the validity of the theme right here
1092 	// In fact it is checked later per default in World::createFromTheme,
1093 	// but after a lot of output. To check it right at the program start
1094 	// after reading the settings is more user friendly.
1095 	if( getTheme() ) {
1096 		if( ! CHECK(isAvailableTheme(getTheme()),
1097 		            "%s \"%s\" is not a valid theme name.\n"
1098 		            " =======================\n"
1099 		            " Available Themes:\n",
1100 		            fn, getTheme()) ) {
1101 			// if theme name is not valid, print list of available themes
1102 			printAvailableThemes( std::cerr,
1103 			                      "   \033[0;1m- ",
1104 			                      "\033[0m\n"  );
1105 			std::cout <<" ======================="<<std::endl;
1106 			return false;
1107 		}
1108 	}
1109 
1110 	return pickySuccess;
1111 }
1112 
1113 /**********************************************************/
1114 
getConfigFilePath(String & path,int & rank)1115 bool WopSettings::getConfigFilePath( String& path, int &rank )
1116 {
1117 	DBG(1) {
1118 		int maxrank = 0;
1119 		while( m_configPaths[maxrank++] );
1120 		ASSERT( 0 <= rank && rank <= maxrank,
1121 		        "WopSettings::getConfigFilePath: parameter rank is "
1122 		        "out of valid range [0,%d]. This is a code bug.\n",
1123 		        maxrank );
1124 	}
1125 
1126 	// rank 0 means "check the command line"
1127 	if( rank == 0 ) {
1128 		Setting *setting = getSetting( "config" );
1129 		// if the command line contains a path to the config file
1130 		if( setting ) {
1131 			// take this path and expand it
1132 			path = setting->getString();
1133 			rank++;
1134 			return expandPath( path );
1135 		// otherwise get the first path in the hierarchy
1136 		} else {
1137 			return getConfigFilePath( path, ++rank );
1138 		}
1139 	// otherwise take the next path in the hierarchy
1140 	} else {
1141 		if( m_configPaths[rank-1] == NULL ) return false;
1142 		path = m_configPaths[(rank++)-1];
1143 	}
1144 
1145 	return expandPath( path );
1146 }
1147 
1148 /**********************************************************/
1149 
expandPath(String & path) const1150 bool WopSettings::expandPath( String& path ) const
1151 {
1152 	const char fn[] = "WopSettings::expandPath:";
1153 
1154 	// check, if there is something to expand: a path must
1155 	// contain at least '~' and a following character to be
1156 	// sensibly expanded
1157 	if( path.getLength() < 2 || path[0] != '~' )  return true;
1158 
1159 #ifdef  NO_HOME_PATH_EXPANSION
1160 
1161 	return CHECK( false,
1162 	              "%s path expansion is disabled. Cannot expand "
1163 	              "path \"%s\"\n", fn, path.getString() );
1164 
1165 #else
1166 
1167 	// '~/' means "home of current user
1168 	String expansion;
1169 	int    iNull = 1;
1170 	if( path[1] == '/' ) {
1171 		expansion = getenv( "HOME" );
1172 		if( !CHECK( expansion.getLength(),
1173 		            "%s could not get content of environment "
1174 		            "variable \"HOME\"", fn ) ) {
1175 			return false;
1176 		}
1177 	// '~' is not followed by a '/', so we need the full path
1178 	// of a different user's home directory
1179 	} else {
1180 		// temporarily crop the user's name
1181 		while( path[iNull] != '/' && path[iNull] != 0 )  iNull++;
1182 		// remember, whether you have cropped the string
1183 		const bool cropped = (path[iNull] == '/');
1184 		path[iNull] = 0;
1185 		// get the user's full home path from /etc/pwd
1186 		struct passwd *pwdentry = getpwnam( path.getString()+1 );
1187 		if( !CHECK( pwdentry != NULL,
1188 		            "%s could not read passwd entry of user \"%s\"",
1189 		            fn, path.getString()+1 ) ) {
1190 			// restore path
1191 			if( cropped ) path[iNull] = '/';
1192 			return false;
1193 		}
1194 		expansion = pwdentry->pw_dir;
1195 		if( !CHECK( expansion.getLength(),
1196 		            "%s could not expand home path of user \"%s\"",
1197 		            fn, path.getString()+1 ) ) {
1198 			// restore path
1199 			if( cropped ) path[iNull] = '/';
1200 			return false;
1201 		}
1202 		if( cropped ) path[iNull] = '/';
1203 	}
1204 
1205 	// concatenate expansion and string
1206 	String extention( path.getString() + iNull );
1207 	path  = expansion;
1208 	path += extention;
1209 
1210 #endif
1211 
1212 	return true;
1213 }
1214 
1215 /**********************************************************/
1216 
writeConfigFile(String & path)1217 bool WopSettings::writeConfigFile( String& path )
1218 {
1219 	const char fn[] = "WopSettings::writeConfigFile";
1220 
1221 	// get default path, if "path" is empty
1222 	if( !path.getLength() ) {
1223 		// if config file is given write to that location else try to
1224 		// write in the current directory
1225 		if ( !WopSettings::getInstance()->getSetting( "config" ) ) {
1226 			int rank = 0;
1227 			WopSettings::getInstance()->getConfigFilePath( path, rank );
1228 		}
1229 		else {
1230 			path = getenv( "HOME" );
1231 			if( path.getLength() ) {
1232 				path += '/';
1233 				path += m_DefaultConfig;
1234 			}
1235 			else
1236 				path = "woprc.txt";
1237 		}
1238 	}
1239 
1240 	INFO( "WopSettings::writeConfigFile: writing configuration "
1241 	      "file to %s\n",
1242 	       path.getLength() ? path.getString() : "woprc.txt" );
1243 
1244 	// open file
1245 	FILE *file = fopen( path, "wt" );
1246 	if( file == NULL ) {
1247 		cerr << FUNCTION_ERROR(fn)
1248 		     << "cannot open \"" << path << "\"\n";
1249 		return false;
1250 	}
1251 
1252 	// write file
1253 	fprintf( file,
1254 	         "## This is a skeleton file for the configuration of\n"
1255 	         "## \"Woerms of Prey\", www.wormsofprey.org\n"
1256 	         "##\n"
1257 	         "## - All settings, used here, can also be specified\n"
1258 	         "##   using the command line. For example\n"
1259 	         "##   \"wop mode=client view=window\" (no spaces between\n"
1260 	         "##   the setting's name and its values)\n"
1261 	         "##   Command line settings OVERWRITE settings read from\n"
1262 	         "##   configuration files.\n"
1263 	         "## - For a quick overview of the possible settings\n"
1264 	         "##   invoke \"wop -help\"\n"
1265 	         "## - On UNIX based systems the shortcuts using \"~\"\n"
1266 	         "##   are expanded correctly.\n"
1267 	         "## - If you want to use the default value of a setting,\n"
1268 	         "##   you can leave it undefined. The default is taken\n"
1269 	         "##   automatically.\n"
1270 	         "## - On the command line you can specify the path of\n"
1271 	         "##   this configuration file using the setting \"config\"\n"
1272 	         "\n" );
1273 	fprintf( file,
1274 	         "## program mode [server,client,replay]\n"
1275 	         "##  - required\n"
1276 	         "mode = client\n"
1277 	         "\n" );
1278 	fprintf( file,
1279 	         "## game mode [deathmatch,teamplay]\n"
1280 	         "##  - optional, default \"deathmatch\"\n"
1281 	         "##  - has effect on \"server\" only\n"
1282 	         "gamemode = %s\n"
1283 	         "\n",
1284 	         m_gamemode_StringMap[getGameMode()].m_String );
1285 	fprintf( file,
1286 	         "## path of the data directory\n"
1287 	         "##  - optional, default \"./data\"\n"
1288 	         "data = %s\n"
1289 	         "\n",
1290 	         getData() );
1291 	fprintf( file,
1292 	         "## server name or IP address\n"
1293 	         "##  - optional, default \"localhost\"\n"
1294 	         "##  - can also be chosen in the menu\n"
1295 	         "# server = localhost\n"
1296 	         "\n" );
1297 	fprintf( file,
1298 	         "## width of the world map\n"
1299 	         "##  - optional, default 1000\n"
1300 	         "##  - has effect on \"server\" only\n"
1301 	         "##  - a selected theme may overwrite \"width\"\n"
1302 	         "width = %d\n"
1303 	         "\n",
1304 	         getWidth() );
1305 	fprintf( file,
1306 	         "## height of the world map\n"
1307 	         "##  - optional, default 1000\n"
1308 	         "##  - has effect on server only\n"
1309 	         "##  - a selected theme may overwrite \"height\"\n"
1310 	         "height = %d\n"
1311 	         "\n",
1312 	         getHeight() );
1313 	fprintf( file,
1314 	         "## theme of the world's map\n"
1315 	         "## [standard, medium, sparse1, fossil, circle,\n"
1316 	         "##  randomcircles]\n"
1317 	         "##  - optional, default \"standard\"\n"
1318 	         "##  - has effect on \"server\" only\n"
1319 	         "##  - for a list of available themes have a look\n"
1320 	         "##    in the configuration file\n"
1321 	         "##    <data directory>/images/themes.conf\n"
1322 	         "##    [standard, medium,\n"
1323 	         "##     sparse1, fossil,\n"
1324 	         "##     circle, sky, randomcircles]\n"
1325 	         "theme = %s\n"
1326 	         "\n",
1327 	         getTheme() );
1328 	fprintf( file,
1329 	         "## view mode [window,fullscreen]\n"
1330 	         "##  - optional, default \"window\"\n"
1331 	         "##  - has effect on \"client\" and \"replay\" only\n"
1332 	         "view = %s\n"
1333 	         "\n",
1334 	         m_view_StringMap[getView()].m_String );
1335 	fprintf( file,
1336 	         "## quiet [yes,no]\n"
1337 	         "##  - default on \"client\" is \"no\"\n"
1338 	         "##  - default on \"server\" is \"yes\"\n"
1339 	         "##  - switches off/on sound\n"
1340 	         "quiet = %s\n"
1341 	         "\n",
1342 	         getQuiet() ? "yes" : "no" );
1343 	fprintf( file,
1344 	         "## player name\n"
1345 	         "##  - has effect on \"client\" only\n"
1346 	         "##  - default \"The Dude\"\n"
1347 	         "##  - can also be specified in option menu\n"
1348 	         "name = \"%s\"\n"
1349 	         "\n",
1350 	         getName() );
1351 	fprintf( file,
1352 	         "## avatar color\n"
1353 	         "##  - default \"255,255,255\"\n"
1354 	         "##  - has effect on \"client\" only\n"
1355 	         "##  - specifies the RED,GREEN,BLUE contributions\n"
1356 	         "##    to the player's color (three comma separated\n"
1357 	         "##    values from 0 to 255\n"
1358 	         "##  - can also be specified in option menu\n"
1359 	         "color = %d,%d,%d\n"
1360 	         "\n",
1361 	         TPLCOL_RED( getPlayerColor() ),
1362 	         TPLCOL_GREEN( getPlayerColor() ),
1363 	         TPLCOL_BLUE( getPlayerColor() ) );
1364 	fprintf( file,
1365 	         "## the team number [0,...,20]\n"
1366 	         "##  - default 0\n"
1367 	         "##  - has effect on \"client\" only, and only, if the\n"
1368 	         "##    gamemode is \"teamplay\"\n"
1369 	         "##  - can also be specified in option menu\n"
1370 	         "team = %d\n"
1371 	         "\n",
1372 	         getTeamID() );
1373 	fprintf( file,
1374 	         "## keyboard layout [us,de]\n"
1375 	         "##  - default \"us\"\n"
1376 	         "##  - this setting generally affects, whether the rope\n"
1377 	         "##    is triggered by \"z\" (us) or by \"y\" (de)\n"
1378 	         "keyboard = %s\n"
1379 	         "\n",
1380 	         getKeyboard() );
1381 	fprintf( file,
1382 	         "## number of balls in the game\n"
1383 	         "##  - default 2\n"
1384 	         "##  - has effect on \"server\" only\n"
1385 	         "nballs = %d\n"
1386 	         "\n",
1387 	         getNumBalls() );
1388 	fprintf( file,
1389 	         "## number of goals in the game\n"
1390 	         "##  - default 2\n"
1391 	         "##  - has effect on \"server\" only\n"
1392 	         "ngoals = %d\n"
1393 	         "\n",
1394 	         getNumGoals() );
1395 	fprintf( file,
1396 	         "## probability for creating skwoerm zones [0.0,1.0]\n"
1397 	         "##  - default 0.01\n"
1398 	         "##  - has effect on \"server\" only\n"
1399 	         "szprob = %f\n"
1400 	         "\n",
1401 	         getSZProb() );
1402 	fprintf( file,
1403 	         "## debug [yes,no]\n"
1404 	         "##  - default: no\n"
1405 	         "##  - warning: enabling turns off the SDL parachute!\n"
1406 	         "debug = %s\n"
1407 	         "\n",
1408 	         getDebug() ? "yes" : "no" );
1409 	fprintf( file,
1410 	         "## juke box directory\n"
1411 	         "##  - no default\n"
1412 	         "##  - this setting specifies a directory where WoP\n"
1413 	         "##    will search files of type \".wav\", \".ogg\", \".mp3\", \".mid\", \".mod\".\n"
1414 	         "##    These files are played in random order as\n"
1415 	         "##    background tunes during the game.\n"
1416 	         "##  - CONTROL in the game:\n"
1417 	         "##     F5 : volume -\n"
1418 	         "##     F6 : volume +\n"
1419 	         "##     F7 : toggle music on/off\n"
1420 	         "# musicdir = \"~/mymusic/woptracks/smooth tracks\"\n"
1421 	         "## on the command line you would have to write\n"
1422 	         "## musicdir=\"~/mymusic/woptracks/smooth\\ tracks\"\n" );
1423 	String musicDir;
1424 	if ( getMusicDir( musicDir ) )
1425 		fprintf( file, "musicdir = %s\n", musicDir.getString() );
1426 	fprintf( file,
1427 	         "\n"
1428 	         "## key configuration\n"
1429 	         "## - can also be changed in option menu\n"
1430 	         "key_left                   = \"%s\"\n"
1431 	         "key_right                  = \"%s\"\n"
1432 	         "key_up                     = \"%s\"\n"
1433 	         "key_down                   = \"%s\"\n"
1434 	         "key_shoot                  = \"%s\"\n"
1435 	         "key_jump                   = \"%s\"\n"
1436 	         "key_dig                    = \"%s\"\n"
1437 	         "key_jet                    = \"%s\"\n"
1438 	         "key_rope_on                = \"%s\"\n"
1439 	         "key_rope_off               = \"%s\"\n"
1440 	         "key_rope_in                = \"%s\"\n"
1441 	         "key_rope_out               = \"%s\"\n"
1442 	         "key_rope_rel               = \"%s\"\n"
1443 	         "key_screenshot             = \"%s\"\n"
1444 	         "key_debug                  = \"%s\"\n"
1445 	         "key_dec_vol                = \"%s\"\n"
1446 	         "key_inc_vol                = \"%s\"\n"
1447 	         "key_toggle_jukebox         = \"%s\"\n"
1448 	         "key_weapon_1               = \"%s\"\n"
1449 	         "key_weapon_2               = \"%s\"\n"
1450 	         "key_weapon_3               = \"%s\"\n"
1451 	         "key_weapon_4               = \"%s\"\n"
1452 	         "key_weapon_5               = \"%s\"\n"
1453 	         "key_weapon_6               = \"%s\"\n"
1454 	         "key_weapon_7               = \"%s\"\n"
1455 	         "key_weapon_8               = \"%s\"\n"
1456 	         "key_weapon_9               = \"%s\"\n"
1457 	         "key_weapon_0               = \"%s\"\n"
1458 	         "key_spawn                  = \"%s\"\n"
1459 	         "key_next_weapon            = \"%s\"\n"
1460 	         "key_prev_weapon            = \"%s\"\n"
1461 	         "key_insert_replay_bookmark = \"%s\"\n"
1462 	         "key_change_focus           = \"%s\"\n"
1463 	         "key_toggle_fullscreen      = \"%s\"\n"
1464 	         "key_show_fps               = \"%s\"\n"
1465 	         "key_limit_fps              = \"%s\"\n",
1466 	         getSetting( "key_left" )->getString(),
1467 	         getSetting( "key_right" )->getString(),
1468 	         getSetting( "key_up" )->getString(),
1469 	         getSetting( "key_down" )->getString(),
1470 	         getSetting( "key_shoot" )->getString(),
1471 	         getSetting( "key_jump" )->getString(),
1472 	         getSetting( "key_dig" )->getString(),
1473 	         getSetting( "key_jet" )->getString(),
1474 	         getSetting( "key_rope_on" )->getString(),
1475 	         getSetting( "key_rope_off" )->getString(),
1476 	         getSetting( "key_rope_in" )->getString(),
1477 	         getSetting( "key_rope_out" )->getString(),
1478 	         getSetting( "key_rope_rel" )->getString(),
1479 	         getSetting( "key_screenshot" )->getString(),
1480 	         getSetting( "key_debug" )->getString(),
1481 	         getSetting( "key_dec_vol" )->getString(),
1482 	         getSetting( "key_inc_vol" )->getString(),
1483 	         getSetting( "key_toggle_jukebox" )->getString(),
1484 	         getSetting( "key_weapon_1" )->getString(),
1485 	         getSetting( "key_weapon_2" )->getString(),
1486 	         getSetting( "key_weapon_3" )->getString(),
1487 	         getSetting( "key_weapon_4" )->getString(),
1488 	         getSetting( "key_weapon_5" )->getString(),
1489 	         getSetting( "key_weapon_6" )->getString(),
1490 	         getSetting( "key_weapon_7" )->getString(),
1491 	         getSetting( "key_weapon_8" )->getString(),
1492 	         getSetting( "key_weapon_9" )->getString(),
1493 	         getSetting( "key_weapon_0" )->getString(),
1494 	         getSetting( "key_spawn" )->getString(),
1495 	         getSetting( "key_next_weapon" )->getString(),
1496 	         getSetting( "key_prev_weapon" )->getString(),
1497 	         getSetting( "key_insert_replay_bookmark" )->getString(),
1498 	         getSetting( "key_change_focus" )->getString(),
1499 	         getSetting( "key_toggle_fullscreen" )->getString(),
1500 	         getSetting( "key_show_fps" )->getString(),
1501 	         getSetting( "key_limit_fps" )->getString() );
1502 
1503 	fclose( file );
1504 
1505 	return true;
1506 }
1507 
1508 /**********************************************************/
1509 
printHelp(const int lowerHelpLevel,const int upperHelpLevel) const1510 void WopSettings::printHelp( const int lowerHelpLevel,
1511                              const int upperHelpLevel ) const
1512 {
1513 	SettingDataBase::printHelp( m_basicSettings,
1514 	                            lowerHelpLevel,
1515 	                            upperHelpLevel );
1516 }
1517 
1518 /**********************************************************/
1519