1 /*
2
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (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, see <http://www.gnu.org/licenses/>.
17
18 */
19 //====================================================================================
20 //
21 // System Shock - ©1994-1995 Looking Glass Technologies, Inc.
22 //
23 // Prefs.c - Handles saving and loading preferences.
24 // Also loads and sets default keybinds.
25 //
26 //====================================================================================
27
28 //--------------------
29 // Includes
30 //--------------------
31 #include "Shock.h"
32 #include "Prefs.h"
33
34 #include "popups.h"
35 #include "olhext.h"
36 #include "hotkey.h"
37 #include "input.h"
38 #include "mainloop.h"
39 #include "movekeys.h"
40
41 //--------------------
42 // Filenames
43 //--------------------
44 static const char *PREFS_FILENAME = "prefs.txt";
45 static const char *KEYBINDS_FILENAME = "keybinds.txt";
46
47 //--------------------
48 // Globals
49 //--------------------
50 ShockPrefs gShockPrefs;
51 char which_lang;
52 uchar sfx_on = TRUE;
53
54 //--------------------
55 // Externs
56 //--------------------
57 extern int _fr_global_detail;
58 extern bool DoubleSize;
59 extern bool SkipLines;
60 extern short mode_id;
61
62 extern uchar curr_vol_lev;
63 extern uchar curr_sfx_vol;
64 extern uchar curr_alog_vol;
65
66 extern uchar audiolog_setting;
67
68 static const char *PREF_LANGUAGE = "language";
69 static const char *PREF_CAPTUREMOUSE = "capture-mouse";
70 static const char *PREF_INVERTMOUSEY = "invert-mousey";
71 static const char *PREF_MUSIC_VOL = "music-volume";
72 static const char *PREF_SFX_VOL = "sfx-volume";
73 static const char *PREF_ALOG_VOL = "alog-volume";
74 static const char *PREF_VIDEOMODE = "video-mode";
75 static const char *PREF_HALFRES = "half-resolution";
76 static const char *PREF_DETAIL = "detail";
77 static const char *PREF_USE_OPENGL = "use-opengl";
78 static const char *PREF_TEX_FILTER = "texture-filter";
79 static const char *PREF_ONSCR_HELP = "onscreen-help";
80 static const char *PREF_GAMMA = "gamma";
81 static const char *PREF_MSG_LENGTH = "message-length";
82 static const char *PREF_ALOG_SETTING = "alog-setting";
83 static const char *PREF_MIDI_BACKEND = "midi-backend";
84 static const char *PREF_MIDI_OUTPUT = "midi-output";
85
86 static void SetShockGlobals(void);
87
88 //--------------------------------------------------------------------
89 // Initialize the preferences to their default settings.
90 //--------------------------------------------------------------------
SetDefaultPrefs(void)91 void SetDefaultPrefs(void) {
92
93 gShockPrefs.prefVer = 0;
94 gShockPrefs.prefPlayIntro = 1; // First time through, play the intro
95 gShockPrefs.goPopupLabels = true;
96 gShockPrefs.soBackMusic = true;
97 #ifdef USE_FLUIDSYNTH
98 gShockPrefs.soMidiBackend = 2; // default to fluidsynth when available
99 #else
100 gShockPrefs.soMidiBackend = 0; // default to adlmidi
101 #endif
102 gShockPrefs.soMidiOutput = 0; // default to zero
103 gShockPrefs.soSoundFX = true;
104 gShockPrefs.doUseQD = false;
105
106 //saved in prefs file
107
108 gShockPrefs.goLanguage = 0; // English
109 gShockPrefs.goCaptureMouse = true;
110 gShockPrefs.goInvertMouseY = false;
111 gShockPrefs.soMusicVolume = 75;
112 gShockPrefs.soSfxVolume = 100;
113 gShockPrefs.soAudioLogVolume = 100;
114 gShockPrefs.doVideoMode = 3;
115 gShockPrefs.doResolution = 0; // High-res.
116 gShockPrefs.doDetail = 3; // Max detail.
117 gShockPrefs.doUseOpenGL = false;
118 gShockPrefs.doTextureFilter = 0; // unfiltered
119 gShockPrefs.goOnScreenHelp = true;
120 gShockPrefs.doGamma = 29; // Default gamma (29 out of 100).
121 gShockPrefs.goMsgLength = 0; // Normal
122 audiolog_setting = 1;
123
124 SetShockGlobals();
125 }
126
GetPrefsPathFilename(void)127 static char *GetPrefsPathFilename(void)
128 {
129 static char filename[512];
130
131 FILE *f = fopen(PREFS_FILENAME, "r");
132 if (f != NULL)
133 {
134 fclose(f);
135 strcpy(filename, PREFS_FILENAME);
136 }
137 else
138 {
139 char *p = SDL_GetPrefPath("Interrupt", "SystemShock");
140 snprintf(filename, sizeof(filename), "%s%s", p, PREFS_FILENAME);
141 SDL_free(p);
142 }
143
144 return filename;
145 }
146
trim(char * s)147 static char *trim(char *s) {
148 while (*s && isspace(*s))
149 s++;
150 char *c = &s[strlen(s) - 1];
151 while (c >= s && isspace(*c))
152 *(c--) = '\0';
153 return s;
154 }
155
is_true(const char * s)156 static bool is_true(const char *s) {
157 return strcasecmp(s, "yes") == 0 || strcasecmp(s, "true") == 0 || strcmp(s, "1") == 0;
158 }
159
160 //--------------------------------------------------------------------
161 // Locate the preferences file and load them to set our global pref settings.
162 //--------------------------------------------------------------------
LoadPrefs(void)163 int16_t LoadPrefs(void) {
164 FILE *f = fopen(GetPrefsPathFilename(), "r");
165 if (!f) {
166 // file can't be open, write default preferences
167 return SavePrefs();
168 }
169
170 char line[64];
171 while (fgets(line, sizeof(line), f)) {
172 char *eq = strchr(line, '=');
173 if (!eq) continue;
174 *eq = '\0';
175
176 const char *key = trim(line);
177 const char *value = trim(eq + 1);
178
179 if (strcasecmp(key, PREF_LANGUAGE) == 0) {
180 int lang = atoi(value);
181 if (lang >= 0 && lang <= 2)
182 gShockPrefs.goLanguage = lang;
183 } else if (strcasecmp(key, PREF_CAPTUREMOUSE) == 0) {
184 gShockPrefs.goCaptureMouse = is_true(value);
185 } else if (strcasecmp(key, PREF_INVERTMOUSEY) == 0) {
186 gShockPrefs.goInvertMouseY = is_true(value);
187 } else if (strcasecmp(key, PREF_MUSIC_VOL) == 0) {
188 int vol = atoi(value);
189 if (vol >= 0 && vol <= 100) {
190 gShockPrefs.soBackMusic = vol > 0;
191 gShockPrefs.soMusicVolume = vol;
192 }
193 } else if (strcasecmp(key, PREF_SFX_VOL) == 0) {
194 int vol = atoi(value);
195 if (vol >= 0 && vol <= 100) {
196 gShockPrefs.soSoundFX = vol > 0;
197 gShockPrefs.soSfxVolume = vol;
198 }
199 } else if (strcasecmp(key, PREF_ALOG_VOL) == 0) {
200 int vol = atoi(value);
201 if (vol >= 0 && vol <= 100)
202 gShockPrefs.soAudioLogVolume = vol;
203 } else if (strcasecmp(key, PREF_VIDEOMODE) == 0) {
204 int mode = atoi(value);
205 if (mode >= 0 && mode <= 4)
206 gShockPrefs.doVideoMode = mode;
207 } else if (strcasecmp(key, PREF_HALFRES) == 0) {
208 gShockPrefs.doResolution = is_true(value);
209 } else if (strcasecmp(key, PREF_DETAIL) == 0) {
210 int detail = atoi(value);
211 if (detail >= 0 && detail <= 3)
212 gShockPrefs.doDetail = detail;
213 } else if (strcasecmp(key, PREF_USE_OPENGL) == 0) {
214 gShockPrefs.doUseOpenGL = is_true(value);
215 } else if (strcasecmp(key, PREF_TEX_FILTER) == 0) {
216 int mode = atoi(value);
217 if (mode >= 0 && mode <= 1)
218 gShockPrefs.doTextureFilter = (short)mode;
219 } else if (strcasecmp(key, PREF_ONSCR_HELP) == 0) {
220 gShockPrefs.goOnScreenHelp = is_true(value);
221 } else if (strcasecmp(key, PREF_GAMMA) == 0) {
222 int gamma = atoi(value);
223 if (gamma < 10) gamma = 10;
224 if (gamma > 100) gamma = 100;
225 gShockPrefs.doGamma = gamma;
226 } else if (strcasecmp(key, PREF_MSG_LENGTH) == 0) {
227 int ml = atoi(value);
228 if (ml >= 0 && ml <= 1)
229 gShockPrefs.goMsgLength = ml;
230 } else if (strcasecmp(key, PREF_ALOG_SETTING) == 0) {
231 int as = atoi(value);
232 if (as >= 0 && as <= 2)
233 audiolog_setting = as;
234 } else if (strcasecmp(key, PREF_MIDI_BACKEND) == 0) {
235 int mb = atoi(value);
236 if (mb >= 0 && mb <= 2)
237 gShockPrefs.soMidiBackend = (short)mb;
238 } else if (strcasecmp(key, PREF_MIDI_OUTPUT) == 0) {
239 int mo = atoi(value);
240 if (mo >= 0)
241 gShockPrefs.soMidiOutput = (short)mo;
242 }
243 }
244
245 fclose(f);
246 SetShockGlobals();
247 return 0;
248 }
249
250 //--------------------------------------------------------------------
251 // Save global settings in the preferences file.
252 //--------------------------------------------------------------------
SavePrefs(void)253 int16_t SavePrefs(void) {
254 INFO("Saving preferences");
255
256 FILE *f = fopen(GetPrefsPathFilename(), "w");
257 if (!f) {
258 printf("ERROR: Failed to open preferences file\n");
259 return -1;
260 }
261
262 fprintf(f, "%s = %d\n", PREF_LANGUAGE, which_lang);
263 fprintf(f, "%s = %s\n", PREF_CAPTUREMOUSE, gShockPrefs.goCaptureMouse ? "yes" : "no");
264 fprintf(f, "%s = %s\n", PREF_INVERTMOUSEY, gShockPrefs.goInvertMouseY ? "yes" : "no");
265 fprintf(f, "%s = %d\n", PREF_MUSIC_VOL, curr_vol_lev);
266 fprintf(f, "%s = %d\n", PREF_SFX_VOL, sfx_on ? curr_sfx_vol : 0);
267 fprintf(f, "%s = %d\n", PREF_ALOG_VOL, curr_alog_vol);
268 fprintf(f, "%s = %d\n", PREF_VIDEOMODE, mode_id);
269 fprintf(f, "%s = %s\n", PREF_HALFRES, DoubleSize ? "yes" : "no");
270 fprintf(f, "%s = %d\n", PREF_DETAIL, _fr_global_detail);
271 fprintf(f, "%s = %s\n", PREF_USE_OPENGL, gShockPrefs.doUseOpenGL ? "yes" : "no");
272 fprintf(f, "%s = %d\n", PREF_TEX_FILTER, gShockPrefs.doTextureFilter);
273 fprintf(f, "%s = %s\n", PREF_ONSCR_HELP, gShockPrefs.goOnScreenHelp ? "yes" : "no");
274 fprintf(f, "%s = %d\n", PREF_GAMMA, gShockPrefs.doGamma);
275 fprintf(f, "%s = %d\n", PREF_MSG_LENGTH, gShockPrefs.goMsgLength);
276 fprintf(f, "%s = %d\n", PREF_ALOG_SETTING, audiolog_setting);
277 fprintf(f, "%s = %d\n", PREF_MIDI_BACKEND, gShockPrefs.soMidiBackend);
278 fprintf(f, "%s = %d\n", PREF_MIDI_OUTPUT, gShockPrefs.soMidiOutput);
279 fclose(f);
280 return 0;
281 }
282
283 //--------------------------------------------------------------------
284 // Set the corresponding Shock globals from the prefs structure.
285 //--------------------------------------------------------------------
SetShockGlobals(void)286 static void SetShockGlobals(void) {
287 popup_cursors = gShockPrefs.goPopupLabels;
288 olh_active = gShockPrefs.goOnScreenHelp;
289 which_lang = gShockPrefs.goLanguage;
290
291 sfx_on = gShockPrefs.soSoundFX;
292 curr_vol_lev = gShockPrefs.soMusicVolume;
293 curr_sfx_vol = gShockPrefs.soSfxVolume;
294 curr_alog_vol = gShockPrefs.soAudioLogVolume;
295
296 mode_id = gShockPrefs.doVideoMode;
297 DoubleSize = (gShockPrefs.doResolution == 1); // Set this True for low-res.
298 SkipLines = gShockPrefs.doUseQD;
299 _fr_global_detail = gShockPrefs.doDetail;
300 }
301
302 //************************************************************************************
303
304 //********
305 //Keybinds
306 //********
307
308
309
310 //Note that Alt / Option (on Mac) modifier key won't work until it is implemented in sdl_events.c
311
312
313
314 static struct {const char *s; int ch, code;} KeyName2ChCode[] =
315 {
316 { "backspace ", 8, 0x33 },
317 { "tab ", 9, 0x30 },
318 { "enter ", 13, 0x24 },
319 { "escape ", 27, 0x35 },
320 { "space ", 32, 0x31 },
321 { "1 ", 49, 0x12 },
322 { "exclamation ", 33, 0x12 },
323 { "2 ", 50, 0x13 },
324 { "atsign ", 64, 0x13 },
325 { "3 ", 51, 0x14 },
326 { "numbersign ", 35, 0x14 },
327 { "4 ", 52, 0x15 },
328 { "dollar ", 36, 0x15 },
329 { "5 ", 53, 0x17 },
330 { "percent ", 37, 0x17 },
331 { "6 ", 54, 0x16 },
332 { "caret ", 94, 0x16 },
333 { "7 ", 55, 0x1A },
334 { "ampersand ", 38, 0x1A },
335 { "8 ", 56, 0x1C },
336 { "asterisk ", 42, 0x1C },
337 { "9 ", 57, 0x19 },
338 { "lparenthesis ", 40, 0x19 },
339 { "0 ", 48, 0x1D },
340 { "rparenthesis ", 41, 0x1D },
341 { "equals ", 61, 0x18 },
342 { "plus ", 43, 0x18 },
343 { "comma ", 44, 0x2B },
344 { "lessthan ", 60, 0x2B },
345 { "minus ", 45, 0x1B },
346 { "underscore ", 95, 0x1B },
347 { "period ", 46, 0x2F },
348 { "greaterthan ", 62, 0x2F },
349 { "slash ", 47, 0x2C },
350 { "questionmark ", 63, 0x2C },
351 { "quote ", 39, 0x27 },
352 { "doublequote ", 34, 0x27 },
353 { "semicolon ", 59, 0x29 },
354 { "colon ", 58, 0x29 },
355 { "a ", 97, 0x00 },
356 { "b ", 98, 0x0B },
357 { "c ", 99, 0x08 },
358 { "d ", 100, 0x02 },
359 { "e ", 101, 0x0E },
360 { "f ", 102, 0x03 },
361 { "g ", 103, 0x05 },
362 { "h ", 104, 0x04 },
363 { "i ", 105, 0x22 },
364 { "j ", 106, 0x26 },
365 { "k ", 107, 0x28 },
366 { "l ", 108, 0x25 },
367 { "m ", 109, 0x2E },
368 { "n ", 110, 0x2D },
369 { "o ", 111, 0x1F },
370 { "p ", 112, 0x23 },
371 { "q ", 113, 0x0C },
372 { "r ", 114, 0x0F },
373 { "s ", 115, 0x01 },
374 { "t ", 116, 0x11 },
375 { "u ", 117, 0x20 },
376 { "v ", 118, 0x09 },
377 { "w ", 119, 0x0D },
378 { "x ", 120, 0x07 },
379 { "y ", 121, 0x10 },
380 { "z ", 122, 0x06 },
381 { "A ", 65, 0x00 },
382 { "B ", 66, 0x0B },
383 { "C ", 67, 0x08 },
384 { "D ", 68, 0x02 },
385 { "E ", 69, 0x0E },
386 { "F ", 70, 0x03 },
387 { "G ", 71, 0x05 },
388 { "H ", 72, 0x04 },
389 { "I ", 73, 0x22 },
390 { "J ", 74, 0x26 },
391 { "K ", 75, 0x28 },
392 { "L ", 76, 0x25 },
393 { "M ", 77, 0x2E },
394 { "N ", 78, 0x2D },
395 { "O ", 79, 0x1F },
396 { "P ", 80, 0x23 },
397 { "Q ", 81, 0x0C },
398 { "R ", 82, 0x0F },
399 { "S ", 83, 0x01 },
400 { "T ", 84, 0x11 },
401 { "U ", 85, 0x20 },
402 { "V ", 86, 0x09 },
403 { "W ", 87, 0x0D },
404 { "X ", 88, 0x07 },
405 { "Y ", 89, 0x10 },
406 { "Z ", 90, 0x06 },
407 { "lbracket ", 91, 0x21 },
408 { "lcurbrace ", 123, 0x21 },
409 { "backslash ", 92, 0x2A },
410 { "vertline ", 124, 0x2A },
411 { "rbracket ", 93, 0x1E },
412 { "rcurbrace ", 125, 0x1E },
413 { "backquote ", 96, 0x32 },
414 { "tilde ", 126, 0x32 },
415 { "delete ", 127, 0x33 },
416
417 //use these invented "ascii" codes for hotkey system
418 //see sdl_events.c
419 { "f1 ", 128 + 0, 0x7A },
420 { "f2 ", 128 + 1, 0x78 },
421 { "f3 ", 128 + 2, 0x63 },
422 { "f4 ", 128 + 3, 0x76 },
423 { "f5 ", 128 + 4, 0x60 },
424 { "f6 ", 128 + 5, 0x61 },
425 { "f7 ", 128 + 6, 0x62 },
426 { "f8 ", 128 + 7, 0x64 },
427 { "f9 ", 128 + 8, 0x65 },
428 { "f10 ", 128 + 9, 0x6D },
429 { "f11 ", 128 + 10, 0x67 },
430 { "f12 ", 128 + 11, 0x6F },
431 { "keypad_divide ", 128 + 12, 0x4B },
432 { "keypad_multiply ", 128 + 13, 0x43 },
433 { "keypad_minus ", 128 + 14, 0x4E },
434 { "keypad_plus ", 128 + 15, 0x45 },
435 { "keypad_enter ", 128 + 16, 0x4C },
436 { "keypad_decimal ", 128 + 17, 0x41 },
437 { "keypad_0 ", 128 + 18, 0x52 },
438
439 //these have no invented "ascii" codes so they can't be used as hotkeys, only move keys
440 { "keypad_home ", 0, 0x59 },
441 { "keypad_up ", 0, 0x5B },
442 { "keypad_pgup ", 0, 0x5C },
443 { "keypad_left ", 0, 0x56 },
444 { "keypad_5 ", 0, 0x57 },
445 { "keypad_right ", 0, 0x58 },
446 { "keypad_end ", 0, 0x53 },
447 { "keypad_down ", 0, 0x54 },
448 { "keypad_pgdn ", 0, 0x55 },
449 { "home ", 0, 0x73 },
450 { "up ", 0, 0x7E },
451 { "pageup ", 0, 0x74 },
452 { "left ", 0, 0x7B },
453 { "right ", 0, 0x7C },
454 { "end ", 0, 0x77 },
455 { "down ", 0, 0x7D },
456 { "pagedown ", 0, 0x79 },
457
458 { NULL, 0, 0 }
459 };
460
461
462
463 //lower cases all characters in string p
464 //also converts tabs to spaces
LowerCaseInPlace(char * p)465 static void LowerCaseInPlace(char *p)
466 {
467 while (*p)
468 {
469 if (*p >= 'A' && *p <= 'Z') *p = *p - 'A' + 'a'; //convert upper to lower case
470 if (*p == '\t') *p = ' '; //convert tab to space
471 p++;
472 }
473 }
474
475
476
477 //*********************************
478 //Set hotkey keybinds (see input.c)
479 //Also handles fire keybinds
480 //*********************************
481
482
483
484 #ifdef AUDIOLOGS
485 extern uchar audiolog_cancel_func(ushort keycode, uint32_t context, intptr_t data);
486 #endif
487 extern uchar posture_hotkey_func(ushort keycode, uint32_t context, intptr_t data);
488 extern uchar toggle_mouse_look(ushort keycode, uint32_t context, intptr_t data);
489 extern uchar change_mode_func(ushort keycode, uint32_t context, intptr_t data);
490 extern uchar clear_fullscreen_func(ushort keycode, uint32_t context, intptr_t data);
491 extern uchar saveload_hotkey_func(ushort keycode, uint32_t context, intptr_t data);
492 extern uchar pause_game_func(ushort keycode, uint32_t context, intptr_t data);
493 extern uchar reload_weapon_hotkey(ushort keycode, uint32_t context, intptr_t data);
494 extern uchar select_grenade_hotkey(ushort keycode, uint32_t context, intptr_t data);
495 extern uchar toggle_olh_func(ushort keycode, uint32_t context, intptr_t data);
496 extern uchar select_drug_hotkey(ushort keycode, uint32_t context, intptr_t data);
497 extern uchar toggle_music_func(ushort keycode, uint32_t context, intptr_t data);
498 extern uchar demo_quit_func(ushort keycode, uint32_t context, intptr_t data);
499 extern uchar cycle_weapons_func(ushort keycode, uint32_t context, intptr_t data);
500 extern uchar MacDetailFunc(ushort keycode, uint32_t context, intptr_t data);
501 extern uchar toggle_opengl_func(ushort keycode, uint32_t context, intptr_t data);
502 extern uchar arm_grenade_hotkey(ushort keycode, uint32_t context, intptr_t data);
503 extern uchar use_drug_hotkey(ushort keycode, uint32_t context, intptr_t data);
504 extern uchar hud_color_bank_cycle(ushort keycode, uint32_t context, intptr_t data);
505 extern uchar olh_overlay_func(ushort keycode, uint32_t context, intptr_t data);
506 extern uchar keypad_hotkey_func(ushort keycode, uint32_t context, intptr_t data);
507 extern uchar MacHelpFunc(ushort keycode, uint32_t context, intptr_t data);
508 extern uchar wrapper_options_func(ushort keycode, uint32_t context, intptr_t data);
509 extern uchar toggle_giveall_func(ushort keycode, uint32_t context, intptr_t data);
510 extern uchar toggle_physics_func(ushort keycode, uint32_t context, intptr_t data);
511 extern uchar toggle_up_level_func(ushort keycode, uint32_t context, intptr_t data);
512 extern uchar toggle_down_level_func(ushort keycode, uint32_t context, intptr_t data);
513
514
515
516 #define TAB_KEY (KEY_TAB | KB_FLAG_DOWN)
517 #define S_TAB_KEY (KEY_TAB | KB_FLAG_DOWN | KB_FLAG_SHIFT)
518
519 #define DOWN(x) ((x) | KB_FLAG_DOWN)
520 #define SHIFT(x) (DOWN(x) | KB_FLAG_SHIFT)
521 #define CTRL(x) (DOWN(x) | KB_FLAG_CTRL)
522 #define ALT(x) (DOWN(x) | KB_FLAG_ALT)
523
524
525
526 typedef struct HOTKEYLOOKUP_STRUCT
527 {
528 const char *s; intptr_t contexts; hotkey_callback func; intptr_t state; bool used; int def1, def2;
529 } HOTKEYLOOKUP;
530
531
532
533 HOTKEYLOOKUP HotKeyLookup[] =
534 {
535 // name contexts func state used default key 1,2
536 #ifdef AUDIOLOGS
537 { "\"audiolog_cancel\"", DEMO_CONTEXT, audiolog_cancel_func, 0 , 0, CTRL('.'), 0 },
538 #endif
539 { "\"stand\"", DEMO_CONTEXT, posture_hotkey_func, 0 , 0, DOWN('t'), SHIFT('t') },
540 { "\"crouch\"", DEMO_CONTEXT, posture_hotkey_func, 1 , 0, DOWN('g'), SHIFT('g') },
541 { "\"prone\"", DEMO_CONTEXT, posture_hotkey_func, 2 , 0, DOWN('b'), SHIFT('b') },
542 { "\"toggle_freelook\"", DEMO_CONTEXT, toggle_mouse_look, TRUE , 0, DOWN('f'), 0 },
543 { "\"full_view\"", DEMO_CONTEXT, change_mode_func, FULLSCREEN_LOOP , 0, CTRL('f'), 0 },
544 { "\"normal_view\"", DEMO_CONTEXT, change_mode_func, GAME_LOOP , 0, CTRL('d'), 0 },
545 { "\"map_view\"", DEMO_CONTEXT, change_mode_func, AUTOMAP_LOOP , 0, CTRL('a'), 0 },
546 { "\"clear_fullscreen\"", DEMO_CONTEXT, clear_fullscreen_func, 0 , 0, DOWN(KEY_BS), 0 },
547 { "\"save_game\"", DEMO_CONTEXT, saveload_hotkey_func, FALSE , 0, CTRL('s'), 0 },
548 { "\"load_game\"", DEMO_CONTEXT, saveload_hotkey_func, TRUE , 0, CTRL('l'), 0 },
549 { "\"pause\"", DEMO_CONTEXT, pause_game_func, TRUE , 0, DOWN('p'), 0 },
550 { "\"reload_weapon 1\"", DEMO_CONTEXT, reload_weapon_hotkey, 1 , 0, CTRL(KEY_BS), 0 },
551 { "\"reload_weapon 0\"", DEMO_CONTEXT, reload_weapon_hotkey, 0 , 0, ALT(KEY_BS), 0 },
552 { "\"select_grenade\"", DEMO_CONTEXT, select_grenade_hotkey, 0 , 0, CTRL('\''), 0 },
553 { "\"toggle_olh\"", DEMO_CONTEXT, toggle_olh_func, 0 , 0, CTRL('h'), 0 },
554 { "\"select_drug\"", DEMO_CONTEXT, select_drug_hotkey, 0 , 0, CTRL(';'), 0 },
555 { "\"toggle_music\"", DEMO_CONTEXT, toggle_music_func, 0 , 0, CTRL('m'), 0 },
556 { "\"quit\"", DEMO_CONTEXT, demo_quit_func, 0 , 0, CTRL('q'), 0 },
557 { "\"cycle_weapons 1\"", DEMO_CONTEXT, cycle_weapons_func, 1 , 0, TAB_KEY, 0 },
558 { "\"cycle_weapons -1\"", DEMO_CONTEXT, cycle_weapons_func, -1 , 0, S_TAB_KEY, 0 },
559 { "\"cycle_detail\"", DEMO_CONTEXT, MacDetailFunc, 0 , 0, CTRL('1'), 0 },
560 { "\"toggle_opengl\"", EVERY_CONTEXT, toggle_opengl_func, 0 , 0, CTRL('g'), 0 },
561 { "\"arm_grenade\"", DEMO_CONTEXT, arm_grenade_hotkey, 0 , 0, ALT('\''), 0 },
562 { "\"use_drug\"", DEMO_CONTEXT, use_drug_hotkey, 0 , 0, ALT(';'), 0 },
563 { "\"hud_color\"", DEMO_CONTEXT, hud_color_bank_cycle, 0 , 0, ALT('h'), 0 },
564 { "\"showhelp\"", DEMO_CONTEXT, olh_overlay_func, (intptr_t)&olh_overlay_on, 0, ALT('o'), 0 },
565 { "\"keypad 0\"", DEMO_CONTEXT, keypad_hotkey_func, 0 , 0, DOWN('0'), 0 },
566 { "\"keypad 1\"", DEMO_CONTEXT, keypad_hotkey_func, 0 , 0, DOWN('1'), 0 },
567 { "\"keypad 2\"", DEMO_CONTEXT, keypad_hotkey_func, 0 , 0, DOWN('2'), 0 },
568 { "\"keypad 3\"", DEMO_CONTEXT, keypad_hotkey_func, 0 , 0, DOWN('3'), 0 },
569 { "\"keypad 4\"", DEMO_CONTEXT, keypad_hotkey_func, 0 , 0, DOWN('4'), 0 },
570 { "\"keypad 5\"", DEMO_CONTEXT, keypad_hotkey_func, 0 , 0, DOWN('5'), 0 },
571 { "\"keypad 6\"", DEMO_CONTEXT, keypad_hotkey_func, 0 , 0, DOWN('6'), 0 },
572 { "\"keypad 7\"", DEMO_CONTEXT, keypad_hotkey_func, 0 , 0, DOWN('7'), 0 },
573 { "\"keypad 8\"", DEMO_CONTEXT, keypad_hotkey_func, 0 , 0, DOWN('8'), 0 },
574 { "\"keypad 9\"", DEMO_CONTEXT, keypad_hotkey_func, 0 , 0, DOWN('9'), 0 },
575 // { "\"mac_help\"", DEMO_CONTEXT, MacHelpFunc, 0 , 0, CTRL('/'), 0 },
576 { "\"toggle_options\"", DEMO_CONTEXT, wrapper_options_func, TRUE , 0, DOWN(KEY_ESC), 0 },
577 { "\"cheat_give_all\"", DEMO_CONTEXT, toggle_giveall_func, TRUE , 0, CTRL('2'), 0 },
578 { "\"cheat_physics\"", DEMO_CONTEXT, toggle_physics_func, TRUE , 0, CTRL('3'), 0 },
579 { "\"cheat_up_level\"", DEMO_CONTEXT, toggle_up_level_func, TRUE , 0, CTRL('4'), 0 },
580 { "\"cheat_down_level\"", DEMO_CONTEXT, toggle_down_level_func, TRUE , 0, CTRL('5'), 0 },
581
582 { NULL, 0, 0, 0 }
583 };
584
585
586
587 //ought to be enough for anybody
588 #define MAX_FIRE_KEYS 16
589
590 int FireKeys[MAX_FIRE_KEYS+1]; //see input.c
591
592
593
GetKeybindsPathFilename(void)594 static char *GetKeybindsPathFilename(void)
595 {
596 static char filename[512];
597
598 FILE *f = fopen(KEYBINDS_FILENAME, "r");
599 if (f != NULL)
600 {
601 fclose(f);
602 strcpy(filename, KEYBINDS_FILENAME);
603 }
604 else
605 {
606 char *p = SDL_GetPrefPath("Interrupt", "SystemShock");
607 snprintf(filename, sizeof(filename), "%s%s", p, KEYBINDS_FILENAME);
608 SDL_free(p);
609 }
610
611 return filename;
612 }
613
614
615
616 //all hotkey initialization and hotkey_add()s are done in this function
617 //also handles setting fire keybinds
LoadHotkeyKeybinds(void)618 void LoadHotkeyKeybinds(void)
619 {
620 FILE *f;
621 char temp[512], *p;
622 const char *string;
623 int len, i, flags, ch, fire_key_index = 0;
624
625 hotkey_init(NUM_HOTKEYS);
626
627 //clear hotkey used flags so we can tell which weren't specified in file
628 //later we can add default key chars for them
629 i = 0;
630 while (HotKeyLookup[i].s != NULL)
631 {
632 HotKeyLookup[i].used = FALSE;
633 i++;
634 }
635
636 f = fopen(GetKeybindsPathFilename(), "r");
637 if (f)
638 {
639 //scan keybinds file line by line
640 while (fgets(temp, sizeof(temp), f))
641 {
642 LowerCaseInPlace(temp);
643 p = temp;
644
645 while (*p && isspace(*p)) p++; //skip leading spaces
646
647 string = "bind"; len = strlen(string);
648 if (strncmp(p, string, len)) continue;
649 p += len;
650
651 while (*p && isspace(*p)) p++; //skip leading spaces
652
653 flags = KB_FLAG_DOWN;
654 ch = 0;
655
656 string = "shift+"; len = strlen(string);
657 if (!strncmp(p, string, len)) {p += len; flags |= KB_FLAG_SHIFT;}
658
659 string = "ctrl+"; len = strlen(string);
660 if (!strncmp(p, string, len)) {p += len; flags |= KB_FLAG_CTRL;}
661
662 string = "alt+"; len = strlen(string);
663 if (!strncmp(p, string, len)) {p += len; flags |= KB_FLAG_ALT;}
664
665 //get ascii char from key name
666 i = 0;
667 while (KeyName2ChCode[i].s != NULL)
668 {
669 string = KeyName2ChCode[i].s; len = strlen(string);
670 if (!strncmp(p, string, len)) {p += len; ch = KeyName2ChCode[i].ch | flags; break;}
671 i++;
672 }
673 if (ch == 0) continue;
674
675 while (*p && isspace(*p)) p++; //skip leading spaces
676
677 //lookup and add specified hotkey info
678 i = 0;
679 while (HotKeyLookup[i].s != NULL)
680 {
681 string = HotKeyLookup[i].s; len = strlen(string);
682 if (!strncmp(p, string, len))
683 {
684 hotkey_add(ch, HotKeyLookup[i].contexts, HotKeyLookup[i].func, HotKeyLookup[i].state);
685 HotKeyLookup[i].used = TRUE;
686 break;
687 }
688 i++;
689 }
690
691 //special case for fire keys
692 string = "\"fire\""; len = strlen(string);
693 if (!strncmp(p, string, len))
694 {
695 if (fire_key_index < MAX_FIRE_KEYS)
696 FireKeys[fire_key_index++] = (ch & ~KB_FLAG_DOWN);
697 }
698 }
699
700 fclose(f);
701 }
702
703 //add defaults for unused hotkeys
704 i = 0;
705 while (HotKeyLookup[i].s != NULL)
706 {
707 if (!HotKeyLookup[i].used)
708 {
709 //add default 1
710 ch = HotKeyLookup[i].def1;
711 if (ch) hotkey_add(ch, HotKeyLookup[i].contexts, HotKeyLookup[i].func, HotKeyLookup[i].state);
712
713 //add default 2
714 ch = HotKeyLookup[i].def2;
715 if (ch) hotkey_add(ch, HotKeyLookup[i].contexts, HotKeyLookup[i].func, HotKeyLookup[i].state);
716 }
717 i++;
718 }
719
720 //add default fire key if none were specified
721 if (fire_key_index == 0)
722 FireKeys[fire_key_index++] = KEY_ENTER;
723
724 //signal end of fire key list
725 FireKeys[fire_key_index] = 0;
726 }
727
728
729
730 //************************************************************************************
731
732
733
734 //**********************************
735 //Set move keybinds (see movekeys.c)
736 //**********************************
737
738
739
740 extern MOVE_KEYBIND MoveKeybinds[MAX_MOVE_KEYBINDS+1];
741 extern MOVE_KEYBIND MoveCyberKeybinds[MAX_MOVE_KEYBINDS+1];
742
743 static MOVE_KEYBIND MoveKeybindsDefault[] =
744 {
745 { CODE_W | KB_FLAG_SHIFT, M_RUNFORWARD },
746 { CODE_UP | KB_FLAG_SHIFT, M_RUNFORWARD },
747 { CODE_UP | KB_FLAG_ALT , M_RUNFORWARD },
748 { CODE_KP_UP | KB_FLAG_SHIFT, M_RUNFORWARD },
749 { CODE_KP_UP | KB_FLAG_ALT , M_RUNFORWARD },
750 { CODE_W , M_FORWARD },
751 { CODE_UP , M_FORWARD },
752 { CODE_KP_UP , M_FORWARD },
753 { CODE_Z | KB_FLAG_SHIFT, M_FASTTURNLEFT },
754 { CODE_LEFT | KB_FLAG_SHIFT, M_FASTTURNLEFT },
755 { CODE_KP_LEFT | KB_FLAG_SHIFT, M_FASTTURNLEFT },
756 { CODE_Z , M_TURNLEFT },
757 { CODE_LEFT , M_TURNLEFT },
758 { CODE_KP_LEFT , M_TURNLEFT },
759 { CODE_C | KB_FLAG_SHIFT, M_FASTTURNRIGHT },
760 { CODE_RIGHT | KB_FLAG_SHIFT, M_FASTTURNRIGHT },
761 { CODE_KP_RIGHT | KB_FLAG_SHIFT, M_FASTTURNRIGHT },
762 { CODE_C , M_TURNRIGHT },
763 { CODE_RIGHT , M_TURNRIGHT },
764 { CODE_KP_RIGHT , M_TURNRIGHT },
765 { CODE_S , M_BACK },
766 { CODE_S | KB_FLAG_SHIFT, M_BACK },
767 { CODE_DOWN , M_BACK },
768 { CODE_DOWN | KB_FLAG_SHIFT, M_BACK },
769 { CODE_DOWN | KB_FLAG_ALT , M_BACK },
770 { CODE_KP_DOWN , M_BACK },
771 { CODE_KP_DOWN | KB_FLAG_SHIFT, M_BACK },
772 { CODE_KP_DOWN | KB_FLAG_ALT , M_BACK },
773 { CODE_A , M_SLIDELEFT },
774 { CODE_A | KB_FLAG_SHIFT, M_SLIDELEFT },
775 { CODE_LEFT | KB_FLAG_ALT , M_SLIDELEFT },
776 { CODE_KP_LEFT | KB_FLAG_ALT , M_SLIDELEFT },
777 { CODE_KP_END , M_SLIDELEFT },
778 { CODE_D , M_SLIDERIGHT },
779 { CODE_D | KB_FLAG_SHIFT, M_SLIDERIGHT },
780 { CODE_RIGHT | KB_FLAG_ALT , M_SLIDERIGHT },
781 { CODE_KP_RIGHT | KB_FLAG_ALT , M_SLIDERIGHT },
782 { CODE_KP_PGDN , M_SLIDERIGHT },
783 { CODE_J , M_JUMP },
784 { CODE_J | KB_FLAG_SHIFT, M_JUMP },
785 { CODE_SPACE , M_JUMP },
786 { CODE_SPACE | KB_FLAG_SHIFT, M_JUMP },
787 { CODE_SPACE | KB_FLAG_CTRL , M_JUMP },
788 { CODE_SPACE | KB_FLAG_ALT , M_JUMP },
789 { CODE_X , M_LEANUP },
790 { CODE_X | KB_FLAG_SHIFT, M_LEANUP },
791 { CODE_X | KB_FLAG_CTRL , M_LEANUP },
792 { CODE_X | KB_FLAG_ALT , M_LEANUP },
793 { CODE_Q , M_LEANLEFT },
794 { CODE_Q | KB_FLAG_SHIFT, M_LEANLEFT },
795 { CODE_LEFT | KB_FLAG_CTRL , M_LEANLEFT },
796 { CODE_KP_LEFT | KB_FLAG_CTRL , M_LEANLEFT },
797 { CODE_E , M_LEANRIGHT },
798 { CODE_E | KB_FLAG_SHIFT, M_LEANRIGHT },
799 { CODE_RIGHT | KB_FLAG_CTRL , M_LEANRIGHT },
800 { CODE_KP_RIGHT | KB_FLAG_CTRL , M_LEANRIGHT },
801 { CODE_R , M_LOOKUP },
802 { CODE_R | KB_FLAG_SHIFT, M_LOOKUP },
803 { CODE_R | KB_FLAG_CTRL , M_LOOKUP },
804 { CODE_UP | KB_FLAG_CTRL , M_LOOKUP },
805 { CODE_KP_UP | KB_FLAG_CTRL , M_LOOKUP },
806 { CODE_V , M_LOOKDOWN },
807 { CODE_V | KB_FLAG_SHIFT, M_LOOKDOWN },
808 { CODE_V | KB_FLAG_CTRL , M_LOOKDOWN },
809 { CODE_DOWN | KB_FLAG_CTRL , M_LOOKDOWN },
810 { CODE_KP_DOWN | KB_FLAG_CTRL , M_LOOKDOWN },
811 { CODE_KP_HOME , M_RUNLEFT },
812 { CODE_KP_PGUP , M_RUNRIGHT },
813 { CODE_S , M_THRUST }, //cyber start
814 { CODE_S | KB_FLAG_SHIFT, M_THRUST },
815 { CODE_KP_5 , M_THRUST },
816 { CODE_W , M_CLIMB },
817 { CODE_W | KB_FLAG_SHIFT, M_CLIMB },
818 { CODE_UP , M_CLIMB },
819 { CODE_UP | KB_FLAG_SHIFT, M_CLIMB },
820 { CODE_UP | KB_FLAG_CTRL , M_CLIMB },
821 { CODE_UP | KB_FLAG_ALT , M_CLIMB },
822 { CODE_KP_UP , M_CLIMB },
823 { CODE_KP_UP | KB_FLAG_SHIFT, M_CLIMB },
824 { CODE_KP_UP | KB_FLAG_CTRL , M_CLIMB },
825 { CODE_KP_UP | KB_FLAG_ALT , M_CLIMB },
826 { CODE_A , M_BANKLEFT },
827 { CODE_A | KB_FLAG_SHIFT, M_BANKLEFT },
828 { CODE_KP_LEFT , M_BANKLEFT },
829 { CODE_KP_LEFT | KB_FLAG_SHIFT, M_BANKLEFT },
830 { CODE_KP_LEFT | KB_FLAG_CTRL , M_BANKLEFT },
831 { CODE_KP_LEFT | KB_FLAG_ALT , M_BANKLEFT },
832 { CODE_D , M_BANKRIGHT },
833 { CODE_D | KB_FLAG_SHIFT, M_BANKRIGHT },
834 { CODE_KP_RIGHT , M_BANKRIGHT },
835 { CODE_KP_RIGHT | KB_FLAG_SHIFT, M_BANKRIGHT },
836 { CODE_KP_RIGHT | KB_FLAG_CTRL , M_BANKRIGHT },
837 { CODE_KP_RIGHT | KB_FLAG_ALT , M_BANKRIGHT },
838 { CODE_X , M_DIVE },
839 { CODE_X | KB_FLAG_SHIFT, M_DIVE },
840 { CODE_DOWN , M_DIVE },
841 { CODE_DOWN | KB_FLAG_SHIFT, M_DIVE },
842 { CODE_DOWN | KB_FLAG_CTRL , M_DIVE },
843 { CODE_DOWN | KB_FLAG_ALT , M_DIVE },
844 { CODE_KP_DOWN , M_DIVE },
845 { CODE_KP_DOWN | KB_FLAG_SHIFT, M_DIVE },
846 { CODE_KP_DOWN | KB_FLAG_CTRL , M_DIVE },
847 { CODE_KP_DOWN | KB_FLAG_ALT , M_DIVE },
848 { CODE_Q , M_ROLLRIGHT },
849 { CODE_Q | KB_FLAG_SHIFT, M_ROLLRIGHT },
850 { CODE_Z , M_ROLLRIGHT },
851 { CODE_Z | KB_FLAG_SHIFT, M_ROLLRIGHT },
852 { CODE_E , M_ROLLLEFT },
853 { CODE_E | KB_FLAG_SHIFT, M_ROLLLEFT },
854 { CODE_C , M_ROLLLEFT },
855 { CODE_C | KB_FLAG_SHIFT, M_ROLLLEFT },
856 { CODE_KP_HOME , M_CLIMBLEFT },
857 { CODE_KP_HOME | KB_FLAG_SHIFT, M_CLIMBLEFT },
858 { CODE_KP_HOME | KB_FLAG_CTRL , M_CLIMBLEFT },
859 { CODE_KP_HOME | KB_FLAG_ALT , M_CLIMBLEFT },
860 { CODE_KP_PGUP , M_CLIMBRIGHT },
861 { CODE_KP_PGUP | KB_FLAG_SHIFT, M_CLIMBRIGHT },
862 { CODE_KP_PGUP | KB_FLAG_CTRL , M_CLIMBRIGHT },
863 { CODE_KP_PGUP | KB_FLAG_ALT , M_CLIMBRIGHT },
864 { CODE_KP_PGDN , M_DIVERIGHT },
865 { CODE_KP_PGDN | KB_FLAG_SHIFT, M_DIVERIGHT },
866 { CODE_KP_PGDN | KB_FLAG_CTRL , M_DIVERIGHT },
867 { CODE_KP_PGDN | KB_FLAG_ALT , M_DIVERIGHT },
868 { CODE_KP_END , M_DIVELEFT },
869 { CODE_KP_END | KB_FLAG_SHIFT, M_DIVELEFT },
870 { CODE_KP_END | KB_FLAG_CTRL , M_DIVELEFT },
871 { CODE_KP_END | KB_FLAG_ALT , M_DIVELEFT },
872
873 { 255 , -1 }
874 };
875
876
877
878 static struct {const char *s; int move;} MoveName2Move[] =
879 {
880 { "\"runforward\"", M_RUNFORWARD },
881 { "\"forward\"", M_FORWARD },
882 { "\"fastturnleft\"", M_FASTTURNLEFT },
883 { "\"turnleft\"", M_TURNLEFT },
884 { "\"fastturnright\"", M_FASTTURNRIGHT },
885 { "\"turnright\"", M_TURNRIGHT },
886 { "\"back\"", M_BACK },
887 { "\"slideleft\"", M_SLIDELEFT },
888 { "\"slideright\"", M_SLIDERIGHT },
889 { "\"jump\"", M_JUMP },
890 { "\"leanup\"", M_LEANUP },
891 { "\"leanleft\"", M_LEANLEFT },
892 { "\"leanright\"", M_LEANRIGHT },
893 { "\"lookup\"", M_LOOKUP },
894 { "\"lookdown\"", M_LOOKDOWN },
895 { "\"runleft\"", M_RUNLEFT },
896 { "\"runright\"", M_RUNRIGHT },
897 { "\"thrust\"", M_THRUST }, //cyber start
898 { "\"climb\"", M_CLIMB },
899 { "\"bankleft\"", M_BANKLEFT },
900 { "\"bankright\"", M_BANKRIGHT },
901 { "\"dive\"", M_DIVE },
902 { "\"rollright\"", M_ROLLRIGHT },
903 { "\"rollleft\"", M_ROLLLEFT },
904 { "\"climbleft\"", M_CLIMBLEFT },
905 { "\"climbright\"", M_CLIMBRIGHT },
906 { "\"diveright\"", M_DIVERIGHT },
907 { "\"diveleft\"", M_DIVELEFT },
908
909 { NULL, 0 }
910 };
911
912
913
LoadMoveKeybinds(void)914 void LoadMoveKeybinds(void)
915 {
916 FILE *f;
917 char temp[512], *p, move_used[NUM_MOVES];
918 const char *string;
919 int len, i, flags, code, move, num_bound = 0, num_cyber_bound = 0;
920
921 //keep track of which moves are specified so we can add default ones for those that are missing
922 memset(move_used, 0, NUM_MOVES);
923
924 f = fopen(GetKeybindsPathFilename(), "r");
925 if (f)
926 {
927 //scan keybinds file line by line
928 while (fgets(temp, sizeof(temp), f))
929 {
930 LowerCaseInPlace(temp);
931 p = temp;
932
933 while (*p && isspace(*p)) p++; //skip leading spaces
934
935 string = "bind"; len = strlen(string);
936 if (strncmp(p, string, len)) continue;
937 p += len;
938
939 while (*p && isspace(*p)) p++; //skip leading spaces
940
941 flags = 0;
942 code = 255;
943
944 string = "shift+"; len = strlen(string);
945 if (!strncmp(p, string, len)) {p += len; flags |= KB_FLAG_SHIFT;}
946
947 string = "ctrl+"; len = strlen(string);
948 if (!strncmp(p, string, len)) {p += len; flags |= KB_FLAG_CTRL;}
949
950 string = "alt+"; len = strlen(string);
951 if (!strncmp(p, string, len)) {p += len; flags |= KB_FLAG_ALT;}
952
953 //get code from key name
954 i = 0;
955 while (KeyName2ChCode[i].s != NULL)
956 {
957 string = KeyName2ChCode[i].s; len = strlen(string);
958 if (!strncmp(p, string, len)) {p += len; code = KeyName2ChCode[i].code | flags; break;}
959 i++;
960 }
961 if (code == 255) continue;
962
963 while (*p && isspace(*p)) p++; //skip leading spaces
964
965 //lookup move
966 i = 0;
967 while (MoveName2Move[i].s != NULL)
968 {
969 string = MoveName2Move[i].s; len = strlen(string);
970 if (!strncmp(p, string, len))
971 {
972 move = MoveName2Move[i].move;
973 move_used[move] = 1;
974
975 if (move < M_THRUST) //non-cyber
976 {
977 if (num_bound < MAX_MOVE_KEYBINDS)
978 {
979 //add keybind to list
980 MoveKeybinds[num_bound].code = code;
981 MoveKeybinds[num_bound].move = move;
982 num_bound++;
983 }
984 }
985 else
986 {
987 if (num_cyber_bound < MAX_MOVE_KEYBINDS)
988 {
989 //add cyber keybind to list
990 MoveCyberKeybinds[num_cyber_bound].code = code;
991 MoveCyberKeybinds[num_cyber_bound].move = move;
992 num_cyber_bound++;
993 }
994 }
995
996 break;
997 }
998 i++;
999 }
1000 }
1001
1002 fclose(f);
1003 }
1004
1005 //for moves that weren't referenced in file, bind default codes to them
1006 for (move = 0; move < NUM_MOVES; move++) if (!move_used[move])
1007 {
1008 i = 0;
1009 while (MoveKeybindsDefault[i].code != 255)
1010 {
1011 if (MoveKeybindsDefault[i].move == move)
1012 {
1013 code = MoveKeybindsDefault[i].code;
1014
1015 if (move < M_THRUST) //non-cyber
1016 {
1017 if (num_bound < MAX_MOVE_KEYBINDS)
1018 {
1019 //add keybind to list
1020 MoveKeybinds[num_bound].code = code;
1021 MoveKeybinds[num_bound].move = move;
1022 num_bound++;
1023 }
1024 }
1025 else
1026 {
1027 if (num_cyber_bound < MAX_MOVE_KEYBINDS)
1028 {
1029 //add cyber keybind to list
1030 MoveCyberKeybinds[num_cyber_bound].code = code;
1031 MoveCyberKeybinds[num_cyber_bound].move = move;
1032 num_cyber_bound++;
1033 }
1034 }
1035 }
1036 i++;
1037 }
1038 }
1039
1040 //signal end of lists
1041 MoveKeybinds[num_bound].code = 255;
1042 MoveCyberKeybinds[num_cyber_bound].code = 255;
1043
1044 extern void init_motion_polling(void); //see movekeys.c
1045 init_motion_polling();
1046 }
1047
1048
1049
1050 //************************************************************************************
1051
1052
1053
1054 //*******************************
1055 //Create default keybinds file
1056 //*******************************
1057
1058
1059
1060 #define JUSTIFY_COLUMN 30
1061
1062
1063
1064 //if ch is 0, use code instead
WriteKeyName(int ch,int code,FILE * f)1065 static bool WriteKeyName(int ch, int code, FILE *f)
1066 {
1067 int i = 0, len = 0;
1068
1069 while (KeyName2ChCode[i].s != NULL)
1070 {
1071 if (ch) { if (KeyName2ChCode[i].ch == (ch & 255)) break; }
1072 else { if (KeyName2ChCode[i].code == (code & 255)) break; }
1073 i++;
1074 }
1075 if (KeyName2ChCode[i].s == NULL) return 0;
1076
1077 fputs("bind ", f); len += 6;
1078
1079 if ((ch ? ch : code) & KB_FLAG_SHIFT) {fputs("shift+", f); len += 6;}
1080 if ((ch ? ch : code) & KB_FLAG_CTRL) {fputs("ctrl+", f); len += 5;}
1081 if ((ch ? ch : code) & KB_FLAG_ALT) {fputs("alt+", f); len += 4;}
1082
1083 fputs(KeyName2ChCode[i].s, f); len += strlen(KeyName2ChCode[i].s);
1084
1085 //add spaces to justify following text
1086 while (len < JUSTIFY_COLUMN) {fputc(' ', f); len++;}
1087
1088 return 1;
1089 }
1090
1091
1092
WriteMoveName(int move,FILE * f)1093 static void WriteMoveName(int move, FILE *f)
1094 {
1095 int i;
1096
1097 //find move name that matches move
1098 i = 0;
1099 while (MoveName2Move[i].s != NULL)
1100 {
1101 if (MoveName2Move[i].move == move) break;
1102 i++;
1103 }
1104
1105 if (MoveName2Move[i].s != NULL)
1106 {
1107 fputs(MoveName2Move[i].s, f);
1108 fputs("\n", f);
1109 }
1110 }
1111
1112
1113
1114 //create default keybinds file if it doesn't already exist
CreateDefaultKeybindsFile(void)1115 void CreateDefaultKeybindsFile(void)
1116 {
1117 FILE *f;
1118 char *filename = GetKeybindsPathFilename();
1119 int i, ch;
1120
1121 //check if file already exists; if so, return
1122 f = fopen(filename, "r");
1123 if (f != NULL) {fclose(f); return;}
1124
1125 //open new file for writing
1126 f = fopen(filename, "w");
1127 if (f == NULL) return;
1128
1129 //write default hotkey keybinds
1130 i = 0;
1131 while (HotKeyLookup[i].s)
1132 {
1133 //default 1 if it exists (it should)
1134 ch = HotKeyLookup[i].def1;
1135 if (ch && WriteKeyName(ch, 0, f))
1136 {
1137 fputs(HotKeyLookup[i].s, f);
1138 fputs("\n", f);
1139 }
1140
1141 //default 2 if it exists (it might not)
1142 ch = HotKeyLookup[i].def2;
1143 if (ch && WriteKeyName(ch, 0, f))
1144 {
1145 fputs(HotKeyLookup[i].s, f);
1146 fputs("\n", f);
1147 }
1148
1149 i++;
1150 }
1151
1152 //write default fire keybind
1153 fputs("\n", f);
1154 WriteKeyName(KEY_ENTER, 0, f);
1155 fputs("\"fire\"\n\n", f);
1156
1157 //write default move keybinds
1158 i = 0;
1159 while (MoveKeybindsDefault[i].code != 255)
1160 {
1161 if (WriteKeyName(0, MoveKeybindsDefault[i].code, f))
1162 WriteMoveName(MoveKeybindsDefault[i].move, f);
1163
1164 i++;
1165 }
1166
1167 fclose(f);
1168 }
1169