shithub: choc

Download patch

ref: 5974dfc9cee388421768bea3ff67e7af11f8bb62
parent: 93e591e18a0091c038d722d6e2081af7f701bc6d
parent: f0bef91e4a100cf2a41dc528c780ca5abdebf3f4
author: Fabian Greffrath <fabian@greffrath.com>
date: Sat Sep 17 09:12:26 EDT 2016

Merge branch 'master' into sdl2-branch

--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -70,6 +70,11 @@
 * Please follow the coding style guidelines described in the
   [HACKING](../HACKING.md) file.
 
+* Please don't make unnecessary changes which just change formatting
+  without any actual change to program logic. While being consistent
+  is nice, such changes destroy the ability to use the `git blame`
+  command to see when code was last changed.
+
 * The guidelines given above in the "feature requests" section also
   apply here. New features which aren't in line with the project
   philosophy are likely to be rejected. If you're not sure, open a
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@
 stamp-h.in
 stamp-h1
 tags
+\#*\#
 
 # These are the default patterns globally ignored by Subversion:
 *.o
--- a/NEWS.md
+++ b/NEWS.md
@@ -12,6 +12,12 @@
   * Checksum calculations were fixed on big endian systems, allowing
     multiplayer games to be played in mixed little/big-endian
     environments. (thanks GhostlyDeath, njankowski)
+  * The NES30, SNES30, and SFC30 gamepads are detected and configured
+    automatically by the Setup tool.  The automap can also be
+    configured to a joystick button. (thanks Jon)
+  * The vanilla limit of 4046 lumps per WAD is now enforced. (thanks
+    Jon, Quasar, Edward-san)
+  * Solidsegs overflow is emulated like in vanilla. (thanks Quasar)
 
 ### Build systems
   * Improved compatibility with BSD Make. (thanks R.Rebello)
@@ -31,6 +37,8 @@
   * Allow starting multiplayer Chex Quest games.
   * Allow Freedoom: Phase 1 ≤ 0.10.1 to be loaded with mods, with
     -gameversion older than ultimate. (thanks Fabian, chungy)
+  * The IWAD order preference for GOG.com installs matches vanilla
+    Final Doom: doom2, plutonia, tnt, doom. (thanks chungy)
   * Added safety checks against write failures when saving a game,
     such as when the directory is read-only.  Try falling back to a
     temporary directory and reporting an error instead.  (thanks
--- a/configure.ac
+++ b/configure.ac
@@ -19,12 +19,6 @@
 
 OPT_LEVEL=2
 
-# Engine room, we need more speed!
-
-AC_ARG_ENABLE(penis-extension, 
-[  --enable-penis-extension   Enable counterproductive compiler optimisations ],
-[ OPT_LEVEL=3 ])
-
 # If this is gcc, we have some options we'd like to turn on.  Turn on 
 # optimisation and debugging symbols.
 
--- a/msvc/.gitignore
+++ b/msvc/.gitignore
@@ -2,6 +2,7 @@
 *.ncb
 *.suo
 *.user
+hexndata
 savegames
 strfsav*
 *.pcx
--- a/msvc/hexen.vcproj
+++ b/msvc/hexen.vcproj
@@ -65,6 +65,7 @@
 				IgnoreAllDefaultLibraries="false"
 				IgnoreDefaultLibraryNames="msvcrt"
 				GenerateDebugInformation="true"
+				GenerateMapFile="true"
 				SubSystem="1"
 				TargetMachine="1"
 			/>
--- a/opl/opl3.c
+++ b/opl/opl3.c
@@ -21,7 +21,7 @@
 //      OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
 //          OPL2 ROMs.
 //
-// version: 1.7.2
+// version: 1.7.4
 //
 
 #include <stdio.h>
@@ -126,7 +126,7 @@
     0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa
 };
 
-// 
+//
 // freq mult table multiplied by 2
 //
 // 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15
@@ -1184,15 +1184,15 @@
     if ((chip->timer & 0x3f) == 0x3f)
     {
         chip->tremolopos = (chip->tremolopos + 1) % 210;
-        if (chip->tremolopos < 105)
-        {
-            chip->tremolo = chip->tremolopos >> chip->tremoloshift;
-        }
-        else
-        {
-            chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift;
-        }
     }
+    if (chip->tremolopos < 105)
+    {
+        chip->tremolo = chip->tremolopos >> chip->tremoloshift;
+    }
+    else
+    {
+        chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift;
+    }
 
     if ((chip->timer & 0x3ff) == 0x3ff)
     {
@@ -1201,9 +1201,13 @@
 
     chip->timer++;
 
-    while (chip->writebuf_cur != chip->writebuf_last
-        && chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt)
+    while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt)
     {
+        if (!(chip->writebuf[chip->writebuf_cur].reg & 0x200))
+        {
+            break;
+        }
+        chip->writebuf[chip->writebuf_cur].reg &= 0x1ff;
         OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg,
                       chip->writebuf[chip->writebuf_cur].data);
         chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE;
@@ -1268,6 +1272,8 @@
     }
     chip->noise = 0x306600;
     chip->rateratio = (samplerate << RSM_FRAC) / 49716;
+    chip->tremoloshift = 4;
+    chip->vibshift = 1;
 }
 
 void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v)
@@ -1372,8 +1378,18 @@
 void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v)
 {
     Bit64u time1, time2;
-    chip->writebuf[chip->writebuf_last % OPL_WRITEBUF_SIZE].reg = reg;
-    chip->writebuf[chip->writebuf_last % OPL_WRITEBUF_SIZE].data = v;
+
+    if (chip->writebuf[chip->writebuf_last].reg & 0x200)
+    {
+        OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff,
+                      chip->writebuf[chip->writebuf_last].data);
+
+        chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE;
+        chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time;
+    }
+
+    chip->writebuf[chip->writebuf_last].reg = reg | 0x200;
+    chip->writebuf[chip->writebuf_last].data = v;
     time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY;
     time2 = chip->writebuf_samplecnt;
 
@@ -1382,7 +1398,7 @@
         time1 = time2;
     }
 
-    chip->writebuf[chip->writebuf_last % OPL_WRITEBUF_SIZE].time = time1;
+    chip->writebuf[chip->writebuf_last].time = time1;
     chip->writebuf_lasttime = time1;
     chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE;
 }
@@ -1390,7 +1406,7 @@
 void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
 {
     Bit32u i;
-	
+
     for(i = 0; i < numsamples; i++)
     {
         OPL3_GenerateResampled(chip, sndptr);
--- a/opl/opl3.h
+++ b/opl/opl3.h
@@ -21,7 +21,7 @@
 //      OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
 //          OPL2 ROMs.
 //
-// version: 1.7.2
+// version: 1.7.4
 //
 
 #ifndef OPL_OPL3_H
@@ -114,7 +114,7 @@
     Bit16s zeromod;
     Bit32s mixbuff[2];
     //OPL3L
-    Bit32s rateratio; 
+    Bit32s rateratio;
     Bit32s samplecnt;
     Bit16s oldsamples[2];
     Bit16s samples[2];
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -12,8 +12,10 @@
 chocolate-hexen-setup
 chocolate-strife-setup
 chocolate-setup
+*.cfg
 *.exe
 *.desktop
+*.txt
 *.appdata.xml
 tags
 TAGS
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@ -154,27 +154,27 @@
         "INSTALLPATH",
     },
 
-    // Ultimate Doom
+    // Doom II
 
     {
         HKEY_LOCAL_MACHINE,
-        SOFTWARE_KEY "\\GOG.com\\Games\\1435827232",
+        SOFTWARE_KEY "\\GOG.com\\Games\\1435848814",
         "PATH",
     },
 
-    // Doom II
+    // Final Doom
 
     {
         HKEY_LOCAL_MACHINE,
-        SOFTWARE_KEY "\\GOG.com\\Games\\1435848814",
+        SOFTWARE_KEY "\\GOG.com\\Games\\1435848742",
         "PATH",
     },
 
-    // Final Doom
+    // Ultimate Doom
 
     {
         HKEY_LOCAL_MACHINE,
-        SOFTWARE_KEY "\\GOG.com\\Games\\1435848742",
+        SOFTWARE_KEY "\\GOG.com\\Games\\1435827232",
         "PATH",
     },
 
@@ -195,8 +195,8 @@
     "Doom2",
     "Final Doom",
     "Ultimate Doom",
-    "TNT",
     "Plutonia",
+    "TNT",
 };
 
 // Location where Steam is installed
@@ -256,7 +256,7 @@
     {
         // Allocate a buffer for the value and read the value
 
-        result = malloc(len);
+        result = malloc(len + 1);
 
         if (RegQueryValueEx(key, reg_val->value, NULL, &valtype,
                             (unsigned char *) result, &len) != ERROR_SUCCESS)
@@ -263,6 +263,11 @@
         {
             free(result);
             result = NULL;
+        }
+        else
+        {
+            // Ensure the value is null-terminated
+            result[len] = '\0';
         }
     }
 
--- a/src/doom/am_map.c
+++ b/src/doom/am_map.c
@@ -32,6 +32,7 @@
 #include "m_controls.h"
 #include "m_misc.h"
 #include "i_system.h"
+#include "i_timer.h"
 
 // Needs access to LFB.
 #include "v_video.h"
@@ -598,10 +599,31 @@
 
     int rc;
     static int bigstate=0;
+    static int joywait = 0;
     static char buffer[20];
     int key;
 
     rc = false;
+
+    if (ev->type == ev_joystick && joybautomap >= 0
+        && (ev->data1 & (1 << joybautomap)) != 0 && joywait < I_GetTime())
+    {
+        joywait = I_GetTime() + 5;
+
+        if (!automapactive)
+        {
+            AM_Start ();
+            viewactive = false;
+        }
+        else
+        {
+            bigstate = 0;
+            viewactive = true;
+            AM_Stop ();
+        }
+
+        return true;
+    }
 
     if (!automapactive)
     {
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -815,6 +815,7 @@
         // with Freedoom and get the right level names.
 
         //!
+        // @category compat
         // @arg <pack>
         //
         // Explicitly specify a Doom II "mission pack" to run as, instead of
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -2220,12 +2220,13 @@
 void G_TimeDemo (char* name) 
 {
     //!
-    // @vanilla 
+    // @category video
+    // @vanilla
     //
     // Disable rendering the screen entirely.
     //
 
-    nodrawers = M_CheckParm ("-nodraw"); 
+    nodrawers = M_CheckParm ("-nodraw");
 
     timingdemo = true; 
     singletics = true; 
--- a/src/doom/m_random.c
+++ b/src/doom/m_random.c
@@ -63,3 +63,10 @@
 {
     rndindex = prndindex = 0;
 }
+
+// inspired by the same routine in Eternity, thanks haleyjd
+int P_SubRandom (void)
+{
+    int r = P_Random();
+    return r - P_Random();
+}
--- a/src/doom/m_random.h
+++ b/src/doom/m_random.h
@@ -24,7 +24,6 @@
 #include "doomtype.h"
 
 
-
 // Returns a number from 0 to 255,
 // from a lookup table.
 int M_Random (void);
@@ -35,5 +34,7 @@
 // Fix randoms for demos.
 void M_ClearRandom (void);
 
+// Defined version of P_Random() - P_Random()
+int P_SubRandom (void);
 
 #endif
--- a/src/doom/p_enemy.c
+++ b/src/doom/p_enemy.c
@@ -777,7 +777,7 @@
 				    actor->target->y);
     
     if (actor->target->flags & MF_SHADOW)
-	actor->angle += (P_Random()-P_Random())<<21;
+	actor->angle += P_SubRandom() << 21;
 }
 
 
@@ -798,7 +798,7 @@
     slope = P_AimLineAttack (actor, angle, MISSILERANGE);
 
     S_StartSound (actor, sfx_pistol);
-    angle += (P_Random()-P_Random())<<20;
+    angle += P_SubRandom() << 20;
     damage = ((P_Random()%5)+1)*3;
     P_LineAttack (actor, angle, MISSILERANGE, slope, damage);
 }
@@ -821,7 +821,7 @@
 
     for (i=0 ; i<3 ; i++)
     {
-	angle = bangle + ((P_Random()-P_Random())<<20);
+	angle = bangle + (P_SubRandom() << 20);
 	damage = ((P_Random()%5)+1)*3;
 	P_LineAttack (actor, angle, MISSILERANGE, slope, damage);
     }
@@ -842,7 +842,7 @@
     bangle = actor->angle;
     slope = P_AimLineAttack (actor, bangle, MISSILERANGE);
 
-    angle = bangle + ((P_Random()-P_Random())<<20);
+    angle = bangle + (P_SubRandom() << 20);
     damage = ((P_Random()%5)+1)*3;
     P_LineAttack (actor, angle, MISSILERANGE, slope, damage);
 }
@@ -1877,7 +1877,7 @@
     int		z;
     mobj_t*	th;
 	
-    x = mo->x + (P_Random () - P_Random ())*2048;
+    x = mo->x +  P_SubRandom() * 2048;
     y = mo->y;
     z = 128 + P_Random()*2*FRACUNIT;
     th = P_SpawnMobj (x,y,z, MT_ROCKET);
--- a/src/doom/p_map.c
+++ b/src/doom/p_map.c
@@ -1351,8 +1351,8 @@
 			  thing->y,
 			  thing->z + thing->height/2, MT_BLOOD);
 	
-	mo->momx = (P_Random() - P_Random ())<<12;
-	mo->momy = (P_Random() - P_Random ())<<12;
+	mo->momx = P_SubRandom() << 12;
+	mo->momy = P_SubRandom() << 12;
     }
 
     // keep checking (crush other things)	
--- a/src/doom/p_mobj.c
+++ b/src/doom/p_mobj.c
@@ -867,7 +867,7 @@
 {
     mobj_t*	th;
 	
-    z += ((P_Random()-P_Random())<<10);
+    z += (P_SubRandom() << 10);
 
     th = P_SpawnMobj (x,y,z, MT_PUFF);
     th->momz = FRACUNIT;
@@ -895,7 +895,7 @@
 {
     mobj_t*	th;
 	
-    z += ((P_Random()-P_Random())<<10);
+    z += (P_SubRandom() << 10);
     th = P_SpawnMobj (x,y,z, MT_BLOOD);
     th->momz = FRACUNIT*2;
     th->tics -= P_Random()&3;
@@ -980,7 +980,7 @@
 
     // fuzzy player
     if (dest->flags & MF_SHADOW)
-	an += (P_Random()-P_Random())<<20;	
+	an += P_SubRandom() << 20;
 
     th->angle = an;
     an >>= ANGLETOFINESHIFT;
--- a/src/doom/p_pspr.c
+++ b/src/doom/p_pspr.c
@@ -470,7 +470,7 @@
 	damage *= 10;
 
     angle = player->mo->angle;
-    angle += (P_Random()-P_Random())<<18;
+    angle += P_SubRandom() << 18;
     slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
     P_LineAttack (player->mo, angle, MELEERANGE, slope, damage);
 
@@ -500,7 +500,7 @@
 
     damage = 2*(P_Random ()%10+1);
     angle = player->mo->angle;
-    angle += (P_Random()-P_Random())<<18;
+    angle += P_SubRandom() << 18;
     
     // use meleerange + 1 se the puff doesn't skip the flash
     slope = P_AimLineAttack (player->mo, angle, MELEERANGE+1);
@@ -643,7 +643,7 @@
     angle = mo->angle;
 
     if (!accurate)
-	angle += (P_Random()-P_Random())<<18;
+	angle += P_SubRandom() << 18;
 
     P_LineAttack (mo, angle, MISSILERANGE, bulletslope, damage);
 }
@@ -726,11 +726,11 @@
     {
 	damage = 5*(P_Random ()%3+1);
 	angle = player->mo->angle;
-	angle += (P_Random()-P_Random())<<ANGLETOFINESHIFT;
+	angle += P_SubRandom() << ANGLETOFINESHIFT;
 	P_LineAttack (player->mo,
 		      angle,
 		      MISSILERANGE,
-		      bulletslope + ((P_Random()-P_Random())<<5), damage);
+		      bulletslope + (P_SubRandom() << 5), damage);
     }
 }
 
--- a/src/doom/p_spec.c
+++ b/src/doom/p_spec.c
@@ -356,7 +356,7 @@
             }
             else if (h == MAX_ADJOINING_SECTORS + 2)
             {
-                // Fatal overflow: game crashes at 22 textures
+                // Fatal overflow: game crashes at 22 sectors
                 I_Error("Sector with more than 22 adjoining sectors. "
                         "Vanilla will crash here");
             }
--- a/src/doom/r_bsp.c
+++ b/src/doom/r_bsp.c
@@ -77,9 +77,15 @@
     
 } cliprange_t;
 
+// We must expand MAXSEGS to the theoretical limit of the number of solidsegs
+// that can be generated in a scene by the DOOM engine. This was determined by
+// Lee Killough during BOOM development to be a function of the screensize.
+// The simplest thing we can do, other than fix this bug, is to let the game
+// render overage and then bomb out by detecting the overflow after the 
+// fact. -haleyjd
+//#define MAXSEGS 32
+#define MAXSEGS (SCREENWIDTH / 2 + 1)
 
-#define MAXSEGS		32
-
 // newend is one past the last valid seg
 cliprange_t*	newend;
 cliprange_t	solidsegs[MAXSEGS];
@@ -532,6 +538,10 @@
 	R_AddLine (line);
 	line++;
     }
+
+    // check for solidsegs overflow - extremely unsatisfactory!
+    if(newend > &solidsegs[32])
+        I_Error("R_Subsector: solidsegs overflow (vanilla may crash here)\n");
 }
 
 
--- a/src/heretic/am_map.c
+++ b/src/heretic/am_map.c
@@ -20,6 +20,7 @@
 
 #include "doomdef.h"
 #include "deh_str.h"
+#include "i_timer.h"
 #include "i_video.h"
 #include "m_controls.h"
 #include "p_local.h"
@@ -508,9 +509,28 @@
     int rc;
     int key;
     static int bigstate = 0;
+    static int joywait = 0;
 
     key = ev->data1;
     rc = false;
+
+    if (ev->type == ev_joystick && joybautomap >= 0
+        && (ev->data1 & (1 << joybautomap)) != 0 && joywait < I_GetTime())
+    {
+        joywait = I_GetTime() + 5;
+
+        if (!automapactive)
+        {
+            AM_Start ();
+            viewactive = false;
+        }
+        else
+        {
+            bigstate = 0;
+            viewactive = true;
+            AM_Stop ();
+        }
+    }
 
     if (!automapactive)
     {
--- a/src/heretic/m_random.c
+++ b/src/heretic/m_random.c
@@ -68,3 +68,9 @@
     rndindex = prndindex = 0;
 }
 
+// inspired by the same routine in Eternity, thanks haleyjd
+int P_SubRandom (void)
+{
+    int r = P_Random();
+    return r - P_Random();
+}
--- a/src/heretic/m_random.h
+++ b/src/heretic/m_random.h
@@ -30,5 +30,8 @@
 
 extern int rndindex;
 
+// Defined version of P_Random() - P_Random()
+int P_SubRandom (void);
+
 #endif // HERETIC_M_RANDOM_H
 
--- a/src/heretic/p_enemy.c
+++ b/src/heretic/p_enemy.c
@@ -813,7 +813,7 @@
                                    actor->target->y);
     if (actor->target->flags & MF_SHADOW)
     {                           // Target is a ghost
-        actor->angle += (P_Random() - P_Random()) << 21;
+        actor->angle += P_SubRandom() << 21;
     }
 }
 
@@ -840,12 +840,16 @@
 void A_DripBlood(mobj_t * actor)
 {
     mobj_t *mo;
+    int r1,r2;
 
-    mo = P_SpawnMobj(actor->x + ((P_Random() - P_Random()) << 11),
-                     actor->y + ((P_Random() - P_Random()) << 11), actor->z,
+    r1 = P_SubRandom();
+    r2 = P_SubRandom();
+
+    mo = P_SpawnMobj(actor->x + (r1 << 11),
+                     actor->y + (r2 << 11), actor->z,
                      MT_BLOOD);
-    mo->momx = (P_Random() - P_Random()) << 10;
-    mo->momy = (P_Random() - P_Random()) << 10;
+    mo->momx = P_SubRandom() << 10;
+    mo->momy = P_SubRandom() << 10;
     mo->flags2 |= MF2_LOGRAV;
 }
 
@@ -889,12 +893,12 @@
     mobj_t *mo;
 
     mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_IMPCHUNK1);
-    mo->momx = (P_Random() - P_Random()) << 10;
-    mo->momy = (P_Random() - P_Random()) << 10;
+    mo->momx = P_SubRandom() << 10;
+    mo->momy = P_SubRandom() << 10;
     mo->momz = 9 * FRACUNIT;
     mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_IMPCHUNK2);
-    mo->momx = (P_Random() - P_Random()) << 10;
-    mo->momy = (P_Random() - P_Random()) << 10;
+    mo->momx = P_SubRandom() << 10;
+    mo->momy = P_SubRandom() << 10;
     mo->momz = 9 * FRACUNIT;
     if (actor->special1.i == 666)
     {                           // Extreme death crash
@@ -912,9 +916,13 @@
 {
     if (P_Random() > 64)
     {
-        P_SpawnMobj(actor->x + ((P_Random() - P_Random()) << 10),
-                    actor->y + ((P_Random() - P_Random()) << 10),
-                    actor->z + ((P_Random() - P_Random()) << 10), MT_PUFFY);
+        int r1,r2,r3;
+        r1 = P_SubRandom();
+        r2 = P_SubRandom();
+        r3 = P_SubRandom();
+        P_SpawnMobj(actor->x + (r1 << 10),
+                    actor->y + (r2 << 10),
+                    actor->z + (r3 << 10), MT_PUFFY);
     }
 }
 
@@ -1179,8 +1187,8 @@
         mo = P_SpawnMobj(actor->x, actor->y, actor->z + 20 * FRACUNIT,
                          MT_FEATHER);
         mo->target = actor;
-        mo->momx = (P_Random() - P_Random()) << 8;
-        mo->momy = (P_Random() - P_Random()) << 8;
+        mo->momx = P_SubRandom() << 8;
+        mo->momy = P_SubRandom() << 8;
         mo->momz = FRACUNIT + (P_Random() << 9);
         P_SetMobjState(mo, S_FEATHER1 + (P_Random() & 7));
     }
@@ -1475,8 +1483,8 @@
     for (i = 0; i < 2; i++)
     {
         mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SOR2FXSPARK);
-        mo->momx = (P_Random() - P_Random()) << 9;
-        mo->momy = (P_Random() - P_Random()) << 9;
+        mo->momx = P_SubRandom() << 9;
+        mo->momy = P_SubRandom() << 9;
         mo->momz = FRACUNIT + (P_Random() << 8);
     }
 }
@@ -1755,10 +1763,14 @@
 void A_MntrFloorFire(mobj_t * actor)
 {
     mobj_t *mo;
+    int r1, r2;
 
+    r1 = P_SubRandom();
+    r2 = P_SubRandom();
+
     actor->z = actor->floorz;
-    mo = P_SpawnMobj(actor->x + ((P_Random() - P_Random()) << 10),
-                     actor->y + ((P_Random() - P_Random()) << 10), ONFLOORZ,
+    mo = P_SpawnMobj(actor->x + (r1 << 10),
+                     actor->y + (r2 << 10), ONFLOORZ,
                      MT_MNTRFX3);
     mo->target = actor->target;
     mo->momx = 1;               // Force block checking
@@ -2122,8 +2134,8 @@
     }
     mo = P_SpawnMobj(source->x, source->y,
                      source->z + (source->height >> 1), type);
-    mo->momx = (P_Random() - P_Random()) << 8;
-    mo->momy = (P_Random() - P_Random()) << 8;
+    mo->momx = P_SubRandom() << 8;
+    mo->momy = P_SubRandom() << 8;
     mo->momz = FRACUNIT * 5 + (P_Random() << 10);
     mo->flags |= MF_DROPPED;
     mo->health = special;
@@ -2233,8 +2245,8 @@
         goo = P_SpawnMobj(actor->x, actor->y,
                           actor->z + 48 * FRACUNIT, MT_PODGOO);
         goo->target = actor;
-        goo->momx = (P_Random() - P_Random()) << 9;
-        goo->momy = (P_Random() - P_Random()) << 9;
+        goo->momx = P_SubRandom() << 9;
+        goo->momy = P_SubRandom() << 9;
         goo->momz = FRACUNIT / 2 + (P_Random() << 9);
     }
 }
@@ -2404,8 +2416,10 @@
 void A_SpawnTeleGlitter(mobj_t * actor)
 {
     mobj_t *mo;
+    int r;
 
-    mo = P_SpawnMobj(actor->x + ((P_Random() & 31) - 16) * FRACUNIT,
+    r = P_Random();
+    mo = P_SpawnMobj(actor->x + ((r & 31) - 16) * FRACUNIT,
                      actor->y + ((P_Random() & 31) - 16) * FRACUNIT,
                      actor->subsector->sector->floorheight, MT_TELEGLITTER);
     mo->momz = FRACUNIT / 4;
@@ -2420,8 +2434,10 @@
 void A_SpawnTeleGlitter2(mobj_t * actor)
 {
     mobj_t *mo;
+    int r;
 
-    mo = P_SpawnMobj(actor->x + ((P_Random() & 31) - 16) * FRACUNIT,
+    r = P_Random();
+    mo = P_SpawnMobj(actor->x + ((r & 31) - 16) * FRACUNIT,
                      actor->y + ((P_Random() & 31) - 16) * FRACUNIT,
                      actor->subsector->sector->floorheight, MT_TELEGLITTER2);
     mo->momz = FRACUNIT / 4;
@@ -2560,8 +2576,8 @@
     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 48 * FRACUNIT,
                      MT_BLOODYSKULL);
     //mo->target = actor;
-    mo->momx = (P_Random() - P_Random()) << 9;
-    mo->momy = (P_Random() - P_Random()) << 9;
+    mo->momx = P_SubRandom() << 9;
+    mo->momy = P_SubRandom() << 9;
     mo->momz = FRACUNIT * 2 + (P_Random() << 6);
     // Attach player mobj to bloody skull
     player = actor->player;
--- a/src/heretic/p_inter.c
+++ b/src/heretic/p_inter.c
@@ -988,9 +988,9 @@
 {
     int randVal;
 
-    target->angle += (P_Random() - P_Random()) << 20;
-    target->momx += (P_Random() - P_Random()) << 10;
-    target->momy += (P_Random() - P_Random()) << 10;
+    target->angle += P_SubRandom() << 20;
+    target->momx += P_SubRandom() << 10;
+    target->momy += P_SubRandom() << 10;
     if (leveltime & 16 && !(target->flags2 & MF2_BOSS))
     {
         randVal = P_Random();
--- a/src/heretic/p_map.c
+++ b/src/heretic/p_map.c
@@ -1656,8 +1656,8 @@
         // spray blood in a random direction
         mo = P_SpawnMobj(thing->x, thing->y, thing->z + thing->height / 2,
                          MT_BLOOD);
-        mo->momx = (P_Random() - P_Random()) << 12;
-        mo->momy = (P_Random() - P_Random()) << 12;
+        mo->momx = P_SubRandom() << 12;
+        mo->momy = P_SubRandom() << 12;
     }
 
     return true;                // keep checking (crush other things)   
--- a/src/heretic/p_mobj.c
+++ b/src/heretic/p_mobj.c
@@ -1192,7 +1192,7 @@
 {
     mobj_t *puff;
 
-    z += ((P_Random() - P_Random()) << 10);
+    z += (P_SubRandom() << 10);
     puff = P_SpawnMobj(x, y, z, PuffType);
     if (puff->info->attacksound)
     {
@@ -1225,7 +1225,7 @@
 {
 	mobj_t  *th;
 	
-	z += ((P_Random()-P_Random())<<10);
+	z += (P_SubRandom()<<10);
 	th = P_SpawnMobj (x,y,z, MT_BLOOD);
 	th->momz = FRACUNIT*2;
 	th->tics -= P_Random()&3;
@@ -1249,8 +1249,8 @@
 
     mo = P_SpawnMobj(x, y, z, MT_BLOODSPLATTER);
     mo->target = originator;
-    mo->momx = (P_Random() - P_Random()) << 9;
-    mo->momy = (P_Random() - P_Random()) << 9;
+    mo->momx = P_SubRandom() << 9;
+    mo->momy = P_SubRandom() << 9;
     mo->momz = FRACUNIT * 2;
 }
 
@@ -1265,9 +1265,9 @@
     mobj_t *th;
     fixed_t x, y, z;
 
-    x = mo->x + ((P_Random() - P_Random()) << 12);
-    y = mo->y + ((P_Random() - P_Random()) << 12);
-    z = mo->z + ((P_Random() - P_Random()) << 12);
+    x = mo->x + (P_SubRandom() << 12);
+    y = mo->y + (P_SubRandom() << 12);
+    z = mo->z + (P_SubRandom() << 12);
     th = P_SpawnMobj(x, y, z, MT_BLOOD);
     th->flags |= MF_NOGRAVITY;
     th->momx = mo->momx >> 1;
@@ -1317,8 +1317,8 @@
             P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASHBASE);
             mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASH);
             mo->target = thing;
-            mo->momx = (P_Random() - P_Random()) << 8;
-            mo->momy = (P_Random() - P_Random()) << 8;
+            mo->momx = P_SubRandom() << 8;
+            mo->momy = P_SubRandom() << 8;
             mo->momz = 2 * FRACUNIT + (P_Random() << 8);
             S_StartSound(mo, sfx_gloop);
             return (FLOOR_WATER);
@@ -1332,8 +1332,8 @@
             P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SLUDGESPLASH);
             mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SLUDGECHUNK);
             mo->target = thing;
-            mo->momx = (P_Random() - P_Random()) << 8;
-            mo->momy = (P_Random() - P_Random()) << 8;
+            mo->momx = P_SubRandom() << 8;
+            mo->momy = P_SubRandom() << 8;
             mo->momz = FRACUNIT + (P_Random() << 8);
             return (FLOOR_SLUDGE);
     }
@@ -1414,7 +1414,7 @@
     an = R_PointToAngle2(source->x, source->y, dest->x, dest->y);
     if (dest->flags & MF_SHADOW)
     {                           // Invisible target
-        an += (P_Random() - P_Random()) << 21;
+        an += P_SubRandom() << 21;
     }
     th->angle = an;
     an >>= ANGLETOFINESHIFT;
--- a/src/heretic/p_pspr.c
+++ b/src/heretic/p_pspr.c
@@ -896,7 +896,7 @@
 
     damage = 5 + (P_Random() & 15);
     angle = player->mo->angle;
-    angle += (P_Random() - P_Random()) << 18;
+    angle += P_SubRandom() << 18;
     slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
     PuffType = MT_STAFFPUFF;
     P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
@@ -925,7 +925,7 @@
     // P_inter.c:P_DamageMobj() handles target momentums
     damage = 18 + (P_Random() & 63);
     angle = player->mo->angle;
-    angle += (P_Random() - P_Random()) << 18;
+    angle += P_SubRandom() << 18;
     slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
     PuffType = MT_STAFFPUFF2;
     P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
@@ -959,7 +959,7 @@
     angle = mo->angle;
     if (player->refire)
     {
-        angle += (P_Random() - P_Random()) << 18;
+        angle += P_SubRandom() << 18;
     }
     PuffType = MT_BLASTERPUFF1;
     P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage);
@@ -1005,7 +1005,7 @@
     angle = mo->angle;
     if (player->refire)
     {
-        angle += (P_Random() - P_Random()) << 18;
+        angle += P_SubRandom() << 18;
     }
     PuffType = MT_GOLDWANDPUFF1;
     P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage);
@@ -1397,8 +1397,8 @@
     if (P_Random() > 50)
     {
         spark = P_SpawnMobj(bolt->x, bolt->y, bolt->z, MT_CRBOWFX4);
-        spark->x += (P_Random() - P_Random()) << 10;
-        spark->y += (P_Random() - P_Random()) << 10;
+        spark->x += P_SubRandom() << 10;
+        spark->y += P_SubRandom() << 10;
     }
 }
 
@@ -1696,8 +1696,8 @@
     }
     pmo = player->mo;
     angle = pmo->angle;
-    x = pmo->x + ((P_Random() - P_Random()) << 9);
-    y = pmo->y + ((P_Random() - P_Random()) << 9);
+    x = pmo->x + (P_SubRandom() << 9);
+    y = pmo->y + (P_SubRandom() << 9);
     z = pmo->z + 26 * FRACUNIT + ((player->lookdir) << FRACBITS) / 173;
     if (pmo->flags2 & MF2_FEETARECLIPPED)
     {
@@ -1773,7 +1773,7 @@
     {
         damage = HITDICE(2);
         dist = 4 * MELEERANGE;
-        angle += (P_Random() - P_Random()) << 17;
+        angle += P_SubRandom() << 17;
         PuffType = MT_GAUNTLETPUFF2;
     }
     else
@@ -1780,7 +1780,7 @@
     {
         damage = HITDICE(2);
         dist = MELEERANGE + 1;
-        angle += (P_Random() - P_Random()) << 18;
+        angle += P_SubRandom() << 18;
         PuffType = MT_GAUNTLETPUFF1;
     }
     slope = P_AimLineAttack(player->mo, angle, dist);
--- a/src/heretic/p_user.c
+++ b/src/heretic/p_user.c
@@ -421,7 +421,7 @@
     pmo = player->mo;
     if (!(pmo->momx + pmo->momy) && P_Random() < 160)
     {                           // Twitch view angle
-        pmo->angle += (P_Random() - P_Random()) << 19;
+        pmo->angle += P_SubRandom() << 19;
     }
     if ((pmo->z <= pmo->floorz) && (P_Random() < 32))
     {                           // Jump and noise
--- a/src/heretic/r_bsp.c
+++ b/src/heretic/r_bsp.c
@@ -58,7 +58,14 @@
     int first, last;
 } cliprange_t;
 
-#define	MAXSEGS	32
+// We must expand MAXSEGS to the theoretical limit of the number of solidsegs
+// that can be generated in a scene by the DOOM engine. This was determined by
+// Lee Killough during BOOM development to be a function of the screensize.
+// The simplest thing we can do, other than fix this bug, is to let the game
+// render overage and then bomb out by detecting the overflow after the 
+// fact. -haleyjd
+//#define MAXSEGS 32
+#define MAXSEGS (SCREENWIDTH / 2 + 1)
 
 cliprange_t solidsegs[MAXSEGS], *newend;        // newend is one past the last valid seg
 
@@ -437,6 +444,10 @@
         R_AddLine(line);
         line++;
     }
+
+    // check for solidsegs overflow - extremely unsatisfactory!
+    if(newend > &solidsegs[32])
+        I_Error("R_Subsector: solidsegs overflow (vanilla may crash here)\n");
 }
 
 
--- a/src/hexen/a_action.c
+++ b/src/hexen/a_action.c
@@ -126,11 +126,14 @@
 void A_DripBlood(mobj_t *actor)
 {
 	mobj_t *mo;
+    int r;
 
-	mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<11),
-		actor->y+((P_Random()-P_Random())<<11), actor->z, MT_BLOOD);
-	mo->momx = (P_Random()-P_Random())<<10;
-	mo->momy = (P_Random()-P_Random())<<10;
+    r = P_SubRandom();
+
+	mo = P_SpawnMobj(actor->x+(r<<11),
+		actor->y+(P_SubRandom()<<11), actor->z, MT_BLOOD);
+	mo->momx = P_SubRandom()<<10;
+	mo->momy = P_SubRandom()<<10;
 	mo->flags2 |= MF2_LOGRAV;
 }
 */
@@ -153,8 +156,8 @@
         if (mo)
         {
             mo->momz = ((P_Random() & 7) + 5) * (3 * FRACUNIT / 4);
-            mo->momx = (P_Random() - P_Random()) << (FRACBITS - 6);
-            mo->momy = (P_Random() - P_Random()) << (FRACBITS - 6);
+            mo->momx = P_SubRandom() << (FRACBITS - 6);
+            mo->momy = P_SubRandom() << (FRACBITS - 6);
         }
     }
     S_StartSound(mo, SFX_POTTERY_EXPLODE);
@@ -265,8 +268,8 @@
         if (mo)
         {
             mo->momz = ((P_Random() & 7) + 5) * (3 * FRACUNIT / 4);
-            mo->momx = (P_Random() - P_Random()) << (FRACBITS - 6);
-            mo->momy = (P_Random() - P_Random()) << (FRACBITS - 6);
+            mo->momx = P_SubRandom() << (FRACBITS - 6);
+            mo->momy = P_SubRandom() << (FRACBITS - 6);
         }
     }
     // Spawn a skull
@@ -275,8 +278,8 @@
     if (mo)
     {
         mo->momz = ((P_Random() & 7) + 5) * (3 * FRACUNIT / 4);
-        mo->momx = (P_Random() - P_Random()) << (FRACBITS - 6);
-        mo->momy = (P_Random() - P_Random()) << (FRACBITS - 6);
+        mo->momx = P_SubRandom() << (FRACBITS - 6);
+        mo->momy = P_SubRandom() << (FRACBITS - 6);
         S_StartSound(mo, SFX_FIRED_DEATH);
     }
     P_RemoveMobj(actor);
@@ -300,8 +303,8 @@
         // see ISO-IEC 9899-1999, [6.5.2.2.10]
         mobjtype_t type = MT_LEAF1 + (P_Random() & 1);
         fixed_t z = actor->z + (P_Random() << 14);
-        fixed_t y = actor->y + ((P_Random() - P_Random()) << 14);
-        fixed_t x = actor->x + ((P_Random() - P_Random()) << 14);
+        fixed_t y = actor->y + (P_SubRandom() << 14);
+        fixed_t x = actor->x + (P_SubRandom() << 14);
 
         mo = P_SpawnMobj(x, y, z, type);
         if (mo)
@@ -1040,8 +1043,8 @@
     angle = P_Random() << 5;    // <<24 >>19
     x = actor->x + FixedMul(radius, finecosine[angle]);
     y = actor->y + FixedMul(radius, finesine[angle]);
-//      x = actor->x + ((P_Random()-P_Random())%radius)<<FRACBITS;
-//      y = actor->y + ((P_Random()-P_Random()<<FRACBITS)%radius);
+//      x = actor->x + (P_SubRandom()%radius)<<FRACBITS;
+//      y = actor->y + ((P_SubRandom()<<FRACBITS)%radius);
     z = actor->z + (P_Random() << 9) + FRACUNIT;
     switch (P_Random() % 6)
     {
@@ -1165,19 +1168,23 @@
 {
     mobj_t *mo;
     int i;
+    int r1,r2,r3;
 
     for (i = 0; i < 10; i++)
     {
-        mo = P_SpawnMobj(actor->x + ((P_Random() - 128) << 12),
-                         actor->y + ((P_Random() - 128) << 12),
-                         actor->z + (P_Random() * actor->height / 256),
+        r1 = P_Random();
+        r2 = P_Random();
+        r3 = P_Random();
+        mo = P_SpawnMobj(actor->x + ((r1 - 128) << 12),
+                         actor->y + ((r2 - 128) << 12),
+                         actor->z + (r3 * actor->height / 256),
                          MT_ZARMORCHUNK);
         P_SetMobjState(mo, mo->info->spawnstate + i);
         if (mo)
         {
             mo->momz = ((P_Random() & 7) + 5) * FRACUNIT;
-            mo->momx = (P_Random() - P_Random()) << (FRACBITS - 6);
-            mo->momy = (P_Random() - P_Random()) << (FRACBITS - 6);
+            mo->momx = P_SubRandom() << (FRACBITS - 6);
+            mo->momy = P_SubRandom() << (FRACBITS - 6);
         }
     }
     if (actor->args[0])
--- a/src/hexen/am_map.c
+++ b/src/hexen/am_map.c
@@ -20,6 +20,7 @@
 #include "doomkeys.h"
 #include "i_video.h"
 #include "i_swap.h"
+#include "i_timer.h"
 #include "m_controls.h"
 #include "m_misc.h"
 #include "p_local.h"
@@ -421,8 +422,32 @@
     int rc;
     int key;
     static int bigstate = 0;
+    static int joywait = 0;
 
     key = ev->data1;
+
+    if (ev->type == ev_joystick && joybautomap >= 0
+        && (ev->data1 & (1 << joybautomap)) != 0 && joywait < I_GetTime())
+    {
+        joywait = I_GetTime() + 5;
+
+        if (!automapactive)
+        {
+            AM_Start ();
+            SB_state = -1;
+            viewactive = false;
+        }
+        else
+        {
+            bigstate = 0;
+            viewactive = true;
+            AM_Stop ();
+            SB_state = -1;
+        }
+
+        return true;
+    }
+
 
     rc = false;
     if (!automapactive)
--- a/src/hexen/m_random.c
+++ b/src/hexen/m_random.c
@@ -72,3 +72,9 @@
     rndindex = prndindex = 0;
 }
 
+// inspired by the same routine in Eternity, thanks haleyjd
+int P_SubRandom (void)
+{
+    int r = P_Random();
+    return r - P_Random();
+}
--- a/src/hexen/m_random.h
+++ b/src/hexen/m_random.h
@@ -30,5 +30,8 @@
 
 extern int rndindex;
 
+// Defined version of P_Random() - P_Random()
+int P_SubRandom (void);
+
 #endif // HEXEN_M_RANDOM_H
 
--- a/src/hexen/p_enemy.c
+++ b/src/hexen/p_enemy.c
@@ -820,7 +820,7 @@
                                    actor->target->y);
     if (actor->target->flags & MF_SHADOW)
     {                           // Target is a ghost
-        actor->angle += (P_Random() - P_Random()) << 21;
+        actor->angle += P_SubRandom() << 21;
     }
 }
 
@@ -1481,10 +1481,13 @@
 void A_MntrFloorFire(mobj_t * actor)
 {
     mobj_t *mo;
+    int r;
 
+    r = P_SubRandom();
+
     actor->z = actor->floorz;
-    mo = P_SpawnMobj(actor->x + ((P_Random() - P_Random()) << 10),
-                     actor->y + ((P_Random() - P_Random()) << 10), ONFLOORZ,
+    mo = P_SpawnMobj(actor->x + (r << 10),
+                     actor->y + (P_SubRandom() << 10), ONFLOORZ,
                      MT_MNTRFX3);
     mo->target = actor->target;
     mo->momx = 1;               // Force block checking
@@ -1597,8 +1600,8 @@
 	}
 	mo = P_SpawnMobj(source->x, source->y,
 		source->z+(source->height>>1), type);
-	mo->momx = (P_Random()-P_Random())<<8;
-	mo->momy = (P_Random()-P_Random())<<8;
+	mo->momx = P_SubRandom()<<8;
+	mo->momy = P_SubRandom()<<8;
 	mo->momz = FRACUNIT*5+(P_Random()<<10);
 	mo->flags2 |= MF2_DROPPED;
 	mo->health = special;
@@ -1773,8 +1776,8 @@
     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 48 * FRACUNIT,
                      MT_BLOODYSKULL);
     //mo->target = actor;
-    mo->momx = (P_Random() - P_Random()) << 9;
-    mo->momy = (P_Random() - P_Random()) << 9;
+    mo->momx = P_SubRandom() << 9;
+    mo->momy = P_SubRandom() << 9;
     mo->momz = FRACUNIT * 2 + (P_Random() << 6);
     // Attach player mobj to bloody skull
     player = actor->player;
@@ -2408,8 +2411,9 @@
 void A_SerpentSpawnGibs(mobj_t * actor)
 {
     mobj_t *mo;
+    int r = P_Random();
 
-    mo = P_SpawnMobj(actor->x + ((P_Random() - 128) << 12),
+    mo = P_SpawnMobj(actor->x + ((r - 128) << 12),
                      actor->y + ((P_Random() - 128) << 12),
                      actor->floorz + FRACUNIT, MT_SERPENT_GIB1);
     if (mo)
@@ -2418,7 +2422,8 @@
         mo->momy = (P_Random() - 128) << 6;
         mo->floorclip = 6 * FRACUNIT;
     }
-    mo = P_SpawnMobj(actor->x + ((P_Random() - 128) << 12),
+    r = P_Random();
+    mo = P_SpawnMobj(actor->x + ((r - 128) << 12),
                      actor->y + ((P_Random() - 128) << 12),
                      actor->floorz + FRACUNIT, MT_SERPENT_GIB2);
     if (mo)
@@ -2427,7 +2432,8 @@
         mo->momy = (P_Random() - 128) << 6;
         mo->floorclip = 6 * FRACUNIT;
     }
-    mo = P_SpawnMobj(actor->x + ((P_Random() - 128) << 12),
+    r = P_Random();
+    mo = P_SpawnMobj(actor->x + ((r - 128) << 12),
                      actor->y + ((P_Random() - 128) << 12),
                      actor->floorz + FRACUNIT, MT_SERPENT_GIB3);
     if (mo)
@@ -2779,6 +2785,7 @@
 void A_BishopPainBlur(mobj_t * actor)
 {
     mobj_t *mo;
+    int r1,r2,r3;
 
     if (P_Random() < 64)
     {
@@ -2785,9 +2792,14 @@
         P_SetMobjState(actor, S_BISHOP_BLUR1);
         return;
     }
-    mo = P_SpawnMobj(actor->x + ((P_Random() - P_Random()) << 12), actor->y
-                     + ((P_Random() - P_Random()) << 12),
-                     actor->z + ((P_Random() - P_Random()) << 11),
+
+    r1 = P_SubRandom();
+    r2 = P_SubRandom();
+    r3 = P_SubRandom();
+
+    mo = P_SpawnMobj(actor->x + (r1 << 12), actor->y
+                     + (r2 << 12),
+                     actor->z + (r3 << 11),
                      MT_BISHOPPAINBLUR);
     if (mo)
     {
@@ -3027,14 +3039,18 @@
 {
     mobj_t *mo;
     int i;
+    int r1,r2,r3;
     int delay;
 
     delay = 16 + (P_Random() >> 3);
     for (i = 1 + (P_Random() & 3); i; i--)
     {
-        mo = P_SpawnMobj(actor->x + ((P_Random() - 128) << 14),
-                         actor->y + ((P_Random() - 128) << 14),
-                         actor->z + ((P_Random() - 128) << 12),
+        r1 = P_Random();
+        r2 = P_Random();
+        r3 = P_Random();
+        mo = P_SpawnMobj(actor->x + ((r1 - 128) << 14),
+                         actor->y + ((r2 - 128) << 14),
+                         actor->z + ((r3 - 128) << 12),
                          MT_DRAGON_FX2);
         if (mo)
         {
@@ -4764,7 +4780,8 @@
 
 void A_FreezeDeath(mobj_t * actor)
 {
-    actor->tics = 75 + P_Random() + P_Random();
+    int r = P_Random();
+    actor->tics = 75 + r + P_Random();
     actor->flags |= MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD;
     actor->flags2 |= MF2_PUSHABLE | MF2_TELESTOMP | MF2_PASSMOBJ | MF2_SLIDE;
     actor->height <<= 2;
@@ -4832,6 +4849,7 @@
 void A_FreezeDeathChunks(mobj_t * actor)
 {
     int i;
+    int r1,r2,r3;
     mobj_t *mo;
 
     if (actor->momx || actor->momy || actor->momz)
@@ -4843,35 +4861,41 @@
 
     for (i = 12 + (P_Random() & 15); i >= 0; i--)
     {
+        r1 = P_Random();
+        r2 = P_Random();
+        r3 = P_Random();
         mo = P_SpawnMobj(actor->x +
-                         (((P_Random() - 128) * actor->radius) >> 7),
+                         (((r1 - 128) * actor->radius) >> 7),
                          actor->y +
-                         (((P_Random() - 128) * actor->radius) >> 7),
-                         actor->z + (P_Random() * actor->height / 255),
+                         (((r2 - 128) * actor->radius) >> 7),
+                         actor->z + (r3 * actor->height / 255),
                          MT_ICECHUNK);
         P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3));
         if (mo)
         {
             mo->momz = FixedDiv(mo->z - actor->z, actor->height) << 2;
-            mo->momx = (P_Random() - P_Random()) << (FRACBITS - 7);
-            mo->momy = (P_Random() - P_Random()) << (FRACBITS - 7);
+            mo->momx = P_SubRandom() << (FRACBITS - 7);
+            mo->momy = P_SubRandom() << (FRACBITS - 7);
             A_IceSetTics(mo);   // set a random tic wait
         }
     }
     for (i = 12 + (P_Random() & 15); i >= 0; i--)
     {
+        r1 = P_Random();
+        r2 = P_Random();
+        r3 = P_Random();
         mo = P_SpawnMobj(actor->x +
-                         (((P_Random() - 128) * actor->radius) >> 7),
+                         (((r1 - 128) * actor->radius) >> 7),
                          actor->y +
-                         (((P_Random() - 128) * actor->radius) >> 7),
-                         actor->z + (P_Random() * actor->height / 255),
+                         (((r2 - 128) * actor->radius) >> 7),
+                         actor->z + (r3 * actor->height / 255),
                          MT_ICECHUNK);
         P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3));
         if (mo)
         {
             mo->momz = FixedDiv(mo->z - actor->z, actor->height) << 2;
-            mo->momx = (P_Random() - P_Random()) << (FRACBITS - 7);
-            mo->momy = (P_Random() - P_Random()) << (FRACBITS - 7);
+            mo->momx = P_SubRandom() << (FRACBITS - 7);
+            mo->momy = P_SubRandom() << (FRACBITS - 7);
             A_IceSetTics(mo);   // set a random tic wait
         }
     }
@@ -4881,8 +4905,8 @@
                          MT_ICECHUNK);
         P_SetMobjState(mo, S_ICECHUNK_HEAD);
         mo->momz = FixedDiv(mo->z - actor->z, actor->height) << 2;
-        mo->momx = (P_Random() - P_Random()) << (FRACBITS - 7);
-        mo->momy = (P_Random() - P_Random()) << (FRACBITS - 7);
+        mo->momx = P_SubRandom() << (FRACBITS - 7);
+        mo->momy = P_SubRandom() << (FRACBITS - 7);
         mo->flags2 |= MF2_ICEDAMAGE;    // used to force blue palette
         mo->flags2 &= ~MF2_FLOORCLIP;
         mo->player = actor->player;
--- a/src/hexen/p_map.c
+++ b/src/hexen/p_map.c
@@ -2277,8 +2277,8 @@
         {
             mo = P_SpawnMobj(thing->x, thing->y, thing->z + thing->height / 2,
                              MT_BLOOD);
-            mo->momx = (P_Random() - P_Random()) << 12;
-            mo->momy = (P_Random() - P_Random()) << 12;
+            mo->momx = P_SubRandom() << 12;
+            mo->momy = P_SubRandom() << 12;
         }
     }
 
--- a/src/hexen/p_mobj.c
+++ b/src/hexen/p_mobj.c
@@ -1755,7 +1755,7 @@
 {
     mobj_t *puff;
 
-    z += ((P_Random() - P_Random()) << 10);
+    z += (P_SubRandom() << 10);
     puff = P_SpawnMobj(x, y, z, PuffType);
     if (linetarget && puff->info->seesound)
     {                           // Hit thing sound
@@ -1792,7 +1792,7 @@
 {
 	mobj_t	*th;
 	
-	z += ((P_Random()-P_Random())<<10);
+	z += (P_SubRandom()<<10);
 	th = P_SpawnMobj (x,y,z, MT_BLOOD);
 	th->momz = FRACUNIT*2;
 	th->tics -= P_Random()&3;
@@ -1816,8 +1816,8 @@
 
     mo = P_SpawnMobj(x, y, z, MT_BLOODSPLATTER);
     mo->target = originator;
-    mo->momx = (P_Random() - P_Random()) << 10;
-    mo->momy = (P_Random() - P_Random()) << 10;
+    mo->momx = P_SubRandom() << 10;
+    mo->momy = P_SubRandom() << 10;
     mo->momz = 3 * FRACUNIT;
 }
 
@@ -1847,9 +1847,9 @@
     mobj_t *th;
     fixed_t x, y, z;
 
-    x = mo->x + ((P_Random() - P_Random()) << 12);
-    y = mo->y + ((P_Random() - P_Random()) << 12);
-    z = mo->z + ((P_Random() - P_Random()) << 12);
+    x = mo->x + (P_SubRandom() << 12);
+    y = mo->y + (P_SubRandom() << 12);
+    z = mo->z + (P_SubRandom() << 12);
     th = P_SpawnMobj(x, y, z, MT_BLOOD);
 //      th->flags |= MF_NOGRAVITY;
     th->momx = mo->momx >> 1;
@@ -1935,8 +1935,8 @@
             {
                 mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASH);
                 mo->target = thing;
-                mo->momx = (P_Random() - P_Random()) << 8;
-                mo->momy = (P_Random() - P_Random()) << 8;
+                mo->momx = P_SubRandom() << 8;
+                mo->momy = P_SubRandom() << 8;
                 mo->momz = 2 * FRACUNIT + (P_Random() << 8);
                 mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASHBASE);
                 if (thing->player)
@@ -1978,8 +1978,8 @@
                 mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ,
                                  MT_SLUDGECHUNK);
                 mo->target = thing;
-                mo->momx = (P_Random() - P_Random()) << 8;
-                mo->momy = (P_Random() - P_Random()) << 8;
+                mo->momx = P_SubRandom() << 8;
+                mo->momy = P_SubRandom() << 8;
                 mo->momz = FRACUNIT + (P_Random() << 8);
                 mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ,
                                  MT_SLUDGESPLASH);
@@ -2066,7 +2066,7 @@
     an = R_PointToAngle2(source->x, source->y, dest->x, dest->y);
     if (dest->flags & MF_SHADOW)
     {                           // Invisible target
-        an += (P_Random() - P_Random()) << 21;
+        an += P_SubRandom() << 21;
     }
     th->angle = an;
     an >>= ANGLETOFINESHIFT;
@@ -2108,7 +2108,7 @@
     an = R_PointToAngle2(source->x, source->y, dest->x, dest->y);
     if (dest->flags & MF_SHADOW)
     {                           // Invisible target
-        an += (P_Random() - P_Random()) << 21;
+        an += P_SubRandom() << 21;
     }
     th->angle = an;
     an >>= ANGLETOFINESHIFT;
@@ -2449,7 +2449,7 @@
     an = R_PointToAngle2(x, y, dest->x, dest->y);
     if (dest->flags & MF_SHADOW)
     {                           // Invisible target
-        an += (P_Random() - P_Random()) << 21;
+        an += P_SubRandom() << 21;
     }
     th->angle = an;
     an >>= ANGLETOFINESHIFT;
--- a/src/hexen/p_pspr.c
+++ b/src/hexen/p_pspr.c
@@ -861,12 +861,16 @@
 void A_FSwordFlames(mobj_t * actor)
 {
     int i;
+    int r1,r2,r3;
 
     for (i = 1 + (P_Random() & 3); i; i--)
     {
-        P_SpawnMobj(actor->x + ((P_Random() - 128) << 12), actor->y
-                    + ((P_Random() - 128) << 12),
-                    actor->z + ((P_Random() - 128) << 11), MT_FSWORD_FLAME);
+        r1 = P_Random();
+        r2 = P_Random();
+        r3 = P_Random();
+        P_SpawnMobj(actor->x + ((r1 - 128) << 12), actor->y
+                    + ((r2 - 128) << 12),
+                    actor->z + ((r3 - 128) << 11), MT_FSWORD_FLAME);
     }
 }
 
@@ -979,6 +983,7 @@
 {
     mobj_t *mo;
     fixed_t deltaZ;
+    int r1,r2;
 
     A_LightningClip(actor);
 
@@ -996,8 +1001,10 @@
     {
         deltaZ = -10 * FRACUNIT;
     }
-    mo = P_SpawnMobj(actor->x + ((P_Random() - 128) * actor->radius / 256),
-                     actor->y + ((P_Random() - 128) * actor->radius / 256),
+    r1 = P_Random();
+    r2 = P_Random();
+    mo = P_SpawnMobj(actor->x + ((r1 - 128) * actor->radius / 256),
+                     actor->y + ((r2 - 128) * actor->radius / 256),
                      actor->z + deltaZ, MT_LIGHTNING_ZAP);
     if (mo)
     {
@@ -1388,8 +1395,10 @@
     int slope;
     int i;
     int useMana;
+    int r;
 
-    damage = 40 + (P_Random() & 15) + (P_Random() & 7);
+    r = P_Random();
+    damage = 40 + (r & 15) + (P_Random() & 7);
     power = 0;
     if (player->mana[MANA_1] > 0)
     {
@@ -1741,7 +1750,7 @@
 	angle = pmo->angle;
 	if(player->refire)
 	{
-		angle += (P_Random()-P_Random())<<17;
+		angle += P_SubRandom()<<17;
 	}
 	P_AimLineAttack(pmo, angle, CFLAMERANGE); // Correctly set linetarget
 	if(!linetarget)
@@ -1851,6 +1860,7 @@
 {
     int j;
     int i;
+    int r;
     mobj_t *mo;
     mobj_t *tail, *next;
 
@@ -1873,8 +1883,9 @@
                 mo->special2.i = (32 + (P_Random() & 7)) << 16;   // lower-left
                 break;
             case 3:
+                r = P_Random();
                 mo->special2.i =
-                    ((32 + (P_Random() & 7)) << 16) + 32 + (P_Random() & 7);
+                    ((32 + (r & 7)) << 16) + 32 + (P_Random() & 7);
                 break;
         }
         mo->z = actor->z;
--- a/src/hexen/r_bsp.c
+++ b/src/hexen/r_bsp.c
@@ -59,7 +59,14 @@
     int first, last;
 } cliprange_t;
 
-#define MAXSEGS 32
+// We must expand MAXSEGS to the theoretical limit of the number of solidsegs
+// that can be generated in a scene by the DOOM engine. This was determined by
+// Lee Killough during BOOM development to be a function of the screensize.
+// The simplest thing we can do, other than fix this bug, is to let the game
+// render overage and then bomb out by detecting the overflow after the 
+// fact. -haleyjd
+//#define MAXSEGS 32
+#define MAXSEGS (SCREENWIDTH / 2 + 1)
 
 cliprange_t solidsegs[MAXSEGS], *newend;        // newend is one past the last valid seg
 
@@ -458,6 +465,10 @@
         R_AddLine(line);
         line++;
     }
+
+    // check for solidsegs overflow - extremely unsatisfactory!
+    if(newend > &solidsegs[32])
+        I_Error("R_Subsector: solidsegs overflow (vanilla may crash here)\n");
 }
 
 
--- a/src/i_sdlmusic.c
+++ b/src/i_sdlmusic.c
@@ -901,7 +901,7 @@
     int i;
 
     //!
-    // @arg <output filename>
+    // @arg <filename>
     //
     // Read all MIDI files from loaded WAD files, dump an example substitution
     // music config file to the specified filename and quit.
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@ -341,6 +341,8 @@
 {
     allocated_sound_t *snd = channels_playing[channel];
 
+    Mix_HaltChannel(channel);
+
     if (snd == NULL)
     {
         return;
@@ -983,8 +985,6 @@
     {
         return;
     }
-
-    Mix_HaltChannel(handle);
 
     // Sound data is no longer needed; release the
     // sound data being used for this channel
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -818,12 +818,13 @@
     int i;
 
     //!
+    // @category video
     // @vanilla
     //
     // Disable blitting the screen.
     //
 
-    noblit = M_CheckParm ("-noblit"); 
+    noblit = M_CheckParm ("-noblit");
 
     //!
     // @category video 
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -1060,6 +1060,12 @@
     CONFIG_VARIABLE_INT(joyb_menu_activate),
 
     //!
+    // Joystick virtual button to toggle the automap.
+    //
+
+    CONFIG_VARIABLE_INT(joyb_toggle_automap),
+
+    //!
     // Joystick virtual button that cycles to the previous weapon.
     //
 
--- a/src/m_controls.c
+++ b/src/m_controls.c
@@ -192,6 +192,7 @@
 int joybnextweapon = -1;
 
 int joybmenu = -1;
+int joybautomap = -1;
 
 // Control whether if a mouse button is double clicked, it acts like 
 // "use" has been pressed
@@ -225,6 +226,7 @@
     M_BindIntVariable("joyb_speed",         &joybspeed);
 
     M_BindIntVariable("joyb_menu_activate", &joybmenu);
+    M_BindIntVariable("joyb_toggle_automap", &joybautomap);
 
     // Extra controls that are not in the Vanilla versions:
 
--- a/src/m_controls.h
+++ b/src/m_controls.h
@@ -150,6 +150,7 @@
 extern int joybnextweapon;
 
 extern int joybmenu;
+extern int joybautomap;
 
 extern int dclick_use;
 
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -1345,6 +1345,7 @@
     querydata.gamemission = sv_gamemission;
 
     //!
+    // @category net
     // @arg <name>
     //
     // When starting a network server, specify a name for the server.
@@ -1785,10 +1786,10 @@
 void NET_SV_RegisterWithMaster(void)
 {
     //!
+    // @category net
+    //
     // When running a server, don't register with the global master server.
     // Implies -server.
-    //
-    // @category net
     //
 
     if (!M_CheckParm("-privateserver"))
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@ -127,22 +127,33 @@
 // Always loaded before others, to get a known starting configuration.
 static const joystick_config_t empty_defaults[] =
 {
-    {"joystick_x_axis",        -1},
-    {"joystick_x_invert",      0},
-    {"joystick_y_axis",        -1},
-    {"joystick_y_invert",      0},
-    {"joystick_strafe_axis",   -1},
-    {"joystick_strafe_invert", 0},
-    {"joyb_fire",              -1},
-    {"joyb_use",               -1},
-    {"joyb_strafe",            -1},
-    {"joyb_speed",             -1},
-    {"joyb_strafeleft",        -1},
-    {"joyb_straferight",       -1},
-    {"joyb_prevweapon",        -1},
-    {"joyb_nextweapon",        -1},
-    {"joyb_jump",              -1},
-    {"joyb_menu_activate",     -1},
+    {"joystick_x_axis",            -1},
+    {"joystick_x_invert",          0},
+    {"joystick_y_axis",            -1},
+    {"joystick_y_invert",          0},
+    {"joystick_strafe_axis",       -1},
+    {"joystick_strafe_invert",     0},
+    {"joyb_fire",                  -1},
+    {"joyb_use",                   -1},
+    {"joyb_strafe",                -1},
+    {"joyb_speed",                 -1},
+    {"joyb_strafeleft",            -1},
+    {"joyb_straferight",           -1},
+    {"joyb_prevweapon",            -1},
+    {"joyb_nextweapon",            -1},
+    {"joyb_jump",                  -1},
+    {"joyb_menu_activate",         -1},
+    {"joyb_toggle_automap",        -1},
+    {"joystick_physical_button0",  0},
+    {"joystick_physical_button1",  1},
+    {"joystick_physical_button2",  2},
+    {"joystick_physical_button3",  3},
+    {"joystick_physical_button4",  4},
+    {"joystick_physical_button5",  5},
+    {"joystick_physical_button6",  6},
+    {"joystick_physical_button7",  7},
+    {"joystick_physical_button8",  8},
+    {"joystick_physical_button9",  9},
     {NULL, 0},
 };
 
@@ -315,7 +326,39 @@
     {NULL, 0},
 };
 
+// http://www.8bitdo.com/nes30pro/
+static const joystick_config_t nes30_pro_controller[] =
+{
+    {"joystick_x_axis",        CREATE_HAT_AXIS(0, HAT_AXIS_HORIZONTAL)},
+    {"joystick_y_axis",        CREATE_HAT_AXIS(0, HAT_AXIS_VERTICAL)},
+    {"joyb_fire",              4},  // Y
+    {"joyb_speed",             1},  // B
+    {"joyb_jump",              2},  // X
+    {"joyb_use",               0},  // A
+    {"joyb_strafeleft",        8},  // L1
+    {"joyb_straferight",       9}, // R1
+    {"joyb_prevweapon",        6},  // L2
+    {"joyb_nextweapon",        7},  // R2
+    {"joyb_menu_activate",     11}, // Start
+    {NULL, 0},
+};
 
+// http://www.8bitdo.com/sfc30/ or http://www.8bitdo.com/snes30/
+static const joystick_config_t sfc30_controller[] =
+{
+    {"joystick_x_axis",        0},
+    {"joystick_y_axis",        1},
+    {"joyb_fire",              4}, // Y
+    {"joyb_speed",             1}, // B
+    {"joyb_jump",              3}, // X
+    {"joyb_use",               0}, // A
+    {"joyb_strafeleft",        6}, // L
+    {"joyb_straferight",       7}, // R
+    {"joyb_menu_activate",    11}, // Start
+    {"joyb_toggle_automap",   10}, // Select
+    {NULL, 0},
+};
+
 static const known_joystick_t known_joysticks[] =
 {
     {
@@ -407,6 +450,52 @@
         2, 8, 1,
         pc_gameport_controller,
     },
+
+    // 8Bitdo NES30 Pro, http://www.8bitdo.com/nes30pro/
+    // Probably some of their other controllers can use the same config.
+    {
+        "8Bitdo NES30 Pro",
+        4, 16, 1,
+        nes30_pro_controller,
+    },
+
+    // 8Bitdo SFC30 SNES replica controller
+    // in default mode and in controller mode (Start+R)
+    // the latter suffixes "Joystick" to the name
+    // http://www.8bitdo.com/sfc30/
+    {
+        "8Bitdo SFC30 GamePad*",
+        4, 16, 1,
+        sfc30_controller,
+    },
+
+    // As above, but as detected on RHEL Linux (odd extra axes)
+    {
+        "8Bitdo SFC30 GamePad*",
+        6, 16, 1,
+        sfc30_controller,
+    },
+
+    // SNES30 colour variation of the above
+    // http://www.8bitdo.com/snes30/
+    {
+        "8Bitdo SNES30 GamePad*",
+        4, 16, 1,
+        sfc30_controller,
+    },
+
+    // 8Bitdo SFC30 SNES replica controller in USB controller mode
+    // tested with firmware V2.68 (Beta); latest stable V2.65 doesn't work on
+    // OS X in USB controller mode
+    // Names seen so far:
+    //     'SFC30 Joystick' (OS X)
+    //     'SFC30              SFC30 Joystick' (Fedora 24; RHEL7)
+    // XXX: there is probably a SNES30 variant of this too
+    {
+        "SFC30 *",
+        4, 12, 1,
+        sfc30_controller,
+    },
 };
 
 static const known_joystick_t *GetJoystickType(int index)
@@ -805,7 +894,7 @@
 
     window = TXT_NewWindow("Gamepad/Joystick configuration");
     TXT_SetTableColumns(window, 6);
-    TXT_SetColumnWidths(window, 18, 10, 2, 14, 10, 0);
+    TXT_SetColumnWidths(window, 18, 10, 1, 15, 10, 0);
     TXT_SetWindowHelpURL(window, WINDOW_HELP_URL);
 
     TXT_AddWidgets(window,
@@ -871,6 +960,8 @@
     }
 
     AddJoystickControl(window, "Activate menu", &joybmenu);
+
+    AddJoystickControl(window, "Toggle Automap", &joybautomap);
 
     TXT_SignalConnect(joystick_button, "pressed", CalibrateJoystick, NULL);
     TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction());
--- a/src/setup/txt_joybinput.c
+++ b/src/setup/txt_joybinput.c
@@ -56,6 +56,7 @@
     &joybnextweapon,
     &joybjump,
     &joybmenu,
+    &joybautomap,
 };
 
 static int PhysicalForVirtualButton(int vbutton)
--- a/src/strife/am_map.c
+++ b/src/strife/am_map.c
@@ -32,6 +32,7 @@
 #include "m_cheat.h"
 #include "m_controls.h"
 #include "i_system.h"
+#include "i_timer.h"
 
 // Needs access to LFB.
 #include "v_video.h"
@@ -579,10 +580,32 @@
 
     int rc;
     static int bigstate=0;
+    static int joywait = 0;
     static char buffer[20];
     int key;
 
     rc = false;
+
+    if (ev->type == ev_joystick && joybautomap >= 0
+        && (ev->data1 & (1 << joybautomap)) != 0 && joywait < I_GetTime())
+    {
+        joywait = I_GetTime() + 5;
+
+        if (!automapactive)
+        {
+            AM_Start ();
+            viewactive = false;
+        }
+        else
+        {
+            bigstate = 0;
+            viewactive = true;
+            AM_Stop ();
+        }
+
+        return true;
+    }
+
 
     if (!automapactive)
     {
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@ -2381,7 +2381,8 @@
 void G_TimeDemo (char* name) 
 {
     //!
-    // @vanilla 
+    // @category video
+    // @vanilla
     //
     // Disable rendering the screen entirely.
     //
--- a/src/strife/p_spec.c
+++ b/src/strife/p_spec.c
@@ -436,7 +436,7 @@
             }
             else if (h == MAX_ADJOINING_SECTORS + 2)
             {
-                // Fatal overflow: game crashes at 22 textures
+                // Fatal overflow: game crashes at 22 sectors
                 I_Error("Sector with more than 22 adjoining sectors. "
                         "Vanilla will crash here");
             }
--- a/src/strife/r_bsp.c
+++ b/src/strife/r_bsp.c
@@ -77,9 +77,15 @@
     
 } cliprange_t;
 
+// We must expand MAXSEGS to the theoretical limit of the number of solidsegs
+// that can be generated in a scene by the DOOM engine. This was determined by
+// Lee Killough during BOOM development to be a function of the screensize.
+// The simplest thing we can do, other than fix this bug, is to let the game
+// render overage and then bomb out by detecting the overflow after the 
+// fact. -haleyjd
+//#define MAXSEGS		32
+#define MAXSEGS (SCREENWIDTH / 2 + 1)
 
-#define MAXSEGS		32
-
 // newend is one past the last valid seg
 cliprange_t*	newend;
 cliprange_t	solidsegs[MAXSEGS];
@@ -532,6 +538,10 @@
 	R_AddLine (line);
 	line++;
     }
+
+    // check for solidsegs overflow - extremely unsatisfactory!
+    if(newend > &solidsegs[32])
+        I_Error("R_Subsector: solidsegs overflow (vanilla may crash here)\n");
 }
 
 
--- a/src/v_diskicon.c
+++ b/src/v_diskicon.c
@@ -55,7 +55,7 @@
     s = src; d = dest;
     for (y = 0; y < h; ++y)
     {
-        memcpy(d, s, w);
+        memcpy(d, s, w * sizeof(*d));
         s += src_pitch;
         d += dest_pitch;
     }
@@ -67,12 +67,14 @@
     patch_t *disk;
 
     // Allocate a complete temporary screen where we'll draw the patch.
-    tmpscreen = Z_Malloc(SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL);
-    memset(tmpscreen, 0, SCREENWIDTH * SCREENHEIGHT);
+    tmpscreen = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*tmpscreen),
+                         PU_STATIC, NULL);
+    memset(tmpscreen, 0, SCREENWIDTH * SCREENHEIGHT * sizeof(*tmpscreen));
     V_UseBuffer(tmpscreen);
 
     // Buffer where we'll save the disk data.
-    disk_data = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL);
+    disk_data = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H * sizeof(*disk_data),
+                         PU_STATIC, NULL);
 
     // Draw the patch and save the result to disk_data.
     disk = W_CacheLumpName(disk_lump, PU_STATIC);
@@ -91,8 +93,9 @@
     loading_disk_xoffs = xoffs;
     loading_disk_yoffs = yoffs;
 
-    saved_background = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC,
-                                NULL);
+    saved_background = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H
+                                 * sizeof(*saved_background),
+                                PU_STATIC, NULL);
     SaveDiskData(lump_name, xoffs, yoffs);
 }
 
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -673,6 +673,7 @@
     pcx->hres = SHORT(width);
     pcx->vres = SHORT(height);
     memset (pcx->palette,0,sizeof(pcx->palette));
+    pcx->reserved = 0;                  // PCX spec: reserved byte must be zero
     pcx->color_planes = 1;		// chunky image
     pcx->bytes_per_line = SHORT(width);
     pcx->palette_type = SHORT(2);	// not a grey scale
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -169,6 +169,7 @@
 	    // Homebrew levels?
 	    if (strncmp(header.identification,"PWAD",4))
 	    {
+		W_CloseFile(wad_file);
 		I_Error ("Wad file %s doesn't have IWAD "
 			 "or PWAD id\n", filename);
 	    }
@@ -177,6 +178,16 @@
 	}
 
 	header.numlumps = LONG(header.numlumps);
+
+         // Vanilla Doom doesn't like WADs with more than 4046 lumps
+         // https://www.doomworld.com/vb/post/1010985
+         if (!strncmp(header.identification,"PWAD",4) && header.numlumps > 4046)
+         {
+                 W_CloseFile(wad_file);
+                 I_Error ("Error: Vanilla limit for lumps in a WAD is 4046, "
+                          "PWAD %s has %d", filename, header.numlumps);
+         }
+
 	header.infotableofs = LONG(header.infotableofs);
 	length = header.numlumps*sizeof(filelump_t);
 	fileinfo = Z_Malloc(length, PU_STATIC, 0);
@@ -189,6 +200,7 @@
     filelumps = calloc(numfilelumps, sizeof(lumpinfo_t));
     if (filelumps == NULL)
     {
+        W_CloseFile(wad_file);
         I_Error("Failed to allocate array for lumps from new file.");
     }
 
@@ -197,6 +209,7 @@
     lumpinfo = realloc(lumpinfo, numlumps * sizeof(lumpinfo_t *));
     if (lumpinfo == NULL)
     {
+        W_CloseFile(wad_file);
         I_Error("Failed to increase lumpinfo[] array size.");
     }
 
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -122,13 +122,13 @@
 
     block->size = mainzone->size - sizeof(memzone_t);
 
-    //!
+    // [Deliberately undocumented]
     // Zone memory debugging flag. If set, memory is zeroed after it is freed
     // to deliberately break any code that attempts to use it after free.
     //
     zero_on_free = M_ParmExists("-zonezero");
 
-    //!
+    // [Deliberately undocumented]
     // Zone memory debugging flag. If set, each time memory is freed, the zone
     // heap is scanned to look for remaining pointers to the freed block.
     //