shithub: choc

Download patch

ref: 5c644aea77f8e37f2f39928b2256c9a13cdebb99
parent: 4d9752561a4575f90a48c75e7d7bfa783c72deff
parent: 26524bf56324364ceb1f9fad6e1efec55315d73d
author: linguica <github@andrewstine.org>
date: Sun Sep 6 08:51:31 EDT 2015

Merge remote-tracking branch 'chocolate-doom/master'

--- a/configure.ac
+++ b/configure.ac
@@ -6,6 +6,7 @@
 PACKAGE_LICENSE="GNU General Public License, version 2"
 PACKAGE_MAINTAINER="Simon Howard"
 PACKAGE_URL="http://www.chocolate-doom.org/"
+PACKAGE_ISSUES="https://github.com/chocolate-doom/chocolate-doom/issues"
 
 AC_CONFIG_AUX_DIR(autotools)
 
@@ -102,7 +103,15 @@
     AC_CHECK_LIB(amd64, amd64_iopl)
 ])
 
-AC_CHECK_TOOL(WINDRES, windres, )
+case $host in
+  *cygwin* | *mingw* )
+    AC_CHECK_TOOL(WINDRES, windres, )
+    ;;
+  *)
+    WINDRES=
+    ;;
+esac
+
 AC_CHECK_TOOL(STRIP, strip, )
 
 AM_CONDITIONAL(HAVE_WINDRES, test "$WINDRES" != "")
@@ -142,6 +151,7 @@
 AC_SUBST(PACKAGE_LICENSE)
 AC_SUBST(PACKAGE_MAINTAINER)
 AC_SUBST(PACKAGE_URL)
+AC_SUBST(PACKAGE_ISSUES)
 
 dnl Shut up the datarootdir warnings.
 AC_DEFUN([AC_DATAROOTDIR_CHECKED])
@@ -159,11 +169,14 @@
 rpm.spec
 data/Makefile
 src/Makefile
+src/doom.appdata.xml
 src/doom.desktop
 src/doom-screensaver.desktop
 src/doom/Makefile
+src/heretic.appdata.xml
 src/heretic.desktop
 src/heretic/Makefile
+src/hexen.appdata.xml
 src/hexen.desktop
 src/hexen/Makefile
 src/resource.rc
@@ -171,6 +184,7 @@
 src/setup/Makefile
 src/setup/setup.desktop
 src/setup/setup-manifest.xml
+src/strife.appdata.xml
 src/strife.desktop
 src/strife/Makefile
 textscreen/Makefile
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,4 +1,5 @@
 MANPAGE_GEN_FILES = environ.man           \
+                    iwad_paths.man        \
                     doom.template         \
                     heretic.template      \
                     hexen.template        \
--- a/man/doom.template
+++ b/man/doom.template
@@ -10,6 +10,8 @@
 to behave as similar to the original DOS version of Doom as is possible.
 .br
 @content
+.SH IWAD SEARCH PATHS
+@include iwad_paths.man
 .SH ENVIRONMENT
 This section describes environment variables that control Chocolate Doom's
 behavior.
--- a/man/environ.man
+++ b/man/environ.man
@@ -1,9 +1,6 @@
 .TP
 \fBDOOMWADDIR\fR, \fBDOOMWADPATH\fR
-These environment variables provide paths to search for Doom .WAD files when
-looking for a game IWAD file or a PWAD file specified with the `\-file' option.
-\fBDOOMWADDIR\fR specifies a single path in which to look for WAD files,
-while \fBDOOMWWADPATH\fR specifies a colon-separated list of paths to search.
+See the section, \fBIWAD SEARCH PATHS\fR above.
 .TP
 \fBPCSOUND_DRIVER\fR
 When running in PC speaker sound effect mode, this environment variable
--- a/man/heretic.template
+++ b/man/heretic.template
@@ -11,6 +11,8 @@
 possible.
 .br
 @content
+.SH IWAD SEARCH PATHS
+@include iwad_paths.man
 .SH ENVIRONMENT
 This section describes environment variables that control Chocolate Heretic's
 behavior.
--- a/man/hexen.template
+++ b/man/hexen.template
@@ -11,6 +11,8 @@
 possible.
 .br
 @content
+.SH IWAD SEARCH PATHS
+@include iwad_paths.man
 .SH ENVIRONMENT
 This section describes environment variables that control Chocolate Hexen's
 behavior.
--- /dev/null
+++ b/man/iwad_paths.man
@@ -1,0 +1,48 @@
+To play, an IWAD file is needed. This is a large file containing all of the
+levels, graphics, sound effects, music and other material that make up the
+game. IWAD files are named according to the game; the standard names are:
+.TP
+\fBdoom.wad, doom1.wad, doom2.wad, tnt.wad, plutonia.wad\fR
+Doom, Doom II, Final Doom
+.TP
+\fBheretic.wad, heretic1.wad, hexen.wad, strife1.wad\fR
+Heretic, Hexen and Strife (commercial Doom engine games).
+.TP
+\fBhacx.wad, chex.wad\fR
+Hacx and Chex Quest - more obscure games based on the Doom engine.
+.TP
+\fBfreedm.wad, freedoom1.wad, freedoom2.wad\fR
+The Freedoom open content IWAD files.
+.LP
+The following directory paths are searched in order to find an IWAD:
+.TP
+\fBCurrent working directory\fR
+Any IWAD files found in the current working directory will be used in
+preference to IWADs found in any other directories.
+.TP
+\fBDOOMWADDIR\fR
+This environment variable can be set to contain a path to a single directory
+in which to look for IWAD files. This environment variable is supported by
+most Doom source ports.
+.TP
+\fBDOOMWADPATH\fR
+This environment variable, if set, can contain a colon-separated list of
+directories in which to look for IWAD files, or alternatively full paths to
+specific IWAD files.
+.TP
+\fB$HOME/.local/share/games/doom\fR
+Writeable directory in the user's home directory. The path can be overridden
+using the \fBXDG_DATA_HOME\fR environment variable (see the XDG Base Directory
+Specification).
+.TP
+\fB/usr/local/share/games/doom, /usr/share/games/doom\fR
+System-wide locations that can be accessed by all users. The path
+\fB/usr/share/games/doom\fR is a standard path that is supported by most
+Doom source ports. These paths can be overridden using the \fBXDG_DATA_DIRS\fR
+environment variable (see the XDG Base Directory Specification).
+.LP
+The above can be overridden on a one-time basis by using the \fB\-iwad\fR
+command line parameter to provide the path to an IWAD file to use. This
+parameter can also be used to specify the name of a particular IWAD to use
+from one of the above paths. For example, '\fB-iwad doom.wad\fR' will search
+the above paths for the file \fBdoom.wad\fR to use.
--- a/man/strife.template
+++ b/man/strife.template
@@ -13,6 +13,8 @@
 
 @content
 
+.SH IWAD SEARCH PATHS
+@include iwad_paths.man
 .SH ENVIRONMENT
 This section describes environment variables that control Chocolate Strife's
 behavior.
--- a/msvc/heretic.vcproj
+++ b/msvc/heretic.vcproj
@@ -469,10 +469,6 @@
 					>
 				</File>
 				<File
-					RelativePath="..\src\heretic\i_sound.c"
-					>
-				</File>
-				<File
 					RelativePath="..\src\heretic\in_lude.c"
 					>
 				</File>
--- a/msvc/hexen.vcproj
+++ b/msvc/hexen.vcproj
@@ -426,19 +426,11 @@
 					>
 				</File>
 				<File
-					RelativePath="..\src\hexen\i_sound.c"
-					>
-				</File>
-				<File
 					RelativePath="..\src\hexen\in_lude.c"
 					>
 				</File>
 				<File
 					RelativePath="..\src\hexen\info.c"
-					>
-				</File>
-				<File
-					RelativePath="..\src\hexen\m_misc.c"
 					>
 				</File>
 				<File
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -14,5 +14,6 @@
 chocolate-setup
 *.exe
 *.desktop
+*.appdata.xml
 tags
 TAGS
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -217,6 +217,25 @@
         doom-screensaver.desktop.in \
         manifest.xml
 
+appdatadir = $(prefix)/share/appdata
+appdata_DATA =                              \
+        @PROGRAM_PREFIX@doom.appdata.xml    \
+        @PROGRAM_PREFIX@heretic.appdata.xml \
+        @PROGRAM_PREFIX@hexen.appdata.xml   \
+        @PROGRAM_PREFIX@strife.appdata.xml
+
+@PROGRAM_PREFIX@doom.appdata.xml : doom.appdata.xml
+	cp doom.appdata.xml $@
+
+@PROGRAM_PREFIX@heretic.appdata.xml : heretic.appdata.xml
+	cp heretic.appdata.xml $@
+
+@PROGRAM_PREFIX@hexen.appdata.xml : hexen.appdata.xml
+	cp hexen.appdata.xml $@
+
+@PROGRAM_PREFIX@strife.appdata.xml : strife.appdata.xml
+	cp strife.appdata.xml $@
+
 appdir = $(prefix)/share/applications
 app_DATA =                                 \
            @PROGRAM_PREFIX@doom.desktop    \
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@ -142,22 +142,53 @@
     },
 };
 
-// Value installed by the Collector's Edition when it is installed
+// Values installed by the GOG.com and Collector's Edition versions
 
-static registry_value_t collectors_edition_value =
+static registry_value_t root_path_keys[] =
 {
-    HKEY_LOCAL_MACHINE,
-    SOFTWARE_KEY "\\Activision\\DOOM Collector's Edition\\v1.0",
-    "INSTALLPATH",
+    // Doom Collector's Edition
+
+    {
+        HKEY_LOCAL_MACHINE,
+        SOFTWARE_KEY "\\Activision\\DOOM Collector's Edition\\v1.0",
+        "INSTALLPATH",
+    },
+
+    // Ultimate Doom
+
+    {
+        HKEY_LOCAL_MACHINE,
+        SOFTWARE_KEY "\\GOG.com\\Games\\1435827232",
+        "PATH",
+    },
+
+    // Doom II
+
+    {
+        HKEY_LOCAL_MACHINE,
+        SOFTWARE_KEY "\\GOG.com\\Games\\1435848814",
+        "PATH",
+    },
+
+    // Final Doom
+
+    {
+        HKEY_LOCAL_MACHINE,
+        SOFTWARE_KEY "\\GOG.com\\Games\\1435848742",
+        "PATH",
+    },
 };
 
 // Subdirectories of the above install path, where IWADs are installed.
 
-static char *collectors_edition_subdirs[] = 
+static char *root_path_subdirs[] =
 {
+    ".",
     "Doom2",
     "Final Doom",
     "Ultimate Doom",
+    "TNT",
+    "Plutonia",
 };
 
 // Location where Steam is installed
@@ -268,30 +299,34 @@
     }
 }
 
-// Check for Doom: Collector's Edition
+// Check for GOG.com and Doom: Collector's Edition
 
-static void CheckCollectorsEdition(void)
+static void CheckInstallRootPaths(void)
 {
-    char *install_path;
-    char *subpath;
     unsigned int i;
 
-    install_path = GetRegistryString(&collectors_edition_value);
-
-    if (install_path == NULL)
+    for (i=0; i<arrlen(root_path_keys); ++i)
     {
-        return;
-    }
+        char *install_path;
+        char *subpath;
+        unsigned int j;
 
-    for (i=0; i<arrlen(collectors_edition_subdirs); ++i)
-    {
-        subpath = M_StringJoin(install_path, DIR_SEPARATOR_S,
-                               collectors_edition_subdirs[i], NULL);
+        install_path = GetRegistryString(&root_path_keys[i]);
 
-        AddIWADDir(subpath);
-    }
+        if (install_path == NULL)
+        {
+            continue;
+        }
 
-    free(install_path);
+        for (j=0; j<arrlen(root_path_subdirs); ++j)
+        {
+            subpath = M_StringJoin(install_path, DIR_SEPARATOR_S,
+                                   root_path_subdirs[j], NULL);
+            AddIWADDir(subpath);
+        }
+
+        free(install_path);
+    }
 }
 
 
@@ -509,47 +544,29 @@
     return mission;
 }
 
-//
-// Add directories from the list in the DOOMWADPATH environment variable.
-//
-
-static void AddDoomWadPath(void)
+// Add IWAD directories parsed from splitting a path string containing
+// paths separated by PATH_SEPARATOR. 'suffix' is a string to concatenate
+// to the end of the paths before adding them.
+static void AddIWADPath(char *path, char *suffix)
 {
-    char *doomwadpath;
-    char *p;
+    char *left, *p;
 
-    // Check the DOOMWADPATH environment variable.
+    path = M_StringDuplicate(path);
 
-    doomwadpath = getenv("DOOMWADPATH");
-
-    if (doomwadpath == NULL)
-    {
-        return;
-    }
-
-    doomwadpath = M_StringDuplicate(doomwadpath);
-
-    // Add the initial directory
-
-    AddIWADDir(doomwadpath);
-
     // Split into individual dirs within the list.
+    left = path;
 
-    p = doomwadpath;
-
     for (;;)
     {
-        p = strchr(p, PATH_SEPARATOR);
-
+        p = strchr(left, PATH_SEPARATOR);
         if (p != NULL)
         {
-            // Break at the separator and store the right hand side
+            // Break at the separator and use the left hand side
             // as another iwad dir
-  
             *p = '\0';
-            p += 1;
 
-            AddIWADDir(p);
+            AddIWADDir(M_StringJoin(left, suffix, NULL));
+            left = p + 1;
         }
         else
         {
@@ -556,9 +573,68 @@
             break;
         }
     }
+
+    AddIWADDir(M_StringJoin(left, suffix, NULL));
+
+    free(path);
 }
 
+// Add standard directories where IWADs are located on Unix systems.
+// To respect the freedesktop.org specification we support overriding
+// using standard environment variables. See the XDG Base Directory
+// Specification:
+// <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>
+static void AddXdgDirs(void)
+{
+    char *env, *tmp_env;
 
+    // Quote:
+    // > $XDG_DATA_HOME defines the base directory relative to which
+    // > user specific data files should be stored. If $XDG_DATA_HOME
+    // > is either not set or empty, a default equal to
+    // > $HOME/.local/share should be used.
+    env = getenv("XDG_DATA_HOME");
+    tmp_env = NULL;
+
+    if (env == NULL)
+    {
+        char *homedir = getenv("HOME");
+        if (homedir == NULL)
+        {
+            homedir = "/";
+        }
+
+        tmp_env = M_StringJoin(homedir, "/.local/share", NULL);
+        env = tmp_env;
+    }
+
+    // We support $XDG_DATA_HOME/games/doom (which will usually be
+    // ~/.local/share/games/doom) as a user-writeable extension to
+    // the usual /usr/share/games/doom location.
+    AddIWADDir(M_StringJoin(env, "/games/doom", NULL));
+    free(tmp_env);
+
+    // Quote:
+    // > $XDG_DATA_DIRS defines the preference-ordered set of base
+    // > directories to search for data files in addition to the
+    // > $XDG_DATA_HOME base directory. The directories in $XDG_DATA_DIRS
+    // > should be seperated with a colon ':'.
+    // >
+    // > If $XDG_DATA_DIRS is either not set or empty, a value equal to
+    // > /usr/local/share/:/usr/share/ should be used.
+    env = getenv("XDG_DATA_DIRS");
+    if (env == NULL)
+    {
+        // (Trailing / omitted from paths, as it is added below)
+        env = "/usr/local/share:/usr/share";
+    }
+
+    // The "standard" location for IWADs on Unix that is supported by most
+    // source ports is /usr/share/games/doom - we support this through the
+    // XDG_DATA_DIRS mechanism, through which it can be overridden.
+    AddIWADPath(env, "/games/doom");
+}
+
 //
 // Build a list of IWAD files
 //
@@ -565,7 +641,7 @@
 
 static void BuildIWADDirList(void)
 {
-    char *doomwaddir;
+    char *env;
 
     if (iwad_dirs_built)
     {
@@ -573,28 +649,28 @@
     }
 
     // Look in the current directory.  Doom always does this.
-
     AddIWADDir(".");
 
     // Add DOOMWADDIR if it is in the environment
+    env = getenv("DOOMWADDIR");
+    if (env != NULL)
+    {
+        AddIWADDir(env);
+    }
 
-    doomwaddir = getenv("DOOMWADDIR");
-
-    if (doomwaddir != NULL)
+    // Add dirs from DOOMWADPATH:
+    env = getenv("DOOMWADPATH");
+    if (env != NULL)
     {
-        AddIWADDir(doomwaddir);
-    }        
+        AddIWADPath(env, "");
+    }
 
-    // Add dirs from DOOMWADPATH
-
-    AddDoomWadPath();
-
 #ifdef _WIN32
 
     // Search the registry and find where IWADs have been installed.
 
     CheckUninstallStrings();
-    CheckCollectorsEdition();
+    CheckInstallRootPaths();
     CheckSteamEdition();
     CheckDOSDefaults();
 
@@ -603,12 +679,7 @@
     CheckSteamGUSPatches();
 
 #else
-
-    // Standard places where IWAD files are installed under Unix.
-
-    AddIWADDir("/usr/share/games/doom");
-    AddIWADDir("/usr/local/share/games/doom");
-
+    AddXdgDirs();
 #endif
 
     // Don't run this function again.
--- a/src/deh_io.c
+++ b/src/deh_io.c
@@ -120,7 +120,7 @@
     context->input_buffer_pos = 0;
 
     context->filename = malloc(9);
-    M_StringCopy(context->filename, lumpinfo[lumpnum].name, 9);
+    M_StringCopy(context->filename, lumpinfo[lumpnum]->name, 9);
 
     return context;
 }
--- /dev/null
+++ b/src/doom.appdata.xml.in
@@ -1,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<component type="desktop">
+  <id>@PROGRAM_PREFIX@doom.desktop</id>
+  <metadata_license>CC0-1.0</metadata_license>
+  <project_license>GPL-2.0+</project_license>
+  <developer_name>@PACKAGE_MAINTAINER@</developer_name>
+  <url type="homepage">@PACKAGE_URL@</url>
+  <url type="bugtracker">@PACKAGE_ISSUES@</url>
+  <description>
+    <p>
+      @PACKAGE_SHORTNAME@ Doom is a conservative,
+      historically-accurate Doom source port, which is compatible with
+      the thousands of mods and levels that were made before the Doom
+      source code was released.  Unlike other source ports, the goal
+      is to preserve the original look, feel, limitations, and bugs of
+      the original DOS executable.
+    </p>
+    <p>
+      Full support for single- and multi-player games is provided, for
+      all of the original Doom games, Chex Quest, and Hacx.  Unlike
+      the original executable, network play is implemented on the IP
+      network stack, allowing it to function on modern LANs and the
+      Internet.
+    </p>
+  </description>
+  <screenshots>
+    <screenshot type="default">
+      <image>http://www.chocolate-doom.org/wiki/images/9/97/GNOME_FreeDM_DEMO4.png</image>
+      <caption>FreeDM, DM05: Metal</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/a/a6/GNOME_Doom_II_DEMO2.png</image>
+      <caption>Doom II, Level 5: The Waste Tunnels</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/4/41/GNOME_Doomsday_of_UAC.png</image>
+      <caption>Doomsday of UAC (uac_dead.wad)</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/2/2a/GNOME_Freedoom_DTWID_DEMO3.png</image>
+      <caption>Doom the Way id Did, on Freedoom. Level 3-2: City of Corpses</caption>
+    </screenshot>
+  </screenshots>
+</component>
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -739,12 +739,12 @@
 
         for (i=0; i<numlumps; ++i)
         {
-            if (!strncasecmp(lumpinfo[i].name, "MAP01", 8))
+            if (!strncasecmp(lumpinfo[i]->name, "MAP01", 8))
             {
                 gamemission = doom2;
                 break;
             } 
-            else if (!strncasecmp(lumpinfo[i].name, "E1M1", 8))
+            else if (!strncasecmp(lumpinfo[i]->name, "E1M1", 8))
             {
                 gamemission = doom;
                 break;
@@ -954,7 +954,7 @@
 static void InitGameVersion(void)
 {
     byte *demolump;
-    byte demolumpname[6];
+    char demolumpname[6];
     int demoversion;
     int p;
     int i;
@@ -1531,7 +1531,7 @@
 
         if (D_AddFile(file))
         {
-            M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,
+            M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name,
                          sizeof(demolumpname));
         }
         else
@@ -1566,7 +1566,7 @@
 
         for (i = numiwadlumps; i < numlumps; ++i)
         {
-            if (!strncmp(lumpinfo[i].name, "DEHACKED", 8))
+            if (!strncmp(lumpinfo[i]->name, "DEHACKED", 8))
             {
                 DEH_LoadLump(i, false, false);
                 loaded++;
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -614,7 +614,8 @@
 
     // The "Sky never changes in Doom II" bug was fixed in
     // the id Anthology version of doom2.exe for Final Doom.
-    if ((gamemode == commercial) && (gameversion == exe_final2))
+    if ((gamemode == commercial)
+     && (gameversion == exe_final2 || gameversion == exe_chex))
     {
         char *skytexturename;
 
--- a/src/doom/hu_stuff.c
+++ b/src/doom/hu_stuff.c
@@ -48,7 +48,7 @@
 #define HU_TITLE2	(mapnames_commercial[gamemap-1])
 #define HU_TITLEP	(mapnames_commercial[gamemap-1 + 32])
 #define HU_TITLET	(mapnames_commercial[gamemap-1 + 64])
-#define HU_TITLE_CHEX   (mapnames[gamemap - 1])
+#define HU_TITLE_CHEX   (mapnames_chex[(gameepisode-1)*9+gamemap-1])
 #define HU_TITLEHEIGHT	1
 #define HU_TITLEX	0
 #define HU_TITLEY	(167 - SHORT(hu_font[0]->height))
@@ -163,6 +163,60 @@
     "NEWLEVEL"
 };
 
+char*   mapnames_chex[] =   // Chex Quest names.
+{
+
+    HUSTR_E1M1,
+    HUSTR_E1M2,
+    HUSTR_E1M3,
+    HUSTR_E1M4,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+    HUSTR_E1M5,
+
+    "NEWLEVEL",
+    "NEWLEVEL",
+    "NEWLEVEL",
+    "NEWLEVEL",
+    "NEWLEVEL",
+    "NEWLEVEL",
+    "NEWLEVEL",
+    "NEWLEVEL",
+    "NEWLEVEL"
+};
+
 // List of names for levels in commercial IWADs
 // (doom2.wad, plutonia.wad, tnt.wad).  These are stored in a
 // single large array; WADs like pl2.wad have a MAP33, and rely on
@@ -351,10 +405,7 @@
          break;
     }
 
-    // Chex.exe always uses the episode 1 level title
-    // eg. E2M1 gives the title for E1M1
-
-    if (gameversion == exe_chex)
+    if (logical_gamemission == doom && gameversion == exe_chex)
     {
         s = HU_TITLE_CHEX;
     }
--- a/src/doom/r_data.c
+++ b/src/doom/r_data.c
@@ -832,7 +832,7 @@
 	if (flatpresent[i])
 	{
 	    lump = firstflat + i;
-	    flatmemory += lumpinfo[lump].size;
+	    flatmemory += lumpinfo[lump]->size;
 	    W_CacheLumpNum(lump, PU_CACHE);
 	}
     }
@@ -869,7 +869,7 @@
 	for (j=0 ; j<texture->patchcount ; j++)
 	{
 	    lump = texture->patches[j].patch;
-	    texturememory += lumpinfo[lump].size;
+	    texturememory += lumpinfo[lump]->size;
 	    W_CacheLumpNum(lump , PU_CACHE);
 	}
     }
@@ -898,7 +898,7 @@
 	    for (k=0 ; k<8 ; k++)
 	    {
 		lump = firstspritelump + sf->lump[k];
-		spritememory += lumpinfo[lump].size;
+		spritememory += lumpinfo[lump]->size;
 		W_CacheLumpNum(lump , PU_CACHE);
 	    }
 	}
--- a/src/doom/r_things.c
+++ b/src/doom/r_things.c
@@ -208,22 +208,22 @@
 	//  filling in the frames for whatever is found
 	for (l=start+1 ; l<end ; l++)
 	{
-	    if (!strncasecmp(lumpinfo[l].name, spritename, 4))
+	    if (!strncasecmp(lumpinfo[l]->name, spritename, 4))
 	    {
-		frame = lumpinfo[l].name[4] - 'A';
-		rotation = lumpinfo[l].name[5] - '0';
+		frame = lumpinfo[l]->name[4] - 'A';
+		rotation = lumpinfo[l]->name[5] - '0';
 
 		if (modifiedgame)
-		    patched = W_GetNumForName (lumpinfo[l].name);
+		    patched = W_GetNumForName (lumpinfo[l]->name);
 		else
 		    patched = l;
 
 		R_InstallSpriteLump (patched, frame, rotation, false);
 
-		if (lumpinfo[l].name[6])
+		if (lumpinfo[l]->name[6])
 		{
-		    frame = lumpinfo[l].name[6] - 'A';
-		    rotation = lumpinfo[l].name[7] - '0';
+		    frame = lumpinfo[l]->name[6] - 'A';
+		    rotation = lumpinfo[l]->name[7] - '0';
 		    R_InstallSpriteLump (l, frame, rotation, true);
 		}
 	    }
--- a/src/doom/st_stuff.c
+++ b/src/doom/st_stuff.c
@@ -618,13 +618,20 @@
       {
 	epsd = buf[0] - '0';
 	map = buf[1] - '0';
-      }
 
-      // Chex.exe always warps to episode 1.
+        // Chex.exe always warps to episode 1.
 
-      if (gameversion == exe_chex)
-      {
-        epsd = 1;
+        if (gameversion == exe_chex)
+        {
+            if (epsd > 1)
+            {
+                epsd = 1;
+            }
+            if (map > 5)
+            {
+                map = 5;
+            }
+        }
       }
 
       // Catch invalid maps.
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -51,7 +51,13 @@
 //
 
 #ifdef __GNUC__
+
+#ifdef __clang__
 #define PACKEDATTR __attribute__((packed))
+#else
+#define PACKEDATTR __attribute__((packed,gcc_struct))
+#endif
+
 #else
 #define PACKEDATTR
 #endif
--- /dev/null
+++ b/src/heretic.appdata.xml.in
@@ -1,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<component type="desktop">
+  <id>@PROGRAM_PREFIX@heretic.desktop</id>
+  <metadata_license>CC0-1.0</metadata_license>
+  <project_license>GPL-2.0+</project_license>
+  <developer_name>@PACKAGE_MAINTAINER@</developer_name>
+  <url type="homepage">@PACKAGE_URL@</url>
+  <url type="bugtracker">@PACKAGE_ISSUES@</url>
+  <description>
+    <p>
+      @PACKAGE_SHORTNAME@ Heretic is a conservative,
+      historically-accurate Heretic source port, which is compatible
+      with mods and levels that were made before the Heretic source
+      code was released.  Unlike other source ports, the goal is to
+      preserve the original look, feel, limitations, and bugs of the
+      original DOS executable.
+    </p>
+    <p>
+      Full support for single- and multi-player games is provided.
+      Unlike the original executable, network play is implemented on
+      the IP network stack, allowing it to function on modern LANs and
+      the Internet.
+    </p>
+  </description>
+  <screenshots>
+    <screenshot type="default">
+      <image>http://www.chocolate-doom.org/wiki/images/9/93/GNOME_Heretic_E5M4.png</image>
+      <caption>Level E5M4: Courtyard</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/1/14/GNOME_Heretic_Shareware_DEMO3.png</image>
+      <caption>Shareware Level E1M9: The Graveyard</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/3/34/GNOME_Heretic_E4M1.png</image>
+      <caption>Level E4M1: Catafalque</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/4/42/GNOME_Heretic_Shareware_DEMO1.png</image>
+      <caption>Shareware Level E1M3: The Gatehouse</caption>
+    </screenshot>
+  </screenshots>
+</component>
--- a/src/heretic/d_main.c
+++ b/src/heretic/d_main.c
@@ -62,7 +62,6 @@
 boolean debugmode;              // checkparm of -debug
 boolean ravpic;                 // checkparm of -ravpic
 boolean cdrom;                  // true if cd-rom mode active
-boolean singletics;             // debug flag to cancel adaptiveness
 boolean noartiskip;             // whether shift-enter skips an artifact
 
 skill_t startskill;
@@ -1019,7 +1018,7 @@
 
         if (D_AddFile(file))
         {
-            M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,
+            M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name,
                          sizeof(demolumpname));
         }
         else
--- a/src/heretic/g_game.c
+++ b/src/heretic/g_game.c
@@ -102,7 +102,6 @@
 
 int consoleplayer;              // player taking events and displaying
 int displayplayer;              // view being displayed
-int gametic;
 int levelstarttic;              // gametic at level start
 int totalkills, totalitems, totalsecret;        // for intermission
 
--- a/src/heretic/r_data.c
+++ b/src/heretic/r_data.c
@@ -671,7 +671,7 @@
         if (flatpresent[i])
         {
             lump = firstflat + i;
-            flatmemory += lumpinfo[lump].size;
+            flatmemory += lumpinfo[lump]->size;
             W_CacheLumpNum(lump, PU_CACHE);
         }
 
@@ -701,7 +701,7 @@
         for (j = 0; j < texture->patchcount; j++)
         {
             lump = texture->patches[j].patch;
-            texturememory += lumpinfo[lump].size;
+            texturememory += lumpinfo[lump]->size;
             W_CacheLumpNum(lump, PU_CACHE);
         }
     }
@@ -731,7 +731,7 @@
             for (k = 0; k < 8; k++)
             {
                 lump = firstspritelump + sf->lump[k];
-                spritememory += lumpinfo[lump].size;
+                spritememory += lumpinfo[lump]->size;
                 W_CacheLumpNum(lump, PU_CACHE);
             }
         }
--- a/src/heretic/r_things.c
+++ b/src/heretic/r_things.c
@@ -175,15 +175,15 @@
         // scan the lumps, filling in the frames for whatever is found
         //
         for (l = start + 1; l < end; l++)
-            if (!strncasecmp(lumpinfo[l].name, spritename, 4))
+            if (!strncasecmp(lumpinfo[l]->name, spritename, 4))
             {
-                frame = lumpinfo[l].name[4] - 'A';
-                rotation = lumpinfo[l].name[5] - '0';
+                frame = lumpinfo[l]->name[4] - 'A';
+                rotation = lumpinfo[l]->name[5] - '0';
                 R_InstallSpriteLump(l, frame, rotation, false);
-                if (lumpinfo[l].name[6])
+                if (lumpinfo[l]->name[6])
                 {
-                    frame = lumpinfo[l].name[6] - 'A';
-                    rotation = lumpinfo[l].name[7] - '0';
+                    frame = lumpinfo[l]->name[6] - 'A';
+                    rotation = lumpinfo[l]->name[7] - '0';
                     R_InstallSpriteLump(l, frame, rotation, true);
                 }
             }
--- /dev/null
+++ b/src/hexen.appdata.xml.in
@@ -1,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<component type="desktop">
+  <id>@PROGRAM_PREFIX@hexen.desktop</id>
+  <metadata_license>CC0-1.0</metadata_license>
+  <project_license>GPL-2.0+</project_license>
+  <developer_name>@PACKAGE_MAINTAINER@</developer_name>
+  <url type="homepage">@PACKAGE_URL@</url>
+  <url type="bugtracker">@PACKAGE_ISSUES@</url>
+  <description>
+    <p>
+      @PACKAGE_SHORTNAME@ Hexen is a conservative,
+      historically-accurate Hexen source port, which is compatible
+      with mods and levels that were made before the Hexen source code
+      was released.  Unlike other source ports, the goal is to
+      preserve the original look, feel, limitations, and bugs of the
+      original DOS executable.
+    </p>
+    <p>
+      Full support for single- and multi-player games is provided.
+      Unlike the original executable, network play is implemented on
+      the IP network stack, allowing it to function on modern LANs and
+      the Internet.
+    </p>
+  </description>
+  <screenshots>
+    <screenshot type="default">
+      <image>http://www.chocolate-doom.org/wiki/images/0/0f/GNOME_Hexen_Guardian_of_Fire.png</image>
+      <caption>Level "Guardian of Fire"</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/5/5c/GNOME_Hexen_Effluvium.png</image>
+      <caption>Level "Effluvium"</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/c/c1/GNOME_Hexen_Dragon_Chapel.png</image>
+      <caption>Level "Dragon Chapel"</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/a/a7/GNOME_Hexen_Darkmere.png</image>
+      <caption>Level "Darkmere"</caption>
+    </screenshot>
+  </screenshots>
+</component>
--- a/src/hexen/g_game.c
+++ b/src/hexen/g_game.c
@@ -88,7 +88,6 @@
 
 int consoleplayer;              // player taking events and displaying
 int displayplayer;              // view being displayed
-int gametic;
 int levelstarttic;              // gametic at level start
 
 char demoname[32];
--- a/src/hexen/h2_main.c
+++ b/src/hexen/h2_main.c
@@ -103,7 +103,6 @@
 boolean ravpic;                 // checkparm of -ravpic
 boolean cdrom = false;          // true if cd-rom mode active
 boolean cmdfrag;                // true if a CMD_FRAG packet should be sent out
-boolean singletics;             // debug flag to cancel adaptiveness
 boolean artiskip;               // whether shift-enter skips an artifact
 int maxzone = 0x800000;         // Maximum allocated for zone heap (8meg default)
 skill_t startskill;
@@ -664,7 +663,7 @@
 
         if (W_AddFile(file) != NULL)
         {
-            M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,
+            M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name,
                          sizeof(demolumpname));
         }
         else
--- a/src/hexen/r_data.c
+++ b/src/hexen/r_data.c
@@ -627,7 +627,7 @@
         if (flatpresent[i])
         {
             lump = firstflat + i;
-            flatmemory += lumpinfo[lump].size;
+            flatmemory += lumpinfo[lump]->size;
             W_CacheLumpNum(lump, PU_CACHE);
         }
 
@@ -658,7 +658,7 @@
         for (j = 0; j < texture->patchcount; j++)
         {
             lump = texture->patches[j].patch;
-            texturememory += lumpinfo[lump].size;
+            texturememory += lumpinfo[lump]->size;
             W_CacheLumpNum(lump, PU_CACHE);
         }
     }
@@ -688,7 +688,7 @@
             for (k = 0; k < 8; k++)
             {
                 lump = firstspritelump + sf->lump[k];
-                spritememory += lumpinfo[lump].size;
+                spritememory += lumpinfo[lump]->size;
                 W_CacheLumpNum(lump, PU_CACHE);
             }
         }
--- a/src/hexen/r_things.c
+++ b/src/hexen/r_things.c
@@ -178,15 +178,15 @@
         // scan the lumps, filling in the frames for whatever is found
         //
         for (l = start + 1; l < end; l++)
-            if (!strncmp(lumpinfo[l].name, namelist[i], 4))
+            if (!strncmp(lumpinfo[l]->name, namelist[i], 4))
             {
-                frame = lumpinfo[l].name[4] - 'A';
-                rotation = lumpinfo[l].name[5] - '0';
+                frame = lumpinfo[l]->name[4] - 'A';
+                rotation = lumpinfo[l]->name[5] - '0';
                 R_InstallSpriteLump(l, frame, rotation, false);
-                if (lumpinfo[l].name[6])
+                if (lumpinfo[l]->name[6])
                 {
-                    frame = lumpinfo[l].name[6] - 'A';
-                    rotation = lumpinfo[l].name[7] - '0';
+                    frame = lumpinfo[l]->name[6] - 'A';
+                    rotation = lumpinfo[l]->name[7] - '0';
                     R_InstallSpriteLump(l, frame, rotation, true);
                 }
             }
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -352,6 +352,12 @@
 char *snd_dmxoption = "";
 int opl_io_port = 0x388;
 
+// If true, OPL sound channels are reversed to their correct arrangement
+// (as intended by the MIDI standard) rather than the backwards one
+// used by DMX due to a bug.
+
+static boolean opl_stereo_correct = false;
+
 // Load instrument table from GENMIDI lump:
 
 static boolean LoadInstrumentTable(void)
@@ -1133,6 +1139,16 @@
     unsigned int reg_pan;
     unsigned int i;
 
+    // The DMX library has the stereo channels backwards, maybe because
+    // Paul Radek had a Soundblaster card with the channels reversed, or
+    // perhaps it was just a bug in the OPL3 support that was never
+    // finished. By default we preserve this bug, but we also provide a
+    // secret DMXOPTION to fix it.
+    if (opl_stereo_correct)
+    {
+        pan = 144 - pan;
+    }
+
     if (opl_opl3mode)
     {
         if (pan >= 96)
@@ -1727,6 +1743,10 @@
         opl_opl3mode = 0;
         num_opl_voices = OPL_NUM_VOICES;
     }
+
+    // Secret, undocumented DMXOPTION that reverses the stereo channels
+    // into their correct orientation.
+    opl_stereo_correct = strstr(dmxoption, "-reverse") != NULL;
 
     // Initialize all registers.
 
--- a/src/i_sdlmusic.c
+++ b/src/i_sdlmusic.c
@@ -752,7 +752,7 @@
 
     for (lumpnum = 0; lumpnum < numlumps; ++lumpnum)
     {
-        strncpy(name, lumpinfo[lumpnum].name, 8);
+        strncpy(name, lumpinfo[lumpnum]->name, 8);
         name[8] = '\0';
 
         if (!IsMusicLump(lumpnum))
--- a/src/setup/mainmenu.c
+++ b/src/setup/mainmenu.c
@@ -91,7 +91,7 @@
     show_endoom = 0;
     dclick_use = 0;
     novert = 1;
-    snd_dmxoption = "-opl3";
+    snd_dmxoption = "-opl3 -reverse";
     png_screenshots = 1;
 }
 
--- a/src/setup/setup-manifest.xml.in
+++ b/src/setup/setup-manifest.xml.in
@@ -32,6 +32,7 @@
       <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <!-- Vista -->
       <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> <!-- 8 -->
       <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> <!-- 8.1 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <!-- 10 -->
     </application>
   </compatibility>
 
--- /dev/null
+++ b/src/strife.appdata.xml.in
@@ -1,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<component type="desktop">
+  <id>@PROGRAM_PREFIX@strife.desktop</id>
+  <metadata_license>CC0-1.0</metadata_license>
+  <project_license>GPL-2.0+</project_license>
+  <developer_name>@PACKAGE_MAINTAINER@</developer_name>
+  <url type="homepage">@PACKAGE_URL@</url>
+  <url type="bugtracker">@PACKAGE_ISSUES@</url>
+  <description>
+    <p>
+      @PACKAGE_SHORTNAME@ Strife is a conservative,
+      historically-accurate recreation of the Strife engine.  It is
+      completely compatible with the original game and mods created
+      with the original engine in mind.  Made with a great reverse
+      engineering effort, it has the goal of preserving the original
+      look, feel, limitations, and bugs of the original DOS
+      executable.
+    </p>
+    <p>
+      Full support for single- and multi-player games is provided.
+      Unlike the original executable, network play is implemented on
+      the IP network stack, allowing it to function on modern LANs and
+      the Internet.
+    </p>
+  </description>
+  <screenshots>
+    <screenshot type="default">
+      <image>http://www.chocolate-doom.org/wiki/images/b/b2/GNOME_Strife_Rowan.png</image>
+      <caption>Talking to Rowan</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/1/1f/GNOME_Strife_Town.png</image>
+      <caption>The Town</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/8/8a/GNOME_Strife_Opening.png</image>
+      <caption>Opening Cinematic</caption>
+    </screenshot>
+    <screenshot>
+      <image>http://www.chocolate-doom.org/wiki/images/c/c4/GNOME_Strife_Sewage.png</image>
+      <caption>In the sewage</caption>
+    </screenshot>
+  </screenshots>
+</component>
--- a/src/strife/d_main.c
+++ b/src/strife/d_main.c
@@ -1629,7 +1629,7 @@
 
         if (D_AddFile (file))
         {
-            M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,
+            M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name,
                          sizeof(demolumpname));
         }
         else
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@ -124,7 +124,6 @@
  
 int             consoleplayer;          // player taking events and displaying 
 int             displayplayer;          // view being displayed 
-int             gametic; 
 int             levelstarttic;          // gametic at level start 
 int             totalkills, /*totalitems,*/ totalsecret;    // for intermission 
  
--- a/src/strife/r_data.c
+++ b/src/strife/r_data.c
@@ -938,7 +938,7 @@
 	if (flatpresent[i])
 	{
 	    lump = firstflat + i;
-	    flatmemory += lumpinfo[lump].size;
+	    flatmemory += lumpinfo[lump]->size;
 	    W_CacheLumpNum(lump, PU_CACHE);
 	}
     }
@@ -975,7 +975,7 @@
 	for (j=0 ; j<texture->patchcount ; j++)
 	{
 	    lump = texture->patches[j].patch;
-	    texturememory += lumpinfo[lump].size;
+	    texturememory += lumpinfo[lump]->size;
 	    W_CacheLumpNum(lump , PU_CACHE);
 	}
     }
@@ -1004,7 +1004,7 @@
 	    for (k=0 ; k<8 ; k++)
 	    {
 		lump = firstspritelump + sf->lump[k];
-		spritememory += lumpinfo[lump].size;
+		spritememory += lumpinfo[lump]->size;
 		W_CacheLumpNum(lump , PU_CACHE);
 	    }
 	}
--- a/src/strife/r_things.c
+++ b/src/strife/r_things.c
@@ -211,22 +211,22 @@
 	//  filling in the frames for whatever is found
 	for (l=start+1 ; l<end ; l++)
 	{
-	    if (!strncasecmp(lumpinfo[l].name, spritename, 4))
+	    if (!strncasecmp(lumpinfo[l]->name, spritename, 4))
 	    {
-		frame = lumpinfo[l].name[4] - 'A';
-		rotation = lumpinfo[l].name[5] - '0';
+		frame = lumpinfo[l]->name[4] - 'A';
+		rotation = lumpinfo[l]->name[5] - '0';
 
 		if (modifiedgame)
-		    patched = W_GetNumForName (lumpinfo[l].name);
+		    patched = W_GetNumForName (lumpinfo[l]->name);
 		else
 		    patched = l;
 
 		R_InstallSpriteLump (patched, frame, rotation, false);
 
-		if (lumpinfo[l].name[6])
+		if (lumpinfo[l]->name[6])
 		{
-		    frame = lumpinfo[l].name[6] - 'A';
-		    rotation = lumpinfo[l].name[7] - '0';
+		    frame = lumpinfo[l]->name[6] - 'A';
+		    rotation = lumpinfo[l]->name[7] - '0';
 		    R_InstallSpriteLump (l, frame, rotation, true);
 		}
 	    }
--- a/src/w_checksum.c
+++ b/src/w_checksum.c
@@ -33,7 +33,7 @@
     int i;
     int result;
 
-    for (i=0; i<num_open_wadfiles; ++i)
+    for (i = 0; i < num_open_wadfiles; ++i)
     {
         if (open_wadfiles[i] == handle)
         {
@@ -77,11 +77,11 @@
     // Go through each entry in the WAD directory, adding information
     // about each entry to the SHA1 hash.
 
-    for (i=0; i<numlumps; ++i)
+    for (i = 0; i < numlumps; ++i)
     {
-        ChecksumAddLump(&sha1_context, &lumpinfo[i]);
+        ChecksumAddLump(&sha1_context, lumpinfo[i]);
     }
-    
+
     SHA1_Final(digest, &sha1_context);
 }
 
--- a/src/w_main.c
+++ b/src/w_main.c
@@ -16,8 +16,10 @@
 //     Common code to parse command line, identifying WAD files to load.
 //
 
+#include "config.h"
 #include "doomfeatures.h"
 #include "d_iwad.h"
+#include "i_system.h"
 #include "m_argv.h"
 #include "w_main.h"
 #include "w_merge.h"
@@ -26,7 +28,6 @@
 
 // Parse the command line, merging WAD files that are sppecified.
 // Returns true if at least one file was added.
-
 boolean W_ParseCommandLine(void)
 {
     boolean modifiedgame = false;
@@ -194,5 +195,46 @@
 //    W_PrintDirectory();
 
     return modifiedgame;
+}
+
+// Lump names that are unique to particular game types. This lets us check
+// the user is not trying to play with the wrong executable, eg.
+// chocolate-doom -iwad hexen.wad.
+static const struct
+{
+    GameMission_t mission;
+    char *lumpname;
+} unique_lumps[] = {
+    { doom,    "POSSA1" },
+    { heretic, "IMPXA1" },
+    { hexen,   "ETTNA1" },
+    { strife,  "AGRDA1" },
+};
+
+void W_CheckCorrectIWAD(GameMission_t mission)
+{
+    int i;
+    lumpindex_t lumpnum;
+
+    for (i = 0; i < arrlen(unique_lumps); ++i)
+    {
+        if (mission != unique_lumps[i].mission)
+        {
+            lumpnum = W_CheckNumForName(unique_lumps[i].lumpname);
+
+            if (lumpnum >= 0)
+            {
+                I_Error("\nYou are trying to use a %s IWAD file with "
+                        "the %s%s binary.\nThis isn't going to work.\n"
+                        "You probably want to use the %s%s binary.",
+                        D_SuggestGameName(unique_lumps[i].mission,
+                                          indetermined),
+                        PROGRAM_PREFIX,
+                        D_GameMissionString(mission),
+                        PROGRAM_PREFIX,
+                        D_GameMissionString(unique_lumps[i].mission));
+            }
+        }
+    }
 }
 
--- a/src/w_main.h
+++ b/src/w_main.h
@@ -18,7 +18,10 @@
 #ifndef W_MAIN_H
 #define W_MAIN_H
 
+#include "d_mode.h"
+
 boolean W_ParseCommandLine(void);
+void W_CheckCorrectIWAD(GameMission_t mission);
 
 #endif /* #ifndef W_MAIN_H */
 
--- a/src/w_merge.c
+++ b/src/w_merge.c
@@ -39,7 +39,7 @@
 
 typedef struct
 {
-    lumpinfo_t *lumps;
+    lumpinfo_t **lumps;
     int numlumps;
 } searchlist_t;
 
@@ -74,7 +74,7 @@
 
     for (i=0; i<list->numlumps; ++i)
     {
-        if (!strncasecmp(list->lumps[i].name, name, 8))
+        if (!strncasecmp(list->lumps[i]->name, name, 8))
             return i;
     }
 
@@ -352,7 +352,7 @@
     
     for (i=0; i<iwad_sprites.numlumps; ++i)
     {
-        AddSpriteLump(&iwad_sprites.lumps[i]);
+        AddSpriteLump(iwad_sprites.lumps[i]);
     }
     
     // Add all sprites from the PWAD
@@ -360,7 +360,7 @@
 
     for (i=0; i<pwad_sprites.numlumps; ++i)
     {
-        AddSpriteLump(&pwad_sprites.lumps[i]);
+        AddSpriteLump(pwad_sprites.lumps[i]);
     }
 }
 
@@ -386,13 +386,13 @@
 static void DoMerge(void)
 {
     section_t current_section;
-    lumpinfo_t *newlumps;
+    lumpinfo_t **newlumps;
     int num_newlumps;
     int lumpindex;
     int i, n;
-    
+
     // Can't ever have more lumps than we already have
-    newlumps = malloc(sizeof(lumpinfo_t) * numlumps);
+    newlumps = calloc(numlumps, sizeof(lumpinfo_t *));
     num_newlumps = 0;
 
     // Add IWAD lumps
@@ -400,7 +400,7 @@
 
     for (i=0; i<iwad.numlumps; ++i)
     {
-        lumpinfo_t *lump = &iwad.lumps[i];
+        lumpinfo_t *lump = iwad.lumps[i];
 
         switch (current_section)
         {
@@ -414,7 +414,7 @@
                     current_section = SECTION_SPRITES;
                 }
 
-                newlumps[num_newlumps++] = *lump;
+                newlumps[num_newlumps++] = lump;
 
                 break;
 
@@ -432,7 +432,7 @@
                         newlumps[num_newlumps++] = pwad_flats.lumps[n];
                     }
 
-                    newlumps[num_newlumps++] = *lump;
+                    newlumps[num_newlumps++] = lump;
 
                     // back to normal reading
                     current_section = SECTION_NORMAL;
@@ -448,7 +448,7 @@
 
                     if (lumpindex < 0)
                     {
-                        newlumps[num_newlumps++] = *lump;
+                        newlumps[num_newlumps++] = lump;
                     }
                 }
 
@@ -460,11 +460,11 @@
 
                 if (!strncasecmp(lump->name, "S_END", 8))
                 {
-                    // add all the pwad sprites
+                    // add all the PWAD sprites
 
                     for (n=0; n<pwad_sprites.numlumps; ++n)
                     {
-                        if (SpriteLumpNeeded(&pwad_sprites.lumps[n]))
+                        if (SpriteLumpNeeded(pwad_sprites.lumps[n]))
                         {
                             newlumps[num_newlumps++] = pwad_sprites.lumps[n];
                         }
@@ -471,7 +471,7 @@
                     }
 
                     // copy the ending
-                    newlumps[num_newlumps++] = *lump;
+                    newlumps[num_newlumps++] = lump;
 
                     // back to normal reading
                     current_section = SECTION_NORMAL;
@@ -483,7 +483,7 @@
 
                     if (SpriteLumpNeeded(lump))
                     {
-                        newlumps[num_newlumps++] = *lump;
+                        newlumps[num_newlumps++] = lump;
                     }
                 }
 
@@ -496,7 +496,7 @@
 
     for (i=0; i<pwad.numlumps; ++i)
     {
-        lumpinfo_t *lump = &pwad.lumps[i];
+        lumpinfo_t *lump = pwad.lumps[i];
 
         switch (current_section)
         {
@@ -515,7 +515,7 @@
                 {
                     // Don't include the headers of sections
        
-                    newlumps[num_newlumps++] = *lump;
+                    newlumps[num_newlumps++] = lump;
                 }
                 break;
 
@@ -550,7 +550,6 @@
     free(lumpinfo);
     lumpinfo = newlumps;
     numlumps = num_newlumps;
-
 }
 
 void W_PrintDirectory(void)
@@ -560,8 +559,8 @@
     // debug
     for (i=0; i<numlumps; ++i)
     {
-        for (n=0; n<8 && lumpinfo[i].name[n] != '\0'; ++n)
-            putchar(lumpinfo[i].name[n]);
+        for (n=0; n<8 && lumpinfo[i]->name[n] != '\0'; ++n)
+            putchar(lumpinfo[i]->name[n]);
         putchar('\n');
     }
 }
@@ -579,7 +578,7 @@
     if (W_AddFile(filename) == NULL)
         return;
 
-    // iwad is at the start, pwad was appended to the end
+    // IWAD is at the start, PWAD was appended to the end
 
     iwad.lumps = lumpinfo;
     iwad.numlumps = old_numlumps;
@@ -606,25 +605,23 @@
 {
     int i;
 
-    // Go through the IWAD list given, replacing lumps with lumps of 
+    // Go through the IWAD list given, replacing lumps with lumps of
     // the same name from the PWAD
-
     for (i=0; i<list->numlumps; ++i)
     {
         int index;
 
-        index = FindInList(&pwad, list->lumps[i].name);
+        index = FindInList(&pwad, list->lumps[i]->name);
 
         if (index > 0)
         {
-            memcpy(&list->lumps[i], &pwad.lumps[index], 
+            memcpy(list->lumps[i], pwad.lumps[index],
                    sizeof(lumpinfo_t));
         }
     }
-    
 }
 
-// Merge sprites and flats in the way NWT does with its -af and -as 
+// Merge sprites and flats in the way NWT does with its -af and -as
 // command-line options.
 
 void W_NWTMergeFile(char *filename, int flags)
@@ -638,7 +635,7 @@
     if (W_AddFile(filename) == NULL)
         return;
 
-    // iwad is at the start, pwad was appended to the end
+    // IWAD is at the start, PWAD was appended to the end
 
     iwad.lumps = lumpinfo;
     iwad.numlumps = old_numlumps;
@@ -645,13 +642,13 @@
 
     pwad.lumps = lumpinfo + old_numlumps;
     pwad.numlumps = numlumps - old_numlumps;
-    
+
     // Setup sprite/flat lists
 
     SetupLists();
 
     // Merge in flats?
-    
+
     if (flags & W_NWT_MERGE_FLATS)
     {
         W_NWTAddLumps(&iwad_flats);
@@ -690,7 +687,7 @@
         return;
     }
 
-    // iwad is at the start, pwad was appended to the end
+    // IWAD is at the start, PWAD was appended to the end
 
     iwad.lumps = lumpinfo;
     iwad.numlumps = old_numlumps;
@@ -697,7 +694,7 @@
 
     pwad.lumps = lumpinfo + old_numlumps;
     pwad.numlumps = numlumps - old_numlumps;
-    
+
     // Setup sprite/flat lists
 
     SetupLists();
@@ -706,12 +703,12 @@
 
     for (i=0; i<iwad_sprites.numlumps; ++i)
     {
-        if (FindInList(&pwad, iwad_sprites.lumps[i].name) >= 0)
+        if (FindInList(&pwad, iwad_sprites.lumps[i]->name) >= 0)
         {
             // Replace this entry with an empty string.  This is what
             // nwt -merge does.
 
-            M_StringCopy(iwad_sprites.lumps[i].name, "", 8);
+            M_StringCopy(iwad_sprites.lumps[i]->name, "", 8);
         }
     }
 
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -26,8 +26,6 @@
 
 #include "doomtype.h"
 
-#include "config.h"
-#include "d_iwad.h"
 #include "i_swap.h"
 #include "i_system.h"
 #include "i_video.h"
@@ -57,16 +55,17 @@
 //
 
 // Location of each lump on disk.
-lumpinfo_t *lumpinfo;
+lumpinfo_t **lumpinfo;
 unsigned int numlumps = 0;
 
 // Hash table for fast lookups
-static lumpinfo_t **lumphash;
+static lumpindex_t *lumphash;
 
 // Variables for the reload hack: filename of the PWAD to reload, and the
 // lumps from WADs before the reload file, so we can resent numlumps and
 // load the file again.
 static wad_file_t *reloadhandle = NULL;
+static lumpinfo_t *reloadlumps = NULL;
 static char *reloadname = NULL;
 static int reloadlump = -1;
 
@@ -87,46 +86,6 @@
     return result;
 }
 
-// Increase the size of the lumpinfo[] array to the specified size.
-static void ExtendLumpInfo(int newnumlumps)
-{
-    lumpinfo_t *newlumpinfo;
-    unsigned int i;
-
-    newlumpinfo = calloc(newnumlumps, sizeof(lumpinfo_t));
-
-    if (newlumpinfo == NULL)
-    {
-	I_Error ("Couldn't realloc lumpinfo");
-    }
-
-    // Copy over lumpinfo_t structures from the old array. If any of
-    // these lumps have been cached, we need to update the user
-    // pointers to the new location.
-    for (i = 0; i < numlumps && i < newnumlumps; ++i)
-    {
-        memcpy(&newlumpinfo[i], &lumpinfo[i], sizeof(lumpinfo_t));
-
-        if (newlumpinfo[i].cache != NULL)
-        {
-            Z_ChangeUser(newlumpinfo[i].cache, &newlumpinfo[i].cache);
-        }
-
-        // We shouldn't be generating a hash table until after all WADs have
-        // been loaded, but just in case...
-        if (lumpinfo[i].next != NULL)
-        {
-            int nextlumpnum = lumpinfo[i].next - lumpinfo;
-            newlumpinfo[i].next = &newlumpinfo[nextlumpnum];
-        }
-    }
-
-    // All done.
-    free(lumpinfo);
-    lumpinfo = newlumpinfo;
-    numlumps = newnumlumps;
-}
-
 //
 // LUMP BASED ROUTINES.
 //
@@ -143,14 +102,14 @@
 wad_file_t *W_AddFile (char *filename)
 {
     wadinfo_t header;
-    lumpinfo_t *lump_p;
-    unsigned int i;
+    lumpindex_t i;
     wad_file_t *wad_file;
     int length;
     int startlump;
     filelump_t *fileinfo;
     filelump_t *filerover;
-    int newnumlumps;
+    lumpinfo_t *filelumps;
+    int numfilelumps;
 
     // If the filename begins with a ~, it indicates that we should use the
     // reload hack.
@@ -180,15 +139,6 @@
 	return NULL;
     }
 
-    // If this is the reload file, we need to save the file handle so that we
-    // can close it later on when we do a reload.
-    if (reloadname)
-    {
-        reloadhandle = wad_file;
-    }
-
-    newnumlumps = numlumps;
-
     if (strcasecmp(filename+strlen(filename)-3 , "wad" ) )
     {
 	// single lump file
@@ -206,9 +156,9 @@
         // extension).
 
 	M_ExtractFileBase (filename, fileinfo->name);
-	newnumlumps++;
+	numfilelumps = 1;
     }
-    else 
+    else
     {
 	// WAD file
         W_Read(wad_file, 0, &header, sizeof(header));
@@ -221,8 +171,8 @@
 		I_Error ("Wad file %s doesn't have IWAD "
 			 "or PWAD id\n", filename);
 	    }
-	    
-	    // ???modifiedgame = true;		
+
+	    // ???modifiedgame = true;
 	}
 
 	header.numlumps = LONG(header.numlumps);
@@ -231,26 +181,36 @@
 	fileinfo = Z_Malloc(length, PU_STATIC, 0);
 
         W_Read(wad_file, header.infotableofs, fileinfo, length);
-	newnumlumps += header.numlumps;
+	numfilelumps = header.numlumps;
     }
 
     // Increase size of numlumps array to accomodate the new file.
+    filelumps = calloc(numfilelumps, sizeof(lumpinfo_t));
+    if (filelumps == NULL)
+    {
+        I_Error("Failed to allocate array for lumps from new file.");
+    }
+
     startlump = numlumps;
-    ExtendLumpInfo(newnumlumps);
+    numlumps += numfilelumps;
+    lumpinfo = realloc(lumpinfo, numlumps * sizeof(lumpinfo_t *));
+    if (lumpinfo == NULL)
+    {
+        I_Error("Failed to increase lumpinfo[] array size.");
+    }
 
-    lump_p = &lumpinfo[startlump];
-
     filerover = fileinfo;
 
-    for (i=startlump; i<numlumps; ++i)
+    for (i = startlump; i < numlumps; ++i)
     {
-	lump_p->wad_file = wad_file;
-	lump_p->position = LONG(filerover->filepos);
-	lump_p->size = LONG(filerover->size);
+        lumpinfo_t *lump_p = &filelumps[i - startlump];
+        lump_p->wad_file = wad_file;
+        lump_p->position = LONG(filerover->filepos);
+        lump_p->size = LONG(filerover->size);
         lump_p->cache = NULL;
-	strncpy(lump_p->name, filerover->name, 8);
+        strncpy(lump_p->name, filerover->name, 8);
+        lumpinfo[i] = lump_p;
 
-        ++lump_p;
         ++filerover;
     }
 
@@ -262,6 +222,14 @@
         lumphash = NULL;
     }
 
+    // If this is the reload file, we need to save some details about the
+    // file so that we can close it later on when we do a reload.
+    if (reloadname)
+    {
+        reloadhandle = wad_file;
+        reloadlumps = filelumps;
+    }
+
     return wad_file;
 }
 
@@ -282,10 +250,9 @@
 // Returns -1 if name not found.
 //
 
-int W_CheckNumForName (char* name)
+lumpindex_t W_CheckNumForName(char* name)
 {
-    lumpinfo_t *lump_p;
-    int i;
+    lumpindex_t i;
 
     // Do we have a hash table yet?
 
@@ -292,28 +259,28 @@
     if (lumphash != NULL)
     {
         int hash;
-        
+
         // We do! Excellent.
 
         hash = W_LumpNameHash(name) % numlumps;
-        
-        for (lump_p = lumphash[hash]; lump_p != NULL; lump_p = lump_p->next)
+
+        for (i = lumphash[hash]; i != -1; i = lumpinfo[i]->next)
         {
-            if (!strncasecmp(lump_p->name, name, 8))
+            if (!strncasecmp(lumpinfo[i]->name, name, 8))
             {
-                return lump_p - lumpinfo;
+                return i;
             }
         }
-    } 
+    }
     else
     {
         // We don't have a hash table generate yet. Linear search :-(
-        // 
+        //
         // scan backwards so patch lump files take precedence
 
-        for (i=numlumps-1; i >= 0; --i)
+        for (i = numlumps - 1; i >= 0; --i)
         {
-            if (!strncasecmp(lumpinfo[i].name, name, 8))
+            if (!strncasecmp(lumpinfo[i]->name, name, 8))
             {
                 return i;
             }
@@ -332,12 +299,12 @@
 // W_GetNumForName
 // Calls W_CheckNumForName, but bombs out if not found.
 //
-int W_GetNumForName (char* name)
+lumpindex_t W_GetNumForName(char* name)
 {
-    int	i;
+    lumpindex_t i;
 
     i = W_CheckNumForName (name);
-    
+
     if (i < 0)
     {
         I_Error ("W_GetNumForName: %s not found!", name);
@@ -351,7 +318,7 @@
 // W_LumpLength
 // Returns the buffer size needed to load the given lump.
 //
-int W_LumpLength (unsigned int lump)
+int W_LumpLength(lumpindex_t lump)
 {
     if (lump >= numlumps)
     {
@@ -358,7 +325,7 @@
 	I_Error ("W_LumpLength: %i >= numlumps", lump);
     }
 
-    return lumpinfo[lump].size;
+    return lumpinfo[lump]->size;
 }
 
 
@@ -368,29 +335,29 @@
 // Loads the lump into the given buffer,
 //  which must be >= W_LumpLength().
 //
-void W_ReadLump(unsigned int lump, void *dest)
+void W_ReadLump(lumpindex_t lump, void *dest)
 {
     int c;
     lumpinfo_t *l;
-	
+
     if (lump >= numlumps)
     {
-	I_Error ("W_ReadLump: %i >= numlumps", lump);
+        I_Error ("W_ReadLump: %i >= numlumps", lump);
     }
 
-    l = lumpinfo+lump;
-	
-    I_BeginRead ();
-	
+    l = lumpinfo[lump];
+
+    I_BeginRead();
+
     c = W_Read(l->wad_file, l->position, dest, l->size);
 
     if (c < l->size)
     {
-	I_Error ("W_ReadLump: only read %i of %i on lump %i",
-		 c, l->size, lump);	
+        I_Error("W_ReadLump: only read %i of %i on lump %i",
+                c, l->size, lump);
     }
 
-    I_EndRead ();
+    I_EndRead();
 }
 
 
@@ -408,7 +375,7 @@
 // when no longer needed (do not use Z_ChangeTag).
 //
 
-void *W_CacheLumpNum(int lumpnum, int tag)
+void *W_CacheLumpNum(lumpindex_t lumpnum, int tag)
 {
     byte *result;
     lumpinfo_t *lump;
@@ -418,7 +385,7 @@
 	I_Error ("W_CacheLumpNum: %i >= numlumps", lumpnum);
     }
 
-    lump = &lumpinfo[lumpnum];
+    lump = lumpinfo[lumpnum];
 
     // Get the pointer to return.  If the lump is in a memory-mapped
     // file, we can just return a pointer to within the memory-mapped
@@ -470,7 +437,7 @@
 // complicated ...
 //
 
-void W_ReleaseLumpNum(int lumpnum)
+void W_ReleaseLumpNum(lumpindex_t lumpnum)
 {
     lumpinfo_t *lump;
 
@@ -479,7 +446,7 @@
 	I_Error ("W_ReleaseLumpNum: %i >= numlumps", lumpnum);
     }
 
-    lump = &lumpinfo[lumpnum];
+    lump = lumpinfo[lumpnum];
 
     if (lump->wad_file->mapped != NULL)
     {
@@ -566,7 +533,7 @@
 
 void W_GenerateHashTable(void)
 {
-    unsigned int i;
+    lumpindex_t i;
 
     // Free the old hash table, if there is one:
     if (lumphash != NULL)
@@ -577,19 +544,23 @@
     // Generate hash table
     if (numlumps > 0)
     {
-        lumphash = Z_Malloc(sizeof(lumpinfo_t *) * numlumps, PU_STATIC, NULL);
-        memset(lumphash, 0, sizeof(lumpinfo_t *) * numlumps);
+        lumphash = Z_Malloc(sizeof(lumpindex_t) * numlumps, PU_STATIC, NULL);
 
-        for (i=0; i<numlumps; ++i)
+        for (i = 0; i < numlumps; ++i)
         {
+            lumphash[i] = -1;
+        }
+
+        for (i = 0; i < numlumps; ++i)
+        {
             unsigned int hash;
 
-            hash = W_LumpNameHash(lumpinfo[i].name) % numlumps;
+            hash = W_LumpNameHash(lumpinfo[i]->name) % numlumps;
 
             // Hook into the hash table
 
-            lumpinfo[i].next = lumphash[hash];
-            lumphash[hash] = &lumpinfo[i];
+            lumpinfo[i]->next = lumphash[hash];
+            lumphash[hash] = i;
         }
     }
 
@@ -605,7 +576,7 @@
 void W_Reload(void)
 {
     char *filename;
-    int i;
+    lumpindex_t i;
 
     if (reloadname == NULL)
     {
@@ -612,12 +583,12 @@
         return;
     }
 
-    // We must release any lumps being held in the PWAD we're about to reload:
+    // We must free any lumps being cached from the PWAD we're about to reload:
     for (i = reloadlump; i < numlumps; ++i)
     {
-        if (lumpinfo[i].cache != NULL)
+        if (lumpinfo[i]->cache != NULL)
         {
-            W_ReleaseLumpNum(i);
+            Z_Free(lumpinfo[i]->cache);
         }
     }
 
@@ -628,6 +599,8 @@
     filename = reloadname;
 
     W_CloseFile(reloadhandle);
+    free(reloadlumps);
+
     reloadname = NULL;
     reloadlump = -1;
     reloadhandle = NULL;
@@ -637,46 +610,5 @@
     // The WAD directory has changed, so we have to regenerate the
     // fast lookup hashtable:
     W_GenerateHashTable();
-}
-
-// Lump names that are unique to particular game types. This lets us check
-// the user is not trying to play with the wrong executable, eg.
-// chocolate-doom -iwad hexen.wad.
-static const struct
-{
-    GameMission_t mission;
-    char *lumpname;
-} unique_lumps[] = {
-    { doom,    "POSSA1" },
-    { heretic, "IMPXA1" },
-    { hexen,   "ETTNA1" },
-    { strife,  "AGRDA1" },
-};
-
-void W_CheckCorrectIWAD(GameMission_t mission)
-{
-    int i;
-    int lumpnum;
-
-    for (i = 0; i < arrlen(unique_lumps); ++i)
-    {
-        if (mission != unique_lumps[i].mission)
-        {
-            lumpnum = W_CheckNumForName(unique_lumps[i].lumpname);
-
-            if (lumpnum >= 0)
-            {
-                I_Error("\nYou are trying to use a %s IWAD file with "
-                        "the %s%s binary.\nThis isn't going to work.\n"
-                        "You probably want to use the %s%s binary.",
-                        D_SuggestGameName(unique_lumps[i].mission,
-                                          indetermined),
-                        PROGRAM_PREFIX,
-                        D_GameMissionString(mission),
-                        PROGRAM_PREFIX,
-                        D_GameMissionString(unique_lumps[i].mission));
-            }
-        }
-    }
 }
 
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -23,8 +23,6 @@
 #include <stdio.h>
 
 #include "doomtype.h"
-#include "d_mode.h"
-
 #include "w_file.h"
 
 
@@ -37,6 +35,7 @@
 //
 
 typedef struct lumpinfo_s lumpinfo_t;
+typedef int lumpindex_t;
 
 struct lumpinfo_s
 {
@@ -47,33 +46,30 @@
     void       *cache;
 
     // Used for hash table lookups
-
-    lumpinfo_t *next;
+    lumpindex_t next;
 };
 
 
-extern lumpinfo_t *lumpinfo;
+extern lumpinfo_t **lumpinfo;
 extern unsigned int numlumps;
 
-wad_file_t *W_AddFile (char *filename);
-void    W_Reload (void);
+wad_file_t *W_AddFile(char *filename);
+void W_Reload(void);
 
-int	W_CheckNumForName (char* name);
-int	W_GetNumForName (char* name);
+lumpindex_t W_CheckNumForName(char *name);
+lumpindex_t W_GetNumForName(char *name);
 
-int	W_LumpLength (unsigned int lump);
-void    W_ReadLump (unsigned int lump, void *dest);
+int W_LumpLength(lumpindex_t lump);
+void W_ReadLump(lumpindex_t lump, void *dest);
 
-void*	W_CacheLumpNum (int lump, int tag);
-void*	W_CacheLumpName (char* name, int tag);
+void *W_CacheLumpNum(lumpindex_t lump, int tag);
+void *W_CacheLumpName(char *name, int tag);
 
-void    W_GenerateHashTable(void);
+void W_GenerateHashTable(void);
 
 extern unsigned int W_LumpNameHash(const char *s);
 
-void    W_ReleaseLumpNum(int lump);
-void    W_ReleaseLumpName(char *name);
-
-void W_CheckCorrectIWAD(GameMission_t mission);
+void W_ReleaseLumpNum(lumpindex_t lump);
+void W_ReleaseLumpName(char *name);
 
 #endif