From aaef412823945719c30231d4c41d3fb9053dc0fb Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Mon, 30 Dec 2019 11:33:22 +0100
Subject: [PATCH 01/37] Add basic textmap support; currently crashes when
 trying to free the virtres, at vres_free().

---
 src/doomdef.h |   2 +
 src/m_misc.c  |  14 ++
 src/p_setup.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++++-
 src/w_wad.c   |   2 +
 4 files changed, 382 insertions(+), 7 deletions(-)

diff --git a/src/doomdef.h b/src/doomdef.h
index 51a15bd64..0d673a426 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -461,6 +461,8 @@ extern void *(*M_Memcpy)(void* dest, const void* src, size_t n) FUNCNONNULL;
 char *va(const char *format, ...) FUNCPRINTF;
 char *M_GetToken(const char *inputString);
 void M_UnGetToken(void);
+UINT32 M_GetTokenPos(void);
+void M_SetTokenPos(UINT32 newPos);
 char *sizeu1(size_t num);
 char *sizeu2(size_t num);
 char *sizeu3(size_t num);
diff --git a/src/m_misc.c b/src/m_misc.c
index b0a1fb8c5..edb24ab1e 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -1908,6 +1908,20 @@ void M_UnGetToken(void)
 	endPos = oldendPos;
 }
 
+/** Returns the current token's position.
+ */
+UINT32 M_GetTokenPos(void)
+{
+	return endPos;
+}
+
+/** Sets the current token's position.
+ */
+void M_SetTokenPos(UINT32 newPos)
+{
+	endPos = newPos;
+}
+
 /** Count bits in a number.
   */
 UINT8 M_CountBits(UINT32 num, UINT8 size)
diff --git a/src/p_setup.c b/src/p_setup.c
index 99da5ccee..c7eb50382 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -83,6 +83,8 @@
 #include "p_slopes.h"
 #endif
 
+#include "fastcmp.h" // textmap parsing
+
 //
 // Map MD5, calculated on level load.
 // Sent to clients in PT_SERVERINFO.
@@ -1236,10 +1238,367 @@ static void P_LoadThings(UINT8 *data)
 	}
 }
 
+// Stores positions for relevant map data spread through a TEXTMAP.
+UINT32 mapthingsPos[UINT16_MAX];
+UINT32 linesPos[UINT16_MAX];
+UINT32 sidesPos[UINT16_MAX];
+UINT32 vertexesPos[UINT16_MAX];
+UINT32 sectorsPos[UINT16_MAX];
+
+static boolean TextmapCount (UINT8 *data, size_t size)
+{
+	char *nsp1 = M_GetToken((char *)data);
+	boolean ret = true;
+
+	// Determine total amount of map data in TEXTMAP.
+	// Look for namespace at the beginning.
+	if (fastcmp(nsp1, "namespace"))
+	{
+		char *nsp2 = M_GetToken(NULL);
+		char *tkn = M_GetToken(NULL);
+
+		// Check if namespace is valid.
+		if (!fastcmp(nsp2, "srb2"))
+			CONS_Alert(CONS_WARNING, "Invalid namespace '%s', only 'srb2' is supported.\n", nsp2);
+		Z_Free(nsp2);
+
+		while (tkn != NULL && M_GetTokenPos() < size)
+		{
+			// Avoid anything inside bracketed stuff, only look for external keywords.
+			// Assuming there's only one level of bracket nesting.
+			if (fastcmp(tkn, "{"))
+			{
+				Z_Free(tkn);
+				while (!fastcmp(tkn, "}"))
+				{
+					Z_Free(tkn);
+					tkn = M_GetToken(NULL);
+				}
+			}
+			// Check for valid fields.
+			else if (fastcmp(tkn, "thing"))
+				mapthingsPos[nummapthings++] = M_GetTokenPos();
+			else if (fastcmp(tkn, "linedef"))
+				linesPos[numlines++] = M_GetTokenPos();
+			else if (fastcmp(tkn, "sidedef"))
+				sidesPos[numsides++] = M_GetTokenPos();
+			else if (fastcmp(tkn, "vertex"))
+				vertexesPos[numvertexes++] = M_GetTokenPos();
+			else if (fastcmp(tkn, "sector"))
+				sectorsPos[numsectors++] = M_GetTokenPos();
+			else
+				CONS_Alert(CONS_NOTICE, "Unknown field '%s'.\n", tkn);
+
+			Z_Free(tkn);
+			tkn = M_GetToken(NULL);
+		}
+	}
+	else
+	{
+		CONS_Alert(CONS_WARNING, "No namespace at beginning of lump!\n");
+		ret = false;
+	}
+
+	Z_Free(nsp1);
+	return ret;
+}
+
+static char* dat;
+
+/** Auxiliary function for TextmapParse.
+  *
+  * \param Vertex number.
+  * \param Parameter string.
+  */
+static void TextmapVertex(UINT32 i, char *param)
+{
+	if (fastcmp(param, "x"))
+		vertexes[i].x = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+	else if (fastcmp(param, "y"))
+		vertexes[i].y = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+}
+
+/** Auxiliary function for TextmapParse.
+  *
+  * \param Sector number.
+  * \param Parameter string.
+  */
+static void TextmapSector(UINT32 i, char *param)
+{
+	if (fastcmp(param, "heightfloor"))
+		sectors[i].floorheight = atol(dat = M_GetToken(NULL)) << FRACBITS;
+	else if (fastcmp(param, "heightceiling"))
+		sectors[i].ceilingheight = atol(dat = M_GetToken(NULL)) << FRACBITS;
+	if (fastcmp(param, "texturefloor"))
+		sectors[i].floorpic = P_AddLevelFlat(dat = M_GetToken(NULL), foundflats);
+	else if (fastcmp(param, "textureceiling"))
+		sectors[i].ceilingpic = P_AddLevelFlat(dat = M_GetToken(NULL), foundflats);
+	else if (fastcmp(param, "lightlevel"))
+		sectors[i].lightlevel = atol(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "special"))
+		sectors[i].special = atol(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "id"))
+		sectors[i].tag = atol(dat = M_GetToken(NULL));
+}
+
+/** Auxiliary function for TextmapParse.
+  *
+  * \param Side number.
+  * \param Parameter string.
+  */
+static void TextmapSide(UINT32 i, char *param)
+{
+	if (fastcmp(param, "offsetx"))
+		sides[i].textureoffset = atol(dat = M_GetToken(NULL))<<FRACBITS;
+	else if (fastcmp(param, "offsety"))
+		sides[i].rowoffset = atol(dat = M_GetToken(NULL))<<FRACBITS;
+	else if (fastcmp(param, "texturetop"))
+		sides[i].toptexture = R_TextureNumForName(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "texturebottom"))
+		sides[i].bottomtexture = R_TextureNumForName(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "texturemiddle"))
+		sides[i].midtexture = R_TextureNumForName(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "sector"))
+		sides[i].sector = &sectors[atol(dat = M_GetToken(NULL))];
+	else if (fastcmp(param, "repeatcnt"))
+		sides[i].repeatcnt = atol(dat = M_GetToken(NULL));
+}
+
+/** Auxiliary function for TextmapParse.
+  *
+  * \param Line number.
+  * \param Parameter string.
+  */
+static void TextmapLine(UINT32 i, char *param)
+{
+	if (fastcmp(param, "id"))
+		lines[i].tag = atol(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "special"))
+		lines[i].special = atol(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "v1"))
+		lines[i].v1 = &vertexes[atol(dat = M_GetToken(NULL))];
+	else if (fastcmp(param, "v2"))
+		lines[i].v2 = &vertexes[atol(dat = M_GetToken(NULL))];
+	else if (fastcmp(param, "sidefront"))
+		lines[i].sidenum[0] = atol(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "sideback"))
+		lines[i].sidenum[1] = atol(dat = M_GetToken(NULL));
+
+	// Flags
+	else if (fastcmp(param, "blocking") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_IMPASSIBLE;
+	else if (fastcmp(param, "blockmonsters") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_BLOCKMONSTERS;
+	else if (fastcmp(param, "twosided") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_TWOSIDED;
+	else if (fastcmp(param, "dontpegtop") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_DONTPEGTOP;
+	else if (fastcmp(param, "dontpegbottom") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_DONTPEGBOTTOM;
+	else if (fastcmp(param, "skewtd") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_EFFECT1;
+	else if (fastcmp(param, "noclimb") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_NOCLIMB;
+	else if (fastcmp(param, "noskew") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_EFFECT2;
+	else if (fastcmp(param, "midpeg") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_EFFECT3;
+	else if (fastcmp(param, "midsolid") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_EFFECT4;
+	else if (fastcmp(param, "wrapmidtex") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_EFFECT5;
+	else if (fastcmp(param, "effect6") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_EFFECT6;
+	else if (fastcmp(param, "nonet") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_NONET;
+	else if (fastcmp(param, "netonly") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_NETONLY;
+	else if (fastcmp(param, "bouncy") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_BOUNCY;
+	else if (fastcmp(param, "transfer") && fastcmp("true", dat = M_GetToken(NULL)))
+		lines[i].flags |= ML_TFERLINE;
+}
+
+/** Auxiliary function for TextmapParse.
+  */
+static void TextmapThing(UINT32 i, char *param)
+{
+	if (fastcmp(param, "x"))
+		mapthings[i].x = atol(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "y"))
+		mapthings[i].y = atol(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "height"))
+		mapthings[i].z = atol(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "angle"))
+		mapthings[i].angle = atol(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "type"))
+		mapthings[i].type = atol(dat = M_GetToken(NULL));
+
+	// Flags
+	else if (fastcmp(param, "extra") && fastcmp("true", dat = M_GetToken(NULL)))
+		mapthings[i].options |= 1;
+	else if (fastcmp(param, "flip") && fastcmp("true", dat = M_GetToken(NULL)))
+		mapthings[i].options |= MTF_OBJECTFLIP;
+	else if (fastcmp(param, "special") && fastcmp("true", dat = M_GetToken(NULL)))
+		mapthings[i].options |= MTF_OBJECTSPECIAL;
+	else if (fastcmp(param, "ambush") && fastcmp("true", dat = M_GetToken(NULL)))
+		mapthings[i].options |= MTF_AMBUSH;
+}
+
+/** From a given position table, run a specified parser function through a {}-encapsuled text.
+  *
+  * \param Position of the data to parse, in the textmap.
+  * \param Structure number (mapthings, sectors, ...).
+  * \param Parser function pointer.
+  */
+static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char *))
+{
+	char *open;
+
+	M_SetTokenPos(dataPos);
+	open = M_GetToken(NULL);
+	if (fastcmp(open, "{"))
+	{
+		char *tkn = M_GetToken(NULL);
+		while (!fastcmp(tkn, "}"))
+		{
+			dat = NULL;
+			parser(num, tkn);
+			if (dat)
+				Z_Free(dat);
+
+			Z_Free(tkn);
+			tkn = M_GetToken(NULL);
+		}
+		Z_Free(tkn);
+	}
+	else
+		CONS_Alert(CONS_WARNING, "Invalid UDMF data capsule!\n");
+	Z_Free(open);
+}
+
+/** Loads the textmap data, after obtaining the elements count and allocating their respective space.
+  */
+static void P_LoadTextmap (void)
+{
+	UINT32 i;
+
+	vertex_t   *vt;
+	sector_t   *sc;
+	line_t     *ld;
+	side_t     *sd;
+	mapthing_t *mt;
+
+	/// Given the UDMF specs, some fields are given a default value.
+	/// If an element's field has a default value set, it is ommited
+	/// from the textmap, and therefore we have to account for it by
+	/// preemptively setting that value beforehand.
+
+	for (i = 0, vt = vertexes; i < numvertexes; i++, vt++)
+	{
+		// Defaults.
+		vt->z = 0;
+
+		TextmapParse(vertexesPos[i], i, TextmapVertex);
+	}
+
+	for (i = 0, sc = sectors; i < numsectors; i++, sc++)
+	{
+		// Defaults.
+		sc->floorheight = 0;
+		sc->ceilingheight = 0;
+
+		sc->floorpic = 0;
+		sc->ceilingpic = 0;
+
+		sc->lightlevel = 255;
+
+		sc->special = 0;
+		sc->tag = 0;
+
+		sc->floor_xoffs = sc->floor_yoffs = sc->ceiling_xoffs = sc->ceiling_yoffs = 0;
+		sc->floorpic_angle = sc->ceilingpic_angle = 0;
+
+		TextmapParse(sectorsPos[i], i, TextmapSector);
+
+		P_InitializeSector(sc);
+	}
+
+	for (i = 0, ld = lines; i < numlines; i++, ld++)
+	{
+		// Defaults.
+		ld->tag = 0;
+		ld->special = 0;
+		ld->sidenum[1] = 0xffff;
+
+		TextmapParse(linesPos[i], i, TextmapLine);
+
+		P_InitializeLinedef(ld);
+	}
+
+	for (i = 0, sd = sides; i < numsides; i++, sd++)
+	{
+		// Defaults.
+		sd->rowoffset = 0;
+		sd->textureoffset = 0;
+
+		sd->toptexture = R_TextureNumForName("-");
+		sd->midtexture = R_TextureNumForName("-");
+		sd->bottomtexture = R_TextureNumForName("-");
+		sd->repeatcnt = 0;
+
+		TextmapParse(sidesPos[i], i, TextmapSide);
+	}
+
+	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
+	{
+		// Defaults.
+		mt->z = 0;
+		mt->angle = 0;
+
+		TextmapParse(mapthingsPos[i], i, TextmapThing);
+	}
+}
+
+/** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
+ */
+static void TextmapFixFlatOffsets (void)
+{
+	fixed_t pc, ps;
+	fixed_t xoffs, yoffs;
+	size_t i;
+	sector_t* sec = sectors;
+	for (i = 0; i < numsectors; i++, sec++)
+	{
+		if (sec->floorpic_angle)
+		{
+			pc = FINECOSINE(sec->floorpic_angle>>ANGLETOFINESHIFT);
+			ps = FINESINE  (sec->floorpic_angle>>ANGLETOFINESHIFT);
+			xoffs = sec->floor_xoffs;
+			yoffs = sec->floor_yoffs;
+			#define MAXFLATSIZE (2048<<FRACBITS)
+			sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+			sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+			#undef MAXFLATSIZE
+		}
+
+		if (sec->ceilingpic_angle)
+		{
+			pc = FINECOSINE(sec->ceilingpic_angle>>ANGLETOFINESHIFT);
+			ps = FINESINE  (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
+			xoffs = sec->ceiling_xoffs;
+			yoffs = sec->ceiling_yoffs;
+			#define MAXFLATSIZE (2048<<FRACBITS)
+			sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+			sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+			#undef MAXFLATSIZE
+		}
+	}
+}
+
 static void P_LoadMapData(const virtres_t *virt)
 {
 	virtlump_t* virtvertexes = NULL, * virtsectors = NULL, * virtsidedefs = NULL, * virtlinedefs = NULL, * virtthings = NULL;
-#ifdef UDMF
 	virtlump_t* textmap = vres_Find(virt, "TEXTMAP");
 
 	// Count map data.
@@ -1252,10 +1611,9 @@ static void P_LoadMapData(const virtres_t *virt)
 		numsectors = 0;
 
 		// Count how many entries for each type we got in textmap.
-		//TextmapCount(vtextmap->data, vtextmap->size);
+		TextmapCount(textmap->data, textmap->size);
 	}
 	else
-#endif
 	{
 		virtthings   = vres_Find(virt, "THINGS");
 		virtvertexes = vres_Find(virt, "VERTEXES");
@@ -1305,15 +1663,14 @@ static void P_LoadMapData(const virtres_t *virt)
 
 	numlevelflats = 0;
 
-#ifdef UDMF
+	// Load map data.
 	if (textmap)
 	{
-
+		P_LoadTextmap();
+		TextmapFixFlatOffsets();
 	}
 	else
-#endif
 	{
-		// Strict map data
 		P_LoadVertices(virtvertexes->data);
 		P_LoadSectors(virtsectors->data);
 		P_LoadLinedefs(virtlinedefs->data);
diff --git a/src/w_wad.c b/src/w_wad.c
index 62992441a..47c3f42d0 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1942,6 +1942,8 @@ void vres_Free(virtres_t* vres)
 		Z_Free(vres->vlumps[vres->numlumps].data);
 	Z_Free(vres->vlumps);
 	Z_Free(vres);
+
+	CONS_Printf("A A A\n");
 }
 
 /** (Debug) Prints lumps from a virtual resource into console.

From ed114f655bd84576d8f3eea314921b9cc611b49e Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Mon, 30 Dec 2019 12:07:02 +0100
Subject: [PATCH 02/37] Fixed missing M_GetToken(NULL);

---
 src/p_setup.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/p_setup.c b/src/p_setup.c
index c7eb50382..71d1ca880 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1269,6 +1269,7 @@ static boolean TextmapCount (UINT8 *data, size_t size)
 			if (fastcmp(tkn, "{"))
 			{
 				Z_Free(tkn);
+				tkn = M_GetToken(NULL);
 				while (!fastcmp(tkn, "}"))
 				{
 					Z_Free(tkn);

From f49b8de5fd8999965f28bc7fcec5ff666ac1b3d3 Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Mon, 30 Dec 2019 12:07:54 +0100
Subject: [PATCH 03/37] Adapt P_MakeMapMD5() for textmaps.

---
 src/p_setup.c | 41 ++++++++++++++++++++++++-----------------
 1 file changed, 24 insertions(+), 17 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 71d1ca880..198b8ae6a 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2668,27 +2668,34 @@ static INT32 P_MakeBufferMD5(const char *buffer, size_t len, void *resblock)
 
 static void P_MakeMapMD5(virtres_t *virt, void *dest)
 {
-	unsigned char linemd5[16];
-	unsigned char sectormd5[16];
-	unsigned char thingmd5[16];
-	unsigned char sidedefmd5[16];
+	virtlump_t* textmap   = vres_Find(virt, "TEXTMAP");
 	unsigned char resmd5[16];
-	UINT8 i;
 
-	// Create a hash for the current map
-	// get the actual lumps!
-	virtlump_t *virtlines = vres_Find(virt, "LINEDEFS");
-	virtlump_t *virtsectors = vres_Find(virt, "SECTORS");
-	virtlump_t *virtmthings = vres_Find(virt, "THINGS");
-	virtlump_t *virtsides = vres_Find(virt, "SIDEDEFS");
+	if (textmap)
+		P_MakeBufferMD5((char*)textmap->data, textmap->size, resmd5);
+	else
+	{
+		unsigned char linemd5[16];
+		unsigned char sectormd5[16];
+		unsigned char thingmd5[16];
+		unsigned char sidedefmd5[16];
+		UINT8 i;
 
-	P_MakeBufferMD5((char*)virtlines->data, virtlines->size, linemd5);
-	P_MakeBufferMD5((char*)virtsectors->data, virtsectors->size, sectormd5);
-	P_MakeBufferMD5((char*)virtmthings->data, virtmthings->size, thingmd5);
-	P_MakeBufferMD5((char*)virtsides->data, virtsides->size, sidedefmd5);
+		// Create a hash for the current map
+		// get the actual lumps!
+		virtlump_t* virtlines   = vres_Find(virt, "LINEDEFS");
+		virtlump_t* virtsectors = vres_Find(virt, "SECTORS");
+		virtlump_t* virtmthings = vres_Find(virt, "THINGS");
+		virtlump_t* virtsides   = vres_Find(virt, "SIDEDEFS");
 
-	for (i = 0; i < 16; i++)
-		resmd5[i] = (linemd5[i] + sectormd5[i] + thingmd5[i] + sidedefmd5[i]) & 0xFF;
+		P_MakeBufferMD5((char*)virtlines->data,   virtlines->size, linemd5);
+		P_MakeBufferMD5((char*)virtsectors->data, virtsectors->size,  sectormd5);
+		P_MakeBufferMD5((char*)virtmthings->data, virtmthings->size,   thingmd5);
+		P_MakeBufferMD5((char*)virtsides->data,   virtsides->size, sidedefmd5);
+
+		for (i = 0; i < 16; i++)
+			resmd5[i] = (linemd5[i] + sectormd5[i] + thingmd5[i] + sidedefmd5[i]) & 0xFF;
+	}
 
 	M_Memcpy(dest, &resmd5, 16);
 }

From 493c6c8ae2e725870ceded26f97ed5b936ff4bea Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Mon, 30 Dec 2019 12:23:31 +0100
Subject: [PATCH 04/37] AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

---
 src/w_wad.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/w_wad.c b/src/w_wad.c
index 47c3f42d0..62992441a 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1942,8 +1942,6 @@ void vres_Free(virtres_t* vres)
 		Z_Free(vres->vlumps[vres->numlumps].data);
 	Z_Free(vres->vlumps);
 	Z_Free(vres);
-
-	CONS_Printf("A A A\n");
 }
 
 /** (Debug) Prints lumps from a virtual resource into console.

From f9aabe753e76bb028746251b7ab867b297285c23 Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Mon, 30 Dec 2019 12:31:55 +0100
Subject: [PATCH 05/37] Refactor TextmapFixFlatOffsets().

---
 src/p_setup.c | 53 +++++++++++++++++++++------------------------------
 1 file changed, 22 insertions(+), 31 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 198b8ae6a..2334a9a03 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1523,6 +1523,7 @@ static void P_LoadTextmap (void)
 		TextmapParse(sectorsPos[i], i, TextmapSector);
 
 		P_InitializeSector(sc);
+		TextmapFixFlatOffsets(sc);
 	}
 
 	for (i = 0, ld = lines; i < numlines; i++, ld++)
@@ -1563,37 +1564,30 @@ static void P_LoadTextmap (void)
 
 /** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
  */
-static void TextmapFixFlatOffsets (void)
+static void TextmapFixFlatOffsets (sector_t* sec)
 {
-	fixed_t pc, ps;
-	fixed_t xoffs, yoffs;
-	size_t i;
-	sector_t* sec = sectors;
-	for (i = 0; i < numsectors; i++, sec++)
+	if (sec->floorpic_angle)
 	{
-		if (sec->floorpic_angle)
-		{
-			pc = FINECOSINE(sec->floorpic_angle>>ANGLETOFINESHIFT);
-			ps = FINESINE  (sec->floorpic_angle>>ANGLETOFINESHIFT);
-			xoffs = sec->floor_xoffs;
-			yoffs = sec->floor_yoffs;
-			#define MAXFLATSIZE (2048<<FRACBITS)
-			sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
-			sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
-			#undef MAXFLATSIZE
-		}
+		fixed_t pc = FINECOSINE(sec->floorpic_angle>>ANGLETOFINESHIFT);
+		fixed_t ps = FINESINE  (sec->floorpic_angle>>ANGLETOFINESHIFT);
+		fixed_t xoffs = sec->floor_xoffs;
+		fixed_t yoffs = sec->floor_yoffs;
+		#define MAXFLATSIZE (2048<<FRACBITS)
+		sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+		sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+		#undef MAXFLATSIZE
+	}
 
-		if (sec->ceilingpic_angle)
-		{
-			pc = FINECOSINE(sec->ceilingpic_angle>>ANGLETOFINESHIFT);
-			ps = FINESINE  (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
-			xoffs = sec->ceiling_xoffs;
-			yoffs = sec->ceiling_yoffs;
-			#define MAXFLATSIZE (2048<<FRACBITS)
-			sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
-			sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
-			#undef MAXFLATSIZE
-		}
+	if (sec->ceilingpic_angle)
+	{
+		fixed_t pc = FINECOSINE(sec->ceilingpic_angle>>ANGLETOFINESHIFT);
+		fixed_t ps = FINESINE  (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
+		fixed_t xoffs = sec->ceiling_xoffs;
+		fixed_t yoffs = sec->ceiling_yoffs;
+		#define MAXFLATSIZE (2048<<FRACBITS)
+		sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+		sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+		#undef MAXFLATSIZE
 	}
 }
 
@@ -1666,10 +1660,7 @@ static void P_LoadMapData(const virtres_t *virt)
 
 	// Load map data.
 	if (textmap)
-	{
 		P_LoadTextmap();
-		TextmapFixFlatOffsets();
-	}
 	else
 	{
 		P_LoadVertices(virtvertexes->data);

From e43df2993fd06e7e3acefdb58a08a8f47b7b4e68 Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Mon, 30 Dec 2019 12:33:24 +0100
Subject: [PATCH 06/37] Move TextmapFixFlatOffsets() above P_LoadTextmap() so
 that it can compile.

---
 src/p_setup.c | 58 +++++++++++++++++++++++++--------------------------
 1 file changed, 29 insertions(+), 29 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 2334a9a03..09ef07c1a 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1478,6 +1478,35 @@ static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char
 	Z_Free(open);
 }
 
+/** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
+ */
+static void TextmapFixFlatOffsets (sector_t* sec)
+{
+	if (sec->floorpic_angle)
+	{
+		fixed_t pc = FINECOSINE(sec->floorpic_angle>>ANGLETOFINESHIFT);
+		fixed_t ps = FINESINE  (sec->floorpic_angle>>ANGLETOFINESHIFT);
+		fixed_t xoffs = sec->floor_xoffs;
+		fixed_t yoffs = sec->floor_yoffs;
+		#define MAXFLATSIZE (2048<<FRACBITS)
+		sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+		sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+		#undef MAXFLATSIZE
+	}
+
+	if (sec->ceilingpic_angle)
+	{
+		fixed_t pc = FINECOSINE(sec->ceilingpic_angle>>ANGLETOFINESHIFT);
+		fixed_t ps = FINESINE  (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
+		fixed_t xoffs = sec->ceiling_xoffs;
+		fixed_t yoffs = sec->ceiling_yoffs;
+		#define MAXFLATSIZE (2048<<FRACBITS)
+		sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+		sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+		#undef MAXFLATSIZE
+	}
+}
+
 /** Loads the textmap data, after obtaining the elements count and allocating their respective space.
   */
 static void P_LoadTextmap (void)
@@ -1562,35 +1591,6 @@ static void P_LoadTextmap (void)
 	}
 }
 
-/** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
- */
-static void TextmapFixFlatOffsets (sector_t* sec)
-{
-	if (sec->floorpic_angle)
-	{
-		fixed_t pc = FINECOSINE(sec->floorpic_angle>>ANGLETOFINESHIFT);
-		fixed_t ps = FINESINE  (sec->floorpic_angle>>ANGLETOFINESHIFT);
-		fixed_t xoffs = sec->floor_xoffs;
-		fixed_t yoffs = sec->floor_yoffs;
-		#define MAXFLATSIZE (2048<<FRACBITS)
-		sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
-		sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
-		#undef MAXFLATSIZE
-	}
-
-	if (sec->ceilingpic_angle)
-	{
-		fixed_t pc = FINECOSINE(sec->ceilingpic_angle>>ANGLETOFINESHIFT);
-		fixed_t ps = FINESINE  (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
-		fixed_t xoffs = sec->ceiling_xoffs;
-		fixed_t yoffs = sec->ceiling_yoffs;
-		#define MAXFLATSIZE (2048<<FRACBITS)
-		sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
-		sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
-		#undef MAXFLATSIZE
-	}
-}
-
 static void P_LoadMapData(const virtres_t *virt)
 {
 	virtlump_t* virtvertexes = NULL, * virtsectors = NULL, * virtsidedefs = NULL, * virtlinedefs = NULL, * virtthings = NULL;

From 4aee4e3684ce3f70e6a6e60fc427eb998ddb0b9c Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Mon, 30 Dec 2019 13:27:05 +0100
Subject: [PATCH 07/37] Refactor TextmapCount

---
 src/p_setup.c | 107 +++++++++++++++++++++++++-------------------------
 1 file changed, 53 insertions(+), 54 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 09ef07c1a..ccb528687 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1245,63 +1245,71 @@ UINT32 sidesPos[UINT16_MAX];
 UINT32 vertexesPos[UINT16_MAX];
 UINT32 sectorsPos[UINT16_MAX];
 
-static boolean TextmapCount (UINT8 *data, size_t size)
+// Determine total amount of map data in TEXTMAP.
+static boolean TextmapCount(UINT8 *data, size_t size)
 {
-	char *nsp1 = M_GetToken((char *)data);
-	boolean ret = true;
+	char *tkn = M_GetToken((char *)data);
+
+	nummapthings = 0;
+	numlines = 0;
+	numsides = 0;
+	numvertexes = 0;
+	numsectors = 0;
 
-	// Determine total amount of map data in TEXTMAP.
 	// Look for namespace at the beginning.
-	if (fastcmp(nsp1, "namespace"))
+	if (!fastcmp(tkn, "namespace"))
 	{
-		char *nsp2 = M_GetToken(NULL);
-		char *tkn = M_GetToken(NULL);
+		Z_Free(tkn);
+		CONS_Alert(CONS_WARNING, "No namespace at beginning of lump!\n");
+		return false;
+	}
+	Z_Free(tkn);
 
-		// Check if namespace is valid.
-		if (!fastcmp(nsp2, "srb2"))
-			CONS_Alert(CONS_WARNING, "Invalid namespace '%s', only 'srb2' is supported.\n", nsp2);
-		Z_Free(nsp2);
+	// Check if namespace is valid.
+	tkn = M_GetToken(NULL);
+	if (!fastcmp(tkn, "srb2"))
+		CONS_Alert(CONS_WARNING, "Invalid namespace '%s', only 'srb2' is supported.\n", tkn);
+	Z_Free(tkn);
 
-		while (tkn != NULL && M_GetTokenPos() < size)
+	tkn = M_GetToken(NULL);
+	while (tkn && M_GetTokenPos() < size)
+	{
+		// Avoid anything inside bracketed stuff, only look for external keywords.
+		// Assuming there's only one level of bracket nesting.
+		if (fastcmp(tkn, "{"))
 		{
-			// Avoid anything inside bracketed stuff, only look for external keywords.
-			// Assuming there's only one level of bracket nesting.
-			if (fastcmp(tkn, "{"))
+			do
 			{
 				Z_Free(tkn);
 				tkn = M_GetToken(NULL);
-				while (!fastcmp(tkn, "}"))
+				if (!tkn || M_GetTokenPos() >= size)
 				{
 					Z_Free(tkn);
-					tkn = M_GetToken(NULL);
+					CONS_Alert(CONS_WARNING, "Opening bracket not closed!\n");
+					return false;
 				}
-			}
-			// Check for valid fields.
-			else if (fastcmp(tkn, "thing"))
-				mapthingsPos[nummapthings++] = M_GetTokenPos();
-			else if (fastcmp(tkn, "linedef"))
-				linesPos[numlines++] = M_GetTokenPos();
-			else if (fastcmp(tkn, "sidedef"))
-				sidesPos[numsides++] = M_GetTokenPos();
-			else if (fastcmp(tkn, "vertex"))
-				vertexesPos[numvertexes++] = M_GetTokenPos();
-			else if (fastcmp(tkn, "sector"))
-				sectorsPos[numsectors++] = M_GetTokenPos();
-			else
-				CONS_Alert(CONS_NOTICE, "Unknown field '%s'.\n", tkn);
-
-			Z_Free(tkn);
-			tkn = M_GetToken(NULL);
+			} while (!fastcmp(tkn, "}"));
 		}
-	}
-	else
-	{
-		CONS_Alert(CONS_WARNING, "No namespace at beginning of lump!\n");
-		ret = false;
+		// Check for valid fields.
+		else if (fastcmp(tkn, "thing"))
+			mapthingsPos[nummapthings++] = M_GetTokenPos();
+		else if (fastcmp(tkn, "linedef"))
+			linesPos[numlines++] = M_GetTokenPos();
+		else if (fastcmp(tkn, "sidedef"))
+			sidesPos[numsides++] = M_GetTokenPos();
+		else if (fastcmp(tkn, "vertex"))
+			vertexesPos[numvertexes++] = M_GetTokenPos();
+		else if (fastcmp(tkn, "sector"))
+			sectorsPos[numsectors++] = M_GetTokenPos();
+		else
+			CONS_Alert(CONS_NOTICE, "Unknown field '%s'.\n", tkn);
+
+		Z_Free(tkn);
+		tkn = M_GetToken(NULL);
 	}
 
-	Z_Free(nsp1);
-	return ret;
+	Z_Free(tkn);
+	return true;
 }
 
 static char* dat;
@@ -1593,21 +1601,12 @@ static void P_LoadTextmap (void)
 
 static void P_LoadMapData(const virtres_t *virt)
 {
-	virtlump_t* virtvertexes = NULL, * virtsectors = NULL, * virtsidedefs = NULL, * virtlinedefs = NULL, * virtthings = NULL;
-	virtlump_t* textmap = vres_Find(virt, "TEXTMAP");
+	virtlump_t *virtvertexes = NULL, *virtsectors = NULL, *virtsidedefs = NULL, *virtlinedefs = NULL, *virtthings = NULL;
+	virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
 
 	// Count map data.
-	if (textmap)
-	{
-		nummapthings = 0;
-		numlines = 0;
-		numsides = 0;
-		numvertexes = 0;
-		numsectors = 0;
-
-		// Count how many entries for each type we got in textmap.
+	if (textmap) // Count how many entries for each type we got in textmap.
 		TextmapCount(textmap->data, textmap->size);
-	}
 	else
 	{
 		virtthings   = vres_Find(virt, "THINGS");
@@ -2659,7 +2658,7 @@ static INT32 P_MakeBufferMD5(const char *buffer, size_t len, void *resblock)
 
 static void P_MakeMapMD5(virtres_t *virt, void *dest)
 {
-	virtlump_t* textmap   = vres_Find(virt, "TEXTMAP");
+	virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
 	unsigned char resmd5[16];
 
 	if (textmap)

From c6c00aa7d5e5ba923b28c3fa8ad53fb5eed501ad Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Mon, 30 Dec 2019 13:38:52 +0100
Subject: [PATCH 08/37] Tweak TextmapCount()'s bracket detection to account for
 multiple levels, if that ever happens.

---
 src/p_setup.c | 19 ++++++-------------
 1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index ccb528687..55f0b4f3e 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1249,6 +1249,7 @@ UINT32 sectorsPos[UINT16_MAX];
 static boolean TextmapCount(UINT8 *data, size_t size)
 {
 	char *tkn = M_GetToken((char *)data);
+	UINT8 brackets = 0;
 
 	nummapthings = 0;
 	numlines = 0;
@@ -1275,21 +1276,13 @@ static boolean TextmapCount(UINT8 *data, size_t size)
 	while (tkn && M_GetTokenPos() < size)
 	{
 		// Avoid anything inside bracketed stuff, only look for external keywords.
-		// Assuming there's only one level of bracket nesting.
-		if (fastcmp(tkn, "{"))
+		if (brackets)
 		{
-			do
-			{
-				Z_Free(tkn);
-				tkn = M_GetToken(NULL);
-				if (!tkn || M_GetTokenPos() >= size)
-				{
-					Z_Free(tkn);
-					CONS_Alert(CONS_WARNING, "Opening bracket not closed!\n");
-					return false;
-				}
-			} while (!fastcmp(tkn, "}"));
+			if (fastcmp(tkn, "}"))
+				brackets--;
 		}
+		else if (fastcmp(tkn, "{"))
+			brackets++;
 		// Check for valid fields.
 		else if (fastcmp(tkn, "thing"))
 			mapthingsPos[nummapthings++] = M_GetTokenPos();

From ea87af007678a8ca516437b4bd713d9242240eaa Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Mon, 30 Dec 2019 14:33:41 +0100
Subject: [PATCH 09/37] Refactor TextmapParse

---
 src/p_setup.c | 40 +++++++++++++++++++++-------------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 55f0b4f3e..16cb785ab 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1455,33 +1455,35 @@ static void TextmapThing(UINT32 i, char *param)
   */
 static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char *))
 {
-	char *open;
+	char *tkn;
 
 	M_SetTokenPos(dataPos);
-	open = M_GetToken(NULL);
-	if (fastcmp(open, "{"))
+	tkn = M_GetToken(NULL);
+	if (!fastcmp(tkn, "{"))
 	{
-		char *tkn = M_GetToken(NULL);
-		while (!fastcmp(tkn, "}"))
-		{
-			dat = NULL;
-			parser(num, tkn);
-			if (dat)
-				Z_Free(dat);
-
-			Z_Free(tkn);
-			tkn = M_GetToken(NULL);
-		}
 		Z_Free(tkn);
-	}
-	else
 		CONS_Alert(CONS_WARNING, "Invalid UDMF data capsule!\n");
-	Z_Free(open);
+		return;
+	}
+
+	Z_Free(tkn);
+	tkn = M_GetToken(NULL);
+	while (!fastcmp(tkn, "}"))
+	{
+		dat = NULL;
+		parser(num, tkn);
+		if (dat)
+			Z_Free(dat);
+
+		Z_Free(tkn);
+		tkn = M_GetToken(NULL);
+	}
+	Z_Free(tkn);
 }
 
 /** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
  */
-static void TextmapFixFlatOffsets (sector_t* sec)
+static void TextmapFixFlatOffsets(sector_t *sec)
 {
 	if (sec->floorpic_angle)
 	{
@@ -1510,7 +1512,7 @@ static void TextmapFixFlatOffsets (sector_t* sec)
 
 /** Loads the textmap data, after obtaining the elements count and allocating their respective space.
   */
-static void P_LoadTextmap (void)
+static void P_LoadTextmap(void)
 {
 	UINT32 i;
 

From 7ae2143c9102c798c7db9d2583aadbc3b77f2631 Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Mon, 30 Dec 2019 14:42:41 +0100
Subject: [PATCH 10/37] Add a disclaimer when loading textmaps/UDMF.

---
 src/p_setup.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/p_setup.c b/src/p_setup.c
index 55f0b4f3e..839e583c8 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1520,6 +1520,8 @@ static void P_LoadTextmap (void)
 	side_t     *sd;
 	mapthing_t *mt;
 
+	CONS_Alert(CONS_NOTICE, "UDMF support is still a work-in-progress; its specs and features are prone to change until it is fully implemented.\n");
+
 	/// Given the UDMF specs, some fields are given a default value.
 	/// If an element's field has a default value set, it is ommited
 	/// from the textmap, and therefore we have to account for it by

From f9d6e26558aa6fd4439edfcb5380691b0074c495 Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Mon, 30 Dec 2019 14:45:39 +0100
Subject: [PATCH 11/37] Replace INT16_MAX with LUMPERROR in lump check.

---
 src/p_setup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 839e583c8..73c3b44f6 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -3536,7 +3536,7 @@ boolean P_LoadLevel(boolean fromnetsave)
 	// internal game map
 	maplumpname = G_BuildMapName(gamemap);
 	lastloadedmaplumpnum = W_CheckNumForName(maplumpname);
-	if (lastloadedmaplumpnum == INT16_MAX)
+	if (lastloadedmaplumpnum == LUMPERROR)
 		I_Error("Map %s not found.\n", maplumpname);
 
 	R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);

From 4a5498473c3ff07feb45006943e59ec0b504282a Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Mon, 30 Dec 2019 14:47:48 +0100
Subject: [PATCH 12/37] Make P_LoadMapData() a return a boolean as well as
 P_LoadMapFromFile(); if they fail to load, they return false, and thus
 P_SetupLevel() will also return false. TextmapCount() also now returns false
 if brackets are left open inside a textmap.

---
 src/p_setup.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 73c3b44f6..f9ca4dcf0 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1261,7 +1261,7 @@ static boolean TextmapCount(UINT8 *data, size_t size)
 	if (!fastcmp(tkn, "namespace"))
 	{
 		Z_Free(tkn);
-		CONS_Alert(CONS_WARNING, "No namespace at beginning of lump!\n");
+		CONS_Alert(CONS_ERROR, "No namespace at beginning of lump!\n");
 		return false;
 	}
 	Z_Free(tkn);
@@ -1302,6 +1302,13 @@ static boolean TextmapCount(UINT8 *data, size_t size)
 	}
 
 	Z_Free(tkn);
+
+	if (brackets)
+	{
+		CONS_Alert(CONS_ERROR, "Unclosed brackets detected in textmap lump.\n");
+		return false;
+	}
+
 	return true;
 }
 
@@ -1594,14 +1601,17 @@ static void P_LoadTextmap (void)
 	}
 }
 
-static void P_LoadMapData(const virtres_t *virt)
+static boolean P_LoadMapData(const virtres_t *virt)
 {
 	virtlump_t *virtvertexes = NULL, *virtsectors = NULL, *virtsidedefs = NULL, *virtlinedefs = NULL, *virtthings = NULL;
 	virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
 
 	// Count map data.
 	if (textmap) // Count how many entries for each type we got in textmap.
-		TextmapCount(textmap->data, textmap->size);
+	{
+		if (!TextmapCount(textmap->data, textmap->size))
+			return false;
+	}
 	else
 	{
 		virtthings   = vres_Find(virt, "THINGS");
@@ -1684,6 +1694,8 @@ static void P_LoadMapData(const virtres_t *virt)
 	memcpy(spawnsectors, sectors, numsectors * sizeof (*sectors));
 	memcpy(spawnlines, lines, numlines * sizeof (*lines));
 	memcpy(spawnsides, sides, numsides * sizeof (*sides));
+
+	return true;
 }
 
 static void P_InitializeSubsector(subsector_t *ss)
@@ -2685,11 +2697,12 @@ static void P_MakeMapMD5(virtres_t *virt, void *dest)
 	M_Memcpy(dest, &resmd5, 16);
 }
 
-static void P_LoadMapFromFile(void)
+static boolean P_LoadMapFromFile(void)
 {
 	virtres_t *virt = vres_GetMap(lastloadedmaplumpnum);
 
-	P_LoadMapData(virt);
+	if (!P_LoadMapData(virt))
+		return false;
 	P_LoadMapBSP(virt);
 	P_LoadMapLUT(virt);
 
@@ -2701,6 +2714,7 @@ static void P_LoadMapFromFile(void)
 	P_MakeMapMD5(virt, &mapmd5);
 
 	vres_Free(virt);
+	return true;
 }
 
 //
@@ -3549,8 +3563,8 @@ boolean P_LoadLevel(boolean fromnetsave)
 
 	P_MapStart();
 
-	if (lastloadedmaplumpnum)
-		P_LoadMapFromFile();
+	if (!P_LoadMapFromFile())
+		return false;
 
 	// init gravity, tag lists,
 	// anything that P_ResetDynamicSlopes/P_LoadThings needs to know

From 72bb67320969b17d116c18392a2781751d19af58 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Mon, 30 Dec 2019 16:28:22 +0100
Subject: [PATCH 13/37] Some minor refactoring of textmap loading code

---
 src/p_setup.c | 54 +++++++++++++++------------------------------------
 1 file changed, 16 insertions(+), 38 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 78792f306..4d4eaff6d 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1314,12 +1314,7 @@ static boolean TextmapCount(UINT8 *data, size_t size)
 
 static char* dat;
 
-/** Auxiliary function for TextmapParse.
-  *
-  * \param Vertex number.
-  * \param Parameter string.
-  */
-static void TextmapVertex(UINT32 i, char *param)
+static void ParseTextmapVertexParameter(UINT32 i, char *param)
 {
 	if (fastcmp(param, "x"))
 		vertexes[i].x = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
@@ -1327,12 +1322,7 @@ static void TextmapVertex(UINT32 i, char *param)
 		vertexes[i].y = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
 }
 
-/** Auxiliary function for TextmapParse.
-  *
-  * \param Sector number.
-  * \param Parameter string.
-  */
-static void TextmapSector(UINT32 i, char *param)
+static void ParseTextmapSectorParameter(UINT32 i, char *param)
 {
 	if (fastcmp(param, "heightfloor"))
 		sectors[i].floorheight = atol(dat = M_GetToken(NULL)) << FRACBITS;
@@ -1350,12 +1340,7 @@ static void TextmapSector(UINT32 i, char *param)
 		sectors[i].tag = atol(dat = M_GetToken(NULL));
 }
 
-/** Auxiliary function for TextmapParse.
-  *
-  * \param Side number.
-  * \param Parameter string.
-  */
-static void TextmapSide(UINT32 i, char *param)
+static void ParseTextmapSidedefParameter(UINT32 i, char *param)
 {
 	if (fastcmp(param, "offsetx"))
 		sides[i].textureoffset = atol(dat = M_GetToken(NULL))<<FRACBITS;
@@ -1373,12 +1358,7 @@ static void TextmapSide(UINT32 i, char *param)
 		sides[i].repeatcnt = atol(dat = M_GetToken(NULL));
 }
 
-/** Auxiliary function for TextmapParse.
-  *
-  * \param Line number.
-  * \param Parameter string.
-  */
-static void TextmapLine(UINT32 i, char *param)
+static void ParseTextmapLinedefParameter(UINT32 i, char *param)
 {
 	if (fastcmp(param, "id"))
 		lines[i].tag = atol(dat = M_GetToken(NULL));
@@ -1428,9 +1408,7 @@ static void TextmapLine(UINT32 i, char *param)
 		lines[i].flags |= ML_TFERLINE;
 }
 
-/** Auxiliary function for TextmapParse.
-  */
-static void TextmapThing(UINT32 i, char *param)
+static void ParseTextmapThingParameter(UINT32 i, char *param)
 {
 	if (fastcmp(param, "x"))
 		mapthings[i].x = atol(dat = M_GetToken(NULL));
@@ -1445,7 +1423,7 @@ static void TextmapThing(UINT32 i, char *param)
 
 	// Flags
 	else if (fastcmp(param, "extra") && fastcmp("true", dat = M_GetToken(NULL)))
-		mapthings[i].options |= 1;
+		mapthings[i].options |= MTF_EXTRA;
 	else if (fastcmp(param, "flip") && fastcmp("true", dat = M_GetToken(NULL)))
 		mapthings[i].options |= MTF_OBJECTFLIP;
 	else if (fastcmp(param, "special") && fastcmp("true", dat = M_GetToken(NULL)))
@@ -1488,6 +1466,8 @@ static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char
 	Z_Free(tkn);
 }
 
+#define MAXFLATSIZE (2048<<FRACBITS)
+
 /** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
  */
 static void TextmapFixFlatOffsets(sector_t *sec)
@@ -1498,10 +1478,8 @@ static void TextmapFixFlatOffsets(sector_t *sec)
 		fixed_t ps = FINESINE  (sec->floorpic_angle>>ANGLETOFINESHIFT);
 		fixed_t xoffs = sec->floor_xoffs;
 		fixed_t yoffs = sec->floor_yoffs;
-		#define MAXFLATSIZE (2048<<FRACBITS)
 		sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
 		sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
-		#undef MAXFLATSIZE
 	}
 
 	if (sec->ceilingpic_angle)
@@ -1510,13 +1488,13 @@ static void TextmapFixFlatOffsets(sector_t *sec)
 		fixed_t ps = FINESINE  (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
 		fixed_t xoffs = sec->ceiling_xoffs;
 		fixed_t yoffs = sec->ceiling_yoffs;
-		#define MAXFLATSIZE (2048<<FRACBITS)
 		sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
 		sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
-		#undef MAXFLATSIZE
 	}
 }
 
+#undef MAXFLATSIZE
+
 /** Loads the textmap data, after obtaining the elements count and allocating their respective space.
   */
 static void P_LoadTextmap(void)
@@ -1532,7 +1510,7 @@ static void P_LoadTextmap(void)
 	CONS_Alert(CONS_NOTICE, "UDMF support is still a work-in-progress; its specs and features are prone to change until it is fully implemented.\n");
 
 	/// Given the UDMF specs, some fields are given a default value.
-	/// If an element's field has a default value set, it is ommited
+	/// If an element's field has a default value set, it is omitted
 	/// from the textmap, and therefore we have to account for it by
 	/// preemptively setting that value beforehand.
 
@@ -1541,7 +1519,7 @@ static void P_LoadTextmap(void)
 		// Defaults.
 		vt->z = 0;
 
-		TextmapParse(vertexesPos[i], i, TextmapVertex);
+		TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter);
 	}
 
 	for (i = 0, sc = sectors; i < numsectors; i++, sc++)
@@ -1561,7 +1539,7 @@ static void P_LoadTextmap(void)
 		sc->floor_xoffs = sc->floor_yoffs = sc->ceiling_xoffs = sc->ceiling_yoffs = 0;
 		sc->floorpic_angle = sc->ceilingpic_angle = 0;
 
-		TextmapParse(sectorsPos[i], i, TextmapSector);
+		TextmapParse(sectorsPos[i], i, ParseTextmapSectorParameter);
 
 		P_InitializeSector(sc);
 		TextmapFixFlatOffsets(sc);
@@ -1574,7 +1552,7 @@ static void P_LoadTextmap(void)
 		ld->special = 0;
 		ld->sidenum[1] = 0xffff;
 
-		TextmapParse(linesPos[i], i, TextmapLine);
+		TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
 
 		P_InitializeLinedef(ld);
 	}
@@ -1590,7 +1568,7 @@ static void P_LoadTextmap(void)
 		sd->bottomtexture = R_TextureNumForName("-");
 		sd->repeatcnt = 0;
 
-		TextmapParse(sidesPos[i], i, TextmapSide);
+		TextmapParse(sidesPos[i], i, ParseTextmapSidedefParameter);
 	}
 
 	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
@@ -1599,7 +1577,7 @@ static void P_LoadTextmap(void)
 		mt->z = 0;
 		mt->angle = 0;
 
-		TextmapParse(mapthingsPos[i], i, TextmapThing);
+		TextmapParse(mapthingsPos[i], i, ParseTextmapThingParameter);
 	}
 }
 

From 05a97530c18a5f8e87fc47d27187b6a3a3542eb5 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Mon, 30 Dec 2019 17:28:10 +0100
Subject: [PATCH 14/37] Add support for flat offset and rotation fields in UDMF

---
 src/p_setup.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 4d4eaff6d..e4b73570f 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1338,6 +1338,18 @@ static void ParseTextmapSectorParameter(UINT32 i, char *param)
 		sectors[i].special = atol(dat = M_GetToken(NULL));
 	else if (fastcmp(param, "id"))
 		sectors[i].tag = atol(dat = M_GetToken(NULL));
+	else if (fastcmp(param, "xpanningfloor"))
+		sectors[i].floor_xoffs = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+	else if (fastcmp(param, "ypanningfloor"))
+		sectors[i].floor_yoffs = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+	else if (fastcmp(param, "xpanningceiling"))
+		sectors[i].ceiling_xoffs = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+	else if (fastcmp(param, "ypanningceiling"))
+		sectors[i].ceiling_yoffs = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+	else if (fastcmp(param, "rotationfloor"))
+		sectors[i].floorpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL))));
+	else if (fastcmp(param, "rotationceiling"))
+		sectors[i].ceilingpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL))));
 }
 
 static void ParseTextmapSidedefParameter(UINT32 i, char *param)
@@ -1536,12 +1548,8 @@ static void P_LoadTextmap(void)
 		sc->special = 0;
 		sc->tag = 0;
 
-		sc->floor_xoffs = sc->floor_yoffs = sc->ceiling_xoffs = sc->ceiling_yoffs = 0;
-		sc->floorpic_angle = sc->ceilingpic_angle = 0;
-
-		TextmapParse(sectorsPos[i], i, ParseTextmapSectorParameter);
-
 		P_InitializeSector(sc);
+		TextmapParse(sectorsPos[i], i, ParseTextmapSectorParameter);
 		TextmapFixFlatOffsets(sc);
 	}
 

From 013f1f70d91dff68e496e481357dfa1d9f9b426f Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Mon, 30 Dec 2019 21:23:00 +0100
Subject: [PATCH 15/37] -Set defaults for vertex and mapthing fields in textmap
 -Fix P_InitializeSector being called too early (band-aid fix for now, will
 reorganize this properly later)

---
 src/p_setup.c | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index e4b73570f..a48cfe909 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -865,11 +865,6 @@ static void P_InitializeSector(sector_t *ss)
 	ss->lightingdata = NULL;
 	ss->fadecolormapdata = NULL;
 
-	ss->floor_xoffs = ss->floor_yoffs = 0;
-	ss->ceiling_xoffs = ss->ceiling_yoffs = 0;
-
-	ss->floorpic_angle = ss->ceilingpic_angle = 0;
-
 	ss->heightsec = -1;
 	ss->camsec = -1;
 
@@ -945,6 +940,11 @@ static void P_LoadSectors(UINT8 *data)
 		ss->special = SHORT(ms->special);
 		ss->tag = SHORT(ms->tag);
 
+		ss->floor_xoffs = ss->floor_yoffs = 0;
+		ss->ceiling_xoffs = ss->ceiling_yoffs = 0;
+
+		ss->floorpic_angle = ss->ceilingpic_angle = 0;
+
 		P_InitializeSector(ss);
 	}
 }
@@ -1235,6 +1235,8 @@ static void P_LoadThings(UINT8 *data)
 			mt->z = mt->options; // NiGHTS Hoops use the full flags bits to set the height.
 		else
 			mt->z = mt->options >> ZSHIFT;
+
+		mt->mobj = NULL;
 	}
 }
 
@@ -1529,7 +1531,7 @@ static void P_LoadTextmap(void)
 	for (i = 0, vt = vertexes; i < numvertexes; i++, vt++)
 	{
 		// Defaults.
-		vt->z = 0;
+		vt->x = vt->y = vt->z = 0;
 
 		TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter);
 	}
@@ -1548,8 +1550,13 @@ static void P_LoadTextmap(void)
 		sc->special = 0;
 		sc->tag = 0;
 
-		P_InitializeSector(sc);
+		sc->floor_xoffs = sc->floor_yoffs = 0;
+		sc->ceiling_xoffs = sc->ceiling_yoffs = 0;
+
+		sc->floorpic_angle = sc->ceilingpic_angle = 0;
+
 		TextmapParse(sectorsPos[i], i, ParseTextmapSectorParameter);
+		P_InitializeSector(sc);
 		TextmapFixFlatOffsets(sc);
 	}
 
@@ -1582,8 +1589,13 @@ static void P_LoadTextmap(void)
 	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
 	{
 		// Defaults.
-		mt->z = 0;
+		mt->x = mt->y = 0;
 		mt->angle = 0;
+		mt->type = 0;
+		mt->options = 0;
+		mt->z = 0;
+		mt->extrainfo = 0;
+		mt->mobj = NULL;
 
 		TextmapParse(mapthingsPos[i], i, ParseTextmapThingParameter);
 	}

From caadf6aa6128c417496aefc358b973c2a49c1361 Mon Sep 17 00:00:00 2001
From: Louis-Antoine <lamr@free.fr>
Date: Wed, 1 Jan 2020 13:29:07 +0100
Subject: [PATCH 16/37] Let the mouse move freely when a menu is open or game
 is paused

That means you can now easily move your mouse out of SRB2's window
and switch between several windows easily by just pressing escape!

Any phase of the game that isn't actual gameplay counts as a menu,
which means you can also move the mouse in cutscenes,
at the title screen, server connection screen,
and even when the chat or console are open.
---
 src/sdl/i_video.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 806516292..d21edb1c6 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -67,6 +67,7 @@
 #include "../s_sound.h"
 #include "../i_joy.h"
 #include "../st_stuff.h"
+#include "../hu_stuff.h"
 #include "../g_game.h"
 #include "../i_video.h"
 #include "../console.h"
@@ -108,6 +109,7 @@ static SDL_bool disable_fullscreen = SDL_FALSE;
 #define USE_FULLSCREEN (disable_fullscreen||!allow_fullscreen)?0:cv_fullscreen.value
 static SDL_bool disable_mouse = SDL_FALSE;
 #define USE_MOUSEINPUT (!disable_mouse && cv_usemouse.value && havefocus)
+#define IGNORE_MOUSE (menuactive || paused || con_destlines || chat_on || gamestate != GS_LEVEL)
 #define MOUSE_MENU false //(!disable_mouse && cv_usemouse.value && menuactive && !USE_FULLSCREEN)
 #define MOUSEBUTTONS_MAX MOUSEBUTTONS
 
@@ -590,7 +592,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
 		}
 		//else firsttimeonmouse = SDL_FALSE;
 
-		if (USE_MOUSEINPUT)
+		if (USE_MOUSEINPUT && !IGNORE_MOUSE)
 			SDLdoGrabMouse();
 	}
 	else if (!mousefocus && !kbfocus)
@@ -639,7 +641,7 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 {
 	if (USE_MOUSEINPUT)
 	{
-		if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window))
+		if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window) || IGNORE_MOUSE)
 		{
 			SDLdoUngrabMouse();
 			return;
@@ -687,7 +689,7 @@ static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type)
 	// this apparently makes a mouse button down event but not a mouse button up event,
 	// resulting in whatever key was pressed down getting "stuck" if we don't ignore it.
 	// -- Monster Iestyn (28/05/18)
-	if (SDL_GetMouseFocus() != window)
+	if (SDL_GetMouseFocus() != window || IGNORE_MOUSE)
 		return;
 
 	/// \todo inputEvent.button.which
@@ -1069,7 +1071,7 @@ void I_StartupMouse(void)
 	}
 	else
 		firsttimeonmouse = SDL_FALSE;
-	if (cv_usemouse.value)
+	if (cv_usemouse.value && !IGNORE_MOUSE)
 		SDLdoGrabMouse();
 	else
 		SDLdoUngrabMouse();
@@ -1702,12 +1704,7 @@ void I_StartupGraphics(void)
 	SDL_RaiseWindow(window);
 
 	if (mousegrabok && !disable_mouse)
-	{
-		SDL_ShowCursor(SDL_DISABLE);
-		SDL_SetRelativeMouseMode(SDL_TRUE);
-		wrapmouseok = SDL_TRUE;
-		SDL_SetWindowGrab(window, SDL_TRUE);
-	}
+		SDLdoGrabMouse();
 
 	graphics_started = true;
 }

From 24d68ba07e862ce4b36633d416b00868c72b6607 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Wed, 1 Jan 2020 13:40:17 +0100
Subject: [PATCH 17/37] P_LoadTextmap: Set defaults for all linedef and sidedef
 fields that UDMF is allowed to set

---
 src/p_setup.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index a48cfe909..e71d6094f 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1563,8 +1563,11 @@ static void P_LoadTextmap(void)
 	for (i = 0, ld = lines; i < numlines; i++, ld++)
 	{
 		// Defaults.
-		ld->tag = 0;
+		ld->v1 = ld->v2 = NULL;
+		ld->flags = 0;
 		ld->special = 0;
+		ld->tag = 0;
+		ld->sidenum[0] = 0xffff;
 		ld->sidenum[1] = 0xffff;
 
 		TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
@@ -1575,12 +1578,12 @@ static void P_LoadTextmap(void)
 	for (i = 0, sd = sides; i < numsides; i++, sd++)
 	{
 		// Defaults.
-		sd->rowoffset = 0;
 		sd->textureoffset = 0;
-
+		sd->rowoffset = 0;
 		sd->toptexture = R_TextureNumForName("-");
 		sd->midtexture = R_TextureNumForName("-");
 		sd->bottomtexture = R_TextureNumForName("-");
+		sd->sector = NULL;
 		sd->repeatcnt = 0;
 
 		TextmapParse(sidesPos[i], i, ParseTextmapSidedefParameter);

From b59532bccaa332182b56087d6a3ca1e2fe1455a2 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Wed, 1 Jan 2020 14:13:24 +0100
Subject: [PATCH 18/37] Setup repeatcnt in P_LoadSidedefs instead of
 P_ProcessLinedefsWithSidedefs, since UDMF can set it directly

---
 src/p_setup.c | 36 ++++++++++++++++++++++--------------
 1 file changed, 22 insertions(+), 14 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index e71d6094f..d00eebfc8 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1014,13 +1014,11 @@ static void P_InitializeLinedef(line_t *ld)
 	{
 		sides[ld->sidenum[0]].special = ld->special;
 		sides[ld->sidenum[0]].line = ld;
-
 	}
 	if (ld->sidenum[1] != 0xffff)
 	{
 		sides[ld->sidenum[1]].special = ld->special;
 		sides[ld->sidenum[1]].line = ld;
-
 	}
 }
 
@@ -1053,10 +1051,30 @@ static void P_LoadSidedefs(UINT8 *data)
 
 	for (i = 0; i < numsides; i++, sd++, msd++)
 	{
+		INT16 textureoffset = SHORT(msd->textureoffset);
 		UINT16 sector_num;
-		boolean isfrontside = !sd->line || sd->line->sidenum[0] == i;
+		boolean isfrontside;
 
-		sd->textureoffset = SHORT(msd->textureoffset)<<FRACBITS;
+		if (!sd->line)
+		{
+			CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1((size_t)(sd - sides)));
+			sd->line = &lines[0];
+		}
+
+		isfrontside = sd->line->sidenum[0] == i;
+
+		// Repeat count for midtexture
+		if (((sd->line->flags & (ML_TWOSIDED|ML_EFFECT5)) == (ML_TWOSIDED|ML_EFFECT5))
+			&& !(sd->special >= 300 && sd->special < 500)) // exempt linedef exec specials
+		{
+			sd->repeatcnt = (INT16)(((unsigned)textureoffset) >> 12);
+			sd->textureoffset = (((unsigned)textureoffset) & 2047) << FRACBITS;
+		}
+		else
+		{
+			sd->repeatcnt = 0;
+			sd->textureoffset = textureoffset << FRACBITS;
+		}
 		sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
 
 		// cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead
@@ -2449,16 +2467,6 @@ static void P_ProcessLinedefsWithSidedefs(void)
 		ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here
 		ld->backsector  = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0;
 
-		// Repeat count for midtexture
-		if ((ld->flags & ML_EFFECT5) && (ld->sidenum[1] != 0xffff)
-			&& !(ld->special >= 300 && ld->special < 500)) // exempt linedef exec specials
-		{
-			sides[ld->sidenum[0]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) >> 12);
-			sides[ld->sidenum[0]].textureoffset = (((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
-			sides[ld->sidenum[1]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[1]].textureoffset >> FRACBITS) >> 12);
-			sides[ld->sidenum[1]].textureoffset = (((unsigned)sides[ld->sidenum[1]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
-		}
-
 		// Compile linedef 'text' from both sidedefs 'text' for appropriate specials.
 		switch(ld->special)
 		{

From fe198b8a324e9cadee99ffcccf69b4102a4c3609 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Wed, 1 Jan 2020 14:27:01 +0100
Subject: [PATCH 19/37] Check if certain mandatory linedef and sidedef fields
 are set, and use fallback values if not

---
 src/p_setup.c | 70 +++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 54 insertions(+), 16 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index d00eebfc8..c915000d9 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1022,6 +1022,26 @@ static void P_InitializeLinedef(line_t *ld)
 	}
 }
 
+static void P_SetLinedefV1(size_t i, UINT16 vertex_num)
+{
+	if (vertex_num >= numvertexes)
+	{
+		CONS_Debug(DBG_SETUP, "P_SetLinedefV1: linedef %s has out-of-range v1 num %u\n", sizeu1(i), vertex_num);
+		vertex_num = 0;
+	}
+	lines[i].v1 = &vertexes[vertex_num];
+}
+
+static void P_SetLinedefV2(size_t i, UINT16 vertex_num)
+{
+	if (vertex_num >= numvertexes)
+	{
+		CONS_Debug(DBG_SETUP, "P_SetLinedefV2: linedef %s has out-of-range v2 num %u\n", sizeu1(i), vertex_num);
+		vertex_num = 0;
+	}
+	lines[i].v2 = &vertexes[vertex_num];
+}
+
 static void P_LoadLinedefs(UINT8 *data)
 {
 	maplinedef_t *mld = (maplinedef_t *)data;
@@ -1033,8 +1053,8 @@ static void P_LoadLinedefs(UINT8 *data)
 		ld->flags = SHORT(mld->flags);
 		ld->special = SHORT(mld->special);
 		ld->tag = SHORT(mld->tag);
-		ld->v1 = &vertexes[SHORT(mld->v1)];
-		ld->v2 = &vertexes[SHORT(mld->v2)];
+		P_SetLinedefV1(i, SHORT(mld->v1));
+		P_SetLinedefV2(i, SHORT(mld->v2));
 
 		ld->sidenum[0] = SHORT(mld->sidenum[0]);
 		ld->sidenum[1] = SHORT(mld->sidenum[1]);
@@ -1043,6 +1063,17 @@ static void P_LoadLinedefs(UINT8 *data)
 	}
 }
 
+static void P_SetSidedefSector(size_t i, UINT16 sector_num)
+{
+	// cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead
+	if (sector_num >= numsectors)
+	{
+		CONS_Debug(DBG_SETUP, "P_SetSidedefSector: sidedef %s has out-of-range sector num %u\n", sizeu1(i), sector_num);
+		sector_num = 0;
+	}
+	sides[i].sector = &sectors[sector_num];
+}
+
 static void P_LoadSidedefs(UINT8 *data)
 {
 	mapsidedef_t *msd = (mapsidedef_t*)data;
@@ -1052,12 +1083,11 @@ static void P_LoadSidedefs(UINT8 *data)
 	for (i = 0; i < numsides; i++, sd++, msd++)
 	{
 		INT16 textureoffset = SHORT(msd->textureoffset);
-		UINT16 sector_num;
 		boolean isfrontside;
 
 		if (!sd->line)
 		{
-			CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1((size_t)(sd - sides)));
+			CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1(i));
 			sd->line = &lines[0];
 		}
 
@@ -1077,14 +1107,7 @@ static void P_LoadSidedefs(UINT8 *data)
 		}
 		sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
 
-		// cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead
-		sector_num = SHORT(msd->sector);
-		if (sector_num >= numsectors)
-		{
-			CONS_Debug(DBG_SETUP, "P_LoadSidedefs: sidedef %s has out-of-range sector num %u\n", sizeu1(i), sector_num);
-			sector_num = 0;
-		}
-		sd->sector = &sectors[sector_num];
+		P_SetSidedefSector(i, SHORT(msd->sector));
 
 		sd->colormap_data = NULL;
 
@@ -1385,7 +1408,7 @@ static void ParseTextmapSidedefParameter(UINT32 i, char *param)
 	else if (fastcmp(param, "texturemiddle"))
 		sides[i].midtexture = R_TextureNumForName(dat = M_GetToken(NULL));
 	else if (fastcmp(param, "sector"))
-		sides[i].sector = &sectors[atol(dat = M_GetToken(NULL))];
+		P_SetSidedefSector(i, atol(dat = M_GetToken(NULL)));
 	else if (fastcmp(param, "repeatcnt"))
 		sides[i].repeatcnt = atol(dat = M_GetToken(NULL));
 }
@@ -1397,9 +1420,9 @@ static void ParseTextmapLinedefParameter(UINT32 i, char *param)
 	else if (fastcmp(param, "special"))
 		lines[i].special = atol(dat = M_GetToken(NULL));
 	else if (fastcmp(param, "v1"))
-		lines[i].v1 = &vertexes[atol(dat = M_GetToken(NULL))];
+		P_SetLinedefV1(i, atol(dat = M_GetToken(NULL)));
 	else if (fastcmp(param, "v2"))
-		lines[i].v2 = &vertexes[atol(dat = M_GetToken(NULL))];
+		P_SetLinedefV2(i, atol(dat = M_GetToken(NULL)));
 	else if (fastcmp(param, "sidefront"))
 		lines[i].sidenum[0] = atol(dat = M_GetToken(NULL));
 	else if (fastcmp(param, "sideback"))
@@ -1589,7 +1612,16 @@ static void P_LoadTextmap(void)
 		ld->sidenum[1] = 0xffff;
 
 		TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
-
+		if (!ld->v1)
+		{
+			CONS_Debug(DBG_SETUP, "P_LoadTextmap: linedef %s has no v1 set; defaulting to 0\n", sizeu1(i));
+			ld->v1 = &vertexes[0];
+		}
+		if (!ld->v2)
+		{
+			CONS_Debug(DBG_SETUP, "P_LoadTextmap: linedef %s has no v2 set; defaulting to 0\n", sizeu1(i));
+			ld->v2 = &vertexes[0];
+		}
 		P_InitializeLinedef(ld);
 	}
 
@@ -1605,6 +1637,12 @@ static void P_LoadTextmap(void)
 		sd->repeatcnt = 0;
 
 		TextmapParse(sidesPos[i], i, ParseTextmapSidedefParameter);
+
+		if (!sd->sector)
+		{
+			CONS_Debug(DBG_SETUP, "P_LoadTextmap: sidedef %s has no sector set; defaulting to 0\n", sizeu1(i));
+			sd->sector = &sectors[0];
+		}
 	}
 
 	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)

From a41c6405591fd6ba8bd3f4081620be321f3dfc06 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Wed, 1 Jan 2020 15:10:41 +0100
Subject: [PATCH 20/37] Move shared parts of sidedef initialization into
 P_InitializeSidedef

---
 src/p_setup.c | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index c915000d9..e2ae29cf7 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1074,6 +1074,19 @@ static void P_SetSidedefSector(size_t i, UINT16 sector_num)
 	sides[i].sector = &sectors[sector_num];
 }
 
+static void P_InitializeSidedef(side_t *sd)
+{
+	if (!sd->line)
+	{
+		CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1(i));
+		sd->line = &lines[0];
+		sd->special = sd->line->special;
+	}
+
+	sd->text = NULL;
+	sd->colormap_data = NULL;
+}
+
 static void P_LoadSidedefs(UINT8 *data)
 {
 	mapsidedef_t *msd = (mapsidedef_t*)data;
@@ -1085,11 +1098,7 @@ static void P_LoadSidedefs(UINT8 *data)
 		INT16 textureoffset = SHORT(msd->textureoffset);
 		boolean isfrontside;
 
-		if (!sd->line)
-		{
-			CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1(i));
-			sd->line = &lines[0];
-		}
+		P_InitializeSidedef(sd);
 
 		isfrontside = sd->line->sidenum[0] == i;
 
@@ -1109,8 +1118,6 @@ static void P_LoadSidedefs(UINT8 *data)
 
 		P_SetSidedefSector(i, SHORT(msd->sector));
 
-		sd->colormap_data = NULL;
-
 		// Special info stored in texture fields!
 		switch (sd->special)
 		{
@@ -1643,6 +1650,7 @@ static void P_LoadTextmap(void)
 			CONS_Debug(DBG_SETUP, "P_LoadTextmap: sidedef %s has no sector set; defaulting to 0\n", sizeu1(i));
 			sd->sector = &sectors[0];
 		}
+		P_InitializeSidedef(sd);
 	}
 
 	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)

From 6724b11c368ced28b65315942de1bd5b6003df98 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Wed, 1 Jan 2020 15:11:39 +0100
Subject: [PATCH 21/37] Whoops

---
 src/p_setup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index e2ae29cf7..f72e25da0 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1078,7 +1078,7 @@ static void P_InitializeSidedef(side_t *sd)
 {
 	if (!sd->line)
 	{
-		CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1(i));
+		CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1((size_t)(sd - sides)));
 		sd->line = &lines[0];
 		sd->special = sd->line->special;
 	}

From 9cda82d896a47179f307c0177525e390494c9219 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Wed, 1 Jan 2020 15:52:59 +0100
Subject: [PATCH 22/37] Rework textmap parser to always read a parameter's
 value, even if it doesn't recognize the parameter

---
 src/p_setup.c | 151 +++++++++++++++++++++++++-------------------------
 1 file changed, 75 insertions(+), 76 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index f72e25da0..dc115ed19 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1362,135 +1362,133 @@ static boolean TextmapCount(UINT8 *data, size_t size)
 	return true;
 }
 
-static char* dat;
-
-static void ParseTextmapVertexParameter(UINT32 i, char *param)
+static void ParseTextmapVertexParameter(UINT32 i, char *param, char *val)
 {
 	if (fastcmp(param, "x"))
-		vertexes[i].x = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+		vertexes[i].x = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "y"))
-		vertexes[i].y = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+		vertexes[i].y = FLOAT_TO_FIXED(atof(val));
 }
 
-static void ParseTextmapSectorParameter(UINT32 i, char *param)
+static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val)
 {
 	if (fastcmp(param, "heightfloor"))
-		sectors[i].floorheight = atol(dat = M_GetToken(NULL)) << FRACBITS;
+		sectors[i].floorheight = atol(val) << FRACBITS;
 	else if (fastcmp(param, "heightceiling"))
-		sectors[i].ceilingheight = atol(dat = M_GetToken(NULL)) << FRACBITS;
+		sectors[i].ceilingheight = atol(val) << FRACBITS;
 	if (fastcmp(param, "texturefloor"))
-		sectors[i].floorpic = P_AddLevelFlat(dat = M_GetToken(NULL), foundflats);
+		sectors[i].floorpic = P_AddLevelFlat(val, foundflats);
 	else if (fastcmp(param, "textureceiling"))
-		sectors[i].ceilingpic = P_AddLevelFlat(dat = M_GetToken(NULL), foundflats);
+		sectors[i].ceilingpic = P_AddLevelFlat(val, foundflats);
 	else if (fastcmp(param, "lightlevel"))
-		sectors[i].lightlevel = atol(dat = M_GetToken(NULL));
+		sectors[i].lightlevel = atol(val);
 	else if (fastcmp(param, "special"))
-		sectors[i].special = atol(dat = M_GetToken(NULL));
+		sectors[i].special = atol(val);
 	else if (fastcmp(param, "id"))
-		sectors[i].tag = atol(dat = M_GetToken(NULL));
+		sectors[i].tag = atol(val);
 	else if (fastcmp(param, "xpanningfloor"))
-		sectors[i].floor_xoffs = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+		sectors[i].floor_xoffs = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "ypanningfloor"))
-		sectors[i].floor_yoffs = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+		sectors[i].floor_yoffs = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "xpanningceiling"))
-		sectors[i].ceiling_xoffs = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+		sectors[i].ceiling_xoffs = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "ypanningceiling"))
-		sectors[i].ceiling_yoffs = FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL)));
+		sectors[i].ceiling_yoffs = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "rotationfloor"))
-		sectors[i].floorpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL))));
+		sectors[i].floorpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
 	else if (fastcmp(param, "rotationceiling"))
-		sectors[i].ceilingpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(dat = M_GetToken(NULL))));
+		sectors[i].ceilingpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
 }
 
-static void ParseTextmapSidedefParameter(UINT32 i, char *param)
+static void ParseTextmapSidedefParameter(UINT32 i, char *param, char *val)
 {
 	if (fastcmp(param, "offsetx"))
-		sides[i].textureoffset = atol(dat = M_GetToken(NULL))<<FRACBITS;
+		sides[i].textureoffset = atol(val)<<FRACBITS;
 	else if (fastcmp(param, "offsety"))
-		sides[i].rowoffset = atol(dat = M_GetToken(NULL))<<FRACBITS;
+		sides[i].rowoffset = atol(val)<<FRACBITS;
 	else if (fastcmp(param, "texturetop"))
-		sides[i].toptexture = R_TextureNumForName(dat = M_GetToken(NULL));
+		sides[i].toptexture = R_TextureNumForName(val);
 	else if (fastcmp(param, "texturebottom"))
-		sides[i].bottomtexture = R_TextureNumForName(dat = M_GetToken(NULL));
+		sides[i].bottomtexture = R_TextureNumForName(val);
 	else if (fastcmp(param, "texturemiddle"))
-		sides[i].midtexture = R_TextureNumForName(dat = M_GetToken(NULL));
+		sides[i].midtexture = R_TextureNumForName(val);
 	else if (fastcmp(param, "sector"))
-		P_SetSidedefSector(i, atol(dat = M_GetToken(NULL)));
+		P_SetSidedefSector(i, atol(val));
 	else if (fastcmp(param, "repeatcnt"))
-		sides[i].repeatcnt = atol(dat = M_GetToken(NULL));
+		sides[i].repeatcnt = atol(val);
 }
 
-static void ParseTextmapLinedefParameter(UINT32 i, char *param)
+static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val)
 {
 	if (fastcmp(param, "id"))
-		lines[i].tag = atol(dat = M_GetToken(NULL));
+		lines[i].tag = atol(val);
 	else if (fastcmp(param, "special"))
-		lines[i].special = atol(dat = M_GetToken(NULL));
+		lines[i].special = atol(val);
 	else if (fastcmp(param, "v1"))
-		P_SetLinedefV1(i, atol(dat = M_GetToken(NULL)));
+		P_SetLinedefV1(i, atol(val));
 	else if (fastcmp(param, "v2"))
-		P_SetLinedefV2(i, atol(dat = M_GetToken(NULL)));
+		P_SetLinedefV2(i, atol(val));
 	else if (fastcmp(param, "sidefront"))
-		lines[i].sidenum[0] = atol(dat = M_GetToken(NULL));
+		lines[i].sidenum[0] = atol(val);
 	else if (fastcmp(param, "sideback"))
-		lines[i].sidenum[1] = atol(dat = M_GetToken(NULL));
+		lines[i].sidenum[1] = atol(val);
 
 	// Flags
-	else if (fastcmp(param, "blocking") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "blocking") && fastcmp("true", val))
 		lines[i].flags |= ML_IMPASSIBLE;
-	else if (fastcmp(param, "blockmonsters") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "blockmonsters") && fastcmp("true", val))
 		lines[i].flags |= ML_BLOCKMONSTERS;
-	else if (fastcmp(param, "twosided") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "twosided") && fastcmp("true", val))
 		lines[i].flags |= ML_TWOSIDED;
-	else if (fastcmp(param, "dontpegtop") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "dontpegtop") && fastcmp("true", val))
 		lines[i].flags |= ML_DONTPEGTOP;
-	else if (fastcmp(param, "dontpegbottom") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "dontpegbottom") && fastcmp("true", val))
 		lines[i].flags |= ML_DONTPEGBOTTOM;
-	else if (fastcmp(param, "skewtd") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "skewtd") && fastcmp("true", val))
 		lines[i].flags |= ML_EFFECT1;
-	else if (fastcmp(param, "noclimb") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "noclimb") && fastcmp("true", val))
 		lines[i].flags |= ML_NOCLIMB;
-	else if (fastcmp(param, "noskew") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "noskew") && fastcmp("true", val))
 		lines[i].flags |= ML_EFFECT2;
-	else if (fastcmp(param, "midpeg") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "midpeg") && fastcmp("true", val))
 		lines[i].flags |= ML_EFFECT3;
-	else if (fastcmp(param, "midsolid") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "midsolid") && fastcmp("true", val))
 		lines[i].flags |= ML_EFFECT4;
-	else if (fastcmp(param, "wrapmidtex") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "wrapmidtex") && fastcmp("true", val))
 		lines[i].flags |= ML_EFFECT5;
-	else if (fastcmp(param, "effect6") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "effect6") && fastcmp("true", val))
 		lines[i].flags |= ML_EFFECT6;
-	else if (fastcmp(param, "nonet") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "nonet") && fastcmp("true", val))
 		lines[i].flags |= ML_NONET;
-	else if (fastcmp(param, "netonly") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "netonly") && fastcmp("true", val))
 		lines[i].flags |= ML_NETONLY;
-	else if (fastcmp(param, "bouncy") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "bouncy") && fastcmp("true", val))
 		lines[i].flags |= ML_BOUNCY;
-	else if (fastcmp(param, "transfer") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "transfer") && fastcmp("true", val))
 		lines[i].flags |= ML_TFERLINE;
 }
 
-static void ParseTextmapThingParameter(UINT32 i, char *param)
+static void ParseTextmapThingParameter(UINT32 i, char *param, char *val)
 {
 	if (fastcmp(param, "x"))
-		mapthings[i].x = atol(dat = M_GetToken(NULL));
+		mapthings[i].x = atol(val);
 	else if (fastcmp(param, "y"))
-		mapthings[i].y = atol(dat = M_GetToken(NULL));
+		mapthings[i].y = atol(val);
 	else if (fastcmp(param, "height"))
-		mapthings[i].z = atol(dat = M_GetToken(NULL));
+		mapthings[i].z = atol(val);
 	else if (fastcmp(param, "angle"))
-		mapthings[i].angle = atol(dat = M_GetToken(NULL));
+		mapthings[i].angle = atol(val);
 	else if (fastcmp(param, "type"))
-		mapthings[i].type = atol(dat = M_GetToken(NULL));
+		mapthings[i].type = atol(val);
 
 	// Flags
-	else if (fastcmp(param, "extra") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "extra") && fastcmp("true", val))
 		mapthings[i].options |= MTF_EXTRA;
-	else if (fastcmp(param, "flip") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "flip") && fastcmp("true", val))
 		mapthings[i].options |= MTF_OBJECTFLIP;
-	else if (fastcmp(param, "special") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "special") && fastcmp("true", val))
 		mapthings[i].options |= MTF_OBJECTSPECIAL;
-	else if (fastcmp(param, "ambush") && fastcmp("true", dat = M_GetToken(NULL)))
+	else if (fastcmp(param, "ambush") && fastcmp("true", val))
 		mapthings[i].options |= MTF_AMBUSH;
 }
 
@@ -1500,32 +1498,33 @@ static void ParseTextmapThingParameter(UINT32 i, char *param)
   * \param Structure number (mapthings, sectors, ...).
   * \param Parser function pointer.
   */
-static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char *))
+static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char *, char *))
 {
-	char *tkn;
+	char *param, *val;
 
 	M_SetTokenPos(dataPos);
-	tkn = M_GetToken(NULL);
-	if (!fastcmp(tkn, "{"))
+	param = M_GetToken(NULL);
+	if (!fastcmp(param, "{"))
 	{
-		Z_Free(tkn);
+		Z_Free(param);
 		CONS_Alert(CONS_WARNING, "Invalid UDMF data capsule!\n");
 		return;
 	}
+	Z_Free(param);
 
-	Z_Free(tkn);
-	tkn = M_GetToken(NULL);
-	while (!fastcmp(tkn, "}"))
+	while (true)
 	{
-		dat = NULL;
-		parser(num, tkn);
-		if (dat)
-			Z_Free(dat);
-
-		Z_Free(tkn);
-		tkn = M_GetToken(NULL);
+		param = M_GetToken(NULL);
+		if (fastcmp(param, "}"))
+		{
+			Z_Free(param);
+			break;
+		}
+		val = M_GetToken(NULL);
+		parser(num, param, val);
+		Z_Free(param);
+		Z_Free(val);
 	}
-	Z_Free(tkn);
 }
 
 #define MAXFLATSIZE (2048<<FRACBITS)

From 4f26a8e73a640195b1f77475c518cd48cbd14d87 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Wed, 1 Jan 2020 16:01:07 +0100
Subject: [PATCH 23/37] Move MAXFLATSIZE define to p_spec.h so p_spec.c doesn't
 have to redefine it

---
 src/p_setup.c | 4 ----
 src/p_spec.c  | 3 ---
 src/p_spec.h  | 3 +++
 3 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index dc115ed19..8b82e8c69 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1527,8 +1527,6 @@ static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char
 	}
 }
 
-#define MAXFLATSIZE (2048<<FRACBITS)
-
 /** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
  */
 static void TextmapFixFlatOffsets(sector_t *sec)
@@ -1554,8 +1552,6 @@ static void TextmapFixFlatOffsets(sector_t *sec)
 	}
 }
 
-#undef MAXFLATSIZE
-
 /** Loads the textmap data, after obtaining the elements count and allocating their respective space.
   */
 static void P_LoadTextmap(void)
diff --git a/src/p_spec.c b/src/p_spec.c
index 4de488654..a97d1f92f 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -52,9 +52,6 @@ mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs
 // Amount (dx, dy) vector linedef is shifted right to get scroll amount
 #define SCROLL_SHIFT 5
 
-// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize.
-#define MAXFLATSIZE (2048<<FRACBITS)
-
 /** Animated texture descriptor
   * This keeps track of an animated texture or an animated flat.
   * \sa P_UpdateSpecials, P_InitPicAnims, animdef_t
diff --git a/src/p_spec.h b/src/p_spec.h
index 630bcb3de..4b64fe05d 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -27,6 +27,9 @@ extern mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs
 //
 #define GETSECSPECIAL(i,j) ((i >> ((j-1)*4))&15)
 
+// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize.
+#define MAXFLATSIZE (2048<<FRACBITS)
+
 // at game start
 void P_InitPicAnims(void);
 

From 2da9c3cf8199ac0ef0206354bbcd84789cf9b00c Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Wed, 1 Jan 2020 23:52:30 +0100
Subject: [PATCH 24/37] P_LoadTextmap: Bail out if certain mandatory fields are
 not set

---
 src/p_setup.c | 28 +++++++++++++++-------------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 8b82e8c69..de6503dca 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1574,9 +1574,15 @@ static void P_LoadTextmap(void)
 	for (i = 0, vt = vertexes; i < numvertexes; i++, vt++)
 	{
 		// Defaults.
-		vt->x = vt->y = vt->z = 0;
+		vt->x = vt->y = INT32_MAX;
+		vt->z = 0;
 
 		TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter);
+
+		if (vt->x == INT32_MAX)
+			I_Error("P_LoadTextmap: vertex %s has no x value set!\n", sizeu1(i));
+		if (vt->y == INT32_MAX)
+			I_Error("P_LoadTextmap: vertex %s has no y value set!\n", sizeu1(i));
 	}
 
 	for (i = 0, sc = sectors; i < numsectors; i++, sc++)
@@ -1614,16 +1620,14 @@ static void P_LoadTextmap(void)
 		ld->sidenum[1] = 0xffff;
 
 		TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
+
 		if (!ld->v1)
-		{
-			CONS_Debug(DBG_SETUP, "P_LoadTextmap: linedef %s has no v1 set; defaulting to 0\n", sizeu1(i));
-			ld->v1 = &vertexes[0];
-		}
+			I_Error("P_LoadTextmap: linedef %s has no v1 value set!\n", sizeu1(i));
 		if (!ld->v2)
-		{
-			CONS_Debug(DBG_SETUP, "P_LoadTextmap: linedef %s has no v2 set; defaulting to 0\n", sizeu1(i));
-			ld->v2 = &vertexes[0];
-		}
+			I_Error("P_LoadTextmap: linedef %s has no v2 value set!\n", sizeu1(i));
+		if (ld->sidenum[0] == 0xffff)
+			I_Error("P_LoadTextmap: linedef %s has no sidefront value set!\n", sizeu1(i));
+
 		P_InitializeLinedef(ld);
 	}
 
@@ -1641,10 +1645,8 @@ static void P_LoadTextmap(void)
 		TextmapParse(sidesPos[i], i, ParseTextmapSidedefParameter);
 
 		if (!sd->sector)
-		{
-			CONS_Debug(DBG_SETUP, "P_LoadTextmap: sidedef %s has no sector set; defaulting to 0\n", sizeu1(i));
-			sd->sector = &sectors[0];
-		}
+			I_Error("P_LoadTextmap: sidedef %s has no sector value set!\n", sizeu1(i));
+
 		P_InitializeSidedef(sd);
 	}
 

From 02acf6222be72d9c594f55792ad6324221e4bc52 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Thu, 2 Jan 2020 00:32:29 +0100
Subject: [PATCH 25/37] P_LoadExtendedSubsectorsAndSegs: Slightly simplify the
 seg vertex reading code

---
 src/p_setup.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index de6503dca..d83b7adaa 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2027,13 +2027,8 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 			for (m = 0; m < subsectors[i].numlines; m++, k++)
 			{
 				UINT16 linenum;
-				UINT32 vert = READUINT32((*data));
 
-				segs[k].v1 = &vertexes[vert];
-				if (m == 0)
-					segs[k + subsectors[i].numlines - 1].v2 = &vertexes[vert];
-				else
-					segs[k - 1].v2 = segs[k].v1;
+				segs[k - 1 + ((m == 0) ? 0 : subsectors[i].numlines)].v2 = segs[k].v1 = &vertexes[READUINT32((*data))];
 
 				(*data) += 4; // partner, can be ignored by software renderer
 				if (nodetype == NT_XGL3)

From 62397a36ec7a618588a8e23dbb94c733ec7f77e4 Mon Sep 17 00:00:00 2001
From: Louis-Antoine <lamr@free.fr>
Date: Thu, 2 Jan 2020 00:38:43 +0100
Subject: [PATCH 26/37] Grab mouse on game startup

---
 src/sdl/i_video.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index d21edb1c6..645b65e84 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -639,11 +639,14 @@ static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type)
 
 static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 {
+	static boolean firstmove = true;
+
 	if (USE_MOUSEINPUT)
 	{
-		if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window) || IGNORE_MOUSE)
+		if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window) || (IGNORE_MOUSE && !firstmove))
 		{
 			SDLdoUngrabMouse();
+			firstmove = false;
 			return;
 		}
 
@@ -657,6 +660,7 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 				mousemovey += -evt.yrel;
 				SDL_SetWindowGrab(window, SDL_TRUE);
 			}
+			firstmove = false;
 			return;
 		}
 
@@ -664,6 +668,7 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 		// of the screen then ignore it.
 		if ((evt.x == realwidth/2) && (evt.y == realheight/2))
 		{
+			firstmove = false;
 			return;
 		}
 
@@ -676,6 +681,8 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 			SDLdoGrabMouse();
 		}
 	}
+
+	firstmove = false;
 }
 
 static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type)

From 5bde4df4393cd586410ad8d08f405dc6459b2776 Mon Sep 17 00:00:00 2001
From: Louis-Antoine <lamr@free.fr>
Date: Thu, 2 Jan 2020 00:45:28 +0100
Subject: [PATCH 27/37] Grab mouse again when closing menu, unpausing, etc

---
 src/android/i_system.c | 2 ++
 src/console.c          | 4 ++++
 src/d_netcmd.c         | 2 ++
 src/dehacked.c         | 2 ++
 src/dummy/i_system.c   | 2 ++
 src/g_game.c           | 1 +
 src/hu_stuff.c         | 4 ++++
 src/i_system.h         | 4 ++++
 src/m_menu.c           | 3 +++
 src/sdl/i_video.c      | 8 ++++++++
 src/win32/win_sys.c    | 2 ++
 11 files changed, 34 insertions(+)

diff --git a/src/android/i_system.c b/src/android/i_system.c
index 58fca7c19..752e9f6c6 100644
--- a/src/android/i_system.c
+++ b/src/android/i_system.c
@@ -245,6 +245,8 @@ void I_GetJoystick2Events(void){}
 
 void I_GetMouseEvents(void){}
 
+void I_UpdateMouseGrab(void){}
+
 char *I_GetEnv(const char *name)
 {
   LOGW("I_GetEnv() called?!");
diff --git a/src/console.c b/src/console.c
index 6549370ee..01d1ddaa2 100644
--- a/src/console.c
+++ b/src/console.c
@@ -592,6 +592,8 @@ void CON_ToggleOff(void)
 	CON_ClearHUD();
 	con_forcepic = 0;
 	con_clipviewtop = -1; // remove console clipping of view
+
+	I_UpdateMouseGrab();
 }
 
 boolean CON_Ready(void)
@@ -616,6 +618,7 @@ void CON_Ticker(void)
 		consoletoggle = false;
 		con_destlines = 0;
 		CON_ClearHUD();
+		I_UpdateMouseGrab();
 	}
 
 	// console key was pushed
@@ -628,6 +631,7 @@ void CON_Ticker(void)
 		{
 			con_destlines = 0;
 			CON_ClearHUD();
+			I_UpdateMouseGrab();
 		}
 		else
 			CON_ChangeHeight();
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 28843c0d7..0cd0ae20a 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -2203,6 +2203,8 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
 		else
 			S_ResumeAudio();
 	}
+
+	I_UpdateMouseGrab();
 }
 
 // Command for stuck characters in netgames, griefing, etc.
diff --git a/src/dehacked.c b/src/dehacked.c
index c9e10c064..78be8b0b3 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -4539,11 +4539,13 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 		if (introchanged)
 		{
 			menuactive = false;
+			I_UpdateMouseGrab();
 			COM_BufAddText("playintro");
 		}
 		else if (titlechanged)
 		{
 			menuactive = false;
+			I_UpdateMouseGrab();
 			COM_BufAddText("exitgame"); // Command_ExitGame_f() but delayed
 		}
 	}
diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c
index a3fe3077c..5c0f7eb99 100644
--- a/src/dummy/i_system.c
+++ b/src/dummy/i_system.c
@@ -150,6 +150,8 @@ void I_GetJoystick2Events(void){}
 
 void I_GetMouseEvents(void){}
 
+void I_UpdateMouseGrab(void){}
+
 char *I_GetEnv(const char *name)
 {
 	(void)name;
diff --git a/src/g_game.c b/src/g_game.c
index e4caa3a36..354d75e82 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1832,6 +1832,7 @@ void G_DoLoadLevel(boolean resetplayer)
 		titlemapinaction = TITLEMAP_OFF;
 
 	G_SetGamestate(GS_LEVEL);
+	I_UpdateMouseGrab();
 
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 07eb5d24e..772d1cd58 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -1173,6 +1173,8 @@ void HU_clearChatChars(void)
 		w_chat[i] = 0; // reset this.
 	chat_on = false;
 	c_input = 0;
+
+	I_UpdateMouseGrab();
 }
 
 #ifndef NONET
@@ -1323,6 +1325,7 @@ boolean HU_Responder(event_t *ev)
 			chat_on = false;
 			c_input = 0; // reset input cursor
 			chat_scrollmedown = true; // you hit enter, so you might wanna autoscroll to see what you just sent. :)
+			I_UpdateMouseGrab();
 		}
 		else if (c == KEY_ESCAPE
 			|| ((c == gamecontrol[gc_talkkey][0] || c == gamecontrol[gc_talkkey][1]
@@ -1331,6 +1334,7 @@ boolean HU_Responder(event_t *ev)
 		{
 			chat_on = false;
 			c_input = 0; // reset input cursor
+			I_UpdateMouseGrab();
 		}
 		else if ((c == KEY_UPARROW || c == KEY_MOUSEWHEELUP) && chat_scroll > 0 && !OLDCHAT) // CHAT SCROLLING YAYS!
 		{
diff --git a/src/i_system.h b/src/i_system.h
index 2532ba0ee..a60b56310 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -288,6 +288,10 @@ void I_GetJoystick2Events(void);
 */
 void I_GetMouseEvents(void);
 
+/**	\brief Checks if the mouse needs to be grabbed
+*/
+void I_UpdateMouseGrab(void);
+
 char *I_GetEnv(const char *name);
 
 INT32 I_PutEnv(char *variable);
diff --git a/src/m_menu.c b/src/m_menu.c
index ae00c8062..1a3c43b75 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2867,6 +2867,7 @@ static void M_GoBack(INT32 choice)
 
 			menuactive = false;
 			wipetypepre = menupres[M_GetYoungestChildMenu()].exitwipe;
+			I_UpdateMouseGrab();
 			D_StartTitle();
 		}
 		else
@@ -3592,6 +3593,8 @@ void M_ClearMenus(boolean callexitmenufunc)
 		currentMenu = &MainDef; // Not like it matters
 	menuactive = false;
 	hidetitlemap = false;
+
+	I_UpdateMouseGrab();
 }
 
 //
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 645b65e84..553ce3676 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -388,6 +388,14 @@ void SDLforceUngrabMouse(void)
 	}
 }
 
+void I_UpdateMouseGrab(void)
+{
+	if (SDL_WasInit(SDL_INIT_VIDEO) == SDL_INIT_VIDEO && window != NULL
+	&& SDL_GetMouseFocus() == window && SDL_GetKeyboardFocus() == window
+	&& !IGNORE_MOUSE)
+		SDLdoGrabMouse();
+}
+
 static void VID_Command_NumModes_f (void)
 {
 	CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), VID_NumModes());
diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c
index c9fdb1c97..42733c309 100644
--- a/src/win32/win_sys.c
+++ b/src/win32/win_sys.c
@@ -1354,6 +1354,8 @@ getBufferedData:
 	}
 }
 
+void I_UpdateMouseGrab(void) {}
+
 // ===========================================================================================
 //                                                                       DIRECT INPUT JOYSTICK
 // ===========================================================================================

From 17636ccc01b82cd4d85dee35a00a474c211b8bde Mon Sep 17 00:00:00 2001
From: Louis-Antoine <lamr@free.fr>
Date: Thu, 2 Jan 2020 00:46:50 +0100
Subject: [PATCH 28/37] Ungrab mouse when watching a record

---
 src/sdl/i_video.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 553ce3676..d6b36d0ed 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -109,7 +109,7 @@ static SDL_bool disable_fullscreen = SDL_FALSE;
 #define USE_FULLSCREEN (disable_fullscreen||!allow_fullscreen)?0:cv_fullscreen.value
 static SDL_bool disable_mouse = SDL_FALSE;
 #define USE_MOUSEINPUT (!disable_mouse && cv_usemouse.value && havefocus)
-#define IGNORE_MOUSE (menuactive || paused || con_destlines || chat_on || gamestate != GS_LEVEL)
+#define IGNORE_MOUSE (menuactive || paused || con_destlines || chat_on || demoplayback || gamestate != GS_LEVEL)
 #define MOUSE_MENU false //(!disable_mouse && cv_usemouse.value && menuactive && !USE_FULLSCREEN)
 #define MOUSEBUTTONS_MAX MOUSEBUTTONS
 

From 3af00ac93e215af45c4a67dbf29b8161f425261b Mon Sep 17 00:00:00 2001
From: Louis-Antoine <lamr@free.fr>
Date: Thu, 2 Jan 2020 00:47:20 +0100
Subject: [PATCH 29/37] Minor code refactoring

---
 src/m_menu.c      | 3 ---
 src/sdl/i_video.c | 7 +------
 2 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 1a3c43b75..5d1bbd07d 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3212,10 +3212,7 @@ boolean M_Responder(event_t *ev)
 
 			case KEY_ESCAPE: // Pop up menu
 				if (chat_on)
-				{
 					HU_clearChatChars();
-					chat_on = false;
-				}
 				else
 					M_StartControlPanel();
 				return true;
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index d6b36d0ed..e1b3781d9 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -380,12 +380,7 @@ static void SDLdoUngrabMouse(void)
 void SDLforceUngrabMouse(void)
 {
 	if (SDL_WasInit(SDL_INIT_VIDEO)==SDL_INIT_VIDEO && window != NULL)
-	{
-		SDL_ShowCursor(SDL_ENABLE);
-		SDL_SetWindowGrab(window, SDL_FALSE);
-		wrapmouseok = SDL_FALSE;
-		SDL_SetRelativeMouseMode(SDL_FALSE);
-	}
+		SDLdoUngrabMouse();
 }
 
 void I_UpdateMouseGrab(void)

From 5ba179ad7c53a82cdb5810c1f292d2a0e00ed2b1 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Thu, 2 Jan 2020 09:51:07 +0100
Subject: [PATCH 30/37] Fix two bugs in extended segs loading, and add some
 error checking while I'm at it

---
 src/p_setup.c | 41 +++++++++++++++++++++++++++++++----------
 1 file changed, 31 insertions(+), 10 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index d83b7adaa..4de5fedc8 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1840,10 +1840,13 @@ static inline float P_SegLengthFloat(seg_t *seg)
 
 static void P_InitializeSeg(seg_t *seg)
 {
-	seg->sidedef = &sides[seg->linedef->sidenum[seg->side]];
+	if (seg->linedef)
+	{
+		seg->sidedef = &sides[seg->linedef->sidenum[seg->side]];
 
-	seg->frontsector = seg->sidedef->sector;
-	seg->backsector = (seg->linedef->flags & ML_TWOSIDED) ? sides[seg->linedef->sidenum[seg->side ^ 1]].sector : NULL;
+		seg->frontsector = seg->sidedef->sector;
+		seg->backsector = (seg->linedef->flags & ML_TWOSIDED) ? sides[seg->linedef->sidenum[seg->side ^ 1]].sector : NULL;
+	}
 
 #ifdef HWRENDER
 	seg->pv1 = seg->pv2 = NULL;
@@ -2026,15 +2029,21 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 		case NT_XGL3:
 			for (m = 0; m < subsectors[i].numlines; m++, k++)
 			{
+				UINT32 vertexnum = READUINT32((*data));
 				UINT16 linenum;
 
-				segs[k - 1 + ((m == 0) ? 0 : subsectors[i].numlines)].v2 = segs[k].v1 = &vertexes[READUINT32((*data))];
+				if (vertexnum >= numvertexes)
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %d in subsector %d has invalid vertex %d!\n", k, m, vertexnum);
 
-				(*data) += 4; // partner, can be ignored by software renderer
+				segs[k - 1 + ((m == 0) ? subsectors[i].numlines : 0)].v2 = segs[k].v1 = &vertexes[vertexnum];
+
+				READUINT32((*data)); // partner, can be ignored by software renderer
 				if (nodetype == NT_XGL3)
-					(*data) += 2; // Line number is 32-bit in XGL3, but we're limited to 16 bits.
+					READUINT16((*data)); // Line number is 32-bit in XGL3, but we're limited to 16 bits.
 
 				linenum = READUINT16((*data));
+				if (linenum != 0xFFFF && linenum >= numlines)
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %d in subsector %d has invalid linedef %d!\n", k, m, linenum);
 				segs[k].glseg = (linenum == 0xFFFF);
 				segs[k].linedef = (linenum == 0xFFFF) ? NULL : &lines[linenum];
 				segs[k].side = READUINT8((*data));
@@ -2044,9 +2053,20 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 		case NT_XNOD:
 			for (m = 0; m < subsectors[i].numlines; m++, k++)
 			{
-				segs[k].v1 = &vertexes[READUINT32((*data))];
-				segs[k].v2 = &vertexes[READUINT32((*data))];
-				segs[k].linedef = &lines[READUINT16((*data))];
+				UINT32 v1num = READUINT32((*data));
+				UINT32 v2num = READUINT32((*data));
+				UINT16 linenum = READUINT16((*data));
+
+				if (v1num >= numvertexes)
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %d in subsector %d has invalid v1 %d!\n", k, m, v1num);
+				if (v2num >= numvertexes)
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %d in subsector %d has invalid v2 %d!\n", k, m, v2num);
+				if (linenum >= numlines)
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %d in subsector %d has invalid linedef %d!\n", k, m, linenum);
+
+				segs[k].v1 = &vertexes[v1num];
+				segs[k].v2 = &vertexes[v2num];
+				segs[k].linedef = &lines[linenum];
 				segs[k].side = READUINT8((*data));
 				segs[k].glseg = false;
 			}
@@ -2063,7 +2083,8 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 		vertex_t *v2 = seg->v2;
 		P_InitializeSeg(seg);
 		seg->angle = R_PointToAngle2(v1->x, v1->y, v2->x, v2->y);
-		seg->offset = FixedHypot(v1->x - seg->linedef->v1->x, v1->y - seg->linedef->v1->y);
+		if (seg->linedef)
+			segs[i].offset = FixedHypot(v1->x - seg->linedef->v1->x, v1->y - seg->linedef->v1->y);
 	}
 
 	return true;

From e0e5e83869e22beeb1dc6abc373d166dc2c1af99 Mon Sep 17 00:00:00 2001
From: Louis-Antoine <lamr@free.fr>
Date: Thu, 2 Jan 2020 20:29:51 +0100
Subject: [PATCH 31/37] Revert "Ungrab mouse when watching a record"

This reverts commit 17636ccc01b82cd4d85dee35a00a474c211b8bde.
---
 src/sdl/i_video.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index e1b3781d9..7123a2d20 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -109,7 +109,7 @@ static SDL_bool disable_fullscreen = SDL_FALSE;
 #define USE_FULLSCREEN (disable_fullscreen||!allow_fullscreen)?0:cv_fullscreen.value
 static SDL_bool disable_mouse = SDL_FALSE;
 #define USE_MOUSEINPUT (!disable_mouse && cv_usemouse.value && havefocus)
-#define IGNORE_MOUSE (menuactive || paused || con_destlines || chat_on || demoplayback || gamestate != GS_LEVEL)
+#define IGNORE_MOUSE (menuactive || paused || con_destlines || chat_on || gamestate != GS_LEVEL)
 #define MOUSE_MENU false //(!disable_mouse && cv_usemouse.value && menuactive && !USE_FULLSCREEN)
 #define MOUSEBUTTONS_MAX MOUSEBUTTONS
 

From dc0c17dbb85ebd06cf0e659ac33cc7ff2add7cc7 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Thu, 2 Jan 2020 22:28:32 +0100
Subject: [PATCH 32/37] P_LoadExtendedSubsectorsAndSegs: Print size_t with %s

---
 src/p_setup.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 4de5fedc8..e3a7e1e58 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2033,7 +2033,7 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 				UINT16 linenum;
 
 				if (vertexnum >= numvertexes)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %d in subsector %d has invalid vertex %d!\n", k, m, vertexnum);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid vertex %d!\n", sizeu1(k), m, vertexnum);
 
 				segs[k - 1 + ((m == 0) ? subsectors[i].numlines : 0)].v2 = segs[k].v1 = &vertexes[vertexnum];
 
@@ -2043,7 +2043,7 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 
 				linenum = READUINT16((*data));
 				if (linenum != 0xFFFF && linenum >= numlines)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %d in subsector %d has invalid linedef %d!\n", k, m, linenum);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
 				segs[k].glseg = (linenum == 0xFFFF);
 				segs[k].linedef = (linenum == 0xFFFF) ? NULL : &lines[linenum];
 				segs[k].side = READUINT8((*data));
@@ -2058,11 +2058,11 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 				UINT16 linenum = READUINT16((*data));
 
 				if (v1num >= numvertexes)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %d in subsector %d has invalid v1 %d!\n", k, m, v1num);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v1 %d!\n", sizeu1(k), m, v1num);
 				if (v2num >= numvertexes)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %d in subsector %d has invalid v2 %d!\n", k, m, v2num);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v2 %d!\n", sizeu1(k), m, v2num);
 				if (linenum >= numlines)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %d in subsector %d has invalid linedef %d!\n", k, m, linenum);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
 
 				segs[k].v1 = &vertexes[v1num];
 				segs[k].v2 = &vertexes[v2num];

From 945f50c2736ee64734dcc94f36ff7dff4085c8e2 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Thu, 2 Jan 2020 14:58:09 -0800
Subject: [PATCH 33/37] Remove Direct Draw from AppVeyor config

---
 appveyor.yml | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/appveyor.yml b/appveyor.yml
index 0edc7ce28..c64e69665 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -55,8 +55,6 @@ cache:
 install:
 - if [%CONFIGURATION%] == [SDL64] ( set "X86_64=1" )
 - if [%CONFIGURATION%] == [SDL64] ( set "CONFIGURATION=SDL" )
-- if [%CONFIGURATION%] == [DD64] ( set "X86_64=1" )
-- if [%CONFIGURATION%] == [DD64] ( set "CONFIGURATION=DD" )
 - if [%X86_64%] == [1] ( set "MINGW_SDK=%MINGW_SDK_64%" )
 - if [%X86_64%] == [1] ( set "CCACHE_CC=%CCACHE_CC_64%" )
 
@@ -75,13 +73,6 @@ install:
 configuration:
 - SDL
 - SDL64
-- DD
-- DD64
-
-matrix:
-  allow_failures:
-    - configuration: DD
-    - configuration: DD64
 
 before_build:
 - set "Path=%MINGW_SDK%\bin;%Path%"

From 49934007d7e904e78a2038d1dc0715f36fffa66b Mon Sep 17 00:00:00 2001
From: Louis-Antoine <lamr@free.fr>
Date: Fri, 3 Jan 2020 00:25:58 +0100
Subject: [PATCH 34/37] Add a "alwaysgrabmouse" console variable

---
 src/sdl/i_video.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 7123a2d20..e6a40327b 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -100,6 +100,7 @@ boolean highcolor = false;
 // synchronize page flipping with screen refresh
 consvar_t cv_vidwait = {"vid_wait", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+static consvar_t cv_alwaysgrabmouse = {"alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 UINT8 graphics_started = 0; // Is used in console.c and screen.c
 
@@ -109,7 +110,7 @@ static SDL_bool disable_fullscreen = SDL_FALSE;
 #define USE_FULLSCREEN (disable_fullscreen||!allow_fullscreen)?0:cv_fullscreen.value
 static SDL_bool disable_mouse = SDL_FALSE;
 #define USE_MOUSEINPUT (!disable_mouse && cv_usemouse.value && havefocus)
-#define IGNORE_MOUSE (menuactive || paused || con_destlines || chat_on || gamestate != GS_LEVEL)
+#define IGNORE_MOUSE (!cv_alwaysgrabmouse.value && (menuactive || paused || con_destlines || chat_on || gamestate != GS_LEVEL))
 #define MOUSE_MENU false //(!disable_mouse && cv_usemouse.value && menuactive && !USE_FULLSCREEN)
 #define MOUSEBUTTONS_MAX MOUSEBUTTONS
 
@@ -1626,6 +1627,7 @@ void I_StartupGraphics(void)
 	COM_AddCommand ("vid_mode", VID_Command_Mode_f);
 	CV_RegisterVar (&cv_vidwait);
 	CV_RegisterVar (&cv_stretch);
+	CV_RegisterVar (&cv_alwaysgrabmouse);
 	disable_mouse = M_CheckParm("-nomouse");
 	disable_fullscreen = M_CheckParm("-win") ? 1 : 0;
 

From 1845266bc835f6d9b82f6ada75a50d4d1c1ad579 Mon Sep 17 00:00:00 2001
From: Louis-Antoine <lamr@free.fr>
Date: Fri, 3 Jan 2020 00:40:49 +0100
Subject: [PATCH 35/37] Do not save netgame-synced console variables

This is a bad thing to do, because if you join a server,
your game will save the host's settings.
---
 src/d_clisrv.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 6520a1aa1..586e3077c 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2985,8 +2985,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 		CL_RemovePlayer(pnum, kickreason);
 }
 
-consvar_t cv_allownewplayer = {"allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL	};
-consvar_t cv_joinnextround = {"joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
+consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL	};
+consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
 static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
 consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}};

From b45ee059e31f01ba20ea2cec89f4a6c41746fab5 Mon Sep 17 00:00:00 2001
From: Louis-Antoine <lamr@free.fr>
Date: Fri, 3 Jan 2020 02:58:23 +0100
Subject: [PATCH 36/37] Fix major issue

---
 src/r_things.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/r_things.c b/src/r_things.c
index a328da03a..2f01ac7f6 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -988,7 +988,7 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis)
 
 //
 // R_SplitSprite
-// runs through a sector's lightlist and
+// runs through a sector's lightlist and Knuckles
 static void R_SplitSprite(vissprite_t *sprite)
 {
 	INT32 i, lightnum, lindex;

From 26bb0b3c67d6e05d4b3ccc54891d10e348bdcc04 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Fri, 3 Jan 2020 21:50:27 +0100
Subject: [PATCH 37/37] Compressing sidedefs can break both special effects and
 netgame syncing, so let's get rid of it

---
 src/p_setup.c | 65 ---------------------------------------------------
 1 file changed, 65 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index dcb2a0ae4..bd1c53104 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2548,69 +2548,6 @@ static void P_ProcessLinedefsWithSidedefs(void)
 	}
 }
 
-static void P_CompressSidedefs(void)
-{
-	side_t *newsides;
-	size_t numnewsides = 0;
-	size_t z;
-	size_t i;
-
-	for (i = 0; i < numsides; i++)
-	{
-		size_t j, k;
-		line_t *ld;
-
-		if (!sides[i].sector)
-			continue;
-
-		for (k = numlines, ld = lines; k--; ld++)
-		{
-			if (ld->sidenum[0] == i)
-				ld->sidenum[0] = (UINT16)numnewsides;
-
-			if (ld->sidenum[1] == i)
-				ld->sidenum[1] = (UINT16)numnewsides;
-		}
-
-		for (j = i + 1; j < numsides; j++)
-		{
-			if (!sides[j].sector)
-				continue;
-
-			if (!memcmp(&sides[i], &sides[j], sizeof(side_t)))
-			{
-				// Find the linedefs that belong to this one
-				for (k = numlines, ld = lines; k--; ld++)
-				{
-					if (ld->sidenum[0] == j)
-						ld->sidenum[0] = (UINT16)numnewsides;
-
-					if (ld->sidenum[1] == j)
-						ld->sidenum[1] = (UINT16)numnewsides;
-				}
-				sides[j].sector = NULL; // Flag for deletion
-			}
-		}
-		numnewsides++;
-	}
-
-	// We're loading crap into this block anyhow, so no point in zeroing it out.
-	newsides = Z_Malloc(numnewsides*sizeof(*newsides), PU_LEVEL, NULL);
-
-	// Copy the sides to their new block of memory.
-	for (i = 0, z = 0; i < numsides; i++)
-	{
-		if (sides[i].sector)
-			M_Memcpy(&newsides[z++], &sides[i], sizeof(side_t));
-	}
-
-	CONS_Debug(DBG_SETUP, "P_CompressSidedefs: Old sides is %s, new sides is %s\n", sizeu1(numsides), sizeu1(numnewsides));
-
-	Z_Free(sides);
-	sides = newsides;
-	numsides = numnewsides;
-}
-
 //
 // P_LinkMapData
 // Builds sector line lists and subsector sector numbers.
@@ -2777,8 +2714,6 @@ static boolean P_LoadMapFromFile(void)
 	P_LoadMapLUT(virt);
 
 	P_ProcessLinedefsWithSidedefs();
-	if (M_CheckParm("-compress"))
-		P_CompressSidedefs();
 	P_LinkMapData();
 
 	P_MakeMapMD5(virt, &mapmd5);