/*         ______   ___    ___
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      Sound setup utility for the Allegro library.
 *
 *      By Shawn Hargreaves.
 *
 *      See readme.txt for copyright information.
 */


#include <stdio.h>
#include <string.h>

#include "allegro.h"
#include "allegro/aintern.h"

#ifdef ALLEGRO_INTERNAL_HEADER
   #include ALLEGRO_INTERNAL_HEADER
#endif

#include "setup.h"



/* these can be customised to suit your program... */
#ifndef SETUP_TITLE
   #define SETUP_TITLE           "Allegro Setup " ALLEGRO_VERSION_STR ", " ALLEGRO_PLATFORM_STR
#endif

#ifndef SETUP_CFG_FILE
   #define SETUP_CFG_FILE        "allegro.cfg"
#endif

#ifndef SETUP_KEYBOARD_FILE
   #define SETUP_KEYBOARD_FILE   "keyboard.dat"
#endif

#ifndef SETUP_LANGUAGE_FILE
   #define SETUP_LANGUAGE_FILE   "language.dat"
#endif

#ifndef SETUP_DATA_FILE
   #define SETUP_DATA_FILE       "setup.dat"
#endif

#ifndef SETUP_SCREEN_W
   #define SETUP_SCREEN_W        320
   #define SETUP_SCREEN_H        200
#endif

#if (defined ALLEGRO_DJGPP) && (!defined SETUP_EMBEDDED)
   #define SETUP_USE_DAT2S
#endif



/* to save executable size, since we only need VGA 13h, 256 color mode */
#if (defined ALLEGRO_DOS) && (!defined SETUP_EMBEDDED)

   #if (SETUP_SCREEN_W == 320) && (SETUP_SCREEN_H == 200)
      BEGIN_GFX_DRIVER_LIST
	 GFX_DRIVER_VGA
      END_GFX_DRIVER_LIST
   #endif

   #ifdef ALLEGRO_COLOR8
      BEGIN_COLOR_DEPTH_LIST
	 COLOR_DEPTH_8
      END_COLOR_DEPTH_LIST
   #endif

#endif



/* info about a hardware driver */
typedef struct SOUNDCARD
{
   int id;
   AL_CONST char *name;
   char **param;
   AL_CONST char *desc;
   int present;
} SOUNDCARD;


static SOUNDCARD *soundcard;

static SOUNDCARD digi_cards[64];

static SOUNDCARD midi_cards[64];



/* different types of parameter */
typedef enum PARAM_TYPE
{
   param_none,
   param_int,
   param_hex,
   param_id,
   param_bool,
   param_file,
   param_list,
   param_num,
   param_str
} PARAM_TYPE;



/* info about a soundcard parameter */
typedef struct PARAMETER
{
   char *name;
   PARAM_TYPE type;
   char value[80];
   char *def;
   int *detect;
   char *label;
   char *e1, *e2;
   char *desc;
} PARAMETER;



/* list of soundcard parameters */
static PARAMETER parameters[] =
{
   /* name                 type           value    default     detect            label       extra1      extra2   desc */
   { "digi_card",          param_id,      "",      "-1",       NULL,             "",         NULL,       NULL,    "" },
   { "midi_card",          param_id,      "",      "-1",       NULL,             "",         NULL,       NULL,    "" },
   { "quality",            param_int,     "",      "0",        NULL,             "Qual:",    NULL,       NULL,    "Sample mixing quality (0 = fast mixing of 8 bit data into 16 bit buffers, 1 = true 16 bit mixing, 2 = interpolated 16 bit mixing)" },
   { "digi_volume",        param_int,     "",      "-1",       NULL,             "Vol:",     NULL,       NULL,    "Digital sound volume (0 to 255)" },
   { "midi_volume",        param_int,     "",      "-1",       NULL,             "Vol:",     NULL,       NULL,    "MIDI music volume (0 to 255)" },
   { "digi_voices",        param_int,     "",      "-1",       NULL,             "Chan:",    NULL,       NULL,    "Number of channels reserved for playing digital sounds (higher values increase polyphony but degrade speed and quality)" },
   { "midi_voices",        param_int,     "",      "-1",       NULL,             "Chan:",    NULL,       NULL,    "Number of channels reserved for playing MIDI music (higher values increase polyphony but degrade speed and quality)" },
   { "flip_pan",           param_bool,    "",      "0",        NULL,             "Pan:",     NULL,       NULL,    "Reverses the left/right stereo placement" },

 #ifndef ALLEGRO_WINDOWS
   { "sound_port",         param_hex,     "",      "-1",       &_sound_port,     "Port:",    NULL,       NULL,    "Port address (usually 220)" },
   { "sound_dma",          param_int,     "",      "-1",       &_sound_dma,      "DMA:",     NULL,       NULL,    "DMA channel (usually 1)" },
   { "sound_irq",          param_int,     "",      "-1",       &_sound_irq,      "IRQ:",     NULL,       NULL,    "IRQ number (usually 7)" },
   { "sound_freq",         param_list,    "",      "-1",       &_sound_freq,     "Freq:",    NULL,       NULL,    "Sample mixing frequency (higher values sound better but require more CPU processing time)" },
   { "sound_bits",         param_int,     "",      "-1",       &_sound_bits,     "Bits:",    NULL,       NULL,    "Bits per sample (8 or 16: higher values sound better but require more memory)" },
   { "sound_stereo",       param_bool,    "",      "0",        &_sound_stereo,   "Stereo:",  NULL,       NULL,    "Use device in stereo mode" },
 #endif

 #ifdef ALLEGRO_DOS
   { "fm_port",            param_hex,     "",      "-1",       &_fm_port,        "Port:",    NULL,       NULL,    "Port address (usually 388)" },
   { "mpu_port",           param_hex,     "",      "-1",       &_mpu_port,       "Port:",    NULL,       NULL,    "Port address (usually 330)" },
   { "mpu_irq",            param_int,     "",      "-1",       &_mpu_irq,        "IRQ:",     NULL,       NULL,    "IRQ number (usually 7)" },
   { "ibk_file",           param_file,    "",      "",         NULL,             "IBK:",     "IBK",      NULL,    "Custom .IBK instrument patch set" },
   { "ibk_drum_file",      param_file,    "",      "",         NULL,             "drumIBK:", "IBK",      NULL,    "Custom .IBK percussion patch set" },
 #endif

 #ifdef DIGI_OSS
   { "oss_driver",         param_str,     "",      "/dev/dsp", NULL,             "Drv:",     "",         NULL,    "Name of the driver (e.g. /dev/dsp)" },
   { "oss_numfrags",       param_int,     "",      "-1",       &_oss_numfrags,   "NumFr:",   NULL,       NULL,    "Number of fragments (use 2, other values make little difference)" },
   { "oss_fragsize",       param_num,     "",      "-1",       &_oss_fragsize,   "Siz:",     NULL,       NULL,    "Size of fragment (small values -- choppy sound, large values -- delayed sound)" },
 #endif

 #ifdef MIDI_OSS
   { "oss_midi_driver",    param_str,     "",      "/dev/sequencer",    NULL,    "Drv:",     "",         NULL,    "Name of the driver (e.g. /dev/sequencer)" },
 #endif

 #ifdef DIGI_ESD
   { "esd_server",         param_str,     "",      "",         NULL,             "Srv:",     "",         NULL,    "Name of the server" },
 #endif

 #ifdef DIGI_ALSA
   { "alsa_card",          param_int,     "",      "",         NULL,             "Card:",    NULL,       NULL,    "ALSA card number" },
   { "alsa_pcmdevice",     param_int,     "",      "",         NULL,             "Dev:",     NULL,       NULL,    "ALSA PCM device number" },
   { "alsa_numfrags",      param_int,     "",      "2",        NULL,             "NumFr:",   NULL,       NULL,    "Number of fragments (use 2, other values make little difference)" },
 #endif

 #ifdef MIDI_DIGMID
   { "patches",            param_file,    "",      "",         NULL,             "Patches:", "CFG;DAT",  NULL,    "MIDI patch set (GUS format default.cfg or Allegro format patches.dat)" },
 #endif

   { NULL,                 param_none,    "",      "",         NULL,             NULL,       NULL,       NULL,    NULL } 
};



/* This is the important bit, where we decide what drivers and parameters to
 * list. We ask the system driver for a list of the available drivers, and
 * then fill in extra information for any that we know about (eg. which
 * port numbers and suchlike things they want to use). If we don't know
 * about a driver, we can still select it, but obviously won't be able to
 * give the user any of the more specialised options for it.
 */
static void find_sound_drivers()
{
   static char *digi_param[] = { "11", "digi_volume", NULL };
   static char *midi_param[] = { "11", "midi_volume", NULL };
   _DRIVER_INFO *info;
   int c;

   /* have to do this at runtime to work with DLL linkage */
   parameters[0].detect = &digi_card;
   parameters[1].detect = &midi_card;
   parameters[2].detect = &_sound_hq;

   /* fill in list of digital drivers */
   digi_cards[0].id = DIGI_AUTODETECT;
   digi_cards[0].name = "Autodetect";
   digi_cards[0].param = digi_param;
   digi_cards[0].desc = "Attempt to autodetect the digital sound hardware";
   digi_cards[0].present = FALSE;

   if (system_driver->digi_drivers)
      info = system_driver->digi_drivers();
   else
      info = _digi_driver_list;

   c = 1;

   while ((info->driver) && (info->id != DIGI_NONE)) {
      digi_cards[c].id = info->id;
      digi_cards[c].name = digi_cards[c].desc = ((DIGI_DRIVER *)info->driver)->ascii_name;
      digi_cards[c].param = NULL;
      digi_cards[c].present = FALSE;

      switch (info->id) {

	 #ifdef DIGI_SB10

	    case DIGI_SB10:
	    {
	       static char *param[] = { "sound_port", "sound_dma", "sound_irq", "digi_volume", "digi_voices", "sound_freq", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "SB v1.0, 8 bit mono using single-shot dma";
	       break;
	    }

	 #endif

	 #ifdef DIGI_SB15

	    case DIGI_SB15:
	    {
	       static char *param[] = { "sound_port", "sound_dma", "sound_irq", "digi_volume", "digi_voices", "sound_freq", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "SB v1.5, 8 bit mono using single shot dma";
	       break;
	    }

	 #endif

	 #ifdef DIGI_SB20

	    case DIGI_SB20:
	    {
	       static char *param[] = { "sound_port", "sound_dma", "sound_irq", "digi_volume", "digi_voices", "sound_freq", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "SB v2.0, 8 bit mono using auto-initialised dma";
	       break;
	    }

	 #endif

	 #ifdef DIGI_SBPRO

	    case DIGI_SBPRO:
	    {
	       static char *param[] = { "sound_port", "sound_dma", "sound_irq", "flip_pan", "quality", "sound_freq", "digi_volume", "digi_voices", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "SB Pro, 8 bit stereo DAC";
	       break;
	    }

	 #endif

	 #ifdef DIGI_SB16

	    case DIGI_SB16:
	    {
	       static char *param[] = { "sound_port", "sound_dma", "sound_irq", "flip_pan", "quality", "sound_freq", "digi_volume", "digi_voices", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "SB16 or AWE32, 16 bit stereo DAC";
	       break;
	    }

	 #endif

	 #ifdef DIGI_AUDIODRIVE

	    case DIGI_AUDIODRIVE:
	    {
	       static char *param[] = { "sound_port", "sound_dma", "sound_irq", "flip_pan", "quality", "sound_freq", "digi_volume", "digi_voices", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "ESS AudioDrive (16 bit stereo DAC)";
	       break;
	    }

	 #endif

	 #ifdef DIGI_SOUNDSCAPE

	    case DIGI_SOUNDSCAPE:
	    {
	       static char *param[] = { "02", "flip_pan", "quality", "sound_freq", "digi_volume", "digi_voices", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "Ensoniq Soundscape (16 bit stereo DAC)";
	       break;
	    }

	 #endif

	 #ifdef DIGI_WINSOUNDSYS

	    case DIGI_WINSOUNDSYS:
	    {
	       static char *param[] = { "sound_port", "sound_dma", "sound_irq", "flip_pan", "quality", "sound_freq", "digi_volume", "digi_voices", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "Windows Sound System or compatible (AD1848, CS4248, CS4231/A or CS4232 CODEC)";
	       break;
	    }

	 #endif

	 #ifdef DIGI_OSS

	    case DIGI_OSS:
	    {
	       static char *param[] = { "flip_pan", "sound_bits", "sound_stereo", "oss_numfrags", "oss_fragsize", "sound_freq", "oss_driver", "digi_volume", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "Open Sound System";
	       break;
	    }

	 #endif

	 #ifdef DIGI_ESD

	    case DIGI_ESD:
	    {
	       static char *param[] = { "flip_pan", "sound_bits", "sound_stereo", "sound_freq", "esd_server", "digi_volume", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "Enlightened Sound Daemon";
	       break;
	    }

	 #endif

	 #ifdef DIGI_ALSA

	    case DIGI_ALSA:
	    {
	       static char *param[] = { "flip_pan", "sound_bits", "sound_stereo", "alsa_numfrags", "alsa_card", "alsa_pcmdevice", "sound_freq", "digi_volume", NULL };
	       digi_cards[c].param = param;
	       digi_cards[c].desc = "ALSA Sound System";
	       break;
	    }

	 #endif
      }

      info++;
      c++;
   }

   digi_cards[c].id = DIGI_NONE;
   digi_cards[c].name = "No Sound";
   digi_cards[c].param = NULL;
   digi_cards[c].desc = "The Sound of Silence...";
   digi_cards[c].present = FALSE;

   /* fill in list of MIDI drivers */
   midi_cards[0].id = MIDI_AUTODETECT;
   midi_cards[0].name = "Autodetect";
   midi_cards[0].param = midi_param;
   midi_cards[0].desc = "Attempt to autodetect the MIDI hardware";
   midi_cards[0].present = FALSE;

   if (system_driver->midi_drivers)
      info = system_driver->midi_drivers();
   else
      info = _midi_driver_list;

   c = 1;

   while ((info->driver) && (info->id != MIDI_NONE)) {
      midi_cards[c].id = info->id;
      midi_cards[c].name = midi_cards[c].desc = ((MIDI_DRIVER *)info->driver)->ascii_name;
      midi_cards[c].param = NULL;
      midi_cards[c].present = FALSE;

      switch (info->id) {

	 #ifdef MIDI_OPL2

	    case MIDI_OPL2:
	    {
	       static char *param[] = { "22", "fm_port", "midi_volume", "", "ibk_file", "ibk_drum_file", NULL };
	       midi_cards[c].param = param;
	       midi_cards[c].desc = "(mono) OPL2 FM synth (used in Adlib and standard SB cards)";
	       break;
	    }

	 #endif

	 #ifdef MIDI_2XOPL2

	    case MIDI_2XOPL2:
	    {
	       static char *param[] = { "22", "fm_port", "midi_volume", "", "ibk_file", "ibk_drum_file", NULL };
	       midi_cards[c].param = param;
	       midi_cards[c].desc = "(stereo) Two OPL2 FM synths (early SB Pro cards)";
	       break;
	    }

	 #endif

	 #ifdef MIDI_OPL3

	    case MIDI_OPL3:
	    {
	       static char *param[] = { "22", "fm_port", "midi_volume", "", "ibk_file", "ibk_drum_file", NULL };
	       midi_cards[c].param = param;
	       midi_cards[c].desc = "(stereo) OPL3 FM synth (Adlib Gold, later SB Pro boards, SB16)";
	       break;
	    }

	 #endif

	 #ifdef MIDI_AWE32

	    case MIDI_AWE32:
	    {
	       static char *param[] = { "21", "midi_voices", "midi_volume", NULL };
	       midi_cards[c].param = param;
	       midi_cards[c].desc = "SoundBlaster AWE32 (EMU8000 synth chip)";
	       break;
	    }

	 #endif

	 #ifdef MIDI_SB_OUT

	    case MIDI_SB_OUT:
	    {
	       static char *param[] = { "21", "sound_port", "midi_volume", NULL };
	       midi_cards[c].param = param;
	       midi_cards[c].desc = "Raw SB MIDI output to an external synth module";
	       break;
	    }

	 #endif

	 #ifdef MIDI_MPU

	    case MIDI_MPU:
	    {
	       static char *param[] = { "22", "mpu_port", "mpu_irq", "", "midi_volume", NULL };
	       midi_cards[c].param = param;
	       midi_cards[c].desc = "MIDI output to an external synth module or wavetable card";
	       break;
	    }

	 #endif

	 #ifdef MIDI_OSS

	    case MIDI_OSS:
	    {
	       static char *param[] = { "21", "oss_midi_driver", "midi_volume", NULL };
	       midi_cards[c].param = param;
	       midi_cards[c].desc = "Open Sound System";
	       break;
	    }

	 #endif

	 #ifdef MIDI_DIGMID

	    case MIDI_DIGMID:
	    {
	       static char *param[] = { "22", "midi_voices", "midi_volume", "", "patches", NULL };
	       midi_cards[c].param = param;
	       midi_cards[c].desc = "Software wavetable synthesis using the digital sound hardware";
	       break;
	    }

	 #endif 
      }

      info++;
      c++;
   }

   midi_cards[c].id = MIDI_NONE;
   midi_cards[c].name = "No Sound";
   midi_cards[c].param = NULL;
   midi_cards[c].desc = "The Sound of Silence...";
   midi_cards[c].present = FALSE;
}



/* in some places we need to double-buffer the display... */
static BITMAP *buffer;



/* background graphic, font, and test sounds */
#ifdef SETUP_USE_DAT2S
   extern DATAFILE setup_data[];
#else
   static DATAFILE *setup_data;
#endif



/* dialogs do fancy stuff as they slide on and off the screen */
typedef enum DIALOG_STATE
{
   state_start,
   state_slideon,
   state_active,
   state_slideoff,
   state_exit,
   state_chain,
   state_redraw
} DIALOG_STATE;



/* info about an active dialog */
typedef struct ACTIVE_DIALOG
{
   DIALOG_STATE state;
   int time;
   DIALOG *dialog;
   DIALOG_PLAYER *player;
   BITMAP *buffer;
   DIALOG_STATE (*handler)(int c);
} ACTIVE_DIALOG;



/* list of active dialogs */
static ACTIVE_DIALOG dialogs[4];

static int dialog_count = 0;



/* scrolly text message at the base of the screen */
static volatile int scroller_time = 0;
static char scroller_msg[256];
static int scroller_pos = 0;
static int scroller_alpha = 256;
static char *scroller_string = "";
static char *wanted_scroller = "";
static int scroller_string_pos = 0;



/* timer interrupt handler */
static void inc_scroller_time(void)
{
   scroller_time++;
}

END_OF_STATIC_FUNCTION(inc_scroller_time);



/* dialog procedure for animating the scroller text */
static int scroller_proc(int msg, DIALOG *d, int c)
{
   int redraw = FALSE;
   int a, i, x;

   if (msg == MSG_IDLE) {
      while (scroller_time > 0) {
	 scroller_pos--;
	 if (scroller_pos <= -8) {
	    scroller_pos = 0;
	    for (i=0; i<SCREEN_W/8+1; i++)
	       scroller_msg[i] = scroller_msg[i+1];
	    if (scroller_string[scroller_string_pos])
	       scroller_msg[i] = scroller_string[scroller_string_pos++];
	    else 
	       scroller_msg[i] = ' ';
	    if (wanted_scroller != scroller_string) {
	       scroller_alpha -= MIN(32, scroller_alpha);
	       if (scroller_alpha <= 0) {
		  memset(scroller_msg, ' ', SCREEN_W/8+2);
		  scroller_string = wanted_scroller;
		  scroller_string_pos = 0;
		  for (x=0; x<4; x++) {
		     if (scroller_string[scroller_string_pos]) {
			for (i=0; i<SCREEN_W/8+1; i++)
			   scroller_msg[i] = scroller_msg[i+1];
			scroller_msg[i] = scroller_string[scroller_string_pos];
			scroller_string_pos++;
		     }
		  }
		  scroller_alpha = 256;
	       }
	    }
	    else
	       scroller_alpha += MIN(16, 256-scroller_alpha);
	 }
	 redraw = TRUE;
	 scroller_time--;
      }
   }
   else if (msg == MSG_RADIO) {
      memset(scroller_msg, ' ', SCREEN_W/8+2);
      scroller_string = wanted_scroller;
      scroller_string_pos = strlen(scroller_string);
      scroller_alpha = 256;
      redraw = TRUE;
   }

   if (redraw) {
      freeze_mouse_flag = TRUE;
      acquire_screen();
      text_mode(0);

      for (i=0; i<SCREEN_W/8+2; i++) {
	 x = i*8+scroller_pos;
	 a = 16 + MID(0, 15-ABS(SCREEN_W/2-x)/(SCREEN_W/32), 15) * scroller_alpha/256;
	 textprintf(screen, font, x, SCREEN_H-16, a, "%c", scroller_msg[i]);
      }

      release_screen();
      freeze_mouse_flag = FALSE;
   }

   return D_O_K;
}



/* helper for drawing a dialog onto a memory bitmap */
static void draw_dialog(ACTIVE_DIALOG *d)
{
   BITMAP *oldscreen = screen;
   int nowhere;

   if (d->player->focus_obj >= 0) {
      SEND_MESSAGE(d->dialog+d->player->focus_obj, MSG_LOSTFOCUS, 0);
      d->dialog[d->player->focus_obj].flags &= ~D_GOTFOCUS;
      d->player->focus_obj = -1;
   }

   if (d->player->mouse_obj >= 0) {
      SEND_MESSAGE(d->dialog+d->player->mouse_obj, MSG_LOSTMOUSE, 0);
      d->dialog[d->player->mouse_obj].flags &= ~D_GOTMOUSE;
      d->player->mouse_obj = -1;
   }

   d->player->res &= ~D_WANTFOCUS;

   clear(d->buffer);
   screen = d->buffer; 
   dialog_message(d->dialog, MSG_DRAW, 0, &nowhere);
   screen = oldscreen;
}



/* start up another dialog */
static void activate_dialog(DIALOG *dlg, DIALOG_STATE (*handler)(int c), int chain)
{
   ACTIVE_DIALOG *d = &dialogs[dialog_count];
   int i;

   for (i=0; dlg[i].proc; i++) {
      if (dlg[i].proc == scroller_proc) {
	 dlg[i].proc = NULL;
	 centre_dialog(dlg);
	 dlg[i].proc = scroller_proc;
	 break;
      }
   }

   d->state = state_start;
   d->time = retrace_count;
   d->dialog = dlg;
   d->player = init_dialog(dlg, -1);
   d->buffer = create_bitmap(SCREEN_W, SCREEN_H);
   d->handler = handler;

   draw_dialog(d);

   if (dialog_count > 0) {
      draw_dialog(&dialogs[dialog_count-1]);
      dialogs[dialog_count-1].state = (chain ? state_chain : state_slideoff);
      dialogs[dialog_count-1].time = retrace_count;
   }
}



/* main dialog update routine */
static int update()
{
   BITMAP *oldscreen = screen;
   ACTIVE_DIALOG *d;
   DIALOG_STATE state;
   PALETTE pal;
   BITMAP *b;
   int pos, ppos, pppos;
   int ret;

   if (dialog_count <= 0)
      return FALSE;

   d = &dialogs[dialog_count-1];

   if (d->state == state_active) {
      /* process the dialog */
      if (_mouse_screen != screen)
	 show_mouse(screen);

      ret = update_dialog(d->player);

      if (!ret) {
	 if (d->handler)
	    state = d->handler(d->player->obj);
	 else
	    state = state_exit;

	 if (state == state_exit) {
	    /* exit this dialog */
	    draw_dialog(d);
	    d->state = state_exit;
	    d->time = retrace_count;
	 }
	 else if (state == state_redraw) {
	    /* redraw the dialog */
	    d->player->res |= D_REDRAW;
	 }
      }
      else {
	 pos = find_dialog_focus(d->dialog);
	 if ((pos >= 0) && (d->dialog[pos].dp3))
	    wanted_scroller = d->dialog[pos].dp3;
      }
   }
   else {
      /* sliding on or off */
      show_mouse(NULL);

      b = setup_data[BACKGROUND].dat;
      stretch_blit(b, buffer, 0, 0, b->w, b->h, 0, 0, SCREEN_W, SCREEN_H);

      text_mode(0);
      textout_centre(buffer, font, SETUP_TITLE, SCREEN_W/2, 0, -1);

      wanted_scroller = "";
      screen = buffer;
      scroller_proc(MSG_IDLE, NULL, 0);
      screen = oldscreen;

      pos = retrace_count - d->time;

      if ((dialog_count == 1) && (d->state == state_start))
	 pos *= 64;
      else if ((dialog_count == 1) && (d->state == state_exit))
	 pos *= 48;
      else if (d->state == state_start)
	 pos *= 96;
      else
	 pos *= 128;

      pos = MID(0, 4096-pos, 4096);
      pppos = (4096 - pos * pos / 4096);
      pos = pppos / 16;

      /* draw the slide effect */
      switch (d->state) {

	 case state_start:
	    ppos = pos;
	    stretch_sprite(buffer, d->buffer, 0, 0, SCREEN_W+(1024-pppos/4)*SCREEN_W/320, SCREEN_H+(1024-pppos/4)*SCREEN_H/200);
	    break;

	 case state_slideon:
	    ppos = pos;
	    stretch_sprite(buffer, d->buffer, 0, 0, SCREEN_W*ppos/256, SCREEN_H*ppos/256);
	    break;

	 case state_slideoff:
	 case state_chain:
	    ppos = 256 - pos;
	    stretch_sprite(buffer, d->buffer, SCREEN_W/2-SCREEN_W*ppos/512, SCREEN_H/2-SCREEN_H*ppos/512, SCREEN_W*ppos/256, SCREEN_H*ppos/256);
	    break;

	 case state_exit:
	    ppos = 256 - pos;
	    stretch_sprite(buffer, d->buffer, SCREEN_W-SCREEN_W*ppos/256, SCREEN_H-SCREEN_H*ppos/256, SCREEN_W*ppos/256, SCREEN_H*ppos/256);
	    break;

	 default:
	    ppos = 0;
	    break;
      }

      if ((dialog_count == 1) && (d->state != state_slideon) && (d->state != state_slideoff) && (d->state != state_chain)) {
	 fade_interpolate(black_palette, setup_data[SETUP_PAL].dat, pal, ppos/4, 0, 255);
	 set_palette(pal);
      }
      else
	 vsync();

      blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);

      if (pos >= 256) {
	 /* finished the slide */
	 switch (d->state) {

	    case state_start:
	    case state_slideon:
	       /* become active */
	       d->state = state_active;
	       d->player->res |= D_REDRAW;
	       break;

	    case state_slideoff:
	       /* activate next dialog, and then get ready to slide back on */
	       dialogs[dialog_count].time = retrace_count;
	       dialog_count++;
	       d->state = state_slideon;
	       break;

	    case state_exit:
	       /* time to die! */
	       shutdown_dialog(d->player);
	       destroy_bitmap(d->buffer);
	       dialog_count--;
	       if (dialog_count > 0)
		  dialogs[dialog_count-1].time = retrace_count;
	       break;

	    case state_chain:
	       /* kill myself, then activate the next dialog */
	       shutdown_dialog(d->player);
	       destroy_bitmap(d->buffer);
	       dialogs[dialog_count].time = retrace_count;
	       dialogs[dialog_count-1] = dialogs[dialog_count];
	       break;

	    default:
	       break;
	 }
      }
   }

   return TRUE;
}



/* helper for checking which drivers are valid */
static void detect_sound()
{
   int i;

   for (i=0; digi_cards[i].id != DIGI_NONE; i++) {
      if (detect_digi_driver(digi_cards[i].id) == 0)
	 digi_cards[i].present = FALSE;
      else
	 digi_cards[i].present = TRUE;
   }
   digi_cards[i].present = TRUE;

   for (i=0; midi_cards[i].id != MIDI_NONE; i++) {
      if (detect_midi_driver(midi_cards[i].id) == 0)
	 midi_cards[i].present = FALSE;
      else
	 midi_cards[i].present = TRUE;
   }
   midi_cards[i].present = TRUE;
}



/* helper for initialising the sound code */
static int init_sound(char *msg)
{
   char b1[80], b2[80];
   int i;

   show_mouse(NULL);

   detect_sound();

   if (install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL) == 0)
      return 0;

   if (strlen(allegro_error) <= 32) {
      strcpy(b1, allegro_error);
      b2[0] = 0;
   }
   else {
      for (i=strlen(allegro_error)*9/16; i>10; i--)
	 if (allegro_error[i] == ' ')
	    break;

      strncpy(b1, allegro_error, i);
      b1[i] = 0;

      strcpy(b2, allegro_error+i+1);
   }

   alert(msg, b1, b2, "Ok", NULL, 0, 0);

   return -1;
}



static BITMAP *popup_bitmap = NULL;
static BITMAP *popup_bitmap2 = NULL;



/* helper for displaying a popup message */
static void popup(AL_CONST char *s1, AL_CONST char *s2)
{
   int w, w2, c, ct;

   if (!popup_bitmap) {
      c = MAX(512*SCREEN_W/320, 512*SCREEN_H/200);
      ct = 16*SCREEN_W/320;

      acquire_screen();

      for (w=c; w>=0; w-=2) {
	 line(screen, w, 16, 0, 16+w, 0);
	 if (!(w%ct)) {
	    release_screen();
	    vsync();
	    acquire_screen();
	 }
      }

      for (w=0; w<c; w+=2) {
	 line(screen, w+1, 16, 0, 17+w, 0);
	 if (!(w%ct)) {
	    release_screen();
	    vsync();
	    acquire_screen();
	 }
      }

      release_screen();

      popup_bitmap = create_bitmap(SCREEN_W, SCREEN_H);
      popup_bitmap2 = create_bitmap(SCREEN_W, SCREEN_H);
      blit(screen, popup_bitmap, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
   }

   blit(popup_bitmap, popup_bitmap2, 0, 0, 0, 0, SCREEN_W, SCREEN_H);

   if ((s1) || (s2)) {
      if (s1)
	 w = text_length(font, s1);
      else
	 w = 0;

      if (s2)
	 w2 = text_length(font, s2);
      else
	 w2 = 0;

      w = MAX(w, w2);

      rectfill(popup_bitmap2, (SCREEN_W-w)/2-15, SCREEN_H/2-31, (SCREEN_W+w)/2+15, SCREEN_H/2+31, 0);
      rect(popup_bitmap2, (SCREEN_W-w)/2-16, SCREEN_H/2-32, (SCREEN_W+w)/2+16, SCREEN_H/2+32, 255);

      text_mode(-1);

      if (s1)
	 textout_centre(popup_bitmap2, font, s1, SCREEN_W/2, SCREEN_H/2-20, -1);

      if (s2)
	 textout_centre(popup_bitmap2, font, s2, SCREEN_W/2, SCREEN_H/2+4, -1);
   }

   blit(popup_bitmap2, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
}



/* ends the display of a popup message */
static void end_popup()
{
   if (popup_bitmap) {
      blit(popup_bitmap, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
      destroy_bitmap(popup_bitmap);
      popup_bitmap = NULL;
      destroy_bitmap(popup_bitmap2);
      popup_bitmap2 = NULL;
   }
}



/* joystick test display */
static void plot_joystick_state(BITMAP *bmp, int i)
{
   char buf[80];
   int j, x, y;
   int c = 0;

   text_mode(-1);

   if (joystick_driver) {
      if (num_joysticks > 1)
	 textprintf(bmp, font, SCREEN_W/2-96, SCREEN_H/2-60+c*20, -1, "%s (%d/%d)", joystick_driver->name, i+1, num_joysticks);
      else
	 textprintf(bmp, font, SCREEN_W/2-96, SCREEN_H/2-60+c*20, -1, joystick_driver->name);
      c++;
   }

   for (j=0; j<joy[i].num_sticks; j++) {
      if (joy[i].stick[j].num_axis == 2) {
	 if (joy[i].stick[j].flags & JOYFLAG_ANALOGUE) {
	    x = (joy[i].stick[j].axis[0].pos+128)*SCREEN_W/256;
	    y = (joy[i].stick[j].axis[1].pos+128)*SCREEN_H/256;
	    hline(bmp, x-12, y, x+12, 15);
	    vline(bmp, x, y-12, y+12, 15);
	    circle(bmp, x, y, 12, 1);
	    circlefill(bmp, x, y, 4, 31);
	 }
	 else {
	    sprintf(buf, "%s:", joy[i].stick[j].name);
	    if (joy[i].stick[j].axis[1].d1) 
	       strcat(buf, " up");
	    if (joy[i].stick[j].axis[1].d2) 
	       strcat(buf, " down");
	    if (joy[i].stick[j].axis[0].d1) 
	       strcat(buf, " left");
	    if (joy[i].stick[j].axis[0].d2) 
	       strcat(buf, " right");
	    textout(bmp, font, buf, SCREEN_W/2-96, SCREEN_H/2-60+c*20, -1);
	    c++;
	 }
      }
      else {
	 sprintf(buf, "%s: %s %4d %s", 
		joy[i].stick[j].name, 
		(joy[i].stick[j].axis[0].d1) ? "<-" : "  ", 
		joy[i].stick[j].axis[0].pos, 
		(joy[i].stick[j].axis[0].d2) ? "->" : "  ");

	 textout(bmp, font, buf, SCREEN_W/2-96, SCREEN_H/2-60+c*20, -1);
	 c++;
      }
   }

   for (j=0; j<joy[i].num_buttons; j++) {
      sprintf(buf, "%s: %s", joy[i].button[j].name, (joy[i].button[j].b) ? "*" : "");

      if (j&1) {
	 textout(bmp, font, buf, SCREEN_W/2+32, SCREEN_H/2-60+c*20, -1);
	 c++;
      }
      else
	 textout(bmp, font, buf, SCREEN_W/2-96, SCREEN_H/2-60+c*20, -1);
   }

   textout_centre(bmp, font, "- press a key to accept -", SCREEN_W/2, SCREEN_H-16, 255);
}



/* helper for calibrating the joystick */
static void joystick_proc(int type)
{
   int i, c;

   scroller_proc(MSG_RADIO, NULL, 0);
   scare_mouse();

   remove_joystick();

   if (install_joystick(type) != 0) {
      alert("Error:", allegro_error, NULL, "Ok", NULL, 0, 0);
      unscare_mouse();
      return;
   }

   for (i=0; i<num_joysticks; i++) {
      while (joy[i].flags & JOYFLAG_CALIBRATE) {
	 popup(calibrate_joystick_name(i), "and press a button");

	 rest(10);
	 do {
	    poll_joystick();
	 } while ((!joy[i].button[0].b) && (!joy[i].button[1].b) && (!keypressed()));

	 if (calibrate_joystick(i) != 0) {
	    remove_joystick();
	    alert("Error calibrating joystick", NULL, NULL, "Ok", NULL, 0, 0);
	    end_popup();
	    unscare_mouse();
	    return;
	 }

	 rest(10);
	 do {
	    poll_joystick();
	 } while ((joy[i].button[0].b) || (joy[i].button[1].b));

	 clear_keybuf();
      }
   }

   if (!popup_bitmap)
      popup(NULL, NULL);

   do {
      poll_mouse();
   } while (mouse_b);

   clear_keybuf();

   i = 0;
   c = 0;

   while ((!c) && (!mouse_b)) {
      poll_joystick();
      poll_mouse();

      blit(popup_bitmap, popup_bitmap2, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
      plot_joystick_state(popup_bitmap2, i);
      blit(popup_bitmap2, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);

      if (keypressed()) {
	 c = readkey();

	 switch (c>>8) {

	    case KEY_UP:
	    case KEY_RIGHT:
	       i++;
	       c = 0;
	       break;

	    case KEY_DOWN:
	    case KEY_LEFT:
	       i--;
	       c = 0;
	       break;

	    default:
	       c &= 0xFF;
	       break;
	 }

	 if (i >= num_joysticks)
	    i = num_joysticks-1;

	 if (i < 0)
	    i = 0;
      }
   }

   end_popup();

   do {
      poll_mouse();
   } while (mouse_b);

   clear_keybuf();

   unscare_mouse();
}



/* helper for choosing a mouse type */
static void mouse_proc(int type)
{
   remove_mouse();
   mouse_type = type;
   install_mouse();
}



static void save_mouse_data(void)
{
   char tmp[64];
   set_config_id(NULL, uconvert_ascii("mouse", tmp), mouse_type);
}



#ifdef ALLEGRO_LINUX

static void get_mouse_drivers(_DRIVER_INFO **list, int *list_size);

static int detect_mouse(void)
{
   int fd;
   int i;
   int retval = -1;
   char buffer[256];
   int count;
   _DRIVER_INFO *list;
   int list_size;

   remove_mouse();

   fd = open ("/dev/mouse", O_RDONLY | O_NONBLOCK);
   if (fd == -1) {
      alert ("Error opening /dev/mouse:", strerror(errno), NULL, "Ok", NULL, 0, 0);
      retval = -1;
      goto finished;
   }

   while (read(fd, buffer, 1) == 1);

   popup ("Move your mouse around", "Press any key to cancel");

   {
      int w,l,r,t,b;
      w = sizeof buffer;
      l = (SCREEN_W - w)/2;
      r = l + w;
      t = SCREEN_H/2 + 64;
      b = t + 16;

      rect(popup_bitmap2, l-1, t-1, r, b+1, gui_fg_color);
      blit(popup_bitmap2, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);

      for (count = 0; count < sizeof buffer;) {
	 if (read(fd, buffer+count, 1) == 1) {
	    vline(popup_bitmap2, l + count*w/sizeof buffer, t, b, gui_mg_color);
	    blit(popup_bitmap2, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
	    count++;
	 }
	 if (keypressed() && readkey()) break;
      }
   }

   if (count == 0) {
      popup ("No data received", NULL);
   } else if (count < sizeof buffer) {
      popup ("Insufficient data received", NULL);
   }

   if (count < sizeof buffer) {
      retval = -1;
      goto finished;
   }

   popup ("Analysing data...", NULL);

   get_mouse_drivers(&list, &list_size);
   for (i = 0; i < list_size; i++) {
      MOUSE_DRIVER *drv = list[i].driver;
      if (drv->analyse_data && drv->analyse_data (buffer, count)) {
	 int x;
	 x = alert ("This driver understands your mouse:", drv->ascii_name, "Select it?", "Yes", "No", 'y', 'n');
	 if (x == 1) break;
      }
   }

   if (i < list_size)
      retval = i;
   else {
      alert ("No drivers understand your mouse", NULL, "Try using the GPM repeater", "Ok", NULL, 0, 0);
      retval = -1;
   }

   finished:
   end_popup();
   install_mouse();
   return retval;
}

#endif



/* dialog callback for retrieving the SB frequency list */
static char *freq_getter(int index, int *list_size)
{
   if (index < 0) {
      if (list_size) {
	 switch (soundcard->id) {

	    #if (defined DIGI_SB10) || (defined DIGI_SB15)

	       case DIGI_SB10:
	       case DIGI_SB15:
		  *list_size = 2;
		  break;

	    #endif

	    #ifdef DIGI_SBPRO

	       case DIGI_SBPRO:
		  *list_size = 3;
		  break;

	    #endif

	    default:
	       *list_size = 4;
	       break;
	 }
      }

      return NULL;
   }

   #ifdef DIGI_AUDIODRIVE

      if (soundcard->id == DIGI_AUDIODRIVE) {
	 static char *ess_freq[] =
	 {
	    "11363 hz",
	    "17046 hz",
	    "22729 hz",
	    "44194 hz"
	 };

	 return ess_freq[index];
      }
      else 

   #endif

   #ifdef DIGI_SOUNDSCAPE

      if (soundcard->id == DIGI_SOUNDSCAPE) {
	 static char *esc_freq[] =
	 {
	    "11025 hz",
	    "16000 hz",
	    "22050 hz",
	    "48000 hz"
	 };

	 return esc_freq[index];
      }
      else 

   #endif

   #ifdef DIGI_WINSOUNDSYS

      if (soundcard->id == DIGI_WINSOUNDSYS) {
	 static char *wss_freq[] =
	 {
	    "11025 hz",
	    "22050 hz",
	    "44100 hz",
	    "48000 hz"
	 };

	 return wss_freq[index];
      }
      else 

   #endif

   #ifdef ALLEGRO_DOS

      {
	 static char *sb_freq[] =
	 {
	    "11906 hz",
	    "16129 hz",
	    "22727 hz",
	    "45454 hz"
	 };

	 return sb_freq[index];
      }

   #else

      {
	 static char *default_freq[] =
	 {
	    "11025 hz",
	    "22050 hz",
	    "44100 hz",
	    "48000 hz"
	 };

	 return default_freq[index];
      }

   #endif
}



/* dialog callback for retrieving information about the soundcard list */
static char *card_getter(int index, int *list_size)
{
   static char buf[80];
   int i;

   if (index < 0) {
      i = 0;
      while (soundcard[i].id)
	 i++;
      if (list_size)
	 *list_size = i+1;
      return NULL;
   }

   if (soundcard[index].present)
      usprintf(buf, "- %s", soundcard[index].name);
   else
      usprintf(buf, "  %s", soundcard[index].name);

   return buf;
}



static char keyboard_type[256] = "";

static int num_keyboard_layouts = 0;
static char *keyboard_layouts[256];
static char *keyboard_names[256];



static char language_type[256] = "";

static int num_language_layouts = 0;
static char *language_layouts[256];
static char *language_names[256];



/* helper for sorting the keyboard list */
static void sort_keyboards()
{
   int done, i;

   do {
      done = TRUE;

      for (i=0; i<num_keyboard_layouts-1; i++) {
	 if (stricmp(keyboard_names[i], keyboard_names[i+1]) > 0) {
	    char *tl = keyboard_layouts[i];
	    char *tn = keyboard_names[i];

	    keyboard_layouts[i] = keyboard_layouts[i+1];
	    keyboard_names[i] = keyboard_names[i+1];

	    keyboard_layouts[i+1] = tl;
	    keyboard_names[i+1] = tn;

	    done = FALSE;
	 }
      }

   } while (!done);
}



/* helper for sorting the language list */
static void sort_languages()
{
   int done, i;

   do {
      done = TRUE;

      for (i=0; i<num_language_layouts-1; i++) {
	 if (stricmp(language_names[i], language_names[i+1]) > 0) {
	    char *tl = language_layouts[i];
	    char *tn = language_names[i];

	    language_layouts[i] = language_layouts[i+1];
	    language_names[i] = language_names[i+1];

	    language_layouts[i+1] = tl;
	    language_names[i+1] = tn;

	    done = FALSE;
	 }
      }

   } while (!done);
}



/* dialog callback for retrieving information about the keyboard list */
static char *keyboard_getter(int index, int *list_size)
{
   if (index < 0) {
      if (list_size)
	 *list_size = num_keyboard_layouts;
      return NULL;
   }

   return keyboard_names[index];
}



/* dialog callback for retrieving information about the language list */
static char *language_getter(int index, int *list_size)
{
   if (index < 0) {
      if (list_size)
	 *list_size = num_language_layouts;
      return NULL;
   }

   return language_names[index];
}



/* counts how many joystick drivers we have, and returns a list of them */
static void get_joystick_drivers(_DRIVER_INFO **list, int *list_size)
{
   _DRIVER_INFO *l;
   int c;

   if (system_driver->joystick_drivers)
      l = system_driver->joystick_drivers();
   else
      l = _joystick_driver_list;

   c = 0;
   while (l[c].driver)
      c++;

   *list = l;
   *list_size = c;
}



/* dialog callback for retrieving information about the joystick list */
static AL_CONST char *joystick_getter(int index, int *list_size)
{
   _DRIVER_INFO *list;
   int size;

   get_joystick_drivers(&list, &size);

   if (index < 0) {
      *list_size = size;
      return NULL;
   }

   return ((JOYSTICK_DRIVER *)list[index].driver)->ascii_name;
}



/* counts how many mouse drivers we have, and returns a list of them */
static void get_mouse_drivers(_DRIVER_INFO **list, int *list_size)
{
   _DRIVER_INFO *l;
   int c;

   if (system_driver->mouse_drivers)
      l = system_driver->mouse_drivers();
   else
      l = _mouse_driver_list;

   c = 0;
   while (l[c].driver)
      c++;

   *list = l;
   *list_size = c;
}



/* dialog callback for retrieving information about the joystick list */
static AL_CONST char *mouse_getter(int index, int *list_size)
{
   _DRIVER_INFO *list;
   int size;

   get_mouse_drivers(&list, &size);

   if (index < 0) {
      *list_size = size;
      return NULL;
   }

   return ((MOUSE_DRIVER *)list[index].driver)->ascii_name;
}



/* dialog procedure for the soundcard selection listbox */
static int card_proc(int msg, DIALOG *d, int c)
{
   int ret = d_list_proc(msg, d, c);
   d->dp3 = (void*)soundcard[d->d1].desc;
   return ret;
}



/* dialog procedure for the filename selection objects */
static int filename_proc(int msg, DIALOG *d, int c)
{
   PARAMETER *p = d->dp2;
   BITMAP *b;
   char buf[256];
   char buf2[256];
   int ret;
   int i;

   if (msg == MSG_START) {
      if (!p->e2)
	 p->e2 = malloc(80);
      strcpy(p->e2, p->value);
   }
   else if (msg == MSG_END) {
      if (p->e2) {
	 free(p->e2);
	 p->e2 = NULL;
      }
   }

   ret = d_check_proc(msg, d, c);

   if (ret & D_CLOSE) {
      if (p->value[0]) {
	 strcpy(p->e2, p->value);
	 p->value[0] = 0;
      }
      else {
	 scroller_proc(MSG_RADIO, NULL, 0);

	 strcpy(buf2, p->desc);

	 for (i=1; buf2[i]; i++) {
	    if (buf2[i] == '(') {
	       buf2[i-1] = 0;
	       break;
	    }
	 }

	 strcpy(buf, p->e2);

	 if (file_select(buf2, buf, p->e1)) {
	    strcpy(p->value, buf);
	    strcpy(p->e2, buf);
	 }

	 scare_mouse();

	 b = setup_data[BACKGROUND].dat;
	 stretch_blit(b, screen, 0, 0, b->w, b->h, 0, 0, SCREEN_W, SCREEN_H);

	 text_mode(0);
	 textout_centre(screen, font, SETUP_TITLE, SCREEN_W/2, 0, -1);
	 unscare_mouse();
      }

      if (p->value[0])
	 d->flags |= D_SELECTED;
      else
	 d->flags &= ~D_SELECTED;

      ret &= ~D_CLOSE;
      ret |= D_REDRAW;
   }

   return ret;
}



/* wrapper for d_list_proc() to free up the dp2 parameter */
static int d_xlist_proc(int msg, DIALOG *d, int c)
{
   void *old_dp2;
   int ret;

   old_dp2 = d->dp2;
   d->dp2 = NULL;

   ret = d_list_proc(msg, d, c);

   d->dp2 = old_dp2;

   return ret;
}



static char backup_str[] =  "Go back to the previous menu";
static char midi_desc[160];
static char digi_desc[160];



static DIALOG main_dlg[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key)    (flags)     (d1)           (d2)     (dp)                          (p)      (help message) */
   { d_button_proc,     30,   32,   125,  23,   -1,   16,   0,       D_EXIT,     0,             0,       "Autodetect",                 NULL,    "Attempt to autodetect your soundcard (ie. guess :-)" },
   { d_button_proc,     166,  32,   125,  23,   -1,   16,   0,       D_EXIT,     0,             0,       "Test",                       NULL,    "Test the current settings" },
   { d_button_proc,     30,   60,   125,  23,   -1,   16,   0,       D_EXIT,     0,             0,       "Digital Driver",             NULL,    "Manually select a driver for playing digital samples" },
   { d_button_proc,     166,  60,   125,  23,   -1,   16,   0,       D_EXIT,     0,             0,       "Midi Driver",                NULL,    "Manually select a driver for playing MIDI music" },
   { d_button_proc,     30,   88,   125,  23,   -1,   16,   0,       D_EXIT,     0,             0,       "Locale",                     NULL,    "Select a keyboard layout and system language" },
   { d_button_proc,     166,  88,   125,  23,   -1,   16,   0,       D_EXIT,     0,             0,       "Joystick",                   NULL,    "Calibrate your joystick" },
   { d_button_proc,     30,   116,  125,  23,   -1,   16,   0,       D_EXIT,     0,             0,       "Mouse",                      NULL,    "Configure your mouse" },
   { d_button_proc,     30,   144,  125,  23,   -1,   16,   0,       D_EXIT,     0,             0,       "Save and Exit",              NULL,    "Exit from the program, saving the current settings into the file '" SETUP_CFG_FILE "'" },
   { d_button_proc,     166,  144,  125,  23,   -1,   16,   0,       D_EXIT,     0,             0,       "Just Exit",                  NULL,    "Exit from the program, without saving the current settings" },
   { scroller_proc,     0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL },
   { NULL,              0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL }
};



static DIALOG test_dlg[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key)    (flags)     (d1)           (d2)     (dp)                          (p)      (help message) */
   { d_button_proc,     100,  50,   121,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "MIDI",                       NULL,    midi_desc },
   { d_button_proc,     30,   87,   81,   25,   -1,   16,   0,       D_EXIT,     0,             0,       "Left",                       NULL,    digi_desc },
   { d_button_proc,     120,  87,   81,   25,   -1,   16,   0,       D_EXIT,     0,             0,       "Centre",                     NULL,    digi_desc },
   { d_button_proc,     210,  87,   81,   25,   -1,   16,   0,       D_EXIT,     0,             0,       "Right",                      NULL,    digi_desc },
   { d_button_proc,     100,  124,  121,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "Exit",                       NULL,    backup_str },
   { scroller_proc,     0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL },
   { NULL,              0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL }
};



static DIALOG card_dlg[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key)    (flags)     (d1)           (d2)     (dp)                          (p)      (help message) */
   { d_button_proc,     30,   132,  125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "OK",                         NULL,    "Use this driver" },
   { d_button_proc,     166,  132,  125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "Cancel",                     NULL,    backup_str },
   { card_proc,         20,   36,   281,  84,   255,  16,   0,       D_EXIT,     0,             0,       card_getter,                  NULL,    NULL },
   { scroller_proc,     0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL },
   { NULL,              0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL }
};



static DIALOG locale_dlg[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key)    (flags)     (d1)           (d2)     (dp)                          (p)      (help message) */
   { d_button_proc,     30,   132,  125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "OK",                         NULL,    "Use this keyboard layout and language" },
   { d_button_proc,     166,  132,  125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "Cancel",                     NULL,    backup_str },
   { d_list_proc,       8,    50,   147,  68,   255,  16,   0,       D_EXIT,     0,             0,       keyboard_getter,              NULL,    "Select a keyboard layout" },
   { d_list_proc,       166,  50,   147,  68,   255,  16,   0,       D_EXIT,     0,             0,       language_getter,              NULL,    "Select language for system messages" },
   { d_ctext_proc,      81,   30,   0,    0,    16,   -1,   0,       0,          0,             0,       "Keyboard",                   NULL,    NULL },
   { d_ctext_proc,      239,  30,   0,    0,    16,   -1,   0,       0,          0,             0,       "Language",                   NULL,    NULL },
   { scroller_proc,     0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL },
   { NULL,              0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL }
};



static DIALOG joystick_dlg[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key)    (flags)     (d1)           (d2)     (dp)                          (p)      (help message) */
   { d_button_proc,     30,   132,  125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "OK",                         NULL,    "Use this joystick type" },
   { d_button_proc,     166,  132,  125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "Cancel",                     NULL,    backup_str },
   { d_list_proc,       60,   36,   201,  84,   255,  16,   0,       D_EXIT,     0,             0,       joystick_getter,              NULL,    "Select a type of joystick" },
   { scroller_proc,     0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL },
   { NULL,              0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL }
};



static DIALOG mouse_dlg[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key)    (flags)     (d1)           (d2)     (dp)                          (p)      (help message) */
   { d_button_proc,     30,   132,  125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "OK",                         NULL,    "Use this mouse type" },
   { d_button_proc,     166,  132,  125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "Cancel",                     NULL,    backup_str },
   { d_list_proc,       85,   36,   151,  84,   255,  16,   0,       D_EXIT,     0,             0,       mouse_getter,                 NULL,    "Select a type of mouse" },
   { scroller_proc,     0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL },
   { NULL,              0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL }
};



#ifdef ALLEGRO_LINUX

static DIALOG linux_mouse_dlg[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key)    (flags)     (d1)           (d2)     (dp)                          (p)      (help message) */
   { d_button_proc,     30,   132,  125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "OK",                         NULL,    "Use this mouse type" },
   { d_button_proc,     166,  132,  125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "Cancel",                     NULL,    backup_str },
   { d_list_proc,       30,   36,   151,  84,   255,  16,   0,       D_EXIT,     0,             0,       mouse_getter,                 NULL,    "Select a type of mouse" },
   { d_button_proc,     192,  36,   125,  25,   -1,   16,   0,       D_EXIT,     0,             0,       "Detect",                     NULL,    "Attempt to detect the type of your mouse" },
   { scroller_proc,     0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL },
   { NULL,              0,    0,    0,    0,    0,    0,    0,       0,          0,             0,       NULL,                         NULL,    NULL }
};

#endif



/* this one is generated depending on which parameters we need */
static DIALOG param_dlg[32];

static int param_ok;



/* handle input from the parameter dialog */
static DIALOG_STATE param_handler(int c)
{
   PARAMETER *p;
   DIALOG *d = param_dlg;
   int i;

   if (c == param_ok) {
      /* save the changes */ 
      while (d->proc) {
	 p = d->dp2;

	 if (p) {
	    switch (p->type) {

	       case param_int:
	       case param_num:
		  if (p->value[0])
		     i = strtol(p->value, NULL, 0);
		  else
		     i = -1;
		  set_config_int("sound", p->name, i);
		  strcpy(p->value, get_config_string("sound", p->name, ""));
		  break;

	       case param_hex:
		  if (p->value[0])
		     i = strtol(p->value, NULL, 16);
		  else
		     i = -1;
		  set_config_hex("sound", p->name, i);
		  strcpy(p->value, get_config_string("sound", p->name, ""));
		  break;

	       case param_id:
		  if (p->value[0])
		     i = strtol(p->value, NULL, 0);
		  else
		     i = -1;
		  set_config_id("sound", p->name, i);
		  strcpy(p->value, get_config_string("sound", p->name, ""));
		  break;

	       case param_bool:
		  set_config_int("sound", p->name, (d->flags & D_SELECTED) ? 1 : 0);
		  strcpy(p->value, get_config_string("sound", p->name, ""));
		  break;

	       case param_file:
		  set_config_string("sound", p->name, p->value);
		  strcpy(p->value, get_config_string("sound", p->name, ""));
		  break;

	       case param_list:
		  i = strtol(freq_getter(d->d1, NULL), NULL, 0);
		  set_config_int("sound", p->name, i);
		  strcpy(p->value, get_config_string("sound", p->name, ""));
		  break;

	       case param_str:
		  set_config_string("sound", p->name, p->value);
		  strcpy(p->value, get_config_string("sound", p->name, ""));
		  break;

	       default:
		  break;
	    }

	 }

	 d++;
      }
   }
   else {
      /* discard the changes */ 
      while (d->proc) {
	 p = d->dp2;

	 if (p)
	    strcpy(p->value, get_config_string("sound", p->name, ""));

	 d++;
      }
   }

   return state_exit;
}



/* sets up the soundcard parameter dialog box */
static void setup_param_dialog()
{
   PARAMETER *p;
   DIALOG *d = param_dlg;
   char **c = soundcard->param;
   char *s;
   int pos = 0;
   int xo = 0;
   int yo = 0;
   int i, x, y, f, g;

   #define DLG(_p, _x, _y, _w, _h, _f, _b, _k, _l, _d1, _d2, _dp, _pa, _m)   \
   {                                                                         \
      d->proc = _p;                                                          \
      d->x = _x;                                                             \
      d->y = _y;                                                             \
      d->w = _w;                                                             \
      d->h = _h;                                                             \
      d->fg = _f;                                                            \
      d->bg = _b;                                                            \
      d->key = _k;                                                           \
      d->flags = _l;                                                         \
      d->d1 = _d1;                                                           \
      d->d2 = _d2;                                                           \
      d->dp = _dp;                                                           \
      d->dp2 = _pa;                                                          \
      d->dp3 = _m;                                                           \
      d++;                                                                   \
   }

   while (*c) {
      if ((uisdigit((*c)[0])) && (uisdigit((*c)[1]))) {
	 xo = (*c)[0] - '0';
	 if (xo)
	    xo = 100 / xo;

	 yo = (*c)[1] - '0';
	 if (yo)
	    yo = 38 / yo;
      }
      else {
	 x = 16 + (pos%3) * 100 + xo;
	 y = 30 + (pos/3) * 38 + yo;
	 pos++;

	 p = NULL;

	 for (i=0; parameters[i].name; i++) {
	    if (stricmp(parameters[i].name, *c) == 0) {
	       p = &parameters[i];
	       break;
	    }
	 }

	 if (p) {
	    switch (p->type) {

	       case param_int:
		  /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)  (d2)  (dp)           (p)         (help) */
		  DLG(d_box_proc,      x,    y,    89,   22,   255,  16,   0,    0,       0,    0,    NULL,          NULL,       NULL);
		  DLG(d_text_proc,     x+4,  y+3,  0,    0,    255,  16,   0,    0,       0,    0,    p->label,      NULL,       NULL);
		  DLG(d_edit_proc,     x+54, y+3,  32,   16,   255,  16,   0,    0,       3,    0,    p->value,      p,          p->desc);
		  break;

	       case param_hex:
		  if (stricmp(p->value, "FFFFFFFF") == 0)
		     strcpy(p->value, "-1");

		  /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)  (d2)  (dp)           (p)         (help) */
		  DLG(d_box_proc,      x,    y,    89,   22,   255,  16,   0,    0,       0,    0,    NULL,          NULL,       NULL);
		  DLG(d_text_proc,     x+4,  y+3,  0,    0,    255,  16,   0,    0,       0,    0,    p->label,      NULL,       NULL);
		  DLG(d_edit_proc,     x+54, y+3,  32,   16,   255,  16,   0,    0,       3,    0,    p->value,      p,          p->desc);
		  break;

	       case param_bool:
		  if (strtol(p->value, NULL, 0) != 0)
		     f = D_SELECTED;
		  else
		     f = 0;

		  /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)  (d2)  (dp)           (p)         (help) */
		  DLG(d_box_proc,      x,    y,    89,   22,   255,  16,   0,    0,       0,    0,    NULL,          NULL,       NULL);
		  DLG(d_text_proc,     x+4,  y+3,  0,    0,    255,  16,   0,    0,       0,    0,    p->label,      NULL,       NULL);
		  DLG(d_check_proc,    x+54, y+3,  32,   16,   255,  16,   0,    f,       0,    0,    " ",           p,          p->desc);
		  break;

	       case param_file:
		  if (p->value[0])
		     f = D_SELECTED | D_EXIT;
		  else
		     f = D_EXIT;

		  /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)  (d2)  (dp)           (p)         (help) */
		  DLG(d_box_proc,      x,    y,    89,   22,   255,  16,   0,    0,       0,    0,    NULL,          NULL,       NULL);
		  DLG(d_text_proc,     x+4,  y+3,  0,    0,    255,  16,   0,    0,       0,    0,    p->label,      NULL,       NULL);
		  DLG(filename_proc,   x+62, y+3,  32,   16,   255,  16,   0,    f,       0,    0,    "",            p,          p->desc);
		  break;

	       case param_list:
		  i = strtol(p->value, NULL, 0);
		  freq_getter(-1, &f);
		  if (i > 0) {
		     for (g=0; g<f; g++) {
			s = freq_getter(g, NULL);
			if (i <= strtol(s, NULL, 0))
			   break;
		     }
		  }
		  else
		     g = 2;

		  /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)  (d2)  (dp)           (p)         (help) */
		  DLG(d_xlist_proc,    x,    y-8,  89,   68,   255,  16,   0,    0,       g,    0,    freq_getter,   p,          p->desc);
		  break;

	       case param_num:
		  /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)  (d2)  (dp)           (p)         (help) */
		  DLG(d_box_proc,      x,    y,    89,   22,   255,  16,   0,    0,       0,    0,    NULL,          NULL,       NULL);
		  DLG(d_text_proc,     x+4,  y+3,  0,    0,    255,  16,   0,    0,       0,    0,    p->label,      NULL,       NULL);
		  DLG(d_edit_proc,     x+40, y+3,  46,   16,   255,  16,   0,    0,       6,    0,    p->value,      p,          p->desc);
		  break;

	       case param_str:
		  /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)  (d2)  (dp)           (p)         (help) */
		  DLG(d_box_proc,      x,    y,    89,   22,   255,  16,   0,    0,       0,    0,    NULL,          NULL,       NULL);
		  DLG(d_text_proc,     x+4,  y+3,  0,    0,    255,  16,   0,    0,       0,    0,    p->label,      NULL,       NULL);
		  DLG(d_edit_proc,     x+40, y+3,  46,   16,   255,  16,   0,    0,       20,   0,    p->value,      p,          p->desc);
		  break;

	       default:
		  break;
	    }
	 }
      }

      c++;
   }

   param_ok = ((int)d - (int)param_dlg) / sizeof(DIALOG);

   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)  (d2)  (dp)        (p)                        (help) */
   DLG(d_button_proc,   30,   142,  125,  25,   -1,   16,   13,   D_EXIT,  0,    0,    "OK",       NULL,                      "Use these parameters");
   DLG(d_button_proc,   166,  142,  125,  25,   -1,   16,   0,    D_EXIT,  0,    0,    "Cancel",   NULL,                      backup_str);
   DLG(scroller_proc,   0,    0,    0,    0,    0,    0,    0,    0,       0,    0,    NULL,       NULL,                      NULL);
   DLG(NULL,            0,    0,    0,    0,    0,    0,    0,    0,       0,    0,    NULL,       NULL,                      NULL);

   activate_dialog(param_dlg, param_handler, TRUE);
}



/* handle input from the test dialog */
static DIALOG_STATE test_handler(int c)
{
   switch (c) {

      case 0:
	 /* MIDI test */
	 play_midi(setup_data[TEST_MIDI].dat, FALSE);
	 return state_redraw;

      case 1:
	 /* left pan */
	 play_sample(setup_data[TEST_SAMPLE].dat, 255, 0, 1000, FALSE);
	 return state_redraw;

      case 2:
	 /* centre pan */
	 play_sample(setup_data[TEST_SAMPLE].dat, 255, 128, 1000, FALSE);
	 return state_redraw;

      case 3:
	 /* right pan */
	 play_sample(setup_data[TEST_SAMPLE].dat, 255, 255, 1000, FALSE);
	 return state_redraw;

      default:
	 /* quit */
	 remove_sound();
	 return state_exit;
   }

   return state_active;
}



/* handle input from the card selection dialog */
static DIALOG_STATE card_handler(int c)
{
   int i;

   switch (c) {

      case 0:
      case 2:
	 /* select driver */
	 i = (soundcard == digi_cards) ? 0 : 1;
	 soundcard += card_dlg[2].d1;
	 set_config_id("sound", parameters[i].name, soundcard->id);
	 strcpy(parameters[i].value, get_config_string("sound", parameters[i].name, ""));
	 if (soundcard->param)
	    setup_param_dialog();
	 else
	    return state_exit;
	 break;

      default:
	 /* quit */
	 return state_exit;
   }

   return state_active;
}



/* handle input from the locale selection dialog */
static DIALOG_STATE locale_handler(int c)
{
   switch (c) {

      case 0:
      case 2:
      case 3:
	 /* select driver */
	 if (locale_dlg[2].d1 < num_keyboard_layouts)
	    strcpy(keyboard_type, keyboard_layouts[locale_dlg[2].d1]);

	 if (locale_dlg[3].d1 < num_language_layouts) {
	    char tmp[128];

	    strcpy(language_type, language_layouts[locale_dlg[3].d1]);

	    push_config_state();
	    sprintf(tmp, "language = %s\n", language_type);
	    set_config_data(tmp, strlen(tmp));
	    _load_config_text();
	    pop_config_state();
	 }

	 return state_exit;

      default:
	 /* quit */
	 return state_exit;
   }

   return state_active;
}



/* handle input from the joystick selection dialog */
static DIALOG_STATE joystick_handler(int c)
{
   _DRIVER_INFO *list;
   int list_size;

   switch (c) {

      case 0:
      case 2:
	 /* select joystick */
	 get_joystick_drivers(&list, &list_size);
	 joystick_proc(list[joystick_dlg[2].d1].id);
	 return state_exit;

      default:
	 /* quit */
	 return state_exit;
   }

   return state_active;
}



/* handle input from the mouse selection dialog */
static DIALOG_STATE mouse_handler(int c)
{
   _DRIVER_INFO *list;
   int list_size;

   #ifdef ALLEGRO_LINUX
      int x;
   #endif

   switch (c) {

      case 0:
      case 2:
	 /* select mouse */
	 get_mouse_drivers(&list, &list_size);

	 #ifdef ALLEGRO_LINUX
	    if (system_driver->id == SYSTEM_LINUX)
	       mouse_proc(list[linux_mouse_dlg[2].d1].id);
	    else
	 #endif
	       mouse_proc(list[mouse_dlg[2].d1].id);

	 return state_exit;

   #ifdef ALLEGRO_LINUX

      case 3:
	 if (system_driver->id == SYSTEM_LINUX) {
	    x = detect_mouse();
	    if (x >= 0) {
	       get_mouse_drivers(&list, &list_size);
	       mouse_proc(list[x].id);
	    }
	 }
	 return state_exit;

   #endif

      default:
	 /* quit */
	 return state_exit;
   }

   return state_active;
}



/* handle input from the main dialog */
static DIALOG_STATE main_handler(int c)
{
   char b1[256], b2[80];
   int i;
   AL_CONST char *s;
   char* s2;
   DATAFILE *data;
   _DRIVER_INFO *list;
   DIALOG *dlg;
   int list_size;

   switch (c) {

      case 0:
	 /* autodetect */
	 scroller_proc(MSG_RADIO, NULL, 0);

	 for (i=0; parameters[i].name; i++) {
	    set_config_string("sound", parameters[i].name, "");
	    parameters[i].value[0] = 0;
	 }

	 reserve_voices(-1, -1);

	 if (cpu_family <= 4)
	    _sound_hq = 0;
	 else if (cpu_family <= 5)
	    _sound_hq = 1;
	 else
	    _sound_hq = 2;

	 if (init_sound("Unable to autodetect!") != 0)
	    return state_redraw;

	 sprintf(b1, "Digital: %s", digi_driver->name);
	 sprintf(b2, "MIDI: %s", midi_driver->name);
	 alert("- detected hardware -", b1, b2, "Ok", NULL, 0, 0);

	 for (i=0; parameters[i].name; i++) {
	    if (parameters[i].detect) {
	       switch (parameters[i].type) {

		  case param_int:
		  case param_num:
		  case param_bool:
		  case param_list:
		     set_config_int("sound", parameters[i].name, *parameters[i].detect);
		     break;

		  case param_id:
		     set_config_id("sound", parameters[i].name, *parameters[i].detect);
		     break;

		  case param_hex:
		     set_config_hex("sound", parameters[i].name, *parameters[i].detect);
		     break;

		  default:
		     break;
	       }
	    }
	    else
	       set_config_string("sound", parameters[i].name, parameters[i].def);

	    strcpy(parameters[i].value, get_config_string("sound", parameters[i].name, ""));
	 }

	 remove_sound();
	 return state_redraw;

      case 1:
	 /* test */
	 scroller_proc(MSG_RADIO, NULL, 0);
	 if (init_sound("Sound initialization failed!") != 0)
	    return state_redraw;
	 sprintf(midi_desc, "Driver: %s        Description: %s", midi_driver->name, midi_driver->desc);
	 sprintf(digi_desc, "Driver: %s        Description: %s", digi_driver->name, digi_driver->desc);
	 activate_dialog(test_dlg, test_handler, FALSE);
	 break;

      case 2:
	 /* choose digital driver */
	 soundcard = digi_cards;
	 for (i=0; soundcard[i].id; i++)
	    if (soundcard[i].id == get_config_id("sound", "digi_card", DIGI_AUTODETECT))
	       break;
	 card_dlg[2].d1 = i;
	 activate_dialog(card_dlg, card_handler, FALSE);
	 break;

      case 3:
	 /* choose MIDI driver */
	 soundcard = midi_cards;
	 for (i=0; soundcard[i].id; i++)
	    if (soundcard[i].id == get_config_id("sound", "midi_card", MIDI_AUTODETECT))
	       break;
	 card_dlg[2].d1 = i;
	 activate_dialog(card_dlg, card_handler, FALSE);
	 break;

      case 4:
	 /* read list of keyboard mappings */
	 if (num_keyboard_layouts <= 0) {
	    if (find_allegro_resource(b1, SETUP_KEYBOARD_FILE, NULL, NULL, NULL, NULL, NULL, sizeof(b1)) == 0)
	       data = load_datafile(b1);
	    else
	       data = NULL;

	    if (!data) {
	       scroller_proc(MSG_RADIO, NULL, 0);
	       alert("Error reading " SETUP_KEYBOARD_FILE, NULL, NULL, "Ok", NULL, 0, 0);
	    }
	    else {
	       for (i=0; data[i].type != DAT_END; i++) {
		  s = get_datafile_property(data+i, DAT_ID('N','A','M','E'));

		  if (s) {
		     s2 = strstr(s, "_CFG");

		     if ((s2) && (s2[4]==0)) {
			s2 = keyboard_layouts[num_keyboard_layouts] = malloc(strlen(s)+1);
			strcpy(s2, s);
			s2[strlen(s2)-4] = 0;

			push_config_state();
			set_config_data(data[i].dat, data[i].size);
			s = get_config_string(NULL, "keyboard_name", s2);
			s2 = keyboard_names[num_keyboard_layouts] = malloc(strlen(s)+1);
			strcpy(s2, s);
			pop_config_state();

			num_keyboard_layouts++;
		     }
		  }
	       }
	       unload_datafile(data);
	    }
	 }

	 /* find the currently selected keyboard mapping */
	 if (num_keyboard_layouts > 0) {
	    sort_keyboards();

	    for (i=0; i<num_keyboard_layouts; i++)
	       if (stricmp(keyboard_type, keyboard_layouts[i]) == 0)
		  break;

	    if (i>=num_keyboard_layouts) {
	       sprintf(b1, "(%s)", keyboard_type);
	       scroller_proc(MSG_RADIO, NULL, 0);
	       alert("Warning: current keyboard", b1, "not found in " SETUP_KEYBOARD_FILE, "Ok", NULL, 0, 0);
	       keyboard_layouts[num_keyboard_layouts] = malloc(strlen(keyboard_type)+1);
	       strcpy(keyboard_layouts[num_keyboard_layouts], keyboard_type);
	       keyboard_names[num_keyboard_layouts] = malloc(strlen(keyboard_type)+1);
	       strcpy(keyboard_names[num_keyboard_layouts], keyboard_type);
	       num_keyboard_layouts++;
	    }

	    locale_dlg[2].d1 = i;
	 }

	 /* read list of languages */
	 if (num_language_layouts <= 0) {
	    if (find_allegro_resource(b1, SETUP_LANGUAGE_FILE, NULL, NULL, NULL, NULL, NULL, sizeof(b1)) == 0)
	       data = load_datafile(b1);
	    else
	       data = NULL;

	    if (!data) {
	       scroller_proc(MSG_RADIO, NULL, 0);
	       alert("Error reading " SETUP_LANGUAGE_FILE, NULL, NULL, "Ok", NULL, 0, 0);
	    }
	    else {
	       for (i=0; data[i].type != DAT_END; i++) {
		  s = get_datafile_property(data+i, DAT_ID('N','A','M','E'));

		  if (s) {
		     s2 = strstr(s, "TEXT_CFG");

		     if ((s2) && (s2[8]==0)) {
			s2 = language_layouts[num_language_layouts] = malloc(strlen(s)+1);
			strcpy(s2, s);
			s2[strlen(s2)-8] = 0;

			push_config_state();
			set_config_data(data[i].dat, data[i].size);
			s = get_config_string(NULL, "language_name", s2);
			s2 = language_names[num_language_layouts] = malloc(strlen(s)+1);
			strcpy(s2, s);
			pop_config_state();

			num_language_layouts++;
		     }
		  }
	       }
	       unload_datafile(data);
	    }
	 }

	 /* find the currently selected language mapping */
	 if (num_language_layouts > 0) {
	    sort_languages();

	    for (i=0; i<num_language_layouts; i++)
	       if (stricmp(language_type, language_layouts[i]) == 0)
		  break;

	    if (i>=num_language_layouts) {
	       sprintf(b1, "(%s)", language_type);
	       scroller_proc(MSG_RADIO, NULL, 0);
	       alert("Warning: current language", b1, "not found in " SETUP_LANGUAGE_FILE, "Ok", NULL, 0, 0);
	       language_layouts[num_language_layouts] = malloc(strlen(language_type)+1);
	       strcpy(language_layouts[num_language_layouts], language_type);
	       language_names[num_language_layouts] = malloc(strlen(language_type)+1);
	       strcpy(language_names[num_language_layouts], language_type);
	       num_language_layouts++;
	    }

	    locale_dlg[3].d1 = i;
	 }

	 if ((num_keyboard_layouts > 0) || (num_language_layouts > 0))
	    activate_dialog(locale_dlg, locale_handler, FALSE);
	 break;

      case 5:
	 /* calibrate joystick */
	 joystick_dlg[2].d1 = 0;
	 get_joystick_drivers(&list, &list_size);

	 for (i=0; list[i].driver; i++) {
	    if (list[i].id == joy_type) {
	       joystick_dlg[2].d1 = i;
	       break;
	    }
	 }

	 activate_dialog(joystick_dlg, joystick_handler, FALSE);
	 break;

      case 6:
	 /* configure mouse */
	 dlg = mouse_dlg;

	 #ifdef ALLEGRO_LINUX
	    if (system_driver->id == SYSTEM_LINUX)
	       dlg = linux_mouse_dlg;
	 #endif

	 dlg[2].d1 = 0;
	 get_mouse_drivers(&list, &list_size);

	 for (i=0; list[i].driver; i++) {
	    if (list[i].id == mouse_type) {
	       dlg[2].d1 = i;
	       break;
	    }
	 }

	 activate_dialog(dlg, mouse_handler, FALSE);
	 break;

      case 7:
	 /* save settings and quit */
	 set_config_file(SETUP_CFG_FILE);
	 for (i=0; parameters[i].name; i++) {
	    if (parameters[i].value[0])
	       set_config_string("sound", parameters[i].name, parameters[i].value);
	    else
	       set_config_string("sound", parameters[i].name, " ");
	 }
	 set_config_string(NULL, "keyboard", keyboard_type);
	 set_config_string(NULL, "language", language_type);
	 save_joystick_data(NULL);
	 save_mouse_data();
	 return state_exit;

      default:
	 /* quit */
	 return state_exit;
   }

   return state_active;
}



#ifdef SETUP_EMBEDDED
   int setup_main()
#else
   int main()
#endif
{
   char buf[256];
   int i;

   #ifndef SETUP_EMBEDDED
      allegro_init();
      install_mouse();
      install_keyboard();
      install_timer();

      fade_out(4);
      set_gfx_mode(GFX_SAFE, SETUP_SCREEN_W, SETUP_SCREEN_H, 0, 0);
   #endif

   set_palette(black_palette);

   #ifdef SETUP_USE_DAT2S
      fixup_datafile(setup_data);
   #else
      setup_data = load_datafile("#");

      if (!setup_data) {
	 if (find_allegro_resource(buf, SETUP_DATA_FILE, NULL, NULL, NULL, NULL, NULL, sizeof(buf)) == 0)
	    setup_data = load_datafile(buf);
      }

      if (!setup_data) {
	 #ifdef SETUP_EMBEDDED
	    set_palette(default_palette);
	    alert("Error loading " SETUP_DATA_FILE, NULL, NULL, "OK", NULL, 13, 0);
	 #else
	    set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
	    allegro_message("Error loading " SETUP_DATA_FILE "\n");
	 #endif
	 return 1;
      }
   #endif

   set_mouse_range(0, 20, SCREEN_W-1, SCREEN_H-32);

   font = setup_data[SETUP_FONT].dat;

   memset(scroller_msg, ' ', SCREEN_W/8+2);

   buffer = create_bitmap(SCREEN_W, SCREEN_H);

   LOCK_VARIABLE(scroller_time);
   LOCK_FUNCTION(inc_scroller_time);
   install_int_ex(inc_scroller_time, BPS_TO_TIMER(160));

   set_config_file(SETUP_CFG_FILE);
   for (i=0; parameters[i].name; i++) {
      strcpy(parameters[i].value, get_config_string("sound", parameters[i].name, parameters[i].def));
      if (!parameters[i].value[0])
	 strcpy(parameters[i].value, parameters[i].def);
   }

   strcpy(keyboard_type, get_config_string(NULL, "keyboard", ""));
   for (i=0; keyboard_type[i]; i++)
      if (!uisspace(keyboard_type[i]))
	 break;

   if (!keyboard_type[i])
      strcpy(keyboard_type, "us");

   strcpy(language_type, get_config_string(NULL, "language", ""));
   for (i=0; language_type[i]; i++)
      if (!uisspace(language_type[i]))
	 break;

   if (!language_type[i])
      strcpy(language_type, "en");

   install_joystick(JOY_TYPE_AUTODETECT);

   find_sound_drivers();

   set_config_data("", 0);
   for (i=0; parameters[i].name; i++)
      set_config_string("sound", parameters[i].name, parameters[i].value);

   detect_sound();

   activate_dialog(main_dlg, main_handler, FALSE);
   dialog_count++;

   do {
   } while (update());

   destroy_bitmap(buffer);

   for (i=0; i<num_keyboard_layouts; i++) {
      free(keyboard_layouts[i]);
      free(keyboard_names[i]);
   }

   for (i=0; i<num_language_layouts; i++) {
      free(language_layouts[i]);
      free(language_names[i]);
   }

   set_mouse_range(0, 0, SCREEN_W-1, SCREEN_H-1);

   #ifndef SETUP_USE_DAT2S
      unload_datafile(setup_data);
   #endif

   remove_int(inc_scroller_time);

   return 0;
}

#ifndef SETUP_EMBEDDED
   END_OF_MAIN();
#endif

