foxygit / doom Log in
commit 22fc405736dc4796958de221c07d52432f1b271b
Merge: f391d85c 64918568
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Tue Aug 31 19:26:32 2010 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Tue Aug 31 19:26:32 2010 +0000

    Merge from trunk.

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

 ChangeLog                  |   78 +++
 INSTALL                    |  159 +++--
 NEWS                       |   40 +-
 README                     |   55 +-
 codeblocks/config.h        |    6 +-
 codeblocks/game-res.rc     |   10 +-
 codeblocks/setup-res.rc    |    8 +-
 configure.in               |    3 +-
 msvc/config.h              |    6 +-
 msvc/win32.rc              |   10 +-
 opl/Makefile.am            |    2 +-
 opl/dbopl.c                | 1602 ++++++++++++++++++++++++++++++++++++++++++++
 opl/dbopl.h                |  203 ++++++
 opl/fmopl.c                | 1155 --------------------------------
 opl/fmopl.h                |  167 -----
 opl/opl_sdl.c              |  158 +++--
 pkg/config.make.in         |    1 +
 pkg/osx/GNUmakefile        |    4 +
 rpm.spec.in                |    5 +-
 src/doom/d_main.c          |    6 +
 src/doom/g_game.c          |  184 ++++-
 src/doom/hu_stuff.c        |   12 +-
 src/doom/r_draw.c          |  111 +--
 src/heretic/d_main.c       |    1 +
 src/hexen/h2_main.c        |    1 +
 src/i_scale.c              |   14 +-
 src/i_sdlsound.c           |   72 +-
 src/i_sound.c              |    5 +
 src/i_video.c              |  281 +++++---
 src/i_video.h              |    4 +-
 src/m_config.c             |  132 +++-
 src/m_controls.c           |   47 +-
 src/m_controls.h           |   17 +-
 src/setup/joystick.c       |    2 +
 src/setup/keyboard.c       |   27 +-
 src/setup/keyboard.h       |   53 --
 src/setup/mainmenu.c       |   60 ++
 src/setup/mouse.c          |    9 +-
 src/setup/multiplayer.c    |   35 +
 src/setup/sound.c          |    1 +
 src/setup/txt_keyinput.c   |    2 +-
 src/setup/txt_mouseinput.c |    4 +-
 src/z_zone.c               |    3 +-
 43 files changed, 3015 insertions(+), 1740 deletions(-)

diff --cc src/doom/d_main.c
index 3fee8439,671c4e9c..f3a4b037
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@@ -340,73 -387,7 +340,79 @@@ void D_Display (void
      } while (!done);
  }

 +//
 +// Add configuration file variable bindings.
 +//
 +
 +void D_BindVariables(void)
 +{
 +    int i;
 +
 +    M_ApplyPlatformDefaults();
 +
 +    I_BindVideoVariables();
 +    I_BindJoystickVariables();
 +    I_BindSoundVariables();
 +
 +    M_BindBaseControls();
 +    M_BindWeaponControls();
 +    M_BindMapControls();
 +    M_BindMenuControls();
++    M_BindChatControls(MAXPLAYERS);
++
++    key_multi_msgplayer[0] = HUSTR_KEYGREEN;
++    key_multi_msgplayer[1] = HUSTR_KEYINDIGO;
++    key_multi_msgplayer[2] = HUSTR_KEYBROWN;
++    key_multi_msgplayer[3] = HUSTR_KEYRED;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    NET_BindVariables();
 +#endif
 +
 +    M_BindVariable("mouse_sensitivity",      &mouseSensitivity);
 +    M_BindVariable("sfx_volume",             &sfxVolume);
 +    M_BindVariable("music_volume",           &musicVolume);
 +    M_BindVariable("show_messages",          &showMessages);
 +    M_BindVariable("screenblocks",           &screenblocks);
 +    M_BindVariable("detaillevel",            &detailLevel);
 +    M_BindVariable("snd_channels",           &snd_channels);
 +    M_BindVariable("vanilla_savegame_limit", &vanilla_savegame_limit);
 +    M_BindVariable("vanilla_demo_limit",     &vanilla_demo_limit);
 +    M_BindVariable("show_endoom",            &show_endoom);
 +
 +    // Multiplayer chat macros
 +
 +    for (i=0; i<10; ++i)
 +    {
 +        char buf[12];
 +
 +        sprintf(buf, "chatmacro%i", i);
 +        M_BindVariable(buf, &chat_macros[i]);
 +    }
 +}
 +
 +//
 +// D_GrabMouseCallback
 +//
 +// Called to determine whether to grab the mouse pointer
 +//
 +
 +boolean D_GrabMouseCallback(void)
 +{
 +    // Drone players don't need mouse focus
 +
 +    if (drone)
 +        return false;

 +    // when menu is active or game is paused, release the mouse
 +
 +    if (menuactive || paused)
 +        return false;
 +
 +    // only grab mouse when playing levels (but not demos)
 +
 +    return (gamestate == GS_LEVEL) && !demoplayback;
 +}

  //
  //  D_DoomLoop
diff --cc src/heretic/d_main.c
index 901d763d,00000000..169a86ea
mode 100644,000000..100644
--- a/src/heretic/d_main.c
+++ b/src/heretic/d_main.c
@@@ -1,1074 -1,0 +1,1075 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 1993-2008 Raven Software
 +// Copyright(C) 2008 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.
 +//
 +//-----------------------------------------------------------------------------
 +
 +// D_main.c
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "txt_main.h"
 +#include "txt_io.h"
 +
 +#include "config.h"
 +#include "ct_chat.h"
 +#include "doomdef.h"
 +#include "deh_main.h"
 +#include "d_iwad.h"
 +#include "i_endoom.h"
 +#include "i_joystick.h"
 +#include "i_sound.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "m_argv.h"
 +#include "m_config.h"
 +#include "m_controls.h"
 +#include "p_local.h"
 +#include "s_sound.h"
 +#include "w_main.h"
 +#include "v_video.h"
 +
 +#define STARTUP_WINDOW_X 17
 +#define STARTUP_WINDOW_Y 7
 +
 +GameMission_t gamemission = heretic;
 +GameMode_t gamemode = indetermined;
 +char *gamedescription = "unknown";
 +
 +boolean nomonsters;             // checkparm of -nomonsters
 +boolean respawnparm;            // checkparm of -respawn
 +boolean debugmode;              // checkparm of -debug
 +boolean ravpic;                 // checkparm of -ravpic
 +boolean cdrom;                  // true if cd-rom mode active
 +boolean singletics;             // debug flag to cancel adaptiveness
 +boolean noartiskip;             // whether shift-enter skips an artifact
 +
 +skill_t startskill;
 +int startepisode;
 +int startmap;
 +int UpdateState;
 +static int graphical_startup = 1;
 +static boolean using_graphical_startup;
 +boolean autostart;
 +extern boolean automapactive;
 +
 +boolean advancedemo;
 +
 +FILE *debugfile;
 +
 +static int show_endoom = 1;
 +
 +void D_CheckNetGame(void);
 +void D_ProcessEvents(void);
 +void G_BuildTiccmd(ticcmd_t * cmd);
 +void D_DoAdvanceDemo(void);
 +void D_PageDrawer(void);
 +void D_AdvanceDemo(void);
 +void F_Drawer(void);
 +boolean F_Responder(event_t * ev);
 +
 +//---------------------------------------------------------------------------
 +//
 +// PROC D_ProcessEvents
 +//
 +// Send all the events of the given timestamp down the responder chain.
 +//
 +//---------------------------------------------------------------------------
 +
 +void D_ProcessEvents(void)
 +{
 +    event_t *ev;
 +
 +    while ((ev = D_PopEvent()) != NULL)
 +    {
 +        if (F_Responder(ev))
 +        {
 +            continue;
 +        }
 +        if (MN_Responder(ev))
 +        {
 +            continue;
 +        }
 +        G_Responder(ev);
 +    }
 +}
 +
 +//---------------------------------------------------------------------------
 +//
 +// PROC DrawMessage
 +//
 +//---------------------------------------------------------------------------
 +
 +void DrawMessage(void)
 +{
 +    player_t *player;
 +
 +    player = &players[consoleplayer];
 +    if (player->messageTics <= 0 || !player->message)
 +    {                           // No message
 +        return;
 +    }
 +    MN_DrTextA(player->message, 160 - MN_TextAWidth(player->message) / 2, 1);
 +}
 +
 +//---------------------------------------------------------------------------
 +//
 +// PROC D_Display
 +//
 +// Draw current display, possibly wiping it from the previous.
 +//
 +//---------------------------------------------------------------------------
 +
 +void R_ExecuteSetViewSize(void);
 +
 +extern boolean finalestage;
 +
 +void D_Display(void)
 +{
 +    extern boolean MenuActive;
 +    extern boolean askforquit;
 +
 +    // Change the view size if needed
 +    if (setsizeneeded)
 +    {
 +        R_ExecuteSetViewSize();
 +    }
 +
 +//
 +// do buffered drawing
 +//
 +    switch (gamestate)
 +    {
 +        case GS_LEVEL:
 +            if (!gametic)
 +                break;
 +            if (automapactive)
 +                AM_Drawer();
 +            else
 +                R_RenderPlayerView(&players[displayplayer]);
 +            CT_Drawer();
 +            UpdateState |= I_FULLVIEW;
 +            SB_Drawer();
 +            break;
 +        case GS_INTERMISSION:
 +            IN_Drawer();
 +            break;
 +        case GS_FINALE:
 +            F_Drawer();
 +            break;
 +        case GS_DEMOSCREEN:
 +            D_PageDrawer();
 +            break;
 +    }
 +
 +    if (paused && !MenuActive && !askforquit)
 +    {
 +        if (!netgame)
 +        {
 +            V_DrawPatch(160, viewwindowy + 5, W_CacheLumpName(DEH_String("PAUSED"),
 +                                                              PU_CACHE));
 +        }
 +        else
 +        {
 +            V_DrawPatch(160, 70, W_CacheLumpName(DEH_String("PAUSED"), PU_CACHE));
 +        }
 +    }
 +    // Handle player messages
 +    DrawMessage();
 +
 +    // Menu drawing
 +    MN_Drawer();
 +
 +    // Send out any new accumulation
 +    NetUpdate();
 +
 +    // Flush buffered stuff to screen
 +    I_FinishUpdate();
 +}
 +
 +//
 +// D_GrabMouseCallback
 +//
 +// Called to determine whether to grab the mouse pointer
 +//
 +
 +boolean D_GrabMouseCallback(void)
 +{
 +    // when menu is active or game is paused, release the mouse
 +
 +    if (MenuActive || paused)
 +        return false;
 +
 +    // only grab mouse when playing levels (but not demos)
 +
 +    return (gamestate == GS_LEVEL) && !demoplayback;
 +}
 +
 +//---------------------------------------------------------------------------
 +//
 +// PROC D_DoomLoop
 +//
 +//---------------------------------------------------------------------------
 +
 +void D_DoomLoop(void)
 +{
 +    if (M_CheckParm("-debugfile"))
 +    {
 +        char filename[20];
 +        sprintf(filename, "debug%i.txt", consoleplayer);
 +        debugfile = fopen(filename, "w");
 +    }
 +    I_SetWindowTitle(gamedescription);
 +    I_InitGraphics();
 +    I_SetGrabMouseCallback(D_GrabMouseCallback);
 +
 +    while (1)
 +    {
 +        // Frame syncronous IO operations
 +        I_StartFrame();
 +
 +        // Process one or more tics
 +        if (singletics)
 +        {
 +            I_StartTic();
 +            D_ProcessEvents();
 +            G_BuildTiccmd(&netcmds[consoleplayer][maketic % BACKUPTICS]);
 +            if (advancedemo)
 +                D_DoAdvanceDemo();
 +            G_Ticker();
 +            gametic++;
 +            maketic++;
 +        }
 +        else
 +        {
 +            // Will run at least one tic
 +            TryRunTics();
 +        }
 +
 +        // Move positional sounds
 +        S_UpdateSounds(players[consoleplayer].mo);
 +        D_Display();
 +    }
 +}
 +
 +/*
 +===============================================================================
 +
 +						DEMO LOOP
 +
 +===============================================================================
 +*/
 +
 +int demosequence;
 +int pagetic;
 +char *pagename;
 +
 +
 +/*
 +================
 +=
 += D_PageTicker
 +=
 += Handles timing for warped projection
 +=
 +================
 +*/
 +
 +void D_PageTicker(void)
 +{
 +    if (--pagetic < 0)
 +        D_AdvanceDemo();
 +}
 +
 +
 +/*
 +================
 +=
 += D_PageDrawer
 +=
 +================
 +*/
 +
 +extern boolean MenuActive;
 +
 +void D_PageDrawer(void)
 +{
 +    V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE));
 +    if (demosequence == 1)
 +    {
 +        V_DrawPatch(4, 160, W_CacheLumpName(DEH_String("ADVISOR"), PU_CACHE));
 +    }
 +    UpdateState |= I_FULLSCRN;
 +}
 +
 +/*
 +=================
 +=
 += D_AdvanceDemo
 +=
 += Called after each demo or intro demosequence finishes
 +=================
 +*/
 +
 +void D_AdvanceDemo(void)
 +{
 +    advancedemo = true;
 +}
 +
 +void D_DoAdvanceDemo(void)
 +{
 +    players[consoleplayer].playerstate = PST_LIVE;      // don't reborn
 +    advancedemo = false;
 +    usergame = false;           // can't save / end game here
 +    paused = false;
 +    gameaction = ga_nothing;
 +    demosequence = (demosequence + 1) % 7;
 +    switch (demosequence)
 +    {
 +        case 0:
 +            pagetic = 210;
 +            gamestate = GS_DEMOSCREEN;
 +            pagename = DEH_String("TITLE");
 +            S_StartSong(mus_titl, false);
 +            break;
 +        case 1:
 +            pagetic = 140;
 +            gamestate = GS_DEMOSCREEN;
 +            pagename = DEH_String("TITLE");
 +            break;
 +        case 2:
 +            BorderNeedRefresh = true;
 +            UpdateState |= I_FULLSCRN;
 +            G_DeferedPlayDemo(DEH_String("demo1"));
 +            break;
 +        case 3:
 +            pagetic = 200;
 +            gamestate = GS_DEMOSCREEN;
 +            pagename = DEH_String("CREDIT");
 +            break;
 +        case 4:
 +            BorderNeedRefresh = true;
 +            UpdateState |= I_FULLSCRN;
 +            G_DeferedPlayDemo(DEH_String("demo2"));
 +            break;
 +        case 5:
 +            pagetic = 200;
 +            gamestate = GS_DEMOSCREEN;
 +            if (gamemode == shareware)
 +            {
 +                pagename = DEH_String("ORDER");
 +            }
 +            else
 +            {
 +                pagename = DEH_String("CREDIT");
 +            }
 +            break;
 +        case 6:
 +            BorderNeedRefresh = true;
 +            UpdateState |= I_FULLSCRN;
 +            G_DeferedPlayDemo(DEH_String("demo3"));
 +            break;
 +    }
 +}
 +
 +
 +/*
 +=================
 +=
 += D_StartTitle
 +=
 +=================
 +*/
 +
 +void D_StartTitle(void)
 +{
 +    gameaction = ga_nothing;
 +    demosequence = -1;
 +    D_AdvanceDemo();
 +}
 +
 +
 +/*
 +==============
 +=
 += D_CheckRecordFrom
 +=
 += -recordfrom <savegame num> <demoname>
 +==============
 +*/
 +
 +void D_CheckRecordFrom(void)
 +{
 +    int p;
 +    char file[256];
 +
 +    p = M_CheckParm("-recordfrom");
 +    if (!p || p > myargc - 2)
 +        return;
 +
 +    if (cdrom)
 +    {
 +        sprintf(file, SAVEGAMENAMECD "%c.hsg", myargv[p + 1][0]);
 +    }
 +    else
 +    {
 +        sprintf(file, SAVEGAMENAME "%c.hsg", myargv[p + 1][0]);
 +    }
 +    G_LoadGame(file);
 +    G_DoLoadGame();             // load the gameskill etc info from savegame
 +
 +    G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p + 2]);
 +    D_DoomLoop();               // never returns
 +}
 +
 +/*
 +===============
 +=
 += D_AddFile
 +=
 +===============
 +*/
 +
 +// MAPDIR should be defined as the directory that holds development maps
 +// for the -wart # # command
 +
 +#define MAPDIR "\\data\\"
 +
 +#define SHAREWAREWADNAME "heretic1.wad"
 +
 +char *iwadfile;
 +
 +char *basedefault = "heretic.cfg";
 +
 +void wadprintf(void)
 +{
 +    if (debugmode)
 +    {
 +        return;
 +    }
 +    // haleyjd FIXME: convert to textscreen code?
 +#ifdef __WATCOMC__
 +    _settextposition(23, 2);
 +    _setbkcolor(1);
 +    _settextcolor(0);
 +    _outtext(exrnwads);
 +    _settextposition(24, 2);
 +    _outtext(exrnwads2);
 +#endif
 +}
 +
 +boolean D_AddFile(char *file)
 +{
 +    wad_file_t *handle;
 +
 +    printf("  adding %s\n", file);
 +
 +    handle = W_AddFile(file);
 +
 +    return handle != NULL;
 +}
 +
 +//==========================================================
 +//
 +//  Startup Thermo code
 +//
 +//==========================================================
 +#define MSG_Y       9
 +#define THERM_X     14
 +#define THERM_Y     14
 +
 +int thermMax;
 +int thermCurrent;
 +char smsg[80];                  // status bar line
 +
 +//
 +//  Heretic startup screen shit
 +//
 +
 +static int startup_line = STARTUP_WINDOW_Y;
 +
 +void hprintf(char *string)
 +{
 +    if (using_graphical_startup)
 +    {
 +        TXT_BGColor(TXT_COLOR_CYAN, 0);
 +        TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 +
 +        TXT_GotoXY(STARTUP_WINDOW_X, startup_line);
 +        ++startup_line;
 +        TXT_Puts(string);
 +
 +        TXT_UpdateScreen();
 +    }
 +
 +    // haleyjd: shouldn't be WATCOMC-only
 +    if (debugmode)
 +        puts(string);
 +}
 +
 +void drawstatus(void)
 +{
 +    int i;
 +
 +    TXT_GotoXY(1, 24);
 +    TXT_BGColor(TXT_COLOR_BLUE, 0);
 +    TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 +
 +    for (i=0; smsg[i] != '\0'; ++i)
 +    {
 +        TXT_PutChar(smsg[i]);
 +    }
 +}
 +
 +void status(char *string)
 +{
 +    if (using_graphical_startup)
 +    {
 +        strcat(smsg, string);
 +        drawstatus();
 +    }
 +}
 +
 +void DrawThermo(void)
 +{
 +    static int last_progress = -1;
 +    int progress;
 +    int i;
 +
 +    if (!using_graphical_startup)
 +    {
 +        return;
 +    }
 +
 +#if 0
 +    progress = (98 * thermCurrent) / thermMax;
 +    screen = (char *) 0xb8000 + (THERM_Y * 160 + THERM_X * 2);
 +    for (i = 0; i < progress / 2; i++)
 +    {
 +        switch (i)
 +        {
 +            case 4:
 +            case 9:
 +            case 14:
 +            case 19:
 +            case 29:
 +            case 34:
 +            case 39:
 +            case 44:
 +                *screen++ = 0xb3;
 +                *screen++ = (THERMCOLOR << 4) + 15;
 +                break;
 +            case 24:
 +                *screen++ = 0xba;
 +                *screen++ = (THERMCOLOR << 4) + 15;
 +                break;
 +            default:
 +                *screen++ = 0xdb;
 +                *screen++ = 0x40 + THERMCOLOR;
 +                break;
 +        }
 +    }
 +    if (progress & 1)
 +    {
 +        *screen++ = 0xdd;
 +        *screen++ = 0x40 + THERMCOLOR;
 +    }
 +#else
 +
 +    // No progress? Don't update the screen.
 +
 +    progress = (50 * thermCurrent) / thermMax + 2;
 +
 +    if (last_progress == progress)
 +    {
 +        return;
 +    }
 +
 +    last_progress = progress;
 +
 +    TXT_GotoXY(THERM_X, THERM_Y);
 +
 +    TXT_FGColor(TXT_COLOR_BRIGHT_GREEN);
 +    TXT_BGColor(TXT_COLOR_GREEN, 0);
 +
 +    for (i = 0; i < progress; i++)
 +    {
 +        TXT_PutChar(0xdb);
 +    }
 +
 +    TXT_UpdateScreen();
 +#endif
 +}
 +
 +void initStartup(void)
 +{
 +    byte *textScreen;
 +    byte *loading;
 +
 +    if (!graphical_startup || debugmode)
 +    {
 +        using_graphical_startup = false;
 +        return;
 +    }
 +
 +    if (!TXT_Init())
 +    {
 +        using_graphical_startup = false;
 +        return;
 +    }
 +
 +    // Blit main screen
 +    textScreen = TXT_GetScreenData();
 +    loading = W_CacheLumpName(DEH_String("LOADING"), PU_CACHE);
 +    memcpy(textScreen, loading, 4000);
 +
 +    // Print version string
 +
 +    TXT_BGColor(TXT_COLOR_RED, 0);
 +    TXT_FGColor(TXT_COLOR_YELLOW);
 +    TXT_GotoXY(46, 2);
 +    TXT_Puts(HERETIC_VERSION_TEXT);
 +
 +    TXT_UpdateScreen();
 +
 +    using_graphical_startup = true;
 +}
 +
 +static void finishStartup(void)
 +{
 +    if (using_graphical_startup)
 +    {
 +        TXT_Shutdown();
 +    }
 +}
 +
 +char tmsg[300];
 +void tprintf(char *msg, int initflag)
 +{
 +    // haleyjd FIXME: convert to textscreen code?
 +#ifdef __WATCOMC__
 +    char temp[80];
 +    int start;
 +    int add;
 +    int i;
 +
 +    if (initflag)
 +        tmsg[0] = 0;
 +    strcat(tmsg, msg);
 +    blitStartup();
 +    DrawThermo();
 +    _setbkcolor(4);
 +    _settextcolor(15);
 +    for (add = start = i = 0; i <= strlen(tmsg); i++)
 +        if ((tmsg[i] == '\n') || (!tmsg[i]))
 +        {
 +            memset(temp, 0, 80);
 +            strncpy(temp, tmsg + start, i - start);
 +            _settextposition(MSG_Y + add, 40 - strlen(temp) / 2);
 +            _outtext(temp);
 +            start = i + 1;
 +            add++;
 +        }
 +    _settextposition(25, 1);
 +    drawstatus();
 +#else
 +    printf("%s", msg);
 +#endif
 +}
 +
 +// haleyjd: moved up, removed WATCOMC code
 +void CleanExit(void)
 +{
 +    DEH_printf("Exited from HERETIC.\n");
 +    exit(1);
 +}
 +
 +void CheckAbortStartup(void)
 +{
 +    // haleyjd: removed WATCOMC
 +    // haleyjd FIXME: this should actually work in text mode too, but how to
 +    // get input before SDL video init?
 +    if(using_graphical_startup)
 +    {
 +        if(TXT_GetChar() == 27)
 +            CleanExit();
 +    }
 +}
 +
 +void IncThermo(void)
 +{
 +    thermCurrent++;
 +    DrawThermo();
 +    CheckAbortStartup();
 +}
 +
 +void InitThermo(int max)
 +{
 +    thermMax = max;
 +    thermCurrent = 0;
 +}
 +
 +//
 +// Add configuration file variable bindings.
 +//
 +
 +void D_BindVariables(void)
 +{
 +    extern int screenblocks;
 +    extern int snd_Channels;
 +    int i;
 +
 +    M_ApplyPlatformDefaults();
 +
 +    I_BindVideoVariables();
 +    I_BindJoystickVariables();
 +    I_BindSoundVariables();
 +
 +    M_BindBaseControls();
 +    M_BindHereticControls();
 +    M_BindWeaponControls();
++    M_BindChatControls(MAXPLAYERS);
 +
 +    M_BindMenuControls();
 +    M_BindMapControls();
 +
 +    M_BindVariable("mouse_sensitivity",      &mouseSensitivity);
 +    M_BindVariable("sfx_volume",             &snd_MaxVolume);
 +    M_BindVariable("music_volume",           &snd_MusicVolume);
 +    M_BindVariable("screenblocks",           &screenblocks);
 +    M_BindVariable("snd_channels",           &snd_Channels);
 +    M_BindVariable("show_endoom",            &show_endoom);
 +    M_BindVariable("graphical_startup",      &graphical_startup);
 +
 +    for (i=0; i<10; ++i)
 +    {
 +        char buf[12];
 +
 +        sprintf(buf, "chatmacro%i", i);
 +        M_BindVariable(buf, &chat_macros[i]);
 +    }
 +}
 +
 +//
 +// Called at exit to display the ENDOOM screen (ENDTEXT in Heretic)
 +//
 +
 +static void D_Endoom(void)
 +{
 +    byte *endoom_data;
 +
 +    // Disable ENDOOM?
 +
 +    if (!show_endoom)
 +    {
 +        return;
 +    }
 +
 +    endoom_data = W_CacheLumpName(DEH_String("ENDTEXT"), PU_STATIC);
 +
 +    I_Endoom(endoom_data);
 +}
 +
 +//---------------------------------------------------------------------------
 +//
 +// PROC D_DoomMain
 +//
 +//---------------------------------------------------------------------------
 +
 +void D_DoomMain(void)
 +{
 +    int p;
 +    char file[256];
 +
 +    I_PrintBanner(PACKAGE_STRING);
 +
 +    I_AtExit(D_Endoom, false);
 +
 +    M_FindResponseFile();
 +    nomonsters = M_CheckParm("-nomonsters");
 +    respawnparm = M_CheckParm("-respawn");
 +    ravpic = M_CheckParm("-ravpic");
 +    noartiskip = M_CheckParm("-noartiskip");
 +    debugmode = M_CheckParm("-debug");
 +    startskill = sk_medium;
 +    startepisode = 1;
 +    startmap = 1;
 +    autostart = false;
 +
 +//
 +// get skill / episode / map from parms
 +//
 +    if (M_CheckParm("-deathmatch"))
 +    {
 +        deathmatch = true;
 +    }
 +
 +    p = M_CheckParm("-skill");
 +    if (p && p < myargc - 1)
 +    {
 +        startskill = myargv[p + 1][0] - '1';
 +        autostart = true;
 +    }
 +
 +    p = M_CheckParm("-episode");
 +    if (p && p < myargc - 1)
 +    {
 +        startepisode = myargv[p + 1][0] - '0';
 +        startmap = 1;
 +        autostart = true;
 +    }
 +
 +    p = M_CheckParm("-warp");
 +    if (p && p < myargc - 2)
 +    {
 +        startepisode = myargv[p + 1][0] - '0';
 +        startmap = myargv[p + 2][0] - '0';
 +        autostart = true;
 +    }
 +
 +//
 +// init subsystems
 +//
 +    DEH_printf("V_Init: allocate screens.\n");
 +    V_Init();
 +
 +    // Check for -CDROM
 +
 +    cdrom = false;
 +
 +#ifdef _WIN32
 +
 +    //!
 +    // @platform windows
 +    // @vanilla
 +    //
 +    // Save configuration data and savegames in c:\heretic.cd,
 +    // allowing play from CD.
 +    //
 +
 +    if (M_CheckParm("-cdrom"))
 +    {
 +        cdrom = true;
 +    }
 +#endif
 +
 +    if (cdrom)
 +    {
 +        M_SetConfigDir(DEH_String("c:\\heretic.cd"));
 +    }
 +    else
 +    {
 +        M_SetConfigDir(NULL);
 +    }
 +
 +    // Load defaults before initing other systems
 +    DEH_printf("M_LoadDefaults: Load system defaults.\n");
 +    D_BindVariables();
 +    M_SetConfigFilenames("heretic.cfg", PROGRAM_PREFIX "heretic.cfg");
 +    M_LoadDefaults();
 +
 +    I_AtExit(M_SaveDefaults, false);
 +
 +    DEH_printf("Z_Init: Init zone memory allocation daemon.\n");
 +    Z_Init();
 +
 +#ifdef FEATURE_DEHACKED
 +    printf("DEH_Init: Init Dehacked support.\n");
 +    DEH_Init();
 +#endif
 +
 +    DEH_printf("W_Init: Init WADfiles.\n");
 +
 +    iwadfile = D_FindIWAD(IWAD_MASK_HERETIC, &gamemission);
 +
 +    if (iwadfile == NULL)
 +    {
 +        I_Error("Game mode indeterminate. No IWAD was found. Try specifying\n"
 +                "one with the '-iwad' command line parameter.");
 +    }
 +
 +    D_AddFile(iwadfile);
 +    W_ParseCommandLine();
 +
 +    p = M_CheckParm("-playdemo");
 +    if (!p)
 +    {
 +        p = M_CheckParm("-timedemo");
 +    }
 +    if (p && p < myargc - 1)
 +    {
 +        DEH_snprintf(file, sizeof(file), "%s.lmp", myargv[p + 1]);
 +        D_AddFile(file);
 +        DEH_printf("Playing demo %s.lmp.\n", myargv[p + 1]);
 +    }
 +
 +    if (W_CheckNumForName(DEH_String("E2M1")) == -1)
 +    {
 +        gamemode = shareware;
 +        gamedescription = "Heretic (shareware)";
 +    }
 +    else if (W_CheckNumForName("EXTENDED") != -1)
 +    {
 +        // Presence of the EXTENDED lump indicates the retail version
 +
 +        gamemode = retail;
 +        gamedescription = "Heretic: Shadow of the Serpent Riders";
 +    }
 +    else
 +    {
 +        gamemode = registered;
 +        gamedescription = "Heretic (registered)";
 +    }
 +
 +    I_PrintStartupBanner(gamedescription);
 +
 +    // haleyjd: removed WATCOMC
 +    initStartup();
 +
 +    //
 +    //  Build status bar line!
 +    //
 +    smsg[0] = 0;
 +    if (deathmatch)
 +        status(DEH_String("DeathMatch..."));
 +    if (nomonsters)
 +        status(DEH_String("No Monsters..."));
 +    if (respawnparm)
 +        status(DEH_String("Respawning..."));
 +    if (autostart)
 +    {
 +        char temp[64];
 +        DEH_snprintf(temp, sizeof(temp),
 +                     "Warp to Episode %d, Map %d, Skill %d ",
 +                     startepisode, startmap, startskill + 1);
 +        status(temp);
 +    }
 +    wadprintf();                // print the added wadfiles
 +
 +    tprintf(DEH_String("MN_Init: Init menu system.\n"), 1);
 +    MN_Init();
 +
 +    CT_Init();
 +
 +    tprintf(DEH_String("R_Init: Init Heretic refresh daemon."), 1);
 +    hprintf(DEH_String("Loading graphics"));
 +    R_Init();
 +    tprintf("\n", 0);
 +
 +    tprintf(DEH_String("P_Init: Init Playloop state.\n"), 1);
 +    hprintf(DEH_String("Init game engine."));
 +    P_Init();
 +    IncThermo();
 +
 +    tprintf(DEH_String("I_Init: Setting up machine state.\n"), 1);
 +    I_CheckIsScreensaver();
 +    I_InitTimer();
 +    I_InitJoystick();
 +    IncThermo();
 +
 +    tprintf(DEH_String("S_Init: Setting up sound.\n"), 1);
 +    S_Init();
 +    //IO_StartupTimer();
 +    S_Start();
 +
 +    tprintf(DEH_String("D_CheckNetGame: Checking network game status.\n"), 1);
 +    hprintf(DEH_String("Checking network game status."));
 +    D_CheckNetGame();
 +    IncThermo();
 +
 +    // haleyjd: removed WATCOMC
 +
 +    tprintf(DEH_String("SB_Init: Loading patches.\n"), 1);
 +    SB_Init();
 +    IncThermo();
 +
 +//
 +// start the apropriate game based on parms
 +//
 +
 +    D_CheckRecordFrom();
 +
 +    p = M_CheckParm("-record");
 +    if (p && p < myargc - 1)
 +    {
 +        G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p + 1]);
 +        D_DoomLoop();           // Never returns
 +    }
 +
 +    p = M_CheckParm("-playdemo");
 +    if (p && p < myargc - 1)
 +    {
 +        singledemo = true;      // Quit after one demo
 +        G_DeferedPlayDemo(myargv[p + 1]);
 +        D_DoomLoop();           // Never returns
 +    }
 +
 +    p = M_CheckParm("-timedemo");
 +    if (p && p < myargc - 1)
 +    {
 +        G_TimeDemo(myargv[p + 1]);
 +        D_DoomLoop();           // Never returns
 +    }
 +
 +    p = M_CheckParm("-loadgame");
 +    if (p && p < myargc - 1)
 +    {
 +        if (cdrom)
 +        {
 +            sprintf(file, SAVEGAMENAMECD "%c.hsg", myargv[p + 1][0]);
 +        }
 +        else
 +        {
 +            sprintf(file, SAVEGAMENAME "%c.hsg", myargv[p + 1][0]);
 +        }
 +        G_LoadGame(file);
 +    }
 +
 +    // Check valid episode and map
 +    if (autostart || netgame)
 +    {
 +        if (!D_ValidEpisodeMap(gamemission, gamemode, startepisode, startmap))
 +        {
 +            startepisode = 1;
 +            startmap = 1;
 +        }
 +    }
 +
 +    if (gameaction != ga_loadgame)
 +    {
 +        UpdateState |= I_FULLSCRN;
 +        BorderNeedRefresh = true;
 +        if (autostart || netgame)
 +        {
 +            G_InitNew(startskill, startepisode, startmap);
 +        }
 +        else
 +        {
 +            D_StartTitle();
 +        }
 +    }
 +
 +    finishStartup();
 +
 +    D_DoomLoop();               // Never returns
 +}
diff --cc src/hexen/h2_main.c
index a20bd91a,00000000..abc58f15
mode 100644,000000..100644
--- a/src/hexen/h2_main.c
+++ b/src/hexen/h2_main.c
@@@ -1,862 -1,0 +1,863 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 1993-2008 Raven Software
 +// Copyright(C) 2008 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.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +// HEADER FILES ------------------------------------------------------------
 +
 +// haleyjd: removed WATCOMC
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <time.h>
 +
 +#include "config.h"
 +
 +#include "h2def.h"
 +#include "ct_chat.h"
 +#include "d_iwad.h"
 +#include "d_mode.h"
 +#include "m_misc.h"
 +#include "s_sound.h"
 +#include "i_joystick.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "m_argv.h"
 +#include "m_config.h"
 +#include "m_controls.h"
 +#include "p_local.h"
 +#include "v_video.h"
 +#include "w_main.h"
 +
 +// MACROS ------------------------------------------------------------------
 +
 +#define MAXWADFILES 20
 +
 +// TYPES -------------------------------------------------------------------
 +
 +typedef struct
 +{
 +    char *name;
 +    void (*func) (char **args, int tag);
 +    int requiredArgs;
 +    int tag;
 +} execOpt_t;
 +
 +// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
 +
 +void R_ExecuteSetViewSize(void);
 +void D_CheckNetGame(void);
 +void G_BuildTiccmd(ticcmd_t * cmd);
 +void F_Drawer(void);
 +boolean F_Responder(event_t * ev);
 +void I_StartupKeyboard(void);
 +void I_StartupJoystick(void);
 +void I_ShutdownKeyboard(void);
 +void S_InitScript(void);
 +
 +// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
 +
 +void H2_ProcessEvents(void);
 +void H2_DoAdvanceDemo(void);
 +void H2_AdvanceDemo(void);
 +void H2_StartTitle(void);
 +void H2_PageTicker(void);
 +
 +// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
 +
 +static void DrawMessage(void);
 +static void PageDrawer(void);
 +static void HandleArgs(void);
 +static void CheckRecordFrom(void);
 +static void DrawAndBlit(void);
 +static void ExecOptionSCRIPTS(char **args, int tag);
 +static void ExecOptionSKILL(char **args, int tag);
 +static void ExecOptionPLAYDEMO(char **args, int tag);
 +static void CreateSavePath(void);
 +static void WarpCheck(void);
 +
 +// EXTERNAL DATA DECLARATIONS ----------------------------------------------
 +
 +extern boolean automapactive;
 +extern boolean MenuActive;
 +extern boolean askforquit;
 +extern char *SavePath;
 +
 +// PUBLIC DATA DEFINITIONS -------------------------------------------------
 +
 +GameMode_t gamemode;
 +GameMission_t gamemission;
 +char *iwadfile;
 +boolean nomonsters;             // checkparm of -nomonsters
 +boolean respawnparm;            // checkparm of -respawn
 +boolean randomclass;            // checkparm of -randclass
 +boolean debugmode;              // checkparm of -debug
 +boolean ravpic;                 // checkparm of -ravpic
 +boolean cdrom = false;          // true if cd-rom mode active
 +boolean cmdfrag;                // true if a CMD_FRAG packet should be sent out
 +boolean singletics;             // debug flag to cancel adaptiveness
 +boolean artiskip;               // whether shift-enter skips an artifact
 +int maxzone = 0x800000;         // Maximum allocated for zone heap (8meg default)
 +skill_t startskill;
 +int startepisode;
 +int startmap;
 +boolean autostart;
 +boolean advancedemo;
 +FILE *debugfile;
 +int UpdateState;
 +
 +// PRIVATE DATA DEFINITIONS ------------------------------------------------
 +
 +static int WarpMap;
 +static int demosequence;
 +static int pagetic;
 +static char *pagename;
 +
 +static execOpt_t ExecOptions[] = {
 +    {"-scripts", ExecOptionSCRIPTS, 1, 0},
 +    {"-skill", ExecOptionSKILL, 1, 0},
 +    {"-playdemo", ExecOptionPLAYDEMO, 1, 0},
 +    {"-timedemo", ExecOptionPLAYDEMO, 1, 0},
 +    {NULL, NULL, 0, 0}          // Terminator
 +};
 +
 +// CODE --------------------------------------------------------------------
 +
 +void D_BindVariables(void)
 +{
 +    int i;
 +
 +    M_ApplyPlatformDefaults();
 +
 +    I_BindVideoVariables();
 +    I_BindJoystickVariables();
 +    I_BindSoundVariables();
 +
 +    M_BindBaseControls();
 +    M_BindMapControls();
 +    M_BindMenuControls();
 +    M_BindWeaponControls();
++    M_BindChatControls(MAXPLAYERS);
 +    M_BindHereticControls();
 +    M_BindHexenControls();
 +
 +    M_BindVariable("graphical_startup",      &graphical_startup);
 +    M_BindVariable("mouse_sensitivity",      &mouseSensitivity);
 +    M_BindVariable("sfx_volume",             &snd_MaxVolume);
 +    M_BindVariable("music_volume",           &snd_MusicVolume);
 +    M_BindVariable("messageson",             &messageson);
 +    M_BindVariable("screenblocks",           &screenblocks);
 +    M_BindVariable("snd_channels",           &snd_Channels);
 +    M_BindVariable("savedir",                &SavePath);
 +
 +    // Multiplayer chat macros
 +
 +    for (i=0; i<10; ++i)
 +    {
 +        char buf[12];
 +
 +        sprintf(buf, "chatmacro%i", i);
 +        M_BindVariable(buf, &chat_macros[i]);
 +    }
 +}
 +
 +// Set the default directory where hub savegames are saved.
 +
 +static void D_SetDefaultSavePath(void)
 +{
 +    SavePath = malloc(strlen(configdir) + 10);
 +    sprintf(SavePath, "%shexndata%c", configdir, DIR_SEPARATOR);
 +}
 +
 +//
 +// D_GrabMouseCallback
 +//
 +// Called to determine whether to grab the mouse pointer
 +//
 +
 +static boolean D_GrabMouseCallback(void)
 +{
 +    // when menu is active or game is paused, release the mouse
 +
 +    if (MenuActive || paused)
 +        return false;
 +
 +    // only grab mouse when playing levels (but not demos)
 +
 +    return (gamestate == GS_LEVEL) && !demoplayback;
 +}
 +
 +// Message displayed when quitting Hexen
 +
 +static void D_HexenQuitMessage(void)
 +{
 +    printf("\nHexen: Beyond Heretic\n");
 +}
 +
 +static void D_AddFile(char *filename)
 +{
 +    printf("  adding %s\n", filename);
 +
 +    W_AddFile(filename);
 +}
 +
 +//==========================================================================
 +//
 +// H2_Main
 +//
 +//==========================================================================
 +void InitMapMusicInfo(void);
 +
 +void D_DoomMain(void)
 +{
 +    int p;
 +
 +    I_AtExit(D_HexenQuitMessage, false);
 +    M_FindResponseFile();
 +    startepisode = 1;
 +    autostart = false;
 +    startskill = sk_medium;
 +    startmap = 1;
 +    gamemode = commercial;
 +
 +    // Initialize subsystems
 +
 +    ST_Message("V_Init: allocate screens.\n");
 +    V_Init();
 +
 +    // Load defaults before initing other systems
 +    ST_Message("M_LoadDefaults: Load system defaults.\n");
 +    D_BindVariables();
 +
 +#ifdef _WIN32
 +
 +    //!
 +    // @platform windows
 +    // @vanilla
 +    //
 +    // Save configuration data and savegames in c:\hexndata,
 +    // allowing play from CD.
 +    //
 +
 +    cdrom = M_ParmExists("-cdrom");
 +#endif
 +
 +    if (cdrom)
 +    {
 +        M_SetConfigDir("c:\\hexndata\\");
 +    }
 +    else
 +    {
 +        M_SetConfigDir(NULL);
 +    }
 +
 +    D_SetDefaultSavePath();
 +    M_SetConfigFilenames("hexen.cfg", PROGRAM_PREFIX "hexen.cfg");
 +    M_LoadDefaults();
 +
 +    I_AtExit(M_SaveDefaults, false);
 +
 +
 +    // Now that the savedir is loaded from .CFG, make sure it exists
 +    CreateSavePath();
 +
 +    ST_Message("Z_Init: Init zone memory allocation daemon.\n");
 +    Z_Init();
 +
 +    // haleyjd: removed WATCOMC
 +
 +    ST_Message("W_Init: Init WADfiles.\n");
 +
 +    iwadfile = D_FindIWAD(IWAD_MASK_HEXEN, &gamemission);
 +
 +    if (iwadfile == NULL)
 +    {
 +        I_Error("Game mode indeterminate. No IWAD was found. Try specifying\n"
 +                "one with the '-iwad' command line parameter.");
 +    }
 +
 +    D_AddFile(iwadfile);
 +
 +    HandleArgs();
 +
 +    I_PrintStartupBanner("Hexen");
 +
 +    ST_Message("MN_Init: Init menu system.\n");
 +    MN_Init();
 +
 +    ST_Message("CT_Init: Init chat mode data.\n");
 +    CT_Init();
 +
 +    InitMapMusicInfo();         // Init music fields in mapinfo
 +
 +    ST_Message("S_InitScript\n");
 +    S_InitScript();
 +
 +    ST_Message("SN_InitSequenceScript: Registering sound sequences.\n");
 +    SN_InitSequenceScript();
 +    ST_Message("I_Init: Setting up machine state.\n");
 +    I_CheckIsScreensaver();
 +    I_InitTimer();
 +    I_InitJoystick();
 +
 +    S_Init();
 +    S_Start();
 +
 +    ST_Message("ST_Init: Init startup screen.\n");
 +    ST_Init();
 +
 +    // Show version message now, so it's visible during R_Init()
 +    ST_Message("R_Init: Init Hexen refresh daemon");
 +    R_Init();
 +    ST_Message("\n");
 +
 +    if (M_CheckParm("-net"))
 +        ST_NetProgress();       // Console player found
 +
 +    ST_Message("P_Init: Init Playloop state.\n");
 +    P_Init();
 +
 +    // Check for command line warping. Follows P_Init() because the
 +    // MAPINFO.TXT script must be already processed.
 +    WarpCheck();
 +
 +    if (autostart)
 +    {
 +        ST_Message("Warp to Map %d (\"%s\":%d), Skill %d\n",
 +                   WarpMap, P_GetMapName(startmap), startmap, startskill + 1);
 +    }
 +
 +    ST_Message("D_CheckNetGame: Checking network game status.\n");
 +    D_CheckNetGame();
 +
 +    ST_Message("SB_Init: Loading patches.\n");
 +    SB_Init();
 +
 +    ST_Done();
 +
 +    CheckRecordFrom();
 +
 +    p = M_CheckParm("-record");
 +    if (p && p < myargc - 1)
 +    {
 +        G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p + 1]);
 +        H2_GameLoop();          // Never returns
 +    }
 +
 +    p = M_CheckParm("-playdemo");
 +    if (p && p < myargc - 1)
 +    {
 +        singledemo = true;      // Quit after one demo
 +        G_DeferedPlayDemo(myargv[p + 1]);
 +        H2_GameLoop();          // Never returns
 +    }
 +
 +    p = M_CheckParm("-timedemo");
 +    if (p && p < myargc - 1)
 +    {
 +        G_TimeDemo(myargv[p + 1]);
 +        H2_GameLoop();          // Never returns
 +    }
 +
 +    p = M_CheckParm("-loadgame");
 +    if (p && p < myargc - 1)
 +    {
 +        G_LoadGame(atoi(myargv[p + 1]));
 +    }
 +
 +    if (gameaction != ga_loadgame)
 +    {
 +        UpdateState |= I_FULLSCRN;
 +        BorderNeedRefresh = true;
 +        if (autostart || netgame)
 +        {
 +            G_StartNewInit();
 +            G_InitNew(startskill, startepisode, startmap);
 +        }
 +        else
 +        {
 +            H2_StartTitle();
 +        }
 +    }
 +    H2_GameLoop();              // Never returns
 +}
 +
 +//==========================================================================
 +//
 +// HandleArgs
 +//
 +//==========================================================================
 +
 +static void HandleArgs(void)
 +{
 +    int p;
 +    execOpt_t *opt;
 +
 +    nomonsters = M_ParmExists("-nomonsters");
 +    respawnparm = M_ParmExists("-respawn");
 +    randomclass = M_ParmExists("-randclass");
 +    ravpic = M_ParmExists("-ravpic");
 +    artiskip = M_ParmExists("-artiskip");
 +    debugmode = M_ParmExists("-debug");
 +    deathmatch = M_ParmExists("-deathmatch");
 +
 +    cmdfrag = M_ParmExists("-cmdfrag");
 +
 +    // Check WAD file command line options
 +    W_ParseCommandLine();
 +
 +    // Process command line options
 +    for (opt = ExecOptions; opt->name != NULL; opt++)
 +    {
 +        p = M_CheckParm(opt->name);
 +        if (p && p < myargc - opt->requiredArgs)
 +        {
 +            opt->func(&myargv[p], opt->tag);
 +        }
 +    }
 +}
 +
 +//==========================================================================
 +//
 +// WarpCheck
 +//
 +//==========================================================================
 +
 +static void WarpCheck(void)
 +{
 +    int p;
 +    int map;
 +
 +    p = M_CheckParm("-warp");
 +    if (p && p < myargc - 1)
 +    {
 +        WarpMap = atoi(myargv[p + 1]);
 +        map = P_TranslateMap(WarpMap);
 +        if (map == -1)
 +        {                       // Couldn't find real map number
 +            startmap = 1;
 +            ST_Message("-WARP: Invalid map number.\n");
 +        }
 +        else
 +        {                       // Found a valid startmap
 +            startmap = map;
 +            autostart = true;
 +        }
 +    }
 +    else
 +    {
 +        WarpMap = 1;
 +        startmap = P_TranslateMap(1);
 +        if (startmap == -1)
 +        {
 +            startmap = 1;
 +        }
 +    }
 +}
 +
 +//==========================================================================
 +//
 +// ExecOptionSKILL
 +//
 +//==========================================================================
 +
 +static void ExecOptionSKILL(char **args, int tag)
 +{
 +    startskill = args[1][0] - '1';
 +    autostart = true;
 +}
 +
 +//==========================================================================
 +//
 +// ExecOptionPLAYDEMO
 +//
 +//==========================================================================
 +
 +static void ExecOptionPLAYDEMO(char **args, int tag)
 +{
 +    char file[256];
 +
 +    strcpy(file, args[1]);
 +
 +    if (strcasecmp(file + strlen(file) - 4, ".lmp") != 0)
 +    {
 +        strcat(file, ".lmp");
 +    }
 +
 +    W_AddFile(file);
 +    ST_Message("Playing demo %s.\n", file);
 +}
 +
 +//==========================================================================
 +//
 +// ExecOptionSCRIPTS
 +//
 +//==========================================================================
 +
 +static void ExecOptionSCRIPTS(char **args, int tag)
 +{
 +    sc_FileScripts = true;
 +    sc_ScriptsDir = args[1];
 +}
 +
 +
 +//==========================================================================
 +//
 +// H2_GameLoop
 +//
 +//==========================================================================
 +
 +void H2_GameLoop(void)
 +{
 +    if (M_CheckParm("-debugfile"))
 +    {
 +        char filename[20];
 +        sprintf(filename, "debug%i.txt", consoleplayer);
 +        debugfile = fopen(filename, "w");
 +    }
 +    I_SetWindowTitle("Hexen");
 +    I_InitGraphics();
 +    I_SetGrabMouseCallback(D_GrabMouseCallback);
 +
 +    while (1)
 +    {
 +        // Frame syncronous IO operations
 +        I_StartFrame();
 +
 +        // Process one or more tics
 +        if (singletics)
 +        {
 +            I_StartTic();
 +            H2_ProcessEvents();
 +            G_BuildTiccmd(&netcmds[consoleplayer][maketic % BACKUPTICS]);
 +            if (advancedemo)
 +            {
 +                H2_DoAdvanceDemo();
 +            }
 +            G_Ticker();
 +            gametic++;
 +            maketic++;
 +        }
 +        else
 +        {
 +            // Will run at least one tic
 +            TryRunTics();
 +        }
 +
 +        // Move positional sounds
 +        S_UpdateSounds(players[displayplayer].mo);
 +
 +        DrawAndBlit();
 +    }
 +}
 +
 +//==========================================================================
 +//
 +// H2_ProcessEvents
 +//
 +// Send all the events of the given timestamp down the responder chain.
 +//
 +//==========================================================================
 +
 +void H2_ProcessEvents(void)
 +{
 +    event_t *ev;
 +
 +    for (;;)
 +    {
 +        ev = D_PopEvent();
 +
 +        if (ev == NULL)
 +        {
 +            break;
 +        }
 +
 +        if (F_Responder(ev))
 +        {
 +            continue;
 +        }
 +        if (MN_Responder(ev))
 +        {
 +            continue;
 +        }
 +        G_Responder(ev);
 +    }
 +}
 +
 +//==========================================================================
 +//
 +// DrawAndBlit
 +//
 +//==========================================================================
 +
 +static void DrawAndBlit(void)
 +{
 +    // Change the view size if needed
 +    if (setsizeneeded)
 +    {
 +        R_ExecuteSetViewSize();
 +    }
 +
 +    // Do buffered drawing
 +    switch (gamestate)
 +    {
 +        case GS_LEVEL:
 +            if (!gametic)
 +            {
 +                break;
 +            }
 +            if (automapactive)
 +            {
 +                AM_Drawer();
 +            }
 +            else
 +            {
 +                R_RenderPlayerView(&players[displayplayer]);
 +            }
 +            CT_Drawer();
 +            UpdateState |= I_FULLVIEW;
 +            SB_Drawer();
 +            break;
 +        case GS_INTERMISSION:
 +            IN_Drawer();
 +            break;
 +        case GS_FINALE:
 +            F_Drawer();
 +            break;
 +        case GS_DEMOSCREEN:
 +            PageDrawer();
 +            break;
 +    }
 +
 +    if (paused && !MenuActive && !askforquit)
 +    {
 +        if (!netgame)
 +        {
 +            V_DrawPatch(160, viewwindowy + 5, W_CacheLumpName("PAUSED",
 +                                                              PU_CACHE));
 +        }
 +        else
 +        {
 +            V_DrawPatch(160, 70, W_CacheLumpName("PAUSED", PU_CACHE));
 +        }
 +    }
 +
 +    // Draw current message
 +    DrawMessage();
 +
 +    // Draw Menu
 +    MN_Drawer();
 +
 +    // Send out any new accumulation
 +    NetUpdate();
 +
 +    // Flush buffered stuff to screen
 +    I_FinishUpdate();
 +}
 +
 +//==========================================================================
 +//
 +// DrawMessage
 +//
 +//==========================================================================
 +
 +static void DrawMessage(void)
 +{
 +    player_t *player;
 +
 +    player = &players[consoleplayer];
 +    if (player->messageTics <= 0 || !player->message)
 +    {                           // No message
 +        return;
 +    }
 +    if (player->yellowMessage)
 +    {
 +        MN_DrTextAYellow(player->message,
 +                         160 - MN_TextAWidth(player->message) / 2, 1);
 +    }
 +    else
 +    {
 +        MN_DrTextA(player->message, 160 - MN_TextAWidth(player->message) / 2,
 +                   1);
 +    }
 +}
 +
 +//==========================================================================
 +//
 +// H2_PageTicker
 +//
 +//==========================================================================
 +
 +void H2_PageTicker(void)
 +{
 +    if (--pagetic < 0)
 +    {
 +        H2_AdvanceDemo();
 +    }
 +}
 +
 +//==========================================================================
 +//
 +// PageDrawer
 +//
 +//==========================================================================
 +
 +static void PageDrawer(void)
 +{
 +    V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE));
 +    if (demosequence == 1)
 +    {
 +        V_DrawPatch(4, 160, W_CacheLumpName("ADVISOR", PU_CACHE));
 +    }
 +    UpdateState |= I_FULLSCRN;
 +}
 +
 +//==========================================================================
 +//
 +// H2_AdvanceDemo
 +//
 +// Called after each demo or intro demosequence finishes.
 +//
 +//==========================================================================
 +
 +void H2_AdvanceDemo(void)
 +{
 +    advancedemo = true;
 +}
 +
 +//==========================================================================
 +//
 +// H2_DoAdvanceDemo
 +//
 +//==========================================================================
 +
 +void H2_DoAdvanceDemo(void)
 +{
 +    players[consoleplayer].playerstate = PST_LIVE;      // don't reborn
 +    advancedemo = false;
 +    usergame = false;           // can't save/end game here
 +    paused = false;
 +    gameaction = ga_nothing;
 +    demosequence = (demosequence + 1) % 7;
 +    switch (demosequence)
 +    {
 +        case 0:
 +            pagetic = 280;
 +            gamestate = GS_DEMOSCREEN;
 +            pagename = "TITLE";
 +            S_StartSongName("hexen", true);
 +            break;
 +        case 1:
 +            pagetic = 210;
 +            gamestate = GS_DEMOSCREEN;
 +            pagename = "TITLE";
 +            break;
 +        case 2:
 +            BorderNeedRefresh = true;
 +            UpdateState |= I_FULLSCRN;
 +            G_DeferedPlayDemo("demo1");
 +            break;
 +        case 3:
 +            pagetic = 200;
 +            gamestate = GS_DEMOSCREEN;
 +            pagename = "CREDIT";
 +            break;
 +        case 4:
 +            BorderNeedRefresh = true;
 +            UpdateState |= I_FULLSCRN;
 +            G_DeferedPlayDemo("demo2");
 +            break;
 +        case 5:
 +            pagetic = 200;
 +            gamestate = GS_DEMOSCREEN;
 +            pagename = "CREDIT";
 +            break;
 +        case 6:
 +            BorderNeedRefresh = true;
 +            UpdateState |= I_FULLSCRN;
 +            G_DeferedPlayDemo("demo3");
 +            break;
 +    }
 +}
 +
 +//==========================================================================
 +//
 +// H2_StartTitle
 +//
 +//==========================================================================
 +
 +void H2_StartTitle(void)
 +{
 +    gameaction = ga_nothing;
 +    demosequence = -1;
 +    H2_AdvanceDemo();
 +}
 +
 +//==========================================================================
 +//
 +// CheckRecordFrom
 +//
 +// -recordfrom <savegame num> <demoname>
 +//
 +//==========================================================================
 +
 +static void CheckRecordFrom(void)
 +{
 +    int p;
 +
 +    p = M_CheckParm("-recordfrom");
 +    if (!p || p > myargc - 2)
 +    {                           // Bad args
 +        return;
 +    }
 +    G_LoadGame(atoi(myargv[p + 1]));
 +    G_DoLoadGame();             // Load the gameskill etc info from savegame
 +    G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p + 2]);
 +
 +    H2_GameLoop();              // Never returns
 +}
 +
 +// haleyjd: removed WATCOMC
 +/*
 +void CleanExit(void)
 +{
 +	union REGS regs;
 +
 +	I_ShutdownKeyboard();
 +	regs.x.eax = 0x3;
 +	int386(0x10, &regs, &regs);
 +	printf("Exited from HEXEN: Beyond Heretic.\n");
 +	exit(1);
 +}
 +*/
 +
 +//==========================================================================
 +//
 +// CreateSavePath
 +//
 +//==========================================================================
 +
 +static void CreateSavePath(void)
 +{
 +    M_MakeDirectory(SavePath);
 +}
 +
diff --cc src/i_sdlsound.c
index 0b3f8aa3,a758289a..7deb683d
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@@ -39,16 -38,18 +38,18 @@@
  #include <samplerate.h>
  #endif

 -#include "deh_main.h"
 +#include "deh_str.h"
 +#include "i_sound.h"
  #include "i_system.h"
+ #include "i_swap.h"
 -#include "s_sound.h"
  #include "m_argv.h"
  #include "w_wad.h"
  #include "z_zone.h"

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

  #define LOW_PASS_FILTER
+ //#define DEBUG_DUMP_WAVS
  #define MAX_SOUND_SLICE_TIME 70 /* ms */
  #define NUM_CHANNELS 16

@@@ -288,16 -160,64 +289,66 @@@ static boolean ConvertibleRatio(int fre
      }
  }

+ #ifdef DEBUG_DUMP_WAVS
+
+ // Debug code to dump resampled sound effects to WAV files for analysis.
+
+ static void WriteWAV(char *filename, byte *data,
+                      uint32_t length, int samplerate)
+ {
+     FILE *wav;
+     unsigned int i;
+     unsigned short s;
+
+     wav = fopen(filename, "wb");
+
+     // Header
+
+     fwrite("RIFF", 1, 4, wav);
+     i = LONG(36 + samplerate);
+     fwrite(&i, 4, 1, wav);
+     fwrite("WAVE", 1, 4, wav);
+
+     // Subchunk 1
+
+     fwrite("fmt ", 1, 4, wav);
+     i = LONG(16);
+     fwrite(&i, 4, 1, wav);           // Length
+     s = SHORT(1);
+     fwrite(&s, 2, 1, wav);           // Format (PCM)
+     s = SHORT(2);
+     fwrite(&s, 2, 1, wav);           // Channels (2=stereo)
+     i = LONG(samplerate);
+     fwrite(&i, 4, 1, wav);           // Sample rate
+     i = LONG(samplerate * 2 * 2);
+     fwrite(&i, 4, 1, wav);           // Byte rate (samplerate * stereo * 16 bit)
+     s = SHORT(2 * 2);
+     fwrite(&s, 2, 1, wav);           // Block align (stereo * 16 bit)
+     s = SHORT(16);
+     fwrite(&s, 2, 1, wav);           // Bits per sample (16 bit)
+
+     // Data subchunk
+
+     fwrite("data", 1, 4, wav);
+     i = LONG(length);
+     fwrite(&i, 4, 1, wav);           // Data length
+     fwrite(data, 1, length, wav);    // Data
+
+     fclose(wav);
+ }
+
+ #endif
+
  // Generic sound expansion function for any sample rate.
 +// Returns number of clipped samples (always 0).

 -static void ExpandSoundData_SDL(byte *data,
 +static void ExpandSoundData_SDL(sfxinfo_t *sfxinfo,
 +                                byte *data,
                                  int samplerate,
 -                                uint32_t length,
 -                                Mix_Chunk *destination)
 +                                int length)
  {
      SDL_AudioCVT convertor;
 +    Mix_Chunk *chunk;
      uint32_t expanded_length;

      // Calculate the length of the expanded version of the sample.
@@@ -307,13 -227,12 +358,13 @@@
      // 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 / optimized SDL conversion routines.
-
+
      if (samplerate <= mixer_freq
       && ConvertibleRatio(samplerate, mixer_freq)
       && SDL_BuildAudioCVT(&convertor,
@@@ -417,21 -350,64 +471,31 @@@ static boolean CacheSFX(sfxinfo_t *sfxi

      // 16 bit sample rate field, 32 bit length field

 -    *samplerate = (data[3] << 8) | data[2];
 -    *length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];
 +    samplerate = (data[3] << 8) | data[2];
 +    length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];

 -    // If the header specifies that the length of the sound is
 -    // greater than the length of the lump itself, this is an invalid
 -    // sound lump.
 +    // If the header specifies that the length of the sound is greater than
 +    // the length of the lump itself, this is an invalid sound lump

 -    if (*length > lumplen - 8)
 +    if (length > lumplen - 8)
      {
 -	W_ReleaseLumpNum(*lumpnum);
 -	return false;
 -    }
 -
 -    // Prune header
 -    *data_ref += 8;
 -
 -    return true;
 -}
 -
 -
 -// Load and convert a sound effect
 -// Returns true if successful
 -
 -static boolean CacheSFX_SDL(int sound)
 -{
 -    int lumpnum;
 -    int samplerate;
 -    uint32_t length;
 -    byte *data;
 -
 -#ifdef HAVE_LIBSAMPLERATE
 -    assert(!use_libsamplerate); // Should be using I_PrecacheSounds_SRC instead
 -#endif
 -
 -    if (!LoadSoundLump(sound, &lumpnum, &samplerate, &length, &data))
          return false;
 +    }

      // Sample rate conversion
 -    // sound_chunks[sound].alen and abuf are determined by ExpandSoundData.
 -
 -    sound_chunks[sound].allocated = 1;
 -    sound_chunks[sound].volume = MIX_MAX_VOLUME;

 -    ExpandSoundData_SDL(data,
 -			samplerate,
 -			length,
 -			&sound_chunks[sound]);
 +    ExpandSoundData(sfxinfo, data + 8, samplerate, length);

+ #ifdef DEBUG_DUMP_WAVS
+     {
+         char filename[16];
+
+         sprintf(filename, "%s.wav", DEH_String(S_sfx[sound].name));
+         WriteWAV(filename, sound_chunks[sound].abuf,
+                  sound_chunks[sound].alen, mixer_freq);
+     }
+ #endif
+
      // don't need the original lump any more

      W_ReleaseLumpNum(lumpnum);
diff --cc src/i_sound.c
index cf9e17a3,00000000..0c771dc2
mode 100644,000000..100644
--- a/src/i_sound.c
+++ b/src/i_sound.c
@@@ -1,431 -1,0 +1,436 @@@
 +// 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"
 +
 +// 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 = SNDDEVICE_GENMIDI;
 +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;
 +extern music_module_t music_opl_module;
 +
++// For OPL module:
++
++extern int opl_io_port;
++
 +// 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,
 +    &music_opl_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 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))
 +        {
 +            // Initialize the module
 +
 +            if (sound_modules[i]->Init(use_sfx_prefix))
 +            {
 +                sound_module = sound_modules[i];
 +                return;
 +            }
 +        }
 +    }
 +}
 +
 +// 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))
 +        {
 +            // 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;
 +
 +    // 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)
 +{
 +    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);
++    M_BindVariable("opl_io_port",       &opl_io_port);
 +#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_video.c
index a370fc08,ee10a12e..733af899
--- a/src/i_video.c
+++ b/src/i_video.c
@@@ -153,15 -105,13 +151,19 @@@ static boolean initialized = false
  // disable mouse?

  static boolean nomouse = false;
 -extern int usemouse;
 +int usemouse = 1;

+ // Bit mask of mouse button state.
+
+ static unsigned int mouse_button_state = 0;
+
 -// if true, screens[0] is screen->pixel
 +// Disallow mouse and joystick movement to cause forward/backward
 +// motion.  Specified with the '-novert' command line parameter.
 +// This is an int to allow saving to config file
 +
 +int novert = 0;
 +
 +// if true, I_VideoBuffer is screen->pixels

  static boolean native_surface;

@@@ -257,10 -186,8 +265,12 @@@ static int shiftdown = 0
  float mouse_acceleration = 2.0;
  int mouse_threshold = 10;

 +// Gamma correction level to use
 +
 +int usegamma = 0;
 +
+ static void ApplyWindowResize(unsigned int w, unsigned int h);
+
  static boolean MouseShouldBeGrabbed()
  {
      // never grab the mouse when in screensaver mode
@@@ -777,17 -662,9 +815,17 @@@ static void I_ReadMouse(void
      if (x != 0 || y != 0)
      {
          ev.type = ev_mouse;
-         ev.data1 = MouseButtonState();
+         ev.data1 = mouse_button_state;
          ev.data2 = AccelerateMouse(x);
 -        ev.data3 = -AccelerateMouse(y);
 +
 +        if (!novert)
 +        {
 +            ev.data3 = -AccelerateMouse(y);
 +        }
 +        else
 +        {
 +            ev.data3 = 0;
 +        }

          D_PostEvent(&ev);
      }
@@@ -1727,7 -1656,20 +1856,20 @@@ void I_InitGraphics(void

      CheckCommandLine();

-     doompal = W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE);
+     // 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
+
+     //
+     // Enter into graphics mode.
+     //
+     // When in screensaver mode, run full screen and auto detect
+     // screen dimensions (don't change video mode)
+     //

      if (screensaver_mode)
      {
diff --cc src/i_video.h
index af865287,94ffcc29..82368967
--- a/src/i_video.h
+++ b/src/i_video.h
@@@ -30,47 -30,34 +30,49 @@@

  #include "doomtype.h"

 +// Screen width and height.
 +
 +#define SCREENWIDTH  320
 +#define SCREENHEIGHT 200
 +
 +// Screen width used for "squash" scale functions
 +
 +#define SCREENWIDTH_4_3 256
 +
 +// Screen height used for "stretch" scale functions.
 +
 +#define SCREENHEIGHT_4_3 240
 +
- typedef struct
+ #define MAX_MOUSE_BUTTONS 8
+
+ typedef struct
  {
 -        // Screen width and height
 +    // Screen width and height

 -        int width;
 -        int height;
 +    int width;
 +    int height;

 -        // Initialisation function to call when using this mode.
 -        // Called with a pointer to the Doom palette.
 -        //
 -        // If NULL, no init function is called.
 +    // Initialisation function to call when using this mode.
 +    // Called with a pointer to the Doom palette.
 +    //
 +    // If NULL, no init function is called.

 -        void (*InitMode)(byte *palette);
 -
 -        // Function to call to draw the screen from the source buffer.
 -        // Return true if draw was successful.
 +    void (*InitMode)(byte *palette);
 +
 +    // Function to call to draw the screen from the source buffer.
 +    // Return true if draw was successful.

 -        boolean (*DrawScreen)(int x1, int y1, int x2, int y2);
 +    boolean (*DrawScreen)(int x1, int y1, int x2, int y2);

 -        // If true, this is a "poor quality" mode.  The autoadjust
 -        // code should always attempt to use a different mode to this
 -        // mode in fullscreen.
 +    // If true, this is a "poor quality" mode.  The autoadjust
 +    // code should always attempt to use a different mode to this
 +    // mode in fullscreen.

 -        boolean poor_quality;
 +    boolean poor_quality;
  } screen_mode_t;

 +typedef boolean (*grabmouse_callback_t)(void);
 +
  // Called by D_DoomMain,
  // determines the hardware configuration
  // and sets up the video mode
diff --cc src/m_config.c
index 056861cc,2065700e..8b93cfd3
--- a/src/m_config.c
+++ b/src/m_config.c
@@@ -95,18 -250,23 +96,23 @@@ typedef struc
  typedef struct
  {
      default_t *defaults;
 -    int        numdefaults;
 -    char      *filename;
 +    int numdefaults;
 +    char *filename;
  } default_collection_t;

 -#define CONFIG_VARIABLE_GENERIC(name, variable, type) \
 -    { #name, &variable, type, 0, 0 }
++#define CONFIG_VARIABLE_GENERIC(name, type) \
++    { #name, NULL, type, 0, 0, false }
+
 -#define CONFIG_VARIABLE_KEY(name, variable) \
 -    CONFIG_VARIABLE_GENERIC(name, variable, DEFAULT_KEY)
 -#define CONFIG_VARIABLE_INT(name, variable) \
 -    CONFIG_VARIABLE_GENERIC(name, variable, DEFAULT_INT)
 -#define CONFIG_VARIABLE_INT_HEX(name, variable) \
 -    CONFIG_VARIABLE_GENERIC(name, variable, DEFAULT_INT_HEX)
 -#define CONFIG_VARIABLE_FLOAT(name, variable) \
 -    CONFIG_VARIABLE_GENERIC(name, variable, DEFAULT_FLOAT)
 -#define CONFIG_VARIABLE_STRING(name, variable) \
 -    CONFIG_VARIABLE_GENERIC(name, variable, DEFAULT_STRING)
 +#define CONFIG_VARIABLE_KEY(name) \
-     { #name, NULL, DEFAULT_KEY, 0, 0, false }
++    CONFIG_VARIABLE_GENERIC(name, DEFAULT_KEY)
 +#define CONFIG_VARIABLE_INT(name) \
-     { #name, NULL, DEFAULT_INT, 0, 0, false }
++    CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT)
++#define CONFIG_VARIABLE_INT_HEX(name) \
++    CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT_HEX)
 +#define CONFIG_VARIABLE_FLOAT(name) \
-     { #name, NULL, DEFAULT_FLOAT, 0, 0, false }
++    CONFIG_VARIABLE_GENERIC(name, DEFAULT_FLOAT)
 +#define CONFIG_VARIABLE_STRING(name) \
-     { #name, NULL, DEFAULT_STRING, 0, 0, false }
++    CONFIG_VARIABLE_GENERIC(name, DEFAULT_STRING)

  //! @begin_config_file default.cfg

@@@ -578,15 -645,22 +584,22 @@@ static default_t extra_defaults_list[]
      // acceleration factor (mouse_acceleration).
      //

 -    CONFIG_VARIABLE_INT(mouse_threshold,           mouse_threshold),
 +    CONFIG_VARIABLE_INT(mouse_threshold),

      //!
-     // Sound output sample rate, in Hz.  Typical values to use are
+     // Sound output sample rate, in Hz.  Typical values to use are
      // 11025, 22050, 44100 and 48000.
      //

 -    CONFIG_VARIABLE_INT(snd_samplerate,            snd_samplerate),
 +    CONFIG_VARIABLE_INT(snd_samplerate),

+     //!
+     // The I/O port to use to access the OPL chip.  Only relevant when
+     // using native OPL music playback.
+     //
+
 -    CONFIG_VARIABLE_INT_HEX(opl_io_port,           opl_io_port),
++    CONFIG_VARIABLE_INT_HEX(opl_io_port),
+
      //!
      // If non-zero, the ENDOOM screen is displayed when exiting the
      // game.  If zero, the ENDOOM screen is not displayed.
@@@ -679,8 -753,20 +692,20 @@@
      // Joystick button to strafe right.
      //

 -    CONFIG_VARIABLE_INT(joyb_straferight,          joybstraferight),
 +    CONFIG_VARIABLE_INT(joyb_straferight),

+     //!
+     // Joystick button to cycle to the previous weapon.
+     //
+
 -    CONFIG_VARIABLE_INT(joyb_prevweapon,           joybprevweapon),
++    CONFIG_VARIABLE_INT(joyb_prevweapon),
+
+     //!
+     // Joystick button to cycle to the next weapon.
+     //
+
 -    CONFIG_VARIABLE_INT(joyb_nextweapon,          joybnextweapon),
++    CONFIG_VARIABLE_INT(joyb_nextweapon),
+
      //!
      // Mouse button to strafe left.
      //
@@@ -703,8 -789,20 +728,20 @@@
      // Mouse button to move backwards.
      //

 -    CONFIG_VARIABLE_INT(mouseb_backward,           mousebbackward),
 +    CONFIG_VARIABLE_INT(mouseb_backward),

+     //!
+     // Mouse button to cycle to the previous weapon.
+     //
+
 -    CONFIG_VARIABLE_INT(mouseb_prevweapon,         mousebprevweapon),
++    CONFIG_VARIABLE_INT(mouseb_prevweapon),
+
+     //!
+     // Mouse button to cycle to the next weapon.
+     //
+
 -    CONFIG_VARIABLE_INT(mouseb_nextweapon,         mousebnextweapon),
++    CONFIG_VARIABLE_INT(mouseb_nextweapon),
+
      //!
      // If non-zero, double-clicking a mouse button acts like pressing
      // the "use" key to use an object in-game, eg. a door or switch.
@@@ -855,8 -953,14 +892,14 @@@
      // Keyboard shortcut to toggle the gamma correction level.
      //

 -    CONFIG_VARIABLE_KEY(key_menu_gamma,            key_menu_gamma),
 +    CONFIG_VARIABLE_KEY(key_menu_gamma),

+     //!
+     // Keyboard shortcut to switch view in multiplayer.
+     //
+
 -    CONFIG_VARIABLE_KEY(key_spy,                   key_spy),
++    CONFIG_VARIABLE_KEY(key_spy),
+
      //!
      // Keyboard shortcut to increase the screen size.
      //
@@@ -987,13 -1091,61 +1030,85 @@@
      // Key to select weapon 8.
      //

 -    CONFIG_VARIABLE_KEY(key_weapon8,               key_weapon8),
 +    CONFIG_VARIABLE_KEY(key_weapon8),

+     //!
+     // Key to cycle to the previous weapon.
+     //
+
 -    CONFIG_VARIABLE_KEY(key_prevweapon,            key_prevweapon),
++    CONFIG_VARIABLE_KEY(key_prevweapon),
+
+     //!
+     // Key to cycle to the next weapon.
+     //
+
 -    CONFIG_VARIABLE_KEY(key_nextweapon,            key_nextweapon),
++    CONFIG_VARIABLE_KEY(key_nextweapon),
+
      //!
      // Key to re-display last message.
      //

 -    CONFIG_VARIABLE_KEY(key_message_refresh,       key_message_refresh),
 +    CONFIG_VARIABLE_KEY(key_message_refresh),
+
+     //!
+     // Key to quit the game when recording a demo.
+     //
+
 -    CONFIG_VARIABLE_KEY(key_demo_quit,             key_demo_quit),
++    CONFIG_VARIABLE_KEY(key_demo_quit),
+
+     //!
+     // Key to send a message during multiplayer games.
+     //
+
 -    CONFIG_VARIABLE_KEY(key_multi_msg,             key_multi_msg),
++    CONFIG_VARIABLE_KEY(key_multi_msg),
++
++    //!
++    // Key to send a message to player 1 during multiplayer games.
++    //
++
++    CONFIG_VARIABLE_KEY(key_multi_msgplayer1),
++
++    //!
++    // Key to send a message to player 2 during multiplayer games.
++    //
++
++    CONFIG_VARIABLE_KEY(key_multi_msgplayer2),
++
++    //!
++    // Key to send a message to player 3 during multiplayer games.
++    //
++
++    CONFIG_VARIABLE_KEY(key_multi_msgplayer3),
+
+     //!
 -    // Key to send a message to the green player during multiplayer games.
++    // Key to send a message to player 4 during multiplayer games.
+     //
+
 -    CONFIG_VARIABLE_KEY(key_multi_msgplayer1,      key_multi_msgplayer[0]),
++    CONFIG_VARIABLE_KEY(key_multi_msgplayer4),
+
+     //!
 -    // Key to send a message to the indigo player during multiplayer games.
++    // Key to send a message to player 5 during multiplayer games.
+     //
+
 -    CONFIG_VARIABLE_KEY(key_multi_msgplayer2,      key_multi_msgplayer[1]),
++    CONFIG_VARIABLE_KEY(key_multi_msgplayer5),
+
+     //!
 -    // Key to send a message to the brown player during multiplayer games.
++    // Key to send a message to player 6 during multiplayer games.
+     //
+
 -    CONFIG_VARIABLE_KEY(key_multi_msgplayer3,      key_multi_msgplayer[2]),
++    CONFIG_VARIABLE_KEY(key_multi_msgplayer6),
+
+     //!
 -    // Key to send a message to the red player during multiplayer games.
++    // Key to send a message to player 7 during multiplayer games.
+     //
+
 -    CONFIG_VARIABLE_KEY(key_multi_msgplayer4,      key_multi_msgplayer[3]),
++    CONFIG_VARIABLE_KEY(key_multi_msgplayer7),
++
++    //!
++    // Key to send a message to player 8 during multiplayer games.
++    //
++
++    CONFIG_VARIABLE_KEY(key_multi_msgplayer8)
  };

  static default_collection_t extra_defaults =
@@@ -1183,53 -1313,61 +1302,54 @@@ 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:
 -                case DEFAULT_INT_HEX:
 -                    * (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;
 +
 +            case DEFAULT_INT:
++            case DEFAULT_INT_HEX:
 +                * (int *) def->location = ParseIntParameter(strparm);
 +                break;
 +
 +            case DEFAULT_KEY:

 -                    defaults[i].original_translated = intparm;
 -                    * (int *) def->location = intparm;
 -                    break;
 +                // translate scancodes read from config
 +                // file (save the old value in untranslated)

 -                case DEFAULT_FLOAT:
 -                    * (float *) def->location = (float) atof(strparm);
 -                    break;
 -            }
 +                intparm = ParseIntParameter(strparm);
 +                def->untranslated = 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_controls.c
index 98b1e51b,00000000..60a96ac2
mode 100644,000000..100644
--- a/src/m_controls.c
+++ b/src/m_controls.c
@@@ -1,350 -1,0 +1,393 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 1993-2008 Raven Software
 +// Copyright(C) 2005-8 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.
 +//
 +//-----------------------------------------------------------------------------
 +
++#include <stdio.h>
++
 +#include "doomtype.h"
 +#include "doomkeys.h"
 +
 +#include "m_config.h"
 +
 +//
 +// Keyboard controls
 +//
 +
 +int key_right = KEY_RIGHTARROW;
 +int key_left = KEY_LEFTARROW;
 +
 +int key_up = KEY_UPARROW;
 +int key_down = KEY_DOWNARROW;
 +int key_strafeleft = ',';
 +int key_straferight = '.';
 +int key_fire = KEY_RCTRL;
 +int key_use = ' ';
 +int key_strafe = KEY_RALT;
 +int key_speed = KEY_RSHIFT;
 +
 +//
 +// Heretic keyboard controls
 +//
 +
 +int key_flyup = KEY_PGUP;
 +int key_flydown = KEY_INS;
 +int key_flycenter = KEY_HOME;
 +
 +int key_lookup = KEY_PGDN;
 +int key_lookdown = KEY_DEL;
 +int key_lookcenter = KEY_END;
 +
 +int key_invleft = '[';
 +int key_invright = ']';
 +int key_useartifact = KEY_ENTER;
 +
 +//
 +// Hexen key controls
 +//
 +
 +int key_jump = '/';
 +
 +//
 +// Mouse controls
 +//
 +
 +int mousebfire = 0;
 +int mousebstrafe = 1;
 +int mousebforward = 2;
 +
 +int mousebjump = -1;
 +
 +int mousebstrafeleft = -1;
 +int mousebstraferight = -1;
 +int mousebbackward = -1;
 +int mousebuse = -1;
 +
++int mousebprevweapon = -1;
++int mousebnextweapon = -1;
++
++
 +int key_message_refresh = KEY_ENTER;
 +int key_pause = KEY_PAUSE;
++int key_demo_quit = 'q';
++int key_spy = KEY_F12;
++
++// Multiplayer chat keys:
++
++int key_multi_msg = 't';
++int key_multi_msgplayer[8];
 +
 +// Weapon selection keys:
 +
 +int key_weapon1 = '1';
 +int key_weapon2 = '2';
 +int key_weapon3 = '3';
 +int key_weapon4 = '4';
 +int key_weapon5 = '5';
 +int key_weapon6 = '6';
 +int key_weapon7 = '7';
 +int key_weapon8 = '8';
++int key_prevweapon = 0;
++int key_nextweapon = 0;
 +
- // Map cotnrols keys:
++// Map control keys:
 +
 +int key_map_north     = KEY_UPARROW;
 +int key_map_south     = KEY_DOWNARROW;
 +int key_map_east      = KEY_RIGHTARROW;
 +int key_map_west      = KEY_LEFTARROW;
 +int key_map_zoomin    = '=';
 +int key_map_zoomout   = '-';
 +int key_map_toggle    = KEY_TAB;
 +int key_map_maxzoom   = '0';
 +int key_map_follow    = 'f';
 +int key_map_grid      = 'g';
 +int key_map_mark      = 'm';
 +int key_map_clearmark = 'c';
 +
 +// menu keys:
 +
 +int key_menu_activate  = KEY_ESCAPE;
 +int key_menu_up        = KEY_UPARROW;
 +int key_menu_down      = KEY_DOWNARROW;
 +int key_menu_left      = KEY_LEFTARROW;
 +int key_menu_right     = KEY_RIGHTARROW;
 +int key_menu_back      = KEY_BACKSPACE;
 +int key_menu_forward   = KEY_ENTER;
 +int key_menu_confirm   = 'y';
 +int key_menu_abort     = 'n';
 +
 +int key_menu_help      = KEY_F1;
 +int key_menu_save      = KEY_F2;
 +int key_menu_load      = KEY_F3;
 +int key_menu_volume    = KEY_F4;
 +int key_menu_detail    = KEY_F5;
 +int key_menu_qsave     = KEY_F6;
 +int key_menu_endgame   = KEY_F7;
 +int key_menu_messages  = KEY_F8;
 +int key_menu_qload     = KEY_F9;
 +int key_menu_quit      = KEY_F10;
 +int key_menu_gamma     = KEY_F11;
 +
 +int key_menu_incscreen = KEY_EQUALS;
 +int key_menu_decscreen = KEY_MINUS;
 +
 +//
 +// Joystick controls
 +//
 +
 +int joybfire = 0;
 +int joybstrafe = 1;
 +int joybuse = 3;
 +int joybspeed = 2;
 +
 +int joybstrafeleft = -1;
 +int joybstraferight = -1;
 +
 +int joybjump = -1;
 +
++int joybprevweapon = -1;
++int joybnextweapon = -1;
++
 +// Control whether if a mouse button is double clicked, it acts like
 +// "use" has been pressed
 +
 +int dclick_use = 1;
 +
 +//
 +// Bind all of the common controls used by Doom and all other games.
 +//
 +
 +void M_BindBaseControls(void)
 +{
 +    M_BindVariable("key_right",          &key_right),
 +    M_BindVariable("key_left",           &key_left),
 +    M_BindVariable("key_up",             &key_up),
 +    M_BindVariable("key_down",           &key_down),
 +    M_BindVariable("key_strafeleft",     &key_strafeleft),
 +    M_BindVariable("key_straferight",    &key_straferight),
 +    M_BindVariable("key_fire",           &key_fire),
 +    M_BindVariable("key_use",            &key_use),
 +    M_BindVariable("key_strafe",         &key_strafe),
 +    M_BindVariable("key_speed",          &key_speed),
 +
 +    M_BindVariable("mouseb_fire",        &mousebfire),
 +    M_BindVariable("mouseb_strafe",      &mousebstrafe),
 +    M_BindVariable("mouseb_forward",     &mousebforward),
 +
 +    M_BindVariable("joyb_fire",          &joybfire),
 +    M_BindVariable("joyb_strafe",        &joybstrafe),
 +    M_BindVariable("joyb_use",           &joybuse),
 +    M_BindVariable("joyb_speed",         &joybspeed),
 +
 +    // Extra controls that are not in the Vanilla versions:
-
++
 +    M_BindVariable("joyb_strafeleft",    &joybstrafeleft);
 +    M_BindVariable("joyb_straferight",   &joybstraferight);
 +    M_BindVariable("mouseb_strafeleft",  &mousebstrafeleft);
 +    M_BindVariable("mouseb_straferight", &mousebstraferight);
 +    M_BindVariable("mouseb_use",         &mousebuse);
 +    M_BindVariable("mouseb_backward",    &mousebbackward);
 +    M_BindVariable("dclick_use",         &dclick_use);
 +    M_BindVariable("key_pause",          &key_pause);
 +    M_BindVariable("key_message_refresh", &key_message_refresh);
 +}
 +
 +void M_BindHereticControls(void)
 +{
 +    M_BindVariable("key_flyup",          &key_flyup);
 +    M_BindVariable("key_flydown",        &key_flydown);
 +    M_BindVariable("key_flycenter",      &key_flycenter);
 +
 +    M_BindVariable("key_lookup",         &key_lookup);
 +    M_BindVariable("key_lookdown",       &key_lookdown);
 +    M_BindVariable("key_lookcenter",     &key_lookcenter);
 +
 +    M_BindVariable("key_invleft",        &key_invleft);
 +    M_BindVariable("key_invright",       &key_invright);
 +    M_BindVariable("key_useartifact",    &key_useartifact);
 +}
 +
 +void M_BindHexenControls(void)
 +{
 +    M_BindVariable("key_jump",           &key_jump);
 +    M_BindVariable("mouseb_jump",        &mousebjump);
 +    M_BindVariable("joyb_jump",          &joybjump);
 +}
 +
 +void M_BindWeaponControls(void)
 +{
 +    M_BindVariable("key_weapon1",        &key_weapon1);
 +    M_BindVariable("key_weapon2",        &key_weapon2);
 +    M_BindVariable("key_weapon3",        &key_weapon3);
 +    M_BindVariable("key_weapon4",        &key_weapon4);
 +    M_BindVariable("key_weapon5",        &key_weapon5);
 +    M_BindVariable("key_weapon6",        &key_weapon6);
 +    M_BindVariable("key_weapon7",        &key_weapon7);
 +    M_BindVariable("key_weapon8",        &key_weapon8);
++
++    M_BindVariable("key_prevweapon",     &key_prevweapon);
++    M_BindVariable("key_nextweapon",     &key_nextweapon);
++
++    M_BindVariable("joyb_prevweapon",    &joybprevweapon);
++    M_BindVariable("joyb_nextweapon",    &joybnextweapon);
++
++    M_BindVariable("mouseb_prevweapon",  &mousebprevweapon);
++    M_BindVariable("mouseb_nextweapon",  &mousebnextweapon);
 +}
 +
 +void M_BindMapControls(void)
 +{
 +    M_BindVariable("key_map_north",      &key_map_north);
 +    M_BindVariable("key_map_south",      &key_map_south);
 +    M_BindVariable("key_map_east",       &key_map_east);
 +    M_BindVariable("key_map_west",       &key_map_west);
 +    M_BindVariable("key_map_zoomin",     &key_map_zoomin);
 +    M_BindVariable("key_map_zoomout",    &key_map_zoomout);
 +    M_BindVariable("key_map_toggle",     &key_map_toggle);
 +    M_BindVariable("key_map_maxzoom",    &key_map_maxzoom);
 +    M_BindVariable("key_map_follow",     &key_map_follow);
 +    M_BindVariable("key_map_grid",       &key_map_grid);
 +    M_BindVariable("key_map_mark",       &key_map_mark);
 +    M_BindVariable("key_map_clearmark",  &key_map_clearmark);
 +}
 +
 +void M_BindMenuControls(void)
 +{
 +    M_BindVariable("key_menu_activate",  &key_menu_activate);
 +    M_BindVariable("key_menu_up",        &key_menu_up);
 +    M_BindVariable("key_menu_down",      &key_menu_down);
 +    M_BindVariable("key_menu_left",      &key_menu_left);
 +    M_BindVariable("key_menu_right",     &key_menu_right);
 +    M_BindVariable("key_menu_back",      &key_menu_back);
 +    M_BindVariable("key_menu_forward",   &key_menu_forward);
 +    M_BindVariable("key_menu_confirm",   &key_menu_confirm);
 +    M_BindVariable("key_menu_abort",     &key_menu_abort);
 +
 +    M_BindVariable("key_menu_help",      &key_menu_help);
 +    M_BindVariable("key_menu_save",      &key_menu_save);
 +    M_BindVariable("key_menu_load",      &key_menu_load);
 +    M_BindVariable("key_menu_volume",    &key_menu_volume);
 +    M_BindVariable("key_menu_detail",    &key_menu_detail);
 +    M_BindVariable("key_menu_qsave",     &key_menu_qsave);
 +    M_BindVariable("key_menu_endgame",   &key_menu_endgame);
 +    M_BindVariable("key_menu_messages",  &key_menu_messages);
 +    M_BindVariable("key_menu_qload",     &key_menu_qload);
 +    M_BindVariable("key_menu_quit",      &key_menu_quit);
 +    M_BindVariable("key_menu_gamma",     &key_menu_gamma);
 +
 +    M_BindVariable("key_menu_incscreen", &key_menu_incscreen);
 +    M_BindVariable("key_menu_decscreen", &key_menu_decscreen);
++    M_BindVariable("key_demo_quit",      &key_demo_quit);
++    M_BindVariable("key_spy",            &key_spy);
++}
++
++void M_BindChatControls(unsigned int num_players)
++{
++    char name[20];
++    int i;
++
++    M_BindVariable("key_multi_msg",     &key_multi_msg);
++
++    for (i=0; i<num_players; ++i)
++    {
++        sprintf(name, "key_multi_msgplayer%i", i + 1);
++        M_BindVariable(name, &key_multi_msgplayer[i]);
++    }
 +}
 +
 +#ifdef _WIN32_WCE
 +
 +#define WIN32_LEAN_AND_MEAN
 +#include <windows.h>
 +
 +static int SystemHasKeyboard(void)
 +{
 +    HKEY key;
 +    DWORD valtype;
 +    DWORD valsize;
 +    DWORD result;
 +
 +    if (RegOpenKeyExW(HKEY_CURRENT_USER,
 +                      L"\\Software\\Microsoft\\Shell", 0,
 +                      KEY_READ, &key) != ERROR_SUCCESS)
 +    {
 +        return 0;
 +    }
 +
 +    valtype = REG_SZ;
 +    valsize = sizeof(DWORD);
 +
 +    if (RegQueryValueExW(key, L"HasKeyboard", NULL, &valtype,
 +                         (LPBYTE) &result, &valsize) != ERROR_SUCCESS)
 +    {
 +        result = 0;
 +    }
 +
 +    // Close the key
 +
 +    RegCloseKey(key);
 +
 +    return result;
 +}
 +
 +//
 +// Apply custom defaults for Windows CE.
 +//
 +
 +static void M_ApplyWindowsCEDefaults(void)
 +{
 +    // If the system doesn't have a keyboard, patch the default
 +    // configuration to use the hardware keys.
 +
 +    if (!SystemHasKeyboard())
 +    {
 +        key_use = KEY_F1;
 +        key_fire = KEY_F2;
 +        key_menu_activate = KEY_F3;
 +        key_map_toggle = KEY_F4;
 +
 +        key_menu_help = 0;
 +        key_menu_save = 0;
 +        key_menu_load = 0;
 +        key_menu_volume = 0;
 +
 +        key_menu_confirm = KEY_ENTER;
 +        key_menu_back = KEY_F2;
 +        key_menu_abort = KEY_F2;
 +    }
 +}
 +
 +#endif
 +
 +//
 +// Apply custom patches to the default values depending on the
 +// platform we are running on.
 +//
 +
 +void M_ApplyPlatformDefaults(void)
 +{
 +#ifdef _WIN32_WCE
 +    M_ApplyWindowsCEDefaults();
 +#endif
 +}
 +
diff --cc src/m_controls.h
index 2a4aa3df,73510861..389c94a0
--- a/src/m_controls.h
+++ b/src/m_controls.h
@@@ -36,58 -32,17 +36,66 @@@ extern int key_fire
  extern int key_use;
  extern int key_strafe;
  extern int key_speed;
 -extern int joybspeed;
 -extern int vanilla_keyboard_mapping;

 -extern int key_pause;
 +extern int key_jump;
 +
 +extern int key_flyup;
 +extern int key_flydown;
 +extern int key_flycenter;
 +extern int key_lookup;
 +extern int key_lookdown;
 +extern int key_lookcenter;
 +extern int key_invleft;
 +extern int key_invright;
 +extern int key_useartifact;

 -// Multiplayer messages:
 +extern int key_message_refresh;
 +extern int key_pause;

+ extern int key_multi_msg;
 -extern int key_multi_msgplayer[];
++extern int key_multi_msgplayer[8];
++
 +extern int key_weapon1;
 +extern int key_weapon2;
 +extern int key_weapon3;
 +extern int key_weapon4;
 +extern int key_weapon5;
 +extern int key_weapon6;
 +extern int key_weapon7;
 +extern int key_weapon8;
 +
++extern int key_demo_quit;
++extern int key_spy;
++extern int key_prevweapon;
++extern int key_nextweapon;
++
 +extern int key_map_north;
 +extern int key_map_south;
 +extern int key_map_east;
 +extern int key_map_west;
 +extern int key_map_zoomin;
 +extern int key_map_zoomout;
 +extern int key_map_toggle;
 +extern int key_map_maxzoom;
 +extern int key_map_follow;
 +extern int key_map_grid;
 +extern int key_map_mark;
 +extern int key_map_clearmark;
 +
 +extern int key_map_north;
 +extern int key_map_south;
 +extern int key_map_east;
 +extern int key_map_west;
 +extern int key_map_zoomin;
 +extern int key_map_zoomout;
 +extern int key_map_toggle;
 +extern int key_map_maxzoom;
 +extern int key_map_follow;
 +extern int key_map_grid;
 +extern int key_map_mark;
 +extern int key_map_clearmark;

 -// Menu keys:
 +// menu keys:

  extern int key_menu_activate;
  extern int key_menu_up;
@@@ -114,37 -70,38 +122,44 @@@ extern int key_menu_gamma
  extern int key_menu_incscreen;
  extern int key_menu_decscreen;

 -// Automap keys:
 +extern int mousebfire;
 +extern int mousebstrafe;
 +extern int mousebforward;

 -extern int key_map_north;
 -extern int key_map_south;
 -extern int key_map_east;
 -extern int key_map_west;
 -extern int key_map_zoomin;
 -extern int key_map_zoomout;
 -extern int key_map_toggle;
 -extern int key_map_maxzoom;
 -extern int key_map_follow;
 -extern int key_map_grid;
 -extern int key_map_mark;
 -extern int key_map_clearmark;
 +extern int mousebjump;

 -// Weapon keys:
 +extern int mousebstrafeleft;
 +extern int mousebstraferight;
 +extern int mousebbackward;
 +extern int mousebuse;

 -extern int key_weapon1;
 -extern int key_weapon2;
 -extern int key_weapon3;
 -extern int key_weapon4;
 -extern int key_weapon5;
 -extern int key_weapon6;
 -extern int key_weapon7;
 -extern int key_weapon8;
 -extern int key_prevweapon;
 -extern int key_nextweapon;
++extern int mousebprevweapon;
++extern int mousebnextweapon;
+
 -extern int key_message_refresh;
 -extern int key_demo_quit;
 +extern int joybfire;
 +extern int joybstrafe;
 +extern int joybuse;
 +extern int joybspeed;
 +
 +extern int joybjump;
 +
 +extern int joybstrafeleft;
 +extern int joybstraferight;
 +
++extern int joybprevweapon;
++extern int joybnextweapon;
++
 +extern int dclick_use;
-
++
 +void M_BindBaseControls(void);
 +void M_BindHereticControls(void);
 +void M_BindHexenControls(void);
 +void M_BindWeaponControls(void);
 +void M_BindMapControls(void);
 +void M_BindMenuControls(void);
++void M_BindChatControls(unsigned int num_players);

 -void ConfigKeyboard(void);
 +void M_ApplyPlatformDefaults(void);

 -#endif /* #ifndef SETUP_KEYBOARD_H */
 +#endif /* #ifndef __M_CONTROLS_H__ */

diff --cc src/setup/joystick.c
index 0094dd81,825cd8b5..fbe3a3f3
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@@ -425,12 -432,9 +425,14 @@@ void ConfigJoystick(void

      AddJoystickControl(button_table, "Strafe Left", &joybstrafeleft);
      AddJoystickControl(button_table, "Strafe Right", &joybstraferight);
+     AddJoystickControl(button_table, "Previous weapon", &joybprevweapon);
+     AddJoystickControl(button_table, "Next weapon", &joybnextweapon);

 +    if (gamemission == hexen)
 +    {
 +        AddJoystickControl(button_table, "Jump", &joybjump);
 +    }
 +
      TXT_SignalConnect(joystick_button, "pressed", CalibrateJoystick, NULL);
      TXT_SignalConnect(window, "closed", JoystickWindowClosed, NULL);

diff --cc src/setup/keyboard.c
index 6e746ee9,81976f85..3d9f9977
--- a/src/setup/keyboard.c
+++ b/src/setup/keyboard.c
@@@ -178,63 -229,28 +180,65 @@@ static void ConfigExtraKeys(TXT_UNCAST_

      table = TXT_NewTable(2);

 -    TXT_SetColumnWidths(table, 25, 10);
 +    TXT_SetColumnWidths(table, 20, 9);

 -    TXT_AddWidgets(table, TXT_NewLabel(" - Weapons - "),
 -                          TXT_NewStrut(0, 0),
 -                          NULL);
 +    if (extra_keys)
 +    {
 +        // When we have extra controls, a scrollable pane must be used.
 +
 +        scrollpane = TXT_NewScrollPane(0, 13, table);
 +        TXT_AddWidget(window, scrollpane);
 +
 +        AddSectionLabel(table, "View", false);
 +
 +        AddKeyControl(table, "Look up", &key_lookup);
 +        AddKeyControl(table, "Look down", &key_lookdown);
 +        AddKeyControl(table, "Center view", &key_lookcenter);
 +
 +        AddSectionLabel(table, "Flying", true);
 +
 +        AddKeyControl(table, "Fly up", &key_flyup);
 +        AddKeyControl(table, "Fly down", &key_flydown);
 +        AddKeyControl(table, "Fly center", &key_flycenter);
 +
 +        AddSectionLabel(table, "Inventory", true);
 +
 +        AddKeyControl(table, "Inventory left", &key_invleft);
 +        AddKeyControl(table, "Inventory right", &key_invright);
 +        AddKeyControl(table, "Use artifact", &key_useartifact);
 +    }
 +    else
 +    {
 +        TXT_AddWidget(window, table);
 +    }

 -    AddKeyControl(table, "Weapon 1",              &key_weapon1);
 -    AddKeyControl(table, "Weapon 2",              &key_weapon2);
 -    AddKeyControl(table, "Weapon 3",              &key_weapon3);
 -    AddKeyControl(table, "Weapon 4",              &key_weapon4);
 -    AddKeyControl(table, "Weapon 5",              &key_weapon5);
 -    AddKeyControl(table, "Weapon 6",              &key_weapon6);
 -    AddKeyControl(table, "Weapon 7",              &key_weapon7);
 -    AddKeyControl(table, "Weapon 8",              &key_weapon8);
 +    AddSectionLabel(table, "Weapons", extra_keys);
 +
 +    AddKeyControl(table, "Weapon 1", &key_weapon1);
 +    AddKeyControl(table, "Weapon 2", &key_weapon2);
 +    AddKeyControl(table, "Weapon 3", &key_weapon3);
 +    AddKeyControl(table, "Weapon 4", &key_weapon4);
 +    AddKeyControl(table, "Weapon 5", &key_weapon5);
 +    AddKeyControl(table, "Weapon 6", &key_weapon6);
 +    AddKeyControl(table, "Weapon 7", &key_weapon7);
 +    AddKeyControl(table, "Weapon 8", &key_weapon8);
+     AddKeyControl(table, "Previous weapon",       &key_prevweapon);
+     AddKeyControl(table, "Next weapon",           &key_nextweapon);
 +}

 -    TXT_AddWidgets(table, TXT_NewStrut(0, 1),
 -                          TXT_NewStrut(0, 1),
 -                          TXT_NewLabel(" - Menu navigation - "),
 -                          TXT_NewStrut(0, 0),
 -                          NULL);
 +static void OtherKeysDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
 +{
 +    txt_window_t *window;
 +    txt_table_t *table;
 +    txt_scrollpane_t *scrollpane;
 +
 +    window = TXT_NewWindow("Other keys");
 +
 +    table = TXT_NewTable(2);
 +
 +    TXT_SetColumnWidths(table, 25, 9);
 +
 +    AddSectionLabel(table, "Menu navigation", false);

      AddKeyControl(table, "Activate menu",         &key_menu_activate);
      AddKeyControl(table, "Move cursor up",        &key_menu_up);
@@@ -265,9 -286,26 +270,9 @@@
      AddKeyControl(table, "Decrease screen size",  &key_menu_decscreen);

      AddKeyControl(table, "Display last message",  &key_message_refresh);
+     AddKeyControl(table, "Finish recording demo", &key_demo_quit);

 -    TXT_AddWidgets(table, TXT_NewStrut(0, 1),
 -                          TXT_NewStrut(0, 1),
 -                          TXT_NewLabel(" - Multiplayer - "),
 -                          TXT_NewStrut(0, 0),
 -                          NULL);
 -
 -    AddKeyControl(table, "Send message",          &key_multi_msg);
 -    AddKeyControl(table, "- to green",            &key_multi_msgplayer[0]);
 -    AddKeyControl(table, "- to indigo",           &key_multi_msgplayer[1]);
 -    AddKeyControl(table, "- to brown",            &key_multi_msgplayer[2]);
 -    AddKeyControl(table, "- to red",              &key_multi_msgplayer[3]);
 -
 -    TXT_AddWidgets(table, TXT_NewStrut(0, 1),
 -                          TXT_NewStrut(0, 1),
 -                          TXT_NewLabel(" - Map - "),
 -                          TXT_NewStrut(0, 0),
 -                          NULL);
 -
 +    AddSectionLabel(table, "Map", true);
-
      AddKeyControl(table, "Toggle map",            &key_map_toggle);
      AddKeyControl(table, "Zoom in",               &key_map_zoomin);
      AddKeyControl(table, "Zoom out",              &key_map_zoomout);
@@@ -281,7 -319,7 +286,21 @@@
      AddKeyControl(table, "Mark location",         &key_map_mark);
      AddKeyControl(table, "Clear all marks",       &key_map_clearmark);

 -    scrollpane = TXT_NewScrollPane(0, 12, table);
++    AddSectionLabel(table, "Multiplayer", true);
++
++    AddKeyControl(table, "Send message",          &key_multi_msg);
++    AddKeyControl(table, "- to green",            &key_multi_msgplayer[0]);
++    AddKeyControl(table, "- to indigo",           &key_multi_msgplayer[1]);
++    AddKeyControl(table, "- to brown",            &key_multi_msgplayer[2]);
++    AddKeyControl(table, "- to red",              &key_multi_msgplayer[3]);
++
++    TXT_AddWidgets(table, TXT_NewStrut(0, 1),
++                          TXT_NewStrut(0, 1),
++                          TXT_NewLabel(" - Map - "),
++                          TXT_NewStrut(0, 0),
++                          NULL);
++
 +    scrollpane = TXT_NewScrollPane(0, 13, table);

      TXT_AddWidget(window, scrollpane);
  }
diff --cc src/setup/keyboard.h
index 2797ba8f,00000000..12059bf8
mode 100644,000000..100644
--- a/src/setup/keyboard.h
+++ b/src/setup/keyboard.h
@@@ -1,82 -1,0 +1,29 @@@
 +// 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.
 +//
 +
 +#ifndef SETUP_KEYBOARD_H
 +#define SETUP_KEYBOARD_H
 +
- // Menu keys:
-
- extern int key_menu_activate;
- extern int key_menu_up;
- extern int key_menu_down;
- extern int key_menu_left;
- extern int key_menu_right;
- extern int key_menu_back;
- extern int key_menu_forward;
- extern int key_menu_confirm;
- extern int key_menu_abort;
-
- extern int key_menu_help;
- extern int key_menu_save;
- extern int key_menu_load;
- extern int key_menu_volume;
- extern int key_menu_detail;
- extern int key_menu_qsave;
- extern int key_menu_endgame;
- extern int key_menu_messages;
- extern int key_menu_qload;
- extern int key_menu_quit;
- extern int key_menu_gamma;
-
- extern int key_menu_incscreen;
- extern int key_menu_decscreen;
-
- // Automap keys:
-
- extern int key_map_north;
- extern int key_map_south;
- extern int key_map_east;
- extern int key_map_west;
- extern int key_map_zoomin;
- extern int key_map_zoomout;
- extern int key_map_toggle;
- extern int key_map_maxzoom;
- extern int key_map_follow;
- extern int key_map_grid;
- extern int key_map_mark;
- extern int key_map_clearmark;
-
- // Weapon keys:
-
- extern int key_weapon1;
- extern int key_weapon2;
- extern int key_weapon3;
- extern int key_weapon4;
- extern int key_weapon5;
- extern int key_weapon6;
- extern int key_weapon7;
- extern int key_weapon8;
-
 +void ConfigKeyboard(void);
 +void BindKeyboardVariables(void);
 +
 +#endif /* #ifndef SETUP_KEYBOARD_H */
 +
diff --cc src/setup/mainmenu.c
index 46e4b4e6,006a3547..c3cb7db5
--- a/src/setup/mainmenu.c
+++ b/src/setup/mainmenu.c
@@@ -46,6 -45,61 +46,64 @@@
  #include "multiplayer.h"
  #include "sound.h"

+ static const int cheat_sequence[] =
+ {
+     KEY_UPARROW, KEY_UPARROW, KEY_DOWNARROW, KEY_DOWNARROW,
+     KEY_LEFTARROW, KEY_RIGHTARROW, KEY_LEFTARROW, KEY_RIGHTARROW,
+     'b', 'a', KEY_ENTER, 0
+ };
+
+ static unsigned int cheat_sequence_index = 0;
+
+ // I think these are good "sensible" defaults:
+
+ static void SensibleDefaults(void)
+ {
++#if 0
++    // TODO for raven-branch
+     key_up = 'w';
+     key_down = 's';
+     key_strafeleft = 'a';
+     key_straferight = 'd';
+     mousebprevweapon = 4;
+     mousebnextweapon = 3;
+     snd_musicdevice = 3;
+     joybspeed = 29;
+     vanilla_savegame_limit = 0;
+     vanilla_keyboard_mapping = 0;
+     vanilla_demo_limit = 0;
+     show_endoom = 0;
+     dclick_use = 0;
+     novert = 1;
++#endif
+ }
+
+ static int MainMenuKeyPress(txt_window_t *window, int key, void *user_data)
+ {
+     if (key == cheat_sequence[cheat_sequence_index])
+     {
+         ++cheat_sequence_index;
+
+         if (cheat_sequence[cheat_sequence_index] == 0)
+         {
+             SensibleDefaults();
+             cheat_sequence_index = 0;
+
+             window = TXT_NewWindow(NULL);
+             TXT_AddWidget(window, TXT_NewLabel("    \x01    "));
+             TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
+
+             return 1;
+         }
+     }
+     else
+     {
+         cheat_sequence_index = 0;
+     }
+
+     return 0;
+ }
+
  static void DoQuit(void *widget, void *dosave)
  {
      if (dosave != NULL)
diff --cc src/setup/mouse.c
index d464261f,16984721..5b555f88
--- a/src/setup/mouse.c
+++ b/src/setup/mouse.c
@@@ -29,16 -27,27 +29,16 @@@
  #include "execute.h"
  #include "txt_mouseinput.h"

 +#include "mode.h"
  #include "mouse.h"

 -int usemouse = 1;
 +static int usemouse = 1;

 -int novert = 0;
 -int mouseSensitivity = 5;
 -float mouse_acceleration = 2.0;
 -int mouse_threshold = 10;
 -int grabmouse = 1;
 -
 -int mousebfire = 0;
 -int mousebforward = 1;
 -int mousebstrafe = 2;
 -int mousebstrafeleft = -1;
 -int mousebstraferight = -1;
 -int mousebbackward = -1;
 -int mousebuse = -1;
 -int mousebprevweapon = -1;
 -int mousebnextweapon = -1;
 -
 -int dclick_use = 1;
 +static int novert = 0;
 +static int mouseSensitivity = 5;
- static float mouse_acceleration = 1.0;
++static float mouse_acceleration = 2.0;
 +static int mouse_threshold = 10;
 +static int grabmouse = 1;

  static int *all_mouse_buttons[] = {
      &mousebfire,
@@@ -48,7 -57,8 +48,9 @@@
      &mousebstraferight,
      &mousebbackward,
      &mousebuse,
-     &mousebjump
++    &mousebjump,
+     &mousebprevweapon,
+     &mousebnextweapon
  };

  static void MouseSetCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(variable))
@@@ -98,11 -108,8 +100,14 @@@ static void ConfigExtraButtons(TXT_UNCA
      AddMouseControl(buttons_table, "Use", &mousebuse);
      AddMouseControl(buttons_table, "Strafe left", &mousebstrafeleft);
      AddMouseControl(buttons_table, "Strafe right", &mousebstraferight);
 +
 +    if (gamemission == hexen)
 +    {
 +        AddMouseControl(buttons_table, "Jump", &mousebjump);
 +    }
++
+     AddMouseControl(buttons_table, "Previous weapon", &mousebprevweapon);
+     AddMouseControl(buttons_table, "Next weapon", &mousebnextweapon);
  }

  void ConfigMouse(void)
diff --cc src/setup/multiplayer.c
index f3b3221d,c5d0d1e9..24cd0670
--- a/src/setup/multiplayer.c
+++ b/src/setup/multiplayer.c
@@@ -23,16 -22,15 +23,17 @@@
  #include <stdlib.h>
  #include <string.h>

 -#include "d_englsh.h"
 -#include "textscreen.h"
  #include "doomtype.h"
 +#include "textscreen.h"

 -#include "configfile.h"
 -
 -#include "execute.h"
 +#include "d_iwad.h"
 +#include "m_config.h"
 +#include "doom/d_englsh.h"
++#include "m_controls.h"

  #include "multiplayer.h"
 +#include "mode.h"
 +#include "execute.h"

  #define NUM_WADS 10
  #define NUM_EXTRA_PARAMS 10
@@@ -849,19 -782,3 +850,53 @@@ void MultiplayerConfig(void
      TXT_AddWidget(window, table);
  }

 +void BindMultiplayerVariables(void)
 +{
 +    char buf[15];
 +    int i;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    M_BindVariable("player_name", &net_player_name);
 +#endif
 +
 +    for (i=0; i<10; ++i)
 +    {
 +        sprintf(buf, "chatmacro%i", i);
 +        M_BindVariable(buf, &chat_macros[i]);
 +    }
++
++    switch (gamemission)
++    {
++        case doom:
++            M_BindChatControls(4);
++            key_multi_msgplayer[0] = 'g';
++            key_multi_msgplayer[1] = 'i';
++            key_multi_msgplayer[2] = 'b';
++            key_multi_msgplayer[3] = 'r';
++            break;
++
++        case heretic:
++            M_BindChatControls(4);
++            key_multi_msgplayer[0] = 'g';
++            key_multi_msgplayer[1] = 'y';
++            key_multi_msgplayer[2] = 'r';
++            key_multi_msgplayer[3] = 'b';
++            break;
++
++        case hexen:
++            M_BindChatControls(8);
++            key_multi_msgplayer[0] = 'b';
++            key_multi_msgplayer[1] = 'r';
++            key_multi_msgplayer[2] = 'y';
++            key_multi_msgplayer[3] = 'g';
++            key_multi_msgplayer[4] = 'j';
++            key_multi_msgplayer[5] = 'w';
++            key_multi_msgplayer[6] = 'h';
++            key_multi_msgplayer[7] = 'p';
++            break;
++
++        default:
++            break;
++    }
 +}
 +
diff --cc src/setup/sound.c
index 61182753,59df0532..627549b5
--- a/src/setup/sound.c
+++ b/src/setup/sound.c
@@@ -60,34 -54,23 +60,35 @@@ static char *musicmode_strings[]
  {
      "Disabled",
      "OPL (Adlib/SB)",
 -    "Native MIDI"
 +    "Native MIDI",
 +    "CD audio"
  };

 -int snd_sfxdevice = SNDDEVICE_SB;
 -int numChannels = 8;
 -int sfxVolume = 15;
 +// Config file variables:

 +int snd_sfxdevice = SNDDEVICE_SB;
  int snd_musicdevice = SNDDEVICE_GENMIDI;
 -int musicVolume = 15;
 -
  int snd_samplerate = 22050;
+ int opl_io_port = 0x388;

 -int use_libsamplerate = 0;
 +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_musmode;
 +static int snd_musicmode;

  static void UpdateSndDevices(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data))
  {