foxygit / doom Log in
commit 993315afc4b1fddaf8952e7e55d1373b5052dd7c
Merge: a1b2ce54 ad11652d
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Sun Oct 28 23:45:08 2012 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Sun Oct 28 23:45:08 2012 +0000

    Merge from trunk.

    Subversion-branch: /branches/v2-branch
    Subversion-revision: 2537

 ChangeLog                      | 2944 +++++++++++++++++++++-------------------
 NEWS                           |   40 +-
 configure.in                   |    2 +-
 data/Makefile.am               |    4 +-
 src/Makefile.am                |    7 +-
 src/aes_prng.c                 | 1048 ++++++++++++++
 src/aes_prng.h                 |   40 +
 src/d_iwad.c                   |    6 +-
 src/deh_defs.h                 |    8 +-
 src/deh_main.c                 |   12 +-
 src/deh_main.h                 |    4 +-
 src/deh_mapping.c              |   10 +-
 src/deh_mapping.h              |    6 +-
 src/doom/d_main.c              |   26 +-
 src/doom/d_net.c               |    4 +-
 src/doom/deh_ammo.c            |    8 +-
 src/doom/deh_frame.c           |    6 +-
 src/doom/deh_misc.c            |    6 +-
 src/doom/deh_ptr.c             |    6 +-
 src/doom/deh_thing.c           |    6 +-
 src/doom/deh_weapon.c          |    6 +-
 src/doom/g_game.c              |    8 +
 src/doom/hu_stuff.c            |    5 +-
 src/doom/m_menu.c              |   43 +-
 src/doom/p_spec.c              |    5 +
 src/heretic/d_net.c            |    4 +-
 src/heretic/deh_ammo.c         |    8 +-
 src/heretic/deh_frame.c        |    6 +-
 src/heretic/deh_thing.c        |    6 +-
 src/heretic/deh_weapon.c       |    8 +-
 src/hexen/d_net.c              |    4 +-
 src/i_oplmusic.c               |  107 ++
 src/i_video.c                  |   45 +-
 src/m_misc.c                   |   23 +-
 src/m_misc.h                   |    1 +
 src/md5.c                      |  264 ----
 src/md5.h                      |   45 -
 src/net_client.c               |   19 +-
 src/net_client.h               |   10 +-
 src/net_defs.h                 |   18 +-
 src/net_gui.c                  |   32 +-
 src/net_query.c                |  127 ++
 src/net_sdl.c                  |   13 +-
 src/net_server.c               |   70 +-
 src/net_structrw.c             |   48 +-
 src/net_structrw.h             |   10 +-
 src/setup/Makefile.am          |    2 +-
 src/setup/multiplayer.c        |   34 +-
 src/sha1.c                     |  319 +++++
 src/sha1.h                     |   48 +
 src/strife/d_net.c             |    4 +-
 src/strife/deh_ammo.c          |    8 +-
 src/strife/deh_frame.c         |    6 +-
 src/strife/deh_misc.c          |    6 +-
 src/strife/deh_ptr.c           |    6 +-
 src/strife/deh_thing.c         |    6 +-
 src/strife/deh_weapon.c        |    6 +-
 src/w_checksum.c               |   24 +-
 src/w_checksum.h               |    2 +-
 textscreen/Makefile.am         |    1 +
 textscreen/examples/guitest.c  |    4 +
 textscreen/txt_button.c        |    2 -
 textscreen/txt_checkbox.c      |    8 +-
 textscreen/txt_dropdown.c      |    1 -
 textscreen/txt_gui.c           |  131 +-
 textscreen/txt_gui.h           |    7 +-
 textscreen/txt_inputbox.c      |   67 +-
 textscreen/txt_io.c            |  106 +-
 textscreen/txt_io.h            |    8 +
 textscreen/txt_label.c         |   33 +-
 textscreen/txt_label.h         |    4 +-
 textscreen/txt_main.h          |   30 +-
 textscreen/txt_radiobutton.c   |    7 +-
 textscreen/txt_sdl.c           |   12 +-
 textscreen/txt_separator.c     |    1 -
 textscreen/txt_spinctrl.c      |   13 +-
 textscreen/txt_utf8.c          |  160 +++
 textscreen/txt_utf8.h          |   31 +
 textscreen/txt_widget.c        |   12 +-
 textscreen/txt_window.c        |   14 +-
 textscreen/txt_window_action.c |    4 -
 81 files changed, 4194 insertions(+), 2061 deletions(-)

diff --cc configure.in
index 094a87c3,175a355f..77a7e6e9
--- a/configure.in
+++ b/configure.in
@@@ -1,7 -1,7 +1,7 @@@
 -AC_INIT(Chocolate Doom, 1.7.0, fraggle@gmail.com, chocolate-doom)
 +AC_INIT(Chocolate Doom, 1.99.0, fraggle@gmail.com, chocolate-doom)

  PACKAGE_SHORTDESC="Conservative Doom source port"
- PACKAGE_COPYRIGHT="Copyright (C) 1993-2011"
+ PACKAGE_COPYRIGHT="Copyright (C) 1993-2012"
  PACKAGE_LICENSE="GNU General Public License, version 2"
  PACKAGE_MAINTAINER="Simon Howard"
  PACKAGE_URL="http://www.chocolate-doom.org/"
diff --cc src/Makefile.am
index af7a43a2,25766ca0..8f393089
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@@ -67,15 -60,51 +67,15 @@@ i_videohr.c          i_videohr.
  m_bbox.c             m_bbox.h              \
  m_cheat.c            m_cheat.h             \
  m_config.c           m_config.h            \
 +m_controls.c         m_controls.h          \
  m_fixed.c            m_fixed.h             \
- md5.c                md5.h                 \
 -m_menu.c             m_menu.h              \
 -m_misc.c             m_misc.h              \
+ sha1.c               sha1.h                \
  memio.c              memio.h               \
 -m_random.c           m_random.h            \
 -p_ceilng.c                                 \
 -p_doors.c                                  \
 -p_enemy.c                                  \
 -p_floor.c                                  \
 -p_inter.c            p_inter.h             \
 -p_lights.c                                 \
 -p_local.h                                  \
 -p_map.c                                    \
 -p_maputl.c                                 \
 -p_mobj.c             p_mobj.h              \
 -p_plats.c                                  \
 -p_pspr.c             p_pspr.h              \
 -p_saveg.c            p_saveg.h             \
 -p_setup.c            p_setup.h             \
 -p_sight.c                                  \
 -p_spec.c             p_spec.h              \
 -p_switch.c                                 \
 -p_telept.c                                 \
 -p_tick.c             p_tick.h              \
 -p_user.c                                   \
 -r_bsp.c              r_bsp.h               \
 -r_data.c             r_data.h              \
 -r_defs.h                                   \
 -r_draw.c             r_draw.h              \
 -r_local.h                                  \
 -r_main.c             r_main.h              \
 -r_plane.c            r_plane.h             \
 -r_segs.c             r_segs.h              \
 -r_sky.c              r_sky.h               \
 -r_state.h                                  \
 -r_things.c           r_things.h            \
 -sounds.c             sounds.h              \
 -s_sound.c            s_sound.h             \
 -st_lib.c             st_lib.h              \
 -st_stuff.c           st_stuff.h            \
  tables.c             tables.h              \
  v_video.c            v_video.h             \
 -wi_stuff.c           wi_stuff.h            \
 +                     v_patch.h             \
  w_checksum.c         w_checksum.h          \
 +w_main.c             w_main.h              \
  w_wad.c              w_wad.h               \
  w_file.c             w_file.h              \
  w_file_stdc.c                              \
diff --cc src/d_iwad.c
index 8c29352b,6d0ade26..aefb6872
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@@ -173,8 -157,10 +173,12 @@@ static char *steam_install_subdirs[]
      "steamapps\\common\\doom 2\\base",
      "steamapps\\common\\final doom\\base",
      "steamapps\\common\\ultimate doom\\base",
 +    "steamapps\\common\\hexen\\base",
 +    "steamapps\\common\\heretic shadow of the serpent riders\\base"
+
+     // From Doom 3: BFG Edition:
+
+     "steamapps\\common\\DOOM 3 BFG Edition\\base\\wads",
  };

  static char *GetRegistryString(registry_value_t *reg_val)
diff --cc src/deh_main.c
index 39d59e8c,e61933fa..4bb3e383
--- a/src/deh_main.c
+++ b/src/deh_main.c
@@@ -54,18 -72,35 +54,18 @@@ boolean deh_allow_long_cheats = false

  boolean deh_apply_cheats = true;

- void DEH_Checksum(md5_digest_t digest)
 -//
 -// List of section types:
 -//
 -
 -static deh_section_t *section_types[] =
 -{
 -    &deh_section_ammo,
 -    &deh_section_cheat,
 -    &deh_section_frame,
 -    &deh_section_misc,
 -    &deh_section_pointer,
 -    &deh_section_sound,
 -    &deh_section_text,
 -    &deh_section_thing,
 -    &deh_section_weapon,
 -};
 -
+ void DEH_Checksum(sha1_digest_t digest)
  {
-     md5_context_t md5_context;
+     sha1_context_t sha1_context;
      unsigned int i;

-     MD5_Init(&md5_context);
+     SHA1_Init(&sha1_context);

 -    for (i=0; i<arrlen(section_types); ++i)
 +    for (i=0; deh_section_types[i] != NULL; ++i)
      {
-         if (deh_section_types[i]->md5_hash != NULL)
 -        if (section_types[i]->sha1_hash != NULL)
++        if (deh_section_types[i]->sha1_hash != NULL)
          {
-             deh_section_types[i]->md5_hash(&md5_context);
 -            section_types[i]->sha1_hash(&sha1_context);
++            deh_section_types[i]->sha1_hash(&sha1_context);
          }
      }

diff --cc src/deh_main.h
index 9ac2c6c7,ad31af7d..ea9e42da
--- a/src/deh_main.h
+++ b/src/deh_main.h
@@@ -27,10 -27,11 +27,10 @@@
  #ifndef DEH_MAIN_H
  #define DEH_MAIN_H

 -#include <stdio.h>
 -
  #include "doomtype.h"
  #include "doomfeatures.h"
- #include "md5.h"
 +#include "deh_str.h"
+ #include "sha1.h"

  // These are the limits that dehacked uses (from dheinit.h in the dehacked
  // source).  If these limits are exceeded, it does not generate an error, but
@@@ -46,8 -47,28 +46,8 @@@ int DEH_LoadLumpByName(char *name)

  boolean DEH_ParseAssignment(char *line, char **variable_name, char **value);

- void DEH_Checksum(md5_digest_t digest);
+ void DEH_Checksum(sha1_digest_t digest);

 -// deh_text.c:
 -//
 -// Used to do dehacked text substitutions throughout the program
 -
 -#ifdef FEATURE_DEHACKED
 -
 -char *DEH_String(char *s);
 -void DEH_printf(char *fmt, ...);
 -void DEH_fprintf(FILE *fstream, char *fmt, ...);
 -void DEH_snprintf(char *buffer, size_t len, char *fmt, ...);
 -
 -#else
 -
 -#define DEH_String(x) (x)
 -#define DEH_printf printf
 -#define DEH_fprintf fprintf
 -#define DEH_snprintf snprintf
 -
 -#endif
 -
  extern boolean deh_allow_long_strings;
  extern boolean deh_allow_long_cheats;
  extern boolean deh_apply_cheats;
diff --cc src/deh_mapping.c
index f061c298,d73cda96..85c675b4
--- a/src/deh_mapping.c
+++ b/src/deh_mapping.c
@@@ -60,111 -86,11 +60,111 @@@ static deh_mapping_entry_t *GetMappingE

      DEH_Warning(context, "Field named '%s' not found", name);

 -    return false;
 +    return NULL;
 +}
 +
 +//
 +// Get the location of the specified field in the specified structure.
 +//
 +
 +static void *GetStructField(void *structptr,
 +                            deh_mapping_t *mapping,
 +                            deh_mapping_entry_t *entry)
 +{
 +    unsigned int offset;
 +
 +    offset = (uint8_t *)entry->location - (uint8_t *)mapping->base;
 +
 +    return (uint8_t *)structptr + offset;
 +}
 +
 +//
 +// Set the value of a particular field in a structure by name
 +//
 +
 +boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping,
 +                       void *structptr, char *name, int value)
 +{
 +    deh_mapping_entry_t *entry;
 +    void *location;
 +
 +    entry = GetMappingEntryByName(context, mapping, name);
 +
 +    if (entry == NULL)
 +    {
 +        return false;
 +    }
 +
 +    // Sanity check:
 +
 +    if (entry->is_string)
 +    {
 +        DEH_Error(context, "Tried to set '%s' as integer (BUG)", name);
 +        return false;
 +    }
 +
 +    location = GetStructField(structptr, mapping, entry);
 +
 +    //       printf("Setting %p::%s to %i (%i bytes)\n",
 +    //               structptr, name, value, entry->size);
 +
 +    // Set field content based on its type:
 +
 +    switch (entry->size)
 +    {
 +        case 1:
 +            * ((uint8_t *) location) = value;
 +            break;
 +        case 2:
 +            * ((uint16_t *) location) = value;
 +            break;
 +        case 4:
 +            * ((uint32_t *) location) = value;
 +            break;
 +        default:
 +            DEH_Error(context, "Unknown field type for '%s' (BUG)", name);
 +            return false;
 +    }
 +
 +    return true;
 +}
 +
 +//
 +// Set the value of a string field in a structure by name
 +//
 +
 +boolean DEH_SetStringMapping(deh_context_t *context, deh_mapping_t *mapping,
 +                             void *structptr, char *name, char *value)
 +{
 +    deh_mapping_entry_t *entry;
 +    void *location;
 +
 +    entry = GetMappingEntryByName(context, mapping, name);
 +
 +    if (entry == NULL)
 +    {
 +        return false;
 +    }
 +
 +    // Sanity check:
 +
 +    if (!entry->is_string)
 +    {
 +        DEH_Error(context, "Tried to set '%s' as string (BUG)", name);
 +        return false;
 +    }
 +
 +    location = GetStructField(structptr, mapping, entry);
 +
 +    // Copy value into field:
 +
 +    strncpy(location, value, entry->size);
 +
 +    return true;
  }

- void DEH_StructMD5Sum(md5_context_t *context, deh_mapping_t *mapping,
-                       void *structptr)
+ void DEH_StructSHA1Sum(sha1_context_t *context, deh_mapping_t *mapping,
+                        void *structptr)
  {
      int i;

diff --cc src/deh_mapping.h
index 129ddcfd,31e8aea3..91e34323
--- a/src/deh_mapping.h
+++ b/src/deh_mapping.h
@@@ -91,12 -81,10 +91,12 @@@ struct deh_mapping_
      deh_mapping_entry_t entries[MAX_MAPPING_ENTRIES];
  };

 -boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping,
 +boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping,
                         void *structptr, char *name, int value);
 +boolean DEH_SetStringMapping(deh_context_t *context, deh_mapping_t *mapping,
 +                             void *structptr, char *name, char *value);
- void DEH_StructMD5Sum(md5_context_t *context, deh_mapping_t *mapping,
-                       void *structptr);
+ void DEH_StructSHA1Sum(sha1_context_t *context, deh_mapping_t *mapping,
+                        void *structptr);

  #endif /* #ifndef DEH_MAPPING_H */

diff --cc src/doom/d_main.c
index d91649a8,025c31eb..0746a579
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@@ -1546,12 -1509,20 +1554,26 @@@ void D_DoomMain (void
      if (gamemode == commercial && W_CheckNumForName("map01") < 0)
          storedemo = true;

+     // Doom 3: BFG Edition includes modified versions of the classic
+     // IWADs. The modified version of doom2.wad does not have a
+     // TITLEPIC lump, so detect this so we can apply a workaround.
+     // We specifically check for TITLEPIC here, after PWADs have been
+     // loaded - this means that we can play with the BFG Edition with
+     // PWADs that change the title screen and still see the modified
+     // titles.
+
+     if (gamemode == commercial && W_CheckNumForName("titlepic") < 0)
+     {
+         printf("BFG Edition: Using INTERPIC instead of TITLEPIC.\n");
+         bfgedition = true;
+     }
+
 +    if (M_CheckParmWithArgs("-statdump", 1))
 +    {
 +        I_AtExit(StatDump, true);
 +        DEH_printf("External statistics registered.\n");
 +    }
 +
      //!
      // @arg <x>
      // @category demo
diff --cc src/doom/d_net.c
index 74893724,00000000..4ab61425
mode 100644,000000..100644
--- a/src/doom/d_net.c
+++ b/src/doom/d_net.c
@@@ -1,304 -1,0 +1,304 @@@
 +// 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 "w_checksum.h"
 +#include "w_wad.h"
 +
 +#include "deh_main.h"
 +
 +#include "d_loop.h"
 +
 +ticcmd_t *netcmds;
 +
 +// Called when a player leaves the game
 +
 +static void 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 RunTic(ticcmd_t *cmds, boolean *ingame)
 +{
 +    extern boolean advancedemo;
 +    unsigned int i;
 +
 +    // Check for player quits.
 +
 +    for (i = 0; i < MAXPLAYERS; ++i)
 +    {
 +        if (!demoplayback && playeringame[i] && !ingame[i])
 +        {
 +            PlayerQuitGame(&players[i]);
 +        }
 +    }
 +
 +    netcmds = cmds;
 +
 +    // check that there are players in the game.  if not, we cannot
 +    // run a tic.
 +
 +    if (advancedemo)
 +        D_DoAdvanceDemo ();
 +
 +    G_Ticker ();
 +}
 +
 +static loop_interface_t doom_loop_interface = {
 +    D_ProcessEvents,
 +    G_BuildTiccmd,
 +    RunTic,
 +    M_Ticker
 +};
 +
 +
 +// Load game settings from the specified structure and
 +// set global variables.
 +
 +static void LoadGameSettings(net_gamesettings_t *settings,
 +                             net_connect_data_t *connect_data)
 +{
 +    unsigned int i;
 +
 +    deathmatch = settings->deathmatch;
 +    ticdup = settings->ticdup;
 +    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");
 +    }
 +
 +    if (!connect_data->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)
 +{
 +    // 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;
 +
 +    connect_data->drone = false;
 +    connect_data->max_players = MAXPLAYERS;
 +
 +    //!
 +    // @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;
 +    }
 +
 +    //
 +    // Connect data
 +    //
 +
 +    // Game type fields:
 +
 +    connect_data->gamemode = gamemode;
 +    connect_data->gamemission = gamemission;
 +
 +    // Are we recording a demo? Possibly set lowres turn mode
 +
 +    connect_data->lowres_turn = settings->lowres_turn;
 +
 +    // Read checksums of our WAD directory and dehacked information
 +
-     W_Checksum(connect_data->wad_md5sum);
-     DEH_Checksum(connect_data->deh_md5sum);
++    W_Checksum(connect_data->wad_sha1sum);
++    DEH_Checksum(connect_data->deh_sha1sum);
 +
 +    // Are we playing with the Freedoom IWAD?
 +
 +    connect_data->is_freedoom = W_CheckNumForName("FREEDOOM") >= 0;
 +}
 +
 +void D_InitSinglePlayerGame(net_gamesettings_t *settings)
 +{
 +    // default values for single player
 +
 +    settings->consoleplayer = 0;
 +    settings->num_players = 1;
 +
 +    netgame = false;
 +
 +    //!
 +    // @category net
 +    //
 +    // Start the game playing as though in a netgame with a single
 +    // player.  This can also be used to play back single player netgame
 +    // demos.
 +    //
 +
 +    if (M_CheckParm("-solo-net") > 0)
 +    {
 +        netgame = true;
 +    }
 +}
 +
 +//
 +// D_CheckNetGame
 +// Works out player numbers among the net participants
 +//
 +void D_CheckNetGame (void)
 +{
 +    net_connect_data_t connect_data;
 +    net_gamesettings_t settings;
 +
 +    D_RegisterLoopCallbacks(&doom_loop_interface);
 +
 +    // Call D_QuitNetGame on exit
 +
 +    I_AtExit(D_QuitNetGame, true);
 +
 +    SaveGameSettings(&settings, &connect_data);
 +
 +    if (D_InitNetGame(&connect_data, &settings))
 +    {
 +        netgame = true;
 +        autostart = true;
 +    }
 +    else
 +    {
 +        D_InitSinglePlayerGame(&settings);
 +    }
 +
 +    LoadGameSettings(&settings, &connect_data);
 +
 +    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 && deathmatch)
 +    {
 +        // 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");
 +        }
 +    }
 +}
 +
diff --cc src/doom/hu_stuff.c
index 9b67a1a5,8547f34a..3aaa5a33
--- a/src/doom/hu_stuff.c
+++ b/src/doom/hu_stuff.c
@@@ -621,6 -674,16 +620,8 @@@ boolean HU_Responder(event_t *ev
  	}
  	else
  	{
+             c = ev->data2;
+
 -            if (vanilla_keyboard_mapping)
 -            {
 -                if (shiftdown || (c >= 'a' && c <= 'z'))
 -                {
 -                    c = shiftxform[c];
 -                }
 -            }
 -
  	    eatkey = HUlib_keyInIText(&w_chat, c);
  	    if (eatkey)
  	    {
diff --cc src/doom/m_menu.c
index e54ac7ff,cd2634a2..b0eeee06
--- a/src/doom/m_menu.c
+++ b/src/doom/m_menu.c
@@@ -1962,10 -2000,9 +2001,9 @@@ void M_Drawer (void


      // DRAW SKULL
 -    V_DrawPatchDirect(x + SKULLXOFF,currentMenu->y - 5 + itemOn*LINEHEIGHT, 0,
 +    V_DrawPatchDirect(x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,
  		      W_CacheLumpName(DEH_String(skullName[whichSkull]),
  				      PU_CACHE));
-
  }


diff --cc src/heretic/d_net.c
index 6a6a8abb,00000000..73a09ec6
mode 100644,000000..100644
--- a/src/heretic/d_net.c
+++ b/src/heretic/d_net.c
@@@ -1,238 -1,0 +1,238 @@@
 +// 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 "m_argv.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "doomdef.h"
 +#include "w_checksum.h"
 +
 +#include "deh_main.h"
 +
 +#include "d_loop.h"
 +
 +ticcmd_t *netcmds;
 +
 +extern void D_DoAdvanceDemo(void);
 +extern void D_ProcessEvents(void);
 +extern void G_BuildTiccmd(ticcmd_t *cmd, int maketic);
 +extern boolean G_CheckDemoStatus(void);
 +
 +// Called when a player leaves the game
 +
 +static void PlayerQuitGame(player_t *player)
 +{
 +    static char exitmsg[80];
 +    unsigned int player_num;
 +
 +    player_num = player - players;
 +
 +    // Note:
 +    // The Heretic source code does this, which doesn't actually work.
 +    // As a result, the exit message is never seen.
 +
 +    strcpy(exitmsg, "PLAYER 1 LEFT THE GAME");
 +    exitmsg[7] += player_num;
 +    players[consoleplayer].message = exitmsg;
 +
 +    playeringame[player_num] = false;
 +    players[consoleplayer].message = exitmsg;
 +
 +    // TODO: check if it is sensible to do this:
 +
 +    if (demorecording)
 +    {
 +        G_CheckDemoStatus ();
 +    }
 +}
 +
 +static void RunTic(ticcmd_t *cmds, boolean *ingame)
 +{
 +    extern boolean advancedemo;
 +    unsigned int i;
 +
 +    // Check for player quits.
 +
 +    for (i = 0; i < MAXPLAYERS; ++i)
 +    {
 +        if (!demoplayback && playeringame[i] && !ingame[i])
 +        {
 +            PlayerQuitGame(&players[i]);
 +        }
 +    }
 +
 +    netcmds = cmds;
 +
 +    // check that there are players in the game.  if not, we cannot
 +    // run a tic.
 +
 +    if (advancedemo)
 +        D_DoAdvanceDemo ();
 +
 +    G_Ticker ();
 +}
 +
 +static loop_interface_t doom_loop_interface = {
 +    D_ProcessEvents,
 +    G_BuildTiccmd,
 +    RunTic,
 +    MN_Ticker
 +};
 +
 +
 +// Load game settings from the specified structure and
 +// set global variables.
 +
 +static void LoadGameSettings(net_gamesettings_t *settings,
 +                             net_connect_data_t *connect_data)
 +{
 +    unsigned int i;
 +
 +    deathmatch = settings->deathmatch;
 +    ticdup = settings->ticdup;
 +    startepisode = settings->episode;
 +    startmap = settings->map;
 +    startskill = settings->skill;
 +    // TODO startloadgame = settings->loadgame;
 +    nomonsters = settings->nomonsters;
 +    respawnparm = settings->respawn_monsters;
 +
 +    if (!connect_data->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)
 +{
 +    // Fill in game settings structure with appropriate parameters
 +    // for the new game
 +
 +    settings->deathmatch = deathmatch;
 +    settings->episode = startepisode;
 +    settings->map = startmap;
 +    settings->skill = startskill;
 +    // TODO settings->loadgame = startloadgame;
 +    settings->gameversion = exe_heretic_1_3;
 +    settings->nomonsters = nomonsters;
 +    settings->respawn_monsters = respawnparm;
 +    settings->timelimit = 0;
 +    settings->lowres_turn = false;
 +
 +    connect_data->drone = false;
 +    connect_data->max_players = MAXPLAYERS;
 +
 +    //
 +    // Connect data
 +    //
 +
 +    // Game type fields:
 +
 +    connect_data->gamemode = gamemode;
 +    connect_data->gamemission = gamemission;
 +
 +    connect_data->lowres_turn = false;
 +
 +    // Read checksums of our WAD directory and dehacked information
 +
-     W_Checksum(connect_data->wad_md5sum);
-     DEH_Checksum(connect_data->deh_md5sum);
++    W_Checksum(connect_data->wad_sha1sum);
++    DEH_Checksum(connect_data->deh_sha1sum);
 +
 +    connect_data->is_freedoom = 0;
 +}
 +
 +void D_InitSinglePlayerGame(net_gamesettings_t *settings)
 +{
 +    // default values for single player
 +
 +    settings->consoleplayer = 0;
 +    settings->num_players = 1;
 +
 +    netgame = false;
 +
 +    //!
 +    // @category net
 +    //
 +    // Start the game playing as though in a netgame with a single
 +    // player.  This can also be used to play back single player netgame
 +    // demos.
 +    //
 +
 +    if (M_CheckParm("-solo-net") > 0)
 +    {
 +        netgame = true;
 +    }
 +}
 +
 +//
 +// D_CheckNetGame
 +// Works out player numbers among the net participants
 +//
 +
 +void D_CheckNetGame (void)
 +{
 +    net_connect_data_t connect_data;
 +    net_gamesettings_t settings;
 +
 +    D_RegisterLoopCallbacks(&doom_loop_interface);
 +
 +    // Call D_QuitNetGame on exit
 +
 +    I_AtExit(D_QuitNetGame, true);
 +
 +    SaveGameSettings(&settings, &connect_data);
 +
 +    if (D_InitNetGame(&connect_data, &settings))
 +    {
 +        netgame = true;
 +        autostart = true;
 +    }
 +    else
 +    {
 +        D_InitSinglePlayerGame(&settings);
 +    }
 +
 +    LoadGameSettings(&settings, &connect_data);
 +}
diff --cc src/heretic/deh_ammo.c
index fe86c757,00000000..08241863
mode 100644,000000..100644
--- a/src/heretic/deh_ammo.c
+++ b/src/heretic/deh_ammo.c
@@@ -1,122 -1,0 +1,122 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// 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.
 +//
 +//-----------------------------------------------------------------------------
 +//
 +// Parses "Ammo" sections in dehacked files
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "doomdef.h"
 +#include "doomtype.h"
 +#include "deh_defs.h"
 +#include "deh_io.h"
 +#include "deh_main.h"
 +#include "p_local.h"
 +
 +static void *DEH_AmmoStart(deh_context_t *context, char *line)
 +{
 +    int ammo_number = 0;
 +
 +    if (sscanf(line, "Ammo %i", &ammo_number) != 1)
 +    {
 +        DEH_Warning(context, "Parse error on section start");
 +        return NULL;
 +    }
 +
 +    if (ammo_number < 0 || ammo_number >= NUMAMMO)
 +    {
 +        DEH_Warning(context, "Invalid ammo number: %i", ammo_number);
 +        return NULL;
 +    }
 +
 +    return &maxammo[ammo_number];
 +}
 +
 +static void DEH_AmmoParseLine(deh_context_t *context, char *line, void *tag)
 +{
 +    char *variable_name, *value;
 +    int ivalue;
 +    int ammo_number;
 +
 +    if (tag == NULL)
 +        return;
 +
 +    ammo_number = ((int *) tag) - maxammo;
 +
 +    // Parse the assignment
 +
 +    if (!DEH_ParseAssignment(line, &variable_name, &value))
 +    {
 +        // Failed to parse
 +
 +        DEH_Warning(context, "Failed to parse assignment");
 +        return;
 +    }
 +
 +    ivalue = atoi(value);
 +
 +    if (!strcasecmp(variable_name, "Per ammo"))
 +    {
 +        // Heretic doesn't have a "per clip" ammo array, instead
 +        // it is per weapon.  However, the weapon number lines
 +        // up with the ammo number if we add one.
 +
 +        GetWeaponAmmo[ammo_number + 1] = ivalue;
 +    }
 +    else if (!strcasecmp(variable_name, "Max ammo"))
 +    {
 +        maxammo[ammo_number] = ivalue;
 +    }
 +    else
 +    {
 +        DEH_Warning(context, "Field named '%s' not found", variable_name);
 +    }
 +}
 +
- static void DEH_AmmoMD5Hash(md5_context_t *context)
++static void DEH_AmmoSHA1Hash(sha1_context_t *context)
 +{
 +    int i;
 +
 +    for (i=0; i<NUMAMMO; ++i)
 +    {
-         MD5_UpdateInt32(context, maxammo[i]);
++        SHA1_UpdateInt32(context, maxammo[i]);
 +    }
 +
 +    for (i=0; i<NUMWEAPONS; ++i)
 +    {
-         MD5_UpdateInt32(context, GetWeaponAmmo[i]);
++        SHA1_UpdateInt32(context, GetWeaponAmmo[i]);
 +    }
 +}
 +
 +deh_section_t deh_section_ammo =
 +{
 +    "Ammo",
 +    NULL,
 +    DEH_AmmoStart,
 +    DEH_AmmoParseLine,
 +    NULL,
-     DEH_AmmoMD5Hash,
++    DEH_AmmoSHA1Hash,
 +};
 +
diff --cc src/heretic/deh_frame.c
index 8623ab0c,00000000..4a1581a7
mode 100644,000000..100644
--- a/src/heretic/deh_frame.c
+++ b/src/heretic/deh_frame.c
@@@ -1,344 -1,0 +1,344 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// 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.
 +//
 +//-----------------------------------------------------------------------------
 +//
 +// Parses "Frame" sections in dehacked files
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "doomtype.h"
 +#include "info.h"
 +
 +#include "deh_defs.h"
 +#include "deh_io.h"
 +#include "deh_main.h"
 +#include "deh_mapping.h"
 +#include "deh_htic.h"
 +
 +#include "p_action.h"
 +
 +typedef struct
 +{
 +    int offsets[deh_hhe_num_versions];
 +    void (*func)();
 +} hhe_action_pointer_t;
 +
 +// Offsets of action pointers within the Heretic executables.
 +// Different versions have different offsets.
 +// (Seriously Greg, was this really necessary?  What was wrong with the
 +// "copying action pointer from another frame" technique used in dehacked?)
 +
 +//    Offset                      Action function
 +//        v1.0    v1.2    v1.3
 +
 +static const hhe_action_pointer_t action_pointers[] =
 +{
 +    { {  77680,  80144,  80208 }, A_AccTeleGlitter },
 +    { {  78608,  81104,  81168 }, A_AddPlayerCorpse },
 +    { { 115808, 118000, 118240 }, A_AddPlayerRain },
 +    { { 112272, 114480, 114720 }, A_BeakAttackPL1 },
 +    { { 112448, 114656, 114896 }, A_BeakAttackPL2 },
 +    { { 111856, 114176, 114416 }, A_BeakRaise },
 +    { { 111568, 113888, 114128 }, A_BeakReady },
 +    { {  74640,  77120,  77184 }, A_BeastAttack },
 +    { {  70480,  72992,  73056 }, A_BeastPuff },
 +    { {  73120,  75600,  75664 }, A_BlueSpark },
 +    { { 115456, 117648, 117888 }, A_BoltSpark },
 +    { {  77344,  79808,  79872 }, A_BossDeath },
 +    { {  69328,  71856,  71920 }, A_Chase },
 +    { {      0,  80976,  81040 }, A_CheckBurnGone },
 +    { {  78480,  80944,  81008 }, A_CheckSkullDone },
 +    { {  78448,  80912,  80976 }, A_CheckSkullFloor },
 +    { {  71376,  73888,  73952 }, A_ChicAttack },
 +    { {  71488,  74000,  74064 }, A_ChicChase },
 +    { {  71456,  73968,  74032 }, A_ChicLook },
 +    { {  71520,  74032,  74096 }, A_ChicPain },
 +    { {  75792,  78208,  78272 }, A_ClinkAttack },
 +    { { 108432, 110816, 111056 }, A_ContMobjSound },
 +    { { 114752, 116944, 117184 }, A_DeathBallImpact },
 +    { {  70016,  72528,  72592 }, A_DripBlood },
 +    { {  77472,  79936,  80000 }, A_ESound },
 +    { {  76784,  79248,  79312 }, A_Explode },
 +    { {  69872,  72400,  72464 }, A_FaceTarget },
 +    { {  71568,  74080,  74144 }, A_Feathers },
 +    { { 112928, 115136, 115376 }, A_FireBlasterPL1 },
 +    { { 113072, 115280, 115520 }, A_FireBlasterPL2 },
 +    { { 115232, 117424, 117664 }, A_FireCrossbowPL1 },
 +    { { 115312, 117504, 117744 }, A_FireCrossbowPL2 },
 +    { { 113152, 115360, 115600 }, A_FireGoldWandPL1 },
 +    { { 113296, 115504, 115744 }, A_FireGoldWandPL2 },
 +    { { 113760, 115968, 116208 }, A_FireMacePL1 },
 +    { { 114624, 116816, 117056 }, A_FireMacePL2 },
 +    { { 116368, 118544, 118784 }, A_FirePhoenixPL1 },
 +    { { 116736, 118896, 119136 }, A_FirePhoenixPL2 },
 +    { { 115568, 117760, 118000 }, A_FireSkullRodPL1 },
 +    { { 115648, 117840, 118080 }, A_FireSkullRodPL2 },
 +    { { 117120, 119280, 119520 }, A_FlameEnd },
 +    { {  78704,  81200,  81264 }, A_FlameSnd },
 +    { { 117152, 119312, 119552 }, A_FloatPuff },
 +    { {  78512,  81008,  81072 }, A_FreeTargMobj },
 +    { { 117184, 119344, 119584 }, A_GauntletAttack },
 +    { {  73232,  75712,  75776 }, A_GenWizard },
 +    { {  75872,  78304,  78368 }, A_GhostOff },
 +    { {  74752,  77232,  77296 }, A_HeadAttack },
 +    { {  75488,  77984,  78048 }, A_HeadFireGrow },
 +    { {  75328,  77824,  77888 }, A_HeadIceImpact },
 +    { { 116336, 118512, 118752 }, A_HideInCeiling },
 +    { {  78736,  81232,  81296 }, A_HideThing },
 +    { {  70976,  73488,  73552 }, A_ImpDeath },
 +    { {  70304,  72816,  72880 }, A_ImpExplode },
 +    { {  70592,  73104,  73168 }, A_ImpMeAttack },
 +    { {  70672,  73184,  73248 }, A_ImpMsAttack },
 +    { {  70880,  73392,  73456 }, A_ImpMsAttack2 },
 +    { {  71024,  73536,  73600 }, A_ImpXDeath1 },
 +    { {  71072,  73584,  73648 }, A_ImpXDeath2 },
 +    { {  77728,  80192,  80256 }, A_InitKeyGizmo },
 +    { { 116720, 118880, 119120 }, A_InitPhoenixPL2 },
 +    { {  70160,  72672,  72736 }, A_KnightAttack },
 +    { { 117648, 119824, 120064 }, A_Light0 },
 +    { {  69200,  71728,  71792 }, A_Look },
 +    { { 111760, 114080, 114320 }, A_Lower },
 +    { { 114032, 116224, 116464 }, A_MaceBallImpact },
 +    { { 114192, 116384, 116624 }, A_MaceBallImpact2 },
 +    { { 113904, 116112, 116352 }, A_MacePL1Check },
 +    { {  77104,  79568,  79632 }, A_MakePod },
 +    { {  73648,  76128,  76192 }, A_MinotaurAtk1 },
 +    { {  74112,  76592,  76656 }, A_MinotaurAtk2 },
 +    { {  74352,  76832,  76896 }, A_MinotaurAtk3 },
 +    { {  74032,  76512,  76576 }, A_MinotaurCharge },
 +    { {  73760,  76240,  76304 }, A_MinotaurDecide },
 +    { {  74528,  77008,  77072 }, A_MntrFloorFire },
 +    { {  71808,  74288,  74352 }, A_MummyAttack },
 +    { {  71920,  74400,  74464 }, A_MummyAttack2 },
 +    { {  72016,  74496,  74560 }, A_MummyFX1Seek },
 +    { {  72048,  74528,  74592 }, A_MummySoul },
 +    { {  76400,  78832,  78896 }, A_NoBlocking },
 +    { {  69984,  72496,  72560 }, A_Pain },
 +    { { 116496, 118656, 118896 }, A_PhoenixPuff },
 +    { {  76896,  79360,  79424 }, A_PodPain },
 +    { { 116272, 118448, 118688 }, A_RainImpact },
 +    { { 111920, 114240, 114480 }, A_Raise },
 +    { { 111696, 114016, 114256 }, A_ReFire },
 +    { {  77056,  79520,  79584 }, A_RemovePod },
 +    { { 116480,      0,      0 }, A_RemovedPhoenixFunc },
 +    { {  81952,  84464,  84528 }, A_RestoreArtifact },
 +    { {  82048,  84544,  84608 }, A_RestoreSpecialThing1 },
 +    { {  82128,  84592,  84656 }, A_RestoreSpecialThing2 },
 +    { {  76144,  78576,  78640 }, A_Scream },
 +    { { 117104, 119264, 119504 }, A_ShutdownPhoenixPL2 },
 +    { {  78288,  80752,  80816 }, A_SkullPop },
 +    { { 115776, 117968, 118208 }, A_SkullRodPL2Seek },
 +    { { 115984, 118176, 118416 }, A_SkullRodStorm },
 +    { {  75632,  78048,  78112 }, A_SnakeAttack },
 +    { {  75712,  78128,  78192 }, A_SnakeAttack2 },
 +    { {  72144,  74624,  74688 }, A_Sor1Chase },
 +    { {  72096,  74576,  74640 }, A_Sor1Pain },
 +    { {  73392,  75872,  75936 }, A_Sor2DthInit },
 +    { {  73424,  75904,  75968 }, A_Sor2DthLoop },
 +    { {  73584,  76064,  76128 }, A_SorDBon },
 +    { {  73552,  76032,  76096 }, A_SorDExp },
 +    { {  73520,  76000,  76064 }, A_SorDSph },
 +    { {  73488,  75968,  76032 }, A_SorRise },
 +    { {  73616,  76096,  76160 }, A_SorSightSnd },
 +    { {  73456,  75936,  76000 }, A_SorZap },
 +    { {  72480,  74960,  75024 }, A_SorcererRise },
 +    { { 115088, 117280, 117520 }, A_SpawnRippers },
 +    { {  77520,  79984,  80048 }, A_SpawnTeleGlitter },
 +    { {  77600,  80064,  80128 }, A_SpawnTeleGlitter2 },
 +    { {  72192,  74672,  74736 }, A_Srcr1Attack },
 +    { {  72896,  75376,  75440 }, A_Srcr2Attack },
 +    { {  72816,  75296,  75360 }, A_Srcr2Decide },
 +    { { 112640, 114848, 115088 }, A_StaffAttackPL1 },
 +    { { 112784, 114992, 115232 }, A_StaffAttackPL2 },
 +    { {  78752,  81248,  81312 }, A_UnHideThing },
 +    { {  78080,  80544,  80608 }, A_VolcBallImpact },
 +    { {  77856,  80320,  80384 }, A_VolcanoBlast },
 +    { {  77824,  80288,  80352 }, A_VolcanoSet },
 +    { { 111168, 113488, 113728 }, A_WeaponReady },
 +    { {  75168,  77664,  77728 }, A_WhirlwindSeek },
 +    { {  75888,  78320,  78384 }, A_WizAtk1 },
 +    { {  75920,  78352,  78416 }, A_WizAtk2 },
 +    { {  75952,  78384,  78448 }, A_WizAtk3 },
 +};
 +
 +DEH_BEGIN_MAPPING(state_mapping, state_t)
 +  DEH_MAPPING("Sprite number",    sprite)
 +  DEH_MAPPING("Sprite subnumber", frame)
 +  DEH_MAPPING("Duration",         tics)
 +  DEH_MAPPING("Next frame",       nextstate)
 +  DEH_MAPPING("Unknown 1",        misc1)
 +  DEH_MAPPING("Unknown 2",        misc2)
 +DEH_END_MAPPING
 +
 +static void DEH_FrameInit(void)
 +{
 +    // Bit of a hack here:
 +    DEH_HereticInit();
 +}
 +
 +static void *DEH_FrameStart(deh_context_t *context, char *line)
 +{
 +    int frame_number = 0;
 +    int mapped_frame_number;
 +    state_t *state;
 +
 +    if (sscanf(line, "Frame %i", &frame_number) != 1)
 +    {
 +        DEH_Warning(context, "Parse error on section start");
 +        return NULL;
 +    }
 +
 +    // Map the HHE frame number (which assumes a Heretic 1.0 state table)
 +    // to the internal frame number (which is is the Heretic 1.3 state table):
 +
 +    mapped_frame_number = DEH_MapHereticFrameNumber(frame_number);
 +
 +    if (mapped_frame_number < 0 || mapped_frame_number >= DEH_HERETIC_NUMSTATES)
 +    {
 +        DEH_Warning(context, "Invalid frame number: %i", frame_number);
 +        return NULL;
 +    }
 +
 +    state = &states[mapped_frame_number];
 +
 +    return state;
 +}
 +
 +static boolean GetActionPointerForOffset(int offset, void **result)
 +{
 +    int i;
 +
 +    // Special case.
 +
 +    if (offset == 0)
 +    {
 +        *result = NULL;
 +        return true;
 +    }
 +
 +    for (i=0; i<arrlen(action_pointers); ++i)
 +    {
 +        if (action_pointers[i].offsets[deh_hhe_version] == offset)
 +        {
 +            *result = action_pointers[i].func;
 +            return true;
 +        }
 +    }
 +
 +    return false;
 +}
 +
 +// If an invalid action pointer is specified, the patch may be for a
 +// different version from the version we are currently set to.  Try to
 +// suggest a different version to use.
 +
 +static void SuggestOtherVersions(unsigned int offset)
 +{
 +    unsigned int i, v;
 +
 +    for (i=0; i<arrlen(action_pointers); ++i)
 +    {
 +        for (v=0; v<deh_hhe_num_versions; ++v)
 +        {
 +            if (action_pointers[i].offsets[v] == offset)
 +            {
 +                DEH_SuggestHereticVersion(v);
 +            }
 +        }
 +    }
 +}
 +
 +static void DEH_FrameParseLine(deh_context_t *context, char *line, void *tag)
 +{
 +    state_t *state;
 +    char *variable_name, *value;
 +    int ivalue;
 +
 +    if (tag == NULL)
 +       return;
 +
 +    state = (state_t *) tag;
 +
 +    // Parse the assignment
 +
 +    if (!DEH_ParseAssignment(line, &variable_name, &value))
 +    {
 +        // Failed to parse
 +
 +        DEH_Warning(context, "Failed to parse assignment");
 +        return;
 +    }
 +
 +    // all values are integers
 +
 +    ivalue = atoi(value);
 +
 +    // Action pointer field is a special case:
 +
 +    if (!strcasecmp(variable_name, "Action pointer"))
 +    {
 +        void *func;
 +
 +        if (!GetActionPointerForOffset(ivalue, &func))
 +        {
 +            SuggestOtherVersions(ivalue);
 +            DEH_Error(context, "Unknown action pointer: %i", ivalue);
 +            return;
 +        }
 +
 +        state->action = func;
 +    }
 +    else
 +    {
 +        // "Next frame" numbers need to undergo mapping.
 +
 +        if (!strcasecmp(variable_name, "Next frame"))
 +        {
 +            ivalue = DEH_MapHereticFrameNumber(ivalue);
 +        }
 +
 +        DEH_SetMapping(context, &state_mapping, state, variable_name, ivalue);
 +    }
 +}
 +
- static void DEH_FrameMD5Sum(md5_context_t *context)
++static void DEH_FrameSHA1Sum(sha1_context_t *context)
 +{
 +    int i;
 +
 +    for (i=0; i<NUMSTATES; ++i)
 +    {
-         DEH_StructMD5Sum(context, &state_mapping, &states[i]);
++        DEH_StructSHA1Sum(context, &state_mapping, &states[i]);
 +    }
 +}
 +
 +deh_section_t deh_section_frame =
 +{
 +    "Frame",
 +    DEH_FrameInit,
 +    DEH_FrameStart,
 +    DEH_FrameParseLine,
 +    NULL,
-     DEH_FrameMD5Sum,
++    DEH_FrameSHA1Sum,
 +};
 +
diff --cc src/heretic/deh_thing.c
index ffededf2,00000000..dbb239e4
mode 100644,000000..100644
--- a/src/heretic/deh_thing.c
+++ b/src/heretic/deh_thing.c
@@@ -1,150 -1,0 +1,150 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// 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.
 +//
 +//-----------------------------------------------------------------------------
 +//
 +// Parses "Thing" sections in dehacked files
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "doomtype.h"
 +#include "m_misc.h"
 +
 +#include "deh_defs.h"
 +#include "deh_main.h"
 +#include "deh_mapping.h"
 +#include "deh_htic.h"
 +
 +#include "info.h"
 +
 +DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t)
 +  DEH_MAPPING("ID #",                doomednum)
 +  DEH_MAPPING("Initial frame",       spawnstate)
 +  DEH_MAPPING("Hit points",          spawnhealth)
 +  DEH_MAPPING("First moving frame",  seestate)
 +  DEH_MAPPING("Alert sound",         seesound)
 +  DEH_MAPPING("Reaction time",       reactiontime)
 +  DEH_MAPPING("Attack sound",        attacksound)
 +  DEH_MAPPING("Injury frame",        painstate)
 +  DEH_MAPPING("Pain chance",         painchance)
 +  DEH_MAPPING("Pain sound",          painsound)
 +  DEH_MAPPING("Close attack frame",  meleestate)
 +  DEH_MAPPING("Far attack frame",    missilestate)
 +  DEH_MAPPING("Burning frame",       crashstate)
 +  DEH_MAPPING("Death frame",         deathstate)
 +  DEH_MAPPING("Exploding frame",     xdeathstate)
 +  DEH_MAPPING("Death sound",         deathsound)
 +  DEH_MAPPING("Speed",               speed)
 +  DEH_MAPPING("Width",               radius)
 +  DEH_MAPPING("Height",              height)
 +  DEH_MAPPING("Mass",                mass)
 +  DEH_MAPPING("Missile damage",      damage)
 +  DEH_MAPPING("Action sound",        activesound)
 +  DEH_MAPPING("Bits 1",              flags)
 +  DEH_MAPPING("Bits 2",              flags2)
 +DEH_END_MAPPING
 +
 +static void *DEH_ThingStart(deh_context_t *context, char *line)
 +{
 +    int thing_number = 0;
 +    mobjinfo_t *mobj;
 +
 +    if (sscanf(line, "Thing %i", &thing_number) != 1)
 +    {
 +        DEH_Warning(context, "Parse error on section start");
 +        return NULL;
 +    }
 +
 +    // HHE thing numbers are indexed from 1
 +    --thing_number;
 +
 +    if (thing_number < 0 || thing_number >= DEH_HERETIC_NUMMOBJTYPES)
 +    {
 +        DEH_Warning(context, "Invalid thing number: %i", thing_number);
 +        return NULL;
 +    }
 +
 +    mobj = &mobjinfo[thing_number];
 +
 +    return mobj;
 +}
 +
 +static void DEH_ThingParseLine(deh_context_t *context, char *line, void *tag)
 +{
 +    mobjinfo_t *mobj;
 +    char *variable_name, *value;
 +    int ivalue;
 +
 +    if (tag == NULL)
 +       return;
 +
 +    mobj = (mobjinfo_t *) tag;
 +
 +    // Parse the assignment
 +
 +    if (!DEH_ParseAssignment(line, &variable_name, &value))
 +    {
 +        // Failed to parse
 +
 +        DEH_Warning(context, "Failed to parse assignment");
 +        return;
 +    }
 +
 +    // all values are integers
 +
 +    ivalue = atoi(value);
 +
 +    // If the value to be set is a frame, the frame number must
 +    // undergo transformation from a Heretic 1.0 index to a
 +    // Heretic 1.3 index.
 +
 +    if (M_StrCaseStr(variable_name, "frame") != NULL)
 +    {
 +        ivalue = DEH_MapHereticFrameNumber(ivalue);
 +    }
 +
 +    // Set the field value
 +
 +    DEH_SetMapping(context, &thing_mapping, mobj, variable_name, ivalue);
 +}
 +
- static void DEH_ThingMD5Sum(md5_context_t *context)
++static void DEH_ThingSHA1Sum(sha1_context_t *context)
 +{
 +    int i;
 +
 +    for (i=0; i<NUMMOBJTYPES; ++i)
 +    {
-         DEH_StructMD5Sum(context, &thing_mapping, &mobjinfo[i]);
++        DEH_StructSHA1Sum(context, &thing_mapping, &mobjinfo[i]);
 +    }
 +}
 +
 +deh_section_t deh_section_thing =
 +{
 +    "Thing",
 +    NULL,
 +    DEH_ThingStart,
 +    DEH_ThingParseLine,
 +    NULL,
-     DEH_ThingMD5Sum,
++    DEH_ThingSHA1Sum,
 +};
 +
diff --cc src/heretic/deh_weapon.c
index 28a90c68,00000000..0c417e2b
mode 100644,000000..100644
--- a/src/heretic/deh_weapon.c
+++ b/src/heretic/deh_weapon.c
@@@ -1,131 -1,0 +1,131 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// 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.
 +//
 +//-----------------------------------------------------------------------------
 +//
 +// Parses "Weapon" sections in dehacked files
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "doomtype.h"
 +#include "m_misc.h"
 +
 +#include "doomdef.h"
 +
 +#include "deh_defs.h"
 +#include "deh_main.h"
 +#include "deh_mapping.h"
 +#include "deh_htic.h"
 +
 +DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t)
 +  DEH_MAPPING("Ammo type",        ammo)
 +  DEH_MAPPING("Deselect frame",   upstate)
 +  DEH_MAPPING("Select frame",     downstate)
 +  DEH_MAPPING("Bobbing frame",    readystate)
 +  DEH_MAPPING("Shooting frame",   atkstate)
 +  DEH_MAPPING("Firing frame",     holdatkstate)
 +  DEH_MAPPING("Unknown frame",    flashstate)
 +DEH_END_MAPPING
 +
 +static void *DEH_WeaponStart(deh_context_t *context, char *line)
 +{
 +    int weapon_number = 0;
 +
 +    if (sscanf(line, "Weapon %i", &weapon_number) != 1)
 +    {
 +        DEH_Warning(context, "Parse error on section start");
 +        return NULL;
 +    }
 +
 +    if (weapon_number < 0 || weapon_number >= NUMWEAPONS * 2)
 +    {
 +        DEH_Warning(context, "Invalid weapon number: %i", weapon_number);
 +        return NULL;
 +    }
 +
 +    // Because of the tome of power, we have two levels of weapons:
 +
 +    if (weapon_number < NUMWEAPONS)
 +    {
 +        return &wpnlev1info[weapon_number];
 +    }
 +    else
 +    {
 +        return &wpnlev2info[weapon_number - NUMWEAPONS];
 +    }
 +}
 +
 +static void DEH_WeaponParseLine(deh_context_t *context, char *line, void *tag)
 +{
 +    char *variable_name, *value;
 +    weaponinfo_t *weapon;
 +    int ivalue;
 +
 +    if (tag == NULL)
 +        return;
 +
 +    weapon = (weaponinfo_t *) tag;
 +
 +    if (!DEH_ParseAssignment(line, &variable_name, &value))
 +    {
 +        // Failed to parse
 +
 +        DEH_Warning(context, "Failed to parse assignment");
 +        return;
 +    }
 +
 +    ivalue = atoi(value);
 +
 +    // If this is a frame field, we need to map from Heretic 1.0 frame
 +    // numbers to Heretic 1.3 frame numbers.
 +
 +    if (M_StrCaseStr(variable_name, "frame") != NULL)
 +    {
 +        ivalue = DEH_MapHereticFrameNumber(ivalue);
 +    }
 +
 +    DEH_SetMapping(context, &weapon_mapping, weapon, variable_name, ivalue);
 +}
 +
- static void DEH_WeaponMD5Sum(md5_context_t *context)
++static void DEH_WeaponSHA1Sum(sha1_context_t *context)
 +{
 +    int i;
 +
 +    for (i=0; i<NUMWEAPONS ;++i)
 +    {
-         DEH_StructMD5Sum(context, &weapon_mapping, &wpnlev1info[i]);
-         DEH_StructMD5Sum(context, &weapon_mapping, &wpnlev2info[i]);
++        DEH_StructSHA1Sum(context, &weapon_mapping, &wpnlev1info[i]);
++        DEH_StructSHA1Sum(context, &weapon_mapping, &wpnlev2info[i]);
 +    }
 +}
 +
 +deh_section_t deh_section_weapon =
 +{
 +    "Weapon",
 +    NULL,
 +    DEH_WeaponStart,
 +    DEH_WeaponParseLine,
 +    NULL,
-     DEH_WeaponMD5Sum,
++    DEH_WeaponSHA1Sum,
 +};
 +
diff --cc src/hexen/d_net.c
index 0fe5ed1e,00000000..7d663154
mode 100644,000000..100644
--- a/src/hexen/d_net.c
+++ b/src/hexen/d_net.c
@@@ -1,280 -1,0 +1,280 @@@
 +// 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 "m_argv.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "h2def.h"
 +#include "p_local.h"
 +#include "s_sound.h"
 +#include "w_checksum.h"
 +
 +#include "deh_main.h"
 +
 +#include "d_loop.h"
 +
 +ticcmd_t *netcmds;
 +
 +extern void H2_DoAdvanceDemo(void);
 +extern void H2_ProcessEvents(void);
 +extern void G_BuildTiccmd(ticcmd_t *cmd, int maketic);
 +extern boolean G_CheckDemoStatus(void);
 +
 +extern boolean demorecording;
 +
 +// Called when a player leaves the game
 +
 +static void PlayerQuitGame(player_t *player)
 +{
 +    static char exitmsg[80];
 +    unsigned int player_num;
 +
 +    player_num = player - players;
 +
 +    strcpy(exitmsg, "PLAYER 1 LEFT THE GAME");
 +    exitmsg[7] += player_num;
 +    P_SetMessage(&players[consoleplayer], exitmsg, true);
 +    S_StartSound(NULL, SFX_CHAT);
 +
 +    playeringame[player_num] = false;
 +
 +    // TODO: check if it is sensible to do this:
 +
 +    if (demorecording)
 +    {
 +        G_CheckDemoStatus ();
 +    }
 +}
 +
 +static void RunTic(ticcmd_t *cmds, boolean *ingame)
 +{
 +    extern boolean advancedemo;
 +    unsigned int i;
 +
 +    // Check for player quits.
 +
 +    for (i = 0; i < MAXPLAYERS; ++i)
 +    {
 +        if (!demoplayback && playeringame[i] && !ingame[i])
 +        {
 +            PlayerQuitGame(&players[i]);
 +        }
 +    }
 +
 +    netcmds = cmds;
 +
 +    // check that there are players in the game.  if not, we cannot
 +    // run a tic.
 +
 +    if (advancedemo)
 +        H2_DoAdvanceDemo ();
 +
 +    G_Ticker ();
 +}
 +
 +static loop_interface_t hexen_loop_interface = {
 +    H2_ProcessEvents,
 +    G_BuildTiccmd,
 +    RunTic,
 +    MN_Ticker
 +};
 +
 +
 +// Load game settings from the specified structure and
 +// set global variables.
 +
 +static void LoadGameSettings(net_gamesettings_t *settings,
 +                             net_connect_data_t *connect_data)
 +{
 +    unsigned int i;
 +
 +    deathmatch = settings->deathmatch;
 +    ticdup = settings->ticdup;
 +    startepisode = settings->episode;
 +    startmap = settings->map;
 +    startskill = settings->skill;
 +    // TODO startloadgame = settings->loadgame;
 +    nomonsters = settings->nomonsters;
 +    respawnparm = settings->respawn_monsters;
 +
 +    if (!connect_data->drone)
 +    {
 +        consoleplayer = settings->consoleplayer;
 +    }
 +    else
 +    {
 +        consoleplayer = 0;
 +    }
 +
 +    for (i=0; i<MAXPLAYERS; ++i)
 +    {
 +        playeringame[i] = i < settings->num_players;
 +        PlayerClass[i] = settings->player_classes[i];
 +
 +        if (PlayerClass[i] >= NUMCLASSES)
 +        {
 +            PlayerClass[i] = PCLASS_FIGHTER;
 +        }
 +    }
 +}
 +
 +// 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;
 +
 +    // jhaley 20120715: Some parts of the structure are being left
 +    // uninitialized. If -class is not used on the command line, this
 +    // can lead to a crash in SB_Init due to player class == 0xCCCCCCCC.
 +    memset(settings, 0, sizeof(*settings));
 +
 +    // Fill in game settings structure with appropriate parameters
 +    // for the new game
 +
 +    settings->deathmatch = deathmatch;
 +    settings->episode = startepisode;
 +    settings->map = startmap;
 +    settings->skill = startskill;
 +    // TODO settings->loadgame = startloadgame;
 +    settings->gameversion = exe_hexen_1_1;
 +    settings->nomonsters = nomonsters;
 +    settings->respawn_monsters = respawnparm;
 +    settings->timelimit = 0;
 +    settings->lowres_turn = false;
 +
 +    //
 +    // Connect data
 +    //
 +
 +    // Game type fields:
 +
 +    connect_data->gamemode = gamemode;
 +    connect_data->gamemission = gamemission;
 +
 +    connect_data->lowres_turn = false;
 +    connect_data->drone = false;
 +    connect_data->max_players = MAXPLAYERS;
 +
 +    //!
 +    // @category net
 +    // @arg <n>
 +    //
 +    // Specify player class: 0=fighter, 1=cleric, 2=mage, 3=pig.
 +    //
 +
 +    i = M_CheckParmWithArgs("-class", 1);
 +
 +    if (i > 0)
 +    {
 +        connect_data->player_class = atoi(myargv[i + 1]);
 +    }
 +    else
 +    {
 +        connect_data->player_class = PCLASS_FIGHTER;
 +    }
 +
 +    // Read checksums of our WAD directory and dehacked information
 +
-     W_Checksum(connect_data->wad_md5sum);
-     memset(connect_data->deh_md5sum, 0, sizeof(md5_digest_t));
++    W_Checksum(connect_data->wad_sha1sum);
++    memset(connect_data->deh_sha1sum, 0, sizeof(sha1_digest_t));
 +
 +    connect_data->is_freedoom = 0;
 +}
 +
 +void D_InitSinglePlayerGame(net_gamesettings_t *settings)
 +{
 +    // default values for single player
 +
 +    settings->consoleplayer = 0;
 +    settings->num_players = 1;
 +
 +    netgame = false;
 +
 +    //!
 +    // @category net
 +    //
 +    // Start the game playing as though in a netgame with a single
 +    // player.  This can also be used to play back single player netgame
 +    // demos.
 +    //
 +
 +    if (M_CheckParm("-solo-net") > 0)
 +    {
 +        netgame = true;
 +    }
 +}
 +
 +//
 +// D_CheckNetGame
 +// Works out player numbers among the net participants
 +//
 +
 +void D_CheckNetGame (void)
 +{
 +    net_connect_data_t connect_data;
 +    net_gamesettings_t settings;
 +
 +    D_RegisterLoopCallbacks(&hexen_loop_interface);
 +
 +    // Call D_QuitNetGame on exit
 +
 +    I_AtExit(D_QuitNetGame, true);
 +
 +    SaveGameSettings(&settings, &connect_data);
 +
 +    if (D_InitNetGame(&connect_data, &settings))
 +    {
 +        netgame = true;
 +        autostart = true;
 +    }
 +    else
 +    {
 +        D_InitSinglePlayerGame(&settings);
 +    }
 +
 +    LoadGameSettings(&settings, &connect_data);
 +}
 +
 +//==========================================================================
 +//
 +// NET_SendFrags
 +//
 +//==========================================================================
 +
 +void NET_SendFrags(player_t * player)
 +{
 +    // Not sure what this is intended for. Unused?
 +}
 +
diff --cc src/i_video.c
index 7ba4c8cd,895043da..959a1472
--- a/src/i_video.c
+++ b/src/i_video.c
@@@ -378,12 -276,44 +378,43 @@@ static void UpdateFocus(void
      screenvisible = (state & SDL_APPACTIVE) != 0;
  }

+ // Show or hide the mouse cursor. We have to use different techniques
+ // depending on the OS.
+
+ static void ShowCursor(boolean show)
+ {
+     // On Windows, using SDL_ShowCursor() adds lag to the mouse input,
+     // so work around this by setting an invisible cursor instead. On
+     // other systems, it isn't possible to change the cursor, so this
+     // hack has to be Windows-only. (Thanks to entryway for this)
+
+ #ifdef _WIN32
+     if (show)
+     {
+         SDL_SetCursor(cursors[1]);
+     }
+     else
+     {
+         SDL_SetCursor(cursors[0]);
+     }
+ #else
+     SDL_ShowCursor(show);
+ #endif
+
+     // When the cursor is hidden, grab the input.
+
+     if (!screensaver_mode)
+     {
+         SDL_WM_GrabInput(!show);
+     }
+ }
+
 -static void LoadDiskImage(void)
 +void I_EnableLoadingDisk(void)
  {
      patch_t *disk;
 +    byte *tmpbuf;
      char *disk_name;
      int y;
 -    int xoffset = SCREENWIDTH - LOADING_DISK_W;
 -    int yoffset = SCREENHEIGHT - LOADING_DISK_H;
      char buf[20];

      SDL_VideoDriverName(buf, 15);
diff --cc src/m_misc.c
index e14436a3,31c87898..47aea8b9
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@@ -207,92 -206,22 +207,111 @@@ boolean M_StrToInt(const char *str, in
          || sscanf(str, " %d", result) == 1;
  }

 +void M_ExtractFileBase(char *path, char *dest)
 +{
 +    char *src;
 +    char *filename;
 +    int length;
 +
 +    src = path + strlen(path) - 1;
 +
 +    // back up until a \ or the start
 +    while (src != path && *(src - 1) != DIR_SEPARATOR)
 +    {
 +	src--;
 +    }
 +
 +    filename = src;
 +
 +    // Copy up to eight characters
 +    // Note: Vanilla Doom exits with an error if a filename is specified
 +    // with a base of more than eight characters.  To remove the 8.3
 +    // filename limit, instead we simply truncate the name.
 +
 +    length = 0;
 +    memset(dest, 0, 8);
 +
 +    while (*src != '\0' && *src != '.')
 +    {
 +        if (length >= 8)
 +        {
 +            printf("Warning: Truncated '%s' lump name to '%.8s'.\n",
 +                   filename, dest);
 +            break;
 +        }
 +
 +	dest[length++] = toupper((int)*src++);
 +    }
 +}
 +
 +//---------------------------------------------------------------------------
 +//
 +// PROC M_ForceUppercase
 +//
 +// Change string to uppercase.
 +//
 +//---------------------------------------------------------------------------
 +
 +void M_ForceUppercase(char *text)
 +{
 +    char *p;
 +
 +    for (p = text; *p != '\0'; ++p)
 +    {
 +        *p = toupper(*p);
 +    }
 +}
 +
 +//
 +// M_StrCaseStr
 +//
 +// Case-insensitive version of strstr()
 +//
 +
 +char *M_StrCaseStr(char *haystack, char *needle)
 +{
 +    unsigned int haystack_len;
 +    unsigned int needle_len;
 +    unsigned int len;
 +    unsigned int i;
 +
 +    haystack_len = strlen(haystack);
 +    needle_len = strlen(needle);
 +
 +    if (haystack_len < needle_len)
 +    {
 +        return NULL;
 +    }
 +
 +    len = haystack_len - needle_len;
 +
 +    for (i = 0; i <= len; ++i)
 +    {
 +        if (!strncasecmp(haystack + i, needle, needle_len))
 +        {
 +            return haystack + i;
 +        }
 +    }
 +
 +    return NULL;
 +}
 +
+ #ifdef _WIN32
+
+ char *M_OEMToUTF8(const char *oem)
+ {
+     unsigned int len = strlen(oem) + 1;
+     wchar_t *tmp;
+     char *result;
+
+     tmp = malloc(len * sizeof(wchar_t));
+     MultiByteToWideChar(CP_OEMCP, 0, oem, len, tmp, len);
+     result = malloc(len * 4);
+     WideCharToMultiByte(CP_UTF8, 0, tmp, len, result, len * 4, NULL, NULL);
+     free(tmp);
+
+     return result;
+ }
+
+ #endif
+
diff --cc src/m_misc.h
index c6be6ccb,6c2da4b1..ac5b5164
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@@ -40,9 -40,7 +40,10 @@@ char *M_TempFile(char *s)
  boolean M_FileExists(char *file);
  long M_FileLength(FILE *handle);
  boolean M_StrToInt(const char *str, int *result);
 +void M_ExtractFileBase(char *path, char *dest);
 +void M_ForceUppercase(char *text);
 +char *M_StrCaseStr(char *haystack, char *needle);
+ char *M_OEMToUTF8(const char *ansi);

  #endif

diff --cc src/net_client.c
index 083dca8b,24fb7f6c..abd94145
--- a/src/net_client.c
+++ b/src/net_client.c
@@@ -905,9 -1133,21 +905,9 @@@ boolean NET_CL_Connect(net_addr_t *addr

      server_addr = addr;

-     memcpy(net_local_wad_md5sum, data->wad_md5sum, sizeof(md5_digest_t));
-     memcpy(net_local_deh_md5sum, data->deh_md5sum, sizeof(md5_digest_t));
 -    // Are we recording a demo? Possibly set lowres turn mode
 -
 -    if (M_CheckParm("-record") > 0 && M_CheckParm("-longtics") == 0)
 -    {
 -        lowres_turn = true;
 -    }
 -
 -    // Read checksums of our WAD directory and dehacked information
 -
 -    W_Checksum(net_local_wad_sha1sum);
 -    DEH_Checksum(net_local_deh_sha1sum);
 -
 -    // Are we playing with the Freedoom IWAD?
 -
 -    net_local_is_freedoom = W_CheckNumForName("FREEDOOM") >= 0;
++    memcpy(net_local_wad_sha1sum, data->wad_sha1sum, sizeof(sha1_digest_t));
++    memcpy(net_local_deh_sha1sum, data->deh_sha1sum, sizeof(sha1_digest_t));
 +    net_local_is_freedoom = data->is_freedoom;

      // create a new network I/O context and add just the
      // necessary module
diff --cc src/net_client.h
index 242bf958,a752b747..3914849d
--- a/src/net_client.h
+++ b/src/net_client.h
@@@ -24,35 -24,40 +24,35 @@@
  #ifndef NET_CLIENT_H
  #define NET_CLIENT_H

 -#include "doomdef.h"
  #include "doomtype.h"
  #include "d_ticcmd.h"
- #include "md5.h"
+ #include "sha1.h"
  #include "net_defs.h"

 -#define MAXPLAYERNAME 30
 -
 -boolean NET_CL_Connect(net_addr_t *addr);
 +boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data);
  void NET_CL_Disconnect(void);
  void NET_CL_Run(void);
  void NET_CL_Init(void);
 -void NET_CL_StartGame();
 +void NET_CL_StartGame(net_gamesettings_t *settings);
  void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic);
 +boolean NET_CL_GetSettings(net_gamesettings_t *_settings);
  void NET_Init(void);

 +void NET_BindVariables(void);
 +
  extern boolean net_client_connected;
  extern boolean net_client_received_wait_data;
 -extern boolean net_client_controller;
 -extern unsigned int net_clients_in_game;
 -extern unsigned int net_drones_in_game;
 +extern net_waitdata_t net_client_wait_data;
  extern boolean net_waiting_for_start;
 -extern char net_player_names[MAXPLAYERS][MAXPLAYERNAME];
 -extern char net_player_addresses[MAXPLAYERS][MAXPLAYERNAME];
 -extern int net_player_number;
  extern char *net_player_name;

- extern md5_digest_t net_server_wad_md5sum;
- extern md5_digest_t net_server_deh_md5sum;
+ extern sha1_digest_t net_server_wad_sha1sum;
+ extern sha1_digest_t net_server_deh_sha1sum;
  extern unsigned int net_server_is_freedoom;
- extern md5_digest_t net_local_wad_md5sum;
- extern md5_digest_t net_local_deh_md5sum;
+ extern sha1_digest_t net_local_wad_sha1sum;
+ extern sha1_digest_t net_local_deh_sha1sum;
  extern unsigned int net_local_is_freedoom;

 +extern boolean drone;

  #endif /* #ifndef NET_CLIENT_H */
 -
diff --cc src/net_defs.h
index aa7b6f6e,c2d4d561..ba50f48e
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@@ -26,31 -26,9 +26,31 @@@
  #ifndef NET_DEFS_H
  #define NET_DEFS_H

 -#include "doomdef.h"
 +#include <stdio.h>
 +
  #include "doomtype.h"
  #include "d_ticcmd.h"
- #include "md5.h"
++#include "sha1.h"
 +
 +// Absolute maximum number of "nodes" in the game.  This is different to
 +// NET_MAXPLAYERS, as there may be observers that are not participating
 +// (eg. left/right monitors)
 +
 +#define MAXNETNODES 16
 +
 +// The maximum number of players, multiplayer/networking.
 +// This is the maximum supported by the networking code; individual games
 +// have their own values for MAXPLAYERS that can be smaller.
 +
 +#define NET_MAXPLAYERS 8
 +
 +// Maximum length of a player's name.
 +
 +#define MAXPLAYERNAME 30
 +
 +// Networking and tick handling related.
 +
 +#define BACKUPTICS 128

  typedef struct _net_module_s net_module_t;
  typedef struct _net_packet_s net_packet_t;
@@@ -140,27 -118,15 +140,33 @@@ typedef enu
      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_QUERY_RESPONSE,
+     NET_MASTER_PACKET_TYPE_GET_METADATA,
+     NET_MASTER_PACKET_TYPE_GET_METADATA_RESPONSE,
+     NET_MASTER_PACKET_TYPE_SIGN_START,
+     NET_MASTER_PACKET_TYPE_SIGN_START_RESPONSE,
+     NET_MASTER_PACKET_TYPE_SIGN_END,
+     NET_MASTER_PACKET_TYPE_SIGN_END_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;
 +    int max_players;
 +    int is_freedoom;
-     md5_digest_t wad_md5sum;
-     md5_digest_t deh_md5sum;
++    sha1_digest_t wad_sha1sum;
++    sha1_digest_t deh_sha1sum;
 +    int 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;
@@@ -228,20 -181,5 +234,20 @@@ typedef struc
      char *description;
  } net_querydata_t;

 -#endif /* #ifndef NET_DEFS_H */
 +// Data sent by the server while waiting for the game to start.

 +typedef struct
 +{
 +    int num_players;
 +    int num_drones;
 +    int max_players;
 +    int is_controller;
 +    int consoleplayer;
 +    char player_names[NET_MAXPLAYERS][MAXPLAYERNAME];
 +    char player_addrs[NET_MAXPLAYERS][MAXPLAYERNAME];
-     md5_digest_t wad_md5sum;
-     md5_digest_t deh_md5sum;
++    sha1_digest_t wad_sha1sum;
++    sha1_digest_t deh_sha1sum;
 +    int is_freedoom;
 +} net_waitdata_t;
 +
 +#endif /* #ifndef NET_DEFS_H */
diff --cc src/net_gui.c
index 7fff33f6,0ff9e55b..14d0d943
--- a/src/net_gui.c
+++ b/src/net_gui.c
@@@ -209,13 -178,11 +209,13 @@@ static void CheckSHA1Sums(void
          return;
      }

-     correct_wad = memcmp(net_local_wad_md5sum,
-                          net_client_wait_data.wad_md5sum,
-                          sizeof(md5_digest_t)) == 0;
-     correct_deh = memcmp(net_local_deh_md5sum,
-                          net_client_wait_data.deh_md5sum,
-                          sizeof(md5_digest_t)) == 0;
 -    correct_wad = memcmp(net_local_wad_sha1sum, net_server_wad_sha1sum,
++    correct_wad = memcmp(net_local_wad_sha1sum,
++                         net_client_wait_data.wad_sha1sum,
+                          sizeof(sha1_digest_t)) == 0;
 -    correct_deh = memcmp(net_local_deh_sha1sum, net_server_deh_sha1sum,
++    correct_deh = memcmp(net_local_deh_sha1sum,
++                         net_client_wait_data.deh_sha1sum,
+                          sizeof(sha1_digest_t)) == 0;
 -    same_freedoom = net_server_is_freedoom == net_local_is_freedoom;
 +    same_freedoom = net_client_wait_data.is_freedoom == net_local_is_freedoom;

      if (correct_wad && correct_deh && same_freedoom)
      {
@@@ -224,9 -191,9 +224,9 @@@

      if (!correct_wad)
      {
-         printf("Warning: WAD MD5 does not match server:\n");
-         PrintMD5Digest("Local", net_local_wad_md5sum);
-         PrintMD5Digest("Server", net_client_wait_data.wad_md5sum);
+         printf("Warning: WAD SHA1 does not match server:\n");
+         PrintSHA1Digest("Local", net_local_wad_sha1sum);
 -        PrintSHA1Digest("Server", net_server_wad_sha1sum);
++        PrintSHA1Digest("Server", net_client_wait_data.wad_sha1sum);
      }

      if (!same_freedoom)
@@@ -239,9 -206,9 +239,9 @@@

      if (!correct_deh)
      {
-         printf("Warning: Dehacked MD5 does not match server:\n");
-         PrintMD5Digest("Local", net_local_deh_md5sum);
-         PrintMD5Digest("Server", net_client_wait_data.deh_md5sum);
+         printf("Warning: Dehacked SHA1 does not match server:\n");
+         PrintSHA1Digest("Local", net_local_deh_sha1sum);
 -        PrintSHA1Digest("Server", net_server_deh_sha1sum);
++        PrintSHA1Digest("Server", net_client_wait_data.deh_sha1sum);
      }

      window = TXT_NewWindow("WARNING");
@@@ -307,8 -273,8 +307,8 @@@ void NET_WaitForStart(net_gamesettings_

      while (net_waiting_for_start)
      {
 -        UpdateGUI();
 +        UpdateGUI(settings);
-         CheckMD5Sums();
+         CheckSHA1Sums();

          TXT_DispatchEvents();
          TXT_DrawDesktop();
diff --cc src/net_server.c
index b837bd70,189e824f..585f0eac
--- a/src/net_server.c
+++ b/src/net_server.c
@@@ -640,18 -622,17 +645,18 @@@ static void NET_SV_ParseSYN(net_packet_

          // Adopt the game mode and mission of the first connecting client

 -        if (num_players == 0 && !cl_drone)
 +        if (num_players == 0 && !data.drone)
          {
 -            sv_gamemode = cl_gamemode;
 -            sv_gamemission = cl_gamemission;
 +            sv_gamemode = data.gamemode;
 +            sv_gamemission = data.gamemission;
          }

-         // Save the MD5 checksums
+         // Save the SHA1 checksums

-         memcpy(client->wad_md5sum, data.wad_md5sum, sizeof(md5_digest_t));
-         memcpy(client->deh_md5sum, data.deh_md5sum, sizeof(md5_digest_t));
 -        memcpy(client->wad_sha1sum, wad_sha1sum, sizeof(sha1_digest_t));
 -        memcpy(client->deh_sha1sum, deh_sha1sum, sizeof(sha1_digest_t));
 -        client->is_freedoom = is_freedoom;
++        memcpy(client->wad_sha1sum, data.wad_sha1sum, sizeof(sha1_digest_t));
++        memcpy(client->deh_sha1sum, data.deh_sha1sum, sizeof(sha1_digest_t));
 +        client->is_freedoom = data.is_freedoom;
 +        client->max_players = data.max_players;

          // Check the connecting client is playing the same game as all
          // the other clients
@@@ -1272,49 -1236,62 +1277,49 @@@ static void NET_SV_SendWaitingData(net_

      controller = NET_SV_Controller();

 -    num_players = NET_SV_NumPlayers();
 +    wait_data.num_players = NET_SV_NumPlayers();
 +    wait_data.num_drones = NET_SV_NumDrones();
 +    wait_data.max_players = NET_SV_MaxPlayers();
 +    wait_data.is_controller = (client == controller);
 +    wait_data.consoleplayer = client->player_number;

 -    // time to send the client another status packet
 -
 -    packet = NET_NewPacket(10);
 -    NET_WriteInt16(packet, NET_PACKET_TYPE_WAITING_DATA);
 -
 -    // include the number of players waiting
 -
 -    NET_WriteInt8(packet, num_players);
 -
 -    // send the number of drone clients
 -
 -    NET_WriteInt8(packet, NET_SV_NumDrones());
 -
 -    // indicate whether the client is the controller
 -
 -    NET_WriteInt8(packet, client == controller);
 -
 -    // send the player number of this client
 -
 -    NET_WriteInt8(packet, client->player_number);
 -
 -    // send the addresses of all players
 +    // Send the WAD and dehacked checksums of the controlling client.
 +    // If no controller found (?), send the details that the client
 +    // is expecting anyway.

 -    for (i=0; i<num_players; ++i)
 +    if (controller != NULL)
      {
 -        char *addr;
 -
 -        // name
 +        controller = client;
 +    }

-     memcpy(&wait_data.wad_md5sum, &controller->wad_md5sum,
-            sizeof(md5_digest_t));
-     memcpy(&wait_data.deh_md5sum, &controller->deh_md5sum,
-            sizeof(md5_digest_t));
 -        NET_WriteString(packet, sv_players[i]->name);
++    memcpy(&wait_data.wad_sha1sum, &controller->wad_sha1sum,
++           sizeof(sha1_digest_t));
++    memcpy(&wait_data.deh_sha1sum, &controller->deh_sha1sum,
++           sizeof(sha1_digest_t));
 +    wait_data.is_freedoom = controller->is_freedoom;

 -        // address
 +    // set name and address of each player:

 -        addr = NET_AddrToString(sv_players[i]->addr);
 +    for (i = 0; i < wait_data.num_players; ++i)
 +    {
 +        strncpy(wait_data.player_names[i],
 +                sv_players[i]->name,
 +                MAXPLAYERNAME);
 +        wait_data.player_names[i][MAXPLAYERNAME-1] = '\0';

 -        NET_WriteString(packet, addr);
 +        strncpy(wait_data.player_addrs[i],
 +                NET_AddrToString(sv_players[i]->addr),
 +                MAXPLAYERNAME);
 +        wait_data.player_addrs[i][MAXPLAYERNAME-1] = '\0';
      }

 -    // Send the WAD and dehacked checksums of the controlling client.
 +    // Construct packet:

 -    if (controller != NULL)
 -    {
 -        NET_WriteSHA1Sum(packet, controller->wad_sha1sum);
 -        NET_WriteSHA1Sum(packet, controller->deh_sha1sum);
 -        NET_WriteInt8(packet, controller->is_freedoom);
 -    }
 -    else
 -    {
 -        NET_WriteSHA1Sum(packet, client->wad_sha1sum);
 -        NET_WriteSHA1Sum(packet, client->deh_sha1sum);
 -        NET_WriteInt8(packet, client->is_freedoom);
 -    }
 +    packet = NET_NewPacket(10);
 +    NET_WriteInt16(packet, NET_PACKET_TYPE_WAITING_DATA);
 +    NET_WriteWaitData(packet, &wait_data);

 -    // send packet to client and free
 +    // Send packet to client and free

      NET_Conn_SendPacket(&client->connection, packet);
      NET_FreePacket(packet);
diff --cc src/net_structrw.c
index f68a6130,818766e4..8f6f8d57
--- a/src/net_structrw.c
+++ b/src/net_structrw.c
@@@ -31,36 -29,8 +31,36 @@@
  #include "net_packet.h"
  #include "net_structrw.h"

 +void NET_WriteConnectData(net_packet_t *packet, net_connect_data_t *data)
 +{
 +    NET_WriteInt8(packet, data->gamemode);
 +    NET_WriteInt8(packet, data->gamemission);
 +    NET_WriteInt8(packet, data->lowres_turn);
 +    NET_WriteInt8(packet, data->drone);
 +    NET_WriteInt8(packet, data->max_players);
 +    NET_WriteInt8(packet, data->is_freedoom);
-     NET_WriteMD5Sum(packet, data->wad_md5sum);
-     NET_WriteMD5Sum(packet, data->deh_md5sum);
++    NET_WriteSHA1Sum(packet, data->wad_sha1sum);
++    NET_WriteSHA1Sum(packet, data->deh_sha1sum);
 +    NET_WriteInt8(packet, data->player_class);
 +}
 +
 +boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data)
 +{
 +    return NET_ReadInt8(packet, (unsigned int *) &data->gamemode)
 +        && NET_ReadInt8(packet, (unsigned int *) &data->gamemission)
 +        && NET_ReadInt8(packet, (unsigned int *) &data->lowres_turn)
 +        && NET_ReadInt8(packet, (unsigned int *) &data->drone)
 +        && NET_ReadInt8(packet, (unsigned int *) &data->max_players)
 +        && NET_ReadInt8(packet, (unsigned int *) &data->is_freedoom)
-         && NET_ReadMD5Sum(packet, data->wad_md5sum)
-         && NET_ReadMD5Sum(packet, data->deh_md5sum)
++        && NET_ReadSHA1Sum(packet, data->wad_sha1sum)
++        && NET_ReadSHA1Sum(packet, data->deh_sha1sum)
 +        && NET_ReadInt8(packet, (unsigned int *) &data->player_class);
 +}
 +
  void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings)
  {
 +    int i;
 +
      NET_WriteInt8(packet, settings->ticdup);
      NET_WriteInt8(packet, settings->extratics);
      NET_WriteInt8(packet, settings->deathmatch);
@@@ -445,68 -321,7 +445,68 @@@ void NET_WriteFullTiccmd(net_packet_t *
      }
  }

 +void NET_WriteWaitData(net_packet_t *packet, net_waitdata_t *data)
 +{
 +    int i;
 +
 +    NET_WriteInt8(packet, data->num_players);
 +    NET_WriteInt8(packet, data->num_drones);
 +    NET_WriteInt8(packet, data->max_players);
 +    NET_WriteInt8(packet, data->is_controller);
 +    NET_WriteInt8(packet, data->consoleplayer);
 +
 +    for (i = 0; i < data->num_players && i < NET_MAXPLAYERS; ++i)
 +    {
 +        NET_WriteString(packet, data->player_names[i]);
 +        NET_WriteString(packet, data->player_addrs[i]);
 +    }
 +
-     NET_WriteMD5Sum(packet, data->wad_md5sum);
-     NET_WriteMD5Sum(packet, data->deh_md5sum);
++    NET_WriteSHA1Sum(packet, data->wad_sha1sum);
++    NET_WriteSHA1Sum(packet, data->deh_sha1sum);
 +    NET_WriteInt8(packet, data->is_freedoom);
 +}
 +
 +boolean NET_ReadWaitData(net_packet_t *packet, net_waitdata_t *data)
 +{
 +    int i;
 +    char *s;
 +
 +    if (!NET_ReadInt8(packet, (unsigned int *) &data->num_players)
 +     || !NET_ReadInt8(packet, (unsigned int *) &data->num_drones)
 +     || !NET_ReadInt8(packet, (unsigned int *) &data->max_players)
 +     || !NET_ReadInt8(packet, (unsigned int *) &data->is_controller)
 +     || !NET_ReadSInt8(packet, &data->consoleplayer))
 +    {
 +        return false;
 +    }
 +
 +    for (i = 0; i < data->num_players && i < NET_MAXPLAYERS; ++i)
 +    {
 +        s = NET_ReadString(packet);
 +
 +        if (s == NULL || strlen(s) >= MAXPLAYERNAME)
 +        {
 +            return false;
 +        }
 +
 +        strcpy(data->player_names[i], s);
 +
 +        s = NET_ReadString(packet);
 +
 +        if (s == NULL || strlen(s) >= MAXPLAYERNAME)
 +        {
 +            return false;
 +        }
 +
 +        strcpy(data->player_addrs[i], s);
 +    }
 +
-     return NET_ReadMD5Sum(packet, data->wad_md5sum)
-         && NET_ReadMD5Sum(packet, data->deh_md5sum)
++    return NET_ReadSHA1Sum(packet, data->wad_sha1sum)
++        && NET_ReadSHA1Sum(packet, data->deh_sha1sum)
 +        && NET_ReadInt8(packet, (unsigned int *) &data->is_freedoom);
 +}
 +
- boolean NET_ReadMD5Sum(net_packet_t *packet, md5_digest_t digest)
+ static boolean NET_ReadBlob(net_packet_t *packet, uint8_t *buf, size_t len)
  {
      unsigned int b;
      int i;
@@@ -534,22 -349,23 +534,42 @@@ static void NET_WriteBlob(net_packet_t
      }
  }

+ boolean NET_ReadSHA1Sum(net_packet_t *packet, sha1_digest_t digest)
+ {
+     return NET_ReadBlob(packet, digest, sizeof(sha1_digest_t));
+ }
+
+ void NET_WriteSHA1Sum(net_packet_t *packet, sha1_digest_t digest)
+ {
+     NET_WriteBlob(packet, digest, sizeof(sha1_digest_t));
+ }
+
+ boolean NET_ReadPRNGSeed(net_packet_t *packet, prng_seed_t seed)
+ {
+     return NET_ReadBlob(packet, seed, sizeof(prng_seed_t));
+ }
+
+ void NET_WritePRNGSeed(net_packet_t *packet, prng_seed_t seed)
+ {
+     NET_WriteBlob(packet, seed, sizeof(prng_seed_t));
+ }
+
 +// "Safe" version of puts, for displaying messages received from the
 +// network.
 +
 +void NET_SafePuts(char *s)
 +{
 +    char *p;
 +
 +    // Do not do a straight "puts" of the string, as this could be
 +    // dangerous (sending control codes to terminals can do all
 +    // kinds of things)
 +
 +    for (p=s; *p; ++p)
 +    {
 +        if (isprint(*p))
 +            putchar(*p);
 +    }
 +
 +    putchar('\n');
 +}
diff --cc src/net_structrw.h
index e894e170,23bd67a1..3d446841
--- a/src/net_structrw.h
+++ b/src/net_structrw.h
@@@ -43,12 -41,11 +44,15 @@@ extern void NET_TiccmdPatch(ticcmd_t *s
  boolean NET_ReadFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd, boolean lowres_turn);
  void NET_WriteFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd, boolean lowres_turn);

- boolean NET_ReadMD5Sum(net_packet_t *packet, md5_digest_t digest);
- void NET_WriteMD5Sum(net_packet_t *packet, md5_digest_t digest);
+ boolean NET_ReadSHA1Sum(net_packet_t *packet, sha1_digest_t digest);
+ void NET_WriteSHA1Sum(net_packet_t *packet, sha1_digest_t digest);

 +void NET_WriteWaitData(net_packet_t *packet, net_waitdata_t *data);
 +boolean NET_ReadWaitData(net_packet_t *packet, net_waitdata_t *data);
 +
 +void NET_SafePuts(char *msg);
 +
+ boolean NET_ReadPRNGSeed(net_packet_t *packet, prng_seed_t seed);
+ void NET_WritePRNGSeed(net_packet_t *packet, prng_seed_t seed);
+
  #endif /* #ifndef NET_STRUCTRW_H */
 -
diff --cc src/setup/Makefile.am
index 7fe051ee,9263a3a8..f58c6294
--- a/src/setup/Makefile.am
+++ b/src/setup/Makefile.am
@@@ -31,12 -40,17 +31,12 @@@ appdir = $(prefix)/share/application
  app_DATA = @PROGRAM_PREFIX@setup.desktop

  @PROGRAM_PREFIX@setup.desktop : setup.desktop
- 	cp $^ $@
+ 	cp setup.desktop $@

 -.rc.o:
 -	$(WINDRES) $^ -o $@
 -%.o : %.rc
 -	$(WINDRES) $^ -o $@
 -
  if HAVE_PYTHON

 -setup_icon.c : ../data/setup8.ico
 -	../data/convert-icon ../data/setup8.ico $@
 +setup_icon.c : $(top_builddir)/data/setup8.ico
 +	$(top_builddir)/data/convert-icon $^ $@

  endif

diff --cc src/setup/multiplayer.c
index d9bda1fe,e8977763..dc0643e3
--- a/src/setup/multiplayer.c
+++ b/src/setup/multiplayer.c
@@@ -1010,12 -824,22 +1032,22 @@@ void SetPlayerNameDefault(void

      if (net_player_name == NULL)
      {
 -        net_player_name = getenv("USERNAME");
 +        net_player_name = strdup(getenv("USERNAME"));
      }

+     // On Windows, environment variables are in OEM codepage
+     // encoding, so convert to UTF8:
+
+ #ifdef _WIN32
+     if (net_player_name != NULL)
+     {
+         net_player_name = M_OEMToUTF8(net_player_name);
+     }
+ #endif
+
      if (net_player_name == NULL)
      {
 -        net_player_name = "player";
 +        net_player_name = strdup("player");
      }
  }

diff --cc src/strife/d_net.c
index cd0a5e66,00000000..a3f5513c
mode 100644,000000..100644
--- a/src/strife/d_net.c
+++ b/src/strife/d_net.c
@@@ -1,316 -1,0 +1,316 @@@
 +// 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 "w_checksum.h"
 +
 +#include "deh_main.h"
 +
 +#include "d_loop.h"
 +
 +ticcmd_t *netcmds;
 +
 +// Called when a player leaves the game
 +
 +static void 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 RunTic(ticcmd_t *cmds, boolean *ingame)
 +{
 +    extern boolean advancedemo;
 +    unsigned int i;
 +
 +    // Check for player quits.
 +
 +    for (i = 0; i < MAXPLAYERS; ++i)
 +    {
 +        if (!demoplayback && playeringame[i] && !ingame[i])
 +        {
 +            PlayerQuitGame(&players[i]);
 +        }
 +    }
 +
 +    netcmds = cmds;
 +
 +    // check that there are players in the game.  if not, we cannot
 +    // run a tic.
 +
 +    if (advancedemo)
 +        D_DoAdvanceDemo ();
 +
 +    M_Ticker();
 +    G_Ticker();
 +}
 +
 +static void NullMenuTicker()
 +{
 +    // no-op.
 +}
 +
 +static loop_interface_t strife_loop_interface = {
 +    D_ProcessEvents,
 +    G_BuildTiccmd,
 +    RunTic,
 +    NullMenuTicker
 +};
 +
 +
 +// Load game settings from the specified structure and
 +// set global variables.
 +
 +static void LoadGameSettings(net_gamesettings_t *settings,
 +                             net_connect_data_t *connect_data)
 +{
 +    unsigned int i;
 +
 +    deathmatch = settings->deathmatch;
 +    ticdup = settings->ticdup;
 +    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");
 +    }
 +
 +    if (!connect_data->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)
 +{
 +    // 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;
 +
 +    connect_data->drone = false;
 +    connect_data->max_players = MAXPLAYERS;
 +
 +    //!
 +    // @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;
 +    }
 +
 +    //
 +    // Connect data
 +    //
 +
 +    // Game type fields:
 +
 +    connect_data->gamemode = gamemode;
 +    connect_data->gamemission = gamemission;
 +
 +    // Are we recording a demo? Possibly set lowres turn mode
 +
 +    connect_data->lowres_turn = settings->lowres_turn;
 +
 +    // Read checksums of our WAD directory and dehacked information
 +
-     W_Checksum(connect_data->wad_md5sum);
-     DEH_Checksum(connect_data->deh_md5sum);
++    W_Checksum(connect_data->wad_sha1sum);
++    DEH_Checksum(connect_data->deh_sha1sum);
 +
 +    connect_data->is_freedoom = 0;
 +}
 +
 +void D_InitSinglePlayerGame(net_gamesettings_t *settings)
 +{
 +    // default values for single player
 +
 +    settings->consoleplayer = 0;
 +    settings->num_players = 1;
 +
 +    netgame = false;
 +
 +    //!
 +    // @category net
 +    //
 +    // Start the game playing as though in a netgame with a single
 +    // player.  This can also be used to play back single player netgame
 +    // demos.
 +    //
 +
 +    if (M_CheckParm("-solo-net") > 0)
 +    {
 +        netgame = true;
 +    }
 +}
 +
 +//
 +// D_CheckNetGame
 +// Works out player numbers among the net participants
 +//
 +void D_CheckNetGame (void)
 +{
 +    net_connect_data_t connect_data;
 +    net_gamesettings_t settings;
 +
 +    D_RegisterLoopCallbacks(&strife_loop_interface);
 +
 +    // Call D_QuitNetGame on exit
 +
 +    I_AtExit(D_QuitNetGame, true);
 +
 +    SaveGameSettings(&settings, &connect_data);
 +
 +    if (D_InitNetGame(&connect_data, &settings))
 +    {
 +        netgame = true;
 +        autostart = true;
 +    }
 +    else
 +    {
 +        D_InitSinglePlayerGame(&settings);
 +    }
 +
 +    LoadGameSettings(&settings, &connect_data);
 +
 +    // Strife games are always deathmatch, though -altdeath is
 +    // supported for respawning items.
 +
 +    if (netgame && deathmatch == 0)
 +    {
 +        deathmatch = 1;
 +    }
 +
 +    DEH_printf("startmap: %i, skill: %i, enemies: %i, random: %i\n",
 +               startmap, startskill, !nomonsters, 0 /* ??? */);
 +
 +
 +    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 && deathmatch)
 +    {
 +        // 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");
 +        }
 +    }
 +}
 +
diff --cc src/strife/deh_ammo.c
index 2c6a9022,00000000..952d8df3
mode 100644,000000..100644
--- a/src/strife/deh_ammo.c
+++ b/src/strife/deh_ammo.c
@@@ -1,112 -1,0 +1,112 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// 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.
 +//
 +//-----------------------------------------------------------------------------
 +//
 +// Parses "Ammo" sections in dehacked files
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "doomdef.h"
 +#include "doomtype.h"
 +#include "deh_defs.h"
 +#include "deh_io.h"
 +#include "deh_main.h"
 +#include "p_local.h"
 +
 +static void *DEH_AmmoStart(deh_context_t *context, char *line)
 +{
 +    int ammo_number = 0;
 +
 +    if (sscanf(line, "Ammo %i", &ammo_number) != 1)
 +    {
 +        DEH_Warning(context, "Parse error on section start");
 +        return NULL;
 +    }
 +
 +    if (ammo_number < 0 || ammo_number >= NUMAMMO)
 +    {
 +        DEH_Warning(context, "Invalid ammo number: %i", ammo_number);
 +        return NULL;
 +    }
 +
 +    return &maxammo[ammo_number];
 +}
 +
 +static void DEH_AmmoParseLine(deh_context_t *context, char *line, void *tag)
 +{
 +    char *variable_name, *value;
 +    int ivalue;
 +    int ammo_number;
 +
 +    if (tag == NULL)
 +        return;
 +
 +    ammo_number = ((int *) tag) - maxammo;
 +
 +    // Parse the assignment
 +
 +    if (!DEH_ParseAssignment(line, &variable_name, &value))
 +    {
 +        // Failed to parse
 +
 +        DEH_Warning(context, "Failed to parse assignment");
 +        return;
 +    }
 +
 +    ivalue = atoi(value);
 +
 +    // maxammo
 +
 +    if (!strcasecmp(variable_name, "Per ammo"))
 +        clipammo[ammo_number] = ivalue;
 +    else if (!strcasecmp(variable_name, "Max ammo"))
 +        maxammo[ammo_number] = ivalue;
 +    else
 +    {
 +        DEH_Warning(context, "Field named '%s' not found", variable_name);
 +    }
 +}
 +
- static void DEH_AmmoMD5Hash(md5_context_t *context)
++static void DEH_AmmoSHA1Hash(sha1_context_t *context)
 +{
 +    int i;
 +
 +    for (i=0; i<NUMAMMO; ++i)
 +    {
-         MD5_UpdateInt32(context, clipammo[i]);
-         MD5_UpdateInt32(context, maxammo[i]);
++        SHA1_UpdateInt32(context, clipammo[i]);
++        SHA1_UpdateInt32(context, maxammo[i]);
 +    }
 +}
 +
 +deh_section_t deh_section_ammo =
 +{
 +    "Ammo",
 +    NULL,
 +    DEH_AmmoStart,
 +    DEH_AmmoParseLine,
 +    NULL,
-     DEH_AmmoMD5Hash,
++    DEH_AmmoSHA1Hash,
 +};
 +
diff --cc src/strife/deh_frame.c
index 9383b94f,00000000..dc68fde5
mode 100644,000000..100644
--- a/src/strife/deh_frame.c
+++ b/src/strife/deh_frame.c
@@@ -1,168 -1,0 +1,168 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// 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.
 +//
 +//-----------------------------------------------------------------------------
 +//
 +// Parses "Frame" sections in dehacked files
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "doomtype.h"
 +#include "d_items.h"
 +#include "info.h"
 +
 +#include "deh_defs.h"
 +#include "deh_io.h"
 +#include "deh_main.h"
 +#include "deh_mapping.h"
 +
 +DEH_BEGIN_MAPPING(state_mapping, state_t)
 +  DEH_MAPPING("Sprite number",    sprite)
 +  DEH_MAPPING("Sprite subnumber", frame)
 +  DEH_MAPPING("Duration",         tics)
 +  DEH_MAPPING("Next frame",       nextstate)
 +  DEH_UNSUPPORTED_MAPPING("Action pointer")
 +DEH_END_MAPPING
 +
 +static void *DEH_FrameStart(deh_context_t *context, char *line)
 +{
 +    int frame_number = 0;
 +    state_t *state;
 +
 +    if (sscanf(line, "Frame %i", &frame_number) != 1)
 +    {
 +        DEH_Warning(context, "Parse error on section start");
 +        return NULL;
 +    }
 +
 +    if (frame_number < 0 || frame_number >= NUMSTATES)
 +    {
 +        DEH_Warning(context, "Invalid frame number: %i", frame_number);
 +        return NULL;
 +    }
 +
 +    if (frame_number >= DEH_VANILLA_NUMSTATES)
 +    {
 +        DEH_Warning(context, "Attempt to modify frame %i: this will cause "
 +                             "problems in Vanilla dehacked.", frame_number);
 +    }
 +
 +    state = &states[frame_number];
 +
 +    return state;
 +}
 +
 +// Simulate a frame overflow: Doom has 967 frames in the states[] array, but
 +// DOS dehacked internally only allocates memory for 966.  As a result,
 +// attempts to set frame 966 (the last frame) will overflow the dehacked
 +// array and overwrite the weaponinfo[] array instead.
 +//
 +// This is noticable in Batman Doom where it is impossible to switch weapons
 +// away from the fist once selected.
 +
 +static void DEH_FrameOverflow(deh_context_t *context, char *varname, int value)
 +{
 +    if (!strcasecmp(varname, "Duration"))
 +    {
 +        weaponinfo[0].ammo = value;
 +    }
 +    else if (!strcasecmp(varname, "Codep frame"))
 +    {
 +        weaponinfo[0].upstate = value;
 +    }
 +    else if (!strcasecmp(varname, "Next frame"))
 +    {
 +        weaponinfo[0].downstate = value;
 +    }
 +    else if (!strcasecmp(varname, "Unknown 1"))
 +    {
 +        weaponinfo[0].readystate = value;
 +    }
 +    else if (!strcasecmp(varname, "Unknown 2"))
 +    {
 +        weaponinfo[0].atkstate = value;
 +    }
 +    else
 +    {
 +        DEH_Error(context, "Unable to simulate frame overflow: field '%s'",
 +                  varname);
 +    }
 +}
 +
 +static void DEH_FrameParseLine(deh_context_t *context, char *line, void *tag)
 +{
 +    state_t *state;
 +    char *variable_name, *value;
 +    int ivalue;
 +
 +    if (tag == NULL)
 +       return;
 +
 +    state = (state_t *) tag;
 +
 +    // Parse the assignment
 +
 +    if (!DEH_ParseAssignment(line, &variable_name, &value))
 +    {
 +        // Failed to parse
 +
 +        DEH_Warning(context, "Failed to parse assignment");
 +        return;
 +    }
 +
 +    // all values are integers
 +
 +    ivalue = atoi(value);
 +
 +    if (state == &states[NUMSTATES - 1])
 +    {
 +        DEH_FrameOverflow(context, variable_name, ivalue);
 +    }
 +    else
 +    {
 +        // set the appropriate field
 +
 +        DEH_SetMapping(context, &state_mapping, state, variable_name, ivalue);
 +    }
 +}
 +
- static void DEH_FrameMD5Sum(md5_context_t *context)
++static void DEH_FrameSHA1Sum(sha1_context_t *context)
 +{
 +    int i;
 +
 +    for (i=0; i<NUMSTATES; ++i)
 +    {
-         DEH_StructMD5Sum(context, &state_mapping, &states[i]);
++        DEH_StructSHA1Sum(context, &state_mapping, &states[i]);
 +    }
 +}
 +
 +deh_section_t deh_section_frame =
 +{
 +    "Frame",
 +    NULL,
 +    DEH_FrameStart,
 +    DEH_FrameParseLine,
 +    NULL,
-     DEH_FrameMD5Sum,
++    DEH_FrameSHA1Sum,
 +};
 +
diff --cc src/strife/deh_misc.c
index e3f2606c,00000000..aaebbadd
mode 100644,000000..100644
--- a/src/strife/deh_misc.c
+++ b/src/strife/deh_misc.c
@@@ -1,237 -1,0 +1,237 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// 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.
 +//
 +//-----------------------------------------------------------------------------
 +//
 +// Parses "Misc" sections in dehacked files
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "doomtype.h"
 +#include "deh_defs.h"
 +#include "deh_io.h"
 +#include "deh_main.h"
 +#include "deh_misc.h"
 +
 +// Dehacked: "Initial Health"
 +// This is the initial health a player has when starting anew.
 +// See G_PlayerReborn in g_game.c
 +
 +int deh_initial_health = DEH_DEFAULT_INITIAL_HEALTH;
 +
 +// Dehacked: "Initial bullets"
 +// This is the number of bullets the player has when starting anew.
 +// See G_PlayerReborn in g_game.c
 +
 +int deh_initial_bullets = DEH_DEFAULT_INITIAL_BULLETS;
 +
 +// Dehacked: "Max Health"
 +// This is the maximum health that can be reached using medikits
 +// alone.  See P_TouchSpecialThing in p_inter.c
 +
 +int deh_max_health = DEH_DEFAULT_MAX_HEALTH;
 +
 +// Dehacked: "Max Armor"
 +// This is the maximum armor which can be reached by picking up
 +// armor helmets. See P_TouchSpecialThing in p_inter.c
 +
 +int deh_max_armor = DEH_DEFAULT_MAX_ARMOR;
 +
 +// Dehacked: "Green Armor Class"
 +// This is the armor class that is given when picking up the green
 +// armor or an armor helmet. See P_TouchSpecialThing in p_inter.c
 +//
 +// DOS dehacked only modifies the behavior of the green armor shirt,
 +// the armor class set by armor helmets is not affected.
 +
 +int deh_green_armor_class = DEH_DEFAULT_GREEN_ARMOR_CLASS;
 +
 +// Dehacked: "Blue Armor Class"
 +// This is the armor class that is given when picking up the blue
 +// armor or a megasphere. See P_TouchSpecialThing in p_inter.c
 +//
 +// DOS dehacked only modifies the MegaArmor behavior and not
 +// the MegaSphere, which always gives armor type 2.
 +
 +int deh_blue_armor_class = DEH_DEFAULT_BLUE_ARMOR_CLASS;
 +
 +// Dehacked: "Max soulsphere"
 +// The maximum health which can be reached by picking up the
 +// soulsphere.  See P_TouchSpecialThing in p_inter.c
 +
 +int deh_max_soulsphere = DEH_DEFAULT_MAX_SOULSPHERE;
 +
 +// Dehacked: "Soulsphere health"
 +// The amount of health bonus that picking up a soulsphere
 +// gives.  See P_TouchSpecialThing in p_inter.c
 +
 +int deh_soulsphere_health = DEH_DEFAULT_SOULSPHERE_HEALTH;
 +
 +// Dehacked: "Megasphere health"
 +// This is what the health is set to after picking up a
 +// megasphere.  See P_TouchSpecialThing in p_inter.c
 +
 +int deh_megasphere_health = DEH_DEFAULT_MEGASPHERE_HEALTH;
 +
 +// Dehacked: "God mode health"
 +// This is what the health value is set to when cheating using
 +// the IDDQD god mode cheat.  See ST_Responder in st_stuff.c
 +
 +int deh_god_mode_health = DEH_DEFAULT_GOD_MODE_HEALTH;
 +
 +// Dehacked: "IDFA Armor"
 +// This is what the armor is set to when using the IDFA cheat.
 +// See ST_Responder in st_stuff.c
 +
 +int deh_idfa_armor = DEH_DEFAULT_IDFA_ARMOR;
 +
 +// Dehacked: "IDFA Armor Class"
 +// This is what the armor class is set to when using the IDFA cheat.
 +// See ST_Responder in st_stuff.c
 +
 +int deh_idfa_armor_class = DEH_DEFAULT_IDFA_ARMOR_CLASS;
 +
 +// Dehacked: "IDKFA Armor"
 +// This is what the armor is set to when using the IDKFA cheat.
 +// See ST_Responder in st_stuff.c
 +
 +int deh_idkfa_armor = DEH_DEFAULT_IDKFA_ARMOR;
 +
 +// Dehacked: "IDKFA Armor Class"
 +// This is what the armor class is set to when using the IDKFA cheat.
 +// See ST_Responder in st_stuff.c
 +
 +int deh_idkfa_armor_class = DEH_DEFAULT_IDKFA_ARMOR_CLASS;
 +
 +// Dehacked: "BFG Cells/Shot"
 +// This is the number of CELLs firing the BFG uses up.
 +// See P_CheckAmmo and A_FireBFG in p_pspr.c
 +
 +int deh_bfg_cells_per_shot = DEH_DEFAULT_BFG_CELLS_PER_SHOT;
 +
 +// Dehacked: "Monsters infight"
 +// This controls whether monsters can harm other monsters of the same
 +// species.  For example, whether an imp fireball will damage other
 +// imps.  The value of this in dehacked patches is weird - '202' means
 +// off, while '221' means on.
 +//
 +// See PIT_CheckThing in p_map.c
 +
 +int deh_species_infighting = DEH_DEFAULT_SPECIES_INFIGHTING;
 +
 +static struct
 +{
 +    char *deh_name;
 +    int *value;
 +} misc_settings[] = {
 +    {"Initial Health",      &deh_initial_health},
 +    {"Initial Bullets",     &deh_initial_bullets},
 +    {"Max Health",          &deh_max_health},
 +    {"Max Armor",           &deh_max_armor},
 +    {"LeatherArmorClass",   &deh_green_armor_class},
 +    {"Metal Armor Class",   &deh_blue_armor_class},
 +    {"Max Soulsphere",      &deh_max_soulsphere},
 +    {"Soulsphere Health",   &deh_soulsphere_health},
 +    {"Megasphere Health",   &deh_megasphere_health},
 +    {"God Mode Health",     &deh_god_mode_health},
 +    {"IDFA Armor",          &deh_idfa_armor},
 +    {"IDFA Armor Class",    &deh_idfa_armor_class},
 +    {"IDKFA Armor",         &deh_idkfa_armor},
 +    {"IDKFA Armor Class",   &deh_idkfa_armor_class},
 +    {"Mauler Cells/Shot",   &deh_bfg_cells_per_shot},
 +};
 +
 +static void *DEH_MiscStart(deh_context_t *context, char *line)
 +{
 +    return NULL;
 +}
 +
 +static void DEH_MiscParseLine(deh_context_t *context, char *line, void *tag)
 +{
 +    char *variable_name, *value;
 +    int ivalue;
 +    size_t i;
 +
 +    if (!DEH_ParseAssignment(line, &variable_name, &value))
 +    {
 +        // Failed to parse
 +
 +        DEH_Warning(context, "Failed to parse assignment");
 +        return;
 +    }
 +
 +    ivalue = atoi(value);
 +
 +    if (!strcasecmp(variable_name, "Monsters Infight"))
 +    {
 +        // See notes above.
 +
 +        if (ivalue == 202)
 +        {
 +            deh_species_infighting = 0;
 +        }
 +        else if (ivalue == 221)
 +        {
 +            deh_species_infighting = 1;
 +        }
 +        else
 +        {
 +            DEH_Warning(context,
 +                        "Invalid value for 'Monsters Infight': %i", ivalue);
 +        }
 +
 +        return;
 +    }
 +
 +    for (i=0; i<arrlen(misc_settings); ++i)
 +    {
 +        if (!strcasecmp(variable_name, misc_settings[i].deh_name))
 +        {
 +            *misc_settings[i].value = ivalue;
 +            return;
 +        }
 +    }
 +
 +    DEH_Warning(context, "Unknown Misc variable '%s'", variable_name);
 +}
 +
- static void DEH_MiscMD5Sum(md5_context_t *context)
++static void DEH_MiscSHA1Sum(sha1_context_t *context)
 +{
 +    unsigned int i;
 +
 +    for (i=0; i<arrlen(misc_settings); ++i)
 +    {
-         MD5_UpdateInt32(context, *misc_settings[i].value);
++        SHA1_UpdateInt32(context, *misc_settings[i].value);
 +    }
 +}
 +
 +deh_section_t deh_section_misc =
 +{
 +    "Misc",
 +    NULL,
 +    DEH_MiscStart,
 +    DEH_MiscParseLine,
 +    NULL,
-     DEH_MiscMD5Sum,
++    DEH_MiscSHA1Sum,
 +};
 +
diff --cc src/strife/deh_ptr.c
index d757a178,00000000..0d1764c9
mode 100644,000000..100644
--- a/src/strife/deh_ptr.c
+++ b/src/strife/deh_ptr.c
@@@ -1,151 -1,0 +1,151 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// 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.
 +//
 +//-----------------------------------------------------------------------------
 +//
 +// Parses Action Pointer entries in dehacked files
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "doomtype.h"
 +#include "info.h"
 +
 +#include "deh_defs.h"
 +#include "deh_io.h"
 +#include "deh_main.h"
 +
 +static actionf_t codeptrs[NUMSTATES];
 +
 +static int CodePointerIndex(actionf_t *ptr)
 +{
 +    int i;
 +
 +    for (i=0; i<NUMSTATES; ++i)
 +    {
 +        if (!memcmp(&codeptrs[i], ptr, sizeof(actionf_t)))
 +        {
 +            return i;
 +        }
 +    }
 +
 +    return -1;
 +}
 +
 +static void DEH_PointerInit(void)
 +{
 +    int i;
 +
 +    // Initialize list of dehacked pointers
 +
 +    for (i=0; i<NUMSTATES; ++i)
 +        codeptrs[i] = states[i].action;
 +}
 +
 +static void *DEH_PointerStart(deh_context_t *context, char *line)
 +{
 +    int frame_number = 0;
 +
 +    // FIXME: can the third argument here be something other than "Frame"
 +    // or are we ok?
 +
 +    if (sscanf(line, "Pointer %*i (%*s %i)", &frame_number) != 1)
 +    {
 +        DEH_Warning(context, "Parse error on section start");
 +        return NULL;
 +    }
 +
 +    if (frame_number < 0 || frame_number >= NUMSTATES)
 +    {
 +        DEH_Warning(context, "Invalid frame number: %i", frame_number);
 +        return NULL;
 +    }
 +
 +    return &states[frame_number];
 +}
 +
 +static void DEH_PointerParseLine(deh_context_t *context, char *line, void *tag)
 +{
 +    state_t *state;
 +    char *variable_name, *value;
 +    int ivalue;
 +
 +    if (tag == NULL)
 +       return;
 +
 +    state = (state_t *) tag;
 +
 +    // Parse the assignment
 +
 +    if (!DEH_ParseAssignment(line, &variable_name, &value))
 +    {
 +        // Failed to parse
 +        DEH_Warning(context, "Failed to parse assignment");
 +        return;
 +    }
 +
 +//    printf("Set %s to %s for state\n", variable_name, value);
 +
 +    // all values are integers
 +
 +    ivalue = atoi(value);
 +
 +    // set the appropriate field
 +
 +    if (!strcasecmp(variable_name, "Codep frame"))
 +    {
 +        if (ivalue < 0 || ivalue >= NUMSTATES)
 +        {
 +            DEH_Warning(context, "Invalid state '%i'", ivalue);
 +        }
 +        else
 +        {
 +            state->action = codeptrs[ivalue];
 +        }
 +    }
 +    else
 +    {
 +        DEH_Warning(context, "Unknown variable name '%s'", variable_name);
 +    }
 +}
 +
- static void DEH_PointerMD5Sum(md5_context_t *context)
++static void DEH_PointerSHA1Sum(sha1_context_t *context)
 +{
 +    int i;
 +
 +    for (i=0; i<NUMSTATES; ++i)
 +    {
-         MD5_UpdateInt32(context, CodePointerIndex(&states[i].action));
++        SHA1_UpdateInt32(context, CodePointerIndex(&states[i].action));
 +    }
 +}
 +
 +deh_section_t deh_section_pointer =
 +{
 +    "Pointer",
 +    DEH_PointerInit,
 +    DEH_PointerStart,
 +    DEH_PointerParseLine,
 +    NULL,
-     DEH_PointerMD5Sum,
++    DEH_PointerSHA1Sum,
 +};
 +
diff --cc src/strife/deh_thing.c
index 14b23b0a,00000000..a1c1f94d
mode 100644,000000..100644
--- a/src/strife/deh_thing.c
+++ b/src/strife/deh_thing.c
@@@ -1,141 -1,0 +1,141 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// 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.
 +//
 +//-----------------------------------------------------------------------------
 +//
 +// Parses "Thing" sections in dehacked files
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "doomtype.h"
 +
 +#include "deh_defs.h"
 +#include "deh_main.h"
 +#include "deh_mapping.h"
 +
 +#include "info.h"
 +
 +DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t)
 +  DEH_MAPPING("ID #",                doomednum)
 +  DEH_MAPPING("Initial frame",       spawnstate)
 +  DEH_MAPPING("Hit points",          spawnhealth)
 +  DEH_MAPPING("First moving frame",  seestate)
 +  DEH_MAPPING("Alert sound",         seesound)
 +  DEH_MAPPING("Reaction time",       reactiontime)
 +  DEH_MAPPING("Attack sound",        attacksound)
 +  DEH_MAPPING("Injury frame",        painstate)
 +  DEH_MAPPING("Pain chance",         painchance)
 +  DEH_MAPPING("Pain sound",          painsound)
 +  DEH_MAPPING("Close attack frame",  meleestate)
 +  DEH_MAPPING("Far attack frame",    missilestate)
 +  DEH_MAPPING("Crash frame",         crashstate)
 +  DEH_MAPPING("Death frame",         deathstate)
 +  DEH_MAPPING("Exploding frame",     xdeathstate)
 +  DEH_MAPPING("Death sound",         deathsound)
 +  DEH_MAPPING("Speed",               speed)
 +  DEH_MAPPING("Width",               radius)
 +  DEH_MAPPING("Height",              height)
 +  DEH_MAPPING("Mass",                mass)
 +  DEH_MAPPING("Missile damage",      damage)
 +  DEH_MAPPING("Action sound",        activesound)
 +  DEH_MAPPING("Bits",                flags)
 +  DEH_UNSUPPORTED_MAPPING("Name pointer")
 +DEH_END_MAPPING
 +
 +static void *DEH_ThingStart(deh_context_t *context, char *line)
 +{
 +    int thing_number = 0;
 +    mobjinfo_t *mobj;
 +
 +    if (sscanf(line, "Thing %i", &thing_number) != 1)
 +    {
 +        DEH_Warning(context, "Parse error on section start");
 +        return NULL;
 +    }
 +
 +    // dehacked files are indexed from 1
 +    --thing_number;
 +
 +    if (thing_number < 0 || thing_number >= NUMMOBJTYPES)
 +    {
 +        DEH_Warning(context, "Invalid thing number: %i", thing_number);
 +        return NULL;
 +    }
 +
 +    mobj = &mobjinfo[thing_number];
 +
 +    return mobj;
 +}
 +
 +static void DEH_ThingParseLine(deh_context_t *context, char *line, void *tag)
 +{
 +    mobjinfo_t *mobj;
 +    char *variable_name, *value;
 +    int ivalue;
 +
 +    if (tag == NULL)
 +       return;
 +
 +    mobj = (mobjinfo_t *) tag;
 +
 +    // Parse the assignment
 +
 +    if (!DEH_ParseAssignment(line, &variable_name, &value))
 +    {
 +        // Failed to parse
 +
 +        DEH_Warning(context, "Failed to parse assignment");
 +        return;
 +    }
 +
 +//    printf("Set %s to %s for mobj\n", variable_name, value);
 +
 +    // all values are integers
 +
 +    ivalue = atoi(value);
 +
 +    // Set the field value
 +
 +    DEH_SetMapping(context, &thing_mapping, mobj, variable_name, ivalue);
 +}
 +
- static void DEH_ThingMD5Sum(md5_context_t *context)
++static void DEH_ThingSHA1Sum(sha1_context_t *context)
 +{
 +    int i;
 +
 +    for (i=0; i<NUMMOBJTYPES; ++i)
 +    {
-         DEH_StructMD5Sum(context, &thing_mapping, &mobjinfo[i]);
++        DEH_StructSHA1Sum(context, &thing_mapping, &mobjinfo[i]);
 +    }
 +}
 +
 +deh_section_t deh_section_thing =
 +{
 +    "Thing",
 +    NULL,
 +    DEH_ThingStart,
 +    DEH_ThingParseLine,
 +    NULL,
-     DEH_ThingMD5Sum,
++    DEH_ThingSHA1Sum,
 +};
 +
diff --cc src/strife/deh_weapon.c
index 8e89b93e,00000000..67cbe5b6
mode 100644,000000..100644
--- a/src/strife/deh_weapon.c
+++ b/src/strife/deh_weapon.c
@@@ -1,111 -1,0 +1,111 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// 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.
 +//
 +//-----------------------------------------------------------------------------
 +//
 +// Parses "Weapon" sections in dehacked files
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "doomtype.h"
 +
 +#include "d_items.h"
 +
 +#include "deh_defs.h"
 +#include "deh_main.h"
 +#include "deh_mapping.h"
 +
 +DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t)
 +  DEH_MAPPING("Ammo type",        ammo)
 +  DEH_MAPPING("Deselect frame",   upstate)
 +  DEH_MAPPING("Select frame",     downstate)
 +  DEH_MAPPING("Bobbing frame",    readystate)
 +  DEH_MAPPING("Shooting frame",   atkstate)
 +  DEH_MAPPING("Firing frame",     flashstate)
 +  DEH_UNSUPPORTED_MAPPING("? Unknown")
 +DEH_END_MAPPING
 +
 +static void *DEH_WeaponStart(deh_context_t *context, char *line)
 +{
 +    int weapon_number = 0;
 +
 +    if (sscanf(line, "Weapon %i", &weapon_number) != 1)
 +    {
 +        DEH_Warning(context, "Parse error on section start");
 +        return NULL;
 +    }
 +
 +    if (weapon_number < 0 || weapon_number >= NUMWEAPONS)
 +    {
 +        DEH_Warning(context, "Invalid weapon number: %i", weapon_number);
 +        return NULL;
 +    }
 +
 +    return &weaponinfo[weapon_number];
 +}
 +
 +static void DEH_WeaponParseLine(deh_context_t *context, char *line, void *tag)
 +{
 +    char *variable_name, *value;
 +    weaponinfo_t *weapon;
 +    int ivalue;
 +
 +    if (tag == NULL)
 +        return;
 +
 +    weapon = (weaponinfo_t *) tag;
 +
 +    if (!DEH_ParseAssignment(line, &variable_name, &value))
 +    {
 +        // Failed to parse
 +
 +        DEH_Warning(context, "Failed to parse assignment");
 +        return;
 +    }
 +
 +    ivalue = atoi(value);
 +
 +    DEH_SetMapping(context, &weapon_mapping, weapon, variable_name, ivalue);
 +}
 +
- static void DEH_WeaponMD5Sum(md5_context_t *context)
++static void DEH_WeaponSHA1Sum(sha1_context_t *context)
 +{
 +    int i;
 +
 +    for (i=0; i<NUMWEAPONS ;++i)
 +    {
-         DEH_StructMD5Sum(context, &weapon_mapping, &weaponinfo[i]);
++        DEH_StructSHA1Sum(context, &weapon_mapping, &weaponinfo[i]);
 +    }
 +}
 +
 +deh_section_t deh_section_weapon =
 +{
 +    "Weapon",
 +    NULL,
 +    DEH_WeaponStart,
 +    DEH_WeaponParseLine,
 +    NULL,
-     DEH_WeaponMD5Sum,
++    DEH_WeaponSHA1Sum,
 +};
 +
diff --cc textscreen/txt_window.c
index 67543735,b08ac658..9ab6da9b
--- a/textscreen/txt_window.c
+++ b/textscreen/txt_window.c
@@@ -25,9 -24,9 +25,10 @@@

  #include "doomkeys.h"

 +#include "txt_label.h"
  #include "txt_desktop.h"
  #include "txt_gui.h"
+ #include "txt_io.h"
  #include "txt_main.h"
  #include "txt_separator.h"
  #include "txt_window.h"