foxygit / doom Log in
commit 8dab0a3e635db40359c8ddeb9afaa9eca626ee98
Merge: 005747a6 678a8f9a
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Fri Dec 10 22:44:01 2010 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Fri Dec 10 22:44:01 2010 +0000

    Merge from trunk. This is slightly out of date as I did the merge
    several days ago.

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

 Makefile.am            |   1 +
 NEWS                   |  48 +++-
 NOT-BUGS               | 112 ++++++++
 README                 |  10 +-
 data/convert-icon      |   7 +-
 man/docgen             |  25 +-
 opl/opl_sdl.c          |   4 +-
 pkg/config.make.in     |   1 +
 pkg/wince/wince-cabgen |  40 ++-
 src/Makefile.am        |   1 +
 src/d_iwad.c           |   1 +
 src/d_mode.h           |   1 +
 src/deh_io.c           | 141 ++++++++--
 src/deh_io.h           |   1 +
 src/deh_main.c         |  49 +++-
 src/deh_main.h         |   2 +
 src/doom/d_main.c      |  86 ++++--
 src/doom/d_net.c       |  24 +-
 src/doom/doomstat.h    |   1 -
 src/doom/m_menu.c      |   2 +
 src/doom/p_setup.c     |  12 +-
 src/doom/p_spec.c      |   5 +-
 src/doom/r_data.c      |  20 +-
 src/i_sdlsound.c       |  41 ++-
 src/i_video.c          | 207 ++++++++++++---
 src/i_video.h          |   1 +
 src/m_argv.c           |   2 +-
 src/m_config.c         |   6 +
 src/net_dedicated.c    |   2 +-
 src/net_defs.h         |   8 +
 src/net_loop.c         |  22 +-
 src/net_query.c        | 691 +++++++++++++++++++++++++++++++++++++++----------
 src/net_query.h        |  15 +-
 src/net_server.c       |  73 +++++-
 src/net_server.h       |   4 +
 src/setup/display.c    | 243 ++++++++++++++++-
 src/setup/joystick.c   |   4 +-
 textscreen/txt_sdl.c   |  33 ++-
 38 files changed, 1630 insertions(+), 316 deletions(-)

diff --cc src/d_iwad.c
index ab1dd823,89a7fba3..9bf53b9a
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@@ -39,19 -39,6 +39,20 @@@
  #include "w_wad.h"
  #include "z_zone.h"

 +static iwad_t iwads[] =
 +{
 +    { "doom2.wad",    doom2,     commercial, "Doom II" },
 +    { "plutonia.wad", pack_plut, commercial, "Final Doom: Plutonia Experiment" },
 +    { "tnt.wad",      pack_tnt,  commercial, "Final Doom: TNT: Evilution" },
 +    { "doom.wad",     doom,      retail,     "Doom" },
 +    { "doom1.wad",    doom,      shareware,  "Doom Shareware" },
 +    { "chex.wad",     doom,      shareware,  "Chex Quest" },
++    { "hacx.wad",     doom2,     commercial, "Hacx" },
 +    { "heretic.wad",  heretic,   retail,     "Heretic" },
 +    { "heretic1.wad", heretic,   shareware,  "Heretic Shareware" },
 +    { "hexen.wad",    hexen,     commercial, "Hexen" },
 +};
 +
  // Array of locations to search for IWAD files
  //
  // "128 IWAD search directories should be enough for anybody".
diff --cc src/d_mode.h
index f7e93562,00000000..29f61c10
mode 100644,000000..100644
--- a/src/d_mode.h
+++ b/src/d_mode.h
@@@ -1,94 -1,0 +1,95 @@@
 +// 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:
 +//   Functions and definitions relating to the game type and operational
 +//   mode.
 +//
 +//-----------------------------------------------------------------------------
 +
 +#ifndef __D_MODE__
 +#define __D_MODE__
 +
 +#include "doomtype.h"
 +
 +// The "mission" controls what game we are playing.
 +
 +typedef enum
 +{
 +    doom,            // Doom 1
 +    doom2,           // Doom 2
 +    pack_tnt,        // Final Doom: TNT: Evilution
 +    pack_plut,       // Final Doom: The Plutonia Experiment
 +    heretic,         // Heretic
 +    hexen,           // Hexen
 +
 +    none
 +} GameMission_t;
 +
 +// The "mode" allows more accurate specification of the game mode we are
 +// in: eg. shareware vs. registered.  So doom1.wad and doom.wad are the
 +// same mission, but a different mode.
 +
 +typedef enum
 +{
 +    shareware,       // Doom/Heretic shareware
 +    registered,      // Doom/Heretic registered
 +    commercial,      // Doom II/Hexen
 +    retail,          // Ultimate Doom
 +    indetermined     // Unknown.
 +} GameMode_t;
 +
 +// What version are we emulating?
 +
 +typedef enum
 +{
 +    exe_doom_1_9,    // Doom 1.9: used for shareware, registered and commercial
++    exe_hacx,        // Hacx
 +    exe_ultimate,    // Ultimate Doom (retail)
 +    exe_final,       // Final Doom
 +    exe_chex,        // Chex Quest executable (based on Final Doom)
 +
 +    exe_heretic_1_3, // Heretic 1.3
 +
 +    exe_hexen_1_1    // Hexen 1.1
 +} GameVersion_t;
 +
 +// Skill level.
 +
 +typedef enum
 +{
 +    sk_noitems = -1,        // the "-skill 0" hack
 +    sk_baby = 0,
 +    sk_easy,
 +    sk_medium,
 +    sk_hard,
 +    sk_nightmare
 +} skill_t;
 +
 +boolean D_ValidGameMode(GameMission_t mission, GameMode_t mode);
 +boolean D_ValidGameVersion(GameMission_t mission, GameVersion_t version);
 +boolean D_ValidEpisodeMap(GameMission_t mission, GameMode_t mode,
 +                          int episode, int map);
 +int D_GetNumEpisodes(GameMission_t mission, GameMode_t mode);
 +boolean D_IsEpisodeMap(GameMission_t mission);
 +
 +#endif /* #ifndef __D_MODE__ */
 +
diff --cc src/doom/d_main.c
index fed6a1b4,032c5caa..69dd6e9f
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@@ -424,22 -397,10 +422,14 @@@ void D_DoomLoop (void
      if (demorecording)
  	G_BeginRecording ();

-     if (M_CheckParm ("-debugfile"))
-     {
- 	char    filename[20];
- 	sprintf (filename,"debug%i.txt",consoleplayer);
- 	printf ("debug output to: %s\n",filename);
- 	debugfile = fopen (filename,"w");
-     }
-
      TryRunTics();

 -    I_InitGraphics ();
 +    I_SetWindowTitle(gamedescription);
 +    I_InitGraphics();
 +    I_EnableLoadingDisk();
 +    I_SetGrabMouseCallback(D_GrabMouseCallback);

 +    V_RestoreBuffer();
      R_ExecuteSetViewSize();

      D_StartGameLoop();
@@@ -606,233 -564,9 +596,245 @@@ void D_StartTitle (void
      D_AdvanceDemo ();
  }

 +// Strings for dehacked replacements of the startup banner
 +//
 +// These are from the original source: some of them are perhaps
 +// not used in any dehacked patches
 +
- static char *banners[] =
++static char *banners[] =
 +{
++    // doom2.wad
++    "                         "
++    "DOOM 2: Hell on Earth v%i.%i"
++    "                           ",
 +    // doom1.wad
 +    "                            "
 +    "DOOM Shareware Startup v%i.%i"
 +    "                           ",
 +    // doom.wad
 +    "                            "
 +    "DOOM Registered Startup v%i.%i"
 +    "                           ",
 +    // Registered DOOM uses this
 +    "                          "
 +    "DOOM System Startup v%i.%i"
 +    "                          ",
 +    // doom.wad (Ultimate DOOM)
 +    "                         "
 +    "The Ultimate DOOM Startup v%i.%i"
 +    "                        ",
-     // doom2.wad
-     "                         "
-     "DOOM 2: Hell on Earth v%i.%i"
-     "                           ",
 +    // tnt.wad
 +    "                     "
 +    "DOOM 2: TNT - Evilution v%i.%i"
 +    "                           ",
 +    // plutonia.wad
 +    "                   "
 +    "DOOM 2: Plutonia Experiment v%i.%i"
 +    "                           ",
 +};

 +//
 +// Get game name: if the startup banner has been replaced, use that.
 +// Otherwise, use the name given
 +//

 +static char *GetGameName(char *gamename)
 +{
 +    size_t i;
 +    char *deh_sub;
 +
 +    for (i=0; i<arrlen(banners); ++i)
 +    {
 +        // Has the banner been replaced?
 +
 +        deh_sub = DEH_String(banners[i]);
 +
 +        if (deh_sub != banners[i])
 +        {
 +            // Has been replaced
 +            // We need to expand via printf to include the Doom version
 +            // number
 +            // We also need to cut off spaces to get the basic name
 +
 +            gamename = Z_Malloc(strlen(deh_sub) + 10, PU_STATIC, 0);
 +            sprintf(gamename, deh_sub, DOOM_VERSION / 100, DOOM_VERSION % 100);
 +
 +            while (gamename[0] != '\0' && isspace(gamename[0]))
 +                strcpy(gamename, gamename+1);
 +
 +            while (gamename[0] != '\0' && isspace(gamename[strlen(gamename)-1]))
 +                gamename[strlen(gamename) - 1] = '\0';
 +
 +            return gamename;
 +        }
 +    }
 +
 +    return gamename;
 +}
 +
 +//
 +// Find out what version of Doom is playing.
 +//
 +
 +void D_IdentifyVersion(void)
 +{
 +    // gamemission is set up by the D_FindIWAD function.  But if
 +    // we specify '-iwad', we have to identify using
 +    // IdentifyIWADByName.  However, if the iwad does not match
 +    // any known IWAD name, we may have a dilemma.  Try to
 +    // identify by its contents.
 +
 +    if (gamemission == none)
 +    {
 +        unsigned int i;
 +
 +        for (i=0; i<numlumps; ++i)
 +        {
 +            if (!strncasecmp(lumpinfo[i].name, "MAP01", 8))
 +            {
 +                gamemission = doom2;
 +                break;
 +            }
 +            else if (!strncasecmp(lumpinfo[i].name, "E1M1", 8))
 +            {
 +                gamemission = doom;
 +                break;
 +            }
 +        }
 +
 +        if (gamemission == none)
 +        {
 +            // Still no idea.  I don't think this is going to work.
 +
 +            I_Error("Unknown or invalid IWAD file.");
 +        }
 +    }
 +
 +    // Make sure gamemode is set up correctly
 +
 +    if (gamemission == doom)
 +    {
 +        // Doom 1.  But which version?
 +
 +        if (W_CheckNumForName("E4M1") > 0)
 +        {
 +            // Ultimate Doom
 +
 +            gamemode = retail;
 +        }
 +        else if (W_CheckNumForName("E3M1") > 0)
 +        {
 +            gamemode = registered;
 +        }
 +        else
 +        {
 +            gamemode = shareware;
 +        }
 +    }
 +    else
 +    {
 +        // Doom 2 of some kind.
 +
 +        gamemode = commercial;
 +    }
 +}
 +
 +// Set the gamedescription string
 +
 +void D_SetGameDescription(void)
 +{
 +    gamedescription = "Unknown";
 +
 +    if (gamemission == doom)
 +    {
 +        // Doom 1.  But which version?
 +
 +        if (gamemode == retail)
 +        {
 +            // Ultimate Doom
 +
 +            gamedescription = GetGameName("The Ultimate DOOM");
 +        }
 +        else if (gamemode == registered)
 +        {
 +            gamedescription = GetGameName("DOOM Registered");
 +        }
 +        else if (gamemode == shareware)
 +        {
 +            gamedescription = GetGameName("DOOM Shareware");
 +        }
 +    }
 +    else
 +    {
 +        // Doom 2 of some kind.  But which mission?
 +
 +        if (gamemission == doom2)
 +            gamedescription = GetGameName("DOOM 2: Hell on Earth");
 +        else if (gamemission == pack_plut)
 +            gamedescription = GetGameName("DOOM 2: Plutonia Experiment");
 +        else if (gamemission == pack_tnt)
 +            gamedescription = GetGameName("DOOM 2: TNT - Evilution");
 +    }
 +}
 +
 +static void SetSaveGameDir(char *iwad_filename)
 +{
 +    char *sep;
 +    char *basefile;
 +
 +    // Extract the base filename
 +
 +    sep = strrchr(iwad_filename, DIR_SEPARATOR);
 +
 +    if (sep == NULL)
 +    {
 +        basefile = iwad_filename;
 +    }
 +    else
 +    {
 +        basefile = sep + 1;
 +    }
 +
 +    // ~/.chocolate-doom/savegames/
 +
 +    savegamedir = Z_Malloc(strlen(configdir) + 30, PU_STATIC, 0);
 +    sprintf(savegamedir, "%ssavegames%c", configdir,
 +                         DIR_SEPARATOR);
 +
 +    M_MakeDirectory(savegamedir);
 +
 +    // eg. ~/.chocolate-doom/savegames/doom2.wad/
 +
 +    sprintf(savegamedir + strlen(savegamedir), "%s%c",
 +            basefile, DIR_SEPARATOR);
 +
 +    M_MakeDirectory(savegamedir);
 +}
 +
 +// Check if the IWAD file is the Chex Quest IWAD.
 +// Returns true if this is chex.wad.
 +
 +static boolean CheckChex(char *iwadname)
 +{
 +    char *chex_iwadname = "chex.wad";
 +
 +    return (strlen(iwadname) > strlen(chex_iwadname)
 +     && !strcasecmp(iwadname + strlen(iwadname) - strlen(chex_iwadname),
 +                    chex_iwadname));
 +}
 +
++// Check if the IWAD file is the Hacx IWAD.
++// Returns true if this is hacx.wad.
++
++static boolean CheckHacx(char *iwadname)
++{
++    char *hacx_iwadname = "hacx.wad";
++
++    return (strlen(iwadname) > strlen(hacx_iwadname)
++     && !strcasecmp(iwadname + strlen(iwadname) - strlen(hacx_iwadname),
++                    hacx_iwadname));
++}
+
  //      print title for every printed line
  char            title[128];

@@@ -954,12 -702,10 +957,18 @@@ static void InitGameVersion(void
      {
          // Determine automatically

 -        if (gameversion == exe_chex || gameversion == exe_hacx)
 +        if (CheckChex(iwadfile))
          {
 -            // Already determined
 +            // chex.exe - identified by iwad filename
 +
 +            gameversion = exe_chex;
 +        }
++        else if (CheckHacx(iwadfile))
++        {
++            // hacx exe: identified by iwad filename
++
++            gameversion = exe_hacx;
+         }
          else if (gamemode == shareware || gamemode == registered)
          {
              // original
@@@ -1041,25 -787,20 +1050,39 @@@ static void LoadChexDeh(void
      }
  }

 +// Function called at exit to display the ENDOOM screen
 +
 +static void D_Endoom(void)
 +{
 +    byte *endoom;
 +
 +    // Don't show ENDOOM if we have it disabled, or we're running
 +    // in screensaver or control test mode.
 +
 +    if (!show_endoom || screensaver_mode || M_CheckParm("-testcontrols") > 0)
 +    {
 +        return;
 +    }
 +
 +    endoom = W_CacheLumpName(DEH_String("ENDOOM"), PU_STATIC);
 +
 +    I_Endoom(endoom);
 +}
 +
+ static void LoadHacxDeh(void)
+ {
+     // If this is the HACX IWAD, we need to load the DEHACKED lump.
+
+     if (gameversion == exe_hacx)
+     {
+         if (!DEH_LoadLumpByName("DEHACKED"))
+         {
+             I_Error("DEHACKED lump not found.  Please check that this is the "
+                     "Hacx v1.2 IWAD.");
+         }
+     }
+ }
+
  //
  // D_DoomMain
  //
@@@ -1365,8 -1253,9 +1409,9 @@@ void D_DoomMain (void
      D_IdentifyVersion();
      InitGameVersion();
      LoadChexDeh();
+     LoadHacxDeh();
      D_SetGameDescription();
 -    D_SetSaveGameDir();
 +    SetSaveGameDir(iwadfile);

      // Check for -file in shareware
      if (modifiedgame)
diff --cc src/doom/d_net.c
index 05d1875f,00000000..b307d97f
mode 100644,000000..100644
--- a/src/doom/d_net.c
+++ b/src/doom/d_net.c
@@@ -1,921 -1,0 +1,929 @@@
 +// 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:
 +//	DOOM Network game communication and protocol,
 +//	all OS independend parts.
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdlib.h>
 +
 +#include "doomfeatures.h"
 +
 +#include "d_main.h"
 +#include "m_argv.h"
 +#include "m_menu.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "g_game.h"
 +#include "doomdef.h"
 +#include "doomstat.h"
 +
 +#include "deh_main.h"
 +
 +#include "net_client.h"
 +#include "net_gui.h"
 +#include "net_io.h"
 +#include "net_query.h"
 +#include "net_server.h"
 +#include "net_sdl.h"
 +#include "net_loop.h"
 +
 +// The complete set of data for a particular tic.
 +
 +typedef struct
 +{
 +    ticcmd_t cmds[MAXPLAYERS];
 +    boolean ingame[MAXPLAYERS];
 +} ticcmd_set_t;
 +
 +//
 +// NETWORKING
 +//
 +// gametic is the tic about to (or currently being) run
 +// maketic is the tic that hasn't had control made for it yet
 +// recvtic is the latest tic received from the server.
 +//
 +// a gametic cannot be run until ticcmds are received for it
 +// from all players.
 +//
 +
 +ticcmd_set_t ticdata[BACKUPTICS];
 +ticcmd_t *netcmds;
 +
 +int             maketic;
 +int             recvtic;
 +
 +// Used for original sync code.
 +
 +static int      skiptics = 0;
 +
 +// Reduce the bandwidth needed by sampling game input less and transmitting
 +// less.  If ticdup is 2, sample half normal, 3 = one third normal, etc.
 +
 +int		ticdup;
 +
 +// Send this many extra (backup) tics in each packet.
 +
 +int             extratics;
 +
 +// Amount to offset the timer for game sync.
 +
 +fixed_t         offsetms;
 +
 +// Use new client syncronisation code
 +
 +boolean         new_sync = true;
 +
 +// 35 fps clock adjusted by offsetms milliseconds
 +
 +static int GetAdjustedTime(void)
 +{
 +    int time_ms;
 +
 +    time_ms = I_GetTimeMS();
 +
 +    if (new_sync)
 +    {
 +	// Use the adjustments from net_client.c only if we are
 +	// using the new sync mode.
 +
 +        time_ms += (offsetms / FRACUNIT);
 +    }
 +
 +    return (time_ms * TICRATE) / 1000;
 +}
 +
 +//
 +// NetUpdate
 +// Builds ticcmds for console player,
 +// sends out a packet
 +//
 +int      lasttime;
 +
 +void NetUpdate (void)
 +{
 +    int nowtime;
 +    int newtics;
 +    int	i;
 +    int	gameticdiv;
 +
 +    // If we are running with singletics (timing a demo), this
 +    // is all done separately.
 +
 +    if (singletics)
 +        return;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +
 +    // Run network subsystems
 +
 +    NET_CL_Run();
 +    NET_SV_Run();
 +
 +#endif
 +
 +    // check time
 +    nowtime = GetAdjustedTime() / ticdup;
 +    newtics = nowtime - lasttime;
 +
 +    lasttime = nowtime;
 +
 +    if (skiptics <= newtics)
 +    {
 +        newtics -= skiptics;
 +        skiptics = 0;
 +    }
 +    else
 +    {
 +        skiptics -= newtics;
 +        newtics = 0;
 +    }
 +
 +    // build new ticcmds for console player
 +    gameticdiv = gametic/ticdup;
 +
 +    for (i=0 ; i<newtics ; i++)
 +    {
 +        ticcmd_t cmd;
 +
 +	I_StartTic ();
 +	D_ProcessEvents ();
 +
 +        // Always run the menu
 +
 +        M_Ticker ();
 +
 +        if (drone)
 +        {
 +            // In drone mode, do not generate any ticcmds.
 +
 +            continue;
 +        }
 +
 +        if (new_sync)
 +        {
 +           // If playing single player, do not allow tics to buffer
 +           // up very far
 +
 +           if ((!netgame || demoplayback) && maketic - gameticdiv > 2)
 +               break;
 +
 +           // Never go more than ~200ms ahead
 +
 +           if (maketic - gameticdiv > 8)
 +               break;
 +        }
 +	else
 +	{
 +           if (maketic - gameticdiv >= 5)
 +               break;
 +	}
 +
 +	//printf ("mk:%i ",maketic);
 +	G_BuildTiccmd(&cmd);
 +
 +#ifdef FEATURE_MULTIPLAYER
 +
 +        if (netgame && !demoplayback)
 +        {
 +            NET_CL_SendTiccmd(&cmd, maketic);
 +        }
 +
 +#endif
 +        ticdata[maketic % BACKUPTICS].cmds[consoleplayer] = cmd;
 +        ticdata[maketic % BACKUPTICS].ingame[consoleplayer] = true;
 +
 +	++maketic;
 +    }
 +}
 +
 +// Called when a player leaves the game
 +
 +static void D_PlayerQuitGame(player_t *player)
 +{
 +    static char exitmsg[80];
 +    unsigned int player_num;
 +
 +    player_num = player - players;
 +
 +    // Do this the same way as Vanilla Doom does, to allow dehacked
 +    // replacements of this message
 +
 +    strncpy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg));
 +    exitmsg[sizeof(exitmsg) - 1] = '\0';
 +
 +    exitmsg[7] += player_num;
 +
 +    playeringame[player_num] = false;
 +    players[consoleplayer].message = exitmsg;
 +
 +    // TODO: check if it is sensible to do this:
 +
 +    if (demorecording)
 +    {
 +        G_CheckDemoStatus ();
 +    }
 +}
 +
 +static void D_Disconnected(void)
 +{
 +    // In drone mode, the game cannot continue once disconnected.
 +
 +    if (drone)
 +    {
 +        I_Error("Disconnected from server in drone mode.");
 +    }
 +
 +    // disconnected from server
 +
 +    printf("Disconnected from server.\n");
 +}
 +
 +//
 +// Invoked by the network engine when a complete set of ticcmds is
 +// available.
 +//
 +
 +void D_ReceiveTic(ticcmd_t *ticcmds, boolean *players_mask)
 +{
 +    int i;
 +
 +    // Disconnected from server?
 +
 +    if (ticcmds == NULL && players_mask == NULL)
 +    {
 +        D_Disconnected();
 +        return;
 +    }
 +
 +    for (i = 0; i < MAXPLAYERS; ++i)
 +    {
 +        if (!drone && i == consoleplayer)
 +        {
 +            // This is us.  Don't overwrite it.
 +        }
 +        else
 +        {
 +            ticdata[recvtic % BACKUPTICS].cmds[i] = ticcmds[i];
 +            ticdata[recvtic % BACKUPTICS].ingame[i] = players_mask[i];
 +        }
 +    }
 +
 +    ++recvtic;
 +}
 +
 +//
 +// Start game loop
 +//
 +// Called after the screen is set but before the game starts running.
 +//
 +
 +void D_StartGameLoop(void)
 +{
 +    lasttime = GetAdjustedTime() / ticdup;
 +}
 +
 +// Load game settings from the specified structure and
 +// set global variables.
 +
 +static void LoadGameSettings(net_gamesettings_t *settings)
 +{
 +    unsigned int i;
 +
 +    deathmatch = settings->deathmatch;
 +    ticdup = settings->ticdup;
 +    extratics = settings->extratics;
 +    startepisode = settings->episode;
 +    startmap = settings->map;
 +    startskill = settings->skill;
 +    startloadgame = settings->loadgame;
 +    lowres_turn = settings->lowres_turn;
 +    nomonsters = settings->nomonsters;
 +    fastparm = settings->fast_monsters;
 +    respawnparm = settings->respawn_monsters;
 +    timelimit = settings->timelimit;
 +
 +    if (lowres_turn)
 +    {
 +        printf("NOTE: Turning resolution is reduced; this is probably "
 +               "because there is a client recording a Vanilla demo.\n");
 +    }
 +
 +    new_sync = settings->new_sync;
 +
 +    if (new_sync == false)
 +    {
 +	printf("Syncing netgames like Vanilla Doom.\n");
 +    }
 +
 +    netgame = true;
 +    autostart = true;
 +
 +    if (!drone)
 +    {
 +        consoleplayer = settings->consoleplayer;
 +    }
 +    else
 +    {
 +        consoleplayer = 0;
 +    }
 +
 +    for (i=0; i<MAXPLAYERS; ++i)
 +    {
 +        playeringame[i] = i < settings->num_players;
 +    }
 +}
 +
 +// Save the game settings from global variables to the specified
 +// game settings structure.
 +
 +static void SaveGameSettings(net_gamesettings_t *settings,
 +                             net_connect_data_t *connect_data)
 +{
 +    int i;
 +
 +    // Fill in game settings structure with appropriate parameters
 +    // for the new game
 +
 +    settings->deathmatch = deathmatch;
 +    settings->episode = startepisode;
 +    settings->map = startmap;
 +    settings->skill = startskill;
 +    settings->loadgame = startloadgame;
 +    settings->gameversion = gameversion;
 +    settings->nomonsters = nomonsters;
 +    settings->fast_monsters = fastparm;
 +    settings->respawn_monsters = respawnparm;
 +    settings->timelimit = timelimit;
 +
 +    settings->lowres_turn = M_CheckParm("-record") > 0
 +                         && M_CheckParm("-longtics") == 0;
 +
 +    //!
 +    // @category net
 +    //
 +    // Use original game sync code.
 +    //
 +
 +    if (M_CheckParm("-oldsync") > 0)
 +	settings->new_sync = 0;
 +    else
 +	settings->new_sync = 1;
 +
 +    //!
 +    // @category net
 +    // @arg <n>
 +    //
 +    // Send n extra tics in every packet as insurance against dropped
 +    // packets.
 +    //
 +
 +    i = M_CheckParm("-extratics");
 +
 +    if (i > 0)
 +        settings->extratics = atoi(myargv[i+1]);
 +    else
 +        settings->extratics = 1;
 +
 +    //!
 +    // @category net
 +    // @arg <n>
 +    //
 +    // Reduce the resolution of the game by a factor of n, reducing
 +    // the amount of network bandwidth needed.
 +    //
 +
 +    i = M_CheckParm("-dup");
 +
 +    if (i > 0)
 +        settings->ticdup = atoi(myargv[i+1]);
 +    else
 +        settings->ticdup = 1;
 +
 +    //
 +    // Connect data
 +    //
 +
 +    // Game type fields:
 +
 +    connect_data->gamemode = gamemode;
 +    connect_data->gamemission = gamemission;
 +
 +    // Drone mode?
 +
 +    connect_data->drone = M_CheckParm("-drone") > 0;
 +
 +    // Are we recording a demo? Possibly set lowres turn mode
 +
 +    connect_data->lowres_turn = settings->lowres_turn;
 +}
 +
 +void D_InitSinglePlayerGame(void)
 +{
 +    int i;
 +
 +    // default values for single player
 +
 +    consoleplayer = 0;
 +    netgame = false;
 +    ticdup = 1;
 +    extratics = 1;
 +    lowres_turn = false;
 +    offsetms = 0;
 +
 +    for (i=0; i<MAXPLAYERS; i++)
 +    {
 +        playeringame[i] = false;
 +    }
 +
 +    recvtic = 0;
 +
 +    playeringame[0] = true;
 +}
 +
 +boolean D_InitNetGame(net_connect_data_t *connect_data,
 +                      net_gamesettings_t *settings)
 +{
 +    net_addr_t *addr = NULL;
 +    int i;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +
 +    //!
 +    // @category net
 +    //
 +    // Start a multiplayer server, listening for connections.
 +    //
 +
 +    if (M_CheckParm("-server") > 0)
 +    {
 +        NET_SV_Init();
 +        NET_SV_AddModule(&net_loop_server_module);
 +        NET_SV_AddModule(&net_sdl_module);
 +
 +        net_loop_client_module.InitClient();
 +        addr = net_loop_client_module.ResolveAddress(NULL);
 +    }
 +    else
 +    {
 +        //!
 +        // @category net
 +        //
 +        // Automatically search the local LAN for a multiplayer
 +        // server and join it.
 +        //
 +
 +        i = M_CheckParm("-autojoin");
 +
 +        if (i > 0)
 +        {
 +            addr = NET_FindLANServer();
++            NET_SV_RegisterWithMaster();
 +
 +            if (addr == NULL)
 +            {
 +                I_Error("No server found on local LAN");
 +            }
 +        }
 +
 +        //!
 +        // @arg <address>
 +        // @category net
 +        //
 +        // Connect to a multiplayer server running on the given
 +        // address.
 +        //
 +
 +        i = M_CheckParm("-connect");
 +
 +        if (i > 0)
 +        {
 +            net_sdl_module.InitClient();
 +            addr = net_sdl_module.ResolveAddress(myargv[i+1]);
 +
 +            if (addr == NULL)
 +            {
 +                I_Error("Unable to resolve '%s'\n", myargv[i+1]);
 +            }
 +        }
 +    }
 +
 +    if (addr != NULL)
 +    {
 +        if (M_CheckParm("-drone") > 0)
 +        {
 +            connect_data->drone = true;
 +        }
 +
 +        //!
 +        // @category net
 +        //
 +        // Run as the left screen in three screen mode.
 +        //
 +
 +        if (M_CheckParm("-left") > 0)
 +        {
 +            viewangleoffset = ANG90;
 +            connect_data->drone = true;
 +        }
 +
 +        //!
 +        // @category net
 +        //
 +        // Run as the right screen in three screen mode.
 +        //
 +
 +        if (M_CheckParm("-right") > 0)
 +        {
 +            viewangleoffset = ANG270;
 +            connect_data->drone = true;
 +        }
 +
 +        if (!NET_CL_Connect(addr, connect_data))
 +        {
 +            I_Error("D_CheckNetGame: Failed to connect to %s\n",
 +                    NET_AddrToString(addr));
 +        }
 +
 +        printf("D_CheckNetGame: Connected to %s\n", NET_AddrToString(addr));
 +
 +        // Wait for game start message received from server.
 +
 +        NET_WaitForStart(settings);
 +
 +        // Read the game settings that were received.
 +
 +        NET_CL_GetSettings(settings);
 +
 +        return true;
 +    }
 +
 +#endif
 +
 +    return false;
 +}
 +
 +//
 +// D_CheckNetGame
 +// Works out player numbers among the net participants
 +//
 +extern	int			viewangleoffset;
 +
 +void D_CheckNetGame (void)
 +{
 +    net_connect_data_t connect_data;
 +    net_gamesettings_t settings;
 +
 +    // Call D_QuitNetGame on exit
 +
 +    I_AtExit(D_QuitNetGame, true);
 +
 +    SaveGameSettings(&settings, &connect_data);
 +
 +    if (D_InitNetGame(&connect_data, &settings))
 +    {
 +        LoadGameSettings(&settings);
 +    }
 +    else
 +    {
 +        D_InitSinglePlayerGame();
 +    }
 +
 +    DEH_printf("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n",
 +               startskill, deathmatch, startmap, startepisode);
 +
 +    DEH_printf("player %i of %i (%i nodes)\n",
 +               consoleplayer+1, settings.num_players, settings.num_players);
 +
 +    // Show players here; the server might have specified a time limit
 +
-     if (timelimit > 0)
++    if (timelimit > 0 && deathmatch)
 +    {
- 	DEH_printf("Levels will end after %d minute", timelimit);
- 	if (timelimit > 1)
- 	    printf("s");
- 	printf(".\n");
++        // Gross hack to work like Vanilla:
++
++        if (timelimit == 20 && M_CheckParm("-avg"))
++        {
++            DEH_printf("Austin Virtual Gaming: Levels will end "
++                           "after 20 minutes\n");
++        }
++        else
++        {
++            DEH_printf("Levels will end after %d minute", timelimit);
++            if (timelimit > 1)
++                printf("s");
++            printf(".\n");
++        }
 +    }
 +}
 +
 +
 +//
 +// D_QuitNetGame
 +// Called before quitting to leave a net game
 +// without hanging the other players
 +//
 +void D_QuitNetGame (void)
 +{
-     if (debugfile)
- 	fclose (debugfile);
-
 +#ifdef FEATURE_MULTIPLAYER
 +
 +    NET_SV_Shutdown();
 +    NET_CL_Disconnect();
 +
 +#endif
 +
 +}
 +
 +static int GetLowTic(void)
 +{
 +    int lowtic;
 +
 +    lowtic = maketic;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    if (net_client_connected)
 +    {
 +        if (drone || recvtic < lowtic)
 +        {
 +            lowtic = recvtic;
 +        }
 +    }
 +#endif
 +
 +    return lowtic;
 +}
 +
 +int	frametics[4];
 +int	frameon;
 +int	frameskip[4];
 +int	oldnettics;
 +
 +static void OldNetSync(void)
 +{
 +    unsigned int i;
 +    unsigned int keyplayer = -1;
 +
 +    frameon++;
 +
 +    // ideally maketic should be 1 - 3 tics above lowtic
 +    // if we are consistantly slower, speed up time
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +        // TODO: playeringame should not be used here.
 +
 +        if (playeringame[i])
 +        {
 +            keyplayer = i;
 +            break;
 +        }
 +    }
 +
 +    if (keyplayer < 0)
 +    {
 +        // If there are no players, we can never advance anyway
 +
 +        return;
 +    }
 +
 +    if (consoleplayer == keyplayer)
 +    {
 +        // the key player does not adapt
 +    }
 +    else
 +    {
 +        if (maketic <= recvtic)
 +        {
 +            lasttime--;
 +            // printf ("-");
 +        }
 +
 +        frameskip[frameon & 3] = oldnettics > recvtic;
 +        oldnettics = maketic;
 +
 +        if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
 +        {
 +            skiptics = 1;
 +            // printf ("+");
 +        }
 +    }
 +}
 +
 +// Returns true if there are players in the game:
 +
 +static boolean PlayersInGame(void)
 +{
 +    boolean result = false;
 +    unsigned int i;
 +
 +    // If we are connected to a server, check if there are any players
 +    // in the game.
 +
 +    if (net_client_connected)
 +    {
 +        for (i = 0; i < MAXPLAYERS; ++i)
 +        {
 +            result = result || playeringame[i];
 +        }
 +    }
 +
 +    // Whether single or multi-player, unless we are running as a drone,
 +    // we are in the game.
 +
 +    if (!drone)
 +    {
 +        result = true;
 +    }
 +
 +    return result;
 +}
 +
 +// When using ticdup, certain values must be cleared out when running
 +// the duplicate ticcmds.
 +
 +static void TicdupSquash(ticcmd_set_t *set)
 +{
 +    ticcmd_t *cmd;
 +    unsigned int i;
 +
 +    for (i = 0; i < MAXPLAYERS ; ++i)
 +    {
 +        cmd = &set->cmds[i];
 +        cmd->chatchar = 0;
 +        if (cmd->buttons & BT_SPECIAL)
 +            cmd->buttons = 0;
 +    }
 +}
 +
 +static void D_RunTic(ticcmd_set_t *set)
 +{
 +    extern boolean advancedemo;
 +    unsigned int i;
 +
 +    // Check for player quits.
 +
 +    for (i = 0; i < MAXPLAYERS; ++i)
 +    {
 +        if (playeringame[i] && !set->ingame[i])
 +        {
 +            D_PlayerQuitGame(&players[i]);
 +        }
 +    }
 +
 +    netcmds = set->cmds;
 +
 +    // check that there are players in the game.  if not, we cannot
 +    // run a tic.
 +
 +    if (advancedemo)
 +        D_DoAdvanceDemo ();
 +
 +    G_Ticker ();
 +}
 +
 +// When running in single player mode, clear all the ingame[] array
 +// except the consoleplayer.
 +
 +static void SinglePlayerClear(ticcmd_set_t *set)
 +{
 +    unsigned int i;
 +
 +    for (i = 0; i < MAXPLAYERS; ++i)
 +    {
 +        if (i != consoleplayer)
 +        {
 +            set->ingame[i] = false;
 +        }
 +    }
 +}
 +
 +//
 +// TryRunTics
 +//
 +
 +void TryRunTics (void)
 +{
 +    int	i;
 +    int	lowtic;
 +    int	entertic;
 +    static int oldentertics;
 +    int realtics;
 +    int	availabletics;
 +    int	counts;
 +
 +    // get real tics
 +    entertic = I_GetTime() / ticdup;
 +    realtics = entertic - oldentertics;
 +    oldentertics = entertic;
 +
 +    // get available tics
 +    NetUpdate ();
 +
 +    lowtic = GetLowTic();
 +
 +    availabletics = lowtic - gametic/ticdup;
 +
 +    // decide how many tics to run
 +
 +    if (new_sync)
 +    {
 +	counts = availabletics;
 +    }
 +    else
 +    {
 +        // decide how many tics to run
 +        if (realtics < availabletics-1)
 +            counts = realtics+1;
 +        else if (realtics < availabletics)
 +            counts = realtics;
 +        else
 +            counts = availabletics;
 +
 +        if (counts < 1)
 +            counts = 1;
 +
 +        if (net_client_connected)
 +        {
 +            OldNetSync();
 +        }
 +    }
 +
 +    if (counts < 1)
 +	counts = 1;
 +
 +    // wait for new tics if needed
 +
 +    while (!PlayersInGame() || lowtic < gametic/ticdup + counts)
 +    {
 +	NetUpdate ();
 +
 +        lowtic = GetLowTic();
 +
 +	if (lowtic < gametic/ticdup)
 +	    I_Error ("TryRunTics: lowtic < gametic");
 +
 +        // Don't stay in this loop forever.  The menu is still running,
 +        // so return to update the screen
 +
 +	if (I_GetTime() / ticdup - entertic > 0)
 +	{
 +	    return;
 +	}
 +
 +        I_Sleep(1);
 +    }
 +
 +    // run the count * ticdup dics
 +    while (counts--)
 +    {
 +        ticcmd_set_t *set;
 +
 +        if (!PlayersInGame())
 +        {
 +            return;
 +        }
 +
 +        set = &ticdata[(gametic / ticdup) % BACKUPTICS];
 +
 +        if (!net_client_connected)
 +        {
 +            SinglePlayerClear(set);
 +        }
 +
 +	for (i=0 ; i<ticdup ; i++)
 +	{
 +            if (gametic/ticdup > lowtic)
 +                I_Error ("gametic>lowtic");
 +
 +            D_RunTic(set);
 +	    gametic++;
 +
 +	    // modify command for duplicated tics
 +
 +            TicdupSquash(set);
 +	}
 +
 +	NetUpdate ();	// check for new console commands
 +    }
 +}
 +
diff --cc src/i_sdlsound.c
index 7deb683d,a26a81dd..1cfafa6f
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@@ -53,9 -53,12 +53,11 @@@
  #define MAX_SOUND_SLICE_TIME 70 /* ms */
  #define NUM_CHANNELS 16

+ static boolean setpanning_workaround = false;
+
  static boolean sound_initialized = false;

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

  static int mixer_freq;
  static Uint16 mixer_format;
@@@ -627,11 -622,16 +629,21 @@@ static void I_SDL_UpdateSoundParams(in
      left = ((254 - sep) * vol) / 127;
      right = ((sep) * vol) / 127;

 +    if (left < 0) left = 0;
 +    else if ( left > 255) left = 255;
 +    if (right < 0) right = 0;
 +    else if (right > 255) right = 255;
 +
+     // SDL_mixer version 1.2.8 and earlier has a bug in the Mix_SetPanning
+     // function.  A workaround is to call Mix_UnregisterAllEffects for
+     // the channel before calling it.  This is undesirable as it may lead
+     // to the channel volumes resetting briefly.
+
+     if (setpanning_workaround)
+     {
+         Mix_UnregisterAllEffects(handle);
+     }
+
      Mix_SetPanning(handle, left, right);
  }

diff --cc src/i_video.c
index 733af899,488c08a0..aa9640bf
--- a/src/i_video.c
+++ b/src/i_video.c
@@@ -131,14 -91,14 +131,20 @@@ static screen_mode_t *screen_modes_corr

  char *video_driver = "";

 +// SDL surface for the screen.
 +
  static SDL_Surface *screen;

 +// Window title
 +
 +static char *window_title = "";
 +
+ // Intermediate 8-bit buffer that we draw to instead of 'screen'.
+ // This is used when we are rendering in 32-bit screen mode.
+ // When in a real 8-bit screen mode, screenbuffer == screen.
+
+ static SDL_Surface *screenbuffer;
+
  // palette

  static SDL_Color palette[256];
@@@ -169,9 -123,13 +175,13 @@@ static boolean native_surface

  // Screen width and height, from configuration file.

 -int screen_width = SCREENWIDTH;
 -int screen_height = SCREENHEIGHT;
 +static int screen_width = SCREENWIDTH;
 +static int screen_height = SCREENHEIGHT;

+ // Color depth.
+
+ int screen_bpp = 8;
+
  // Automatically adjust video settings if the selected mode is
  // not a valid video mode.

@@@ -911,17 -760,18 +921,18 @@@ static boolean BlitArea(int x1, int y1
  	return true;
      }

-     x_offset = (screen->w - screen_mode->width) / 2;
-     y_offset = (screen->h - screen_mode->height) / 2;
+     x_offset = (screenbuffer->w - screen_mode->width) / 2;
+     y_offset = (screenbuffer->h - screen_mode->height) / 2;

-     if (SDL_LockSurface(screen) >= 0)
+     if (SDL_LockSurface(screenbuffer) >= 0)
      {
 -        I_InitScale(screens[0],
 +        I_InitScale(I_VideoBuffer,
-                     (byte *) screen->pixels + (y_offset * screen->pitch)
-                                             + x_offset,
+                     (byte *) screenbuffer->pixels
 -                                + (y_offset * screenbuffer->pitch)
++                                + (y_offset * screen->pitch)
+                                 + x_offset,
 -                    screenbuffer->pitch);
 +                    screen->pitch);
          result = screen_mode->DrawScreen(x1, y1, x2, y2);
-       	SDL_UnlockSurface(screen);
+       	SDL_UnlockSurface(screenbuffer);
      }
      else
      {
@@@ -1993,30 -1915,5 +2113,51 @@@ void I_InitGraphics(void
      }

      initialized = true;
 +
 +    // Call I_ShutdownGraphics on quit
 +
 +    I_AtExit(I_ShutdownGraphics, true);
 +}
 +
 +// Bind all variables controlling video options into the configuration
 +// file system.
 +
 +void I_BindVideoVariables(void)
 +{
 +    M_BindVariable("use_mouse",                 &usemouse);
 +    M_BindVariable("autoadjust_video_settings", &autoadjust_video_settings);
 +    M_BindVariable("fullscreen",                &fullscreen);
 +    M_BindVariable("aspect_ratio_correct",      &aspect_ratio_correct);
 +    M_BindVariable("startup_delay",             &startup_delay);
 +    M_BindVariable("screen_width",              &screen_width);
 +    M_BindVariable("screen_height",             &screen_height);
 +    M_BindVariable("grabmouse",                 &grabmouse);
 +    M_BindVariable("mouse_acceleration",        &mouse_acceleration);
 +    M_BindVariable("mouse_threshold",           &mouse_threshold);
 +    M_BindVariable("video_driver",              &video_driver);
 +    M_BindVariable("usegamma",                  &usegamma);
 +    M_BindVariable("vanilla_keyboard_mapping",  &vanilla_keyboard_mapping);
 +    M_BindVariable("novert",                    &novert);
++
++    // Windows Vista or later?  Set screen color depth to
++    // 32 bits per pixel, as 8-bit palettized screen modes
++    // don't work properly in recent versions.
++
++#if defined(_WIN32) && !defined(_WIN32_WCE)
++    {
++        OSVERSIONINFOEX version_info;
++
++        ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
++        version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
++
++        GetVersionEx((OSVERSIONINFO *) &version_info);
++
++        if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT
++         && version_info.dwMajorVersion >= 6)
++        {
++            screen_bpp = 32;
++        }
++    }
++#endif
  }

diff --cc src/i_video.h
index 82368967,44fe1cd2..c3c50c4b
--- a/src/i_video.h
+++ b/src/i_video.h
@@@ -92,36 -80,23 +92,37 @@@ void I_ReadScreen (byte* scr)
  void I_BeginRead (void);
  void I_EndRead (void);

 -void I_SetWindowCaption(void);
 -void I_SetWindowIcon(void);
 +void I_SetWindowTitle(char *title);

  void I_CheckIsScreensaver(void);
 +void I_SetGrabMouseCallback(grabmouse_callback_t func);
 +
 +void I_DisplayFPSDots(boolean dots_on);
 +void I_BindVideoVariables(void);
 +
 +// Called before processing any tics in a frame (just after displaying a frame).
 +// Time consuming syncronous operations are performed here (joystick reading).
 +
 +void I_StartFrame (void);
 +
 +// Called before processing each tic in a frame.
 +// Quick syncronous operations are performed here.
 +
 +void I_StartTic (void);
 +
 +// Enable the loading disk image displayed when reading from disk.
 +
 +void I_EnableLoadingDisk(void);

  extern char *video_driver;
 -extern int autoadjust_video_settings;
  extern boolean screenvisible;
 -extern int screen_width, screen_height;
 -extern int screen_bpp;
 -extern int fullscreen;
 -extern int aspect_ratio_correct;
 -extern int grabmouse;
++
  extern float mouse_acceleration;
  extern int mouse_threshold;
 -extern int startup_delay;
  extern int vanilla_keyboard_mapping;
 +extern boolean screensaver_mode;
 +extern int usegamma;
 +extern byte *I_VideoBuffer;

  #endif

diff --cc src/m_config.c
index a277fc38,3abe7c63..cb7d449c
--- a/src/m_config.c
+++ b/src/m_config.c
@@@ -552,8 -613,14 +552,14 @@@ static default_t extra_defaults_list[]
      // will run.
      //

 -    CONFIG_VARIABLE_INT(screen_height,             screen_height),
 +    CONFIG_VARIABLE_INT(screen_height),

+     //!
+     // Color depth of the screen, in bits.
+     //
+
 -    CONFIG_VARIABLE_INT(screen_bpp,                screen_bpp),
++    CONFIG_VARIABLE_INT(screen_bpp),
+
      //!
      // If this is non-zero, the mouse will be "grabbed" when running
      // in windowed mode so that it can be used as an input device.
diff --cc src/net_defs.h
index 72e0e6d2,d9e3b920..1fa9a9af
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@@ -128,22 -113,14 +128,30 @@@ typedef enu
      NET_PACKET_TYPE_QUERY_RESPONSE,
  } net_packet_type_t;

+ typedef enum
+ {
+     NET_MASTER_PACKET_TYPE_ADD,
+     NET_MASTER_PACKET_TYPE_ADD_RESPONSE,
+     NET_MASTER_PACKET_TYPE_QUERY,
+     NET_MASTER_PACKET_TYPE_QUERY_RESPONSE
+ } net_master_packet_type_t;
+
 +// Settings specified when the client connects to the server.
 +
 +typedef struct
 +{
 +    int gamemode;
 +    int gamemission;
 +    int lowres_turn;
 +    int drone;
 +    // TODO: is_freedoom in here?  WAD/DEH checksums?
 +    // TODO: [Hexen] Requested player class
 +
 +} net_connect_data_t;
 +
 +// Game settings sent by client to server when initiating game start,
 +// and received from the server by clients when the game starts.
 +
  typedef struct
  {
      int ticdup;
diff --cc src/setup/display.c
index 9fd0963b,42a1dced..0def546f
--- a/src/setup/display.c
+++ b/src/setup/display.c
@@@ -72,16 -90,15 +93,17 @@@ static screen_mode_t *screen_modes_full

  static int vidmode = 0;

 -char *video_driver = "";
 -int autoadjust_video_settings = 1;
 -int aspect_ratio_correct = 1;
 -int fullscreen = 1;
 -int screen_width = 320;
 -int screen_height = 200;
 -int screen_bpp = 8;
 -int startup_delay = 1000;
 -int show_endoom = 1;
 +static char *video_driver = "";
 +static int autoadjust_video_settings = 1;
 +static int aspect_ratio_correct = 1;
 +static int fullscreen = 1;
 +static int screen_width = 320;
 +static int screen_height = 200;
++static int screen_bpp = 8;
 +static int startup_delay = 1000;
 +static int graphical_startup = 1;
 +static int show_endoom = 1;
 +static int usegamma = 0;

  // These are the last screen width/height values that were chosen by the
  // user.  These are used when finding the "nearest" mode, so when
@@@ -442,49 -644,24 +649,79 @@@ void ConfigDisplay(void

      TXT_AddWidgets(window,
                     TXT_NewSeparator("Screen mode"),
+                    bpp_table = TXT_NewTable(2),
                     modes_table,
                     TXT_NewSeparator("Misc."),
 -                   TXT_NewCheckBox("Show ENDOOM screen", &show_endoom),
                     NULL);

 +    if (gamemission == heretic || gamemission == hexen)
 +    {
 +        TXT_AddWidget(window,
 +                      TXT_NewCheckBox("Graphical startup", &graphical_startup));
 +    }
 +
 +    if (gamemission == doom || gamemission == heretic)
 +    {
 +        TXT_AddWidget(window,
 +                      TXT_NewCheckBox("Show ENDOOM screen", &show_endoom));
 +    }
 +
+     TXT_AddWidgets(bpp_table,
+                    TXT_NewLabel("Color depth: "),
+                    bpp_selector = TXT_NewDropdownList(&selected_bpp,
+                                                       supported_bpps,
+                                                       num_supported_bpps),
+                    NULL);
+
+
+     TXT_SignalConnect(bpp_selector, "changed", UpdateBPP, modes_table);
      TXT_SignalConnect(fs_checkbox, "changed", GenerateModesTable, modes_table);
      TXT_SignalConnect(ar_checkbox, "changed", GenerateModesTable, modes_table);

      GenerateModesTable(NULL, modes_table);
  }

 +void BindDisplayVariables(void)
 +{
 +    M_BindVariable("autoadjust_video_settings", &autoadjust_video_settings);
 +    M_BindVariable("aspect_ratio_correct",      &aspect_ratio_correct);
 +    M_BindVariable("fullscreen",                &fullscreen);
 +    M_BindVariable("screen_width",              &screen_width);
 +    M_BindVariable("screen_height",             &screen_height);
 +    M_BindVariable("startup_delay",             &startup_delay);
 +    M_BindVariable("video_driver",              &video_driver);
 +    M_BindVariable("usegamma",                  &usegamma);
 +
 +
 +    if (gamemission == doom || gamemission == heretic)
 +    {
 +        M_BindVariable("show_endoom",               &show_endoom);
 +    }
 +
 +    if (gamemission == heretic || gamemission == hexen)
 +    {
 +        M_BindVariable("graphical_startup",        &graphical_startup);
 +    }
 +
++    // Windows Vista or later?  Set screen color depth to
++    // 32 bits per pixel, as 8-bit palettized screen modes
++    // don't work properly in recent versions.
++
++#if defined(_WIN32) && !defined(_WIN32_WCE)
++    {
++        OSVERSIONINFOEX version_info;
++
++        ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
++        version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
++
++        GetVersionEx((OSVERSIONINFO *) &version_info);
++
++        if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT
++         && version_info.dwMajorVersion >= 6)
++        {
++            screen_bpp = 32;
++        }
++    }
++#endif
 +}
 +
diff --cc src/setup/joystick.c
index fbe3a3f3,1fc9a7a9..0fc00ea1
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@@ -65,8 -72,8 +65,8 @@@ static int joystick_y_invert = 0
  static txt_button_t *joystick_button;

  static int *all_joystick_buttons[] = {
-         &joybstraferight, &joybstrafeleft, &joybfire, &joybspeed,
-         &joybuse, &joybstrafe, &joybjump
+     &joybstraferight, &joybstrafeleft, &joybfire, &joybspeed,
 -    &joybuse, &joybstrafe, &joybprevweapon, &joybnextweapon
++    &joybuse, &joybstrafe, &joybprevweapon, &joybnextweapon, &joybjump
  };

  //