diff --git a/debian/docs b/debian/docs
index f3d4ef20e5a4ce3dd58bd8cf313bd95fcd7ec564..b43bf86b50fd8d3529a0dc062c30006ed38f309e 100644
--- a/debian/docs
+++ b/debian/docs
@@ -1,2 +1 @@
-readme.txt
-readme.txt
+README.md
diff --git a/src/Makefile b/src/Makefile
index 01f7a84c7e3685b0c101f6a62ebc96b86f98093d..76f013c5285aaf684c2517cb0cf05a0069f08d42 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -179,6 +179,9 @@ endif
 
 ifdef LINUX
 UNIXCOMMON=1
+ifndef NOGME
+HAVE_LIBGME=1
+endif
 endif
 
 ifdef SOLARIS
@@ -315,6 +318,13 @@ LIBS+=$(PNG_LDFLAGS)
 CFLAGS+=$(PNG_CFLAGS)
 endif
 
+ZLIB_PKGCONFIG?=zlib
+ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags)
+ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs)
+
+LIBS+=$(ZLIB_LDFLAGS)
+CFLAGS+=$(ZLIB_CFLAGS)
+
 ifdef HAVE_LIBGME
 OPTS+=-DHAVE_LIBGME
 
diff --git a/src/android/i_system.c b/src/android/i_system.c
index 150cbd505c1d40b40c1e5ac69bc911ec8eb849df..58fca7c1943448ff81ede9a67854f3791c93bc00 100644
--- a/src/android/i_system.c
+++ b/src/android/i_system.c
@@ -258,6 +258,18 @@ INT32 I_PutEnv(char *variable)
   return -1;
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 void I_RegisterSysCommands(void) {}
 
 #include "../sdl/dosstr.c"
diff --git a/src/console.c b/src/console.c
index 025bc1c19f2068812cf14ade4740eaeeaa3587c7..70f8ab6f8db5923b4a81e5d7768510ff0e81137b 100644
--- a/src/console.c
+++ b/src/console.c
@@ -84,19 +84,23 @@ UINT32 con_scalefactor;            // text size scale factor
 
 // hold 32 last lines of input for history
 #define CON_MAXPROMPTCHARS 256
-#define CON_PROMPTCHAR '>'
+#define CON_PROMPTCHAR '$'
 
 static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines
 
 static INT32 inputline;    // current input line number
 static INT32 inputhist;    // line number of history input line to restore
-static size_t input_cx;  // position in current input line
+static size_t input_cur; // position of cursor in line
+static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected")
+static size_t input_len; // length of current line, used to bound cursor and such
+// notice: input does NOT include the "$" at the start of the line. - 11/3/16
 
 // protos.
 static void CON_InputInit(void);
 static void CON_RecalcSize(void);
 
 static void CONS_hudlines_Change(void);
+static void CONS_backcolor_Change(void);
 static void CON_DrawBackpic(patch_t *pic, INT32 startx, INT32 destwidth);
 //static void CON_DrawBackpic2(pic_t *pic, INT32 startx, INT32 destwidth);
 
@@ -129,10 +133,11 @@ static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}
 // whether to use console background picture, or translucent mode
 static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 
-static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Orange"},
-												{2, "Blue"}, {3, "Green"}, {4, "Gray"},
-												{5, "Red"}, {0, NULL}};
-consvar_t cons_backcolor = {"con_backcolor", "3", CV_SAVE, backcolor_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, 	{1, "Gray"},	{2, "Brown"},
+												{3, "Red"},		{4, "Orange"},	{5, "Yellow"},
+												{6, "Green"},	{7, "Blue"},	{8,	"Cyan"},
+												{0, NULL}};
+consvar_t cons_backcolor = {"con_backcolor", "Green", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL};
 
 static void CON_Print(char *msg);
 
@@ -219,8 +224,9 @@ static void CONS_Bind_f(void)
 //                          CONSOLE SETUP
 //======================================================================
 
-// Prepare a colormap for GREEN ONLY translucency over background
-//
+// Font colormap colors
+// TODO: This could probably be improved somehow...
+// These colormaps are 99% identical, with just a few changed bytes
 UINT8 *yellowmap;
 UINT8 *purplemap;
 UINT8 *lgreenmap;
@@ -229,44 +235,49 @@ UINT8 *graymap;
 UINT8 *redmap;
 UINT8 *orangemap;
 
-// Console BG colors
-UINT8 *cwhitemap;
-UINT8 *corangemap;
-UINT8 *cbluemap;
-UINT8 *cgreenmap;
-UINT8 *cgraymap;
-UINT8 *credmap;
+// Console BG color
+UINT8 *consolebgmap = NULL;
 
-void CON_ReSetupBackColormap(UINT16 num)
+void CON_SetupBackColormap(void)
 {
-	UINT16 i, j;
-	UINT8 k;
-	UINT8 *pal = W_CacheLumpName(R_GetPalname(num), PU_CACHE);
+	UINT16 i, palsum;
+	UINT8 j, palindex;
+	UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);
+
+	if (!consolebgmap)
+		consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
+
+	switch (cons_backcolor.value)
+	{
+		case 0:		palindex = 15; 	break; // White
+		case 1:		palindex = 31;	break; // Gray
+		case 2:		palindex = 63;	break; // Brown
+		case 3:		palindex = 143;	break; // Red
+		case 4:		palindex = 95;	break; // Orange
+		case 5:		palindex = 111;	break; // Yellow
+		case 6:		palindex = 175;	break; // Green
+		case 7:		palindex = 239;	break; // Blue
+		case 8:		palindex = 219;	break; // Cyan
+		// Default green
+		default:	palindex = 175; break;
+}
 
-	// setup the green translucent background colormaps
-	for (i = 0, k = 0; i < 768; i += 3, k++)
+	// setup background colormap
+	for (i = 0, j = 0; i < 768; i += 3, j++)
 	{
-		j = pal[i] + pal[i+1] + pal[i+2];
-		cwhitemap[k] = (UINT8)(15 - (j>>6));
-		corangemap[k] = (UINT8)(95 - (j>>6));
-		cbluemap[k] = (UINT8)(239 - (j>>6));
-		cgreenmap[k] = (UINT8)(175 - (j>>6));
-		cgraymap[k] = (UINT8)(31 - (j>>6));
-		credmap[k] = (UINT8)(143 - (j>>6));
+		palsum = (pal[i] + pal[i+1] + pal[i+2]) >> 6;
+		consolebgmap[j] = (UINT8)(palindex - palsum);
 	}
 }
 
-static void CON_SetupBackColormap(void)
+static void CONS_backcolor_Change(void)
 {
-	INT32 i, j, k;
-	UINT8 *pal;
+	CON_SetupBackColormap();
+}
 
-	cwhitemap   = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
-	corangemap  = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
-	cbluemap    = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
-	cgreenmap   = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
-	cgraymap    = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
-	credmap     = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
+static void CON_SetupColormaps(void)
+{
+	INT32 i;
 
 	yellowmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
 	graymap   = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
@@ -276,20 +287,6 @@ static void CON_SetupBackColormap(void)
 	redmap    = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
 	orangemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
 
-	pal = W_CacheLumpName("PLAYPAL", PU_CACHE);
-
-	// setup the green translucent background colormaps
-	for (i = 0, k = 0; i < 768; i += 3, k++)
-	{
-		j = pal[i] + pal[i+1] + pal[i+2];
-		cwhitemap[k] = (UINT8)(15 - (j>>6));
-		corangemap[k] = (UINT8)(95 - (j>>6));
-		cbluemap[k] = (UINT8)(239 - (j>>6));
-		cgreenmap[k] = (UINT8)(175 - (j>>6));
-		cgraymap[k] = (UINT8)(31 - (j>>6));
-		credmap[k] = (UINT8)(143 - (j>>6));
-	}
-
 	// setup the other colormaps, for console text
 
 	// these don't need to be aligned, unless you convert the
@@ -320,6 +317,9 @@ static void CON_SetupBackColormap(void)
 	redmap[9]    = (UINT8)127;
 	orangemap[3] = (UINT8)85;
 	orangemap[9] = (UINT8)90;
+
+	// Init back colormap
+	CON_SetupBackColormap();
 }
 
 // Setup the console text buffer
@@ -343,7 +343,7 @@ void CON_Init(void)
 	con_width = 0;
 	CON_RecalcSize();
 
-	CON_SetupBackColormap();
+	CON_SetupColormaps();
 
 	//note: CON_Ticker should always execute at least once before D_Display()
 	con_clipviewtop = -1; // -1 does not clip
@@ -386,14 +386,10 @@ void CON_Init(void)
 //
 static void CON_InputInit(void)
 {
-	INT32 i;
-
 	// prepare the first prompt line
 	memset(inputlines, 0, sizeof (inputlines));
-	for (i = 0; i < 32; i++)
-		inputlines[i][0] = CON_PROMPTCHAR;
 	inputline = 0;
-	input_cx = 1;
+	input_cur = input_sel = input_len = 0;
 }
 
 //======================================================================
@@ -618,13 +614,91 @@ void CON_Ticker(void)
 	}
 }
 
+//
+// ----
+//
+// Shortcuts for adding and deleting characters, strings, and sections
+// Necessary due to moving cursor
+//
+
+static void CON_InputClear(void)
+{
+	memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
+	input_cur = input_sel = input_len = 0;
+}
+
+static void CON_InputSetString(const char *c)
+{
+	memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
+	strcpy(inputlines[inputline], c);
+	input_cur = input_sel = input_len = strlen(c);
+}
+
+static void CON_InputAddString(const char *c)
+{
+	size_t csize = strlen(c);
+	if (input_len + csize > CON_MAXPROMPTCHARS-1)
+		return;
+	if (input_cur != input_len)
+		memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur);
+	memcpy(&inputlines[inputline][input_cur], c, csize);
+	input_len += csize;
+	input_sel = (input_cur += csize);
+}
+
+static void CON_InputDelSelection(void)
+{
+	size_t start, end, len;
+	if (input_cur > input_sel)
+	{
+		start = input_sel;
+		end = input_cur;
+	}
+	else
+	{
+		start = input_cur;
+		end = input_sel;
+	}
+	len = (end - start);
+
+	if (end != input_len)
+		memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end);
+	memset(&inputlines[inputline][input_len - len], 0, len);
+
+	input_len -= len;
+	input_sel = input_cur = start;
+}
+
+static void CON_InputAddChar(char c)
+{
+	if (input_len >= CON_MAXPROMPTCHARS-1)
+		return;
+	if (input_cur != input_len)
+		memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur);
+	inputlines[inputline][input_cur++] = c;
+	inputlines[inputline][++input_len] = 0;
+	input_sel = input_cur;
+}
+
+static void CON_InputDelChar(void)
+{
+	if (!input_cur)
+		return;
+	if (input_cur != input_len)
+		memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur);
+	inputlines[inputline][--input_len] = 0;
+	input_sel = --input_cur;
+}
+
+//
+// ----
+//
+
 // Handles console key input
 //
 boolean CON_Responder(event_t *ev)
 {
-	static boolean consdown;
-	static boolean shiftdown;
-	static boolean ctrldown;
+	static UINT8 consdown = false; // console is treated differently due to rare usage
 
 	// sequential completions a la 4dos
 	static char completion[80];
@@ -639,13 +713,8 @@ boolean CON_Responder(event_t *ev)
 	// let go keyup events, don't eat them
 	if (ev->type != ev_keydown && ev->type != ev_console)
 	{
-		if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
-			shiftdown = false;
-		else if (ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL)
-			ctrldown = false;
-		else if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
+		if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
 			consdown = false;
-
 		return false;
 	}
 
@@ -684,94 +753,110 @@ boolean CON_Responder(event_t *ev)
 			consoletoggle = true;
 			return true;
 		}
-
-	}
-
-	// eat shift only if console active
-	if (key == KEY_LSHIFT || key == KEY_RSHIFT)
-	{
-		shiftdown = true;
-		return true;
 	}
 
-	// same for ctrl
-	if (key == KEY_LCTRL || key == KEY_RCTRL)
-	{
-		ctrldown = true;
+	// Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
+	if (key == KEY_LSHIFT || key == KEY_RSHIFT
+	 || key == KEY_LCTRL || key == KEY_RCTRL
+	 || key == KEY_LALT || key == KEY_RALT)
 		return true;
-	}
 
-	// command completion forward (tab) and backward (shift-tab)
-	if (key == KEY_TAB)
+	// ctrl modifier -- changes behavior, adds shortcuts
+	if (ctrldown)
 	{
 		// show all cvars/commands that match what we have inputted
-		if (ctrldown)
+		if (key == KEY_TAB)
 		{
-			UINT32 i;
-			size_t stop = input_cx - 1;
-			char nameremainder[255];
+			size_t i, len;
 
-			if (input_cx < 2 || strlen(inputlines[inputline]+1) >= 80)
-				return true;
-
-			strcpy(completion, inputlines[inputline]+1);
-
-			// trimming: stop at the first newline
-			for (i = 0; i < input_cx - 1; ++i)
+			if (!completion[0])
 			{
-				if (completion[i] == ' ')
-				{
-					completion[i] = '\0';
-					stop = i;
-					break;
-				}
+				if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
+					return true;
+				strcpy(completion, inputlines[inputline]);
+				comskips = varskips = 0;
 			}
-
-			i = 0;
+			len = strlen(completion);
 
 			//first check commands
 			CONS_Printf("\nCommands:\n");
-
-			for (cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, i))
-			{
-				strncpy(nameremainder, cmd+(stop), strlen(cmd)-(stop));
-				nameremainder[strlen(cmd)-(stop)] = '\0';
-
-				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, nameremainder);
-				++i;
-			}
-			if (i == 0)
-				CONS_Printf("  (none)\n");
-
-			i = 0;
+			for (i = 0, cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, ++i))
+				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, cmd+len);
+			if (i == 0) CONS_Printf("  (none)\n");
 
 			//now we move on to CVARs
 			CONS_Printf("Variables:\n");
+			for (i = 0, cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, ++i))
+				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, cmd+len);
+			if (i == 0) CONS_Printf("  (none)\n");
 
-			for (cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, i))
-			{
-				strncpy(nameremainder, cmd+(stop), strlen(cmd)-(stop));
-				nameremainder[strlen(cmd)-(stop)] = '\0';
+			return true;
+		}
+		// ---
 
-				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, nameremainder);
-				++i;
-			}
-			if (i == 0)
-				CONS_Printf("  (none)\n");
+		if (key == KEY_HOME) // oldest text in buffer
+		{
+			con_scrollup = (con_totallines-((con_curlines-16)>>3));
+			return true;
+		}
+		else if (key == KEY_END) // most recent text in buffer
+		{
+			con_scrollup = 0;
+			return true;
+		}
 
+		if (key == 'x' || key == 'X')
+		{
+			if (input_sel > input_cur)
+				I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
+			else
+				I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
+			CON_InputDelSelection();
+			completion[0] = 0;
+			return true;
+		}
+		else if (key == 'c' || key == 'C')
+		{
+			if (input_sel > input_cur)
+				I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
+			else
+				I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
+			return true;
+		}
+		else if (key == 'v' || key == 'V')
+		{
+			const char *paste = I_ClipboardPaste();
+			if (input_sel != input_cur)
+				CON_InputDelSelection();
+			if (paste != NULL)
+				CON_InputAddString(paste);
+			completion[0] = 0;
 			return true;
 		}
 
+		// Select all
+		if (key == 'a' || key == 'A')
+		{
+			input_sel = 0;
+			input_cur = input_len;
+			return true;
+		}
+
+		// don't eat the key
+		return false;
+	}
+
+	// command completion forward (tab) and backward (shift-tab)
+	if (key == KEY_TAB)
+	{
 		// sequential command completion forward and backward
 
 		// remember typing for several completions (a-la-4dos)
-		if (inputlines[inputline][input_cx-1] != ' ')
+		if (!completion[0])
 		{
-			if (strlen(inputlines[inputline]+1) < 80)
-				strcpy(completion, inputlines[inputline]+1);
-			else
-				completion[0] = 0;
-
+			if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
+				return true;
+			strcpy(completion, inputlines[inputline]);
 			comskips = varskips = 0;
 		}
 		else
@@ -783,37 +868,26 @@ boolean CON_Responder(event_t *ev)
 					if (--varskips < 0)
 						comskips = -comskips - 2;
 				}
-				else if (comskips > 0)
-					comskips--;
+				else if (comskips > 0) comskips--;
 			}
 			else
 			{
-				if (comskips < 0)
-					varskips++;
-				else
-					comskips++;
+				if (comskips < 0) varskips++;
+				else              comskips++;
 			}
 		}
 
 		if (comskips >= 0)
 		{
 			cmd = COM_CompleteCommand(completion, comskips);
-			if (!cmd)
-				// dirty: make sure if comskips is zero, to have a neg value
+			if (!cmd) // dirty: make sure if comskips is zero, to have a neg value
 				comskips = -comskips - 1;
 		}
 		if (comskips < 0)
 			cmd = CV_CompleteVar(completion, varskips);
 
 		if (cmd)
-		{
-			memset(inputlines[inputline]+1, 0, CON_MAXPROMPTCHARS-1);
-			strcpy(inputlines[inputline]+1, cmd);
-			input_cx = strlen(cmd) + 1;
-			inputlines[inputline][input_cx] = ' ';
-			input_cx++;
-			inputlines[inputline][input_cx] = 0;
-		}
+			CON_InputSetString(va("%s ", cmd));
 		else
 		{
 			if (comskips > 0)
@@ -839,47 +913,80 @@ boolean CON_Responder(event_t *ev)
 		return true;
 	}
 
-	if (key == KEY_HOME) // oldest text in buffer
+	if (key == KEY_LEFTARROW)
+	{
+		if (input_cur != 0)
+			--input_cur;
+		if (!shiftdown)
+			input_sel = input_cur;
+		return true;
+	}
+	else if (key == KEY_RIGHTARROW)
+	{
+		if (input_cur < input_len)
+			++input_cur;
+		if (!shiftdown)
+			input_sel = input_cur;
+		return true;
+	}
+	else if (key == KEY_HOME)
 	{
-		con_scrollup = (con_totallines-((con_curlines-16)>>3));
+		input_cur = 0;
+		if (!shiftdown)
+			input_sel = input_cur;
 		return true;
 	}
-	else if (key == KEY_END) // most recent text in buffer
+	else if (key == KEY_END)
 	{
-		con_scrollup = 0;
+		input_cur = input_len;
+		if (!shiftdown)
+			input_sel = input_cur;
 		return true;
 	}
 
+	// At this point we're messing with input
+	// Clear completion
+	completion[0] = 0;
+
 	// command enter
 	if (key == KEY_ENTER)
 	{
-		if (input_cx < 2)
+		if (!input_len)
 			return true;
 
 		// push the command
-		COM_BufAddText(inputlines[inputline]+1);
+		COM_BufAddText(inputlines[inputline]);
 		COM_BufAddText("\n");
 
-		CONS_Printf("%s\n", inputlines[inputline]);
+		CONS_Printf("\x86""%c""\x80""%s\n", CON_PROMPTCHAR, inputlines[inputline]);
 
 		inputline = (inputline+1) & 31;
 		inputhist = inputline;
-
-		memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
-		inputlines[inputline][0] = CON_PROMPTCHAR;
-		input_cx = 1;
+		CON_InputClear();
 
 		return true;
 	}
 
-	// backspace command prompt
-	if (key == KEY_BACKSPACE)
+	// backspace and delete command prompt
+	if (input_sel != input_cur)
 	{
-		if (input_cx > 1)
+		if (key == KEY_BACKSPACE || key == KEY_DEL)
 		{
-			input_cx--;
-			inputlines[inputline][input_cx] = 0;
+			CON_InputDelSelection();
+			return true;
 		}
+	}
+	else if (key == KEY_BACKSPACE)
+	{
+		CON_InputDelChar();
+		return true;
+	}
+	else if (key == KEY_DEL)
+	{
+		if (input_cur == input_len)
+			return true;
+		++input_cur;
+		CON_InputDelChar();
 		return true;
 	}
 
@@ -888,18 +995,15 @@ boolean CON_Responder(event_t *ev)
 	{
 		// copy one of the previous inputlines to the current
 		do
-		{
 			inputhist = (inputhist - 1) & 31; // cycle back
-		} while (inputhist != inputline && !inputlines[inputhist][1]);
+		while (inputhist != inputline && !inputlines[inputhist][0]);
 
 		// stop at the last history input line, which is the
 		// current line + 1 because we cycle through the 32 input lines
 		if (inputhist == inputline)
 			inputhist = (inputline + 1) & 31;
 
-		M_Memcpy(inputlines[inputline], inputlines[inputhist], CON_MAXPROMPTCHARS);
-		input_cx = strlen(inputlines[inputline]);
-
+		CON_InputSetString(inputlines[inputhist]);
 		return true;
 	}
 
@@ -909,23 +1013,14 @@ boolean CON_Responder(event_t *ev)
 		if (inputhist == inputline)
 			return true;
 		do
-		{
 			inputhist = (inputhist + 1) & 31;
-		} while (inputhist != inputline && !inputlines[inputhist][1]);
-
-		memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
+		while (inputhist != inputline && !inputlines[inputhist][0]);
 
 		// back to currentline
 		if (inputhist == inputline)
-		{
-			inputlines[inputline][0] = CON_PROMPTCHAR;
-			input_cx = 1;
-		}
+			CON_InputClear();
 		else
-		{
-			strcpy(inputlines[inputline], inputlines[inputhist]);
-			input_cx = strlen(inputlines[inputline]);
-		}
+			CON_InputSetString(inputlines[inputhist]);
 		return true;
 	}
 
@@ -950,15 +1045,12 @@ boolean CON_Responder(event_t *ev)
 		return false;
 
 	// add key to cmd line here
-	if (input_cx < CON_MAXPROMPTCHARS)
-	{
-		if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers
-			key = key + 'a' - 'A';
+	if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers
+		key = key + 'a' - 'A';
 
-		inputlines[inputline][input_cx] = (char)key;
-		inputlines[inputline][input_cx + 1] = 0;
-		input_cx++;
-	}
+	if (input_sel != input_cur)
+		CON_InputDelSelection();
+	CON_InputAddChar(key);
 
 	return true;
 }
@@ -1242,26 +1334,89 @@ void CONS_Error(const char *msg)
 //
 static void CON_DrawInput(void)
 {
-	char *p;
-	size_t c;
-	INT32 x, y;
 	INT32 charwidth = (INT32)con_scalefactor << 3;
-
-	// input line scrolls left if it gets too long
-	p = inputlines[inputline];
-	if (input_cx >= con_width-11)
-		p += input_cx - (con_width-11) + 1;
+	const char *p = inputlines[inputline];
+	size_t c, clen, cend;
+	UINT8 lellip = 0, rellip = 0;
+	INT32 x, y, i;
 
 	y = con_curlines - 12 * con_scalefactor;
+	x = charwidth*2;
 
-	for (c = 0, x = charwidth; c < con_width-11; c++, x += charwidth)
-		V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+	clen = con_width-13;
 
-	// draw the blinking cursor
-	//
-	x = ((input_cx >= con_width-11) ? (INT32)(con_width-11) : (INT32)((input_cx + 1)) * charwidth);
-	if (con_tick < 4)
-		V_DrawCharacter(x, y, '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+	if (input_len <= clen)
+	{
+		c = 0;
+		clen = input_len;
+	}
+	else // input line scrolls left if it gets too long
+	{
+		clen -= 2; // There will always be some extra truncation -- but where is what we'll find out
+
+		if (input_cur <= clen/2)
+		{
+			// Close enough to right edge to show all
+			c = 0;
+			// Always will truncate right side from this position, so always draw right ellipsis
+			rellip = 1;
+		}
+		else
+		{
+			// Cursor in the middle (or right side) of input
+			// Move over for the ellipsis
+			c = input_cur - (clen/2) + 2;
+			x += charwidth*2;
+			lellip = 1;
+
+			if (c + clen >= input_len)
+			{
+				// Cursor in the right side of input
+				// We were too far over, so move back
+				c = input_len - clen;
+			}
+			else
+			{
+				// Cursor in the middle -- ellipses on both sides
+				clen -= 2;
+				rellip = 1;
+			}
+		}
+	}
+
+	if (lellip)
+	{
+		x -= charwidth*3;
+		if (input_sel < c)
+			V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 107 | V_NOSCALESTART);
+		for (i = 0; i < 3; ++i, x += charwidth)
+			V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
+	}
+	else
+		V_DrawCharacter(x-charwidth, y, CON_PROMPTCHAR | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
+
+	for (cend = c + clen; c < cend; ++c, x += charwidth)
+	{
+		if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c))
+		{
+			V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 107 | V_NOSCALESTART);
+			V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, !cv_allcaps.value);
+		}
+		else
+			V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+
+		if (c == input_cur && con_tick >= 4)
+			V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+	}
+	if (cend == input_cur && con_tick >= 4)
+		V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+	if (rellip)
+	{
+		if (input_sel > cend)
+			V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 107 | V_NOSCALESTART);
+		for (i = 0; i < 3; ++i, x += charwidth)
+			V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
+	}
 }
 
 // draw the last lines of console text to the top of the screen
@@ -1417,7 +1572,7 @@ static void CON_DrawConsole(void)
 	{
 		// inu: no more width (was always 0 and vid.width)
 		if (rendermode != render_none)
-			V_DrawFadeConsBack(con_curlines, cons_backcolor.value); // translucent background
+			V_DrawFadeConsBack(con_curlines); // translucent background
 	}
 
 	// draw console text lines from top to bottom
diff --git a/src/console.h b/src/console.h
index 47af65e21226319cd051712efdc329677b91fbea..8cf6483ff697914cff53b85ef7dbac5d0717c2c6 100644
--- a/src/console.h
+++ b/src/console.h
@@ -40,11 +40,10 @@ extern consvar_t cons_backcolor;
 
 extern UINT8 *yellowmap, *purplemap, *lgreenmap, *bluemap, *graymap, *redmap, *orangemap;
 
-// Console bg colors:
-extern UINT8 *cwhitemap, *corangemap, *cbluemap, *cgreenmap, *cgraymap,
-	*credmap;
+// Console bg color (auto updated to match)
+extern UINT8 *consolebgmap;
 
-void CON_ReSetupBackColormap(UINT16 num);
+void CON_SetupBackColormap(void);
 void CON_ClearHUD(void); // clear heads up messages
 
 void CON_Ticker(void);
diff --git a/src/d_main.c b/src/d_main.c
index b61ec41435f08f991d1d5bcaf82f0bbfadb2a620..2caf50087407c4f60ac8f6e1d0ee2ef2c2176e27 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -73,6 +73,7 @@ int	snprintf(char *str, size_t n, const char *fmt, ...);
 #include "dehacked.h" // Dehacked list test
 #include "m_cond.h" // condition initialization
 #include "fastcmp.h"
+#include "keys.h"
 
 #ifdef CMAKECONFIG
 #include "config.h"
@@ -176,6 +177,38 @@ void D_PostEvent(const event_t *ev)
 void D_PostEvent_end(void) {};
 #endif
 
+// modifier keys
+UINT8 shiftdown = 0; // 0x1 left, 0x2 right
+UINT8 ctrldown = 0; // 0x1 left, 0x2 right
+UINT8 altdown = 0; // 0x1 left, 0x2 right
+//
+// D_ModifierKeyResponder
+// Sets global shift/ctrl/alt variables, never actually eats events
+//
+static inline void D_ModifierKeyResponder(event_t *ev)
+{
+	if (ev->type == ev_keydown) switch (ev->data1)
+	{
+		case KEY_LSHIFT: shiftdown |= 0x1; return;
+		case KEY_RSHIFT: shiftdown |= 0x2; return;
+		case KEY_LCTRL: ctrldown |= 0x1; return;
+		case KEY_RCTRL: ctrldown |= 0x2; return;
+		case KEY_LALT: altdown |= 0x1; return;
+		case KEY_RALT: altdown |= 0x2; return;
+		default: return;
+	}
+	else if (ev->type == ev_keyup) switch (ev->data1)
+	{
+		case KEY_LSHIFT: shiftdown &= ~0x1; return;
+		case KEY_RSHIFT: shiftdown &= ~0x2; return;
+		case KEY_LCTRL: ctrldown &= ~0x1; return;
+		case KEY_RCTRL: ctrldown &= ~0x2; return;
+		case KEY_LALT: altdown &= ~0x1; return;
+		case KEY_RALT: altdown &= ~0x2; return;
+		default: return;
+	}
+}
+
 //
 // D_ProcessEvents
 // Send all the events of the given timestamp down the responder chain
@@ -188,6 +221,9 @@ void D_ProcessEvents(void)
 	{
 		ev = &events[eventtail];
 
+		// Set global shift/ctrl/alt down variables
+		D_ModifierKeyResponder(ev); // never eats events
+
 		// Screenshots over everything so that they can be taken anywhere.
 		if (M_ScreenshotResponder(ev))
 			continue; // ate the event
diff --git a/src/djgppdos/i_system.c b/src/djgppdos/i_system.c
index 854d68f4d5b98ac3e145468ef3eaac4949af69a6..dae9ed16e3b426d195bf91c8ee62890fc42d7f48 100644
--- a/src/djgppdos/i_system.c
+++ b/src/djgppdos/i_system.c
@@ -1721,6 +1721,18 @@ INT32 I_PutEnv(char *variable)
 	return putenv(variable);
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 const CPUInfoFlags *I_CPUInfo(void)
 {
 	static CPUInfoFlags DOS_CPUInfo;
diff --git a/src/doomdef.h b/src/doomdef.h
index fb8ab1ca270aa10d6adc856665f79f762ccfa79c..4b2d8c737b8dcc703c971ef122c4b9fdeb4740c8 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -394,6 +394,9 @@ extern INT32 cv_debug;
 // Misc stuff for later...
 // =======================
 
+// Modifier key variables, accessible anywhere
+extern UINT8 shiftdown, ctrldown, altdown;
+
 // if we ever make our alloc stuff...
 #define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL)
 
diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c
index c1e48b60bb0239de595447dc9999ecb4ec949073..a3fe3077c2a46f6956a5723eb1de40127da7e798 100644
--- a/src/dummy/i_system.c
+++ b/src/dummy/i_system.c
@@ -162,6 +162,18 @@ INT32 I_PutEnv(char *variable)
 	return -1;
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 void I_RegisterSysCommands(void) {}
 
 #include "../sdl/dosstr.c"
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index 60183b58e72a168ceea2a2971f6b18f3e8fe5831..f23753ee53ff4ef65833cd3c661b80391e45526a 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -656,6 +656,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
 {
 	FOutVector v[4];
 	FSurfaceInfo Surf;
+	float sdupx, sdupy;
 
 	if (w < 0 || h < 0)
 		return; // consistency w/ software
@@ -664,10 +665,16 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
 //  | /|
 //  |/ |
 //  0--1
-	v[0].x = v[3].x = (x - 160.0f)/160.0f;
-	v[2].x = v[1].x = ((x+w) - 160.0f)/160.0f;
-	v[0].y = v[1].y = -(y - 100.0f)/100.0f;
-	v[2].y = v[3].y = -((y+h) - 100.0f)/100.0f;
+	sdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f;
+	sdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f;
+
+	if (color & V_NOSCALESTART)
+		sdupx = sdupy = 2.0f;
+
+	v[0].x = v[3].x = (x*sdupx)/vid.width - 1;
+	v[2].x = v[1].x = (x*sdupx + w*sdupx)/vid.width - 1;
+	v[0].y = v[1].y = 1-(y*sdupy)/vid.height;
+	v[2].y = v[3].y = 1-(y*sdupy + h*sdupy)/vid.height;
 
 	//Hurdler: do we still use this argb color? if not, we should remove it
 	v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //;
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 8e48ec110388c6f3082b7a0b928a7b83c58af81b..4ec2e346a54b227f806ecf210e84085cc16e1a4e 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -308,6 +308,23 @@ static md2_model_t *md2_readModel(const char *filename)
 
 	model->header.numSkins = 1;
 
+#define MD2LIMITCHECK(field, max, msgname) \
+	if (field > max) \
+	{ \
+		CONS_Alert(CONS_ERROR, "md2_readModel: %s has too many " msgname " (# found: %d, maximum: %d)\n", filename, field, max); \
+		md2_freeModel (model); \
+		return 0; \
+	}
+
+	// Uncomment if these are actually needed
+//	MD2LIMITCHECK(model->header.numSkins,     MD2_MAX_SKINS,     "skins")
+//	MD2LIMITCHECK(model->header.numTexCoords, MD2_MAX_TEXCOORDS, "texture coordinates")
+	MD2LIMITCHECK(model->header.numTriangles, MD2_MAX_TRIANGLES, "triangles")
+	MD2LIMITCHECK(model->header.numFrames,    MD2_MAX_FRAMES,    "frames")
+	MD2LIMITCHECK(model->header.numVertices,  MD2_MAX_VERTICES,  "vertices")
+
+#undef MD2LIMITCHECK
+
 	// read skins
 	fseek(file, model->header.offsetSkins, SEEK_SET);
 	if (model->header.numSkins > 0)
@@ -319,8 +336,6 @@ static md2_model_t *md2_readModel(const char *filename)
 			md2_freeModel (model);
 			return 0;
 		}
-
-		;
 	}
 
 	// read texture coordinates
@@ -334,8 +349,6 @@ static md2_model_t *md2_readModel(const char *filename)
 			md2_freeModel (model);
 			return 0;
 		}
-
-
 	}
 
 	// read triangles
@@ -769,6 +782,7 @@ void HWR_InitMD2(void)
 		md2_playermodels[s].grpatch = NULL;
 		md2_playermodels[s].skin = -1;
 		md2_playermodels[s].notfound = true;
+		md2_playermodels[s].error = false;
 	}
 	for (i = 0; i < NUMSPRITES; i++)
 	{
@@ -777,6 +791,7 @@ void HWR_InitMD2(void)
 		md2_models[i].grpatch = NULL;
 		md2_models[i].skin = -1;
 		md2_models[i].notfound = true;
+		md2_models[i].error = false;
 	}
 
 	// read the md2.dat file
@@ -1269,6 +1284,8 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 		else
 			md2 = &md2_models[spr->mobj->sprite];
 
+		if (md2->error)
+			return; // we already failed loading this before :(
 		if (!md2->model)
 		{
 			//CONS_Debug(DBG_RENDER, "Loading MD2... (%s)", sprnames[spr->mobj->sprite]);
@@ -1282,6 +1299,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 			else
 			{
 				//CONS_Debug(DBG_RENDER, " FAILED\n");
+				md2->error = true; // prevent endless fail
 				return;
 			}
 		}
diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h
index 36078268b50d590114e5025833b0197b0c3a0b19..5a7e6d2b3cdaf0c3ae7d5813e835d5cf8711403e 100644
--- a/src/hardware/hw_md2.h
+++ b/src/hardware/hw_md2.h
@@ -123,6 +123,7 @@ typedef struct
 	void        *blendgrpatch;
 	boolean     notfound;
 	INT32       skin;
+	boolean     error;
 } md2_t;
 
 extern md2_t md2_models[NUMSPRITES];
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 97035cd9617dfbf1b81d083846904340b7bf011b..646bdcbad6d0ba9299fea195e735a67c3f9d22c9 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -757,15 +757,8 @@ void HU_clearChatChars(void)
 //
 boolean HU_Responder(event_t *ev)
 {
-	static boolean shiftdown = false;
 	UINT8 c;
 
-	if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
-	{
-		shiftdown = (ev->type == ev_keydown);
-		return chat_on;
-	}
-
 	if (ev->type != ev_keydown)
 		return false;
 
@@ -797,6 +790,14 @@ boolean HU_Responder(event_t *ev)
 	}
 	else // if chat_on
 	{
+		// Ignore modifier keys
+		// Note that we do this here so users can still set
+		// their chat keys to one of these, if they so desire.
+		if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT
+		 || ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL
+		 || ev->data1 == KEY_LALT || ev->data1 == KEY_RALT)
+			return true;
+
 		c = (UINT8)ev->data1;
 
 		// use console translations
diff --git a/src/i_system.h b/src/i_system.h
index c161851e05ce93c6c460da4575f1c14f8dc5bbc4..d61f2d16e7730c8516b556d3d1f9705e5fb565fc 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -296,6 +296,14 @@ char *I_GetEnv(const char *name);
 
 INT32 I_PutEnv(char *variable);
 
+/** \brief Put data in system clipboard
+*/
+INT32 I_ClipboardCopy(const char *data, size_t size);
+
+/** \brief Retrieve data from system clipboard
+*/
+const char *I_ClipboardPaste(void);
+
 void I_RegisterSysCommands(void);
 
 #endif
diff --git a/src/lua_hook.h b/src/lua_hook.h
index 804d99e12e5728a49015829644b6b827ce649613..53e0a7d8e8bf2eb402ed50c62e89bb49704bdec8 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -60,7 +60,7 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which);
 #define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type
 boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type
 #define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type
-#define LUAh_MobjThinker(mo) LUAh_MobjHook(mo, hook_MobjThinker) // Hook for P_MobjThinker or P_SceneryThinker by mobj type
+boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type
 #define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type
 UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
 boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 1b9652571164a9de45056cf62d1fc66958efa01f..a24473bad47b69fd9cf11608773357cd605efc19 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -74,12 +74,30 @@ typedef struct hook_s* hook_p;
 
 #define FMT_HOOKID "hook_%d"
 
+// For each mobj type, a linked list to its thinker and collision hooks.
+// That way, we don't have to iterate through all the hooks.
+// We could do that with all other mobj hooks, but it would probably just be
+// a waste of memory since they are only called occasionally. Probably...
+static hook_p mobjthinkerhooks[NUMMOBJTYPES];
+static hook_p mobjcollidehooks[NUMMOBJTYPES];
+
+// For each mobj type, a linked list for other mobj hooks
+static hook_p mobjhooks[NUMMOBJTYPES];
+
+// A linked list for player hooks
+static hook_p playerhooks;
+
+// A linked list for linedef executor hooks
+static hook_p linedefexecutorhooks;
+
+// For other hooks, a unique linked list
 hook_p roothook;
 
 // Takes hook, function, and additional arguments (mobj type to act on, etc.)
 static int lib_addHook(lua_State *L)
 {
 	static struct hook_s hook = {NULL, 0, 0, {0}, false};
+	static UINT32 nextid;
 	hook_p hookp, *lastp;
 
 	hook.type = luaL_checkoption(L, 1, NULL, hookNames);
@@ -109,6 +127,7 @@ static int lib_addHook(lua_State *L)
 		hook.s.mt = MT_NULL;
 		if (lua_isnumber(L, 2))
 			hook.s.mt = lua_tonumber(L, 2);
+		luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t");
 		break;
 	case hook_BotAI:
 		hook.s.skinname = NULL;
@@ -141,18 +160,49 @@ static int lib_addHook(lua_State *L)
 
 	hooksAvailable[hook.type/8] |= 1<<(hook.type%8);
 
-	// iterate the hook metadata structs
 	// set hook.id to the highest id + 1
-	// set lastp to the last hook struct's "next" pointer.
-	lastp = &roothook;
-	hook.id = 0;
-	for (hookp = roothook; hookp; hookp = hookp->next)
+	hook.id = nextid++;
+
+	// Special cases for some hook types (see the comments above mobjthinkerhooks declaration)
+	switch(hook.type)
 	{
-		if (hookp->id >= hook.id)
-			hook.id = hookp->id+1;
-		lastp = &hookp->next;
+	case hook_MobjThinker:
+		lastp = &mobjthinkerhooks[hook.s.mt];
+		break;
+	case hook_MobjCollide:
+	case hook_MobjMoveCollide:
+		lastp = &mobjcollidehooks[hook.s.mt];
+		break;
+	case hook_MobjSpawn:
+	case hook_TouchSpecial:
+	case hook_MobjFuse:
+	case hook_BossThinker:
+	case hook_ShouldDamage:
+	case hook_MobjDamage:
+	case hook_MobjDeath:
+	case hook_BossDeath:
+	case hook_MobjRemoved:
+		lastp = &mobjhooks[hook.s.mt];
+		break;
+	case hook_JumpSpecial:
+	case hook_AbilitySpecial:
+	case hook_SpinSpecial:
+	case hook_JumpSpinSpecial:
+	case hook_PlayerSpawn:
+		lastp = &playerhooks;
+		break;
+	case hook_LinedefExecute:
+		lastp = &linedefexecutorhooks;
+		break;
+	default:
+		lastp = &roothook;
+		break;
 	}
 
+	// iterate the hook metadata structs
+	// set lastp to the last hook struct's "next" pointer.
+	for (hookp = *lastp; hookp; hookp = hookp->next)
+		lastp = &hookp->next;
 	// allocate a permanent memory struct to stuff hook.
 	hookp = ZZ_Alloc(sizeof(struct hook_s));
 	memcpy(hookp, &hook, sizeof(struct hook_s));
@@ -183,9 +233,29 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == which
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == mo->type))
+	// Look for all generic mobj hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == which)
+		{
+			if (lua_gettop(gL) == 0)
+				LUA_PushUserdata(gL, mo, META_MOBJ);
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -2);
+			if (lua_pcall(gL, 1, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (lua_toboolean(gL, -1))
+				hooked = true;
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next)
+		if (hookp->type == which)
 		{
 			if (lua_gettop(gL) == 0)
 				LUA_PushUserdata(gL, mo, META_MOBJ);
@@ -217,7 +287,7 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
+	for (hookp = playerhooks; hookp; hookp = hookp->next)
 		if (hookp->type == which)
 		{
 			if (lua_gettop(gL) == 0)
@@ -338,9 +408,38 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == which
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == thing1->type))
+	// Look for all generic mobj collision hooks
+	for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == which)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, thing1, META_MOBJ);
+				LUA_PushUserdata(gL, thing2, META_MOBJ);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -3);
+			lua_pushvalue(gL, -3);
+			if (lua_pcall(gL, 2, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (!lua_isnil(gL, -1))
+			{ // if nil, leave shouldCollide = 0.
+				if (lua_toboolean(gL, -1))
+					shouldCollide = 1; // Force yes
+				else
+					shouldCollide = 2; // Force no
+			}
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next)
+		if (hookp->type == which)
 		{
 			if (lua_gettop(gL) == 0)
 			{
@@ -372,6 +471,59 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
 	return shouldCollide;
 }
 
+// Hook for mobj thinkers
+boolean LUAh_MobjThinker(mobj_t *mo)
+{
+	hook_p hookp;
+	boolean hooked = false;
+	if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8))))
+		return false;
+
+	lua_settop(gL, 0);
+
+	// Look for all generic mobj thinker hooks
+	for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next)
+	{
+		if (lua_gettop(gL) == 0)
+			LUA_PushUserdata(gL, mo, META_MOBJ);
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		lua_pushvalue(gL, -2);
+		if (lua_pcall(gL, 1, 1, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+			continue;
+		}
+		if (lua_toboolean(gL, -1))
+			hooked = true;
+		lua_pop(gL, 1);
+	}
+
+	for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next)
+	{
+		if (lua_gettop(gL) == 0)
+			LUA_PushUserdata(gL, mo, META_MOBJ);
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		lua_pushvalue(gL, -2);
+		if (lua_pcall(gL, 1, 1, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+			continue;
+		}
+		if (lua_toboolean(gL, -1))
+			hooked = true;
+		lua_pop(gL, 1);
+	}
+
+	lua_settop(gL, 0);
+	return hooked;
+}
+
 // Hook for P_TouchSpecialThing by mobj type
 boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
 {
@@ -382,9 +534,33 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == hook_TouchSpecial
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == special->type))
+	// Look for all generic touch special hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_TouchSpecial)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, special, META_MOBJ);
+				LUA_PushUserdata(gL, toucher, META_MOBJ);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -3);
+			lua_pushvalue(gL, -3);
+			if (lua_pcall(gL, 2, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (lua_toboolean(gL, -1))
+				hooked = true;
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_TouchSpecial)
 		{
 			if (lua_gettop(gL) == 0)
 			{
@@ -421,9 +597,42 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == hook_ShouldDamage
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
+	// Look for all generic should damage hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_ShouldDamage)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, target, META_MOBJ);
+				LUA_PushUserdata(gL, inflictor, META_MOBJ);
+				LUA_PushUserdata(gL, source, META_MOBJ);
+				lua_pushinteger(gL, damage);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			if (lua_pcall(gL, 4, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (!lua_isnil(gL, -1))
+			{
+				if (lua_toboolean(gL, -1))
+					shouldDamage = 1; // Force yes
+				else
+					shouldDamage = 2; // Force no
+			}
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_ShouldDamage)
 		{
 			if (lua_gettop(gL) == 0)
 			{
@@ -469,9 +678,37 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == hook_MobjDamage
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
+	// Look for all generic mobj damage hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_MobjDamage)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, target, META_MOBJ);
+				LUA_PushUserdata(gL, inflictor, META_MOBJ);
+				LUA_PushUserdata(gL, source, META_MOBJ);
+				lua_pushinteger(gL, damage);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			if (lua_pcall(gL, 4, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (lua_toboolean(gL, -1))
+				hooked = true;
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_MobjDamage)
 		{
 			if (lua_gettop(gL) == 0)
 			{
@@ -512,9 +749,35 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == hook_MobjDeath
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
+	// Look for all generic mobj death hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_MobjDeath)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, target, META_MOBJ);
+				LUA_PushUserdata(gL, inflictor, META_MOBJ);
+				LUA_PushUserdata(gL, source, META_MOBJ);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -4);
+			lua_pushvalue(gL, -4);
+			lua_pushvalue(gL, -4);
+			if (lua_pcall(gL, 3, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (lua_toboolean(gL, -1))
+				hooked = true;
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_MobjDeath)
 		{
 			if (lua_gettop(gL) == 0)
 			{
@@ -652,9 +915,8 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == hook_LinedefExecute
-		&& !strcmp(hookp->s.funcname, line->text))
+	for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next)
+		if (!strcmp(hookp->s.funcname, line->text))
 		{
 			if (lua_gettop(gL) == 0)
 			{
diff --git a/src/m_menu.c b/src/m_menu.c
index 78c3812730669acde16add9452c143c3c74bc0e1..d7b4d9080ebf130f1be06b642f11eed905da97e5 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -182,9 +182,6 @@ static INT32 vidm_selected = 0;
 static INT32 vidm_nummodes;
 static INT32 vidm_column_size;
 
-// what a headache.
-static boolean shiftdown = false;
-
 //
 // PROTOTYPES
 //
@@ -2080,11 +2077,6 @@ boolean M_Responder(event_t *ev)
 	|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION)
 		return false;
 
-	if (ev->type == ev_keyup && (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT))
-	{
-		shiftdown = false;
-		return false;
-	}
 	if (noFurtherInput)
 	{
 		// Ignore input after enter/escape/other buttons
@@ -2098,10 +2090,6 @@ boolean M_Responder(event_t *ev)
 		// added 5-2-98 remap virtual keys (mouse & joystick buttons)
 		switch (ch)
 		{
-			case KEY_LSHIFT:
-			case KEY_RSHIFT:
-				shiftdown = true;
-				break; //return false;
 			case KEY_MOUSE1:
 			case KEY_JOY1:
 			case KEY_JOY1 + 2:
diff --git a/src/nds/i_system.c b/src/nds/i_system.c
index 0ed58029c194c12ce43c977c54b4cc0a18250b58..3e5c4b8c6d33fb6d266d12aad24e3d50748a9769 100644
--- a/src/nds/i_system.c
+++ b/src/nds/i_system.c
@@ -269,6 +269,18 @@ INT32 I_PutEnv(char *variable)
 	return -1;
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 void I_RegisterSysCommands(void) {}
 
 #include "../sdl/dosstr.c"
diff --git a/src/p_setup.c b/src/p_setup.c
index e56c44c70616814a2f6f216864f07aaa0e2e8b8f..111c717d377e96606de5156226aba6db14336d05 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2585,7 +2585,7 @@ boolean P_SetupLevel(boolean skipprecip)
 	lastloadedmaplumpnum = W_GetNumForName(maplumpname = G_BuildMapName(gamemap));
 
 	R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);
-	CON_ReSetupBackColormap(mapheaderinfo[gamemap-1]->palette);
+	CON_SetupBackColormap();
 
 	// now part of level loading since in future each level may have
 	// its own anim texture sequences, switches etc.
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index ea8ade8c1f5b901e800810f64c691982040ada8e..71ee3f79474b6bb48b23a2fbf4127a74fd40e115 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -2647,6 +2647,47 @@ INT32 I_PutEnv(char *variable)
 #endif
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	char storage[256];
+	if (size > 255)
+		size = 255;
+	memcpy(storage, data, size);
+	storage[size] = 0;
+
+	if (SDL_SetClipboardText(storage))
+		return 0;
+	return -1;
+}
+
+const char *I_ClipboardPaste(void)
+{
+	static char clipboard_modified[256];
+	char *clipboard_contents, *i = clipboard_modified;
+
+	if (!SDL_HasClipboardText())
+		return NULL;
+	clipboard_contents = SDL_GetClipboardText();
+	memcpy(clipboard_modified, clipboard_contents, 255);
+	SDL_free(clipboard_contents);
+	clipboard_modified[255] = 0;
+
+	while (*i)
+	{
+		if (*i == '\n' || *i == '\r')
+		{ // End on newline
+			*i = 0;
+			break;
+		}
+		else if (*i == '\t')
+			*i = ' '; // Tabs become spaces
+		else if (*i < 32 || (unsigned)*i > 127)
+			*i = '?'; // Nonprintable chars become question marks
+		++i;
+	}
+	return (const char *)&clipboard_modified;
+}
+
 /**	\brief	The isWadPathOk function
 
 	\param	path	string path to check
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 71baca510dabd7e21fd247721a10b050e2981f46..aa572e6e036fe41323c4cbcbc3802b5d13aa7c9d 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -33,14 +33,6 @@
 #pragma warning(default : 4214 4244)
 #endif
 
-#if SDL_VERSION_ATLEAST(1,3,0)
-#define SDLK_EQUALS SDLK_KP_EQUALSAS400
-#define SDLK_LMETA SDLK_LGUI
-#define SDLK_RMETA SDLK_RGUI
-#else
-#define HAVE_SDLMETAKEYS
-#endif
-
 #ifdef HAVE_TTF
 #include "i_ttf.h"
 #endif
@@ -189,14 +181,14 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
 			wasfullscreen = SDL_TRUE;
 			SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
 		}
-		else if (!fullscreen && wasfullscreen)
+		else if (wasfullscreen)
 		{
 			wasfullscreen = SDL_FALSE;
 			SDL_SetWindowFullscreen(window, 0);
 			SDL_SetWindowSize(window, width, height);
 			SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(1), SDL_WINDOWPOS_CENTERED_DISPLAY(1));
 		}
-		else if (!wasfullscreen)
+		else
 		{
 			// Reposition window only in windowed mode
 			SDL_SetWindowSize(window, width, height);
@@ -282,129 +274,70 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code)
 	}
 	switch (code)
 	{
-		case SDL_SCANCODE_F11: // F11 and F12 are
-			return KEY_F11;    // separated from the
-		case SDL_SCANCODE_F12: // rest of the function
-			return KEY_F12;    // keys
-
-		case SDL_SCANCODE_KP_0:
-			return KEY_KEYPAD0;
-		case SDL_SCANCODE_KP_1:
-			return KEY_KEYPAD1;
-		case SDL_SCANCODE_KP_2:
-			return KEY_KEYPAD2;
-		case SDL_SCANCODE_KP_3:
-			return KEY_KEYPAD3;
-		case SDL_SCANCODE_KP_4:
-			return KEY_KEYPAD4;
-		case SDL_SCANCODE_KP_5:
-			return KEY_KEYPAD5;
-		case SDL_SCANCODE_KP_6:
-			return KEY_KEYPAD6;
-		case SDL_SCANCODE_KP_7:
-			return KEY_KEYPAD7;
-		case SDL_SCANCODE_KP_8:
-			return KEY_KEYPAD8;
-		case SDL_SCANCODE_KP_9:
-			return KEY_KEYPAD9;
-
-		case SDL_SCANCODE_RETURN:
-			return KEY_ENTER;
-		case SDL_SCANCODE_ESCAPE:
-			return KEY_ESCAPE;
-		case SDL_SCANCODE_BACKSPACE:
-			return KEY_BACKSPACE;
-		case SDL_SCANCODE_TAB:
-			return KEY_TAB;
-		case SDL_SCANCODE_SPACE:
-			return KEY_SPACE;
-		case SDL_SCANCODE_MINUS:
-			return KEY_MINUS;
-		case SDL_SCANCODE_EQUALS:
-			return KEY_EQUALS;
-		case SDL_SCANCODE_LEFTBRACKET:
-			return '[';
-		case SDL_SCANCODE_RIGHTBRACKET:
-			return ']';
-		case SDL_SCANCODE_BACKSLASH:
-			return '\\';
-		case SDL_SCANCODE_NONUSHASH:
-			return '#';
-		case SDL_SCANCODE_SEMICOLON:
-			return ';';
-		case SDL_SCANCODE_APOSTROPHE:
-			return '\'';
-		case SDL_SCANCODE_GRAVE:
-			return '`';
-		case SDL_SCANCODE_COMMA:
-			return ',';
-		case SDL_SCANCODE_PERIOD:
-			return '.';
-		case SDL_SCANCODE_SLASH:
-			return '/';
-		case SDL_SCANCODE_CAPSLOCK:
-			return KEY_CAPSLOCK;
-		case SDL_SCANCODE_PRINTSCREEN:
-			return 0; // undefined?
-		case SDL_SCANCODE_SCROLLLOCK:
-			return KEY_SCROLLLOCK;
-		case SDL_SCANCODE_PAUSE:
-			return KEY_PAUSE;
-		case SDL_SCANCODE_INSERT:
-			return KEY_INS;
-		case SDL_SCANCODE_HOME:
-			return KEY_HOME;
-		case SDL_SCANCODE_PAGEUP:
-			return KEY_PGUP;
-		case SDL_SCANCODE_DELETE:
-			return KEY_DEL;
-		case SDL_SCANCODE_END:
-			return KEY_END;
-		case SDL_SCANCODE_PAGEDOWN:
-			return KEY_PGDN;
-		case SDL_SCANCODE_RIGHT:
-			return KEY_RIGHTARROW;
-		case SDL_SCANCODE_LEFT:
-			return KEY_LEFTARROW;
-		case SDL_SCANCODE_DOWN:
-			return KEY_DOWNARROW;
-		case SDL_SCANCODE_UP:
-			return KEY_UPARROW;
-		case SDL_SCANCODE_NUMLOCKCLEAR:
-			return KEY_NUMLOCK;
-		case SDL_SCANCODE_KP_DIVIDE:
-			return KEY_KPADSLASH;
-		case SDL_SCANCODE_KP_MULTIPLY:
-			return '*'; // undefined?
-		case SDL_SCANCODE_KP_MINUS:
-			return KEY_MINUSPAD;
-		case SDL_SCANCODE_KP_PLUS:
-			return KEY_PLUSPAD;
-		case SDL_SCANCODE_KP_ENTER:
-			return KEY_ENTER;
-		case SDL_SCANCODE_KP_PERIOD:
-			return KEY_KPADDEL;
-		case SDL_SCANCODE_NONUSBACKSLASH:
-			return '\\';
-
-		case SDL_SCANCODE_LSHIFT:
-			return KEY_LSHIFT;
-		case SDL_SCANCODE_RSHIFT:
-			return KEY_RSHIFT;
-		case SDL_SCANCODE_LCTRL:
-			return KEY_LCTRL;
-		case SDL_SCANCODE_RCTRL:
-			return KEY_RCTRL;
-		case SDL_SCANCODE_LALT:
-			return KEY_LALT;
-		case SDL_SCANCODE_RALT:
-			return KEY_RALT;
-		case SDL_SCANCODE_LGUI:
-			return KEY_LEFTWIN;
-		case SDL_SCANCODE_RGUI:
-			return KEY_RIGHTWIN;
-		default:
-			break;
+		// F11 and F12 are separated from the rest of the function keys
+		case SDL_SCANCODE_F11: return KEY_F11;
+		case SDL_SCANCODE_F12: return KEY_F12;
+
+		case SDL_SCANCODE_KP_0: return KEY_KEYPAD0;
+		case SDL_SCANCODE_KP_1: return KEY_KEYPAD1;
+		case SDL_SCANCODE_KP_2: return KEY_KEYPAD2;
+		case SDL_SCANCODE_KP_3: return KEY_KEYPAD3;
+		case SDL_SCANCODE_KP_4: return KEY_KEYPAD4;
+		case SDL_SCANCODE_KP_5: return KEY_KEYPAD5;
+		case SDL_SCANCODE_KP_6: return KEY_KEYPAD6;
+		case SDL_SCANCODE_KP_7: return KEY_KEYPAD7;
+		case SDL_SCANCODE_KP_8: return KEY_KEYPAD8;
+		case SDL_SCANCODE_KP_9: return KEY_KEYPAD9;
+
+		case SDL_SCANCODE_RETURN:         return KEY_ENTER;
+		case SDL_SCANCODE_ESCAPE:         return KEY_ESCAPE;
+		case SDL_SCANCODE_BACKSPACE:      return KEY_BACKSPACE;
+		case SDL_SCANCODE_TAB:            return KEY_TAB;
+		case SDL_SCANCODE_SPACE:          return KEY_SPACE;
+		case SDL_SCANCODE_MINUS:          return KEY_MINUS;
+		case SDL_SCANCODE_EQUALS:         return KEY_EQUALS;
+		case SDL_SCANCODE_LEFTBRACKET:    return '[';
+		case SDL_SCANCODE_RIGHTBRACKET:   return ']';
+		case SDL_SCANCODE_BACKSLASH:      return '\\';
+		case SDL_SCANCODE_NONUSHASH:      return '#';
+		case SDL_SCANCODE_SEMICOLON:      return ';';
+		case SDL_SCANCODE_APOSTROPHE:     return '\'';
+		case SDL_SCANCODE_GRAVE:          return '`';
+		case SDL_SCANCODE_COMMA:          return ',';
+		case SDL_SCANCODE_PERIOD:         return '.';
+		case SDL_SCANCODE_SLASH:          return '/';
+		case SDL_SCANCODE_CAPSLOCK:       return KEY_CAPSLOCK;
+		case SDL_SCANCODE_PRINTSCREEN:    return 0; // undefined?
+		case SDL_SCANCODE_SCROLLLOCK:     return KEY_SCROLLLOCK;
+		case SDL_SCANCODE_PAUSE:          return KEY_PAUSE;
+		case SDL_SCANCODE_INSERT:         return KEY_INS;
+		case SDL_SCANCODE_HOME:           return KEY_HOME;
+		case SDL_SCANCODE_PAGEUP:         return KEY_PGUP;
+		case SDL_SCANCODE_DELETE:         return KEY_DEL;
+		case SDL_SCANCODE_END:            return KEY_END;
+		case SDL_SCANCODE_PAGEDOWN:       return KEY_PGDN;
+		case SDL_SCANCODE_RIGHT:          return KEY_RIGHTARROW;
+		case SDL_SCANCODE_LEFT:           return KEY_LEFTARROW;
+		case SDL_SCANCODE_DOWN:           return KEY_DOWNARROW;
+		case SDL_SCANCODE_UP:             return KEY_UPARROW;
+		case SDL_SCANCODE_NUMLOCKCLEAR:   return KEY_NUMLOCK;
+		case SDL_SCANCODE_KP_DIVIDE:      return KEY_KPADSLASH;
+		case SDL_SCANCODE_KP_MULTIPLY:    return '*'; // undefined?
+		case SDL_SCANCODE_KP_MINUS:       return KEY_MINUSPAD;
+		case SDL_SCANCODE_KP_PLUS:        return KEY_PLUSPAD;
+		case SDL_SCANCODE_KP_ENTER:       return KEY_ENTER;
+		case SDL_SCANCODE_KP_PERIOD:      return KEY_KPADDEL;
+		case SDL_SCANCODE_NONUSBACKSLASH: return '\\';
+
+		case SDL_SCANCODE_LSHIFT: return KEY_LSHIFT;
+		case SDL_SCANCODE_RSHIFT: return KEY_RSHIFT;
+		case SDL_SCANCODE_LCTRL:  return KEY_LCTRL;
+		case SDL_SCANCODE_RCTRL:  return KEY_RCTRL;
+		case SDL_SCANCODE_LALT:   return KEY_LALT;
+		case SDL_SCANCODE_RALT:   return KEY_RALT;
+		case SDL_SCANCODE_LGUI:   return KEY_LEFTWIN;
+		case SDL_SCANCODE_RGUI:   return KEY_RIGHTWIN;
+		default:                  break;
 	}
 #ifdef HWRENDER
 	DBG_Printf("Unknown incoming scancode: %d, represented %c\n",
@@ -432,15 +365,10 @@ static void VID_Command_NumModes_f (void)
 	CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), VID_NumModes());
 }
 
+// SDL2 doesn't have SDL_GetVideoSurface or a lot of the SDL_Surface flags that SDL 1.2 had
 static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
 {
-#if 1
-	(void)infoSurface;
-	(void)SurfaceText;
-	SDL2STUB();
-#else
 	INT32 vfBPP;
-	const SDL_Surface *VidSur = SDL_GetVideoSurface();
 
 	if (!infoSurface)
 		return;
@@ -453,49 +381,12 @@ static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
 	CONS_Printf("\x82" "%s\n", SurfaceText);
 	CONS_Printf(M_GetText(" %ix%i at %i bit color\n"), infoSurface->w, infoSurface->h, vfBPP);
 
-	if (infoSurface->flags&SDL_HWSURFACE)
-		CONS_Printf("%s", M_GetText(" Stored in video memory\n"));
-	else if (infoSurface->flags&SDL_OPENGL)
-		CONS_Printf("%s", M_GetText(" Stored in an OpenGL context\n"));
-	else if (infoSurface->flags&SDL_PREALLOC)
+	if (infoSurface->flags&SDL_PREALLOC)
 		CONS_Printf("%s", M_GetText(" Uses preallocated memory\n"));
 	else
 		CONS_Printf("%s", M_GetText(" Stored in system memory\n"));
-
-	if (infoSurface->flags&SDL_ASYNCBLIT)
-		CONS_Printf("%s", M_GetText(" Uses asynchronous blits if possible\n"));
-	else
-		CONS_Printf("%s", M_GetText(" Uses synchronous blits if possible\n"));
-
-	if (infoSurface->flags&SDL_ANYFORMAT)
-		CONS_Printf("%s", M_GetText(" Allows any pixel-format\n"));
-
-	if (infoSurface->flags&SDL_HWPALETTE)
-		CONS_Printf("%s", M_GetText(" Has exclusive palette access\n"));
-	else if (VidSur == infoSurface)
-		CONS_Printf("%s", M_GetText(" Has nonexclusive palette access\n"));
-
-	if (infoSurface->flags&SDL_DOUBLEBUF)
-		CONS_Printf("%s", M_GetText(" Double buffered\n"));
-	else if (VidSur == infoSurface)
-		CONS_Printf("%s", M_GetText(" No hardware flipping\n"));
-
-	if (infoSurface->flags&SDL_FULLSCREEN)
-		CONS_Printf("%s", M_GetText(" Full screen\n"));
-	else if (infoSurface->flags&SDL_RESIZABLE)
-		CONS_Printf("%s", M_GetText(" Resizable window\n"));
-	else if (VidSur == infoSurface)
-		CONS_Printf("%s", M_GetText(" Nonresizable window\n"));
-
-	if (infoSurface->flags&SDL_HWACCEL)
-		CONS_Printf("%s", M_GetText(" Uses hardware acceleration blit\n"));
-	if (infoSurface->flags&SDL_SRCCOLORKEY)
-		CONS_Printf("%s", M_GetText(" Use colorkey blitting\n"));
 	if (infoSurface->flags&SDL_RLEACCEL)
 		CONS_Printf("%s", M_GetText(" Colorkey RLE acceleration blit\n"));
-	if (infoSurface->flags&SDL_SRCALPHA)
-		CONS_Printf("%s", M_GetText(" Use alpha blending acceleration blit\n"));
-#endif
 }
 
 static void VID_Command_Info_f (void)
@@ -579,23 +470,6 @@ static void VID_Command_Mode_f (void)
 		setmodeneeded = modenum+1; // request vid mode change
 }
 
-#if 0
-#if defined(RPC_NO_WINDOWS_H)
-static VOID MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
-	UNREFERENCED_PARAMETER(hWnd);
-	UNREFERENCED_PARAMETER(message);
-	UNREFERENCED_PARAMETER(wParam);
-	switch (message)
-	{
-		case WM_SETTEXT:
-			COM_BufAddText((LPCSTR)lParam);
-			break;
-	}
-}
-#endif
-#endif
-
 static inline void SDLJoyRemap(event_t *event)
 {
 	(void)event;
@@ -954,218 +828,6 @@ void I_GetEvent(void)
 	// In order to make wheels act like buttons, we have to set their state to Up.
 	// This is because wheel messages don't have an up/down state.
 	gamekeydown[KEY_MOUSEWHEELDOWN] = gamekeydown[KEY_MOUSEWHEELUP] = 0;
-
-#if 0
-	SDL_Event inputEvent;
-	static SDL_bool sdlquit = SDL_FALSE; //Alam: once, just once
-	event_t event;
-
-	if (!graphics_started)
-		return;
-
-	memset(&inputEvent, 0x00, sizeof(inputEvent));
-	while (SDL_PollEvent(&inputEvent))
-	{
-		memset(&event,0x00,sizeof (event_t));
-		switch (inputEvent.type)
-		{
-			case SDL_ACTIVEEVENT:
-				if (inputEvent.active.state  & (SDL_APPACTIVE|SDL_APPINPUTFOCUS))
-				{
-					// pause music when alt-tab
-					if (inputEvent.active.gain /*&& !paused */)
-					{
-						static SDL_bool firsttimeonmouse = SDL_TRUE;
-						if (!firsttimeonmouse)
-						{
-							if (cv_usemouse.value) I_StartupMouse();
-						}
-						else firsttimeonmouse = SDL_FALSE;
-						//if (!netgame && !con_destlines) paused = false;
-						if (gamestate == GS_LEVEL)
-							if (!paused) I_ResumeSong(0); //resume it
-					}
-					else /*if (!paused)*/
-					{
-						if (!disable_mouse)
-							SDLforceUngrabMouse();
-						if (!netgame && gamestate == GS_LEVEL) paused = true;
-						memset(gamekeydown, 0, NUMKEYS);
-						//S_PauseSound();
-						if (gamestate == GS_LEVEL)
-							I_PauseSong(0); //pause it
-					}
-				}
-				if (MOUSE_MENU)
-				{
-					SDLdoUngrabMouse();
-					break;
-				}
-				if ((SDL_APPMOUSEFOCUS&inputEvent.active.state) && USE_MOUSEINPUT && inputEvent.active.gain)
-					HalfWarpMouse(realwidth, realheight);
-				break;
-			case SDL_KEYDOWN:
-			case SDL_KEYUP:
-				/// \todo inputEvent.key.which?
-				if (inputEvent.type == SDL_KEYUP)
-					event.type = ev_keyup;
-				else if (inputEvent.type == SDL_KEYDOWN)
-					event.type = ev_keydown;
-				else break;
-				event.data1 = SDLatekey(inputEvent.key.keysym.sym);
-				if (event.data1) D_PostEvent(&event);
-				break;
-			case SDL_MOUSEMOTION:
-				/// \todo inputEvent.motion.which
-				if (MOUSE_MENU)
-				{
-					SDLdoUngrabMouse();
-					break;
-				}
-				//if (USE_MOUSEINPUT) TODO SDL2 stub
-				{
-					// If the event is from warping the pointer back to middle
-					// of the screen then ignore it.
-					if ((inputEvent.motion.x == realwidth/2) &&
-					    (inputEvent.motion.y == realheight/2))
-					{
-						break;
-					}
-					else
-					{
-						event.data2 = +inputEvent.motion.xrel;
-						event.data3 = -inputEvent.motion.yrel;
-					}
-					event.type = ev_mouse;
-					D_PostEvent(&event);
-					// Warp the pointer back to the middle of the window
-					//  or we cannot move any further if it's at a border.
-					if ((inputEvent.motion.x < (realwidth/2 )-(realwidth/4 )) ||
-					    (inputEvent.motion.y < (realheight/2)-(realheight/4)) ||
-					    (inputEvent.motion.x > (realwidth/2 )+(realwidth/4 )) ||
-					    (inputEvent.motion.y > (realheight/2)+(realheight/4) ) )
-					{
-						//if (SDL_GRAB_ON == SDL_WM_GrabInput(SDL_GRAB_QUERY) || !mousegrabok)
-							HalfWarpMouse(realwidth, realheight);
-					}
-				}
-				break;
-			case SDL_MOUSEBUTTONDOWN:
-			case SDL_MOUSEBUTTONUP:
-				/// \todo inputEvent.button.which
-				if (USE_MOUSEINPUT)
-				{
-					if (inputEvent.type == SDL_MOUSEBUTTONUP)
-						event.type = ev_keyup;
-					else if (inputEvent.type == SDL_MOUSEBUTTONDOWN)
-						event.type = ev_keydown;
-					else break;
-					if (inputEvent.button.button==SDL_BUTTON_WHEELUP || inputEvent.button.button==SDL_BUTTON_WHEELDOWN)
-					{
-						if (inputEvent.type == SDL_MOUSEBUTTONUP)
-							event.data1 = 0; //Alam: dumb! this could be a real button with some mice
-						else
-							event.data1 = KEY_MOUSEWHEELUP + inputEvent.button.button - SDL_BUTTON_WHEELUP;
-					}
-					else if (inputEvent.button.button == SDL_BUTTON_MIDDLE)
-						event.data1 = KEY_MOUSE1+2;
-					else if (inputEvent.button.button == SDL_BUTTON_RIGHT)
-						event.data1 = KEY_MOUSE1+1;
-					else if (inputEvent.button.button <= MOUSEBUTTONS)
-						event.data1 = KEY_MOUSE1 + inputEvent.button.button - SDL_BUTTON_LEFT;
-					if (event.data1) D_PostEvent(&event);
-				}
-				break;
-			case SDL_JOYAXISMOTION:
-				inputEvent.jaxis.which++;
-				inputEvent.jaxis.axis++;
-				event.data1 = event.data2 = event.data3 = INT32_MAX;
-				if (cv_usejoystick.value == inputEvent.jaxis.which)
-				{
-					event.type = ev_joystick;
-				}
-				else if (cv_usejoystick.value == inputEvent.jaxis.which)
-				{
-					event.type = ev_joystick2;
-				}
-				else break;
-				//axis
-				if (inputEvent.jaxis.axis > JOYAXISSET*2)
-					break;
-				//vaule
-				if (inputEvent.jaxis.axis%2)
-				{
-					event.data1 = inputEvent.jaxis.axis / 2;
-					event.data2 = SDLJoyAxis(inputEvent.jaxis.value, event.type);
-				}
-				else
-				{
-					inputEvent.jaxis.axis--;
-					event.data1 = inputEvent.jaxis.axis / 2;
-					event.data3 = SDLJoyAxis(inputEvent.jaxis.value, event.type);
-				}
-				D_PostEvent(&event);
-				break;
-			case SDL_JOYBALLMOTION:
-			case SDL_JOYHATMOTION:
-				break; //NONE
-			case SDL_JOYBUTTONDOWN:
-			case SDL_JOYBUTTONUP:
-				inputEvent.jbutton.which++;
-				if (cv_usejoystick.value == inputEvent.jbutton.which)
-					event.data1 = KEY_JOY1;
-				else if (cv_usejoystick.value == inputEvent.jbutton.which)
-					event.data1 = KEY_2JOY1;
-				else break;
-				if (inputEvent.type == SDL_JOYBUTTONUP)
-					event.type = ev_keyup;
-				else if (inputEvent.type == SDL_JOYBUTTONDOWN)
-					event.type = ev_keydown;
-				else break;
-				if (inputEvent.jbutton.button < JOYBUTTONS)
-					event.data1 += inputEvent.jbutton.button;
-				else
-					break;
-				SDLJoyRemap(&event);
-				if (event.type != ev_console) D_PostEvent(&event);
-				break;
-			case SDL_QUIT:
-				if (!sdlquit)
-				{
-					sdlquit = SDL_TRUE;
-					M_QuitResponse('y');
-				}
-				break;
-#if defined(RPC_NO_WINDOWS_H)
-			case SDL_SYSWMEVENT:
-				MainWndproc(inputEvent.syswm.msg->hwnd,
-					inputEvent.syswm.msg->msg,
-					inputEvent.syswm.msg->wParam,
-					inputEvent.syswm.msg->lParam);
-				break;
-#endif
-			case SDL_VIDEORESIZE:
-				if (gamestate == GS_LEVEL || gamestate == GS_TITLESCREEN || gamestate == GS_EVALUATION)
-				    setmodeneeded = VID_GetModeForSize(inputEvent.resize.w,inputEvent.resize.h)+1;
-				if (render_soft == rendermode)
-				{
-					SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW);
-					if (vidSurface) SDL_SetColors(vidSurface, localPalette, 0, 256);
-				}
-				else
-					SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW);
-				if (!vidSurface)
-					I_Error("Could not reset vidmode: %s\n",SDL_GetError());
-				break;
-			case SDL_VIDEOEXPOSE:
-				exposevideo = SDL_TRUE;
-				break;
-			default:
-				break;
-		}
-	}
-	//reset wheel like in win32, I don't understand it but works
-#endif
 }
 
 void I_StartupMouse(void)
@@ -1494,11 +1156,6 @@ void VID_PrepareModeList(void)
 #endif
 }
 
-static inline void SDLESSet(void)
-{
-	SDL2STUB();
-}
-
 INT32 VID_SetMode(INT32 modeNum)
 {
 	SDLdoUngrabMouse();
@@ -1550,6 +1207,12 @@ INT32 VID_SetMode(INT32 modeNum)
 static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
 {
 	int flags = 0;
+
+	if (rendermode == render_none) // dedicated
+	{
+		return SDL_TRUE; // Monster Iestyn -- not sure if it really matters what we return here tbh
+	}
+
 	if (window != NULL)
 	{
 		return SDL_FALSE;
@@ -1568,38 +1231,43 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
 #ifdef HWRENDER
 	if (rendermode == render_opengl)
 	{
-		window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
-				realwidth, realheight, flags | SDL_WINDOW_OPENGL);
-		if (window != NULL)
+		flags |= SDL_WINDOW_OPENGL;
+	}
+#endif
+
+	// Create a window
+	window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+			realwidth, realheight, flags);
+
+	if (window == NULL)
+	{
+		CONS_Printf(M_GetText("Couldn't create window: %s\n"), SDL_GetError());
+		return SDL_FALSE;
+	}
+
+	// Renderer-specific stuff
+#ifdef HWRENDER
+	if (rendermode == render_opengl)
+	{
+		sdlglcontext = SDL_GL_CreateContext(window);
+		if (sdlglcontext == NULL)
 		{
-			sdlglcontext = SDL_GL_CreateContext(window);
-			if (sdlglcontext == NULL)
-			{
-				SDL_DestroyWindow(window);
-				I_Error("Failed to create a GL context: %s\n", SDL_GetError());
-			}
-			else
-			{
-				SDL_GL_MakeCurrent(window, sdlglcontext);
-			}
+			SDL_DestroyWindow(window);
+			I_Error("Failed to create a GL context: %s\n", SDL_GetError());
 		}
-		else return SDL_FALSE;
+		SDL_GL_MakeCurrent(window, sdlglcontext);
 	}
+	else
 #endif
 	if (rendermode == render_soft)
 	{
-		window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
-				realwidth, realheight, flags);
-		if (window != NULL)
+		renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0));
+		if (renderer == NULL)
 		{
-			renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0));
-			if (renderer != NULL)
-			{
-				SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
-			}
-			else return SDL_FALSE;
+			CONS_Printf(M_GetText("Couldn't create rendering context: %s\n"), SDL_GetError());
+			return SDL_FALSE;
 		}
-		else return SDL_FALSE;
+		SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
 	}
 
 	return SDL_TRUE;
@@ -1620,7 +1288,7 @@ static void Impl_SetWindowIcon(void)
 	{
 		return;
 	}
-	SDL2STUB();
+	//SDL2STUB(); // Monster Iestyn: why is this stubbed?
 	SDL_SetWindowIcon(window, icoSurface);
 }
 
@@ -1718,7 +1386,6 @@ void I_StartupGraphics(void)
 	borderlesswindow = M_CheckParm("-borderless");
 
 	//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2);
-	SDLESSet();
 	VID_Command_ModeList_f();
 #ifdef HWRENDER
 	if (M_CheckParm("-opengl") || rendermode == render_opengl)
diff --git a/src/sdl/ogl_sdl.h b/src/sdl/ogl_sdl.h
index 7e144644ce325d7c18b1d0916ad3fdae3f384421..2d6209f2bb94c1e28e35e96e949c5ddb641f567c 100644
--- a/src/sdl/ogl_sdl.h
+++ b/src/sdl/ogl_sdl.h
@@ -24,7 +24,6 @@ boolean OglSdlSurface(INT32 w, INT32 h);
 
 void OglSdlFinishUpdate(boolean vidwait);
 
-extern SDL_Window *window;
 extern SDL_Renderer *renderer;
 extern SDL_GLContext sdlglcontext;
 extern Uint16      realwidth;
diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h
index 7ac32f4b3bf0268be5aaada164426a207195d790..fea1e16487dd8f5c4f7acfbbcf2de94d9ea77b1b 100644
--- a/src/sdl/sdlmain.h
+++ b/src/sdl/sdlmain.h
@@ -71,4 +71,7 @@ void I_GetConsoleEvents(void);
 
 void SDLforceUngrabMouse(void);
 
+// Needed for some WIN32 functions
+extern SDL_Window *window;
+
 #endif
diff --git a/src/sdl12/i_system.c b/src/sdl12/i_system.c
index 888a6a507637e5a6c5d4e2c5de9099fcf34933d1..ed0db653d1f51df07ae714199f5bd9f68c05de21 100644
--- a/src/sdl12/i_system.c
+++ b/src/sdl12/i_system.c
@@ -2666,6 +2666,18 @@ INT32 I_PutEnv(char *variable)
 #endif
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 /**	\brief	The isWadPathOk function
 
 	\param	path	string path to check
diff --git a/src/v_video.c b/src/v_video.c
index 3cc6d195f51e5b879e2704320cc65c70744a2615..f6a966e673713f0f9e55fe7ec40325e5f40741ca 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -774,43 +774,51 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
 	if (!screens[0])
 		return;
 
-	if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
-	{ // Clear the entire screen, from dest to deststop. Yes, this really works.
-		memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp);
-		return;
+	if (c & V_NOSCALESTART)
+	{
+		dest = screens[0] + y*vid.width + x;
+		deststop = screens[0] + vid.rowbytes * vid.height;
 	}
-
-	dest = screens[0] + y*dupy*vid.width + x*dupx;
-	deststop = screens[0] + vid.rowbytes * vid.height;
-
-	if (w == BASEVIDWIDTH)
-		w = vid.width;
-	else
-		w *= dupx;
-	if (h == BASEVIDHEIGHT)
-		h = vid.height;
 	else
-		h *= dupy;
-
-	if (x && y && x + w < vid.width && y + h < vid.height)
 	{
-		// Center it if necessary
-		if (vid.width != BASEVIDWIDTH * dupx)
-		{
-			// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
-			// so center this imaginary screen
-			if (c & V_SNAPTORIGHT)
-				dest += (vid.width - (BASEVIDWIDTH * dupx));
-			else if (!(c & V_SNAPTOLEFT))
-				dest += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
+		if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
+		{ // Clear the entire screen, from dest to deststop. Yes, this really works.
+			memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp);
+			return;
 		}
-		if (vid.height != BASEVIDHEIGHT * dupy)
+
+		dest = screens[0] + y*dupy*vid.width + x*dupx;
+		deststop = screens[0] + vid.rowbytes * vid.height;
+
+		if (w == BASEVIDWIDTH)
+			w = vid.width;
+		else
+			w *= dupx;
+		if (h == BASEVIDHEIGHT)
+			h = vid.height;
+		else
+			h *= dupy;
+
+		if (x && y && x + w < vid.width && y + h < vid.height)
 		{
-			// same thing here
-			if (c & V_SNAPTOBOTTOM)
-				dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width;
-			else if (!(c & V_SNAPTOTOP))
-				dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
+			// Center it if necessary
+			if (vid.width != BASEVIDWIDTH * dupx)
+			{
+				// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
+				// so center this imaginary screen
+				if (c & V_SNAPTORIGHT)
+					dest += (vid.width - (BASEVIDWIDTH * dupx));
+				else if (!(c & V_SNAPTOLEFT))
+					dest += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
+			}
+			if (vid.height != BASEVIDHEIGHT * dupy)
+			{
+				// same thing here
+				if (c & V_SNAPTOBOTTOM)
+					dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width;
+				else if (!(c & V_SNAPTOTOP))
+					dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
+			}
 		}
 	}
 
@@ -968,45 +976,38 @@ void V_DrawFadeScreen(void)
 }
 
 // Simple translucency with one color, over a set number of lines starting from the top.
-void V_DrawFadeConsBack(INT32 plines, INT32 pcolor)
+void V_DrawFadeConsBack(INT32 plines)
 {
-	UINT8 *deststop, *colormap, *buf;
+	UINT8 *deststop, *buf;
 
 #ifdef HWRENDER // not win32 only 19990829 by Kin
 	if (rendermode != render_soft && rendermode != render_none)
 	{
 		UINT32 hwcolor;
-		switch (pcolor)
+		switch (cons_backcolor.value)
 		{
-			case 0:		hwcolor = 0xffffff00;	break;	//white
-			case 1:		hwcolor = 0xff800000;	break;	//orange
-			case 2:		hwcolor = 0x0000ff00;	break;	//blue
-			case 3:		hwcolor = 0x00800000;	break;	//green
-			case 4:		hwcolor = 0x80808000;	break;	//gray
-			case 5:		hwcolor = 0xff000000;	break;	//red
-			default:	hwcolor = 0x00800000;	break;	//green
+			case 0:		hwcolor = 0xffffff00;	break; // White
+			case 1:		hwcolor = 0x80808000;	break; // Gray
+			case 2:		hwcolor = 0x40201000;	break; // Brown
+			case 3:		hwcolor = 0xff000000;	break; // Red
+			case 4:		hwcolor = 0xff800000;	break; // Orange
+			case 5:		hwcolor = 0x80800000;	break; // Yellow
+			case 6:		hwcolor = 0x00800000;	break; // Green
+			case 7:		hwcolor = 0x0000ff00;	break; // Blue
+			case 8:		hwcolor = 0x4080ff00;	break; // Cyan
+			// Default green
+			default:	hwcolor = 0x00800000;	break;
 		}
 		HWR_DrawConsoleBack(hwcolor, plines);
 		return;
 	}
 #endif
 
-	switch (pcolor)
-	{
-		case 0:		colormap = cwhitemap; 	break;
-		case 1:		colormap = corangemap;	break;
-		case 2:		colormap = cbluemap;	break;
-		case 3:		colormap = cgreenmap;	break;
-		case 4:		colormap = cgraymap;	break;
-		case 5:		colormap = credmap;		break;
-		default:	colormap = cgreenmap;	break;
-	}
-
 	// heavily simplified -- we don't need to know x or y position,
 	// just the stop position
 	deststop = screens[0] + vid.rowbytes * min(plines, vid.height);
 	for (buf = screens[0]; buf < deststop; ++buf)
-		*buf = colormap[*buf];
+		*buf = consolebgmap[*buf];
 }
 
 // Gets string colormap, used for 0x80 color codes
diff --git a/src/v_video.h b/src/v_video.h
index 70255d0ef9c72c80cd866eb65051d0b403dcf2d8..353f84c1d973223e642436000f1464211fdeb7a0 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -145,7 +145,7 @@ void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum);
 // fade down the screen buffer before drawing the menu over
 void V_DrawFadeScreen(void);
 
-void V_DrawFadeConsBack(INT32 plines, INT32 pcolor);
+void V_DrawFadeConsBack(INT32 plines);
 
 // draw a single character
 void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed);
diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg
index f309f7db11d158fb878eaa8a4615bed1b9dbf6b3..99b8bc9b2ca4b6949944429546b3e379808e804a 100644
--- a/src/win32/Makefile.cfg
+++ b/src/win32/Makefile.cfg
@@ -85,13 +85,21 @@ endif
 	OBJS=$(OBJDIR)/dx_error.o $(OBJDIR)/fabdxlib.o $(OBJDIR)/win_vid.o $(OBJDIR)/win_dll.o
 endif
 
+
+ZLIB_CFLAGS?=-I../libs/zlib
+ifdef MINGW64
+ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz64
+else
+ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz32
+endif
+
 ifndef NOPNG
 ifndef PNG_CONFIG
-	PNG_CFLAGS?=-I../libs/libpng-src -I../libs/zlib
+	PNG_CFLAGS?=-I../libs/libpng-src
 ifdef MINGW64
-	PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64 -L../libs/zlib/win32 -lz64
+	PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64
 else
-	PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32 -L../libs/zlib/win32 -lz32
+	PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32
 endif #MINGW64
 endif #PNG_CONFIG
 endif #NOPNG
diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c
index 0331080c8e42acf6941f20ce39588a71e416acc3..80b89a6e64ccc549242b60ee216d0721c4f0096f 100644
--- a/src/win32/win_sys.c
+++ b/src/win32/win_sys.c
@@ -3598,6 +3598,18 @@ INT32 I_PutEnv(char *variable)
 	return putenv(variable);
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+const char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 typedef BOOL (WINAPI *p_IsProcessorFeaturePresent) (DWORD);
 
 const CPUInfoFlags *I_CPUInfo(void)
diff --git a/src/win32ce/win_sys.c b/src/win32ce/win_sys.c
index 88764ef733c70f2400ce9101433f69b7fd441337..3b6a472587cc161f214736005c48df406dde9925 100644
--- a/src/win32ce/win_sys.c
+++ b/src/win32ce/win_sys.c
@@ -3470,6 +3470,18 @@ INT32 I_PutEnv(char *variable)
 	return putenv(variable);
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 typedef BOOL (WINAPI *MyFunc3) (DWORD);
 
 const CPUInfoFlags *I_CPUInfo(void)