diff --git a/src/deh_soc.c b/src/deh_soc.c
index 5960ac33693a8a307cb5e829d6e689c31d16d43a..36a0678b86e3374b5bf8ab4016ffc89cb560fc7e 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -3010,12 +3010,10 @@ void reademblemdata(MYFILE *f, INT32 num)
 				emblemlocations[num-1].tag = (INT16)value;
 			else if (fastcmp(word, "MAPNUM"))
 			{
-				if (!value)
-				{
+				if (G_IsValidMapName(word2))
 					value = G_GetMapNumber(word2);
-					if (!value)
-						value = get_number(word2);
-				}
+				if (!value)
+					value = get_number(word2);
 
 				emblemlocations[num-1].level = (INT16)value;
 			}
@@ -3606,12 +3604,10 @@ void readmaincfg(MYFILE *f)
 				// i.e., Level AB, Level FZ, etc.
 
 				// Convert to map number
-				if (!value)
-				{
+				if (G_IsValidMapName(word2))
 					value = G_GetMapNumber(word2);
-					if (!value)
-						value = get_number(word2);
-				}
+				if (!value)
+					value = get_number(word2);
 
 				spstage_start = spmarathon_start = (INT16)value;
 			}
@@ -3621,12 +3617,10 @@ void readmaincfg(MYFILE *f)
 				// i.e., Level AB, Level FZ, etc.
 
 				// Convert to map number
-				if (!value)
-				{
+				if (G_IsValidMapName(word2))
 					value = G_GetMapNumber(word2);
-					if (!value)
-						value = get_number(word2);
-				}
+				if (!value)
+					value = get_number(word2);
 
 				spmarathon_start = (INT16)value;
 			}
@@ -3636,12 +3630,10 @@ void readmaincfg(MYFILE *f)
 				// i.e., Level AB, Level FZ, etc.
 
 				// Convert to map number
-				if (!value)
-				{
+				if (G_IsValidMapName(word2))
 					value = G_GetMapNumber(word2);
-					if (!value)
-						value = get_number(word2);
-				}
+				if (!value)
+					value = get_number(word2);
 
 				sstage_start = (INT16)value;
 				sstage_end = (INT16)(sstage_start+7); // 7 special stages total plus one weirdo
@@ -3652,12 +3644,10 @@ void readmaincfg(MYFILE *f)
 				// i.e., Level AB, Level FZ, etc.
 
 				// Convert to map number
-				if (!value)
-				{
+				if (G_IsValidMapName(word2))
 					value = G_GetMapNumber(word2);
-					if (!value)
-						value = get_number(word2);
-				}
+				if (!value)
+					value = get_number(word2);
 
 				smpstage_start = (INT16)value;
 				smpstage_end = (INT16)(smpstage_start+6); // 7 special stages total
@@ -3748,12 +3738,10 @@ void readmaincfg(MYFILE *f)
 				// i.e., Level AB, Level FZ, etc.
 
 				// Convert to map number
-				if (!value)
-				{
+				if (G_IsValidMapName(word2))
 					value = G_GetMapNumber(word2);
-					if (!value)
-						value = get_number(word2);
-				}
+				if (!value)
+					value = get_number(word2);
 
 				titlemap = (INT16)value;
 				titlechanged = true;
@@ -3922,15 +3910,12 @@ void readmaincfg(MYFILE *f)
 				// i.e., Level AB, Level FZ, etc.
 
 				// Convert to map number
-				if (!value)
-				{
+				if (G_IsValidMapName(word2))
 					value = G_GetMapNumber(word2);
-					if (!value)
-						value = get_number(word2);
-				}
+				if (!value)
+					value = get_number(word2);
 
 				bootmap = (INT16)value;
-				//titlechanged = true;
 			}
 			else if (fastcmp(word, "STARTCHAR"))
 			{
@@ -3943,12 +3928,10 @@ void readmaincfg(MYFILE *f)
 				// i.e., Level AB, Level FZ, etc.
 
 				// Convert to map number
-				if (!value)
-				{
+				if (G_IsValidMapName(word2))
 					value = G_GetMapNumber(word2);
-					if (!value)
-						value = get_number(word2);
-				}
+				if (!value)
+					value = get_number(word2);
 
 				tutorialmap = (INT16)value;
 			}
diff --git a/src/dehacked.c b/src/dehacked.c
index 142459fece3b3ccfaf78898df495a3dff42921b0..dcb5859317695f67d2fde7368d1c429142e8d1c0 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -364,14 +364,28 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 				}
 				else if (fastcmp(word, "LEVEL"))
 				{
-					if (!isdigit(word2[0]))
+					boolean is_valid_level = false;
+
+					if (G_IsValidMapName(word2))
 					{
 						INT16 mapnum = G_GetMapNumber(word2);
 						if (mapnum != 0)
+						{
 							i = mapnum;
+							is_valid_level = true;
+						}
+					}
+					else
+					{
+						is_valid_level = true;
 					}
 
-					if (i > 0 && i <= numgamemaps)
+					if (!is_valid_level)
+					{
+						deh_warning("Unknown level %s", word2);
+						ignorelines(f);
+					}
+					else if (i > 0 && i <= numgamemaps)
 						readlevelheader(f, i);
 					else
 					{
diff --git a/src/doomstat.h b/src/doomstat.h
index 3ad304452ca1e4df0705d21efa6e10ae8c162851..408e2a081af3169d6e67287e2f0125e68591921a 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -288,6 +288,7 @@ typedef struct
 typedef struct
 {
 	mapname_t name;
+	UINT32 lumpnum;
 	char *thumbnail;
 	char *thumbnail_wide;
 	char *music;
diff --git a/src/g_game.c b/src/g_game.c
index d11cbedbaff09ebec4888434214aed0a15a044a0..f6d231d0a0894d8b4be8534504ba74d18cf1899c 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -873,7 +873,7 @@ void G_InitMaps(void)
 	for (UINT16 i = 0; i < NUMBASEMAPS; i++)
 	{
 		const char *name = G_BuildClassicMapName(i + 1);
-		G_AddMap(name);
+		G_AddMap(name, LUMPERROR);
 	}
 
 	G_MakeMapName(&nextmapnames[0], "SCENE_TITLE");
@@ -922,23 +922,22 @@ UINT16 G_GetNextMapNumber(const char *name)
 	return MapIDForHashedString(name, name_length, name_hash);
 }
 
-UINT16 G_AddMap(const char *name)
+UINT16 G_AddMap(const char *name, UINT32 lumpnum)
 {
+	// Too many maps loaded
 	if (numgamemaps == MAXMAPS)
 		return 0;
 
 	UINT16 mapnum = G_GetMapNumber(name);
 	if (mapnum != 0)
 	{
-		// That map already exists, silly.
+		// Update that map's lumpnum
+		gamemaps[mapnum - 1].lumpnum = lumpnum;
 		return mapnum;
 	}
 
-	size_t name_len = strlen(name);
-	if (name_len > MAX_MAP_NAME_SIZE)
-		return 0;
-
 	G_MakeMapName(&gamemaps[numgamemaps].name, name);
+	gamemaps[numgamemaps].lumpnum = lumpnum;
 
 	numgamemaps++;
 
@@ -949,14 +948,33 @@ UINT16 G_AddMap(const char *name)
 
 boolean G_MapFileExists(const char *name)
 {
-	return W_CheckNumForLongName(name) != LUMPERROR;
+	UINT16 mapnum = G_GetMapNumber(name);
+	if (mapnum == 0)
+		return false;
+
+	return gamemaps[mapnum - 1].lumpnum != LUMPERROR;
+}
+
+static boolean IsValidMapNameStartChar(const char chr)
+{
+	return isalpha(chr) || chr == '_' || chr == '$';
 }
 
 boolean G_IsValidMapName(const char *name)
 {
-	if (name[0] == '\0' || !isalpha(name[0]))
+	// Can't be empty, and must begin with a letter, an underscore, or a dollar sign
+	if (name[0] == '\0' || !IsValidMapNameStartChar(name[0]))
 		return false;
 
+	size_t length = strlen(name);
+
+	// Middle and end of name must be a letter, a digit, an underscore, or a dollar sign
+	for (size_t i = 1; i < length; i++)
+	{
+		if (!(IsValidMapNameStartChar(name[i]) || isdigit(name[i])))
+			return false;
+	}
+
 	return true;
 }
 
diff --git a/src/g_game.h b/src/g_game.h
index 930bc0b990d079668476f415263c27c98b5f12e9..22d84c016e4267728e1ec83c31bdf1f3c382f089 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -120,7 +120,7 @@ const char *G_BuildMapName(INT32 map);
 void G_InitMaps(void);
 UINT16 G_GetMapNumber(const char *name);
 UINT16 G_GetNextMapNumber(const char *name);
-UINT16 G_AddMap(const char *name);
+UINT16 G_AddMap(const char *name, UINT32 lumpnum);
 boolean G_MapFileExists(const char *name);
 boolean G_IsValidMapName(const char *name);
 
diff --git a/src/p_setup.c b/src/p_setup.c
index 0ab8974073c1338972915e031f3eecab13894c52..8a850fb6949f04889801827d5e57d7577e5d170c 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -8119,60 +8119,90 @@ static lumpinfo_t* FindFolder(const char *folName, UINT16 *start, UINT16 *end, l
 	return lumpinfo;
 }
 
+static int P_AddMap(const char *name, UINT32 lumpnum)
+{
+	// Check filename length
+	size_t name_len = strlen(name);
+	if (name_len > MAX_MAP_NAME_SIZE)
+	{
+		CONS_Alert(CONS_WARNING, "Map name's length of %s exceeds maximum of %d -- skipping\n", sizeu1(name_len), MAX_MAP_NAME_SIZE);
+		return 0;
+	}
+
+	if (!G_IsValidMapName(name))
+	{
+		CONS_Alert(CONS_WARNING, "Map name %s is invalid -- skipping\n", name);
+		return 0;
+	}
+
+	INT16 num = G_AddMap(name, lumpnum);
+	if (num == 0)
+	{
+		CONS_Alert(CONS_ERROR, "Too many maps loaded!\n");
+		return -1;
+	}
+
+	//If you replaced the map you're on, end the level when done.
+	if (gamestate == GS_LEVEL && num == gamemap)
+		replacedcurrentmap = true;
+
+	return 1;
+}
+
 void P_LoadMapsFromFile(UINT16 wadnum, boolean added_ingame)
 {
 	boolean mapsadded = false;
 
-	lumpinfo_t *lumpinfo = wadfiles[wadnum]->lumpinfo;
+	lumpinfo_t *lumpinfo = NULL;
+	UINT16 numlumps = 0;
 
 	INT16 num;
 	const char *name;
 
 	if (W_FileHasFolders(wadfiles[wadnum]))
 	{
-		UINT16 posStart = W_CheckNumForFolderStartPK3("Maps/", wadnum, 0);
-		if (posStart == INT16_MAX)
-			return;
+		UINT32 *list = NULL;
+		UINT16 capacity = 0;
 
-		UINT16 posEnd = W_CheckNumForFolderEndPK3("Maps/", wadnum, posStart);
+		W_GetFolderLumpsPwad("Maps/", wadnum, &list, &capacity, &numlumps);
 
-		lumpinfo += posStart;
-
-		for (; posStart < posEnd; posStart++, lumpinfo++)
+		for (UINT16 i = 0; i < numlumps; i++)
 		{
+			UINT32 lumpnum = list[i];
+
+			lumpinfo = wadfiles[wadnum]->lumpinfo + LUMPNUM(lumpnum);
+
 			name = M_GetFilenameFromPath(lumpinfo->fullname); // Full lump name, with its extension
 
+			// Extension must be .wad
 			if (!M_CheckFilenameExtension(name, "wad"))
-			{
-				// Extension must be .wad
 				continue;
-			}
-
-			name = lumpinfo->longname; // Name without the extension
 
-			num = G_AddMap(name);
+			// Get the name without the extension
+			name = lumpinfo->longname;
 
-			if (num == 0)
+			int status = P_AddMap(name, lumpnum);
+			if (status == 1)
 			{
-				CONS_Alert(CONS_ERROR, "Too many maps loaded!\n");
-				break;
+				if (added_ingame)
+					CONS_Printf("%s\n", name);
+				mapsadded = true;
 			}
-			//If you replaced the map you're on, end the level when done.
-			else if (gamestate == GS_LEVEL && num == gamemap)
-				replacedcurrentmap = true;
-
-			if (added_ingame)
-				CONS_Printf("%s\n", name);
-			mapsadded = true;
+			else if (status < 0)
+				break;
 		}
+
+		Z_Free(list);
 	}
 	else
 	{
-		UINT16 numlumps = wadfiles[wadnum]->numlumps;
+		lumpinfo = wadfiles[wadnum]->lumpinfo;
+		numlumps = wadfiles[wadnum]->numlumps;
+
 		for (size_t i = 0; i < numlumps; i++, lumpinfo++)
 		{
 			name = lumpinfo->name;
-			if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers
+			if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P')
 			{
 				if (name[5]!='\0')
 					continue;
@@ -8187,6 +8217,41 @@ void P_LoadMapsFromFile(UINT16 wadnum, boolean added_ingame)
 				mapsadded = true;
 			}
 		}
+
+		// Now do markers
+		UINT16 start = W_CheckNumForMarkerStartPwad("LV_START", wadnum, 0);
+		UINT16 end = W_CheckNumForNamePwad("LV_END", wadnum, start);
+
+		lumpinfo = wadfiles[wadnum]->lumpinfo + start;
+
+		for (UINT16 l = start; l < end; l++, lumpinfo++)
+		{
+			name = lumpinfo->name;
+
+			// Ignore any of these lump names
+			if (stricmp(name, "THINGS") == 0
+			|| stricmp(name, "LINEDEFS") == 0
+			|| stricmp(name, "SIDEDEFS") == 0
+			|| stricmp(name, "VERTEXES") == 0
+			|| stricmp(name, "SEGS") == 0
+			|| stricmp(name, "SSECTORS") == 0
+			|| stricmp(name, "NODES") == 0
+			|| stricmp(name, "SECTORS") == 0
+			|| stricmp(name, "REJECT") == 0
+			|| stricmp(name, "BLOCKMAP") == 0
+			|| stricmp(name, "BEHAVIOR") == 0)
+				continue;
+
+			int status = P_AddMap(name, (wadnum << 16) + l);
+			if (status == 1)
+			{
+				if (added_ingame)
+					CONS_Printf("%s\n", name);
+				mapsadded = true;
+			}
+			else if (status < 0)
+				break;
+		}
 	}
 
 	if (!mapsadded && added_ingame)
diff --git a/src/w_wad.c b/src/w_wad.c
index 13749f630078f27382a456d52b522eda1798f892..e991531dc782834a662a97fa28284b884f4b2614 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1158,6 +1158,7 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup)
 	numwadfiles++;
 
 	W_ReadFileShaders(wadfile);
+	P_LoadMapsFromFile(numwadfiles - 1, !startup);
 	W_LoadDehackedLumpsPK3(numwadfiles - 1, mainfile);
 	W_InvalidateLumpnumCache();
 
@@ -1338,6 +1339,42 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump)
 	return i;
 }
 
+void W_GetFolderLumpsPwad(const char *name, UINT16 wad, UINT32 **list, UINT16 *list_capacity, UINT16 *numlumps)
+{
+	size_t name_length = strlen(name);
+	lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo;
+
+	UINT16 capacity = *list_capacity;
+	UINT16 count = *numlumps;
+
+	for (UINT16 i = 0; i < wadfiles[wad]->numlumps; i++, lump_p++)
+	{
+		if (strnicmp(name, lump_p->fullname, name_length) == 0)
+		{
+			if (strlen(lump_p->fullname) > name_length)
+			{
+				if (!capacity || count >= capacity)
+				{
+					capacity = capacity ? (capacity * 2) : 16;
+					*list = Z_Realloc(*list, capacity * sizeof(UINT32), PU_STATIC, NULL);
+				}
+
+				(*list)[count] = (wad << 16) + i;
+				count++;
+			}
+		}
+	}
+
+	(*list_capacity) = capacity;
+	(*numlumps) = count;
+}
+
+void W_GetFolderLumps(const char *name, UINT32 **list, UINT16 *list_capacity, UINT16 *numlumps)
+{
+	for (UINT16 i = 0; i < numwadfiles; i++)
+		W_GetFolderLumpsPwad(name, i, list, list_capacity, numlumps);
+}
+
 // In a PK3 type of resource file, it looks for an entry with the specified full name.
 // Returns lump position in PK3's lumpinfo, or INT16_MAX if not found.
 UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump)
diff --git a/src/w_wad.h b/src/w_wad.h
index ffb9095ba21c9e1d79049adadaa893953b097260..4f59c40e05461405f3c60f86a551e25e3643747c 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -181,6 +181,9 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump);
 UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump);
 UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump);
 
+void W_GetFolderLumpsPwad(const char *name, UINT16 wad, UINT32 **list, UINT16 *list_capacity, UINT16 *numlumps);
+void W_GetFolderLumps(const char *name, UINT32 **list, UINT16 *list_capacity, UINT16 *numlumps);
+
 lumpnum_t W_CheckNumForMap(const char *name);
 lumpnum_t W_CheckNumForName(const char *name);
 lumpnum_t W_CheckNumForLongName(const char *name);