From d542c2440dfd93c853e7c16b2c611f105671c973 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Tue, 18 Sep 2018 15:50:12 -0400
Subject: [PATCH 01/15] musicplus-feature-endoflevel 2.2 -> 2.1 backport

---
 src/dehacked.c   |  2 ++
 src/doomstat.h   |  3 +++
 src/g_game.c     |  2 +-
 src/lua_maplib.c |  4 +++-
 src/p_setup.c    |  3 ++-
 src/p_user.c     | 39 +++++++++++++++++++++++++++++++++++++++
 src/s_sound.h    |  6 ++++++
 src/y_inter.c    | 10 ++++++++--
 8 files changed, 64 insertions(+), 5 deletions(-)

diff --git a/src/dehacked.c b/src/dehacked.c
index e2df11142..002ad8071 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1205,6 +1205,8 @@ static void readlevelheader(MYFILE *f, INT32 num)
 				mapheaderinfo[num-1]->skynum = (INT16)i;
 			else if (fastcmp(word, "INTERSCREEN"))
 				strncpy(mapheaderinfo[num-1]->interscreen, word2, 8);
+			else if (fastcmp(word, "MUSICINTERFADEOUT"))
+				mapheaderinfo[num-1]->musicinterfadeout = (UINT32)get_number(word2);
 			else if (fastcmp(word, "PRECUTSCENENUM"))
 				mapheaderinfo[num-1]->precutscenenum = (UINT8)i;
 			else if (fastcmp(word, "CUTSCENENUM"))
diff --git a/src/doomstat.h b/src/doomstat.h
index d6fd046b4..cbeb4de42 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -246,6 +246,9 @@ typedef struct
 	UINT8 numGradedMares;   ///< Internal. For grade support.
 	nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful.
 
+	// Music stuff.
+	UINT32 musicinterfadeout;  ///< Fade out level music on intermission screen in milliseconds
+
 	// Lua stuff.
 	// (This is not ifdeffed so the map header structure can stay identical, just in case.)
 	UINT8 numCustomOptions;     ///< Internal. For Lua custom value support.
diff --git a/src/g_game.c b/src/g_game.c
index 6be4cf96e..0c53d17ff 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3541,7 +3541,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 	{
 		// Clear a bunch of variables
 		tokenlist = token = sstimer = redscore = bluescore = lastmap = 0;
-		countdown = countdown2 = 0;
+		countdown = countdown2 = exitfadestarted = 0;
 
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index a1d7994bf..b68574a7f 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1211,7 +1211,9 @@ static int mapheaderinfo_get(lua_State *L)
 			if (!header->interscreen[i])
 				break;
 		lua_pushlstring(L, header->interscreen, i);
-	} else if (fastcmp(field,"runsoc"))
+	} else if (fastcmp(field,"musicinterfadeout"))
+		lua_pushinteger(L, header->musicinterfadeout);
+	else if (fastcmp(field,"runsoc"))
 		lua_pushstring(L, header->runsoc);
 	else if (fastcmp(field,"scriptname"))
 		lua_pushstring(L, header->scriptname);
diff --git a/src/p_setup.c b/src/p_setup.c
index 6c6b9153d..424aed5bc 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -227,6 +227,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
 	mapheaderinfo[num]->menuflags = 0;
 	// TODO grades support for delfile (pfft yeah right)
 	P_DeleteGrades(num);
+	mapheaderinfo[num]->musicinterfadeout = 0;
 	// an even further impossibility, delfile custom opts support
 	mapheaderinfo[num]->customopts = NULL;
 	mapheaderinfo[num]->numCustomOptions = 0;
@@ -2189,7 +2190,7 @@ static void P_LevelInitStuff(void)
 			players[i].lives = cv_startinglives.value;
 		}
 
-		players[i].realtime = countdown = countdown2 = 0;
+		players[i].realtime = countdown = countdown2 = exitfadestarted = 0;
 
 		players[i].gotcontinue = false;
 
diff --git a/src/p_user.c b/src/p_user.c
index 03b037fed..1d34ba5f1 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8789,6 +8789,45 @@ void P_PlayerThink(player_t *player)
 	if (player->exiting && countdown2)
 		player->exiting = 5;
 
+	// Same check as below, just at 1 second before
+	// so we can fade music
+	if (!exitfadestarted &&
+		player->exiting <= 1*TICRATE &&
+		(!multiplayer || gametype == GT_COOP ? !mapheaderinfo[gamemap-1]->musicinterfadeout : true) &&
+			// don't fade if we're fading during intermission. follows Y_StartIntermission intertype = int_coop
+		(gametype == GT_RACE || gametype == GT_COMPETITION ? countdown2 == 0 : true) && // don't fade on timeout
+		player->lives > 0 && // don't fade on game over (competition)
+		P_IsLocalPlayer(player))
+	{
+		if (cv_playersforexit.value)
+		{
+			INT32 i, total = 0, exiting = 0;
+
+			for (i = 0; i < MAXPLAYERS; i++)
+			{
+				if (!playeringame[i] || players[i].spectator || players[i].bot)
+					continue;
+				if (players[i].lives <= 0)
+					continue;
+
+				total++;
+				if (players[i].exiting && players[i].exiting < 1*TICRATE+1)
+					exiting++;
+			}
+
+			if (!total || ((4*exiting)/total) >= cv_playersforexit.value)
+			{
+				exitfadestarted = true;
+				S_FadeOutStopMusic(1*MUSICRATE);
+			}
+		}
+		else
+		{
+			exitfadestarted = true;
+			S_FadeOutStopMusic(1*MUSICRATE);
+		}
+	}
+
 	if (player->exiting == 2 || countdown2 == 2)
 	{
 		if (cv_playersforexit.value) // Count to be sure everyone's exited
diff --git a/src/s_sound.h b/src/s_sound.h
index 538707ffb..852ed4c27 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -23,6 +23,12 @@
 // mask used to indicate sound origin is player item pickup
 #define PICKUP_SOUND 0x8000
 
+// Game state stuff
+
+boolean exitfadestarted;
+
+// Sound stuff
+
 extern consvar_t stereoreverse;
 extern consvar_t cv_soundvolume, cv_digmusicvolume, cv_midimusicvolume;
 extern consvar_t cv_numChannels;
diff --git a/src/y_inter.c b/src/y_inter.c
index e7df165bf..1f4b49ddf 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -696,7 +696,10 @@ void Y_Ticker(void)
 		boolean anybonuses = false;
 
 		if (!intertic) // first time only
-			S_ChangeMusicInternal("lclear", false); // don't loop it
+			if (mapheaderinfo[gamemap-1]->musicinterfadeout)
+				S_FadeOutStopMusic(mapheaderinfo[gamemap-1]->musicinterfadeout);
+			else
+				S_ChangeMusicInternal("_clear", false); // don't loop it
 
 		if (intertic < TICRATE) // one second pause before tally begins
 			return;
@@ -757,7 +760,10 @@ void Y_Ticker(void)
 
 		if (!intertic) // first time only
 		{
-			S_ChangeMusicInternal("lclear", false); // don't loop it
+			if (mapheaderinfo[gamemap-1]->musicinterfadeout)
+				S_FadeOutStopMusic(mapheaderinfo[gamemap-1]->musicinterfadeout);
+			else
+				S_ChangeMusicInternal("_clear", false); // don't loop it
 			tallydonetic = 0;
 		}
 

From 1dd47e850b976ece7b01ef22d6573bcadc6bac94 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Tue, 18 Sep 2018 23:27:00 -0400
Subject: [PATCH 02/15] EndOfLevel: Check player->exiting > 0 && <= TICRATE,
 fixes start-of-level fading in 2.1

Braces in y_inter.c
---
 src/p_user.c  | 2 +-
 src/y_inter.c | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/p_user.c b/src/p_user.c
index 1d34ba5f1..ae7bba582 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8792,7 +8792,7 @@ void P_PlayerThink(player_t *player)
 	// Same check as below, just at 1 second before
 	// so we can fade music
 	if (!exitfadestarted &&
-		player->exiting <= 1*TICRATE &&
+		player->exiting > 0 && player->exiting <= 1*TICRATE &&
 		(!multiplayer || gametype == GT_COOP ? !mapheaderinfo[gamemap-1]->musicinterfadeout : true) &&
 			// don't fade if we're fading during intermission. follows Y_StartIntermission intertype = int_coop
 		(gametype == GT_RACE || gametype == GT_COMPETITION ? countdown2 == 0 : true) && // don't fade on timeout
diff --git a/src/y_inter.c b/src/y_inter.c
index 1f4b49ddf..5e9cb7112 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -696,10 +696,12 @@ void Y_Ticker(void)
 		boolean anybonuses = false;
 
 		if (!intertic) // first time only
+		{
 			if (mapheaderinfo[gamemap-1]->musicinterfadeout)
 				S_FadeOutStopMusic(mapheaderinfo[gamemap-1]->musicinterfadeout);
 			else
 				S_ChangeMusicInternal("_clear", false); // don't loop it
+		}
 
 		if (intertic < TICRATE) // one second pause before tally begins
 			return;

From b057b2932df1eede66e45c9eafb09108d4aa20fe Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Tue, 18 Sep 2018 23:28:39 -0400
Subject: [PATCH 03/15] EndOfLevel: 2.1 dehacked setup fix

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

diff --git a/src/p_setup.c b/src/p_setup.c
index 424aed5bc..38182ecb6 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -187,6 +187,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
 	mapheaderinfo[num]->mustrack = 0;
 	DEH_WriteUndoline("MUSICPOS", va("%d", mapheaderinfo[num]->mustrack), UNDO_NONE);
 	mapheaderinfo[num]->muspos = 0;
+	DEH_WriteUndoline("MUSICINTERFADEOUT", va("%d", mapheaderinfo[num]->musicinterfadeout), UNDO_NONE);
+	mapheaderinfo[num]->musicinterfadeout = 0;
 	DEH_WriteUndoline("FORCECHARACTER", va("%d", mapheaderinfo[num]->forcecharacter), UNDO_NONE);
 	mapheaderinfo[num]->forcecharacter[0] = '\0';
 	DEH_WriteUndoline("WEATHER", va("%d", mapheaderinfo[num]->weather), UNDO_NONE);
@@ -227,7 +229,6 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
 	mapheaderinfo[num]->menuflags = 0;
 	// TODO grades support for delfile (pfft yeah right)
 	P_DeleteGrades(num);
-	mapheaderinfo[num]->musicinterfadeout = 0;
 	// an even further impossibility, delfile custom opts support
 	mapheaderinfo[num]->customopts = NULL;
 	mapheaderinfo[num]->numCustomOptions = 0;

From 481c0d7623f77fe405401bfeaa5e645e108c9093 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Tue, 18 Sep 2018 23:32:00 -0400
Subject: [PATCH 04/15] EndOfLevel: 2.1 _clear -> lclear music in y_inter

---
 src/y_inter.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/y_inter.c b/src/y_inter.c
index 5e9cb7112..c20753e83 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -700,7 +700,7 @@ void Y_Ticker(void)
 			if (mapheaderinfo[gamemap-1]->musicinterfadeout)
 				S_FadeOutStopMusic(mapheaderinfo[gamemap-1]->musicinterfadeout);
 			else
-				S_ChangeMusicInternal("_clear", false); // don't loop it
+				S_ChangeMusicInternal("lclear", false); // don't loop it
 		}
 
 		if (intertic < TICRATE) // one second pause before tally begins
@@ -765,7 +765,7 @@ void Y_Ticker(void)
 			if (mapheaderinfo[gamemap-1]->musicinterfadeout)
 				S_FadeOutStopMusic(mapheaderinfo[gamemap-1]->musicinterfadeout);
 			else
-				S_ChangeMusicInternal("_clear", false); // don't loop it
+				S_ChangeMusicInternal("lclear", false); // don't loop it
 			tallydonetic = 0;
 		}
 

From 0186f6784b800c8d1edba5d78e331b9522104bbc Mon Sep 17 00:00:00 2001
From: Jimita the Cat <lazymyuutsu@gmail.com>
Date: Wed, 6 Mar 2019 21:40:38 -0300
Subject: [PATCH 05/15] a

---
 src/p_mobj.c |  2 +-
 src/p_user.c | 11 +----------
 2 files changed, 2 insertions(+), 11 deletions(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index 850ec2987..e2543f553 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -6033,7 +6033,7 @@ void P_RunOverlays(void)
 		{
 			angle_t viewingangle;
 
-			if (players[displayplayer].awayviewtics)
+			if (players[displayplayer].awayviewtics && players[displayplayer].awayviewmobj != NULL && !P_MobjWasRemoved(players[displayplayer].awayviewmobj))
 				viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y);
 			else if (!camera.chase && players[displayplayer].mo)
 				viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y);
diff --git a/src/p_user.c b/src/p_user.c
index 285d36ca9..b0d716cea 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8741,14 +8741,8 @@ void P_PlayerThink(player_t *player)
 	if (player->flashcount)
 		player->flashcount--;
 
-	// Re-fixed by Jimita (11-12-2018)
-	if (player->awayviewtics)
-	{
+	if (player->awayviewtics && player->awayviewtics != -1)
 		player->awayviewtics--;
-		if (!player->awayviewtics)
-			player->awayviewtics = -1;
-		// The timer might've reached zero, but we'll run the remote view camera anyway by setting it to -1.
-	}
 
 	/// \note do this in the cheat code
 	if (player->pflags & PF_NOCLIP)
@@ -9526,9 +9520,6 @@ void P_PlayerAfterThink(player_t *player)
 		}
 	}
 
-	if (player->awayviewtics < 0)
-		player->awayviewtics = 0;
-
 	// spectator invisibility and nogravity.
 	if ((netgame || multiplayer) && player->spectator)
 	{

From b5fc27c5450845f724c394524fc88a918bceb420 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Fri, 15 Mar 2019 01:00:50 -0400
Subject: [PATCH 06/15] Implement MUSICINTERFADEOUT level header

---
 src/dehacked.c   | 2 ++
 src/doomstat.h   | 1 +
 src/lua_maplib.c | 4 +++-
 src/p_setup.c    | 2 ++
 src/y_inter.c    | 8 ++++++--
 5 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/dehacked.c b/src/dehacked.c
index 6978dd16a..a5fddfea2 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1198,6 +1198,8 @@ static void readlevelheader(MYFILE *f, INT32 num)
 				mapheaderinfo[num-1]->mustrack = ((UINT16)i - 1);
 			else if (fastcmp(word, "MUSICPOS"))
 				mapheaderinfo[num-1]->muspos = (UINT32)get_number(word2);
+			else if (fastcmp(word, "MUSICINTERFADEOUT"))
+				mapheaderinfo[num-1]->musicinterfadeout = (UINT32)get_number(word2);
 			else if (fastcmp(word, "MUSICINTER"))
 				deh_strlcpy(mapheaderinfo[num-1]->musintername, word2,
 					sizeof(mapheaderinfo[num-1]->musintername), va("Level header %d: intermission music", num));
diff --git a/src/doomstat.h b/src/doomstat.h
index 716c4d654..82654ba1a 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -248,6 +248,7 @@ typedef struct
 	nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful.
 
 	// Music stuff.
+	UINT32 musicinterfadeout;  ///< Fade out level music on intermission screen in milliseconds
 	char musintername[7];    ///< Intermission screen music.
 
 	// Lua stuff.
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index d77e636b3..2715f807f 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1489,7 +1489,9 @@ static int mapheaderinfo_get(lua_State *L)
 			if (!header->interscreen[i])
 				break;
 		lua_pushlstring(L, header->interscreen, i);
-	} else if (fastcmp(field,"runsoc"))
+	} else if (fastcmp(field,"musicinterfadeout"))
+		lua_pushinteger(L, header->musicinterfadeout);
+	else if (fastcmp(field,"runsoc"))
 		lua_pushstring(L, header->runsoc);
 	else if (fastcmp(field,"scriptname"))
 		lua_pushstring(L, header->scriptname);
diff --git a/src/p_setup.c b/src/p_setup.c
index 4409ec356..e6aa635e5 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -189,6 +189,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
 	mapheaderinfo[num]->mustrack = 0;
 	DEH_WriteUndoline("MUSICPOS", va("%d", mapheaderinfo[num]->muspos), UNDO_NONE);
 	mapheaderinfo[num]->muspos = 0;
+	DEH_WriteUndoline("MUSICINTERFADEOUT", va("%d", mapheaderinfo[num]->musicinterfadeout), UNDO_NONE);
+	mapheaderinfo[num]->musicinterfadeout = 0;
 	DEH_WriteUndoline("MUSICINTER", mapheaderinfo[num]->musintername, UNDO_NONE);
 	mapheaderinfo[num]->musintername[0] = '\0';
 	DEH_WriteUndoline("FORCECHARACTER", va("%d", mapheaderinfo[num]->forcecharacter), UNDO_NONE);
diff --git a/src/y_inter.c b/src/y_inter.c
index e6e5a1d22..ab4c21692 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -697,7 +697,9 @@ void Y_Ticker(void)
 
 		if (!intertic) // first time only
 		{
-			if (mapheaderinfo[gamemap-1]->musintername[0] && S_MusicExists(mapheaderinfo[gamemap-1]->musintername, !midi_disabled, !digital_disabled))
+			if (mapheaderinfo[gamemap-1]->musicinterfadeout)
+				S_FadeOutStopMusic(mapheaderinfo[gamemap-1]->musicinterfadeout);
+			else if (mapheaderinfo[gamemap-1]->musintername[0] && S_MusicExists(mapheaderinfo[gamemap-1]->musintername, !midi_disabled, !digital_disabled))
 				S_ChangeMusicInternal(mapheaderinfo[gamemap-1]->musintername, false); // don't loop it
 			else
 				S_ChangeMusicInternal("lclear", false); // don't loop it
@@ -762,7 +764,9 @@ void Y_Ticker(void)
 
 		if (!intertic) // first time only
 		{
-			if (mapheaderinfo[gamemap-1]->musintername[0] && S_MusicExists(mapheaderinfo[gamemap-1]->musintername, !midi_disabled, !digital_disabled))
+			if (mapheaderinfo[gamemap-1]->musicinterfadeout)
+				S_FadeOutStopMusic(mapheaderinfo[gamemap-1]->musicinterfadeout);
+			else if (mapheaderinfo[gamemap-1]->musintername[0] && S_MusicExists(mapheaderinfo[gamemap-1]->musintername, !midi_disabled, !digital_disabled))
 				S_ChangeMusicInternal(mapheaderinfo[gamemap-1]->musintername, false); // don't loop it
 			else
 				S_ChangeMusicInternal("lclear", false); // don't loop it

From 587a51a95786d982d65dd22f2ea1741cb0a5e519 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Fri, 15 Mar 2019 03:47:30 -0400
Subject: [PATCH 07/15] Fix end-of-level fading for cv_playersforexit

(Code in p_user.c was from 2.2, where cv_playersforexit has different values so that the player exit check works differently)
---
 src/p_user.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/p_user.c b/src/p_user.c
index 97ee04a28..edc4d8a58 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8836,7 +8836,7 @@ void P_PlayerThink(player_t *player)
 	{
 		if (cv_playersforexit.value)
 		{
-			INT32 i, total = 0, exiting = 0;
+			INT32 i;
 
 			for (i = 0; i < MAXPLAYERS; i++)
 			{
@@ -8845,12 +8845,11 @@ void P_PlayerThink(player_t *player)
 				if (players[i].lives <= 0)
 					continue;
 
-				total++;
-				if (players[i].exiting && players[i].exiting < 1*TICRATE+1)
-					exiting++;
+				if (!players[i].exiting || players[i].exiting > 1*TICRATE)
+					break;
 			}
 
-			if (!total || ((4*exiting)/total) >= cv_playersforexit.value)
+			if (i == MAXPLAYERS)
 			{
 				exitfadestarted = true;
 				S_FadeOutStopMusic(1*MUSICRATE);

From f18103a473dbd0f52d5cf293d6a04496547fc2a8 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Fri, 15 Mar 2019 11:01:41 -0400
Subject: [PATCH 08/15] Declare exitfadestarted properly

---
 src/doomstat.h | 1 +
 src/g_game.c   | 1 +
 src/s_sound.h  | 6 ------
 3 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/doomstat.h b/src/doomstat.h
index d9132798f..4511cf297 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -131,6 +131,7 @@ extern UINT8 skincolor_redteam, skincolor_blueteam, skincolor_redring, skincolor
 
 extern tic_t countdowntimer;
 extern boolean countdowntimeup;
+extern boolean exitfadestarted;
 
 typedef struct
 {
diff --git a/src/g_game.c b/src/g_game.c
index cba9c6575..ac814fa64 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -130,6 +130,7 @@ UINT8 skincolor_bluering = SKINCOLOR_STEELBLUE;
 
 tic_t countdowntimer = 0;
 boolean countdowntimeup = false;
+boolean exitfadestarted = false;
 
 cutscene_t *cutscenes[128];
 
diff --git a/src/s_sound.h b/src/s_sound.h
index 3a5188c2f..157b8b1cc 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -23,12 +23,6 @@
 // mask used to indicate sound origin is player item pickup
 #define PICKUP_SOUND 0x8000
 
-// Game state stuff
-
-boolean exitfadestarted;
-
-// Sound stuff
-
 extern consvar_t stereoreverse;
 extern consvar_t cv_soundvolume, cv_digmusicvolume, cv_midimusicvolume;
 extern consvar_t cv_numChannels;

From 45922f80d1e35aadc9da6e20fa32fa78c1044dfd Mon Sep 17 00:00:00 2001
From: Sryder <sryder13@gmail.com>
Date: Sun, 23 Jun 2019 12:26:52 +0100
Subject: [PATCH 09/15] Don't read from a per-map COLORMAP if it is too big.
 Could this be changed to only read the first so many bytes?

---
 src/r_data.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/r_data.c b/src/r_data.c
index a21ba49ae..bf570e3ab 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -1073,6 +1073,15 @@ void R_ReInitColormaps(UINT16 num)
 	lump = W_GetNumForName(colormap);
 	if (lump == LUMPERROR)
 		lump = W_GetNumForName("COLORMAP");
+	else
+	{
+		if (W_LumpLength(lump) > W_LumpLength(W_GetNumForName("COLORMAP")))
+		{
+			CONS_Alert(CONS_WARNING, "%s lump size is too big, using COLORMAP.\n", colormap);
+			lump = W_GetNumForName("COLORMAP");
+		}
+	}
+
 	W_ReadLump(lump, colormaps);
 
 	// Init Boom colormaps.

From bc254d9cf7ca36985481f988af0e8daa4741c7cd Mon Sep 17 00:00:00 2001
From: Sryder <sryder13@gmail.com>
Date: Sun, 23 Jun 2019 13:48:29 +0100
Subject: [PATCH 10/15] Kill Texture SOC feature. As far as I know it's
 basically unused, and the strstr is inherently unsafe because there's no
 guarantee that a patch's contents are NULL terminated.

---
 src/r_data.c | 49 +++++++++++++++++++------------------------------
 1 file changed, 19 insertions(+), 30 deletions(-)

diff --git a/src/r_data.c b/src/r_data.c
index bf570e3ab..c50cbf209 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -484,42 +484,31 @@ void R_LoadTextures(void)
 		{
 			patchlump = W_CacheLumpNumPwad((UINT16)w, texstart + j, PU_CACHE);
 
-			// Then, check the lump directly to see if it's a texture SOC,
-			// and if it is, load it using dehacked instead.
-			if (strstr((const char *)patchlump, "TEXTURE"))
-			{
-				CONS_Alert(CONS_WARNING, "%s is a Texture SOC.\n", W_CheckNameForNumPwad((UINT16)w,texstart+j));
-				Z_Unlock(patchlump);
-				DEH_LoadDehackedLumpPwad((UINT16)w, texstart + j);
-			}
-			else
-			{
-				//CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height);
-				texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
+			//CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height);
+			texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
 
-				// Set texture properties.
-				M_Memcpy(texture->name, W_CheckNameForNumPwad((UINT16)w, texstart + j), sizeof(texture->name));
-				texture->width = SHORT(patchlump->width);
-				texture->height = SHORT(patchlump->height);
-				texture->patchcount = 1;
-				texture->holes = false;
+			// Set texture properties.
+			M_Memcpy(texture->name, W_CheckNameForNumPwad((UINT16)w, texstart + j), sizeof(texture->name));
+			texture->width = SHORT(patchlump->width);
+			texture->height = SHORT(patchlump->height);
+			texture->patchcount = 1;
+			texture->holes = false;
 
-				// Allocate information for the texture's patches.
-				patch = &texture->patches[0];
+			// Allocate information for the texture's patches.
+			patch = &texture->patches[0];
 
-				patch->originx = patch->originy = 0;
-				patch->wad = (UINT16)w;
-				patch->lump = texstart + j;
+			patch->originx = patch->originy = 0;
+			patch->wad = (UINT16)w;
+			patch->lump = texstart + j;
 
-				Z_Unlock(patchlump);
+			Z_Unlock(patchlump);
 
-				k = 1;
-				while (k << 1 <= texture->width)
-					k <<= 1;
+			k = 1;
+			while (k << 1 <= texture->width)
+				k <<= 1;
 
-				texturewidthmask[i] = k - 1;
-				textureheight[i] = texture->height << FRACBITS;
-			}
+			texturewidthmask[i] = k - 1;
+			textureheight[i] = texture->height << FRACBITS;
 		}
 	}
 }

From bb9b1b3b1f0f133feef8395cefa3a9cc3939a6e1 Mon Sep 17 00:00:00 2001
From: Sryder <sryder13@gmail.com>
Date: Sun, 23 Jun 2019 13:49:39 +0100
Subject: [PATCH 11/15] Change COLORMAP lump size check to be exact A lower
 size could technically be valid, but could easily run into strange issues.

---
 src/r_data.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/r_data.c b/src/r_data.c
index c50cbf209..be7a5dc98 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -1064,9 +1064,9 @@ void R_ReInitColormaps(UINT16 num)
 		lump = W_GetNumForName("COLORMAP");
 	else
 	{
-		if (W_LumpLength(lump) > W_LumpLength(W_GetNumForName("COLORMAP")))
+		if (W_LumpLength(lump) != W_LumpLength(W_GetNumForName("COLORMAP")))
 		{
-			CONS_Alert(CONS_WARNING, "%s lump size is too big, using COLORMAP.\n", colormap);
+			CONS_Alert(CONS_WARNING, "%s lump size does not match COLORMAP, using COLORMAP instead.\n", colormap);
 			lump = W_GetNumForName("COLORMAP");
 		}
 	}

From 5f339fc2a922aa00a94027e885c29173e43edac7 Mon Sep 17 00:00:00 2001
From: Sryder <sryder13@gmail.com>
Date: Sun, 23 Jun 2019 14:52:49 +0100
Subject: [PATCH 12/15] Don't overlap strncpy in WAD file load

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

diff --git a/src/w_wad.c b/src/w_wad.c
index 8d96449f1..e18c5a084 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -149,9 +149,15 @@ FILE *W_OpenWadFile(const char **filename, boolean useerrors)
 {
 	FILE *handle;
 
-	strncpy(filenamebuf, *filename, MAX_WADPATH);
-	filenamebuf[MAX_WADPATH - 1] = '\0';
-	*filename = filenamebuf;
+	// Officially, strncpy should not have overlapping buffers, since W_VerifyNMUSlumps is called after this, and it
+	// changes filename to point at filenamebuf, it would technically be doing that. I doubt any issue will occur since
+	// they point to the same location, but it's better to be safe and this is a simple change.
+	if (filenamebuf != *filename)
+	{
+		strncpy(filenamebuf, *filename, MAX_WADPATH);
+		filenamebuf[MAX_WADPATH - 1] = '\0';
+		*filename = filenamebuf;
+	}
 
 	// open wad file
 	if ((handle = fopen(*filename, "rb")) == NULL)

From 8a778a407001c6cf861c87ecfedbb8ed8f09860d Mon Sep 17 00:00:00 2001
From: Sryder <sryder13@gmail.com>
Date: Sun, 23 Jun 2019 15:02:32 +0100
Subject: [PATCH 13/15] Simply truncate the per-map COLORMAP lump instead of
 not reading it at all. Keep the warning though.

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

diff --git a/src/r_data.c b/src/r_data.c
index be7a5dc98..29a9c52bb 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -1054,6 +1054,7 @@ void R_ReInitColormaps(UINT16 num)
 {
 	char colormap[9] = "COLORMAP";
 	lumpnum_t lump;
+	const lumpnum_t basecolormaplump = W_GetNumForName(colormap);
 
 	if (num > 0 && num <= 10000)
 		snprintf(colormap, 8, "CLM%04u", num-1);
@@ -1061,17 +1062,16 @@ void R_ReInitColormaps(UINT16 num)
 	// Load in the light tables, now 64k aligned for smokie...
 	lump = W_GetNumForName(colormap);
 	if (lump == LUMPERROR)
-		lump = W_GetNumForName("COLORMAP");
+		lump = basecolormaplump;
 	else
 	{
-		if (W_LumpLength(lump) != W_LumpLength(W_GetNumForName("COLORMAP")))
+		if (W_LumpLength(lump) != W_LumpLength(basecolormaplump))
 		{
-			CONS_Alert(CONS_WARNING, "%s lump size does not match COLORMAP, using COLORMAP instead.\n", colormap);
-			lump = W_GetNumForName("COLORMAP");
+			CONS_Alert(CONS_WARNING, "%s lump size does not match COLORMAP, results may be unexpected.\n", colormap);
 		}
 	}
 
-	W_ReadLump(lump, colormaps);
+	W_ReadLumpHeader(lump, colormaps, W_LumpLength(basecolormaplump), 0U);
 
 	// Init Boom colormaps.
 	R_ClearColormaps();

From d9ca8b45d316d3573201604ef506bcea8abf055a Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Sun, 3 Mar 2019 22:09:34 +0000
Subject: [PATCH 14/15] Saving work so far, UNTESTED

# Conflicts:
#	src/r_data.c
---
 src/r_data.c | 63 +++++++++++++++++++++++++++++++++++-----------------
 src/w_wad.c  | 16 +++++++++++++
 src/w_wad.h  |  1 +
 3 files changed, 60 insertions(+), 20 deletions(-)

diff --git a/src/r_data.c b/src/r_data.c
index a21ba49ae..f9088a8d5 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -404,16 +404,7 @@ void R_LoadTextures(void)
 	// but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures.
 	for (w = 0, numtextures = 0; w < numwadfiles; w++)
 	{
-		if (wadfiles[w]->type == RET_PK3)
-		{
-			texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0);
-			texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart);
-		}
-		else
-		{
-			texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1;
-			texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
-		}
+		// Count the textures from TEXTURES lumps
 
 		texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
 		while (texturesLumpPos != INT16_MAX)
@@ -422,19 +413,43 @@ void R_LoadTextures(void)
 			texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1);
 		}
 
-		// Add all the textures between TX_START and TX_END
-		if (texstart != INT16_MAX && texend != INT16_MAX)
+		// Count single-patch textures
+
+		if (wadfiles[w]->type == RET_PK3)
+		{
+			texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0);
+			texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart);
+		}
+		else
+		{
+			texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0);
+			texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
+		}
+
+		if (texstart == INT16_MAX || texend == INT16_MAX)
+			continue;
+
+		texstart++; // Do not count the first marker
+
+		// PK3s have subfolders, so we can't just make a simple sum
+		if (wadfiles[w]->type == RET_PK3)
+		{
+			for (j = texstart; j < texend; j++)
+			{
+				if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it
+					numtextures++;
+			}
+		}
+		else // Add all the textures between TX_START and TX_END
 		{
 			numtextures += (UINT32)(texend - texstart);
 		}
-
-		// If no textures found by this point, bomb out
-		if (!numtextures && w == (numwadfiles - 1))
-		{
-			I_Error("No textures detected in any WADs!\n");
-		}
 	}
 
+	// If no textures found by this point, bomb out
+	if (!numtextures)
+		I_Error("No textures detected in any WADs!\n");
+
 	// Allocate memory and initialize to 0 for all the textures we are initialising.
 	// There are actually 5 buffers allocated in one for convenience.
 	textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL);
@@ -469,7 +484,7 @@ void R_LoadTextures(void)
 		}
 		else
 		{
-			texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1;
+			texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0);
 			texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
 			texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
 			if (texturesLumpPos != INT16_MAX)
@@ -479,9 +494,16 @@ void R_LoadTextures(void)
 		if (texstart == INT16_MAX || texend == INT16_MAX)
 			continue;
 
+		texstart++; // Do not count the first marker
+
 		// Work through each lump between the markers in the WAD.
-		for (j = 0; j < (texend - texstart); i++, j++)
+		for (j = 0; j < (texend - texstart); j++)
 		{
+			if (wadfiles[w]->type == RET_PK3)
+			{
+				if (W_IsLumpFolder((UINT16)w, texstart + j)) // Check if lump is a folder
+					continue; // If it is then SKIP IT
+			}
 			patchlump = W_CacheLumpNumPwad((UINT16)w, texstart + j, PU_CACHE);
 
 			// Then, check the lump directly to see if it's a texture SOC,
@@ -520,6 +542,7 @@ void R_LoadTextures(void)
 				texturewidthmask[i] = k - 1;
 				textureheight[i] = texture->height << FRACBITS;
 			}
+			i++;
 		}
 	}
 }
diff --git a/src/w_wad.c b/src/w_wad.c
index 8d96449f1..9c8fccc6e 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1153,6 +1153,22 @@ boolean W_IsLumpWad(lumpnum_t lumpnum)
 	return false; // WADs should never be inside non-PK3s as far as SRB2 is concerned
 }
 
+//
+// W_IsLumpFolder
+// Is the lump a folder? (in a PK3 obviously)
+//
+boolean W_IsLumpFolder(UINT16 wad, UINT16 lump);
+{
+	if (wadfiles[wad]->type == RET_PK3)
+	{
+		const char *name = wadfiles[wad]->lumpinfo[lump]->name2;
+
+		return (name[strlen(name)-1] == '/') // folders end in '/'
+	}
+
+	return false; // non-PK3s don't have folders
+}
+
 #ifdef HAVE_ZLIB
 /* report a zlib or i/o error */
 void zerr(int ret)
diff --git a/src/w_wad.h b/src/w_wad.h
index e2e17740f..87566c3ee 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -154,6 +154,7 @@ size_t W_LumpLengthPwad(UINT16 wad, UINT16 lump);
 size_t W_LumpLength(lumpnum_t lumpnum);
 
 boolean W_IsLumpWad(lumpnum_t lumpnum); // for loading maps from WADs in PK3s
+boolean W_IsLumpFolder(UINT16 wad, UINT16 lump); // for detecting folder "lumps"
 
 #ifdef HAVE_ZLIB
 void zerr(int ret); // zlib error checking

From 826e8e1aaf3d84ef96a8bb76ee640f13a01918bc Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Sun, 23 Jun 2019 17:07:20 +0100
Subject: [PATCH 15/15] Fixed goofups I missed back 3 months ago

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

diff --git a/src/w_wad.c b/src/w_wad.c
index 9c8fccc6e..e06337810 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1157,13 +1157,13 @@ boolean W_IsLumpWad(lumpnum_t lumpnum)
 // W_IsLumpFolder
 // Is the lump a folder? (in a PK3 obviously)
 //
-boolean W_IsLumpFolder(UINT16 wad, UINT16 lump);
+boolean W_IsLumpFolder(UINT16 wad, UINT16 lump)
 {
 	if (wadfiles[wad]->type == RET_PK3)
 	{
-		const char *name = wadfiles[wad]->lumpinfo[lump]->name2;
+		const char *name = wadfiles[wad]->lumpinfo[lump].name2;
 
-		return (name[strlen(name)-1] == '/') // folders end in '/'
+		return (name[strlen(name)-1] == '/'); // folders end in '/'
 	}
 
 	return false; // non-PK3s don't have folders