summaryrefslogtreecommitdiff
path: root/src/uqm/setupmenu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/setupmenu.c')
-rw-r--r--src/uqm/setupmenu.c1613
1 files changed, 1613 insertions, 0 deletions
diff --git a/src/uqm/setupmenu.c b/src/uqm/setupmenu.c
new file mode 100644
index 0000000..b858617
--- /dev/null
+++ b/src/uqm/setupmenu.c
@@ -0,0 +1,1613 @@
+// Copyright Michael Martin, 2004.
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "setupmenu.h"
+
+#include "controls.h"
+#include "options.h"
+#include "setup.h"
+#include "sounds.h"
+#include "colors.h"
+#include "libs/gfxlib.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/widgets.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/strlib.h"
+#include "libs/reslib.h"
+#include "libs/inplib.h"
+#include "libs/vidlib.h"
+#include "libs/sound/sound.h"
+#include "libs/resource/stringbank.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "resinst.h"
+#include "nameref.h"
+#include <math.h>
+
+
+static STRING SetupTab;
+
+typedef struct setup_menu_state {
+ BOOLEAN (*InputFunc) (struct setup_menu_state *pInputState);
+
+ BOOLEAN initialized;
+ int anim_frame_count;
+ DWORD NextTime;
+} SETUP_MENU_STATE;
+
+static BOOLEAN DoSetupMenu (SETUP_MENU_STATE *pInputState);
+static BOOLEAN done;
+static WIDGET *current, *next;
+
+static int quit_main_menu (WIDGET *self, int event);
+static int quit_sub_menu (WIDGET *self, int event);
+static int do_graphics (WIDGET *self, int event);
+static int do_audio (WIDGET *self, int event);
+static int do_engine (WIDGET *self, int event);
+static int do_resources (WIDGET *self, int event);
+static int do_keyconfig (WIDGET *self, int event);
+static int do_advanced (WIDGET *self, int event);
+static int do_editkeys (WIDGET *self, int event);
+static void change_template (WIDGET_CHOICE *self, int oldval);
+static void rename_template (WIDGET_TEXTENTRY *self);
+static void rebind_control (WIDGET_CONTROLENTRY *widget);
+static void clear_control (WIDGET_CONTROLENTRY *widget);
+
+#define MENU_COUNT 8
+#define CHOICE_COUNT 24
+#define SLIDER_COUNT 4
+#define BUTTON_COUNT 10
+#define LABEL_COUNT 4
+#define TEXTENTRY_COUNT 1
+#define CONTROLENTRY_COUNT 7
+
+/* The space for our widgets */
+static WIDGET_MENU_SCREEN menus[MENU_COUNT];
+static WIDGET_CHOICE choices[CHOICE_COUNT];
+static WIDGET_SLIDER sliders[SLIDER_COUNT];
+static WIDGET_BUTTON buttons[BUTTON_COUNT];
+static WIDGET_LABEL labels[LABEL_COUNT];
+static WIDGET_TEXTENTRY textentries[TEXTENTRY_COUNT];
+static WIDGET_CONTROLENTRY controlentries[CONTROLENTRY_COUNT];
+
+/* The hardcoded data that isn't strings */
+
+typedef int (*HANDLER)(WIDGET *, int);
+
+static int choice_widths[CHOICE_COUNT] = {
+ 3, 2, 3, 3, 2, 2, 2, 2, 2, 2,
+ 2, 2, 3, 2, 2, 3, 3, 2, 3, 3,
+ 3, 2, 2, 2 };
+
+static HANDLER button_handlers[BUTTON_COUNT] = {
+ quit_main_menu, quit_sub_menu, do_graphics, do_engine,
+ do_audio, do_resources, do_keyconfig, do_advanced, do_editkeys,
+ do_keyconfig };
+
+/* These refer to uninitialized widgets, but that's OK; we'll fill
+ * them in before we touch them */
+static WIDGET *main_widgets[] = {
+ (WIDGET *)(&buttons[2]),
+ (WIDGET *)(&buttons[3]),
+ (WIDGET *)(&buttons[4]),
+ (WIDGET *)(&buttons[5]),
+ (WIDGET *)(&buttons[6]),
+ (WIDGET *)(&buttons[7]),
+ (WIDGET *)(&buttons[0]),
+ NULL };
+
+static WIDGET *graphics_widgets[] = {
+ (WIDGET *)(&choices[0]),
+ (WIDGET *)(&choices[23]),
+ (WIDGET *)(&choices[10]),
+ (WIDGET *)(&sliders[3]),
+ (WIDGET *)(&choices[2]),
+ (WIDGET *)(&choices[3]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static WIDGET *audio_widgets[] = {
+ (WIDGET *)(&sliders[0]),
+ (WIDGET *)(&sliders[1]),
+ (WIDGET *)(&sliders[2]),
+ (WIDGET *)(&choices[14]),
+ (WIDGET *)(&choices[9]),
+ (WIDGET *)(&choices[21]),
+ (WIDGET *)(&choices[22]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static WIDGET *engine_widgets[] = {
+ (WIDGET *)(&choices[4]),
+ (WIDGET *)(&choices[5]),
+ (WIDGET *)(&choices[6]),
+ (WIDGET *)(&choices[7]),
+ (WIDGET *)(&choices[8]),
+ (WIDGET *)(&choices[13]),
+ (WIDGET *)(&choices[11]),
+ (WIDGET *)(&choices[17]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static WIDGET *advanced_widgets[] = {
+#ifdef HAVE_OPENGL
+ (WIDGET *)(&choices[1]),
+#endif
+ (WIDGET *)(&choices[12]),
+ (WIDGET *)(&choices[15]),
+ (WIDGET *)(&choices[16]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static WIDGET *keyconfig_widgets[] = {
+ (WIDGET *)(&choices[18]),
+ (WIDGET *)(&choices[19]),
+ (WIDGET *)(&labels[1]),
+ (WIDGET *)(&buttons[8]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static WIDGET *editkeys_widgets[] = {
+ (WIDGET *)(&choices[20]),
+ (WIDGET *)(&labels[2]),
+ (WIDGET *)(&textentries[0]),
+ (WIDGET *)(&controlentries[0]),
+ (WIDGET *)(&controlentries[1]),
+ (WIDGET *)(&controlentries[2]),
+ (WIDGET *)(&controlentries[3]),
+ (WIDGET *)(&controlentries[4]),
+ (WIDGET *)(&controlentries[5]),
+ (WIDGET *)(&controlentries[6]),
+ (WIDGET *)(&buttons[9]),
+ NULL };
+
+static WIDGET *incomplete_widgets[] = {
+ (WIDGET *)(&labels[0]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static const struct
+{
+ WIDGET **widgets;
+ int bgIndex;
+}
+menu_defs[] =
+{
+ {main_widgets, 0},
+ {graphics_widgets, 1},
+ {audio_widgets, 1},
+ {engine_widgets, 2},
+ {incomplete_widgets, 3},
+ {keyconfig_widgets, 1},
+ {advanced_widgets, 2},
+ {editkeys_widgets, 1},
+ {NULL, 0}
+};
+
+// Start with reasonable gamma bounds. These will get updated
+// as we find out the actual bounds.
+static float minGamma = 0.4f;
+static float maxGamma = 2.5f;
+// The gamma slider uses an exponential curve
+// We use y = e^(2.1972*(x-1)) curve to give us a nice spread of
+// gamma values 0.11 < g < 9.0 centered at g=1.0
+#define GAMMA_CURVE_B 2.1972f
+static float minGammaX;
+static float maxGammaX;
+
+
+static int
+number_res_options (void)
+{
+ if (TFB_SupportsHardwareScaling ())
+ {
+ return 5;
+ }
+ else
+ {
+ return 2;
+ }
+}
+
+static int
+quit_main_menu (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = NULL;
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+quit_sub_menu (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[0]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_SELECT);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_graphics (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[1]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_audio (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[2]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_engine (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[3]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_resources (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[4]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_keyconfig (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[5]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_advanced (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[6]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static void
+populate_editkeys (int templat)
+{
+ int i, j;
+
+ strncpy (textentries[0].value, input_templates[templat].name, textentries[0].maxlen);
+ textentries[0].value[textentries[0].maxlen-1] = 0;
+
+ for (i = 0; i < NUM_KEYS; i++)
+ {
+ for (j = 0; j < 2; j++)
+ {
+ InterrogateInputState (templat, i, j, controlentries[i].controlname[j], WIDGET_CONTROLENTRY_WIDTH);
+ }
+ }
+}
+
+static int
+do_editkeys (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[7]);
+ /* Prepare the components */
+ choices[20].selected = 0;
+
+ populate_editkeys (0);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static void
+change_template (WIDGET_CHOICE *self, int oldval)
+{
+ (void) oldval;
+ populate_editkeys (self->selected);
+}
+
+static void
+rename_template (WIDGET_TEXTENTRY *self)
+{
+ /* TODO: This will have to change if the size of the
+ input_templates name is changed. It would probably be nice
+ to track this symbolically or ensure that self->value's
+ buffer is always at least this big; this will require some
+ reworking of widgets */
+ strncpy (input_templates[choices[20].selected].name, self->value, 30);
+ input_templates[choices[20].selected].name[29] = 0;
+}
+
+#define NUM_STEPS 20
+#define X_STEP (SCREEN_WIDTH / NUM_STEPS)
+#define Y_STEP (SCREEN_HEIGHT / NUM_STEPS)
+#define MENU_FRAME_RATE (ONE_SECOND / 20)
+
+static void
+SetDefaults (void)
+{
+ GLOBALOPTS opts;
+
+ GetGlobalOptions (&opts);
+ if (opts.res == OPTVAL_CUSTOM)
+ {
+ choices[0].numopts = number_res_options () + 1;
+ }
+ else
+ {
+ choices[0].numopts = number_res_options ();
+ }
+ choices[0].selected = opts.res;
+ choices[1].selected = opts.driver;
+ choices[2].selected = opts.scaler;
+ choices[3].selected = opts.scanlines;
+ choices[4].selected = opts.menu;
+ choices[5].selected = opts.text;
+ choices[6].selected = opts.cscan;
+ choices[7].selected = opts.scroll;
+ choices[8].selected = opts.subtitles;
+ choices[9].selected = opts.music3do;
+ choices[10].selected = opts.fullscreen;
+ choices[11].selected = opts.intro;
+ choices[12].selected = opts.fps;
+ choices[13].selected = opts.meleezoom;
+ choices[14].selected = opts.stereo;
+ choices[15].selected = opts.adriver;
+ choices[16].selected = opts.aquality;
+ choices[17].selected = opts.shield;
+ choices[18].selected = opts.player1;
+ choices[19].selected = opts.player2;
+ choices[20].selected = 0;
+ choices[21].selected = opts.musicremix;
+ choices[22].selected = opts.speech;
+ choices[23].selected = opts.keepaspect;
+
+ sliders[0].value = opts.musicvol;
+ sliders[1].value = opts.sfxvol;
+ sliders[2].value = opts.speechvol;
+ sliders[3].value = opts.gamma;
+}
+
+static void
+PropagateResults (void)
+{
+ GLOBALOPTS opts;
+ opts.res = choices[0].selected;
+ opts.driver = choices[1].selected;
+ opts.scaler = choices[2].selected;
+ opts.scanlines = choices[3].selected;
+ opts.menu = choices[4].selected;
+ opts.text = choices[5].selected;
+ opts.cscan = choices[6].selected;
+ opts.scroll = choices[7].selected;
+ opts.subtitles = choices[8].selected;
+ opts.music3do = choices[9].selected;
+ opts.fullscreen = choices[10].selected;
+ opts.intro = choices[11].selected;
+ opts.fps = choices[12].selected;
+ opts.meleezoom = choices[13].selected;
+ opts.stereo = choices[14].selected;
+ opts.adriver = choices[15].selected;
+ opts.aquality = choices[16].selected;
+ opts.shield = choices[17].selected;
+ opts.player1 = choices[18].selected;
+ opts.player2 = choices[19].selected;
+ opts.musicremix = choices[21].selected;
+ opts.speech = choices[22].selected;
+ opts.keepaspect = choices[23].selected;
+
+ opts.musicvol = sliders[0].value;
+ opts.sfxvol = sliders[1].value;
+ opts.speechvol = sliders[2].value;
+ opts.gamma = sliders[3].value;
+ SetGlobalOptions (&opts);
+}
+
+static BOOLEAN
+DoSetupMenu (SETUP_MENU_STATE *pInputState)
+{
+ /* Cancel any presses of the Pause key. */
+ GamePaused = FALSE;
+
+ if (!pInputState->initialized)
+ {
+ SetDefaultMenuRepeatDelay ();
+ pInputState->NextTime = GetTimeCounter ();
+ SetDefaults ();
+ Widget_SetFont (StarConFont);
+ Widget_SetWindowColors (SHADOWBOX_BACKGROUND_COLOR,
+ SHADOWBOX_DARK_COLOR, SHADOWBOX_MEDIUM_COLOR);
+
+ current = NULL;
+ next = (WIDGET *)(&menus[0]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+
+ pInputState->initialized = TRUE;
+ }
+ if (current != next)
+ {
+ SetTransitionSource (NULL);
+ }
+
+ BatchGraphics ();
+ (*next->draw)(next, 0, 0);
+
+ if (current != next)
+ {
+ ScreenTransition (3, NULL);
+ current = next;
+ }
+
+ UnbatchGraphics ();
+
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ Widget_Event (WIDGET_EVENT_UP);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ Widget_Event (WIDGET_EVENT_DOWN);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_LEFT])
+ {
+ Widget_Event (WIDGET_EVENT_LEFT);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT])
+ {
+ Widget_Event (WIDGET_EVENT_RIGHT);
+ }
+ if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ Widget_Event (WIDGET_EVENT_SELECT);
+ }
+ if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ Widget_Event (WIDGET_EVENT_CANCEL);
+ }
+ if (PulsedInputState.menu[KEY_MENU_DELETE])
+ {
+ Widget_Event (WIDGET_EVENT_DELETE);
+ }
+
+ SleepThreadUntil (pInputState->NextTime + MENU_FRAME_RATE);
+ pInputState->NextTime = GetTimeCounter ();
+ return !((GLOBAL (CurrentActivity) & CHECK_ABORT) ||
+ (next == NULL));
+}
+
+static void
+redraw_menu (void)
+{
+ BatchGraphics ();
+ (*next->draw)(next, 0, 0);
+ UnbatchGraphics ();
+}
+
+static BOOLEAN
+OnTextEntryChange (TEXTENTRY_STATE *pTES)
+{
+ WIDGET_TEXTENTRY *widget = (WIDGET_TEXTENTRY *) pTES->CbParam;
+
+ widget->cursor_pos = pTES->CursorPos;
+ if (pTES->JoystickMode)
+ widget->state |= WTE_BLOCKCUR;
+ else
+ widget->state &= ~WTE_BLOCKCUR;
+
+ // XXX TODO: Here, we can examine the text entered so far
+ // to make sure it fits on the screen, for example,
+ // and return FALSE to disallow the last change
+
+ return TRUE; // allow change
+}
+
+static BOOLEAN
+OnTextEntryFrame (TEXTENTRY_STATE *pTES)
+{
+ redraw_menu ();
+
+ SleepThreadUntil (pTES->NextTime);
+ pTES->NextTime = GetTimeCounter () + MENU_FRAME_RATE;
+
+ return TRUE; // continue
+}
+
+static int
+OnTextEntryEvent (WIDGET_TEXTENTRY *widget)
+{ // Going to edit the text
+ TEXTENTRY_STATE tes;
+ UNICODE revert_buf[256];
+
+ // position cursor at the end of text
+ widget->cursor_pos = utf8StringCount (widget->value);
+ widget->state = WTE_EDITING;
+ redraw_menu ();
+
+ // make a backup copy for revert on cancel
+ utf8StringCopy (revert_buf, sizeof (revert_buf), widget->value);
+
+ // text entry setup
+ tes.Initialized = FALSE;
+ tes.NextTime = GetTimeCounter () + MENU_FRAME_RATE;
+ tes.BaseStr = widget->value;
+ tes.MaxSize = widget->maxlen;
+ tes.CursorPos = widget->cursor_pos;
+ tes.CbParam = widget;
+ tes.ChangeCallback = OnTextEntryChange;
+ tes.FrameCallback = OnTextEntryFrame;
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_SELECT);
+ if (!DoTextEntry (&tes))
+ { // editing failed (canceled) -- revert the changes
+ utf8StringCopy (widget->value, widget->maxlen, revert_buf);
+ }
+ else
+ {
+ if (widget->onChange)
+ {
+ (*(widget->onChange))(widget);
+ }
+ }
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ widget->state = WTE_NORMAL;
+ redraw_menu ();
+
+ return TRUE; // event handled
+}
+
+static inline float
+gammaCurve (float x)
+{
+ // The slider uses an exponential curve
+ return exp ((x - 1) * GAMMA_CURVE_B);
+}
+
+static inline float
+solveGammaCurve (float y)
+{
+ return log (y) / GAMMA_CURVE_B + 1;
+}
+
+static int
+gammaToSlider (float gamma)
+{
+ const float x = solveGammaCurve (gamma);
+ const float step = (maxGammaX - minGammaX) / 100;
+ return (int) ((x - minGammaX) / step + 0.5);
+}
+
+static float
+sliderToGamma (int value)
+{
+ const float step = (maxGammaX - minGammaX) / 100;
+ const float x = minGammaX + step * value;
+ const float g = gammaCurve (x);
+ // report any value that is close enough as 1.0
+ return (fabs (g - 1.0f) < 0.001f) ? 1.0f : g;
+}
+
+static void
+updateGammaBounds (bool useUpper)
+{
+ float g, x;
+ int slider;
+
+ // The slider uses an exponential curve.
+ // Calculate where on the curve the min and max gamma values are
+ minGammaX = solveGammaCurve (minGamma);
+ maxGammaX = solveGammaCurve (maxGamma);
+
+ // We have 100 discrete steps through the range, so the slider may
+ // skip over a 1.0 gamma. We need to ensure that there always is
+ // a 1.0 on the slider by tweaking the range (expanding/contracting).
+ slider = gammaToSlider (1.0f);
+ g = sliderToGamma (slider);
+ if (g == 1.0f)
+ return; // no adjustment needed
+
+ x = solveGammaCurve (g);
+ if (useUpper)
+ { // Move the upper bound up or down to land on 1.0
+ const float d = (x - 1.0f) * 100 / slider;
+ maxGammaX -= d;
+ maxGamma = gammaCurve (maxGammaX);
+ }
+ else
+ { // Move the lower bound up or down to land on 1.0
+ const float d = (x - 1.0f) * 100 / (100 - slider);
+ minGammaX -= d;
+ minGamma = gammaCurve (minGammaX);
+ }
+}
+
+static int
+gamma_HandleEventSlider (WIDGET *_self, int event)
+{
+ WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self;
+ int prevValue = self->value;
+ float gamma;
+ bool set;
+
+ switch (event)
+ {
+ case WIDGET_EVENT_LEFT:
+ self->value -= self->step;
+ break;
+ case WIDGET_EVENT_RIGHT:
+ self->value += self->step;
+ break;
+ default:
+ return FALSE;
+ }
+
+ // Limit the slider to values accepted by gfx subsys
+ gamma = sliderToGamma (self->value);
+ set = TFB_SetGamma (gamma);
+ if (!set)
+ { // revert
+ self->value = prevValue;
+ gamma = sliderToGamma (self->value);
+ }
+
+ // Grow or shrink the range based on accepted values
+ if (gamma < minGamma || (!set && event == WIDGET_EVENT_LEFT))
+ {
+ minGamma = gamma;
+ updateGammaBounds (true);
+ // at the lowest end
+ self->value = 0;
+ }
+ else if (gamma > maxGamma || (!set && event == WIDGET_EVENT_RIGHT))
+ {
+ maxGamma = gamma;
+ updateGammaBounds (false);
+ // at the highest end
+ self->value = 100;
+ }
+ return TRUE;
+}
+
+static void
+gamma_DrawValue (WIDGET_SLIDER *self, int x, int y)
+{
+ TEXT t;
+ char buf[16];
+ float gamma = sliderToGamma (self->value);
+ snprintf (buf, sizeof buf, "%.4f", gamma);
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ t.pStr = buf;
+
+ font_DrawText (&t);
+}
+
+static void
+rebind_control (WIDGET_CONTROLENTRY *widget)
+{
+ int templat = choices[20].selected;
+ int control = widget->controlindex;
+ int index = widget->highlighted;
+
+ FlushInput ();
+ DrawLabelAsWindow (&labels[3], NULL);
+ RebindInputState (templat, control, index);
+ populate_editkeys (templat);
+ FlushInput ();
+}
+
+static void
+clear_control (WIDGET_CONTROLENTRY *widget)
+{
+ int templat = choices[20].selected;
+ int control = widget->controlindex;
+ int index = widget->highlighted;
+
+ RemoveInputState (templat, control, index);
+ populate_editkeys (templat);
+}
+
+static int
+count_widgets (WIDGET **widgets)
+{
+ int count;
+
+ for (count = 0; *widgets != NULL; ++widgets, ++count)
+ ;
+ return count;
+}
+
+static stringbank *bank = NULL;
+static FRAME setup_frame = NULL;
+
+static void
+init_widgets (void)
+{
+ const char *buffer[100], *str, *title;
+ int count, i, index;
+
+ if (bank == NULL)
+ {
+ bank = StringBank_Create ();
+ }
+
+ if (setup_frame == NULL)
+ {
+ setup_frame = CaptureDrawable (LoadGraphic (MENUBKG_PMAP_ANIM));
+ }
+
+ count = GetStringTableCount (SetupTab);
+
+ if (count < 3)
+ {
+ log_add (log_Fatal, "PANIC: Setup string table too short to even hold all indices!");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Menus */
+ title = StringBank_AddOrFindString (bank, GetStringAddress (SetAbsStringTableIndex (SetupTab, 0)));
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, 1)), '\n', 100, buffer, bank) != MENU_COUNT)
+ {
+ /* TODO: Ignore extras instead of dying. */
+ log_add (log_Fatal, "PANIC: Incorrect number of Menu Subtitles");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < MENU_COUNT; i++)
+ {
+ menus[i].tag = WIDGET_TYPE_MENU_SCREEN;
+ menus[i].parent = NULL;
+ menus[i].handleEvent = Widget_HandleEventMenuScreen;
+ menus[i].receiveFocus = Widget_ReceiveFocusMenuScreen;
+ menus[i].draw = Widget_DrawMenuScreen;
+ menus[i].height = Widget_HeightFullScreen;
+ menus[i].width = Widget_WidthFullScreen;
+ menus[i].title = title;
+ menus[i].subtitle = buffer[i];
+ menus[i].bgStamp.origin.x = 0;
+ menus[i].bgStamp.origin.y = 0;
+ menus[i].bgStamp.frame = SetAbsFrameIndex (setup_frame, menu_defs[i].bgIndex);
+ menus[i].num_children = count_widgets (menu_defs[i].widgets);
+ menus[i].child = menu_defs[i].widgets;
+ menus[i].highlighted = 0;
+ }
+ if (menu_defs[i].widgets != NULL)
+ {
+ log_add (log_Error, "Menu definition array has more items!");
+ }
+
+ /* Options */
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, 2)), '\n', 100, buffer, bank) != CHOICE_COUNT)
+ {
+ log_add (log_Fatal, "PANIC: Incorrect number of Choice Options");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < CHOICE_COUNT; i++)
+ {
+ choices[i].tag = WIDGET_TYPE_CHOICE;
+ choices[i].parent = NULL;
+ choices[i].handleEvent = Widget_HandleEventChoice;
+ choices[i].receiveFocus = Widget_ReceiveFocusChoice;
+ choices[i].draw = Widget_DrawChoice;
+ choices[i].height = Widget_HeightChoice;
+ choices[i].width = Widget_WidthFullScreen;
+ choices[i].category = buffer[i];
+ choices[i].numopts = 0;
+ choices[i].options = NULL;
+ choices[i].selected = 0;
+ choices[i].highlighted = 0;
+ choices[i].maxcolumns = choice_widths[i];
+ choices[i].onChange = NULL;
+ }
+
+ /* Fill in the options now */
+ index = 3; /* Index into string table */
+ for (i = 0; i < CHOICE_COUNT; i++)
+ {
+ int j, optcount;
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading choices");
+ exit (EXIT_FAILURE);
+ }
+ str = GetStringAddress (SetAbsStringTableIndex (SetupTab, index++));
+ optcount = SplitString (str, '\n', 100, buffer, bank);
+ choices[i].numopts = optcount;
+ choices[i].options = HMalloc (optcount * sizeof (CHOICE_OPTION));
+ for (j = 0; j < optcount; j++)
+ {
+ choices[i].options[j].optname = buffer[j];
+ choices[i].options[j].tooltip[0] = "";
+ choices[i].options[j].tooltip[1] = "";
+ choices[i].options[j].tooltip[2] = "";
+ }
+ for (j = 0; j < optcount; j++)
+ {
+ int k, tipcount;
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading choices");
+ exit (EXIT_FAILURE);
+ }
+ str = GetStringAddress (SetAbsStringTableIndex (SetupTab, index++));
+ tipcount = SplitString (str, '\n', 100, buffer, bank);
+ if (tipcount > 3)
+ {
+ tipcount = 3;
+ }
+ for (k = 0; k < tipcount; k++)
+ {
+ choices[i].options[j].tooltip[k] = buffer[k];
+ }
+ }
+ }
+
+ /* The first choice is resolution, and is handled specially */
+ choices[0].numopts = number_res_options ();
+
+ /* Choices 18-20 are also special, being the names of the key configurations */
+ for (i = 0; i < 6; i++)
+ {
+ choices[18].options[i].optname = input_templates[i].name;
+ choices[19].options[i].optname = input_templates[i].name;
+ choices[20].options[i].optname = input_templates[i].name;
+ }
+
+ /* Choice 20 has a special onChange handler, too. */
+ choices[20].onChange = change_template;
+
+ /* Sliders */
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading sliders");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != SLIDER_COUNT)
+ {
+ /* TODO: Ignore extras instead of dying. */
+ log_add (log_Fatal, "PANIC: Incorrect number of Slider Options");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < SLIDER_COUNT; i++)
+ {
+ sliders[i].tag = WIDGET_TYPE_SLIDER;
+ sliders[i].parent = NULL;
+ sliders[i].handleEvent = Widget_HandleEventSlider;
+ sliders[i].receiveFocus = Widget_ReceiveFocusSimple;
+ sliders[i].draw = Widget_DrawSlider;
+ sliders[i].height = Widget_HeightOneLine;
+ sliders[i].width = Widget_WidthFullScreen;
+ sliders[i].draw_value = Widget_Slider_DrawValue;
+ sliders[i].min = 0;
+ sliders[i].max = 100;
+ sliders[i].step = 5;
+ sliders[i].value = 75;
+ sliders[i].category = buffer[i];
+ sliders[i].tooltip[0] = "";
+ sliders[i].tooltip[1] = "";
+ sliders[i].tooltip[2] = "";
+ }
+ // gamma is a special case
+ sliders[3].step = 1;
+ sliders[3].handleEvent = gamma_HandleEventSlider;
+ sliders[3].draw_value = gamma_DrawValue;
+
+ for (i = 0; i < SLIDER_COUNT; i++)
+ {
+ int j, tipcount;
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading sliders");
+ exit (EXIT_FAILURE);
+ }
+ str = GetStringAddress (SetAbsStringTableIndex (SetupTab, index++));
+ tipcount = SplitString (str, '\n', 100, buffer, bank);
+ if (tipcount > 3)
+ {
+ tipcount = 3;
+ }
+ for (j = 0; j < tipcount; j++)
+ {
+ sliders[i].tooltip[j] = buffer[j];
+ }
+ }
+
+ /* Buttons */
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading buttons");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != BUTTON_COUNT)
+ {
+ /* TODO: Ignore extras instead of dying. */
+ log_add (log_Fatal, "PANIC: Incorrect number of Button Options");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < BUTTON_COUNT; i++)
+ {
+ buttons[i].tag = WIDGET_TYPE_BUTTON;
+ buttons[i].parent = NULL;
+ buttons[i].handleEvent = button_handlers[i];
+ buttons[i].receiveFocus = Widget_ReceiveFocusSimple;
+ buttons[i].draw = Widget_DrawButton;
+ buttons[i].height = Widget_HeightOneLine;
+ buttons[i].width = Widget_WidthFullScreen;
+ buttons[i].name = buffer[i];
+ buttons[i].tooltip[0] = "";
+ buttons[i].tooltip[1] = "";
+ buttons[i].tooltip[2] = "";
+ }
+
+ for (i = 0; i < BUTTON_COUNT; i++)
+ {
+ int j, tipcount;
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading buttons");
+ exit (EXIT_FAILURE);
+ }
+ str = GetStringAddress (SetAbsStringTableIndex (SetupTab, index++));
+ tipcount = SplitString (str, '\n', 100, buffer, bank);
+ if (tipcount > 3)
+ {
+ tipcount = 3;
+ }
+ for (j = 0; j < tipcount; j++)
+ {
+ buttons[i].tooltip[j] = buffer[j];
+ }
+ }
+
+ /* Labels */
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading labels");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != LABEL_COUNT)
+ {
+ /* TODO: Ignore extras instead of dying. */
+ log_add (log_Fatal, "PANIC: Incorrect number of Label Options");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < LABEL_COUNT; i++)
+ {
+ labels[i].tag = WIDGET_TYPE_LABEL;
+ labels[i].parent = NULL;
+ labels[i].handleEvent = Widget_HandleEventIgnoreAll;
+ labels[i].receiveFocus = Widget_ReceiveFocusRefuseFocus;
+ labels[i].draw = Widget_DrawLabel;
+ labels[i].height = Widget_HeightLabel;
+ labels[i].width = Widget_WidthFullScreen;
+ labels[i].line_count = 0;
+ labels[i].lines = NULL;
+ }
+
+ for (i = 0; i < LABEL_COUNT; i++)
+ {
+ int j, linecount;
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading labels");
+ exit (EXIT_FAILURE);
+ }
+ str = GetStringAddress (SetAbsStringTableIndex (SetupTab, index++));
+ linecount = SplitString (str, '\n', 100, buffer, bank);
+ labels[i].line_count = linecount;
+ labels[i].lines = (const char **)HMalloc(linecount * sizeof(const char *));
+ for (j = 0; j < linecount; j++)
+ {
+ labels[i].lines[j] = buffer[j];
+ }
+ }
+
+ /* Text Entry boxes */
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading text entries");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != TEXTENTRY_COUNT)
+ {
+ log_add (log_Fatal, "PANIC: Incorrect number of Text Entries");
+ exit (EXIT_FAILURE);
+ }
+ for (i = 0; i < TEXTENTRY_COUNT; i++)
+ {
+ textentries[i].tag = WIDGET_TYPE_TEXTENTRY;
+ textentries[i].parent = NULL;
+ textentries[i].handleEvent = Widget_HandleEventTextEntry;
+ textentries[i].receiveFocus = Widget_ReceiveFocusSimple;
+ textentries[i].draw = Widget_DrawTextEntry;
+ textentries[i].height = Widget_HeightOneLine;
+ textentries[i].width = Widget_WidthFullScreen;
+ textentries[i].handleEventSelect = OnTextEntryEvent;
+ textentries[i].category = buffer[i];
+ textentries[i].value[0] = 0;
+ textentries[i].maxlen = WIDGET_TEXTENTRY_WIDTH-1;
+ textentries[i].state = WTE_NORMAL;
+ textentries[i].cursor_pos = 0;
+ }
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading text entries");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != TEXTENTRY_COUNT)
+ {
+ /* TODO: Ignore extras instead of dying. */
+ log_add (log_Fatal, "PANIC: Incorrect number of Text Entries");
+ exit (EXIT_FAILURE);
+ }
+ for (i = 0; i < TEXTENTRY_COUNT; i++)
+ {
+ strncpy (textentries[i].value, buffer[i], textentries[i].maxlen);
+ textentries[i].value[textentries[i].maxlen] = 0;
+ }
+ textentries[0].onChange = rename_template;
+
+ /* Control Entry boxes */
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading control entries");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != CONTROLENTRY_COUNT)
+ {
+ log_add (log_Fatal, "PANIC: Incorrect number of Control Entries");
+ exit (EXIT_FAILURE);
+ }
+ for (i = 0; i < CONTROLENTRY_COUNT; i++)
+ {
+ controlentries[i].tag = WIDGET_TYPE_CONTROLENTRY;
+ controlentries[i].parent = NULL;
+ controlentries[i].handleEvent = Widget_HandleEventControlEntry;
+ controlentries[i].receiveFocus = Widget_ReceiveFocusControlEntry;
+ controlentries[i].draw = Widget_DrawControlEntry;
+ controlentries[i].height = Widget_HeightOneLine;
+ controlentries[i].width = Widget_WidthFullScreen;
+ controlentries[i].category = buffer[i];
+ controlentries[i].highlighted = 0;
+ controlentries[i].controlname[0][0] = 0;
+ controlentries[i].controlname[1][0] = 0;
+ controlentries[i].controlindex = i;
+ controlentries[i].onChange = rebind_control;
+ controlentries[i].onDelete = clear_control;
+ }
+
+ /* Check for garbage at the end */
+ if (index < count)
+ {
+ log_add (log_Warning, "WARNING: Setup strings had %d garbage entries at the end.",
+ count - index);
+ }
+}
+
+static void
+clean_up_widgets (void)
+{
+ int i;
+
+ for (i = 0; i < CHOICE_COUNT; i++)
+ {
+ if (choices[i].options)
+ {
+ HFree (choices[i].options);
+ }
+ }
+
+ for (i = 0; i < LABEL_COUNT; i++)
+ {
+ if (labels[i].lines)
+ {
+ HFree ((void *)labels[i].lines);
+ }
+ }
+
+ /* Clear out the master tables */
+
+ if (SetupTab)
+ {
+ DestroyStringTable (ReleaseStringTable (SetupTab));
+ SetupTab = 0;
+ }
+ if (bank)
+ {
+ StringBank_Free (bank);
+ bank = NULL;
+ }
+ if (setup_frame)
+ {
+ DestroyDrawable (ReleaseDrawable (setup_frame));
+ setup_frame = NULL;
+ }
+}
+
+void
+SetupMenu (void)
+{
+ SETUP_MENU_STATE s;
+
+ s.InputFunc = DoSetupMenu;
+ s.initialized = FALSE;
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ SetupTab = CaptureStringTable (LoadStringTable (SETUP_MENU_STRTAB));
+ if (SetupTab)
+ {
+ init_widgets ();
+ }
+ else
+ {
+ log_add (log_Fatal, "PANIC: Could not find strings for the setup menu!");
+ exit (EXIT_FAILURE);
+ }
+ done = FALSE;
+
+ DoInput (&s, TRUE);
+ GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
+ PropagateResults ();
+ if (SetupTab)
+ {
+ clean_up_widgets ();
+ }
+}
+
+void
+GetGlobalOptions (GLOBALOPTS *opts)
+{
+ bool whichBound;
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_BILINEAR)
+ {
+ opts->scaler = OPTVAL_BILINEAR_SCALE;
+ }
+ else if (GfxFlags & TFB_GFXFLAGS_SCALE_BIADAPT)
+ {
+ opts->scaler = OPTVAL_BIADAPT_SCALE;
+ }
+ else if (GfxFlags & TFB_GFXFLAGS_SCALE_BIADAPTADV)
+ {
+ opts->scaler = OPTVAL_BIADV_SCALE;
+ }
+ else if (GfxFlags & TFB_GFXFLAGS_SCALE_TRISCAN)
+ {
+ opts->scaler = OPTVAL_TRISCAN_SCALE;
+ }
+ else if (GfxFlags & TFB_GFXFLAGS_SCALE_HQXX)
+ {
+ opts->scaler = OPTVAL_HQXX_SCALE;
+ }
+ else
+ {
+ opts->scaler = OPTVAL_NO_SCALE;
+ }
+ opts->fullscreen = (GfxFlags & TFB_GFXFLAGS_FULLSCREEN) ?
+ OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->subtitles = optSubtitles ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->scanlines = (GfxFlags & TFB_GFXFLAGS_SCANLINES) ?
+ OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->menu = (optWhichMenu == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->text = (optWhichFonts == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->cscan = (optWhichCoarseScan == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->scroll = (optSmoothScroll == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->intro = (optWhichIntro == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->shield = (optWhichShield == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->fps = (GfxFlags & TFB_GFXFLAGS_SHOWFPS) ?
+ OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->meleezoom = (optMeleeScale == TFB_SCALE_STEP) ?
+ OPTVAL_PC : OPTVAL_3DO;
+ opts->stereo = optStereoSFX ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ /* These values are read in, but won't change during a run. */
+ opts->music3do = opt3doMusic ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->musicremix = optRemixMusic ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->speech = optSpeech ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->keepaspect = optKeepAspectRatio ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ switch (snddriver) {
+ case audio_DRIVER_OPENAL:
+ opts->adriver = OPTVAL_OPENAL;
+ break;
+ case audio_DRIVER_MIXSDL:
+ opts->adriver = OPTVAL_MIXSDL;
+ break;
+ default:
+ opts->adriver = OPTVAL_SILENCE;
+ break;
+ }
+ if (soundflags & audio_QUALITY_HIGH)
+ {
+ opts->aquality = OPTVAL_HIGH;
+ }
+ else if (soundflags & audio_QUALITY_LOW)
+ {
+ opts->aquality = OPTVAL_LOW;
+ }
+ else
+ {
+ opts->aquality = OPTVAL_MEDIUM;
+ }
+
+ /* Work out resolution. On the way, try to guess a good default
+ * for config.alwaysgl, then overwrite it if it was set previously. */
+ opts->driver = OPTVAL_PURE_IF_POSSIBLE;
+ switch (ScreenWidthActual)
+ {
+ case 320:
+ if (GraphicsDriver == TFB_GFXDRIVER_SDL_PURE)
+ {
+ opts->res = OPTVAL_320_240;
+ }
+ else
+ {
+ if (ScreenHeightActual != 240)
+ {
+ opts->res = OPTVAL_CUSTOM;
+ }
+ else
+ {
+ opts->res = OPTVAL_320_240;
+ opts->driver = OPTVAL_ALWAYS_GL;
+ }
+ }
+ break;
+ case 640:
+ if (GraphicsDriver == TFB_GFXDRIVER_SDL_PURE)
+ {
+ opts->res = OPTVAL_640_480;
+ }
+ else
+ {
+ if (ScreenHeightActual != 480)
+ {
+ opts->res = OPTVAL_CUSTOM;
+ }
+ else
+ {
+ opts->res = OPTVAL_640_480;
+ opts->driver = OPTVAL_ALWAYS_GL;
+ }
+ }
+ break;
+ case 800:
+ if (ScreenHeightActual != 600)
+ {
+ opts->res = OPTVAL_CUSTOM;
+ }
+ else
+ {
+ opts->res = OPTVAL_800_600;
+ }
+ break;
+ case 1024:
+ if (ScreenHeightActual != 768)
+ {
+ opts->res = OPTVAL_CUSTOM;
+ }
+ else
+ {
+ opts->res = OPTVAL_1024_768;
+ }
+ break;
+ case 1280:
+ if (ScreenHeightActual != 960)
+ {
+ opts->res = OPTVAL_CUSTOM;
+ }
+ else
+ {
+ opts->res = OPTVAL_1280_960;
+ }
+ break;
+ default:
+ opts->res = OPTVAL_CUSTOM;
+ break;
+ }
+
+ if (res_IsBoolean ("config.alwaysgl"))
+ {
+ if (res_GetBoolean ("config.alwaysgl"))
+ {
+ opts->driver = OPTVAL_ALWAYS_GL;
+ }
+ else
+ {
+ opts->driver = OPTVAL_PURE_IF_POSSIBLE;
+ }
+ }
+
+ whichBound = (optGamma < maxGamma);
+ // The option supplied by the user may be beyond our starting range
+ // but valid nonetheless. We need to account for that.
+ if (optGamma <= minGamma)
+ minGamma = optGamma - 0.03f;
+ else if (optGamma >= maxGamma)
+ maxGamma = optGamma + 0.3f;
+ updateGammaBounds (whichBound);
+ opts->gamma = gammaToSlider (optGamma);
+
+ opts->player1 = PlayerControls[0];
+ opts->player2 = PlayerControls[1];
+
+ opts->musicvol = (((int)(musicVolumeScale * 100.0f) + 2) / 5) * 5;
+ opts->sfxvol = (((int)(sfxVolumeScale * 100.0f) + 2) / 5) * 5;
+ opts->speechvol = (((int)(speechVolumeScale * 100.0f) + 2) / 5) * 5;
+}
+
+void
+SetGlobalOptions (GLOBALOPTS *opts)
+{
+ int NewGfxFlags = GfxFlags;
+ int NewWidth = ScreenWidthActual;
+ int NewHeight = ScreenHeightActual;
+ int NewDriver = GraphicsDriver;
+
+ NewGfxFlags &= ~TFB_GFXFLAGS_SCALE_ANY;
+
+ switch (opts->res) {
+ case OPTVAL_320_240:
+ NewWidth = 320;
+ NewHeight = 240;
+#ifdef HAVE_OPENGL
+ NewDriver = (opts->driver == OPTVAL_ALWAYS_GL ? TFB_GFXDRIVER_SDL_OPENGL : TFB_GFXDRIVER_SDL_PURE);
+#else
+ NewDriver = TFB_GFXDRIVER_SDL_PURE;
+#endif
+ break;
+ case OPTVAL_640_480:
+ NewWidth = 640;
+ NewHeight = 480;
+#ifdef HAVE_OPENGL
+ NewDriver = (opts->driver == OPTVAL_ALWAYS_GL ? TFB_GFXDRIVER_SDL_OPENGL : TFB_GFXDRIVER_SDL_PURE);
+#else
+ NewDriver = TFB_GFXDRIVER_SDL_PURE;
+#endif
+ break;
+ case OPTVAL_800_600:
+ NewWidth = 800;
+ NewHeight = 600;
+ NewDriver = TFB_GFXDRIVER_SDL_OPENGL;
+ break;
+ case OPTVAL_1024_768:
+ NewWidth = 1024;
+ NewHeight = 768;
+ NewDriver = TFB_GFXDRIVER_SDL_OPENGL;
+ break;
+ case OPTVAL_1280_960:
+ NewWidth = 1280;
+ NewHeight = 960;
+ NewDriver = TFB_GFXDRIVER_SDL_OPENGL;
+ break;
+ default:
+ /* Don't mess with the custom value */
+ break;
+ }
+
+ res_PutInteger ("config.reswidth", NewWidth);
+ res_PutInteger ("config.resheight", NewHeight);
+ res_PutBoolean ("config.alwaysgl", opts->driver == OPTVAL_ALWAYS_GL);
+ res_PutBoolean ("config.usegl", NewDriver == TFB_GFXDRIVER_SDL_OPENGL);
+
+ switch (opts->scaler) {
+ case OPTVAL_BILINEAR_SCALE:
+ NewGfxFlags |= TFB_GFXFLAGS_SCALE_BILINEAR;
+ res_PutString ("config.scaler", "bilinear");
+ break;
+ case OPTVAL_BIADAPT_SCALE:
+ NewGfxFlags |= TFB_GFXFLAGS_SCALE_BIADAPT;
+ res_PutString ("config.scaler", "biadapt");
+ break;
+ case OPTVAL_BIADV_SCALE:
+ NewGfxFlags |= TFB_GFXFLAGS_SCALE_BIADAPTADV;
+ res_PutString ("config.scaler", "biadv");
+ break;
+ case OPTVAL_TRISCAN_SCALE:
+ NewGfxFlags |= TFB_GFXFLAGS_SCALE_TRISCAN;
+ res_PutString ("config.scaler", "triscan");
+ break;
+ case OPTVAL_HQXX_SCALE:
+ NewGfxFlags |= TFB_GFXFLAGS_SCALE_HQXX;
+ res_PutString ("config.scaler", "hq");
+ break;
+ default:
+ /* OPTVAL_NO_SCALE has no equivalent in gfxflags. */
+ res_PutString ("config.scaler", "no");
+ break;
+ }
+ if (opts->scanlines) {
+ NewGfxFlags |= TFB_GFXFLAGS_SCANLINES;
+ } else {
+ NewGfxFlags &= ~TFB_GFXFLAGS_SCANLINES;
+ }
+ if (opts->fullscreen)
+ NewGfxFlags |= TFB_GFXFLAGS_FULLSCREEN;
+ else
+ NewGfxFlags &= ~TFB_GFXFLAGS_FULLSCREEN;
+
+ res_PutBoolean ("config.scanlines", (BOOLEAN)opts->scanlines);
+ res_PutBoolean ("config.fullscreen", (BOOLEAN)opts->fullscreen);
+
+
+ if ((NewWidth != ScreenWidthActual) ||
+ (NewHeight != ScreenHeightActual) ||
+ (NewDriver != GraphicsDriver) ||
+ (NewGfxFlags != GfxFlags))
+ {
+ FlushGraphics ();
+ UninitVideoPlayer ();
+ TFB_DrawScreen_ReinitVideo (NewDriver, NewGfxFlags, NewWidth, NewHeight);
+ FlushGraphics ();
+ InitVideoPlayer (TRUE);
+ }
+
+ // Avoid setting gamma when it is not necessary
+ if (optGamma != 1.0f || sliderToGamma (opts->gamma) != 1.0f)
+ {
+ optGamma = sliderToGamma (opts->gamma);
+ setGammaCorrection (optGamma);
+ }
+
+ optSubtitles = (opts->subtitles == OPTVAL_ENABLED) ? TRUE : FALSE;
+ optWhichMenu = (opts->menu == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optWhichFonts = (opts->text == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optWhichCoarseScan = (opts->cscan == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optSmoothScroll = (opts->scroll == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optWhichShield = (opts->shield == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optMeleeScale = (opts->meleezoom == OPTVAL_3DO) ? TFB_SCALE_TRILINEAR : TFB_SCALE_STEP;
+ opt3doMusic = (opts->music3do == OPTVAL_ENABLED);
+ optRemixMusic = (opts->musicremix == OPTVAL_ENABLED);
+ optSpeech = (opts->speech == OPTVAL_ENABLED);
+ optWhichIntro = (opts->intro == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optStereoSFX = (opts->stereo == OPTVAL_ENABLED);
+ optKeepAspectRatio = (opts->keepaspect == OPTVAL_ENABLED);
+ PlayerControls[0] = opts->player1;
+ PlayerControls[1] = opts->player2;
+
+ res_PutBoolean ("config.subtitles", opts->subtitles == OPTVAL_ENABLED);
+ res_PutBoolean ("config.textmenu", opts->menu == OPTVAL_PC);
+ res_PutBoolean ("config.textgradients", opts->text == OPTVAL_PC);
+ res_PutBoolean ("config.iconicscan", opts->cscan == OPTVAL_3DO);
+ res_PutBoolean ("config.smoothscroll", opts->scroll == OPTVAL_3DO);
+
+ res_PutBoolean ("config.3domusic", opts->music3do == OPTVAL_ENABLED);
+ res_PutBoolean ("config.remixmusic", opts->musicremix == OPTVAL_ENABLED);
+ res_PutBoolean ("config.speech", opts->speech == OPTVAL_ENABLED);
+ res_PutBoolean ("config.3domovies", opts->intro == OPTVAL_3DO);
+ res_PutBoolean ("config.showfps", opts->fps == OPTVAL_ENABLED);
+ res_PutBoolean ("config.smoothmelee", opts->meleezoom == OPTVAL_3DO);
+ res_PutBoolean ("config.positionalsfx", opts->stereo == OPTVAL_ENABLED);
+ res_PutBoolean ("config.pulseshield", opts->shield == OPTVAL_3DO);
+ res_PutBoolean ("config.keepaspectratio", opts->keepaspect == OPTVAL_ENABLED);
+ res_PutInteger ("config.gamma", (int) (optGamma * GAMMA_SCALE + 0.5));
+ res_PutInteger ("config.player1control", opts->player1);
+ res_PutInteger ("config.player2control", opts->player2);
+
+ switch (opts->adriver) {
+ case OPTVAL_SILENCE:
+ res_PutString ("config.audiodriver", "none");
+ break;
+ case OPTVAL_MIXSDL:
+ res_PutString ("config.audiodriver", "mixsdl");
+ break;
+ case OPTVAL_OPENAL:
+ res_PutString ("config.audiodriver", "openal");
+ default:
+ /* Shouldn't happen; leave config untouched */
+ break;
+ }
+
+ switch (opts->aquality) {
+ case OPTVAL_LOW:
+ res_PutString ("config.audioquality", "low");
+ break;
+ case OPTVAL_MEDIUM:
+ res_PutString ("config.audioquality", "medium");
+ break;
+ case OPTVAL_HIGH:
+ res_PutString ("config.audioquality", "high");
+ break;
+ default:
+ /* Shouldn't happen; leave config untouched */
+ break;
+ }
+
+ res_PutInteger ("config.musicvol", opts->musicvol);
+ res_PutInteger ("config.sfxvol", opts->sfxvol);
+ res_PutInteger ("config.speechvol", opts->speechvol);
+ musicVolumeScale = opts->musicvol / 100.0f;
+ sfxVolumeScale = opts->sfxvol / 100.0f;
+ speechVolumeScale = opts->speechvol / 100.0f;
+ // update actual volumes
+ SetMusicVolume (musicVolume);
+ SetSpeechVolume (speechVolumeScale);
+
+ res_PutString ("keys.1.name", input_templates[0].name);
+ res_PutString ("keys.2.name", input_templates[1].name);
+ res_PutString ("keys.3.name", input_templates[2].name);
+ res_PutString ("keys.4.name", input_templates[3].name);
+ res_PutString ("keys.5.name", input_templates[4].name);
+ res_PutString ("keys.6.name", input_templates[5].name);
+
+ SaveResourceIndex (configDir, "uqm.cfg", "config.", TRUE);
+ SaveKeyConfiguration (configDir, "flight.cfg");
+}