1 /*
2 
3 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 	and the "Aleph One" developers.
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 3 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	This license is contained in the file "COPYING",
17 	which is included with this source code; it is available online at
18 	http://www.gnu.org/licenses/gpl.html
19 
20 Feb 5, 2002 (Br'fin (Jeremy Parsons)):
21 	Default to keyboard and mouse control under Carbon
22 	for there are no InputSprockets
23 
24 Apr 30, 2002 (Loren Petrich):
25 	Converting to a MML-based preferences system
26 
27 May 16, 2002 (Woody Zenfell):
28     Added UI/preferences elements for configurable mouse sensitivity
29     Support for "don't auto-recenter" behavior modifier
30     Routines to let other code disable/reenable/query behavior modification
31 
32 Jul 21, 2002 (Loren Petrich):
33 	AS had added some code to fix the OSX preferences behavior;
34 	I modified it so that it would not be used in the Classic version
35 
36 Apr 10-22, 2003 (Woody Zenfell):
37         Join hinting and autogathering have Preferences entries now
38         Being less obnoxious with unrecognized Prefs stuff
39         Macintosh Enviroprefs popup style can be set in Preferences file
40 
41 May 22, 2003 (Woody Zenfell):
42 	Support for preferences for multiple network game protocols; configurable local game port.
43 
44  May 27, 2003 (Gregory Smith):
45 	Preferences for speex netmic
46 
47  August 27, 2003 (Woody Zenfell):
48 	Preferences for netscript.  Some reworking of index-based Mac FSSpec reading along the way.
49  */
50 
51 /*
52  *  preferences.cpp - Preferences handling
53  */
54 
55 #include "cseries.h"
56 #include "FileHandler.h"
57 
58 #include "map.h"
59 #include "shell.h" /* For the screen_mode structure */
60 #include "interface.h"
61 #include "SoundManager.h"
62 
63 #include "preferences.h"
64 #include "wad.h"
65 #include "wad_prefs.h"
66 #include "game_errors.h"
67 #include "network.h" // for _ethernet, etc.
68 #include "find_files.h"
69 #include "game_wad.h" // for set_map_file
70 #include "screen.h"
71 #include "fades.h"
72 #include "extensions.h"
73 #include "Console.h"
74 #include "Plugins.h"
75 
76 #include "InfoTree.h"
77 #include "StarGameProtocol.h"
78 #include "RingGameProtocol.h"
79 
80 #include "tags.h"
81 #include "Logging.h"
82 
83 #include <string.h>
84 #include <stdlib.h>
85 
86 #include "sdl_dialogs.h"
87 #include "sdl_fonts.h"
88 #include "sdl_widgets.h"
89 #include "images.h"
90 #include "preference_dialogs.h"
91 #include "preferences_widgets_sdl.h"
92 #include "mouse.h"
93 
94 #include "Music.h"
95 #include "HTTP.h"
96 #include "alephversion.h"
97 
98 #include <cmath>
99 #include <sstream>
100 #include <boost/algorithm/hex.hpp>
101 
102 #ifdef HAVE_UNISTD_H
103 #include <unistd.h>
104 #endif
105 
106 #ifdef __WIN32__
107 #include <windows.h> // for GetUserName()
108 #endif
109 
110 #include "joystick.h"
111 
112 // 8-bit support is still here if you undefine this, but you'll need to fix it
113 #define TRUE_COLOR_ONLY 1
114 
115 using namespace alephone;
116 
117 static const char sPasswordMask[] = "reverof nohtaram";
118 
119 static const char* sNetworkGameProtocolNames[] =
120 {	// These should match up with _network_game_protocol_ring, etc.
121 	"ring",
122 	"star"
123 };
124 
125 static const size_t NUMBER_OF_NETWORK_GAME_PROTOCOL_NAMES = sizeof(sNetworkGameProtocolNames) / sizeof(sNetworkGameProtocolNames[0]);
126 
127 
128 // Have the prefs been inited?
129 static bool PrefsInited = false;
130 
131 
132 // Global preferences data
133 struct graphics_preferences_data *graphics_preferences = NULL;
134 struct network_preferences_data *network_preferences = NULL;
135 struct player_preferences_data *player_preferences = NULL;
136 struct input_preferences_data *input_preferences = NULL;
137 SoundManager::Parameters *sound_preferences = NULL;
138 struct environment_preferences_data *environment_preferences = NULL;
139 
140 // LP: fake portable-files stuff
memory_error()141 inline short memory_error() {return 0;}
142 
143 static bool ethernet_active(void);
144 static std::string get_name_from_system(void);
145 
146 // LP: getting rid of the (void *) mechanism as inelegant and non-type-safe
147 static void default_graphics_preferences(graphics_preferences_data *preferences);
148 static bool validate_graphics_preferences(graphics_preferences_data *preferences);
149 static void default_network_preferences(network_preferences_data *preferences);
150 static bool validate_network_preferences(network_preferences_data *preferences);
151 static void default_player_preferences(player_preferences_data *preferences);
152 static bool validate_player_preferences(player_preferences_data *preferences);
153 static void default_input_preferences(input_preferences_data *preferences);
154 static bool validate_input_preferences(input_preferences_data *preferences);
155 static void default_environment_preferences(environment_preferences_data *preferences);
156 static bool validate_environment_preferences(environment_preferences_data *preferences);
157 
158 void parse_graphics_preferences(InfoTree root, std::string version);
159 void parse_player_preferences(InfoTree root, std::string version);
160 void parse_input_preferences(InfoTree root, std::string version);
161 void parse_sound_preferences(InfoTree root, std::string version);
162 void parse_network_preferences(InfoTree root, std::string version);
163 void parse_environment_preferences(InfoTree root, std::string version);
164 
165 // Prototypes
166 static void player_dialog(void *arg);
167 static void online_dialog(void *arg);
168 static void graphics_dialog(void *arg);
169 static void sound_dialog(void *arg);
170 static void controls_dialog(void *arg);
171 static void environment_dialog(void *arg);
172 static void plugins_dialog(void *arg);
173 static void keyboard_dialog(void *arg);
174 //static void texture_options_dialog(void *arg);
175 
176 /*
177  *  Get user name
178  */
179 
get_name_from_system()180 static std::string get_name_from_system()
181 {
182 #if defined(unix) || (defined (__APPLE__) && defined (__MACH__)) || defined(__NetBSD__) || defined(__OpenBSD__)
183 
184 	const char *login_name = getlogin();
185 	std::string login = (login_name ? login_name : "");
186 	if (login.length())
187 		return login;
188 
189 #elif defined(__WIN32__)
190 
191 	char login[17];
192 	DWORD len = 17;
193 
194 	bool hasName = (GetUserName((LPSTR)login, &len) == TRUE);
195 	if (hasName && strpbrk(login, "\\/:*?\"<>|") == NULL) // Ignore illegal names
196 		return login;
197 
198 #else
199 //#error get_name_from_system() not implemented for this platform
200 #endif
201 
202 	return "Bob User";
203 }
204 
205 
206 /*
207  *  Ethernet always available
208  */
209 
ethernet_active(void)210 static bool ethernet_active(void)
211 {
212 	return true;
213 }
214 
215 
216 /*
217  *  Main preferences dialog
218  */
219 
handle_preferences(void)220 void handle_preferences(void)
221 {
222 	// Save the existing preferences, in case we have to reload them
223 	write_preferences();
224 
225 	// Create top-level dialog
226 	dialog d;
227 	vertical_placer *placer = new vertical_placer;
228 	w_title *w_header = new w_title("PREFERENCES");
229 	d.add(w_header);
230 	w_button *w_player = new w_button("PLAYER", player_dialog, &d);
231 	d.add(w_player);
232 	w_button *w_online = new w_button("INTERNET", online_dialog, &d);
233 	d.add(w_online);
234 	w_button *w_graphics = new w_button("GRAPHICS", graphics_dialog, &d);
235 	d.add(w_graphics);
236 	w_button *w_sound = new w_button("SOUND", sound_dialog, &d);
237 	d.add(w_sound);
238 	w_button *w_controls = new w_button("CONTROLS", controls_dialog, &d);
239 	d.add(w_controls);
240 	w_button *w_environment = new w_button("ENVIRONMENT", environment_dialog, &d);
241 	d.add(w_environment);
242 
243 	w_button *w_return = new w_button("RETURN", dialog_cancel, &d);
244 	d.add(w_return);
245 
246 	placer->add(w_header);
247 	placer->add(new w_spacer, true);
248 	placer->add(w_player);
249 	placer->add(w_online);
250 	placer->add(w_graphics);
251 	placer->add(w_sound);
252 	placer->add(w_controls);
253 	placer->add(w_environment);
254 	placer->add(new w_spacer, true);
255 	placer->add(w_return);
256 
257 	d.set_widget_placer(placer);
258 
259 	// Clear menu screen
260 	clear_screen();
261 
262 	// Run dialog
263 	d.run();
264 
265 	// Redraw main menu
266 	display_main_menu();
267 }
268 
269 class CrosshairPref : public Bindable<int>
270 {
271 public:
CrosshairPref(short & pref)272 	CrosshairPref(short& pref) : m_pref(pref) { }
273 
bind_export()274 	virtual int bind_export() {
275 		return (m_pref - 1);
276 	}
277 
bind_import(int value)278 	virtual void bind_import(int value) {
279 		m_pref = value + 1;
280 	}
281 
282 protected:
283 	short& m_pref;
284 };
285 
286 class ColorComponentPref : public Bindable<int>
287 {
288 public:
ColorComponentPref(uint16 & pref)289 	ColorComponentPref(uint16& pref) : m_pref(pref) { }
290 
bind_export()291 	virtual int bind_export() {
292 		return (m_pref >> 12);
293 	}
294 
bind_import(int value)295 	virtual void bind_import(int value) {
296 		m_pref = value << 12;
297 	}
298 
299 protected:
300 	uint16& m_pref;
301 };
302 
303 class OpacityPref : public Bindable<int>
304 {
305 public:
OpacityPref(float & pref)306 	OpacityPref(float& pref) : m_pref(pref) { }
307 
bind_export()308 	virtual int bind_export() {
309 		return (static_cast<int>(floor(m_pref * 16)));
310 	}
311 
bind_import(int value)312 	virtual void bind_import(int value) {
313 		m_pref = ((float) value / 16.0);
314 	}
315 protected:
316 	float& m_pref;
317 };
318 
319 static const char *shape_labels[3] = {
320 	"Cross", "Octagon", NULL
321 };
322 
323 enum { kCrosshairWidget };
324 
325 static std::unique_ptr<BinderSet> crosshair_binders;
326 
327 struct update_crosshair_display
328 {
operator ()update_crosshair_display329 	void operator()(dialog *d) {
330 		crosshair_binders->migrate_all_first_to_second();
331 	}
332 };
333 
334 class w_crosshair_slider : public w_slider {
335 public:
w_crosshair_slider(int num_items,int sel)336 	w_crosshair_slider(int num_items, int sel) : w_slider(num_items, sel) {
337 		init_formatted_value();
338 	}
339 
formatted_value(void)340 	virtual std::string formatted_value(void) {
341 		std::ostringstream ss;
342 		ss << (selection + 1);
343 		return ss.str();
344 	}
345 };
346 
crosshair_dialog(void * arg)347 static void crosshair_dialog(void *arg)
348 {
349 	CrosshairData OldCrosshairs = player_preferences->Crosshairs;
350 	crosshair_binders.reset(new BinderSet);
351 
352 	dialog *parent = (dialog *) arg;
353 	(void)parent;
354 
355 	dialog d;
356 	vertical_placer *placer = new vertical_placer;
357 	w_title *w_header = new w_title("CROSSHAIR SETTINGS");
358 	placer->dual_add(w_header, d);
359 	placer->add(new w_spacer, true);
360 
361 	placer->dual_add(new w_static_text("HUD plugins may override these settings."), d);
362 	placer->add(new w_spacer, true);
363 
364 	w_crosshair_display *crosshair_w = new w_crosshair_display();
365 	placer->dual_add(crosshair_w, d);
366 
367 	placer->add(new w_spacer, true);
368 
369 	table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET));
370 	table->col_flags(0, placeable::kAlignRight);
371 
372 	// Shape
373 	w_select *shape_w = new w_select(0, shape_labels);
374 	SelectSelectorWidget shapeWidget(shape_w);
375 	Int16Pref shapePref(player_preferences->Crosshairs.Shape);
376 	crosshair_binders->insert<int> (&shapeWidget, &shapePref);
377 	table->dual_add(shape_w->label("Shape"), d);
378 	table->dual_add(shape_w, d);
379 
380 	table->add_row(new w_spacer(), true);
381 
382 	// Thickness
383 	w_slider* thickness_w = new w_crosshair_slider(7, 0);
384 	SliderSelectorWidget thicknessWidget(thickness_w);
385 	CrosshairPref thicknessPref(player_preferences->Crosshairs.Thickness);
386 	crosshair_binders->insert<int> (&thicknessWidget, &thicknessPref);
387 	table->dual_add(thickness_w->label("Width"), d);
388 	table->dual_add(thickness_w, d);
389 
390 	// From Center
391 	w_slider *from_center_w = new w_slider(15, 0);
392 	SliderSelectorWidget fromCenterWidget(from_center_w);
393 	Int16Pref fromCenterPref(player_preferences->Crosshairs.FromCenter);
394 	crosshair_binders->insert<int> (&fromCenterWidget, &fromCenterPref);
395 	table->dual_add(from_center_w->label("Gap"), d);
396 	table->dual_add(from_center_w, d);
397 
398 	// Length
399 	w_slider *length_w = new w_crosshair_slider(15, 0);
400 	SliderSelectorWidget lengthWidget(length_w);
401 	CrosshairPref lengthPref(player_preferences->Crosshairs.Length);
402 	crosshair_binders->insert<int> (&lengthWidget, &lengthPref);
403 	table->dual_add(length_w->label("Size"), d);
404 	table->dual_add(length_w, d);
405 
406 	table->add_row(new w_spacer(), true);
407 	table->dual_add_row(new w_static_text("Color"), d);
408 
409 	// Color
410 	w_slider *red_w = new w_percentage_slider(16, 0);
411 	SliderSelectorWidget redWidget(red_w);
412 	ColorComponentPref redPref(player_preferences->Crosshairs.Color.red);
413 	crosshair_binders->insert<int> (&redWidget, &redPref);
414 	table->dual_add(red_w->label("Red"), d);
415 	table->dual_add(red_w, d);
416 
417 	w_slider *green_w = new w_percentage_slider(16, 0);
418 	SliderSelectorWidget greenWidget(green_w);;
419 	ColorComponentPref greenPref(player_preferences->Crosshairs.Color.green);
420 	crosshair_binders->insert<int> (&greenWidget, &greenPref);
421 	table->dual_add(green_w->label("Green"), d);
422 	table->dual_add(green_w, d);
423 
424 	w_slider *blue_w = new w_percentage_slider(16, 0);
425 	SliderSelectorWidget blueWidget(blue_w);
426 	ColorComponentPref bluePref(player_preferences->Crosshairs.Color.blue);
427 	crosshair_binders->insert<int> (&blueWidget, &bluePref);
428 	table->dual_add(blue_w->label("Blue"), d);
429 	table->dual_add(blue_w, d);
430 
431 	table->add_row(new w_spacer(), true);
432 	table->dual_add_row(new w_static_text("OpenGL Only (no preview)"), d);
433 
434 	w_slider *opacity_w = new w_percentage_slider(16, 0);
435 	SliderSelectorWidget opacityWidget(opacity_w);
436 	OpacityPref opacityPref(player_preferences->Crosshairs.Opacity);
437 	crosshair_binders->insert<int> (&opacityWidget, &opacityPref);
438 	table->dual_add(opacity_w->label("Opacity"), d);
439 	table->dual_add(opacity_w, d);
440 
441 	placer->add(table, true);
442 	placer->add(new w_spacer, true);
443 
444 	horizontal_placer *button_placer = new horizontal_placer;
445 	w_button *w_accept = new w_button("ACCEPT", dialog_ok, &d);
446 	button_placer->dual_add(w_accept, d);
447 	w_button *w_cancel = new w_button("CANCEL", dialog_cancel, &d);
448 	button_placer->dual_add(w_cancel, d);
449 	placer->add(button_placer, true);
450 
451 	d.set_widget_placer(placer);
452 	d.set_processing_function(update_crosshair_display());
453 
454 	crosshair_binders->migrate_all_second_to_first();
455 
456 	clear_screen();
457 
458 	if (d.run() == 0) // Accepted
459 	{
460 		crosshair_binders->migrate_all_first_to_second();
461 		player_preferences->Crosshairs.PreCalced = false;
462 		write_preferences();
463 	}
464 	else
465 	{
466 		player_preferences->Crosshairs = OldCrosshairs;
467 	}
468 
469 	crosshair_binders.reset(0);
470 }
471 
472 /*
473  *  Player dialog
474  */
475 
476 enum {
477 	NAME_W
478 };
479 
player_dialog(void * arg)480 static void player_dialog(void *arg)
481 {
482 	// Create dialog
483 	dialog d;
484 	vertical_placer *placer = new vertical_placer;
485 	placer->dual_add(new w_title("PLAYER SETTINGS"), d);
486 	placer->add(new w_spacer());
487 
488 	table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
489 	table->col_flags(0, placeable::kAlignRight);
490 	table->col_flags(1, placeable::kAlignLeft);
491 
492 	w_select *level_w = new w_select(player_preferences->difficulty_level, NULL /*level_labels*/);
493 	level_w->set_labels_stringset(kDifficultyLevelsStringSetID);
494 	table->dual_add(level_w->label("Difficulty"), d);
495 	table->dual_add(level_w, d);
496 
497 	table->add_row(new w_spacer(), true);
498 
499 	table->dual_add_row(new w_static_text("Appearance"), d);
500 
501 	w_text_entry *name_w = new w_text_entry(PREFERENCES_NAME_LENGTH, player_preferences->name);
502 	name_w->set_identifier(NAME_W);
503 	name_w->set_enter_pressed_callback(dialog_try_ok);
504 	name_w->set_value_changed_callback(dialog_disable_ok_if_empty);
505 	name_w->enable_mac_roman_input();
506 	table->dual_add(name_w->label("Name"), d);
507 	table->dual_add(name_w, d);
508 
509 	w_player_color *pcolor_w = new w_player_color(player_preferences->color);
510 	table->dual_add(pcolor_w->label("Color"), d);
511 	table->dual_add(pcolor_w, d);
512 
513 	w_player_color *tcolor_w = new w_player_color(player_preferences->team);
514 	table->dual_add(tcolor_w->label("Team"), d);
515 	table->dual_add(tcolor_w, d);
516 
517 	table->add_row(new w_spacer(), true);
518 
519 	w_toggle *crosshairs_active_w = new w_toggle(player_preferences->crosshairs_active);
520 	table->dual_add(crosshairs_active_w->label("Show crosshairs"), d);
521 	table->dual_add(crosshairs_active_w, d);
522 
523 	placer->add(table, true);
524 
525 	placer->add(new w_spacer(), true);
526 
527 	w_button *crosshair_button = new w_button("CROSSHAIR SETTINGS", crosshair_dialog, &d);
528 	placer->dual_add(crosshair_button, d);
529 
530 	placer->add(new w_spacer(), true);
531 
532 	horizontal_placer *button_placer = new horizontal_placer;
533 
534 	w_button* ok_button = new w_button("ACCEPT", dialog_ok, &d);
535 	ok_button->set_identifier(iOK);
536 	button_placer->dual_add(ok_button, d);
537 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
538 
539 	placer->add(button_placer, true);
540 
541 	d.set_widget_placer(placer);
542 
543 	// Clear screen
544 	clear_screen();
545 
546 	// Run dialog
547 	if (d.run() == 0) {	// Accepted
548 		bool changed = false;
549 
550 		const char *name = name_w->get_text();
551 		if (strcmp(name, player_preferences->name)) {
552 			strncpy(player_preferences->name, name, PREFERENCES_NAME_LENGTH);
553 			player_preferences->name[PREFERENCES_NAME_LENGTH] = '\0';
554 			changed = true;
555 		}
556 
557 		int16 level = static_cast<int16>(level_w->get_selection());
558 		assert(level >= 0);
559 		if (level != player_preferences->difficulty_level) {
560 			player_preferences->difficulty_level = level;
561 			changed = true;
562 		}
563 
564 		int16 color = static_cast<int16>(pcolor_w->get_selection());
565 		assert(color >= 0);
566 		if (color != player_preferences->color) {
567 			player_preferences->color = color;
568 			changed = true;
569 		}
570 
571 		int16 team = static_cast<int16>(tcolor_w->get_selection());
572 		assert(team >= 0);
573 		if (team != player_preferences->team) {
574 			player_preferences->team = team;
575 			changed = true;
576 		}
577 
578 		bool crosshair = crosshairs_active_w->get_selection();
579 		if (crosshair != player_preferences->crosshairs_active) {
580 			player_preferences->crosshairs_active = crosshair;
581 			changed = true;
582 		}
583 
584 		if (changed)
585 			write_preferences();
586 	}
587 }
588 
589 /*
590  *  Online (lhowon.org) dialog
591  */
592 
593 const int iONLINE_USERNAME_W = 10;
594 const int iONLINE_PASSWORD_W = 11;
595 const int iSIGNUP_EMAIL_W = 20;
596 const int iSIGNUP_USERNAME_W = 21;
597 const int iSIGNUP_PASSWORD_W = 22;
598 
proc_account_link(void * arg)599 static void proc_account_link(void *arg)
600 {
601 	dialog *d = static_cast<dialog *>(arg);
602 
603 	HTTPClient conn;
604 	HTTPClient::parameter_map params;
605 	w_text_entry *username_w = static_cast<w_text_entry *>(d->get_widget_by_id(iONLINE_USERNAME_W));
606 	w_text_entry *password_w = static_cast<w_text_entry *>(d->get_widget_by_id(iONLINE_PASSWORD_W));
607 
608 	params["username"] = username_w->get_text();
609 	params["password"] = password_w->get_text();
610 	params["salt"] = "";
611 
612 	std::string url = A1_METASERVER_SETTINGS_URL;
613 	if (conn.Post(A1_METASERVER_LOGIN_URL, params))
614 	{
615 		std::string token = boost::algorithm::hex(conn.Response());
616 		url += "?token=" + token;
617 	}
618 
619 	toggle_fullscreen(false);
620 	launch_url_in_browser(url.c_str());
621 	d->draw();
622 }
623 
signup_dialog_ok(void * arg)624 static void signup_dialog_ok(void *arg)
625 {
626 	dialog *d = static_cast<dialog *>(arg);
627 	w_text_entry *email_w = static_cast<w_text_entry *>(d->get_widget_by_id(iSIGNUP_EMAIL_W));
628 	w_text_entry *login_w = static_cast<w_text_entry *>(d->get_widget_by_id(iSIGNUP_USERNAME_W));
629 	w_password_entry *password_w = static_cast<w_password_entry *>(d->get_widget_by_id(iSIGNUP_PASSWORD_W));
630 
631 	// check that fields are filled out
632 	if (strlen(email_w->get_text()) == 0)
633 	{
634 		alert_user("Please enter your email address.", infoError);
635 	}
636 	else if (strlen(login_w->get_text()) == 0)
637 	{
638 		alert_user("Please enter a username.", infoError);
639 	}
640 	else if (strlen(password_w->get_text()) == 0)
641 	{
642 		alert_user("Please enter a password.", infoError);
643 	}
644 	else
645 	{
646 		// send parameters to server
647 		HTTPClient conn;
648 		HTTPClient::parameter_map params;
649 		params["email"] = email_w->get_text();
650 		params["username"] = login_w->get_text();
651 		params["password"] = password_w->get_text();
652 
653 		if (conn.Post(A1_METASERVER_SIGNUP_URL, params))
654 		{
655 			if (conn.Response() == "OK")
656 			{
657 				// account was created successfully, save username and password
658 				strncpy(network_preferences->metaserver_login, login_w->get_text(), network_preferences_data::kMetaserverLoginLength);
659 				strncpy(network_preferences->metaserver_password, password_w->get_text(), network_preferences_data::kMetaserverLoginLength);
660 				write_preferences();
661 				d->quit(0);
662 			}
663 			else
664 			{
665 				alert_user(conn.Response().c_str(), infoError);
666 			}
667 		}
668 		else
669 		{
670 			alert_user("There was a problem contacting the server.", infoError);
671 		}
672 	}
673 }
674 
signup_dialog(void * arg)675 static void signup_dialog(void *arg)
676 {
677 	dialog d;
678 	vertical_placer *placer = new vertical_placer;
679 	placer->dual_add(new w_title("ACCOUNT SIGN UP"), d);
680 	placer->add(new w_spacer());
681 
682 	table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
683 	table->col_flags(0, placeable::kAlignRight);
684 	table->col_flags(1, placeable::kAlignLeft);
685 
686 	w_text_entry *email_w = new w_text_entry(256, "");
687 	email_w->set_identifier(iSIGNUP_EMAIL_W);
688 	table->dual_add(email_w->label("Email Address"), d);
689 	table->dual_add(email_w, d);
690 
691 	w_text_entry *login_w = new w_text_entry(network_preferences_data::kMetaserverLoginLength, network_preferences->metaserver_login);
692 	login_w->set_identifier(iSIGNUP_USERNAME_W);
693 	table->dual_add(login_w->label("Username"), d);
694 	table->dual_add(login_w, d);
695 
696 	w_password_entry *password_w = new w_password_entry(network_preferences_data::kMetaserverLoginLength, network_preferences->metaserver_password);
697 	password_w->set_identifier(iSIGNUP_PASSWORD_W);
698 	table->dual_add(password_w->label("Password"), d);
699 	table->dual_add(password_w, d);
700 
701 	table->add_row(new w_spacer(), true);
702 	placer->add(table, true);
703 
704 	horizontal_placer *button_placer = new horizontal_placer;
705 
706 	w_button* ok_button = new w_button("SIGN UP", signup_dialog_ok, &d);
707 	ok_button->set_identifier(iOK);
708 	button_placer->dual_add(ok_button, d);
709 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
710 
711 	placer->add(button_placer, true);
712 
713 	d.set_widget_placer(placer);
714 
715 	clear_screen();
716 
717 	if (d.run() == 0)
718 	{
719 		// account was successfully created, update parent fields with new account info
720 		dialog *parent = static_cast<dialog *>(arg);
721 		w_text_entry *login_w = static_cast<w_text_entry *>(parent->get_widget_by_id(iONLINE_USERNAME_W));
722 		login_w->set_text(network_preferences->metaserver_login);
723 		w_password_entry *password_w = static_cast<w_password_entry *>(parent->get_widget_by_id(iONLINE_PASSWORD_W));
724 		password_w->set_text(network_preferences->metaserver_password);
725 	}
726 }
727 
online_dialog(void * arg)728 static void online_dialog(void *arg)
729 {
730 	// Create dialog
731 	dialog d;
732 	vertical_placer *placer = new vertical_placer;
733 	placer->dual_add(new w_title("INTERNET GAME SETUP"), d);
734 	placer->add(new w_spacer());
735 
736 	tab_placer* tabs = new tab_placer();
737 
738 	std::vector<std::string> labels;
739 	labels.push_back("ACCOUNT");
740 	labels.push_back("PREGAME LOBBY");
741 	labels.push_back("STATS");
742 	w_tab *tab_w = new w_tab(labels, tabs);
743 
744 	placer->dual_add(tab_w, d);
745 	placer->add(new w_spacer(), true);
746 
747 	vertical_placer *account = new vertical_placer();
748 	table_placer *account_table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
749 	account_table->col_flags(0, placeable::kAlignRight);
750 	account_table->col_flags(1, placeable::kAlignLeft);
751 
752 	w_text_entry *login_w = new w_text_entry(network_preferences_data::kMetaserverLoginLength, network_preferences->metaserver_login);
753 	login_w->set_identifier(iONLINE_USERNAME_W);
754 	account_table->dual_add(login_w->label("Username"), d);
755 	account_table->dual_add(login_w, d);
756 
757 	w_password_entry *password_w = new w_password_entry(network_preferences_data::kMetaserverLoginLength, network_preferences->metaserver_password);
758 	password_w->set_identifier(iONLINE_PASSWORD_W);
759 	account_table->dual_add(password_w->label("Password"), d);
760 	account_table->dual_add(password_w, d);
761 
762 	w_hyperlink *account_link_w = new w_hyperlink("", "Visit my lhowon.org account page");
763 	account_link_w->set_callback(proc_account_link, &d);
764 	account_table->dual_add_row(account_link_w, d);
765 
766 	account_table->add_row(new w_spacer(), true);
767 
768 	w_button *signup_button = new w_button("SIGN UP", signup_dialog, &d);
769 	account_table->dual_add_row(signup_button, d);
770 
771 	account_table->add_row(new w_spacer(), true);
772 
773 	account->add(account_table, true);
774 
775 	vertical_placer *lobby = new vertical_placer();
776 	table_placer *lobby_table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
777 	lobby_table->col_flags(0, placeable::kAlignRight);
778 	lobby_table->col_flags(1, placeable::kAlignLeft);
779 
780 	w_text_entry *name_w = new w_text_entry(PREFERENCES_NAME_LENGTH, player_preferences->name);
781 	name_w->set_identifier(NAME_W);
782 	name_w->set_enter_pressed_callback(dialog_try_ok);
783 	name_w->set_value_changed_callback(dialog_disable_ok_if_empty);
784 	name_w->enable_mac_roman_input();
785 	lobby_table->dual_add(name_w->label("Name"), d);
786 	lobby_table->dual_add(name_w, d);
787 
788 	w_enabling_toggle *custom_colors_w = new w_enabling_toggle(network_preferences->use_custom_metaserver_colors);
789 	lobby_table->dual_add(custom_colors_w->label("Custom Chat Colors"), d);
790 	lobby_table->dual_add(custom_colors_w, d);
791 
792 	w_color_picker *primary_w = new w_color_picker(network_preferences->metaserver_colors[0]);
793 	lobby_table->dual_add(primary_w->label("Primary"), d);
794 	lobby_table->dual_add(primary_w, d);
795 
796 	w_color_picker *secondary_w = new w_color_picker(network_preferences->metaserver_colors[1]);
797 	lobby_table->dual_add(secondary_w->label("Secondary"), d);
798 	lobby_table->dual_add(secondary_w, d);
799 
800 	custom_colors_w->add_dependent_widget(primary_w);
801 	custom_colors_w->add_dependent_widget(secondary_w);
802 
803 	w_toggle *mute_guests_w = new w_toggle(network_preferences->mute_metaserver_guests);
804 	lobby_table->dual_add(mute_guests_w->label("Mute All Guest Chat"), d);
805 	lobby_table->dual_add(mute_guests_w, d);
806 
807 	lobby_table->add_row(new w_spacer(), true);
808 
809 	w_toggle *join_meta_w = new w_toggle(network_preferences->join_metaserver_by_default);
810 	lobby_table->dual_add(join_meta_w->label("Join Pregame Lobby by Default"), d);
811 	lobby_table->dual_add(join_meta_w, d);
812 
813 	lobby_table->add_row(new w_spacer(), true);
814 
815 	lobby->add(lobby_table, true);
816 
817 	vertical_placer *stats = new vertical_placer();
818 	stats->dual_add(new w_hyperlink(A1_LEADERBOARD_URL, "Visit the leaderboards"), d);
819 	stats->add(new w_spacer(), true);
820 
821 	horizontal_placer *stats_box = new horizontal_placer();
822 
823 	w_toggle *allow_stats_w = new w_toggle(network_preferences->allow_stats);
824 	stats_box->dual_add(allow_stats_w, d);
825 	stats_box->dual_add(allow_stats_w->label("Send Stats to Lhowon.org"), d);
826 
827 	stats->add(stats_box, true);
828 	stats->add(new w_spacer(), true);
829 
830 	stats->dual_add(new w_static_text("To compete on the leaderboards,"), d);
831 	stats->dual_add(new w_static_text("you need an online account, and a"), d);
832 	stats->dual_add(new w_static_text("Stats plugin installed and enabled."), d);
833 
834 	stats->add(new w_spacer(), true);
835 	stats->dual_add(new w_button("PLUGINS", plugins_dialog, &d), d);
836 
837 	stats->add(new w_spacer(), true);
838 
839 	tabs->add(account, true);
840 	tabs->add(lobby, true);
841 	tabs->add(stats, true);
842 
843 	placer->add(tabs, true);
844 	placer->add(new w_spacer(), true);
845 
846 	horizontal_placer *button_placer = new horizontal_placer;
847 
848 	w_button* ok_button = new w_button("ACCEPT", dialog_ok, &d);
849 	ok_button->set_identifier(iOK);
850 	button_placer->dual_add(ok_button, d);
851 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
852 
853 	placer->add(button_placer, true);
854 
855 	d.set_widget_placer(placer);
856 
857 	// Clear screen
858 	clear_screen();
859 
860 	// Run dialog
861 	if (d.run() == 0) {	// Accepted
862 		bool changed = false;
863 
864 		const char *name = name_w->get_text();
865 		if (strcmp(name, player_preferences->name)) {
866 			strncpy(player_preferences->name, name, PREFERENCES_NAME_LENGTH);
867 			player_preferences->name[PREFERENCES_NAME_LENGTH] = '\0';
868 			changed = true;
869 		}
870 
871 		const char *metaserver_login = login_w->get_text();
872 		if (strcmp(metaserver_login, network_preferences->metaserver_login)) {
873 			strncpy(network_preferences->metaserver_login, metaserver_login, network_preferences_data::kMetaserverLoginLength-1);
874 			network_preferences->metaserver_login[network_preferences_data::kMetaserverLoginLength-1] = '\0';
875 			changed = true;
876 		}
877 
878 		// clear password if login has been cleared
879 		if (!strlen(metaserver_login)) {
880 			if (strlen(network_preferences->metaserver_password)) {
881 				network_preferences->metaserver_password[0] = '\0';
882 				changed = true;
883 			}
884 		} else {
885 			const char *metaserver_password = password_w->get_text();
886 			if (strcmp(metaserver_password, network_preferences->metaserver_password)) {
887 				strncpy(network_preferences->metaserver_password, metaserver_password, network_preferences_data::kMetaserverLoginLength-1);
888 				network_preferences->metaserver_password[network_preferences_data::kMetaserverLoginLength-1] = '\0';
889 				changed = true;
890 			}
891 		}
892 
893 		bool use_custom_metaserver_colors = custom_colors_w->get_selection();
894 		if (use_custom_metaserver_colors != network_preferences->use_custom_metaserver_colors)
895 		{
896 			network_preferences->use_custom_metaserver_colors = use_custom_metaserver_colors;
897 			changed = true;
898 		}
899 
900 		if (use_custom_metaserver_colors)
901 		{
902 			rgb_color primary_color = primary_w->get_selection();
903 			if (primary_color.red != network_preferences->metaserver_colors[0].red || primary_color.green != network_preferences->metaserver_colors[0].green || primary_color.blue != network_preferences->metaserver_colors[0].blue)
904 			{
905 				network_preferences->metaserver_colors[0] = primary_color;
906 				changed = true;
907 			}
908 
909 			rgb_color secondary_color = secondary_w->get_selection();
910 			if (secondary_color.red != network_preferences->metaserver_colors[1].red || secondary_color.green != network_preferences->metaserver_colors[1].green || secondary_color.blue != network_preferences->metaserver_colors[1].blue)			{
911 				network_preferences->metaserver_colors[1] = secondary_color;
912 				changed = true;
913 			}
914 
915 		}
916 
917 		bool mute_metaserver_guests = mute_guests_w->get_selection() == 1;
918 		if (mute_metaserver_guests != network_preferences->mute_metaserver_guests)
919 		{
920 			network_preferences->mute_metaserver_guests = mute_metaserver_guests;
921 			changed = true;
922 		}
923 
924 		bool join_meta = join_meta_w->get_selection() == 1;
925 		if (join_meta != network_preferences->join_metaserver_by_default)
926 		{
927 			network_preferences->join_metaserver_by_default = join_meta;
928 			changed = true;
929 		}
930 
931 		bool allow_stats = allow_stats_w->get_selection() == 1;
932 		if (allow_stats != network_preferences->allow_stats)
933 		{
934 			network_preferences->allow_stats = allow_stats;
935 			Plugins::instance()->invalidate();
936 			changed = true;
937 		}
938 
939 
940 		if (changed)
941 			write_preferences();
942 	}
943 }
944 
945 /*
946  *  Handle graphics dialog
947  */
948 
949 #ifdef TRUE_COLOR_ONLY
950 static const char* depth_labels[3] = {
951 	"16 Bit", "32 Bit", NULL
952 };
953 #else
954 static const char *depth_labels[4] = {
955 	"8 Bit", "16 Bit", "32 Bit", NULL
956 };
957 #endif
958 
959 static const char *resolution_labels[3] = {
960 	"Low", "High", NULL
961 };
962 
963 static const char *sw_alpha_blending_labels[4] = {
964 	"Off", "Fast", "Nice", NULL
965 };
966 
967 static const char *sw_sdl_driver_labels[5] = {
968 	"Default", "None", "Direct3D", "OpenGL", NULL
969 };
970 
971 static const char *gamma_labels[9] = {
972 	"Darkest", "Darker", "Dark", "Normal", "Light", "Really Light", "Even Lighter", "Lightest", NULL
973 };
974 
975 static const char* renderer_labels[] = {
976 	"Software", "OpenGL", NULL
977 };
978 
979 static const char* hud_scale_labels[] = {
980 "Normal", "Double", "Largest", NULL
981 };
982 
983 static const char* term_scale_labels[] = {
984 "Normal", "Double", "Largest", NULL
985 };
986 
987 static const char* mouse_accel_labels[] = {
988 	"Off", "Classic", NULL
989 };
990 
991 static const char* max_saves_labels[] = {
992 	"20", "100", "500", "Unlimited", NULL
993 };
994 static const uint32 max_saves_values[] = {
995 	20, 100, 500, 0
996 };
997 
998 
999 enum {
1000     iRENDERING_SYSTEM = 1000
1001 };
1002 
build_stringvector_from_cstring_array(const char ** label_array)1003 static const vector<string> build_stringvector_from_cstring_array (const char** label_array)
1004 {
1005 	std::vector<std::string> label_vector;
1006 	for (int i = 0; label_array[i] != NULL; ++i)
1007 		label_vector.push_back(std::string(label_array[i]));
1008 
1009 	return label_vector;
1010 }
1011 
1012 
software_rendering_options_dialog(void * arg)1013 static void software_rendering_options_dialog(void* arg)
1014 {
1015 	// Create dialog
1016 	dialog d;
1017 	vertical_placer *placer = new vertical_placer;
1018 	placer->dual_add(new w_title("SOFTWARE RENDERING OPTIONS"), d);
1019 	placer->add(new w_spacer(), true);
1020 
1021 	table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
1022 	table->col_flags(0, placeable::kAlignRight);
1023 
1024 #ifdef TRUE_COLOR_ONLY
1025 	w_select *depth_w = new w_select(graphics_preferences->screen_mode.bit_depth == 16 ? 0 : 1, depth_labels);
1026 #else
1027 	w_select *depth_w = new w_select(graphics_preferences->screen_mode.bit_depth == 8 ? 0 : graphics_preferences->screen_mode.bit_depth == 16 ? 1 : 2, depth_labels);
1028 #endif
1029 	table->dual_add(depth_w->label("Color Depth"), d);
1030 	table->dual_add(depth_w, d);
1031 
1032 	w_toggle *resolution_w = new w_toggle(graphics_preferences->screen_mode.high_resolution, resolution_labels);
1033 	table->dual_add(resolution_w->label("Resolution"), d);
1034 	table->dual_add(resolution_w, d);
1035 
1036 	table->add_row(new w_spacer(), true);
1037 
1038 	w_select *sw_alpha_blending_w = new w_select(graphics_preferences->software_alpha_blending, sw_alpha_blending_labels);
1039 	table->dual_add(sw_alpha_blending_w->label("Transparent Liquids"), d);
1040 	table->dual_add(sw_alpha_blending_w, d);
1041 
1042 	w_select *sw_driver_w = new w_select(graphics_preferences->software_sdl_driver, sw_sdl_driver_labels);
1043 	table->dual_add(sw_driver_w->label("Acceleration"), d);
1044 	table->dual_add(sw_driver_w, d);
1045 
1046 	placer->add(table, true);
1047 
1048 	placer->add(new w_spacer(), true);
1049 	horizontal_placer *button_placer = new horizontal_placer;
1050 	button_placer->dual_add(new w_button("ACCEPT", dialog_ok, &d), d);
1051 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
1052 	placer->add(button_placer, true);
1053 
1054 	d.set_widget_placer(placer);
1055 	// Clear screen
1056 	clear_screen();
1057 
1058 	// Run dialog
1059 	if (d.run() == 0) {	// Accepted
1060 		bool changed = false;
1061 
1062 #ifdef TRUE_COLOR_ONLY
1063 		int depth = (depth_w->get_selection() == 0 ? 16 : 32);
1064 #else
1065 		int depth = (depth_w->get_selection() == 0 ? 8 : depth_w->get_selection() == 1 ? 16 : 32);
1066 #endif
1067 		if (depth != graphics_preferences->screen_mode.bit_depth) {
1068 			graphics_preferences->screen_mode.bit_depth = depth;
1069 			changed = true;
1070 			// don't change mode now; it will be changed when the game starts
1071 		}
1072 
1073 		bool hi_res = resolution_w->get_selection() != 0;
1074 		if (hi_res != graphics_preferences->screen_mode.high_resolution) {
1075 			graphics_preferences->screen_mode.high_resolution = hi_res;
1076 			changed = true;
1077 		}
1078 
1079 		if (sw_alpha_blending_w->get_selection() != graphics_preferences->software_alpha_blending)
1080 		{
1081 			graphics_preferences->software_alpha_blending = sw_alpha_blending_w->get_selection();
1082 			changed = true;
1083 		}
1084 
1085 		if (sw_driver_w->get_selection() != graphics_preferences->software_sdl_driver)
1086 		{
1087 			graphics_preferences->software_sdl_driver = sw_driver_w->get_selection();
1088 			changed = true;
1089 		}
1090 
1091 		if (changed)
1092 			write_preferences();
1093 	}
1094 }
1095 
1096 // ZZZ addition: bounce to correct renderer-config box based on selected rendering system.
rendering_options_dialog_demux(void * arg)1097 static void rendering_options_dialog_demux(void* arg)
1098 {
1099 	int theSelectedRenderer = get_selection_control_value((dialog*) arg, iRENDERING_SYSTEM) - 1;
1100 
1101 	switch(theSelectedRenderer) {
1102 		case _no_acceleration:
1103 			software_rendering_options_dialog(arg);
1104 			break;
1105 
1106 		case _opengl_acceleration:
1107 			OpenGLDialog::Create (theSelectedRenderer)->OpenGLPrefsByRunning ();
1108 			break;
1109 
1110 		default:
1111 			assert(false);
1112 			break;
1113 	}
1114 }
1115 
build_resolution_labels()1116 std::vector<std::string> build_resolution_labels()
1117 {
1118 	std::vector<std::string> result;
1119 	bool first_mode = true;
1120 	for (std::vector<std::pair<int, int> >::const_iterator it = Screen::instance()->GetModes().begin(); it != Screen::instance()->GetModes().end(); ++it)
1121 	{
1122 		std::ostringstream os;
1123 		os << it->first << "x" << it->second;
1124 		if (first_mode)
1125 		{
1126 			result.push_back("Automatic");
1127 			first_mode = false;
1128 		}
1129 		result.push_back(os.str());
1130 	}
1131 
1132 	return result;
1133 }
1134 
graphics_dialog(void * arg)1135 static void graphics_dialog(void *arg)
1136 {
1137 	dialog *parent = (dialog *)arg;
1138 
1139 	// Create dialog
1140 	dialog d;
1141 
1142 	vertical_placer *placer = new vertical_placer;
1143 	placer->dual_add(new w_title("GRAPHICS SETUP"), d);
1144 	placer->add(new w_spacer(), true);
1145 
1146 	table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
1147 	table->col_flags(0, placeable::kAlignRight);
1148 
1149 	w_select* renderer_w = new w_select(graphics_preferences->screen_mode.acceleration, renderer_labels);
1150 	renderer_w->set_identifier(iRENDERING_SYSTEM);
1151 #ifndef HAVE_OPENGL
1152 	renderer_w->set_selection(_no_acceleration);
1153 	renderer_w->set_enabled(false);
1154 #endif
1155 	table->dual_add(renderer_w->label("Rendering System"), d);
1156 	table->dual_add(renderer_w, d);
1157 
1158 	w_select_popup *size_w = new w_select_popup();
1159 	size_w->set_labels(build_resolution_labels());
1160 	if (graphics_preferences->screen_mode.auto_resolution)
1161 		size_w->set_selection(0);
1162 	else
1163 		size_w->set_selection(Screen::instance()->FindMode(graphics_preferences->screen_mode.width, graphics_preferences->screen_mode.height) + 1);
1164 	table->dual_add(size_w->label("Screen Size"), d);
1165 	table->dual_add(size_w, d);
1166 
1167 	w_toggle *high_dpi_w = NULL;
1168 	high_dpi_w = new w_toggle(graphics_preferences->screen_mode.high_dpi);
1169 #if (defined(__APPLE__) && defined(__MACH__))
1170 	// SDL's DPI support only enabled on macOS
1171 	table->dual_add(high_dpi_w->label("Use High DPI"), d);
1172 	table->dual_add(high_dpi_w, d);
1173 #endif
1174 
1175 	w_toggle *fixh_w = new w_toggle(!graphics_preferences->screen_mode.fix_h_not_v);
1176 	table->dual_add(fixh_w->label("Limit Vertical View"), d);
1177 	table->dual_add(fixh_w, d);
1178 
1179 	w_toggle *bob_w = new w_toggle(graphics_preferences->screen_mode.camera_bob);
1180 	table->dual_add(bob_w->label("Camera Bobbing"), d);
1181 	table->dual_add(bob_w, d);
1182 
1183   	w_select_popup *gamma_w = new w_select_popup();
1184 	gamma_w->set_labels(build_stringvector_from_cstring_array(gamma_labels));
1185 	gamma_w->set_selection(graphics_preferences->screen_mode.gamma_level);
1186 	table->dual_add(gamma_w->label("Brightness"), d);
1187 	table->dual_add(gamma_w, d);
1188 
1189 	table->add_row(new w_spacer(), true);
1190 
1191 	w_toggle *fullscreen_w = new w_toggle(!graphics_preferences->screen_mode.fullscreen);
1192 	table->dual_add(fullscreen_w->label("Windowed Mode"), d);
1193 	table->dual_add(fullscreen_w, d);
1194 
1195 	table->add_row(new w_spacer(), true);
1196 	table->dual_add_row(new w_static_text("Heads-Up Display"), d);
1197 	w_enabling_toggle *hud_w = new w_enabling_toggle(graphics_preferences->screen_mode.hud);
1198 	table->dual_add(hud_w->label("Show HUD"), d);
1199 	table->dual_add(hud_w, d);
1200 
1201 	w_select_popup *hud_scale_w = new w_select_popup();
1202 	hud_scale_w->set_labels(build_stringvector_from_cstring_array(hud_scale_labels));
1203 	hud_scale_w->set_selection(graphics_preferences->screen_mode.hud_scale_level);
1204 	table->dual_add(hud_scale_w->label("HUD Size"), d);
1205 	table->dual_add(hud_scale_w, d);
1206 	hud_w->add_dependent_widget(hud_scale_w);
1207 
1208 	w_select_popup *term_scale_w = new w_select_popup();
1209 	term_scale_w->set_labels(build_stringvector_from_cstring_array(term_scale_labels));
1210 	term_scale_w->set_selection(graphics_preferences->screen_mode.term_scale_level);
1211 	table->dual_add(term_scale_w->label("Terminal Size"), d);
1212 	table->dual_add(term_scale_w, d);
1213 
1214 	w_toggle *map_w = new w_toggle(graphics_preferences->screen_mode.translucent_map);
1215 	table->dual_add(map_w->label("Overlay Map"), d);
1216 	table->dual_add(map_w, d);
1217 
1218 	placer->add(table, true);
1219 
1220 	placer->add(new w_spacer(), true);
1221 	placer->dual_add(new w_button("RENDERING OPTIONS", rendering_options_dialog_demux, &d), d);
1222 	placer->add(new w_spacer(), true);
1223 
1224 #ifndef HAVE_OPENGL
1225 	expand_app_variables(temporary, "This copy of $appName$ was built without OpenGL support.");
1226 	placer->dual_add(new w_static_text(temporary), d);
1227 #endif
1228 	placer->add(new w_spacer(), true);
1229 
1230 	horizontal_placer *button_placer = new horizontal_placer;
1231 	button_placer->dual_add(new w_button("ACCEPT", dialog_ok, &d), d);
1232 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
1233 
1234 	placer->add(button_placer, true);
1235 
1236 	d.set_widget_placer(placer);
1237 
1238 	// Clear screen
1239 	clear_screen();
1240 
1241     // Run dialog
1242     if (d.run() == 0) {	// Accepted
1243 	    bool changed = false;
1244 
1245 	    bool fullscreen = fullscreen_w->get_selection() == 0;
1246 	    if (fullscreen != graphics_preferences->screen_mode.fullscreen) {
1247 		    graphics_preferences->screen_mode.fullscreen = fullscreen;
1248 		    changed = true;
1249 	    }
1250 
1251 	    short renderer = static_cast<short>(renderer_w->get_selection());
1252 	    assert(renderer >= 0);
1253 	    if(renderer != graphics_preferences->screen_mode.acceleration) {
1254 		    graphics_preferences->screen_mode.acceleration = renderer;
1255 		    if (renderer) graphics_preferences->screen_mode.bit_depth = 32;
1256 		    changed = true;
1257 	    }
1258 
1259 	    short resolution = static_cast<short>(size_w->get_selection());
1260 		if (resolution == 0)
1261 		{
1262 			if (!graphics_preferences->screen_mode.auto_resolution) {
1263 				graphics_preferences->screen_mode.auto_resolution = true;
1264 				changed = true;
1265 			}
1266 		}
1267 	    else if (Screen::instance()->ModeWidth(resolution - 1) != graphics_preferences->screen_mode.width || Screen::instance()->ModeHeight(resolution - 1) != graphics_preferences->screen_mode.height || graphics_preferences->screen_mode.auto_resolution)
1268 	    {
1269 		    graphics_preferences->screen_mode.width = Screen::instance()->ModeWidth(resolution - 1);
1270 		    graphics_preferences->screen_mode.height = Screen::instance()->ModeHeight(resolution - 1);
1271 			graphics_preferences->screen_mode.auto_resolution = false;
1272 		    changed = true;
1273 	    }
1274 
1275 		bool high_dpi = high_dpi_w->get_selection() != 0;
1276 		if (high_dpi != graphics_preferences->screen_mode.high_dpi) {
1277 			graphics_preferences->screen_mode.high_dpi = high_dpi;
1278 			changed = true;
1279 		}
1280 
1281 	    short gamma = static_cast<short>(gamma_w->get_selection());
1282 	    if (gamma != graphics_preferences->screen_mode.gamma_level) {
1283 		    graphics_preferences->screen_mode.gamma_level = gamma;
1284 		    changed = true;
1285 	    }
1286 
1287         bool fix_h_not_v = fixh_w->get_selection() == 0;
1288         if (fix_h_not_v != graphics_preferences->screen_mode.fix_h_not_v) {
1289             graphics_preferences->screen_mode.fix_h_not_v = fix_h_not_v;
1290             changed = true;
1291         }
1292 
1293 	    bool hud = hud_w->get_selection() != 0;
1294 	    if (hud != graphics_preferences->screen_mode.hud)
1295 	    {
1296 		    graphics_preferences->screen_mode.hud = hud;
1297 		    changed = true;
1298 	    }
1299 
1300 	    short hud_scale = static_cast<short>(hud_scale_w->get_selection());
1301 	    if (hud_scale != graphics_preferences->screen_mode.hud_scale_level)
1302 	    {
1303 		    graphics_preferences->screen_mode.hud_scale_level = hud_scale;
1304 		    changed = true;
1305 	    }
1306 
1307 	    short term_scale = static_cast<short>(term_scale_w->get_selection());
1308 	    if (term_scale != graphics_preferences->screen_mode.term_scale_level)
1309 	    {
1310 		    graphics_preferences->screen_mode.term_scale_level = term_scale;
1311 		    changed = true;
1312 	    }
1313 
1314 		bool translucent_map = map_w->get_selection() != 0;
1315 		if (translucent_map != graphics_preferences->screen_mode.translucent_map) {
1316 			graphics_preferences->screen_mode.translucent_map = translucent_map;
1317 			changed = true;
1318 		}
1319 
1320 		bool camera_bob = bob_w->get_selection() != 0;
1321 		if (camera_bob != graphics_preferences->screen_mode.camera_bob) {
1322 			graphics_preferences->screen_mode.camera_bob = camera_bob;
1323 			changed = true;
1324 		}
1325 
1326 	    if (changed) {
1327 		    write_preferences();
1328 		    change_screen_mode(&graphics_preferences->screen_mode, true);
1329 		    clear_screen(true);
1330 		    parent->layout();
1331 		    parent->draw();		// DirectX seems to need this
1332 	    }
1333     }
1334 }
1335 
1336 /*
1337  *  Sound dialog
1338  */
1339 
1340 class w_toggle *stereo_w, *dynamic_w;
1341 
1342 class w_stereo_toggle : public w_toggle {
1343 public:
w_stereo_toggle(bool selection)1344 	w_stereo_toggle(bool selection) : w_toggle(selection) {}
1345 
selection_changed(void)1346 	void selection_changed(void)
1347 	{
1348 		// Turning off stereo turns off dynamic tracking
1349 		w_toggle::selection_changed();
1350 		if (selection == false)
1351 			dynamic_w->set_selection(false);
1352 	}
1353 };
1354 
1355 class w_dynamic_toggle : public w_toggle {
1356 public:
w_dynamic_toggle(bool selection)1357 	w_dynamic_toggle(bool selection) : w_toggle(selection) {}
1358 
selection_changed(void)1359 	void selection_changed(void)
1360 	{
1361 		// Turning on dynamic tracking turns on stereo
1362 		w_toggle::selection_changed();
1363 		if (selection == true)
1364 			stereo_w->set_selection(true);
1365 	}
1366 };
1367 
1368 static const char *channel_labels[] = {"1", "2", "4", "8", "16", "32", NULL};
1369 
1370 class w_volume_slider : public w_percentage_slider {
1371 public:
w_volume_slider(int vol)1372 	w_volume_slider(int vol) : w_percentage_slider(NUMBER_OF_SOUND_VOLUME_LEVELS, vol) {}
~w_volume_slider()1373 	~w_volume_slider() {}
1374 
item_selected(void)1375 	void item_selected(void)
1376 	{
1377 		SoundManager::instance()->TestVolume(selection, _snd_adjust_volume);
1378 	}
1379 };
1380 
sound_dialog(void * arg)1381 static void sound_dialog(void *arg)
1382 {
1383 	// Create dialog
1384 	dialog d;
1385 	vertical_placer *placer = new vertical_placer;
1386 	placer->dual_add(new w_title("SOUND SETUP"), d);
1387 	placer->add(new w_spacer(), true);
1388 
1389 	table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
1390 	table->col_flags(0, placeable::kAlignRight);
1391 
1392 	static const char *quality_labels[3] = {"8 Bit", "16 Bit", NULL};
1393 	w_toggle *quality_w = new w_toggle(TEST_FLAG(sound_preferences->flags, _16bit_sound_flag), quality_labels);
1394 	table->dual_add(quality_w->label("Quality"), d);
1395 	table->dual_add(quality_w, d);
1396 
1397 	stereo_w = new w_stereo_toggle(sound_preferences->flags & _stereo_flag);
1398 	table->dual_add(stereo_w->label("Stereo"), d);
1399 	table->dual_add(stereo_w, d);
1400 
1401 	dynamic_w = new w_dynamic_toggle(TEST_FLAG(sound_preferences->flags, _dynamic_tracking_flag));
1402 	table->dual_add(dynamic_w->label("Active Panning"), d);
1403 	table->dual_add(dynamic_w, d);
1404 
1405 	w_toggle *ambient_w = new w_toggle(TEST_FLAG(sound_preferences->flags, _ambient_sound_flag));
1406 	table->dual_add(ambient_w->label("Ambient Sounds"), d);
1407 	table->dual_add(ambient_w, d);
1408 
1409 	w_toggle *more_w = new w_toggle(TEST_FLAG(sound_preferences->flags, _more_sounds_flag));
1410 	table->dual_add(more_w->label("More Sounds"), d);
1411 	table->dual_add(more_w, d);
1412 
1413 	w_toggle *button_sounds_w = new w_toggle(TEST_FLAG(input_preferences->modifiers, _inputmod_use_button_sounds));
1414 	table->dual_add(button_sounds_w->label("Interface Button Sounds"), d);
1415 	table->dual_add(button_sounds_w, d);
1416 
1417 	w_select *channels_w = new w_select(static_cast<int>(std::floor(std::log(static_cast<float>(sound_preferences->channel_count)) / std::log(2.0) + 0.5)), channel_labels);
1418 	table->dual_add(channels_w->label("Channels"), d);
1419 	table->dual_add(channels_w, d);
1420 
1421 	w_volume_slider *volume_w = new w_volume_slider(sound_preferences->volume);
1422 	table->dual_add(volume_w->label("Volume"), d);
1423 	table->dual_add(volume_w, d);
1424 
1425 	w_slider *music_volume_w = new w_percentage_slider(NUMBER_OF_SOUND_VOLUME_LEVELS, sound_preferences->music);
1426 	table->dual_add(music_volume_w->label("Music Volume"), d);
1427 	table->dual_add(music_volume_w, d);
1428 
1429 
1430 	table->add_row(new w_spacer(), true);
1431 	table->dual_add_row(new w_static_text("Network Microphone"), d);
1432 
1433 	w_toggle* mute_while_transmitting_w = new w_toggle(!sound_preferences->mute_while_transmitting);
1434 	table->dual_add(mute_while_transmitting_w->label("Headset Mic Mode"), d);
1435 	table->dual_add(mute_while_transmitting_w, d);
1436 
1437 	table->add_row(new w_spacer(), true);
1438 	table->dual_add_row(new w_static_text("Experimental Sound Options"), d);
1439 		w_toggle *zrd_w = new w_toggle(TEST_FLAG(sound_preferences->flags, _zero_restart_delay));
1440 	table->dual_add(zrd_w->label("Zero Restart Delay"), d);
1441 	table->dual_add(zrd_w, d);
1442 
1443 	placer->add(table, true);
1444 
1445 	placer->add(new w_spacer(), true);
1446 
1447 	horizontal_placer *button_placer = new horizontal_placer;
1448 	button_placer->dual_add(new w_button("ACCEPT", dialog_ok, &d), d);
1449 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
1450 
1451 	placer->add(button_placer, true);
1452 
1453 	d.set_widget_placer(placer);
1454 	// Clear screen
1455 	clear_screen();
1456 
1457 	// Run dialog
1458 	if (d.run() == 0) {	// Accepted
1459 		bool changed = false;
1460 
1461 		uint16 flags = 0;
1462 		if (quality_w->get_selection()) flags |= _16bit_sound_flag;
1463 		if (stereo_w->get_selection()) flags |= _stereo_flag;
1464 		if (dynamic_w->get_selection()) flags |= _dynamic_tracking_flag;
1465 		if (ambient_w->get_selection()) flags |= _ambient_sound_flag;
1466 		if (more_w->get_selection()) flags |= _more_sounds_flag;
1467 		if (zrd_w->get_selection()) flags |= _zero_restart_delay;
1468 
1469 		if (flags != sound_preferences->flags) {
1470 			sound_preferences->flags = flags;
1471 			changed = true;
1472 		}
1473 
1474 		flags = input_preferences->modifiers & ~_inputmod_use_button_sounds;
1475 		if (button_sounds_w->get_selection()) flags |= _inputmod_use_button_sounds;
1476 		if (flags != input_preferences->modifiers) {
1477 			input_preferences->modifiers = flags;
1478 			changed = true;
1479 		}
1480 
1481 		int16 channel_count = 1 << (channels_w->get_selection() == UNONE ? 1 : channels_w->get_selection());
1482 		if (channel_count != sound_preferences->channel_count) {
1483 			sound_preferences->channel_count = channel_count;
1484 			changed = true;
1485 		}
1486 
1487 		int volume = volume_w->get_selection();
1488 		if (volume != sound_preferences->volume) {
1489 			sound_preferences->volume = volume;
1490 			changed = true;
1491 		}
1492 
1493 		int music_volume = music_volume_w->get_selection();
1494 		if (music_volume != sound_preferences->music) {
1495 			sound_preferences->music = music_volume;
1496 			changed = true;
1497 		}
1498 
1499 		bool mute_while_transmitting = !mute_while_transmitting_w->get_selection();
1500 		if (mute_while_transmitting != sound_preferences->mute_while_transmitting)
1501 		{
1502 			sound_preferences->mute_while_transmitting = mute_while_transmitting;
1503 			changed = true;
1504 		}
1505 
1506 		if (changed) {
1507 //			set_sound_manager_parameters(sound_preferences);
1508 			SoundManager::instance()->SetParameters(*sound_preferences);
1509 			write_preferences();
1510 		}
1511 	}
1512 }
1513 
1514 
1515 /*
1516  *  Controls dialog
1517  */
1518 
1519 const float kMinSensitivityLog = -3.0f;
1520 const float kMaxSensitivityLog = 3.0f;
1521 const float kSensitivityLogRange = kMaxSensitivityLog - kMinSensitivityLog;
1522 
1523 class w_sens_slider : public w_slider {
1524 public:
w_sens_slider(int num_items,int sel)1525 	w_sens_slider(int num_items, int sel) : w_slider(num_items, sel) {
1526 		init_formatted_value();
1527 	}
1528 
formatted_value(void)1529 	virtual std::string formatted_value(void) {
1530 		std::ostringstream ss;
1531 		float val = std::exp(selection * kSensitivityLogRange / 1000.0f + kMinSensitivityLog);
1532 		if (val >= 1.f)
1533 			ss.precision(4);
1534 		else if (val >= 0.1f)
1535 			ss.precision(3);
1536 		else if (val >= 0.01f)
1537 			ss.precision(2);
1538 		else
1539 			ss.precision(1);
1540 		ss << std::showpoint << val;
1541 		return ss.str();
1542 	}
1543 };
1544 
1545 class w_deadzone_slider : public w_slider {
1546 public:
w_deadzone_slider(int num_items,int sel)1547 	w_deadzone_slider(int num_items, int sel) : w_slider(num_items, sel) {
1548 		init_formatted_value();
1549 	}
1550 
formatted_value(void)1551 	virtual std::string formatted_value(void) {
1552 		std::ostringstream ss;
1553 		ss << selection << "%";
1554 		return ss.str();
1555 	}
1556 };
1557 
1558 
1559 const int NUM_KEYS = 21;
1560 
1561 static const char *action_name[NUM_KEYS] = {
1562 	"Move Forward", "Move Backward", "Turn Left", "Turn Right", "Sidestep Left", "Sidestep Right",
1563 	"Glance Left", "Glance Right", "Look Up", "Look Down", "Recenter View",
1564 	"Previous Weapon", "Next Weapon", "Trigger", "2nd Trigger",
1565 	"Turn -> Sidestep", "Run/Swim", "Move -> Look",
1566 	"Action", "Auto Map", "Microphone"
1567 };
1568 
1569 static key_binding_map default_key_bindings = {
1570 	{ 0, { SDL_SCANCODE_W,
1571 		   static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_NEGATIVE + SDL_CONTROLLER_AXIS_LEFTY)
1572 	} },
1573 	{ 1, { SDL_SCANCODE_S,
1574 			static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE + SDL_CONTROLLER_AXIS_LEFTY)
1575 	} },
1576 	{ 2, { SDL_SCANCODE_LEFT,
1577 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_NEGATIVE + SDL_CONTROLLER_AXIS_RIGHTX)
1578 	} },
1579 	{ 3, { SDL_SCANCODE_RIGHT,
1580 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE + SDL_CONTROLLER_AXIS_RIGHTX)
1581 	} },
1582 	{ 4, { SDL_SCANCODE_A,
1583 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_NEGATIVE + SDL_CONTROLLER_AXIS_LEFTX)
1584 	} },
1585 	{ 5, { SDL_SCANCODE_D,
1586 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE + SDL_CONTROLLER_AXIS_LEFTX)
1587 	} },
1588 	{ 6, { SDL_SCANCODE_Q,
1589 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_DPAD_LEFT)
1590 	} },
1591 	{ 7, { SDL_SCANCODE_E,
1592 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
1593 	} },
1594 	{ 8, { SDL_SCANCODE_UP,
1595 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_NEGATIVE + SDL_CONTROLLER_AXIS_RIGHTY)
1596 	} },
1597 	{ 9, { SDL_SCANCODE_DOWN,
1598 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE + SDL_CONTROLLER_AXIS_RIGHTY)
1599 	} },
1600 	{ 10, { SDL_SCANCODE_V,
1601 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_RIGHTSTICK)
1602 	} },
1603 	{ 11, { SDL_SCANCODE_F,
1604 		static_cast<SDL_Scancode>(AO_SCANCODE_MOUSESCROLL_UP),
1605 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
1606 	} },
1607 	{ 12, { SDL_SCANCODE_R,
1608 		static_cast<SDL_Scancode>(AO_SCANCODE_MOUSESCROLL_DOWN),
1609 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
1610 	} },
1611 	{ 13, { SDL_SCANCODE_SPACE,
1612 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_MOUSE_BUTTON + SDL_BUTTON_LEFT - 1),
1613 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE + SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
1614 	} },
1615 	{ 14, { SDL_SCANCODE_LSHIFT,
1616 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_MOUSE_BUTTON + SDL_BUTTON_RIGHT - 1),
1617 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE + SDL_CONTROLLER_AXIS_TRIGGERLEFT)
1618 	} },
1619 	{ 15, { SDL_SCANCODE_LALT
1620 	} },
1621 	{ 16, { SDL_SCANCODE_LCTRL,
1622 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_LEFTSTICK)
1623 	} },
1624 	{ 17, { SDL_SCANCODE_LGUI
1625 	} },
1626 	{ 18, { SDL_SCANCODE_TAB,
1627 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_A)
1628 	} },
1629 	{ 19, { SDL_SCANCODE_M,
1630 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_X)
1631 	} },
1632 	{ 20, { SDL_SCANCODE_GRAVE,
1633 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_Y)
1634 	} },
1635 };
1636 
1637 static const char *shell_action_name[NUMBER_OF_SHELL_KEYS] = {
1638 	"Inventory Left", "Inventory Right", "Switch Player View", "Volume Up", "Volume Down", "Zoom Map In", "Zoom Map Out", "Toggle FPS", "Chat/Console", "Network Stats"
1639 };
1640 
1641 static key_binding_map default_shell_key_bindings = {
1642 	{ 0, { SDL_SCANCODE_LEFTBRACKET
1643 	} },
1644 	{ 1, { SDL_SCANCODE_RIGHTBRACKET
1645 	} },
1646 	{ 2, { SDL_SCANCODE_BACKSPACE
1647 	} },
1648 	{ 3, { SDL_SCANCODE_PERIOD
1649 	} },
1650 	{ 4, { SDL_SCANCODE_COMMA
1651 	} },
1652 	{ 5, { SDL_SCANCODE_EQUALS,
1653 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_DPAD_UP)
1654 	} },
1655 	{ 6, { SDL_SCANCODE_MINUS,
1656 		static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_DPAD_DOWN)
1657 	} },
1658 	{ 7, { SDL_SCANCODE_SLASH
1659 	} },
1660 	{ 8, { SDL_SCANCODE_BACKSLASH
1661 	} },
1662 	{ 9, { SDL_SCANCODE_1
1663 	} },
1664 };
1665 
1666 class w_prefs_key;
1667 
1668 typedef std::multimap<int, w_prefs_key*> prefsKeyMap;
1669 typedef std::pair<int, w_prefs_key*> prefsKeyMapPair;
1670 static prefsKeyMap key_w;
1671 static prefsKeyMap shell_key_w;
1672 
1673 class w_prefs_key : public w_key {
1674 public:
w_prefs_key(SDL_Scancode key,w_key::Type event_type)1675 	w_prefs_key(SDL_Scancode key, w_key::Type event_type) : w_key(key, event_type) {}
1676 
set_key(SDL_Scancode new_key)1677 	void set_key(SDL_Scancode new_key)
1678 	{
1679 		// Key used for in-game function?
1680 		int error = NONE;
1681 		switch (new_key) {
1682 		case SDL_SCANCODE_F1:
1683 		case SDL_SCANCODE_F2:
1684 		case SDL_SCANCODE_F3:
1685 		case SDL_SCANCODE_F4:
1686 		case SDL_SCANCODE_F5:
1687 		case SDL_SCANCODE_F6:
1688 		case SDL_SCANCODE_F7:
1689 		case SDL_SCANCODE_F8:
1690 		case SDL_SCANCODE_F9:
1691 		case SDL_SCANCODE_F10:
1692 		case SDL_SCANCODE_F11:
1693 		case SDL_SCANCODE_F12:
1694 		case SDL_SCANCODE_ESCAPE: // (ZZZ: for quitting)
1695 		case AO_SCANCODE_JOYSTICK_ESCAPE:
1696 			error = keyIsUsedAlready;
1697 			break;
1698 
1699 		default:
1700 			break;
1701 		}
1702 		if (error != NONE) {
1703 			alert_user(infoError, strERRORS, error, 0);
1704 			return;
1705 		}
1706 
1707 		w_key::set_key(new_key);
1708 		dirty = true;
1709 		if (new_key == SDL_SCANCODE_UNKNOWN)
1710 			return;
1711 
1712 		// Remove binding to this key from all other widgets
1713 		for (auto it = key_w.begin(); it != key_w.end(); ++it) {
1714 			if (it->second != this && it->second->get_key() == new_key) {
1715 				it->second->set_key(SDL_SCANCODE_UNKNOWN);
1716 				it->second->dirty = true;
1717 			}
1718 		}
1719 		for (auto it = shell_key_w.begin(); it != shell_key_w.end(); ++it) {
1720 			if (it->second != this && it->second->get_key() == new_key) {
1721 				it->second->set_key(SDL_SCANCODE_UNKNOWN);
1722 				it->second->dirty = true;
1723 			}
1724 		}
1725 	}
1726 };
1727 
load_default_keys(void * arg)1728 static void load_default_keys(void *arg)
1729 {
1730 	for (int i = 0; i < NUM_KEYS; i++) {
1731 		SDL_Scancode kcode = SDL_SCANCODE_UNKNOWN;
1732 		SDL_Scancode mcode = SDL_SCANCODE_UNKNOWN;
1733 		SDL_Scancode jcode = SDL_SCANCODE_UNKNOWN;
1734 		for (auto it = default_key_bindings[i].begin(); it != default_key_bindings[i].end(); ++it) {
1735 			SDL_Scancode code = *it;
1736 			if (code == SDL_SCANCODE_UNKNOWN)
1737 				continue;
1738 			switch (w_key::event_type_for_key(code)) {
1739 				case w_key::MouseButton:
1740 					mcode = code;
1741 					break;
1742 				case w_key::JoystickButton:
1743 					jcode = code;
1744 					break;
1745 				case w_key::KeyboardKey:
1746 				default:
1747 					kcode = code;
1748 					break;
1749 			}
1750 		}
1751 		auto range = key_w.equal_range(i);
1752 		for (auto ik = range.first; ik != range.second; ++ik) {
1753 			w_prefs_key *pk = ik->second;
1754 			switch (pk->event_type) {
1755 				case w_key::MouseButton:
1756 					pk->set_key(mcode);
1757 					break;
1758 				case w_key::JoystickButton:
1759 					pk->set_key(jcode);
1760 					break;
1761 				case w_key::KeyboardKey:
1762 				default:
1763 					pk->set_key(kcode);
1764 					break;
1765 			}
1766 		}
1767 	}
1768 
1769 	for (int i = 0; i < NUMBER_OF_SHELL_KEYS; i++) {
1770 		SDL_Scancode kcode = SDL_SCANCODE_UNKNOWN;
1771 		SDL_Scancode mcode = SDL_SCANCODE_UNKNOWN;
1772 		SDL_Scancode jcode = SDL_SCANCODE_UNKNOWN;
1773 		for (auto it = default_shell_key_bindings[i].begin(); it != default_shell_key_bindings[i].end(); ++it) {
1774 			SDL_Scancode code = *it;
1775 			if (code == SDL_SCANCODE_UNKNOWN)
1776 				continue;
1777 			switch (w_key::event_type_for_key(code)) {
1778 				case w_key::MouseButton:
1779 					mcode = code;
1780 					break;
1781 				case w_key::JoystickButton:
1782 					jcode = code;
1783 					break;
1784 				case w_key::KeyboardKey:
1785 				default:
1786 					kcode = code;
1787 					break;
1788 			}
1789 		}
1790 		auto range = shell_key_w.equal_range(i);
1791 		for (auto ik = range.first; ik != range.second; ++ik) {
1792 			w_prefs_key *pk = ik->second;
1793 			switch (pk->event_type) {
1794 				case w_key::MouseButton:
1795 					pk->set_key(mcode);
1796 					break;
1797 				case w_key::JoystickButton:
1798 					pk->set_key(jcode);
1799 					break;
1800 				case w_key::KeyboardKey:
1801 				default:
1802 					pk->set_key(kcode);
1803 					break;
1804 			}
1805 		}
1806 	}
1807 
1808 	dialog *d = (dialog *)arg;
1809 	d->draw();
1810 }
1811 
unset_scancode(SDL_Scancode code)1812 static void unset_scancode(SDL_Scancode code)
1813 {
1814 	for (int i = 0; i < NUM_KEYS; ++i)
1815 		input_preferences->key_bindings[i].erase(code);
1816 	for (int i = 0; i < NUMBER_OF_SHELL_KEYS; ++i)
1817 		input_preferences->shell_key_bindings[i].erase(code);
1818 }
1819 
1820 enum {
1821 	KEYBOARD_TABS,
1822 	TAB_KEYS,
1823 	TAB_MORE_KEYS
1824 };
1825 
1826 const std::vector<std::string> mouse_feel_labels = { "Classic", "Modern", "(custom)" };
1827 static w_select_popup *mouse_feel_w;
1828 static w_select_popup *mouse_feel_details_w;
1829 static w_toggle *mouse_raw_w;
1830 static w_toggle *mouse_vertical_w;
1831 static w_toggle *mouse_accel_w;
1832 static w_toggle *mouse_precision_w;
1833 static bool inside_callback = false;  // prevent circular changes
1834 
mouse_feel_details_changed(void * arg)1835 static void mouse_feel_details_changed(void *arg)
1836 {
1837 	if (inside_callback)
1838 		return;
1839 	inside_callback = true;
1840 	switch (mouse_feel_details_w->get_selection())
1841 	{
1842 		case 0:
1843 			mouse_raw_w->set_selection(0);
1844 			mouse_accel_w->set_selection(1);
1845 			mouse_vertical_w->set_selection(1);
1846 			mouse_precision_w->set_selection(1);
1847 			break;
1848 		case 1:
1849 			mouse_raw_w->set_selection(1);
1850 			mouse_accel_w->set_selection(0);
1851 			mouse_vertical_w->set_selection(0);
1852 			mouse_precision_w->set_selection(0);
1853 			break;
1854 		default:
1855 			break;
1856 	}
1857 	inside_callback = false;
1858 }
1859 
update_mouse_feel_details(void * arg)1860 static void update_mouse_feel_details(void *arg)
1861 {
1862 	if (inside_callback)
1863 		return;
1864 	inside_callback = true;
1865 	if (mouse_raw_w->get_selection() == 0 &&
1866 		mouse_accel_w->get_selection() == 1 &&
1867 		mouse_vertical_w->get_selection() == 1 &&
1868 		mouse_precision_w->get_selection() == 1)
1869 	{
1870 		mouse_feel_details_w->set_selection(0);
1871 	}
1872 	else if (mouse_raw_w->get_selection() == 1 &&
1873 			 mouse_accel_w->get_selection() == 0 &&
1874 			 mouse_vertical_w->get_selection() == 0 &&
1875 			 mouse_precision_w->get_selection() == 0)
1876 	{
1877 		mouse_feel_details_w->set_selection(1);
1878 	}
1879 	else
1880 	{
1881 		mouse_feel_details_w->set_selection(2);
1882 	}
1883 	inside_callback = false;
1884 }
1885 
update_mouse_feel(void * arg)1886 static void update_mouse_feel(void *arg)
1887 {
1888 	if (input_preferences->raw_mouse_input == false &&
1889 		input_preferences->mouse_accel_type == _mouse_accel_classic &&
1890 		input_preferences->classic_vertical_aim == true &&
1891 		input_preferences->extra_mouse_precision == false)
1892 	{
1893 		mouse_feel_w->set_selection(0);
1894 	}
1895 	else if (input_preferences->raw_mouse_input == true &&
1896 			 input_preferences->mouse_accel_type == _mouse_accel_none &&
1897 			 input_preferences->classic_vertical_aim == false &&
1898 			 input_preferences->extra_mouse_precision == true)
1899 	{
1900 		mouse_feel_w->set_selection(1);
1901 	}
1902 	else
1903 	{
1904 		mouse_feel_w->set_selection(2);
1905 	}
1906 }
1907 
apply_mouse_feel(int selection)1908 static bool apply_mouse_feel(int selection)
1909 {
1910 	bool changed = false;
1911 	switch (selection)
1912 	{
1913 		case 0:
1914 			if (false != input_preferences->raw_mouse_input) {
1915 				input_preferences->raw_mouse_input = false;
1916 				changed = true;
1917 			}
1918 			if (_mouse_accel_classic != input_preferences->mouse_accel_type) {
1919 				input_preferences->mouse_accel_type = _mouse_accel_classic;
1920 				changed = true;
1921 			}
1922 			if (true != input_preferences->classic_vertical_aim) {
1923 				input_preferences->classic_vertical_aim = true;
1924 				changed = true;
1925 			}
1926 			if (false != input_preferences->extra_mouse_precision) {
1927 				input_preferences->extra_mouse_precision = false;
1928 				changed = true;
1929 			}
1930 			break;
1931 		case 1:
1932 			if (true != input_preferences->raw_mouse_input) {
1933 				input_preferences->raw_mouse_input = true;
1934 				changed = true;
1935 			}
1936 			if (_mouse_accel_none != input_preferences->mouse_accel_type) {
1937 				input_preferences->mouse_accel_type = _mouse_accel_none;
1938 				changed = true;
1939 			}
1940 			if (false != input_preferences->classic_vertical_aim) {
1941 				input_preferences->classic_vertical_aim = false;
1942 				changed = true;
1943 			}
1944 			if (true != input_preferences->extra_mouse_precision) {
1945 				input_preferences->extra_mouse_precision = true;
1946 				changed = true;
1947 			}
1948 			break;
1949 		default:
1950 			break;
1951 	}
1952 	return changed;
1953 }
1954 
mouse_custom_dialog(void * arg)1955 static void mouse_custom_dialog(void *arg)
1956 {
1957 	dialog d;
1958 	vertical_placer *placer = new vertical_placer;
1959 	placer->dual_add(new w_title("MOUSE ADVANCED"), d);
1960 	placer->add(new w_spacer());
1961 
1962 	table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
1963 	table->col_flags(0, placeable::kAlignRight);
1964 
1965 	float hSensitivity = ((float) input_preferences->sens_horizontal) / FIXED_ONE;
1966 	if (hSensitivity <= 0.0f) hSensitivity = 1.0f;
1967 	float hSensitivityLog = std::log(hSensitivity);
1968 	int hSliderPosition =
1969 		(int) ((hSensitivityLog - kMinSensitivityLog) * (1000.0f / kSensitivityLogRange) + 0.5f);
1970 	w_sens_slider *mouse_h_sens_w = new w_sens_slider(1000, hSliderPosition);
1971 	table->dual_add(mouse_h_sens_w->label("Horizontal Sensitivity"), d);
1972 	table->dual_add(mouse_h_sens_w, d);
1973 
1974 	float vSensitivity = ((float) input_preferences->sens_vertical) / FIXED_ONE;
1975 	if (vSensitivity <= 0.0f) vSensitivity = 1.0f;
1976 	float vSensitivityLog = std::log(vSensitivity);
1977 	int vSliderPosition =
1978 		(int) ((vSensitivityLog - kMinSensitivityLog) * (1000.0f / kSensitivityLogRange) + 0.5f);
1979 	w_sens_slider *mouse_v_sens_w = new w_sens_slider(1000, vSliderPosition);
1980 	table->dual_add(mouse_v_sens_w->label("Vertical Sensitivity"), d);
1981 	table->dual_add(mouse_v_sens_w, d);
1982 
1983 	w_toggle *mouse_v_invert_w = new w_toggle(input_preferences->modifiers & _inputmod_invert_mouse);
1984 	mouse_v_invert_w->set_selection_changed_callback(update_mouse_feel_details);
1985 	table->dual_add(mouse_v_invert_w->label("Invert Vertical Aim"), d);
1986 	table->dual_add(mouse_v_invert_w, d);
1987 
1988 	table->add_row(new w_spacer(), true);
1989 
1990 	mouse_feel_details_w = new w_select_popup();
1991 	mouse_feel_details_w->set_labels(mouse_feel_labels);
1992 	mouse_feel_details_w->set_selection(mouse_feel_w->get_selection());
1993 	mouse_feel_details_w->set_popup_callback(mouse_feel_details_changed, NULL);
1994 	table->dual_add(mouse_feel_details_w->label("Mouse Feel"), d);
1995 	table->dual_add(mouse_feel_details_w, d);
1996 
1997 	mouse_raw_w = new w_toggle(input_preferences->raw_mouse_input);
1998 	mouse_raw_w->set_selection_changed_callback(update_mouse_feel_details);
1999 	table->dual_add(mouse_raw_w->label("Raw Input Mode"), d);
2000 	table->dual_add(mouse_raw_w, d);
2001 
2002 	mouse_accel_w = new w_toggle(input_preferences->mouse_accel_type == _mouse_accel_classic);
2003 	mouse_accel_w->set_selection_changed_callback(update_mouse_feel_details);
2004 	table->dual_add(mouse_accel_w->label("Acceleration"), d);
2005 	table->dual_add(mouse_accel_w, d);
2006 
2007 	mouse_vertical_w = new w_toggle(input_preferences->classic_vertical_aim);
2008 	mouse_vertical_w->set_selection_changed_callback(update_mouse_feel_details);
2009 	table->dual_add(mouse_vertical_w->label("Adjust Vertical Speed"), d);
2010 	table->dual_add(mouse_vertical_w, d);
2011 
2012 	mouse_precision_w = new w_toggle(!input_preferences->extra_mouse_precision);
2013 	mouse_precision_w->set_selection_changed_callback(update_mouse_feel_details);
2014 	table->dual_add(mouse_precision_w->label("Snap View to Weapon Aim"), d);
2015 	table->dual_add(mouse_precision_w, d);
2016 
2017 	placer->add(table);
2018 	placer->add(new w_spacer(), true);
2019 
2020 	horizontal_placer *button_placer = new horizontal_placer;
2021 	button_placer->dual_add(new w_button("ACCEPT", dialog_ok, &d), d);
2022 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
2023 	placer->add(button_placer, true);
2024 
2025 	d.set_widget_placer(placer);
2026 	mouse_feel_details_changed(NULL);
2027 
2028 	// Run dialog
2029 	if (d.run() == 0) {	// Accepted
2030 		bool changed = false;
2031 
2032 		int hPos = mouse_h_sens_w->get_selection();
2033 		float hLog = kMinSensitivityLog + ((float) hPos) * (kSensitivityLogRange / 1000.0f);
2034 		_fixed hNorm = _fixed(std::exp(hLog) * FIXED_ONE);
2035 		if (hNorm != input_preferences->sens_horizontal) {
2036 			input_preferences->sens_horizontal = hNorm;
2037 			changed = true;
2038 		}
2039 
2040 		int vPos = mouse_v_sens_w->get_selection();
2041 		float vLog = kMinSensitivityLog + ((float) vPos) * (kSensitivityLogRange / 1000.0f);
2042 		_fixed vNorm = _fixed(std::exp(vLog) * FIXED_ONE);
2043 		if (vNorm != input_preferences->sens_vertical) {
2044 			input_preferences->sens_vertical = vNorm;
2045 			changed = true;
2046 		}
2047 
2048 		uint16 flags = input_preferences->modifiers;
2049 		if (mouse_v_invert_w->get_selection()) {
2050 			flags |= _inputmod_invert_mouse;
2051 		} else {
2052 			flags &= ~_inputmod_invert_mouse;
2053 		}
2054 		if (flags != input_preferences->modifiers) {
2055 			input_preferences->modifiers = flags;
2056 			changed = true;
2057 		}
2058 
2059 		if (mouse_raw_w->get_selection() != input_preferences->raw_mouse_input) {
2060 			input_preferences->raw_mouse_input = mouse_raw_w->get_selection();
2061 			changed = true;
2062 		}
2063 
2064 		if (mouse_accel_w->get_selection() != input_preferences->mouse_accel_type) {
2065 			input_preferences->mouse_accel_type = mouse_accel_w->get_selection();
2066 			changed = true;
2067 		}
2068 
2069 		bool vert = mouse_vertical_w->get_selection();
2070 		if (vert != input_preferences->classic_vertical_aim) {
2071 			input_preferences->classic_vertical_aim = vert;
2072 			changed = true;
2073 		}
2074 
2075 		bool precision = (mouse_precision_w->get_selection() == 0);
2076 		if (precision != input_preferences->extra_mouse_precision) {
2077 			input_preferences->extra_mouse_precision = precision;
2078 			changed = true;
2079 		}
2080 
2081 		if (changed) {
2082 			write_preferences();
2083 		}
2084 		update_mouse_feel(NULL);
2085 	}
2086 }
2087 
2088 
controller_details_dialog(void * arg)2089 static void controller_details_dialog(void *arg)
2090 {
2091 	dialog d;
2092 	vertical_placer *placer = new vertical_placer;
2093 	placer->dual_add(new w_title("CONTROLLER ADVANCED"), d);
2094 	placer->add(new w_spacer());
2095 
2096 	table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
2097 	table->col_flags(0, placeable::kAlignRight);
2098 
2099 	float joySensitivity = ((float) input_preferences->controller_sensitivity) / FIXED_ONE;
2100 	if (joySensitivity <= 0.0f) joySensitivity = 1.0f;
2101 	float joySensitivityLog = std::log(joySensitivity);
2102 	int joySliderPosition =
2103 	(int) ((joySensitivityLog - kMinSensitivityLog) * (1000.0f / kSensitivityLogRange) + 0.5f);
2104 
2105 	w_sens_slider* sens_joy_w = new w_sens_slider(1000, joySliderPosition);
2106 	table->dual_add(sens_joy_w->label("Aiming Sensitivity"), d);
2107 	table->dual_add(sens_joy_w, d);
2108 
2109 	int joyDeadzone = (int)((input_preferences->controller_deadzone / 655.36f) + 0.5f);
2110 	w_deadzone_slider* dead_joy_w = new w_deadzone_slider(11, joyDeadzone);
2111 	table->dual_add(dead_joy_w->label("Analog Dead Zone"), d);
2112 	table->dual_add(dead_joy_w, d);
2113 
2114 	table->add_row(new w_spacer(), true);
2115 	placer->add(table, true);
2116 
2117 	horizontal_placer *button_placer = new horizontal_placer;
2118 	button_placer->dual_add(new w_button("ACCEPT", dialog_ok, &d), d);
2119 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
2120 	placer->add(button_placer, true);
2121 
2122 	d.set_widget_placer(placer);
2123 
2124 	// Run dialog
2125 	if (d.run() == 0) {	// Accepted
2126 		bool changed = false;
2127 
2128 		int sensPos = sens_joy_w->get_selection();
2129 		float sensLog = kMinSensitivityLog + ((float) sensPos) * (kSensitivityLogRange / 1000.0f);
2130 		_fixed sensNorm = _fixed(std::exp(sensLog) * FIXED_ONE);
2131 		if (sensNorm != input_preferences->controller_sensitivity) {
2132 			input_preferences->controller_sensitivity = sensNorm;
2133 			changed = true;
2134 		}
2135 
2136 		int deadPos = dead_joy_w->get_selection();
2137 		int deadNorm = deadPos * 655.36f;
2138 		if (deadNorm != input_preferences->controller_deadzone) {
2139 			input_preferences->controller_deadzone = deadNorm;
2140 			changed = true;
2141 		}
2142 
2143 		if (changed) {
2144 			write_preferences();
2145 		}
2146 	}
2147 }
2148 
2149 
controls_dialog(void * arg)2150 static void controls_dialog(void *arg)
2151 {
2152 	// Clear array of key widgets (because w_prefs_key::set_key() scans it)
2153 	key_w.clear();
2154 	shell_key_w.clear();
2155 
2156 	// Create dialog
2157 	dialog d;
2158 	vertical_placer *placer = new vertical_placer;
2159 	placer->dual_add(new w_title("CONTROLS"), d);
2160 	placer->add(new w_spacer());
2161 
2162 	// create all key widgets
2163 	for (int i = 0; i < NUM_KEYS; i++) {
2164 		SDL_Scancode kcode = SDL_SCANCODE_UNKNOWN;
2165 		SDL_Scancode mcode = SDL_SCANCODE_UNKNOWN;
2166 		SDL_Scancode jcode = SDL_SCANCODE_UNKNOWN;
2167 		for (std::set<SDL_Scancode>::const_iterator bit = input_preferences->key_bindings[i].begin(); bit != input_preferences->key_bindings[i].end(); ++bit) {
2168 			SDL_Scancode code = *bit;
2169 			if (code >= AO_SCANCODE_BASE_JOYSTICK_BUTTON && code < (AO_SCANCODE_BASE_JOYSTICK_BUTTON + NUM_SDL_JOYSTICK_BUTTONS)) {
2170 				jcode = code;
2171 			} else if (code >= AO_SCANCODE_BASE_MOUSE_BUTTON && code < (AO_SCANCODE_BASE_MOUSE_BUTTON + NUM_SDL_MOUSE_BUTTONS)) {
2172 				mcode = code;
2173 			} else {
2174 				kcode = code;
2175 			}
2176 		}
2177 		key_w.insert(prefsKeyMapPair(i, new w_prefs_key(kcode, w_key::KeyboardKey)));
2178 		key_w.insert(prefsKeyMapPair(i, new w_prefs_key(mcode, w_key::MouseButton)));
2179 		key_w.insert(prefsKeyMapPair(i, new w_prefs_key(jcode, w_key::JoystickButton)));
2180 	}
2181 	for (int i = 0; i < NUMBER_OF_SHELL_KEYS; i++) {
2182 		SDL_Scancode kcode = SDL_SCANCODE_UNKNOWN;
2183 		SDL_Scancode mcode = SDL_SCANCODE_UNKNOWN;
2184 		SDL_Scancode jcode = SDL_SCANCODE_UNKNOWN;
2185 		for (std::set<SDL_Scancode>::const_iterator bit = input_preferences->shell_key_bindings[i].begin(); bit != input_preferences->shell_key_bindings[i].end(); ++bit) {
2186 			SDL_Scancode code = *bit;
2187 			if (code >= AO_SCANCODE_BASE_JOYSTICK_BUTTON && code < (AO_SCANCODE_BASE_JOYSTICK_BUTTON + NUM_SDL_JOYSTICK_BUTTONS)) {
2188 				jcode = code;
2189 			} else if (code >= AO_SCANCODE_BASE_MOUSE_BUTTON && code < (AO_SCANCODE_BASE_MOUSE_BUTTON + NUM_SDL_MOUSE_BUTTONS)) {
2190 				mcode = code;
2191 			} else {
2192 				kcode = code;
2193 			}
2194 		}
2195 		shell_key_w.insert(prefsKeyMapPair(i, new w_prefs_key(kcode, w_key::KeyboardKey)));
2196 		shell_key_w.insert(prefsKeyMapPair(i, new w_prefs_key(mcode, w_key::MouseButton)));
2197 		shell_key_w.insert(prefsKeyMapPair(i, new w_prefs_key(jcode, w_key::JoystickButton)));
2198 	}
2199 
2200 	tab_placer* tabs = new tab_placer();
2201 
2202 	std::vector<std::string> labels = { "AIM", "MOVE", "ACTIONS", "INTERFACE", "OTHER" };
2203 	w_tab *tab_w = new w_tab(labels, tabs);
2204 
2205 	placer->dual_add(tab_w, d);
2206 	placer->add(new w_spacer(), true);
2207 
2208 	vertical_placer *move = new vertical_placer();
2209 	table_placer *move_table = new table_placer(4, get_theme_space(ITEM_WIDGET), true);
2210 	move_table->col_flags(0, placeable::kAlignRight);
2211 	move_table->col_flags(1, placeable::kAlignLeft);
2212 	move_table->col_flags(2, placeable::kAlignLeft);
2213 	move_table->col_flags(3, placeable::kAlignLeft);
2214 	move_table->add(new w_spacer(), true);
2215 	move_table->dual_add(new w_label("Keyboard"), d);
2216 	move_table->dual_add(new w_label("Mouse"), d);
2217 	move_table->dual_add(new w_label("Controller"), d);
2218 
2219 	std::vector<int> move_keys = { 0, 1, 4, 5, -1, 16, 15, 17 };
2220 	for (auto it = move_keys.begin(); it != move_keys.end(); ++it) {
2221 		if (*it < 0) {
2222 			move_table->add_row(new w_spacer(), true);
2223 		} else if (*it >= 100) {
2224 			int i = *it - 100;
2225 			move_table->dual_add(new w_label(shell_action_name[i]), d);
2226 			auto range = shell_key_w.equal_range(i);
2227 			for (auto ik = range.first; ik != range.second; ++ik) {
2228 				move_table->dual_add(ik->second, d);
2229 			}
2230 		} else {
2231 			int i = *it;
2232 			move_table->dual_add(new w_label(action_name[i]), d);
2233 			auto range = key_w.equal_range(i);
2234 			for (auto ik = range.first; ik != range.second; ++ik) {
2235 				if ((ik->second->event_type == w_key::MouseButton) &&
2236 					(i == 0 || i == 1 || i == 4 || i == 5))
2237 					move_table->dual_add(new w_label(""), d);
2238 				else
2239 					move_table->dual_add(ik->second, d);
2240 			}
2241 		}
2242 	}
2243 	move->add(move_table, true);
2244 	move->add(new w_spacer(), true);
2245 
2246 	table_placer *move_options = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
2247 	move_options->col_flags(0, placeable::kAlignRight);
2248 
2249 	w_toggle *always_run_w = new w_toggle(input_preferences->modifiers & _inputmod_interchange_run_walk);
2250 	move_options->dual_add(always_run_w->label("Always Run"), d);
2251 	move_options->dual_add(always_run_w, d);
2252 
2253 	w_toggle *always_swim_w = new w_toggle(TEST_FLAG(input_preferences->modifiers, _inputmod_interchange_swim_sink));
2254 	move_options->dual_add(always_swim_w->label("Always Swim"), d);
2255 	move_options->dual_add(always_swim_w, d);
2256 
2257 	move->add(move_options, true);
2258 
2259 	vertical_placer *look = new vertical_placer();
2260 	table_placer *look_table = new table_placer(4, get_theme_space(ITEM_WIDGET), true);
2261 	look_table->col_flags(0, placeable::kAlignRight);
2262 	look_table->col_flags(1, placeable::kAlignLeft);
2263 	look_table->col_flags(2, placeable::kAlignLeft);
2264 	look_table->col_flags(3, placeable::kAlignLeft);
2265 	look_table->add(new w_spacer(), true);
2266 	look_table->dual_add(new w_label("Keyboard"), d);
2267 	look_table->dual_add(new w_label("Mouse"), d);
2268 	look_table->dual_add(new w_label("Controller"), d);
2269 
2270 	std::vector<int> look_keys = { 8, 9, 2, 3, -1, 6, 7, 10 };
2271 	for (auto it = look_keys.begin(); it != look_keys.end(); ++it) {
2272 		if (*it < 0) {
2273 			look_table->add_row(new w_spacer(), true);
2274 		} else if (*it >= 100) {
2275 			int i = *it - 100;
2276 			look_table->dual_add(new w_label(shell_action_name[i]), d);
2277 			auto range = shell_key_w.equal_range(i);
2278 			for (auto ik = range.first; ik != range.second; ++ik) {
2279 				look_table->dual_add(ik->second, d);
2280 			}
2281 		} else {
2282 			int i = *it;
2283 			look_table->dual_add(new w_label(action_name[i]), d);
2284 			auto range = key_w.equal_range(i);
2285 			for (auto ik = range.first; ik != range.second; ++ik) {
2286 				if (ik->second->event_type == w_key::MouseButton) {
2287 					w_text_entry* txt = NULL;
2288 					switch (i) {
2289 						case 8:
2290 							txt = new w_text_entry(12, "move up");
2291 							break;
2292 						case 9:
2293 							txt = new w_text_entry(12, "move down");
2294 							break;
2295 						case 2:
2296 							txt = new w_text_entry(12, "move left");
2297 							break;
2298 						case 3:
2299 							txt = new w_text_entry(12, "move right");
2300 							break;
2301 						default:
2302 							break;
2303 					}
2304 					if (txt) {
2305 						txt->set_enabled(false);
2306 						txt->set_min_width(50);
2307 						look_table->dual_add(txt, d);
2308 						continue;
2309 					}
2310 				}
2311 				look_table->dual_add(ik->second, d);
2312 			}
2313 		}
2314 	}
2315 	look->add(look_table, true);
2316 	look->add(new w_spacer(), true);
2317 
2318 	table_placer *look_options = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
2319 	look_options->col_flags(0, placeable::kAlignRight);
2320 
2321 	w_toggle* auto_recenter_w = new w_toggle(!(input_preferences->modifiers & _inputmod_dont_auto_recenter));
2322 	look_options->dual_add(auto_recenter_w->label("Auto-Recenter View"), d);
2323 	look_options->dual_add(auto_recenter_w, d);
2324 
2325 	look_options->add_row(new w_spacer(), true);
2326 
2327 	table_placer *mouse_options = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
2328 	mouse_options->col_flags(0, placeable::kAlignRight);
2329 	mouse_options->col_flags(1, placeable::kAlignLeft);
2330 
2331 	w_toggle *enable_mouse_w = new w_toggle(input_preferences->input_device == _mouse_yaw_pitch);
2332 	mouse_options->dual_add(enable_mouse_w->label("Mouse Aiming"), d);
2333 	mouse_options->dual_add(enable_mouse_w, d);
2334 
2335 	mouse_feel_w = new w_select_popup();
2336 	mouse_feel_w->set_labels(mouse_feel_labels);
2337 	update_mouse_feel(NULL);
2338 	mouse_options->dual_add(mouse_feel_w->label("Mouse Feel"), d);
2339 	mouse_options->dual_add(mouse_feel_w, d);
2340 
2341 	mouse_options->add_row(new w_spacer(), true);
2342 	mouse_options->dual_add_row(new w_button("MOUSE ADVANCED", mouse_custom_dialog, &d), d);
2343 
2344 	look_options->add(mouse_options, true);
2345 
2346 	table_placer *controller_options = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
2347 	controller_options->col_flags(0, placeable::kAlignRight);
2348 	controller_options->col_flags(1, placeable::kAlignLeft);
2349 
2350 	controller_options->dual_add_row(new w_label(""), d);
2351 	std::vector<std::string> joystick_aiming_labels = { "Treat as Analog Stick", "Treat as D-Pad" };
2352 	w_select_popup *joystick_aiming_w = new w_select_popup();
2353 	joystick_aiming_w->set_labels(joystick_aiming_labels);
2354 	joystick_aiming_w->set_selection(input_preferences->controller_analog ? 0 : 1);
2355 	controller_options->dual_add(joystick_aiming_w->label("Controller Feel"), d);
2356 	controller_options->dual_add(joystick_aiming_w, d);
2357 
2358 	controller_options->add_row(new w_spacer(), true);
2359 	controller_options->dual_add_row(new w_button("CONTROLLER ADVANCED", controller_details_dialog, &d), d);
2360 
2361 	look_options->add(controller_options, true);
2362 
2363 	look->add(look_options, true);
2364 
2365 	vertical_placer *actions = new vertical_placer();
2366 	table_placer *actions_table = new table_placer(4, get_theme_space(ITEM_WIDGET), true);
2367 	actions_table->col_flags(0, placeable::kAlignRight);
2368 	actions_table->col_flags(1, placeable::kAlignLeft);
2369 	actions_table->col_flags(2, placeable::kAlignLeft);
2370 	actions_table->col_flags(3, placeable::kAlignLeft);
2371 	actions_table->add(new w_spacer(), true);
2372 	actions_table->dual_add(new w_label("Keyboard"), d);
2373 	actions_table->dual_add(new w_label("Mouse"), d);
2374 	actions_table->dual_add(new w_label("Controller"), d);
2375 
2376 	std::vector<int> actions_keys = { 13, 14, 11, 12, -1, 18, -1, 20, 108 };
2377 	for (auto it = actions_keys.begin(); it != actions_keys.end(); ++it) {
2378 		if (*it < 0) {
2379 			actions_table->add_row(new w_spacer(), true);
2380 		} else if (*it >= 100) {
2381 			int i = *it - 100;
2382 			actions_table->dual_add(new w_label(shell_action_name[i]), d);
2383 			auto range = shell_key_w.equal_range(i);
2384 			for (auto ik = range.first; ik != range.second; ++ik) {
2385 				actions_table->dual_add(ik->second, d);
2386 			}
2387 		} else {
2388 			int i = *it;
2389 			actions_table->dual_add(new w_label(action_name[i]), d);
2390 			auto range = key_w.equal_range(i);
2391 			for (auto ik = range.first; ik != range.second; ++ik) {
2392 				actions_table->dual_add(ik->second, d);
2393 			}
2394 		}
2395 	}
2396 	actions->add(actions_table, true);
2397 	actions->add(new w_spacer(), true);
2398 
2399 	table_placer *actions_options = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
2400 	actions_options->col_flags(0, placeable::kAlignRight);
2401 
2402 	w_toggle *weapon_w = new w_toggle(!(input_preferences->modifiers & _inputmod_dont_switch_to_new_weapon));
2403 	actions_options->dual_add(weapon_w->label("Auto-Switch Weapons"), d);
2404 	actions_options->dual_add(weapon_w, d);
2405 
2406 	actions->add(actions_options, true);
2407 
2408 	actions->add(new w_spacer(), true);
2409 	actions->dual_add(new w_static_text("Warning: Auto-Switch Weapons is always ON in"), d);
2410 	actions->dual_add(new w_static_text("network play.  Turning it OFF will also disable"), d);
2411 	actions->dual_add(new w_static_text("film recording for single-player games."), d);
2412 
2413 
2414 	vertical_placer *iface = new vertical_placer();
2415 	table_placer *interface_table = new table_placer(4, get_theme_space(ITEM_WIDGET), true);
2416 	interface_table->col_flags(0, placeable::kAlignRight);
2417 	interface_table->col_flags(1, placeable::kAlignLeft);
2418 	interface_table->col_flags(2, placeable::kAlignLeft);
2419 	interface_table->col_flags(3, placeable::kAlignLeft);
2420 	interface_table->add(new w_spacer(), true);
2421 	interface_table->dual_add(new w_label("Keyboard"), d);
2422 	interface_table->dual_add(new w_label("Mouse"), d);
2423 	interface_table->dual_add(new w_label("Controller"), d);
2424 
2425 	std::vector<int> interface_keys = { 19, 105, 106, -1, 103, 104, -1, 100, 101, -1, 102, 107, 109, -1, -2 };
2426 	for (auto it = interface_keys.begin(); it != interface_keys.end(); ++it) {
2427 		if (*it == -2) {
2428 			interface_table->dual_add(new w_label("Exit Game"), d);
2429 			w_prefs_key *kb = new w_prefs_key(SDL_SCANCODE_ESCAPE, w_key::KeyboardKey);
2430 			kb->set_enabled(false);
2431 			interface_table->dual_add(kb, d);
2432 			interface_table->dual_add(new w_label(""), d);
2433 			w_prefs_key *cn = new w_prefs_key(AO_SCANCODE_JOYSTICK_ESCAPE, w_key::JoystickButton);
2434 			cn->set_enabled(false);
2435 			interface_table->dual_add(cn, d);
2436 		} else if (*it < 0) {
2437 			interface_table->add_row(new w_spacer(), true);
2438 		} else if (*it >= 100) {
2439 			int i = *it - 100;
2440 			interface_table->dual_add(new w_label(shell_action_name[i]), d);
2441 			auto range = shell_key_w.equal_range(i);
2442 			for (auto ik = range.first; ik != range.second; ++ik) {
2443 				interface_table->dual_add(ik->second, d);
2444 			}
2445 		} else {
2446 			int i = *it;
2447 			interface_table->dual_add(new w_label(action_name[i]), d);
2448 			auto range = key_w.equal_range(i);
2449 			for (auto ik = range.first; ik != range.second; ++ik) {
2450 				interface_table->dual_add(ik->second, d);
2451 			}
2452 		}
2453 	}
2454 	iface->add(interface_table, true);
2455 
2456 	vertical_placer *other = new vertical_placer();
2457 	other->dual_add(new w_static_text("These keyboard shortcuts cannot be changed."), d);
2458 	other->add(new w_spacer());
2459 
2460 	table_placer *other_table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
2461 	other_table->dual_add(new w_label("Main Menu"), d);
2462 	other_table->dual_add(new w_label("In Game"), d);
2463 
2464 	table_placer *other_menu = new table_placer(2, get_theme_space(ITEM_WIDGET), false);
2465 	other_menu->col_flags(0, placeable::kAlignRight);
2466 	other_menu->col_flags(1, placeable::kAlignLeft);
2467 	std::vector<std::string> menu_shortcuts = {
2468 		"N", "Begin new game",
2469 #if (defined(__APPLE__) && defined(__MACH__))
2470 		"Cmd-Option-N", "Level select",
2471 #else
2472 		"Ctrl+Shift+N", "Level select",
2473 #endif
2474 		"O", "Continue saved game",
2475 		"G", "Gather network game",
2476 		"J", "Join network game",
2477 		"R", "Replay saved film",
2478 		"P", "Preferences",
2479 		"Q", "Quit",
2480 		"C", "Scenario credits",
2481 		"A", "About Aleph One",
2482 #if (defined(__APPLE__) && defined(__MACH__))
2483 		"Cmd-Return", "Toggle fullscreen",
2484 #else
2485 		"Alt+Enter", "Toggle fullscreen",
2486 #endif
2487 	};
2488 	for (auto it = menu_shortcuts.begin(); it != menu_shortcuts.end(); ++it) {
2489 		other_menu->dual_add(new w_label(it->c_str()), d);
2490 	}
2491 	other_table->add(other_menu, true);
2492 
2493 	table_placer *other_game = new table_placer(2, get_theme_space(ITEM_WIDGET), false);
2494 	other_game->col_flags(0, placeable::kAlignRight);
2495 	other_game->col_flags(1, placeable::kAlignLeft);
2496 	std::vector<std::string> game_shortcuts = {
2497 		"F1", "Decrease resolution",
2498 		"F2", "Increase resolution",
2499 		"F8", "Crosshairs",
2500 		"F9", "Screenshot",
2501 		"F10", "Debug info",
2502 		"F11", "Decrease brightness",
2503 		"F12", "Increase brightness",
2504 #if (defined(__APPLE__) && defined(__MACH__))
2505 		"Cmd-Return", "Toggle fullscreen",
2506 #else
2507 		"Alt+Enter", "Toggle fullscreen",
2508 #endif
2509 		"Escape", "Exit game"
2510 	};
2511 	for (auto it = game_shortcuts.begin(); it != game_shortcuts.end(); ++it) {
2512 		other_game->dual_add(new w_label(it->c_str()), d);
2513 	}
2514 	other_table->add(other_game, true);
2515 
2516 	other->add(other_table, true);
2517 
2518 	tabs->add(look, true);
2519 	tabs->add(move, true);
2520 	tabs->add(actions, true);
2521 	tabs->add(iface, true);
2522 	tabs->add(other, true);
2523 	placer->add(tabs, true);
2524 
2525 	placer->add(new w_spacer(), true);
2526 	placer->dual_add(new w_button("RESET ALL KEYS TO DEFAULTS", load_default_keys, &d), d);
2527 	placer->add(new w_spacer(), true);
2528 
2529 	horizontal_placer *button_placer = new horizontal_placer;
2530 	button_placer->dual_add(new w_button("ACCEPT", dialog_ok, &d), d);
2531 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
2532 	placer->add(button_placer, true);
2533 
2534 	d.set_widget_placer(placer);
2535 
2536 	// Clear screen
2537 	clear_screen();
2538 
2539 	enter_joystick();
2540 
2541 	// Run dialog
2542 	if (d.run() == 0) {	// Accepted
2543 		bool changed = false;
2544 
2545 		uint16 flags = input_preferences->modifiers & (_inputmod_use_button_sounds|_inputmod_invert_mouse);
2546 		if (always_run_w->get_selection()) flags |= _inputmod_interchange_run_walk;
2547 		if (always_swim_w->get_selection()) flags |= _inputmod_interchange_swim_sink;
2548 		if (!(weapon_w->get_selection())) flags |= _inputmod_dont_switch_to_new_weapon;
2549 		if (!(auto_recenter_w->get_selection())) flags |= _inputmod_dont_auto_recenter;
2550 
2551 		if (flags != input_preferences->modifiers) {
2552 			input_preferences->modifiers = flags;
2553 			changed = true;
2554 		}
2555 
2556 		for (int i = 0; i < NUM_KEYS; i++) {
2557 			input_preferences->key_bindings[i].clear();
2558 		}
2559 		for (int i = 0; i < NUMBER_OF_SHELL_KEYS; i++) {
2560 			input_preferences->shell_key_bindings[i].clear();
2561 		}
2562 
2563 		for (auto it = key_w.begin(); it != key_w.end(); ++it) {
2564 			int i = it->first;
2565 			SDL_Scancode key = it->second->get_key();
2566 			if (key != SDL_SCANCODE_UNKNOWN) {
2567 				unset_scancode(key);
2568 				input_preferences->key_bindings[i].insert(key);
2569 				changed = true;
2570 			}
2571 		}
2572 
2573 		for (auto it = shell_key_w.begin(); it != shell_key_w.end(); ++it) {
2574 			int i = it->first;
2575 			SDL_Scancode key = it->second->get_key();
2576 			if (key != SDL_SCANCODE_UNKNOWN) {
2577 				unset_scancode(key);
2578 				input_preferences->shell_key_bindings[i].insert(key);
2579 				changed = true;
2580 			}
2581 		}
2582 
2583 		int16 device = enable_mouse_w->get_selection() ? _mouse_yaw_pitch : _keyboard_or_game_pad;
2584 		if (input_preferences->input_device != device) {
2585 			input_preferences->input_device = device;
2586 			changed = true;
2587 		}
2588 
2589 		bool jaim = (joystick_aiming_w->get_selection() == 1);
2590 		if (input_preferences->controller_analog != jaim) {
2591 			input_preferences->controller_analog = jaim;
2592 			changed = true;
2593 		}
2594 
2595 		if (apply_mouse_feel(mouse_feel_w->get_selection())) {
2596 			changed = true;
2597 		}
2598 
2599 		if (changed)
2600 			write_preferences();
2601 	}
2602 
2603 	exit_joystick();
2604 }
2605 
2606 extern void ResetAllMMLValues();
2607 
plugins_dialog(void *)2608 static void plugins_dialog(void *)
2609 {
2610 	dialog d;
2611 	vertical_placer *placer = new vertical_placer;
2612 	w_title *w_header = new w_title("PLUGINS");
2613 	placer->dual_add(w_header, d);
2614 	placer->add(new w_spacer, true);
2615 
2616 	std::vector<Plugin> plugins(Plugins::instance()->begin(), Plugins::instance()->end());
2617 	w_plugins* plugins_w = new w_plugins(plugins, 400, 7);
2618 	placer->dual_add(plugins_w, d);
2619 
2620 	placer->add(new w_spacer, true);
2621 
2622 	horizontal_placer* button_placer = new horizontal_placer;
2623 	w_button* accept_w = new w_button("ACCEPT", dialog_ok, &d);
2624 	button_placer->dual_add(accept_w, d);
2625 	w_button* cancel_w = new w_button("CANCEL", dialog_cancel, &d);
2626 	button_placer->dual_add(cancel_w, d);
2627 
2628 	placer->add(button_placer, true);
2629 
2630 	d.set_widget_placer(placer);
2631 	d.activate_widget(plugins_w);
2632 
2633 	if (d.run() == 0) {
2634 		bool changed = false;
2635 		Plugins::iterator plugin = Plugins::instance()->begin();
2636 		for (Plugins::iterator it = plugins.begin(); it != plugins.end(); ++it, ++plugin) {
2637 			changed |= (plugin->enabled != it->enabled);
2638 			plugin->enabled = it->enabled;
2639 		}
2640 
2641 		if (changed) {
2642 			Plugins::instance()->invalidate();
2643 			write_preferences();
2644 
2645 			ResetAllMMLValues();
2646 			LoadBaseMMLScripts();
2647 			Plugins::instance()->load_mml();
2648 		}
2649 	}
2650 }
2651 
2652 
2653 /*
2654  *  Environment dialog
2655  */
2656 
2657 static const char* film_profile_labels[] = {
2658 	"Aleph One",
2659 	"Marathon 2",
2660 	"Marathon Infinity",
2661 	0
2662 };
2663 
environment_dialog(void * arg)2664 static void environment_dialog(void *arg)
2665 {
2666 	dialog *parent = (dialog *)arg;
2667 
2668 	// Create dialog
2669 	dialog d;
2670 	vertical_placer *placer = new vertical_placer;
2671 	w_title *w_header = new w_title("ENVIRONMENT SETTINGS");
2672 	placer->dual_add(w_header, d);
2673 	placer->add(new w_spacer, true);
2674 
2675 	table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
2676 	table->col_flags(0, placeable::kAlignRight);
2677 
2678 #ifndef MAC_APP_STORE
2679 	w_env_select *map_w = new w_env_select(environment_preferences->map_file, "AVAILABLE MAPS", _typecode_scenario, &d);
2680 	table->dual_add(map_w->label("Map"), d);
2681 	table->dual_add(map_w, d);
2682 
2683 	w_env_select *physics_w = new w_env_select(environment_preferences->physics_file, "AVAILABLE PHYSICS MODELS", _typecode_physics, &d);
2684 	table->dual_add(physics_w->label("Physics"), d);
2685 	table->dual_add(physics_w, d);
2686 
2687 	w_env_select *shapes_w = new w_env_select(environment_preferences->shapes_file, "AVAILABLE SHAPES", _typecode_shapes, &d);
2688 	table->dual_add(shapes_w->label("Shapes"), d);
2689 	table->dual_add(shapes_w, d);
2690 
2691 	w_env_select *sounds_w = new w_env_select(environment_preferences->sounds_file, "AVAILABLE SOUNDS", _typecode_sounds, &d);
2692 	table->dual_add(sounds_w->label("Sounds"), d);
2693 	table->dual_add(sounds_w, d);
2694 
2695 	w_env_select* resources_w = new w_env_select(environment_preferences->resources_file, "AVAILABLE FILES", _typecode_unknown, &d);
2696 	table->dual_add(resources_w->label("External Resources"), d);
2697 	table->dual_add(resources_w, d);
2698 #endif
2699 
2700 	table->add_row(new w_spacer, true);
2701 	table->dual_add_row(new w_button("PLUGINS", plugins_dialog, &d), d);
2702 
2703 #ifndef MAC_APP_STORE
2704 	table->add_row(new w_spacer, true);
2705 	table->dual_add_row(new w_static_text("Solo Script"), d);
2706 	w_enabling_toggle* use_solo_lua_w = new w_enabling_toggle(environment_preferences->use_solo_lua);
2707 	table->dual_add(use_solo_lua_w->label("Use Solo Script"), d);
2708 	table->dual_add(use_solo_lua_w, d);
2709 
2710 	w_file_chooser *solo_lua_w = new w_file_chooser("Choose Script", _typecode_netscript);
2711 	solo_lua_w->set_file(environment_preferences->solo_lua_file);
2712 	table->dual_add(solo_lua_w->label("Script File"), d);
2713 	table->dual_add(solo_lua_w, d);
2714 	use_solo_lua_w->add_dependent_widget(solo_lua_w);
2715 #endif
2716 
2717 	table->add_row(new w_spacer, true);
2718 	table->dual_add_row(new w_static_text("Film Playback"), d);
2719 
2720 	w_select* film_profile_w = new w_select(environment_preferences->film_profile, film_profile_labels);
2721 	table->dual_add(film_profile_w->label("Default Playback Profile"), d);
2722 	table->dual_add(film_profile_w, d);
2723 
2724 #ifndef MAC_APP_STORE
2725 	w_enabling_toggle* use_replay_net_lua_w = new w_enabling_toggle(environment_preferences->use_replay_net_lua);
2726 	table->dual_add(use_replay_net_lua_w->label("Use Netscript in Films"), d);
2727 	table->dual_add(use_replay_net_lua_w, d);
2728 
2729 	w_file_chooser *replay_net_lua_w = new w_file_chooser("Choose Script", _typecode_netscript);
2730 	replay_net_lua_w->set_file(network_preferences->netscript_file);
2731 	table->dual_add(replay_net_lua_w->label("Netscript File"), d);
2732 	table->dual_add(replay_net_lua_w, d);
2733 	use_replay_net_lua_w->add_dependent_widget(replay_net_lua_w);
2734 #endif
2735 
2736 	table->add_row(new w_spacer, true);
2737 	table->dual_add_row(new w_static_text("Options"), d);
2738 
2739 #ifndef MAC_APP_STORE
2740 	w_toggle *hide_extensions_w = new w_toggle(environment_preferences->hide_extensions);
2741 	table->dual_add(hide_extensions_w->label("Hide File Extensions"), d);
2742 	table->dual_add(hide_extensions_w, d);
2743 #endif
2744 
2745 	w_select *max_saves_w = new w_select(0, max_saves_labels);
2746 	for (int i = 0; max_saves_labels[i] != NULL; ++i) {
2747 		if (max_saves_values[i] == environment_preferences->maximum_quick_saves)
2748 			max_saves_w->set_selection(i);
2749 	}
2750 	table->dual_add(max_saves_w->label("Unnamed Saves to Keep"), d);
2751 	table->dual_add(max_saves_w, d);
2752 
2753 	placer->add(table, true);
2754 
2755 	placer->add(new w_spacer, true);
2756 
2757 	horizontal_placer *button_placer = new horizontal_placer;
2758 	w_button *w_accept = new w_button("ACCEPT", dialog_ok, &d);
2759 	button_placer->dual_add(w_accept, d);
2760 	w_button *w_cancel = new w_button("CANCEL", dialog_cancel, &d);
2761 	button_placer->dual_add(w_cancel, d);
2762 	placer->add(button_placer, true);
2763 
2764 	d.set_widget_placer(placer);
2765 
2766 	// Clear screen
2767 	clear_screen();
2768 
2769 	// Run dialog
2770 	bool theme_changed = false;
2771 	FileSpecifier old_theme;
2772 	const Plugin* theme_plugin = Plugins::instance()->find_theme();
2773 	if (theme_plugin)
2774 	{
2775 		old_theme = theme_plugin->directory + theme_plugin->theme;
2776 	}
2777 
2778 	if (d.run() == 0) {	// Accepted
2779 		bool changed = false;
2780 
2781 #ifndef MAC_APP_STORE
2782 		const char *path = map_w->get_path();
2783 		if (strcmp(path, environment_preferences->map_file)) {
2784 			strncpy(environment_preferences->map_file, path, 256);
2785 			environment_preferences->map_checksum = read_wad_file_checksum(map_w->get_file_specifier());
2786 			changed = true;
2787 		}
2788 
2789 		path = physics_w->get_path();
2790 		if (strcmp(path, environment_preferences->physics_file)) {
2791 			strncpy(environment_preferences->physics_file, path, 256);
2792 			environment_preferences->physics_checksum = read_wad_file_checksum(physics_w->get_file_specifier());
2793 			changed = true;
2794 		}
2795 
2796 		path = shapes_w->get_path();
2797 		if (strcmp(path, environment_preferences->shapes_file)) {
2798 			strncpy(environment_preferences->shapes_file, path, 256);
2799 			environment_preferences->shapes_mod_date = shapes_w->get_file_specifier().GetDate();
2800 			changed = true;
2801 		}
2802 
2803 		path = sounds_w->get_path();
2804 		if (strcmp(path, environment_preferences->sounds_file)) {
2805 			strncpy(environment_preferences->sounds_file, path, 256);
2806 			environment_preferences->sounds_mod_date = sounds_w->get_file_specifier().GetDate();
2807 			changed = true;
2808 		}
2809 
2810 		path = resources_w->get_path();
2811 		if (strcmp(path, environment_preferences->resources_file) != 0)
2812 		{
2813 			strncpy(environment_preferences->resources_file, path, 256);
2814 			changed = true;
2815 		}
2816 
2817 		bool use_solo_lua = use_solo_lua_w->get_selection() != 0;
2818 		if (use_solo_lua != environment_preferences->use_solo_lua)
2819 		{
2820 			environment_preferences->use_solo_lua = use_solo_lua;
2821 			changed = true;
2822 		}
2823 
2824 		path = solo_lua_w->get_file().GetPath();
2825 		if (strcmp(path, environment_preferences->solo_lua_file)) {
2826 			strncpy(environment_preferences->solo_lua_file, path, 256);
2827 			changed = true;
2828 		}
2829 
2830 		bool use_replay_net_lua = use_replay_net_lua_w->get_selection() != 0;
2831 		if (use_replay_net_lua != environment_preferences->use_replay_net_lua)
2832 		{
2833 			environment_preferences->use_replay_net_lua = use_replay_net_lua;
2834 			changed = true;
2835 		}
2836 
2837 		path = replay_net_lua_w->get_file().GetPath();
2838 		if (strcmp(path, network_preferences->netscript_file)) {
2839 			strncpy(network_preferences->netscript_file, path, 256);
2840 			changed = true;
2841 		}
2842 #endif
2843 
2844 		FileSpecifier new_theme;
2845 		theme_plugin = Plugins::instance()->find_theme();
2846 		if (theme_plugin)
2847 		{
2848 			new_theme = theme_plugin->directory + theme_plugin->theme;
2849 		}
2850 
2851 		if (new_theme != old_theme)
2852 		{
2853 			theme_changed = true;
2854 		}
2855 
2856 #ifndef MAC_APP_STORE
2857 		bool hide_extensions = hide_extensions_w->get_selection() != 0;
2858 		if (hide_extensions != environment_preferences->hide_extensions)
2859 		{
2860 			environment_preferences->hide_extensions = hide_extensions;
2861 			changed = true;
2862 		}
2863 #endif
2864 
2865 		if (film_profile_w->get_selection() != environment_preferences->film_profile)
2866 		{
2867 			environment_preferences->film_profile = static_cast<FilmProfileType>(film_profile_w->get_selection());
2868 
2869 			changed = true;
2870 		}
2871 
2872 		bool saves_changed = false;
2873 		int saves = max_saves_values[max_saves_w->get_selection()];
2874 		if (saves != environment_preferences->maximum_quick_saves) {
2875 			environment_preferences->maximum_quick_saves = saves;
2876 			saves_changed = true;
2877 		}
2878 
2879 		if (changed)
2880 			load_environment_from_preferences();
2881 
2882 		if (theme_changed) {
2883 			load_dialog_theme();
2884 		}
2885 
2886 		if (changed || theme_changed || saves_changed)
2887 			write_preferences();
2888 	}
2889 
2890 	// Redraw parent dialog
2891 	if (theme_changed)
2892 		parent->quit(0);	// Quit the parent dialog so it won't draw in the old theme
2893 }
2894 
2895 
2896 extern void hub_set_minimum_send_period(int32);
2897 extern int32& hub_get_minimum_send_period();
2898 
2899 struct set_latency_tolerance
2900 {
operator ()set_latency_tolerance2901 	void operator() (const std::string& arg) const {
2902 		hub_set_minimum_send_period(atoi(arg.c_str()));
2903 		screen_printf("latency tolerance is now %i", atoi(arg.c_str()));
2904 		write_preferences();
2905 	}
2906 };
2907 
2908 struct get_latency_tolerance
2909 {
operator ()get_latency_tolerance2910 	void operator() (const std::string&) const {
2911 		screen_printf("latency tolerance is %i", hub_get_minimum_send_period());
2912 	}
2913 };
2914 
transition_preferences(const DirectorySpecifier & legacy_preferences_dir)2915 void transition_preferences(const DirectorySpecifier& legacy_preferences_dir)
2916 {
2917 	FileSpecifier prefs;
2918 	prefs.SetToPreferencesDir();
2919 	prefs += getcstr(temporary, strFILENAMES, filenamePREFERENCES);
2920 	if (!prefs.Exists())
2921 	{
2922 		FileSpecifier oldPrefs;
2923 		oldPrefs = legacy_preferences_dir;
2924 		oldPrefs += getcstr(temporary, strFILENAMES, filenamePREFERENCES);
2925 		if (oldPrefs.Exists())
2926 		{
2927 			oldPrefs.Rename(prefs);
2928 		}
2929 	}
2930 }
2931 
2932 /*
2933  *  Initialize preferences (load from file or setup defaults)
2934  */
2935 
initialize_preferences(void)2936 void initialize_preferences(
2937 	void)
2938 {
2939 	logContext("initializing preferences");
2940 
2941 	// In case this function gets called more than once...
2942 	if (!PrefsInited)
2943 	{
2944 		graphics_preferences= new graphics_preferences_data;
2945 		player_preferences= new player_preferences_data;
2946 		input_preferences= new input_preferences_data;
2947 		sound_preferences = new SoundManager::Parameters;
2948 		network_preferences= new network_preferences_data;
2949 		environment_preferences= new environment_preferences_data;
2950 
2951 		for (int i = 0; i < NUM_KEYS; ++i)
2952 			input_preferences->key_bindings[i] = std::set<SDL_Scancode>();
2953 		for (int i = 0; i < NUMBER_OF_SHELL_KEYS; ++i)
2954 			input_preferences->shell_key_bindings[i] = std::set<SDL_Scancode>();
2955 
2956 		PrefsInited = true;
2957 
2958 		CommandParser PreferenceSetCommandParser;
2959 		PreferenceSetCommandParser.register_command("latency_tolerance", set_latency_tolerance());
2960 		CommandParser PreferenceGetCommandParser;
2961 		PreferenceGetCommandParser.register_command("latency_tolerance", get_latency_tolerance());
2962 
2963 		CommandParser PreferenceCommandParser;
2964 		PreferenceCommandParser.register_command("set", PreferenceSetCommandParser);
2965 		PreferenceCommandParser.register_command("get", PreferenceGetCommandParser);
2966 		Console::instance()->register_command("preferences", PreferenceCommandParser);
2967 
2968 		read_preferences ();
2969 	}
2970 }
2971 
read_preferences()2972 void read_preferences ()
2973 {
2974 	// Set to defaults; will be overridden by reading in the XML stuff
2975 	default_graphics_preferences(graphics_preferences);
2976 	default_network_preferences(network_preferences);
2977 	default_player_preferences(player_preferences);
2978 	default_input_preferences(input_preferences);
2979 	*sound_preferences = SoundManager::Parameters();
2980 	default_environment_preferences(environment_preferences);
2981 
2982 	// Slurp in the file and parse it
2983 
2984 	FileSpecifier FileSpec;
2985 
2986 	FileSpec.SetToPreferencesDir();
2987 	FileSpec += getcstr(temporary, strFILENAMES, filenamePREFERENCES);
2988 
2989 	OpenedFile OFile;
2990 	bool defaults = false;
2991 	bool opened = FileSpec.Open(OFile);
2992 
2993 	if (!opened) {
2994 		defaults = true;
2995 		FileSpec.SetNameWithPath(getcstr(temporary, strFILENAMES, filenamePREFERENCES));
2996 		opened = FileSpec.Open(OFile);
2997 	}
2998 
2999 	bool parse_error = false;
3000 	if (opened)
3001 	{
3002 		OFile.Close();
3003 		try {
3004 			InfoTree prefs = InfoTree::load_xml(FileSpec);
3005 			InfoTree root = prefs.get_child("mara_prefs");
3006 
3007 			std::string version = "";
3008 			root.read_attr("version", version);
3009 			if (!version.length())
3010 				logWarning("Reading older preferences of unknown version. Preferences will be upgraded to version %s when saved. (%s)", A1_DATE_VERSION, FileSpec.GetPath());
3011 			else if (version < A1_DATE_VERSION)
3012 				logWarning("Reading older preferences of version %s. Preferences will be upgraded to version %s when saved. (%s)", version.c_str(), A1_DATE_VERSION, FileSpec.GetPath());
3013 			else if (version > A1_DATE_VERSION)
3014 				logWarning("Reading newer preferences of version %s. Preferences will be downgraded to version %s when saved. (%s)", version.c_str(), A1_DATE_VERSION, FileSpec.GetPath());
3015 
3016 			BOOST_FOREACH(InfoTree child, root.children_named("graphics"))
3017 				parse_graphics_preferences(child, version);
3018 			BOOST_FOREACH(InfoTree child, root.children_named("player"))
3019 				parse_player_preferences(child, version);
3020 			BOOST_FOREACH(InfoTree child, root.children_named("input"))
3021 				parse_input_preferences(child, version);
3022 			BOOST_FOREACH(InfoTree child, root.children_named("sound"))
3023 				parse_sound_preferences(child, version);
3024 #if !defined(DISABLE_NETWORKING)
3025 			BOOST_FOREACH(InfoTree child, root.children_named("network"))
3026 				parse_network_preferences(child, version);
3027 #endif
3028 			BOOST_FOREACH(InfoTree child, root.children_named("environment"))
3029 				parse_environment_preferences(child, version);
3030 
3031 		} catch (InfoTree::parse_error ex) {
3032 			logError("Error parsing preferences file (%s): %s", FileSpec.GetPath(), ex.what());
3033 			parse_error = true;
3034 		} catch (InfoTree::path_error ep) {
3035 			logError("Could not find mara_prefs in preferences file (%s): %s", FileSpec.GetPath(), ep.what());
3036 			parse_error = true;
3037 		} catch (InfoTree::data_error ed) {
3038 			logError("Unexpected data error in preferences file (%s): %s", FileSpec.GetPath(), ed.what());
3039 			parse_error = true;
3040 		} catch (InfoTree::unexpected_error ee) {
3041 			logError("Unexpected error in preferences file (%s): %s", FileSpec.GetPath(), ee.what());
3042 			parse_error = true;
3043 		}
3044 	}
3045 
3046 	if (!opened || parse_error)
3047 	{
3048 		if (defaults)
3049 			alert_user(expand_app_variables("There were default preferences-file parsing errors (see $appLogFile$ for details)").c_str(), infoError);
3050 		else
3051 			alert_user(expand_app_variables("There were preferences-file parsing errors (see $appLogFile$ for details)").c_str(), infoError);
3052 	}
3053 
3054 	// Check on the read-in prefs
3055 	validate_graphics_preferences(graphics_preferences);
3056 	validate_network_preferences(network_preferences);
3057 	validate_player_preferences(player_preferences);
3058 	validate_input_preferences(input_preferences);
3059 	validate_environment_preferences(environment_preferences);
3060 
3061 	// jkvw: If we try to load a default file, but can't, we'll have set the game error.
3062 	//       But that's not useful, because we're just going to try loading the file
3063 	//       from user preferences.  It used to be this code was only called in initialisation,
3064 	//       and any game error generated here was simply clobbered by an init time
3065 	//       clear_game_error().  Since I'm using this more generally now, I'm clearing the
3066 	//       error right here, because it's not like we're bothered when we can't load a
3067 	//       default file.
3068 	//       (Problem is SDL specific - socre one for Carbon? :) )
3069 	clear_game_error ();
3070 }
3071 
3072 
3073 /*
3074  *  Write preferences to file
3075  */
3076 
3077 
graphics_preferences_tree()3078 InfoTree graphics_preferences_tree()
3079 {
3080 	InfoTree root;
3081 
3082 	root.put_attr("scmode_width", graphics_preferences->screen_mode.width);
3083 	root.put_attr("scmode_height", graphics_preferences->screen_mode.height);
3084 	root.put_attr("scmode_auto_resolution", graphics_preferences->screen_mode.auto_resolution);
3085 	root.put_attr("scmode_high_dpi", graphics_preferences->screen_mode.high_dpi);
3086 	root.put_attr("scmode_hud", graphics_preferences->screen_mode.hud);
3087 	root.put_attr("scmode_hud_scale", graphics_preferences->screen_mode.hud_scale_level);
3088 	root.put_attr("scmode_term_scale", graphics_preferences->screen_mode.term_scale_level);
3089 	root.put_attr("scmode_translucent_map", graphics_preferences->screen_mode.translucent_map);
3090 	root.put_attr("scmode_camera_bob", graphics_preferences->screen_mode.camera_bob);
3091 	root.put_attr("scmode_accel", graphics_preferences->screen_mode.acceleration);
3092 	root.put_attr("scmode_highres", graphics_preferences->screen_mode.high_resolution);
3093 	root.put_attr("scmode_fullscreen", graphics_preferences->screen_mode.fullscreen);
3094 	root.put_attr("scmode_bitdepth", graphics_preferences->screen_mode.bit_depth);
3095 	root.put_attr("scmode_gamma", graphics_preferences->screen_mode.gamma_level);
3096 	root.put_attr("scmode_fix_h_not_v", graphics_preferences->screen_mode.fix_h_not_v);
3097 	root.put_attr("ogl_flags", graphics_preferences->OGL_Configure.Flags);
3098 	root.put_attr("software_alpha_blending", graphics_preferences->software_alpha_blending);
3099 	root.put_attr("software_sdl_driver", graphics_preferences->software_sdl_driver);
3100 	root.put_attr("anisotropy_level", graphics_preferences->OGL_Configure.AnisotropyLevel);
3101 	root.put_attr("multisamples", graphics_preferences->OGL_Configure.Multisamples);
3102 	root.put_attr("geforce_fix", graphics_preferences->OGL_Configure.GeForceFix);
3103 	root.put_attr("wait_for_vsync", graphics_preferences->OGL_Configure.WaitForVSync);
3104 	root.put_attr("gamma_corrected_blending", graphics_preferences->OGL_Configure.Use_sRGB);
3105 	root.put_attr("use_npot", graphics_preferences->OGL_Configure.Use_NPOT);
3106 	root.put_attr("double_corpse_limit", graphics_preferences->double_corpse_limit);
3107 	root.put_attr("hog_the_cpu", graphics_preferences->hog_the_cpu);
3108 	root.put_attr("movie_export_video_quality", graphics_preferences->movie_export_video_quality);
3109 	root.put_attr("movie_export_audio_quality", graphics_preferences->movie_export_audio_quality);
3110 
3111 	root.add_color("void.color", graphics_preferences->OGL_Configure.VoidColor);
3112 
3113 	for (int i = 0; i < 4; ++i)
3114 		for (int j = 0; j < 2; ++j)
3115 			root.add_color("landscapes.color", graphics_preferences->OGL_Configure.LscpColors[i][j], 2*i+j);
3116 
3117 	for (int i = 0; i <= OGL_NUMBER_OF_TEXTURE_TYPES; ++i)
3118 	{
3119 		OGL_Texture_Configure& Config = (i == OGL_NUMBER_OF_TEXTURE_TYPES) ? graphics_preferences->OGL_Configure.ModelConfig : graphics_preferences->OGL_Configure.TxtrConfigList[i];
3120 
3121 		InfoTree tex;
3122 		tex.put_attr("index", i);
3123 		tex.put_attr("near_filter", Config.NearFilter);
3124 		tex.put_attr("far_filter", Config.FarFilter);
3125 		tex.put_attr("resolution", Config.Resolution);
3126 		tex.put_attr("color_format", Config.ColorFormat);
3127 		tex.put_attr("max_size", Config.MaxSize);
3128 		root.add_child("texture", tex);
3129 	}
3130 	return root;
3131 }
3132 
player_preferences_tree()3133 InfoTree player_preferences_tree()
3134 {
3135 	InfoTree root;
3136 
3137 	root.put_attr_cstr("name", player_preferences->name);
3138 	root.put_attr("color", player_preferences->color);
3139 	root.put_attr("team", player_preferences->team);
3140 	root.put_attr("last_time_ran", player_preferences->last_time_ran);
3141 	root.put_attr("difficulty", player_preferences->difficulty_level);
3142 	root.put_attr("bkgd_music", player_preferences->background_music_on);
3143 	root.put_attr("crosshairs_active", player_preferences->crosshairs_active);
3144 
3145 	ChaseCamData& ChaseCam = player_preferences->ChaseCam;
3146 	InfoTree cam;
3147 	cam.put_attr("behind", ChaseCam.Behind);
3148 	cam.put_attr("upward", ChaseCam.Upward);
3149 	cam.put_attr("rightward", ChaseCam.Rightward);
3150 	cam.put_attr("flags", ChaseCam.Flags);
3151 	cam.put_attr("damping", ChaseCam.Damping);
3152 	cam.put_attr("spring", ChaseCam.Spring);
3153 	cam.put_attr("opacity", ChaseCam.Opacity);
3154 	root.put_child("chase_cam", cam);
3155 
3156 	CrosshairData& Crosshairs = player_preferences->Crosshairs;
3157 	InfoTree cross;
3158 	cross.put_attr("thickness", Crosshairs.Thickness);
3159 	cross.put_attr("from_center", Crosshairs.FromCenter);
3160 	cross.put_attr("length", Crosshairs.Length);
3161 	cross.put_attr("shape", Crosshairs.Shape);
3162 	cross.put_attr("opacity", Crosshairs.Opacity);
3163 	cross.add_color("color", Crosshairs.Color);
3164 	root.put_child("crosshairs", cross);
3165 
3166 	return root;
3167 }
3168 
3169 // symbolic names for key and button bindings
3170 static const char *binding_action_name[NUM_KEYS] = {
3171 	"forward", "back", "look-left", "look-right", "strafe-left",
3172 	"strafe-right", "glance-left", "glance-right", "look-up", "look-down",
3173 	"look-ahead", "prev-weapon", "next-weapon", "trigger-1", "trigger-2",
3174 	"strafe", "run", "look", "action", "map",
3175 	"microphone"
3176 };
3177 static const char *binding_shell_action_name[NUMBER_OF_SHELL_KEYS] = {
3178 	"inventory-left", "inventory-right", "switch-player-view", "volume-up", "volume-down",
3179 	"map-zoom-in", "map-zoom-out", "fps", "chat", "net-stats"
3180 };
3181 static const char *binding_mouse_button_name[NUM_SDL_MOUSE_BUTTONS] = {
3182 	"mouse-left", "mouse-middle", "mouse-right", "mouse-x1", "mouse-x2",
3183 	"mouse-scroll-up", "mouse-scroll-down"
3184 };
3185 static const char *binding_joystick_button_name[NUM_SDL_JOYSTICK_BUTTONS] = {
3186 	"controller-a", "controller-b", "controller-x", "controller-y",
3187 	"controller-back", "controller-guide", "controller-start",
3188 	"controller-ls", "controller-rs", "controller-lb", "controller-rb",
3189 	"controller-up", "controller-down", "controller-left", "controller-right",
3190 	"controller-ls-right", "controller-ls-down", "controller-rs-right",
3191 	"controller-rs-down", "controller-lt", "controller-rt",
3192 	"controller-ls-left", "controller-ls-up", "controller-rs-left",
3193 	"controller-rs-up", "controller-lt-neg", "controller-rt-neg"
3194 };
3195 static const int binding_num_scancodes = 285;
3196 static const char *binding_scancode_name[binding_num_scancodes] = {
3197 	"unknown", "unknown-1", "unknown-2", "unknown-3", "a",
3198 	"b", "c", "d", "e", "f",
3199 	"g", "h", "i", "j", "k",
3200 	"l", "m", "n", "o", "p",
3201 	"q", "r", "s", "t", "u",
3202 	"v", "w", "x", "y", "z",
3203 	"1", "2", "3", "4", "5",
3204 	"6", "7", "8", "9", "unknown-39",
3205 	"return", "escape", "backspace", "tab", "space",
3206 	"minus", "equals", "leftbracket", "rightbracket", "backslash",
3207 	"nonushash", "semicolon", "apostrophe", "grave", "comma",
3208 	"period", "slash", "capslock", "f1", "f2",
3209 	"f3", "f4", "f5", "f6", "f7",
3210 	"f8", "f9", "f10", "f11", "f12",
3211 	"printscreen", "scrolllock", "pause", "insert", "home",
3212 	"pageup", "delete", "end", "pagedown", "right",
3213 	"left", "down", "up", "numlockclear", "kp-divide",
3214 	"kp-multiply", "kp-minus", "kp-plus", "kp-enter", "kp-1",
3215 	"kp-2", "kp-3", "kp-4", "kp-5", "kp-6",
3216 	"kp-7", "kp-8", "kp-9", "kp-0", "kp-period",
3217 	"nonusbackslash", "application", "power", "kp-equals", "f13",
3218 	"f14", "f15", "f16", "f17", "f18",
3219 	"f19", "f20", "f21", "f22", "f23",
3220 	"f24", "execute", "help", "menu", "select",
3221 	"stop", "again", "undo", "cut", "copy",
3222 	"paste", "find", "mute", "volumeup", "volumedown",
3223 	"unknown-130", "unknown-131", "unknown-132", "kp-comma", "kp-equalsas400",
3224 	"international1", "international2", "international3", "international4", "international5",
3225 	"international6", "international7", "international8", "international9", "lang1",
3226 	"lang2", "lang3", "lang4", "lang5", "lang6",
3227 	"lang7", "lang8", "lang9", "alterase", "sysreq",
3228 	"cancel", "clear", "prior", "return2", "separator",
3229 	"out", "oper", "clearagain", "crsel", "exsel",
3230 	"unknown-165", "unknown-166", "unknown-167", "unknown-168", "unknown-169",
3231 	"unknown-170", "unknown-171", "unknown-172", "unknown-173", "unknown-174",
3232 	"unknown-175", "kp-00", "kp-000", "thousandsseparator", "decimalseparator",
3233 	"currencyunit", "currencysubunit", "kp-leftparen", "kp-rightparen", "kp-leftbrace",
3234 	"kp-rightbrace", "kp-tab", "kp-backspace", "kp-a", "kp-b",
3235 	"kp-c", "kp-d", "kp-e", "kp-f", "kp-xor",
3236 	"kp-power", "kp-percent", "kp-less", "kp-greater", "kp-ampersand",
3237 	"kp-dblampersand", "kp-verticalbar", "kp-dblverticalbar", "kp-colon", "kp-hash",
3238 	"kp-space", "kp-at", "kp-exclam", "kp-memstore", "kp-memrecall",
3239 	"kp-memclear", "kp-memadd", "kp-memsubtract", "kp-memmultiply", "kp-memdivide",
3240 	"kp-plusminus", "kp-clear", "kp-clearentry", "kp-binary", "kp-octal",
3241 	"kp-decimal", "kp-hexadecimal", "unknown-222", "unknown-223", "lctrl",
3242 	"lshift", "lalt", "lgui", "rctrl", "rshift",
3243 	"ralt", "rgui", "unknown-232", "unknown-233", "unknown-234",
3244 	"unknown-235", "unknown-236", "unknown-237", "unknown-238", "unknown-239",
3245 	"unknown-240", "unknown-241", "unknown-242", "unknown-243", "unknown-244",
3246 	"unknown-245", "unknown-246", "unknown-247", "unknown-248", "unknown-249",
3247 	"unknown-250", "unknown-251", "unknown-252", "unknown-253", "unknown-254",
3248 	"unknown-255", "unknown-256", "mode", "audionext", "audioprev",
3249 	"audiostop", "audioplay", "audiomute", "mediaselect", "www",
3250 	"mail", "calculator", "computer", "ac-search", "ac-home",
3251 	"ac-back", "ac-forward", "ac-stop", "ac-refresh", "ac-bookmarks",
3252 	"brightnessdown", "brightnessup", "displayswitch", "kbdillumtoggle", "kbdillumdown",
3253 	"kbdillumup", "eject", "sleep", "app1", "app2"
3254 };
3255 
binding_name_for_code(SDL_Scancode code)3256 static const char *binding_name_for_code(SDL_Scancode code)
3257 {
3258 	int i = static_cast<int>(code);
3259 	if (i >= 0 &&
3260 		i < binding_num_scancodes)
3261 		return binding_scancode_name[i];
3262 	else if (i >= AO_SCANCODE_BASE_MOUSE_BUTTON &&
3263 			 i < (AO_SCANCODE_BASE_MOUSE_BUTTON + NUM_SDL_MOUSE_BUTTONS))
3264 		return binding_mouse_button_name[i - AO_SCANCODE_BASE_MOUSE_BUTTON];
3265 	else if (i >= AO_SCANCODE_BASE_JOYSTICK_BUTTON &&
3266 			 i < (AO_SCANCODE_BASE_JOYSTICK_BUTTON + NUM_SDL_JOYSTICK_BUTTONS))
3267 		return binding_joystick_button_name[i - AO_SCANCODE_BASE_JOYSTICK_BUTTON];
3268 	return "unknown";
3269 }
3270 
code_for_binding_name(std::string name)3271 static SDL_Scancode code_for_binding_name(std::string name)
3272 {
3273 	for (int i = 0; i < binding_num_scancodes; ++i)
3274 	{
3275 		if (name == binding_scancode_name[i])
3276 			return static_cast<SDL_Scancode>(i);
3277 	}
3278 	for (int i = 0; i < NUM_SDL_MOUSE_BUTTONS; ++i)
3279 	{
3280 		if (name == binding_mouse_button_name[i])
3281 			return static_cast<SDL_Scancode>(i + AO_SCANCODE_BASE_MOUSE_BUTTON);
3282 	}
3283 	for (int i = 0; i < NUM_SDL_JOYSTICK_BUTTONS; ++i)
3284 	{
3285 		if (name == binding_joystick_button_name[i])
3286 			return static_cast<SDL_Scancode>(i + AO_SCANCODE_BASE_JOYSTICK_BUTTON);
3287 	}
3288 	return SDL_SCANCODE_UNKNOWN;
3289 }
3290 
index_for_action_name(std::string name,bool & is_shell)3291 static int index_for_action_name(std::string name, bool& is_shell)
3292 {
3293 	for (int i = 0; i < NUM_KEYS; ++i)
3294 	{
3295 		if (name == binding_action_name[i])
3296 		{
3297 			is_shell = false;
3298 			return i;
3299 		}
3300 	}
3301 	for (int i = 0; i < NUMBER_OF_SHELL_KEYS; ++i)
3302 	{
3303 		if (name == binding_shell_action_name[i])
3304 		{
3305 			is_shell = true;
3306 			return i;
3307 		}
3308 	}
3309 	return -1;
3310 }
3311 
input_preferences_tree()3312 InfoTree input_preferences_tree()
3313 {
3314 	InfoTree root;
3315 
3316 	root.put_attr("device", input_preferences->input_device);
3317 	root.put_attr("modifiers", input_preferences->modifiers);
3318 	root.put_attr("sens_horizontal", input_preferences->sens_horizontal);
3319 	root.put_attr("sens_vertical", input_preferences->sens_vertical);
3320 	root.put_attr("classic_vertical_aim", input_preferences->classic_vertical_aim);
3321 	root.put_attr("classic_aim_speed_limits", input_preferences->classic_aim_speed_limits);
3322 	root.put_attr("mouse_accel_type", input_preferences->mouse_accel_type);
3323 	root.put_attr("mouse_accel_scale", input_preferences->mouse_accel_scale);
3324 	root.put_attr("raw_mouse_input", input_preferences->raw_mouse_input);
3325 	root.put_attr("extra_mouse_precision", input_preferences->extra_mouse_precision);
3326 
3327 	root.put_attr("controller_analog", input_preferences->controller_analog);
3328 	root.put_attr("controller_sensitivity", input_preferences->controller_sensitivity);
3329 	root.put_attr("controller_deadzone", input_preferences->controller_deadzone);
3330 
3331 	for (int i = 0; i < (NUMBER_OF_KEYS + NUMBER_OF_SHELL_KEYS); ++i)
3332 	{
3333 		std::set<SDL_Scancode> codeset;
3334 		const char *name;
3335 		if (i < NUMBER_OF_KEYS) {
3336 			codeset = input_preferences->key_bindings[i];
3337 			name = binding_action_name[i];
3338 		} else {
3339 			codeset = input_preferences->shell_key_bindings[i - NUMBER_OF_KEYS];
3340 			name = binding_shell_action_name[i - NUMBER_OF_KEYS];
3341 		}
3342 
3343 		BOOST_FOREACH(const SDL_Scancode &code, codeset)
3344 		{
3345 			if (code == SDL_SCANCODE_UNKNOWN)
3346 				continue;
3347 			InfoTree key;
3348 			key.put_attr("action", name);
3349 			key.put_attr("pressed", binding_name_for_code(code));
3350 			root.add_child("binding", key);
3351 		}
3352 	}
3353 
3354 	return root;
3355 }
3356 
sound_preferences_tree()3357 InfoTree sound_preferences_tree()
3358 {
3359 	InfoTree root;
3360 
3361 	root.put_attr("channels", sound_preferences->channel_count);
3362 	root.put_attr("volume", sound_preferences->volume);
3363 	root.put_attr("music_volume", sound_preferences->music);
3364 	root.put_attr("flags", sound_preferences->flags);
3365 	root.put_attr("rate", sound_preferences->rate);
3366 	root.put_attr("samples", sound_preferences->samples);
3367 	root.put_attr("volume_while_speaking", sound_preferences->volume_while_speaking);
3368 	root.put_attr("mute_while_transmitting", sound_preferences->mute_while_transmitting);
3369 
3370 	return root;
3371 }
3372 
network_preferences_tree()3373 InfoTree network_preferences_tree()
3374 {
3375 	InfoTree root;
3376 
3377 	root.put_attr("microphone", network_preferences->allow_microphone);
3378 	root.put_attr("untimed", network_preferences->game_is_untimed);
3379 	root.put_attr("type", network_preferences->type);
3380 	root.put_attr("game_type", network_preferences->game_type);
3381 	root.put_attr("difficulty", network_preferences->difficulty_level);
3382 	root.put_attr("game_options", network_preferences->game_options);
3383 	root.put_attr("time_limit", network_preferences->time_limit);
3384 	root.put_attr("kill_limit", network_preferences->kill_limit);
3385 	root.put_attr("entry_point", network_preferences->entry_point);
3386 	root.put_attr("autogather", network_preferences->autogather);
3387 	root.put_attr("join_by_address", network_preferences->join_by_address);
3388 	root.put_attr("join_address", network_preferences->join_address);
3389 	root.put_attr("local_game_port", network_preferences->game_port);
3390 	root.put_attr("game_protocol", sNetworkGameProtocolNames[network_preferences->game_protocol]);
3391 	root.put_attr("use_speex_netmic_encoder", network_preferences->use_speex_encoder);
3392 	root.put_attr("use_netscript", network_preferences->use_netscript);
3393 	root.put_attr_path("netscript_file", network_preferences->netscript_file);
3394 	root.put_attr("cheat_flags", network_preferences->cheat_flags);
3395 	root.put_attr("advertise_on_metaserver", network_preferences->advertise_on_metaserver);
3396 	root.put_attr("attempt_upnp", network_preferences->attempt_upnp);
3397 	root.put_attr("check_for_updates", network_preferences->check_for_updates);
3398 	root.put_attr("verify_https", network_preferences->verify_https);
3399 	root.put_attr("metaserver_login", network_preferences->metaserver_login);
3400 
3401 	char passwd[33];
3402 	for (int i = 0; i < 16; i++)
3403 		sprintf(&passwd[2*i], "%.2x", network_preferences->metaserver_password[i] ^ sPasswordMask[i]);
3404 	passwd[32] = '\0';
3405 	root.put_attr("metaserver_password", passwd);
3406 
3407 	root.put_attr("use_custom_metaserver_colors", network_preferences->use_custom_metaserver_colors);
3408 	root.put_attr("mute_metaserver_guests", network_preferences->mute_metaserver_guests);
3409 	root.put_attr("join_metaserver_by_default", network_preferences->join_metaserver_by_default);
3410 	root.put_attr("allow_stats", network_preferences->allow_stats);
3411 
3412 	for (int i = 0; i < 2; i++)
3413 		root.add_color("color", network_preferences->metaserver_colors[i], i);
3414 
3415 	root.put_child("star_protocol", StarPreferencesTree());
3416 	root.put_child("ring_protocol", RingPreferencesTree());
3417 
3418 	return root;
3419 }
3420 
environment_preferences_tree()3421 InfoTree environment_preferences_tree()
3422 {
3423 	InfoTree root;
3424 
3425 	root.put_attr_path("map_file", environment_preferences->map_file);
3426 	root.put_attr_path("physics_file", environment_preferences->physics_file);
3427 	root.put_attr_path("shapes_file", environment_preferences->shapes_file);
3428 	root.put_attr_path("sounds_file", environment_preferences->sounds_file);
3429 	root.put_attr_path("resources_file", environment_preferences->resources_file);
3430 	root.put_attr("map_checksum", environment_preferences->map_checksum);
3431 	root.put_attr("physics_checksum", environment_preferences->physics_checksum);
3432 	root.put_attr("shapes_mod_date", static_cast<uint32>(environment_preferences->shapes_mod_date));
3433 	root.put_attr("sounds_mod_date", static_cast<uint32>(environment_preferences->sounds_mod_date));
3434 	root.put_attr("group_by_directory", environment_preferences->group_by_directory);
3435 	root.put_attr("reduce_singletons", environment_preferences->reduce_singletons);
3436 	root.put_attr("smooth_text", environment_preferences->smooth_text);
3437 	root.put_attr_path("solo_lua_file", environment_preferences->solo_lua_file);
3438 	root.put_attr("use_solo_lua", environment_preferences->use_solo_lua);
3439 	root.put_attr("use_replay_net_lua", environment_preferences->use_replay_net_lua);
3440 	root.put_attr("hide_alephone_extensions", environment_preferences->hide_extensions);
3441 	root.put_attr("film_profile", static_cast<uint32>(environment_preferences->film_profile));
3442 	root.put_attr("maximum_quick_saves", environment_preferences->maximum_quick_saves);
3443 
3444 	for (Plugins::iterator it = Plugins::instance()->begin(); it != Plugins::instance()->end(); ++it) {
3445 		if (it->compatible() && !it->enabled) {
3446 			InfoTree disable;
3447 			disable.put_attr_path("path", it->directory.GetPath());
3448 			root.add_child("disable_plugin", disable);
3449 		}
3450 	}
3451 
3452 	return root;
3453 }
3454 
write_preferences()3455 void write_preferences()
3456 {
3457 	InfoTree root;
3458 	root.put_attr("version", A1_DATE_VERSION);
3459 
3460 	root.put_child("graphics", graphics_preferences_tree());
3461 	root.put_child("player", player_preferences_tree());
3462 	root.put_child("input", input_preferences_tree());
3463 	root.put_child("sound", sound_preferences_tree());
3464 #if !defined(DISABLE_NETWORKING)
3465 	root.put_child("network", network_preferences_tree());
3466 #endif
3467 	root.put_child("environment", environment_preferences_tree());
3468 
3469 	InfoTree fileroot;
3470 	fileroot.put_child("mara_prefs", root);
3471 
3472 	FileSpecifier FileSpec;
3473 	FileSpec.SetToPreferencesDir();
3474 	FileSpec += getcstr(temporary, strFILENAMES, filenamePREFERENCES);
3475 
3476 	try {
3477 		fileroot.save_xml(FileSpec);
3478 	} catch (InfoTree::parse_error ex) {
3479 		logError("Error saving preferences file (%s): %s", FileSpec.GetPath(), ex.what());
3480 	} catch (InfoTree::unexpected_error ex) {
3481 		logError("Error saving preferences file (%s): %s", FileSpec.GetPath(), ex.what());
3482 	}
3483 }
3484 
3485 
3486 /*
3487  *  Setup default preferences
3488  */
3489 
default_graphics_preferences(graphics_preferences_data * preferences)3490 static void default_graphics_preferences(graphics_preferences_data *preferences)
3491 {
3492   memset(&preferences->screen_mode, '\0', sizeof(screen_mode_data));
3493 	preferences->screen_mode.gamma_level= DEFAULT_GAMMA_LEVEL;
3494 
3495 	preferences->screen_mode.width = 640;
3496 	preferences->screen_mode.height = 480;
3497 	preferences->screen_mode.auto_resolution = true;
3498 	preferences->screen_mode.high_dpi = true;
3499 	preferences->screen_mode.hud = true;
3500 	preferences->screen_mode.hud_scale_level = 0;
3501 	preferences->screen_mode.term_scale_level = 2;
3502 	preferences->screen_mode.translucent_map = true;
3503 	preferences->screen_mode.acceleration = _opengl_acceleration;
3504 	preferences->screen_mode.high_resolution = true;
3505 	preferences->screen_mode.fullscreen = true;
3506 	preferences->screen_mode.fix_h_not_v = true;
3507 	preferences->screen_mode.camera_bob = true;
3508 	preferences->screen_mode.bit_depth = 32;
3509 
3510 	preferences->screen_mode.draw_every_other_line= false;
3511 
3512 	OGL_SetDefaults(preferences->OGL_Configure);
3513 
3514 	preferences->double_corpse_limit= false;
3515 	preferences->hog_the_cpu = false;
3516 
3517 	preferences->software_alpha_blending = _sw_alpha_off;
3518 	preferences->software_sdl_driver = _sw_driver_default;
3519 
3520 	preferences->movie_export_video_quality = 50;
3521 	preferences->movie_export_audio_quality = 50;
3522 }
3523 
default_network_preferences(network_preferences_data * preferences)3524 static void default_network_preferences(network_preferences_data *preferences)
3525 {
3526 	preferences->type= _ethernet;
3527 
3528 	preferences->allow_microphone = true;
3529 	preferences->game_is_untimed = false;
3530 	preferences->difficulty_level = 2;
3531 	preferences->game_options =	_multiplayer_game | _ammo_replenishes | _weapons_replenish
3532 		| _specials_replenish | _burn_items_on_death
3533 		| _force_unique_teams | _live_network_stats;
3534 	preferences->time_limit = 10 * TICKS_PER_SECOND * 60;
3535 	preferences->kill_limit = 10;
3536 	preferences->entry_point= 0;
3537 	preferences->game_type= _game_of_kill_monsters;
3538 	preferences->autogather= false;
3539 	preferences->join_by_address= false;
3540 	obj_clear(preferences->join_address);
3541 	preferences->game_port= DEFAULT_GAME_PORT;
3542 	preferences->game_protocol= _network_game_protocol_default;
3543 #if !defined(DISABLE_NETWORKING)
3544 	DefaultStarPreferences();
3545 	DefaultRingPreferences();
3546 #endif // !defined(DISABLE_NETWORKING)
3547 	preferences->use_speex_encoder = true;
3548 	preferences->use_netscript = false;
3549 	preferences->netscript_file[0] = '\0';
3550 	preferences->cheat_flags = _allow_tunnel_vision | _allow_crosshair | _allow_behindview | _allow_overlay_map;
3551 	preferences->advertise_on_metaserver = false;
3552 	preferences->attempt_upnp = false;
3553 	preferences->check_for_updates = true;
3554 	preferences->verify_https = false;
3555 	strncpy(preferences->metaserver_login, "guest", preferences->kMetaserverLoginLength);
3556 	memset(preferences->metaserver_password, 0, preferences->kMetaserverLoginLength);
3557 	preferences->mute_metaserver_guests = false;
3558 	preferences->use_custom_metaserver_colors = false;
3559 	preferences->metaserver_colors[0] = get_interface_color(PLAYER_COLOR_BASE_INDEX);
3560 	preferences->metaserver_colors[1] = get_interface_color(PLAYER_COLOR_BASE_INDEX);
3561 	preferences->join_metaserver_by_default = false;
3562 	preferences->allow_stats = false;
3563 }
3564 
default_player_preferences(player_preferences_data * preferences)3565 static void default_player_preferences(player_preferences_data *preferences)
3566 {
3567 	obj_clear(*preferences);
3568 
3569 	preferences->difficulty_level= 2;
3570 	strncpy(preferences->name, get_name_from_system().c_str(), PREFERENCES_NAME_LENGTH+1);
3571 
3572 	// LP additions for new fields:
3573 
3574 	preferences->ChaseCam.Behind = 1536;
3575 	preferences->ChaseCam.Upward = 0;
3576 	preferences->ChaseCam.Rightward = 0;
3577 	preferences->ChaseCam.Flags = 0;
3578 	preferences->ChaseCam.Damping = 0.5;
3579 	preferences->ChaseCam.Spring = 0;
3580 	preferences->ChaseCam.Opacity = 1;
3581 
3582 	preferences->Crosshairs.Thickness = 3;
3583 	preferences->Crosshairs.FromCenter = 2;
3584 	preferences->Crosshairs.Length = 1;
3585 	preferences->Crosshairs.Shape = CHShape_RealCrosshairs;
3586 	preferences->Crosshairs.Color = rgb_white;
3587 	preferences->Crosshairs.Opacity = 0.5;
3588 	preferences->Crosshairs.PreCalced = false;
3589 }
3590 
default_input_preferences(input_preferences_data * preferences)3591 static void default_input_preferences(input_preferences_data *preferences)
3592 {
3593 	preferences->input_device= _mouse_yaw_pitch;
3594 	preferences->key_bindings = default_key_bindings;
3595 	preferences->shell_key_bindings = default_shell_key_bindings;
3596 
3597 	// LP addition: set up defaults for modifiers:
3598 	// interchange run and walk, but don't interchange swim and sink.
3599 	preferences->modifiers = _inputmod_interchange_run_walk;
3600 
3601 	// LP: split into horizontal and vertical sensitivities
3602 	// ZZZ addition: sensitivity factor starts at 1 (no adjustment)
3603 	preferences->sens_horizontal = FIXED_ONE;
3604 	preferences->sens_vertical = FIXED_ONE;
3605 	preferences->mouse_accel_type = _mouse_accel_none;
3606 	preferences->mouse_accel_scale = 1.f;
3607 	preferences->raw_mouse_input = true;
3608 	preferences->extra_mouse_precision = true;
3609 	preferences->classic_vertical_aim = false;
3610 	preferences->classic_aim_speed_limits = true;
3611 
3612 	preferences->controller_analog = true;
3613 	preferences->controller_sensitivity = FIXED_ONE;
3614 	preferences->controller_deadzone = 3276;
3615 }
3616 
default_environment_preferences(environment_preferences_data * preferences)3617 static void default_environment_preferences(environment_preferences_data *preferences)
3618 {
3619 	obj_set(*preferences, NONE);
3620 
3621 	FileSpecifier DefaultMapFile;
3622 	FileSpecifier DefaultShapesFile;
3623 	FileSpecifier DefaultSoundsFile;
3624 	FileSpecifier DefaultPhysicsFile;
3625 	FileSpecifier DefaultExternalResourcesFile;
3626 
3627 	get_default_map_spec(DefaultMapFile);
3628 	get_default_physics_spec(DefaultPhysicsFile);
3629 	get_default_shapes_spec(DefaultShapesFile);
3630 	get_default_sounds_spec(DefaultSoundsFile);
3631 	get_default_external_resources_spec(DefaultExternalResourcesFile);
3632 
3633 	preferences->map_checksum= read_wad_file_checksum(DefaultMapFile);
3634 	strncpy(preferences->map_file, DefaultMapFile.GetPath(), 256);
3635 	preferences->map_file[255] = 0;
3636 
3637 	preferences->physics_checksum= read_wad_file_checksum(DefaultPhysicsFile);
3638 	strncpy(preferences->physics_file, DefaultPhysicsFile.GetPath(), 256);
3639 	preferences->physics_file[255] = 0;
3640 
3641 	preferences->shapes_mod_date = DefaultShapesFile.GetDate();
3642 	strncpy(preferences->shapes_file, DefaultShapesFile.GetPath(), 256);
3643 	preferences->shapes_file[255] = 0;
3644 
3645 	preferences->sounds_mod_date = DefaultSoundsFile.GetDate();
3646 	strncpy(preferences->sounds_file, DefaultSoundsFile.GetPath(), 256);
3647 	preferences->sounds_file[255] = 0;
3648 
3649 	strncpy(preferences->resources_file, DefaultExternalResourcesFile.GetPath(), 256);
3650 	preferences->resources_file[255] = 0;
3651 
3652 	preferences->group_by_directory = true;
3653 	preferences->reduce_singletons = false;
3654 	preferences->smooth_text = true;
3655 
3656 	preferences->solo_lua_file[0] = 0;
3657 	preferences->use_solo_lua = false;
3658 	preferences->use_replay_net_lua = false;
3659 	preferences->hide_extensions = true;
3660 	preferences->film_profile = FILM_PROFILE_DEFAULT;
3661 	preferences->maximum_quick_saves = 0;
3662 }
3663 
3664 
3665 /*
3666  *  Validate preferences
3667  */
3668 
validate_graphics_preferences(graphics_preferences_data * preferences)3669 static bool validate_graphics_preferences(graphics_preferences_data *preferences)
3670 {
3671 	bool changed= false;
3672 
3673 	// Fix bool options
3674 	preferences->screen_mode.high_resolution = !!preferences->screen_mode.high_resolution;
3675 	preferences->screen_mode.fullscreen = !!preferences->screen_mode.fullscreen;
3676 	preferences->screen_mode.draw_every_other_line = !!preferences->screen_mode.draw_every_other_line;
3677     preferences->screen_mode.fix_h_not_v = !!preferences->screen_mode.fix_h_not_v;
3678 
3679 	if(preferences->screen_mode.gamma_level<0 || preferences->screen_mode.gamma_level>=NUMBER_OF_GAMMA_LEVELS)
3680 	{
3681 		preferences->screen_mode.gamma_level= DEFAULT_GAMMA_LEVEL;
3682 		changed= true;
3683 	}
3684 
3685 	if (preferences->screen_mode.acceleration != _no_acceleration && preferences->screen_mode.acceleration != _opengl_acceleration)
3686 		preferences->screen_mode.acceleration = _opengl_acceleration;
3687 
3688 	// OpenGL requires at least 16 bit color depth
3689 	if (preferences->screen_mode.acceleration != _no_acceleration && preferences->screen_mode.bit_depth == 8)
3690 	{
3691 		preferences->screen_mode.bit_depth= 16;
3692 		changed= true;
3693 	}
3694 
3695 #ifdef TRUE_COLOR_ONLY
3696 	if (preferences->screen_mode.bit_depth == 8)
3697 	{
3698 		preferences->screen_mode.bit_depth = 16;
3699 		changed = true;
3700 	}
3701 #endif
3702 
3703 	return changed;
3704 }
3705 
validate_network_preferences(network_preferences_data * preferences)3706 static bool validate_network_preferences(network_preferences_data *preferences)
3707 {
3708 	bool changed= false;
3709 
3710 	// Fix bool options
3711 	preferences->allow_microphone = !!preferences->allow_microphone;
3712 	preferences->game_is_untimed = !!preferences->game_is_untimed;
3713 
3714 	if(preferences->type<0||preferences->type>_ethernet)
3715 	{
3716 		if(ethernet_active())
3717 		{
3718 			preferences->type= _ethernet;
3719 		} else {
3720 			preferences->type= _localtalk;
3721 		}
3722 		changed= true;
3723 	}
3724 
3725 	if(preferences->game_is_untimed != true && preferences->game_is_untimed != false)
3726 	{
3727 		preferences->game_is_untimed= false;
3728 		changed= true;
3729 	}
3730 
3731 	if(preferences->allow_microphone != true && preferences->allow_microphone != false)
3732 	{
3733 		preferences->allow_microphone= true;
3734 		changed= true;
3735 	}
3736 
3737 	if(preferences->game_type<0 || preferences->game_type >= NUMBER_OF_GAME_TYPES)
3738 	{
3739 		preferences->game_type= _game_of_kill_monsters;
3740 		changed= true;
3741 	}
3742 
3743         // ZZZ: is this relevant anymore now with XML prefs?  if so, should validate autogather, join_by_address, and join_address.
3744 
3745 	if(preferences->game_protocol >= NUMBER_OF_NETWORK_GAME_PROTOCOLS)
3746 	{
3747 		preferences->game_protocol= _network_game_protocol_default;
3748 		changed= true;
3749 	}
3750 
3751 	return changed;
3752 }
3753 
validate_player_preferences(player_preferences_data * preferences)3754 static bool validate_player_preferences(player_preferences_data *preferences)
3755 {
3756 	// Fix bool options
3757 	preferences->background_music_on = !!preferences->background_music_on;
3758 
3759 	return false;
3760 }
3761 
validate_input_preferences(input_preferences_data * preferences)3762 static bool validate_input_preferences(input_preferences_data *preferences)
3763 {
3764 	(void) (preferences);
3765 	return false;
3766 }
3767 
validate_environment_preferences(environment_preferences_data * preferences)3768 static bool validate_environment_preferences(environment_preferences_data *preferences)
3769 {
3770 	(void) (preferences);
3771 	return false;
3772 }
3773 
3774 
3775 /*
3776  *  Load the environment
3777  */
3778 
3779 /* Load the environment.. */
load_environment_from_preferences(void)3780 void load_environment_from_preferences(
3781 	void)
3782 {
3783 	FileSpecifier File;
3784 	struct environment_preferences_data *prefs= environment_preferences;
3785 
3786 	File = prefs->map_file;
3787 	if (File.Exists()) {
3788 		set_map_file(File);
3789 	} else {
3790 		/* Try to find the checksum */
3791 		if(find_wad_file_that_has_checksum(File,
3792 			_typecode_scenario, strPATHS, prefs->map_checksum))	{
3793 			set_map_file(File);
3794 		} else {
3795 			set_to_default_map();
3796 		}
3797 	}
3798 
3799 	File = prefs->physics_file;
3800 	if (File.Exists()) {
3801 		set_physics_file(File);
3802 		import_definition_structures();
3803 	} else {
3804 		if(find_wad_file_that_has_checksum(File,
3805 			_typecode_physics, strPATHS, prefs->physics_checksum)) {
3806 			set_physics_file(File);
3807 			import_definition_structures();
3808 		} else {
3809 			/* Didn't find it.  Don't change them.. */
3810 		}
3811 	}
3812 
3813 	File = prefs->shapes_file;
3814 	if (File.Exists()) {
3815 		open_shapes_file(File);
3816 	} else {
3817 		if(find_file_with_modification_date(File,
3818 			_typecode_shapes, strPATHS, prefs->shapes_mod_date))
3819 		{
3820 			open_shapes_file(File);
3821 		} else {
3822 			/* What should I do? */
3823 		}
3824 	}
3825 
3826 	File = prefs->sounds_file;
3827 	if (File.Exists()) {
3828 		SoundManager::instance()->OpenSoundFile(File);
3829 	} else {
3830 		if(find_file_with_modification_date(File,
3831 			_typecode_sounds, strPATHS, prefs->sounds_mod_date)) {
3832 			SoundManager::instance()->OpenSoundFile(File);
3833 		} else {
3834 			/* What should I do? */
3835 		}
3836 	}
3837 
3838 	File = prefs->resources_file;
3839 	if (File.Exists())
3840 	{
3841 		set_external_resources_file(File);
3842 	}
3843 	set_external_resources_images_file(File);
3844 }
3845 
3846 
3847 // LP addition: get these from the preferences data
GetChaseCamData()3848 ChaseCamData& GetChaseCamData() {return player_preferences->ChaseCam;}
GetCrosshairData()3849 CrosshairData& GetCrosshairData() {return player_preferences->Crosshairs;}
Get_OGL_ConfigureData()3850 OGL_ConfigureData& Get_OGL_ConfigureData() {return graphics_preferences->OGL_Configure;}
3851 
3852 
3853 // ZZZ: override player-behavior modifiers
3854 static bool sStandardizeModifiers = false;
3855 
3856 
3857 void
standardize_player_behavior_modifiers()3858 standardize_player_behavior_modifiers() {
3859     sStandardizeModifiers = true;
3860 }
3861 
3862 
3863 void
restore_custom_player_behavior_modifiers()3864 restore_custom_player_behavior_modifiers() {
3865     sStandardizeModifiers = false;
3866 }
3867 
3868 
3869 bool
is_player_behavior_standard()3870 is_player_behavior_standard() {
3871 	return !dont_switch_to_new_weapon();
3872 }
3873 
3874 
3875 // LP addition: modification of Josh Elsasser's dont-switch-weapons patch
3876 // so as to access preferences stuff here
dont_switch_to_new_weapon()3877 bool dont_switch_to_new_weapon() {
3878     // ZZZ: let game require standard modifiers for a while
3879     if(!sStandardizeModifiers)
3880 	    return TEST_FLAG(input_preferences->modifiers,_inputmod_dont_switch_to_new_weapon);
3881     else
3882         return false;
3883 }
3884 
3885 
3886 bool
dont_auto_recenter()3887 dont_auto_recenter() {
3888 	return TEST_FLAG(input_preferences->modifiers, _inputmod_dont_auto_recenter);
3889 }
3890 
3891 
3892 // LP additions: MML-like prefs stuff
3893 // These parsers are intended to work correctly on both Mac and SDL prefs files;
3894 // including one crossing over to the other platform (uninterpreted fields become defaults)
3895 
3896 // To get around both RGBColor and rgb_color being used in the code
CopyColor(CType1 & Dest,CType2 & Src)3897 template<class CType1, class CType2> void CopyColor(CType1& Dest, CType2& Src)
3898 {
3899 	Dest.red = Src.red;
3900 	Dest.green = Src.green;
3901 	Dest.blue = Src.blue;
3902 }
3903 
3904 
3905 
3906 struct ViewSizeData
3907 {
3908 	short Width, Height;
3909 	bool HUD;
3910 };
3911 
3912 const ViewSizeData LegacyViewSizes[32] =
3913 {
3914 	{ 320, 160, true},
3915 	{ 480, 240, true},
3916 	{ 640, 480, true},
3917 	{ 640, 480, false},
3918 	{ 800, 600, true},
3919 	{ 800, 600, false},
3920 	{ 1024, 768, true},
3921 	{ 1024, 768, false},
3922 	{ 1280, 1024, true},
3923 	{ 1280, 1024, false},
3924 	{ 1600, 1200, true},
3925 	{ 1600, 1200, false},
3926 	{ 1024, 640, true},
3927 	{ 1024, 640, false},
3928 	{ 1280, 800, true},
3929 	{ 1280, 800, false},
3930 	{ 1280, 854, true},
3931 	{ 1280, 854, false},
3932 	{ 1440, 900, true},
3933 	{ 1440, 900, false},
3934 	{ 1680, 1050, true},
3935 	{ 1680, 1050, false},
3936 	{ 1920, 1200, true},
3937 	{ 1920, 1200, false},
3938 	{ 2560, 1600, true},
3939 	{ 2560, 1600, false},
3940 	{ 1280, 768, true},
3941 	{ 1280, 768, false},
3942 	{ 1280, 960, true},
3943 	{ 1280, 960, false},
3944 	{ 1280, 720, true},
3945 	{ 1280, 720, false}
3946 };
3947 
parse_graphics_preferences(InfoTree root,std::string version)3948 void parse_graphics_preferences(InfoTree root, std::string version)
3949 {
3950 	int scmode = -1;
3951 	root.read_attr("scmode_size", scmode);
3952 	if (scmode >= 0 && scmode < 32)
3953 	{
3954 		graphics_preferences->screen_mode.height = LegacyViewSizes[scmode].Height;
3955 		graphics_preferences->screen_mode.width = LegacyViewSizes[scmode].Width;
3956 		graphics_preferences->screen_mode.hud = LegacyViewSizes[scmode].HUD;
3957 	}
3958 
3959 	root.read_attr("scmode_height", graphics_preferences->screen_mode.height);
3960 	root.read_attr("scmode_width", graphics_preferences->screen_mode.width);
3961 	root.read_attr("scmode_auto_resolution", graphics_preferences->screen_mode.auto_resolution);
3962 	root.read_attr("scmode_high_dpi", graphics_preferences->screen_mode.high_dpi);
3963 	root.read_attr("scmode_hud", graphics_preferences->screen_mode.hud);
3964 	root.read_attr("scmode_hud_scale", graphics_preferences->screen_mode.hud_scale_level);
3965 	root.read_attr("scmode_term_scale", graphics_preferences->screen_mode.term_scale_level);
3966 	root.read_attr("scmode_translucent_map", graphics_preferences->screen_mode.translucent_map);
3967 	root.read_attr("scmode_camera_bob", graphics_preferences->screen_mode.camera_bob);
3968 	root.read_attr("scmode_accel", graphics_preferences->screen_mode.acceleration);
3969 	root.read_attr("scmode_highres", graphics_preferences->screen_mode.high_resolution);
3970 	root.read_attr("scmode_fullscreen", graphics_preferences->screen_mode.fullscreen);
3971 
3972 	root.read_attr("scmode_fix_h_not_v", graphics_preferences->screen_mode.fix_h_not_v);
3973 	root.read_attr("scmode_bitdepth", graphics_preferences->screen_mode.bit_depth);
3974 	root.read_attr("scmode_gamma", graphics_preferences->screen_mode.gamma_level);
3975 	root.read_attr("ogl_flags", graphics_preferences->OGL_Configure.Flags);
3976 	root.read_attr("software_alpha_blending", graphics_preferences->software_alpha_blending);
3977 	root.read_attr("software_sdl_driver", graphics_preferences->software_sdl_driver);
3978 	root.read_attr("anisotropy_level", graphics_preferences->OGL_Configure.AnisotropyLevel);
3979 	root.read_attr("multisamples", graphics_preferences->OGL_Configure.Multisamples);
3980 	root.read_attr("geforce_fix", graphics_preferences->OGL_Configure.GeForceFix);
3981 	root.read_attr("wait_for_vsync", graphics_preferences->OGL_Configure.WaitForVSync);
3982 	root.read_attr("gamma_corrected_blending", graphics_preferences->OGL_Configure.Use_sRGB);
3983 	root.read_attr("use_npot", graphics_preferences->OGL_Configure.Use_NPOT);
3984 	root.read_attr("double_corpse_limit", graphics_preferences->double_corpse_limit);
3985 	root.read_attr("hog_the_cpu", graphics_preferences->hog_the_cpu);
3986 	root.read_attr_bounded<int16>("movie_export_video_quality", graphics_preferences->movie_export_video_quality, 0, 100);
3987 	root.read_attr_bounded<int16>("movie_export_audio_quality", graphics_preferences->movie_export_audio_quality, 0, 100);
3988 
3989 
3990 	BOOST_FOREACH(InfoTree vtree, root.children_named("void"))
3991 	{
3992 		BOOST_FOREACH(InfoTree color, vtree.children_named("color"))
3993 		{
3994 			color.read_color(graphics_preferences->OGL_Configure.VoidColor);
3995 		}
3996 	}
3997 
3998 	BOOST_FOREACH(InfoTree landscape, root.children_named("landscapes"))
3999 	{
4000 		BOOST_FOREACH(InfoTree color, root.children_named("color"))
4001 		{
4002 			int16 index;
4003 			if (color.read_indexed("index", index, 8))
4004 				color.read_color(graphics_preferences->OGL_Configure.LscpColors[index / 2][index % 2]);
4005 		}
4006 	}
4007 
4008 	BOOST_FOREACH(InfoTree tex, root.children_named("texture"))
4009 	{
4010 		int16 index;
4011 		if (tex.read_indexed("index", index, OGL_NUMBER_OF_TEXTURE_TYPES+1))
4012 		{
4013 			OGL_Texture_Configure& Config = (index == OGL_NUMBER_OF_TEXTURE_TYPES) ? graphics_preferences->OGL_Configure.ModelConfig : graphics_preferences->OGL_Configure.TxtrConfigList[index];
4014 			tex.read_attr("near_filter", Config.NearFilter);
4015 			tex.read_attr("far_filter", Config.FarFilter);
4016 			tex.read_attr("resolution", Config.Resolution);
4017 			tex.read_attr("color_format", Config.ColorFormat);
4018 			tex.read_attr("max_size", Config.MaxSize);
4019 		}
4020 	}
4021 }
4022 
4023 
parse_player_preferences(InfoTree root,std::string version)4024 void parse_player_preferences(InfoTree root, std::string version)
4025 {
4026 	root.read_cstr("name", player_preferences->name, PREFERENCES_NAME_LENGTH);
4027 	root.read_attr("color", player_preferences->color);
4028 	root.read_attr("team", player_preferences->team);
4029 	root.read_attr("last_time_ran", player_preferences->last_time_ran);
4030 	root.read_attr("difficulty", player_preferences->difficulty_level);
4031 	root.read_attr("bkgd_music", player_preferences->background_music_on);
4032 	root.read_attr("crosshairs_active", player_preferences->crosshairs_active);
4033 
4034 	BOOST_FOREACH(InfoTree child, root.children_named("chase_cam"))
4035 	{
4036 		child.read_attr("behind", player_preferences->ChaseCam.Behind);
4037 		child.read_attr("upward", player_preferences->ChaseCam.Upward);
4038 		child.read_attr("rightward", player_preferences->ChaseCam.Rightward);
4039 		child.read_attr("flags", player_preferences->ChaseCam.Flags);
4040 		child.read_attr("damping", player_preferences->ChaseCam.Damping);
4041 		child.read_attr("spring", player_preferences->ChaseCam.Spring);
4042 		child.read_attr("opacity", player_preferences->ChaseCam.Opacity);
4043 	}
4044 
4045 	BOOST_FOREACH(InfoTree child, root.children_named("crosshairs"))
4046 	{
4047 		child.read_attr("thickness", player_preferences->Crosshairs.Thickness);
4048 		child.read_attr("from_center", player_preferences->Crosshairs.FromCenter);
4049 		child.read_attr("length", player_preferences->Crosshairs.Length);
4050 		child.read_attr("shape", player_preferences->Crosshairs.Shape);
4051 		child.read_attr("opacity", player_preferences->Crosshairs.Opacity);
4052 
4053 		BOOST_FOREACH(InfoTree color, child.children_named("color"))
4054 			color.read_color(player_preferences->Crosshairs.Color);
4055 	}
4056 }
4057 
translate_old_key(int code)4058 SDL_Scancode translate_old_key(int code)
4059 {
4060 	static int num_key_lookups = 323;
4061 	static SDL_Keycode key_lookups[] = {
4062 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4063 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_BACKSPACE, SDLK_TAB,
4064 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_CLEAR, SDLK_RETURN, SDLK_UNKNOWN,
4065 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_PAUSE,
4066 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4067 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_ESCAPE, SDLK_UNKNOWN, SDLK_UNKNOWN,
4068 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_SPACE, SDLK_EXCLAIM, SDLK_QUOTEDBL,
4069 		SDLK_HASH, SDLK_DOLLAR, SDLK_UNKNOWN, SDLK_AMPERSAND, SDLK_QUOTE,
4070 		SDLK_LEFTPAREN, SDLK_RIGHTPAREN, SDLK_ASTERISK, SDLK_PLUS, SDLK_COMMA,
4071 		SDLK_MINUS, SDLK_PERIOD, SDLK_SLASH, SDLK_0, SDLK_1,
4072 		SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6,
4073 		SDLK_7, SDLK_8, SDLK_9, SDLK_COLON, SDLK_SEMICOLON,
4074 		SDLK_LESS, SDLK_EQUALS, SDLK_GREATER, SDLK_QUESTION, SDLK_AT,
4075 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4076 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4077 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4078 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4079 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4080 		SDLK_UNKNOWN, SDLK_LEFTBRACKET, SDLK_BACKSLASH, SDLK_RIGHTBRACKET, SDLK_CARET,
4081 		SDLK_UNDERSCORE, SDLK_BACKQUOTE, SDLK_a, SDLK_b, SDLK_c,
4082 		SDLK_d, SDLK_e, SDLK_f, SDLK_g, SDLK_h,
4083 		SDLK_i, SDLK_j, SDLK_k, SDLK_l, SDLK_m,
4084 		SDLK_n, SDLK_o, SDLK_p, SDLK_q, SDLK_r,
4085 		SDLK_s, SDLK_t, SDLK_u, SDLK_v, SDLK_w,
4086 		SDLK_x, SDLK_y, SDLK_z, SDLK_UNKNOWN, SDLK_UNKNOWN,
4087 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_DELETE, SDLK_UNKNOWN, SDLK_UNKNOWN,
4088 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4089 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4090 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4091 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4092 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4093 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4094 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4095 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4096 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4097 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4098 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4099 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4100 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4101 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4102 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4103 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4104 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4105 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4106 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4107 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4108 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4109 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4110 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4111 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4112 		SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4113 		SDLK_UNKNOWN, SDLK_KP_0, SDLK_KP_1, SDLK_KP_2, SDLK_KP_3,
4114 		SDLK_KP_4, SDLK_KP_5, SDLK_KP_6, SDLK_KP_7, SDLK_KP_8,
4115 		SDLK_KP_9, SDLK_KP_PERIOD, SDLK_KP_DIVIDE, SDLK_KP_MULTIPLY, SDLK_KP_MINUS,
4116 		SDLK_KP_PLUS, SDLK_KP_ENTER, SDLK_KP_EQUALS, SDLK_UP, SDLK_DOWN,
4117 		SDLK_RIGHT, SDLK_LEFT, SDLK_INSERT, SDLK_HOME, SDLK_END,
4118 		SDLK_PAGEUP, SDLK_PAGEDOWN, SDLK_F1, SDLK_F2, SDLK_F3,
4119 		SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8,
4120 		SDLK_F9, SDLK_F10, SDLK_F11, SDLK_F12, SDLK_F13,
4121 		SDLK_F14, SDLK_F15, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
4122 		SDLK_NUMLOCKCLEAR, SDLK_CAPSLOCK, SDLK_SCROLLLOCK, SDLK_RSHIFT, SDLK_LSHIFT,
4123 		SDLK_RCTRL, SDLK_LCTRL, SDLK_RALT, SDLK_LALT, SDLK_RGUI,
4124 		SDLK_LGUI, SDLK_LGUI, SDLK_RGUI, SDLK_MODE, SDLK_UNKNOWN,
4125 		SDLK_HELP, SDLK_PRINTSCREEN, SDLK_SYSREQ, SDLK_UNKNOWN, SDLK_MENU,
4126 		SDLK_POWER, SDLK_CURRENCYUNIT, SDLK_UNDO
4127 	};
4128 
4129 	if (code >= 65 && code < 73)
4130 		return static_cast<SDL_Scancode>(AO_SCANCODE_BASE_MOUSE_BUTTON + (code - 65));
4131 	else if (code >= 73 && code < 91)
4132 		return static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + (code - 73));
4133 	else if (code < num_key_lookups)
4134 		return SDL_GetScancodeFromKey(key_lookups[code]);
4135 	return SDL_SCANCODE_UNKNOWN;
4136 }
4137 
parse_input_preferences(InfoTree root,std::string version)4138 void parse_input_preferences(InfoTree root, std::string version)
4139 {
4140 	root.read_attr("device", input_preferences->input_device);
4141 	root.read_attr("modifiers", input_preferences->modifiers);
4142 
4143 	// old prefs may have combined sensitivity
4144 	root.read_attr("sensitivity", input_preferences->sens_horizontal);
4145 	root.read_attr("sensitivity", input_preferences->sens_vertical);
4146 	root.read_attr("sens_horizontal", input_preferences->sens_horizontal);
4147 	root.read_attr("sens_vertical", input_preferences->sens_vertical);
4148 
4149 	if (!version.length() || version < "20181113")
4150 		input_preferences->classic_vertical_aim = true;
4151 	else if (version < "20190317")
4152 		input_preferences->classic_vertical_aim = false;
4153 	root.read_attr("classic_vertical_aim", input_preferences->classic_vertical_aim);
4154 
4155 	if (!root.read_attr("classic_aim_speed_limits", input_preferences->classic_aim_speed_limits))
4156 	{
4157 		// Assume users with older prefs with "mouse_max_speed" above its default value don't want classic limits
4158 		float mouse_max_speed;
4159 		if (root.read_attr("mouse_max_speed", mouse_max_speed) && mouse_max_speed > 0.25)
4160 			input_preferences->classic_aim_speed_limits = false;
4161 	}
4162 
4163 	// old prefs may have boolean acceleration flag
4164 	bool accel = false;
4165 	if (root.read_attr("mouse_acceleration", accel))
4166 	{
4167 		input_preferences->mouse_accel_type = _mouse_accel_classic;
4168 		input_preferences->mouse_accel_scale = 1.f;
4169 	}
4170 	root.read_attr_bounded<int16>("mouse_accel_type",
4171 								  input_preferences->mouse_accel_type,
4172 								  0, NUMBER_OF_MOUSE_ACCEL_TYPES - 1);
4173 	root.read_attr("mouse_accel_scale", input_preferences->mouse_accel_scale);
4174 
4175 	// old prefs mixed "classic vertical aim" with acceleration type
4176 	if (version >= "20181113" && version < "20190317")
4177 	{
4178 		if (input_preferences->mouse_accel_type == _mouse_accel_symmetric)
4179 		{
4180 			input_preferences->classic_vertical_aim = false;
4181 			input_preferences->mouse_accel_type = _mouse_accel_classic;
4182 		}
4183 		else if (input_preferences->mouse_accel_type == _mouse_accel_classic)
4184 		{
4185 			input_preferences->classic_vertical_aim = true;
4186 			input_preferences->sens_vertical *= 4.f;
4187 		}
4188 	}
4189 
4190 	if (!version.length() || version < "20170821")
4191 		input_preferences->raw_mouse_input = false;
4192 	root.read_attr("raw_mouse_input", input_preferences->raw_mouse_input);
4193 	if (!version.length() || version < "20181208")
4194 		input_preferences->extra_mouse_precision = false;
4195 	root.read_attr("extra_mouse_precision", input_preferences->extra_mouse_precision);
4196 	root.read_attr("controller_analog", input_preferences->controller_analog);
4197 	root.read_attr("controller_sensitivity", input_preferences->controller_sensitivity);
4198 	root.read_attr("controller_deadzone", input_preferences->controller_deadzone);
4199 
4200 	// remove default key bindings the first time we see one from these prefs
4201 	bool seen_key[NUMBER_OF_KEYS];
4202 	memset(seen_key, 0, sizeof(seen_key));
4203 	bool seen_shell_key[NUMBER_OF_SHELL_KEYS];
4204 	memset(seen_shell_key, 0, sizeof(seen_shell_key));
4205 
4206 	// import old key bindings
4207 	BOOST_FOREACH(InfoTree key, root.children_named("sdl_key"))
4208 	{
4209 		int16 index;
4210 		if (key.read_indexed("index", index, NUMBER_OF_KEYS))
4211 		{
4212 			if (!seen_key[index])
4213 			{
4214 				input_preferences->key_bindings[index].clear();
4215 				seen_key[index] = true;
4216 			}
4217 			int code;
4218 			if (key.read_attr("value", code))
4219 			{
4220 				SDL_Scancode translated = translate_old_key(code);
4221 				unset_scancode(translated);
4222 				input_preferences->key_bindings[index].insert(translated);
4223 			}
4224 		}
4225 		else if (key.read_indexed("index", index, NUMBER_OF_KEYS + NUMBER_OF_SHELL_KEYS))
4226 		{
4227 			int shell_index = index - NUMBER_OF_KEYS;
4228 			if (!seen_shell_key[shell_index])
4229 			{
4230 				input_preferences->shell_key_bindings[shell_index].clear();
4231 				seen_shell_key[shell_index] = true;
4232 			}
4233 			int code;
4234 			if (key.read_attr("value", code))
4235 			{
4236 				SDL_Scancode translated = translate_old_key(code);
4237 				unset_scancode(translated);
4238 				input_preferences->shell_key_bindings[shell_index].insert(translated);
4239 			}
4240 		}
4241 	}
4242 
4243 	BOOST_FOREACH(InfoTree key, root.children_named("binding"))
4244 	{
4245 		std::string action_name, pressed_name;
4246 		if (key.read_attr("action", action_name) &&
4247 			key.read_attr("pressed", pressed_name))
4248 		{
4249 			bool shell = false;
4250 			int index = index_for_action_name(action_name, shell);
4251 			if (index < 0)
4252 				continue;
4253 			SDL_Scancode code = code_for_binding_name(pressed_name);
4254 			if (shell)
4255 			{
4256 				if (!seen_shell_key[index])
4257 				{
4258 					input_preferences->shell_key_bindings[index].clear();
4259 					seen_shell_key[index] = true;
4260 				}
4261 				unset_scancode(code);
4262 				input_preferences->shell_key_bindings[index].insert(code);
4263 			}
4264 			else
4265 			{
4266 				if (!seen_key[index])
4267 				{
4268 					input_preferences->key_bindings[index].clear();
4269 					seen_key[index] = true;
4270 				}
4271 				unset_scancode(code);
4272 				input_preferences->key_bindings[index].insert(code);
4273 			}
4274 		}
4275 	}
4276 }
4277 
parse_sound_preferences(InfoTree root,std::string version)4278 void parse_sound_preferences(InfoTree root, std::string version)
4279 {
4280 	root.read_attr("channels", sound_preferences->channel_count);
4281 	root.read_attr("volume", sound_preferences->volume);
4282 	root.read_attr("music_volume", sound_preferences->music);
4283 	root.read_attr("flags", sound_preferences->flags);
4284 	root.read_attr("rate", sound_preferences->rate);
4285 	root.read_attr("samples", sound_preferences->samples);
4286 	root.read_attr("volume_while_speaking", sound_preferences->volume_while_speaking);
4287 	root.read_attr("mute_while_transmitting", sound_preferences->mute_while_transmitting);
4288 }
4289 
4290 
4291 
parse_network_preferences(InfoTree root,std::string version)4292 void parse_network_preferences(InfoTree root, std::string version)
4293 {
4294 	root.read_attr("microphone", network_preferences->allow_microphone);
4295 	root.read_attr("untimed", network_preferences->game_is_untimed);
4296 	root.read_attr("type", network_preferences->type);
4297 	root.read_attr("game_type", network_preferences->game_type);
4298 	root.read_attr("difficulty", network_preferences->difficulty_level);
4299 	root.read_attr("game_options", network_preferences->game_options);
4300 	root.read_attr("time_limit", network_preferences->time_limit);
4301 	root.read_attr("kill_limit", network_preferences->kill_limit);
4302 	root.read_attr("entry_point", network_preferences->entry_point);
4303 	root.read_attr("autogather", network_preferences->autogather);
4304 	root.read_attr("join_by_address", network_preferences->join_by_address);
4305 	root.read_cstr("join_address", network_preferences->join_address, 255);
4306 	root.read_attr("local_game_port", network_preferences->game_port);
4307 
4308 	std::string protocol;
4309 	if (root.read_attr("game_protocol", protocol))
4310 	{
4311 		for (int i = 0; i < NUMBER_OF_NETWORK_GAME_PROTOCOL_NAMES; ++i)
4312 		{
4313 			if (protocol == sNetworkGameProtocolNames[i])
4314 			{
4315 				network_preferences->game_protocol = i;
4316 				break;
4317 			}
4318 		}
4319 	}
4320 
4321 	root.read_attr("use_speex_netmic_encoder", network_preferences->use_speex_encoder);
4322 	root.read_attr("use_netscript", network_preferences->use_netscript);
4323 	root.read_path("netscript_file", network_preferences->netscript_file);
4324 	root.read_attr("cheat_flags", network_preferences->cheat_flags);
4325 	root.read_attr("advertise_on_metaserver", network_preferences->advertise_on_metaserver);
4326 	root.read_attr("attempt_upnp", network_preferences->attempt_upnp);
4327 	root.read_attr("check_for_updates", network_preferences->check_for_updates);
4328 	root.read_attr("verify_https", network_preferences->verify_https);
4329 	root.read_attr("use_custom_metaserver_colors", network_preferences->use_custom_metaserver_colors);
4330 	root.read_cstr("metaserver_login", network_preferences->metaserver_login, 15);
4331 	root.read_attr("mute_metaserver_guests", network_preferences->mute_metaserver_guests);
4332 	root.read_cstr("metaserver_clear_password", network_preferences->metaserver_password, 15);
4333 
4334 	char obscured_password[33];
4335 	if (root.read_cstr("metaserver_password", obscured_password, 32))
4336 	{
4337 		for (int i = 0; i < 15; i++)
4338 		{
4339 			unsigned int c;
4340 			sscanf(obscured_password + i*2, "%2x", &c);
4341 			network_preferences->metaserver_password[i] = (char) c ^ sPasswordMask[i];
4342 		}
4343 		network_preferences->metaserver_password[15] = '\0';
4344 	}
4345 
4346 	root.read_attr("join_metaserver_by_default", network_preferences->join_metaserver_by_default);
4347 	root.read_attr("allow_stats", network_preferences->allow_stats);
4348 
4349 	BOOST_FOREACH(InfoTree color, root.children_named("color"))
4350 	{
4351 		int16 index;
4352 		if (color.read_indexed("index", index, 2))
4353 			color.read_color(network_preferences->metaserver_colors[index]);
4354 	}
4355 
4356 	BOOST_FOREACH(InfoTree child, root.children_named("star_protocol"))
4357 		StarGameProtocol::ParsePreferencesTree(child, version);
4358 	BOOST_FOREACH(InfoTree child, root.children_named("ring_protocol"))
4359 		RingGameProtocol::ParsePreferencesTree(child, version);
4360 }
4361 
parse_environment_preferences(InfoTree root,std::string version)4362 void parse_environment_preferences(InfoTree root, std::string version)
4363 {
4364 	root.read_path("map_file", environment_preferences->map_file);
4365 	root.read_path("physics_file", environment_preferences->physics_file);
4366 	root.read_path("shapes_file", environment_preferences->shapes_file);
4367 	root.read_path("sounds_file", environment_preferences->sounds_file);
4368 	root.read_path("resources_file", environment_preferences->resources_file);
4369 	root.read_attr("map_checksum", environment_preferences->map_checksum);
4370 	root.read_attr("physics_checksum", environment_preferences->physics_checksum);
4371 	root.read_attr("shapes_mod_date", environment_preferences->shapes_mod_date);
4372 	root.read_attr("sounds_mod_date", environment_preferences->sounds_mod_date);
4373 	root.read_attr("group_by_directory", environment_preferences->group_by_directory);
4374 	root.read_attr("reduce_singletons", environment_preferences->reduce_singletons);
4375 	root.read_attr("smooth_text", environment_preferences->smooth_text);
4376 	root.read_path("solo_lua_file", environment_preferences->solo_lua_file);
4377 	root.read_attr("use_solo_lua", environment_preferences->use_solo_lua);
4378 	root.read_attr("use_replay_net_lua", environment_preferences->use_replay_net_lua);
4379 	root.read_attr("hide_alephone_extensions", environment_preferences->hide_extensions);
4380 
4381 	uint32 profile = FILM_PROFILE_DEFAULT + 1;
4382 	root.read_attr("film_profile", profile);
4383 	if (profile <= FILM_PROFILE_DEFAULT)
4384 		environment_preferences->film_profile = static_cast<FilmProfileType>(profile);
4385 
4386 	root.read_attr("maximum_quick_saves", environment_preferences->maximum_quick_saves);
4387 
4388 	BOOST_FOREACH(InfoTree plugin, root.children_named("disable_plugin"))
4389 	{
4390 		char tempstr[256];
4391 		if (plugin.read_path("path", tempstr))
4392 		{
4393 			Plugins::instance()->disable(tempstr);
4394 		}
4395 	}
4396 }
4397