1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
4 // Copyright(C) 1993-1996 Id Software, Inc.
5 // Copyright(C) 2005 Simon Howard
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24 // Configuration file load/save code.
26 //-----------------------------------------------------------------------------
38 #define WIN32_LEAN_AND_MEAN
42 #include <sys/types.h>
45 #include "SDL_mixer.h"
49 #include "doomfeatures.h"
56 #include "compatibility.h"
61 #include "multiplayer.h"
70 void M_MakeDirectory(char *path
)
83 // Sets the location of the configuration directory, where configuration
84 // files are stored - default.cfg, chocolate-doom.cfg, savegames, etc.
87 void M_SetConfigDir(void)
89 #if !defined(_WIN32) || defined(_WIN32_WCE)
91 // Configuration settings are stored in ~/.chocolate-doom/,
92 // except on Windows, where we behave like Vanilla Doom and
93 // save in the current directory.
97 homedir
= getenv("HOME");
101 // put all configuration in a config directory off the
104 configdir
= malloc(strlen(homedir
) + strlen(PACKAGE_TARNAME
) + 5);
106 sprintf(configdir
, "%s%c.%s%c", homedir
, DIR_SEPARATOR
,
107 PACKAGE_TARNAME
, DIR_SEPARATOR
);
109 // make the directory if it doesnt already exist
111 M_MakeDirectory(configdir
);
114 #endif /* #ifndef _WIN32 */
116 #if defined(_WIN32) && !defined(_WIN32_WCE)
117 // when given the -cdrom option, save config+savegames in
118 // c:\doomdata. This only applies under Windows.
120 if (M_CheckParm("-cdrom") > 0)
123 configdir
= strdup("c:\\doomdata\\");
125 M_MakeDirectory(configdir
);
130 configdir
= strdup("");
141 // These are options that are not controlled by setup.
143 static int showMessages
= 1;
144 static int screenblocks
= 9;
145 static int detailLevel
= 0;
146 static int usegamma
= 0;
148 // dos specific options: these are unused but should be maintained
149 // so that the config file can be shared between chocolate
152 static int snd_sbport
= 0;
153 static int snd_sbirq
= 0;
154 static int snd_sbdma
= 0;
155 static int snd_mport
= 0;
168 // Name of the variable
171 // Pointer to the location in memory of the variable
174 // Type of the variable
177 // If this is a key value, the original integer scancode we read from
178 // the config file before translating it to the internal key value.
179 // If zero, we didn't read this value from a config file.
182 // The value we translated the scancode into when we read the
183 // config file on startup. If the variable value is different from
184 // this, it has been changed and needs to be converted; otherwise,
185 // use the 'untranslated' value.
186 int original_translated
;
194 } default_collection_t
;
196 static default_t doom_defaults_list
[] =
198 {"mouse_sensitivity", &mouseSensitivity
, DEFAULT_INT
, 0, 0},
199 {"sfx_volume",&sfxVolume
, DEFAULT_INT
, 0, 0},
200 {"music_volume",&musicVolume
, DEFAULT_INT
, 0, 0},
201 {"show_messages",&showMessages
, DEFAULT_INT
, 0, 0},
203 {"key_right",&key_right
, DEFAULT_KEY
, 0, 0},
204 {"key_left",&key_left
, DEFAULT_KEY
, 0, 0},
205 {"key_up",&key_up
, DEFAULT_KEY
, 0, 0},
206 {"key_down",&key_down
, DEFAULT_KEY
, 0, 0},
207 {"key_strafeleft",&key_strafeleft
, DEFAULT_KEY
, 0, 0},
208 {"key_straferight",&key_straferight
, DEFAULT_KEY
, 0, 0},
210 {"key_fire",&key_fire
, DEFAULT_KEY
, 0, 0},
211 {"key_use",&key_use
, DEFAULT_KEY
, 0, 0},
212 {"key_strafe",&key_strafe
, DEFAULT_KEY
, 0, 0},
213 {"key_speed",&key_speed
, DEFAULT_KEY
, 0, 0},
215 {"use_mouse",&usemouse
, DEFAULT_INT
, 0, 0},
216 {"mouseb_fire",&mousebfire
, DEFAULT_INT
, 0, 0},
217 {"mouseb_strafe",&mousebstrafe
, DEFAULT_INT
, 0, 0},
218 {"mouseb_forward",&mousebforward
, DEFAULT_INT
, 0, 0},
220 {"use_joystick",&usejoystick
, DEFAULT_INT
, 0, 0},
221 {"joyb_fire",&joybfire
, DEFAULT_INT
, 0, 0},
222 {"joyb_strafe",&joybstrafe
, DEFAULT_INT
, 0, 0},
223 {"joyb_use",&joybuse
, DEFAULT_INT
, 0, 0},
224 {"joyb_speed",&joybspeed
, DEFAULT_INT
, 0, 0},
226 {"screenblocks",&screenblocks
, DEFAULT_INT
, 0, 0},
227 {"detaillevel",&detailLevel
, DEFAULT_INT
, 0, 0},
229 {"snd_channels",&numChannels
, DEFAULT_INT
, 0, 0},
231 {"snd_musicdevice", &snd_musicdevice
, DEFAULT_INT
, 0, 0},
232 {"snd_sfxdevice", &snd_sfxdevice
, DEFAULT_INT
, 0, 0},
233 {"snd_sbport", &snd_sbport
, DEFAULT_INT
, 0, 0},
234 {"snd_sbirq", &snd_sbirq
, DEFAULT_INT
, 0, 0},
235 {"snd_sbdma", &snd_sbdma
, DEFAULT_INT
, 0, 0},
236 {"snd_mport", &snd_mport
, DEFAULT_INT
, 0, 0},
238 {"usegamma", &usegamma
, DEFAULT_INT
, 0, 0},
240 {"chatmacro0", &chat_macros
[0], DEFAULT_STRING
, 0, 0 },
241 {"chatmacro1", &chat_macros
[1], DEFAULT_STRING
, 0, 0 },
242 {"chatmacro2", &chat_macros
[2], DEFAULT_STRING
, 0, 0 },
243 {"chatmacro3", &chat_macros
[3], DEFAULT_STRING
, 0, 0 },
244 {"chatmacro4", &chat_macros
[4], DEFAULT_STRING
, 0, 0 },
245 {"chatmacro5", &chat_macros
[5], DEFAULT_STRING
, 0, 0 },
246 {"chatmacro6", &chat_macros
[6], DEFAULT_STRING
, 0, 0 },
247 {"chatmacro7", &chat_macros
[7], DEFAULT_STRING
, 0, 0 },
248 {"chatmacro8", &chat_macros
[8], DEFAULT_STRING
, 0, 0 },
249 {"chatmacro9", &chat_macros
[9], DEFAULT_STRING
, 0, 0 },
253 static default_collection_t doom_defaults
=
256 arrlen(doom_defaults_list
),
260 static default_t extra_defaults_list
[] =
262 {"autoadjust_video_settings", &autoadjust_video_settings
, DEFAULT_INT
, 0, 0},
263 {"fullscreen", &fullscreen
, DEFAULT_INT
, 0, 0},
264 {"aspect_ratio_correct", &aspect_ratio_correct
, DEFAULT_INT
, 0, 0},
265 {"startup_delay", &startup_delay
, DEFAULT_INT
, 0, 0},
266 {"screen_width", &screen_width
, DEFAULT_INT
, 0, 0},
267 {"screen_height", &screen_height
, DEFAULT_INT
, 0, 0},
268 {"grabmouse", &grabmouse
, DEFAULT_INT
, 0, 0},
269 {"novert", &novert
, DEFAULT_INT
, 0, 0},
270 {"mouse_acceleration", &mouse_acceleration
, DEFAULT_FLOAT
, 0, 0},
271 {"mouse_threshold", &mouse_threshold
, DEFAULT_INT
, 0, 0},
272 {"snd_samplerate", &snd_samplerate
, DEFAULT_INT
, 0, 0},
273 {"opl_io_port", &opl_io_port
, DEFAULT_INT_HEX
, 0, 0},
274 {"show_endoom", &show_endoom
, DEFAULT_INT
, 0, 0},
275 {"vanilla_savegame_limit", &vanilla_savegame_limit
, DEFAULT_INT
, 0, 0},
276 {"vanilla_demo_limit", &vanilla_demo_limit
, DEFAULT_INT
, 0, 0},
277 {"vanilla_keyboard_mapping", &vanilla_keyboard_mapping
, DEFAULT_INT
, 0, 0},
278 #ifdef FEATURE_MULTIPLAYER
279 {"player_name", &net_player_name
, DEFAULT_STRING
, 0, 0},
281 {"video_driver", &video_driver
, DEFAULT_STRING
, 0, 0},
282 {"joystick_index", &joystick_index
, DEFAULT_INT
, 0, 0},
283 {"joystick_x_axis", &joystick_x_axis
, DEFAULT_INT
, 0, 0},
284 {"joystick_x_invert", &joystick_x_invert
, DEFAULT_INT
, 0, 0},
285 {"joystick_y_axis", &joystick_y_axis
, DEFAULT_INT
, 0, 0},
286 {"joystick_y_invert", &joystick_y_invert
, DEFAULT_INT
, 0, 0},
287 {"joyb_strafeleft", &joybstrafeleft
, DEFAULT_INT
, 0, 0},
288 {"joyb_straferight", &joybstraferight
, DEFAULT_INT
, 0, 0},
289 {"joyb_prevweapon", &joybprevweapon
, DEFAULT_INT
, 0, 0},
290 {"joyb_nextweapon", &joybnextweapon
, DEFAULT_INT
, 0, 0},
291 {"dclick_use", &dclick_use
, DEFAULT_INT
, 0, 0},
292 {"mouseb_strafeleft", &mousebstrafeleft
, DEFAULT_INT
, 0, 0},
293 {"mouseb_straferight", &mousebstraferight
, DEFAULT_INT
, 0, 0},
294 {"mouseb_use", &mousebuse
, DEFAULT_INT
, 0, 0},
295 {"mouseb_backward", &mousebbackward
, DEFAULT_INT
, 0, 0},
296 {"mouseb_prevweapon", &mousebprevweapon
, DEFAULT_INT
, 0, 0},
297 {"mouseb_nextweapon", &mousebnextweapon
, DEFAULT_INT
, 0, 0},
298 {"use_libsamplerate", &use_libsamplerate
, DEFAULT_INT
, 0, 0},
300 {"key_pause", &key_pause
, DEFAULT_KEY
, 0, 0},
301 {"key_menu_activate", &key_menu_activate
, DEFAULT_KEY
, 0, 0},
302 {"key_menu_up", &key_menu_up
, DEFAULT_KEY
, 0, 0},
303 {"key_menu_down", &key_menu_down
, DEFAULT_KEY
, 0, 0},
304 {"key_menu_left", &key_menu_left
, DEFAULT_KEY
, 0, 0},
305 {"key_menu_right", &key_menu_right
, DEFAULT_KEY
, 0, 0},
306 {"key_menu_back", &key_menu_back
, DEFAULT_KEY
, 0, 0},
307 {"key_menu_forward", &key_menu_forward
, DEFAULT_KEY
, 0, 0},
308 {"key_menu_confirm", &key_menu_confirm
, DEFAULT_KEY
, 0, 0},
309 {"key_menu_abort", &key_menu_abort
, DEFAULT_KEY
, 0, 0},
310 {"key_menu_help", &key_menu_help
, DEFAULT_KEY
, 0, 0},
311 {"key_menu_save", &key_menu_save
, DEFAULT_KEY
, 0, 0},
312 {"key_menu_load", &key_menu_load
, DEFAULT_KEY
, 0, 0},
313 {"key_menu_volume", &key_menu_volume
, DEFAULT_KEY
, 0, 0},
314 {"key_menu_detail", &key_menu_detail
, DEFAULT_KEY
, 0, 0},
315 {"key_menu_qsave", &key_menu_qsave
, DEFAULT_KEY
, 0, 0},
316 {"key_menu_endgame", &key_menu_endgame
, DEFAULT_KEY
, 0, 0},
317 {"key_menu_messages", &key_menu_messages
, DEFAULT_KEY
, 0, 0},
318 {"key_menu_qload", &key_menu_qload
, DEFAULT_KEY
, 0, 0},
319 {"key_menu_quit", &key_menu_quit
, DEFAULT_KEY
, 0, 0},
320 {"key_menu_gamma", &key_menu_gamma
, DEFAULT_KEY
, 0, 0},
321 {"key_spy", &key_spy
, DEFAULT_KEY
, 0, 0},
322 {"key_menu_incscreen", &key_menu_incscreen
, DEFAULT_KEY
, 0, 0},
323 {"key_menu_decscreen", &key_menu_decscreen
, DEFAULT_KEY
, 0, 0},
325 {"key_map_toggle", &key_map_toggle
, DEFAULT_KEY
, 0, 0},
326 {"key_map_north", &key_map_north
, DEFAULT_KEY
, 0, 0},
327 {"key_map_south", &key_map_south
, DEFAULT_KEY
, 0, 0},
328 {"key_map_east", &key_map_east
, DEFAULT_KEY
, 0, 0},
329 {"key_map_west", &key_map_west
, DEFAULT_KEY
, 0, 0},
330 {"key_map_zoomin", &key_map_zoomin
, DEFAULT_KEY
, 0, 0},
331 {"key_map_zoomout", &key_map_zoomout
, DEFAULT_KEY
, 0, 0},
332 {"key_map_maxzoom", &key_map_maxzoom
, DEFAULT_KEY
, 0, 0},
333 {"key_map_follow", &key_map_follow
, DEFAULT_KEY
, 0, 0},
334 {"key_map_grid", &key_map_grid
, DEFAULT_KEY
, 0, 0},
335 {"key_map_mark", &key_map_mark
, DEFAULT_KEY
, 0, 0},
336 {"key_map_clearmark", &key_map_clearmark
, DEFAULT_KEY
, 0, 0},
337 {"key_weapon1", &key_weapon1
, DEFAULT_KEY
, 0, 0},
338 {"key_weapon2", &key_weapon2
, DEFAULT_KEY
, 0, 0},
339 {"key_weapon3", &key_weapon3
, DEFAULT_KEY
, 0, 0},
340 {"key_weapon4", &key_weapon4
, DEFAULT_KEY
, 0, 0},
341 {"key_weapon5", &key_weapon5
, DEFAULT_KEY
, 0, 0},
342 {"key_weapon6", &key_weapon6
, DEFAULT_KEY
, 0, 0},
343 {"key_weapon7", &key_weapon7
, DEFAULT_KEY
, 0, 0},
344 {"key_weapon8", &key_weapon8
, DEFAULT_KEY
, 0, 0},
345 {"key_prevweapon", &key_prevweapon
, DEFAULT_KEY
, 0, 0},
346 {"key_nextweapon", &key_nextweapon
, DEFAULT_KEY
, 0, 0},
347 {"key_message_refresh", &key_message_refresh
, DEFAULT_KEY
, 0, 0},
348 {"key_demo_quit", &key_demo_quit
, DEFAULT_KEY
, 0, 0},
349 {"key_multi_msg", &key_multi_msg
, DEFAULT_KEY
, 0, 0},
350 {"key_multi_msgplayer1", &key_multi_msgplayer
[0], DEFAULT_KEY
, 0, 0},
351 {"key_multi_msgplayer2", &key_multi_msgplayer
[1], DEFAULT_KEY
, 0, 0},
352 {"key_multi_msgplayer3", &key_multi_msgplayer
[2], DEFAULT_KEY
, 0, 0},
353 {"key_multi_msgplayer4", &key_multi_msgplayer
[3], DEFAULT_KEY
, 0, 0},
356 static default_collection_t extra_defaults
=
359 arrlen(extra_defaults_list
),
363 static int scantokey
[128] =
365 0 , 27, '1', '2', '3', '4', '5', '6',
366 '7', '8', '9', '0', '-', '=', KEY_BACKSPACE
, 9,
367 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
368 'o', 'p', '[', ']', 13, KEY_RCTRL
, 'a', 's',
369 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
370 '\'', '`', KEY_RSHIFT
,'\\', 'z', 'x', 'c', 'v',
371 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT
,KEYP_MULTIPLY
,
372 KEY_RALT
, ' ', KEY_CAPSLOCK
,KEY_F1
, KEY_F2
, KEY_F3
, KEY_F4
, KEY_F5
,
373 KEY_F6
, KEY_F7
, KEY_F8
, KEY_F9
, KEY_F10
, KEY_PAUSE
,KEY_SCRLCK
,KEY_HOME
,
374 KEY_UPARROW
,KEY_PGUP
,KEYP_MINUS
,KEY_LEFTARROW
,KEYP_5
,KEY_RIGHTARROW
,KEYP_PLUS
,KEY_END
,
375 KEY_DOWNARROW
,KEY_PGDN
,KEY_INS
,KEY_DEL
,0, 0, 0, KEY_F11
,
376 KEY_F12
, 0, 0, 0, 0, 0, 0, 0,
377 0, 0, 0, 0, 0, 0, 0, 0,
378 0, 0, 0, 0, 0, 0, 0, 0,
379 0, 0, 0, 0, 0, 0, 0, 0,
380 0, 0, 0, 0, 0, 0, 0, 0
384 static void SaveDefaultCollection(default_collection_t
*collection
)
390 f
= fopen (collection
->filename
, "w");
392 return; // can't write the file, but don't complain
394 defaults
= collection
->defaults
;
396 for (i
=0 ; i
<collection
->numdefaults
; i
++)
400 // Print the name and line up all values at 30 characters
402 chars_written
= fprintf(f
, "%s ", defaults
[i
].name
);
404 for (; chars_written
< 30; ++chars_written
)
409 switch (defaults
[i
].type
)
413 // use the untranslated version if we can, to reduce
414 // the possibility of screwing up the user's config
417 v
= * (int *) defaults
[i
].location
;
419 if (defaults
[i
].untranslated
420 && v
== defaults
[i
].original_translated
)
422 // Has not been changed since the last time we
423 // read the config file.
425 v
= defaults
[i
].untranslated
;
429 // search for a reverse mapping back to a scancode
430 // in the scantokey table
434 for (s
=0; s
<128; ++s
)
436 if (scantokey
[s
] == v
)
447 case DEFAULT_INT_HEX
:
448 fprintf(f
, "0x%x", * (int *) defaults
[i
].location
);
452 fprintf(f
, "%i", * (int *) defaults
[i
].location
);
456 fprintf(f
, "%f", * (float *) defaults
[i
].location
);
460 fprintf(f
,"\"%s\"", * (char **) (defaults
[i
].location
));
470 // Parses integer values in the configuration file
472 static int ParseIntParameter(char *strparm
)
476 if (strparm
[0] == '0' && strparm
[1] == 'x')
477 sscanf(strparm
+2, "%x", &parm
);
479 sscanf(strparm
, "%i", &parm
);
484 static void LoadDefaultCollection(default_collection_t
*collection
)
486 default_t
*defaults
= collection
->defaults
;
492 // read the file in, overriding any set defaults
493 f
= fopen(collection
->filename
, "r");
497 // File not opened, but don't complain
504 if (fscanf (f
, "%79s %[^\n]\n", defname
, strparm
) != 2)
506 // This line doesn't match
511 // Strip off trailing non-printable characters (\r characters
512 // from DOS text files)
514 while (strlen(strparm
) > 0 && !isprint(strparm
[strlen(strparm
)-1]))
516 strparm
[strlen(strparm
)-1] = '\0';
519 // Find the setting in the list
521 for (i
=0; i
<collection
->numdefaults
; ++i
)
523 default_t
*def
= &collection
->defaults
[i
];
527 if (strcmp(defname
, def
->name
) != 0)
538 s
= strdup(strparm
+ 1);
539 s
[strlen(s
) - 1] = '\0';
540 * (char **) def
->location
= s
;
544 case DEFAULT_INT_HEX
:
545 * (int *) def
->location
= ParseIntParameter(strparm
);
550 // translate scancodes read from config
551 // file (save the old value in untranslated)
553 intparm
= ParseIntParameter(strparm
);
554 defaults
[i
].untranslated
= intparm
;
556 if (intparm
>= 0 && intparm
< 128)
558 intparm
= scantokey
[intparm
];
565 defaults
[i
].original_translated
= intparm
;
566 * (int *) def
->location
= intparm
;
570 * (float *) def
->location
= atof(strparm
);
587 void M_SaveDefaults (void)
589 SaveDefaultCollection(&doom_defaults
);
590 SaveDefaultCollection(&extra_defaults
);
598 void M_LoadDefaults (void)
602 // check for a custom default file
603 i
= M_CheckParm ("-config");
607 doom_defaults
.filename
= myargv
[i
+1];
608 printf (" default file: %s\n",doom_defaults
.filename
);
612 doom_defaults
.filename
= malloc(strlen(configdir
) + 20);
613 sprintf(doom_defaults
.filename
, "%sdefault.cfg", configdir
);
616 // printf("saving config in %s\n", doom_defaults.filename);
618 i
= M_CheckParm("-extraconfig");
622 extra_defaults
.filename
= myargv
[i
+1];
623 printf(" extra configuration file: %s\n",
624 extra_defaults
.filename
);
628 extra_defaults
.filename
629 = malloc(strlen(configdir
) + strlen(PACKAGE_TARNAME
) + 10);
630 sprintf(extra_defaults
.filename
, "%s%s.cfg",
631 configdir
, PACKAGE_TARNAME
);
634 LoadDefaultCollection(&doom_defaults
);
635 LoadDefaultCollection(&extra_defaults
);
639 // Save normal (default.cfg) defaults to a given file
642 void M_SaveMainDefaults(char *filename
)
646 // Save the normal filename and set this one
648 main_filename
= doom_defaults
.filename
;
649 doom_defaults
.filename
= filename
;
653 SaveDefaultCollection(&doom_defaults
);
655 // Restore the normal filename
657 doom_defaults
.filename
= main_filename
;
661 // Save extra (chocolate-doom.cfg) defaults to a given file
664 void M_SaveExtraDefaults(char *filename
)
668 // Save the normal filename and set this one
670 main_filename
= extra_defaults
.filename
;
671 extra_defaults
.filename
= filename
;
675 SaveDefaultCollection(&extra_defaults
);
677 // Restore the normal filename
679 extra_defaults
.filename
= main_filename
;
684 static int SystemHasKeyboard(void)
691 if (RegOpenKeyExW(HKEY_CURRENT_USER
,
692 L
"\\Software\\Microsoft\\Shell", 0,
693 KEY_READ
, &key
) != ERROR_SUCCESS
)
699 valsize
= sizeof(DWORD
);
701 if (RegQueryValueExW(key
, L
"HasKeyboard", NULL
, &valtype
,
702 (LPBYTE
) &result
, &valsize
) != ERROR_SUCCESS
)
715 // Apply custom defaults for Windows CE.
718 static void M_ApplyWindowsCEDefaults(void)
720 // If the system doesn't have a keyboard, patch the default
721 // configuration to use the hardware keys.
723 if (!SystemHasKeyboard())
727 key_menu_activate
= KEY_F3
;
728 key_map_toggle
= KEY_F4
;
735 key_menu_confirm
= KEY_ENTER
;
736 key_menu_back
= KEY_F2
;
737 key_menu_abort
= KEY_F2
;
745 // Apply custom patches to the default values depending on the
746 // platform we are running on.
749 void M_ApplyPlatformDefaults(void)
752 M_ApplyWindowsCEDefaults();
755 // Before SDL_mixer version 1.2.11, MIDI music caused the game
756 // to crash when it looped. If this is an old SDL_mixer version,
761 const SDL_version
*v
= Mix_Linked_Version();
763 if (SDL_VERSIONNUM(v
->major
, v
->minor
, v
->patch
)
764 < SDL_VERSIONNUM(1, 2, 11))
766 snd_musicdevice
= SNDDEVICE_NONE
;