diff --git a/source/core/mapinfo.cpp b/source/core/mapinfo.cpp
index 1c4918731..daa4c1627 100644
--- a/source/core/mapinfo.cpp
+++ b/source/core/mapinfo.cpp
@@ -34,6 +34,7 @@
 */ 
 
 #include "mapinfo.h"
+#include "raze_music.h"
 
 MapRecord mapList[512];		// Due to how this gets used it needs to be static. EDuke defines 7 episode plus one spare episode with 64 potential levels each and relies on the static array which is freely accessible by scripts.
 MapRecord *currentLevel;	// level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.)
@@ -70,11 +71,11 @@ MapRecord *FindMapByLevelNum(int num)
 
 MapRecord *FindNextMap(MapRecord *thismap)
 {
-	if (thismap->nextLevel != -1) return mapList[thismap->nextlevel];
+	if (thismap->nextLevel != -1) return &mapList[thismap->nextLevel];
 	return FindMapByLevelNum(thismap->levelNumber+1);
 }
 
-bool SetMusicForMap(const char* mapname, const char* music, bool namehack = false)
+bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
 {
 	static const char* specials[] = { "intro", "briefing", "loading" };
 	for (int i = 0; i < 3; i++)
@@ -86,10 +87,10 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack = fals
 		}
 	}
 
-	int index = FindMapByName(mapname);
+	auto index = FindMapByName(mapname);
 
 	// This is for the DEFS parser's MUSIC command which never bothered to check for the real map name.
-	if (index < 0 && namehack)
+	if (index == nullptr && namehack)
 	{
 		int lev, ep;
 		signed char b1, b2;
@@ -102,21 +103,21 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack = fals
 		index = FindMapByLevelNum(ep*100 + lev);
 
 	}
-	if (index >= 0)
+	if (index != nullptr)
 	{
-		mapList[index].music = music;
+		index->music = music;
 		return true;
 	}
 	return false;
 }
 
-MapRecord *AlloocateMap()
+MapRecord *AllocateMap()
 {
 	return &mapList[numUsedSlots++];
 }
 
 
-MapRecord* SetupUserMap(const char* boardfilename)
+MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic)
 {
 	for (unsigned i = 0; i < numUsedSlots; i++)
 	{
@@ -126,11 +127,11 @@ MapRecord* SetupUserMap(const char* boardfilename)
 			return &map;
 		}
 	}
-	auto map = AllocateMap()
+	auto map = AllocateMap();
 	map->name = "";
 	map->SetFileName(boardfilename);
 	map->flags = MI_USERMAP|MI_FORCEEOG;
-	map->music = G_SetupFilenameBasedMusic(boardfilename, !isRR() ? "dethtoll.mid" : nullptr);
+	map->music = G_SetupFilenameBasedMusic(boardfilename, defaultmusic);
 	return map;
 }
 
diff --git a/source/core/mapinfo.h b/source/core/mapinfo.h
index 053748f6f..fddf666ec 100644
--- a/source/core/mapinfo.h
+++ b/source/core/mapinfo.h
@@ -78,6 +78,8 @@ void InitRREndMap();
 MapRecord *FindMapByName(const char *nm);
 MapRecord *FindMapByLevelNum(int num);
 MapRecord *FindNextMap(MapRecord *thismap);
+MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr);
+MapRecord* AllocateMap();
 
 enum
 {
diff --git a/source/core/savegamehelp.cpp b/source/core/savegamehelp.cpp
index 6d036c0c3..17a9114dd 100644
--- a/source/core/savegamehelp.cpp
+++ b/source/core/savegamehelp.cpp
@@ -305,13 +305,14 @@ int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu)
 	}
 
 	int savever;
-	FString engine, gamegrp, mapgrp, title, filename;
+	FString engine, gamegrp, mapgrp, title, filename, label;
 
 	arc("Save Version", savever)
 		("Engine", engine)
 		("Game Resource", gamegrp)
 		("Map Resource", mapgrp)
 		("Title", title)
+		("Nap Label", label)
 		("Map File", filename);
 
 	auto savesig = gi->GetSaveSig();
@@ -324,25 +325,16 @@ int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu)
 		return 0;
 	}
 
-	MapRecord *curLevel = nullptr;
+	MapRecord *curLevel = FindMapByName(label);
 
-	if (strncmp(filename, "file://", 7) != 0)
+	// If the map does not exist, check if it's a user map.
+	if (!curLevel)
 	{
-		for (auto& mr : mapList)
-		{
-			if (mr.fileName.Compare(filename) == 0)
-			{
-				curLevel = &mr;
-			}
-		}
-	}
-	else
-	{
-		curLevel = &userMapRecord;
+		curLevel = AllocateMap();
 		if (!formenu)
 		{
-			userMapRecord.name = "";
-			userMapRecord.SetFileName(filename);
+			curLevel->name = "";
+			curLevel->SetFileName(filename);
 		}
 	}
 	if (!curLevel) return 0;
diff --git a/source/duke3d/src/premap.cpp b/source/duke3d/src/premap.cpp
index 905b9f194..bceac1052 100644
--- a/source/duke3d/src/premap.cpp
+++ b/source/duke3d/src/premap.cpp
@@ -1744,7 +1744,6 @@ int G_EnterLevel(int gameMode)
     auto   &p0 = *g_player[0].ps;
     int16_t playerAngle;
 
-    char levelName[BMAX_PATH];
     NET_75_CHECK++; // a major problem with how STAT_NETALLOC works, is that loadboard loads sprites directly into the arrays and does not take from
                     // STAT_NETALLOC, even though the loaded sprites are very, very likely to be relevant to the netcode.
 
diff --git a/source/duke3d/src/sounds.cpp b/source/duke3d/src/sounds.cpp
index b6476cded..82299ed09 100644
--- a/source/duke3d/src/sounds.cpp
+++ b/source/duke3d/src/sounds.cpp
@@ -30,6 +30,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 BEGIN_EDUKE_NS
 
+extern MapRecord userMapRecord;
 
 class DukeSoundEngine : public SoundEngine
 {
diff --git a/source/games/duke/src/2d_r.cpp b/source/games/duke/src/2d_r.cpp
index 09320505a..aff2a3f71 100644
--- a/source/games/duke/src/2d_r.cpp
+++ b/source/games/duke/src/2d_r.cpp
@@ -600,7 +600,6 @@ void dobonus_r(bool bonusonly, CompletionFunc completion)
 
 class DRRLoadScreen : public DScreenJob
 {
-	std::function<int(void)> callback;
 	std::function<int(void)> callback;
 	MapRecord* rec;
 
diff --git a/source/games/duke/src/actors_r.cpp b/source/games/duke/src/actors_r.cpp
index c784dafb3..36ba591f2 100644
--- a/source/games/duke/src/actors_r.cpp
+++ b/source/games/duke/src/actors_r.cpp
@@ -2139,7 +2139,7 @@ static void rrra_specialstats()
 		{
 			ps[screenpeek].gm = MODE_EOL;
 			ud.eog = 1;
-			ud.nextLevel = FindNextMap(currentLevel);
+			ud.nextLevel = nullptr;
 		}
 	}
 
diff --git a/source/games/duke/src/ccmds.cpp b/source/games/duke/src/ccmds.cpp
index 0e8fac9e6..c084c2991 100644
--- a/source/games/duke/src/ccmds.cpp
+++ b/source/games/duke/src/ccmds.cpp
@@ -43,7 +43,7 @@ bool cheatStuff(cheatseq_t* s);
 bool cheatKeys(cheatseq_t* s);
 bool cheatInventory(cheatseq_t* s);
 
-static void dowarp(int volume, int level)
+static void dowarp(MapRecord *map)
 {
     ud.m_monsters_off = ud.monsters_off = 0;
 
@@ -54,10 +54,10 @@ static void dowarp(int volume, int level)
 
     if (ps[myconnectindex].gm & MODE_GAME)
     {
-        G_NewGame(volume, level, ud.m_player_skill);
+        G_NewGame(map, ud.m_player_skill);
         ps[myconnectindex].gm = MODE_RESTART;
     }
-    else G_NewGame_EnterLevel(volume, level, ud.m_player_skill);
+    else G_NewGame_EnterLevel(map, ud.m_player_skill);
 }
 
 static int ccmd_levelwarp(CCmdFuncPtr parm)
@@ -71,7 +71,13 @@ static int ccmd_levelwarp(CCmdFuncPtr parm)
         Printf(TEXTCOLOR_RED "Invalid level!: E%sL%s\n", parm->parms[0], parm->parms[1]);
         return CCMD_OK;
     }
-    dowarp(e - 1, m - 1);
+    auto map = FindMapByLevelNum(levelnum(e - 1, m - 1));
+    if (!map)
+    {
+        Printf(TEXTCOLOR_RED "Level not found!: E%sL%s\n", parm->parms[0], parm->parms[1]);
+        return CCMD_OK;
+    }
+    dowarp(map);
 
     return CCMD_OK;
 }
@@ -89,36 +95,26 @@ static int ccmd_map(CCmdFuncPtr parm)
         Printf(TEXTCOLOR_RED "map: file \"%s\" not found.\n", mapname.GetChars());
         return CCMD_OK;
     }
-    int volume, level;
 	// Check if the map is already defined.
-    for (int i = 0; i < 512; i++)
+    auto map = FindMapByName(mapname);
+    if (map == nullptr)
     {
-        if (mapList[i].labelName.CompareNoCase(mapname) == 0)
+        // got a user map
+        if (VOLUMEONE)
         {
-           volume = i / MAXLEVELS;
-           level = i % MAXLEVELS;
-           goto foundone;
+            Printf(TEXTCOLOR_RED "Cannot use user maps in shareware.\n");
+            return CCMD_OK;
         }
+        DefaultExtension(mapname, ".map");
+        if (mapname[0] != '/') mapname.Insert(0, "/");
+        map = SetupUserMap(mapname, !isRR() ? "dethtoll.mid" : nullptr);
     }
-	if (VOLUMEONE)
-	{
-		Printf(TEXTCOLOR_RED "Cannot use user maps in shareware.\n");
-		return CCMD_OK;
-	}
-	// Treat as user map if not found in the list of regular maps.
-    boardfilename[0] = '/';
-    boardfilename[1] = 0;
-    volume = 0;
-    level = 7;
-	DefaultExtension(mapname, ".map");
-    strcat(boardfilename, mapname);
-foundone:
     if (numplayers > 1)
     {
         return CCMD_OK;
     }
 
-    dowarp(volume, level);
+    dowarp(map);
     return CCMD_OK;
 }
 
diff --git a/source/games/duke/src/cheats.cpp b/source/games/duke/src/cheats.cpp
index 5c07ef519..9440f7ccf 100644
--- a/source/games/duke/src/cheats.cpp
+++ b/source/games/duke/src/cheats.cpp
@@ -179,8 +179,10 @@ static bool cheatLevel(cheatseq_t *s)
 	levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0') - 1;
 	
 	// Instead of hard coded range checks on volume and level, let's just check if the level is defined.
-	if (mapList[volnume*MAXLEVELS + levnume].fileName.IsNotEmpty())
+	auto map = FindMapByLevelNum(levelnum(volnume, levnume));
+	if (map)
 	{
+		ud.nextLevel = map;
 		FX_StopAllSounds();
 		FX_SetReverb(0);
 		ps[myconnectindex].gm |= MODE_RESTART;
diff --git a/source/games/duke/src/d_menu.cpp b/source/games/duke/src/d_menu.cpp
index afecb8c98..fa7e701e4 100644
--- a/source/games/duke/src/d_menu.cpp
+++ b/source/games/duke/src/d_menu.cpp
@@ -40,6 +40,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
 #include "gstrings.h"
 #include "version.h"
 #include "names.h"
+#include "mapinfo.h"
 #include "../../glbackend/glbackend.h"
 
 
@@ -317,7 +318,11 @@ void GameInterface::StartGame(FNewGameStartup& gs)
 	ud.m_respawn_items = 0;
 	ud.m_respawn_inventory = 0;
 	ud.multimode = 1;
-	G_NewGame_EnterLevel(gs.Episode, gs.Level, ud.m_player_skill);
+	auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level));
+	if (map)
+	{
+		G_NewGame_EnterLevel(map, ud.m_player_skill);
+	}
 
 }
 
diff --git a/source/games/duke/src/funct.h b/source/games/duke/src/funct.h
index 656983f05..65529c92c 100644
--- a/source/games/duke/src/funct.h
+++ b/source/games/duke/src/funct.h
@@ -220,5 +220,6 @@ void prelevel_d(int g);
 void prelevel_r(int g);
 void e4intro(CompletionFunc completion);
 void clearfrags(void);
+int exitlevel();
 
 END_DUKE_NS
diff --git a/source/games/duke/src/game.h b/source/games/duke/src/game.h
index 94f0a4ac1..2404d2969 100644
--- a/source/games/duke/src/game.h
+++ b/source/games/duke/src/game.h
@@ -45,8 +45,6 @@ extern int rtsplaying;
 #ifndef ONLY_USERDEFS
 
 extern char boardfilename[BMAX_PATH];
-#define USERMAPMUSICFAKEVOLUME MAXVOLUMES
-#define USERMAPMUSICFAKELEVEL (MAXLEVELS-1)
 
 extern int32_t g_Shareware;
 extern int32_t cameraclock;
@@ -90,11 +88,11 @@ static inline int32_t calc_smoothratio(ClockTicks totalclk, ClockTicks ototalclk
 }
 
 
-static inline void G_NewGame_EnterLevel(int volume, int level, int skill)
+static inline void G_NewGame_EnterLevel(MapRecord *map, int skill)
 {
-    G_NewGame(volume, level, skill);
+    G_NewGame(map, skill);
 
-    if (G_EnterLevel(MODE_GAME))
+    if (enterlevel(map, MODE_GAME))
         G_BackToMenu();
 }
 
diff --git a/source/games/duke/src/gamedef.cpp b/source/games/duke/src/gamedef.cpp
index 6c067dfda..a8c07dd8f 100644
--- a/source/games/duke/src/gamedef.cpp
+++ b/source/games/duke/src/gamedef.cpp
@@ -42,6 +42,7 @@ into many sub-files.
 #include "menu.h"
 #include "global.h"
 #include "m_argv.h"
+#include "sounds.h"
 
 BEGIN_DUKE_NS
 
@@ -63,6 +64,16 @@ extern int* labelcode;
 
 TArray<int> ScriptCode;
 
+struct TempMusic
+{
+	int levnum;
+	FString music;
+};
+
+// This is for situations where the music gets defined before the map. Since the map records do not exist yet, we need a temporary buffer.
+static TArray<TempMusic> tempMusic;
+
+
 //---------------------------------------------------------------------------
 //
 // synthesize the instruction list
@@ -752,35 +763,40 @@ int parsecommand()
 		popscriptvalue();
 		transnum(); // Volume Number (0/4)
 		k = popscriptvalue() - 1;
+		if (k < 0) specialmusic.Clear();
 
-		if (k == -1) k = MAXVOLUMES;
-
-		if (k >= 0) // if it's background music
+		i = 0;
+		// get the file name...
+		while (keyword() == -1)
 		{
-			i = 0;
-			// get the file name...
-			while (keyword() == -1)
+			while (isaltok(*textptr) == 0)
 			{
-				while (isaltok(*textptr) == 0)
-				{
-					if (*textptr == 0x0a) line_number++;
-					textptr++;
-					if (*textptr == 0) break;
-				}
-				j = 0;
-				parsebuffer.Clear();
-				while (isaltok(*(textptr + j)))
-				{
-					parsebuffer.Push(textptr[j]);
-					j++;
-				}
-				parsebuffer.Push(0);
-				mapList[(MAXLEVELS * k) + i].music = parsebuffer.Data();
-				textptr += j;
-				if (i > MAXLEVELS) break;
-				i++;
+				if (*textptr == 0x0a) line_number++;
+				textptr++;
+				if (*textptr == 0) break;
 			}
+			j = 0;
+			parsebuffer.Clear();
+			while (isaltok(*(textptr + j)))
+			{
+				parsebuffer.Push(textptr[j]);
+				j++;
+			}
+			parsebuffer.Push(0);
+			if (k >= 0)
+			{
+				tempMusic.Reserve(1);
+				tempMusic.Last().levnum = levelnum(k, i);
+				tempMusic.Last().music = parsebuffer.Data();
+				textptr += j;
+			}
+			else
+			{
+				specialmusic.Push(parsebuffer.Data());
+			}
+			i++;
 		}
+
 		return 0;
 	}
 	case concmd_include:
@@ -1395,6 +1411,7 @@ int parsecommand()
 		return 0;
 
 	case concmd_definelevelname:
+	{
 		popscriptvalue();
 		transnum();
 		j = popscriptvalue();
@@ -1410,22 +1427,25 @@ int parsecommand()
 			textptr++, i++;
 		}
 		parsebuffer.Push(0);
-		mapList[j * MAXLEVELS + k].SetFileName(parsebuffer.Data());
+		auto levnum = levelnum(j, k);
+		auto map = FindMapByLevelNum(levnum);
+		if (!map) map = AllocateMap();
+		map->SetFileName(parsebuffer.Data());
 
 		while (*textptr == ' ') textptr++;
 
-		mapList[j * MAXLEVELS + k].parTime =
+		map->parTime =
 			(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) +
 			(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26);
 
 		textptr += 5;
 		while (*textptr == ' ') textptr++;
 
-		mapList[j * MAXLEVELS + k].designerTime =
+		map->designerTime =
 			(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) +
 			(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26);
 
-		mapList[j * MAXLEVELS + k].levelNumber = k + j * 100;
+		map->levelNumber = levnum;
 
 		textptr += 5;
 		while (*textptr == ' ') textptr++;
@@ -1439,9 +1459,9 @@ int parsecommand()
 			textptr++, i++;
 		}
 		parsebuffer.Push(0);
-		mapList[j * MAXLEVELS + k].name = parsebuffer.Data();
+		map->name = parsebuffer.Data();
 		return 0;
-
+	}
 	case concmd_definequote:
 		popscriptvalue();
 		transnum();
@@ -1703,6 +1723,12 @@ void loadcons(const char* filenam)
 	// These can only be retrieved AFTER loading the scripts.
 	InitGameVarPointers();
 	ResetSystemDefaults();
+	for (auto& tm : tempMusic)
+	{
+		auto map = FindMapByLevelNum(tm.levnum);
+		if (map) map->music = tm.music;
+	}
+	tempMusic.Clear();
 
 }
 
diff --git a/source/games/duke/src/gamevar.cpp b/source/games/duke/src/gamevar.cpp
index 44db1e8a8..6d73e446a 100644
--- a/source/games/duke/src/gamevar.cpp
+++ b/source/games/duke/src/gamevar.cpp
@@ -560,6 +560,7 @@ void InitGameVarPointers(void)
 //
 //---------------------------------------------------------------------------
 
+// These are deliberately not stored in accessible variables anymore
 int getmap() { return mapfromlevelnum(currentLevel->levelNumber); }
 int getvol() { return volfromlevelnum(currentLevel->levelNumber); }
 
diff --git a/source/games/duke/src/global.h b/source/games/duke/src/global.h
index 0a18fc851..ac5299b49 100644
--- a/source/games/duke/src/global.h
+++ b/source/games/duke/src/global.h
@@ -40,15 +40,12 @@ BEGIN_DUKE_NS
 
 #define MOVEFIFOSIZ         256
 
-#define MAXLEVELS           64
 #define MAXGAMETYPES        16
 
 enum {
-    MUS_FIRST_SPECIAL = MAXVOLUMES * MAXLEVELS,
-
-    MUS_INTRO = MUS_FIRST_SPECIAL,
-    MUS_BRIEFING = MUS_FIRST_SPECIAL + 1,
-    MUS_LOADING = MUS_FIRST_SPECIAL + 2,
+    MUS_INTRO = 0,
+    MUS_BRIEFING = 1,
+    MUS_LOADING = 2,
 };
 
 
@@ -127,8 +124,7 @@ G_EXTERN int32_t g_earthquakeTime;
 G_EXTERN int32_t g_freezerSelfDamage;
 #define freezerhurtowner g_freezerSelfDamage
 G_EXTERN int32_t g_gameQuit;
-G_EXTERN int32_t g_globalRandom;
-#define global_random g_globalRandom
+G_EXTERN int32_t global_random;
 G_EXTERN int32_t impact_damage;
 extern int32_t labelcnt;
 G_EXTERN int32_t g_maxPlayerHealth;
@@ -220,7 +216,7 @@ G_EXTERN int16_t fakebubba_spawn, mamaspawn_count, banjosound, g_bellTime, BellS
 #define BellTime g_bellTime
 #define word_119BE0 BellSprite
 G_EXTERN uint8_t g_spriteExtra[MAXSPRITES], g_sectorExtra[MAXSECTORS]; // move these back into the base structs!
-G_EXTERN uint8_t enemysizecheat, ufospawnsminion, pistonsound, chickenphase, RRRA_ExitedLevel, RRRA_EndEpisode, fogactive;
+G_EXTERN uint8_t enemysizecheat, ufospawnsminion, pistonsound, chickenphase, RRRA_ExitedLevel, fogactive;
 G_EXTERN int32_t g_cdTrack;
 #define raat607 enemysizecheat // only as a reminder
 #define raat605 chickenphase
@@ -241,7 +237,6 @@ G_EXTERN player_orig po[MAXPLAYERS];
 #pragma pack(pop)
 
 G_EXTERN uint32_t everyothertime;
-G_EXTERN uint32_t g_moveThingsCount;
 G_EXTERN double g_gameUpdateTime;
 G_EXTERN double g_gameUpdateAndDrawTime;
 #define GAMEUPDATEAVGTIMENUMSAMPLES 100
diff --git a/source/games/duke/src/premap.cpp b/source/games/duke/src/premap.cpp
index 1007127b4..fe79f5edf 100644
--- a/source/games/duke/src/premap.cpp
+++ b/source/games/duke/src/premap.cpp
@@ -908,17 +908,6 @@ int enterlevel(MapRecord *mi, int gamemode)
     return 0;
 }
 
-//---------------------------------------------------------------------------
-//
-//
-//
-//---------------------------------------------------------------------------
-
-void setmapfog(int fogtype)
-{
-    GLInterface.SetMapFog(fogtype != 0);
-}
-
 //---------------------------------------------------------------------------
 //
 // Ideally this will become the only place where map progression gets set up.
@@ -964,4 +953,58 @@ bool setnextmap(bool checksecretexit)
     return false;
 }
 
+//---------------------------------------------------------------------------
+//
+//
+//
+//---------------------------------------------------------------------------
+
+int exitlevel(void)
+{
+    bool endofgame = ud.eog || (currentLevel->flags & MI_FORCEEOG) || ud.nextLevel == nullptr;
+    STAT_Update(endofgame);
+    setpal(&ps[myconnectindex]);
+
+    if (ps[myconnectindex].gm & MODE_RESTART)
+    {
+        ud.nextLevel = currentLevel;
+    }
+
+    if (ps[myconnectindex].gm & MODE_EOL)
+    {
+        ready2send = 0;
+        dobonus(0);
+
+        // Clear potentially loaded per-map ART only after the bonus screens.
+        artClearMapArt();
+
+        if (endofgame)
+        {
+            ud.eog = 0;
+            if (ud.multimode < 2)
+            {
+                if (!VOLUMEALL)
+                    doorders([](bool) {});
+                ps[myconnectindex].gm = 0;
+                return 2;
+            }
+            else
+            {
+                ud.nextLevel = FindMapByLevelNum(0);
+                if (!ud.nextLevel) return 2;
+            }
+        }
+    }
+
+    ready2send = 0;
+
+    if (numplayers > 1)
+        ps[myconnectindex].gm = MODE_GAME;
+
+    int res = enterlevel(ud.nextLevel, ps[myconnectindex].gm);
+    ud.nextLevel = nullptr;
+    return res ? 2 : 1;
+}
+
+
 END_DUKE_NS  
\ No newline at end of file
diff --git a/source/games/duke/src/premap.h b/source/games/duke/src/premap.h
index 7b9bb5947..11b937610 100644
--- a/source/games/duke/src/premap.h
+++ b/source/games/duke/src/premap.h
@@ -29,10 +29,8 @@ BEGIN_DUKE_NS
 
 extern int16_t ambientlotag[64];
 extern int16_t ambienthitag[64];
-int G_EnterLevel(MapRecord *mi, int gameMode);
-int G_FindLevelByFile(const char *fileName);
-void G_NewGame(int volumeNum, int levelNum, int skillNum);
-void G_ResetTimers(uint8_t keepgtics);
+int enterlevel(MapRecord *mi, int gameMode);
+void G_NewGame(MapRecord *mi, int skillNum);
 void P_ResetPlayer(int pn);
 void G_ResetInterpolations(void);
 void G_InitRRRASkies(void);
diff --git a/source/games/duke/src/sounds.cpp b/source/games/duke/src/sounds.cpp
index b954deda2..b880d82fb 100644
--- a/source/games/duke/src/sounds.cpp
+++ b/source/games/duke/src/sounds.cpp
@@ -44,35 +44,37 @@ source as it is released.
 
 BEGIN_DUKE_NS
 
+TArray<FString> specialmusic;
+
 class DukeSoundEngine : public SoundEngine
 {
-    // client specific parts of the sound engine go in this class.
-    void CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan* chan) override;
-    TArray<uint8_t> ReadSound(int lumpnum) override;
+	// client specific parts of the sound engine go in this class.
+	void CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan* chan) override;
+	TArray<uint8_t> ReadSound(int lumpnum) override;
 
 public:
-    DukeSoundEngine()
-    {
-        S_Rolloff.RolloffType = ROLLOFF_Doom;   // Seems like Duke uses the same rolloff type as Doom.
-        S_Rolloff.MinDistance = 144;            // was originally 576 which looks like a bug and sounds like crap.
-        S_Rolloff.MaxDistance = 1088;
-    }
+	DukeSoundEngine()
+	{
+		S_Rolloff.RolloffType = ROLLOFF_Doom;   // Seems like Duke uses the same rolloff type as Doom.
+		S_Rolloff.MinDistance = 144;            // was originally 576 which looks like a bug and sounds like crap.
+		S_Rolloff.MaxDistance = 1088;
+	}
 
-    void StopChannel(FSoundChan* chan) override
-    {
-        if (chan && chan->SysChannel != NULL && !(chan->ChanFlags & CHANF_EVICTED) && chan->SourceType == SOURCE_Actor)
-        {
-            chan->Source = NULL;
-            chan->SourceType = SOURCE_Unattached;
-        }
-        SoundEngine::StopChannel(chan);
-    }
+	void StopChannel(FSoundChan* chan) override
+	{
+		if (chan && chan->SysChannel != NULL && !(chan->ChanFlags & CHANF_EVICTED) && chan->SourceType == SOURCE_Actor)
+		{
+			chan->Source = NULL;
+			chan->SourceType = SOURCE_Unattached;
+		}
+		SoundEngine::StopChannel(chan);
+	}
 
 };
 
 void S_InitSound()
 {
-    soundEngine = new DukeSoundEngine;
+	soundEngine = new DukeSoundEngine;
 }
 
 //==========================================================================
@@ -83,8 +85,8 @@ void S_InitSound()
 
 TArray<uint8_t> DukeSoundEngine::ReadSound(int lumpnum)
 {
-    auto wlump = fileSystem.OpenFileReader(lumpnum);
-    return wlump.Read();
+	auto wlump = fileSystem.OpenFileReader(lumpnum);
+	return wlump.Read();
 }
 
 //==========================================================================
@@ -95,7 +97,7 @@ TArray<uint8_t> DukeSoundEngine::ReadSound(int lumpnum)
 
 void S_PauseSounds(bool paused)
 {
-    soundEngine->SetPaused(paused);
+	soundEngine->SetPaused(paused);
 }
 
 //==========================================================================
@@ -106,14 +108,14 @@ void S_PauseSounds(bool paused)
 
 void cacheAllSounds(void)
 {
-    auto& sfx = soundEngine->GetSounds();
-    int i = 0;
-    for(auto &snd : sfx)
-    {
-        soundEngine->CacheSound(&snd);
-        if (((++i)&31) == 0)
-            handleevents();
-    }
+	auto& sfx = soundEngine->GetSounds();
+	int i = 0;
+	for(auto &snd : sfx)
+	{
+		soundEngine->CacheSound(&snd);
+		if (((++i)&31) == 0)
+			handleevents();
+	}
 }
 
 //==========================================================================
@@ -124,23 +126,23 @@ void cacheAllSounds(void)
 
 static inline int S_GetPitch(int num)
 {
-    auto const* snd = soundEngine->GetUserData(num+1);
-    if (!snd) return 0;
-    int const   range = abs(snd[kPitchEnd] - snd[kPitchStart]);
-    return (range == 0) ? snd[kPitchStart] : min(snd[kPitchStart], snd[kPitchEnd]) + rand() % range;
+	auto const* snd = soundEngine->GetUserData(num+1);
+	if (!snd) return 0;
+	int const   range = abs(snd[kPitchEnd] - snd[kPitchStart]);
+	return (range == 0) ? snd[kPitchStart] : min(snd[kPitchStart], snd[kPitchEnd]) + rand() % range;
 }
 
 float S_ConvertPitch(int lpitch)
 {
-    return pow(2, lpitch / 1200.);   // I hope I got this right that ASS uses a linear scale where 1200 is a full octave.
+	return pow(2, lpitch / 1200.);   // I hope I got this right that ASS uses a linear scale where 1200 is a full octave.
 }
 
 int S_GetUserFlags(int num)
 {
-    if (!soundEngine->isValidSoundId(num+1)) return 0;
-    auto const* snd = soundEngine->GetUserData(num + 1);
-    if (!snd) return 0;
-    return snd[kFlags];
+	if (!soundEngine->isValidSoundId(num+1)) return 0;
+	auto const* snd = soundEngine->GetUserData(num + 1);
+	if (!snd) return 0;
+	return snd[kFlags];
 }
 
 //==========================================================================
@@ -151,41 +153,41 @@ int S_GetUserFlags(int num)
 
 int S_DefineSound(unsigned index, const char *filename, int minpitch, int maxpitch, int priority, int type, int distance, float volume)
 {
-    auto& S_sfx = soundEngine->GetSounds();
-    index++;
-    unsigned oldindex = S_sfx.Size();
-    if (index >= S_sfx.Size())
-    {
-        S_sfx.Resize(index + 1);
-        for (; oldindex <= index; oldindex++)
-        {
-            S_sfx[oldindex].Clear();
-        }
-    }
-    auto sfx = &S_sfx[index];
-    bool alreadydefined = !sfx->bTentative;
-    sfx->UserData.Resize(kMaxUserData);
-    auto sndinf = sfx->UserData.Data();
-    sndinf[kFlags] = type & ~SF_ONEINST_INTERNAL;
-    if (sndinf[kFlags] & SF_LOOP)
-        sndinf[kFlags] |= SF_ONEINST_INTERNAL;
+	auto& S_sfx = soundEngine->GetSounds();
+	index++;
+	unsigned oldindex = S_sfx.Size();
+	if (index >= S_sfx.Size())
+	{
+		S_sfx.Resize(index + 1);
+		for (; oldindex <= index; oldindex++)
+		{
+			S_sfx[oldindex].Clear();
+		}
+	}
+	auto sfx = &S_sfx[index];
+	bool alreadydefined = !sfx->bTentative;
+	sfx->UserData.Resize(kMaxUserData);
+	auto sndinf = sfx->UserData.Data();
+	sndinf[kFlags] = type & ~SF_ONEINST_INTERNAL;
+	if (sndinf[kFlags] & SF_LOOP)
+		sndinf[kFlags] |= SF_ONEINST_INTERNAL;
 
-    sfx->lumpnum = S_LookupSound(filename);
-    sndinf[kPitchStart] = clamp(minpitch, INT16_MIN, INT16_MAX);
-    sndinf[kPitchEnd] = clamp(maxpitch, INT16_MIN, INT16_MAX);
-    sndinf[kPriority] = priority & 255;
-    sndinf[kVolAdjust] = clamp(distance, INT16_MIN, INT16_MAX);
-    sfx->Volume = volume;
-    sfx->NearLimit = 6;
-    sfx->bTentative = false;
-    sfx->name = filename;
-    return 0;
+	sfx->lumpnum = S_LookupSound(filename);
+	sndinf[kPitchStart] = clamp(minpitch, INT16_MIN, INT16_MAX);
+	sndinf[kPitchEnd] = clamp(maxpitch, INT16_MIN, INT16_MAX);
+	sndinf[kPriority] = priority & 255;
+	sndinf[kVolAdjust] = clamp(distance, INT16_MIN, INT16_MAX);
+	sfx->Volume = volume;
+	sfx->NearLimit = 6;
+	sfx->bTentative = false;
+	sfx->name = filename;
+	return 0;
 }
 
 
 inline bool S_IsAmbientSFX(int spriteNum)
 {
-    return (sprite[spriteNum].picnum == MUSICANDSFX && sprite[spriteNum].lotag < 999);
+	return (sprite[spriteNum].picnum == MUSICANDSFX && sprite[spriteNum].lotag < 999);
 }
 
 //==========================================================================
@@ -195,56 +197,56 @@ inline bool S_IsAmbientSFX(int spriteNum)
 //==========================================================================
 
 static int GetPositionInfo(int spriteNum, int soundNum, int sectNum,
-                             const vec3_t *cam, const vec3_t *pos, int *distPtr, FVector3 *sndPos)
+							 const vec3_t *cam, const vec3_t *pos, int *distPtr, FVector3 *sndPos)
 {
-    // There's a lot of hackery going on here that could be mapped to rolloff and attenuation parameters.
-    // However, ultimately rolloff would also just reposition the sound source so this can remain as it is.
+	// There's a lot of hackery going on here that could be mapped to rolloff and attenuation parameters.
+	// However, ultimately rolloff would also just reposition the sound source so this can remain as it is.
 
-    auto sp = &sprite[spriteNum];
-    int orgsndist = 0, sndang = 0, sndist = 0, explosion = 0;
-    auto const* snd = soundEngine->GetUserData(soundNum + 1);
-    int userflags = snd ? snd[kFlags] : 0;
-    int dist_adjust = snd ? snd[kVolAdjust] : 0;
+	auto sp = &sprite[spriteNum];
+	int orgsndist = 0, sndang = 0, sndist = 0, explosion = 0;
+	auto const* snd = soundEngine->GetUserData(soundNum + 1);
+	int userflags = snd ? snd[kFlags] : 0;
+	int dist_adjust = snd ? snd[kVolAdjust] : 0;
 
-    if (sp->picnum != TILE_APLAYER || sp->yvel != screenpeek)
-    {
-        orgsndist = sndist = FindDistance3D(cam->x - pos->x, cam->y - pos->y, (cam->z - pos->z));
+	if (sp->picnum != TILE_APLAYER || sp->yvel != screenpeek)
+	{
+		orgsndist = sndist = FindDistance3D(cam->x - pos->x, cam->y - pos->y, (cam->z - pos->z));
 
-        if ((userflags & (SF_GLOBAL | SF_DTAG)) != SF_GLOBAL && sp->picnum == MUSICANDSFX && sp->lotag < 999 && (sector[sp->sectnum].lotag & 0xff) < ST_9_SLIDING_ST_DOOR)
-            sndist = divscale14(sndist, sp->hitag + 1);
-    }
+		if ((userflags & (SF_GLOBAL | SF_DTAG)) != SF_GLOBAL && sp->picnum == MUSICANDSFX && sp->lotag < 999 && (sector[sp->sectnum].lotag & 0xff) < ST_9_SLIDING_ST_DOOR)
+			sndist = divscale14(sndist, sp->hitag + 1);
+	}
 
-    sndist += dist_adjust;
-    if (sndist < 0) sndist = 0;
+	sndist += dist_adjust;
+	if (sndist < 0) sndist = 0;
 
-    if (sectNum > -1 && sndist && sp->picnum != MUSICANDSFX && !cansee(cam->x, cam->y, cam->z - (24 << 8), sectNum, sp->x, sp->y, sp->z - (24 << 8), sp->sectnum))
-        sndist += sndist >> (isRR() ? 2 : 5);
+	if (sectNum > -1 && sndist && sp->picnum != MUSICANDSFX && !cansee(cam->x, cam->y, cam->z - (24 << 8), sectNum, sp->x, sp->y, sp->z - (24 << 8), sp->sectnum))
+		sndist += sndist >> (isRR() ? 2 : 5);
 
-    // Here the sound distance was clamped to a minimum of 144*4. 
-    // It's better to handle rolloff in the backend instead of whacking the sound origin here.
-    // That way the lower end can be made customizable instead of losing all precision right here at the source.
-    if (sndist < 0) sndist = 0;
+	// Here the sound distance was clamped to a minimum of 144*4. 
+	// It's better to handle rolloff in the backend instead of whacking the sound origin here.
+	// That way the lower end can be made customizable instead of losing all precision right here at the source.
+	if (sndist < 0) sndist = 0;
 
-    if (distPtr)
-    {
-        *distPtr = sndist;
-    }
+	if (distPtr)
+	{
+		*distPtr = sndist;
+	}
 
-    if (sndPos)
-    {
-        FVector3 sndorg = GetSoundPos(pos);
-        FVector3 campos = GetSoundPos(cam);
-        // Now calculate the virtual position in sound system coordinates.
-        FVector3 sndvec = sndorg - campos;
-        if (orgsndist > 0)
-        {
-            float scale = float(sndist) / orgsndist;                                                // adjust by what was calculated above;
-            *sndPos = campos + sndvec * scale;
-        }
-        else *sndPos = campos;
-    }
+	if (sndPos)
+	{
+		FVector3 sndorg = GetSoundPos(pos);
+		FVector3 campos = GetSoundPos(cam);
+		// Now calculate the virtual position in sound system coordinates.
+		FVector3 sndvec = sndorg - campos;
+		if (orgsndist > 0)
+		{
+			float scale = float(sndist) / orgsndist;   // adjust by what was calculated above;
+			*sndPos = campos + sndvec * scale;
+		}
+		else *sndPos = campos;
+	}
 
-    return false;
+	return false;
 }
 
 //==========================================================================
@@ -255,19 +257,19 @@ static int GetPositionInfo(int spriteNum, int soundNum, int sectNum,
 
 void S_GetCamera(vec3_t** c, int32_t* ca, int32_t* cs)
 {
-    if (ud.camerasprite == -1)
-    {
-        auto p = &ps[screenpeek];
-        if (c) *c = &p->pos;
-        if (cs) *cs = p->cursectnum;
-        if (ca) *ca = p->getang();
-    }
-    else
-    {
-        if (c) *c = &sprite[ud.camerasprite].pos;
-        if (cs) *cs = sprite[ud.camerasprite].sectnum;
-        if (ca) *ca = sprite[ud.camerasprite].ang;
-    }
+	if (ud.camerasprite == -1)
+	{
+		auto p = &ps[screenpeek];
+		if (c) *c = &p->pos;
+		if (cs) *cs = p->cursectnum;
+		if (ca) *ca = p->getang();
+	}
+	else
+	{
+		if (c) *c = &sprite[ud.camerasprite].pos;
+		if (cs) *cs = sprite[ud.camerasprite].sectnum;
+		if (ca) *ca = sprite[ud.camerasprite].ang;
+	}
 }
 
 //=========================================================================
@@ -280,42 +282,42 @@ void S_GetCamera(vec3_t** c, int32_t* ca, int32_t* cs)
 
 void DukeSoundEngine::CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan* chan)
 {
-    if (pos != nullptr)
-    {
-        vec3_t* campos;
-        int32_t camsect;
+	if (pos != nullptr)
+	{
+		vec3_t* campos;
+		int32_t camsect;
 
-        S_GetCamera(&campos, nullptr, &camsect);
-        if (vel) vel->Zero();
+		S_GetCamera(&campos, nullptr, &camsect);
+		if (vel) vel->Zero();
 
-        if (type == SOURCE_Unattached)
-        {
-            pos->X = pt[0];
-            pos->Y = pt[1];
-            pos->Z = pt[2];
-        }
-        else if (type == SOURCE_Actor)
-        {
-            auto actor = (spritetype*)source;
-            assert(actor != nullptr);
-            if (actor != nullptr)
-            {
-                GetPositionInfo(int(actor - sprite), chanSound - 1, camsect, campos, &actor->pos, nullptr, pos);
-                /*
-                if (vel) // DN3D does not properly maintain this.
-                {
-                    vel->X = float(actor->Vel.X * TICRATE);
-                    vel->Y = float(actor->Vel.Z * TICRATE);
-                    vel->Z = float(actor->Vel.Y * TICRATE);
-                }
-                */
-            }
-        }
-        if ((chanflags & CHANF_LISTENERZ) && campos != nullptr && type != SOURCE_None)
-        {
-            pos->Y = campos->z / 256.f;
-        }
-    }
+		if (type == SOURCE_Unattached)
+		{
+			pos->X = pt[0];
+			pos->Y = pt[1];
+			pos->Z = pt[2];
+		}
+		else if (type == SOURCE_Actor)
+		{
+			auto actor = (spritetype*)source;
+			assert(actor != nullptr);
+			if (actor != nullptr)
+			{
+				GetPositionInfo(int(actor - sprite), chanSound - 1, camsect, campos, &actor->pos, nullptr, pos);
+				/*
+				if (vel) // DN3D does not properly maintain this.
+				{
+					vel->X = float(actor->Vel.X * TICRATE);
+					vel->Y = float(actor->Vel.Z * TICRATE);
+					vel->Z = float(actor->Vel.Y * TICRATE);
+				}
+				*/
+			}
+		}
+		if ((chanflags & CHANF_LISTENERZ) && campos != nullptr && type != SOURCE_None)
+		{
+			pos->Y = campos->z / 256.f;
+		}
+	}
 }
 
 
@@ -327,40 +329,40 @@ void DukeSoundEngine::CalcPosVel(int type, const void* source, const float pt[3]
 
 void S_Update(void)
 {
-    SoundListener listener;
-    vec3_t* c;
-    int32_t ca, cs;
+	SoundListener listener;
+	vec3_t* c;
+	int32_t ca, cs;
 	
-    auto& gm = g_player[myconnectindex].ps->gm;
-    if (isRR() && !Mus_IsPlaying() && (gm && gm & MODE_GAME))
-        S_PlayRRMusic(); 
+	auto& gm = g_player[myconnectindex].ps->gm;
+	if (isRR() && !Mus_IsPlaying() && (gm && gm & MODE_GAME))
+		S_PlayRRMusic(); 
 
-    S_GetCamera(&c, &ca, &cs);
+	S_GetCamera(&c, &ca, &cs);
 
-    if (c != nullptr)
-    {
-        listener.angle = -(float)ca * pi::pi() / 1024; // Build uses a period of 2048.
-        listener.velocity.Zero();
-        listener.position = GetSoundPos(c);
-        listener.underwater = false; 
-        // This should probably use a real environment instead of the pitch hacking in S_PlaySound3D.
-        // listenactor->waterlevel == 3;
-        //assert(primaryLevel->Zones.Size() > listenactor->Sector->ZoneNumber);
-        listener.Environment = 0;// primaryLevel->Zones[listenactor->Sector->ZoneNumber].Environment;
-        listener.valid = true;
-    }
-    else
-    { 
-        listener.angle = 0;
-        listener.position.Zero();
-        listener.velocity.Zero();
-        listener.underwater = false;
-        listener.Environment = nullptr;
-        listener.valid = false;
-    }
-    listener.ListenerObject = ud.camerasprite == -1 ? nullptr : &sprite[ud.camerasprite];
-    soundEngine->SetListener(listener);
-    soundEngine->UpdateSounds((int)totalclock);
+	if (c != nullptr)
+	{
+		listener.angle = -(float)ca * pi::pi() / 1024; // Build uses a period of 2048.
+		listener.velocity.Zero();
+		listener.position = GetSoundPos(c);
+		listener.underwater = false; 
+		// This should probably use a real environment instead of the pitch hacking in S_PlaySound3D.
+		// listenactor->waterlevel == 3;
+		//assert(primaryLevel->Zones.Size() > listenactor->Sector->ZoneNumber);
+		listener.Environment = 0;// primaryLevel->Zones[listenactor->Sector->ZoneNumber].Environment;
+		listener.valid = true;
+	}
+	else
+	{ 
+		listener.angle = 0;
+		listener.position.Zero();
+		listener.velocity.Zero();
+		listener.underwater = false;
+		listener.Environment = nullptr;
+		listener.valid = false;
+	}
+	listener.ListenerObject = ud.camerasprite == -1 ? nullptr : &sprite[ud.camerasprite];
+	soundEngine->SetListener(listener);
+	soundEngine->UpdateSounds((int)totalclock);
 }
 
 
@@ -372,81 +374,81 @@ void S_Update(void)
 
 int S_PlaySound3D(int sndnum, int spriteNum, const vec3_t* pos, int channel, EChanFlags flags)
 {
-    auto const pl = &ps[myconnectindex];
-    if (!soundEngine->isValidSoundId(sndnum+1) || !SoundEnabled() || (unsigned)spriteNum >= MAXSPRITES || (pl->gm & MODE_MENU) ||
-        (pl->timebeforeexit > 0 && pl->timebeforeexit <= REALGAMETICSPERSEC * 3)) return -1;
+	auto const pl = &ps[myconnectindex];
+	if (!soundEngine->isValidSoundId(sndnum+1) || !SoundEnabled() || (unsigned)spriteNum >= MAXSPRITES || (pl->gm & MODE_MENU) ||
+		(pl->timebeforeexit > 0 && pl->timebeforeexit <= REALGAMETICSPERSEC * 3)) return -1;
 
-    int userflags = S_GetUserFlags(sndnum);
+	int userflags = S_GetUserFlags(sndnum);
 
-    auto sp = &sprite[spriteNum];
+	auto sp = &sprite[spriteNum];
 
-    if ((userflags & (SF_DTAG | SF_GLOBAL)) == SF_DTAG)
-    {
-        // Duke-Tag sound does not play in 3D.
-        return S_PlaySound(sndnum);
-    }
+	if ((userflags & (SF_DTAG | SF_GLOBAL)) == SF_DTAG)
+	{
+		// Duke-Tag sound does not play in 3D.
+		return S_PlaySound(sndnum);
+	}
 
-    if (userflags & SF_TALK)
-    {
-        if (snd_speech == 0 || (ud.multimode > 1 && sp->picnum == TILE_APLAYER && sp->yvel != screenpeek && ud.coop != 1)) return -1;
-        bool foundone =  soundEngine->EnumerateChannels([&](FSoundChan* chan)
-            {
-                auto sid = chan->OrgID;
-                auto flags = S_GetUserFlags(sid - 1);
-                return !!(flags & SF_TALK);
-            });
-        // don't play if any Duke talk sounds are already playing
-        if (foundone) return -1;
-    }
+	if (userflags & SF_TALK)
+	{
+		if (snd_speech == 0 || (ud.multimode > 1 && sp->picnum == TILE_APLAYER && sp->yvel != screenpeek && ud.coop != 1)) return -1;
+		bool foundone =  soundEngine->EnumerateChannels([&](FSoundChan* chan)
+			{
+				auto sid = chan->OrgID;
+				auto flags = S_GetUserFlags(sid - 1);
+				return !!(flags & SF_TALK);
+			});
+		// don't play if any Duke talk sounds are already playing
+		if (foundone) return -1;
+	}
 
-    int32_t    sndist;
-    FVector3 sndpos;    // this is in sound engine space.
+	int32_t    sndist;
+	FVector3 sndpos;    // this is in sound engine space.
 
-    vec3_t* campos;
-    int32_t camsect;
+	vec3_t* campos;
+	int32_t camsect;
 
-    S_GetCamera(&campos, nullptr, &camsect);
-    GetPositionInfo(spriteNum, sndnum, camsect, campos, pos, &sndist, &sndpos);
-    int        pitch = S_GetPitch(sndnum);
+	S_GetCamera(&campos, nullptr, &camsect);
+	GetPositionInfo(spriteNum, sndnum, camsect, campos, pos, &sndist, &sndpos);
+	int        pitch = S_GetPitch(sndnum);
 
-    bool explosion = ((userflags & (SF_GLOBAL | SF_DTAG)) == (SF_GLOBAL | SF_DTAG)) || ((sndnum == PIPEBOMB_EXPLODE || sndnum == LASERTRIP_EXPLODE || sndnum == RPG_EXPLODE));
+	bool explosion = ((userflags & (SF_GLOBAL | SF_DTAG)) == (SF_GLOBAL | SF_DTAG)) || ((sndnum == PIPEBOMB_EXPLODE || sndnum == LASERTRIP_EXPLODE || sndnum == RPG_EXPLODE));
 
-    bool underwater = ps[screenpeek].cursectnum > -1 && sector[ps[screenpeek].cursectnum].lotag == ST_2_UNDERWATER;
-    if (explosion)
-    {
-        if (underwater)
-            pitch -= 1024;
-    }
-    else
-    {
-        if (sndist > 32767 && sp->picnum != MUSICANDSFX && (userflags & (SF_LOOP | SF_MSFX)) == 0)
-            return -1;
+	bool underwater = ps[screenpeek].cursectnum > -1 && sector[ps[screenpeek].cursectnum].lotag == ST_2_UNDERWATER;
+	if (explosion)
+	{
+		if (underwater)
+			pitch -= 1024;
+	}
+	else
+	{
+		if (sndist > 32767 && sp->picnum != MUSICANDSFX && (userflags & (SF_LOOP | SF_MSFX)) == 0)
+			return -1;
 
-        if (underwater && (userflags & SF_TALK) == 0)
-            pitch = -768;
-    }
+		if (underwater && (userflags & SF_TALK) == 0)
+			pitch = -768;
+	}
 
-    bool is_playing = soundEngine->GetSoundPlayingInfo(SOURCE_Any, nullptr, sndnum+1);
-    if (is_playing && sp->picnum != MUSICANDSFX)
-        S_StopEnvSound(sndnum, spriteNum);
+	bool is_playing = soundEngine->GetSoundPlayingInfo(SOURCE_Any, nullptr, sndnum+1);
+	if (is_playing && sp->picnum != MUSICANDSFX)
+		S_StopEnvSound(sndnum, spriteNum);
 
-    int const repeatp = (userflags & SF_LOOP);
+	int const repeatp = (userflags & SF_LOOP);
 
-    if (repeatp && (userflags & SF_ONEINST_INTERNAL) && is_playing)
-    {
-        return -1;
-    }
+	if (repeatp && (userflags & SF_ONEINST_INTERNAL) && is_playing)
+	{
+		return -1;
+	}
 
-    // These explosion sounds originally used some distance hackery to make them louder but due to how the rolloff was set up they always played at full volume as a result.
-    // I think it is better to lower their attenuation so that they are louder than the rest but still fade in the distance.
-    // For the original effect, attenuation needs to be set to ATTN_NONE here.
-    float attenuation;
-    if (explosion) attenuation = 0.5f;
-    else attenuation = (userflags & (SF_GLOBAL | SF_DTAG)) == SF_GLOBAL ? ATTN_NONE : ATTN_NORM;
+	// These explosion sounds originally used some distance hackery to make them louder but due to how the rolloff was set up they always played at full volume as a result.
+	// I think it is better to lower their attenuation so that they are louder than the rest but still fade in the distance.
+	// For the original effect, attenuation needs to be set to ATTN_NONE here.
+	float attenuation;
+	if (explosion) attenuation = 0.5f;
+	else attenuation = (userflags & (SF_GLOBAL | SF_DTAG)) == SF_GLOBAL ? ATTN_NONE : ATTN_NORM;
 
-    if (userflags & SF_LOOP) flags |= CHANF_LOOP;
-    auto chan = soundEngine->StartSound(SOURCE_Actor, &sprite[spriteNum], &sndpos, CHAN_AUTO, flags, sndnum+1, attenuation == ATTN_NONE? 0.8f : 1.f, attenuation, nullptr, S_ConvertPitch(pitch));
-    return chan ? 0 : -1;
+	if (userflags & SF_LOOP) flags |= CHANF_LOOP;
+	auto chan = soundEngine->StartSound(SOURCE_Actor, &sprite[spriteNum], &sndpos, CHAN_AUTO, flags, sndnum+1, attenuation == ATTN_NONE? 0.8f : 1.f, attenuation, nullptr, S_ConvertPitch(pitch));
+	return chan ? 0 : -1;
 }
 
 //==========================================================================
@@ -457,17 +459,17 @@ int S_PlaySound3D(int sndnum, int spriteNum, const vec3_t* pos, int channel, ECh
 
 int S_PlaySound(int sndnum, int channel, EChanFlags flags)
 {
-    if (!soundEngine->isValidSoundId(sndnum+1) || !SoundEnabled()) return -1;
+	if (!soundEngine->isValidSoundId(sndnum+1) || !SoundEnabled()) return -1;
 
-    int userflags = S_GetUserFlags(sndnum);
-    if ((!(snd_speech & 1) && (userflags & SF_TALK)) || ((userflags & SF_ADULT)))
-        return -1;
+	int userflags = S_GetUserFlags(sndnum);
+	if ((!(snd_speech & 1) && (userflags & SF_TALK)) || ((userflags & SF_ADULT)))
+		return -1;
 
-    int const pitch = S_GetPitch(sndnum);
+	int const pitch = S_GetPitch(sndnum);
 
-    if (userflags & SF_LOOP) flags |= CHANF_LOOP;
-    auto chan = soundEngine->StartSound(SOURCE_None, nullptr, nullptr, channel, flags, sndnum + 1, 0.8f, ATTN_NONE, nullptr, S_ConvertPitch(pitch));
-    return chan ? 0 : -1;
+	if (userflags & SF_LOOP) flags |= CHANF_LOOP;
+	auto chan = soundEngine->StartSound(SOURCE_None, nullptr, nullptr, channel, flags, sndnum + 1, 0.8f, ATTN_NONE, nullptr, S_ConvertPitch(pitch));
+	return chan ? 0 : -1;
 }
 
 //==========================================================================
@@ -478,38 +480,38 @@ int S_PlaySound(int sndnum, int channel, EChanFlags flags)
 
 int A_PlaySound(int soundNum, int spriteNum, int channel, EChanFlags flags)
 {
-    return (unsigned)spriteNum >= MAXSPRITES ? S_PlaySound(soundNum, channel, flags) :
-        S_PlaySound3D(soundNum, spriteNum, &sprite[spriteNum].pos, channel, flags);
+	return (unsigned)spriteNum >= MAXSPRITES ? S_PlaySound(soundNum, channel, flags) :
+		S_PlaySound3D(soundNum, spriteNum, &sprite[spriteNum].pos, channel, flags);
 }
 
 void S_StopEnvSound(int sndNum, int sprNum, int channel)
 {
-    if (sprNum < -1 || sprNum >= MAXSPRITES) return;
+	if (sprNum < -1 || sprNum >= MAXSPRITES) return;
 
-    if (sprNum == -1) soundEngine->StopSoundID(sndNum+1);
-    else
-    {
-        if (channel == -1) soundEngine->StopSound(SOURCE_Actor, &sprite[sprNum], -1, sndNum + 1);
-        else soundEngine->StopSound(SOURCE_Actor, &sprite[sprNum], channel, -1);
+	if (sprNum == -1) soundEngine->StopSoundID(sndNum+1);
+	else
+	{
+		if (channel == -1) soundEngine->StopSound(SOURCE_Actor, &sprite[sprNum], -1, sndNum + 1);
+		else soundEngine->StopSound(SOURCE_Actor, &sprite[sprNum], channel, -1);
 
-        // StopSound kills the actor reference so this cannot be delayed until ChannelEnded gets called. At that point the actor may also not be valid anymore.
-        if (S_IsAmbientSFX(sprNum) && sector[sprite[sprNum].sectnum].lotag < 3)  // ST_2_UNDERWATER
-            hittype[sprNum].temp_data[0] = 0;
-    }
+		// StopSound kills the actor reference so this cannot be delayed until ChannelEnded gets called. At that point the actor may also not be valid anymore.
+		if (S_IsAmbientSFX(sprNum) && sector[sprite[sprNum].sectnum].lotag < 3)  // ST_2_UNDERWATER
+			hittype[sprNum].temp_data[0] = 0;
+	}
 }
 
 void S_ChangeSoundPitch(int soundNum, int spriteNum, int pitchoffset)
 {
-    if (spriteNum < -1 || spriteNum >= MAXSPRITES) return;
-    double expitch = pow(2, pitchoffset / 1200.);   // I hope I got this right that ASS uses a linear scale where 1200 is a full octave.
-    if (spriteNum == -1)
-    {
-        soundEngine->ChangeSoundPitch(SOURCE_Unattached, nullptr, CHAN_AUTO, expitch, soundNum+1);
-    }
-    else
-    {
-        soundEngine->ChangeSoundPitch(SOURCE_Actor, &sprite[spriteNum], CHAN_AUTO, expitch, soundNum+1);
-    }
+	if (spriteNum < -1 || spriteNum >= MAXSPRITES) return;
+	double expitch = pow(2, pitchoffset / 1200.);   // I hope I got this right that ASS uses a linear scale where 1200 is a full octave.
+	if (spriteNum == -1)
+	{
+		soundEngine->ChangeSoundPitch(SOURCE_Unattached, nullptr, CHAN_AUTO, expitch, soundNum+1);
+	}
+	else
+	{
+		soundEngine->ChangeSoundPitch(SOURCE_Actor, &sprite[spriteNum], CHAN_AUTO, expitch, soundNum+1);
+	}
 }
 
 //==========================================================================
@@ -520,21 +522,21 @@ void S_ChangeSoundPitch(int soundNum, int spriteNum, int pitchoffset)
 
 int A_CheckSoundPlaying(int spriteNum, int soundNum, int channel)
 {
-    if (spriteNum == -1) return soundEngine->GetSoundPlayingInfo(SOURCE_Any, nullptr, soundNum+1);
-    if ((unsigned)spriteNum >= MAXSPRITES) return false;
-    return soundEngine->IsSourcePlayingSomething(SOURCE_Actor, &sprite[spriteNum], channel, soundNum+1);
+	if (spriteNum == -1) return soundEngine->GetSoundPlayingInfo(SOURCE_Any, nullptr, soundNum+1);
+	if ((unsigned)spriteNum >= MAXSPRITES) return false;
+	return soundEngine->IsSourcePlayingSomething(SOURCE_Actor, &sprite[spriteNum], channel, soundNum+1);
 }
 
 // Check if actor <i> is playing any sound.
 int A_CheckAnySoundPlaying(int spriteNum)
 {
-    if ((unsigned)spriteNum >= MAXSPRITES) return false;
-    return soundEngine->IsSourcePlayingSomething(SOURCE_Actor, &sprite[spriteNum], CHAN_AUTO, 0);
+	if ((unsigned)spriteNum >= MAXSPRITES) return false;
+	return soundEngine->IsSourcePlayingSomething(SOURCE_Actor, &sprite[spriteNum], CHAN_AUTO, 0);
 }
 
 int S_CheckSoundPlaying(int soundNum)
 {
-    return soundEngine->GetSoundPlayingInfo(SOURCE_Any, nullptr, soundNum+1);
+	return soundEngine->GetSoundPlayingInfo(SOURCE_Any, nullptr, soundNum+1);
 }
 
 //==========================================================================
@@ -545,30 +547,30 @@ int S_CheckSoundPlaying(int soundNum)
 
 void S_MenuSound(void)
 {
-    static int menunum;
-    int/*static const short*/ menusnds[] =
-    {
-        LASERTRIP_EXPLODE,
-        DUKE_GRUNT,
-        DUKE_LAND_HURT,
-        CHAINGUN_FIRE,
-        SQUISHED,
-        KICK_HIT,
-        PISTOL_RICOCHET,
-        PISTOL_BODYHIT,
-        PISTOL_FIRE,
-        SHOTGUN_FIRE,
-        BOS1_WALK,
-        RPG_EXPLODE,
-        PIPEBOMB_BOUNCE,
-        PIPEBOMB_EXPLODE,
-        NITEVISION_ONOFF,
-        RPG_SHOOT,
-        SELECT_WEAPON
-    };
-    int s = isRR() ? 390 : menusnds[menunum++ % countof(menusnds)];
-    if (s != -1)
-        S_PlaySound(s, CHAN_AUTO, CHANF_UI);
+	static int menunum;
+	static const short menusnds[] =
+	{
+		LASERTRIP_EXPLODE,
+		DUKE_GRUNT,
+		DUKE_LAND_HURT,
+		CHAINGUN_FIRE,
+		SQUISHED,
+		KICK_HIT,
+		PISTOL_RICOCHET,
+		PISTOL_BODYHIT,
+		PISTOL_FIRE,
+		SHOTGUN_FIRE,
+		BOS1_WALK,
+		RPG_EXPLODE,
+		PIPEBOMB_BOUNCE,
+		PIPEBOMB_EXPLODE,
+		NITEVISION_ONOFF,
+		RPG_SHOOT,
+		SELECT_WEAPON
+	};
+	int s = isRR() ? 390 : menusnds[menunum++ % countof(menusnds)];
+	if (s != -1)
+		S_PlaySound(s, CHAN_AUTO, CHANF_UI);
 }
 
 //==========================================================================
@@ -581,19 +583,18 @@ static bool cd_disabled = false;    // This is in case mus_redbook is enabled bu
 
 void S_PlayLevelMusic(MapRecord *mi)
 {
-    if (isRR() && mi->music.IsEmpty() && mus_redbook && !cd_disabled) return;
-    Mus_Play(mi->labelName, mi->music, true);
+	if (isRR() && mi->music.IsEmpty() && mus_redbook && !cd_disabled) return;
+	Mus_Play(mi->labelName, mi->music, true);
 }
 
 void S_PlaySpecialMusic(unsigned int m)
 {
-    if (isRR()) return;   // Can only be MUS_LOADING, isRR() does not use it.
-    auto& musicfn = mapList[m].music;
-    if (musicfn.IsNotEmpty())
-    {
-        Mus_Play(nullptr, musicfn, true);
-        
-    }
+	if (isRR() || m >= specialmusic.Size()) return;   // Can only be MUS_LOADING, isRR() does not use it.
+	auto& musicfn = specialmusic[m];
+	if (musicfn.IsNotEmpty())
+	{
+		Mus_Play(nullptr, musicfn, true);
+	}
 }
 
 //---------------------------------------------------------------------------
@@ -604,28 +605,28 @@ void S_PlaySpecialMusic(unsigned int m)
 
 void S_PlayRRMusic(int newTrack)
 {
-    if (!isRR() || !mus_redbook || cd_disabled || currentLevel->music.IsNotEmpty())
-        return;
-    Mus_Stop();
+	if (!isRR() || !mus_redbook || cd_disabled || currentLevel->music.IsNotEmpty())
+		return;
+	Mus_Stop();
 
-    for (int i = 0; i < 10; i++)
-    {
-        g_cdTrack = newTrack != -1 ? newTrack : g_cdTrack + 1;
-        if (newTrack != 10 && (g_cdTrack > 9 || g_cdTrack < 2))
-            g_cdTrack = 2;
+	for (int i = 0; i < 10; i++)
+	{
+		g_cdTrack = newTrack != -1 ? newTrack : g_cdTrack + 1;
+		if (newTrack != 10 && (g_cdTrack > 9 || g_cdTrack < 2))
+			g_cdTrack = 2;
 
-        FStringf filename("track%02d.ogg", g_cdTrack);
-        if (Mus_Play(nullptr, filename, false)) return;
-    }
-    // If none of the tracks managed to start, disable the CD music for this session so that regular music can play if defined.
-    cd_disabled = true;
+		FStringf filename("track%02d.ogg", g_cdTrack);
+		if (Mus_Play(nullptr, filename, false)) return;
+	}
+	// If none of the tracks managed to start, disable the CD music for this session so that regular music can play if defined.
+	cd_disabled = true;
 }
 
 
 void PlayBonusMusic()
 {
-    if (MusicEnabled() && mus_enabled)
-        S_PlaySound(BONUSMUSIC, CHAN_AUTO, CHANF_UI);
+	if (MusicEnabled() && mus_enabled)
+		S_PlaySound(BONUSMUSIC, CHAN_AUTO, CHANF_UI);
 }
 
 END_DUKE_NS
diff --git a/source/games/duke/src/sounds.h b/source/games/duke/src/sounds.h
index 2fed8c7ec..b489a198e 100644
--- a/source/games/duke/src/sounds.h
+++ b/source/games/duke/src/sounds.h
@@ -109,6 +109,8 @@ inline bool StartCommentary(int tag, int sprnum)
     return false;
 }
 
+extern TArray<FString> specialmusic;
+
 
 END_DUKE_NS
 
diff --git a/source/games/duke/src/zz_premap.cpp b/source/games/duke/src/zz_premap.cpp
index 5fafbe15f..a858555a3 100644
--- a/source/games/duke/src/zz_premap.cpp
+++ b/source/games/duke/src/zz_premap.cpp
@@ -133,13 +133,13 @@ void G_NewGame(MapRecord *map, int skillNum)
 
 void resetpspritevars(int gameMode);
 
-static inline void clearfrags(void)
+void clearfrags(void)
 {
     for (int i = 0; i < ud.multimode; i++)
     {
         playerdata_t *const pPlayerData = &g_player[i];
         pPlayerData->ps->frag = pPlayerData->ps->fraggedself = 0;
-        Bmemset(pPlayerData->frags, 0, sizeof(pPlayerData->frags));
+        memset(pPlayerData->frags, 0, sizeof(pPlayerData->frags));
     }
 }
 
diff --git a/source/rr/src/sounds.cpp b/source/rr/src/sounds.cpp
index 91f23326e..c6e767935 100644
--- a/source/rr/src/sounds.cpp
+++ b/source/rr/src/sounds.cpp
@@ -30,6 +30,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 BEGIN_RR_NS
 
+extern MapRecord userMapRecord;
+
 class DukeSoundEngine : public SoundEngine
 {
     // client specific parts of the sound engine go in this class.