1 /*
2  * Tux Racer
3  * Copyright (C) 1999-2001 Jasmin F. Patry
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19 
20 /* This file is complex.  However, the ultimate purpose is to make
21    adding new configuration parameters easy.  Here's what you need to
22    do to add a new parameter:
23 
24    1. Choose a name and type for the parameter.  By convention,
25    parameters have lower case names and words_are_separated_like_this.
26    Possible types are bool, int, char, and string.  (Nothing is ruling
27    out floating point types; I just haven't needed them.)  As an
28    example in the subsequent steps, suppose we wish to add a parameter
29    foo_bar of type string.
30 
31    2. Add a field for the parameter to the 'params' struct defined
32    below.  In our example, we would add the line
33        struct param foo_bar;
34    to the definition of struct params.
35 
36    Note that the order of the entries in this struct determines the
37    order that the parameters will appear in the configuration file.
38 
39    3. Initialize and assign a default value to the parameter in the
40    init_game_configuration() function.  The INIT_PARAM_<TYPE> macros
41    will do this for you.  In our example, we would add the line
42        INIT_PARAM_STRING( foo_bar, "baz" )
43    to assign a default value of "baz" to the parameter foo_bar.
44 
45    4. Create the getparam/setparam functions for the parameter.  This
46    is done using the FN_PARAM_<TYPE> macros.  In our example, we would
47    add the line
48        FN_PARAM_STRING( foo_bar )
49    somewhere in the top-level scope of this file (to keep things neat
50    group it with the other definitions).  The will create
51    getparam_foo_bar() and setparam_foo_bar() functions that can be
52    used to query the value of the parameter.
53 
54    5. Create the prototypes for the getparam/setparam functions.  This
55    is done in game_config.h using the PROTO_PARAM_<TYPE> macros.  In
56    our example, we would add the line
57        PROTO_PARAM_STRING( foo_bar );
58    to game_config.h.
59 
60    6. You're done!  */
61 
62 #include "tuxracer.h"
63 
64 #if defined( WIN32 )
65 #  define OLD_CONFIG_FILE "tuxracer.cfg"
66 #else
67 #  define OLD_CONFIG_FILE ".tuxracer"
68 #endif /* defined( WIN32 ) */
69 
70 #if defined( WIN32 )
71 #  define CONFIG_DIR "config"
72 #  define CONFIG_FILE "options.txt"
73 #else
74 #  define CONFIG_DIR ".tuxracer"
75 #  define CONFIG_FILE "options"
76 #endif /* defined( WIN32 ) */
77 
78 #ifndef DATA_DIR
79 #  if defined( WIN32 )
80 #    define DATA_DIR "."
81 #  else
82 #    define DATA_DIR "/usr/local/share/tuxracer"
83 #  endif /* defined( WIN32 ) */
84 #endif
85 
86 /* Identifies the parameter type */
87 typedef enum {
88     PARAM_STRING,
89     PARAM_CHAR,
90     PARAM_INT,
91     PARAM_BOOL
92 } param_type;
93 
94 /* Stores the value for all types */
95 typedef union {
96     char* string_val;
97     char  char_val;
98     int   int_val;
99     bool_t bool_val;
100 } param_val;
101 
102 /* Stores state for each parameter */
103 struct param {
104     int loaded;
105     char *name;
106     param_type type;
107     param_val val;
108     param_val deflt;
109     char *comment;
110 };
111 
112 /*
113  * These macros are used to initialize parameter values
114  */
115 
116 #define INIT_PARAM( nam, val, typename, commnt ) \
117    Params.nam.loaded = False; \
118    Params.nam.name = #nam; \
119    Params.nam.deflt.typename ## _val  = val; \
120    Params.nam.comment = commnt;
121 
122 #define INIT_PARAM_STRING( nam, val, commnt ) \
123    INIT_PARAM( nam, val, string, commnt ); \
124    Params.nam.type = PARAM_STRING;
125 
126 #define INIT_PARAM_CHAR( nam, val, commnt ) \
127    INIT_PARAM( nam, val, char, commnt ); \
128    Params.nam.type = PARAM_CHAR;
129 
130 #define INIT_PARAM_INT( nam, val, commnt ) \
131    INIT_PARAM( nam, val, int, commnt ); \
132    Params.nam.type = PARAM_INT;
133 
134 #define INIT_PARAM_BOOL( nam, val, commnt ) \
135    INIT_PARAM( nam, val, bool, commnt ); \
136    Params.nam.type = PARAM_BOOL;
137 
138 
139 /*
140  * These functions are used to get and set parameter values
141  */
142 
fetch_param_string(struct param * p)143 void fetch_param_string( struct param *p )
144 {
145     char *val;
146 
147     check_assertion( p->type == PARAM_STRING,
148 		     "configuration parameter type mismatch" );
149 
150     val = Tcl_GetVar( g_game.tcl_interp, p->name, TCL_GLOBAL_ONLY );
151     if ( val == NULL ) {
152 	p->val.string_val = string_copy( p->deflt.string_val );
153     } else {
154 	p->val.string_val = string_copy( val );
155     }
156     p->loaded = True;
157 
158 }
159 
set_param_string(struct param * p,char * new_val)160 void set_param_string( struct param *p, char *new_val )
161 {
162     char *ret;
163 
164     check_assertion( p->type == PARAM_STRING,
165 		     "configuration parameter type mismatch" );
166 
167     if ( p->loaded ) {
168 	free( p->val.string_val );
169     }
170     ret = Tcl_SetVar( g_game.tcl_interp, p->name, new_val, TCL_GLOBAL_ONLY );
171     if ( ret == NULL ) {
172 	p->val.string_val = string_copy( p->deflt.string_val );
173     } else {
174 	p->val.string_val = string_copy( new_val );
175     }
176     p->loaded = True;
177 
178 }
179 
fetch_param_char(struct param * p)180 void fetch_param_char( struct param *p )
181 {
182     char *str_val;
183 
184     check_assertion( p->type == PARAM_CHAR,
185 		     "configuration parameter type mismatch" );
186 
187     str_val = Tcl_GetVar( g_game.tcl_interp, p->name, TCL_GLOBAL_ONLY );
188 
189     if ( str_val == NULL || str_val[0] == '\0' ) {
190 	p->val.char_val = p->deflt.char_val;
191     } else {
192 	p->val.char_val = str_val[0];
193     }
194     p->loaded = True;
195 }
196 
set_param_char(struct param * p,char new_val)197 void set_param_char( struct param *p, char new_val )
198 {
199     char buff[2];
200     char *ret;
201 
202     check_assertion( p->type == PARAM_CHAR,
203 		     "configuration parameter type mismatch" );
204 
205     buff[0] = new_val;
206     buff[1] = '\0';
207 
208     ret = Tcl_SetVar( g_game.tcl_interp, p->name, buff, TCL_GLOBAL_ONLY );
209     if ( ret == NULL ) {
210 	p->val.char_val = p->deflt.char_val;
211     } else {
212 	p->val.char_val = new_val;
213     }
214     p->loaded = True;
215 
216 }
217 
fetch_param_int(struct param * p)218 void fetch_param_int( struct param *p )
219 {
220     char *str_val;
221     int val;
222 
223     check_assertion( p->type == PARAM_INT,
224 		     "configuration parameter type mismatch" );
225 
226     str_val = Tcl_GetVar( g_game.tcl_interp, p->name, TCL_GLOBAL_ONLY );
227 
228     if ( str_val == NULL
229 	 || Tcl_GetInt( g_game.tcl_interp, str_val, &val) == TCL_ERROR  )
230     {
231 	p->val.int_val = p->deflt.int_val;
232     } else {
233 	p->val.int_val = val;
234     }
235     p->loaded = True;
236 }
237 
set_param_int(struct param * p,int new_val)238 void set_param_int( struct param *p, int new_val )
239 {
240     char buff[30];
241     char *ret;
242 
243     check_assertion( p->type == PARAM_INT,
244 		     "configuration parameter type mismatch" );
245 
246     sprintf( buff, "%d", new_val );
247 
248     ret = Tcl_SetVar( g_game.tcl_interp, p->name, buff, TCL_GLOBAL_ONLY );
249     if ( ret == NULL ) {
250 	p->val.int_val = p->deflt.int_val;
251     } else {
252 	p->val.int_val = new_val;
253     }
254     p->loaded = True;
255 
256 }
257 
fetch_param_bool(struct param * p)258 void fetch_param_bool( struct param *p )
259 {
260     char *str_val;
261     int val;
262     bool_t no_val = False;
263 
264     check_assertion( p->type == PARAM_BOOL,
265 		     "configuration parameter type mismatch" );
266 
267     str_val = Tcl_GetVar( g_game.tcl_interp, p->name, TCL_GLOBAL_ONLY );
268 
269     if ( str_val == NULL ) {
270 	no_val = True;
271     } else if ( string_cmp_no_case( str_val, "false" ) == 0 ) {
272 	p->val.bool_val = False;
273     } else if ( string_cmp_no_case( str_val, "true" ) == 0 ) {
274 	p->val.bool_val = True;
275     } else if ( Tcl_GetInt( g_game.tcl_interp, str_val, &val) == TCL_ERROR ) {
276 	no_val = True;
277     } else {
278 	p->val.bool_val = (val == 0) ? False : True ;
279     }
280 
281     if ( no_val ) {
282 	p->val.bool_val = p->deflt.bool_val;
283     }
284 
285     p->loaded = True;
286 }
287 
set_param_bool(struct param * p,bool_t new_val)288 void set_param_bool( struct param *p, bool_t new_val )
289 {
290     char buff[2];
291     char *ret;
292 
293     check_assertion( p->type == PARAM_BOOL,
294 		     "configuration parameter type mismatch" );
295 
296     sprintf( buff, "%d", new_val ? 1 : 0 );
297 
298     ret = Tcl_SetVar( g_game.tcl_interp, p->name, buff, TCL_GLOBAL_ONLY );
299     if ( ret == NULL ) {
300 	p->val.bool_val = p->deflt.bool_val;
301     } else {
302 	p->val.bool_val = new_val;
303     }
304     p->loaded = True;
305 }
306 
307 
308 /*
309  * Creates set/get functions for each parameter
310  */
311 #define FN_PARAM( name, typename, type ) \
312     type getparam_ ## name() { \
313         if ( !Params.name.loaded ) { \
314             fetch_param_ ## typename( &( Params.name ) ); \
315         } \
316         return Params.name.val.typename ## _val; \
317     } \
318     void setparam_ ## name( type val) { \
319         set_param_ ## typename( &( Params.name ), val ); }
320 
321 #define FN_PARAM_STRING( name ) \
322     FN_PARAM( name, string, char* )
323 
324 #define FN_PARAM_CHAR( name ) \
325     FN_PARAM( name, char, char )
326 
327 #define FN_PARAM_INT( name ) \
328     FN_PARAM( name, int, int )
329 
330 #define FN_PARAM_BOOL( name ) \
331     FN_PARAM( name, bool, bool_t )
332 
333 
334 /*
335  * Main parameter struct
336  */
337 struct params {
338     struct param data_dir;
339     struct param fullscreen;
340     struct param x_resolution;
341     struct param y_resolution;
342     struct param bpp_mode;
343     struct param capture_mouse;
344     struct param force_window_position;
345     struct param quit_key;
346     struct param turn_left_key;
347     struct param turn_right_key;
348     struct param trick_modifier_key;
349     struct param brake_key;
350     struct param paddle_key;
351     struct param jump_key;
352     struct param reset_key;
353     struct param follow_view_key;
354     struct param behind_view_key;
355     struct param above_view_key;
356     struct param view_mode; /* coresponds to view_mode_t */
357     struct param screenshot_key;
358     struct param pause_key;
359 
360     struct param joystick_paddle_button;
361     struct param joystick_brake_button;
362     struct param joystick_jump_button;
363     struct param joystick_trick_button;
364     struct param joystick_continue_button;
365     struct param joystick_x_axis;
366     struct param joystick_y_axis;
367 
368     struct param no_audio;
369     struct param sound_enabled;
370     struct param music_enabled;
371     struct param sound_volume; /* 0-128 */
372     struct param music_volume; /* 0-128 */
373     struct param audio_freq_mode; /* 0 = 11025,
374 				     1 = 22050,
375 				     2 = 44100 */
376     struct param audio_format_mode; /* 0 = 8 bits,
377 				       1 = 16 bits */
378     struct param audio_stereo;
379     struct param audio_buffer_size;
380 
381     struct param display_fps;
382     struct param course_detail_level;
383     struct param forward_clip_distance;
384     struct param backward_clip_distance;
385     struct param tree_detail_distance;
386     struct param terrain_blending;
387     struct param perfect_terrain_blending;
388     struct param terrain_envmap;
389     struct param disable_fog;
390     struct param draw_tux_shadow;
391     struct param tux_sphere_divisions;
392     struct param tux_shadow_sphere_divisions;
393     struct param draw_particles;
394     struct param track_marks;
395     struct param ui_snow;
396     struct param nice_fog;
397     struct param use_cva;
398     struct param cva_hack;
399     struct param use_sphere_display_list;
400     struct param do_intro_animation;
401     struct param mipmap_type; /* 0 = GL_NEAREST,
402 				 1 = GL_LINEAR,
403 				 2 = GL_NEAREST_MIPMAP_NEAREST,
404 				 3 = GL_LINEAR_MIPMAP_NEAREST,
405 				 4 = GL_NEAREST_MIPMAP_LINEAR,
406 				 5 = GL_LINEAR_MIPMAP_LINEAR
407 			      */
408     struct param ode_solver; /* 0 = Euler,
409 				1 = ODE23,
410 				2 = ODE45
411 			     */
412     struct param fov;
413     struct param debug;
414     struct param warning_level;
415     struct param write_diagnostic_log;
416 };
417 
418 static struct params Params;
419 
420 
421 /*
422  * Initialize parameter data
423  */
424 
init_game_configuration()425 void init_game_configuration()
426 {
427     INIT_PARAM_STRING(
428 	data_dir, DATA_DIR,
429 	"# The location of the Tux Racer data files" );
430 
431     INIT_PARAM_BOOL(
432 	draw_tux_shadow, False,
433 	"# Set this to true to display Tux's shadow.  Note that this is a \n"
434 	"# hack and is quite expensive in terms of framerate.\n"
435 	"# [EXPERT] This looks better if your card has a stencil buffer; \n"
436 	"# if compiling use the --enable-stencil-buffer configure option \n"
437 	"# to enable the use of the stencil buffer" );
438 
439     INIT_PARAM_BOOL(
440 	draw_particles, True,
441 	"# Controls the drawing of snow particles that are kicked up as Tux\n"
442 	"# turns and brakes.  Setting this to false should help improve \n"
443 	"# performance." );
444 
445     INIT_PARAM_INT(
446 	tux_sphere_divisions, 6,
447 	"# [EXPERT] Higher values result in a more finely subdivided mesh \n"
448 	"# for Tux, and vice versa.  If you're experiencing low framerates,\n"
449 	"# try lowering this value." );
450 
451     INIT_PARAM_INT(
452 	tux_shadow_sphere_divisions, 3,
453 	"# [EXPERT] The level of subdivision of Tux's shadow." );
454 
455     INIT_PARAM_BOOL(
456 	nice_fog, True,
457 	"# [EXPERT] If true, then the GL_NICEST hint will be used when\n"
458 	"# rendering fog.  On some cards, setting this to false may improve\n"
459 	"# performance.");
460 
461     INIT_PARAM_BOOL(
462 	use_sphere_display_list, True,
463 	"# [EXPERT]  Mesa 3.1 sometimes renders Tux strangely when display \n"
464 	"# lists are used.  Setting this to false should solve the problem \n"
465 	"# at the cost of a few Hz." );
466 
467     INIT_PARAM_BOOL(
468 	display_fps, False,
469 	"# Set this to true to display the current framerate in Hz." );
470 
471     INIT_PARAM_INT(
472 	x_resolution, 640,
473 	"# The horizontal size of the Tux Racer window" );
474 
475     INIT_PARAM_INT(
476 	y_resolution, 480,
477 	"# The vertical size of the Tux Racer window" );
478 
479     INIT_PARAM_BOOL(
480 	capture_mouse, False,
481 	"# If true, then the mouse will not be able to leave the \n"
482 	"# Tux Racer window.\n"
483 	"# If you lose keyboard focus while running Tux Racer, try setting\n"
484 	"# this to true." );
485 
486     INIT_PARAM_BOOL(
487 	do_intro_animation, True,
488 	"# If false, then the introductory animation sequence will be skipped."
489 	);
490 
491     INIT_PARAM_INT(
492 	mipmap_type, 3,
493 	"# [EXPERT] Allows you to control which type of texture\n"
494 	"# interpolation/mipmapping is used when rendering textures.  The\n"
495 	"# values correspond to the following OpenGL settings:\n"
496 	"#\n"
497         "#  0: GL_NEAREST\n"
498         "#  1: GL_LINEAR\n"
499         "#  2: GL_NEAREST_MIPMAP_NEAREST\n"
500 	"#  3: GL_LINEAR_MIPMAP_NEAREST\n"
501         "#  4: GL_NEAREST_MIPMAP_LINEAR\n"
502         "#  5: GL_LINEAR_MIPMAP_LINEAR\n"
503 	"#\n"
504 	"# On some cards, you may be able to improve performance by\n"
505         "# decreasing this number, at the cost of lower image quality." );
506 
507     INIT_PARAM_BOOL(
508 	fullscreen, True,
509 	"# If true then the game will run in full-screen mode." );
510 
511     INIT_PARAM_INT(
512 	bpp_mode, 0,
513 	"# Controls how many bits per pixel are used in the game.\n"
514 	"# Valid values are:\n"
515 	"#\n"
516 	"#  0: Use current bpp setting of operating system\n"
517 	"#  1: 16 bpp\n"
518 	"#  2: 32 bpp\n"
519 	"# Note that some cards (e.g., Voodoo1, Voodoo2, Voodoo3) only support\n"
520 	"# 16 bits per pixel." );
521 
522     INIT_PARAM_BOOL(
523 	force_window_position, False ,
524 	"# If true, then the Tux Racer window will automatically be\n"
525 	"# placed at (0,0)" );
526 
527     INIT_PARAM_INT(
528 	ode_solver, 1 ,
529 	"# Selects the ODE (ordinary differential equation) solver.  \n"
530 	"# Possible values are:\n"
531 	"#\n"
532 	"#   0: Modified Euler     (fastest but least accurate)\n"
533         "#   1: Runge-Kutta (2,3)\n"
534 	"#   2: Runge-Kutta (4,5)  (slowest but most accurate)" );
535 
536     INIT_PARAM_STRING(
537 	quit_key, "q escape" ,
538 	"# Key binding for quitting a race" );
539     INIT_PARAM_STRING(
540 	turn_left_key, "j left" ,
541 	"# Key binding for turning left" );
542     INIT_PARAM_STRING(
543 	turn_right_key, "l right" ,
544 	"# Key binding for turning right" );
545     INIT_PARAM_STRING(
546 	trick_modifier_key, "d" ,
547 	"# Key binding for doing tricks" );
548     INIT_PARAM_STRING(
549 	brake_key, "k space down" ,
550 	"# Key binding for braking" );
551     INIT_PARAM_STRING(
552 	paddle_key, "i up" ,
553 	"# Key binding for paddling (on the ground) and flapping (in the air)"
554 	);
555     INIT_PARAM_STRING(
556 	follow_view_key, "1" ,
557 	"# Key binding for the \"Follow\" camera mode" );
558     INIT_PARAM_STRING(
559 	behind_view_key, "2" ,
560 	"# Key binding for the \"Behind\" camera mode" );
561     INIT_PARAM_STRING(
562 	above_view_key, "3" ,
563 	"# Key binding for the \"Above\" camera mode" );
564     INIT_PARAM_INT(
565 	view_mode, 1 ,
566 	"# Default view mode. Possible values are\n"
567 	"#\n"
568 	"#   0: Behind\n"
569 	"#   1: Follow\n"
570 	"#   2: Above" );
571     INIT_PARAM_STRING(
572 	screenshot_key, "=" ,
573 	"# Key binding for taking a screenshot" );
574     INIT_PARAM_STRING(
575 	pause_key, "p" ,
576 	"# Key binding for pausing the game" );
577     INIT_PARAM_STRING(
578 	reset_key, "backspace" ,
579 	"# Key binding for resetting the player position" );
580     INIT_PARAM_STRING(
581 	jump_key, "e" ,
582 	"# Key binding for jumping" );
583 
584     INIT_PARAM_INT(
585 	joystick_paddle_button, 0 ,
586 	"# Joystick button for paddling (numbering starts at 0).\n"
587 	"# Set to -1 to disable." );
588 
589     INIT_PARAM_INT(
590 	joystick_brake_button, 2 ,
591 	"# Joystick button for braking (numbering starts at 0).\n"
592 	"# Set to -1 to disable." );
593 
594     INIT_PARAM_INT(
595 	joystick_jump_button, 3 ,
596 	"# Joystick button for jumping (numbering starts at 0)" );
597 
598     INIT_PARAM_INT(
599 	joystick_trick_button, 1 ,
600 	"# Joystick button for doing tricks (numbering starts at 0)" );
601 
602     INIT_PARAM_INT(
603 	joystick_continue_button, 0 ,
604 	"# Joystick button for moving past intro, paused, and \n"
605 	"# game over screens (numbering starts at 0)" );
606 
607     INIT_PARAM_INT(
608 	joystick_x_axis, 0 ,
609 	"# Joystick axis to use for turning (numbering starts at 0)" );
610 
611     INIT_PARAM_INT(
612 	joystick_y_axis, 1 ,
613 	"# Joystick axis to use for paddling/braking (numbering starts at 0)" );
614 
615     INIT_PARAM_INT(
616 	fov, 60 ,
617 	"# [EXPERT] Sets the camera field-of-view" );
618     INIT_PARAM_STRING(
619 	debug, "" ,
620 	"# [EXPERT] Controls the Tux Racer debugging modes" );
621     INIT_PARAM_INT(
622 	warning_level, 100 ,
623 	"# [EXPERT] Controls the Tux Racer warning messages" );
624     INIT_PARAM_INT(
625 	forward_clip_distance, 75 ,
626 	"# Controls how far ahead of the camera the course\n"
627 	"# is rendered.  Larger values mean that more of the course is\n"
628 	"# rendered, resulting in slower performance. Decreasing this \n"
629 	"# value is an effective way to improve framerates." );
630     INIT_PARAM_INT(
631 	backward_clip_distance, 10 ,
632 	"# [EXPERT] Some objects aren't yet clipped to the view frustum, \n"
633 	"# so this value is used to control how far up the course these \n"
634 	"# objects are drawn." );
635     INIT_PARAM_INT(
636 	tree_detail_distance, 20 ,
637 	"# [EXPERT] Controls the distance at which trees are drawn with \n"
638 	"# two rectangles instead of one." );
639     INIT_PARAM_BOOL(
640 	terrain_blending, True ,
641 	"# Controls the blending of the terrain textures.  Setting this\n"
642 	"# to false will help improve performance." );
643     INIT_PARAM_BOOL(
644 	perfect_terrain_blending, False ,
645 	"# [EXPERT] If true, then terrain triangles with three different\n"
646 	"# terrain types at the vertices will be blended correctly\n"
647 	"# (instead of using a faster but imperfect approximation)." );
648     INIT_PARAM_BOOL(
649 	terrain_envmap, True ,
650 	"# If true, then the ice will be drawn with an \"environment map\",\n"
651 	"# which gives the ice a shiny appearance.  Setting this to false\n"
652 	"# will help improve performance." );
653     INIT_PARAM_BOOL(
654 	disable_fog, False ,
655 	"# If true, then fog will be turned off.  Some Linux drivers for the\n"
656 	"# ATI Rage128 seem to have a bug in their fog implementation which\n"
657 	"# makes the screen nearly pure white when racing; if you experience\n"
658 	"# this problem then set this variable to true." );
659     INIT_PARAM_BOOL(
660 	use_cva, True ,
661 	"# [EXPERT] If true, then compiled vertex arrays will be used when\n"
662 	"# drawing the terrain.  Whether or not this helps performance\n"
663 	"# is driver- and card-dependent." );
664     INIT_PARAM_BOOL(
665 	cva_hack, True ,
666 	"# Some card/driver combinations render the terrrain incorrectly\n"
667 	"# when using compiled vertex arrays.  This activates a hack \n"
668 	"# to work around that problem." );
669     INIT_PARAM_INT(
670 	course_detail_level, 75 ,
671 	"# [EXPERT] This controls how accurately the course terrain is \n"
672 	"# rendered. A high value results in greater accuracy at the cost of \n"
673 	"# performance, and vice versa.  This value can be decreased and \n"
674 	"# increased in 10% increments at runtime using the F9 and F10 keys.\n"
675 	"# To better see the effect, activate wireframe mode using the F11 \n"
676 	"# key (this is a toggle)." );
677     INIT_PARAM_BOOL(
678 	no_audio, False ,
679 	"# If True, then audio in the game is completely disabled." );
680     INIT_PARAM_BOOL(
681 	sound_enabled, True ,
682 	"# Use this to turn sound effects on and off." );
683     INIT_PARAM_BOOL(
684 	music_enabled, True ,
685 	"# Use this to turn music on and off." );
686     INIT_PARAM_INT(
687 	sound_volume, 127 ,
688 	"# This controls the sound volume (valid range is 0-127)." );
689     INIT_PARAM_INT(
690 	music_volume, 64 ,
691 	"# This controls the music volume (valid range is 0-127)." );
692     INIT_PARAM_INT(
693 	audio_freq_mode, 1 ,
694 	"# The controls the frequency of the audio.  Valid values are:\n"
695 	"# \n"
696 	"#   0: 11025 Hz\n"
697 	"#   1: 22050 Hz\n"
698 	"#   2: 44100 Hz" );
699     INIT_PARAM_INT(
700 	audio_format_mode, 1 ,
701 	"# This controls the number of bits per sample for the audio.\n"
702 	"# Valid values are:\n"
703 	"#\n"
704 	"#   0: 8 bits\n"
705 	"#   1: 16 bits" );
706     INIT_PARAM_BOOL(
707 	audio_stereo, True ,
708 	"# Audio will be played in stereo of true, and mono if false" );
709     INIT_PARAM_INT(
710 	audio_buffer_size, 2048 ,
711 	"# [EXPERT] Controls the size of the audio buffer.  \n"
712 	"# Increase the buffer size if you experience choppy audio\n"
713 	"# (at the cost of greater audio latency)" );
714     INIT_PARAM_BOOL(
715 	track_marks, True ,
716 	"# If true, then the players will leave track marks in the snow." );
717     INIT_PARAM_BOOL(
718 	ui_snow, True ,
719 	"# If true, then the ui screens will have falling snow." );
720 
721     INIT_PARAM_BOOL(
722 	write_diagnostic_log, False ,
723 	"# If true, then a file called diagnostic_log.txt will be generated\n"
724 	"# which you should attach to any bug reports you make.\n"
725 	"# To generate the file, set this variable to \"true\", and\n"
726 	"# then run the game so that you reproduce the bug, if possible."
727 	);
728 }
729 
730 
731 /*
732  * Create the set/get functions for parameters
733  */
734 
735 FN_PARAM_STRING( data_dir )
FN_PARAM_BOOL(draw_tux_shadow)736 FN_PARAM_BOOL( draw_tux_shadow )
737 FN_PARAM_BOOL( draw_particles )
738 FN_PARAM_INT( tux_sphere_divisions )
739 FN_PARAM_INT( tux_shadow_sphere_divisions )
740 FN_PARAM_BOOL( nice_fog )
741 FN_PARAM_BOOL( use_sphere_display_list )
742 FN_PARAM_BOOL( display_fps )
743 FN_PARAM_INT( x_resolution )
744 FN_PARAM_INT( y_resolution )
745 FN_PARAM_BOOL( capture_mouse )
746 FN_PARAM_BOOL( do_intro_animation )
747 FN_PARAM_INT( mipmap_type )
748 FN_PARAM_BOOL( fullscreen )
749 FN_PARAM_INT( bpp_mode )
750 FN_PARAM_BOOL( force_window_position )
751 FN_PARAM_INT( ode_solver )
752 FN_PARAM_STRING( quit_key )
753 FN_PARAM_STRING( turn_left_key )
754 FN_PARAM_STRING( turn_right_key )
755 FN_PARAM_STRING( trick_modifier_key )
756 FN_PARAM_STRING( brake_key )
757 FN_PARAM_STRING( paddle_key )
758 FN_PARAM_STRING( above_view_key )
759 FN_PARAM_STRING( behind_view_key )
760 FN_PARAM_STRING( follow_view_key )
761 FN_PARAM_INT( view_mode )
762 FN_PARAM_STRING( screenshot_key )
763 FN_PARAM_STRING( pause_key )
764 FN_PARAM_STRING( reset_key )
765 FN_PARAM_STRING( jump_key )
766 FN_PARAM_INT( joystick_jump_button )
767 FN_PARAM_INT( joystick_brake_button )
768 FN_PARAM_INT( joystick_paddle_button )
769 FN_PARAM_INT( joystick_trick_button )
770 FN_PARAM_INT( joystick_continue_button )
771 FN_PARAM_INT( joystick_x_axis )
772 FN_PARAM_INT( joystick_y_axis )
773 FN_PARAM_INT( fov )
774 FN_PARAM_STRING( debug )
775 FN_PARAM_INT( warning_level )
776 FN_PARAM_INT( forward_clip_distance )
777 FN_PARAM_INT( backward_clip_distance )
778 FN_PARAM_INT( tree_detail_distance )
779 FN_PARAM_INT( course_detail_level )
780 FN_PARAM_BOOL( terrain_blending )
781 FN_PARAM_BOOL( perfect_terrain_blending )
782 FN_PARAM_BOOL( terrain_envmap )
783 FN_PARAM_BOOL( disable_fog )
784 FN_PARAM_BOOL( use_cva )
785 FN_PARAM_BOOL( cva_hack )
786 FN_PARAM_BOOL( track_marks )
787 FN_PARAM_BOOL( ui_snow )
788 
789 FN_PARAM_BOOL( no_audio )
790 FN_PARAM_BOOL( sound_enabled )
791 FN_PARAM_BOOL( music_enabled )
792 FN_PARAM_INT( sound_volume )
793 FN_PARAM_INT( music_volume )
794 FN_PARAM_INT( audio_freq_mode )
795 FN_PARAM_INT( audio_format_mode )
796 FN_PARAM_BOOL( audio_stereo )
797 FN_PARAM_INT( audio_buffer_size )
798 FN_PARAM_BOOL( write_diagnostic_log )
799 
800 
801 
802 /*
803  * Functions to read and write the configuration file
804  */
805 
806 int get_old_config_file_name( char *buff, int len )
807 {
808 #if defined( WIN32 )
809     if ( strlen( OLD_CONFIG_FILE ) +1 > len ) {
810 	return 1;
811     }
812     strcpy( buff, OLD_CONFIG_FILE );
813     return 0;
814 #else
815     struct passwd *pwent;
816 
817     pwent = getpwuid( getuid() );
818     if ( pwent == NULL ) {
819 	perror( "getpwuid" );
820 	return 1;
821     }
822 
823     if ( strlen( pwent->pw_dir ) + strlen( OLD_CONFIG_FILE ) + 2 > len ) {
824 	return 1;
825     }
826 
827     sprintf( buff, "%s/%s", pwent->pw_dir, OLD_CONFIG_FILE );
828     return 0;
829 #endif /* defined( WIN32 ) */
830 }
831 
get_config_dir_name(char * buff,int len)832 int get_config_dir_name( char *buff, int len )
833 {
834 #if defined( WIN32 )
835     if ( strlen( CONFIG_DIR ) +1 > len ) {
836 	return 1;
837     }
838     strcpy( buff, CONFIG_DIR );
839     return 0;
840 #else
841     struct passwd *pwent;
842 
843     pwent = getpwuid( getuid() );
844     if ( pwent == NULL ) {
845 	perror( "getpwuid" );
846 	return 1;
847     }
848 
849     if ( strlen( pwent->pw_dir ) + strlen( CONFIG_DIR) + 2 > len ) {
850 	return 1;
851     }
852 
853     sprintf( buff, "%s/%s", pwent->pw_dir, CONFIG_DIR );
854     return 0;
855 #endif /* defined( WIN32 ) */
856 }
857 
get_config_file_name(char * buff,int len)858 int get_config_file_name( char *buff, int len )
859 {
860     if (get_config_dir_name( buff, len ) != 0) {
861 	return 1;
862     }
863     if ( strlen( buff ) + strlen( CONFIG_FILE ) +2 > len ) {
864 	return 1;
865     }
866 
867 #if defined( WIN32 )
868     strcat( buff, "\\" );
869 #else
870     strcat( buff, "/" );
871 #endif /* defined( WIN32 ) */
872 
873     strcat( buff, CONFIG_FILE);
874     return 0;
875 }
876 
clear_config_cache()877 void clear_config_cache()
878 {
879     struct param *parm;
880     int i;
881 
882     for (i=0; i<sizeof(Params)/sizeof(struct param); i++) {
883 	parm = (struct param*)&Params + i;
884 	parm->loaded = False;
885     }
886 }
887 
read_config_file()888 void read_config_file()
889 {
890     char config_file[BUFF_LEN];
891     char config_dir[BUFF_LEN];
892 
893     clear_config_cache();
894 
895     if ( get_config_file_name( config_file, sizeof( config_file ) ) != 0 ) {
896 	return;
897     }
898     if ( get_config_dir_name( config_dir, sizeof( config_dir ) ) != 0 ) {
899 	return;
900     }
901 
902 
903     if ( dir_exists( config_dir ) ) {
904 	if ( file_exists( config_file ) ) {
905 	    /* File exists -- let's try to evaluate it. */
906 	    if ( Tcl_EvalFile( g_game.tcl_interp, config_file ) != TCL_OK ) {
907 		handle_error( 1, "error evalating %s: %s", config_file,
908 			      Tcl_GetStringResult( g_game.tcl_interp ) );
909 	    }
910 	}
911 	return;
912     }
913 
914     /* File does not exist -- look for old version */
915     if ( get_old_config_file_name( config_file, sizeof( config_file ) ) != 0 ) {
916 	return;
917     }
918     if ( !file_exists( config_file ) ) {
919 	return;
920     }
921     /* Old file exists -- let's try to evaluate it. */
922     if ( Tcl_EvalFile( g_game.tcl_interp, config_file ) != TCL_OK ) {
923 	handle_error( 1, "error evalating deprecated %s: %s", config_file,
924 		      Tcl_GetStringResult( g_game.tcl_interp ) );
925     } else {
926 	/* Remove old file and save info in new file location */
927 	remove(config_file);
928 	write_config_file();
929     }
930 }
931 
write_config_file()932 void write_config_file()
933 {
934     FILE *config_stream;
935     char config_file[BUFF_LEN];
936     char config_dir[BUFF_LEN];
937     struct param *parm;
938     int i;
939 
940     if ( get_config_file_name( config_file, sizeof( config_file ) ) != 0 ) {
941 	return;
942     }
943     if ( get_config_dir_name( config_dir, sizeof( config_dir ) ) != 0 ) {
944 	return;
945     }
946 
947     if ( !dir_exists( config_dir ) ) {
948 
949 #if defined(WIN32) && !defined(__CYGWIN__)
950 	if (mkdir( config_dir ) != 0) {
951 	    return;
952 	}
953 #else
954 	if (mkdir( config_dir, 0775) != 0) {
955 	    return;
956 	}
957 #endif
958 
959     }
960 
961     config_stream = fopen( config_file, "w" );
962 
963     if ( config_stream == NULL ) {
964 	print_warning( CRITICAL_WARNING,
965 		       "couldn't open %s for writing: %s",
966 		       config_file, strerror(errno) );
967 	return;
968     }
969 
970     fprintf( config_stream,
971 	     "# Tux Racer " VERSION " configuration file\n"
972 	     "#\n"
973 	);
974 
975     for (i=0; i<sizeof(Params)/sizeof(struct param); i++) {
976 	parm = (struct param*)&Params + i;
977 	if ( parm->comment != NULL ) {
978 	    fprintf( config_stream, "\n# %s\n#\n%s\n#\n",
979 		     parm->name, parm->comment );
980 	}
981 	switch ( parm->type ) {
982 	case PARAM_STRING:
983 	    fetch_param_string( parm );
984 	    fprintf( config_stream, "set %s \"%s\"\n",
985 		     parm->name, parm->val.string_val );
986 	    break;
987 	case PARAM_CHAR:
988 	    fetch_param_char( parm );
989 	    fprintf( config_stream, "set %s %c\n",
990 		     parm->name, parm->val.char_val );
991 	    break;
992 	case PARAM_INT:
993 	    fetch_param_int( parm );
994 	    fprintf( config_stream, "set %s %d\n",
995 		     parm->name, parm->val.int_val );
996 	    break;
997 	case PARAM_BOOL:
998 	    fetch_param_bool( parm );
999 	    fprintf( config_stream, "set %s %s\n",
1000 		     parm->name, parm->val.bool_val ? "true" : "false" );
1001 	    break;
1002 	default:
1003 	    code_not_reached();
1004 	}
1005     }
1006 
1007     if ( fclose( config_stream ) != 0 ) {
1008 	perror( "fclose" );
1009     }
1010 }
1011 
1012 /*
1013  * Tcl callback to allow reading of game configuration variables from Tcl.
1014  */
get_param_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])1015 static int get_param_cb ( ClientData cd, Tcl_Interp *ip,
1016 			  int argc, char *argv[])
1017 {
1018     int i;
1019     int num_params;
1020     struct param *parm;
1021 
1022     if ( argc != 2 ) {
1023         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
1024 			 "Usage: ", argv[0], " <parameter name>",
1025 			 (char *)0 );
1026         return TCL_ERROR;
1027     }
1028 
1029     /* Search for parameter */
1030     parm = NULL;
1031     num_params = sizeof(Params)/sizeof(struct param);
1032     for (i=0; i<num_params; i++) {
1033 	parm = (struct param*)&Params + i;
1034 
1035 	if ( strcmp( parm->name, argv[1] ) == 0 ) {
1036 	    break;
1037 	}
1038     }
1039 
1040     /* If can't find parameter, report error */
1041     if ( parm == NULL || i == num_params ) {
1042 	Tcl_AppendResult(ip, argv[0], ": invalid parameter `",
1043 			 argv[1], "'", (char *)0 );
1044 	return TCL_ERROR;
1045     }
1046 
1047     /* Get value of parameter */
1048     switch ( parm->type ) {
1049     case PARAM_STRING:
1050 	fetch_param_string( parm );
1051 	Tcl_SetObjResult( ip, Tcl_NewStringObj( parm->val.string_val, -1 ) );
1052 	break;
1053 
1054     case PARAM_CHAR:
1055 	fetch_param_char( parm );
1056 	Tcl_SetObjResult( ip, Tcl_NewStringObj( &parm->val.char_val, 1 ) );
1057 	break;
1058 
1059     case PARAM_INT:
1060 	fetch_param_int( parm );
1061 	Tcl_SetObjResult( ip, Tcl_NewIntObj( parm->val.int_val ) );
1062 	break;
1063 
1064     case PARAM_BOOL:
1065 	fetch_param_bool( parm );
1066 	Tcl_SetObjResult( ip, Tcl_NewBooleanObj( parm->val.bool_val ) );
1067 	break;
1068 
1069     default:
1070 	code_not_reached();
1071     }
1072 
1073     return TCL_OK;
1074 }
1075 
1076 /*
1077  * Tcl callback to allow setting of game configuration variables from Tcl.
1078  */
set_param_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])1079 static int set_param_cb ( ClientData cd, Tcl_Interp *ip,
1080 			  int argc, char *argv[])
1081 {
1082     int i;
1083     int tmp_int;
1084     int num_params;
1085     struct param *parm;
1086 
1087     if ( argc != 3 ) {
1088         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
1089 			 "Usage: ", argv[0], " <parameter name> <value>",
1090 			 (char *)0 );
1091         return TCL_ERROR;
1092     }
1093 
1094     /* Search for parameter */
1095     parm = NULL;
1096     num_params = sizeof(Params)/sizeof(struct param);
1097     for (i=0; i<num_params; i++) {
1098 	parm = (struct param*)&Params + i;
1099 
1100 	if ( strcmp( parm->name, argv[1] ) == 0 ) {
1101 	    break;
1102 	}
1103     }
1104 
1105     /* If can't find parameter, report error */
1106     if ( parm == NULL || i == num_params ) {
1107 	Tcl_AppendResult(ip, argv[0], ": invalid parameter `",
1108 			 argv[1], "'", (char *)0 );
1109 	return TCL_ERROR;
1110     }
1111 
1112     /* Set value of parameter */
1113     switch ( parm->type ) {
1114     case PARAM_STRING:
1115 	set_param_string( parm, argv[2] );
1116 	break;
1117 
1118     case PARAM_CHAR:
1119 	if ( strlen( argv[2] ) > 1 ) {
1120 	    Tcl_AppendResult(ip, "\n", argv[0], ": value for `",
1121 			     argv[1], "' must be a single character",
1122 			     (char *)0 );
1123 	    return TCL_ERROR;
1124 	}
1125 	set_param_char( parm, argv[2][0] );
1126 	break;
1127 
1128     case PARAM_INT:
1129 	if ( Tcl_GetInt( ip, argv[2], &tmp_int ) != TCL_OK ) {
1130 	    Tcl_AppendResult(ip, "\n", argv[0], ": value for `",
1131 			     argv[1], "' must be an integer",
1132 			     (char *)0 );
1133 	    return TCL_ERROR;
1134 	}
1135 	set_param_int( parm, tmp_int );
1136 	break;
1137 
1138     case PARAM_BOOL:
1139 	if ( Tcl_GetBoolean( ip, argv[2], &tmp_int ) != TCL_OK ) {
1140 	    Tcl_AppendResult(ip, "\n", argv[0], ": value for `",
1141 			     argv[1], "' must be a boolean",
1142 			     (char *)0 );
1143 	    return TCL_ERROR;
1144 	}
1145 	check_assertion( tmp_int == 0 || tmp_int == 1,
1146 			 "invalid boolean value" );
1147 	set_param_bool( parm, (bool_t) tmp_int );
1148 	break;
1149 
1150     default:
1151 	code_not_reached();
1152     }
1153 
1154     return TCL_OK;
1155 }
1156 
register_game_config_callbacks(Tcl_Interp * ip)1157 void register_game_config_callbacks( Tcl_Interp *ip )
1158 {
1159     Tcl_CreateCommand (ip, "tux_get_param", get_param_cb,   0,0);
1160     Tcl_CreateCommand (ip, "tux_set_param", set_param_cb,   0,0);
1161 }
1162 
1163