From 26e527a8a671161f468d8efc6d7fa80d3832e636 Mon Sep 17 00:00:00 2001
From: Spoike <acceptthis@users.sourceforge.net>
Date: Sat, 3 Aug 2019 01:58:03 +0000
Subject: [PATCH] Fix compile issues when HAVE_PACKET is disabled, also
 removing references to resulting unusable hostnames. Fix recent sizeof(void)
 error. Fix crashes from 0-byte lit files (and a few other related bugs that
 noone else noticed yet). r_loadlit 3 now generates e5bgr9 .lit format (for
 over-over-bright). Also supports world.light for minlight values, now also
 uses super-sampling (slower but nicer). Additionally disable PEXT_TRANS in
 the FortressOne fork of ezQuake (sidestepping its inherited bugs). Fix q3's
 sprites getting horizontally flipped.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5501 fc73d0e0-1445-4013-8a0c-d673dee63da5
---
 engine/client/cl_ui.c        |  2 ++
 engine/client/clq3_parse.c   |  2 ++
 engine/client/image.c        |  9 ++++-
 engine/client/m_download.c   |  2 +-
 engine/client/net_master.c   |  6 ++++
 engine/client/render.h       |  2 +-
 engine/client/screen.h       |  2 +-
 engine/common/com_mesh.c     |  6 ++++
 engine/common/config_fteqw.h |  2 +-
 engine/common/net_wins.c     | 11 ++++--
 engine/common/netinc.h       | 14 ++++----
 engine/gl/gl_alias.c         |  1 +
 engine/gl/gl_heightmap.c     |  4 +--
 engine/gl/gl_model.c         | 68 ++++++++++++++++++++++++++----------
 engine/gl/ltface.c           | 58 +++++++++++++++++++++++++-----
 engine/server/sv_user.c      |  6 ++--
 16 files changed, 148 insertions(+), 47 deletions(-)

diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c
index 496004e35..d179305a6 100644
--- a/engine/client/cl_ui.c
+++ b/engine/client/cl_ui.c
@@ -1665,7 +1665,9 @@ void UI_Stop (void)
 		//note that q3 checks every frame. we only check when the ui is closed.
 		if (Cvar_UnsavedArchive())
 			Cmd_ExecuteString("cfg_save", RESTRICT_LOCAL);
+#if defined(CL_MASTER)
 		MasterInfo_WriteServers();
+#endif
 	}
 }
 
diff --git a/engine/client/clq3_parse.c b/engine/client/clq3_parse.c
index 2d7ff59d0..1630afd2a 100644
--- a/engine/client/clq3_parse.c
+++ b/engine/client/clq3_parse.c
@@ -1009,6 +1009,7 @@ void CLQ3_SendCmd(usercmd_t *cmd)
 
 void CLQ3_SendAuthPacket(netadr_t *gameserver)
 {
+#ifdef HAVE_PACKET
 	char data[2048];
 	sizebuf_t msg;
 
@@ -1045,6 +1046,7 @@ void CLQ3_SendAuthPacket(netadr_t *gameserver)
 				Con_Printf("    failed\n");
 		}
 	}
+#endif
 }
 
 void CLQ3_SendConnectPacket(netadr_t *to, int challenge, int qport)
diff --git a/engine/client/image.c b/engine/client/image.c
index 75f04e284..7ee4f0afd 100644
--- a/engine/client/image.c
+++ b/engine/client/image.c
@@ -7937,16 +7937,23 @@ static void Image_DecompressFormat(struct pendingtextureinfo *mips)
 	{
 		for (mip = 0; mip < mips->mipcount; mip++)
 		{
+			size_t sz;
 			void *out;
 			if (decodefunc64)
+			{
+				sz = sizeof(pixel64_t);
 				out = Image_Block_Decode64(mips->mip[mip].data, mips->mip[mip].datasize, mips->mip[mip].width, mips->mip[mip].height, decodefunc64, mips->encoding);
+			}
 			else
+			{
+				sz = sizeof(pixel32_t);
 				out = Image_Block_Decode(mips->mip[mip].data, mips->mip[mip].datasize, mips->mip[mip].width, mips->mip[mip].height, decodefunc, mips->encoding);
+			}
 			if (mips->mip[mip].needfree)
 				BZ_Free(mips->mip[mip].data);
 			mips->mip[mip].data = out;
 			mips->mip[mip].needfree = true;
-			mips->mip[mip].datasize = mips->mip[mip].width*mips->mip[mip].height*sizeof(*out);
+			mips->mip[mip].datasize = mips->mip[mip].width*mips->mip[mip].height*sz;
 		}
 		if (mips->extrafree)
 			BZ_Free(mips->extrafree);	//might as well free this now, as nothing is poking it any more.
diff --git a/engine/client/m_download.c b/engine/client/m_download.c
index 22986e676..49de84c01 100644
--- a/engine/client/m_download.c
+++ b/engine/client/m_download.c
@@ -695,7 +695,7 @@ static qboolean PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *ur
 				else
 					subprefix = com_token;
 
-#ifdef HAVE_LEGACY
+#if defined(HAVE_LEGACY) && defined(WEBCLIENT)
 				//hack. I'm trying to retire the self-signed cert on [fte.]triptohell.info
 				if (!strcmp(url, "https://triptohell.info/downloadables.php") || !strcmp(url, "https://fte.triptohell.info/downloadables.php"))
 				{
diff --git a/engine/client/net_master.c b/engine/client/net_master.c
index 8bf586b45..3130f855e 100644
--- a/engine/client/net_master.c
+++ b/engine/client/net_master.c
@@ -138,6 +138,7 @@ static net_masterlist_t net_masterlist[] = {
 	{MP_QUAKE3,		CVARC("net_q3master4", "", Net_Masterlist_Callback)},
 #endif
 
+#ifdef HAVE_PACKET
 #ifndef QUAKETC
 	//engine-specified/maintained master lists (so users can be lazy and update the engine without having to rewrite all their configs).
 	{MP_QUAKEWORLD, CVARFC("net_qwmasterextra1", "qwmaster.ocrana.de:27000",						CVAR_NOSAVE, Net_Masterlist_Callback),	"Ocrana(2nd)"},	//german. admin unknown
@@ -193,6 +194,7 @@ static net_masterlist_t net_masterlist[] = {
 	{MP_QUAKE3,		CVARFC("net_q3masterextra5",	"master.maverickservers.com:27950",				CVAR_NOSAVE, Net_Masterlist_Callback),	"US: Maverickservers.com"},
 	{MP_QUAKE3,		CVARFC("net_q3masterextra6",	"dpmaster.deathmask.net:27950",					CVAR_NOSAVE, Net_Masterlist_Callback),	"US: DeathMask.net"},
 	{MP_QUAKE3,		CVARFC("net_q3masterextra8",	"master3.idsoftware.com:27950",					CVAR_NOSAVE, Net_Masterlist_Callback),	"US: id Software Quake III Master"},
+#endif
 #endif
 
 	{MP_UNSPECIFIED, CVAR(NULL, NULL)}
@@ -3609,14 +3611,17 @@ void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_
 #ifdef Q3CLIENT
 static void NetQ3_LocalServers_f(void)
 {
+#if defined(CL_MASTER)
 	netadr_t na;
 	MasterInfo_Refresh(true);
 
 	if (NET_StringToAdr("255.255.255.255", PORT_Q3SERVER, &na))
 		NET_SendPollPacket (14, va("%c%c%c%cgetstatus\n", 255, 255, 255, 255), na);
+#endif
 }
 static void NetQ3_GlobalServers_f(void)
 {
+#if defined(CL_MASTER)
 	size_t masternum = atoi(Cmd_Argv(1));
 	int protocol = atoi(Cmd_Argv(2));
 	char *keywords;
@@ -3645,6 +3650,7 @@ static void NetQ3_GlobalServers_f(void)
 		for (i = 0; i < n; i++)
 			NET_SendPollPacket (strlen(str), str, adr[i]);
 	}
+#endif
 }
 #endif
 void Net_Master_Init(void)
diff --git a/engine/client/render.h b/engine/client/render.h
index 9b361c630..d1c415de2 100644
--- a/engine/client/render.h
+++ b/engine/client/render.h
@@ -554,7 +554,7 @@ void Mod_ModelLoaded(void *ctx, void *data, size_t a, size_t b);
 struct relight_ctx_s;
 struct llightinfo_s;
 void LightFace (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, int surfnum);	//version that is aware of bsp trees
-void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, qbyte surf_styles[4], qbyte *surf_rgbsamples, qbyte *surf_deluxesamples, vec4_t surf_plane, vec4_t surf_texplanes[2], vec2_t exactmins, vec2_t exactmaxs, int texmins[2], int texsize[2], float lmscale);	//special version that doesn't know what a face is or anything.
+void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, qbyte surf_styles[4], unsigned int *surf_expsamples, qbyte *surf_rgbsamples, qbyte *surf_deluxesamples, vec4_t surf_plane, vec4_t surf_texplanes[2], vec2_t exactmins, vec2_t exactmaxs, int texmins[2], int texsize[2], float lmscale);	//special version that doesn't know what a face is or anything.
 struct relight_ctx_s *LightStartup(struct relight_ctx_s *ctx, struct model_s *model, qboolean shadows, qboolean skiplit);
 void LightReloadEntities(struct relight_ctx_s *ctx, const char *entstring, qboolean ignorestyles);
 void LightShutdown(struct relight_ctx_s *ctx, struct model_s *mod);
diff --git a/engine/client/screen.h b/engine/client/screen.h
index 770bc57e9..4d25fc33b 100644
--- a/engine/client/screen.h
+++ b/engine/client/screen.h
@@ -150,7 +150,7 @@ typedef enum uploadfmt
 	PTI_ETC2_RGB8_SRGB,
 	PTI_ETC2_RGB8A1_SRGB,
 	PTI_ETC2_RGB8A8_SRGB,
-	PTI_EAC_R11,	//no idea what this might be used for, whatever
+	PTI_EAC_R11,	//might be useful for overlays, with swizzles.
 	PTI_EAC_R11_SNORM,	//no idea what this might be used for, whatever
 	PTI_EAC_RG11,	//useful for normalmaps (calculate blue)
 	PTI_EAC_RG11_SNORM,	//useful for normalmaps (calculate blue)
diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c
index 56a08a2ba..8a7276ab2 100644
--- a/engine/common/com_mesh.c
+++ b/engine/common/com_mesh.c
@@ -5275,6 +5275,12 @@ static galiasinfo_t *Mod_LoadQ3ModelLod(model_t *mod, int *surfcount, void *buff
 			indexes[i*3+0] = LittleLong(intris[i].indexes[0]);
 			indexes[i*3+1] = LittleLong(intris[i].indexes[1]);
 			indexes[i*3+2] = LittleLong(intris[i].indexes[2]);
+
+			if (indexes[i*3+0] >= numverts || indexes[i*3+1] >= numverts || indexes[i*3+2] >= numverts)
+			{
+				Con_Printf(CON_WARNING "Warning: surface %s has invalid vertex indexes\n", galias->surfacename);
+				indexes[i*3+0] = indexes[i*3+1] = indexes[i*3+2] = 0;
+			}
 		}
 
 		//figure out where we're putting the pose data
diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h
index c80ba3d83..66d9475c2 100644
--- a/engine/common/config_fteqw.h
+++ b/engine/common/config_fteqw.h
@@ -157,7 +157,7 @@
 
 // Other Audio Options
 #define VOICECHAT
-#define HAVE_SPEEX            //Support the speex codec.
+//#define HAVE_SPEEX				//Support the speex codec.
 #define HAVE_OPUS               //Support the opus codec.
 #define HAVE_MEDIA_DECODER		//can play cin/roq, more with plugins
 #define HAVE_MEDIA_ENCODER		//capture/capturedemo work.
diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c
index 71f58352f..d1ee2e12a 100644
--- a/engine/common/net_wins.c
+++ b/engine/common/net_wins.c
@@ -18,8 +18,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 */
 // net_wins.c
-struct sockaddr;
-
 #include "quakedef.h"
 #include "netinc.h"
 #include <stddef.h>
@@ -266,6 +264,10 @@ int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s)
 
 void SockadrToNetadr (struct sockaddr_qstorage *s, int sizeofsockaddr, netadr_t *a)
 {
+#ifndef HAVE_PACKET
+	memset(a, 0, sizeof(*a));
+	a->type = NA_INVALID;
+#else
 	a->scopeid = 0;
 	a->connum = 0;
 	a->prot = NP_DGRAM;
@@ -336,6 +338,7 @@ void SockadrToNetadr (struct sockaddr_qstorage *s, int sizeofsockaddr, netadr_t
 		a->type = NA_INVALID;
 		break;
 	}
+#endif
 }
 char	*NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a, size_t sizeofa)
 {
@@ -588,7 +591,7 @@ qboolean NET_AddressSmellsFunny(netadr_t *a)
 	}
 }
 
-#if _POSIX_C_SOURCE >= 200112L || defined(getnameinfo)
+#if (_POSIX_C_SOURCE >= 200112L || defined(getnameinfo)) && defined(HAVE_PACKET)
 static void NET_AdrToStringDoResolve(void *ctx, void *data, size_t a, size_t b)
 {
 	netadr_t *n = data;
@@ -1042,6 +1045,7 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhin
 	if (!(*s) || !addresses)
 		return result;
 
+#ifdef WEBCLIENT
 	//EVIL HACK!
 	//updates.tth uses a known self-signed certificate (to protect against dns hijacks like fteqw.com suffered).
 	//its not meant to be used for browsers etc, and I cba to register dns stuff for it.
@@ -1049,6 +1053,7 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhin
 	//redirect the dns to the base host without affecting http(s) hosts/certificates.
 	if (!strcmp(s, "updates.triptohell.info"))
 		s += 8;
+#endif
 
 	memset (sadr, 0, sizeof(*sadr));
 
diff --git a/engine/common/netinc.h b/engine/common/netinc.h
index 66c152ce5..b47da8367 100644
--- a/engine/common/netinc.h
+++ b/engine/common/netinc.h
@@ -5,10 +5,10 @@
 #ifndef HAVE_PACKET
 
 #ifndef _XBOX
-	struct sockaddr
-	{
-		short  sa_family;
-	};
+//	struct sockaddr
+//	{
+//		short  sa_family;
+//	};
 
 	#define ntohs BigShort
 	#define htons BigShort
@@ -381,8 +381,8 @@ vfsfile_t *FS_OpenTCPSocket(SOCKET socket, qboolean conpending, const char *peer
 #endif
 vfsfile_t *FS_OpenTCP(const char *name, int defaultport);
 
-#ifndef SOCK_CLOEXEC
-#define SOCK_CLOEXEC 0
-#endif
+/*#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC naught
+#endif*/
 
 #endif //NETINC_INCLUDED
diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c
index 8417c74e5..44cc14e3c 100644
--- a/engine/gl/gl_alias.c
+++ b/engine/gl/gl_alias.c
@@ -2614,6 +2614,7 @@ static void R_Sprite_GenerateTrisoup(entity_t *e, int bemode)
 	{
 		genframe.down = genframe.left = -1;
 		genframe.up = genframe.right = 1;
+		genframe.xmirror = false;
 		sprtype = SPR_VP_PARALLEL;
 		frame = &genframe;
 	}
diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c
index 529f779af..d267ce5d8 100644
--- a/engine/gl/gl_heightmap.c
+++ b/engine/gl/gl_heightmap.c
@@ -4375,7 +4375,7 @@ qbyte *Heightmap_ClusterPVS	(model_t *model, int num, pvsbuffer_t *buffer, pvsme
 }
 int	Heightmap_ClusterForPoint	(model_t *model, const vec3_t point, int *area)
 {
-	if (*area)
+	if (area)
 		*area = 0;
 	return -1;
 }
@@ -5669,7 +5669,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)
 
 				dorelight = false;
 				br->faces[j].relight = false;
-				LightPlane (hm->relightcontext, hm->lightthreadmem, styles, br->faces[j].lightdata, NULL, br->planes[j], br->faces[j].stdir, exactmins, exactmaxs, br->faces[j].lmbias, texsize, br->faces[j].lmscale);	//special version that doesn't know what a face is or anything.
+				LightPlane (hm->relightcontext, hm->lightthreadmem, styles, NULL, br->faces[j].lightdata, NULL, br->planes[j], br->faces[j].stdir, exactmins, exactmaxs, br->faces[j].lmbias, texsize, br->faces[j].lmscale);	//special version that doesn't know what a face is or anything.
 				br->faces[j].relit = true;
 			}
 			if (br->faces[j].relit && br->faces[j].lightmap >= 0)
diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c
index d8fa5fb04..054095338 100644
--- a/engine/gl/gl_model.c
+++ b/engine/gl/gl_model.c
@@ -429,8 +429,16 @@ void Mod_Think (void)
 				f = FS_OpenVFS(filename, "wb", FS_GAME);
 				if (f)
 				{
-					VFS_WRITE(f, "QLIT\1\0\0\0", 8);
-					VFS_WRITE(f, lightmodel->lightdata, numlightdata*3);
+					if (lightmodel->lightmaps.fmt == LM_E5BGR9)
+					{
+						VFS_WRITE(f, "QLIT\x01\0\x01\0", 8);
+						VFS_WRITE(f, lightmodel->lightdata, numlightdata*4);
+					}
+					else
+					{
+						VFS_WRITE(f, "QLIT\1\0\0\0", 8);
+						VFS_WRITE(f, lightmodel->lightdata, numlightdata*3);
+					}
 					VFS_CLOSE(f);
 				}
 				else
@@ -1889,10 +1897,10 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
 			litsize = 0;
 		}
 
-		if (litdata && litsize >= 8)
+		if (litdata)
 		{	//validate it, if we loaded one.
 			int litver = LittleLong(*(int *)&litdata[4]);
-			if (litdata[0] != 'Q' || litdata[1] != 'L' || litdata[2] != 'I' || litdata[3] != 'T')
+			if (litsize < 8 || litdata[0] != 'Q' || litdata[1] != 'L' || litdata[2] != 'I' || litdata[3] != 'T')
 			{
 				litdata = NULL;
 				Con_Printf("lit \"%s\" isn't a lit\n", litname);
@@ -1915,7 +1923,7 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
 					expdata = litdata+8;	//header+version
 				litdata = NULL;
 			}
-			else if (litver == 2 && overrides)
+			else if (litver == 2 && overrides && litsize > sizeof(qlit2_t))
 			{
 				qlit2_t *ql2 = (qlit2_t*)litdata;
 				unsigned int *offsets = (unsigned int*)(ql2+1);
@@ -1932,6 +1940,11 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
 					litdata = NULL;
 					Con_Printf("lit \"%s\" doesn't match level. Ignored.\n", litname);
 				}
+				else if (litsize != sizeof(qlit2_t)+ql2->numsurfs*4+ql2->lmsize*6)
+				{
+					litdata = NULL;
+					Con_Printf("lit \"%s\" is truncated. Ignored.\n", litname);
+				}
 				else
 				{
 					inhibitvalidation = true;
@@ -1958,7 +1971,7 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
 		}
 
 		exptmp = littmp = false;
-		if (!litdata)
+		if (!litdata && !expdata)
 		{
 			int size;
 			/*FIXME: bspx support for extents+lmscale, may require style+offset lumps too, not sure what to do here*/
@@ -1976,7 +1989,7 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
 		}
 		else if (!inhibitvalidation)
 		{
-			if (lumdata)
+			if (lumdata && litdata)
 			{
 				float prop;
 				int i;
@@ -2060,7 +2073,7 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
 		}
 		else
 		{
-			if (luxdata[0] == 'Q' && luxdata[1] == 'L' && luxdata[2] == 'I' && luxdata[3] == 'T')
+			if (luxsz < 8 || (luxdata[0] == 'Q' && luxdata[1] == 'L' && luxdata[2] == 'I' && luxdata[3] == 'T'))
 			{
 				if (LittleLong(*(int *)&luxdata[4]) == 1)
 					luxdata+=8;
@@ -2082,9 +2095,9 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
 #ifdef RUNTIMELIGHTING
 	if ((loadmodel->type == mod_brush && loadmodel->fromgame == fg_quake) || loadmodel->type == mod_heightmap)
 	{	//we only support a couple of formats. :(
-		if (!lightmodel && r_loadlits.value == 2 && (!litdata || (!luxdata && r_deluxemapping)))
+		if (!lightmodel && r_loadlits.value == 2 && ((!litdata&&!expdata) || (!luxdata && r_deluxemapping)))
 		{
-			writelitfile = !litdata;
+			writelitfile = !litdata&&!expdata;
 			numlightdata = l->filelen;
 			lightmodel = loadmodel;
 			relitsurface = 0;
@@ -2103,20 +2116,37 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
 	}
 
 	/*if we're relighting, make sure there's the proper lit data to be updated*/
-	if (lightmodel == loadmodel && !litdata)
+	if (lightmodel == loadmodel && !litdata && !expdata)
 	{
 		int i;
-		litdata = ZG_Malloc(&loadmodel->memgroup, samples*3);
-		littmp = false;
-		if (lumdata)
+		unsigned int *ergb;
+
+		if (r_loadlits.ival >= 3)
 		{
-			for (i = 0; i < samples; i++)
+			ergb = ZG_Malloc(&loadmodel->memgroup, samples*4);
+			expdata = (qbyte*)ergb;
+			littmp = false;
+			if (lumdata)
 			{
-				litdata[i*3+0] = lumdata[i];
-				litdata[i*3+1] = lumdata[i];
-				litdata[i*3+2] = lumdata[i];
+				for (i = 0; i < samples; i++)
+					ergb[i] = 15<<27 | lumdata[i]<<18 | lumdata[i]<<9 << lumdata[i]<<0;
+				lumdata = NULL;
+			}
+		}
+		else
+		{
+			litdata = ZG_Malloc(&loadmodel->memgroup, samples*3);
+			littmp = false;
+			if (lumdata)
+			{
+				for (i = 0; i < samples; i++)
+				{
+					litdata[i*3+0] = lumdata[i];
+					litdata[i*3+1] = lumdata[i];
+					litdata[i*3+2] = lumdata[i];
+				}
+				lumdata = NULL;
 			}
-			lumdata = NULL;
 		}
 	}
 	/*if we're relighting, make sure there's the proper lux data to be updated*/
diff --git a/engine/gl/ltface.c b/engine/gl/ltface.c
index ea9d86ae0..709e3022e 100644
--- a/engine/gl/ltface.c
+++ b/engine/gl/ltface.c
@@ -21,6 +21,7 @@ struct relight_ctx_s
 	unsigned int nummodels;
 	model_t *models[2048];
 
+	float minlight;
 	qboolean skiplit;	//lux only
 	qboolean shadows;
 	mentity_t *entities;
@@ -45,7 +46,7 @@ struct relight_ctx_s
 
 #define scaledist 1
 #define rangescale 0.5
-#define extrasamples 0
+#define extrasamples 1
 #define scalecos 0.5
 
 
@@ -263,6 +264,10 @@ void LightReloadEntities(struct relight_ctx_s *ctx, const char *entstring, qbool
 		ctx->num_entities++;
 	}
 
+	if (ctx->num_entities)
+		if (ctx->entities[0].light)
+			ctx->minlight = ctx->entities[0].light;
+
 	for (mapent = ctx->entities; mapent < &ctx->entities[ctx->num_entities]; mapent++)
 	{
 		if (*mapent->target)
@@ -704,9 +709,7 @@ FixMinlight
 static void FixMinlight (llightinfo_t *l)
 {
 	int		i, j;
-	float	minlight;
-	
-	minlight = 0;
+	float	minlight = l->ctx->minlight;
 
 // if minlight is set, there must be a style 0 light map
 	if (!minlight)
@@ -744,13 +747,38 @@ static void FixMinlight (llightinfo_t *l)
 	}
 }
 
+static unsigned int PackE5BRG9(vec3_t rgb)
+{	//5 bits exponent, 3*9 bits of mantissa. no sign bit.
+	int e = 0;
+	float m = max(max(rgb[0], rgb[1]), rgb[2]);
+	float scale;
+	unsigned int hdr;
+
+	if (m >= 0.5)
+	{	//positive exponent
+		while (m >= (1<<(e)) && e < 30-15)	//don't do nans.
+			e++;
+	}
+	else
+	{	//negative exponent...
+		while (m < 1/(1<<-e) && e > -15)	//don't do denormals.
+			e--;
+	}
+
+	scale = pow(2, e-9);
+	hdr = ((e+15)<<27);
+	hdr |= bound(0, (int)(rgb[0]/scale + 0.5), 0x1ff)<<0;
+	hdr |= bound(0, (int)(rgb[1]/scale + 0.5), 0x1ff)<<9;
+	hdr |= bound(0, (int)(rgb[2]/scale + 0.5), 0x1ff)<<18;
+	return hdr;
+}
 
 /*
 ============
 LightFace
 ============
 */
-void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, qbyte surf_styles[MAXQ1LIGHTMAPS], qbyte *surf_rgbsamples, qbyte *surf_deluxesamples, vec4_t surf_plane, vec4_t surf_texplanes[2], vec2_t exactmins, vec2_t exactmaxs, int texmins[2], int texsize[2], float lmscale)
+void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, qbyte surf_styles[MAXQ1LIGHTMAPS], unsigned int *surf_expsamples, qbyte *surf_rgbsamples, qbyte *surf_deluxesamples, vec4_t surf_plane, vec4_t surf_texplanes[2], vec2_t exactmins, vec2_t exactmaxs, int texmins[2], int texsize[2], float lmscale)
 {
 	int		s, t;
 	int		i,c,ch;
@@ -761,6 +789,7 @@ void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, qbyte surf_s
 	int		lightmapsize;
 	byte	*out;
 #endif
+	unsigned int *expout;
 	qbyte	*rgbout;
 	qbyte	*dulout;
 	vec3_t	*light, *norm;
@@ -771,7 +800,7 @@ void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, qbyte surf_s
 //
 // some surfaces don't need lightmaps
 //	
-	if (!surf_rgbsamples)
+	if (!surf_rgbsamples && !surf_expsamples)
 		return;
 
 //	memset (l, 0, sizeof(*l));
@@ -840,21 +869,28 @@ void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, qbyte surf_s
 	if (runningrgblightdatabase)
 	{
 		out = GetFakeFileSpace(&f->lightofs, lightmapsize);
+		expout = NULL;
 		rgbout = runningrgblightdatabase + f->lightofs*3;
 		dulout = runninglightnormbase + f->lightofs*3;
 	}
 	else
 	{
 		out = GetFileSpace (&f->lightofs, lightmapsize);
-
+		expout = NULL;
 		rgbout = GetRGBFileSpace (f->lightofs, lightmapsize);
 		dulout = GetNormFileSpace (f->lightofs, lightmapsize);
 	}
 #else
 	if (!ctx->skiplit)
+	{
+		expout = surf_expsamples;
 		rgbout = surf_rgbsamples;
+	}
 	else
+	{
+		expout = NULL;
 		rgbout = NULL;
+	}
 	if (l->ctx->models[0]->deluxdata)
 	{
 		dulout = surf_deluxesamples;
@@ -906,6 +942,7 @@ void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, qbyte surf_s
 						wnorm[ch] = norm[c][ch];
 					}
 					total *= rangescale;	// scale before clamping
+					temp[ch] = total/0x80;	// quake bsps store logical light values between 0 and 2 for overbrights. normalise it appropriately.
 #ifndef UTILITY
 //					if (total > *rgbout)	//sorry - for qw
 //						total = *rgbout;
@@ -919,6 +956,8 @@ void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, qbyte surf_s
 						*rgbout++ = total;
 					mean += total;
 				}
+				if (expout)
+					*expout++ = PackE5BRG9(temp);
 #ifdef UTILITY
 				*out++ = mean/3;
 #endif
@@ -961,6 +1000,9 @@ void LightFace (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, int f
 		return;
 
 	LightCalcFaceExtents(ctx->models[0], f, exactmins, exactmaxs, texmins, texsize);
-	LightPlane(ctx, threadctx, f->styles, f->samples, f->samples - ctx->models[0]->lightdata + ctx->models[0]->deluxdata, plane, f->texinfo->vecs, exactmins, exactmaxs, texmins, texsize, 1<<f->lmshift);
+	if (ctx->models[0]->lightmaps.fmt == LM_E5BGR9)
+		LightPlane(ctx, threadctx, f->styles, (unsigned int*)f->samples, NULL, 3*(f->samples - ctx->models[0]->lightdata)/4 + ctx->models[0]->deluxdata, plane, f->texinfo->vecs, exactmins, exactmaxs, texmins, texsize, 1<<f->lmshift);
+	else
+		LightPlane(ctx, threadctx, f->styles, NULL, f->samples, f->samples - ctx->models[0]->lightdata + ctx->models[0]->deluxdata, plane, f->texinfo->vecs, exactmins, exactmaxs, texmins, texsize, 1<<f->lmshift);
 }
 #endif
diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c
index ba31b16da..8ae0aa5ef 100644
--- a/engine/server/sv_user.c
+++ b/engine/server/sv_user.c
@@ -310,10 +310,10 @@ void SV_New_f (void)
 		const char *s;
 		int ver;
 		s = InfoBuf_ValueForKey(&host_client->userinfo, "*client");
-		if (!strncmp(s, "ezQuake", 7))
+		if (!strncmp(s, "ezQuake", 7) || !strncmp(s, "FortressOne", 11))
 		{
-			s += 7;
-			COM_Parse(s);
+			COM_Parse(s);	//skip name-of-fork
+			COM_Parse(s);	//tokenize the version
 			ver = atoi(com_token);
 
 			//this should actually have been resolved now, but for future use...