foxygit / doom Log in
commit afa0c4c7979c1508605d0e79487f299cd03adda6
Merge: 095bc111 2c6e7b2f
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Sat Nov 21 16:36:46 2009 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Sat Nov 21 16:36:46 2009 +0000

    Merge from trunk.

    Subversion-branch: /branches/raven-branch
    Subversion-revision: 1737

 Makefile.am                               |   1 +
 acinclude.m4                              |  36 +++++++
 configure.in                              |  57 ++++++++----
 pcsound/pcsound.c                         |   2 +-
 pcsound/pcsound_sdl.c                     |  18 ++--
 pkg/Makefile.am                           |   3 +
 pkg/win32/GNUmakefile.am                  |  36 +++++++
 pkg/win32/README                          |   4 +
 pkg/wince/{Makefile.am => GNUmakefile.am} |   0
 src/d_iwad.c                              |   2 +-
 src/d_iwad.h                              |   2 +-
 src/deh_str.c                             |   4 +-
 src/doom/d_main.c                         |   4 +-
 src/doom/d_net.h                          |   2 +-
 src/doom/deh_defs.h                       |   2 +-
 src/doom/deh_main.c                       |   4 +-
 src/doom/deh_ptr.c                        |   2 +-
 src/doom/p_doors.c                        |  37 +++++++-
 src/doom/p_map.c                          |   3 +-
 src/doom/p_spec.c                         | 150 +++++++++++++++++++++++++++---
 src/doomtype.h                            |   2 +-
 src/i_joystick.c                          |   2 +-
 src/i_pcsound.c                           |  18 ++--
 src/i_sdlmusic.c                          |  74 ++++++++-------
 src/i_sdlsound.c                          |  16 ++--
 src/i_sound.c                             |  38 +++++---
 src/i_timer.c                             |   2 +-
 src/i_timer.h                             |   2 +-
 src/i_video.c                             | 102 ++++++++++++--------
 src/m_config.c                            |   9 +-
 src/m_misc.c                              |   8 ++
 src/m_misc.h                              |   1 +
 src/net_client.c                          |   4 +-
 src/net_common.c                          |   4 +-
 src/net_defs.h                            |   4 +-
 src/net_gui.c                             |   2 +-
 src/net_loop.c                            |   4 +-
 src/net_sdl.c                             |   2 +-
 src/net_server.c                          |  14 +--
 src/net_server.h                          |   2 +-
 src/setup/Makefile.am                     |   1 +
 src/setup/execute.c                       | 123 ++++++++++++++----------
 src/setup/joystick.c                      |   2 +-
 src/setup/mainmenu.c                      |   9 +-
 src/setup/multiplayer.c                   |   2 +-
 src/setup/sound.c                         |  29 ++++--
 src/w_merge.c                             |   2 +-
 textscreen/txt_desktop.c                  |   4 +-
 textscreen/txt_dropdown.c                 |   2 +-
 textscreen/txt_gui.c                      |   9 +-
 textscreen/txt_gui.h                      |   6 +-
 textscreen/txt_inputbox.c                 |   2 +-
 textscreen/txt_io.c                       |   6 +-
 textscreen/txt_io.h                       |   2 +-
 textscreen/txt_main.h                     |   2 +-
 textscreen/txt_scrollpane.c               |  76 ++++++++++++++-
 textscreen/txt_sdl.c                      |  49 ++++++++--
 textscreen/txt_table.c                    |  82 ++++++++++++++++
 textscreen/txt_table.h                    |  13 +++
 textscreen/txt_widget.c                   |   4 +-
 textscreen/txt_widget.h                   |   4 +-
 textscreen/txt_window_action.c            |   2 +-
 textscreen/txt_window_action.h            |   2 +-
 wince/Makefile.am                         |   2 +-
 wince/dummy.c                             |   8 ++
 65 files changed, 842 insertions(+), 280 deletions(-)

diff --cc Makefile.am
index cb3c4a89,dc3a40d0..15ad2252
--- a/Makefile.am
+++ b/Makefile.am
@@@ -52,7 -44,8 +52,8 @@@ EXTRA_DIST
  MAINTAINERCLEANFILES =  $(AUX_DIST_GEN)

  docdir=$(prefix)/share/doc/@PACKAGE@
 -SUBDIRS=wince textscreen pcsound src man setup
 +SUBDIRS=wince textscreen pcsound src man
+ DIST_SUBDIRS=pkg $(SUBDIRS)

  if HAVE_PYTHON

diff --cc configure.in
index 16c7df8a,60610ea1..0f4b48a0
--- a/configure.in
+++ b/configure.in
@@@ -113,17 -118,15 +128,19 @@@ Makefil
  wince/Makefile
  textscreen/Makefile
  textscreen/examples/Makefile
 -setup/Makefile
  man/Makefile
  src/Makefile
 +src/doom/Makefile
 +src/heretic/Makefile
 +src/hexen/Makefile
 +src/setup/Makefile
  pcsound/Makefile
- pkg/wince/Makefile
+ pkg/Makefile
+ pkg/wince/GNUmakefile
+ pkg/win32/GNUmakefile
  src/resource.rc
 +src/setup-res.rc
 +src/setup/setup-manifest.xml
  src/doom-screensaver.desktop
 -setup/setup-res.rc
  ])

diff --cc pkg/win32/GNUmakefile.am
index 00000000,edd57b6e..e9964b22
mode 000000,100644..100644
--- a/pkg/win32/GNUmakefile.am
+++ b/pkg/win32/GNUmakefile.am
@@@ -1,0 -1,34 +1,36 @@@
+
+ TOPLEVEL=../..
+
 -EXE_FILES=$(TOPLEVEL)/src/@PACKAGE_TARNAME@.exe        \
 -          $(TOPLEVEL)/src/chocolate-server.exe         \
 -          $(TOPLEVEL)/setup/chocolate-setup.exe
++EXE_FILES=$(TOPLEVEL)/src/@PROGRAM_PREFIX@doom.exe        \
++          $(TOPLEVEL)/src/@PROGRAM_PREFIX@-heretic.exe    \
++          $(TOPLEVEL)/src/@PROGRAM_PREFIX@-hexen.exe      \
++          $(TOPLEVEL)/src/@PROGRAM_PREFIX@-server.exe     \
++          $(TOPLEVEL)/src/@PROGRAM_PREFIX@-setup.exe
+
+ DLL_FILES=$(TOPLEVEL)/src/SDL.dll                      \
+           $(TOPLEVEL)/src/SDL_mixer.dll                \
+           $(TOPLEVEL)/src/SDL_net.dll
+
+ DOC_FILES=README       \
+           COPYING      \
+           ChangeLog    \
+           NEWS         \
+           BUGS         \
+           CMDLINE      \
+           TODO
+
+ noinst_DATA=@PACKAGE_TARNAME@-@PACKAGE_VERSION@-win32.zip
+
+ @PACKAGE_TARNAME@-@PACKAGE_VERSION@-win32.zip : staging
+ 	zip -j -r $@ staging/
+
+ staging: $(EXE_FILES) $(DLL_FILES) $(patsubst %,../../%,$(DOC_FILES))
+ 	rm -rf staging
+ 	mkdir staging
+ 	cp $(EXE_FILES) $(DLL_FILES) staging/
+ 	$(STRIP) staging/*.exe
+ 	for f in $(DOC_FILES); do                    \
+ 		cp $(TOPLEVEL)/$$f staging/$$f.txt;  \
+ 		unix2dos staging/$$f.txt;            \
+ 	done
+
diff --cc src/deh_str.c
index 3ede4508,9e8cc79d..0baaa7e8
--- a/src/deh_str.c
+++ b/src/deh_str.c
@@@ -160,22 -149,105 +160,22 @@@ static void DEH_AddToHashtable(deh_subs
      ++hash_table_entries;
  }

 -// Given a string length, find the maximum length of a
 -// string that can replace it.
 -
 -static int TXT_MaxStringLength(int len)
 -{
 -    // Enough bytes for the string and the NUL terminator
 -
 -    len += 1;
 -
 -    // All strings in doom.exe are on 4-byte boundaries, so we may be able
 -    // to support a slightly longer string.
 -    // Extend up to the next 4-byte boundary
 -
 -    len += (4 - (len % 4)) % 4;
 -
 -    // Less one for the NUL terminator.
 -
 -    return len - 1;
 -}
 -
 -static void DEH_TextInit(void)
 -{
 -    // init hash table
 -
 -    hash_table_entries = 0;
 -    hash_table_length = 16;
 -    hash_table = Z_Malloc(sizeof(deh_substitution_t *) * hash_table_length,
 -                          PU_STATIC, NULL);
 -    memset(hash_table, 0, sizeof(deh_substitution_t *) * hash_table_length);
 -}
 -
 -static void *DEH_TextStart(deh_context_t *context, char *line)
 +void DEH_AddStringReplacement(char *from_text, char *to_text)
  {
      deh_substitution_t *sub;
 -    int fromlen, tolen;
 -    int i;
 -
 -    if (sscanf(line, "Text %i %i", &fromlen, &tolen) != 2)
 -    {
 -        DEH_Warning(context, "Parse error on section start");
 -        return NULL;
 -    }
 -
 -    // Only allow string replacements that are possible in Vanilla Doom.
 -    // Chocolate Doom is unforgiving!
 -
 -    if (!deh_allow_long_strings && tolen > TXT_MaxStringLength(fromlen))
 -    {
 -        DEH_Error(context, "Replacement string is longer than the maximum "
 -                           "possible in doom.exe");
 -        return NULL;
 -    }
 -
 -    sub = Z_Malloc(sizeof(deh_substitution_t), PU_STATIC, NULL);
 -    sub->from_text = Z_Malloc(fromlen + 1, PU_STATIC, NULL);
 -    sub->to_text = Z_Malloc(tolen + 1, PU_STATIC, NULL);

-     // Initialise the hash table if this is the first time
 -    // read in the "from" text
++    // Initialize the hash table if this is the first time

 -    for (i=0; i<fromlen; ++i)
 +    if (hash_table_length < 0)
      {
 -        int c;
 -
 -        c = DEH_GetChar(context);
 -
 -        sub->from_text[i] = c;
 +        InitHashTable();
      }

 -    sub->from_text[fromlen] = '\0';
 -
 -    // read in the "to" text
 +    sub = Z_Malloc(sizeof(*sub), PU_STATIC, 0);

 -    for (i=0; i<tolen; ++i)
 -    {
 -        int c;
 -
 -        c = DEH_GetChar(context);
 -
 -        sub->to_text[i] = c;
 -    }
 -    sub->to_text[tolen] = '\0';
 +    sub->from_text = from_text;
 +    sub->to_text = to_text;

      DEH_AddToHashtable(sub);
 -
 -    return NULL;
 -}
 -
 -static void DEH_TextParseLine(deh_context_t *context, char *line, void *tag)
 -{
 -    // not used
  }

 -deh_section_t deh_section_text =
 -{
 -    "Text",
 -    DEH_TextInit,
 -    DEH_TextStart,
 -    DEH_TextParseLine,
 -    NULL,
 -    NULL,
 -};
 -
diff --cc src/doom/d_main.c
index 9fa9ae3b,3580c346..4e7812cc
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@@ -1696,12 -1447,10 +1696,12 @@@ void D_DoomMain (void
      P_Init ();

      printf (DEH_String("I_Init: Setting up machine state.\n"));
 -    I_Init ();
 +    I_CheckIsScreensaver();
 +    I_InitTimer();
 +    I_InitJoystick();

  #ifdef FEATURE_MULTIPLAYER
-     printf ("NET_Init: Initialise network subsystem.\n");
+     printf ("NET_Init: Init network subsystem.\n");
      NET_Init ();
  #endif

diff --cc src/doomtype.h
index e38d921f,0a5b5e38..b458c1cd
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@@ -29,41 -29,6 +29,41 @@@
  #ifndef __DOOMTYPE__
  #define __DOOMTYPE__

 +// #define macros to provide functions missing in Windows.
 +// Outside Windows, we use strings.h for str[n]casecmp.
 +
 +
 +#ifdef _WIN32
 +
 +#define snprintf _snprintf
 +#if _MSC_VER < 1400 /* not needed for Visual Studio 2008 */
 +#define vsnprintf _vsnprintf
 +#endif
 +#define strcasecmp stricmp
 +#define strncasecmp strnicmp
 +
 +#else
 +
 +#include <strings.h>
 +
 +#endif
 +
 +
 +//
 +// The packed attribute forces structures to be packed into the minimum
 +// space necessary.  If this is not done, the compiler may align structure
- // fields differently to optimise memory access, inflating the overall
++// fields differently to optimize memory access, inflating the overall
 +// structure size.  It is important to use the packed attribute on certain
 +// structures where alignment is important, particularly data read/written
 +// to disk.
 +//
 +
 +#ifdef __GNUC__
 +#define PACKEDATTR __attribute__((packed))
 +#else
 +#define PACKEDATTR
 +#endif
 +
  // Windows CE is missing some vital ANSI C functions.  We have to
  // use our own replacements.

diff --cc src/i_pcsound.c
index 36d4873e,dce7f2b6..907aeb54
--- a/src/i_pcsound.c
+++ b/src/i_pcsound.c
@@@ -36,10 -37,9 +36,10 @@@

  #include "pcsound.h"

- static boolean pcs_initialised = false;
+ static boolean pcs_initialized = false;

  static SDL_mutex *sound_lock;
 +static boolean use_sfx_prefix;

  static uint8_t *current_sound_lump = NULL;
  static uint8_t *current_sound_pos = NULL;
diff --cc src/i_sdlsound.c
index ff0ef10e,d51345c7..3273907e
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@@ -46,14 -46,16 +46,14 @@@
  #include "w_wad.h"
  #include "z_zone.h"

 -#include "doomdef.h"
 +#include "doomtype.h"

  #define LOW_PASS_FILTER
 -#define MAX_SOUND_SLICE_TIME 70 /* ms */
  #define NUM_CHANNELS 16

- static boolean sound_initialised = false;
+ static boolean sound_initialized = false;

 -static Mix_Chunk sound_chunks[NUMSFX];
 -static int channels_playing[NUM_CHANNELS];
 +static sfxinfo_t *channels_playing[NUM_CHANNELS];

  static int mixer_freq;
  static Uint16 mixer_format;
@@@ -306,12 -176,11 +306,12 @@@ static void ExpandSoundData_SDL(sfxinfo
      // Double up twice: 8 -> 16 bit and mono -> stereo

      expanded_length *= 4;
 -    destination->alen = expanded_length;
 -    destination->abuf
 -        = Z_Malloc(expanded_length, PU_STATIC, &destination->abuf);
 +
 +    // Allocate a chunk in which to expand the sound
 +
 +    chunk = AllocateChunk(sfxinfo, expanded_length);

-     // If we can, use the standard / optimised SDL conversion routines.
+     // If we can, use the standard / optimized SDL conversion routines.

      if (samplerate <= mixer_freq
       && ConvertibleRatio(samplerate, mixer_freq)
@@@ -678,11 -667,37 +678,11 @@@ static void I_SDL_ShutdownSound(void
      Mix_CloseAudio();
      SDL_QuitSubSystem(SDL_INIT_AUDIO);

-     sound_initialised = false;
+     sound_initialized = false;
  }

 -// Calculate slice size, based on MAX_SOUND_SLICE_TIME.
 -// The result must be a power of two.
 -
 -static int GetSliceSize(void)
 -{
 -    int limit;
 -    int n;

 -    limit = (snd_samplerate * MAX_SOUND_SLICE_TIME) / 1000;
 -
 -    // Try all powers of two, not exceeding the limit.
 -
 -    for (n=0;; ++n)
 -    {
 -        // 2^n <= limit < 2^n+1 ?
 -
 -        if ((1 << (n + 1)) > limit)
 -        {
 -            return (1 << n);
 -        }
 -    }
 -
 -    // Should never happen?
 -
 -    return 1024;
 -}
 -
 -static boolean I_SDL_InitSound(void)
 +static boolean I_SDL_InitSound(boolean _use_sfx_prefix)
  {
      int i;

diff --cc src/i_sound.c
index 34f4ea46,00000000..a9e953ce
mode 100644,000000..100644
--- a/src/i_sound.c
+++ b/src/i_sound.c
@@@ -1,420 -1,0 +1,430 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// 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.
 +//
 +// DESCRIPTION:  none
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
++#include "SDL_mixer.h"
++
 +#include "config.h"
 +#include "doomfeatures.h"
 +#include "doomtype.h"
 +
 +#include "i_sound.h"
 +#include "i_video.h"
 +#include "m_argv.h"
 +#include "m_config.h"
 +
- // Disable music on OSX by default; there are problems with SDL_mixer.
-
- #ifndef __APPLE__
- #define DEFAULT_MUSIC_DEVICE SNDDEVICE_SB
- #else
- #define DEFAULT_MUSIC_DEVICE SNDDEVICE_NONE
- #endif
-
 +// Sound sample rate to use for digital output (Hz)
 +
 +int snd_samplerate = 44100;
 +
 +// Low-level sound and music modules we are using
 +
 +static sound_module_t *sound_module;
 +static music_module_t *music_module;
 +
- int snd_musicdevice = DEFAULT_MUSIC_DEVICE;
++int snd_musicdevice = SNDDEVICE_SB;
 +int snd_sfxdevice = SNDDEVICE_SB;
 +
 +// Sound modules
 +
 +extern sound_module_t sound_sdl_module;
 +extern sound_module_t sound_pcsound_module;
 +extern music_module_t music_sdl_module;
 +
 +// DOS-specific options: These are unused but should be maintained
 +// so that the config file can be shared between chocolate
 +// doom and doom.exe
 +
 +static int snd_sbport = 0;
 +static int snd_sbirq = 0;
 +static int snd_sbdma = 0;
 +static int snd_mport = 0;
 +
 +// Compiled-in sound modules:
 +
 +static sound_module_t *sound_modules[] =
 +{
 +#ifdef FEATURE_SOUND
 +    &sound_sdl_module,
 +    &sound_pcsound_module,
 +#endif
 +    NULL,
 +};
 +
 +// Compiled-in music modules:
 +
 +static music_module_t *music_modules[] =
 +{
 +#ifdef FEATURE_SOUND
 +    &music_sdl_module,
 +#endif
 +    NULL,
 +};
 +
 +// Check if a sound device is in the given list of devices
 +
 +static boolean SndDeviceInList(snddevice_t device, snddevice_t *list,
 +                               int len)
 +{
 +    int i;
 +
 +    for (i=0; i<len; ++i)
 +    {
 +        if (device == list[i])
 +        {
 +            return true;
 +        }
 +    }
 +
 +    return false;
 +}
 +
- // Find and initialise a sound_module_t appropriate for the setting
++// Find and initialize a sound_module_t appropriate for the setting
 +// in snd_sfxdevice.
 +
 +static void InitSfxModule(boolean use_sfx_prefix)
 +{
 +    int i;
 +
 +    sound_module = NULL;
 +
 +    for (i=0; sound_modules[i] != NULL; ++i)
 +    {
 +        // Is the sfx device in the list of devices supported by
 +        // this module?
 +
 +        if (SndDeviceInList(snd_sfxdevice,
 +                            sound_modules[i]->sound_devices,
 +                            sound_modules[i]->num_sound_devices))
 +        {
-             // Initialise the module
++            // Initialize the module
 +
 +            if (sound_modules[i]->Init(use_sfx_prefix))
 +            {
 +                sound_module = sound_modules[i];
 +                return;
 +            }
 +        }
 +    }
 +}
 +
- // Initialise music according to snd_musicdevice.
++// Initialize music according to snd_musicdevice.
 +
 +static void InitMusicModule(void)
 +{
 +    int i;
 +
 +    music_module = NULL;
 +
 +    for (i=0; music_modules[i] != NULL; ++i)
 +    {
 +        // Is the music device in the list of devices supported
 +        // by this module?
 +
 +        if (SndDeviceInList(snd_musicdevice,
 +                            music_modules[i]->sound_devices,
 +                            music_modules[i]->num_sound_devices))
 +        {
-             // Initialise the module
++            // Initialize the module
 +
 +            if (music_modules[i]->Init())
 +            {
 +                music_module = music_modules[i];
 +                return;
 +            }
 +        }
 +    }
 +}
 +
 +//
 +// Initializes sound stuff, including volume
 +// Sets channels, SFX and music volume,
 +//  allocates channel buffer, sets S_sfx lookup.
 +//
 +
 +void I_InitSound(boolean use_sfx_prefix)
 +{
 +    boolean nosound, nosfx, nomusic;
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Disable all sound output.
 +    //
 +
 +    nosound = M_CheckParm("-nosound") > 0;
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Disable sound effects.
 +    //
 +
 +    nosfx = M_CheckParm("-nosfx") > 0;
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Disable music.
 +    //
 +
 +    nomusic = M_CheckParm("-nomusic") > 0;
 +
-     // Initialise the sound and music subsystems.
++    // Initialize the sound and music subsystems.
 +
 +    if (!nosound && !screensaver_mode)
 +    {
 +        if (!nosfx)
 +        {
 +            InitSfxModule(use_sfx_prefix);
 +        }
 +
 +        if (!nomusic)
 +        {
 +            InitMusicModule();
 +        }
 +    }
 +}
 +
 +void I_ShutdownSound(void)
 +{
 +    if (sound_module != NULL)
 +    {
 +        sound_module->Shutdown();
 +    }
 +
 +    if (music_module != NULL)
 +    {
 +        music_module->Shutdown();
 +    }
 +}
 +
 +int I_GetSfxLumpNum(sfxinfo_t *sfxinfo)
 +{
 +    if (sound_module != NULL)
 +    {
 +        return sound_module->GetSfxLumpNum(sfxinfo);
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +void I_UpdateSound(void)
 +{
 +    if (sound_module != NULL)
 +    {
 +        sound_module->Update();
 +    }
 +}
 +
 +static void CheckVolumeSeparation(int *sep, int *vol)
 +{
 +return;
 +    if (*sep < 0)
 +    {
 +        *sep = 0;
 +    }
 +    else if (*sep > 254)
 +    {
 +        *sep = 254;
 +    }
 +
 +    if (*vol < 0)
 +    {
 +        *vol = 0;
 +    }
 +    else if (*vol > 127)
 +    {
 +        *vol = 127;
 +    }
 +}
 +
 +void I_UpdateSoundParams(int channel, int vol, int sep)
 +{
 +    if (sound_module != NULL)
 +    {
 +        CheckVolumeSeparation(&vol, &sep);
 +        sound_module->UpdateSoundParams(channel, vol, sep);
 +    }
 +}
 +
 +int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep)
 +{
 +    if (sound_module != NULL)
 +    {
 +        CheckVolumeSeparation(&vol, &sep);
 +        return sound_module->StartSound(sfxinfo, channel, vol, sep);
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +void I_StopSound(int channel)
 +{
 +    if (sound_module != NULL)
 +    {
 +        sound_module->StopSound(channel);
 +    }
 +}
 +
 +boolean I_SoundIsPlaying(int channel)
 +{
 +    if (sound_module != NULL)
 +    {
 +        return sound_module->SoundIsPlaying(channel);
 +    }
 +    else
 +    {
 +        return false;
 +    }
 +}
 +
 +void I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds)
 +{
 +    if (sound_module != NULL && sound_module->CacheSounds != NULL)
 +    {
 +	sound_module->CacheSounds(sounds, num_sounds);
 +    }
 +}
 +
 +void I_InitMusic(void)
 +{
 +}
 +
 +void I_ShutdownMusic(void)
 +{
 +
 +}
 +
 +void I_SetMusicVolume(int volume)
 +{
 +    if (music_module != NULL)
 +    {
 +        music_module->SetMusicVolume(volume);
 +    }
 +}
 +
 +void I_PauseSong(void)
 +{
 +    if (music_module != NULL)
 +    {
 +        music_module->PauseMusic();
 +    }
 +}
 +
 +void I_ResumeSong(void)
 +{
 +    if (music_module != NULL)
 +    {
 +        music_module->ResumeMusic();
 +    }
 +}
 +
 +void *I_RegisterSong(void *data, int len)
 +{
 +    if (music_module != NULL)
 +    {
 +        return music_module->RegisterSong(data, len);
 +    }
 +    else
 +    {
 +        return NULL;
 +    }
 +}
 +
 +void I_UnRegisterSong(void *handle)
 +{
 +    if (music_module != NULL)
 +    {
 +        music_module->UnRegisterSong(handle);
 +    }
 +}
 +
 +void I_PlaySong(void *handle, boolean looping)
 +{
 +    if (music_module != NULL)
 +    {
 +        music_module->PlaySong(handle, looping);
 +    }
 +}
 +
 +void I_StopSong(void)
 +{
 +    if (music_module != NULL)
 +    {
 +        music_module->StopSong();
 +    }
 +}
 +
 +boolean I_MusicIsPlaying(void)
 +{
 +    if (music_module != NULL)
 +    {
 +        return music_module->MusicIsPlaying();
 +    }
 +    else
 +    {
 +        return false;
 +    }
 +}
 +
 +void I_BindSoundVariables(void)
 +{
 +    extern int use_libsamplerate;
 +
 +    M_BindVariable("snd_musicdevice",   &snd_musicdevice);
 +    M_BindVariable("snd_sfxdevice",     &snd_sfxdevice);
 +    M_BindVariable("snd_sbport",        &snd_sbport);
 +    M_BindVariable("snd_sbirq",         &snd_sbirq);
 +    M_BindVariable("snd_sbdma",         &snd_sbdma);
 +    M_BindVariable("snd_mport",         &snd_mport);
 +    M_BindVariable("snd_samplerate",    &snd_samplerate);
 +#ifdef FEATURE_SOUND
 +    M_BindVariable("use_libsamplerate", &use_libsamplerate);
 +#endif
++
++    // Before SDL_mixer version 1.2.11, MIDI music caused the game
++    // to crash when it looped.  If this is an old SDL_mixer version,
++    // disable MIDI.
++
++#ifdef __MACOSX__
++    {
++        const SDL_version *v = Mix_Linked_Version();
++
++        if (SDL_VERSIONNUM(v->major, v->minor, v->patch)
++          < SDL_VERSIONNUM(1, 2, 11))
++        {
++            snd_musicdevice = SNDDEVICE_NONE;
++        }
++    }
++#endif
 +}
 +
diff --cc src/i_timer.h
index 17390884,d90094c8..56810468
--- a/src/i_timer.h
+++ b/src/i_timer.h
@@@ -40,11 -38,8 +40,11 @@@ int I_GetTimeMS (void)
  // Pause for a specified number of ms
  void I_Sleep(int ms);

- // Initialise timer
+ // Initialize timer
  void I_InitTimer(void);

 +// Wait for vertical retrace or pause a bit.
 +void I_WaitVBL(int count);
 +
  #endif

diff --cc src/i_video.c
index 6f82e60a,481ee0ea..64a2e8eb
--- a/src/i_video.c
+++ b/src/i_video.c
@@@ -50,49 -50,9 +50,52 @@@
  #include "w_wad.h"
  #include "z_zone.h"

 +// Lookup table for mapping ASCII characters to their equivalent when
 +// shift is pressed on an American layout keyboard:
 +
 +static const char shiftxform[] =
 +{
 +    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
 +    11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
 +    21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
 +    31, ' ', '!', '"', '#', '$', '%', '&',
 +    '"', // shift-'
 +    '(', ')', '*', '+',
 +    '<', // shift-,
 +    '_', // shift--
 +    '>', // shift-.
 +    '?', // shift-/
 +    ')', // shift-0
 +    '!', // shift-1
 +    '@', // shift-2
 +    '#', // shift-3
 +    '$', // shift-4
 +    '%', // shift-5
 +    '^', // shift-6
 +    '&', // shift-7
 +    '*', // shift-8
 +    '(', // shift-9
 +    ':',
 +    ':', // shift-;
 +    '<',
 +    '+', // shift-=
 +    '>', '?', '@',
 +    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
 +    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
 +    '[', // shift-[
 +    '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK
 +    ']', // shift-]
 +    '"', '_',
 +    '\'', // shift-`
 +    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
 +    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
 +    '{', '|', '}', '~', 127
 +};
 +
 +
+ #define LOADING_DISK_W 16
+ #define LOADING_DISK_H 16
+
  // Non aspect ratio-corrected modes (direct multiples of 320x200)

  static screen_mode_t *screen_modes[] = {
@@@ -373,33 -282,23 +375,31 @@@ void I_EnableLoadingDisk(void

      disk = W_CacheLumpName(disk_name, PU_STATIC);

 +    // Draw the patch into a temporary buffer
 +
 +    tmpbuf = Z_Malloc(SCREENWIDTH * (disk->height + 1), PU_STATIC, NULL);
 +    V_UseBuffer(tmpbuf);
 +
-     disk_image_w = SHORT(disk->width);
-     disk_image_h = SHORT(disk->height);
-     V_DrawPatch(0, 0, disk);
+     // Draw the disk to the screen:

-     // Copy the disk into the disk_image buffer.
 -    V_DrawPatch(SCREENWIDTH - LOADING_DISK_W,
 -                SCREENHEIGHT - LOADING_DISK_H,
 -                0, disk);
++    V_DrawPatch(0, 0, disk);

-     disk_image = Z_Malloc(disk_image_w * disk_image_h, PU_STATIC, NULL);
-     saved_background = Z_Malloc(disk_image_w * disk_image_h, PU_STATIC, NULL);
+     disk_image = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL);
+     saved_background = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL);

-     for (y=0; y<disk_image_h; ++y)
+     for (y=0; y<LOADING_DISK_H; ++y)
      {
-         memcpy(disk_image + disk_image_w * y,
+         memcpy(disk_image + LOADING_DISK_W * y,
 -               screens[0] + SCREENWIDTH * (y + yoffset) + xoffset,
 +               tmpbuf + SCREENWIDTH * y,
-                disk_image_w);
+                LOADING_DISK_W);
      }

 +    // All done - free the screen buffer and restore the normal
 +    // video buffer.
 +
      W_ReleaseLumpName(disk_name);
 +    V_RestoreBuffer();
 +    Z_Free(tmpbuf);
  }

  //
@@@ -898,9 -754,12 +916,12 @@@ static void UpdateRect(int x1, int y1,

  void I_BeginRead(void)
  {
 -    byte *screenloc = screens[0]
++    byte *screenloc = I_VideoBuffer
+                     + (SCREENHEIGHT - LOADING_DISK_H) * SCREENWIDTH
+                     + (SCREENWIDTH - LOADING_DISK_W);
      int y;

-     if (!initialised || disk_image == NULL)
+     if (!initialized || disk_image == NULL)
          return;

      // save background and copy the disk image in
@@@ -924,9 -782,12 +944,12 @@@

  void I_EndRead(void)
  {
 -    byte *screenloc = screens[0]
++    byte *screenloc = I_VideoBuffer
+                     + (SCREENHEIGHT - LOADING_DISK_H) * SCREENWIDTH
+                     + (SCREENWIDTH - LOADING_DISK_W);
      int y;

-     if (!initialised || disk_image == NULL)
+     if (!initialized || disk_image == NULL)
          return;

      // save background and copy the disk image in
@@@ -1749,8 -1570,10 +1771,10 @@@ void I_InitGraphics(void
      // Set up title and icon.  Windows cares about the ordering; this
      // has to be done before the call to SDL_SetVideoMode.

 -    I_SetWindowCaption();
 +    I_InitWindowTitle();
+ #if !SDL_VERSION_ATLEAST(1, 3, 0)
 -    I_SetWindowIcon();
 +    I_InitWindowIcon();
+ #endif

      // Set the video mode.

@@@ -1874,31 -1699,6 +1898,31 @@@
          CenterMouse();
      }

-     initialised = true;
+     initialized = true;
 +
 +    // Call I_ShutdownGraphics on quit
 +
 +    I_AtExit(I_ShutdownGraphics, true);
 +}
 +
 +// Bind all variables controlling video options into the configuration
 +// file system.
 +
 +void I_BindVideoVariables(void)
 +{
 +    M_BindVariable("use_mouse",                 &usemouse);
 +    M_BindVariable("autoadjust_video_settings", &autoadjust_video_settings);
 +    M_BindVariable("fullscreen",                &fullscreen);
 +    M_BindVariable("aspect_ratio_correct",      &aspect_ratio_correct);
 +    M_BindVariable("startup_delay",             &startup_delay);
 +    M_BindVariable("screen_width",              &screen_width);
 +    M_BindVariable("screen_height",             &screen_height);
 +    M_BindVariable("grabmouse",                 &grabmouse);
 +    M_BindVariable("mouse_acceleration",        &mouse_acceleration);
 +    M_BindVariable("mouse_threshold",           &mouse_threshold);
 +    M_BindVariable("video_driver",              &video_driver);
 +    M_BindVariable("usegamma",                  &usegamma);
 +    M_BindVariable("vanilla_keyboard_mapping",  &vanilla_keyboard_mapping);
 +    M_BindVariable("novert",                    &novert);
  }

diff --cc src/m_config.c
index f70e112a,a2f3ac58..056861cc
--- a/src/m_config.c
+++ b/src/m_config.c
@@@ -1183,46 -1204,60 +1183,53 @@@ static void LoadDefaultCollection(defau

          // Find the setting in the list

 -        for (i=0; i<collection->numdefaults; ++i)
 +        def = SearchCollection(collection, defname);
 +
 +        if (def == NULL || !def->bound)
          {
 -            default_t *def = &collection->defaults[i];
 -            char *s;
 -            int intparm;
 -
 -            if (strcmp(defname, def->name) != 0)
 -            {
 -                // not this one
 -                continue;
 -            }
 -
 -            // parameter found
 -
 -            switch (def->type)
 -            {
 -                case DEFAULT_STRING:
 -                    s = strdup(strparm + 1);
 -                    s[strlen(s) - 1] = '\0';
 -                    * (char **) def->location = s;
 -                    break;
 -
 -                case DEFAULT_INT:
 -                    * (int *) def->location = ParseIntParameter(strparm);
 -                    break;
 -
 -                case DEFAULT_KEY:
 -
 -                    // translate scancodes read from config
 -                    // file (save the old value in untranslated)
 -
 -                    intparm = ParseIntParameter(strparm);
 -                    defaults[i].untranslated = intparm;
 -                    if (intparm >= 0 && intparm < 128)
 -                    {
 -                        intparm = scantokey[intparm];
 -                    }
 -                    else
 -                    {
 -                        intparm = 0;
 -                    }
 +            // Unknown variable?  Unbound variables are also treated
 +            // as unknown.
 +
 +            continue;
 +        }
 +
 +        // parameter found
 +
 +        switch (def->type)
 +        {
 +            case DEFAULT_STRING:
 +                s = strdup(strparm + 1);
 +                s[strlen(s) - 1] = '\0';
 +                * (char **) def->location = s;
 +                break;

 -                    defaults[i].original_translated = intparm;
 -                    * (int *) def->location = intparm;
 -                    break;
 +            case DEFAULT_INT:
 +                * (int *) def->location = ParseIntParameter(strparm);
 +                break;
 +
 +            case DEFAULT_KEY:

 -                case DEFAULT_FLOAT:
 -                    * (float *) def->location = (float) atof(strparm);
 -                    break;
 -            }
 +                // translate scancodes read from config
 +                // file (save the old value in untranslated)
 +
 +                intparm = ParseIntParameter(strparm);
 +                def->untranslated = intparm;
-                 intparm = scantokey[intparm];
++                if (intparm >= 0 && intparm < 128)
++                {
++                    intparm = scantokey[intparm];
++                }
++                else
++                {
++                    intparm = 0;
++                }

 -            // finish
 +                def->original_translated = intparm;
 +                * (int *) def->location = intparm;
 +                break;

 -            break;
 +            case DEFAULT_FLOAT:
 +                * (float *) def->location = (float) atof(strparm);
 +                break;
          }
      }

diff --cc src/m_misc.c
index 4f3becfb,9a5fb84a..5847f1a2
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@@ -199,51 -198,11 +199,59 @@@ char *M_TempFile(char *s
      return result;
  }

+ boolean M_StrToInt(const char *str, int *result)
+ {
+     return sscanf(str, " 0x%x", result) == 1
+         || sscanf(str, " 0X%x", result) == 1
+         || sscanf(str, " 0%o", result) == 1
+         || sscanf(str, " %d", result) == 1;
+ }
+
 +void M_ExtractFileBase(char *path, char *dest)
 +{
 +    char*	src;
 +    int		length;
 +
 +    src = path + strlen(path) - 1;
 +
 +    // back up until a \ or the start
 +    while (src != path && *(src - 1) != DIR_SEPARATOR)
 +    {
 +	src--;
 +    }
 +
 +    // copy up to eight characters
 +    memset(dest, 0, 8);
 +    length = 0;
 +
 +    while (*src != '\0' && *src != '.')
 +    {
 +        ++length;
 +
 +	if (length > 8)
 +        {
 +	    I_Error ("Filename base of %s >8 chars", path);
 +        }
 +
 +	*dest++ = toupper((int)*src++);
 +    }
 +}
 +
 +//---------------------------------------------------------------------------
 +//
 +// PROC M_ForceUppercase
 +//
 +// Change string to uppercase.
 +//
 +//---------------------------------------------------------------------------
 +
 +void M_ForceUppercase(char *text)
 +{
 +    char *p;
 +
 +    for (p = text; *p != '\0'; ++p)
 +    {
 +        *p = toupper(*p);
 +    }
 +}
 +
diff --cc src/m_misc.h
index ec70a405,0fe8e62e..c92ddde8
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@@ -39,9 -39,7 +39,10 @@@ void M_MakeDirectory(char *dir)
  char *M_TempFile(char *s);
  boolean M_FileExists(char *file);
  long M_FileLength(FILE *handle);
+ boolean M_StrToInt(const char *str, int *result);
 +void M_ExtractFileBase(char *path, char *dest);
 +void M_ForceUppercase(char *text);
 +

  #endif

diff --cc src/setup/Makefile.am
index 737dd278,00000000..58561b62
mode 100644,000000..100644
--- a/src/setup/Makefile.am
+++ b/src/setup/Makefile.am
@@@ -1,29 -1,0 +1,30 @@@
 +
 +gamesdir = $(prefix)/games
 +
 +AM_CFLAGS = @SDL_CFLAGS@                                           \
++            @SDLMIXER_CFLAGS@                                      \
 +            -I$(top_builddir)/textscreen -I..                      \
 +            -DINSTALL_DIR="\"$(gamesdir)\""
 +
 +noinst_LIBRARIES = libsetup.a
 +
 +SOURCE_FILES =                                  \
 +    compatibility.c   compatibility.h           \
 +    display.c         display.h                 \
 +    joystick.c        joystick.h                \
 +    keyboard.c        keyboard.h                \
 +    mainmenu.c                                  \
 +    mode.c            mode.h                    \
 +    mouse.c           mouse.h                   \
 +    multiplayer.c     multiplayer.h             \
 +    sound.c           sound.h                   \
 +    execute.c         execute.h                 \
 +    txt_joybinput.c   txt_joybinput.h           \
 +    txt_keyinput.c    txt_keyinput.h            \
 +    txt_mouseinput.c  txt_mouseinput.h
 +
 +libsetup_a_SOURCES = $(SOURCE_FILES)
 +
 +EXTRA_DIST=                                     \
 +    setup_icon.c
 +
diff --cc src/setup/mainmenu.c
index 3b79bc94,4ad1ac02..46e4b4e6
--- a/src/setup/mainmenu.c
+++ b/src/setup/mainmenu.c
@@@ -175,9 -138,13 +176,13 @@@ void MainMenu(void
      TXT_SetWindowAction(window, TXT_HORIZ_LEFT, quit_action);
  }

+ //
+ // Initialize all configuration variables, load config file, etc
+ //
+
  static void InitConfig(void)
  {
 -    M_ApplyPlatformDefaults();
 +    InitBindings();

      SetChatMacroDefaults();
      SetPlayerNameDefault();
diff --cc src/setup/sound.c
index 0361ac62,00000000..97037ee9
mode 100644,000000..100644
--- a/src/setup/sound.c
+++ b/src/setup/sound.c
@@@ -1,241 -1,0 +1,250 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 2006 Simon Howard
 +//
 +// 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.
 +//
 +
 +// Sound control menu
 +
 +#include <stdlib.h>
 +
++#include "SDL_mixer.h"
++
 +#include "textscreen.h"
 +#include "m_config.h"
 +
 +#include "i_sound.h"
 +#include "mode.h"
 +#include "sound.h"
 +
 +typedef enum
 +{
 +    SFXMODE_DISABLED,
 +    SFXMODE_DIGITAL,
 +    SFXMODE_PCSPEAKER,
 +    NUM_SFXMODES
 +} sfxmode_t;
 +
 +static char *sfxmode_strings[] =
 +{
 +    "Disabled",
 +    "Digital",
 +    "PC speaker"
 +};
 +
 +typedef enum
 +{
 +    MUSICMODE_DISABLED,
 +    MUSICMODE_MIDI,
 +    MUSICMODE_CD,
 +    NUM_MUSICMODES
 +} musicmode_t;
 +
 +static char *musicmode_strings[] =
 +{
 +    "Disabled",
 +    "MIDI",
 +    "CD audio"
 +};
 +
- // Disable MIDI music on OSX: there are problems with the native
- // MIDI code in SDL_mixer.
-
- #ifdef __MACOSX__
- #define DEFAULT_MUSIC_DEVICE SNDDEVICE_NONE
- #else
- #define DEFAULT_MUSIC_DEVICE SNDDEVICE_SB
- #endif
-
 +// Config file variables:
 +
 +int snd_sfxdevice = SNDDEVICE_SB;
- int snd_musicdevice = DEFAULT_MUSIC_DEVICE;
++int snd_musicdevice = SNDDEVICE_SB;
 +int snd_samplerate = 22050;
 +
 +static int numChannels = 8;
 +static int sfxVolume = 15;
 +static int musicVolume = 15;
 +static int use_libsamplerate = 0;
 +
 +// DOS specific variables: these are unused but should be maintained
 +// so that the config file can be shared between chocolate
 +// doom and doom.exe
 +
 +static int snd_sbport = 0;
 +static int snd_sbirq = 0;
 +static int snd_sbdma = 0;
 +static int snd_mport = 0;
 +
 +// GUI variables:
 +
 +static int snd_sfxmode;
 +static int snd_musicmode;
 +
 +static void UpdateSndDevices(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data))
 +{
 +    switch (snd_sfxmode)
 +    {
 +        case SFXMODE_DISABLED:
 +            snd_sfxdevice = SNDDEVICE_NONE;
 +            break;
 +        case SFXMODE_PCSPEAKER:
 +            snd_sfxdevice = SNDDEVICE_PCSPEAKER;
 +            break;
 +        case SFXMODE_DIGITAL:
 +            snd_sfxdevice = SNDDEVICE_SB;
 +            break;
 +    }
 +
 +    switch (snd_musicmode)
 +    {
 +        case MUSICMODE_DISABLED:
 +            snd_musicdevice = SNDDEVICE_NONE;
 +            break;
 +        case MUSICMODE_MIDI:
 +            snd_musicdevice = SNDDEVICE_SB;
 +            break;
 +        case MUSICMODE_CD:
 +            break;
 +    }
 +}
 +
 +void ConfigSound(void)
 +{
 +    txt_window_t *window;
 +    txt_table_t *sfx_table;
 +    txt_table_t *music_table;
 +    txt_dropdown_list_t *sfx_mode_control;
 +    txt_dropdown_list_t *music_mode_control;
 +    int num_sfx_modes, num_music_modes;
 +
 +    // Work out what sfx mode we are currently using:
 +
 +    if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
 +    {
 +        snd_sfxmode = SFXMODE_PCSPEAKER;
 +    }
 +    else if (snd_sfxdevice >= SNDDEVICE_SB)
 +    {
 +        snd_sfxmode = SFXMODE_DIGITAL;
 +    }
 +    else
 +    {
 +        snd_sfxmode = SFXMODE_DISABLED;
 +    }
 +
 +    // Is music enabled?
 +
 +    if (snd_musicdevice == SNDDEVICE_NONE)
 +    {
 +        snd_musicmode = MUSICMODE_DISABLED;
 +    }
 +    else if (snd_musicmode == SNDDEVICE_CD)
 +    {
 +        snd_musicmode = MUSICMODE_CD;
 +    }
 +    else
 +    {
 +        snd_musicmode = MUSICMODE_MIDI;
 +    }
 +
 +    // Doom has PC speaker sound effects, but others do not:
 +
 +    if (gamemission == doom)
 +    {
 +        num_sfx_modes = NUM_SFXMODES;
 +    }
 +    else
 +    {
 +        num_sfx_modes = NUM_SFXMODES - 1;
 +    }
 +
 +    // Hexen has CD audio; others do not.
 +
 +    if (gamemission == hexen)
 +    {
 +        num_music_modes = NUM_MUSICMODES;
 +    }
 +    else
 +    {
 +        num_music_modes = NUM_MUSICMODES - 1;
 +    }
 +
 +    // Build the window
 +
 +    window = TXT_NewWindow("Sound configuration");
 +
 +    TXT_AddWidgets(window,
 +               TXT_NewSeparator("Sound effects"),
 +               sfx_table = TXT_NewTable(2),
 +               TXT_NewSeparator("Music"),
 +               music_table = TXT_NewTable(2),
 +               NULL);
 +
 +    TXT_SetColumnWidths(sfx_table, 20, 5);
 +
 +    TXT_AddWidgets(sfx_table,
 +                   TXT_NewLabel("Sound effects"),
 +                   sfx_mode_control = TXT_NewDropdownList(&snd_sfxmode,
 +                                                          sfxmode_strings,
 +                                                          num_sfx_modes),
 +                   TXT_NewLabel("Sound channels"),
 +                   TXT_NewSpinControl(&numChannels, 1, 8),
 +                   TXT_NewLabel("SFX volume"),
 +                   TXT_NewSpinControl(&sfxVolume, 0, 15),
 +                   NULL);
 +
 +    TXT_SetColumnWidths(music_table, 20, 5);
 +
 +    TXT_AddWidgets(music_table,
 +                   TXT_NewLabel("Music"),
 +                   music_mode_control = TXT_NewDropdownList(&snd_musicmode,
 +                                                            musicmode_strings,
 +                                                            num_music_modes),
 +                   TXT_NewLabel("Music volume"),
 +                   TXT_NewSpinControl(&musicVolume, 0, 15),
 +                   NULL);
 +
 +    TXT_SignalConnect(sfx_mode_control, "changed", UpdateSndDevices, NULL);
 +    TXT_SignalConnect(music_mode_control, "changed", UpdateSndDevices, NULL);
 +
 +}
 +
 +void BindSoundVariables(void)
 +{
 +    M_BindVariable("snd_sfxdevice",       &snd_sfxdevice);
 +    M_BindVariable("snd_musicdevice",     &snd_musicdevice);
 +    M_BindVariable("snd_channels",        &numChannels);
 +    M_BindVariable("sfx_volume",          &sfxVolume);
 +    M_BindVariable("music_volume",        &musicVolume);
 +    M_BindVariable("snd_samplerate",      &snd_samplerate);
 +    M_BindVariable("use_libsamplerate",   &use_libsamplerate);
 +
 +    M_BindVariable("snd_sbport",          &snd_sbport);
 +    M_BindVariable("snd_sbirq",          &snd_sbirq);
 +    M_BindVariable("snd_sbdma",          &snd_sbdma);
 +    M_BindVariable("snd_mport",          &snd_mport);
++
++    // Before SDL_mixer version 1.2.11, MIDI music caused the game
++    // to crash when it looped.  If this is an old SDL_mixer version,
++    // disable MIDI.
++
++#ifdef __MACOSX__
++    {
++        const SDL_version *v = Mix_Linked_Version();
++
++        if (SDL_VERSIONNUM(v->major, v->minor, v->patch)
++          < SDL_VERSIONNUM(1, 2, 11))
++        {
++            snd_musicdevice = SNDDEVICE_NONE;
++        }
++    }
++#endif
 +}
 +