[quakespasm] 01/05: Imported Upstream version 0.92.0+dfsg

Stephen Kitt skitt at moszumanska.debian.org
Mon Jul 25 06:24:01 UTC 2016


This is an automated email from the git hooks/post-receive script.

skitt pushed a commit to branch master
in repository quakespasm.

commit d8ff8e2c16c7b07c3f1f6189755115ba707b0a58
Author: Stephen Kitt <steve at sk2.org>
Date:   Mon Jul 25 08:10:27 2016 +0200

    Imported Upstream version 0.92.0+dfsg
---
 Linux/CodeBlocks/QuakeSpasm-SDL2.cbp |   2 +-
 Linux/CodeBlocks/QuakeSpasm.cbp      |   2 +-
 Linux/sgml/Quakespasm.sgml           |  87 ++++++-
 Misc/qs_pak/default.cfg              |   8 +
 Quake/Makefile.w32                   |   6 +-
 Quake/Makefile.w64                   |   6 +-
 Quake/bgmusic.c                      |  10 +
 Quake/bspfile.h                      |   2 +-
 Quake/cl_demo.c                      |   1 +
 Quake/cl_input.c                     |   4 +-
 Quake/cl_main.c                      |   2 +-
 Quake/cl_parse.c                     |  50 ++--
 Quake/cl_tent.c                      |  72 +++---
 Quake/client.h                       |   1 +
 Quake/common.c                       |  48 +++-
 Quake/common.h                       |  12 +-
 Quake/gl_draw.c                      |   6 +-
 Quake/gl_model.c                     |   2 +
 Quake/gl_rlight.c                    |   2 -
 Quake/gl_rmain.c                     |   9 +-
 Quake/gl_screen.c                    |   4 +-
 Quake/gl_texmgr.c                    |  14 +-
 Quake/gl_vidsdl.c                    |  33 ++-
 Quake/gl_warp.c                      |   3 +
 Quake/glquake.h                      |   4 +-
 Quake/host.c                         |   5 +-
 Quake/host_cmd.c                     |  76 +++++-
 Quake/in_sdl.c                       | 439 ++++++++++++++++++++++++++++++++++-
 Quake/input.h                        |   2 +-
 Quake/keys.c                         |  13 +-
 Quake/keys.h                         |  15 +-
 Quake/mathlib.h                      |   1 +
 Quake/menu.c                         |  64 ++++-
 Quake/net_sys.h                      |   2 +-
 Quake/pr_cmds.c                      |  10 +-
 Quake/pr_edict.c                     |   2 +-
 Quake/protocol.h                     |  13 +-
 Quake/quakedef.h                     |   2 +-
 Quake/quakespasm.pak                 | Bin 558328 -> 558584 bytes
 Quake/r_brush.c                      |  16 +-
 Quake/r_part.c                       |   2 +-
 Quake/server.h                       |   1 +
 Quake/sv_main.c                      |  76 ++++--
 Quake/sv_user.c                      |  23 +-
 Quake/sys_sdl_unix.c                 |   1 +
 Quake/sys_sdl_win.c                  |   1 +
 Quake/view.c                         |   2 +-
 Quake/view.h                         |   1 +
 Quake/world.c                        |   6 +-
 Quakespasm.html                      | 140 ++++++++---
 Quakespasm.txt                       | 245 ++++++++++++++-----
 51 files changed, 1263 insertions(+), 285 deletions(-)

diff --git a/Linux/CodeBlocks/QuakeSpasm-SDL2.cbp b/Linux/CodeBlocks/QuakeSpasm-SDL2.cbp
index 4cead89..3085352 100644
--- a/Linux/CodeBlocks/QuakeSpasm-SDL2.cbp
+++ b/Linux/CodeBlocks/QuakeSpasm-SDL2.cbp
@@ -264,9 +264,9 @@
 			<Option compilerVar="CC" />
 		</Unit>
 		<Unit filename="../../Quake/snd_modplug.c">
-		<Unit filename="../../Quake/snd_modplug.h" />
 			<Option compilerVar="CC" />
 		</Unit>
+		<Unit filename="../../Quake/snd_modplug.h" />
 		<Unit filename="../../Quake/snd_mp3.c">
 			<Option compilerVar="CC" />
 		</Unit>
diff --git a/Linux/CodeBlocks/QuakeSpasm.cbp b/Linux/CodeBlocks/QuakeSpasm.cbp
index 31eee89..b1812e3 100644
--- a/Linux/CodeBlocks/QuakeSpasm.cbp
+++ b/Linux/CodeBlocks/QuakeSpasm.cbp
@@ -263,9 +263,9 @@
 			<Option compilerVar="CC" />
 		</Unit>
 		<Unit filename="../../Quake/snd_modplug.c">
-		<Unit filename="../../Quake/snd_modplug.h" />
 			<Option compilerVar="CC" />
 		</Unit>
+		<Unit filename="../../Quake/snd_modplug.h" />
 		<Unit filename="../../Quake/snd_mp3.c">
 			<Option compilerVar="CC" />
 		</Unit>
diff --git a/Linux/sgml/Quakespasm.sgml b/Linux/sgml/Quakespasm.sgml
index 9572fca..13bfe5a 100644
--- a/Linux/sgml/Quakespasm.sgml
+++ b/Linux/sgml/Quakespasm.sgml
@@ -4,16 +4,18 @@
 <toc>
 <verb></verb>
 
-<em>Page last edited: December 2015</em>
+<em>Page last edited: July 2016</em>
 
 <sect> About <p>
 
 <url url="http://quakespasm.sourceforge.net" name="QuakeSpasm">
-is a Quake 1 engine based on the SDL port of
-<url url="http://www.celephais.net/fitzquake" name="FitzQuake">.
+is a modern, cross-platform Quake 1 engine based on <url url="http://www.celephais.net/fitzquake" name="FitzQuake">.
 </p><p>
-It includes support for 64 bit CPUs and custom music playback, and includes a new
+It includes support for 64 bit CPUs and custom music playback, a new
 sound driver, some graphical niceities, and numerous bug-fixes and other improvements.
+</p><p>
+Quakespasm utilizes either the SDL or SDL2 frameworks, so choose which one works best for you.
+SDL is probably less buggy, but SDL2 has nicer features and smoother mouse input - though no CD support.
 
 <sect> Downloads <p>
 
@@ -32,7 +34,7 @@ sound driver, some graphical niceities, and numerous bug-fixes and other improve
 
 <item>Quakespasm's custom data is stored in "quakespasm.pak". Install this file alongside your id1 directory to enable the custom console background and other minor features.
 
-<item>For different sound drivers use "<bf>SDL_AUDIODRIVER=</bf><em>DRIVER</em><bf> ./quakespasm</bf>"
+<item>For different sound backend drivers use "<bf>SDL_AUDIODRIVER=</bf><em>DRIVER</em><bf> ./quakespasm</bf>"
 , where DRIVER may be alsa, dsp, pulse, esd ...
 
 <item><bf>Shift+Escape</bf> draws the Console.
@@ -57,10 +59,57 @@ Quakespasm can play various external music formats, including MP3, OGG and FLAC.
 <item>See <url url="Quakespasm-Music.txt"> for more details.
 </itemize>
 
+<sect1>Controller Support<p>
+The SDL2 variant of Quakespasm supports Xbox 360 style game controllers.
+<p>
+The default configuration uses the left analog stick for movement and the right for looking.
+<p>
+If your controller doesn't work you can try placing <url url="https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt" name="this file"> in your Quake directory, it is a community-maintained database that adds support for more controllers to SDL2.
+
+<sect2>Cvars<p>
+<itemize>
+<item>joy_deadzone - Fraction of the stick travel to be deadzone, between 0 and 1. Default 0.2.
+<item>joy_deadzone_trigger - Fraction of trigger range required to register a button press on the analog triggers, between 0 and 1. Default 0.001.
+<item>joy_sensitivity_yaw/pitch - Max angular speed in degrees/second when looking. Defaults are 300 for yaw (turning left/right) and 150 for pitch (up/down).
+<item>joy_exponent - For the look stick, the stick displacement (between 0 and 1) is raised to this power. Default is 3. A value of 1 would give a linear relationship between stick displacement and fraction of the maximum angular speed.
+<item>joy_invert - Set to 1 to invert the vertical axis of the look stick.
+<item>joy_swapmovelook - Set to 1 to swap the left and right analog stick functions. Default 0, move on the left stick, look on the right stick.
+<item>joy_enable - Set to 0 to disable controller support. Default 1.
+</itemize>
+
+<sect2>Buttons<p>
+Some of the controller buttons are hardcoded to allow navigating the menu:
+
+<itemize>
+<item>Back - alias for TAB
+<item>Start - alias for ESC
+<item>DPad, analog sticks - mapped to arrow keys
+<item>A Button - alias for ENTER in menus
+<item>B Button - alias for ESC in menus
+</itemize>
+
+These buttons can be bound normally:
+
+<itemize>
+<item>LTRIGGER - Left trigger
+<item>RTRIGGER - Right trigger
+<item>LSHOULDER - Left shoulder button
+<item>RSHOULDER - Right shoulder button
+<item>LTHUMB - Clicking the left thumbstick
+<item>RTHUMB - Clicking the right thumbstick
+<item>ABUTTON
+<item>BBUTTON
+<item>XBUTTON
+<item>YBUTTON
+</itemize>
+quakespasm.pak contains a default.cfg which has been updated to give some default bindings. L/R shoulder buttons are bound to weapon switching, and L/R triggers are jump and attack.
+<p>
+The controller support started as Jeremiah Sypult's implementation in Quakespasm-Rift and also uses ideas and code from LordHavoc (DarkPlaces).
 
 <sect> Compiling and Installation<p>
 <p>Quakespasm's (optional) custom data is now stored in the file <bf>quakespasm.pak</bf>. This file should be placed alongside your quakespasm binary and <bf>id1</bf> directory.</p>
-<p><em>To check-out the latest version of QuakeSpasm, use</em> <bf>svn co svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm</bf></p>
+<p><em>To checkout the latest version of QuakeSpasm, do:</em>
+<bf>svn co svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm</bf></p>
 
 <sect1> Linux/Unix <p>
 After extracting the source tarball, browse the Makefile and edit the music streaming options, then
@@ -98,9 +147,9 @@ Some versions of Xorg and SDL have brightness issues.
 Try setting "export SDL_VIDEO_X11_NODIRECTCOLOR=1", or if you have Xorg >= 7.5 and broken brightness,
 these patched libSDL binaries may help.
 <itemize>
-    <item><url url="http://sourceforge.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched.tgz/download"
+    <item><url url="http://sf.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched.tgz/download"
     name="Gamma patched libSDL (i686-linux)"></li>
-    <item><url url="http://sourceforge.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched-AMD64.tgz/download"
+    <item><url url="http://sf.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched-AMD64.tgz/download"
     name="Gamma patched libSDL (x86_64-linux)">
 </itemize>
 <p>
@@ -108,7 +157,25 @@ The "game" command doesn't execute quake.rc in the new game directory being swit
 </p>
 <sect> Changes<p>
 
-<sect1> Changes in 0.91.1<p>
+<sect1> Changes in 0.92.0<p>
+<itemize>
+<item> SDL2 Game Controller support.
+<item> Contrast support with new "contrast" cvar, behaving the same as MarkV. It may be a useful alternative to the existing gamma control for laptops in a bright environment, etc. Raising contrast gives less of a gray/washed out look than raising gamma, but at a disadvantage: colors near white get clipped to white.
+<item> RMQ protocol (999) support, adapted from RMQEngine.
+<item> New "-protocol x" command line option. Accepted values for 'x' are 15 (NetQuake), 666 (FitzQuake, default), and 999 (RMQ).
+<item> New "setpos" console command.
+<item> New "vid_borderless" cvar for getting a borderless window.
+<item> Increased MAX_MAP_LEAFS from 65535 to 70000 and MAX_LIGHTMAPS from 256 to 512 in order to handle the oms3 map pack.
+<item> Server edicts are now allocated using malloc instead of allocating on the hunk.
+<item> gl_clear now defaults to 1.
+<item> Fix items falling out of the world on oms3.bsp on SSE builds.
+<item> Worked around an OSX 10.6 driver bug when using FSAA, which was leading to an unplayable HOM effect on the rest of the screen.
+<item> Fix wrong trace endpoint from the tracepos console command.
+<item> Updated some of the third-party libraries. Other fixes/clean-ups.
+</itemize>
+</p>
+
+<sect1> Changes in 0.91.0<p>
 <sect2> Bugfixes
 <itemize>
 <item> Fix unwanted fog mode change upon video restart.
@@ -413,7 +480,7 @@ The "game" command doesn't execute quake.rc in the new game directory being swit
 <itemize>
 <item><url url="http://sourceforge.net/projects/quakespasm" name="QuakeSpasm Project page">
 <item><url url="http://sourceforge.net/p/quakespasm/bugs/?source=navbar" name="Bug reports">
-<item><url url="mailto:gmail - dot - com - username - sezeroz" name="Ozkan"> (project leader),  
+<item><url url="mailto:gmail - dot - com - username - sezeroz" name="Ozkan">,  
 <url url="mailto:gmail - dot - com - username - ewasylishen" name="Eric">,  
 <url url="mailto:gmail - dot - com - username - a.h.vandijk" name="Sander">,  
 <url url="mailto:yahoo - dot - com - username - stevenaaus" name="Stevenaaus">
diff --git a/Misc/qs_pak/default.cfg b/Misc/qs_pak/default.cfg
index 27186fd..25a4b05 100644
--- a/Misc/qs_pak/default.cfg
+++ b/Misc/qs_pak/default.cfg
@@ -94,6 +94,14 @@ bind	MOUSE2		+jump
 //bind	MOUSE3		+mlook
 
 //
+// game controller
+//
+bind	LSHOULDER	"impulse 12"
+bind	RSHOULDER	"impulse 10"
+bind	LTRIGGER	+jump
+bind	RTRIGGER	+attack
+
+//
 // default cvars
 //
 gamma 			1.0
diff --git a/Quake/Makefile.w32 b/Quake/Makefile.w32
index 9af0d46..1b84bc3 100644
--- a/Quake/Makefile.w32
+++ b/Quake/Makefile.w32
@@ -48,9 +48,9 @@ STRIP = strip
 #CPUFLAGS= -mtune=i686
 #CPUFLAGS= -march=pentium4
 CPUFLAGS=
-LDFLAGS =
+LDFLAGS = -m32 -mwindows
 DFLAGS ?=
-CFLAGS ?= -Wall -Wno-trigraphs
+CFLAGS ?= -m32 -Wall -Wno-trigraphs
 CFLAGS += $(CPUFLAGS)
 
 ifneq ($(DEBUG),0)
@@ -183,7 +183,7 @@ DEFAULT_TARGET := quakespasm.exe
 %.o:	%.c
 	$(CC) $(DFLAGS) -c $(CFLAGS) $(SDL_CFLAGS) -o $@ $^
 %.res:	../Windows/%.rc
-	$(WINDRES) --output-format=coff -I../Windows -o $@ $^
+	$(WINDRES) -I../Windows --output-format=coff --target=pe-i386 -o $@ $^
 
 # ----------------------------------------------------------------------------
 # objects
diff --git a/Quake/Makefile.w64 b/Quake/Makefile.w64
index a4de742..538a9d6 100644
--- a/Quake/Makefile.w64
+++ b/Quake/Makefile.w64
@@ -46,9 +46,9 @@ WINDRES = windres
 STRIP = strip
 
 CPUFLAGS=
-LDFLAGS =
+LDFLAGS = -m64 -mwindows
 DFLAGS ?=
-CFLAGS ?= -Wall -Wno-trigraphs
+CFLAGS ?= -m64 -Wall -Wno-trigraphs
 CFLAGS += $(CPUFLAGS)
 
 ifneq ($(DEBUG),0)
@@ -181,7 +181,7 @@ DEFAULT_TARGET := quakespasm.exe
 %.o:	%.c
 	$(CC) $(DFLAGS) -c $(CFLAGS) $(SDL_CFLAGS) -o $@ $^
 %.res:	../Windows/%.rc
-	$(WINDRES) --output-format=coff -I../Windows -o $@ $^
+	$(WINDRES) -I../Windows --output-format=coff --target=pe-x86-64 -o $@ $^
 
 # ----------------------------------------------------------------------------
 # objects
diff --git a/Quake/bgmusic.c b/Quake/bgmusic.c
index 5b0cbe0..3334496 100644
--- a/Quake/bgmusic.c
+++ b/Quake/bgmusic.c
@@ -366,6 +366,7 @@ void BGM_Resume (void)
 
 static void BGM_UpdateStream (void)
 {
+	qboolean did_rewind = false;
 	int	res;	/* Number of bytes read. */
 	int	bufferSamples;
 	int	fileSamples;
@@ -415,11 +416,19 @@ static void BGM_UpdateStream (void)
 							bgmstream->info.width,
 							bgmstream->info.channels,
 							raw, bgmvolume.value);
+			did_rewind = false;
 		}
 		else if (res == 0)	/* EOF */
 		{
 			if (bgmloop)
 			{
+				if (did_rewind)
+				{
+					Con_Printf("Stream keeps returning EOF.\n");
+					BGM_Stop();
+					return;
+				}
+
 				res = S_CodecRewindStream(bgmstream);
 				if (res != 0)
 				{
@@ -427,6 +436,7 @@ static void BGM_UpdateStream (void)
 					BGM_Stop();
 					return;
 				}
+				did_rewind = true;
 			}
 			else
 			{
diff --git a/Quake/bspfile.h b/Quake/bspfile.h
index dd31457..d1a36f1 100644
--- a/Quake/bspfile.h
+++ b/Quake/bspfile.h
@@ -35,7 +35,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define	MAX_MAP_PLANES		32767
 #define	MAX_MAP_NODES		32767 // because negative shorts are contents
 #define	MAX_MAP_CLIPNODES	32767
-#define	MAX_MAP_LEAFS		65535 //johnfitz -- was 8192
+#define	MAX_MAP_LEAFS		70000 //johnfitz -- was 8192
 #define	MAX_MAP_VERTS		65535
 #define	MAX_MAP_FACES		65535
 #define	MAX_MAP_MARKSURFACES 65535
diff --git a/Quake/cl_demo.c b/Quake/cl_demo.c
index b49b905..d1ad2bf 100644
--- a/Quake/cl_demo.c
+++ b/Quake/cl_demo.c
@@ -416,6 +416,7 @@ void CL_PlayDemo_f (void)
 // fscanf skips that byte too and screws up further reads.
 //	fscanf (cls.demofile, "%i\n", &cls.forcetrack);
 	cls.forcetrack = 0;
+	c = 0; /* silence pesky compiler warnings */
 	neg = false;
 	// read a decimal integer possibly with a leading '-',
 	// followed by a '\n':
diff --git a/Quake/cl_input.c b/Quake/cl_input.c
index ba33345..1183794 100644
--- a/Quake/cl_input.c
+++ b/Quake/cl_input.c
@@ -361,9 +361,9 @@ void CL_SendMove (const usercmd_t *cmd)
 	for (i=0 ; i<3 ; i++)
 		//johnfitz -- 16-bit angles for PROTOCOL_FITZQUAKE
 		if (cl.protocol == PROTOCOL_NETQUAKE)
-			MSG_WriteAngle (&buf, cl.viewangles[i]);
+			MSG_WriteAngle (&buf, cl.viewangles[i], cl.protocolflags);
 		else
-			MSG_WriteAngle16 (&buf, cl.viewangles[i]);
+			MSG_WriteAngle16 (&buf, cl.viewangles[i], cl.protocolflags);
 		//johnfitz
 
 	MSG_WriteShort (&buf, cmd->forwardmove);
diff --git a/Quake/cl_main.c b/Quake/cl_main.c
index 85d0cd2..4159f1c 100644
--- a/Quake/cl_main.c
+++ b/Quake/cl_main.c
@@ -726,7 +726,7 @@ void CL_Tracepos_f (void)
 {
 	vec3_t	v, w;
 
-	VectorScale(vpn, 8192.0, v);
+	VectorMA(r_refdef.vieworg, 8192.0, vpn, v);
 	TraceLine(r_refdef.vieworg, v, w);
 
 	if (VectorLength(w) == 0)
diff --git a/Quake/cl_parse.c b/Quake/cl_parse.c
index 9d55677..6e14ef4 100644
--- a/Quake/cl_parse.c
+++ b/Quake/cl_parse.c
@@ -180,7 +180,7 @@ void CL_ParseStartSoundPacket(void)
 		Host_Error ("CL_ParseStartSoundPacket: ent = %i", ent);
 
 	for (i = 0; i < 3; i++)
-		pos[i] = MSG_ReadCoord ();
+		pos[i] = MSG_ReadCoord (cl.protocolflags);
 
 	S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation);
 }
@@ -276,13 +276,27 @@ void CL_ParseServerInfo (void)
 // parse protocol version number
 	i = MSG_ReadLong ();
 	//johnfitz -- support multiple protocols
-	if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE) {
+	if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE && i != PROTOCOL_RMQ) {
 		Con_Printf ("\n"); //because there's no newline after serverinfo print
-		Host_Error ("Server returned version %i, not %i or %i", i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE);
+		Host_Error ("Server returned version %i, not %i or %i or %i", i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ);
 	}
 	cl.protocol = i;
 	//johnfitz
 
+	if (cl.protocol == PROTOCOL_RMQ)
+	{
+		const unsigned int supportedflags = (PRFL_SHORTANGLE | PRFL_FLOATANGLE | PRFL_24BITCOORD | PRFL_FLOATCOORD | PRFL_EDICTSCALE | PRFL_INT32COORD);
+		
+		// mh - read protocol flags from server so that we know what protocol features to expect
+		cl.protocolflags = (unsigned int) MSG_ReadLong ();
+		
+		if (0 != (cl.protocolflags & (~supportedflags)))
+		{
+			Con_Warning("PROTOCOL_RMQ protocolflags %i contains unsupported flags\n", cl.protocolflags);
+		}
+	}
+	else cl.protocolflags = 0;
+	
 // parse maxclients
 	cl.maxclients = MSG_ReadByte ();
 	if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD)
@@ -428,7 +442,7 @@ void CL_ParseUpdate (int bits)
 	}
 
 	//johnfitz -- PROTOCOL_FITZQUAKE
-	if (cl.protocol == PROTOCOL_FITZQUAKE)
+	if (cl.protocol == PROTOCOL_FITZQUAKE || cl.protocol == PROTOCOL_RMQ)
 	{
 		if (bits & U_EXTEND1)
 			bits |= MSG_ReadByte() << 16;
@@ -502,29 +516,29 @@ void CL_ParseUpdate (int bits)
 	VectorCopy (ent->msg_angles[0], ent->msg_angles[1]);
 
 	if (bits & U_ORIGIN1)
-		ent->msg_origins[0][0] = MSG_ReadCoord ();
+		ent->msg_origins[0][0] = MSG_ReadCoord (cl.protocolflags);
 	else
 		ent->msg_origins[0][0] = ent->baseline.origin[0];
 	if (bits & U_ANGLE1)
-		ent->msg_angles[0][0] = MSG_ReadAngle();
+		ent->msg_angles[0][0] = MSG_ReadAngle(cl.protocolflags);
 	else
 		ent->msg_angles[0][0] = ent->baseline.angles[0];
 
 	if (bits & U_ORIGIN2)
-		ent->msg_origins[0][1] = MSG_ReadCoord ();
+		ent->msg_origins[0][1] = MSG_ReadCoord (cl.protocolflags);
 	else
 		ent->msg_origins[0][1] = ent->baseline.origin[1];
 	if (bits & U_ANGLE2)
-		ent->msg_angles[0][1] = MSG_ReadAngle();
+		ent->msg_angles[0][1] = MSG_ReadAngle(cl.protocolflags);
 	else
 		ent->msg_angles[0][1] = ent->baseline.angles[1];
 
 	if (bits & U_ORIGIN3)
-		ent->msg_origins[0][2] = MSG_ReadCoord ();
+		ent->msg_origins[0][2] = MSG_ReadCoord (cl.protocolflags);
 	else
 		ent->msg_origins[0][2] = ent->baseline.origin[2];
 	if (bits & U_ANGLE3)
-		ent->msg_angles[0][2] = MSG_ReadAngle();
+		ent->msg_angles[0][2] = MSG_ReadAngle(cl.protocolflags);
 	else
 		ent->msg_angles[0][2] = ent->baseline.angles[2];
 
@@ -539,12 +553,14 @@ void CL_ParseUpdate (int bits)
 	//johnfitz
 
 	//johnfitz -- PROTOCOL_FITZQUAKE and PROTOCOL_NEHAHRA
-	if (cl.protocol == PROTOCOL_FITZQUAKE)
+	if (cl.protocol == PROTOCOL_FITZQUAKE || cl.protocol == PROTOCOL_RMQ)
 	{
 		if (bits & U_ALPHA)
 			ent->alpha = MSG_ReadByte();
 		else
 			ent->alpha = ent->baseline.alpha;
+		if (bits & U_SCALE)
+			MSG_ReadByte(); // PROTOCOL_RMQ: currently ignored
 		if (bits & U_FRAME2)
 			ent->frame = (ent->frame & 0x00FF) | (MSG_ReadByte() << 8);
 		if (bits & U_MODEL2)
@@ -634,8 +650,8 @@ void CL_ParseBaseline (entity_t *ent, int version) //johnfitz -- added argument
 	ent->baseline.skin = MSG_ReadByte();
 	for (i = 0; i < 3; i++)
 	{
-		ent->baseline.origin[i] = MSG_ReadCoord ();
-		ent->baseline.angles[i] = MSG_ReadAngle ();
+		ent->baseline.origin[i] = MSG_ReadCoord (cl.protocolflags);
+		ent->baseline.angles[i] = MSG_ReadAngle (cl.protocolflags);
 	}
 
 	ent->baseline.alpha = (bits & B_ALPHA) ? MSG_ReadByte() : ENTALPHA_DEFAULT; //johnfitz -- PROTOCOL_FITZQUAKE
@@ -896,7 +912,7 @@ void CL_ParseStaticSound (int version) //johnfitz -- added argument
 	int			i;
 
 	for (i = 0; i < 3; i++)
-		org[i] = MSG_ReadCoord ();
+		org[i] = MSG_ReadCoord (cl.protocolflags);
 
 	//johnfitz -- PROTOCOL_FITZQUAKE
 	if (version == 2)
@@ -987,8 +1003,8 @@ void CL_ParseServerMessage (void)
 		case svc_version:
 			i = MSG_ReadLong ();
 			//johnfitz -- support multiple protocols
-			if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE)
-				Host_Error ("Server returned version %i, not %i or %i", i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE);
+			if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE && i != PROTOCOL_RMQ)
+				Host_Error ("Server returned version %i, not %i or %i or %i", i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ);
 			cl.protocol = i;
 			//johnfitz
 			break;
@@ -1023,7 +1039,7 @@ void CL_ParseServerMessage (void)
 
 		case svc_setangle:
 			for (i=0 ; i<3 ; i++)
-				cl.viewangles[i] = MSG_ReadAngle ();
+				cl.viewangles[i] = MSG_ReadAngle (cl.protocolflags);
 			break;
 
 		case svc_setview:
diff --git a/Quake/cl_tent.c b/Quake/cl_tent.c
index b3bcbb2..b1c9b8c 100644
--- a/Quake/cl_tent.c
+++ b/Quake/cl_tent.c
@@ -65,13 +65,13 @@ void CL_ParseBeam (qmodel_t *m)
 
 	ent = MSG_ReadShort ();
 
-	start[0] = MSG_ReadCoord ();
-	start[1] = MSG_ReadCoord ();
-	start[2] = MSG_ReadCoord ();
+	start[0] = MSG_ReadCoord (cl.protocolflags);
+	start[1] = MSG_ReadCoord (cl.protocolflags);
+	start[2] = MSG_ReadCoord (cl.protocolflags);
 
-	end[0] = MSG_ReadCoord ();
-	end[1] = MSG_ReadCoord ();
-	end[2] = MSG_ReadCoord ();
+	end[0] = MSG_ReadCoord (cl.protocolflags);
+	end[1] = MSG_ReadCoord (cl.protocolflags);
+	end[2] = MSG_ReadCoord (cl.protocolflags);
 
 // override any beam with the same entity
 	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
@@ -125,25 +125,25 @@ void CL_ParseTEnt (void)
 	switch (type)
 	{
 	case TE_WIZSPIKE:			// spike hitting wall
-		pos[0] = MSG_ReadCoord ();
-		pos[1] = MSG_ReadCoord ();
-		pos[2] = MSG_ReadCoord ();
+		pos[0] = MSG_ReadCoord (cl.protocolflags);
+		pos[1] = MSG_ReadCoord (cl.protocolflags);
+		pos[2] = MSG_ReadCoord (cl.protocolflags);
 		R_RunParticleEffect (pos, vec3_origin, 20, 30);
 		S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
 		break;
 
 	case TE_KNIGHTSPIKE:			// spike hitting wall
-		pos[0] = MSG_ReadCoord ();
-		pos[1] = MSG_ReadCoord ();
-		pos[2] = MSG_ReadCoord ();
+		pos[0] = MSG_ReadCoord (cl.protocolflags);
+		pos[1] = MSG_ReadCoord (cl.protocolflags);
+		pos[2] = MSG_ReadCoord (cl.protocolflags);
 		R_RunParticleEffect (pos, vec3_origin, 226, 20);
 		S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
 		break;
 
 	case TE_SPIKE:			// spike hitting wall
-		pos[0] = MSG_ReadCoord ();
-		pos[1] = MSG_ReadCoord ();
-		pos[2] = MSG_ReadCoord ();
+		pos[0] = MSG_ReadCoord (cl.protocolflags);
+		pos[1] = MSG_ReadCoord (cl.protocolflags);
+		pos[2] = MSG_ReadCoord (cl.protocolflags);
 		R_RunParticleEffect (pos, vec3_origin, 0, 10);
 		if ( rand() % 5 )
 			S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
@@ -159,9 +159,9 @@ void CL_ParseTEnt (void)
 		}
 		break;
 	case TE_SUPERSPIKE:			// super spike hitting wall
-		pos[0] = MSG_ReadCoord ();
-		pos[1] = MSG_ReadCoord ();
-		pos[2] = MSG_ReadCoord ();
+		pos[0] = MSG_ReadCoord (cl.protocolflags);
+		pos[1] = MSG_ReadCoord (cl.protocolflags);
+		pos[2] = MSG_ReadCoord (cl.protocolflags);
 		R_RunParticleEffect (pos, vec3_origin, 0, 20);
 
 		if ( rand() % 5 )
@@ -179,16 +179,16 @@ void CL_ParseTEnt (void)
 		break;
 
 	case TE_GUNSHOT:			// bullet hitting wall
-		pos[0] = MSG_ReadCoord ();
-		pos[1] = MSG_ReadCoord ();
-		pos[2] = MSG_ReadCoord ();
+		pos[0] = MSG_ReadCoord (cl.protocolflags);
+		pos[1] = MSG_ReadCoord (cl.protocolflags);
+		pos[2] = MSG_ReadCoord (cl.protocolflags);
 		R_RunParticleEffect (pos, vec3_origin, 0, 20);
 		break;
 
 	case TE_EXPLOSION:			// rocket explosion
-		pos[0] = MSG_ReadCoord ();
-		pos[1] = MSG_ReadCoord ();
-		pos[2] = MSG_ReadCoord ();
+		pos[0] = MSG_ReadCoord (cl.protocolflags);
+		pos[1] = MSG_ReadCoord (cl.protocolflags);
+		pos[2] = MSG_ReadCoord (cl.protocolflags);
 		R_ParticleExplosion (pos);
 		dl = CL_AllocDlight (0);
 		VectorCopy (pos, dl->origin);
@@ -199,9 +199,9 @@ void CL_ParseTEnt (void)
 		break;
 
 	case TE_TAREXPLOSION:			// tarbaby explosion
-		pos[0] = MSG_ReadCoord ();
-		pos[1] = MSG_ReadCoord ();
-		pos[2] = MSG_ReadCoord ();
+		pos[0] = MSG_ReadCoord (cl.protocolflags);
+		pos[1] = MSG_ReadCoord (cl.protocolflags);
+		pos[2] = MSG_ReadCoord (cl.protocolflags);
 		R_BlobExplosion (pos);
 
 		S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
@@ -226,23 +226,23 @@ void CL_ParseTEnt (void)
 // PGM 01/21/97
 
 	case TE_LAVASPLASH:
-		pos[0] = MSG_ReadCoord ();
-		pos[1] = MSG_ReadCoord ();
-		pos[2] = MSG_ReadCoord ();
+		pos[0] = MSG_ReadCoord (cl.protocolflags);
+		pos[1] = MSG_ReadCoord (cl.protocolflags);
+		pos[2] = MSG_ReadCoord (cl.protocolflags);
 		R_LavaSplash (pos);
 		break;
 
 	case TE_TELEPORT:
-		pos[0] = MSG_ReadCoord ();
-		pos[1] = MSG_ReadCoord ();
-		pos[2] = MSG_ReadCoord ();
+		pos[0] = MSG_ReadCoord (cl.protocolflags);
+		pos[1] = MSG_ReadCoord (cl.protocolflags);
+		pos[2] = MSG_ReadCoord (cl.protocolflags);
 		R_TeleportSplash (pos);
 		break;
 
 	case TE_EXPLOSION2:				// color mapped explosion
-		pos[0] = MSG_ReadCoord ();
-		pos[1] = MSG_ReadCoord ();
-		pos[2] = MSG_ReadCoord ();
+		pos[0] = MSG_ReadCoord (cl.protocolflags);
+		pos[1] = MSG_ReadCoord (cl.protocolflags);
+		pos[2] = MSG_ReadCoord (cl.protocolflags);
 		colorStart = MSG_ReadByte ();
 		colorLength = MSG_ReadByte ();
 		R_ParticleExplosion2 (pos, colorStart, colorLength);
diff --git a/Quake/client.h b/Quake/client.h
index 37f8678..6f64ffa 100644
--- a/Quake/client.h
+++ b/Quake/client.h
@@ -224,6 +224,7 @@ typedef struct
 	scoreboard_t	*scores;		// [cl.maxclients]
 
 	unsigned	protocol; //johnfitz
+	unsigned	protocolflags;
 } client_state_t;
 
 
diff --git a/Quake/common.c b/Quake/common.c
index baaaac1..b1b3459 100644
--- a/Quake/common.c
+++ b/Quake/common.c
@@ -683,20 +683,32 @@ void MSG_WriteCoord32f (sizebuf_t *sb, float f)
 	MSG_WriteFloat (sb, f);
 }
 
-void MSG_WriteCoord (sizebuf_t *sb, float f)
+void MSG_WriteCoord (sizebuf_t *sb, float f, unsigned int flags)
 {
-	MSG_WriteCoord16 (sb, f);
+	if (flags & PRFL_FLOATCOORD)
+		MSG_WriteFloat (sb, f);
+	else if (flags & PRFL_INT32COORD)
+		MSG_WriteLong (sb, Q_rint (f * 16));
+	else if (flags & PRFL_24BITCOORD)
+		MSG_WriteCoord24 (sb, f);
+	else MSG_WriteCoord16 (sb, f);
 }
 
-void MSG_WriteAngle (sizebuf_t *sb, float f)
+void MSG_WriteAngle (sizebuf_t *sb, float f, unsigned int flags)
 {
-	MSG_WriteByte (sb, Q_rint(f * 256.0 / 360.0) & 255); //johnfitz -- use Q_rint instead of (int)
+	if (flags & PRFL_FLOATANGLE)
+		MSG_WriteFloat (sb, f);
+	else if (flags & PRFL_SHORTANGLE)
+		MSG_WriteShort (sb, Q_rint(f * 65536.0 / 360.0) & 65535);
+	else MSG_WriteByte (sb, Q_rint(f * 256.0 / 360.0) & 255); //johnfitz -- use Q_rint instead of (int)	}
 }
 
 //johnfitz -- for PROTOCOL_FITZQUAKE
-void MSG_WriteAngle16 (sizebuf_t *sb, float f)
+void MSG_WriteAngle16 (sizebuf_t *sb, float f, unsigned int flags)
 {
-	MSG_WriteShort (sb, Q_rint(f * 65536.0 / 360.0) & 65535);
+	if (flags & PRFL_FLOATANGLE)
+		MSG_WriteFloat (sb, f);
+	else MSG_WriteShort (sb, Q_rint(f * 65536.0 / 360.0) & 65535);
 }
 //johnfitz
 
@@ -842,20 +854,32 @@ float MSG_ReadCoord32f (void)
 	return MSG_ReadFloat();
 }
 
-float MSG_ReadCoord (void)
+float MSG_ReadCoord (unsigned int flags)
 {
-	return MSG_ReadCoord16();
+	if (flags & PRFL_FLOATCOORD)
+		return MSG_ReadFloat ();
+	else if (flags & PRFL_INT32COORD)
+		return MSG_ReadLong () * (1.0 / 16.0);
+	else if (flags & PRFL_24BITCOORD)
+		return MSG_ReadCoord24 ();
+	else return MSG_ReadCoord16 ();
 }
 
-float MSG_ReadAngle (void)
+float MSG_ReadAngle (unsigned int flags)
 {
-	return MSG_ReadChar() * (360.0/256);
+	if (flags & PRFL_FLOATANGLE)
+		return MSG_ReadFloat ();
+	else if (flags & PRFL_SHORTANGLE)
+		return MSG_ReadShort () * (360.0 / 65536);
+	else return MSG_ReadChar () * (360.0 / 256);
 }
 
 //johnfitz -- for PROTOCOL_FITZQUAKE
-float MSG_ReadAngle16 (void)
+float MSG_ReadAngle16 (unsigned int flags)
 {
-	return MSG_ReadShort() * (360.0 / 65536);
+	if (flags & PRFL_FLOATANGLE)
+		return MSG_ReadFloat ();	// make sure
+	else return MSG_ReadShort () * (360.0 / 65536);
 }
 //johnfitz
 
diff --git a/Quake/common.h b/Quake/common.h
index e10a454..8146114 100644
--- a/Quake/common.h
+++ b/Quake/common.h
@@ -100,9 +100,9 @@ void MSG_WriteShort (sizebuf_t *sb, int c);
 void MSG_WriteLong (sizebuf_t *sb, int c);
 void MSG_WriteFloat (sizebuf_t *sb, float f);
 void MSG_WriteString (sizebuf_t *sb, const char *s);
-void MSG_WriteCoord (sizebuf_t *sb, float f);
-void MSG_WriteAngle (sizebuf_t *sb, float f);
-void MSG_WriteAngle16 (sizebuf_t *sb, float f); //johnfitz
+void MSG_WriteCoord (sizebuf_t *sb, float f, unsigned int flags);
+void MSG_WriteAngle (sizebuf_t *sb, float f, unsigned int flags);
+void MSG_WriteAngle16 (sizebuf_t *sb, float f, unsigned int flags); //johnfitz
 
 extern	int			msg_readcount;
 extern	qboolean	msg_badread;		// set if a read goes beyond end of message
@@ -115,9 +115,9 @@ int MSG_ReadLong (void);
 float MSG_ReadFloat (void);
 const char *MSG_ReadString (void);
 
-float MSG_ReadCoord (void);
-float MSG_ReadAngle (void);
-float MSG_ReadAngle16 (void); //johnfitz
+float MSG_ReadCoord (unsigned int flags);
+float MSG_ReadAngle (unsigned int flags);
+float MSG_ReadAngle16 (unsigned int flags); //johnfitz
 
 //============================================================================
 
diff --git a/Quake/gl_draw.c b/Quake/gl_draw.c
index ff6d213..b8cbe49 100644
--- a/Quake/gl_draw.c
+++ b/Quake/gl_draw.c
@@ -701,10 +701,10 @@ void GL_SetCanvas (canvastype newcanvas)
 		glViewport (glx, gly, glwidth, glheight);
 		break;
 	case CANVAS_MENU:
-		s = q_min((float)glwidth / 320.0, (float)glheight / 200.0);
+		s = q_min((float)glwidth / 640.0, (float)glheight / 200.0); // ericw -- doubled width to 640 to accommodate long keybindings
 		s = CLAMP (1.0, scr_menuscale.value, s);
-		glOrtho (0, 320, 200, 0, -99999, 99999);
-		glViewport (glx + (glwidth - 320*s) / 2, gly + (glheight - 200*s) / 2, 320*s, 200*s);
+		glOrtho (0, 640, 200, 0, -99999, 99999);
+		glViewport (glx + (glwidth - 320*s) / 2, gly + (glheight - 200*s) / 2, 640*s, 200*s);
 		break;
 	case CANVAS_SBAR:
 		s = CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0);
diff --git a/Quake/gl_model.c b/Quake/gl_model.c
index a231b15..99d2b95 100644
--- a/Quake/gl_model.c
+++ b/Quake/gl_model.c
@@ -534,8 +534,10 @@ void Mod_LoadTextures (lump_t *l)
 					q_snprintf (filename2, sizeof(filename2), "%s_glow", filename);
 					data = Image_LoadImage (filename2, &fwidth, &fheight);
 					if (!data)
+					{
 						q_snprintf (filename2, sizeof(filename2), "%s_luma", filename);
 						data = Image_LoadImage (filename2, &fwidth, &fheight);
+					}
 
 					if (data)
 						tx->fullbright = TexMgr_LoadImage (loadmodel, filename2, fwidth, fheight,
diff --git a/Quake/gl_rlight.c b/Quake/gl_rlight.c
index e37c988..86c73b8 100644
--- a/Quake/gl_rlight.c
+++ b/Quake/gl_rlight.c
@@ -267,8 +267,6 @@ mplane_t		*lightplane;
 vec3_t			lightspot;
 vec3_t			lightcolor; //johnfitz -- lit support via lordhavoc
 
-#define DoublePrecisionDotProduct(x,y) ((double)x[0]*y[0]+(double)x[1]*y[1]+(double)x[2]*y[2])
-
 /*
 =============
 RecursiveLightPoint -- johnfitz -- replaced entire function for lit support via lordhavoc
diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c
index 908425a..78d6828 100644
--- a/Quake/gl_rmain.c
+++ b/Quake/gl_rmain.c
@@ -71,7 +71,7 @@ cvar_t	r_dynamic = {"r_dynamic","1",CVAR_ARCHIVE};
 cvar_t	r_novis = {"r_novis","0",CVAR_ARCHIVE};
 
 cvar_t	gl_finish = {"gl_finish","0",CVAR_NONE};
-cvar_t	gl_clear = {"gl_clear","0",CVAR_NONE};
+cvar_t	gl_clear = {"gl_clear","1",CVAR_NONE};
 cvar_t	gl_cull = {"gl_cull","1",CVAR_NONE};
 cvar_t	gl_smoothmodels = {"gl_smoothmodels","1",CVAR_NONE};
 cvar_t	gl_affinemodels = {"gl_affinemodels","0",CVAR_NONE};
@@ -124,6 +124,7 @@ static int r_gamma_texture_width, r_gamma_texture_height;
 
 // uniforms used in gamma shader
 static GLuint gammaLoc;
+static GLuint contrastLoc;
 static GLuint textureLoc;
 
 /*
@@ -158,9 +159,11 @@ static void GLSLGamma_CreateShaders (void)
 		"\n"
 		"uniform sampler2D GammaTexture;\n"
 		"uniform float GammaValue;\n"
+		"uniform float ContrastValue;\n"
 		"\n"
 		"void main(void) {\n"
 		"	  vec4 frag = texture2D(GammaTexture, gl_TexCoord[0].xy);\n"
+		"	  frag.rgb = frag.rgb * ContrastValue;\n"
 		"	  gl_FragColor = vec4(pow(frag.rgb, vec3(GammaValue)), 1.0);\n"
 		"}\n";
 
@@ -171,6 +174,7 @@ static void GLSLGamma_CreateShaders (void)
 
 // get uniform locations
 	gammaLoc = GL_GetUniformLocation (&r_gamma_program, "GammaValue");
+	contrastLoc = GL_GetUniformLocation (&r_gamma_program, "ContrastValue");
 	textureLoc = GL_GetUniformLocation (&r_gamma_program, "GammaTexture");
 }
 
@@ -186,7 +190,7 @@ void GLSLGamma_GammaCorrect (void)
 	if (!gl_glsl_gamma_able)
 		return;
 
-	if (vid_gamma.value == 1)
+	if (vid_gamma.value == 1 && vid_contrast.value == 1)
 		return;
 
 // create render-to-texture texture if needed
@@ -227,6 +231,7 @@ void GLSLGamma_GammaCorrect (void)
 // draw the texture back to the framebuffer with a fragment shader
 	GL_UseProgramFunc (r_gamma_program);
 	GL_Uniform1fFunc (gammaLoc, vid_gamma.value);
+	GL_Uniform1fFunc (contrastLoc, q_min(2.0, q_max(1.0, vid_contrast.value)));
 	GL_Uniform1iFunc (textureLoc, 0); // use texture unit 0
 
 	glDisable (GL_ALPHA_TEST);
diff --git a/Quake/gl_screen.c b/Quake/gl_screen.c
index 1c3c806..0d56047 100644
--- a/Quake/gl_screen.c
+++ b/Quake/gl_screen.c
@@ -924,6 +924,8 @@ int SCR_ModalMessage (const char *text, float timeout) //johnfitz -- timeout
 	} while (lastchar != 'y' && lastchar != 'Y' &&
 		 lastchar != 'n' && lastchar != 'N' &&
 		 lastkey != K_ESCAPE &&
+		 lastkey != K_ABUTTON &&
+		 lastkey != K_BBUTTON &&
 		 time2 <= time1);
 	Key_EndInputGrab ();
 
@@ -934,7 +936,7 @@ int SCR_ModalMessage (const char *text, float timeout) //johnfitz -- timeout
 		return false;
 	//johnfitz
 
-	return (lastchar == 'y' || lastchar == 'Y');
+	return (lastchar == 'y' || lastchar == 'Y' || lastkey == K_ABUTTON);
 }
 
 
diff --git a/Quake/gl_texmgr.c b/Quake/gl_texmgr.c
index 4637406..eacc7eb 100644
--- a/Quake/gl_texmgr.c
+++ b/Quake/gl_texmgr.c
@@ -671,8 +671,8 @@ TexMgr_SafeTextureSize -- return a size with hardware and user prefs in mind
 */
 int TexMgr_SafeTextureSize (int s)
 {
-    if (!gl_texture_NPOT)
-        s = TexMgr_Pad(s);
+	if (!gl_texture_NPOT)
+		s = TexMgr_Pad(s);
 	if ((int)gl_max_size.value > 0)
 		s = q_min(TexMgr_Pad((int)gl_max_size.value), s);
 	s = q_min(gl_hardware_maxsize, s);
@@ -1439,7 +1439,7 @@ void TexMgr_ReloadNobrightImages (void)
 ================================================================================
 */
 
-static GLuint	currenttexture[3] = {-1, -1, -1}; // to avoid unnecessary texture sets
+static GLuint	currenttexture[3] = {GL_UNUSED_TEXTURE, GL_UNUSED_TEXTURE, GL_UNUSED_TEXTURE}; // to avoid unnecessary texture sets
 static GLenum	currenttarget = GL_TEXTURE0_ARB;
 qboolean	mtexenabled = false;
 
@@ -1517,9 +1517,9 @@ static void GL_DeleteTexture (gltexture_t *texture)
 {
 	glDeleteTextures (1, &texture->texnum);
 
-	if (texture->texnum == currenttexture[0]) currenttexture[0] = -1;
-    if (texture->texnum == currenttexture[1]) currenttexture[1] = -1;
-    if (texture->texnum == currenttexture[2]) currenttexture[2] = -1;
+	if (texture->texnum == currenttexture[0]) currenttexture[0] = GL_UNUSED_TEXTURE;
+	if (texture->texnum == currenttexture[1]) currenttexture[1] = GL_UNUSED_TEXTURE;
+	if (texture->texnum == currenttexture[2]) currenttexture[2] = GL_UNUSED_TEXTURE;
 
 	texture->texnum = 0;
 }
@@ -1538,6 +1538,6 @@ void GL_ClearBindings(void)
 	int i;
 	for (i = 0; i < 3; i++)
 	{
-		currenttexture[i] = -1;
+		currenttexture[i] = GL_UNUSED_TEXTURE;
 	}
 }
diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c
index 169eba7..00a62a9 100644
--- a/Quake/gl_vidsdl.c
+++ b/Quake/gl_vidsdl.c
@@ -149,9 +149,11 @@ static cvar_t	vid_bpp = {"vid_bpp", "16", CVAR_ARCHIVE};
 static cvar_t	vid_vsync = {"vid_vsync", "0", CVAR_ARCHIVE};
 static cvar_t	vid_fsaa = {"vid_fsaa", "0", CVAR_ARCHIVE}; // QuakeSpasm
 static cvar_t	vid_desktopfullscreen = {"vid_desktopfullscreen", "0", CVAR_ARCHIVE}; // QuakeSpasm
+static cvar_t	vid_borderless = {"vid_borderless", "0", CVAR_ARCHIVE}; // QuakeSpasm
 //johnfitz
 
 cvar_t		vid_gamma = {"gamma", "1", CVAR_ARCHIVE}; //johnfitz -- moved here from view.c
+cvar_t		vid_contrast = {"contrast", "1", CVAR_ARCHIVE}; //QuakeSpasm, MarkV
 
 //==========================================================================
 //
@@ -271,7 +273,7 @@ static void VID_Gamma_f (cvar_t *var)
 	for (i = 0; i < 256; i++)
 	{
 		vid_gamma_red[i] =
-			CLAMP(0, (int) (255 * pow((i + 0.5)/255.5, var->value) + 0.5), 255) << 8;
+			CLAMP(0, (int) ((255 * pow((i + 0.5)/255.5, vid_gamma.value) + 0.5) * vid_contrast.value), 255) << 8;
 		vid_gamma_green[i] = vid_gamma_red[i];
 		vid_gamma_blue[i] = vid_gamma_red[i];
 	}
@@ -287,7 +289,9 @@ VID_Gamma_Init -- call on init
 static void VID_Gamma_Init (void)
 {
 	Cvar_RegisterVariable (&vid_gamma);
+	Cvar_RegisterVariable (&vid_contrast);
 	Cvar_SetCallback (&vid_gamma, VID_Gamma_f);
+	Cvar_SetCallback (&vid_contrast, VID_Gamma_f);
 
 	if (gl_glsl_gamma_able)
 		return;
@@ -491,9 +495,9 @@ VID_ValidMode
 static qboolean VID_ValidMode (int width, int height, int bpp, qboolean fullscreen)
 {
 // ignore width / height / bpp if vid_desktopfullscreen is enabled
-    if (fullscreen && vid_desktopfullscreen.value)
-        return true;
-    
+	if (fullscreen && vid_desktopfullscreen.value)
+		return true;
+
 	if (width < 320)
 		return false;
 
@@ -572,6 +576,9 @@ static qboolean VID_SetMode (int width, int height, int bpp, qboolean fullscreen
 	{
 		flags = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN;
 
+		if (vid_borderless.value)
+			flags |= SDL_WINDOW_BORDERLESS;
+		
 		draw_context = SDL_CreateWindow (caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags);
 		if (!draw_context) { // scale back fsaa
 			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
@@ -601,6 +608,7 @@ static qboolean VID_SetMode (int width, int height, int bpp, qboolean fullscreen
 	SDL_SetWindowSize (draw_context, width, height);
 	SDL_SetWindowPosition (draw_context, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
 	SDL_SetWindowDisplayMode (draw_context, VID_SDL2_GetDisplayMode(width, height, bpp));
+	SDL_SetWindowBordered (draw_context, vid_borderless.value ? SDL_FALSE : SDL_TRUE);
 
 	/* Make window fullscreen if needed, and show the window */
 
@@ -630,7 +638,9 @@ static qboolean VID_SetMode (int width, int height, int bpp, qboolean fullscreen
 	flags = DEFAULT_SDL_FLAGS;
 	if (fullscreen)
 		flags |= SDL_FULLSCREEN;
-
+	if (vid_borderless.value)
+		flags |= SDL_NOFRAME;
+	
 	gl_swap_control = true;
 	if (SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, (vid_vsync.value) ? 1 : 0) == -1)
 		gl_swap_control = false;
@@ -1231,6 +1241,10 @@ static void GL_Init (void)
 	gl_version = (const char *) glGetString (GL_VERSION);
 	gl_extensions = (const char *) glGetString (GL_EXTENSIONS);
 
+	Con_SafePrintf ("GL_VENDOR: %s\n", gl_vendor);
+	Con_SafePrintf ("GL_RENDERER: %s\n", gl_renderer);
+	Con_SafePrintf ("GL_VERSION: %s\n", gl_version);
+	
 	if (gl_version == NULL || sscanf(gl_version, "%d.%d", &gl_version_major, &gl_version_minor) < 2)
 	{
 		gl_version_major = 0;
@@ -1494,7 +1508,8 @@ void	VID_Init (void)
 					 "vid_bpp",
 					 "vid_vsync",
 					 "vid_fsaa",
-					 "vid_desktopfullscreen" };
+					 "vid_desktopfullscreen",
+					 "vid_borderless"};
 #define num_readvars	( sizeof(read_vars)/sizeof(read_vars[0]) )
 
 	Cvar_RegisterVariable (&vid_fullscreen); //johnfitz
@@ -1504,6 +1519,7 @@ void	VID_Init (void)
 	Cvar_RegisterVariable (&vid_vsync); //johnfitz
 	Cvar_RegisterVariable (&vid_fsaa); //QuakeSpasm
 	Cvar_RegisterVariable (&vid_desktopfullscreen); //QuakeSpasm
+	Cvar_RegisterVariable (&vid_borderless); //QuakeSpasm
 	Cvar_SetCallback (&vid_fullscreen, VID_Changed_f);
 	Cvar_SetCallback (&vid_width, VID_Changed_f);
 	Cvar_SetCallback (&vid_height, VID_Changed_f);
@@ -1511,7 +1527,8 @@ void	VID_Init (void)
 	Cvar_SetCallback (&vid_vsync, VID_Changed_f);
 	Cvar_SetCallback (&vid_fsaa, VID_FSAA_f);
 	Cvar_SetCallback (&vid_desktopfullscreen, VID_Changed_f);
-
+	Cvar_SetCallback (&vid_borderless, VID_Changed_f);
+	
 	Cmd_AddCommand ("vid_unlock", VID_Unlock); //johnfitz
 	Cmd_AddCommand ("vid_restart", VID_Restart); //johnfitz
 	Cmd_AddCommand ("vid_test", VID_Test); //johnfitz
@@ -1934,6 +1951,7 @@ static void VID_MenuKey (int key)
 	switch (key)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		VID_SyncCvars (); //sync cvars before leaving menu. FIXME: there are other ways to leave menu
 		S_LocalSound ("misc/menu1.wav");
 		M_Menu_Options_f ();
@@ -1997,6 +2015,7 @@ static void VID_MenuKey (int key)
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		m_entersound = true;
 		switch (video_options_cursor)
 		{
diff --git a/Quake/gl_warp.c b/Quake/gl_warp.c
index 17a7ee3..9c73b02 100644
--- a/Quake/gl_warp.c
+++ b/Quake/gl_warp.c
@@ -260,6 +260,9 @@ void R_UpdateWarpTextures (void)
 		tx->update_warp = false;
 	}
 
+	// ericw -- workaround for osx 10.6 driver bug when using FSAA. R_Clear only clears the warpimage part of the screen.
+	GL_SetCanvas(CANVAS_DEFAULT);
+
 	//if warp render went down into sbar territory, we need to be sure to refresh it next frame
 	if (gl_warpimagesize + sb_lines > glheight)
 		Sbar_Changed ();
diff --git a/Quake/glquake.h b/Quake/glquake.h
index a9df1e4..ff61cb1 100644
--- a/Quake/glquake.h
+++ b/Quake/glquake.h
@@ -30,6 +30,8 @@ void GL_Set2D (void);
 
 extern	int glx, gly, glwidth, glheight;
 
+#define	GL_UNUSED_TEXTURE	(~(GLuint)0)
+
 // r_local.h -- private refresh defs
 
 #define ALIAS_BASE_SIZE_RATIO		(1.0 / 11.0)
@@ -288,7 +290,7 @@ extern overflowtimes_t dev_overflows; //this stores the last time overflow messa
 
 //johnfitz -- moved here from r_brush.c
 extern int gl_lightmap_format, lightmap_bytes;
-#define MAX_LIGHTMAPS 256 //johnfitz -- was 64
+#define MAX_LIGHTMAPS 512 //johnfitz -- was 64
 extern gltexture_t *lightmap_textures[MAX_LIGHTMAPS]; //johnfitz -- changed to an array
 
 extern int gl_warpimagesize; //johnfitz -- for water warp
diff --git a/Quake/host.c b/Quake/host.c
index a39df6b..8481b09 100644
--- a/Quake/host.c
+++ b/Quake/host.c
@@ -221,7 +221,7 @@ void Host_Version_f (void)
 {
 	Con_Printf ("Quake Version %1.2f\n", VERSION);
 	Con_Printf ("QuakeSpasm Version %1.2f.%d\n", QUAKESPASM_VERSION, QUAKESPASM_VER_PATCH);
-	Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
+	Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n");
 }
 
 /* cvar callback functions : */
@@ -549,6 +549,7 @@ void Host_ClearMemory (void)
 /* host_hunklevel MUST be set at this point */
 	Hunk_FreeToLowMark (host_hunklevel);
 	cls.signon = 0;
+	free(sv.edicts); // ericw -- sv.edicts switched to use malloc()
 	memset (&sv, 0, sizeof(sv));
 	memset (&cl, 0, sizeof(cl));
 }
@@ -844,7 +845,7 @@ void Host_Init (void)
 	NET_Init ();
 	SV_Init ();
 
-	Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
+	Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n");
 	Con_Printf ("%4.1f megabyte heap\n", host_parms->memsize/ (1024*1024.0));
 
 	if (cls.state != ca_dedicated)
diff --git a/Quake/host_cmd.c b/Quake/host_cmd.c
index a43e37d..b8ff444 100644
--- a/Quake/host_cmd.c
+++ b/Quake/host_cmd.c
@@ -641,6 +641,67 @@ void Host_Noclip_f (void)
 }
 
 /*
+====================
+Host_SetPos_f
+
+adapted from fteqw, originally by Alex Shadowalker
+====================
+*/
+void Host_SetPos_f(void)
+{
+	if (cmd_source == src_command)
+	{
+		Cmd_ForwardToServer ();
+		return;
+	}
+	
+	if (pr_global_struct->deathmatch)
+		return;
+	
+	if (Cmd_Argc() != 7 && Cmd_Argc() != 4)
+	{
+		SV_ClientPrintf("usage:\n");
+		SV_ClientPrintf("   setpos <x> <y> <z>\n");
+		SV_ClientPrintf("   setpos <x> <y> <z> <pitch> <yaw> <roll>\n");
+		SV_ClientPrintf("current values:\n");
+		SV_ClientPrintf("   %i %i %i %i %i %i\n",
+			(int)sv_player->v.origin[0],
+			(int)sv_player->v.origin[1],
+			(int)sv_player->v.origin[2],
+			(int)sv_player->v.v_angle[0],
+			(int)sv_player->v.v_angle[1],
+			(int)sv_player->v.v_angle[2]);
+		return;
+	}
+	
+	if (sv_player->v.movetype != MOVETYPE_NOCLIP)
+	{
+		noclip_anglehack = true;
+		sv_player->v.movetype = MOVETYPE_NOCLIP;
+		SV_ClientPrintf ("noclip ON\n");
+	}
+	
+	//make sure they're not going to whizz away from it
+	sv_player->v.velocity[0] = 0;
+	sv_player->v.velocity[1] = 0;
+	sv_player->v.velocity[2] = 0;
+	
+	sv_player->v.origin[0] = atof(Cmd_Argv(1));
+	sv_player->v.origin[1] = atof(Cmd_Argv(2));
+	sv_player->v.origin[2] = atof(Cmd_Argv(3));
+	
+	if (Cmd_Argc() == 7)
+	{
+		sv_player->v.angles[0] = atof(Cmd_Argv(4));
+		sv_player->v.angles[1] = atof(Cmd_Argv(5));
+		sv_player->v.angles[2] = atof(Cmd_Argv(6));
+		sv_player->v.fixangle = 1;
+	}
+	
+	SV_LinkEdict (sv_player, false);
+}
+
+/*
 ==================
 Host_Fly_f
 
@@ -1164,10 +1225,14 @@ void Host_Loadgame_f (void)
 		}
 		else
 		{	// parse an edict
-
 			ent = EDICT_NUM(entnum);
-			memset (&ent->v, 0, progs->entityfields * 4);
-			ent->free = false;
+			if (entnum < sv.num_edicts) {
+				ent->free = false;
+				memset (&ent->v, 0, progs->entityfields * 4);
+			}
+			else {
+				memset (ent, 0, pr_edict_size);
+			}
 			ED_ParseEdict (start, ent);
 
 		// link it into the bsp tree
@@ -1650,8 +1715,8 @@ void Host_Spawn_f (void)
 	ent = EDICT_NUM( 1 + (host_client - svs.clients) );
 	MSG_WriteByte (&host_client->message, svc_setangle);
 	for (i = 0; i < 2; i++)
-		MSG_WriteAngle (&host_client->message, ent->v.angles[i] );
-	MSG_WriteAngle (&host_client->message, 0 );
+		MSG_WriteAngle (&host_client->message, ent->v.angles[i], sv.protocolflags );
+	MSG_WriteAngle (&host_client->message, 0, sv.protocolflags );
 
 	SV_WriteClientdataToMessage (sv_player, &host_client->message);
 
@@ -2236,6 +2301,7 @@ void Host_InitCommands (void)
 	Cmd_AddCommand ("reconnect", Host_Reconnect_f);
 	Cmd_AddCommand ("name", Host_Name_f);
 	Cmd_AddCommand ("noclip", Host_Noclip_f);
+	Cmd_AddCommand ("setpos", Host_SetPos_f); //QuakeSpasm
 
 	Cmd_AddCommand ("say", Host_Say_f);
 	Cmd_AddCommand ("say_team", Host_Say_Team_f);
diff --git a/Quake/in_sdl.c b/Quake/in_sdl.c
index 752f7a5..881af38 100644
--- a/Quake/in_sdl.c
+++ b/Quake/in_sdl.c
@@ -48,6 +48,21 @@ static cvar_t in_debugkeys = {"in_debugkeys", "0", CVAR_NONE};
 #include <IOKit/hidsystem/event_status_driver.h>
 #endif
 
+// SDL2 Game Controller cvars
+cvar_t	joy_deadzone = { "joy_deadzone", "0.175", CVAR_ARCHIVE };
+cvar_t	joy_deadzone_trigger = { "joy_deadzone_trigger", "0.001", CVAR_ARCHIVE };
+cvar_t	joy_sensitivity_yaw = { "joy_sensitivity_yaw", "300", CVAR_ARCHIVE };
+cvar_t	joy_sensitivity_pitch = { "joy_sensitivity_pitch", "150", CVAR_ARCHIVE };
+cvar_t	joy_invert = { "joy_invert", "0", CVAR_ARCHIVE };
+cvar_t	joy_exponent = { "joy_exponent", "3", CVAR_ARCHIVE };
+cvar_t	joy_swapmovelook = { "joy_swapmovelook", "0", CVAR_ARCHIVE };
+cvar_t	joy_enable = { "joy_enable", "1", CVAR_ARCHIVE };
+
+#if defined(USE_SDL2)
+static SDL_JoystickID joy_active_instaceid = -1;
+static SDL_GameController *joy_active_controller = NULL;
+#endif
+
 static qboolean	no_mouse = false;
 
 static int buttonremap[] =
@@ -257,6 +272,73 @@ void IN_Deactivate (qboolean free_cursor)
 	IN_BeginIgnoringMouseEvents();
 }
 
+void IN_StartupJoystick (void)
+{
+#if defined(USE_SDL2)
+	int i;
+	int nummappings;
+	char controllerdb[MAX_OSPATH];
+	SDL_GameController *gamecontroller;
+	
+	if (COM_CheckParm("-nojoy"))
+		return;
+	
+	if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1 )
+	{
+		Con_Warning("could not initialize SDL Game Controller\n");
+		return;
+	}
+	
+	// Load additional SDL2 controller definitions from gamecontrollerdb.txt
+	q_snprintf (controllerdb, sizeof(controllerdb), "%s/gamecontrollerdb.txt", com_basedir);
+	nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb);
+	if (nummappings > 0)
+		Con_Printf("%d mappings loaded from gamecontrollerdb.txt\n", nummappings);
+	
+	// Also try host_parms->userdir
+	if (host_parms->userdir != host_parms->basedir)
+	{
+		q_snprintf (controllerdb, sizeof(controllerdb), "%s/gamecontrollerdb.txt", host_parms->userdir);
+		nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb);
+		if (nummappings > 0)
+			Con_Printf("%d mappings loaded from gamecontrollerdb.txt\n", nummappings);
+	}
+
+	for (i = 0; i < SDL_NumJoysticks(); i++)
+	{
+		const char *joyname = SDL_JoystickNameForIndex(i);
+		if ( SDL_IsGameController(i) )
+		{
+			const char *controllername = SDL_GameControllerNameForIndex(i);
+			gamecontroller = SDL_GameControllerOpen(i);
+			if (gamecontroller)
+			{
+				Con_Printf("detected controller: %s\n", controllername != NULL ? controllername : "NULL");
+				
+				joy_active_instaceid = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller));
+				joy_active_controller = gamecontroller;
+				break;
+			}
+			else
+			{
+				Con_Warning("failed to open controller: %s\n", controllername != NULL ? controllername : "NULL");
+			}
+		}
+		else
+		{
+			Con_Warning("joystick missing controller mappings: %s\n", joyname != NULL ? joyname : "NULL" );
+		}
+	}
+#endif
+}
+
+void IN_ShutdownJoystick (void)
+{
+#if defined(USE_SDL2)
+	SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
+#endif
+}
+
 void IN_Init (void)
 {
 	textmode = Key_TextEntry();
@@ -282,31 +364,334 @@ void IN_Init (void)
 	Cvar_RegisterVariable(&in_disablemacosxmouseaccel);
 #endif
 	Cvar_RegisterVariable(&in_debugkeys);
+	Cvar_RegisterVariable(&joy_sensitivity_yaw);
+	Cvar_RegisterVariable(&joy_sensitivity_pitch);
+	Cvar_RegisterVariable(&joy_deadzone);
+	Cvar_RegisterVariable(&joy_deadzone_trigger);
+	Cvar_RegisterVariable(&joy_invert);
+	Cvar_RegisterVariable(&joy_exponent);
+	Cvar_RegisterVariable(&joy_swapmovelook);
+	Cvar_RegisterVariable(&joy_enable);
 
 	IN_Activate();
+	IN_StartupJoystick();
 }
 
 void IN_Shutdown (void)
 {
 	IN_Deactivate(true);
-}
-
-void IN_Commands (void)
-{
-/* TODO: implement this for joystick support */
+	IN_ShutdownJoystick();
 }
 
 extern cvar_t cl_maxpitch; /* johnfitz -- variable pitch clamping */
 extern cvar_t cl_minpitch; /* johnfitz -- variable pitch clamping */
 
 
-void IN_MouseMove(int dx, int dy)
+void IN_MouseMotion(int dx, int dy)
 {
 	total_dx += dx;
 	total_dy += dy;
 }
 
-void IN_Move (usercmd_t *cmd)
+#if defined(USE_SDL2)
+typedef struct joyaxis_s
+{
+	float x;
+	float y;
+} joyaxis_t;
+
+typedef struct joy_buttonstate_s
+{
+	qboolean buttondown[SDL_CONTROLLER_BUTTON_MAX];
+} joybuttonstate_t;
+
+typedef struct axisstate_s
+{
+	float axisvalue[SDL_CONTROLLER_AXIS_MAX]; // normalized to +-1
+} joyaxisstate_t;
+
+static joybuttonstate_t joy_buttonstate;
+static joyaxisstate_t joy_axisstate;
+
+static double joy_buttontimer[SDL_CONTROLLER_BUTTON_MAX];
+static double joy_emulatedkeytimer[10];
+
+/*
+================
+IN_AxisMagnitude
+
+Returns the vector length of the given joystick axis
+================
+*/
+static vec_t IN_AxisMagnitude(joyaxis_t axis)
+{
+	vec_t magnitude = sqrtf((axis.x * axis.x) + (axis.y * axis.y));
+	return magnitude;
+}
+
+/*
+================
+IN_ApplyLookEasing
+
+assumes axis values are in [-1, 1] and the vector magnitude has been clamped at 1.
+Raises the axis values to the given exponent, keeping signs.
+================
+*/
+static joyaxis_t IN_ApplyLookEasing(joyaxis_t axis, float exponent)
+{
+	joyaxis_t result = {0};
+	vec_t eased_magnitude;
+	vec_t magnitude = IN_AxisMagnitude(axis);
+	
+	if (magnitude == 0)
+		return result;
+	
+	eased_magnitude = powf(magnitude, exponent);
+	
+	result.x = axis.x * (eased_magnitude / magnitude);
+	result.y = axis.y * (eased_magnitude / magnitude);
+	return result;
+}
+
+/*
+================
+IN_ApplyMoveEasing
+
+clamps coordinates to a square with coordinates +/- sqrt(2)/2, then scales them to +/- 1.
+This wastes a bit of stick range, but gives the diagonals coordinates of (+/-1,+/-1),
+so holding the stick on a diagonal gives the same speed boost as holding the forward and strafe keyboard keys.
+================
+*/
+static joyaxis_t IN_ApplyMoveEasing(joyaxis_t axis)
+{
+	joyaxis_t result = {0};
+	const float v = sqrtf(2.0f) / 2.0f;
+	
+	result.x = q_max(-v, q_min(v, axis.x));
+	result.y = q_max(-v, q_min(v, axis.y));
+	
+	result.x /= v;
+	result.y /= v;
+
+	return result;
+}
+
+/*
+================
+IN_ApplyDeadzone
+
+in: raw joystick axis values converted to floats in +-1
+out: applies a circular deadzone and clamps the magnitude at 1
+     (my 360 controller is slightly non-circular and the stick travels further on the diagonals)
+
+deadzone is expected to satisfy 0 < deadzone < 1
+
+from https://github.com/jeremiah-sypult/Quakespasm-Rift
+and adapted from http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html
+================
+*/
+static joyaxis_t IN_ApplyDeadzone(joyaxis_t axis, float deadzone)
+{
+	joyaxis_t result = {0};
+	vec_t magnitude = IN_AxisMagnitude(axis);
+	
+	if ( magnitude > deadzone ) {
+		const vec_t new_magnitude = q_min(1.0, (magnitude - deadzone) / (1.0 - deadzone));
+		const vec_t scale = new_magnitude / magnitude;
+		result.x = axis.x * scale;
+		result.y = axis.y * scale;
+	}
+	
+	return result;
+}
+
+/*
+================
+IN_KeyForControllerButton
+================
+*/
+static int IN_KeyForControllerButton(SDL_GameControllerButton button)
+{
+	switch (button)
+	{
+		case SDL_CONTROLLER_BUTTON_A: return K_ABUTTON;
+		case SDL_CONTROLLER_BUTTON_B: return K_BBUTTON;
+		case SDL_CONTROLLER_BUTTON_X: return K_XBUTTON;
+		case SDL_CONTROLLER_BUTTON_Y: return K_YBUTTON;
+		case SDL_CONTROLLER_BUTTON_BACK: return K_TAB;
+		case SDL_CONTROLLER_BUTTON_START: return K_ESCAPE;
+		case SDL_CONTROLLER_BUTTON_LEFTSTICK: return K_LTHUMB;
+		case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return K_RTHUMB;
+		case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return K_LSHOULDER;
+		case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return K_RSHOULDER;
+		case SDL_CONTROLLER_BUTTON_DPAD_UP: return K_UPARROW;
+		case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return K_DOWNARROW;
+		case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return K_LEFTARROW;
+		case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return K_RIGHTARROW;
+		default: return 0;
+	}
+}
+
+/*
+================
+IN_JoyKeyEvent
+
+Sends a Key_Event if a unpressed -> pressed or pressed -> unpressed transition occurred,
+and generates key repeats if the button is held down.
+
+Adapted from DarkPlaces by lordhavoc
+================
+*/
+static void IN_JoyKeyEvent(qboolean wasdown, qboolean isdown, int key, double *timer)
+{
+	// we can't use `realtime` for key repeats because it is not monotomic
+	const double currenttime = Sys_DoubleTime();
+	
+	if (wasdown)
+	{
+		if (isdown)
+		{
+			if (currenttime >= *timer)
+			{
+				*timer = currenttime + 0.1;
+				Key_Event(key, true);
+			}
+		}
+		else
+		{
+			*timer = 0;
+			Key_Event(key, false);
+		}
+	}
+	else
+	{
+		if (isdown)
+		{
+			*timer = currenttime + 0.5;
+			Key_Event(key, true);
+		}
+	}
+}
+#endif
+
+/*
+================
+IN_Commands
+
+Emit key events for game controller buttons, including emulated buttons for analog sticks/triggers
+================
+*/
+void IN_Commands (void)
+{
+#if defined(USE_SDL2)
+	joyaxisstate_t newaxisstate;
+	int i;
+	const float stickthreshold = 0.9;
+	const float triggerthreshold = joy_deadzone_trigger.value;
+	
+	if (!joy_enable.value)
+		return;
+	
+	if (!joy_active_controller)
+		return;
+
+	// emit key events for controller buttons
+	for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
+	{
+		qboolean newstate = SDL_GameControllerGetButton(joy_active_controller, (SDL_GameControllerButton)i);
+		qboolean oldstate = joy_buttonstate.buttondown[i];
+		
+		joy_buttonstate.buttondown[i] = newstate;
+		
+		// NOTE: This can cause a reentrant call of IN_Commands, via SCR_ModalMessage when confirming a new game.
+		IN_JoyKeyEvent(oldstate, newstate, IN_KeyForControllerButton((SDL_GameControllerButton)i), &joy_buttontimer[i]);
+	}
+	
+	for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; i++)
+	{
+		newaxisstate.axisvalue[i] = SDL_GameControllerGetAxis(joy_active_controller, (SDL_GameControllerAxis)i) / 32768.0f;
+	}
+	
+	// emit emulated arrow keys so the analog sticks can be used in the menu
+	if (key_dest != key_game)
+	{
+		IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] < -stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] < -stickthreshold, K_LEFTARROW, &joy_emulatedkeytimer[0]);
+		IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] > stickthreshold,  newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] > stickthreshold, K_RIGHTARROW, &joy_emulatedkeytimer[1]);
+		IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] < -stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] < -stickthreshold, K_UPARROW, &joy_emulatedkeytimer[2]);
+		IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] > stickthreshold,  newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] > stickthreshold, K_DOWNARROW, &joy_emulatedkeytimer[3]);
+		IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] < -stickthreshold,newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] < -stickthreshold, K_LEFTARROW, &joy_emulatedkeytimer[4]);
+		IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] > stickthreshold, K_RIGHTARROW, &joy_emulatedkeytimer[5]);
+		IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] < -stickthreshold,newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] < -stickthreshold, K_UPARROW, &joy_emulatedkeytimer[6]);
+		IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] > stickthreshold, K_DOWNARROW, &joy_emulatedkeytimer[7]);
+	}
+	
+	// emit emulated keys for the analog triggers
+	IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERLEFT] > triggerthreshold,  newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERLEFT] > triggerthreshold, K_LTRIGGER, &joy_emulatedkeytimer[8]);
+	IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] > triggerthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] > triggerthreshold, K_RTRIGGER, &joy_emulatedkeytimer[9]);
+	
+	joy_axisstate = newaxisstate;
+#endif
+}
+
+/*
+================
+IN_JoyMove
+================
+*/
+void IN_JoyMove (usercmd_t *cmd)
+{
+#if defined(USE_SDL2)
+	float	speed;
+	joyaxis_t moveRaw, moveDeadzone, moveEased;
+	joyaxis_t lookRaw, lookDeadzone, lookEased;
+
+	if (!joy_enable.value)
+		return;
+	
+	if (!joy_active_controller)
+		return;
+	
+	moveRaw.x = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX];
+	moveRaw.y = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY];
+	lookRaw.x = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX];
+	lookRaw.y = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY];
+	
+	if (joy_swapmovelook.value)
+	{
+		joyaxis_t temp = moveRaw;
+		moveRaw = lookRaw;
+		lookRaw = temp;
+	}
+	
+	moveDeadzone = IN_ApplyDeadzone(moveRaw, joy_deadzone.value);
+	lookDeadzone = IN_ApplyDeadzone(lookRaw, joy_deadzone.value);
+
+	moveEased = IN_ApplyMoveEasing(moveDeadzone);
+	lookEased = IN_ApplyLookEasing(lookDeadzone, joy_exponent.value);
+	
+	if (in_speed.state & 1)
+		speed = cl_movespeedkey.value;
+	else
+		speed = 1;
+
+	cmd->sidemove += (cl_sidespeed.value * speed * moveEased.x);
+	cmd->forwardmove -= (cl_forwardspeed.value * speed * moveEased.y);
+
+	cl.viewangles[YAW] -= lookEased.x * joy_sensitivity_yaw.value * host_frametime;
+	cl.viewangles[PITCH] += lookEased.y * joy_sensitivity_pitch.value * (joy_invert.value ? -1.0 : 1.0) * host_frametime;
+
+	if (lookEased.x != 0 || lookEased.y != 0)
+		V_StopPitchDrift();
+
+	/* johnfitz -- variable pitch clamping */
+	if (cl.viewangles[PITCH] > cl_maxpitch.value)
+		cl.viewangles[PITCH] = cl_maxpitch.value;
+	if (cl.viewangles[PITCH] < cl_minpitch.value)
+		cl.viewangles[PITCH] = cl_minpitch.value;
+#endif
+}
+
+void IN_MouseMove(usercmd_t *cmd)
 {
 	int		dmx, dmy;
 
@@ -345,6 +730,12 @@ void IN_Move (usercmd_t *cmd)
 	}
 }
 
+void IN_Move(usercmd_t *cmd)
+{
+	IN_JoyMove(cmd);
+	IN_MouseMove(cmd);
+}
+
 void IN_ClearStates (void)
 {
 }
@@ -682,9 +1073,41 @@ void IN_SendKeyEvents (void)
 #endif
 
 		case SDL_MOUSEMOTION:
-			IN_MouseMove(event.motion.xrel, event.motion.yrel);
+			IN_MouseMotion(event.motion.xrel, event.motion.yrel);
 			break;
 
+#if defined(USE_SDL2)
+		case SDL_CONTROLLERDEVICEADDED:
+			if (joy_active_instaceid == -1)
+			{
+				joy_active_controller = SDL_GameControllerOpen(event.cdevice.which);
+				if (joy_active_controller == NULL)
+					Con_DPrintf("Couldn't open game controller\n");
+				else
+				{
+					SDL_Joystick *joy;
+					joy = SDL_GameControllerGetJoystick(joy_active_controller);
+					joy_active_instaceid = SDL_JoystickInstanceID(joy);
+				}
+			}
+			else
+				Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEADDED\n");
+			break;
+		case SDL_CONTROLLERDEVICEREMOVED:
+			if (joy_active_instaceid != -1 && event.cdevice.which == joy_active_instaceid)
+			{
+				SDL_GameControllerClose(joy_active_controller);
+				joy_active_controller = NULL;
+				joy_active_instaceid = -1;
+			}
+			else
+				Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEREMOVED\n");
+			break;
+		case SDL_CONTROLLERDEVICEREMAPPED:
+			Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEREMAPPED\n");
+			break;
+#endif
+				
 		case SDL_QUIT:
 			CL_Disconnect ();
 			Sys_Quit ();
diff --git a/Quake/input.h b/Quake/input.h
index ed7877f..fe551fb 100644
--- a/Quake/input.h
+++ b/Quake/input.h
@@ -32,7 +32,7 @@ void IN_Commands (void);
 // oportunity for devices to stick commands on the script buffer
 
 // mouse moved by dx and dy pixels
-void IN_MouseMove(int dx, int dy);
+void IN_MouseMotion(int dx, int dy);
 
 
 void IN_SendKeyEvents (void);
diff --git a/Quake/keys.c b/Quake/keys.c
index 9773976..69e2c20 100644
--- a/Quake/keys.c
+++ b/Quake/keys.c
@@ -40,8 +40,6 @@ int		history_line = 0;
 
 keydest_t	key_dest;
 
-#define		MAX_KEYS 256
-
 char		*keybindings[MAX_KEYS];
 qboolean	consolekeys[MAX_KEYS];	// if true, can't be rebound while in console
 qboolean	menubound[MAX_KEYS];	// if true, can't be rebound while in menu
@@ -163,6 +161,17 @@ keyname_t keynames[] =
 	{"BACKQUOTE", '`'},	// because a raw backquote may toggle the console
 	{"TILDE", '~'},		// because a raw tilde may toggle the console
 
+	{"LTHUMB", K_LTHUMB},
+	{"RTHUMB", K_RTHUMB},
+	{"LSHOULDER", K_LSHOULDER},
+	{"RSHOULDER", K_RSHOULDER},
+	{"ABUTTON", K_ABUTTON},
+	{"BBUTTON", K_BBUTTON},
+	{"XBUTTON", K_XBUTTON},
+	{"YBUTTON", K_YBUTTON},
+	{"LTRIGGER", K_LTRIGGER},
+	{"RTRIGGER", K_RTRIGGER},
+
 	{NULL,		0}
 };
 
diff --git a/Quake/keys.h b/Quake/keys.h
index 422fd4b..02cc41f 100644
--- a/Quake/keys.h
+++ b/Quake/keys.h
@@ -142,13 +142,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define K_MOUSE4		241
 #define K_MOUSE5		242
 
+// SDL2 game controller keys
+#define K_LTHUMB		243
+#define K_RTHUMB		244
+#define K_LSHOULDER		245
+#define K_RSHOULDER		246
+#define K_ABUTTON		247
+#define K_BBUTTON		248
+#define K_XBUTTON		249
+#define K_YBUTTON		250
+#define K_LTRIGGER		251
+#define K_RTRIGGER		252
+
+#define	MAX_KEYS		253
 
 #define	MAXCMDLINE	256
 
 typedef enum {key_game, key_console, key_message, key_menu} keydest_t;
 
 extern keydest_t	key_dest;
-extern	char	*keybindings[256];
+extern	char	*keybindings[MAX_KEYS];
 
 extern	char	key_lines[32][MAXCMDLINE];
 extern	int		edit_line;
diff --git a/Quake/mathlib.h b/Quake/mathlib.h
index a900131..a607905 100644
--- a/Quake/mathlib.h
+++ b/Quake/mathlib.h
@@ -52,6 +52,7 @@ static inline int IS_NAN (float x) {
 #define Q_rint(x) ((x) > 0 ? (int)((x) + 0.5) : (int)((x) - 0.5)) //johnfitz -- from joequake
 
 #define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
+#define DoublePrecisionDotProduct(x,y) ((double)x[0]*y[0]+(double)x[1]*y[1]+(double)x[2]*y[2])
 #define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
 #define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
 #define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
diff --git a/Quake/menu.c b/Quake/menu.c
index c9fd724..5a8ea15 100644
--- a/Quake/menu.c
+++ b/Quake/menu.c
@@ -276,6 +276,7 @@ void M_Main_Key (int key)
 	switch (key)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		IN_Activate();
 		key_dest = key_game;
 		m_state = m_none;
@@ -300,6 +301,7 @@ void M_Main_Key (int key)
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		m_entersound = true;
 
 		switch (m_main_cursor)
@@ -364,6 +366,7 @@ void M_SinglePlayer_Key (int key)
 	switch (key)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_Main_f ();
 		break;
 
@@ -381,6 +384,7 @@ void M_SinglePlayer_Key (int key)
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		m_entersound = true;
 
 		switch (m_singleplayer_cursor)
@@ -514,11 +518,13 @@ void M_Load_Key (int k)
 	switch (k)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_SinglePlayer_f ();
 		break;
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		S_LocalSound ("misc/menu2.wav");
 		if (!loadable[load_cursor])
 			return;
@@ -558,11 +564,13 @@ void M_Save_Key (int k)
 	switch (k)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_SinglePlayer_f ();
 		break;
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		m_state = m_none;
 		IN_Activate();
 		key_dest = key_game;
@@ -628,6 +636,7 @@ void M_MultiPlayer_Key (int key)
 	switch (key)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_Main_f ();
 		break;
 
@@ -645,6 +654,7 @@ void M_MultiPlayer_Key (int key)
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		m_entersound = true;
 		switch (m_multiplayer_cursor)
 		{
@@ -735,6 +745,7 @@ void M_Setup_Key (int k)
 	switch (k)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_MultiPlayer_f ();
 		break;
 
@@ -774,6 +785,7 @@ forward:
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		if (setup_cursor == 0 || setup_cursor == 1)
 			return;
 
@@ -926,6 +938,7 @@ again:
 	switch (k)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_MultiPlayer_f ();
 		break;
 
@@ -943,6 +956,7 @@ again:
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		m_entersound = true;
 		M_Menu_LanConfig_f ();
 		break;
@@ -965,6 +979,7 @@ enum
 	OPT_SCALE,
 	OPT_SCRSIZE,
 	OPT_GAMMA,
+	OPT_CONTRAST,
 	OPT_MOUSESPEED,
 	OPT_SBALPHA,
 	OPT_SNDVOL,
@@ -1024,6 +1039,12 @@ void M_AdjustSliders (int dir)
 		else if (f > 1)	f = 1;
 		Cvar_SetValue ("gamma", f);
 		break;
+	case OPT_CONTRAST:	// contrast
+		f = vid_contrast.value + dir * 0.1;
+		if (f < 1)	f = 1;
+		else if (f > 2)	f = 2;
+		Cvar_SetValue ("contrast", f);
+		break;
 	case OPT_MOUSESPEED:	// mouse speed
 		f = sensitivity.value + dir * 0.5;
 		if (f > 11)	f = 11;
@@ -1151,6 +1172,11 @@ void M_Options_Draw (void)
 	r = (1.0 - vid_gamma.value) / 0.5;
 	M_DrawSlider (220, 32 + 8*OPT_GAMMA, r);
 
+	// OPT_CONTRAST:
+	M_Print (16, 32 + 8*OPT_CONTRAST,	"              Contrast");
+	r = vid_contrast.value - 1.0;
+	M_DrawSlider (220, 32 + 8*OPT_CONTRAST, r);
+	
 	// OPT_MOUSESPEED:
 	M_Print (16, 32 + 8*OPT_MOUSESPEED,	"           Mouse Speed");
 	r = (sensitivity.value - 1)/10;
@@ -1209,11 +1235,13 @@ void M_Options_Key (int k)
 	switch (k)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_Main_f ();
 		break;
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		m_entersound = true;
 		switch (options_cursor)
 		{
@@ -1313,27 +1341,27 @@ void M_Menu_Keys_f (void)
 }
 
 
-void M_FindKeysForCommand (const char *command, int *twokeys)
+void M_FindKeysForCommand (const char *command, int *threekeys)
 {
 	int		count;
 	int		j;
 	int		l;
 	char	*b;
 
-	twokeys[0] = twokeys[1] = -1;
+	threekeys[0] = threekeys[1] = threekeys[2] = -1;
 	l = strlen(command);
 	count = 0;
 
-	for (j = 0; j < 256; j++)
+	for (j = 0; j < MAX_KEYS; j++)
 	{
 		b = keybindings[j];
 		if (!b)
 			continue;
 		if (!strncmp (b, command, l) )
 		{
-			twokeys[count] = j;
+			threekeys[count] = j;
 			count++;
-			if (count == 2)
+			if (count == 3)
 				break;
 		}
 	}
@@ -1347,7 +1375,7 @@ void M_UnbindCommand (const char *command)
 
 	l = strlen(command);
 
-	for (j = 0; j < 256; j++)
+	for (j = 0; j < MAX_KEYS; j++)
 	{
 		b = keybindings[j];
 		if (!b)
@@ -1362,7 +1390,7 @@ extern qpic_t	*pic_up, *pic_down;
 void M_Keys_Draw (void)
 {
 	int		i, x, y;
-	int		keys[2];
+	int		keys[3];
 	const char	*name;
 	qpic_t	*p;
 
@@ -1394,8 +1422,15 @@ void M_Keys_Draw (void)
 			x = strlen(name) * 8;
 			if (keys[1] != -1)
 			{
+				name = Key_KeynumToString (keys[1]);
 				M_Print (140 + x + 8, y, "or");
-				M_Print (140 + x + 32, y, Key_KeynumToString (keys[1]));
+				M_Print (140 + x + 32, y, name);
+				x = x + 32 + strlen(name) * 8;
+				if (keys[2] != -1)
+				{
+					M_Print (140 + x + 8, y, "or");
+					M_Print (140 + x + 32, y, Key_KeynumToString (keys[2]));
+				}
 			}
 		}
 	}
@@ -1410,7 +1445,7 @@ void M_Keys_Draw (void)
 void M_Keys_Key (int k)
 {
 	char	cmd[80];
-	int		keys[2];
+	int		keys[3];
 
 	if (bind_grab)
 	{	// defining a key
@@ -1429,6 +1464,7 @@ void M_Keys_Key (int k)
 	switch (k)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_Options_f ();
 		break;
 
@@ -1450,9 +1486,10 @@ void M_Keys_Key (int k)
 
 	case K_ENTER:		// go into bind mode
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		M_FindKeysForCommand (bindnames[keys_cursor][0], keys);
 		S_LocalSound ("misc/menu2.wav");
-		if (keys[1] != -1)
+		if (keys[2] != -1)
 			M_UnbindCommand (bindnames[keys_cursor][0]);
 		bind_grab = true;
 		IN_Activate(); // activate to allow mouse key binding
@@ -1515,6 +1552,7 @@ void M_Help_Key (int key)
 	switch (key)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_Main_f ();
 		break;
 
@@ -1744,6 +1782,7 @@ void M_LanConfig_Key (int key)
 	switch (key)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_Net_f ();
 		break;
 
@@ -1763,6 +1802,7 @@ void M_LanConfig_Key (int key)
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		if (lanConfig_cursor == 0)
 			break;
 
@@ -2254,6 +2294,7 @@ void M_GameOptions_Key (int key)
 	switch (key)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_Net_f ();
 		break;
 
@@ -2287,6 +2328,7 @@ void M_GameOptions_Key (int key)
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		S_LocalSound ("misc/menu2.wav");
 		if (gameoptions_cursor == 0)
 		{
@@ -2418,6 +2460,7 @@ void M_ServerList_Key (int k)
 	switch (k)
 	{
 	case K_ESCAPE:
+	case K_BBUTTON:
 		M_Menu_LanConfig_f ();
 		break;
 
@@ -2443,6 +2486,7 @@ void M_ServerList_Key (int k)
 
 	case K_ENTER:
 	case K_KP_ENTER:
+	case K_ABUTTON:
 		S_LocalSound ("misc/menu2.wav");
 		m_return_state = m_state;
 		m_return_onerror = true;
diff --git a/Quake/net_sys.h b/Quake/net_sys.h
index e61e02e..1ce742e 100644
--- a/Quake/net_sys.h
+++ b/Quake/net_sys.h
@@ -122,7 +122,7 @@ typedef unsigned int	in_addr_t;	/* u_int32_t */
 	WaitSelect((_N),(_R),(_W),(_E),(_T),NULL)
 #define	IOCTLARG_P(x)	(char *) x
 #if defined(__AMIGA__) && !defined(__MORPHOS__)
-#define	inet_ntoa(x) Inet_NtoA((ULONG *)&x)
+#define	inet_ntoa(x) Inet_NtoA(x.s_addr) /* Inet_NtoA(*(ULONG*)&x) */
 #define	h_errno Errno()
 #endif
 
diff --git a/Quake/pr_cmds.c b/Quake/pr_cmds.c
index bf1c4ee..afdd69b 100644
--- a/Quake/pr_cmds.c
+++ b/Quake/pr_cmds.c
@@ -589,7 +589,7 @@ static void PF_ambientsound (void)
 	//johnfitz
 
 	for (i = 0; i < 3; i++)
-		MSG_WriteCoord(&sv.signon, pos[i]);
+		MSG_WriteCoord(&sv.signon, pos[i], sv.protocolflags);
 
 	//johnfitz -- PROTOCOL_FITZQUAKE
 	if (large)
@@ -1506,12 +1506,12 @@ static void PF_WriteLong (void)
 
 static void PF_WriteAngle (void)
 {
-	MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1));
+	MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1), sv.protocolflags);
 }
 
 static void PF_WriteCoord (void)
 {
-	MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1));
+	MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1), sv.protocolflags);
 }
 
 static void PF_WriteString (void)
@@ -1583,8 +1583,8 @@ static void PF_makestatic (void)
 	MSG_WriteByte (&sv.signon, ent->v.skin);
 	for (i = 0; i < 3; i++)
 	{
-		MSG_WriteCoord(&sv.signon, ent->v.origin[i]);
-		MSG_WriteAngle(&sv.signon, ent->v.angles[i]);
+		MSG_WriteCoord(&sv.signon, ent->v.origin[i], sv.protocolflags);
+		MSG_WriteAngle(&sv.signon, ent->v.angles[i], sv.protocolflags);
 	}
 
 	//johnfitz -- PROTOCOL_FITZQUAKE
diff --git a/Quake/pr_edict.c b/Quake/pr_edict.c
index 90f2c49..9932e08 100644
--- a/Quake/pr_edict.c
+++ b/Quake/pr_edict.c
@@ -129,7 +129,7 @@ edict_t *ED_Alloc (void)
 
 	sv.num_edicts++;
 	e = EDICT_NUM(i);
-	ED_ClearEdict (e);
+	memset(e, 0, pr_edict_size); // ericw -- switched sv.edicts to malloc(), so we are accessing uninitialized memory and must fully zero it, not just ED_ClearEdict
 
 	return e;
 }
diff --git a/Quake/protocol.h b/Quake/protocol.h
index a53267b..81c7e96 100644
--- a/Quake/protocol.h
+++ b/Quake/protocol.h
@@ -27,6 +27,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #define	PROTOCOL_NETQUAKE	15 //johnfitz -- standard quake protocol
 #define PROTOCOL_FITZQUAKE	666 //johnfitz -- added new protocol for fitzquake 0.85
+#define PROTOCOL_RMQ		999
+
+// PROTOCOL_RMQ protocol flags
+#define PRFL_SHORTANGLE		(1 << 1)
+#define PRFL_FLOATANGLE		(1 << 2)
+#define PRFL_24BITCOORD		(1 << 3)
+#define PRFL_FLOATCOORD		(1 << 4)
+#define PRFL_EDICTSCALE		(1 << 5)
+#define PRFL_ALPHASANITY	(1 << 6)	// cleanup insanity with alpha
+#define PRFL_INT32COORD		(1 << 7)
+#define PRFL_MOREFLAGS		(1 << 31)	// not supported
 
 // if the high bit of the servercmd is set, the low bits are fast update flags:
 #define	U_MOREBITS		(1<<0)
@@ -52,7 +63,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define U_FRAME2		(1<<17) // 1 byte, this is .frame & 0xFF00 (second byte)
 #define U_MODEL2		(1<<18) // 1 byte, this is .modelindex & 0xFF00 (second byte)
 #define U_LERPFINISH	(1<<19) // 1 byte, 0.0-1.0 maps to 0-255, not sent if exactly 0.1, this is ent->v.nextthink - sv.time, used for lerping
-#define U_UNUSED20		(1<<20)
+#define U_SCALE			(1<<20) // 1 byte, for PROTOCOL_RMQ PRFL_EDICTSCALE, currently read but ignored
 #define U_UNUSED21		(1<<21)
 #define U_UNUSED22		(1<<22)
 #define U_EXTEND2		(1<<23) // another byte to follow, future expansion
diff --git a/Quake/quakedef.h b/Quake/quakedef.h
index f9ae266..639741c 100644
--- a/Quake/quakedef.h
+++ b/Quake/quakedef.h
@@ -36,7 +36,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define	X11_VERSION		1.10
 
 #define	FITZQUAKE_VERSION	0.85	//johnfitz
-#define	QUAKESPASM_VERSION	0.91
+#define	QUAKESPASM_VERSION	0.92
 #define	QUAKESPASM_VER_PATCH	0	// helper to print a string like 0.91.0
 
 //define	PARANOID			// speed sapping error checking
diff --git a/Quake/quakespasm.pak b/Quake/quakespasm.pak
index c0c7b09..52a5b10 100644
Binary files a/Quake/quakespasm.pak and b/Quake/quakespasm.pak differ
diff --git a/Quake/r_brush.c b/Quake/r_brush.c
index bb9e5e7..d9bde1e 100644
--- a/Quake/r_brush.c
+++ b/Quake/r_brush.c
@@ -555,7 +555,7 @@ void R_DrawBrushModel (entity_t *e)
 		}
 	}
 
-    glPushMatrix ();
+	glPushMatrix ();
 	e->angles[0] = -e->angles[0];	// stupid quake bug
 	if (gl_zfix.value)
 	{
@@ -1154,6 +1154,7 @@ void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
 
 	// add all the lightmaps
 		if (lightmap)
+		{
 			for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ;
 				 maps++)
 			{
@@ -1169,6 +1170,7 @@ void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
 				}
 				//johnfitz
 			}
+		}
 
 	// add all the dynamic lights
 		if (surf->dlightframe == r_framecount)
@@ -1203,9 +1205,9 @@ void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
 					g = *bl++ >> 7;
 					b = *bl++ >> 7;
 				}
-				if (r > 255) r = 255; *dest++ = r;
-				if (g > 255) g = 255; *dest++ = g;
-				if (b > 255) b = 255; *dest++ = b;
+				*dest++ = (r > 255)? 255 : r;
+				*dest++ = (g > 255)? 255 : g;
+				*dest++ = (b > 255)? 255 : b;
 				*dest++ = 255;
 			}
 		}
@@ -1229,9 +1231,9 @@ void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
 					g = *bl++ >> 7;
 					b = *bl++ >> 7;
 				}
-				if (b > 255) b = 255; *dest++ = b;
-				if (g > 255) g = 255; *dest++ = g;
-				if (r > 255) r = 255; *dest++ = r;
+				*dest++ = (b > 255)? 255 : b;
+				*dest++ = (g > 255)? 255 : g;
+				*dest++ = (r > 255)? 255 : r;
 				*dest++ = 255;
 			}
 		}
diff --git a/Quake/r_part.c b/Quake/r_part.c
index 3f474b2..8bc1bf3 100644
--- a/Quake/r_part.c
+++ b/Quake/r_part.c
@@ -329,7 +329,7 @@ void R_ParseParticleEffect (void)
 	int			i, count, msgcount, color;
 
 	for (i=0 ; i<3 ; i++)
-		org[i] = MSG_ReadCoord ();
+		org[i] = MSG_ReadCoord (cl.protocolflags);
 	for (i=0 ; i<3 ; i++)
 		dir[i] = MSG_ReadChar () * (1.0/16);
 	msgcount = MSG_ReadByte ();
diff --git a/Quake/server.h b/Quake/server.h
index 635a567..114432d 100644
--- a/Quake/server.h
+++ b/Quake/server.h
@@ -74,6 +74,7 @@ typedef struct
 	byte		signon_buf[MAX_MSGLEN-2]; //johnfitz -- was 8192, now uses MAX_MSGLEN
 
 	unsigned	protocol; //johnfitz
+	unsigned	protocolflags;
 } server_t;
 
 
diff --git a/Quake/sv_main.c b/Quake/sv_main.c
index b5d11ac..401b43a 100644
--- a/Quake/sv_main.c
+++ b/Quake/sv_main.c
@@ -50,13 +50,13 @@ void SV_Protocol_f (void)
 		break;
 	case 2:
 		i = atoi(Cmd_Argv(1));
-		if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE)
-			Con_Printf ("sv_protocol must be %i or %i\n", PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE);
+		if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE && i != PROTOCOL_RMQ)
+			Con_Printf ("sv_protocol must be %i or %i or %i\n", PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ);
 		else
 		{
 			sv_protocol = i;
 			if (sv.active)
-			Con_Printf ("changes will not take effect until the next level load.\n");
+				Con_Printf ("changes will not take effect until the next level load.\n");
 		}
 		break;
 	default:
@@ -73,6 +73,7 @@ SV_Init
 void SV_Init (void)
 {
 	int		i;
+	const char	*p;
 	extern	cvar_t	sv_maxvelocity;
 	extern	cvar_t	sv_gravity;
 	extern	cvar_t	sv_nostep;
@@ -86,6 +87,8 @@ void SV_Init (void)
 	extern	cvar_t	sv_aim;
 	extern	cvar_t	sv_altnoclip; //johnfitz
 
+	sv.edicts = NULL; // ericw -- sv.edicts switched to use malloc()
+
 	Cvar_RegisterVariable (&sv_maxvelocity);
 	Cvar_RegisterVariable (&sv_gravity);
 	Cvar_RegisterVariable (&sv_friction);
@@ -106,6 +109,27 @@ void SV_Init (void)
 
 	for (i=0 ; i<MAX_MODELS ; i++)
 		sprintf (localmodels[i], "*%i", i);
+
+	i = COM_CheckParm ("-protocol");
+	if (i && i < com_argc - 1)
+		sv_protocol = atoi (com_argv[i + 1]);
+	switch (sv_protocol)
+	{
+	case PROTOCOL_NETQUAKE:
+		p = "NetQuake";
+		break;
+	case PROTOCOL_FITZQUAKE:
+		p = "FitzQuake";
+		break;
+	case PROTOCOL_RMQ:
+		p = "RMQ";
+		break;
+	default:
+		Sys_Error ("Bad protocol version request %i. Accepted values: %i, %i, %i.",
+				sv_protocol, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ);
+		p = "Unknown";
+	}
+	Sys_Printf ("Server using protocol %i (%s)\n", sv_protocol, p);
 }
 
 /*
@@ -130,9 +154,9 @@ void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
 	if (sv.datagram.cursize > MAX_DATAGRAM-16)
 		return;
 	MSG_WriteByte (&sv.datagram, svc_particle);
-	MSG_WriteCoord (&sv.datagram, org[0]);
-	MSG_WriteCoord (&sv.datagram, org[1]);
-	MSG_WriteCoord (&sv.datagram, org[2]);
+	MSG_WriteCoord (&sv.datagram, org[0], sv.protocolflags);
+	MSG_WriteCoord (&sv.datagram, org[1], sv.protocolflags);
+	MSG_WriteCoord (&sv.datagram, org[2], sv.protocolflags);
 	for (i=0 ; i<3 ; i++)
 	{
 		v = dir[i]*16;
@@ -239,7 +263,7 @@ void SV_StartSound (edict_t *entity, int channel, const char *sample, int volume
 	//johnfitz
 
 	for (i = 0; i < 3; i++)
-		MSG_WriteCoord (&sv.datagram, entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]));
+		MSG_WriteCoord (&sv.datagram, entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]), sv.protocolflags);
 }
 
 /*
@@ -270,6 +294,13 @@ void SV_SendServerinfo (client_t *client)
 
 	MSG_WriteByte (&client->message, svc_serverinfo);
 	MSG_WriteLong (&client->message, sv.protocol); //johnfitz -- sv.protocol instead of PROTOCOL_VERSION
+	
+	if (sv.protocol == PROTOCOL_RMQ)
+	{
+		// mh - now send protocol flags so that the client knows the protocol features to expect
+		MSG_WriteLong (&client->message, sv.protocolflags);
+	}
+	
 	MSG_WriteByte (&client->message, svs.maxclients);
 
 	if (!coop.value && deathmatch.value)
@@ -673,17 +704,17 @@ void SV_WriteEntitiesToClient (edict_t	*clent, sizebuf_t *msg)
 		if (bits & U_EFFECTS)
 			MSG_WriteByte (msg, ent->v.effects);
 		if (bits & U_ORIGIN1)
-			MSG_WriteCoord (msg, ent->v.origin[0]);
+			MSG_WriteCoord (msg, ent->v.origin[0], sv.protocolflags);
 		if (bits & U_ANGLE1)
-			MSG_WriteAngle(msg, ent->v.angles[0]);
+			MSG_WriteAngle(msg, ent->v.angles[0], sv.protocolflags);
 		if (bits & U_ORIGIN2)
-			MSG_WriteCoord (msg, ent->v.origin[1]);
+			MSG_WriteCoord (msg, ent->v.origin[1], sv.protocolflags);
 		if (bits & U_ANGLE2)
-			MSG_WriteAngle(msg, ent->v.angles[1]);
+			MSG_WriteAngle(msg, ent->v.angles[1], sv.protocolflags);
 		if (bits & U_ORIGIN3)
-			MSG_WriteCoord (msg, ent->v.origin[2]);
+			MSG_WriteCoord (msg, ent->v.origin[2], sv.protocolflags);
 		if (bits & U_ANGLE3)
-			MSG_WriteAngle(msg, ent->v.angles[2]);
+			MSG_WriteAngle(msg, ent->v.angles[2], sv.protocolflags);
 
 		//johnfitz -- PROTOCOL_FITZQUAKE
 		if (bits & U_ALPHA)
@@ -748,7 +779,7 @@ void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg)
 		MSG_WriteByte (msg, ent->v.dmg_save);
 		MSG_WriteByte (msg, ent->v.dmg_take);
 		for (i=0 ; i<3 ; i++)
-			MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i]));
+			MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i]), sv.protocolflags );
 
 		ent->v.dmg_take = 0;
 		ent->v.dmg_save = 0;
@@ -764,7 +795,7 @@ void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg)
 	{
 		MSG_WriteByte (msg, svc_setangle);
 		for (i=0 ; i < 3 ; i++)
-			MSG_WriteAngle (msg, ent->v.angles[i] );
+			MSG_WriteAngle (msg, ent->v.angles[i], sv.protocolflags );
 		ent->v.fixangle = 0;
 	}
 
@@ -1208,8 +1239,8 @@ void SV_CreateBaseline (void)
 		MSG_WriteByte (&sv.signon, svent->baseline.skin);
 		for (i=0 ; i<3 ; i++)
 		{
-			MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]);
-			MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]);
+			MSG_WriteCoord(&sv.signon, svent->baseline.origin[i], sv.protocolflags);
+			MSG_WriteAngle(&sv.signon, svent->baseline.angles[i], sv.protocolflags);
 		}
 
 		//johnfitz -- PROTOCOL_FITZQUAKE
@@ -1325,6 +1356,14 @@ void SV_SpawnServer (const char *server)
 	q_strlcpy (sv.name, server, sizeof(sv.name));
 
 	sv.protocol = sv_protocol; // johnfitz
+	
+	if (sv.protocol == PROTOCOL_RMQ)
+	{
+		// set up the protocol flags used by this server
+		// (note - these could be cvar-ised so that server admins could choose the protocol features used by their servers)
+		sv.protocolflags = PRFL_INT32COORD | PRFL_SHORTANGLE;
+	}
+	else sv.protocolflags = 0;
 
 // load progs to get entity field count
 	PR_LoadProgs ();
@@ -1332,7 +1371,7 @@ void SV_SpawnServer (const char *server)
 // allocate server memory
 	/* Host_ClearMemory() called above already cleared the whole sv structure */
 	sv.max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS); //johnfitz -- max_edicts cvar
-	sv.edicts = (edict_t *) Hunk_AllocName (sv.max_edicts*pr_edict_size, "edicts");
+	sv.edicts = (edict_t *) malloc (sv.max_edicts*pr_edict_size); // ericw -- sv.edicts switched to use malloc()
 
 	sv.datagram.maxsize = sizeof(sv.datagram_buf);
 	sv.datagram.cursize = 0;
@@ -1348,6 +1387,7 @@ void SV_SpawnServer (const char *server)
 
 // leave slots at start for clients only
 	sv.num_edicts = svs.maxclients+1;
+	memset(sv.edicts, 0, sv.num_edicts*pr_edict_size); // ericw -- sv.edicts switched to use malloc()
 	for (i=0 ; i<svs.maxclients ; i++)
 	{
 		ent = EDICT_NUM(i+1);
diff --git a/Quake/sv_user.c b/Quake/sv_user.c
index abb72eb..c3e4cb5 100644
--- a/Quake/sv_user.c
+++ b/Quake/sv_user.c
@@ -31,9 +31,6 @@ extern	cvar_t	sv_stopspeed;
 
 static	vec3_t		forward, right, up;
 
-vec3_t	wishdir;
-float	wishspeed;
-
 // world
 float	*angles;
 float	*origin;
@@ -168,7 +165,7 @@ SV_Accelerate
 */
 cvar_t	sv_maxspeed = {"sv_maxspeed", "320", CVAR_NOTIFY|CVAR_SERVERINFO};
 cvar_t	sv_accelerate = {"sv_accelerate", "10", CVAR_NONE};
-void SV_Accelerate (void)
+void SV_Accelerate (float wishspeed, const vec3_t wishdir)
 {
 	int			i;
 	float		addspeed, accelspeed, currentspeed;
@@ -185,7 +182,7 @@ void SV_Accelerate (void)
 		velocity[i] += accelspeed*wishdir[i];
 }
 
-void SV_AirAccelerate (vec3_t wishveloc)
+void SV_AirAccelerate (float wishspeed, vec3_t wishveloc)
 {
 	int			i;
 	float		addspeed, wishspd, accelspeed, currentspeed;
@@ -229,8 +226,7 @@ void SV_WaterMove (void)
 {
 	int		i;
 	vec3_t	wishvel;
-	float	speed, newspeed, addspeed, accelspeed;
-	//float	wishspeed;
+	float	speed, newspeed, wishspeed, addspeed, accelspeed;
 
 //
 // user intentions
@@ -329,7 +325,8 @@ SV_AirMove
 void SV_AirMove (void)
 {
 	int			i;
-	vec3_t		wishvel;
+	vec3_t		wishvel, wishdir;
+	float		wishspeed;
 	float		fmove, smove;
 
 	AngleVectors (sv_player->v.angles, forward, right, up);
@@ -364,11 +361,11 @@ void SV_AirMove (void)
 	else if ( onground )
 	{
 		SV_UserFriction ();
-		SV_Accelerate ();
+		SV_Accelerate (wishspeed, wishdir);
 	}
 	else
 	{	// not on ground, so little effect on velocity
-		SV_AirAccelerate (wishvel);
+		SV_AirAccelerate (wishspeed, wishvel);
 	}
 }
 
@@ -453,9 +450,9 @@ void SV_ReadClientMove (usercmd_t *move)
 	for (i=0 ; i<3 ; i++)
 		//johnfitz -- 16-bit angles for PROTOCOL_FITZQUAKE
 		if (sv.protocol == PROTOCOL_NETQUAKE)
-			angle[i] = MSG_ReadAngle ();
+			angle[i] = MSG_ReadAngle (sv.protocolflags);
 		else
-			angle[i] = MSG_ReadAngle16 ();
+			angle[i] = MSG_ReadAngle16 (sv.protocolflags);
 		//johnfitz
 
 	VectorCopy (angle, host_client->edict->v.v_angle);
@@ -543,6 +540,8 @@ nextmsg:
 					ret = 1;
 				else if (q_strncasecmp(s, "noclip", 6) == 0)
 					ret = 1;
+				else if (q_strncasecmp(s, "setpos", 6) == 0)
+					ret = 1;
 				else if (q_strncasecmp(s, "say", 3) == 0)
 					ret = 1;
 				else if (q_strncasecmp(s, "say_team", 8) == 0)
diff --git a/Quake/sys_sdl_unix.c b/Quake/sys_sdl_unix.c
index b86858f..32f6d81 100644
--- a/Quake/sys_sdl_unix.c
+++ b/Quake/sys_sdl_unix.c
@@ -469,6 +469,7 @@ void Sys_Sleep (unsigned long msecs)
 
 void Sys_SendKeyEvents (void)
 {
+	IN_Commands();		//ericw -- allow joysticks to add keys so they can be used to confirm SCR_ModalMessage
 	IN_SendKeyEvents();
 }
 
diff --git a/Quake/sys_sdl_win.c b/Quake/sys_sdl_win.c
index 8921794..431f38d 100644
--- a/Quake/sys_sdl_win.c
+++ b/Quake/sys_sdl_win.c
@@ -436,6 +436,7 @@ void Sys_Sleep (unsigned long msecs)
 
 void Sys_SendKeyEvents (void)
 {
+	IN_Commands();		//ericw -- allow joysticks to add keys so they can be used to confirm SCR_ModalMessage
 	IN_SendKeyEvents();
 }
 
diff --git a/Quake/view.c b/Quake/view.c
index 924d492..6190042 100644
--- a/Quake/view.c
+++ b/Quake/view.c
@@ -270,7 +270,7 @@ void V_ParseDamage (void)
 	armor = MSG_ReadByte ();
 	blood = MSG_ReadByte ();
 	for (i=0 ; i<3 ; i++)
-		from[i] = MSG_ReadCoord ();
+		from[i] = MSG_ReadCoord (cl.protocolflags);
 
 	count = blood*0.5 + armor*0.5;
 	if (count < 10)
diff --git a/Quake/view.h b/Quake/view.h
index 48827df..af0cdf1 100644
--- a/Quake/view.h
+++ b/Quake/view.h
@@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define _QUAKE_VIEW_H
 
 extern	cvar_t		vid_gamma;
+extern	cvar_t		vid_contrast;
 
 extern float v_blend[4];
 
diff --git a/Quake/world.c b/Quake/world.c
index 6c0e1e5..552f455 100644
--- a/Quake/world.c
+++ b/Quake/world.c
@@ -495,7 +495,7 @@ int SV_HullPointContents (hull_t *hull, int num, vec3_t p)
 		if (plane->type < 3)
 			d = p[plane->type] - plane->dist;
 		else
-			d = DotProduct (plane->normal, p) - plane->dist;
+			d = DoublePrecisionDotProduct (plane->normal, p) - plane->dist;
 		if (d < 0)
 			num = node->children[1];
 		else
@@ -606,8 +606,8 @@ qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec
 	}
 	else
 	{
-		t1 = DotProduct (plane->normal, p1) - plane->dist;
-		t2 = DotProduct (plane->normal, p2) - plane->dist;
+		t1 = DoublePrecisionDotProduct (plane->normal, p1) - plane->dist;
+		t2 = DoublePrecisionDotProduct (plane->normal, p2) - plane->dist;
 	}
 
 #if 1
diff --git a/Quakespasm.html b/Quakespasm.html
index 5e3c172..0e6a55d 100644
--- a/Quakespasm.html
+++ b/Quakespasm.html
@@ -12,7 +12,7 @@
 <PRE>
 </PRE>
 </P>
-<P><EM>Page last edited: December 2015</EM></P>
+<P><EM>Page last edited: July 2016</EM></P>
 
 <P>
 <H2><A NAME="toc1">1.</A> <A HREF="Quakespasm.html#s1">About </A></H2>
@@ -25,6 +25,7 @@
 
 <UL>
 <LI><A NAME="toc3.1">3.1</A> <A HREF="Quakespasm.html#ss3.1">Music Playback</A>
+<LI><A NAME="toc3.2">3.2</A> <A HREF="Quakespasm.html#ss3.2">Controller Support</A>
 </UL>
 <P>
 <H2><A NAME="toc4">4.</A> <A HREF="Quakespasm.html#s4">Compiling and Installation</A></H2>
@@ -41,18 +42,19 @@
 <H2><A NAME="toc6">6.</A> <A HREF="Quakespasm.html#s6">Changes</A></H2>
 
 <UL>
-<LI><A NAME="toc6.1">6.1</A> <A HREF="Quakespasm.html#ss6.1">Changes in 0.91.1</A>
-<LI><A NAME="toc6.2">6.2</A> <A HREF="Quakespasm.html#ss6.2">Changes in 0.90.1</A>
-<LI><A NAME="toc6.3">6.3</A> <A HREF="Quakespasm.html#ss6.3">Changes in 0.90.0</A>
-<LI><A NAME="toc6.4">6.4</A> <A HREF="Quakespasm.html#ss6.4">Changes in 0.85.9</A>
-<LI><A NAME="toc6.5">6.5</A> <A HREF="Quakespasm.html#ss6.5">Changes in 0.85.8</A>
-<LI><A NAME="toc6.6">6.6</A> <A HREF="Quakespasm.html#ss6.6">Changes in 0.85.7</A>
-<LI><A NAME="toc6.7">6.7</A> <A HREF="Quakespasm.html#ss6.7">Changes in 0.85.6</A>
-<LI><A NAME="toc6.8">6.8</A> <A HREF="Quakespasm.html#ss6.8">Changes in 0.85.5</A>
-<LI><A NAME="toc6.9">6.9</A> <A HREF="Quakespasm.html#ss6.9">Changes in 0.85.4</A>
-<LI><A NAME="toc6.10">6.10</A> <A HREF="Quakespasm.html#ss6.10">Changes in 0.85.3</A>
-<LI><A NAME="toc6.11">6.11</A> <A HREF="Quakespasm.html#ss6.11">Changes in 0.85.2</A>
-<LI><A NAME="toc6.12">6.12</A> <A HREF="Quakespasm.html#ss6.12">Changes in 0.85.1</A>
+<LI><A NAME="toc6.1">6.1</A> <A HREF="Quakespasm.html#ss6.1">Changes in 0.92.0</A>
+<LI><A NAME="toc6.2">6.2</A> <A HREF="Quakespasm.html#ss6.2">Changes in 0.91.0</A>
+<LI><A NAME="toc6.3">6.3</A> <A HREF="Quakespasm.html#ss6.3">Changes in 0.90.1</A>
+<LI><A NAME="toc6.4">6.4</A> <A HREF="Quakespasm.html#ss6.4">Changes in 0.90.0</A>
+<LI><A NAME="toc6.5">6.5</A> <A HREF="Quakespasm.html#ss6.5">Changes in 0.85.9</A>
+<LI><A NAME="toc6.6">6.6</A> <A HREF="Quakespasm.html#ss6.6">Changes in 0.85.8</A>
+<LI><A NAME="toc6.7">6.7</A> <A HREF="Quakespasm.html#ss6.7">Changes in 0.85.7</A>
+<LI><A NAME="toc6.8">6.8</A> <A HREF="Quakespasm.html#ss6.8">Changes in 0.85.6</A>
+<LI><A NAME="toc6.9">6.9</A> <A HREF="Quakespasm.html#ss6.9">Changes in 0.85.5</A>
+<LI><A NAME="toc6.10">6.10</A> <A HREF="Quakespasm.html#ss6.10">Changes in 0.85.4</A>
+<LI><A NAME="toc6.11">6.11</A> <A HREF="Quakespasm.html#ss6.11">Changes in 0.85.3</A>
+<LI><A NAME="toc6.12">6.12</A> <A HREF="Quakespasm.html#ss6.12">Changes in 0.85.2</A>
+<LI><A NAME="toc6.13">6.13</A> <A HREF="Quakespasm.html#ss6.13">Changes in 0.85.1</A>
 </UL>
 <P>
 <H2><A NAME="toc7">7.</A> <A HREF="Quakespasm.html#s7">Todo </A></H2>
@@ -73,10 +75,12 @@
 
 <P>
 <A HREF="http://quakespasm.sourceforge.net">QuakeSpasm</A>
-is a Quake 1 engine based on the SDL port of
+is a modern, cross-platform Quake 1 engine based on 
 <A HREF="http://www.celephais.net/fitzquake">FitzQuake</A>.</P>
-<P>It includes support for 64 bit CPUs and custom music playback, and includes a new
+<P>It includes support for 64 bit CPUs and custom music playback, a new
 sound driver, some graphical niceities, and numerous bug-fixes and other improvements.</P>
+<P>Quakespasm utilizes either the SDL or SDL2 frameworks, so choose which one works best for you.
+SDL is probably less buggy, but SDL2 has nicer features and smoother mouse input - though no CD support.</P>
 
 <H2><A NAME="s2">2.</A> <A HREF="#toc2">Downloads </A></H2>
 
@@ -102,7 +106,7 @@ sound driver, some graphical niceities, and numerous bug-fixes and other improve
 </LI>
 <LI>Quakespasm's custom data is stored in "quakespasm.pak". Install this file alongside your id1 directory to enable the custom console background and other minor features.
 </LI>
-<LI>For different sound drivers use "<B>SDL_AUDIODRIVER=</B><EM>DRIVER</EM><B> ./quakespasm</B>"
+<LI>For different sound backend drivers use "<B>SDL_AUDIODRIVER=</B><EM>DRIVER</EM><B> ./quakespasm</B>"
 , where DRIVER may be alsa, dsp, pulse, esd ...
 </LI>
 <LI><B>Shift+Escape</B> draws the Console.</LI>
@@ -130,12 +134,64 @@ sound driver, some graphical niceities, and numerous bug-fixes and other improve
 </UL>
 </P>
 
+<H2><A NAME="ss3.2">3.2</A> <A HREF="#toc3.2">Controller Support</A>
+</H2>
+
+<P>The SDL2 variant of Quakespasm supports Xbox 360 style game controllers.</P>
+<P>The default configuration uses the left analog stick for movement and the right for looking.</P>
+<P>If your controller doesn't work you can try placing 
+<A HREF="https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt">this file</A> in your Quake directory, it is a community-maintained database that adds support for more controllers to SDL2.</P>
+
+<H3>Cvars</H3>
+
+<P>
+<UL>
+<LI>joy_deadzone - Fraction of the stick travel to be deadzone, between 0 and 1. Default 0.2.</LI>
+<LI>joy_deadzone_trigger - Fraction of trigger range required to register a button press on the analog triggers, between 0 and 1. Default 0.001.</LI>
+<LI>joy_sensitivity_yaw/pitch - Max angular speed in degrees/second when looking. Defaults are 300 for yaw (turning left/right) and 150 for pitch (up/down).</LI>
+<LI>joy_exponent - For the look stick, the stick displacement (between 0 and 1) is raised to this power. Default is 3. A value of 1 would give a linear relationship between stick displacement and fraction of the maximum angular speed.</LI>
+<LI>joy_invert - Set to 1 to invert the vertical axis of the look stick.</LI>
+<LI>joy_swapmovelook - Set to 1 to swap the left and right analog stick functions. Default 0, move on the left stick, look on the right stick.</LI>
+<LI>joy_enable - Set to 0 to disable controller support. Default 1.</LI>
+</UL>
+</P>
+
+<H3>Buttons</H3>
+
+<P>Some of the controller buttons are hardcoded to allow navigating the menu:</P>
+<P>
+<UL>
+<LI>Back - alias for TAB</LI>
+<LI>Start - alias for ESC</LI>
+<LI>DPad, analog sticks - mapped to arrow keys</LI>
+<LI>A Button - alias for ENTER in menus</LI>
+<LI>B Button - alias for ESC in menus</LI>
+</UL>
+</P>
+<P>These buttons can be bound normally:</P>
+<P>
+<UL>
+<LI>LTRIGGER - Left trigger</LI>
+<LI>RTRIGGER - Right trigger</LI>
+<LI>LSHOULDER - Left shoulder button</LI>
+<LI>RSHOULDER - Right shoulder button</LI>
+<LI>LTHUMB - Clicking the left thumbstick</LI>
+<LI>RTHUMB - Clicking the right thumbstick</LI>
+<LI>ABUTTON</LI>
+<LI>BBUTTON</LI>
+<LI>XBUTTON</LI>
+<LI>YBUTTON</LI>
+</UL>
+
+quakespasm.pak contains a default.cfg which has been updated to give some default bindings. L/R shoulder buttons are bound to weapon switching, and L/R triggers are jump and attack.</P>
+<P>The controller support started as Jeremiah Sypult's implementation in Quakespasm-Rift and also uses ideas and code from LordHavoc (DarkPlaces).</P>
 
 <H2><A NAME="s4">4.</A> <A HREF="#toc4">Compiling and Installation</A></H2>
 
 
 <P>Quakespasm's (optional) custom data is now stored in the file <B>quakespasm.pak</B>. This file should be placed alongside your quakespasm binary and <B>id1</B> directory.</P>
-<P><EM>To check-out the latest version of QuakeSpasm, use</EM> <B>svn co svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm</B></P>
+<P><EM>To checkout the latest version of QuakeSpasm, do:</EM>
+<B>svn co svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm</B></P>
 <H2><A NAME="ss4.1">4.1</A> <A HREF="#toc4.1">Linux/Unix </A>
 </H2>
 
@@ -178,9 +234,9 @@ Try setting "export SDL_VIDEO_X11_NODIRECTCOLOR=1", or if you have Xorg >= 7.5 a
 these patched libSDL binaries may help.
 <UL>
 <LI>
-<A HREF="http://sourceforge.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched.tgz/download">Gamma patched libSDL (i686-linux)</A></LI>
+<A HREF="http://sf.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched.tgz/download">Gamma patched libSDL (i686-linux)</A></LI>
 <LI>
-<A HREF="http://sourceforge.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched-AMD64.tgz/download">Gamma patched libSDL (x86_64-linux)</A></LI>
+<A HREF="http://sf.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched-AMD64.tgz/download">Gamma patched libSDL (x86_64-linux)</A></LI>
 </UL>
 </P>
 <P>The "game" command doesn't execute quake.rc in the new game directory being switched to. This means any custom key bindings in a mod's config.cfg or special settings in a quake.rc won't be loaded. The only workaround is launching the engine with the -game command-line switch instead of using the game console command. Or, after running the game command, you can run "exec quake.rc" by yourself (YMMV).</P>
@@ -188,7 +244,27 @@ these patched libSDL binaries may help.
 
 
 
-<H2><A NAME="ss6.1">6.1</A> <A HREF="#toc6.1">Changes in 0.91.1</A>
+<H2><A NAME="ss6.1">6.1</A> <A HREF="#toc6.1">Changes in 0.92.0</A>
+</H2>
+
+<P>
+<UL>
+<LI> SDL2 Game Controller support.</LI>
+<LI> Contrast support with new "contrast" cvar, behaving the same as MarkV. It may be a useful alternative to the existing gamma control for laptops in a bright environment, etc. Raising contrast gives less of a gray/washed out look than raising gamma, but at a disadvantage: colors near white get clipped to white.</LI>
+<LI> RMQ protocol (999) support, adapted from RMQEngine.</LI>
+<LI> New "-protocol x" command line option. Accepted values for 'x' are 15 (NetQuake), 666 (FitzQuake, default), and 999 (RMQ).</LI>
+<LI> New "setpos" console command.</LI>
+<LI> New "vid_borderless" cvar for getting a borderless window.</LI>
+<LI> Increased MAX_MAP_LEAFS from 65535 to 70000 and MAX_LIGHTMAPS from 256 to 512 in order to handle the oms3 map pack.</LI>
+<LI> Server edicts are now allocated using malloc instead of allocating on the hunk.</LI>
+<LI> gl_clear now defaults to 1.</LI>
+<LI> Fix items falling out of the world on oms3.bsp on SSE builds.</LI>
+<LI> Worked around an OSX 10.6 driver bug when using FSAA, which was leading to an unplayable HOM effect on the rest of the screen.</LI>
+<LI> Fix wrong trace endpoint from the tracepos console command.</LI>
+<LI> Updated some of the third-party libraries. Other fixes/clean-ups.</LI>
+</UL>
+</P>
+<H2><A NAME="ss6.2">6.2</A> <A HREF="#toc6.2">Changes in 0.91.0</A>
 </H2>
 
 
@@ -248,7 +324,7 @@ these patched libSDL binaries may help.
 <LI> Raised MAX_SFX to 1024 (was 512).</LI>
 </UL>
 </P>
-<H2><A NAME="ss6.2">6.2</A> <A HREF="#toc6.2">Changes in 0.90.1</A>
+<H2><A NAME="ss6.3">6.3</A> <A HREF="#toc6.3">Changes in 0.90.1</A>
 </H2>
 
 
@@ -308,7 +384,7 @@ these patched libSDL binaries may help.
 <LI> Update 3rd-party libraries.</LI>
 </UL>
 </P>
-<H2><A NAME="ss6.3">6.3</A> <A HREF="#toc6.3">Changes in 0.90.0</A>
+<H2><A NAME="ss6.4">6.4</A> <A HREF="#toc6.4">Changes in 0.90.0</A>
 </H2>
 
 <P>
@@ -353,7 +429,7 @@ these patched libSDL binaries may help.
 <LI> Other fixes and clean-ups.</LI>
 </UL>
 </P>
-<H2><A NAME="ss6.4">6.4</A> <A HREF="#toc6.4">Changes in 0.85.9</A>
+<H2><A NAME="ss6.5">6.5</A> <A HREF="#toc6.5">Changes in 0.85.9</A>
 </H2>
 
 <P>
@@ -377,7 +453,7 @@ these patched libSDL binaries may help.
 <LI> Several other minor fixes/cleanups.</LI>
 </UL>
 </P>
-<H2><A NAME="ss6.5">6.5</A> <A HREF="#toc6.5">Changes in 0.85.8</A>
+<H2><A NAME="ss6.6">6.6</A> <A HREF="#toc6.6">Changes in 0.85.8</A>
 </H2>
 
 <P>
@@ -402,7 +478,7 @@ these patched libSDL binaries may help.
 <LI> Miscellaneous source code cleanups.</LI>
 </UL>
 </P>
-<H2><A NAME="ss6.6">6.6</A> <A HREF="#toc6.6">Changes in 0.85.7</A>
+<H2><A NAME="ss6.7">6.7</A> <A HREF="#toc6.7">Changes in 0.85.7</A>
 </H2>
 
 <P>
@@ -420,7 +496,7 @@ these patched libSDL binaries may help.
 <LI> Several other small changes mostly invisible to the end-user</LI>
 </UL>
 </P>
-<H2><A NAME="ss6.7">6.7</A> <A HREF="#toc6.7">Changes in 0.85.6</A>
+<H2><A NAME="ss6.8">6.8</A> <A HREF="#toc6.8">Changes in 0.85.6</A>
 </H2>
 
 <P>
@@ -431,7 +507,7 @@ these patched libSDL binaries may help.
 <LI> Minor SDL video fixes.</LI>
 </UL>
 </P>
-<H2><A NAME="ss6.8">6.8</A> <A HREF="#toc6.8">Changes in 0.85.5</A>
+<H2><A NAME="ss6.9">6.9</A> <A HREF="#toc6.9">Changes in 0.85.5</A>
 </H2>
 
 <P>
@@ -450,7 +526,7 @@ these patched libSDL binaries may help.
 <LI> Several code updates from uHexen2 project, several code cleanups.</LI>
 </UL>
 </P>
-<H2><A NAME="ss6.9">6.9</A> <A HREF="#toc6.9">Changes in 0.85.4</A>
+<H2><A NAME="ss6.10">6.10</A> <A HREF="#toc6.10">Changes in 0.85.4</A>
 </H2>
 
 <P>
@@ -468,7 +544,7 @@ these patched libSDL binaries may help.
 <LI> Other minor sound and cdaudio updates</LI>
 </UL>
 </P>
-<H2><A NAME="ss6.10">6.10</A> <A HREF="#toc6.10">Changes in 0.85.3</A>
+<H2><A NAME="ss6.11">6.11</A> <A HREF="#toc6.11">Changes in 0.85.3</A>
 </H2>
 
 <P>
@@ -491,7 +567,7 @@ these patched libSDL binaries may help.
 </UL>
 </P>
 
-<H2><A NAME="ss6.11">6.11</A> <A HREF="#toc6.11">Changes in 0.85.2</A>
+<H2><A NAME="ss6.12">6.12</A> <A HREF="#toc6.12">Changes in 0.85.2</A>
 </H2>
 
 <P>
@@ -510,7 +586,7 @@ these patched libSDL binaries may help.
 </UL>
 </P>
 
-<H2><A NAME="ss6.12">6.12</A> <A HREF="#toc6.12">Changes in 0.85.1</A>
+<H2><A NAME="ss6.13">6.13</A> <A HREF="#toc6.13">Changes in 0.85.1</A>
 </H2>
 
 <P>
@@ -564,7 +640,7 @@ these patched libSDL binaries may help.
 <LI>
 <A HREF="http://sourceforge.net/p/quakespasm/bugs/?source=navbar">Bug reports</A></LI>
 <LI>
-<A HREF="mailto:gmail - dot - com - username - sezeroz">Ozkan</A> (project leader),  
+<A HREF="mailto:gmail - dot - com - username - sezeroz">Ozkan</A>,  
 <A HREF="mailto:gmail - dot - com - username - ewasylishen">Eric</A>,  
 <A HREF="mailto:gmail - dot - com - username - a.h.vandijk">Sander</A>,  
 <A HREF="mailto:yahoo - dot - com - username - stevenaaus">Stevenaaus</A></LI>
diff --git a/Quakespasm.txt b/Quakespasm.txt
index 4757e4e..d176698 100644
--- a/Quakespasm.txt
+++ b/Quakespasm.txt
@@ -8,6 +8,9 @@
   2. Downloads
   3. Hints
      3.1 Music Playback
+     3.2 Controller Support
+        3.2.1 Cvars
+        3.2.2 Buttons
 
   4. Compiling and Installation
      4.1 Linux/Unix
@@ -16,28 +19,29 @@
 
   5. Known Bugs
   6. Changes
-     6.1 Changes in 0.91.1
-        6.1.1 Bugfixes
-        6.1.2 Visual improvements
-        6.1.3 Interface improvements
-        6.1.4 Code cleanup / Other
-        6.1.5 Raised limits
-     6.2 Changes in 0.90.1
+     6.1 Changes in 0.92.0
+     6.2 Changes in 0.91.0
         6.2.1 Bugfixes
-        6.2.2 Performance
-        6.2.3 Visual improvements
-        6.2.4 Interface improvements
-        6.2.5 Code cleanup
-     6.3 Changes in 0.90.0
-     6.4 Changes in 0.85.9
-     6.5 Changes in 0.85.8
-     6.6 Changes in 0.85.7
-     6.7 Changes in 0.85.6
-     6.8 Changes in 0.85.5
-     6.9 Changes in 0.85.4
-     6.10 Changes in 0.85.3
-     6.11 Changes in 0.85.2
-     6.12 Changes in 0.85.1
+        6.2.2 Visual improvements
+        6.2.3 Interface improvements
+        6.2.4 Code cleanup / Other
+        6.2.5 Raised limits
+     6.3 Changes in 0.90.1
+        6.3.1 Bugfixes
+        6.3.2 Performance
+        6.3.3 Visual improvements
+        6.3.4 Interface improvements
+        6.3.5 Code cleanup
+     6.4 Changes in 0.90.0
+     6.5 Changes in 0.85.9
+     6.6 Changes in 0.85.8
+     6.7 Changes in 0.85.7
+     6.8 Changes in 0.85.6
+     6.9 Changes in 0.85.5
+     6.10 Changes in 0.85.4
+     6.11 Changes in 0.85.3
+     6.12 Changes in 0.85.2
+     6.13 Changes in 0.85.1
 
   7. Todo
   8. Copyright
@@ -47,18 +51,22 @@
   ______________________________________________________________________
 
 
-  Page last edited: December 2015
+  Page last edited: July 2016
 
 
   1.  About
 
-  QuakeSpasm <http://quakespasm.sourceforge.net> is a Quake 1 engine
-  based on the SDL port of FitzQuake
+  QuakeSpasm <http://quakespasm.sourceforge.net> is a modern, cross-
+  platform Quake 1 engine based on FitzQuake
   <http://www.celephais.net/fitzquake>.
 
-  It includes support for 64 bit CPUs and custom music playback, and
-  includes a new sound driver, some graphical niceities, and numerous
-  bug-fixes and other improvements.
+  It includes support for 64 bit CPUs and custom music playback, a new
+  sound driver, some graphical niceities, and numerous bug-fixes and
+  other improvements.
+
+  Quakespasm utilizes either the SDL or SDL2 frameworks, so choose
+  which one works best for you.  SDL is probably less buggy, but SDL2
+  has nicer features and smoother mouse input - though no CD support.
 
 
   2.  Downloads
@@ -79,8 +87,9 @@
      this file alongside your id1 directory to enable the custom console
      background and other minor features.
 
-  o  For different sound drivers use "SDL_AUDIODRIVER=DRIVER
-     ./quakespasm" , where DRIVER may be alsa, dsp, pulse, esd ...
+  o  For different sound backend drivers use :
+     "SDL_AUDIODRIVER=DRIVER ./quakespasm"
+     where DRIVER may be alsa, dsp, pulse, esd ...
 
   o  Shift+Escape draws the Console.
 
@@ -118,6 +127,91 @@
 
   o  See Quakespasm-Music.txt for more details.
 
+  3.2.  Controller Support
+
+  The SDL2 variant of Quakespasm supports Xbox 360 style game
+  controllers.
+
+  The default configuration uses the left analog stick for movement and
+  the right for looking.
+
+  If your controller doesn't work you can try placing this file
+  https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt
+  in your Quake directory, it is a community-maintained database that
+  adds support for more controllers to SDL2.
+
+  3.2.1.  Cvars
+
+  o  joy_deadzone - Fraction of the stick travel to be deadzone, between
+     0 and 1. Default 0.2.
+
+  o  joy_deadzone_trigger - Fraction of trigger range required to
+     register a button press on the analog triggers, between 0 and 1.
+     Default 0.001.
+
+  o  joy_sensitivity_yaw/pitch - Max angular speed in degrees/second
+     when looking. Defaults are 300 for yaw (turning left/right) and 150
+     for pitch (up/down).
+
+  o  joy_exponent - For the look stick, the stick displacement (between
+     0 and 1) is raised to this power. Default is 3. A value of 1 would
+     give a linear relationship between stick displacement and fraction
+     of the maximum angular speed.
+
+  o  joy_invert - Set to 1 to invert the vertical axis of the look
+     stick.
+
+  o  joy_swapmovelook - Set to 1 to swap the left and right analog stick
+     functions. Default 0, move on the left stick, look on the right
+     stick.
+
+  o  joy_enable - Set to 0 to disable controller support. Default 1.
+
+  3.2.2.  Buttons
+
+  Some of the controller buttons are hardcoded to allow navigating the
+  menu:
+
+  o  Back - alias for TAB
+
+  o  Start - alias for ESC
+
+  o  DPad, analog sticks - mapped to arrow keys
+
+  o  A Button - alias for ENTER in menus
+
+  o  B Button - alias for ESC in menus
+
+  These buttons can be bound normally:
+
+  o  LTRIGGER - Left trigger
+
+  o  RTRIGGER - Right trigger
+
+  o  LSHOULDER - Left shoulder button
+
+  o  RSHOULDER - Right shoulder button
+
+  o  LTHUMB - Clicking the left thumbstick
+
+  o  RTHUMB - Clicking the right thumbstick
+
+  o  ABUTTON
+
+  o  BBUTTON
+
+  o  XBUTTON
+
+  o  YBUTTON
+
+  quakespasm.pak contains a default.cfg which has been updated to give
+  some default bindings. L/R shoulder buttons are bound to weapon
+  switching, and L/R triggers are jump and attack.
+
+  The controller support started as Jeremiah Sypult's implementation in
+  Quakespasm-Rift and also uses ideas and code from LordHavoc
+  (DarkPlaces).
+
 
   4.  Compiling and Installation
 
@@ -125,7 +219,7 @@
   quakespasm.pak. This file should be placed alongside your quakespasm
   binary and id1 directory.
 
-  To check-out the latest version of QuakeSpasm, use
+  To checkout the latest version of QuakeSpasm, do:
   svn co svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm
 
   4.1.  Linux/Unix
@@ -182,10 +276,10 @@
   help.
 
   o  Gamma patched libSDL (i686-linux)
-     http://sourceforge.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched.tgz/download
+     http://sf.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched.tgz/download
 
   o  Gamma patched libSDL (x86_64-linux)
-     http://sourceforge.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched-AMD64.tgz/download
+     http://sf.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched-AMD64.tgz/download
 
   The "game" command doesn't execute quake.rc in the new game directory
   being switched to. This means any custom key bindings in a mod's
@@ -198,9 +292,46 @@
   6.  Changes
 
 
-  6.1.  Changes in 0.91.1
+  6.1.  Changes in 0.92.0
+
+  o  SDL2 Game Controller support.
+
+  o  Contrast support with new "contrast" cvar, behaving the same as
+     MarkV. It may be a useful alternative to the existing gamma control
+     for laptops in a bright environment, etc. Raising contrast gives
+     less of a gray/washed out look than raising gamma, but at a
+     disadvantage: colors near white get clipped to white.
+
+  o  RMQ protocol (999) support, adapted from RMQEngine.
+
+  o  New "-protocol x" command line option. Accepted values for 'x' are
+     15 (NetQuake), 666 (FitzQuake, default), and 999 (RMQ).
+
+  o  New "setpos" console command.
 
-  6.1.1.  Bugfixes
+  o  New "vid_borderless" cvar for getting a borderless window.
+
+  o  Increased MAX_MAP_LEAFS from 65535 to 70000 and MAX_LIGHTMAPS from
+     256 to 512 in order to handle the oms3 map pack.
+
+  o  Server edicts are now allocated using malloc instead of allocating
+     on the hunk.
+
+  o  gl_clear now defaults to 1.
+
+  o  Fix items falling out of the world on oms3.bsp on SSE builds.
+
+  o  Worked around an OSX 10.6 driver bug when using FSAA, which was
+     leading to an unplayable HOM effect on the rest of the screen.
+
+  o  Fix wrong trace endpoint from the tracepos console command.
+
+  o  Updated some of the third-party libraries. Other fixes/clean-ups.
+
+
+  6.2.  Changes in 0.91.0
+
+  6.2.1.  Bugfixes
 
   o  Fix unwanted fog mode change upon video restart.
 
@@ -236,7 +367,7 @@
 
   o  Prevent a possible vulnerability in MSG_ReadString (old Q1/Q2 bug).
 
-  6.1.2.  Visual improvements
+  6.2.2.  Visual improvements
 
   o  New cvars r_lavaalpha, r_slimealpha, r_telealpha for fine-tuning
      specific liquid opacities (from DirectQ/RMQEngine, non-archived,
@@ -247,18 +378,18 @@
   o  GLSL gamma is now supported on older hardware without NPOT
      extension.
 
-  6.1.3.  Interface improvements
+  6.2.3.  Interface improvements
 
   o  New r_pos command to show player position.
 
   o  NaN detection in traceline with "developer 1" set now warns instead
      of errors.
 
-  6.1.4.  Code cleanup / Other
+  6.2.4.  Code cleanup / Other
 
   o  Update third-party libraries.
 
-  6.1.5.  Raised limits
+  6.2.5.  Raised limits
 
   o  Default max_edicts 8192 (was 2048) and no longer saved to
      config.cfg.
@@ -270,9 +401,9 @@
   o  Raised MAX_SFX to 1024 (was 512).
 
 
-  6.2.  Changes in 0.90.1
+  6.3.  Changes in 0.90.1
 
-  6.2.1.  Bugfixes
+  6.3.1.  Bugfixes
 
   o  Fix dynamic light artifact where changing lightmap are rendered one
      frame late (bug introduced in 0.90.0).
@@ -295,13 +426,13 @@
 
   o  Fix crash on out-of-bounds skin number.
 
-  6.2.2.  Performance
+  6.3.2.  Performance
 
   o  Use multithreaded OpenGL on OS X for better performance.
 
   o  New, faster mdl renderer using GLSL. Disable with "-noglslalias".
 
-  6.2.3.  Visual improvements
+  6.3.3.  Visual improvements
 
   o  New gamma correction implementation using GLSL. Fixes all known
      gamma issues (affecting the full display, persisting after
@@ -315,7 +446,7 @@
 
   o  r_noshadow_list cvar added (from MarkV.)
 
-  6.2.4.  Interface improvements
+  6.3.4.  Interface improvements
 
   o  Support pausing demo playback with the "pause" command.
 
@@ -332,14 +463,14 @@
      "trying to load ent", "bad chunk length", "meshing",
      "PR_AlocStringSlots: realloc'ing"
 
-  6.2.5.  Code cleanup
+  6.3.5.  Code cleanup
 
   o  Clean up IDE project files to build on fresh systems.
 
   o  Update 3rd-party libraries.
 
 
-  6.3.  Changes in 0.90.0
+  6.4.  Changes in 0.90.0
 
   o  Fix issues on Windows systems with DPI scaling.
 
@@ -447,7 +578,7 @@
   o  Other fixes and clean-ups.
 
 
-  6.4.  Changes in 0.85.9
+  6.5.  Changes in 0.85.9
 
   o  Fixes for several undefined behaviors in C code (gcc-4.8 support.)
 
@@ -494,7 +625,7 @@
   o  Several other minor fixes/cleanups.
 
 
-  6.5.  Changes in 0.85.8
+  6.6.  Changes in 0.85.8
 
   o  Made Quake shareware 1.00 and 1.01 versions to be recognized
      properly.
@@ -541,7 +672,7 @@
   o  Miscellaneous source code cleanups.
 
 
-  6.6.  Changes in 0.85.7
+  6.7.  Changes in 0.85.7
 
   o  Added support for cross-level demo playback
 
@@ -567,7 +698,7 @@
   o  Several other small changes mostly invisible to the end-user
 
 
-  6.7.  Changes in 0.85.6
+  6.8.  Changes in 0.85.6
 
   o  More work for string buffer safety
 
@@ -580,7 +711,7 @@
   o  Minor SDL video fixes.
 
 
-  6.8.  Changes in 0.85.5
+  6.9.  Changes in 0.85.5
 
   o  SDL input driver updated adding native keymap and dead key support
      to the console
@@ -611,7 +742,7 @@
   o  Several code updates from uHexen2 project, several code cleanups.
 
 
-  6.9.  Changes in 0.85.4
+  6.10.  Changes in 0.85.4
 
   o  Implement music (OGG, MP3, WAV) playback
 
@@ -639,7 +770,7 @@
   o  Other minor sound and cdaudio updates
 
 
-  6.10.  Changes in 0.85.3
+  6.11.  Changes in 0.85.3
 
   o  Fix the "-dedicated" option (thanks Oz) and add platform specific
      networking code (default) rather than SDL_net
@@ -676,7 +807,7 @@
      some other CD tweaks.
 
 
-  6.11.  Changes in 0.85.2
+  6.12.  Changes in 0.85.2
 
   o  Replace the old "Screen size" slider with a "Scale" slider
 
@@ -704,7 +835,7 @@
   o  Add OSX Makefile (tested?)
 
 
-  6.12.  Changes in 0.85.1
+  6.13.  Changes in 0.85.1
 
   o  64 bit CPU support
 
@@ -768,9 +899,9 @@
   o  Bug reports:
      http://sourceforge.net/p/quakespasm/bugs/?source=navbar
 
-  o  Ozkan <mailto:gmail - dot - com - username - sezeroz> (project
-     leader), Eric <mailto:gmail - dot - com - username - ewasylishen>,
-     Sander <mailto:gmail - dot - com - username - a.h.vandijk>,
+  o  Ozkan <mailto:gmail - dot - com - username - sezeroz>
+     Eric <mailto:gmail - dot - com - username - ewasylishen>
+     Sander <mailto:gmail - dot - com - username - a.h.vandijk>
      Stevenaaus <mailto:yahoo - dot - com - username - stevenaaus>
 
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/quakespasm.git



More information about the Pkg-games-commits mailing list