shithub: choc

Download patch

ref: 83a7dfb9af4b8819b6d578044809fc91cc21661a
parent: b58976aa244ae7bc681a2c254a9a117df0a82ed8
parent: 52ccdb80987010c768d6f65a1b6c87843a1fcdf3
author: Jonathan Dowland <[email protected]>
date: Mon Jul 17 09:30:03 EDT 2017

Merge branch 'sdl2-branch' into chocolate-midivolume

--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,10 @@
 stamp-h1
 tags
 \#*\#
+DOOM*.png
+HTIC*.png
+HEXEN*.png
+STRIFE*.png
 
 # These are the default patterns globally ignored by Subversion:
 *.o
@@ -37,3 +41,9 @@
 .#*
 .*.swp
 .DS_store
+
+# Ignore GNU Global tags and html files
+GPATH
+GRTAGS
+GTAGS
+/HTML/
--- a/NEWS.md
+++ b/NEWS.md
@@ -43,6 +43,8 @@
   * There is no longer a soft dependency on Zenity on Unix systems; the
     SDL API is now used to display error dialogs.
   * Joysticks are identified more precisely using GUID now.
+  * A new parameter -savedir allows users to specify a directory from
+    which to load and save games. (thanks CapnClever)
 
 ### Hexen
   * The CD audio option for music playback has been removed; the CD
@@ -59,12 +61,6 @@
   * Handling of code pages was cleaned up, so it is easier to change the
     code to work with a different code page now.
   * Lots of the UI code was changed to use UTF-8 strings.
-
-## HEAD
-
-### General
-  * A new parameter -savedir allows users to specify a directory from
-    which to load and save games. (thanks CapnClever)
 
 ## 2.3.0 (2016-12-29)
 
--- a/configure.ac
+++ b/configure.ac
@@ -113,15 +113,16 @@
 
 WINDOWS_RC_VERSION=`echo $PACKAGE_VERSION | sed 's/-.*//; s/\./, /g; s/$/, 0/'`
 
-# This controls the prefix added to the start of program names.  For example,
-# if this is changed to "lemon-", the programs generated will be named
-# lemon-doom, lemon-heretic, etc.
+dnl Without a hyphen. This is used for the bash-completion scripts.
+PROGRAM_SPREFIX=$(echo $PACKAGE_SHORTNAME | tr A-Z a-z)
 
-PROGRAM_PREFIX=chocolate-
+dnl With a hyphen, used almost everywhere else.
+PROGRAM_PREFIX=${PROGRAM_SPREFIX}-
 
 AC_SUBST(PROGRAM_PREFIX)
 AC_DEFINE_UNQUOTED(PROGRAM_PREFIX, "$PROGRAM_PREFIX",
                    Change this when you create your awesome forked version)
+AC_SUBST(PROGRAM_SPREFIX)
 
 AM_CONFIG_HEADER(config.h:config.hin)
 
@@ -151,6 +152,10 @@
 Makefile
 man/Makefile
 man/bash-completion/Makefile
+man/bash-completion/doom.template
+man/bash-completion/heretic.template
+man/bash-completion/hexen.template
+man/bash-completion/strife.template
 midiproc/Makefile
 opl/Makefile
 opl/examples/Makefile
--- a/data/convert-icon
+++ b/data/convert-icon
@@ -23,8 +23,13 @@
 try:
         import Image
 except ImportError:
-        print("WARNING: Could not update %s.  Please install the Python Imaging library." % sys.argv[2])
-        sys.exit(0)
+        try:
+                from PIL import Image
+        except ImportError:
+                print("WARNING: Could not update %s.  "
+                      "Please install the Python Imaging library or Pillow."
+                      % sys.argv[2])
+                sys.exit(0)
 
 
 def convert_image(filename, output_filename):
--- a/man/bash-completion/.gitignore
+++ b/man/bash-completion/.gitignore
@@ -2,3 +2,4 @@
 *heretic
 *hexen
 *strife
+*.template
--- a/man/bash-completion/doom.template
+++ /dev/null
@@ -1,51 +1,0 @@
-# bash completion for Chocolate Doom                       -*- shell-script -*-
-
-_chocolate_doom()
-{
-    local cur prev words cword
-    _init_completion || return
-
-    # Save the previous switch on the command line in the prevsw variable
-    local i prevsw=""
-    for (( i=1; $cword > 1 && i <= $cword; i++ )); do
-        if [[ ${words[i]} == -* ]]; then
-            prevsw=${words[i]}
-        fi
-    done
-
-    # Allow adding more than one file with the same extension to the same switch
-    case $prevsw in
-        -config|-extraconfig)
-            _filedir cfg
-            ;;
-        -file|-iwad|-aa|-af|-as|-merge|-nwtmerge)
-            _filedir wad
-            ;;
-        -playdemo|-timedemo)
-            _filedir lmp
-            ;;
-        -deh)
-            _filedir '@(bex|deh)'
-            ;;
-    esac
-
-    case $prev in
-        -pack)
-            COMPREPLY=(doom2 tnt plutonia)
-            ;;
-        -gameversion)
-            COMPREPLY=(1.9 ultimate final final2 hacx chex)
-            ;;
-        -setmem)
-            COMPREPLY=(dos622 dos71 dosbox)
-            ;;
-    esac
-
-    if [[ $cur == -* ]]; then
-        COMPREPLY=( $( compgen -W '@content' -- "$cur" ) )
-    fi
-} &&
-
-complete -F _chocolate_doom chocolate-doom
-
-# ex: ts=4 sw=4 et filetype=sh
--- /dev/null
+++ b/man/bash-completion/doom.template.in
@@ -1,0 +1,51 @@
+# bash completion for @PACKAGE_SHORTNAME@ Doom               -*- shell-script -*-
+
+_@PROGRAM_SPREFIX@_doom()
+{
+    local cur prev words cword
+    _init_completion || return
+
+    # Save the previous switch on the command line in the prevsw variable
+    local i prevsw=""
+    for (( i=1; $cword > 1 && i <= $cword; i++ )); do
+        if [[ ${words[i]} == -* ]]; then
+            prevsw=${words[i]}
+        fi
+    done
+
+    # Allow adding more than one file with the same extension to the same switch
+    case $prevsw in
+        -config|-extraconfig)
+            _filedir cfg
+            ;;
+        -file|-iwad|-aa|-af|-as|-merge|-nwtmerge)
+            _filedir wad
+            ;;
+        -playdemo|-timedemo)
+            _filedir lmp
+            ;;
+        -deh)
+            _filedir '@(bex|deh)'
+            ;;
+    esac
+
+    case $prev in
+        -pack)
+            COMPREPLY=(doom2 tnt plutonia)
+            ;;
+        -gameversion)
+            COMPREPLY=(1.9 ultimate final final2 hacx chex)
+            ;;
+        -setmem)
+            COMPREPLY=(dos622 dos71 dosbox)
+            ;;
+    esac
+
+    if [[ $cur == -* ]]; then
+        COMPREPLY=( $( compgen -W '@content' -- "$cur" ) )
+    fi
+} &&
+
+complete -F _@PROGRAM_SPREFIX@_doom @PROGRAM_PREFIX@doom
+
+# ex: ts=4 sw=4 et filetype=sh
--- a/man/bash-completion/heretic.template
+++ /dev/null
@@ -1,48 +1,0 @@
-# bash completion for Chocolate Heretic                    -*- shell-script -*-
-
-_chocolate_heretic()
-{
-    local cur prev words cword
-    _init_completion || return
-
-    # Save the previous switch on the command line in the prevsw variable
-    local i prevsw=""
-    for (( i=1; $cword > 1 && i <= $cword; i++ )); do
-        if [[ ${words[i]} == -* ]]; then
-            prevsw=${words[i]}
-        fi
-    done
-
-    # Allow adding more than one file with the same extension to the same switch
-    case $prevsw in
-        -config|-extraconfig)
-            _filedir cfg
-            ;;
-        -file|-iwad|-aa|-af|-as|-merge|-nwtmerge)
-            _filedir wad
-            ;;
-        -playdemo|-timedemo)
-            _filedir lmp
-            ;;
-        -deh)
-            _filedir hhe
-            ;;
-    esac
-
-    case $prev in
-        -hhever)
-            COMPREPLY=(1.0 1.2 1.3)
-            ;;
-        -setmem)
-            COMPREPLY=(dos622 dos71 dosbox)
-            ;;
-    esac
-
-    if [[ $cur == -* ]]; then
-        COMPREPLY=( $( compgen -W '@content' -- "$cur" ) )
-    fi
-} &&
-
-complete -F _chocolate_heretic chocolate-heretic
-
-# ex: ts=4 sw=4 et filetype=sh
--- /dev/null
+++ b/man/bash-completion/heretic.template.in
@@ -1,0 +1,48 @@
+# bash completion for @PACKAGE_SHORTNAME@ Heretic            -*- shell-script -*-
+
+_@PROGRAM_SPREFIX@_heretic()
+{
+    local cur prev words cword
+    _init_completion || return
+
+    # Save the previous switch on the command line in the prevsw variable
+    local i prevsw=""
+    for (( i=1; $cword > 1 && i <= $cword; i++ )); do
+        if [[ ${words[i]} == -* ]]; then
+            prevsw=${words[i]}
+        fi
+    done
+
+    # Allow adding more than one file with the same extension to the same switch
+    case $prevsw in
+        -config|-extraconfig)
+            _filedir cfg
+            ;;
+        -file|-iwad|-aa|-af|-as|-merge|-nwtmerge)
+            _filedir wad
+            ;;
+        -playdemo|-timedemo)
+            _filedir lmp
+            ;;
+        -deh)
+            _filedir hhe
+            ;;
+    esac
+
+    case $prev in
+        -hhever)
+            COMPREPLY=(1.0 1.2 1.3)
+            ;;
+        -setmem)
+            COMPREPLY=(dos622 dos71 dosbox)
+            ;;
+    esac
+
+    if [[ $cur == -* ]]; then
+        COMPREPLY=( $( compgen -W '@content' -- "$cur" ) )
+    fi
+} &&
+
+complete -F _@PROGRAM_SPREFIX@_heretic @PROGRAM_PREFIX@heretic
+
+# ex: ts=4 sw=4 et filetype=sh
--- a/man/bash-completion/hexen.template
+++ /dev/null
@@ -1,42 +1,0 @@
-# bash completion for Chocolate Hexen                     -*- shell-script -*-
-
-_chocolate_hexen()
-{
-    local cur prev words cword
-    _init_completion || return
-
-    # Save the previous switch on the command line in the prevsw variable
-    local i prevsw=""
-    for (( i=1; $cword > 1 && i <= $cword; i++ )); do
-        if [[ ${words[i]} == -* ]]; then
-            prevsw=${words[i]}
-        fi
-    done
-
-    # Allow adding more than one file with the same extension to the same switch
-    case $prevsw in
-        -config|-extraconfig)
-            _filedir cfg
-            ;;
-        -file|-iwad|-aa|-af|-as|-merge|-nwtmerge)
-            _filedir wad
-            ;;
-        -playdemo|-timedemo)
-            _filedir lmp
-            ;;
-    esac
-
-    case $prev in
-        -setmem)
-            COMPREPLY=(dos622 dos71 dosbox)
-            ;;
-    esac
-
-    if [[ $cur == -* ]]; then
-        COMPREPLY=( $( compgen -W '@content' -- "$cur" ) )
-    fi
-} &&
-
-complete -F _chocolate_hexen chocolate-hexen
-
-# ex: ts=4 sw=4 et filetype=sh
--- /dev/null
+++ b/man/bash-completion/hexen.template.in
@@ -1,0 +1,42 @@
+# bash completion for @PACKAGE_SHORTNAME@ Hexen              -*- shell-script -*-
+
+_@PROGRAM_SPREFIX@_hexen()
+{
+    local cur prev words cword
+    _init_completion || return
+
+    # Save the previous switch on the command line in the prevsw variable
+    local i prevsw=""
+    for (( i=1; $cword > 1 && i <= $cword; i++ )); do
+        if [[ ${words[i]} == -* ]]; then
+            prevsw=${words[i]}
+        fi
+    done
+
+    # Allow adding more than one file with the same extension to the same switch
+    case $prevsw in
+        -config|-extraconfig)
+            _filedir cfg
+            ;;
+        -file|-iwad|-aa|-af|-as|-merge|-nwtmerge)
+            _filedir wad
+            ;;
+        -playdemo|-timedemo)
+            _filedir lmp
+            ;;
+    esac
+
+    case $prev in
+        -setmem)
+            COMPREPLY=(dos622 dos71 dosbox)
+            ;;
+    esac
+
+    if [[ $cur == -* ]]; then
+        COMPREPLY=( $( compgen -W '@content' -- "$cur" ) )
+    fi
+} &&
+
+complete -F _@PROGRAM_SPREFIX@_hexen @PROGRAM_PREFIX@hexen
+
+# ex: ts=4 sw=4 et filetype=sh
--- a/man/bash-completion/strife.template
+++ /dev/null
@@ -1,48 +1,0 @@
-# bash completion for Chocolate Strife                     -*- shell-script -*-
-
-_chocolate_strife()
-{
-    local cur prev words cword
-    _init_completion || return
-
-    # Save the previous switch on the command line in the prevsw variable
-    local i prevsw=""
-    for (( i=1; $cword > 1 && i <= $cword; i++ )); do
-        if [[ ${words[i]} == -* ]]; then
-            prevsw=${words[i]}
-        fi
-    done
-
-    # Allow adding more than one file with the same extension to the same switch
-    case $prevsw in
-        -config|-extraconfig)
-            _filedir cfg
-            ;;
-        -file|-iwad|-aa|-af|-as|-merge|-nwtmerge)
-            _filedir wad
-            ;;
-        -playdemo|-timedemo)
-            _filedir lmp
-            ;;
-        -deh)
-            _filedir seh
-            ;;
-    esac
-
-    case $prev in
-        -gameversion)
-            COMPREPLY=(1.2 1.31)
-            ;;
-        -setmem)
-            COMPREPLY=(dos622 dos71 dosbox)
-            ;;
-    esac
-
-    if [[ $cur == -* ]]; then
-        COMPREPLY=( $( compgen -W '@content' -- "$cur" ) )
-    fi
-} &&
-
-complete -F _chocolate_strife chocolate-strife
-
-# ex: ts=4 sw=4 et filetype=sh
--- /dev/null
+++ b/man/bash-completion/strife.template.in
@@ -1,0 +1,48 @@
+# bash completion for @PACKAGE_SHORTNAME@ Strife             -*- shell-script -*-
+
+_@PROGRAM_SPREFIX@_strife()
+{
+    local cur prev words cword
+    _init_completion || return
+
+    # Save the previous switch on the command line in the prevsw variable
+    local i prevsw=""
+    for (( i=1; $cword > 1 && i <= $cword; i++ )); do
+        if [[ ${words[i]} == -* ]]; then
+            prevsw=${words[i]}
+        fi
+    done
+
+    # Allow adding more than one file with the same extension to the same switch
+    case $prevsw in
+        -config|-extraconfig)
+            _filedir cfg
+            ;;
+        -file|-iwad|-aa|-af|-as|-merge|-nwtmerge)
+            _filedir wad
+            ;;
+        -playdemo|-timedemo)
+            _filedir lmp
+            ;;
+        -deh)
+            _filedir seh
+            ;;
+    esac
+
+    case $prev in
+        -gameversion)
+            COMPREPLY=(1.2 1.31)
+            ;;
+        -setmem)
+            COMPREPLY=(dos622 dos71 dosbox)
+            ;;
+    esac
+
+    if [[ $cur == -* ]]; then
+        COMPREPLY=( $( compgen -W '@content' -- "$cur" ) )
+    fi
+} &&
+
+complete -F _@PROGRAM_SPREFIX@_strife @PROGRAM_PREFIX@strife
+
+# ex: ts=4 sw=4 et filetype=sh
--- a/pkg/osx/Resources/launcher.nib/designable.nib
+++ b/pkg/osx/Resources/launcher.nib/designable.nib
@@ -1,8 +1,9 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9532" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
     <dependencies>
         <deployment version="1070" identifier="macosx"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9532"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
         <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -51,8 +52,8 @@
                         <autoresizingMask key="autoresizingMask"/>
                         <imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="axesIndependently" image="128x128" id="327"/>
                     </imageView>
-                    <textField verticalHuggingPriority="750" id="218">
-                        <rect key="frame" x="18" y="125" width="360" height="11"/>
+                    <textField verticalHuggingPriority="750" misplaced="YES" id="218">
+                        <rect key="frame" x="18" y="125" width="350" height="11"/>
                         <autoresizingMask key="autoresizingMask"/>
                         <textFieldCell key="cell" sendsActionOnEndEditing="YES" id="329">
                             <font key="font" metaFont="miniSystem"/>
@@ -66,9 +67,9 @@
                         <rect key="frame" x="20" y="49" width="346" height="68"/>
                         <autoresizingMask key="autoresizingMask"/>
                         <textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="border" drawsBackground="YES" id="330">
-                            <font key="font" size="11" name="CourierNewPSMT"/>
-                            <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
-                            <color key="backgroundColor" red="0.29793848167539272" green="0.29793848167539272" blue="0.29793848167539272" alpha="1" colorSpace="calibratedRGB"/>
+                            <font key="font" metaFont="fixedUser" size="11"/>
+                            <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
                         </textFieldCell>
                     </textField>
                     <button horizontalHuggingPriority="750" verticalHuggingPriority="750" id="5zZ-CS-NR1">
binary files a/pkg/osx/Resources/launcher.nib/keyedobjects.nib b/pkg/osx/Resources/launcher.nib/keyedobjects.nib differ
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@ -650,6 +650,11 @@
     // 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");
+
+    // The convention set by RBDOOM-3-BFG is to install Doom 3: BFG
+    // Edition into this directory, under which includes the Doom
+    // Classic WADs.
+    AddIWADPath(env, "/games/doom3bfg/base/wads");
 }
 #endif
 
--- a/src/doom/am_map.c
+++ b/src/doom/am_map.c
@@ -33,6 +33,7 @@
 #include "m_misc.h"
 #include "i_system.h"
 #include "i_timer.h"
+#include "i_video.h"
 
 // Needs access to LFB.
 #include "v_video.h"
@@ -599,7 +600,6 @@
 
     int rc;
     static int bigstate=0;
-    static int joywait = 0;
     static char buffer[20];
     int key;
 
@@ -606,7 +606,7 @@
     rc = false;
 
     if (ev->type == ev_joystick && joybautomap >= 0
-        && (ev->data1 & (1 << joybautomap)) != 0 && joywait < I_GetTime())
+        && (ev->data1 & (1 << joybautomap)) != 0)
     {
         joywait = I_GetTime() + 5;
 
--- a/src/doom/doomdata.h
+++ b/src/doom/doomdata.h
@@ -54,16 +54,16 @@
 
 
 // A single Vertex.
-typedef struct
+typedef PACKED_STRUCT (
 {
   short		x;
   short		y;
-} PACKEDATTR mapvertex_t;
+}) mapvertex_t;
 
 
 // A SideDef, defining the visual appearance of a wall,
 // by setting textures and offsets.
-typedef struct
+typedef PACKED_STRUCT (
 {
   short		textureoffset;
   short		rowoffset;
@@ -72,13 +72,13 @@
   char		midtexture[8];
   // Front sector, towards viewer.
   short		sector;
-} PACKEDATTR mapsidedef_t;
+}) mapsidedef_t;
 
 
 
 // A LineDef, as used for editing, and as input
 // to the BSP builder.
-typedef struct
+typedef PACKED_STRUCT (
 {
   short		v1;
   short		v2;
@@ -87,7 +87,7 @@
   short		tag;
   // sidenum[1] will be -1 if one sided
   short		sidenum[2];		
-} PACKEDATTR maplinedef_t;
+}) maplinedef_t;
 
 
 //
@@ -135,7 +135,7 @@
 
 
 // Sector definition, from editing.
-typedef	struct
+typedef	PACKED_STRUCT (
 {
   short		floorheight;
   short		ceilingheight;
@@ -144,20 +144,20 @@
   short		lightlevel;
   short		special;
   short		tag;
-} PACKEDATTR mapsector_t;
+}) mapsector_t;
 
 // SubSector, as generated by BSP.
-typedef struct
+typedef PACKED_STRUCT (
 {
   short		numsegs;
   // Index of first one, segs are stored sequentially.
   short		firstseg;	
-} PACKEDATTR mapsubsector_t;
+}) mapsubsector_t;
 
 
 // LineSeg, generated by splitting LineDefs
 // using partition lines selected by BSP builder.
-typedef struct
+typedef PACKED_STRUCT (
 {
   short		v1;
   short		v2;
@@ -165,7 +165,7 @@
   short		linedef;
   short		side;
   short		offset;
-} PACKEDATTR mapseg_t;
+}) mapseg_t;
 
 
 
@@ -174,7 +174,7 @@
 // Indicate a leaf.
 #define	NF_SUBSECTOR	0x8000
 
-typedef struct
+typedef PACKED_STRUCT (
 {
   // Partition line from (x,y) to x+dx,y+dy)
   short		x;
@@ -190,7 +190,7 @@
   // else it's a node of another subtree.
   unsigned short	children[2];
 
-} PACKEDATTR mapnode_t;
+}) mapnode_t;
 
 
 
@@ -197,7 +197,7 @@
 
 // Thing definition, position, orientation and type,
 // plus skill/visibility flags and attributes.
-typedef struct
+typedef PACKED_STRUCT (
 {
     short		x;
     short		y;
@@ -204,7 +204,7 @@
     short		angle;
     short		type;
     short		options;
-} PACKEDATTR mapthing_t;
+}) mapthing_t;
 
 
 
--- a/src/doom/m_menu.c
+++ b/src/doom/m_menu.c
@@ -112,6 +112,7 @@
 int			saveStringEnter;              
 int             	saveSlot;	// which slot to save in
 int			saveCharIndex;	// which char we're editing
+static boolean          joypadSave = false; // was the save action initiated by joypad?
 // old save description before edit
 char			saveOldString[SAVESTRINGSIZE];  
 
@@ -631,6 +632,17 @@
 }
 
 //
+// Generate a default save slot name when the user saves to
+// an empty slot via the joypad.
+//
+static void SetDefaultSaveName(int slot)
+{
+    M_snprintf(savegamestrings[itemOn], SAVESTRINGSIZE - 1,
+               "JOYSTICK SLOT %i", itemOn + 1);
+    joypadSave = false;
+}
+
+//
 // User wants to save. Start string input for M_Responder
 //
 void M_SaveSelect(int choice)
@@ -648,7 +660,14 @@
     saveSlot = choice;
     M_StringCopy(saveOldString,savegamestrings[choice], SAVESTRINGSIZE);
     if (!strcmp(savegamestrings[choice], EMPTYSTRING))
-	savegamestrings[choice][0] = 0;
+    {
+        savegamestrings[choice][0] = 0;
+
+        if (joypadSave)
+        {
+            SetDefaultSaveName(choice);
+        }
+    }
     saveCharIndex = strlen(savegamestrings[choice]);
 }
 
@@ -1347,7 +1366,6 @@
     int             ch;
     int             key;
     int             i;
-    static  int     joywait = 0;
     static  int     mousewait = 0;
     static  int     mousey = 0;
     static  int     lasty = 0;
@@ -1394,8 +1412,10 @@
     ch = 0;
     key = -1;
 	
-    if (ev->type == ev_joystick && joywait < I_GetTime())
+    if (ev->type == ev_joystick)
     {
+        // Simulate key presses from joystick events to interact with the menu.
+
 	if (ev->data3 < 0)
 	{
 	    key = key_menu_up;
@@ -1417,21 +1437,55 @@
 	    key = key_menu_right;
 	    joywait = I_GetTime() + 2;
 	}
-		
-	if (ev->data1&1)
-	{
-	    key = key_menu_forward;
-	    joywait = I_GetTime() + 5;
-	}
-	if (ev->data1&2)
-	{
-	    key = key_menu_back;
-	    joywait = I_GetTime() + 5;
-	}
-        if (joybmenu >= 0 && (ev->data1 & (1 << joybmenu)) != 0)
+
+#define JOY_BUTTON_MAPPED(x) ((x) >= 0)
+#define JOY_BUTTON_PRESSED(x) (JOY_BUTTON_MAPPED(x) && (ev->data1 & (1 << (x))) != 0)
+
+        if (JOY_BUTTON_PRESSED(joybfire))
         {
+            // Simulate a 'Y' keypress when Doom show a Y/N dialog with Fire button.
+            if (messageToPrint && messageNeedsInput)
+            {
+                key = key_menu_confirm;
+            }
+            // Simulate pressing "Enter" when we are supplying a save slot name
+            else if (saveStringEnter)
+            {
+                key = KEY_ENTER;
+            }
+            else
+            {
+                // if selecting a save slot via joypad, set a flag
+                if (currentMenu == &SaveDef)
+                {
+                    joypadSave = true;
+                }
+                key = key_menu_forward;
+            }
+            joywait = I_GetTime() + 5;
+        }
+        if (JOY_BUTTON_PRESSED(joybuse))
+        {
+            // Simulate a 'N' keypress when Doom show a Y/N dialog with Use button.
+            if (messageToPrint && messageNeedsInput)
+            {
+                key = key_menu_abort;
+            }
+            // If user was entering a save name, back out
+            else if (saveStringEnter)
+            {
+                key = KEY_ESCAPE;
+            }
+            else
+            {
+                key = key_menu_back;
+            }
+            joywait = I_GetTime() + 5;
+        }
+        if (JOY_BUTTON_PRESSED(joybmenu))
+        {
             key = key_menu_activate;
-	    joywait = I_GetTime() + 5;
+            joywait = I_GetTime() + 5;
         }
     }
     else
--- a/src/doom/p_setup.c
+++ b/src/doom/p_setup.c
@@ -677,11 +677,13 @@
 
     unsigned int rejectpad[4] =
     {
-        ((totallines * 4 + 3) & ~3) + 24,     // Size
+        0,                                    // Size
         0,                                    // Part of z_zone block header
         50,                                   // PU_LEVEL
         0x1d4a11                              // DOOM_CONST_ZONEID
     };
+
+    rejectpad[0] = ((totallines * 4 + 3) & ~3) + 24;
 
     // Copy values from rejectpad into the destination array.
 
--- a/src/doom/r_data.c
+++ b/src/doom/r_data.c
@@ -56,7 +56,7 @@
 // into the rectangular texture space using origin
 // and possibly other attributes.
 //
-typedef struct
+typedef PACKED_STRUCT (
 {
     short	originx;
     short	originy;
@@ -63,7 +63,7 @@
     short	patch;
     short	stepdir;
     short	colormap;
-} PACKEDATTR mappatch_t;
+}) mappatch_t;
 
 
 //
@@ -71,7 +71,7 @@
 // A DOOM wall texture is a list of patches
 // which are to be combined in a predefined order.
 //
-typedef struct
+typedef PACKED_STRUCT (
 {
     char		name[8];
     int			masked;	
@@ -80,7 +80,7 @@
     int                 obsolete;
     short		patchcount;
     mappatch_t	patches[1];
-} PACKEDATTR maptexture_t;
+}) maptexture_t;
 
 
 // A single patch from a texture definition,
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -63,7 +63,15 @@
 #define PACKEDATTR
 #endif
 
-// C99 integer types; with gcc we just use this.  Other compilers 
+#ifdef __WATCOMC__
+#define PACKEDPREFIX _Packed
+#else
+#define PACKEDPREFIX
+#endif
+
+#define PACKED_STRUCT(...) PACKEDPREFIX struct __VA_ARGS__ PACKEDATTR
+
+// C99 integer types; with gcc we just use this.  Other compilers
 // should add conditional statements that define the C99 types.
 
 // What is really wanted here is stdint.h; however, some old versions
--- a/src/heretic/d_main.c
+++ b/src/heretic/d_main.c
@@ -456,15 +456,6 @@
     {
         return;
     }
-    // haleyjd FIXME: convert to textscreen code?
-#ifdef __WATCOMC__
-    _settextposition(23, 2);
-    _setbkcolor(1);
-    _settextcolor(0);
-    _outtext(exrnwads);
-    _settextposition(24, 2);
-    _outtext(exrnwads2);
-#endif
 }
 
 boolean D_AddFile(char *file)
@@ -550,41 +541,6 @@
         return;
     }
 
-#if 0
-    progress = (98 * thermCurrent) / thermMax;
-    screen = (char *) 0xb8000 + (THERM_Y * 160 + THERM_X * 2);
-    for (i = 0; i < progress / 2; i++)
-    {
-        switch (i)
-        {
-            case 4:
-            case 9:
-            case 14:
-            case 19:
-            case 29:
-            case 34:
-            case 39:
-            case 44:
-                *screen++ = 0xb3;
-                *screen++ = (THERMCOLOR << 4) + 15;
-                break;
-            case 24:
-                *screen++ = 0xba;
-                *screen++ = (THERMCOLOR << 4) + 15;
-                break;
-            default:
-                *screen++ = 0xdb;
-                *screen++ = 0x40 + THERMCOLOR;
-                break;
-        }
-    }
-    if (progress & 1)
-    {
-        *screen++ = 0xdd;
-        *screen++ = 0x40 + THERMCOLOR;
-    }
-#else
-
     // No progress? Don't update the screen.
 
     progress = (50 * thermCurrent) / thermMax + 2;
@@ -607,7 +563,6 @@
     }
 
     TXT_UpdateScreen();
-#endif
 }
 
 void initStartup(void)
@@ -658,39 +613,7 @@
 char tmsg[300];
 void tprintf(char *msg, int initflag)
 {
-    // haleyjd FIXME: convert to textscreen code?
-#ifdef __WATCOMC__
-    char temp[80];
-    int start;
-    int add;
-    int i;
-
-    if (initflag)
-        tmsg[0] = 0;
-    M_StringConcat(tmsg, msg, sizeof(tmsg));
-    blitStartup();
-    DrawThermo();
-    _setbkcolor(4);
-    _settextcolor(15);
-    for (add = start = i = 0; i <= strlen(tmsg); i++)
-        if ((tmsg[i] == '\n') || (!tmsg[i]))
-        {
-            memset(temp, 0, 80);
-            M_StringCopy(temp, tmsg + start, sizeof(temp));
-            if (i - start < sizeof(temp))
-            {
-                temp[i - start] = '\0';
-            }
-            _settextposition(MSG_Y + add, 40 - strlen(temp) / 2);
-            _outtext(temp);
-            start = i + 1;
-            add++;
-        }
-    _settextposition(25, 1);
-    drawstatus();
-#else
     printf("%s", msg);
-#endif
 }
 
 // haleyjd: moved up, removed WATCOMC code
--- a/src/heretic/doomdata.h
+++ b/src/heretic/doomdata.h
@@ -48,26 +48,26 @@
 };
 
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short x, y;
-} PACKEDATTR mapvertex_t;
+}) mapvertex_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short textureoffset;
     short rowoffset;
     char toptexture[8], bottomtexture[8], midtexture[8];
     short sector;               // on viewer's side
-} PACKEDATTR mapsidedef_t;
+}) mapsidedef_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short v1, v2;
     short flags;
     short special, tag;
     short sidenum[2];           // sidenum[1] will be -1 if one sided
-} PACKEDATTR maplinedef_t;
+}) maplinedef_t;
 
 #define	ML_BLOCKING			1
 #define	ML_BLOCKMONSTERS	2
@@ -88,43 +88,43 @@
 #define	ML_MAPPED			256     // set if allready drawn in automap
 
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short floorheight, ceilingheight;
     char floorpic[8], ceilingpic[8];
     short lightlevel;
     short special, tag;
-} PACKEDATTR mapsector_t;
+}) mapsector_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short numsegs;
     short firstseg;             // segs are stored sequentially
-} PACKEDATTR mapsubsector_t;
+}) mapsubsector_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short v1, v2;
     short angle;
     short linedef, side;
     short offset;
-} PACKEDATTR mapseg_t;
+}) mapseg_t;
 
 #define	NF_SUBSECTOR	0x8000
-typedef struct
+typedef PACKED_STRUCT (
 {
     short x, y, dx, dy;         // partition line
     short bbox[2][4];           // bounding box for each child
     unsigned short children[2]; // if NF_SUBSECTOR its a subsector
-} PACKEDATTR mapnode_t;
+}) mapnode_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short x, y;
     short angle;
     short type;
     short options;
-} PACKEDATTR mapthing_t;
+}) mapthing_t;
 
 #define	MTF_EASY		1
 #define	MTF_NORMAL		2
@@ -139,7 +139,7 @@
 ===============================================================================
 */
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short originx;
     short originy;
@@ -146,9 +146,9 @@
     short patch;
     short stepdir;
     short colormap;
-} PACKEDATTR mappatch_t;
+}) mappatch_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     char name[8];
     boolean masked;
@@ -157,7 +157,7 @@
     int obsolete;
     short patchcount;
     mappatch_t patches[1];
-} PACKEDATTR maptexture_t;
+}) maptexture_t;
 
 
 /*
--- a/src/hexen/h2_main.c
+++ b/src/hexen/h2_main.c
@@ -187,7 +187,7 @@
     {
         // only get hexen.cfg path if one is not already found
 
-        if (!strcmp(SavePathConfig, ""))
+        if (SavePathConfig == NULL || !strcmp(SavePathConfig, ""))
         {
             // If we are not using a savegame path (probably because we are on
             // Windows and not using a config dir), behave like Vanilla Hexen
--- a/src/hexen/p_acs.c
+++ b/src/hexen/p_acs.c
@@ -45,12 +45,12 @@
 
 // TYPES -------------------------------------------------------------------
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     int marker;
     int infoOffset;
     int code;
-} PACKEDATTR acsHeader_t;
+}) acsHeader_t;
 
 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
 
--- a/src/hexen/xddefs.h
+++ b/src/hexen/xddefs.h
@@ -44,13 +44,13 @@
     ML_BEHAVIOR
 };
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short x;
     short y;
-} PACKEDATTR mapvertex_t;
+})  mapvertex_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short textureoffset;
     short rowoffset;
@@ -58,9 +58,9 @@
     char bottomtexture[8];
     char midtexture[8];
     short sector;               // on viewer's side
-} PACKEDATTR mapsidedef_t;
+}) mapsidedef_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short v1;
     short v2;
@@ -72,7 +72,7 @@
     byte arg4;
     byte arg5;
     short sidenum[2];           // sidenum[1] will be -1 if one sided
-} PACKEDATTR maplinedef_t;
+}) maplinedef_t;
 
 #define	ML_BLOCKING			0x0001
 #define	ML_BLOCKMONSTERS	0x0002
@@ -96,7 +96,7 @@
 #define SPAC_PUSH		4       // when player/monster pushes line
 #define SPAC_PCROSS		5       // when projectile crosses line
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short floorheight;
     short ceilingheight;
@@ -105,15 +105,15 @@
     short lightlevel;
     short special;
     short tag;
-} PACKEDATTR mapsector_t;
+}) mapsector_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short numsegs;
     short firstseg;             // segs are stored sequentially
-} PACKEDATTR mapsubsector_t;
+}) mapsubsector_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short v1;
     short v2;
@@ -121,17 +121,17 @@
     short linedef;
     short side;
     short offset;
-} PACKEDATTR mapseg_t;
+}) mapseg_t;
 
 #define	NF_SUBSECTOR	0x8000
-typedef struct
+typedef PACKED_STRUCT (
 {
     short x, y, dx, dy;         // partition line
     short bbox[2][4];           // bounding box for each child
     unsigned short children[2]; // if NF_SUBSECTOR its a subsector
-} PACKEDATTR mapnode_t;
+}) mapnode_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short tid;
     short x;
@@ -146,7 +146,7 @@
     byte arg3;
     byte arg4;
     byte arg5;
-} PACKEDATTR mapthing_t;
+}) mapthing_t;
 
 #define MTF_EASY		1
 #define MTF_NORMAL		2
@@ -166,7 +166,7 @@
 //
 //--------------------------------------------------------------------------
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     short originx;
     short originy;
@@ -173,9 +173,9 @@
     short patch;
     short stepdir;
     short colormap;
-} PACKEDATTR mappatch_t;
+}) mappatch_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     char name[8];
     boolean masked;
@@ -184,6 +184,6 @@
     int obsolete;
     short patchcount;
     mappatch_t patches[1];
-} PACKEDATTR maptexture_t;
+}) maptexture_t;
 
 #endif // __XDDEFS__
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -46,7 +46,7 @@
 
 #define PERCUSSION_LOG_LEN 16
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     byte tremolo;
     byte attack;
@@ -54,9 +54,9 @@
     byte waveform;
     byte scale;
     byte level;
-} PACKEDATTR genmidi_op_t;
+}) genmidi_op_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     genmidi_op_t modulator;
     byte feedback;
@@ -63,9 +63,9 @@
     genmidi_op_t carrier;
     byte unused;
     short base_note_offset;
-} PACKEDATTR genmidi_voice_t;
+}) genmidi_voice_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     unsigned short flags;
     byte fine_tuning;
@@ -72,7 +72,7 @@
     byte fixed_note;
 
     genmidi_voice_t voices[2];
-} PACKEDATTR genmidi_instr_t;
+}) genmidi_instr_t;
 
 // Data associated with a channel of a track that is currently playing.
 
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -21,7 +21,9 @@
 #include "SDL_opengl.h"
 
 #ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
 #define WIN32_LEAN_AND_MEAN
+#endif
 #include <windows.h>
 #endif
 
@@ -28,6 +30,7 @@
 #include "icon.c"
 
 #include "config.h"
+#include "d_loop.h"
 #include "deh_str.h"
 #include "doomtype.h"
 #include "i_input.h"
@@ -99,7 +102,7 @@
 
 // Window position:
 
-static char *window_position = "center";
+char *window_position = "center";
 
 // SDL display number on which to run.
 
@@ -126,6 +129,14 @@
 
 int aspect_ratio_correct = true;
 
+// Force integer scales for resolution-independent rendering
+
+int integer_scaling = false;
+
+// VGA Porch palette change emulation
+
+int vga_porch_flash = false;
+
 // Force software rendering, for systems which lack effective hardware
 // acceleration
 
@@ -177,11 +188,16 @@
 // Window resize state.
 
 static boolean need_resize = false;
+static unsigned int last_resize_time;
+#define RESIZE_DELAY 500
 
 // Gamma correction level to use
 
 int usegamma = 0;
 
+// Joystick/gamepad hysteresis
+unsigned int joywait = 0;
+
 static boolean MouseShouldBeGrabbed()
 {
     // never grab the mouse when in screensaver mode
@@ -305,7 +321,7 @@
 
 static void HandleWindowEvent(SDL_WindowEvent *event)
 {
-    int i, flags;
+    int i;
 
     switch (event->event)
     {
@@ -321,18 +337,7 @@
 
         case SDL_WINDOWEVENT_RESIZED:
             need_resize = true;
-            // When the window is resized (we're not in fullscreen mode),
-            // save the new window size.
-            flags = SDL_GetWindowFlags(screen);
-            if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0)
-            {
-                SDL_GetWindowSize(screen, &window_width, &window_height);
-
-                // Adjust the window by resizing again so that the window
-                // is the right aspect ratio.
-                AdjustWindowSize();
-                SDL_SetWindowSize(screen, window_width, window_height);
-            }
+            last_resize_time = SDL_GetTicks();
             break;
 
         // Don't render the screen when the window is minimized:
@@ -491,7 +496,10 @@
         I_ReadMouse();
     }
 
-    I_UpdateJoystick();
+    if (joywait < I_GetTime())
+    {
+        I_UpdateJoystick();
+    }
 }
 
 
@@ -564,7 +572,8 @@
         --*h_upscale;
     }
 
-    if (*w_upscale < 1 || *h_upscale < 1)
+    if ((*w_upscale < 1 && rinfo.max_texture_width > 0) ||
+        (*h_upscale < 1 && rinfo.max_texture_height > 0))
     {
         I_Error("CreateUpscaledTexture: Can't create a texture big enough for "
                 "the whole screen! Maximum texture size %dx%d",
@@ -703,9 +712,29 @@
 
     if (need_resize)
     {
-        CreateUpscaledTexture(false);
-        need_resize = false;
-        palette_to_set = true;
+        if (SDL_GetTicks() > last_resize_time + RESIZE_DELAY)
+        {
+            int flags;
+            // When the window is resized (we're not in fullscreen mode),
+            // save the new window size.
+            flags = SDL_GetWindowFlags(screen);
+            if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0)
+            {
+                SDL_GetWindowSize(screen, &window_width, &window_height);
+
+                // Adjust the window by resizing again so that the window
+                // is the right aspect ratio.
+                AdjustWindowSize();
+                SDL_SetWindowSize(screen, window_width, window_height);
+            }
+            CreateUpscaledTexture(false);
+            need_resize = false;
+            palette_to_set = true;
+        }
+        else
+        {
+            return;
+        }
     }
 
     UpdateGrab();
@@ -741,6 +770,14 @@
     {
         SDL_SetPaletteColors(screenbuffer->format->palette, palette, 0, 256);
         palette_to_set = false;
+
+        if (vga_porch_flash)
+        {
+            // "flash" the pillars/letterboxes with palette changes, emulating
+            // VGA "porch" behaviour (GitHub issue #832)
+            SDL_SetRenderDrawColor(renderer, palette[0].r, palette[0].g,
+                palette[0].b, SDL_ALPHA_OPAQUE);
+        }
     }
 
     // Blit from the paletted 8-bit screen buffer to the intermediate
@@ -1064,17 +1101,6 @@
 {
     SDL_Rect bounds;
 
-    // Check that video_display corresponds to a display that really exists,
-    // and if it doesn't, reset it.
-    if (video_display < 0 || video_display >= SDL_GetNumVideoDisplays())
-    {
-        fprintf(stderr,
-                "CenterWindow: We were configured to run on display #%d, but "
-                "it no longer exists (max %d). Moving to display 0.\n",
-                video_display, SDL_GetNumVideoDisplays() - 1);
-        video_display = 0;
-    }
-
     if (SDL_GetDisplayBounds(video_display, &bounds) < 0)
     {
         fprintf(stderr, "CenterWindow: Failed to read display bounds "
@@ -1086,8 +1112,19 @@
     *y = bounds.y + SDL_max((bounds.h - h) / 2, 0);
 }
 
-static void GetWindowPosition(int *x, int *y, int w, int h)
+void I_GetWindowPosition(int *x, int *y, int w, int h)
 {
+    // Check that video_display corresponds to a display that really exists,
+    // and if it doesn't, reset it.
+    if (video_display < 0 || video_display >= SDL_GetNumVideoDisplays())
+    {
+        fprintf(stderr,
+                "I_GetWindowPosition: We were configured to run on display #%d, "
+                "but it no longer exists (max %d). Moving to display 0.\n",
+                video_display, SDL_GetNumVideoDisplays() - 1);
+        video_display = 0;
+    }
+
     // in fullscreen mode, the window "position" still matters, because
     // we use it to control which display we run fullscreen on.
 
@@ -1114,7 +1151,7 @@
     else if (sscanf(window_position, "%i,%i", x, y) != 2)
     {
         // invalid format: revert to default
-        fprintf(stderr, "GetWindowPosition: invalid window_position setting\n");
+        fprintf(stderr, "I_GetWindowPosition: invalid window_position setting\n");
         *x = *y = SDL_WINDOWPOS_UNDEFINED;
     }
 }
@@ -1126,6 +1163,7 @@
     unsigned int rmask, gmask, bmask, amask;
     int unused_bpp;
     int window_flags = 0, renderer_flags = 0;
+    SDL_DisplayMode mode;
 
     w = window_width;
     h = window_height;
@@ -1155,7 +1193,7 @@
         }
     }
 
-    GetWindowPosition(&x, &y, w, h);
+    I_GetWindowPosition(&x, &y, w, h);
 
     // Create window and renderer contexts. We set the window title
     // later anyway and leave the window position "undefined". If
@@ -1183,7 +1221,19 @@
     // The SDL_RENDERER_TARGETTEXTURE flag is required to render the
     // intermediate texture into the upscaled texture.
     renderer_flags = SDL_RENDERER_TARGETTEXTURE;
+	
+    if (SDL_GetCurrentDisplayMode(video_display, &mode) != 0)
+    {
+        I_Error("Could not get display mode for video display #%d: %s",
+        video_display, SDL_GetError());
+    }
 
+    // Turn on vsync if we aren't in a -timedemo
+    if (!singletics && mode.refresh_rate > 0)
+    {
+        renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
+    }
+
     if (force_software_renderer)
     {
         renderer_flags |= SDL_RENDERER_SOFTWARE;
@@ -1210,6 +1260,12 @@
                              SCREENWIDTH,
                              EffectiveScreenHeight());
 
+    // Force integer scales for resolution-independent rendering.
+
+#if SDL_VERSION_ATLEAST(2, 0, 5)
+    SDL_RenderSetIntegerScale(renderer, integer_scaling);
+#endif
+
     // Blank out the full screen area in case there is any junk in
     // the borders that won't otherwise be overwritten.
 
@@ -1389,6 +1445,8 @@
     M_BindIntVariable("fullscreen",                &fullscreen);
     M_BindIntVariable("video_display",             &video_display);
     M_BindIntVariable("aspect_ratio_correct",      &aspect_ratio_correct);
+    M_BindIntVariable("integer_scaling",           &integer_scaling);
+    M_BindIntVariable("vga_porch_flash",           &vga_porch_flash);
     M_BindIntVariable("startup_delay",             &startup_delay);
     M_BindIntVariable("fullscreen_width",          &fullscreen_width);
     M_BindIntVariable("fullscreen_height",         &fullscreen_height);
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -90,6 +90,14 @@
 extern int screen_height;
 extern int fullscreen;
 extern int aspect_ratio_correct;
+extern int integer_scaling;
+extern int vga_porch_flash;
 extern int force_software_renderer;
+
+extern char *window_position;
+void I_GetWindowPosition(int *x, int *y, int w, int h);
+
+// Joystic/gamepad hysteresis
+extern unsigned int joywait;
 
 #endif
--- a/src/i_videohr.c
+++ b/src/i_videohr.c
@@ -21,6 +21,7 @@
 
 #include "doomtype.h"
 #include "i_timer.h"
+#include "i_video.h"
 
 // Palette fade-in takes two seconds
 
@@ -35,15 +36,18 @@
 
 boolean I_SetVideoModeHR(void)
 {
+    int x, y;
+
     if (SDL_Init(SDL_INIT_VIDEO) < 0)
     {
         return false;
     }
 
+    I_GetWindowPosition(&x, &y, HR_SCREENWIDTH, HR_SCREENHEIGHT);
+
     // Create screen surface at the native desktop pixel depth (bpp=0),
     // as we cannot trust true 8-bit to reliably work nowadays.
-    hr_screen = SDL_CreateWindow(window_title,
-        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+    hr_screen = SDL_CreateWindow(window_title, x, y,
         HR_SCREENWIDTH, HR_SCREENHEIGHT,
         0);
 
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -728,6 +728,18 @@
     CONFIG_VARIABLE_INT(aspect_ratio_correct),
 
     //!
+    // If non-zero, forces integer scales for resolution-independent rendering.
+    //
+
+    CONFIG_VARIABLE_INT(integer_scaling),
+
+    // If non-zero, any pillar/letter boxes drawn around the game area
+    // will "flash" when the game palette changes, simulating the VGA
+    // "porch"
+
+    CONFIG_VARIABLE_INT(vga_porch_flash),
+
+    //!
     // Window width when running in windowed mode.
     //
 
--- a/src/midifile.c
+++ b/src/midifile.c
@@ -33,19 +33,19 @@
 #pragma pack(push, 1)
 #endif
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     byte chunk_id[4];
     unsigned int chunk_size;
-} PACKEDATTR chunk_header_t;
+}) chunk_header_t;
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     chunk_header_t chunk_header;
     unsigned short format_type;
     unsigned short num_tracks;
     unsigned short time_division;
-} PACKEDATTR midi_header_t;
+}) midi_header_t;
 
 // haleyjd 09/09/10: packing off.
 #ifdef _MSC_VER
--- a/src/mus2mid.c
+++ b/src/mus2mid.c
@@ -53,7 +53,7 @@
 } midievent;
 
 // Structure to hold MUS file header
-typedef struct
+typedef PACKED_STRUCT (
 {
     byte id[4];
     unsigned short scorelength;
@@ -61,7 +61,7 @@
     unsigned short primarychannels;
     unsigned short secondarychannels;
     unsigned short instrumentcount;
-} PACKEDATTR musheader;
+}) musheader;
 
 // Standard MIDI type 0 header + track header
 static const byte midiheader[] =
--- a/src/resource.rc.in
+++ b/src/resource.rc.in
@@ -1,5 +1,7 @@
 1 ICON "@top_srcdir@/data/doom.ico"
 
+#include <winuser.h>
+
 CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@top_srcdir@/src/manifest.xml"
 
 1 VERSIONINFO
--- a/src/setup/display.c
+++ b/src/setup/display.c
@@ -66,6 +66,8 @@
 static char *video_driver = "";
 static char *window_position = "";
 static int aspect_ratio_correct = 1;
+static int integer_scaling = 0;
+static int vga_porch_flash = 0;
 static int force_software_renderer = 0;
 static int fullscreen = 1;
 static int fullscreen_width = 0, fullscreen_height = 0;
@@ -194,6 +196,9 @@
     TXT_AddWidgets(window,
         ar_checkbox = TXT_NewCheckBox("Fix aspect ratio",
                                       &aspect_ratio_correct),
+#if SDL_VERSION_ATLEAST(2, 0, 5)
+        TXT_NewCheckBox("Integer scaling", &integer_scaling),
+#endif
         TXT_If(gamemission == heretic || gamemission == hexen
             || gamemission == strife,
             TXT_NewCheckBox("Graphical startup", &graphical_startup)),
@@ -205,6 +210,8 @@
         TXT_NewCheckBox("Save screenshots in PNG format",
                         &png_screenshots),
 #endif
+        TXT_NewCheckBox("Flash borders (VGA porch emulation)",
+                        &vga_porch_flash),
         NULL);
 
     TXT_SignalConnect(ar_checkbox, "changed", GenerateSizesTable, sizes_table);
@@ -250,6 +257,7 @@
 void BindDisplayVariables(void)
 {
     M_BindIntVariable("aspect_ratio_correct",      &aspect_ratio_correct);
+    M_BindIntVariable("integer_scaling",           &integer_scaling);
     M_BindIntVariable("fullscreen",                &fullscreen);
     M_BindIntVariable("fullscreen_width",          &fullscreen_width);
     M_BindIntVariable("fullscreen_height",         &fullscreen_height);
@@ -260,6 +268,7 @@
     M_BindStringVariable("window_position",        &window_position);
     M_BindIntVariable("usegamma",                  &usegamma);
     M_BindIntVariable("png_screenshots",           &png_screenshots);
+    M_BindIntVariable("vga_porch_flash",           &vga_porch_flash);
     M_BindIntVariable("force_software_renderer",   &force_software_renderer);
     M_BindIntVariable("max_scaling_buffer_pixels", &max_scaling_buffer_pixels);
 
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@ -326,7 +326,7 @@
     {NULL, 0},
 };
 
-// http://www.8bitdo.com/nes30pro/
+// http://www.8bitdo.com/nes30pro/ and http://www.8bitdo.com/fc30pro/
 static const joystick_config_t nes30_pro_controller[] =
 {
     {"joystick_x_axis",        CREATE_HAT_AXIS(0, HAT_AXIS_HORIZONTAL)},
@@ -333,17 +333,19 @@
     {"joystick_y_axis",        CREATE_HAT_AXIS(0, HAT_AXIS_VERTICAL)},
     {"joyb_fire",              4},  // Y
     {"joyb_speed",             1},  // B
-    {"joyb_jump",              2},  // X
+    {"joyb_jump",              3},  // X
     {"joyb_use",               0},  // A
-    {"joyb_strafeleft",        8},  // L1
-    {"joyb_straferight",       9}, // R1
-    {"joyb_prevweapon",        6},  // L2
-    {"joyb_nextweapon",        7},  // R2
+    {"joyb_strafeleft",        6},  // L1
+    {"joyb_straferight",       7}, // R1
+    {"joyb_prevweapon",        8},  // L2
+    {"joyb_nextweapon",        9},  // R2
     {"joyb_menu_activate",     11}, // Start
+    {"joyb_toggle_automap",    10}, // Select
     {NULL, 0},
 };
 
 // http://www.8bitdo.com/sfc30/ or http://www.8bitdo.com/snes30/
+// and http://www.nes30.com/ and http://www.fc30.com/
 static const joystick_config_t sfc30_controller[] =
 {
     {"joystick_x_axis",        0},
@@ -452,7 +454,6 @@
     },
 
     // 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,
@@ -459,6 +460,68 @@
         nes30_pro_controller,
     },
 
+    // the above, NES variant, via USB on Linux/Raspbian (odd values)
+    {
+        "8Bitdo NES30 Pro*",
+        6, 15, 1,
+        nes30_pro_controller,
+    },
+
+    // the above, NES variant, connected over bluetooth
+    {
+        "8Bitdo NES30 Pro",
+        6, 16, 1,
+        nes30_pro_controller,
+    },
+
+    // 8bitdo NES30 Pro, in joystick mode (R1+Power), swaps the D-Pad
+    // and analog stick inputs.  Only applicable over Bluetooth. On USB,
+    // this mode registers the device as an Xbox 360 pad.
+    {
+        "8Bitdo NES30 Pro Joystick",
+        6, 16, 1,
+        nes30_pro_controller,
+    },
+
+    // variant of the above, via USB on Mac
+    // Note: untested, but theorized to exist based on us comparing
+    // a NES30 Pro tested on Linux with a FC30 Pro tested with Mac & Linux
+    {
+        "8Bitdo NES30 Pro",
+        4, 15, 1,
+        nes30_pro_controller,
+    },
+
+
+    // 8Bitdo FC30 Pro, http://8bitdo.cn/fc30pro/
+    // connected over bluetooth
+    {
+        "8Bitdo FC30 Pro",
+        4, 16, 1,
+        nes30_pro_controller,
+    },
+
+    // variant of the above, via USB on Linux/Raspbian
+    {
+        "8Bitdo FC30 Pro*",
+        6, 15, 1,
+        nes30_pro_controller,
+    },
+
+    // variant of the above, Linux/bluetooth
+    {
+        "8Bitdo FC30 Pro",
+	6, 16, 1,
+	nes30_pro_controller,
+    },
+
+    // variant of the above, via USB on Mac
+    {
+        "8Bitdo FC30 Pro",
+        4, 15, 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
@@ -495,6 +558,34 @@
         "SFC30 *",
         4, 12, 1,
         sfc30_controller,
+    },
+
+    // NES30 (not pro), tested in default and "hold R whilst turning on"
+    // mode, with whatever firmware it came with out of the box. Latter
+    // mode puts " Joystick" suffix on the name string
+    {
+        "8Bitdo NES30 GamePad*",
+        4, 16, 1,
+        sfc30_controller, // identical to SFC30
+    },
+    // FC30 variant of the above
+    {
+        "8Bitdo FC30 GamePad*",
+        4, 16, 1,
+        sfc30_controller, // identical to SFC30
+    },
+
+    // NES30 in USB mode
+    {
+        "NES30*",
+        4, 12, 1,
+        sfc30_controller, // identical to SFC30
+    },
+    // FC30 variant of the above
+    {
+        "FC30*",
+        4, 12, 1,
+        sfc30_controller, // identical to SFC30
     },
 };
 
--- a/src/setup/txt_joyaxis.c
+++ b/src/setup/txt_joyaxis.c
@@ -150,12 +150,12 @@
 
     for (i = 0; i < SDL_JoystickNumAxes(joystick_axis->joystick); ++i)
     {
-       //if (bad_axis[i])
-       //{
-       //    continue;
-       //}
-
         axis_value = SDL_JoystickGetAxis(joystick_axis->joystick, i);
+
+        if (joystick_axis->bad_axis[i])
+        {
+            continue;
+        }
 
         if (abs(axis_value) > best_value)
         {
--- a/src/strife/doomdata.h
+++ b/src/strife/doomdata.h
@@ -54,16 +54,16 @@
 
 
 // A single Vertex.
-typedef struct
+typedef PACKED_STRUCT (
 {
   short		x;
   short		y;
-} PACKEDATTR mapvertex_t;
+}) mapvertex_t;
 
 
 // A SideDef, defining the visual appearance of a wall,
 // by setting textures and offsets.
-typedef struct
+typedef PACKED_STRUCT (
 {
   short		textureoffset;
   short		rowoffset;
@@ -72,13 +72,13 @@
   char		midtexture[8];
   // Front sector, towards viewer.
   short		sector;
-} PACKEDATTR mapsidedef_t;
+}) mapsidedef_t;
 
 
 
 // A LineDef, as used for editing, and as input
 // to the BSP builder.
-typedef struct
+typedef PACKED_STRUCT (
 {
   short		v1;
   short		v2;
@@ -87,7 +87,7 @@
   short		tag;
   // sidenum[1] will be -1 if one sided
   short		sidenum[2];		
-} PACKEDATTR maplinedef_t;
+}) maplinedef_t;
 
 
 //
@@ -147,7 +147,7 @@
 
 
 // Sector definition, from editing.
-typedef	struct
+typedef	PACKED_STRUCT (
 {
   short		floorheight;
   short		ceilingheight;
@@ -156,20 +156,20 @@
   short		lightlevel;
   short		special;
   short		tag;
-} PACKEDATTR mapsector_t;
+}) mapsector_t;
 
 // SubSector, as generated by BSP.
-typedef struct
+typedef PACKED_STRUCT (
 {
   short		numsegs;
   // Index of first one, segs are stored sequentially.
   short		firstseg;	
-} PACKEDATTR mapsubsector_t;
+}) mapsubsector_t;
 
 
 // LineSeg, generated by splitting LineDefs
 // using partition lines selected by BSP builder.
-typedef struct
+typedef PACKED_STRUCT (
 {
   short		v1;
   short		v2;
@@ -177,7 +177,7 @@
   short		linedef;
   short		side;
   short		offset;
-} PACKEDATTR mapseg_t;
+}) mapseg_t;
 
 
 
@@ -186,7 +186,7 @@
 // Indicate a leaf.
 #define	NF_SUBSECTOR	0x8000
 
-typedef struct
+typedef PACKED_STRUCT (
 {
   // Partition line from (x,y) to x+dx,y+dy)
   short		x;
@@ -202,7 +202,7 @@
   // else it's a node of another subtree.
   unsigned short	children[2];
 
-} PACKEDATTR mapnode_t;
+}) mapnode_t;
 
 
 
@@ -209,7 +209,7 @@
 
 // Thing definition, position, orientation and type,
 // plus skill/visibility flags and attributes.
-typedef struct
+typedef PACKED_STRUCT (
 {
     short		x;
     short		y;
@@ -216,7 +216,7 @@
     short		angle;
     short		type;
     short		options;
-} PACKEDATTR mapthing_t;
+}) mapthing_t;
 
 
 
--- a/src/strife/m_saves.c
+++ b/src/strife/m_saves.c
@@ -25,6 +25,8 @@
 #include <win_opendir.h>
 #elif defined(__GNUC__) || defined(POSIX)
 #include <dirent.h>
+#elif defined(__WATCOMC__)
+#include <direct.h>
 #else
 #error Need an include for dirent.h!
 #endif
--- a/src/strife/p_setup.c
+++ b/src/strife/p_setup.c
@@ -670,11 +670,13 @@
 
     unsigned int rejectpad[4] =
     {
-        ((totallines * 4 + 3) & ~3) + 24,     // Size
+        0,                                    // Size
         0,                                    // Part of z_zone block header
         50,                                   // PU_LEVEL
         0x1d4a11                              // DOOM_CONST_ZONEID
     };
+
+    rejectpad[0] = ((totallines * 4 + 3) & ~3) + 24;
 
     // Copy values from rejectpad into the destination array.
 
--- a/src/strife/r_data.c
+++ b/src/strife/r_data.c
@@ -52,7 +52,7 @@
 // into the rectangular texture space using origin
 // and possibly other attributes.
 //
-typedef struct
+typedef PACKED_STRUCT (
 {
     short	originx;
     short	originy;
@@ -59,7 +59,7 @@
     short	patch;
     //short	stepdir;    // villsa [STRIFE] removed
     //short	colormap;   // villsa [STRIFE] removed
-} PACKEDATTR mappatch_t;
+}) mappatch_t;
 
 
 //
@@ -67,7 +67,7 @@
 // A DOOM wall texture is a list of patches
 // which are to be combined in a predefined order.
 //
-typedef struct
+typedef PACKED_STRUCT (
 {
     char		name[8];
     int			masked;	
@@ -76,7 +76,7 @@
     //int               obsolete;   // villsa [STRIFE] removed
     short		patchcount;
     mappatch_t	patches[1];
-} PACKEDATTR maptexture_t;
+}) maptexture_t;
 
 
 // A single patch from a texture definition,
--- a/src/v_patch.h
+++ b/src/v_patch.h
@@ -26,22 +26,22 @@
 // and we compose textures from the TEXTURE1/2 lists
 // of patches.
 
-typedef struct 
-{ 
-    short		width;		// bounding box size 
-    short		height; 
-    short		leftoffset;	// pixels to the left of origin 
-    short		topoffset;	// pixels below the origin 
+typedef PACKED_STRUCT (
+{
+    short		width;		// bounding box size
+    short		height;
+    short		leftoffset;	// pixels to the left of origin
+    short		topoffset;	// pixels below the origin
     int			columnofs[8];	// only [width] used
-    // the [0] is &columnofs[width] 
-} PACKEDATTR patch_t;
+    // the [0] is &columnofs[width]
+}) patch_t;
 
 // posts are runs of non masked source pixels
-typedef struct
+typedef PACKED_STRUCT (
 {
     byte		topdelta;	// -1 is the last post in a column
     byte		length; 	// length data bytes follows
-} PACKEDATTR post_t;
+}) post_t;
 
 // column_t is a list of 0 or more post_t, (byte)-1 terminated
 typedef post_t	column_t;
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -624,7 +624,7 @@
 // SCREEN SHOTS
 //
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     char		manufacturer;
     char		version;
@@ -648,7 +648,7 @@
     
     char		filler[58];
     unsigned char	data;		// unbounded
-} PACKEDATTR pcx_t;
+}) pcx_t;
 
 
 //
@@ -849,7 +849,16 @@
 
     if (i == 100)
     {
-        I_Error ("V_ScreenShot: Couldn't create a PCX");
+#ifdef HAVE_LIBPNG
+        if (png_screenshots)
+        {
+            I_Error ("V_ScreenShot: Couldn't create a PNG");
+        }
+        else
+#endif
+        {
+            I_Error ("V_ScreenShot: Couldn't create a PCX");
+        }
     }
 
 #ifdef HAVE_LIBPNG
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -35,21 +35,21 @@
 
 #include "w_wad.h"
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     // Should be "IWAD" or "PWAD".
     char		identification[4];
     int			numlumps;
     int			infotableofs;
-} PACKEDATTR wadinfo_t;
+}) wadinfo_t;
 
 
-typedef struct
+typedef PACKED_STRUCT (
 {
     int			filepos;
     int			size;
     char		name[8];
-} PACKEDATTR filelump_t;
+}) filelump_t;
 
 //
 // GLOBALS
--- a/textscreen/txt_fileselect.c
+++ b/textscreen/txt_fileselect.c
@@ -512,6 +512,45 @@
     return ZenityAvailable();
 }
 
+//
+// ExpandExtension
+// given an extension (like wad)
+// return a pointer to a string that is a case-insensitive
+// pattern representation (like [Ww][Aa][Dd])
+//
+static char *ExpandExtension(char *orig)
+{
+    int oldlen, newlen, i;
+    char *c, *newext = NULL;
+
+    oldlen = strlen(orig);
+    newlen = oldlen * 4; // pathological case: 'w' => '[Ww]'
+    newext = malloc(newlen+1);
+
+    if (newext == NULL)
+    {
+        return NULL;
+    }
+
+    c = newext;
+    for (i = 0; i < oldlen; ++i)
+    {
+        if (isalpha(orig[i]))
+        {
+            *c++ = '[';
+            *c++ = tolower(orig[i]);
+            *c++ = toupper(orig[i]);
+            *c++ = ']';
+        }
+        else
+        {
+            *c++ = orig[i];
+        }
+    }
+    *c = '\0';
+    return newext;
+}
+
 char *TXT_SelectFile(char *window_title, char **extensions)
 {
     unsigned int i;
@@ -525,7 +564,7 @@
         return NULL;
     }
 
-    argv = calloc(4 + NumExtensions(extensions), sizeof(char *));
+    argv = calloc(5 + NumExtensions(extensions), sizeof(char *));
     argv[0] = ZENITY_BINARY;
     argv[1] = "--file-selection";
     argc = 2;
@@ -547,12 +586,20 @@
     {
         for (i = 0; extensions[i] != NULL; ++i)
         {
-            len = 30 + strlen(extensions[i]) * 2;
-            argv[argc] = malloc(len);
-            TXT_snprintf(argv[argc], len, "--file-filter=.%s | *.%s",
-                         extensions[i], extensions[i]);
-            ++argc;
+            char * newext = ExpandExtension(extensions[i]);
+            if (newext)
+            {
+                len = 30 + strlen(extensions[i]) + strlen(newext);
+                argv[argc] = malloc(len);
+                TXT_snprintf(argv[argc], len, "--file-filter=.%s | *.%s",
+                             extensions[i], newext);
+                ++argc;
+                free(newext);
+            }
         }
+
+        argv[argc] = strdup("--file-filter=*.* | *.*");
+        ++argc;
     }
 
     argv[argc] = NULL;
--- a/textscreen/txt_window.c
+++ b/textscreen/txt_window.c
@@ -539,6 +539,7 @@
     {
         fprintf(stderr,
                 "xdg-utils is not installed. Can't open this URL:\n%s\n", url);
+        free(cmd);
         return;
     }