diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 9e61c90e7..68096178d 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -1141,7 +1141,6 @@ set (PCH_SOURCES
 	common/engine/cycler.cpp
 	common/engine/d_event.cpp
 	common/engine/date.cpp
-	common/engine/filesys_doom.cpp
 	common/engine/stats.cpp
 	common/engine/sc_man.cpp
 	common/engine/palettecontainer.cpp
@@ -1289,7 +1288,6 @@ set( GAME_SOURCES
 	common/filesystem/source/file_whres.cpp
 	common/filesystem/source/file_ssi.cpp
 	common/filesystem/source/file_hog.cpp
-	common/filesystem/source/file_hog2.cpp
 	common/filesystem/source/file_mvl.cpp
 	common/filesystem/source/file_directory.cpp
 	common/filesystem/source/resourcefile.cpp
diff --git a/source/common/2d/wipe.cpp b/source/common/2d/wipe.cpp
index c829f7a8d..cc57ca29f 100755
--- a/source/common/2d/wipe.cpp
+++ b/source/common/2d/wipe.cpp
@@ -43,8 +43,6 @@
 #include "s_soundinternal.h"
 #include "i_time.h"
 
-EXTERN_CVAR(Bool, cl_capfps)
-
 class FBurnTexture : public FTexture
 {
 	TArray<uint32_t> WorkBuffer;
@@ -165,8 +163,6 @@ protected:
 public:
 	virtual ~Wiper();
 	virtual bool Run(int ticks) = 0;
-	virtual bool RunInterpolated(double ticks) { return true; };
-	virtual bool Interpolatable() { return false; }
 	virtual void SetTextures(FGameTexture* startscreen, FGameTexture* endscreen)
 	{
 		startScreen = startscreen;
@@ -181,11 +177,9 @@ class Wiper_Crossfade : public Wiper
 {
 public:
 	bool Run(int ticks) override;
-	bool RunInterpolated(double ticks) override;
-	bool Interpolatable() override { return true; }
 	
 private:
-	float Clock = 0;
+	int Clock = 0;
 };
 
 class Wiper_Melt : public Wiper
@@ -193,12 +187,10 @@ class Wiper_Melt : public Wiper
 public:
 	Wiper_Melt();
 	bool Run(int ticks) override;
-	bool RunInterpolated(double ticks) override;
-	bool Interpolatable() override { return true; }
 	
 private:
 	enum { WIDTH = 320, HEIGHT = 200 };
-	double y[WIDTH];
+	int y[WIDTH];
 };
 
 class Wiper_Burn : public Wiper
@@ -276,23 +268,7 @@ bool Wiper_Crossfade::Run(int ticks)
 	Clock += ticks;
 	DrawTexture(twod, startScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
 	DrawTexture(twod, endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, DTA_Alpha, clamp(Clock / 32.f, 0.f, 1.f), TAG_DONE);
-	return Clock >= 32.;
-}
-
-//==========================================================================
-//
-// OpenGLFrameBuffer :: Wiper_Crossfade :: Run
-//
-// Fades the old screen into the new one over 32 ticks.
-//
-//==========================================================================
-
-bool Wiper_Crossfade::RunInterpolated(double ticks)
-{
-	Clock += ticks;
-	DrawTexture(twod, startScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
-	DrawTexture(twod, endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, DTA_Alpha, clamp(Clock / 32.f, 0.f, 1.f), TAG_DONE);
-	return Clock >= 32.;
+	return Clock >= 32;
 }
 
 //==========================================================================
@@ -306,7 +282,7 @@ Wiper_Melt::Wiper_Melt()
 	y[0] = -(M_Random() & 15);
 	for (int i = 1; i < WIDTH; ++i)
 	{
-		y[i] = clamp(y[i-1] + (double)(M_Random() % 3) - 1., -15., 0.);
+		y[i] = clamp(y[i-1] + (M_Random() % 3) - 1, -15, 0);
 	}
 }
 
@@ -331,25 +307,25 @@ bool Wiper_Melt::Run(int ticks)
 		{
 			if (y[i] < HEIGHT)
 			{
-				if (y[i] < 0.)
-					y[i] = y[i] + 1.;
-				else if (y[i] < 16.)
-					y[i] += y[i] + 1.;
+				if (y[i] < 0)
+					y[i]++;
+				else if (y[i] < 16)
+					y[i] += y[i] + 1;
 				else
-					y[i] = min<double>(y[i] + 8., HEIGHT);
+					y[i] = min<int>(y[i] + 8, HEIGHT);
 				done = false;
 			}
 			if (ticks == 0)
 			{
 				struct {
 					int32_t x;
-					double y;
+					int32_t y;
 				} dpt;
 				struct {
 					int32_t left;
-					double top;
+					int32_t top;
 					int32_t right;
-					double bottom;
+					int32_t bottom;
 				} rect;
 				
 				// Only draw for the final tick.
@@ -357,7 +333,7 @@ bool Wiper_Melt::Run(int ticks)
 				int w = startScreen->GetTexelWidth();
 				int h = startScreen->GetTexelHeight();
 				dpt.x = i * w / WIDTH;
-				dpt.y = max(0., y[i] * (double)h / (double)HEIGHT);
+				dpt.y = max(0, y[i] * h / HEIGHT);
 				rect.left = dpt.x;
 				rect.top = 0;
 				rect.right = (i + 1) * w / WIDTH;
@@ -372,77 +348,6 @@ bool Wiper_Melt::Run(int ticks)
 	return done;
 }
 
-//==========================================================================
-//
-// Wiper_Melt :: RunInterpolated
-//
-// Melts the old screen into the new one over 32 ticks (interpolated).
-//
-//==========================================================================
-
-bool Wiper_Melt::RunInterpolated(double ticks)
-{
-	bool done = false;
-	DrawTexture(twod, endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
-
-	// Copy the old screen in vertical strips on top of the new one.
-	while (ticks > 0.)
-	{
-		done = true;
-		for (int i = 0; i < WIDTH; i++)
-		{
-			if (y[i] < (double)HEIGHT)
-			{
-				if (ticks > 0. && ticks < 1.)
-				{
-					if (y[i] < 0)
-						y[i] += ticks;
-					else if (y[i] < 16)
-						y[i] += (y[i] + 1) * ticks;
-					else
-						y[i] = min<double>(y[i] + (8 * ticks), (double)HEIGHT);
-				}
-				else if (y[i] < 0.)
-					y[i] = y[i] + 1.;
-				else if (y[i] < 16.)
-					y[i] += y[i] + 1.;
-				else
-					y[i] = min<double>(y[i] + 8., HEIGHT);
-				done = false;
-			}
-		}
-		ticks -= 1.;
-	}
-	for (int i = 0; i < WIDTH; i++)
-	{
-		struct {
-			int32_t x;
-			double y;
-		} dpt;
-		struct {
-			int32_t left;
-			double top;
-			int32_t right;
-			double bottom;
-		} rect;
-
-		// Only draw for the final tick.
-		int w = startScreen->GetTexelWidth();
-		double h = startScreen->GetTexelHeight();
-		dpt.x = i * w / WIDTH;
-		dpt.y = max(0., y[i] * (double)h / (double)HEIGHT);
-		rect.left = dpt.x;
-		rect.top = 0;
-		rect.right = (i + 1) * w / WIDTH;
-		rect.bottom = h - dpt.y;
-		if (rect.bottom > rect.top)
-		{
-			DrawTexture(twod, startScreen, 0, dpt.y, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_ClipLeft, rect.left, DTA_ClipRight, rect.right, DTA_Masked, false, TAG_DONE);
-		}
-	}
-	return done;
-}
-
 //==========================================================================
 //
 // OpenGLFrameBuffer :: Wiper_Burn Constructor
@@ -518,7 +423,6 @@ void PerformWipe(FTexture* startimg, FTexture* endimg, int wipe_type, bool stops
 {
 	// wipe update
 	uint64_t wipestart, nowtime, diff;
-	double diff_frac;
 	bool done;
 
 	GSnd->SetSfxPaused(true, 1);
@@ -534,34 +438,20 @@ void PerformWipe(FTexture* startimg, FTexture* endimg, int wipe_type, bool stops
 
 	do
 	{
-		if (wiper->Interpolatable() && !cl_capfps)
+		do
 		{
+			I_WaitVBL(2);
 			nowtime = I_msTime();
-			diff_frac = (nowtime - wipestart) * 40. / 1000.;	// Using 35 here feels too slow.
-			wipestart = nowtime;
-			twod->Begin(screen->GetWidth(), screen->GetHeight());
-			done = wiper->RunInterpolated(diff_frac);
-			if (overlaydrawer) overlaydrawer();
-			twod->End();
-			screen->Update();
-			twod->OnFrameDone();
-		}
-		else
-		{
-			do
-			{
-				I_WaitVBL(2);
-				nowtime = I_msTime();
-				diff = (nowtime - wipestart) * 40 / 1000;	// Using 35 here feels too slow.
-			} while (diff < 1);
-			wipestart = nowtime;
-			twod->Begin(screen->GetWidth(), screen->GetHeight());
-			done = wiper->Run(1);
-			if (overlaydrawer) overlaydrawer();
-			twod->End();
-			screen->Update();
-			twod->OnFrameDone();
-		}
+			diff = (nowtime - wipestart) * 40 / 1000;	// Using 35 here feels too slow.
+		} while (diff < 1);
+		wipestart = nowtime;
+		twod->Begin(screen->GetWidth(), screen->GetHeight());
+		done = wiper->Run(1);
+		if (overlaydrawer) overlaydrawer();
+		twod->End();
+		screen->Update();
+		twod->OnFrameDone();
+
 	} while (!done);
 	delete wiper;
 	I_FreezeTime(false);
diff --git a/source/common/audio/music/i_music.cpp b/source/common/audio/music/i_music.cpp
index 54f66be9a..d190537d4 100644
--- a/source/common/audio/music/i_music.cpp
+++ b/source/common/audio/music/i_music.cpp
@@ -53,6 +53,9 @@
 #include "s_music.h"
 #include "filereadermusicinterface.h"
 
+using namespace FileSys;
+
+
 void I_InitSoundFonts();
 
 EXTERN_CVAR (Int, snd_samplerate)
@@ -185,7 +188,7 @@ static void SetupGenMidi()
 
 static void SetupWgOpn()
 {
-	int lump = fileSystem.FindFile("xg.wopn");
+	int lump = fileSystem.CheckNumForFullName("xg.wopn");
 	if (lump < 0)
 	{
 		return;
@@ -310,7 +313,7 @@ static ZMusic_MidiSource GetMIDISource(const char *fn)
 	if (src.Compare("*") == 0) src = mus_playing.name;
 
 	auto lump = fileSystem.CheckNumForName(src.GetChars(), ns_music);
-	if (lump < 0) lump = fileSystem.FindFile(src.GetChars());
+	if (lump < 0) lump = fileSystem.CheckNumForFullName(src.GetChars());
 	if (lump < 0)
 	{
 		Printf("Cannot find MIDI lump %s.\n", src.GetChars());
diff --git a/source/common/audio/music/i_soundfont.cpp b/source/common/audio/music/i_soundfont.cpp
index 3d3a6fd44..9737374fc 100644
--- a/source/common/audio/music/i_soundfont.cpp
+++ b/source/common/audio/music/i_soundfont.cpp
@@ -298,7 +298,7 @@ FileReader FPatchSetReader::OpenFile(const char *name)
 
 FLumpPatchSetReader::FLumpPatchSetReader(const char *filename)
 {
-	mLumpIndex = fileSystem.FindFile(filename);
+	mLumpIndex = fileSystem.CheckNumForFullName(filename);
 
 	mBasePath = filename;
 	FixPathSeperator(mBasePath);
@@ -314,9 +314,9 @@ FileReader FLumpPatchSetReader::OpenMainConfigFile()
 FileReader FLumpPatchSetReader::OpenFile(const char *name)
 {
 	FString path;
-	if (IsAbsPath(name)) return FileReader();	// no absolute paths in the virtual file system.
+	if (IsAbsPath(name)) return FileReader();	// no absolute paths in the lump directory.
 	path = mBasePath + name;
-	auto index = fileSystem.FindFile(path.GetChars());
+	auto index = fileSystem.CheckNumForFullName(path.GetChars());
 	if (index < 0) return FileReader();
 	return fileSystem.ReopenFileReader(index);
 }
@@ -472,7 +472,7 @@ FSoundFontReader *FSoundFontManager::OpenSoundFont(const char *const name, int a
 	// To avoid clashes this will only be done if the name has the '.cfg' extension.
 	// Sound fonts cannot be loaded this way.
 	const char *p = name + strlen(name) - 4;
-	if (p > name && !stricmp(p, ".cfg") && fileSystem.FindFile(name) >= 0)
+	if (p > name && !stricmp(p, ".cfg") && fileSystem.CheckNumForFullName(name) >= 0)
 	{
 		return new FLumpPatchSetReader(name);
 	}
diff --git a/source/common/audio/music/music.cpp b/source/common/audio/music/music.cpp
index 3e9202bef..9664c5458 100644
--- a/source/common/audio/music/music.cpp
+++ b/source/common/audio/music/music.cpp
@@ -114,7 +114,7 @@ static FileReader OpenMusic(const char* musicname)
 	{
 		int lumpnum;
 		lumpnum = mus_cb.FindMusic(musicname);
-		if (lumpnum == -1) lumpnum = fileSystem.CheckNumForName(musicname, ns_music);
+		if (lumpnum == -1) lumpnum = fileSystem.CheckNumForName(musicname, FileSys::ns_music);
 		if (lumpnum == -1)
 		{
 			Printf("Music \"%s\" not found\n", musicname);
@@ -143,7 +143,7 @@ bool MusicExists(const char* music_name)
 	{
 		int lumpnum;
 		lumpnum = mus_cb.FindMusic(music_name);
-		if (lumpnum == -1) lumpnum = fileSystem.CheckNumForName(music_name, ns_music);
+		if (lumpnum == -1) lumpnum = fileSystem.CheckNumForName(music_name, FileSys::ns_music);
 		if (lumpnum != -1 && fileSystem.FileLength(lumpnum) != 0)
 			return true;
 	}
diff --git a/source/common/audio/sound/s_sound.cpp b/source/common/audio/sound/s_sound.cpp
index d8ae7fe00..852d389f5 100644
--- a/source/common/audio/sound/s_sound.cpp
+++ b/source/common/audio/sound/s_sound.cpp
@@ -61,7 +61,7 @@ enum
 {
 	DEFAULT_PITCH = 128,
 };
-static FCRandom pr_soundpitch ("SoundPitch");
+static FRandom pr_soundpitch ("SoundPitch");
 SoundEngine* soundEngine;
 
 //==========================================================================
diff --git a/source/common/console/c_bind.cpp b/source/common/console/c_bind.cpp
index 2d7903298..fe794c736 100644
--- a/source/common/console/c_bind.cpp
+++ b/source/common/console/c_bind.cpp
@@ -730,15 +730,15 @@ void ReadBindings(int lump, bool override)
 
 void C_SetDefaultKeys(const char* baseconfig)
 {
-	auto lump = fileSystem.FindFile("engine/commonbinds.txt");
+	auto lump = fileSystem.CheckNumForFullName("engine/commonbinds.txt");
 	if (lump >= 0)
 	{
 		// Bail out if a mod tries to override this. Main game resources are allowed to do this, though.
 		auto fileno2 = fileSystem.GetFileContainer(lump);
-		if (fileno2 > fileSystem.GetMaxBaseNum())
+		if (fileno2 > fileSystem.GetMaxIwadNum())
 		{
 			I_FatalError("File %s is overriding core lump %s.",
-				fileSystem.GetContainerFullName(fileno2), "engine/commonbinds.txt");
+				fileSystem.GetResourceFileFullName(fileno2), "engine/commonbinds.txt");
 		}
 
 		ReadBindings(lump, true);
@@ -748,7 +748,7 @@ void C_SetDefaultKeys(const char* baseconfig)
 	while ((lump = fileSystem.FindLumpFullName(baseconfig, &lastlump)) != -1)
 	{
 		// Read this only from the main game resources.
-		if (fileSystem.GetFileContainer(lump) <= fileSystem.GetMaxBaseNum())
+		if (fileSystem.GetFileContainer(lump) <= fileSystem.GetMaxIwadNum())
 			ReadBindings(lump, true);
 	}
 
@@ -758,7 +758,7 @@ void C_SetDefaultKeys(const char* baseconfig)
 		// [SW] - We need to check to see the origin of the DEFBINDS... if it
 		// Comes from an IWAD/IPK3/IPK7 allow it to override the users settings...
 		// If it comes from a user mod however, don't.
-		if (fileSystem.GetFileContainer(lump) > fileSystem.GetMaxBaseNum())
+		if (fileSystem.GetFileContainer(lump) > fileSystem.GetMaxIwadNum())
 			ReadBindings(lump, false);
 		else
 			ReadBindings(lump, true);
diff --git a/source/common/console/c_commandline.cpp b/source/common/console/c_commandline.cpp
index 7fa9d1ff2..4f500ffe6 100644
--- a/source/common/console/c_commandline.cpp
+++ b/source/common/console/c_commandline.cpp
@@ -193,7 +193,7 @@ int FCommandLine::argc ()
 	return _argc;
 }
 
-const char *FCommandLine::operator[] (int i)
+char *FCommandLine::operator[] (int i)
 {
 	if (_argv == NULL)
 	{
diff --git a/source/common/console/c_commandline.h b/source/common/console/c_commandline.h
index 4886aa9c7..dc5466df1 100644
--- a/source/common/console/c_commandline.h
+++ b/source/common/console/c_commandline.h
@@ -44,7 +44,7 @@ public:
 	FCommandLine (const char *commandline, bool no_escapes = false);
 	~FCommandLine ();
 	int argc ();
-	const char *operator[] (int i);
+	char *operator[] (int i);
 	const char *args () { return cmd; }
 	void Shift();
 
diff --git a/source/common/console/c_enginecmds.cpp b/source/common/console/c_enginecmds.cpp
index 8fbee1f20..d6a2e027e 100644
--- a/source/common/console/c_enginecmds.cpp
+++ b/source/common/console/c_enginecmds.cpp
@@ -233,18 +233,18 @@ CCMD (wdir)
 	if (argv.argc() != 2) wadnum = -1;
 	else 
 	{
-		wadnum = fileSystem.CheckIfContainerLoaded (argv[1]);
+		wadnum = fileSystem.CheckIfResourceFileLoaded (argv[1]);
 		if (wadnum < 0)
 		{
 			Printf ("%s must be loaded to view its directory.\n", argv[1]);
 			return;
 		}
 	}
-	for (int i = 0; i < fileSystem.GetFileCount(); ++i)
+	for (int i = 0; i < fileSystem.GetNumEntries(); ++i)
 	{
 		if (wadnum == -1 || fileSystem.GetFileContainer(i) == wadnum)
 		{
-			Printf ("%10ld %s\n", fileSystem.FileLength(i), fileSystem.GetFileName(i));
+			Printf ("%10ld %s\n", fileSystem.FileLength(i), fileSystem.GetFileFullName(i));
 		}
 	}
 }
diff --git a/source/common/cutscenes/movieplayer.cpp b/source/common/cutscenes/movieplayer.cpp
index 27d8f7271..a1b04385f 100644
--- a/source/common/cutscenes/movieplayer.cpp
+++ b/source/common/cutscenes/movieplayer.cpp
@@ -508,7 +508,7 @@ public:
 			}
 			if (!MusicStream)
 			{
-				Printf(PRINT_BOLD, "Failed to decode %s\n", fileSystem.GetFileName(soundtrack));
+				Printf(PRINT_BOLD, "Failed to decode %s\n", fileSystem.GetFileFullName(soundtrack, false));
 			}
 		}
 		animtex.SetSize(AnimTexture::VPX, width, height);
diff --git a/source/common/engine/filesys_doom.cpp b/source/common/engine/filesys_doom.cpp
deleted file mode 100644
index b19b99a78..000000000
--- a/source/common/engine/filesys_doom.cpp
+++ /dev/null
@@ -1,648 +0,0 @@
-/*
-** filesys_doom.cpp
-**
-** the very special lump name lookup code for Doom's short names.
-** Not useful in a generic system.
-**
-**---------------------------------------------------------------------------
-** Copyright 1998-2016 Randy Heit
-** Copyright 2005-2024 Christoph Oelckers
-** All rights reserved.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions
-** are met:
-**
-** 1. Redistributions of source code must retain the above copyright
-**    notice, this list of conditions and the following disclaimer.
-** 2. Redistributions in binary form must reproduce the above copyright
-**    notice, this list of conditions and the following disclaimer in the
-**    documentation and/or other materials provided with the distribution.
-** 3. The name of the author may not be used to endorse or promote products
-**    derived from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**---------------------------------------------------------------------------
-**
-**
-*/
-
-#include "filesystem.h"
-#include "printf.h"
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-void FileSystem::InitHashChains()
-{
-	Super::InitHashChains();
-	unsigned NumEntries = GetFileCount();
-	for (unsigned i = 0; i < (unsigned)NumEntries; i++)
-	{
-		files[i].HashFirst = files[i].HashNext = NULL_INDEX;
-	}
-	// Now set up the chains
-	for (unsigned i = 0; i < (unsigned)NumEntries; i++)
-	{
-		if (files[i].Namespace == ns_hidden || files[i].ShortName[0] == 0) continue;
-		unsigned j = FileSys::MakeHash(files[i].ShortName, 8) % NumEntries;
-		files[i].HashNext = files[j].HashFirst;
-		files[j].HashFirst = i;
-	}
-}
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-static void UpperCopy(char* to, const char* from)
-{
-	int i;
-
-	for (i = 0; i < 8 && from[i]; i++)
-		to[i] = toupper(from[i]);
-	for (; i < 8; i++)
-		to[i] = 0;
-	to[8] = 0;
-}
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-void FileSystem::SetupName(int fileindex)
-{
-	const char* name = GetFileName(fileindex);
-	int containerflags = GetContainerFlags(GetFileContainer(fileindex));
-	int lflags = GetFileFlags(fileindex);
-
-	if ((containerflags & wadflags) == wadflags)
-	{
-		UpperCopy(files[fileindex].ShortName, name);
-	}
-	else if ((lflags & FileSys::RESFF_EMBEDDED) || !*name)
-	{
-		files[fileindex].Namespace = ns_hidden;
-	}
-	else
-	{
-		if (lflags & FileSys::RESFF_FULLPATH) files[fileindex].Flags |= LUMPF_FULLPATH;	// copy for easier access in lookup function.
-		auto slash = strrchr(name, '/');
-		auto base = slash ? (slash + 1) : name;
-		UpperCopy(files[fileindex].ShortName, base);
-		auto dot = strrchr(files[fileindex].ShortName, '.');
-		if (dot) while (*dot) *dot++ = 0;
-	}
-}
-
-
-//==========================================================================
-//
-// IsMarker
-//
-// (from BOOM)
-//
-//==========================================================================
-
-inline bool FileSystem::IsMarker(int lump, const char* marker) noexcept
-{
-	auto name = files[lump].ShortName;
-	if (name[0] == marker[0])
-	{
-		return (!strcmp(name, marker) ||
-			(marker[1] == '_' && !strcmp(name + 1, marker)));
-	}
-	else return false;
-}
-
-//==========================================================================
-//
-// SetNameSpace
-//
-// Sets namespace information for the lumps. It always looks for the first
-// x_START and the last x_END lump, except when loading flats. In this case
-// F_START may be absent and if that is the case all lumps with a size of
-// 4096 will be flagged appropriately.
-//
-//==========================================================================
-
-// This class was supposed to be local in the function but GCC
-// does not like that.
-struct Marker
-{
-	int markertype;
-	int index;
-};
-
-void FileSystem::SetNamespace(int filenum, const char* startmarker, const char* endmarker, namespace_t space, FileSys::FileSystemMessageFunc Printf, bool flathack)
-{
-	using FileSys::FSMessageLevel;
-	bool warned = false;
-	int numstartmarkers = 0, numendmarkers = 0;
-	TArray<Marker> markers;
-	int FirstLump = GetFirstEntry(filenum);
-	int LastLump = GetLastEntry(filenum);
-	auto FileName = GetContainerName(filenum);
-
-	for (int i = FirstLump; i <= LastLump; i++)
-	{
-		if (IsMarker(i, startmarker))
-		{
-			Marker m = { 0, i };
-			markers.push_back(m);
-			numstartmarkers++;
-		}
-		else if (IsMarker(i, endmarker))
-		{
-			Marker m = { 1, i };
-			markers.push_back(m);
-			numendmarkers++;
-		}
-	}
-
-	if (numstartmarkers == 0)
-	{
-		if (numendmarkers == 0) return;	// no markers found
-
-		if (Printf)
-			Printf(FSMessageLevel::Warning, "%s: %s marker without corresponding %s found.\n", FileName, endmarker, startmarker);
-
-
-		if (flathack)
-		{
-			// We have found no F_START but one or more F_END markers.
-			// mark all lumps before the last F_END marker as potential flats.
-			unsigned int end = markers[markers.size() - 1].index;
-			for (int ii = FirstLump; ii <= LastLump; ii++)
-			{
-				if (FileLength(ii) == 4096)
-				{
-					// We can't add this to the flats namespace but 
-					// it needs to be flagged for the texture manager.
-					if (Printf) Printf(FSMessageLevel::DebugNotify, "%s: Marking %s as potential flat\n", FileName, files[ii].ShortName);
-					files[ii].Namespace = ns_maybeflat;
-				}
-			}
-		}
-		return;
-	}
-
-	size_t i = 0;
-	while (i < markers.size())
-	{
-		int start, end;
-		if (markers[i].markertype != 0)
-		{
-			if (Printf) Printf(FSMessageLevel::Warning, "%s: %s marker without corresponding %s found.\n", FileName, endmarker, startmarker);
-			i++;
-			continue;
-		}
-		start = int(i++);
-
-		// skip over subsequent x_START markers
-		while (i < markers.size() && markers[i].markertype == 0)
-		{
-			if (Printf) Printf(FSMessageLevel::Warning, "%s: duplicate %s marker found.\n", FileName, startmarker);
-			i++;
-			continue;
-		}
-		// same for x_END markers
-		while (i < markers.size() - 1 && (markers[i].markertype == 1 && markers[i + 1].markertype == 1))
-		{
-			if (Printf) Printf(FSMessageLevel::Warning, "%s: duplicate %s marker found.\n", FileName, endmarker);
-			i++;
-			continue;
-		}
-		// We found a starting marker but no end marker. Ignore this block.
-		if (i >= markers.size())
-		{
-			if (Printf) Printf(FSMessageLevel::Warning, "%s: %s marker without corresponding %s found.\n", FileName, startmarker, endmarker);
-			end = LastLump + 1;
-		}
-		else
-		{
-			end = markers[i++].index;
-		}
-
-		// we found a marked block
-		if (Printf) Printf(FSMessageLevel::DebugNotify, "%s: Found %s block at (%d-%d)\n", FileName, startmarker, markers[start].index, end);
-		for (int j = markers[start].index + 1; j < end; j++)
-		{
-			if (files[j].Namespace != ns_global)
-			{
-				if (!warned && Printf)
-				{
-					Printf(FSMessageLevel::Warning, "%s: Overlapping namespaces found (lump %d)\n", FileName, j);
-				}
-				warned = true;
-			}
-			else if (space == ns_sprites && FileLength(j) < 8)
-			{
-				// sf 26/10/99:
-				// ignore sprite lumps smaller than 8 bytes (the smallest possible)
-				// in size -- this was used by some dmadds wads
-				// as an 'empty' graphics resource
-				if (Printf) Printf(FSMessageLevel::DebugWarn, "%s: Skipped empty sprite %s (lump %d)\n", FileName, files[j].ShortName, j);
-			}
-			else
-			{
-				files[j].Namespace = space;
-			}
-		}
-	}
-}
-
-
-//==========================================================================
-//
-// W_SkinHack
-//
-// Tests a wad file to see if it contains an S_SKIN marker. If it does,
-// every lump in the wad is moved into a new namespace. Because skins are
-// only supposed to replace player sprites, sounds, or faces, this should
-// not be a problem. Yes, there are skins that replace more than that, but
-// they are such a pain, and breaking them like this was done on purpose.
-// This also renames any S_SKINxx lumps to just S_SKIN.
-//
-//==========================================================================
-
-void FileSystem::SkinHack(int filenum, FileSys::FileSystemMessageFunc Printf)
-{
-	using FileSys::FSMessageLevel;
-	// this being static is not a problem. The only relevant thing is that each skin gets a different number.
-	bool skinned = false;
-	bool hasmap = false;
-
-	int FirstLump = GetFirstEntry(filenum);
-	int LastLump = GetLastEntry(filenum);
-	auto FileName = GetContainerName(filenum);
-
-	for (int i = FirstLump; i <= LastLump; i++)
-	{
-		auto lump = &files[i];
-
-		if (!strnicmp(lump->ShortName, "S_SKIN", 6))
-		{ // Wad has at least one skin.
-			lump->ShortName[6] = 0;
-			lump->ShortName[7] = 0;
-			if (!skinned)
-			{
-				skinned = true;
-
-				for (int j = FirstLump; j <= LastLump; j++)
-				{
-					files[j].Namespace = skin_namespc;
-				}
-				skin_namespc++;
-			}
-		}
-		// needless to say, this check is entirely useless these days as map names can be more diverse..
-		if ((lump->ShortName[0] == 'M' &&
-			lump->ShortName[1] == 'A' &&
-			lump->ShortName[2] == 'P' &&
-			lump->ShortName[3] >= '0' && lump->ShortName[3] <= '9' &&
-			lump->ShortName[4] >= '0' && lump->ShortName[4] <= '9' &&
-			lump->ShortName[5] == '\0')
-			||
-			(lump->ShortName[0] == 'E' &&
-				lump->ShortName[1] >= '0' && lump->ShortName[1] <= '9' &&
-				lump->ShortName[2] == 'M' &&
-				lump->ShortName[3] >= '0' && lump->ShortName[3] <= '9' &&
-				lump->ShortName[4] == '\0'))
-		{
-			hasmap = true;
-		}
-	}
-	if (skinned && hasmap && Printf)
-	{
-		Printf(FSMessageLevel::Attention, "%s: The maps will not be loaded because it has a skin.\n", FileName);
-		Printf(FSMessageLevel::Attention, "You should remove the skin from the wad to play these maps.\n");
-	}
-}
-
-//==========================================================================
-//
-// 
-//
-//==========================================================================
-
-void FileSystem::SetupNamespace(int filenum, FileSys::FileSystemMessageFunc Printf)
-{
-	int flags = GetContainerFlags(filenum);
-
-	// Set namespace for entries from WADs.
-	if ((flags & wadflags) == wadflags)
-	{
-		SetNamespace(filenum, "S_START", "S_END", ns_sprites, Printf);
-		SetNamespace(filenum, "F_START", "F_END", ns_flats, Printf, true);
-		SetNamespace(filenum, "C_START", "C_END", ns_colormaps, Printf);
-		SetNamespace(filenum, "A_START", "A_END", ns_acslibrary, Printf);
-		SetNamespace(filenum, "TX_START", "TX_END", ns_newtextures, Printf);
-		SetNamespace(filenum, "V_START", "V_END", ns_strifevoices, Printf);
-		SetNamespace(filenum, "HI_START", "HI_END", ns_hires, Printf);
-		SetNamespace(filenum, "VX_START", "VX_END", ns_voxels, Printf);
-		SkinHack(filenum, Printf);
-	}
-	else if (!(flags & FResourceFile::NO_FOLDERS))
-	{
-		int FirstLump = GetFirstEntry(filenum);
-		int LastLump = GetLastEntry(filenum);
-		auto FileName = GetContainerName(filenum);
-
-		for (int i = FirstLump; i <= LastLump; i++)
-		{
-			auto lump = &files[i];
-
-			auto LongName = GetFileName(i);
-			// Map some directories to WAD namespaces.
-			// Note that some of these namespaces don't exist in WADS.
-			// CheckNumForName will handle any request for these namespaces accordingly.
-			int Namespace = !strncmp(LongName, "flats/", 6) ? ns_flats :
-				!strncmp(LongName, "textures/", 9) ? ns_newtextures :
-				!strncmp(LongName, "hires/", 6) ? ns_hires :
-				!strncmp(LongName, "sprites/", 8) ? ns_sprites :
-				!strncmp(LongName, "voxels/", 7) ? ns_voxels :
-				!strncmp(LongName, "colormaps/", 10) ? ns_colormaps :
-				!strncmp(LongName, "acs/", 4) ? ns_acslibrary :
-				!strncmp(LongName, "voices/", 7) ? ns_strifevoices :
-				!strncmp(LongName, "patches/", 8) ? ns_patches :
-				!strncmp(LongName, "graphics/", 9) ? ns_graphics :
-				!strncmp(LongName, "sounds/", 7) ? ns_sounds :
-				!strncmp(LongName, "music/", 6) ? ns_music :
-				!strchr(LongName, '/') ? ns_global :
-				ns_hidden;
-
-			lump->Namespace = Namespace;
-
-			switch (Namespace)
-			{
-			case ns_hidden:
-				memset(lump->ShortName, 0, sizeof(lump->ShortName));
-				break;
-
-			case ns_sprites:
-			case ns_voxels:
-			case ns_hires:
-				// Since '\' can't be used as a file name's part inside a ZIP
-				// we have to work around this for sprites because it is a valid
-				// frame character.
-				for (auto& c : lump->ShortName)
-				{
-					if (c == '^') c = '\\';
-				}
-				break;
-			}
-		}
-	}
-}
-
-//==========================================================================
-//
-// 
-//
-//==========================================================================
-
-bool FileSystem::InitFiles(std::vector<std::string>& filenames, FileSys::FileSystemFilterInfo* filter, FileSys::FileSystemMessageFunc Printf, bool allowduplicates)
-{
-	if (!Super::InitFiles(filenames, filter, Printf, allowduplicates)) return false;
-	files.Resize(GetFileCount());
-	memset(files.Data(), 0, sizeof(files[0]) * files.size());
-	int numfiles = GetFileCount();
-	for (int i = 0; i < numfiles; i++)
-	{
-		SetupName(i);
-	}
-
-	int numresfiles = GetContainerCount();
-	for (int i = 0; i < numresfiles; i++)
-	{
-		SetupNamespace(i, Printf);
-	}
-	return true;
-}
-
-//==========================================================================
-//
-// CheckNumForName
-//
-// Returns -1 if name not found. The version with a third parameter will
-// look exclusively in the specified wad for the lump.
-//
-// [RH] Changed to use hash lookup ala BOOM instead of a linear search
-// and namespace parameter
-//==========================================================================
-
-int FileSystem::CheckNumForName(const char* name, int space) const
-{
-	char uname[9];
-	uint32_t i;
-
-	if (name == nullptr)
-	{
-		return -1;
-	}
-
-	// Let's not search for names that are longer than 8 characters and contain path separators
-	// They are almost certainly full path names passed to this function.
-	if (strlen(name) > 8 && strpbrk(name, "/."))
-	{
-		return -1;
-	}
-
-	UpperCopy(uname, name);
-	i = files[FileSys::MakeHash(uname, 8) % files.Size()].HashFirst;
-
-	while (i != NULL_INDEX)
-	{
-		auto& lump = files[i];
-
-		if (!memcmp(lump.ShortName, uname, 8))
-		{
-			if (lump.Namespace == space) break;
-			// If the lump is from one of the special namespaces exclusive to Zips
-			// the check has to be done differently:
-			// If we find a lump with this name in the global namespace that does not come
-			// from a Zip return that. WADs don't know these namespaces and single lumps must
-			// work as well.
-			if (space > ns_specialzipdirectory && lump.Namespace == ns_global && !(lump.Flags & LUMPF_FULLPATH))
-				break;
-		}
-		i = lump.HashNext;
-	}
-
-	return i != NULL_INDEX ? i : -1;
-}
-
-int FileSystem::CheckNumForName(const char* name, int space, int rfnum, bool exact) const
-{
-	char uname[9];
-	uint32_t i;
-
-	if (rfnum < 0)
-	{
-		return CheckNumForName(name, space);
-	}
-
-	UpperCopy(uname, name);
-	i = files[FileSys::MakeHash(uname, 8) % files.Size()].HashFirst;
-
-	// If exact is true if will only find lumps in the same WAD, otherwise
-	// also those in earlier WADs.
-
-	while (i != NULL_INDEX &&
-		(memcmp(files[i].ShortName, uname, 8) || files[i].Namespace != space ||
-			(exact ? (GetFileContainer(i) != rfnum) : (GetFileContainer(i) > rfnum))))
-	{
-		i = files[i].HashNext;
-	}
-
-	return i != NULL_INDEX ? i : -1;
-}
-
-//==========================================================================
-//
-// GetNumForName
-//
-// Calls CheckNumForName, but bombs out if not found.
-//
-//==========================================================================
-
-int FileSystem::GetNumForName(const char* name, int space) const
-{
-	int	i;
-
-	i = CheckNumForName(name, space);
-
-	if (i == -1)
-		throw FileSys::FileSystemException("GetNumForName: %s not found!", name);
-
-	return i;
-}
-
-
-
-//==========================================================================
-//
-// returns a modifiable pointer to the short name
-// 
-// should only be called before the hash chains are set up.
-// If done later this needs rehashing.
-// 
-// This is for custom setup through postprocessFunc
-//
-//==========================================================================
-
-char* FileSystem::GetShortName(int i)
-{
-	if ((unsigned)i >= files.Size()) 
-		throw FileSys::FileSystemException("GetShortName: Invalid index");
-	return files[i].ShortName;
-}
-
-//==========================================================================
-//
-// W_FindLump
-//
-// Find a named lump. Specifically allows duplicates for merging of e.g.
-// SNDINFO lumps.
-//
-//==========================================================================
-
-int FileSystem::FindLump(const char* name, int* lastlump, bool anyns)
-{
-	if ((size_t)*lastlump >= files.size()) return -1;
-	char name8[9];
-	UpperCopy(name8, name);
-
-	assert(lastlump != nullptr && *lastlump >= 0);
-
-	const int last = (int)files.size();
-
-	for(int lump = *lastlump; lump < last; lump++)
-	{
-		const FileEntry* const lump_p = &files[lump];
-		if ((anyns || lump_p->Namespace == ns_global) && !memcmp(lump_p->ShortName, name8, 8))
-		{
-			*lastlump = lump + 1;
-			return lump;
-		}
-	}
-
-	*lastlump = last;
-	return -1;
-}
-
-//==========================================================================
-//
-// W_FindLumpMulti
-//
-// Find a named lump. Specifically allows duplicates for merging of e.g.
-// SNDINFO lumps. Returns everything having one of the passed names.
-//
-//==========================================================================
-
-int FileSystem::FindLumpMulti(const char** names, int* lastlump, bool anyns, int* nameindex)
-{
-	assert(lastlump != nullptr && *lastlump >= 0);
-
-	int last = files.Size();
-
-	for (int lump = *lastlump; lump < last; lump++)
-	{
-		auto lump_p = &files[lump];
-		if (anyns || lump_p->Namespace == ns_global)
-		{
-			for (const char** name = names; *name != nullptr; name++)
-			{
-				if (!strnicmp(*name, lump_p->ShortName, 8))
-				{
-					*lastlump = lump + 1;
-					if (nameindex != nullptr) *nameindex = int(name - names);
-					return lump;
-				}
-			}
-		}
-	}
-
-	*lastlump = last;
-	return -1;
-}
-
-//==========================================================================
-//
-// This function combines lookup from regular lists and Doom's special one.
-//
-//==========================================================================
-
-int FileSystem::CheckNumForAnyName(const char* name, namespace_t namespc) const
-{
-	if (name != NULL)
-	{
-		// Check short names first to avoid interference from short names in the real file system.
-		if (strlen(name) <= 8 && !strpbrk(name, "./"))
-		{
-			return CheckNumForName(name, namespc);
-		}
-
-		int lookup = Super::FindFile(name, false);
-		if (lookup >= 0) return lookup;
-	}
-	return -1;
-}
-
diff --git a/source/common/engine/filesystem.h b/source/common/engine/filesystem.h
index f8a791e7b..53f491677 100644
--- a/source/common/engine/filesystem.h
+++ b/source/common/engine/filesystem.h
@@ -1,129 +1,7 @@
 #pragma once
 #include "fs_filesystem.h"
 
+using FileSys::FileSystem;
 using FileSys::FResourceFile;
 
-// [RH] Namespaces from BOOM.
-// These are needed here in the low level part so that WAD files can be properly set up.
-enum namespace_t : int {
-	ns_hidden = -1,
-
-	ns_global = 0,
-	ns_sprites,
-	ns_flats,
-	ns_colormaps,
-	ns_acslibrary,
-	ns_newtextures,
-	ns_bloodraw,	// no longer used - kept for ZScript.
-	ns_bloodsfx,	// no longer used - kept for ZScript.
-	ns_bloodmisc,	// no longer used - kept for ZScript.
-	ns_strifevoices,
-	ns_hires,
-	ns_voxels,
-	ns_maybeflat,
-
-	// These namespaces are only used to mark lumps in special subdirectories
-	// so that their contents doesn't interfere with the global namespace.
-	// searching for data in these namespaces works differently for lumps coming
-	// from Zips or other files.
-	ns_specialzipdirectory,
-	ns_sounds,
-	ns_patches,
-	ns_graphics,
-	ns_music,
-
-	ns_firstskin,
-};
-
-
-// extended class that adds Doom's very special short name lookup to the file system.
-class FileSystem : public FileSys::FileSystem
-{
-private:
-	struct FileEntry
-	{
-		char ShortName[9];
-		uint8_t Namespace;
-		uint8_t Flags;
-		uint32_t HashFirst;
-		uint32_t HashNext;
-	};
-
-	const int wadflags = FileSys::FResourceFile::NO_EXTENSIONS | FileSys::FResourceFile::NO_FOLDERS | FileSys::FResourceFile::SHORTNAMES;
-	const uint32_t NULL_INDEX = 0xffffffff;
-
-	TArray<FileEntry> files;
-	int skin_namespc = ns_firstskin;
-
-
-	void Start(FileSys::FileSystemMessageFunc Printf);
-	void SetupName(int fileindex);
-	bool IsMarker(int lump, const char* marker) noexcept;
-	void SetNamespace(int filenum, const char* startmarker, const char* endmarker, namespace_t space, FileSys::FileSystemMessageFunc Printf, bool flathack = false);
-	void SkinHack(int filenum, FileSys::FileSystemMessageFunc Printf);
-	void SetupNamespace(int filenum, FileSys::FileSystemMessageFunc Printf);
-
-	using Super = FileSys::FileSystem;
-
-public:
-	enum
-	{
-		LUMPF_MAYBEFLAT = 1,
-		LUMPF_FULLPATH = 2,
-	};
-
-	bool InitFiles(std::vector<std::string>& filenames, FileSys::FileSystemFilterInfo* filter = nullptr, FileSys::FileSystemMessageFunc Printf = nullptr, bool allowduplicates = false) override;
-	void InitHashChains() override;
-
-	int CheckNumForAnyName(const char* cname, namespace_t namespc = ns_global) const;
-
-	int CheckNumForName(const char* name, int namespc) const;
-	int CheckNumForName(const char* name, int namespc, int wadfile, bool exact = true) const;
-	int GetNumForName(const char* name, int namespc) const;
-
-	inline int CheckNumForName(const uint8_t* name) const { return CheckNumForName((const char*)name, ns_global); }
-	inline int CheckNumForName(const char* name) const { return CheckNumForName(name, ns_global); }
-	inline int CheckNumForName(const uint8_t* name, int ns) const { return CheckNumForName((const char*)name, ns); }
-	inline int GetNumForName(const char* name) const { return GetNumForName(name, ns_global); }
-	inline int GetNumForName(const uint8_t* name) const { return GetNumForName((const char*)name); }
-	inline int GetNumForName(const uint8_t* name, int ns) const { return GetNumForName((const char*)name, ns); }
-
-	int FindLump(const char* name, int* lastlump, bool anyns = false);		// [RH] Find lumps with duplication
-	int FindLumpMulti(const char** names, int* lastlump, bool anyns = false, int* nameindex = nullptr); // same with multiple possible names
-
-	bool CheckFileName(int lump, const char* name) const noexcept
-	{
-		if ((size_t)lump >= files.size())
-			return false;
-
-		return !strnicmp(files[lump].ShortName, name, 8);
-	}
-
-	const char* GetFileShortName(int lump) const noexcept
-	{
-		if ((size_t)lump >= files.size())
-			return nullptr;
-		else
-			return files[lump].ShortName;
-	}
-
-	int GetFileNamespace(int lump) const noexcept
-	{
-		if ((size_t)lump >= files.size())
-			return ns_global;
-		else
-			return files[lump].Namespace;
-	}
-
-	void SetFileNamespace(int lump, int ns) noexcept
-	{
-		if ((size_t)lump < files.size()) files[lump].Namespace = ns;
-	}
-
-	// This is only for code that wants to edit the names before the game starts.
-	char* GetShortName(int i);
-
-};
-
-inline FileSystem fileSystem;
-
+inline FileSys::FileSystem fileSystem;
diff --git a/source/common/engine/i_net.cpp b/source/common/engine/i_net.cpp
index ad106bbc4..33b4c1267 100644
--- a/source/common/engine/i_net.cpp
+++ b/source/common/engine/i_net.cpp
@@ -132,8 +132,7 @@ enum
 	PRE_CONACK,				// Sent from host to guest to acknowledge PRE_CONNECT receipt
 	PRE_ALLFULL,			// Sent from host to an unwanted guest
 	PRE_ALLHEREACK,			// Sent from guest to host to acknowledge PRE_ALLHEREACK receipt
-	PRE_GO,					// Sent from host to guest to continue game startup
-	PRE_IN_PROGRESS,		// Sent from host to guest if the game has already started
+	PRE_GO					// Sent from host to guest to continue game startup
 };
 
 // Set PreGamePacket.fake to this so that the game rejects any pregame packets
@@ -270,8 +269,6 @@ void PacketSend (void)
 	//			I_Error ("SendPacket error: %s",strerror(errno));
 }
 
-void PreSend(const void* buffer, int bufferlen, const sockaddr_in* to);
-void SendConAck(int num_connected, int num_needed);
 
 //
 // PacketGet
@@ -306,7 +303,7 @@ void PacketGet (void)
 					GetPlayerName(node).GetChars());
 			}
 
-			doomcom.data[0] = NCMD_EXIT;
+			doomcom.data[0] = 0x80;	// NCMD_EXIT
 			c = 1;
 		}
 		else if (err != WSAEWOULDBLOCK)
@@ -344,11 +341,10 @@ void PacketGet (void)
 	}
 	else if (c > 0)
 	{	//The packet is not from any in-game node, so we might as well discard it.
-		if (TransmitBuffer[0] == PRE_FAKE)
+		// Don't show the message for disconnect notifications.
+		if (c != 2 || TransmitBuffer[0] != PRE_FAKE || TransmitBuffer[1] != PRE_DISCONNECT)
 		{
-			// If it's someone waiting in the lobby, let them know the game already started
-			uint8_t msg[] = { PRE_FAKE, PRE_IN_PROGRESS };
-			PreSend(msg, 2, &fromaddress);
+			DPrintf(DMSG_WARNING, "Dropped packet: Unknown host (%s:%d)\n", inet_ntoa(fromaddress.sin_addr), fromaddress.sin_port);
 		}
 		doomcom.remotenode = -1;
 		return;
@@ -373,22 +369,7 @@ sockaddr_in *PreGet (void *buffer, int bufferlen, bool noabort)
 		int err = WSAGetLastError();
 		if (err == WSAEWOULDBLOCK || (noabort && err == WSAECONNRESET))
 			return NULL;	// no packet
-
-		if (doomcom.consoleplayer == 0)
-		{
-			int node = FindNode(&fromaddress);
-			I_NetMessage("Got unexpected disconnect.");
-			doomcom.numnodes--;
-			for (; node < doomcom.numnodes; ++node)
-				sendaddress[node] = sendaddress[node + 1];
-
-			// Let remaining guests know that somebody left.
-			SendConAck(doomcom.numnodes, doomcom.numplayers);
-		}
-		else
-		{
-			I_NetError("The host disbanded the game unexpectedly");
-		}
+		I_Error ("PreGet: %s", neterror ());
 	}
 	return &fromaddress;
 }
@@ -518,7 +499,7 @@ void SendAbort (void)
 	}
 }
 
-void SendConAck (int num_connected, int num_needed)
+static void SendConAck (int num_connected, int num_needed)
 {
 	PreGamePacket packet;
 
@@ -727,7 +708,7 @@ bool HostGame (int i)
 
 	doomcom.numnodes = 1;
 
-	I_NetInit ("Hosting game", numplayers);
+	I_NetInit ("Waiting for players", numplayers);
 
 	// Wait for numplayers-1 different connections
 	if (!I_NetLoop (Host_CheckForConnects, (void *)(intptr_t)numplayers))
@@ -802,15 +783,13 @@ bool Guest_ContactHost (void *userdata)
 			}
 			else if (packet.Message == PRE_DISCONNECT)
 			{
-				I_NetError("The host cancelled the game.");
+				doomcom.numnodes = 0;
+				I_FatalError ("The host cancelled the game.");
 			}
 			else if (packet.Message == PRE_ALLFULL)
 			{
-				I_NetError("The game is full.");
-			}
-			else if (packet.Message == PRE_IN_PROGRESS)
-			{
-				I_NetError("The game was already started.");
+				doomcom.numnodes = 0;
+				I_FatalError ("The game is full.");
 			}
 		}
 	}
@@ -871,7 +850,7 @@ bool Guest_WaitForOthers (void *userdata)
 			return true;
 
 		case PRE_DISCONNECT:
-			I_NetError("The host cancelled the game.");
+			I_FatalError ("The host cancelled the game.");
 			break;
 		}
 	}
@@ -896,7 +875,6 @@ bool JoinGame (int i)
 	BuildAddress (&sendaddress[1], Args->GetArg(i+1));
 	sendplayer[1] = 0;
 	doomcom.numnodes = 2;
-	doomcom.consoleplayer = -1;
 
 
 	// Let host know we are here
@@ -1068,13 +1046,6 @@ void I_NetMessage(const char* text, ...)
 #endif
 }
 
-void I_NetError(const char* error)
-{
-	doomcom.numnodes = 0;
-	StartWindow->NetClose();
-	I_FatalError(error);
-}
-
 // todo: later these must be dispatched by the main menu, not the start screen.
 void I_NetProgress(int val)
 {
diff --git a/source/common/engine/i_net.h b/source/common/engine/i_net.h
index 15720374d..c52072c86 100644
--- a/source/common/engine/i_net.h
+++ b/source/common/engine/i_net.h
@@ -7,7 +7,6 @@
 int I_InitNetwork (void);
 void I_NetCmd (void);
 void I_NetMessage(const char*, ...);
-void I_NetError(const char* error);
 void I_NetProgress(int val);
 void I_NetInit(const char* msg, int num);
 bool I_NetLoop(bool (*timer_callback)(void*), void* userdata);
diff --git a/source/common/engine/m_random.cpp b/source/common/engine/m_random.cpp
index 11c4350b7..3af14526b 100644
--- a/source/common/engine/m_random.cpp
+++ b/source/common/engine/m_random.cpp
@@ -83,7 +83,7 @@ FRandom pr_exrandom("EX_Random");
 
 // PUBLIC DATA DEFINITIONS -------------------------------------------------
 
-FCRandom M_Random;
+FRandom M_Random;
 
 // Global seed. This is modified predictably to initialize every RNG.
 uint32_t rngseed;
@@ -126,8 +126,8 @@ CCMD(rngseed)
 
 // PRIVATE DATA DEFINITIONS ------------------------------------------------
 
-FRandom *FRandom::RNGList, *FRandom::CRNGList;
-static TDeletingArray<FRandom *> NewRNGs, NewCRNGs;
+FRandom *FRandom::RNGList;
+static TDeletingArray<FRandom *> NewRNGs;
 
 // CODE --------------------------------------------------------------------
 
@@ -139,22 +139,14 @@ static TDeletingArray<FRandom *> NewRNGs, NewCRNGs;
 //
 //==========================================================================
 
-FRandom::FRandom (bool client)
-: NameCRC (0), bClient(client)
+FRandom::FRandom ()
+: NameCRC (0)
 {
 #ifndef NDEBUG
 	Name = NULL;
 #endif
-	if (bClient)
-	{
-		Next = CRNGList;
-		CRNGList = this;
-	}
-	else
-	{
-		Next = RNGList;
-		RNGList = this;
-	}
+	Next = RNGList;
+	RNGList = this;
 	Init(0);
 }
 
@@ -166,7 +158,7 @@ FRandom::FRandom (bool client)
 //
 //==========================================================================
 
-FRandom::FRandom (const char *name, bool client) : bClient(client)
+FRandom::FRandom (const char *name)
 {
 	NameCRC = CalcCRC32 ((const uint8_t *)name, (unsigned int)strlen (name));
 #ifndef NDEBUG
@@ -178,7 +170,7 @@ FRandom::FRandom (const char *name, bool client) : bClient(client)
 #endif
 
 	// Insert the RNG in the list, sorted by CRC
-	FRandom **prev = (bClient ? &CRNGList : &RNGList), * probe = (bClient ? CRNGList : RNGList);
+	FRandom **prev = &RNGList, *probe = RNGList;
 
 	while (probe != NULL && probe->NameCRC < NameCRC)
 	{
@@ -213,8 +205,8 @@ FRandom::~FRandom ()
 
 	FRandom *last = NULL;
 
-	prev = bClient ? &CRNGList : &RNGList;
-	rng = bClient ? CRNGList : RNGList;
+	prev = &RNGList;
+	rng = RNGList;
 
 	while (rng != NULL && rng != this)
 	{
@@ -245,11 +237,6 @@ void FRandom::StaticClearRandom ()
 	{
 		rng->Init(rngseed);
 	}
-
-	for (FRandom* rng = FRandom::CRNGList; rng != NULL; rng = rng->Next)
-	{
-		rng->Init(rngseed);
-	}
 }
 
 //==========================================================================
@@ -358,15 +345,15 @@ void FRandom::StaticReadRNGState(FSerializer &arc)
 //
 //==========================================================================
 
-FRandom *FRandom::StaticFindRNG (const char *name, bool client)
+FRandom *FRandom::StaticFindRNG (const char *name)
 {
 	uint32_t NameCRC = CalcCRC32 ((const uint8_t *)name, (unsigned int)strlen (name));
 
 	// Use the default RNG if this one happens to have a CRC of 0.
-	if (NameCRC == 0) return client ? &M_Random : &pr_exrandom;
+	if (NameCRC == 0) return &pr_exrandom;
 
 	// Find the RNG in the list, sorted by CRC
-	FRandom **prev = (client ? &CRNGList : &RNGList), *probe = (client ? CRNGList : RNGList);
+	FRandom **prev = &RNGList, *probe = RNGList;
 
 	while (probe != NULL && probe->NameCRC < NameCRC)
 	{
@@ -377,32 +364,14 @@ FRandom *FRandom::StaticFindRNG (const char *name, bool client)
 	if (probe == NULL || probe->NameCRC != NameCRC)
 	{
 		// A matching RNG doesn't exist yet so create it.
-		probe = new FRandom(name, client);
+		probe = new FRandom(name);
 
 		// Store the new RNG for destruction when ZDoom quits.
-		if (client)
-			NewCRNGs.Push(probe);
-		else
-			NewRNGs.Push(probe);
+		NewRNGs.Push(probe);
 	}
 	return probe;
 }
 
-void FRandom::SaveRNGState(TArray<FRandom>& backups)
-{
-	for (auto cur = RNGList; cur != nullptr; cur = cur->Next)
-		backups.Push(*cur);
-}
-
-void FRandom::RestoreRNGState(TArray<FRandom>& backups)
-{
-	unsigned int i = 0u;
-	for (auto cur = RNGList; cur != nullptr; cur = cur->Next)
-		*cur = backups[i++];
-
-	backups.Clear();
-}
-
 //==========================================================================
 //
 // FRandom :: StaticPrintSeeds
diff --git a/source/common/engine/m_random.h b/source/common/engine/m_random.h
index ba0bcaf62..d9eec6c44 100644
--- a/source/common/engine/m_random.h
+++ b/source/common/engine/m_random.h
@@ -44,9 +44,9 @@ class FSerializer;
 class FRandom : public SFMTObj
 {
 public:
-	FRandom() : FRandom(false) {}
-	FRandom(const char* name) : FRandom(name, false) {}
-	~FRandom();
+	FRandom ();
+	FRandom (const char *name);
+	~FRandom ();
 
 	int Seed() const
 	{
@@ -170,34 +170,20 @@ public:
 	static void StaticClearRandom ();
 	static void StaticReadRNGState (FSerializer &arc);
 	static void StaticWriteRNGState (FSerializer &file);
-	static FRandom *StaticFindRNG(const char *name, bool client);
-	static void SaveRNGState(TArray<FRandom>& backups);
-	static void RestoreRNGState(TArray<FRandom>& backups);
+	static FRandom *StaticFindRNG(const char *name);
 
 #ifndef NDEBUG
 	static void StaticPrintSeeds ();
 #endif
 
-protected:
-	FRandom(bool client);
-	FRandom(const char* name, bool client);
-
 private:
 #ifndef NDEBUG
 	const char *Name;
 #endif
 	FRandom *Next;
 	uint32_t NameCRC;
-	bool bClient;
 
-	static FRandom *RNGList, *CRNGList;
-};
-
-class FCRandom : public FRandom
-{
-public:
-	FCRandom() : FRandom(true) {}
-	FCRandom(const char* name) : FRandom(name, true) {}
+	static FRandom *RNGList;
 };
 
 extern uint32_t rngseed;			// The starting seed (not part of state)
@@ -207,6 +193,6 @@ extern bool use_staticrng;
 
 
 // M_Random can be used for numbers that do not affect gameplay
-extern FCRandom M_Random;
+extern FRandom M_Random;
 
 #endif
diff --git a/source/common/engine/namedef.h b/source/common/engine/namedef.h
index be0c47649..13827382c 100644
--- a/source/common/engine/namedef.h
+++ b/source/common/engine/namedef.h
@@ -41,12 +41,6 @@ xx(Random2)
 xx(RandomPick)
 xx(FRandomPick)
 xx(SetRandomSeed)
-xx(CRandom)
-xx(CFRandom)
-xx(CRandom2)
-xx(CRandomPick)
-xx(CFRandomPick)
-xx(CSetRandomSeed)
 xx(BuiltinRandomSeed)
 xx(BuiltinNew)
 xx(GetClass)
diff --git a/source/common/engine/sc_man.cpp b/source/common/engine/sc_man.cpp
index 5af08fa57..ac7b4799f 100644
--- a/source/common/engine/sc_man.cpp
+++ b/source/common/engine/sc_man.cpp
@@ -124,7 +124,7 @@ FScanner::FScanner(int lumpnum, TMap<FName, Symbol>* extsymbols) : symbols(extsy
 
 void FScanner::Open (const char *name)
 {
-	int lump = fileSystem.CheckNumForAnyName(name);
+	int lump = fileSystem.CheckNumForFullName(name, true);
 	if (lump == -1)
 	{
 		I_Error("Could not find script lump '%s'\n", name);
diff --git a/source/common/engine/serializer.cpp b/source/common/engine/serializer.cpp
index 5c5c9f566..97ded1adf 100644
--- a/source/common/engine/serializer.cpp
+++ b/source/common/engine/serializer.cpp
@@ -58,6 +58,8 @@
 #include "vm.h"
 #include "i_interface.h"
 
+using namespace FileSys;
+
 extern DObject *WP_NOCHANGE;
 bool save_full = false;	// for testing. Should be removed afterward.
 
@@ -153,13 +155,13 @@ bool FSerializer::OpenReader(const char *buffer, size_t length)
 //
 //==========================================================================
 
-bool FSerializer::OpenReader(FileSys::FCompressedBuffer *input)
+bool FSerializer::OpenReader(FCompressedBuffer *input)
 {
 	if (input->mSize <= 0 || input->mBuffer == nullptr) return false;
 	if (w != nullptr || r != nullptr) return false;
 
 	mErrors = 0;
-	if (input->mMethod == FileSys::METHOD_STORED)
+	if (input->mMethod == METHOD_STORED)
 	{
 		r = new FReader((char*)input->mBuffer, input->mSize);
 	}
@@ -783,10 +785,10 @@ const char *FSerializer::GetOutput(unsigned *len)
 //
 //==========================================================================
 
-FileSys::FCompressedBuffer FSerializer::GetCompressedOutput()
+FCompressedBuffer FSerializer::GetCompressedOutput()
 {
 	if (isReading()) return{ 0,0,0,0,0,nullptr };
-	FileSys::FCompressedBuffer buff;
+	FCompressedBuffer buff;
 	WriteObjects();
 	EndObject();
 	buff.filename = nullptr;
@@ -825,7 +827,7 @@ FileSys::FCompressedBuffer FSerializer::GetCompressedOutput()
 	if (err == Z_OK)
 	{
 		buff.mBuffer = new char[buff.mCompressedSize];
-		buff.mMethod = FileSys::METHOD_DEFLATE;
+		buff.mMethod = METHOD_DEFLATE;
 		memcpy(buff.mBuffer, compressbuf, buff.mCompressedSize);
 		delete[] compressbuf;
 		return buff;
@@ -834,7 +836,7 @@ FileSys::FCompressedBuffer FSerializer::GetCompressedOutput()
 error:
 	memcpy(compressbuf, w->mOutString.GetString(), buff.mSize + 1);
 	buff.mCompressedSize = buff.mSize;
-	buff.mMethod = FileSys::METHOD_STORED;
+	buff.mMethod = METHOD_STORED;
 	return buff;
 }
 
@@ -1179,7 +1181,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe
 
 			if (TexMan.GetLinkedTexture(lump) == pic)
 			{
-				name = fileSystem.GetFileName(lump);
+				name = fileSystem.GetFileFullName(lump);
 			}
 			else
 			{
diff --git a/source/common/engine/serializer.h b/source/common/engine/serializer.h
index fb906aee8..40b6ab706 100644
--- a/source/common/engine/serializer.h
+++ b/source/common/engine/serializer.h
@@ -252,7 +252,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, struct ModelOverride &
 FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimModelOverride &mo, struct AnimModelOverride *def);
 FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnim &ao, ModelAnim *def);
 FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnimFrame &ao, ModelAnimFrame *def);
-FSerializer &Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval);
+FSerializer& Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval);
 
 void SerializeFunctionPointer(FSerializer &arc, const char *key, FunctionPointerValue *&p);
 
diff --git a/source/common/engine/st_start.h b/source/common/engine/st_start.h
index b09991d04..a8b78aaa8 100644
--- a/source/common/engine/st_start.h
+++ b/source/common/engine/st_start.h
@@ -55,7 +55,6 @@ public:
 	virtual void NetInit(const char *message, int num_players) {}
 	virtual void NetProgress(int count) {}
 	virtual void NetDone() {}
-	virtual void NetClose() {}
 	virtual bool NetLoop(bool (*timer_callback)(void *), void *userdata) { return false; }
 	virtual void AppendStatusLine(const char* status) {}
 	virtual void LoadingStatus(const char* message, int colors) {}
@@ -75,7 +74,6 @@ public:
 	void NetProgress(int count);
 	void NetMessage(const char* format, ...);	// cover for printf
 	void NetDone();
-	void NetClose();
 	bool NetLoop(bool (*timer_callback)(void*), void* userdata);
 protected:
 	int NetMaxPos, NetCurPos;
diff --git a/source/common/engine/stringtable.cpp b/source/common/engine/stringtable.cpp
index d748157db..d9d739b57 100644
--- a/source/common/engine/stringtable.cpp
+++ b/source/common/engine/stringtable.cpp
@@ -47,7 +47,7 @@
 //
 //==========================================================================
 
-void FStringTable::LoadStrings (FileSystem& fileSystem, const char *language)
+void FStringTable::LoadStrings (FileSys::FileSystem& fileSystem, const char *language)
 {
 	int lastlump, lump;
 
diff --git a/source/common/engine/stringtable.h b/source/common/engine/stringtable.h
index 193045d9f..181d8c7f3 100644
--- a/source/common/engine/stringtable.h
+++ b/source/common/engine/stringtable.h
@@ -84,7 +84,7 @@ public:
 	using LangMap = TMap<uint32_t, StringMap>;
 	using StringMacroMap = TMap<FName, StringMacro>;
 
-	void LoadStrings(FileSystem& fileSystem, const char *language);
+	void LoadStrings(FileSys::FileSystem& fileSystem, const char *language);
 	void UpdateLanguage(const char* language);
 	StringMap GetDefaultStrings() { return allStrings[default_table]; }	// Dehacked needs these for comparison
 	void SetOverrideStrings(StringMap & map)
diff --git a/source/common/filesystem/include/fs_filesystem.h b/source/common/filesystem/include/fs_filesystem.h
index 8747c2499..858b74837 100644
--- a/source/common/filesystem/include/fs_filesystem.h
+++ b/source/common/filesystem/include/fs_filesystem.h
@@ -13,6 +13,15 @@
 
 namespace FileSys {
 	
+union LumpShortName
+{
+	char		String[9];
+
+	uint32_t		dword;			// These are for accessing the first 4 or 8 chars of
+	uint64_t		qword;			// Name as a unit without breaking strict aliasing rules
+};
+
+
 struct FolderEntry
 {
 	const char *name;
@@ -23,34 +32,46 @@ class FileSystem
 {
 public:
 	FileSystem();
-	virtual ~FileSystem ();
-	// do not copy!
-	FileSystem(const FileSystem& other) = delete;
-	FileSystem& operator =(const FileSystem& other) = delete;
+	~FileSystem ();
 
-	bool Initialize(std::vector<std::string>& filenames, FileSystemFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, bool allowduplicates = false);
+	// The wadnum for the IWAD
+	int GetIwadNum() { return IwadIndex; }
+	void SetIwadNum(int x) { IwadIndex = x; }
 
-	// The container for the IWAD
-	int GetBaseNum() { return BaseIndex; }
-	void SetBaseNum(int x) { BaseIndex = x; }
+	int GetMaxIwadNum() { return MaxIwadIndex; }
+	void SetMaxIwadNum(int x) { MaxIwadIndex = x; }
 
-	int GetMaxBaseNum() { return MaxBaseIndex; }
-	void SetMaxBaseNum(int x) { MaxBaseIndex = x; }
-
-	int CheckIfContainerLoaded (const char *name) noexcept;
+	bool InitSingleFile(const char *filename, FileSystemMessageFunc Printf = nullptr);
+	bool InitMultipleFiles (std::vector<std::string>& filenames, LumpFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, bool allowduplicates = false, FILE* hashfile = nullptr);
+	void AddFile (const char *filename, FileReader *wadinfo, LumpFilterInfo* filter, FileSystemMessageFunc Printf, FILE* hashfile);
+	int CheckIfResourceFileLoaded (const char *name) noexcept;
 	void AddAdditionalFile(const char* filename, FileReader* wadinfo = NULL) {}
 
-	const char *GetContainerName (int container) const noexcept;
-	const char *GetContainerFullName (int container) const noexcept;
+	const char *GetResourceFileName (int filenum) const noexcept;
+	const char *GetResourceFileFullName (int wadnum) const noexcept;
 
-	int GetFirstEntry(int container) const noexcept;
-	int GetLastEntry(int container) const noexcept;
-    int GetEntryCount(int container) const noexcept;
-	int GetContainerFlags(int container) const noexcept;
+	int GetFirstEntry(int wadnum) const noexcept;
+	int GetLastEntry(int wadnum) const noexcept;
+    int GetEntryCount(int wadnum) const noexcept;
 
-	int FindFile (const char *cname, bool ignoreext = false) const;
-	int GetFileInContainer (const char *name, int wadfile) const;
-	int GetFile (const char *name) const;
+	int CheckNumForName (const char *name, int namespc) const;
+	int CheckNumForName (const char *name, int namespc, int wadfile, bool exact = true) const;
+	int GetNumForName (const char *name, int namespc) const;
+
+	inline int CheckNumForName (const uint8_t *name) const { return CheckNumForName ((const char *)name, ns_global); }
+	inline int CheckNumForName (const char *name) const { return CheckNumForName (name, ns_global); }
+	inline int CheckNumForName (const uint8_t *name, int ns) const { return CheckNumForName ((const char *)name, ns); }
+	inline int GetNumForName (const char *name) const { return GetNumForName (name, ns_global); }
+	inline int GetNumForName (const uint8_t *name) const { return GetNumForName ((const char *)name); }
+	inline int GetNumForName (const uint8_t *name, int ns) const { return GetNumForName ((const char *)name, ns); }
+
+	int CheckNumForFullName (const char *cname, bool trynormal = false, int namespc = ns_global, bool ignoreext = false) const;
+	int CheckNumForFullName (const char *name, int wadfile) const;
+	int GetNumForFullName (const char *name) const;
+	int FindFile(const char* name) const
+	{
+		return CheckNumForFullName(name);
+	}
 
 	bool FileExists(const char* name) const
 	{
@@ -62,106 +83,101 @@ public:
 		return FindFile(name.c_str()) >= 0;
 	}
 
+	LumpShortName& GetShortName(int i);	// may only be called before the hash chains are set up.
 	void RenameFile(int num, const char* fn);
 	bool CreatePathlessCopy(const char* name, int id, int flags);
 
-	void ReadFile (int filenum, void *dest);
+	void ReadFile (int lump, void *dest);
 	// These should only be used if the file data really needs padding.
-	FileData ReadFile (int filenum);
-	FileData ReadFileFullName(const char* name) { return ReadFile(GetFile(name)); }
+	FileData ReadFile (int lump);
+	FileData ReadFile (const char *name) { return ReadFile (GetNumForName (name)); }
+	FileData ReadFileFullName(const char* name) { return ReadFile(GetNumForFullName(name)); }
 
-	FileReader OpenFileReader(int filenum, int readertype, int readerflags);		// opens a reader that redirects to the containing file's one.
+	FileReader OpenFileReader(int lump, int readertype, int readerflags);		// opens a reader that redirects to the containing file's one.
 	FileReader OpenFileReader(const char* name);
 	FileReader ReopenFileReader(const char* name, bool alwayscache = false);
-	FileReader OpenFileReader(int filenum)
+	FileReader OpenFileReader(int lump)
 	{
-		return OpenFileReader(filenum, READER_SHARED, READERFLAG_SEEKABLE);
+		return OpenFileReader(lump, READER_SHARED, READERFLAG_SEEKABLE);
 	}
 
-	FileReader ReopenFileReader(int filenum, bool alwayscache = false)
+	FileReader ReopenFileReader(int lump, bool alwayscache = false)
 	{
-		return OpenFileReader(filenum, alwayscache ? READER_CACHED : READER_NEW, READERFLAG_SEEKABLE);
+		return OpenFileReader(lump, alwayscache ? READER_CACHED : READER_NEW, READERFLAG_SEEKABLE);
 	}
 
 
+	int FindLump (const char *name, int *lastlump, bool anyns=false);		// [RH] Find lumps with duplication
+	int FindLumpMulti (const char **names, int *lastlump, bool anyns = false, int *nameindex = NULL); // same with multiple possible names
 	int FindLumpFullName(const char* name, int* lastlump, bool noext = false);
+	bool CheckFileName (int lump, const char *name);	// [RH] True if lump's name == name
 
 	int FindFileWithExtensions(const char* name, const char* const* exts, int count) const;
 	int FindResource(int resid, const char* type, int filenum = -1) const noexcept;
 	int GetResource(int resid, const char* type, int filenum = -1) const;
 
 
-	ptrdiff_t FileLength(int filenum) const;
-	int GetFileFlags (int filenum);					// Return the flags for this filenum
-	const char* GetFileName(int filenum) const;	// Gets uninterpreted name from the FResourceFile
-	std::string GetFileFullPath (int filenum) const;		// [RH] Returns wad's name + filenum's full name
-	int GetFileContainer (int filenum) const;			
-	// [RH] Returns container for a specified filenum
-	int GetResourceId(int filenum) const;				// Returns the RFF index number for this filenum
-	const char* GetResourceType(int filenum) const;
+	static uint32_t LumpNameHash (const char *name);		// [RH] Create hash key from an 8-char name
+
+	ptrdiff_t FileLength (int lump) const;
+	int GetFileFlags (int lump);					// Return the flags for this lump
+	const char* GetFileShortName(int lump) const;
+	const char *GetFileFullName (int lump, bool returnshort = true) const;	// [RH] Returns the lump's full name
+	std::string GetFileFullPath (int lump) const;		// [RH] Returns wad's name + lump's full name
+	int GetFileContainer (int lump) const;				// [RH] Returns wadnum for a specified lump
+	int GetFileNamespace (int lump) const;			// [RH] Returns the namespace a lump belongs to
+	void SetFileNamespace(int lump, int ns);
+	int GetResourceId(int lump) const;				// Returns the RFF index number for this lump
+	const char* GetResourceType(int lump) const;
+	bool CheckFileName (int lump, const char *name) const;	// [RH] Returns true if the names match
 	unsigned GetFilesInFolder(const char *path, std::vector<FolderEntry> &result, bool atomic) const;
 
-	int GetFileCount() const
+	int GetNumEntries() const
 	{
 		return NumEntries;
 	}
 
-	int GetContainerCount() const
+	int GetNumWads() const
 	{
 		return (int)Files.size();
 	}
 
 	int AddFromBuffer(const char* name, char* data, int size, int id, int flags);
-	FileReader* GetFileReader(int container);	// Gets a FileReader object to the entire WAD
+	FileReader* GetFileReader(int wadnum);	// Gets a FileReader object to the entire WAD
+	void InitHashChains();
 
 protected:
 
 	struct LumpRecord;
-	const uint32_t NULL_INDEX = 0xffffffff;
 
 	std::vector<FResourceFile *> Files;
 	std::vector<LumpRecord> FileInfo;
 
 	std::vector<uint32_t> Hashes;	// one allocation for all hash lists.
+	uint32_t *FirstLumpIndex = nullptr;	// [RH] Hashing stuff moved out of lumpinfo structure
+	uint32_t *NextLumpIndex = nullptr;
 
-	uint32_t *FirstFileIndex_FullName = nullptr;	// The same information for fully qualified paths from .zips
-	uint32_t *NextFileIndex_FullName = nullptr;
+	uint32_t *FirstLumpIndex_FullName = nullptr;	// The same information for fully qualified paths from .zips
+	uint32_t *NextLumpIndex_FullName = nullptr;
 
-	uint32_t *FirstFileIndex_NoExt = nullptr;	// The same information for fully qualified paths from .zips
-	uint32_t *NextFileIndex_NoExt = nullptr;
+	uint32_t *FirstLumpIndex_NoExt = nullptr;	// The same information for fully qualified paths from .zips
+	uint32_t *NextLumpIndex_NoExt = nullptr;
 
-	uint32_t* FirstFileIndex_ResId = nullptr;	// The same information for fully qualified paths from .zips
-	uint32_t* NextFileIndex_ResId = nullptr;
+	uint32_t* FirstLumpIndex_ResId = nullptr;	// The same information for fully qualified paths from .zips
+	uint32_t* NextLumpIndex_ResId = nullptr;
 
 	uint32_t NumEntries = 0;					// Not necessarily the same as FileInfo.Size()
 	uint32_t NumWads = 0;
 
-	int BaseIndex = -1;
-	int MaxBaseIndex = -1;
+	int IwadIndex = -1;
+	int MaxIwadIndex = -1;
 
 	StringPool* stringpool = nullptr;
 
 private:
 	void DeleteAll();
-	void MoveFilesInFolder(const char *);
-	void AddFile(const char* filename, FileReader* wadinfo, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf);
-protected:
-
-	// These two functions must be overridden by subclasses which want to extend the file system.
-	virtual bool InitFiles(std::vector<std::string>& filenames, FileSystemFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, bool allowduplicates = false);
-public:
-	virtual void InitHashChains();
+	void MoveLumpsInFolder(const char *);
 
 };
 
-//djb2 hash algorithm with case insensitivity hack
-inline static uint32_t MakeHash(const char* str, size_t length = SIZE_MAX)
-{
-	uint32_t hash = 5381;
-	uint32_t c;
-	while (length-- > 0 && (c = *str++)) hash = hash * 33 + (c | 32);
-	return hash;
-}
-
-
 }
\ No newline at end of file
diff --git a/source/common/filesystem/include/resourcefile.h b/source/common/filesystem/include/resourcefile.h
index 982579112..a23914444 100644
--- a/source/common/filesystem/include/resourcefile.h
+++ b/source/common/filesystem/include/resourcefile.h
@@ -16,7 +16,7 @@ std::string ExtractBaseName(const char* path, bool include_extension = false);
 void strReplace(std::string& str, const char* from, const char* to);
 
 // user context in which the file system gets opened. This also contains a few callbacks to avoid direct dependencies on the engine.
-struct FileSystemFilterInfo
+struct LumpFilterInfo
 {
 	std::vector<std::string> gameTypeFilter;	// this can contain multiple entries
 
@@ -45,10 +45,41 @@ using FileSystemMessageFunc = int(*)(FSMessageLevel msglevel, const char* format
 
 class FResourceFile;
 
+// [RH] Namespaces from BOOM.
+// These are needed here in the low level part so that WAD files can be properly set up.
+typedef enum {
+	ns_hidden = -1,
+
+	ns_global = 0,
+	ns_sprites,
+	ns_flats,
+	ns_colormaps,
+	ns_acslibrary,
+	ns_newtextures,
+	ns_bloodraw,	// no longer used - kept for ZScript.
+	ns_bloodsfx,	// no longer used - kept for ZScript.
+	ns_bloodmisc,	// no longer used - kept for ZScript.
+	ns_strifevoices,
+	ns_hires,
+	ns_voxels,
+
+	// These namespaces are only used to mark lumps in special subdirectories
+	// so that their contents doesn't interfere with the global namespace.
+	// searching for data in these namespaces works differently for lumps coming
+	// from Zips or other files.
+	ns_specialzipdirectory,
+	ns_sounds,
+	ns_patches,
+	ns_graphics,
+	ns_music,
+
+	ns_firstskin,
+} namespace_t;
 
 enum ELumpFlags
 {
-	RESFF_FULLPATH = 2,		// contains a full path.
+	RESFF_MAYBEFLAT = 1,	// might be a flat inside a WAD outside F_START/END
+	RESFF_FULLPATH = 2,		// contains a full path. This will trigger extended namespace checks when looking up short names.
 	RESFF_EMBEDDED = 4,		// marks an embedded resource file for later processing.
 	RESFF_SHORTNAME = 8,	// the stored name is a short extension-less name
 	RESFF_COMPRESSED = 16,	// compressed or encrypted, i.e. cannot be read with the container file's reader.
@@ -69,10 +100,11 @@ struct FResourceEntry
 	size_t CompressedSize;
 	const char* FileName;
 	size_t Position;
-	int ResourceID; // Only Blood RFF uses this natively.
+	int ResourceID;
 	uint32_t CRC32;
 	uint16_t Flags;
 	uint16_t Method;
+	int16_t Namespace;
 };
 
 void SetMainThread();
@@ -80,25 +112,17 @@ void SetMainThread();
 class FResourceFile
 {
 public:
-	enum
-	{
-		// descibes which kind of limitations this file type has.
-		NO_FOLDERS = 1,			// no paths - only one rpot folder (e.g. Build GRP, Blood RFF, Descent HOG
-		NO_EXTENSIONS = 2,		// no extensions for file names (e.g. Doom WADs.)
-		SHORTNAMES = 4,			// Name is (at most) 8.3 DOS ASCII format, (8.0 in case of WAD)
-	};
-	FResourceFile(const char* filename, StringPool* sp, int flags);
-	FResourceFile(const char* filename, FileReader& r, StringPool* sp, int flags);
-	const char* NormalizeFileName(const char* fn, int fallbackcp = 0, bool allowbackslash = false);
+	FResourceFile(const char* filename, StringPool* sp);
+	FResourceFile(const char* filename, FileReader& r, StringPool* sp);
+	const char* NormalizeFileName(const char* fn, int fallbackcp = 0);
 	FResourceEntry* AllocateEntries(int count);
 	void GenerateHash();
-	void PostProcessArchive(FileSystemFilterInfo* filter);
+	void PostProcessArchive(LumpFilterInfo* filter);
 protected:
 	FileReader Reader;
 	const char* FileName;
 	FResourceEntry* Entries = nullptr;
 	uint32_t NumLumps;
-	int flags = 0;
 	char Hash[48];
 	StringPool* stringpool;
 
@@ -108,7 +132,7 @@ protected:
 		Entries[entry].Flags &= ~RESFF_NEEDFILESTART;
 	}
 	bool IsFileInFolder(const char* const resPath);
-	void CheckEmbedded(uint32_t entry, FileSystemFilterInfo* lfi);
+	void CheckEmbedded(uint32_t entry, LumpFilterInfo* lfi);
 
 private:
 	uint32_t FirstLump;
@@ -116,20 +140,19 @@ private:
 	int FilterLumps(const std::string& filtername, uint32_t max);
 	bool FindPrefixRange(const char* filter, uint32_t max, uint32_t &start, uint32_t &end);
 	void JunkLeftoverFilters(uint32_t max);
-	void FindCommonFolder(FileSystemFilterInfo* filter);
-	static FResourceFile *DoOpenResourceFile(const char *filename, FileReader &file, bool containeronly, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+	void FindCommonFolder(LumpFilterInfo* filter);
+	static FResourceFile *DoOpenResourceFile(const char *filename, FileReader &file, bool containeronly, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
 
 public:
-	static FResourceFile *OpenResourceFile(const char *filename, FileReader &file, bool containeronly = false, FileSystemFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, StringPool* sp = nullptr);
-	static FResourceFile *OpenResourceFile(const char *filename, bool containeronly = false, FileSystemFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, StringPool* sp = nullptr);
-	static FResourceFile *OpenDirectory(const char *filename, FileSystemFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, StringPool* sp = nullptr);
+	static FResourceFile *OpenResourceFile(const char *filename, FileReader &file, bool containeronly = false, LumpFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, StringPool* sp = nullptr);
+	static FResourceFile *OpenResourceFile(const char *filename, bool containeronly = false, LumpFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, StringPool* sp = nullptr);
+	static FResourceFile *OpenDirectory(const char *filename, LumpFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, StringPool* sp = nullptr);
 	virtual ~FResourceFile();
     // If this FResourceFile represents a directory, the Reader object is not usable so don't return it.
 	FileReader *GetContainerReader() { return Reader.isOpen()? &Reader : nullptr; }
 	const char* GetFileName() const { return FileName; }
 	uint32_t GetFirstEntry() const { return FirstLump; }
-	int GetFlags() const noexcept { return flags; }
-	void SetFirstFile(uint32_t f) { FirstLump = f; }
+	void SetFirstLump(uint32_t f) { FirstLump = f; }
 	const char* GetHash() const { return Hash; }
 
 	int EntryCount() const { return NumLumps; }
@@ -152,6 +175,11 @@ public:
 		return (entry < NumLumps) ? Entries[entry].Flags : 0;
 	}
 
+	int GetEntryNamespace(uint32_t entry)
+	{
+		return (entry < NumLumps) ? Entries[entry].Namespace : (int)ns_hidden;
+	}
+
 	int GetEntryResourceID(uint32_t entry)
 	{
 		return (entry < NumLumps) ? Entries[entry].ResourceID : -1;
diff --git a/source/common/filesystem/source/file_7z.cpp b/source/common/filesystem/source/file_7z.cpp
index 6a5e135e9..21d6049e3 100644
--- a/source/common/filesystem/source/file_7z.cpp
+++ b/source/common/filesystem/source/file_7z.cpp
@@ -177,7 +177,7 @@ class F7ZFile : public FResourceFile
 
 public:
 	F7ZFile(const char * filename, FileReader &filer, StringPool* sp);
-	bool Open(FileSystemFilterInfo* filter, FileSystemMessageFunc Printf);
+	bool Open(LumpFilterInfo* filter, FileSystemMessageFunc Printf);
 	virtual ~F7ZFile();
 	FileData Read(uint32_t entry) override;
 	FileReader GetEntryReader(uint32_t entry, int, int) override;
@@ -192,7 +192,7 @@ public:
 //==========================================================================
 
 F7ZFile::F7ZFile(const char * filename, FileReader &filer, StringPool* sp)
-	: FResourceFile(filename, filer, sp, 0) 
+	: FResourceFile(filename, filer, sp) 
 {
 	Archive = nullptr;
 }
@@ -204,7 +204,7 @@ F7ZFile::F7ZFile(const char * filename, FileReader &filer, StringPool* sp)
 //
 //==========================================================================
 
-bool F7ZFile::Open(FileSystemFilterInfo *filter, FileSystemMessageFunc Printf)
+bool F7ZFile::Open(LumpFilterInfo *filter, FileSystemMessageFunc Printf)
 {
 	Archive = new C7zArchive(Reader);
 	int skipped = 0;
@@ -268,6 +268,7 @@ bool F7ZFile::Open(FileSystemFilterInfo *filter, FileSystemMessageFunc Printf)
 		Entries[j].Length = SzArEx_GetFileSize(archPtr, i);
 		Entries[j].Flags = RESFF_FULLPATH|RESFF_COMPRESSED;
 		Entries[j].ResourceID = -1;
+		Entries[j].Namespace = ns_global;
 		Entries[j].Method = METHOD_INVALID;
 		Entries[j].Position = i;
 		j++;
@@ -350,7 +351,7 @@ FileReader F7ZFile::GetEntryReader(uint32_t entry, int, int)
 //
 //==========================================================================
 
-FResourceFile *Check7Z(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *Check7Z(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	char head[k7zSignatureSize];
 
diff --git a/source/common/filesystem/source/file_directory.cpp b/source/common/filesystem/source/file_directory.cpp
index 40b4df2f9..ca87fd9ea 100644
--- a/source/common/filesystem/source/file_directory.cpp
+++ b/source/common/filesystem/source/file_directory.cpp
@@ -57,11 +57,11 @@ class FDirectory : public FResourceFile
 	const char** SystemFilePath;
 
 
-	int AddDirectory(const char* dirpath, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf);
+	int AddDirectory(const char* dirpath, LumpFilterInfo* filter, FileSystemMessageFunc Printf);
 
 public:
 	FDirectory(const char * dirname, StringPool* sp, bool nosubdirflag = false);
-	bool Open(FileSystemFilterInfo* filter, FileSystemMessageFunc Printf);
+	bool Open(LumpFilterInfo* filter, FileSystemMessageFunc Printf);
 	FileReader GetEntryReader(uint32_t entry, int, int) override;
 };
 
@@ -74,7 +74,7 @@ public:
 //==========================================================================
 
 FDirectory::FDirectory(const char * directory, StringPool* sp, bool nosubdirflag)
-	: FResourceFile("", sp, 0), nosubdir(nosubdirflag)
+	: FResourceFile("", sp), nosubdir(nosubdirflag)
 {
 	auto fn = FS_FullPath(directory);
 	if (fn.back() != '/') fn += '/';
@@ -87,7 +87,7 @@ FDirectory::FDirectory(const char * directory, StringPool* sp, bool nosubdirflag
 //
 //==========================================================================
 
-int FDirectory::AddDirectory(const char *dirpath, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf)
+int FDirectory::AddDirectory(const char *dirpath, LumpFilterInfo* filter, FileSystemMessageFunc Printf)
 {
 	int count = 0;
 
@@ -141,6 +141,7 @@ int FDirectory::AddDirectory(const char *dirpath, FileSystemFilterInfo* filter,
 					Entries[count].Flags = RESFF_FULLPATH;
 					Entries[count].ResourceID = -1;
 					Entries[count].Method = METHOD_STORED;
+					Entries[count].Namespace = ns_global;
 					Entries[count].Position = count;
 					count++;
 				}
@@ -156,7 +157,7 @@ int FDirectory::AddDirectory(const char *dirpath, FileSystemFilterInfo* filter,
 //
 //==========================================================================
 
-bool FDirectory::Open(FileSystemFilterInfo* filter, FileSystemMessageFunc Printf)
+bool FDirectory::Open(LumpFilterInfo* filter, FileSystemMessageFunc Printf)
 {
 	NumLumps = AddDirectory(FileName, filter, Printf);
 	PostProcessArchive(filter);
@@ -192,7 +193,7 @@ FileReader FDirectory::GetEntryReader(uint32_t entry, int readertype, int)
 //
 //==========================================================================
 
-FResourceFile *CheckDir(const char *filename, bool nosubdirflag, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *CheckDir(const char *filename, bool nosubdirflag, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	auto rf = new FDirectory(filename, sp, nosubdirflag);
 	if (rf->Open(filter, Printf)) return rf;
diff --git a/source/common/filesystem/source/file_grp.cpp b/source/common/filesystem/source/file_grp.cpp
index 25f79c6ec..57206b4b1 100644
--- a/source/common/filesystem/source/file_grp.cpp
+++ b/source/common/filesystem/source/file_grp.cpp
@@ -71,7 +71,7 @@ struct GrpLump
 //
 //==========================================================================
 
-static bool OpenGrp(FResourceFile* file, FileSystemFilterInfo* filter)
+static bool OpenGrp(FResourceFile* file, LumpFilterInfo* filter)
 {
 	GrpHeader header;
 
@@ -91,6 +91,7 @@ static bool OpenGrp(FResourceFile* file, FileSystemFilterInfo* filter)
 		Entries[i].CompressedSize = Entries[i].Length = LittleLong(fileinfo[i].Size);
 		Position += fileinfo[i].Size;
 		Entries[i].Flags = 0;
+		Entries[i].Namespace = ns_global;
 		fileinfo[i].NameWithZero[12] = '\0';	// Be sure filename is null-terminated
 		Entries[i].ResourceID = -1;
 		Entries[i].Method = METHOD_STORED;
@@ -108,7 +109,7 @@ static bool OpenGrp(FResourceFile* file, FileSystemFilterInfo* filter)
 //
 //==========================================================================
 
-FResourceFile *CheckGRP(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *CheckGRP(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	char head[12];
 
@@ -119,7 +120,7 @@ FResourceFile *CheckGRP(const char *filename, FileReader &file, FileSystemFilter
 		file.Seek(0, FileReader::SeekSet);
 		if (!memcmp(head, "KenSilverman", 12))
 		{
-			auto rf = new FResourceFile(filename, file, sp, FResourceFile::NO_FOLDERS | FResourceFile::SHORTNAMES);
+			auto rf = new FResourceFile(filename, file, sp);
 			if (OpenGrp(rf, filter)) return rf;
 			file = rf->Destroy();
 		}
diff --git a/source/common/filesystem/source/file_hog.cpp b/source/common/filesystem/source/file_hog.cpp
index 0ed4fa7ce..d12f7e2d8 100644
--- a/source/common/filesystem/source/file_hog.cpp
+++ b/source/common/filesystem/source/file_hog.cpp
@@ -44,7 +44,7 @@ namespace FileSys {
 
 
 
-static bool OpenHog(FResourceFile* rf, FileSystemFilterInfo* filter)
+static bool OpenHog(FResourceFile* rf, LumpFilterInfo* filter)
 {
     auto Reader = rf->GetContainerReader();
     FileReader::Size length = Reader->GetLength();
@@ -66,6 +66,7 @@ static bool OpenHog(FResourceFile* rf, FileSystemFilterInfo* filter)
         Entry.CompressedSize = Entry.Length = elength;
         Entry.Flags = 0;
         Entry.CRC32 = 0;
+        Entry.Namespace = ns_global;
         Entry.ResourceID = -1;
         Entry.Method = METHOD_STORED;
         Entry.FileName = rf->NormalizeFileName(name);
@@ -85,7 +86,7 @@ static bool OpenHog(FResourceFile* rf, FileSystemFilterInfo* filter)
 //
 //==========================================================================
 
-FResourceFile* CheckHog(const char* filename, FileReader& file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile* CheckHog(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
     char head[3];
 
@@ -95,7 +96,7 @@ FResourceFile* CheckHog(const char* filename, FileReader& file, FileSystemFilter
         file.Read(&head, 3);
         if (!memcmp(head, "DHF", 3))
         {
-            auto rf = new FResourceFile(filename, file, sp, FResourceFile::NO_FOLDERS | FResourceFile::SHORTNAMES);
+            auto rf = new FResourceFile(filename, file, sp);
             if (OpenHog(rf, filter)) return rf;
             file = rf->Destroy();
         }
diff --git a/source/common/filesystem/source/file_hog2.cpp b/source/common/filesystem/source/file_hog2.cpp
deleted file mode 100644
index 10cc327cc..000000000
--- a/source/common/filesystem/source/file_hog2.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
-** file_hog.cpp
-**
-** reads Descent 3 .hog2 files
-**
-**---------------------------------------------------------------------------
-** Copyright 2024 Christoph Oelckers
-** All rights reserved.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions
-** are met:
-**
-** 1. Redistributions of source code must retain the above copyright
-**    notice, this list of conditions and the following disclaimer.
-** 2. Redistributions in binary form must reproduce the above copyright
-**    notice, this list of conditions and the following disclaimer in the
-**    documentation and/or other materials provided with the distribution.
-** 3. The name of the author may not be used to endorse or promote products
-**    derived from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**---------------------------------------------------------------------------
-**
-**
-*/
-
-
-#include "resourcefile.h"
-#include "fs_swap.h"
-
-namespace FileSys {
-    using namespace byteswap;
-
-
-
-static bool OpenHog2(FResourceFile* rf, FileSystemFilterInfo* filter)
-{
-    auto Reader = rf->GetContainerReader();
-    FileReader::Size length = Reader->GetLength();
-
-	uint32_t numfiles = Reader->ReadUInt32();
-	uint32_t offset = Reader->ReadUInt32();	    // offset to first file (end of file list)
-	Reader->Seek(56, FileReader::SeekEnd);		// filled with FF
-    auto Entries = rf->AllocateEntries((int)numfiles);
-	
-    for(uint32_t i = 0; i < numfiles; i++)
-	{
-        char name[37];
-		Reader->Read(name, 36);
-		name[36] = 0;
-		Reader->ReadUInt32();
-		uint32_t size = Reader->ReadUInt32();;
-		Reader->ReadUInt32();
-		
-		FResourceEntry& Entry = Entries[i];
-        Entry.Position = offset;
-        Entry.CompressedSize = Entry.Length = size;
-        Entry.Flags = RESFF_FULLPATH;
-        Entry.CRC32 = 0;
-        Entry.ResourceID = -1;
-        Entry.Method = METHOD_STORED;
-        Entry.FileName = rf->NormalizeFileName(name);
-		offset += size;
-    }
-    rf->GenerateHash();
-    return true;
-}
-
-
-//==========================================================================
-//
-// File open
-//
-//==========================================================================
-
-FResourceFile* CheckHog2(const char* filename, FileReader& file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
-{
-    char head[4];
-
-    if (file.GetLength() >= 68)
-    {
-        file.Seek(0, FileReader::SeekSet);
-        file.Read(&head, 4);
-        if (!memcmp(head, "HOG2", 4))
-        {
-            auto rf = new FResourceFile(filename, file, sp, 0);
-            if (OpenHog2(rf, filter)) return rf;
-            file = rf->Destroy();
-        }
-        file.Seek(0, FileReader::SeekSet);
-    }
-    return nullptr;
-}
-
-
-}
-
diff --git a/source/common/filesystem/source/file_lump.cpp b/source/common/filesystem/source/file_lump.cpp
index 37dee28bd..b01d46b5d 100644
--- a/source/common/filesystem/source/file_lump.cpp
+++ b/source/common/filesystem/source/file_lump.cpp
@@ -41,10 +41,11 @@ namespace FileSys {
 //
 //==========================================================================
 
-static bool OpenLump(FResourceFile* file, FileSystemFilterInfo*)
+static bool OpenLump(FResourceFile* file, LumpFilterInfo*)
 {
 	auto Entries = file->AllocateEntries(1);
 	Entries[0].FileName = file->NormalizeFileName(ExtractBaseName(file->GetFileName(), true).c_str());
+	Entries[0].Namespace = ns_global;
 	Entries[0].ResourceID = -1;
 	Entries[0].Position = 0;
 	Entries[0].CompressedSize = Entries[0].Length = file->GetContainerReader()->GetLength();
@@ -59,10 +60,10 @@ static bool OpenLump(FResourceFile* file, FileSystemFilterInfo*)
 //
 //==========================================================================
 
-FResourceFile *CheckLump(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *CheckLump(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	// always succeeds
-	auto rf = new FResourceFile(filename, file, sp, FResourceFile::NO_FOLDERS);
+	auto rf = new FResourceFile(filename, file, sp);
 	if (OpenLump(rf, filter)) return rf;
 	file = rf->Destroy();
 	return NULL;
diff --git a/source/common/filesystem/source/file_mvl.cpp b/source/common/filesystem/source/file_mvl.cpp
index c9ebf0f8c..f08db3161 100644
--- a/source/common/filesystem/source/file_mvl.cpp
+++ b/source/common/filesystem/source/file_mvl.cpp
@@ -43,7 +43,7 @@ namespace FileSys {
 
 
 
-static bool OpenMvl(FResourceFile* rf, FileSystemFilterInfo* filter)
+static bool OpenMvl(FResourceFile* rf, LumpFilterInfo* filter)
 {
     auto Reader = rf->GetContainerReader();
     auto count = Reader->ReadUInt32();
@@ -75,7 +75,7 @@ static bool OpenMvl(FResourceFile* rf, FileSystemFilterInfo* filter)
 //
 //==========================================================================
 
-FResourceFile* CheckMvl(const char* filename, FileReader& file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile* CheckMvl(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
     char head[4];
 
@@ -85,7 +85,7 @@ FResourceFile* CheckMvl(const char* filename, FileReader& file, FileSystemFilter
         file.Read(&head, 4);
         if (!memcmp(head, "DMVL", 4))
         {
-            auto rf = new FResourceFile(filename, file, sp, FResourceFile::NO_FOLDERS | FResourceFile::SHORTNAMES);
+            auto rf = new FResourceFile(filename, file, sp);
             if (OpenMvl(rf, filter)) return rf;
             file = rf->Destroy();
         }
diff --git a/source/common/filesystem/source/file_pak.cpp b/source/common/filesystem/source/file_pak.cpp
index 84265f2eb..b66a0f3d9 100644
--- a/source/common/filesystem/source/file_pak.cpp
+++ b/source/common/filesystem/source/file_pak.cpp
@@ -63,7 +63,7 @@ struct dpackheader_t
 //
 //==========================================================================
 
-static bool OpenPak(FResourceFile* file, FileSystemFilterInfo* filter)
+static bool OpenPak(FResourceFile* file, LumpFilterInfo* filter)
 {
 	dpackheader_t header;
 
@@ -82,6 +82,7 @@ static bool OpenPak(FResourceFile* file, FileSystemFilterInfo* filter)
 		Entries[i].Position = LittleLong(fileinfo[i].filepos);
 		Entries[i].CompressedSize = Entries[i].Length = LittleLong(fileinfo[i].filelen);
 		Entries[i].Flags = RESFF_FULLPATH;
+		Entries[i].Namespace = ns_global;
 		Entries[i].ResourceID = -1;
 		Entries[i].Method = METHOD_STORED;
 		Entries[i].FileName = file->NormalizeFileName(fileinfo[i].name);
@@ -98,7 +99,7 @@ static bool OpenPak(FResourceFile* file, FileSystemFilterInfo* filter)
 //
 //==========================================================================
 
-FResourceFile *CheckPak(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *CheckPak(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	char head[4];
 
@@ -109,7 +110,7 @@ FResourceFile *CheckPak(const char *filename, FileReader &file, FileSystemFilter
 		file.Seek(0, FileReader::SeekSet);
 		if (!memcmp(head, "PACK", 4))
 		{
-			auto rf = new FResourceFile(filename, file, sp, 0);
+			auto rf = new FResourceFile(filename, file, sp);
 			if (OpenPak(rf, filter)) return rf;
 			file = rf->Destroy();
 		}
diff --git a/source/common/filesystem/source/file_rff.cpp b/source/common/filesystem/source/file_rff.cpp
index e887b7fd7..673a08ce0 100644
--- a/source/common/filesystem/source/file_rff.cpp
+++ b/source/common/filesystem/source/file_rff.cpp
@@ -91,7 +91,7 @@ void BloodCrypt (void *data, int key, int len)
 //
 //==========================================================================
 
-static bool OpenRFF(FResourceFile* file, FileSystemFilterInfo*)
+static bool OpenRFF(FResourceFile* file, LumpFilterInfo*)
 {
 	RFFLump *lumps;
 	RFFInfo header;
@@ -123,6 +123,7 @@ static bool OpenRFF(FResourceFile* file, FileSystemFilterInfo*)
 			Entries[i].Flags = 0;
 			Entries[i].Method = METHOD_STORED;
 		}
+		Entries[i].Namespace = ns_global;
 		Entries[i].ResourceID = LittleLong(lumps[i].IndexNum);
 	
 		// Rearrange the name and extension to construct the fullname.
@@ -149,7 +150,7 @@ static bool OpenRFF(FResourceFile* file, FileSystemFilterInfo*)
 //
 //==========================================================================
 
-FResourceFile *CheckRFF(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *CheckRFF(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	char head[4];
 
@@ -160,7 +161,7 @@ FResourceFile *CheckRFF(const char *filename, FileReader &file, FileSystemFilter
 		file.Seek(0, FileReader::SeekSet);
 		if (!memcmp(head, "RFF\x1a", 4))
 		{
-			auto rf = new FResourceFile(filename, file, sp, FResourceFile::NO_FOLDERS | FResourceFile::SHORTNAMES);
+			auto rf = new FResourceFile(filename, file, sp);
 			if (OpenRFF(rf, filter)) return rf;
 			file = rf->Destroy();
 		}
diff --git a/source/common/filesystem/source/file_ssi.cpp b/source/common/filesystem/source/file_ssi.cpp
index 47c7b53b0..82885c6bb 100644
--- a/source/common/filesystem/source/file_ssi.cpp
+++ b/source/common/filesystem/source/file_ssi.cpp
@@ -44,7 +44,7 @@ namespace FileSys {
 //
 //==========================================================================
 
-static bool OpenSSI(FResourceFile* file, int version, int EntryCount, FileSystemFilterInfo*)
+static bool OpenSSI(FResourceFile* file, int version, int EntryCount, LumpFilterInfo*)
 {
 	uint32_t NumLumps = EntryCount * 2;
 	auto Entries = file->AllocateEntries(NumLumps);
@@ -65,6 +65,7 @@ static bool OpenSSI(FResourceFile* file, int version, int EntryCount, FileSystem
 		Entries[i].Position = j;
 		Entries[i].CompressedSize = Entries[i].Length = flength;
 		Entries[i].Flags = 0;
+		Entries[i].Namespace = ns_global;
 		Entries[i].Method = METHOD_STORED;
 		Entries[i].ResourceID = -1;
 		Entries[i].FileName = file->NormalizeFileName(fn);
@@ -77,6 +78,7 @@ static bool OpenSSI(FResourceFile* file, int version, int EntryCount, FileSystem
 		Entries[i + 1].Position = j;
 		Entries[i + 1].CompressedSize = Entries[i + 1].Length = flength;
 		Entries[i + 1].Flags = 0;
+		Entries[i + 1].Namespace = ns_global;
 		Entries[i + 1].ResourceID = -1;
 		Entries[i + 1].FileName = file->NormalizeFileName(fn);
 		Entries[i + 1].Method = METHOD_STORED;
@@ -97,7 +99,7 @@ static bool OpenSSI(FResourceFile* file, int version, int EntryCount, FileSystem
 //
 //==========================================================================
 
-FResourceFile* CheckSSI(const char* filename, FileReader& file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile* CheckSSI(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	char zerobuf[72];
 	char buf[72];
@@ -127,7 +129,7 @@ FResourceFile* CheckSSI(const char* filename, FileReader& file, FileSystemFilter
 			{
 				if (!skipstring(70)) return nullptr;
 			}
-			auto ssi = new FResourceFile(filename, file, sp, FResourceFile::NO_FOLDERS | FResourceFile::SHORTNAMES);
+			auto ssi = new FResourceFile(filename, file, sp);
 			if (OpenSSI(ssi, version, numfiles, filter)) return ssi;
 			file = ssi->Destroy();
 		}
diff --git a/source/common/filesystem/source/file_wad.cpp b/source/common/filesystem/source/file_wad.cpp
index 3fe736bb0..4f8574b73 100644
--- a/source/common/filesystem/source/file_wad.cpp
+++ b/source/common/filesystem/source/file_wad.cpp
@@ -58,22 +58,52 @@ struct wadlump_t
 	char		Name[8];
 };
 
+//==========================================================================
+//
+// Wad file
+//
+//==========================================================================
+
+class FWadFile : public FResourceFile
+{
+	bool IsMarker(int lump, const char *marker);
+	void SetNamespace(const char *startmarker, const char *endmarker, namespace_t space, FileSystemMessageFunc Printf, bool flathack=false);
+	void SkinHack (FileSystemMessageFunc Printf);
+
+public:
+	FWadFile(const char * filename, FileReader &file, StringPool* sp);
+	bool Open(LumpFilterInfo* filter, FileSystemMessageFunc Printf);
+};
+
+
+//==========================================================================
+//
+// FWadFile::FWadFile
+//
+// Initializes a WAD file
+//
+//==========================================================================
+
+FWadFile::FWadFile(const char *filename, FileReader &file, StringPool* sp)
+	: FResourceFile(filename, file, sp)
+{
+}
+
 //==========================================================================
 //
 // Open it
 //
 //==========================================================================
 
-static bool OpenWAD(FResourceFile* file, FileSystemFilterInfo*, FileSystemMessageFunc Printf)
+bool FWadFile::Open(LumpFilterInfo*, FileSystemMessageFunc Printf)
 {
 	wadinfo_t header;
 	uint32_t InfoTableOfs;
 	bool isBigEndian = false; // Little endian is assumed until proven otherwise
-	auto Reader = file->GetContainerReader();
-	auto wadSize = Reader->GetLength();
+	auto wadSize = Reader.GetLength();
 
-	Reader->Read(&header, sizeof(header));
-	uint32_t NumLumps = LittleLong(header.NumLumps);
+	Reader.Read(&header, sizeof(header));
+	NumLumps = LittleLong(header.NumLumps);
 	InfoTableOfs = LittleLong(header.InfoTableOfs);
 
 	// Check to see if the little endian interpretation is valid
@@ -87,16 +117,16 @@ static bool OpenWAD(FResourceFile* file, FileSystemFilterInfo*, FileSystemMessag
 		// Check again to detect broken wads
 		if (InfoTableOfs + NumLumps*sizeof(wadlump_t) > (unsigned)wadSize)
 		{
-			Printf(FSMessageLevel::Error, "%s: Bad directory offset.\n", file->GetFileName());
+			Printf(FSMessageLevel::Error, "%s: Bad directory offset.\n", FileName);
 			return false;
 		}
 	}
 
-	Reader->Seek(InfoTableOfs, FileReader::SeekSet);
-	auto fd = Reader->Read(NumLumps * sizeof(wadlump_t));
+	Reader.Seek(InfoTableOfs, FileReader::SeekSet);
+	auto fd = Reader.Read(NumLumps * sizeof(wadlump_t));
 	auto fileinfo = (const wadlump_t*)fd.data();
 
-	auto Entries = file->AllocateEntries(NumLumps);
+	AllocateEntries(NumLumps);
 
 	for(uint32_t i = 0; i < NumLumps; i++)
 	{
@@ -113,17 +143,17 @@ static bool OpenWAD(FResourceFile* file, FileSystemFilterInfo*, FileSystemMessag
 		else if (ishigh > 1)
 		{
 			// This may not end up printing something proper because we do not know what encoding might have been used.
-			Printf(FSMessageLevel::Warning, "%s: Lump name %.8s contains invalid characters\n", file->GetFileName(), fileinfo[i].Name);
-			n[0] = 0;
+			Printf(FSMessageLevel::Warning, "%s: Lump name %.8s contains invalid characters\n", FileName, fileinfo[i].Name);
 		}
 
 		Entries[i].FileName = nullptr;
 		Entries[i].Position = isBigEndian ? BigLong(fileinfo[i].FilePos) : LittleLong(fileinfo[i].FilePos);
 		Entries[i].CompressedSize = Entries[i].Length = isBigEndian ? BigLong(fileinfo[i].Size) : LittleLong(fileinfo[i].Size);
 
+		Entries[i].Namespace = ns_global;
 		Entries[i].Flags = ishigh? RESFF_SHORTNAME | RESFF_COMPRESSED : RESFF_SHORTNAME;
 		Entries[i].Method = ishigh == 1? METHOD_LZSS : METHOD_STORED;
-		Entries[i].FileName = file->NormalizeFileName(n, 0, true);
+		Entries[i].FileName = stringpool->Strdup(n);
 		// This doesn't set up the namespace yet.
 	}
 	for (uint32_t i = 0; i < NumLumps; i++)
@@ -131,21 +161,250 @@ static bool OpenWAD(FResourceFile* file, FileSystemFilterInfo*, FileSystemMessag
 		if (Entries[i].Method == METHOD_LZSS)
 		{
 			// compressed size is implicit.
-			Entries[i].CompressedSize = (i == NumLumps - 1 ? Reader->GetLength() : Entries[i + 1].Position) - Entries[i].Position;
+			Entries[i].CompressedSize = (i == NumLumps - 1 ? Reader.GetLength() : Entries[i + 1].Position) - Entries[i].Position;
 		}
 	}
 
-	file->GenerateHash(); // Do this before the lump processing below.
+
+	GenerateHash(); // Do this before the lump processing below.
+
+	SetNamespace("s_start", "s_end", ns_sprites, Printf);
+	SetNamespace("f_start", "f_end", ns_flats, Printf, true);
+	SetNamespace("c_start", "c_end", ns_colormaps, Printf);
+	SetNamespace("a_start", "a_end", ns_acslibrary, Printf);
+	SetNamespace("tx_start", "tx_end", ns_newtextures, Printf);
+	SetNamespace("v_start", "v_end", ns_strifevoices, Printf);
+	SetNamespace("hi_start", "hi_end", ns_hires, Printf);
+	SetNamespace("vx_start", "vx_end", ns_voxels, Printf);
+	SkinHack(Printf);
+
 	return true;
 }
 
+//==========================================================================
+//
+// IsMarker
+//
+// (from BOOM)
+//
+//==========================================================================
+
+inline bool FWadFile::IsMarker(int lump, const char *marker)
+{
+	if (Entries[lump].FileName[0] == marker[0])
+	{
+		return (!strcmp(Entries[lump].FileName, marker) ||
+			(marker[1] == '_' && !strcmp(Entries[lump].FileName +1, marker)));
+	}
+	else return false;
+}
+
+//==========================================================================
+//
+// SetNameSpace
+//
+// Sets namespace information for the lumps. It always looks for the first
+// x_START and the last x_END lump, except when loading flats. In this case
+// F_START may be absent and if that is the case all lumps with a size of
+// 4096 will be flagged appropriately.
+//
+//==========================================================================
+
+// This class was supposed to be local in the function but GCC
+// does not like that.
+struct Marker
+{
+	int markertype;
+	unsigned int index;
+};
+
+void FWadFile::SetNamespace(const char *startmarker, const char *endmarker, namespace_t space, FileSystemMessageFunc Printf, bool flathack)
+{
+	bool warned = false;
+	int numstartmarkers = 0, numendmarkers = 0;
+	unsigned int i;
+	std::vector<Marker> markers;
+
+	for(i = 0; i < NumLumps; i++)
+	{
+		if (IsMarker(i, startmarker))
+		{
+			Marker m = { 0, i };
+			markers.push_back(m);
+			numstartmarkers++;
+		}
+		else if (IsMarker(i, endmarker))
+		{
+			Marker m = { 1, i };
+			markers.push_back(m);
+			numendmarkers++;
+		}
+	}
+
+	if (numstartmarkers == 0)
+	{
+		if (numendmarkers == 0) return;	// no markers found
+
+		Printf(FSMessageLevel::Warning, "%s: %s marker without corresponding %s found.\n", FileName, endmarker, startmarker);
+
+
+		if (flathack)
+		{
+			// We have found no F_START but one or more F_END markers.
+			// mark all lumps before the last F_END marker as potential flats.
+			unsigned int end = markers[markers.size()-1].index;
+			for(unsigned int ii = 0; ii < end; ii++)
+			{
+				if (Entries[ii].Length == 4096)
+				{
+					// We can't add this to the flats namespace but 
+					// it needs to be flagged for the texture manager.
+					Printf(FSMessageLevel::DebugNotify, "%s: Marking %s as potential flat\n", FileName, Entries[ii].FileName);
+					Entries[ii].Flags |= RESFF_MAYBEFLAT;
+				}
+			}
+		}
+		return;
+	}
+
+	i = 0;
+	while (i < markers.size())
+	{
+		int start, end;
+		if (markers[i].markertype != 0)
+		{
+			Printf(FSMessageLevel::Warning, "%s: %s marker without corresponding %s found.\n", FileName, endmarker, startmarker);
+			i++;
+			continue;
+		}
+		start = i++;
+
+		// skip over subsequent x_START markers
+		while (i < markers.size() && markers[i].markertype == 0)
+		{
+			Printf(FSMessageLevel::Warning, "%s: duplicate %s marker found.\n", FileName, startmarker);
+			i++;
+			continue;
+		}
+		// same for x_END markers
+		while (i < markers.size()-1 && (markers[i].markertype == 1 && markers[i+1].markertype == 1))
+		{
+			Printf(FSMessageLevel::Warning, "%s: duplicate %s marker found.\n", FileName, endmarker);
+			i++;
+			continue;
+		}
+		// We found a starting marker but no end marker. Ignore this block.
+		if (i >= markers.size())
+		{
+			Printf(FSMessageLevel::Warning, "%s: %s marker without corresponding %s found.\n", FileName, startmarker, endmarker);
+			end = NumLumps;
+		}
+		else
+		{
+			end = markers[i++].index;
+		}
+
+		// we found a marked block
+		Printf(FSMessageLevel::DebugNotify, "%s: Found %s block at (%d-%d)\n", FileName, startmarker, markers[start].index, end);
+		for(int j = markers[start].index + 1; j < end; j++)
+		{
+			if (Entries[j].Namespace != ns_global)
+			{
+				if (!warned)
+				{
+					Printf(FSMessageLevel::Warning, "%s: Overlapping namespaces found (lump %d)\n", FileName, j);
+				}
+				warned = true;
+			}
+			else if (space == ns_sprites && Entries[j].Length < 8)
+			{
+				// sf 26/10/99:
+				// ignore sprite lumps smaller than 8 bytes (the smallest possible)
+				// in size -- this was used by some dmadds wads
+				// as an 'empty' graphics resource
+				Printf(FSMessageLevel::DebugWarn, "%s: Skipped empty sprite %s (lump %d)\n", FileName, Entries[j].FileName, j);
+			}
+			else
+			{
+				Entries[j].Namespace = space;
+			}
+		}
+	}
+}
+
+
+//==========================================================================
+//
+// W_SkinHack
+//
+// Tests a wad file to see if it contains an S_SKIN marker. If it does,
+// every lump in the wad is moved into a new namespace. Because skins are
+// only supposed to replace player sprites, sounds, or faces, this should
+// not be a problem. Yes, there are skins that replace more than that, but
+// they are such a pain, and breaking them like this was done on purpose.
+// This also renames any S_SKINxx lumps to just S_SKIN.
+//
+//==========================================================================
+
+void FWadFile::SkinHack (FileSystemMessageFunc Printf)
+{
+	// this being static is not a problem. The only relevant thing is that each skin gets a different number.
+	static int namespc = ns_firstskin;
+	bool skinned = false;
+	bool hasmap = false;
+	uint32_t i;
+
+	for (i = 0; i < NumLumps; i++)
+	{
+		auto lump = &Entries[i];
+
+		if (!strnicmp(lump->FileName, "S_SKIN", 6))
+		{ // Wad has at least one skin.
+			lump->FileName = "S_SKIN";
+			if (!skinned)
+			{
+				skinned = true;
+				uint32_t j;
+
+				for (j = 0; j < NumLumps; j++)
+				{
+					Entries[j].Namespace = namespc;
+				}
+				namespc++;
+			}
+		}
+		// needless to say, this check is entirely useless these days as map names can be more diverse..
+		if ((lump->FileName[0] == 'M' &&
+			 lump->FileName[1] == 'A' &&
+			 lump->FileName[2] == 'P' &&
+			 lump->FileName[3] >= '0' && lump->FileName[3] <= '9' &&
+			 lump->FileName[4] >= '0' && lump->FileName[4] <= '9' &&
+			 lump->FileName[5] == '\0')
+			||
+			(lump->FileName[0] == 'E' &&
+			 lump->FileName[1] >= '0' && lump->FileName[1] <= '9' &&
+			 lump->FileName[2] == 'M' &&
+			 lump->FileName[3] >= '0' && lump->FileName[3] <= '9' &&
+			 lump->FileName[4] == '\0'))
+		{
+			hasmap = true;
+		}
+	}
+	if (skinned && hasmap)
+	{
+		Printf(FSMessageLevel::Attention, "%s: The maps will not be loaded because it has a skin.\n", FileName);
+		Printf(FSMessageLevel::Attention, "You should remove the skin from the wad to play these maps.\n");
+	}
+}
+
+
 //==========================================================================
 //
 // File open
 //
 //==========================================================================
 
-FResourceFile *CheckWad(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *CheckWad(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	char head[4];
 
@@ -156,8 +415,8 @@ FResourceFile *CheckWad(const char *filename, FileReader &file, FileSystemFilter
 		file.Seek(0, FileReader::SeekSet);
 		if (!memcmp(head, "IWAD", 4) || !memcmp(head, "PWAD", 4))
 		{
-			auto rf = new FResourceFile(filename, file, sp, FResourceFile::NO_FOLDERS | FResourceFile::NO_EXTENSIONS | FResourceFile::SHORTNAMES);
-			if (OpenWAD(rf, filter, Printf)) return rf;
+			auto rf = new FWadFile(filename, file, sp);
+			if (rf->Open(filter, Printf)) return rf;
 
 			file = rf->Destroy();
 		}
diff --git a/source/common/filesystem/source/file_whres.cpp b/source/common/filesystem/source/file_whres.cpp
index a4cc5dd93..d1d2d1a50 100644
--- a/source/common/filesystem/source/file_whres.cpp
+++ b/source/common/filesystem/source/file_whres.cpp
@@ -47,7 +47,7 @@ namespace FileSys {
 //
 //==========================================================================
 
-bool OpenWHRes(FResourceFile* file, FileSystemFilterInfo*)
+bool OpenWHRes(FResourceFile* file, LumpFilterInfo*)
 {
 	uint32_t directory[1024];
 
@@ -83,6 +83,7 @@ bool OpenWHRes(FResourceFile* file, FileSystemFilterInfo*)
 		Entries[i].Position = offset;
 		Entries[i].CompressedSize = Entries[i].Length = length;
 		Entries[i].Flags = RESFF_FULLPATH;
+		Entries[i].Namespace = ns_global;
 		Entries[i].ResourceID = -1;
 		Entries[i].Method = METHOD_STORED;
 		Entries[i].FileName = file->NormalizeFileName(synthname.c_str());
@@ -98,7 +99,7 @@ bool OpenWHRes(FResourceFile* file, FileSystemFilterInfo*)
 //
 //==========================================================================
 
-FResourceFile *CheckWHRes(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *CheckWHRes(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	if (file.GetLength() >= 8192) // needs to be at least 8192 to contain one file and the directory.
 	{
@@ -118,7 +119,7 @@ FResourceFile *CheckWHRes(const char *filename, FileReader &file, FileSystemFilt
 			if (offset != checkpos || length == 0 || offset + length >= (size_t)size - 4096 ) return nullptr;
 			checkpos += (length+4095) / 4096;
 		}
-		auto rf = new FResourceFile(filename, file, sp, FResourceFile::NO_EXTENSIONS);
+		auto rf = new FResourceFile(filename, file, sp);
 		if (OpenWHRes(rf, filter)) return rf;
 		file = rf->Destroy();
 	}
diff --git a/source/common/filesystem/source/file_zip.cpp b/source/common/filesystem/source/file_zip.cpp
index d49da9e5e..8351b7146 100644
--- a/source/common/filesystem/source/file_zip.cpp
+++ b/source/common/filesystem/source/file_zip.cpp
@@ -113,7 +113,7 @@ class FZipFile : public FResourceFile
 
 public:
 	FZipFile(const char* filename, FileReader& file, StringPool* sp);
-	bool Open(FileSystemFilterInfo* filter, FileSystemMessageFunc Printf);
+	bool Open(LumpFilterInfo* filter, FileSystemMessageFunc Printf);
 	FCompressedBuffer GetRawData(uint32_t entry) override;
 };
 
@@ -124,11 +124,11 @@ public:
 //==========================================================================
 
 FZipFile::FZipFile(const char * filename, FileReader &file, StringPool* sp)
-: FResourceFile(filename, file, sp, 0)
+: FResourceFile(filename, file, sp)
 {
 }
 
-bool FZipFile::Open(FileSystemFilterInfo* filter, FileSystemMessageFunc Printf)
+bool FZipFile::Open(LumpFilterInfo* filter, FileSystemMessageFunc Printf)
 {
 	bool zip64 = false;
 	uint32_t centraldir = Zip_FindCentralDir(Reader, &zip64);
@@ -376,7 +376,7 @@ void FZipFile::SetEntryAddress(uint32_t entry)
 //
 //==========================================================================
 
-FResourceFile *CheckZip(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *CheckZip(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	char head[4];
 
diff --git a/source/common/filesystem/source/filesystem.cpp b/source/common/filesystem/source/filesystem.cpp
index 104b306a0..fee5f1011 100644
--- a/source/common/filesystem/source/filesystem.cpp
+++ b/source/common/filesystem/source/filesystem.cpp
@@ -52,6 +52,28 @@ namespace FileSys {
 	
 // MACROS ------------------------------------------------------------------
 
+#define NULL_INDEX		(0xffffffff)
+
+static void UpperCopy(char* to, const char* from)
+{
+	int i;
+
+	for (i = 0; i < 8 && from[i]; i++)
+		to[i] = toupper(from[i]);
+	for (; i < 8; i++)
+		to[i] = 0;
+}
+
+
+//djb2
+static uint32_t MakeHash(const char* str, size_t length = SIZE_MAX)
+{
+	uint32_t hash = 5381;
+	uint32_t c;
+	while (length-- > 0 && (c = *str++)) hash = hash * 33 + (c | 32);
+	return hash;
+}
+
 static void md5Hash(FileReader& reader, uint8_t* digest) 
 {
 	using namespace md5;
@@ -70,13 +92,15 @@ static void md5Hash(FileReader& reader, uint8_t* digest)
 struct FileSystem::LumpRecord
 {
 	FResourceFile *resfile;
-	const char*	Name;
+	LumpShortName shortName;
+	const char*	LongName;
 	int			resindex;
 	int16_t		rfnum;		// this is not necessarily the same as resfile's index!
+	int16_t		Namespace;
 	int			resourceId;
 	int			flags;
 
-	void SetFromFile(FResourceFile* file, int fileindex, int filenum, StringPool* sp, const char* name = nullptr)
+	void SetFromLump(FResourceFile* file, int fileindex, int filenum, StringPool* sp, const char* name = nullptr)
 	{
 		resfile = file;
 		resindex = fileindex;
@@ -85,24 +109,52 @@ struct FileSystem::LumpRecord
 
 		auto lflags = file->GetEntryFlags(fileindex);
 		if (!name) name = file->getName(fileindex);
-		if ((lflags & RESFF_EMBEDDED) || !name || !*name)
+		if (lflags & RESFF_SHORTNAME)
 		{
-			Name = "";
+			UpperCopy(shortName.String, name);
+			shortName.String[8] = 0;
+			LongName = "";
+			Namespace = file->GetEntryNamespace(fileindex);
+			resourceId = -1;
+		}
+		else if ((lflags & RESFF_EMBEDDED) || !name || !*name)
+		{
+			shortName.qword = 0;
+			LongName = "";
+			Namespace = ns_hidden;
 			resourceId = -1;
 		}
 		else
 		{
-			Name = name;
+			LongName = name;
 			resourceId = file->GetEntryResourceID(fileindex);
 
-			// allow embedding a resource ID in the name - we need to strip that out here.
-			if (strstr(Name, ".{"))
+			// Map some directories to WAD namespaces.
+			// Note that some of these namespaces don't exist in WADS.
+			// CheckNumForName will handle any request for these namespaces accordingly.
+			Namespace = !strncmp(LongName, "flats/", 6) ? ns_flats :
+				!strncmp(LongName, "textures/", 9) ? ns_newtextures :
+				!strncmp(LongName, "hires/", 6) ? ns_hires :
+				!strncmp(LongName, "sprites/", 8) ? ns_sprites :
+				!strncmp(LongName, "voxels/", 7) ? ns_voxels :
+				!strncmp(LongName, "colormaps/", 10) ? ns_colormaps :
+				!strncmp(LongName, "acs/", 4) ? ns_acslibrary :
+				!strncmp(LongName, "voices/", 7) ? ns_strifevoices :
+				!strncmp(LongName, "patches/", 8) ? ns_patches :
+				!strncmp(LongName, "graphics/", 9) ? ns_graphics :
+				!strncmp(LongName, "sounds/", 7) ? ns_sounds :
+				!strncmp(LongName, "music/", 6) ? ns_music :
+				!strchr(LongName, '/') ? ns_global :
+				ns_hidden;
+
+			if (Namespace == ns_hidden) shortName.qword = 0;
+			else if (strstr(LongName, ".{"))
 			{
-				std::string longName = Name;
+				std::string longName = LongName;
 				ptrdiff_t encodedResID = longName.find_last_of(".{");
 				if (resourceId == -1 && (size_t)encodedResID != std::string::npos)
 				{
-					const char* p = Name + encodedResID;
+					const char* p = LongName + encodedResID;
 					char* q;
 					int id = (int)strtoull(p + 2, &q, 10);	// only decimal numbers allowed here.
 					if (q[0] == '}' && (q[1] == '.' || q[1] == 0))
@@ -110,13 +162,27 @@ struct FileSystem::LumpRecord
 						longName.erase(longName.begin() + encodedResID, longName.begin() + (q - p) + 1);
 						resourceId = id;
 					}
-					Name = sp->Strdup(longName.c_str());
+					LongName = sp->Strdup(longName.c_str());
 				}
 			}
-			auto slash = strrchr(Name, '/');
-			std::string base = slash ? (slash + 1) : Name;
+			auto slash = strrchr(LongName, '/');
+			std::string base = slash ? (slash + 1) : LongName;
 			auto dot = base.find_last_of('.');
 			if (dot != std::string::npos) base.resize(dot);
+			UpperCopy(shortName.String, base.c_str());
+
+			// Since '\' can't be used as a file name's part inside a ZIP
+			// we have to work around this for sprites because it is a valid
+			// frame character.
+			if (Namespace == ns_sprites || Namespace == ns_voxels || Namespace == ns_hires)
+			{
+				char* c;
+
+				while ((c = (char*)memchr(shortName.String, '^', 8)))
+				{
+					*c = '\\';
+				}
+			}
 		}
 	}
 };
@@ -157,16 +223,22 @@ void FileSystem::DeleteAll ()
 
 //==========================================================================
 //
-// Initialize
+// InitMultipleFiles
 //
-// Pass a vector of files to use. All files are optional,
+// Pass a null terminated list of files to use. All files are optional,
 // but at least one file must be found. File names can appear multiple
 // times. The name searcher looks backwards, so a later file can
 // override an earlier one.
 //
 //==========================================================================
 
-bool FileSystem::InitFiles(std::vector<std::string>& filenames, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, bool allowduplicates)
+bool FileSystem::InitSingleFile(const char* filename, FileSystemMessageFunc Printf)
+{
+	std::vector<std::string> filenames = { filename };
+	return InitMultipleFiles(filenames, nullptr, Printf);
+}
+
+bool FileSystem::InitMultipleFiles (std::vector<std::string>& filenames, LumpFilterInfo* filter, FileSystemMessageFunc Printf, bool allowduplicates, FILE* hashfile)
 {
 	int numfiles;
 
@@ -182,9 +254,9 @@ bool FileSystem::InitFiles(std::vector<std::string>& filenames, FileSystemFilter
 	// first, check for duplicates
 	if (!allowduplicates)
 	{
-		for (size_t i = 0; i < filenames.size(); i++)
+		for (size_t i=0;i<filenames.size(); i++)
 		{
-			for (size_t j = i + 1; j < filenames.size(); j++)
+			for (size_t j=i+1;j<filenames.size(); j++)
 			{
 				if (filenames[i] == filenames[j])
 				{
@@ -195,14 +267,14 @@ bool FileSystem::InitFiles(std::vector<std::string>& filenames, FileSystemFilter
 		}
 	}
 
-	for (size_t i = 0; i < filenames.size(); i++)
+	for(size_t i=0;i<filenames.size(); i++)
 	{
-		AddFile(filenames[i].c_str(), nullptr, filter, Printf);
+		AddFile(filenames[i].c_str(), nullptr, filter, Printf, hashfile);
 
-		if (i == (unsigned)MaxBaseIndex) MoveFilesInFolder("after_iwad/");
+		if (i == (unsigned)MaxIwadIndex) MoveLumpsInFolder("after_iwad/");
 		std::string path = "filter/%s";
 		path += Files.back()->GetHash();
-		MoveFilesInFolder(path.c_str());
+		MoveLumpsInFolder(path.c_str());
 	}
 
 	NumEntries = (uint32_t)FileInfo.size();
@@ -210,12 +282,6 @@ bool FileSystem::InitFiles(std::vector<std::string>& filenames, FileSystemFilter
 	{
 		return false;
 	}
-	return true;
-}
-
-bool FileSystem::Initialize(std::vector<std::string>& filenames, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, bool allowduplicates)
-{
-	if (!InitFiles(filenames, filter, Printf, allowduplicates)) return false;
 	if (filter && filter->postprocessFunc) filter->postprocessFunc();
 
 	// [RH] Set up hash table
@@ -237,8 +303,8 @@ int FileSystem::AddFromBuffer(const char* name, char* data, int size, int id, in
 	FileData blob(data, size);
 	fr.OpenMemoryArray(blob);
 
-	// wrap this into a single filenum resource file (should be done a little better later.)
-	auto rf = new FResourceFile(name, fr, stringpool, 0);
+	// wrap this into a single lump resource file (should be done a little better later.)
+	auto rf = new FResourceFile(name, fr, stringpool);
 	auto Entries = rf->AllocateEntries(1);
 	Entries[0].FileName = rf->NormalizeFileName(ExtractBaseName(name, true).c_str());
 	Entries[0].ResourceID = -1;
@@ -246,8 +312,8 @@ int FileSystem::AddFromBuffer(const char* name, char* data, int size, int id, in
 
 	Files.push_back(rf);
 	FileInfo.resize(FileInfo.size() + 1);
-	FileSystem::LumpRecord* file_p = &FileInfo.back();
-	file_p->SetFromFile(rf, 0, (int)Files.size() - 1, stringpool);
+	FileSystem::LumpRecord* lump_p = &FileInfo.back();
+	lump_p->SetFromLump(rf, 0, (int)Files.size() - 1, stringpool);
 	return (int)FileInfo.size() - 1;
 }
 
@@ -256,12 +322,12 @@ int FileSystem::AddFromBuffer(const char* name, char* data, int size, int id, in
 // AddFile
 //
 // Files with a .wad extension are wadlink files with multiple lumps,
-// other files are single lumps with the base filename for the filenum name.
+// other files are single lumps with the base filename for the lump name.
 //
 // [RH] Removed reload hack
 //==========================================================================
 
-void FileSystem::AddFile (const char *filename, FileReader *filer, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf)
+void FileSystem::AddFile (const char *filename, FileReader *filer, LumpFilterInfo* filter, FileSystemMessageFunc Printf, FILE* hashfile)
 {
 	int startlump;
 	bool isdir = false;
@@ -308,17 +374,17 @@ void FileSystem::AddFile (const char *filename, FileReader *filer, FileSystemFil
 	if (resfile != NULL)
 	{
 		if (Printf) 
-			Printf(FSMessageLevel::Message, "adding %s, %d files\n", filename, resfile->EntryCount());
+			Printf(FSMessageLevel::Message, "adding %s, %d lumps\n", filename, resfile->EntryCount());
 
 		uint32_t lumpstart = (uint32_t)FileInfo.size();
 
-		resfile->SetFirstFile(lumpstart);
+		resfile->SetFirstLump(lumpstart);
 		Files.push_back(resfile);
 		for (int i = 0; i < resfile->EntryCount(); i++)
 		{
 			FileInfo.resize(FileInfo.size() + 1);
-			FileSystem::LumpRecord* file_p = &FileInfo.back();
-			file_p->SetFromFile(resfile, i, (int)Files.size() - 1, stringpool);
+			FileSystem::LumpRecord* lump_p = &FileInfo.back();
+			lump_p->SetFromLump(resfile, i, (int)Files.size() - 1, stringpool);
 		}
 
 		for (int i = 0; i < resfile->EntryCount(); i++)
@@ -330,10 +396,49 @@ void FileSystem::AddFile (const char *filename, FileReader *filer, FileSystemFil
 				path += ':';
 				path += resfile->getName(i);
 				auto embedded = resfile->GetEntryReader(i, READER_CACHED);
-				AddFile(path.c_str(), &embedded, filter, Printf);
+				AddFile(path.c_str(), &embedded, filter, Printf, hashfile);
 			}
 		}
 
+		if (hashfile)
+		{
+			uint8_t cksum[16];
+			char cksumout[33];
+			memset(cksumout, 0, sizeof(cksumout));
+
+			if (filereader.isOpen())
+			{
+				filereader.Seek(0, FileReader::SeekSet);
+				md5Hash(filereader, cksum);
+
+				for (size_t j = 0; j < sizeof(cksum); ++j)
+				{
+					snprintf(cksumout + (j * 2), 3, "%02X", cksum[j]);
+				}
+
+				fprintf(hashfile, "file: %s, hash: %s, size: %td\n", filename, cksumout, filereader.GetLength());
+			}
+
+			else
+				fprintf(hashfile, "file: %s, Directory structure\n", filename);
+
+			for (int i = 0; i < resfile->EntryCount(); i++)
+			{
+				int flags = resfile->GetEntryFlags(i);
+				if (!(flags & RESFF_EMBEDDED))
+				{
+					auto reader = resfile->GetEntryReader(i, READER_SHARED, 0);
+					md5Hash(filereader, cksum);
+
+					for (size_t j = 0; j < sizeof(cksum); ++j)
+					{
+						snprintf(cksumout + (j * 2), 3, "%02X", cksum[j]);
+					}
+
+					fprintf(hashfile, "file: %s, lump: %s, hash: %s, size: %zu\n", filename, resfile->getName(i), cksumout, (uint64_t)resfile->Length(i));
+				}
+			}
+		}
 		return;
 	}
 }
@@ -349,7 +454,7 @@ void FileSystem::AddFile (const char *filename, FileReader *filer, FileSystemFil
 //
 //==========================================================================
 
-int FileSystem::CheckIfContainerLoaded (const char *name) noexcept
+int FileSystem::CheckIfResourceFileLoaded (const char *name) noexcept
 {
 	unsigned int i;
 
@@ -357,7 +462,7 @@ int FileSystem::CheckIfContainerLoaded (const char *name) noexcept
 	{
 		for (i = 0; i < (unsigned)Files.size(); ++i)
 		{
-			if (stricmp (GetContainerFullName (i), name) == 0)
+			if (stricmp (GetResourceFileFullName (i), name) == 0)
 			{
 				return i;
 			}
@@ -367,7 +472,7 @@ int FileSystem::CheckIfContainerLoaded (const char *name) noexcept
 	{
 		for (i = 0; i < (unsigned)Files.size(); ++i)
 		{
-			auto pth = ExtractBaseName(GetContainerName(i), true);
+			auto pth = ExtractBaseName(GetResourceFileName(i), true);
 			if (stricmp (pth.c_str(), name) == 0)
 			{
 				return i;
@@ -377,6 +482,114 @@ int FileSystem::CheckIfContainerLoaded (const char *name) noexcept
 	return -1;
 }
 
+//==========================================================================
+//
+// CheckNumForName
+//
+// Returns -1 if name not found. The version with a third parameter will
+// look exclusively in the specified wad for the lump.
+//
+// [RH] Changed to use hash lookup ala BOOM instead of a linear search
+// and namespace parameter
+//==========================================================================
+
+int FileSystem::CheckNumForName (const char *name, int space) const
+{
+	union
+	{
+		char uname[8];
+		uint64_t qname;
+	};
+	uint32_t i;
+
+	if (name == NULL)
+	{
+		return -1;
+	}
+
+	// Let's not search for names that are longer than 8 characters and contain path separators
+	// They are almost certainly full path names passed to this function.
+	if (strlen(name) > 8 && strpbrk(name, "/."))
+	{
+		return -1;
+	}
+
+	UpperCopy (uname, name);
+	i = FirstLumpIndex[MakeHash(uname, 8) % NumEntries];
+
+	while (i != NULL_INDEX)
+	{
+
+		if (FileInfo[i].shortName.qword == qname)
+		{
+			auto &lump = FileInfo[i];
+			if (lump.Namespace == space) break;
+			// If the lump is from one of the special namespaces exclusive to Zips
+			// the check has to be done differently:
+			// If we find a lump with this name in the global namespace that does not come
+			// from a Zip return that. WADs don't know these namespaces and single lumps must
+			// work as well.
+			auto lflags = lump.resfile->GetEntryFlags(lump.resindex);
+			if (space > ns_specialzipdirectory && lump.Namespace == ns_global && 
+				!((lflags ^lump.flags) & RESFF_FULLPATH)) break;
+		}
+		i = NextLumpIndex[i];
+	}
+
+	return i != NULL_INDEX ? i : -1;
+}
+
+int FileSystem::CheckNumForName (const char *name, int space, int rfnum, bool exact) const
+{
+	union
+	{
+		char uname[8];
+		uint64_t qname;
+	};
+	uint32_t i;
+
+	if (rfnum < 0)
+	{
+		return CheckNumForName (name, space);
+	}
+
+	UpperCopy (uname, name);
+	i = FirstLumpIndex[MakeHash (uname, 8) % NumEntries];
+
+	// If exact is true if will only find lumps in the same WAD, otherwise
+	// also those in earlier WADs.
+
+	while (i != NULL_INDEX &&
+		(FileInfo[i].shortName.qword != qname || FileInfo[i].Namespace != space ||
+		 (exact? (FileInfo[i].rfnum != rfnum) : (FileInfo[i].rfnum > rfnum)) ))
+	{
+		i = NextLumpIndex[i];
+	}
+
+	return i != NULL_INDEX ? i : -1;
+}
+
+//==========================================================================
+//
+// GetNumForName
+//
+// Calls CheckNumForName, but bombs out if not found.
+//
+//==========================================================================
+
+int FileSystem::GetNumForName (const char *name, int space) const
+{
+	int	i;
+
+	i = CheckNumForName (name, space);
+
+	if (i == -1)
+		throw FileSystemException("GetNumForName: %s not found!", name);
+
+	return i;
+}
+
+
 //==========================================================================
 //
 // CheckNumForFullName
@@ -387,7 +600,7 @@ int FileSystem::CheckIfContainerLoaded (const char *name) noexcept
 //
 //==========================================================================
 
-int FileSystem::FindFile (const char *name, bool ignoreext) const
+int FileSystem::CheckNumForFullName (const char *name, bool trynormal, int namespc, bool ignoreext) const
 {
 	uint32_t i;
 
@@ -396,40 +609,45 @@ int FileSystem::FindFile (const char *name, bool ignoreext) const
 		return -1;
 	}
 	if (*name == '/') name++;	// ignore leading slashes in file names.
-	uint32_t *fli = ignoreext ? FirstFileIndex_NoExt : FirstFileIndex_FullName;
-	uint32_t *nli = ignoreext ? NextFileIndex_NoExt : NextFileIndex_FullName;
+	uint32_t *fli = ignoreext ? FirstLumpIndex_NoExt : FirstLumpIndex_FullName;
+	uint32_t *nli = ignoreext ? NextLumpIndex_NoExt : NextLumpIndex_FullName;
 	auto len = strlen(name);
 
 	for (i = fli[MakeHash(name) % NumEntries]; i != NULL_INDEX; i = nli[i])
 	{
-		if (strnicmp(name, FileInfo[i].Name, len)) continue;
-		if (FileInfo[i].Name[len] == 0) break;	// this is a full match
-		if (ignoreext && FileInfo[i].Name[len] == '.') 
+		if (strnicmp(name, FileInfo[i].LongName, len)) continue;
+		if (FileInfo[i].LongName[len] == 0) break;	// this is a full match
+		if (ignoreext && FileInfo[i].LongName[len] == '.') 
 		{
 			// is this the last '.' in the last path element, indicating that the remaining part of the name is only an extension?
-			if (strpbrk(FileInfo[i].Name + len + 1, "./") == nullptr) break;	
+			if (strpbrk(FileInfo[i].LongName + len + 1, "./") == nullptr) break;	
 		}
 	}
 
 	if (i != NULL_INDEX) return i;
+
+	if (trynormal && strlen(name) <= 8 && !strpbrk(name, "./"))
+	{
+		return CheckNumForName(name, namespc);
+	}
 	return -1;
 }
 
-int FileSystem::GetFileInContainer (const char *name, int rfnum) const
+int FileSystem::CheckNumForFullName (const char *name, int rfnum) const
 {
 	uint32_t i;
 
 	if (rfnum < 0)
 	{
-		return FindFile (name);
+		return CheckNumForFullName (name);
 	}
 
-	i = FirstFileIndex_FullName[MakeHash (name) % NumEntries];
+	i = FirstLumpIndex_FullName[MakeHash (name) % NumEntries];
 
 	while (i != NULL_INDEX && 
-		(stricmp(name, FileInfo[i].Name) || FileInfo[i].rfnum != rfnum))
+		(stricmp(name, FileInfo[i].LongName) || FileInfo[i].rfnum != rfnum))
 	{
-		i = NextFileIndex_FullName[i];
+		i = NextLumpIndex_FullName[i];
 	}
 
 	return i != NULL_INDEX ? i : -1;
@@ -443,11 +661,11 @@ int FileSystem::GetFileInContainer (const char *name, int rfnum) const
 //
 //==========================================================================
 
-int FileSystem::GetFile (const char *name) const
+int FileSystem::GetNumForFullName (const char *name) const
 {
 	int	i;
 
-	i = FindFile (name);
+	i = CheckNumForFullName (name);
 
 	if (i == -1)
 		throw FileSystemException("GetNumForFullName: %s not found!", name);
@@ -472,16 +690,16 @@ int FileSystem::FindFileWithExtensions(const char* name, const char *const *exts
 		return -1;
 	}
 	if (*name == '/') name++;	// ignore leading slashes in file names.
-	uint32_t* fli = FirstFileIndex_NoExt;
-	uint32_t* nli = NextFileIndex_NoExt;
+	uint32_t* fli = FirstLumpIndex_NoExt;
+	uint32_t* nli = NextLumpIndex_NoExt;
 	auto len = strlen(name);
 
 	for (i = fli[MakeHash(name) % NumEntries]; i != NULL_INDEX; i = nli[i])
 	{
-		if (strnicmp(name, FileInfo[i].Name, len)) continue;
-		if (FileInfo[i].Name[len] != '.') continue;	// we are looking for extensions but this file doesn't have one.
+		if (strnicmp(name, FileInfo[i].LongName, len)) continue;
+		if (FileInfo[i].LongName[len] != '.') continue;	// we are looking for extensions but this file doesn't have one.
 
-		auto cp = FileInfo[i].Name + len + 1;
+		auto cp = FileInfo[i].LongName + len + 1;
 		// is this the last '.' in the last path element, indicating that the remaining part of the name is only an extension?
 		if (strpbrk(cp, "./") != nullptr) continue;	// No, so it cannot be a valid entry.
 
@@ -510,14 +728,14 @@ int FileSystem::FindResource (int resid, const char *type, int filenum) const no
 		return -1;
 	}
 
-	uint32_t* fli = FirstFileIndex_ResId;
-	uint32_t* nli = NextFileIndex_ResId;
+	uint32_t* fli = FirstLumpIndex_ResId;
+	uint32_t* nli = NextLumpIndex_ResId;
 
 	for (i = fli[resid % NumEntries]; i != NULL_INDEX; i = nli[i])
 	{
 		if (filenum > 0 && FileInfo[i].rfnum != filenum) continue;
 		if (FileInfo[i].resourceId != resid) continue;
-		auto extp = strrchr(FileInfo[i].Name, '.');
+		auto extp = strrchr(FileInfo[i].LongName, '.');
 		if (!extp) continue;
 		if (!stricmp(extp + 1, type)) return i;
 	}
@@ -549,18 +767,18 @@ int FileSystem::GetResource (int resid, const char *type, int filenum) const
 //
 // FileLength
 //
-// Returns the buffer size needed to load the given filenum.
+// Returns the buffer size needed to load the given lump.
 //
 //==========================================================================
 
-ptrdiff_t FileSystem::FileLength (int filenum) const
+ptrdiff_t FileSystem::FileLength (int lump) const
 {
-	if ((size_t)filenum >= NumEntries)
+	if ((size_t)lump >= NumEntries)
 	{
 		return -1;
 	}
-	const auto &file_p = FileInfo[filenum];
-	return (int)file_p.resfile->Length(file_p.resindex);
+	const auto &lump_p = FileInfo[lump];
+	return (int)lump_p.resfile->Length(lump_p.resindex);
 }
 
 //==========================================================================
@@ -569,15 +787,15 @@ ptrdiff_t FileSystem::FileLength (int filenum) const
 //
 //==========================================================================
 
-int FileSystem::GetFileFlags (int filenum)
+int FileSystem::GetFileFlags (int lump)
 {
-	if ((size_t)filenum >= NumEntries)
+	if ((size_t)lump >= NumEntries)
 	{
 		return 0;
 	}
 
-	const auto& file_p = FileInfo[filenum];
-	return file_p.resfile->GetEntryFlags(file_p.resindex) ^ file_p.flags;
+	const auto& lump_p = FileInfo[lump];
+	return lump_p.resfile->GetEntryFlags(lump_p.resindex) ^ lump_p.flags;
 }
 
 //==========================================================================
@@ -594,39 +812,45 @@ void FileSystem::InitHashChains (void)
 	unsigned int i, j;
 
 	NumEntries = (uint32_t)FileInfo.size();
-	Hashes.resize(6 * NumEntries);
+	Hashes.resize(8 * NumEntries);
 	// Mark all buckets as empty
 	memset(Hashes.data(), -1, Hashes.size() * sizeof(Hashes[0]));
-	FirstFileIndex_FullName = &Hashes[NumEntries * 0];
-	NextFileIndex_FullName = &Hashes[NumEntries * 1];
-	FirstFileIndex_NoExt = &Hashes[NumEntries * 2];
-	NextFileIndex_NoExt = &Hashes[NumEntries * 3];
-	FirstFileIndex_ResId = &Hashes[NumEntries * 4];
-	NextFileIndex_ResId = &Hashes[NumEntries * 5];
+	FirstLumpIndex = &Hashes[0];
+	NextLumpIndex = &Hashes[NumEntries];
+	FirstLumpIndex_FullName = &Hashes[NumEntries * 2];
+	NextLumpIndex_FullName = &Hashes[NumEntries * 3];
+	FirstLumpIndex_NoExt = &Hashes[NumEntries * 4];
+	NextLumpIndex_NoExt = &Hashes[NumEntries * 5];
+	FirstLumpIndex_ResId = &Hashes[NumEntries * 6];
+	NextLumpIndex_ResId = &Hashes[NumEntries * 7];
 
 
 	// Now set up the chains
 	for (i = 0; i < (unsigned)NumEntries; i++)
 	{
-		// Do the same for the full paths
-		if (FileInfo[i].Name[0] != 0)
-		{
-			j = MakeHash(FileInfo[i].Name) % NumEntries;
-			NextFileIndex_FullName[i] = FirstFileIndex_FullName[j];
-			FirstFileIndex_FullName[j] = i;
+		j = MakeHash (FileInfo[i].shortName.String, 8) % NumEntries;
+		NextLumpIndex[i] = FirstLumpIndex[j];
+		FirstLumpIndex[j] = i;
 
-			std::string nameNoExt = FileInfo[i].Name;
+		// Do the same for the full paths
+		if (FileInfo[i].LongName[0] != 0)
+		{
+			j = MakeHash(FileInfo[i].LongName) % NumEntries;
+			NextLumpIndex_FullName[i] = FirstLumpIndex_FullName[j];
+			FirstLumpIndex_FullName[j] = i;
+
+			std::string nameNoExt = FileInfo[i].LongName;
 			auto dot = nameNoExt.find_last_of('.');
 			auto slash = nameNoExt.find_last_of('/');
 			if ((dot > slash || slash == std::string::npos) && dot != std::string::npos) nameNoExt.resize(dot);
 
 			j = MakeHash(nameNoExt.c_str()) % NumEntries;
-			NextFileIndex_NoExt[i] = FirstFileIndex_NoExt[j];
-			FirstFileIndex_NoExt[j] = i;
+			NextLumpIndex_NoExt[i] = FirstLumpIndex_NoExt[j];
+			FirstLumpIndex_NoExt[j] = i;
 
 			j = FileInfo[i].resourceId % NumEntries;
-			NextFileIndex_ResId[i] = FirstFileIndex_ResId[j];
-			FirstFileIndex_ResId[j] = i;
+			NextLumpIndex_ResId[i] = FirstLumpIndex_ResId[j];
+			FirstLumpIndex_ResId[j] = i;
 
 		}
 	}
@@ -634,10 +858,24 @@ void FileSystem::InitHashChains (void)
 	Files.shrink_to_fit();
 }
 
+//==========================================================================
+//
+// should only be called before the hash chains are set up.
+// If done later this needs rehashing.
+//
+//==========================================================================
+
+LumpShortName& FileSystem::GetShortName(int i)
+{
+	if ((unsigned)i >= NumEntries) throw FileSystemException("GetShortName: Invalid index");
+	return FileInfo[i].shortName;
+}
+
 void FileSystem::RenameFile(int num, const char* newfn)
 {
 	if ((unsigned)num >= NumEntries) throw FileSystemException("RenameFile: Invalid index");
-	FileInfo[num].Name = stringpool->Strdup(newfn);
+	FileInfo[num].LongName = stringpool->Strdup(newfn);
+	// This does not alter the short name - call GetShortname to do that!
 }
 
 //==========================================================================
@@ -653,7 +891,7 @@ void FileSystem::RenameFile(int num, const char* newfn)
 //
 //==========================================================================
 
-void FileSystem::MoveFilesInFolder(const char *path)
+void FileSystem::MoveLumpsInFolder(const char *path)
 {
 	if (FileInfo.size() == 0)
 	{
@@ -667,14 +905,15 @@ void FileSystem::MoveFilesInFolder(const char *path)
 	for (i = 0; i < FileInfo.size(); i++)
 	{
 		auto& li = FileInfo[i];
-		if (li.rfnum >= GetBaseNum()) break;
-		if (strnicmp(li.Name, path, len) == 0)
+		if (li.rfnum >= GetIwadNum()) break;
+		if (strnicmp(li.LongName, path, len) == 0)
 		{
 			auto lic = li;	// make a copy before pushing.
 			FileInfo.push_back(lic);
-			li.Name = "";	//nuke the name of the old record.
+			li.LongName = "";	//nuke the name of the old record.
+			li.shortName.qword = 0;
 			auto &ln = FileInfo.back();
-			ln.SetFromFile(li.resfile, li.resindex, rfnum, stringpool, ln.Name + len);
+			ln.SetFromLump(li.resfile, li.resindex, rfnum, stringpool, ln.LongName + len);
 		}
 	}
 }
@@ -683,7 +922,88 @@ void FileSystem::MoveFilesInFolder(const char *path)
 //
 // W_FindLump
 //
-// Find a named filenum. Specifically allows duplicates for merging of e.g.
+// Find a named lump. Specifically allows duplicates for merging of e.g.
+// SNDINFO lumps.
+//
+//==========================================================================
+
+int FileSystem::FindLump (const char *name, int *lastlump, bool anyns)
+{
+	if ((size_t)*lastlump >= FileInfo.size()) return -1;
+	union
+	{
+		char name8[8];
+		uint64_t qname;
+	};
+
+	UpperCopy (name8, name);
+
+	assert(lastlump != NULL && *lastlump >= 0);
+
+	const LumpRecord * last = FileInfo.data() + FileInfo.size();
+
+	LumpRecord * lump_p = FileInfo.data() + *lastlump;
+
+	while (lump_p < last)
+	{
+		if ((anyns || lump_p->Namespace == ns_global) && lump_p->shortName.qword == qname)
+		{
+			int lump = int(lump_p - FileInfo.data());
+			*lastlump = lump + 1;
+			return lump;
+		}
+		lump_p++;
+	}
+
+	*lastlump = NumEntries;
+	return -1;
+}
+
+//==========================================================================
+//
+// W_FindLumpMulti
+//
+// Find a named lump. Specifically allows duplicates for merging of e.g.
+// SNDINFO lumps. Returns everything having one of the passed names.
+//
+//==========================================================================
+
+int FileSystem::FindLumpMulti (const char **names, int *lastlump, bool anyns, int *nameindex)
+{
+	assert(lastlump != NULL && *lastlump >= 0);
+
+	const LumpRecord * last = FileInfo.data() + FileInfo.size();
+
+	LumpRecord * lump_p = FileInfo.data() + *lastlump;
+
+	while (lump_p < last)
+	{
+		if (anyns || lump_p->Namespace == ns_global)
+		{
+
+			for(const char **name = names; *name != NULL; name++)
+			{
+				if (!strnicmp(*name, lump_p->shortName.String, 8))
+				{
+					int lump = int(lump_p - FileInfo.data());
+					*lastlump = lump + 1;
+					if (nameindex != NULL) *nameindex = int(name - names);
+					return lump;
+				}
+			}
+		}
+		lump_p++;
+	}
+
+	*lastlump = NumEntries;
+	return -1;
+}
+
+//==========================================================================
+//
+// W_FindLump
+//
+// Find a named lump. Specifically allows duplicates for merging of e.g.
 // SNDINFO lumps.
 //
 //==========================================================================
@@ -694,38 +1014,38 @@ int FileSystem::FindLumpFullName(const char* name, int* lastlump, bool noext)
 
 	const LumpRecord * last = FileInfo.data() + FileInfo.size();
 
-	LumpRecord * file_p = FileInfo.data() + *lastlump;
+	LumpRecord * lump_p = FileInfo.data() + *lastlump;
 
 	if (!noext)
 	{
-		while (file_p < last)
+		while (lump_p < last)
 		{
-			if (!stricmp(name, file_p->Name))
+			if (!stricmp(name, lump_p->LongName))
 			{
-				int filenum = int(file_p - FileInfo.data());
-				*lastlump = filenum + 1;
-				return filenum;
+				int lump = int(lump_p - FileInfo.data());
+				*lastlump = lump + 1;
+				return lump;
 			}
-			file_p++;
+			lump_p++;
 		}
 	}
 	else
 	{
 		auto len = strlen(name);
-		while (file_p <= &FileInfo.back())
+		while (lump_p <= &FileInfo.back())
 		{
-			auto res = strnicmp(name, file_p->Name, len);
+			auto res = strnicmp(name, lump_p->LongName, len);
 			if (res == 0)
 			{
-				auto p = file_p->Name + len;
+				auto p = lump_p->LongName + len;
 				if (*p == 0 || (*p == '.' && strpbrk(p + 1, "./") == 0))
 				{
-					int filenum = int(file_p - FileInfo.data());
-					*lastlump = filenum + 1;
-					return filenum;
+					int lump = int(lump_p - FileInfo.data());
+					*lastlump = lump + 1;
+					return lump;
 				}
 			}
-			file_p++;
+			lump_p++;
 		}
 	}
 
@@ -736,56 +1056,107 @@ int FileSystem::FindLumpFullName(const char* name, int* lastlump, bool noext)
 
 //==========================================================================
 //
-// FileSystem :: GetFileName
-//
-// Returns the filenum's internal name
+// W_CheckLumpName
 //
 //==========================================================================
 
-const char* FileSystem::GetFileName(int filenum) const
+bool FileSystem::CheckFileName (int lump, const char *name)
 {
-	if ((size_t)filenum >= NumEntries)
+	if ((size_t)lump >= NumEntries)
+		return false;
+
+	return !strnicmp (FileInfo[lump].shortName.String, name, 8);
+}
+
+//==========================================================================
+//
+// GetLumpName
+//
+//==========================================================================
+
+const char* FileSystem::GetFileShortName(int lump) const
+{
+	if ((size_t)lump >= NumEntries)
+		return nullptr;
+	else
+		return FileInfo[lump].shortName.String;
+}
+
+//==========================================================================
+//
+// FileSystem :: GetFileFullName
+//
+// Returns the lump's full name if it has one or its short name if not.
+//
+//==========================================================================
+
+const char *FileSystem::GetFileFullName (int lump, bool returnshort) const
+{
+	if ((size_t)lump >= NumEntries)
 		return NULL;
-	else return FileInfo[filenum].Name;
+	else if (FileInfo[lump].LongName[0] != 0)
+		return FileInfo[lump].LongName;
+	else if (returnshort)
+		return FileInfo[lump].shortName.String;
+	else return nullptr;
 }
 
 //==========================================================================
 //
 // FileSystem :: GetFileFullPath
 //
-// Returns the name of the filenum's wad prefixed to the filenum's full name.
+// Returns the name of the lump's wad prefixed to the lump's full name.
 //
 //==========================================================================
 
-std::string FileSystem::GetFileFullPath(int filenum) const
+std::string FileSystem::GetFileFullPath(int lump) const
 {
 	std::string foo;
 
-	if ((size_t) filenum <  NumEntries)
+	if ((size_t) lump <  NumEntries)
 	{
-		foo = GetContainerName(FileInfo[filenum].rfnum);
+		foo = GetResourceFileName(FileInfo[lump].rfnum);
 		foo += ':';
-		foo += +GetFileName(filenum);
+		foo += +GetFileFullName(lump);
 	}
 	return foo;
 }
 
+//==========================================================================
+//
+// GetFileNamespace
+//
+//==========================================================================
+
+int FileSystem::GetFileNamespace (int lump) const
+{
+	if ((size_t)lump >= NumEntries)
+		return ns_global;
+	else
+		return FileInfo[lump].Namespace;
+}
+
+void FileSystem::SetFileNamespace(int lump, int ns)
+{
+	if ((size_t)lump < NumEntries) FileInfo[lump].Namespace = ns;
+}
+
 //==========================================================================
 //
 // FileSystem :: GetResourceId
 //
-// Returns the index number for this filenum. This is *not* the filenum's position
-// in the filenum directory, but rather a special value that RFF can associate
+// Returns the index number for this lump. This is *not* the lump's position
+// in the lump directory, but rather a special value that RFF can associate
 // with files. Other archive types will return 0, since they don't have it.
 //
 //==========================================================================
 
-int FileSystem::GetResourceId(int filenum) const
+int FileSystem::GetResourceId(int lump) const
 {
-	if ((size_t)filenum >= NumEntries)
+	if ((size_t)lump >= NumEntries)
 		return -1;
 	else
-		return FileInfo[filenum].resourceId;
+		return FileInfo[lump].resourceId;
 }
 
 //==========================================================================
@@ -796,13 +1167,13 @@ int FileSystem::GetResourceId(int filenum) const
 //
 //==========================================================================
 
-const char *FileSystem::GetResourceType(int filenum) const
+const char *FileSystem::GetResourceType(int lump) const
 {
-	if ((size_t)filenum >= NumEntries)
+	if ((size_t)lump >= NumEntries)
 		return nullptr;
 	else
 	{
-		auto p = strrchr(FileInfo[filenum].Name, '.');
+		auto p = strrchr(FileInfo[lump].LongName, '.');
 		if (!p) return "";	// has no extension
 		if (strchr(p, '/')) return "";	// the '.' is part of a directory.
 		return p + 1;
@@ -815,11 +1186,11 @@ const char *FileSystem::GetResourceType(int filenum) const
 //
 //==========================================================================
 
-int FileSystem::GetFileContainer (int filenum) const
+int FileSystem::GetFileContainer (int lump) const
 {
-	if ((size_t)filenum >= FileInfo.size())
+	if ((size_t)lump >= FileInfo.size())
 		return -1;
-	return FileInfo[filenum].rfnum;
+	return FileInfo[lump].rfnum;
 }
 
 //==========================================================================
@@ -854,12 +1225,12 @@ unsigned FileSystem::GetFilesInFolder(const char *inpath, std::vector<FolderEntr
 	result.clear();
 	for (size_t i = 0; i < FileInfo.size(); i++)
 	{
-		if (strncmp(FileInfo[i].Name, path.c_str(), path.length()) == 0)
+		if (strncmp(FileInfo[i].LongName, path.c_str(), path.length()) == 0)
 		{
 			// Only if it hasn't been replaced.
-			if ((unsigned)FindFile(FileInfo[i].Name) == i)
+			if ((unsigned)CheckNumForFullName(FileInfo[i].LongName) == i)
 			{
-				FolderEntry fe{ FileInfo[i].Name, (uint32_t)i };
+				FolderEntry fe{ FileInfo[i].LongName, (uint32_t)i };
 				result.push_back(fe);
 			}
 		}
@@ -890,20 +1261,20 @@ unsigned FileSystem::GetFilesInFolder(const char *inpath, std::vector<FolderEntr
 //
 // W_ReadFile
 //
-// Loads the filenum into the given buffer, which must be >= W_LumpLength().
+// Loads the lump into the given buffer, which must be >= W_LumpLength().
 //
 //==========================================================================
 
-void FileSystem::ReadFile (int filenum, void *dest)
+void FileSystem::ReadFile (int lump, void *dest)
 {
-	auto lumpr = OpenFileReader (filenum);
+	auto lumpr = OpenFileReader (lump);
 	auto size = lumpr.GetLength ();
 	auto numread = lumpr.Read (dest, size);
 
 	if (numread != size)
 	{
 		throw FileSystemException("W_ReadFile: only read %td of %td on '%s'\n",
-			numread, size, FileInfo[filenum].Name);
+			numread, size, FileInfo[lump].LongName);
 	}
 }
 
@@ -912,17 +1283,17 @@ void FileSystem::ReadFile (int filenum, void *dest)
 //
 // ReadFile - variant 2
 //
-// Loads the filenum into a newly created buffer and returns it.
+// Loads the lump into a newly created buffer and returns it.
 //
 //==========================================================================
 
-FileData FileSystem::ReadFile (int filenum)
+FileData FileSystem::ReadFile (int lump)
 {
-	if ((unsigned)filenum >= (unsigned)FileInfo.size())
+	if ((unsigned)lump >= (unsigned)FileInfo.size())
 	{
-		throw FileSystemException("ReadFile: %u >= NumEntries", filenum);
+		throw FileSystemException("ReadFile: %u >= NumEntries", lump);
 	}
-	return FileInfo[filenum].resfile->Read(FileInfo[filenum].resindex);
+	return FileInfo[lump].resfile->Read(FileInfo[lump].resindex);
 }
 
 //==========================================================================
@@ -934,30 +1305,30 @@ FileData FileSystem::ReadFile (int filenum)
 //==========================================================================
 
 
-FileReader FileSystem::OpenFileReader(int filenum, int readertype, int readerflags)
+FileReader FileSystem::OpenFileReader(int lump, int readertype, int readerflags)
 {
-	if ((unsigned)filenum >= (unsigned)FileInfo.size())
+	if ((unsigned)lump >= (unsigned)FileInfo.size())
 	{
-		throw FileSystemException("OpenFileReader: %u >= NumEntries", filenum);
+		throw FileSystemException("OpenFileReader: %u >= NumEntries", lump);
 	}
 
-	auto file = FileInfo[filenum].resfile;
-	return file->GetEntryReader(FileInfo[filenum].resindex, readertype, readerflags);
+	auto file = FileInfo[lump].resfile;
+	return file->GetEntryReader(FileInfo[lump].resindex, readertype, readerflags);
 }
 
 FileReader FileSystem::OpenFileReader(const char* name)
 {
 	FileReader fr;
-	auto filenum = FindFile(name);
-	if (filenum >= 0) fr = OpenFileReader(filenum);
+	auto lump = CheckNumForFullName(name);
+	if (lump >= 0) fr = OpenFileReader(lump);
 	return fr;
 }
 
 FileReader FileSystem::ReopenFileReader(const char* name, bool alwayscache)
 {
 	FileReader fr;
-	auto filenum = FindFile(name);
-	if (filenum >= 0) fr = ReopenFileReader(filenum, alwayscache);
+	auto lump = CheckNumForFullName(name);
+	if (lump >= 0) fr = ReopenFileReader(lump, alwayscache);
 	return fr;
 }
 
@@ -982,13 +1353,13 @@ FileReader *FileSystem::GetFileReader(int rfnum)
 
 //==========================================================================
 //
-// GetContainerName
+// GetResourceFileName
 //
 // Returns the name of the given wad.
 //
 //==========================================================================
 
-const char *FileSystem::GetContainerName (int rfnum) const noexcept
+const char *FileSystem::GetResourceFileName (int rfnum) const noexcept
 {
 	const char *name, *slash;
 
@@ -1022,21 +1393,6 @@ int FileSystem::GetFirstEntry (int rfnum) const noexcept
 //
 //==========================================================================
 
-int FileSystem::GetContainerFlags(int rfnum) const noexcept
-{
-	if ((uint32_t)rfnum >= Files.size())
-	{
-		return 0;
-	}
-
-	return Files[rfnum]->GetFlags();
-}
-
-//==========================================================================
-//
-//
-//==========================================================================
-
 int FileSystem::GetLastEntry (int rfnum) const noexcept
 {
 	if ((uint32_t)rfnum >= Files.size())
@@ -1071,7 +1427,7 @@ int FileSystem::GetEntryCount (int rfnum) const noexcept
 //
 //==========================================================================
 
-const char *FileSystem::GetContainerFullName (int rfnum) const noexcept
+const char *FileSystem::GetResourceFileFullName (int rfnum) const noexcept
 {
 	if ((unsigned int)rfnum >= Files.size())
 	{
@@ -1094,21 +1450,21 @@ bool FileSystem::CreatePathlessCopy(const char *name, int id, int /*flags*/)
 
 	// The old code said 'filename' and ignored the path, this looked like a bug.
 	FixPathSeparator(&name2.front());
-	auto filenum = FindFile(name2.c_str());
-	if (filenum < 0) return false;		// Does not exist.
+	auto lump = FindFile(name2.c_str());
+	if (lump < 0) return false;		// Does not exist.
 
-	auto oldlump = FileInfo[filenum];
-	auto slash = strrchr(oldlump.Name, '/');
+	auto oldlump = FileInfo[lump];
+	auto slash = strrchr(oldlump.LongName, '/');
 
 	if (slash == nullptr)
 	{
-		FileInfo[filenum].flags = RESFF_FULLPATH;
+		FileInfo[lump].flags = RESFF_FULLPATH;
 		return true;	// already is pathless.
 	}
 
 
 	// just create a new reference to the original data with a different name.
-	oldlump.Name = slash + 1;
+	oldlump.LongName = slash + 1;
 	oldlump.resourceId = id;
 	oldlump.flags = RESFF_FULLPATH;
 	FileInfo.push_back(oldlump);
diff --git a/source/common/filesystem/source/resourcefile.cpp b/source/common/filesystem/source/resourcefile.cpp
index 6736cfa71..00a5234ca 100644
--- a/source/common/filesystem/source/resourcefile.cpp
+++ b/source/common/filesystem/source/resourcefile.cpp
@@ -113,7 +113,7 @@ bool FResourceFile::IsFileInFolder(const char* const resPath)
 	return 0 == stricmp(filePath.c_str(), resPath);
 }
 
-void FResourceFile::CheckEmbedded(uint32_t entry, FileSystemFilterInfo* lfi)
+void FResourceFile::CheckEmbedded(uint32_t entry, LumpFilterInfo* lfi)
 {
 	// Checks for embedded archives
 	auto FullName = Entries[entry].FileName;
@@ -138,30 +138,29 @@ void FResourceFile::CheckEmbedded(uint32_t entry, FileSystemFilterInfo* lfi)
 //
 //==========================================================================
 
-typedef FResourceFile * (*CheckFunc)(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+typedef FResourceFile * (*CheckFunc)(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
 
-FResourceFile *CheckWad(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile *CheckGRP(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile *CheckRFF(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile *CheckPak(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile *CheckZip(const char *filename, FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile *Check7Z(const char *filename,  FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile* CheckSSI(const char* filename, FileReader& file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile* CheckHog(const char* filename, FileReader& file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile* CheckHog2(const char* filename, FileReader& file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile* CheckMvl(const char* filename, FileReader& file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile* CheckWHRes(const char* filename, FileReader& file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile *CheckLump(const char *filename,FileReader &file, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
-FResourceFile *CheckDir(const char *filename, bool nosub, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile *CheckWad(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile *CheckGRP(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile *CheckRFF(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile *CheckPak(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile *CheckZip(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile *Check7Z(const char *filename,  FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile* CheckSSI(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile* CheckHog(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile* CheckMvl(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile* CheckWHRes(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile *CheckLump(const char *filename,FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
+FResourceFile *CheckDir(const char *filename, bool nosub, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp);
 
-static CheckFunc funcs[] = { CheckWad, CheckZip, Check7Z, CheckPak, CheckGRP, CheckRFF, CheckHog, CheckMvl, CheckHog2, CheckSSI, CheckWHRes, CheckLump };
+static CheckFunc funcs[] = { CheckWad, CheckZip, Check7Z, CheckPak, CheckGRP, CheckRFF, CheckSSI, CheckHog, CheckMvl, CheckWHRes, CheckLump };
 
 static int nulPrintf(FSMessageLevel msg, const char* fmt, ...)
 {
 	return 0;
 }
 
-FResourceFile *FResourceFile::DoOpenResourceFile(const char *filename, FileReader &file, bool containeronly, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *FResourceFile::DoOpenResourceFile(const char *filename, FileReader &file, bool containeronly, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	if (!file.isOpen()) return nullptr;
 	if (Printf == nullptr) Printf = nulPrintf;
@@ -174,20 +173,20 @@ FResourceFile *FResourceFile::DoOpenResourceFile(const char *filename, FileReade
 	return NULL;
 }
 
-FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader &file, bool containeronly, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader &file, bool containeronly, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	return DoOpenResourceFile(filename, file, containeronly, filter, Printf, sp);
 }
 
 
-FResourceFile *FResourceFile::OpenResourceFile(const char *filename, bool containeronly, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *FResourceFile::OpenResourceFile(const char *filename, bool containeronly, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	FileReader file;
 	if (!file.OpenFile(filename)) return nullptr;
 	return DoOpenResourceFile(filename, file, containeronly, filter, Printf, sp);
 }
 
-FResourceFile *FResourceFile::OpenDirectory(const char *filename, FileSystemFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
+FResourceFile *FResourceFile::OpenDirectory(const char *filename, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp)
 {
 	if (Printf == nullptr) Printf = nulPrintf;
 	return CheckDir(filename, false, filter, Printf, sp);
@@ -199,15 +198,14 @@ FResourceFile *FResourceFile::OpenDirectory(const char *filename, FileSystemFilt
 //
 //==========================================================================
 
-FResourceFile::FResourceFile(const char *filename, StringPool* sp, int flags_)
-	:flags(flags_)
+FResourceFile::FResourceFile(const char *filename, StringPool* sp)
 {
 	stringpool = sp ? sp : new StringPool(false);
 	FileName = stringpool->Strdup(filename);
 }
 
-FResourceFile::FResourceFile(const char *filename, FileReader &r, StringPool* sp, int flags)
-	: FResourceFile(filename,sp, flags)
+FResourceFile::FResourceFile(const char *filename, FileReader &r, StringPool* sp)
+	: FResourceFile(filename,sp)
 {
 	Reader = std::move(r);
 }
@@ -252,7 +250,7 @@ FCompressedBuffer FResourceFile::GetRawData(uint32_t entry)
 //
 //==========================================================================
 
-const char* FResourceFile::NormalizeFileName(const char* fn, int fallbackcp, bool allowbackslash)
+const char* FResourceFile::NormalizeFileName(const char* fn, int fallbackcp)
 {
 	if (!fn || !*fn) return "";
 	auto norm = tolower_normalize(fn);
@@ -276,8 +274,7 @@ const char* FResourceFile::NormalizeFileName(const char* fn, int fallbackcp, boo
 			norm = tolower_normalize(&ffn.front());
 		}
 	}
-	// The WAD format can have legal backslashes in its names so this must be optional.
-	if (!allowbackslash) FixPathSeparator(norm);
+	FixPathSeparator(norm);
 	auto pooled = stringpool->Strdup(norm);
 	free(norm);
 	return pooled;
@@ -357,7 +354,7 @@ void FResourceFile::GenerateHash()
 //
 //==========================================================================
 
-void FResourceFile::PostProcessArchive(FileSystemFilterInfo *filter)
+void FResourceFile::PostProcessArchive(LumpFilterInfo *filter)
 {
 	// only do this for archive types which contain full file names. All others are assumed to be pre-sorted.
 	if (NumLumps == 0 || !(Entries[0].Flags & RESFF_FULLPATH)) return;
@@ -419,7 +416,7 @@ void FResourceFile::PostProcessArchive(FileSystemFilterInfo *filter)
 //
 //==========================================================================
 
-void FResourceFile::FindCommonFolder(FileSystemFilterInfo* filter)
+void FResourceFile::FindCommonFolder(LumpFilterInfo* filter)
 {
 	std::string name0, name1;
 	bool foundspeciallump = false;
diff --git a/source/common/fonts/v_font.cpp b/source/common/fonts/v_font.cpp
index 9cd5cb2a8..d838fb181 100644
--- a/source/common/fonts/v_font.cpp
+++ b/source/common/fonts/v_font.cpp
@@ -120,7 +120,7 @@ FFont *V_GetFont(const char *name, const char *fontlumpname)
 		}
 
 
-		lump = fileSystem.CheckNumForAnyName(fontlumpname? fontlumpname : name);
+		lump = fileSystem.CheckNumForFullName(fontlumpname? fontlumpname : name, true);
 
 		if (lump != -1 && fileSystem.GetFileContainer(lump) >= folderfile)
 		{
@@ -290,7 +290,7 @@ void V_InitCustomFonts()
 					{
 						*p = TexMan.GetGameTexture(texid);
 					}
-					else if (fileSystem.GetFileContainer(sc.LumpNum) >= fileSystem.GetBaseNum())
+					else if (fileSystem.GetFileContainer(sc.LumpNum) >= fileSystem.GetIwadNum())
 					{
 						// Print a message only if this isn't in zdoom.pk3
 						sc.ScriptMessage("%s: Unable to find texture in font definition for %s", sc.String, namebuffer.GetChars());
@@ -871,7 +871,7 @@ void V_InitFonts()
 	FFont *CreateHexLumpFont(const char *fontname, int lump);
 	FFont *CreateHexLumpFont2(const char *fontname, int lump);
 
-	auto lump = fileSystem.GetFileInContainer("newconsolefont.hex", 0);	// This is always loaded from gzdoom.pk3 to prevent overriding it with incomplete replacements.
+	auto lump = fileSystem.CheckNumForFullName("newconsolefont.hex", 0);	// This is always loaded from gzdoom.pk3 to prevent overriding it with incomplete replacements.
 	if (lump == -1) I_FatalError("newconsolefont.hex not found");	// This font is needed - do not start up without it.
 	NewConsoleFont = CreateHexLumpFont("NewConsoleFont", lump);
 	NewSmallFont = CreateHexLumpFont2("NewSmallFont", lump);
diff --git a/source/common/menu/menudef.cpp b/source/common/menu/menudef.cpp
index 42d006d9d..ad02001e0 100644
--- a/source/common/menu/menudef.cpp
+++ b/source/common/menu/menudef.cpp
@@ -1524,7 +1524,7 @@ void M_ParseMenuDefs()
 	DefaultOptionMenuSettings->Reset();
 	OptionSettings.mLinespacing = 17;
 
-	int IWADMenu = fileSystem.CheckNumForName("MENUDEF", ns_global, fileSystem.GetBaseNum());
+	int IWADMenu = fileSystem.CheckNumForName("MENUDEF", FileSys::ns_global, fileSystem.GetIwadNum());
 
 	while ((lump = fileSystem.FindLump ("MENUDEF", &lastlump)) != -1)
 	{
diff --git a/source/common/models/model.cpp b/source/common/models/model.cpp
index ee44e4bca..0f1603415 100644
--- a/source/common/models/model.cpp
+++ b/source/common/models/model.cpp
@@ -88,7 +88,7 @@ void FModel::DestroyVertexBuffer()
 
 static int FindGFXFile(FString & fn)
 {
-	int lump = fileSystem.FindFile(fn.GetChars());	// if we find something that matches the name plus the extension, return it and do not enter the substitution logic below.
+	int lump = fileSystem.CheckNumForFullName(fn.GetChars());	// if we find something that matches the name plus the extension, return it and do not enter the substitution logic below.
 	if (lump != -1) return lump;
 
 	int best = -1;
@@ -100,7 +100,7 @@ static int FindGFXFile(FString & fn)
 
 	for (const char ** extp=extensions; *extp; extp++)
 	{
-		lump = fileSystem.FindFile((fn + *extp).GetChars());
+		lump = fileSystem.CheckNumForFullName((fn + *extp).GetChars());
 		if (lump >= best)  best = lump;
 	}
 	return best;
@@ -120,7 +120,7 @@ FTextureID LoadSkin(const char * path, const char * fn)
 	buffer.Format("%s%s", path, fn);
 
 	int texlump = FindGFXFile(buffer);
-	const char * const texname = texlump < 0 ? fn : fileSystem.GetFileName(texlump);
+	const char * const texname = texlump < 0 ? fn : fileSystem.GetFileFullName(texlump);
 	return TexMan.CheckForTexture(texname, ETextureType::Any, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ForceLookup);
 }
 
@@ -148,7 +148,7 @@ unsigned FindModel(const char * path, const char * modelfile, bool silent)
 
 	if (path) fullname.Format("%s%s", path, modelfile);
 	else fullname = modelfile;
-	int lump = fileSystem.FindFile(fullname.GetChars());
+	int lump = fileSystem.CheckNumForFullName(fullname.GetChars());
 
 	if (lump<0)
 	{
@@ -175,7 +175,7 @@ unsigned FindModel(const char * path, const char * modelfile, bool silent)
 	{
 		FString anivfile = fullname.GetChars();
 		anivfile.Substitute("_d.3d","_a.3d");
-		if ( fileSystem.FindFile(anivfile.GetChars()) > 0 )
+		if ( fileSystem.CheckNumForFullName(anivfile.GetChars()) > 0 )
 		{
 			model = new FUE1Model;
 		}
@@ -184,7 +184,7 @@ unsigned FindModel(const char * path, const char * modelfile, bool silent)
 	{
 		FString datafile = fullname.GetChars();
 		datafile.Substitute("_a.3d","_d.3d");
-		if ( fileSystem.FindFile(datafile.GetChars()) > 0 )
+		if ( fileSystem.CheckNumForFullName(datafile.GetChars()) > 0 )
 		{
 			model = new FUE1Model;
 		}
diff --git a/source/common/models/models_ue1.cpp b/source/common/models/models_ue1.cpp
index f67d76589..810659261 100644
--- a/source/common/models/models_ue1.cpp
+++ b/source/common/models/models_ue1.cpp
@@ -49,18 +49,18 @@ bool FUE1Model::Load( const char *filename, int lumpnum, const char *buffer, int
 {
 	int lumpnum2;
 	hasSurfaces = true;
-	FString realfilename = fileSystem.GetFileName(lumpnum);
+	FString realfilename = fileSystem.GetFileFullName(lumpnum);
 	if ( (size_t)realfilename.IndexOf("_d.3d") == realfilename.Len()-5 )
 	{
 		realfilename.Substitute("_d.3d","_a.3d");
-		lumpnum2 = fileSystem.FindFile(realfilename.GetChars());
+		lumpnum2 = fileSystem.CheckNumForFullName(realfilename.GetChars());
 		mDataLump = lumpnum;
 		mAnivLump = lumpnum2;
 	}
 	else
 	{
 		realfilename.Substitute("_a.3d","_d.3d");
-		lumpnum2 = fileSystem.FindFile(realfilename.GetChars());
+		lumpnum2 = fileSystem.CheckNumForFullName(realfilename.GetChars());
 		mAnivLump = lumpnum;
 		mDataLump = lumpnum2;
 	}
diff --git a/source/common/models/voxels.cpp b/source/common/models/voxels.cpp
index af48bd39d..4d1332353 100644
--- a/source/common/models/voxels.cpp
+++ b/source/common/models/voxels.cpp
@@ -309,7 +309,7 @@ FVoxelDef *R_LoadVoxelDef(int lumpnum, int spin)
 	FVoxel *vox = R_LoadKVX(lumpnum);
 	if (vox == NULL)
 	{
-		Printf("%s is not a valid voxel file\n", fileSystem.GetFileName(lumpnum));
+		Printf("%s is not a valid voxel file\n", fileSystem.GetFileFullName(lumpnum));
 		return NULL;
 	}
 	else
diff --git a/source/common/platform/posix/cocoa/st_console.h b/source/common/platform/posix/cocoa/st_console.h
index 91f77d124..b2af7bade 100644
--- a/source/common/platform/posix/cocoa/st_console.h
+++ b/source/common/platform/posix/cocoa/st_console.h
@@ -66,7 +66,6 @@ public:
 	void NetInit(const char* message, int playerCount);
 	void NetProgress(int count);
 	void NetDone();
-	void NetClose();
 
 private:
 	NSWindow*            m_window;
diff --git a/source/common/platform/posix/cocoa/st_console.mm b/source/common/platform/posix/cocoa/st_console.mm
index 95d0ee11e..1c45776e6 100644
--- a/source/common/platform/posix/cocoa/st_console.mm
+++ b/source/common/platform/posix/cocoa/st_console.mm
@@ -531,8 +531,3 @@ void FConsoleWindow::NetDone()
 		m_netAbortButton = nil;
 	}
 }
-
-void FConsoleWindow::NetClose()
-{
-	// TODO: Implement this
-}
diff --git a/source/common/platform/posix/cocoa/st_start.mm b/source/common/platform/posix/cocoa/st_start.mm
index 2cd73104d..ee6ea2627 100644
--- a/source/common/platform/posix/cocoa/st_start.mm
+++ b/source/common/platform/posix/cocoa/st_start.mm
@@ -110,11 +110,6 @@ void FBasicStartupScreen::NetDone()
 	FConsoleWindow::GetInstance().NetDone();
 }
 
-void FBasicStartupScreen::NetClose()
-{
-	FConsoleWindow::GetInstance().NetClose();
-}
-
 bool FBasicStartupScreen::NetLoop(bool (*timerCallback)(void*), void* const userData)
 {
 	while (true)
diff --git a/source/common/platform/posix/i_system.h b/source/common/platform/posix/i_system.h
index 5573e26d3..4d800d53b 100644
--- a/source/common/platform/posix/i_system.h
+++ b/source/common/platform/posix/i_system.h
@@ -54,6 +54,17 @@ bool I_WriteIniFailed (const char* filename);
 class FGameTexture;
 bool I_SetCursor(FGameTexture *);
 
+static inline char *strlwr(char *str)
+{
+	char *ptr = str;
+	while(*ptr)
+	{
+		*ptr = tolower(*ptr);
+		++ptr;
+	}
+	return str;
+}
+
 inline int I_GetNumaNodeCount() { return 1; }
 inline int I_GetNumaNodeThreadCount(int numaNode) { return std::max<int>(std::thread::hardware_concurrency(), 1); }
 inline void I_SetThreadNumaNode(std::thread &thread, int numaNode) { }
diff --git a/source/common/platform/posix/sdl/i_system.cpp b/source/common/platform/posix/sdl/i_system.cpp
index 2c28368b0..31976ba3c 100644
--- a/source/common/platform/posix/sdl/i_system.cpp
+++ b/source/common/platform/posix/sdl/i_system.cpp
@@ -308,7 +308,7 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad, int&
 #ifdef __APPLE__
 	return I_PickIWad_Cocoa (wads, numwads, showwin, defaultiwad);
 #else
-	return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags, &extraArgs);
+	return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags, extraArgs);
 #endif
 }
 
diff --git a/source/common/platform/posix/sdl/st_start.cpp b/source/common/platform/posix/sdl/st_start.cpp
index 5bd88684b..019a66122 100644
--- a/source/common/platform/posix/sdl/st_start.cpp
+++ b/source/common/platform/posix/sdl/st_start.cpp
@@ -57,7 +57,6 @@ class FTTYStartupScreen : public FStartupScreen
 		void NetInit(const char *message, int num_players);
 		void NetProgress(int count);
 		void NetDone();
-		void NetClose();
 		bool NetLoop(bool (*timer_callback)(void *), void *userdata);
 	protected:
 		bool DidNetInit;
@@ -238,11 +237,6 @@ void FTTYStartupScreen::NetProgress(int count)
 	}
 }
 
-void FTTYStartupScreen::NetClose()
-{
-	// TODO: Implement this
-}
-
 //===========================================================================
 //
 // FTTYStartupScreen :: NetLoop
diff --git a/source/common/platform/win32/i_main.cpp b/source/common/platform/win32/i_main.cpp
index c68b5a7aa..acadc2dc8 100644
--- a/source/common/platform/win32/i_main.cpp
+++ b/source/common/platform/win32/i_main.cpp
@@ -134,39 +134,6 @@ void I_SetIWADInfo()
 {
 }
 
-//==========================================================================
-//
-// isConsoleApp()
-//
-// runtime detection to detect if this is a console subsystem app.
-//
-// the reason for doing this is because it is possible to edit a binary directly and change its subsystem
-// type via hexedit so in order to gain flexibility it makes no sense to just compile out the unused code.
-//
-// we may plan to publish tools to allow users to do this manually on their own.
-//
-//==========================================================================
-
-bool isConsoleApp()
-{
-	static bool alreadychecked = false;
-	static bool returnvalue;
-
-	if (!alreadychecked)
-	{
-		DWORD pids[2];
-		DWORD num_pids = GetConsoleProcessList(pids, 2);
-		bool win32con_is_exclusive = (num_pids <= 1);
-
-		returnvalue = ((GetConsoleWindow() != NULL && !win32con_is_exclusive) || (GetStdHandle(STD_OUTPUT_HANDLE) != NULL));
-		alreadychecked = true;
-	}
-
-	//printf("isConsoleApp is %i\n", returnvalue);
-
-	return returnvalue;
-}
-
 //==========================================================================
 //
 // DoMain
@@ -191,22 +158,7 @@ int DoMain (HINSTANCE hInstance)
 		Args->AppendArg(FString(wargv[i]));
 	}
 
-	if (isConsoleApp())
-	{
-		StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
-
-		SetConsoleCP(CP_UTF8);
-		SetConsoleOutputCP(CP_UTF8);
-
-		DWORD mode;
-
-		if (GetConsoleMode(StdOut, &mode))
-		{
-			if (SetConsoleMode(StdOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
-				FancyStdOut = IsWindows10OrGreater(); // Windows 8.1 and lower do not understand ANSI formatting.
-		}
-	}
-	else if (Args->CheckParm("-stdout") || Args->CheckParm("-norun"))
+	if (Args->CheckParm("-stdout") || Args->CheckParm("-norun"))
 	{
 		// As a GUI application, we don't normally get a console when we start.
 		// If we were run from the shell and are on XP+, we can attach to its
@@ -523,11 +475,6 @@ CUSTOM_CVAR(Bool, disablecrashlog, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
 //
 //==========================================================================
 
-int wmain()
-{
-    return wWinMain(GetModuleHandle(0), 0, GetCommandLineW(), SW_SHOW);
-}
-
 int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int nCmdShow)
 {
 	g_hInst = hInstance;
diff --git a/source/common/platform/win32/i_mainwindow.cpp b/source/common/platform/win32/i_mainwindow.cpp
index 5bdd3569c..95dda5031 100644
--- a/source/common/platform/win32/i_mainwindow.cpp
+++ b/source/common/platform/win32/i_mainwindow.cpp
@@ -128,11 +128,6 @@ void MainWindow::HideNetStartPane()
 	NetStartWindow::HideNetStartPane();
 }
 
-void MainWindow::CloseNetStartPane()
-{
-	NetStartWindow::NetClose();
-}
-
 void MainWindow::SetNetStartProgress(int pos)
 {
 	NetStartWindow::SetNetStartProgress(pos);
diff --git a/source/common/platform/win32/i_mainwindow.h b/source/common/platform/win32/i_mainwindow.h
index 83567135c..3c0c7e55d 100644
--- a/source/common/platform/win32/i_mainwindow.h
+++ b/source/common/platform/win32/i_mainwindow.h
@@ -29,7 +29,6 @@ public:
 	void SetNetStartProgress(int pos);
 	bool RunMessageLoop(bool (*timer_callback)(void*), void* userdata);
 	void HideNetStartPane();
-	void CloseNetStartPane();
 
 	void SetWindowTitle(const char* caption);
 
diff --git a/source/common/platform/win32/i_system.cpp b/source/common/platform/win32/i_system.cpp
index caed820b0..2c7eed495 100644
--- a/source/common/platform/win32/i_system.cpp
+++ b/source/common/platform/win32/i_system.cpp
@@ -102,7 +102,6 @@
 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
 
 void DestroyCustomCursor();
-bool isConsoleApp();
 
 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
 
@@ -307,7 +306,6 @@ static void PrintToStdOut(const char *cpt, HANDLE StdOut)
 			else break;
 		}
 	}
-
 	DWORD bytes_written;
 	WriteFile(StdOut, printData.GetChars(), (DWORD)printData.Len(), &bytes_written, NULL);
 	if (terminal) 
@@ -372,7 +370,7 @@ int I_PickIWad(WadStuff *wads, int numwads, bool showwin, int defaultiwad, int&
 	}
 	if (showwin || (vkey != 0 && GetAsyncKeyState(vkey)))
 	{
-		return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags, &extraArgs);
+		return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags, extraArgs);
 	}
 	return defaultiwad;
 }
diff --git a/source/common/platform/win32/st_start.cpp b/source/common/platform/win32/st_start.cpp
index fceeb3ced..8bc48d26e 100644
--- a/source/common/platform/win32/st_start.cpp
+++ b/source/common/platform/win32/st_start.cpp
@@ -201,8 +201,3 @@ bool FBasicStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata
 {
 	return mainwindow.RunMessageLoop(timer_callback, userdata);
 }
-
-void FBasicStartupScreen::NetClose()
-{
-	mainwindow.CloseNetStartPane();
-}
diff --git a/source/common/rendering/gl/gl_shader.cpp b/source/common/rendering/gl/gl_shader.cpp
index d7fbec23e..bafbf89b8 100644
--- a/source/common/rendering/gl/gl_shader.cpp
+++ b/source/common/rendering/gl/gl_shader.cpp
@@ -373,10 +373,10 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
 	i_data += "#define NPOT_EMULATION\nuniform vec2 uNpotEmulation;\n";
 #endif
 
-	int vp_lump = fileSystem.GetFileInContainer(vert_prog_lump, 0);
+	int vp_lump = fileSystem.CheckNumForFullName(vert_prog_lump, 0);
 	if (vp_lump == -1) I_Error("Unable to load '%s'", vert_prog_lump);
 
-	int fp_lump = fileSystem.GetFileInContainer(frag_prog_lump, 0);
+	int fp_lump = fileSystem.CheckNumForFullName(frag_prog_lump, 0);
 	if (fp_lump == -1) I_Error("Unable to load '%s'", frag_prog_lump);
 
 
@@ -418,8 +418,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
 
 		if (*proc_prog_lump != '#')
 		{
-			int pp_lump = fileSystem.GetFileInContainer(proc_prog_lump, 0);	// if it's a core shader, ignore overrides by user mods.
-			if (pp_lump == -1) pp_lump = fileSystem.FindFile(proc_prog_lump);
+			int pp_lump = fileSystem.CheckNumForFullName(proc_prog_lump, 0);	// if it's a core shader, ignore overrides by user mods.
+			if (pp_lump == -1) pp_lump = fileSystem.CheckNumForFullName(proc_prog_lump);
 			if (pp_lump == -1) I_Error("Unable to load '%s'", proc_prog_lump);
 			FString pp_data = GetStringFromLump(pp_lump);
 
@@ -429,13 +429,13 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
 
 				if (pp_data.IndexOf("GetTexCoord") >= 0)
 				{
-					int pl_lump = fileSystem.GetFileInContainer("shaders/glsl/func_defaultmat2.fp", 0);
+					int pl_lump = fileSystem.CheckNumForFullName("shaders/glsl/func_defaultmat2.fp", 0);
 					if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders/glsl/func_defaultmat2.fp");
 					fp_comb << "\n" << GetStringFromLump(pl_lump);
 				}
 				else
 				{
-					int pl_lump = fileSystem.GetFileInContainer("shaders/glsl/func_defaultmat.fp", 0);
+					int pl_lump = fileSystem.CheckNumForFullName("shaders/glsl/func_defaultmat.fp", 0);
 					if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders/glsl/func_defaultmat.fp");
 					fp_comb << "\n" << GetStringFromLump(pl_lump);
 
@@ -461,7 +461,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
 
 			if (pp_data.IndexOf("ProcessLight") < 0)
 			{
-				int pl_lump = fileSystem.GetFileInContainer("shaders/glsl/func_defaultlight.fp", 0);
+				int pl_lump = fileSystem.CheckNumForFullName("shaders/glsl/func_defaultlight.fp", 0);
 				if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders/glsl/func_defaultlight.fp");
 				fp_comb << "\n" << GetStringFromLump(pl_lump);
 			}
@@ -483,7 +483,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
 
 	if (light_fragprog)
 	{
-		int pp_lump = fileSystem.GetFileInContainer(light_fragprog, 0);
+		int pp_lump = fileSystem.CheckNumForFullName(light_fragprog, 0);
 		if (pp_lump == -1) I_Error("Unable to load '%s'", light_fragprog);
 		fp_comb << GetStringFromLump(pp_lump) << "\n";
 	}
diff --git a/source/common/rendering/gl/gl_shaderprogram.cpp b/source/common/rendering/gl/gl_shaderprogram.cpp
index b74a1c059..1c4fd48a4 100644
--- a/source/common/rendering/gl/gl_shaderprogram.cpp
+++ b/source/common/rendering/gl/gl_shaderprogram.cpp
@@ -87,7 +87,7 @@ void FShaderProgram::CreateShader(ShaderType type)
 
 void FShaderProgram::Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion)
 {
-	int lump = fileSystem.FindFile(lumpName);
+	int lump = fileSystem.CheckNumForFullName(lumpName);
 	if (lump == -1) I_FatalError("Unable to load '%s'", lumpName);
 	FString code = GetStringFromLump(lump);
 
diff --git a/source/common/rendering/gles/gles_shader.cpp b/source/common/rendering/gles/gles_shader.cpp
index 40c12dc64..dba9a233a 100644
--- a/source/common/rendering/gles/gles_shader.cpp
+++ b/source/common/rendering/gles/gles_shader.cpp
@@ -381,10 +381,10 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
 	i_data += "#define NPOT_EMULATION\nuniform vec2 uNpotEmulation;\n";
 #endif
 
-	int vp_lump = fileSystem.GetFileInContainer(vert_prog_lump.GetChars(), 0);
+	int vp_lump = fileSystem.CheckNumForFullName(vert_prog_lump.GetChars(), 0);
 	if (vp_lump == -1) I_Error("Unable to load '%s'", vert_prog_lump.GetChars());
 
-	int fp_lump = fileSystem.GetFileInContainer(frag_prog_lump.GetChars(), 0);
+	int fp_lump = fileSystem.CheckNumForFullName(frag_prog_lump.GetChars(), 0);
 	if (fp_lump == -1) I_Error("Unable to load '%s'", frag_prog_lump.GetChars());
 
 
@@ -418,7 +418,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
 
 		if (proc_prog_lump[0] != '#')
 		{
-			int pp_lump = fileSystem.FindFile(proc_prog_lump.GetChars());
+			int pp_lump = fileSystem.CheckNumForFullName(proc_prog_lump.GetChars());
 			if (pp_lump == -1) I_Error("Unable to load '%s'", proc_prog_lump.GetChars());
 			FString pp_data = GetStringFromLump(pp_lump);
 
@@ -428,13 +428,13 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
 
 				if (pp_data.IndexOf("GetTexCoord") >= 0)
 				{
-					int pl_lump = fileSystem.GetFileInContainer("shaders_gles/glsl/func_defaultmat2.fp", 0);
+					int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultmat2.fp", 0);
 					if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultmat2.fp");
 					fp_comb << "\n" << GetStringFromLump(pl_lump);
 				}
 				else
 				{
-					int pl_lump = fileSystem.GetFileInContainer("shaders_gles/glsl/func_defaultmat.fp", 0);
+					int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultmat.fp", 0);
 					if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultmat.fp");
 					fp_comb << "\n" << GetStringFromLump(pl_lump);
 
@@ -460,7 +460,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
 
 			if (pp_data.IndexOf("ProcessLight") < 0)
 			{
-				int pl_lump = fileSystem.GetFileInContainer("shaders_gles/glsl/func_defaultlight.fp", 0);
+				int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultlight.fp", 0);
 				if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultlight.fp");
 				fp_comb << "\n" << GetStringFromLump(pl_lump);
 			}
@@ -482,7 +482,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
 
 	if (light_fragprog.Len())
 	{
-		int pp_lump = fileSystem.GetFileInContainer(light_fragprog.GetChars(), 0);
+		int pp_lump = fileSystem.CheckNumForFullName(light_fragprog.GetChars(), 0);
 		if (pp_lump == -1) I_Error("Unable to load '%s'", light_fragprog.GetChars());
 		fp_comb << GetStringFromLump(pp_lump) << "\n";
 	}
diff --git a/source/common/rendering/gles/gles_shaderprogram.cpp b/source/common/rendering/gles/gles_shaderprogram.cpp
index 1e107bd1b..efc94c71d 100644
--- a/source/common/rendering/gles/gles_shaderprogram.cpp
+++ b/source/common/rendering/gles/gles_shaderprogram.cpp
@@ -87,7 +87,7 @@ void FShaderProgram::CreateShader(ShaderType type)
 
 void FShaderProgram::Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion)
 {
-	int lump = fileSystem.FindFile(lumpName);
+	int lump = fileSystem.CheckNumForFullName(lumpName);
 	if (lump == -1) I_FatalError("Unable to load '%s'", lumpName);
 	FString code = GetStringFromLump(lump);
 	Compile(type, lumpName, code, defines, maxGlslVersion);
diff --git a/source/common/rendering/r_videoscale.cpp b/source/common/rendering/r_videoscale.cpp
index bfed3ec73..705fe905f 100644
--- a/source/common/rendering/r_videoscale.cpp
+++ b/source/common/rendering/r_videoscale.cpp
@@ -248,7 +248,7 @@ CCMD (vid_scaletoheight)
 	}
 }
 
-inline bool atob(const char* I)
+inline bool atob(char* I)
 {
     if (stricmp (I, "true") == 0 || stricmp (I, "1") == 0)
         return true;
diff --git a/source/common/rendering/vulkan/shaders/vk_ppshader.cpp b/source/common/rendering/vulkan/shaders/vk_ppshader.cpp
index 38f2201cb..dcf72a06d 100644
--- a/source/common/rendering/vulkan/shaders/vk_ppshader.cpp
+++ b/source/common/rendering/vulkan/shaders/vk_ppshader.cpp
@@ -67,7 +67,7 @@ void VkPPShader::Reset()
 
 FString VkPPShader::LoadShaderCode(const FString &lumpName, const FString &defines, int version)
 {
-	int lump = fileSystem.FindFile(lumpName.GetChars());
+	int lump = fileSystem.CheckNumForFullName(lumpName.GetChars());
 	if (lump == -1) I_FatalError("Unable to load '%s'", lumpName.GetChars());
 	FString code = GetStringFromLump(lump);
 
diff --git a/source/common/rendering/vulkan/shaders/vk_shader.cpp b/source/common/rendering/vulkan/shaders/vk_shader.cpp
index 0b908bfe5..00734ac7f 100644
--- a/source/common/rendering/vulkan/shaders/vk_shader.cpp
+++ b/source/common/rendering/vulkan/shaders/vk_shader.cpp
@@ -465,15 +465,15 @@ FString VkShaderManager::GetTargetGlslVersion()
 
 FString VkShaderManager::LoadPublicShaderLump(const char *lumpname)
 {
-	int lump = fileSystem.GetFileInContainer(lumpname, 0);
-	if (lump == -1) lump = fileSystem.FindFile(lumpname);
+	int lump = fileSystem.CheckNumForFullName(lumpname, 0);
+	if (lump == -1) lump = fileSystem.CheckNumForFullName(lumpname);
 	if (lump == -1) I_Error("Unable to load '%s'", lumpname);
 	return GetStringFromLump(lump);
 }
 
 FString VkShaderManager::LoadPrivateShaderLump(const char *lumpname)
 {
-	int lump = fileSystem.GetFileInContainer(lumpname, 0);
+	int lump = fileSystem.CheckNumForFullName(lumpname, 0);
 	if (lump == -1) I_Error("Unable to load '%s'", lumpname);
 	return GetStringFromLump(lump);
 }
diff --git a/source/common/scripting/backend/codegen.cpp b/source/common/scripting/backend/codegen.cpp
index 86bd606f9..b14fb267c 100644
--- a/source/common/scripting/backend/codegen.cpp
+++ b/source/common/scripting/backend/codegen.cpp
@@ -8268,12 +8268,8 @@ static bool CheckFunctionCompatiblity(FScriptPosition &ScriptPosition, PFunction
 FxFunctionCall::FxFunctionCall(FName methodname, FName rngname, FArgumentList &&args, const FScriptPosition &pos)
 : FxExpression(EFX_FunctionCall, pos)
 {
-	const bool isClient = methodname == NAME_CRandom || methodname == NAME_CFRandom
-							|| methodname == NAME_CRandomPick || methodname == NAME_CFRandomPick
-							|| methodname == NAME_CRandom2 || methodname == NAME_CSetRandomSeed;
-
 	MethodName = methodname;
-	RNG = isClient ? &M_Random : &pr_exrandom;
+	RNG = &pr_exrandom;
 	ArgList = std::move(args);
 	if (rngname != NAME_None)
 	{
@@ -8285,16 +8281,7 @@ FxFunctionCall::FxFunctionCall(FName methodname, FName rngname, FArgumentList &&
 		case NAME_FRandomPick:
 		case NAME_Random2:
 		case NAME_SetRandomSeed:
-			RNG = FRandom::StaticFindRNG(rngname.GetChars(), false);
-			break;
-
-		case NAME_CRandom:
-		case NAME_CFRandom:
-		case NAME_CRandomPick:
-		case NAME_CFRandomPick:
-		case NAME_CRandom2:
-		case NAME_CSetRandomSeed:
-			RNG = FRandom::StaticFindRNG(rngname.GetChars(), true);
+			RNG = FRandom::StaticFindRNG(rngname.GetChars());
 			break;
 
 		default:
@@ -8560,22 +8547,13 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
 		}
 		break;
 
-	case NAME_CSetRandomSeed:
-		if (CheckArgSize(NAME_CRandom, ArgList, 1, 1, ScriptPosition))
-		{
-			func = new FxRandomSeed(RNG, ArgList[0], ScriptPosition, ctx.FromDecorate);
-			ArgList[0] = nullptr;
-		}
-		break;
-
 	case NAME_Random:
-	case NAME_CRandom:
 		// allow calling Random without arguments to default to (0, 255)
 		if (ArgList.Size() == 0)
 		{
 			func = new FxRandom(RNG, new FxConstant(0, ScriptPosition), new FxConstant(255, ScriptPosition), ScriptPosition, ctx.FromDecorate);
 		}
-		else if (CheckArgSize(MethodName, ArgList, 2, 2, ScriptPosition))
+		else if (CheckArgSize(NAME_Random, ArgList, 2, 2, ScriptPosition))
 		{
 			func = new FxRandom(RNG, ArgList[0], ArgList[1], ScriptPosition, ctx.FromDecorate);
 			ArgList[0] = ArgList[1] = nullptr;
@@ -8583,8 +8561,7 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
 		break;
 
 	case NAME_FRandom:
-	case NAME_CFRandom:
-		if (CheckArgSize(MethodName, ArgList, 2, 2, ScriptPosition))
+		if (CheckArgSize(NAME_FRandom, ArgList, 2, 2, ScriptPosition))
 		{
 			func = new FxFRandom(RNG, ArgList[0], ArgList[1], ScriptPosition);
 			ArgList[0] = ArgList[1] = nullptr;
@@ -8593,17 +8570,14 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
 
 	case NAME_RandomPick:
 	case NAME_FRandomPick:
-	case NAME_CRandomPick:
-	case NAME_CFRandomPick:
 		if (CheckArgSize(MethodName, ArgList, 1, -1, ScriptPosition))
 		{
-			func = new FxRandomPick(RNG, ArgList, MethodName == NAME_FRandomPick || MethodName == NAME_CFRandomPick, ScriptPosition, ctx.FromDecorate);
+			func = new FxRandomPick(RNG, ArgList, MethodName == NAME_FRandomPick, ScriptPosition, ctx.FromDecorate);
 		}
 		break;
 
 	case NAME_Random2:
-	case NAME_CRandom2:
-		if (CheckArgSize(MethodName, ArgList, 0, 1, ScriptPosition))
+		if (CheckArgSize(NAME_Random2, ArgList, 0, 1, ScriptPosition))
 		{
 			func = new FxRandom2(RNG, ArgList.Size() == 0? nullptr : ArgList[0], ScriptPosition, ctx.FromDecorate);
 			if (ArgList.Size() > 0) ArgList[0] = nullptr;
@@ -12666,15 +12640,6 @@ FxExpression *FxLocalVariableDeclaration::Resolve(FCompileContext &ctx)
 	if (ValueType->RegType == REGT_NIL && ValueType != TypeAuto)
 	{
 		auto sfunc = static_cast<VMScriptFunction *>(ctx.Function->Variants[0].Implementation);
-
-		const unsigned MAX_STACK_ALLOC = 512 * 1024; // Windows stack is 1 MB, but we cannot go up there without problems
-		if (uint64_t(ValueType->Size) + uint64_t(sfunc->ExtraSpace) > MAX_STACK_ALLOC)
-		{
-			ScriptPosition.Message(MSG_ERROR, "%s exceeds max. allowed size of 512kb for local variables at variable %s", sfunc->Name.GetChars(), Name.GetChars());
-			delete this;
-			return nullptr;
-		}
-
 		StackOffset = sfunc->AllocExtraStack(ValueType);
 
 		if (Init != nullptr)
diff --git a/source/common/scripting/backend/vmbuilder.cpp b/source/common/scripting/backend/vmbuilder.cpp
index 5437ca848..71a766d9a 100644
--- a/source/common/scripting/backend/vmbuilder.cpp
+++ b/source/common/scripting/backend/vmbuilder.cpp
@@ -879,18 +879,10 @@ void FFunctionBuildList::Build()
 		{
 			if (!item.Code->CheckReturn())
 			{
-				if (ctx.ReturnProto == nullptr || !ctx.ReturnProto->ReturnTypes.Size())
-				{
-					auto newcmpd = new FxCompoundStatement(item.Code->ScriptPosition);
-					newcmpd->Add(item.Code);
-					newcmpd->Add(new FxReturnStatement(nullptr, item.Code->ScriptPosition));
-					item.Code = newcmpd->Resolve(ctx);
-				}
-				else
-				{
-					item.Code->ScriptPosition.Message(MSG_ERROR, "Missing return statement in %s", item.PrintableName.GetChars());
-					continue;
-				}
+				auto newcmpd = new FxCompoundStatement(item.Code->ScriptPosition);
+				newcmpd->Add(item.Code);
+				newcmpd->Add(new FxReturnStatement(nullptr, item.Code->ScriptPosition));
+				item.Code = newcmpd->Resolve(ctx);
 			}
 
 			item.Proto = ctx.ReturnProto;
diff --git a/source/common/scripting/core/types.cpp b/source/common/scripting/core/types.cpp
index fef3e6ec2..b70783f3e 100644
--- a/source/common/scripting/core/types.cpp
+++ b/source/common/scripting/core/types.cpp
@@ -2585,7 +2585,7 @@ static void PMapValueWriter(FSerializer &ar, const M *map, const PMap *m)
 
 						if (TexMan.GetLinkedTexture(lump) == tex)
 						{
-							name = fileSystem.GetFileName(lump);
+							name = fileSystem.GetFileFullName(lump);
 						}
 						else
 						{
diff --git a/source/common/scripting/frontend/zcc_compile.cpp b/source/common/scripting/frontend/zcc_compile.cpp
index faef50959..d109717a3 100644
--- a/source/common/scripting/frontend/zcc_compile.cpp
+++ b/source/common/scripting/frontend/zcc_compile.cpp
@@ -2286,11 +2286,6 @@ PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize,
 			Error(arraysize, "Array size must be positive");
 			return TypeError;
 		}
-		if (uint64_t(size) * baseType->Size > 0x7fffffff)
-		{
-			Error(arraysize, "Array size overflow. Total size must be less than 2GB");
-			return TypeError;
-		}
 		baseType = NewArray(baseType, size);
 	}
 
diff --git a/source/common/scripting/frontend/zcc_parser.cpp b/source/common/scripting/frontend/zcc_parser.cpp
index 26bf25391..6ea3c316e 100644
--- a/source/common/scripting/frontend/zcc_parser.cpp
+++ b/source/common/scripting/frontend/zcc_parser.cpp
@@ -304,7 +304,7 @@ static void ParseSingleFile(FScanner *pSC, const char *filename, int lump, void
 	{
 		if (filename != nullptr)
 		{
-			lump = fileSystem.CheckNumForAnyName(filename);
+			lump = fileSystem.CheckNumForFullName(filename, true);
 			if (lump >= 0)
 			{
 				lsc.OpenLumpNum(lump);
@@ -480,7 +480,7 @@ PNamespace *ParseOneScript(const int baselump, ZCCParseState &state)
 	ParseSingleFile(&sc, nullptr, lumpnum, parser, state);
 	for (unsigned i = 0; i < Includes.Size(); i++)
 	{
-		lumpnum = fileSystem.CheckNumForAnyName(Includes[i].GetChars());
+		lumpnum = fileSystem.CheckNumForFullName(Includes[i].GetChars(), true);
 		if (lumpnum == -1)
 		{
 			IncludeLocs[i].Message(MSG_ERROR, "Include script lump %s not found", Includes[i].GetChars());
@@ -491,7 +491,7 @@ PNamespace *ParseOneScript(const int baselump, ZCCParseState &state)
 			if (fileno == 0 && fileno2 != 0)
 			{
 				I_FatalError("File %s is overriding core lump %s.",
-					fileSystem.GetContainerFullName(fileSystem.GetFileContainer(lumpnum)), Includes[i].GetChars());
+					fileSystem.GetResourceFileFullName(fileSystem.GetFileContainer(lumpnum)), Includes[i].GetChars());
 			}
 
 			ParseSingleFile(nullptr, nullptr, lumpnum, parser, state);
diff --git a/source/common/scripting/interface/vmnatives.cpp b/source/common/scripting/interface/vmnatives.cpp
index 922f56a67..2b57ceb25 100644
--- a/source/common/scripting/interface/vmnatives.cpp
+++ b/source/common/scripting/interface/vmnatives.cpp
@@ -426,7 +426,7 @@ DEFINE_ACTION_FUNCTION(_TexMan, GetName)
 			// Textures for full path names do not have their own name, they merely link to the source lump.
 			auto lump = tex->GetSourceLump();
 			if (TexMan.GetLinkedTexture(lump) == tex)
-				retval = fileSystem.GetFileName(lump);
+				retval = fileSystem.GetFileFullName(lump);
 		}
 	}
 	ACTION_RETURN_STRING(retval);
@@ -799,7 +799,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(FFont, GetChar, ::GetChar)
 DEFINE_ACTION_FUNCTION(_Wads, GetNumLumps)
 {
 	PARAM_PROLOGUE;
-	ACTION_RETURN_INT(fileSystem.GetFileCount());
+	ACTION_RETURN_INT(fileSystem.GetNumEntries());
 }
 
 DEFINE_ACTION_FUNCTION(_Wads, CheckNumForName)
@@ -816,7 +816,7 @@ DEFINE_ACTION_FUNCTION(_Wads, CheckNumForFullName)
 {
 	PARAM_PROLOGUE;
 	PARAM_STRING(name);
-	ACTION_RETURN_INT(fileSystem.FindFile(name.GetChars()));
+	ACTION_RETURN_INT(fileSystem.CheckNumForFullName(name.GetChars()));
 }
 
 DEFINE_ACTION_FUNCTION(_Wads, FindLump)
@@ -825,7 +825,7 @@ DEFINE_ACTION_FUNCTION(_Wads, FindLump)
 	PARAM_STRING(name);
 	PARAM_INT(startlump);
 	PARAM_INT(ns);
-	const bool isLumpValid = startlump >= 0 && startlump < fileSystem.GetFileCount();
+	const bool isLumpValid = startlump >= 0 && startlump < fileSystem.GetNumEntries();
 	ACTION_RETURN_INT(isLumpValid ? fileSystem.FindLump(name.GetChars(), &startlump, 0 != ns) : -1);
 }
 
@@ -835,7 +835,7 @@ DEFINE_ACTION_FUNCTION(_Wads, FindLumpFullName)
 	PARAM_STRING(name);
 	PARAM_INT(startlump);
 	PARAM_BOOL(noext);
-	const bool isLumpValid = startlump >= 0 && startlump < fileSystem.GetFileCount();
+	const bool isLumpValid = startlump >= 0 && startlump < fileSystem.GetNumEntries();
 	ACTION_RETURN_INT(isLumpValid ? fileSystem.FindLumpFullName(name.GetChars(), &startlump, noext) : -1);
 }
 
@@ -850,7 +850,7 @@ DEFINE_ACTION_FUNCTION(_Wads, GetLumpFullName)
 {
 	PARAM_PROLOGUE;
 	PARAM_INT(lump);
-	ACTION_RETURN_STRING(fileSystem.GetFileName(lump));
+	ACTION_RETURN_STRING(fileSystem.GetFileFullName(lump));
 }
 
 DEFINE_ACTION_FUNCTION(_Wads, GetLumpNamespace)
@@ -864,7 +864,7 @@ DEFINE_ACTION_FUNCTION(_Wads, ReadLump)
 {
 	PARAM_PROLOGUE;
 	PARAM_INT(lump);
-	const bool isLumpValid = lump >= 0 && lump < fileSystem.GetFileCount();
+	const bool isLumpValid = lump >= 0 && lump < fileSystem.GetNumEntries();
 	ACTION_RETURN_STRING(isLumpValid ? GetStringFromLump(lump, false) : FString());
 }
 
diff --git a/source/common/startscreen/endoom.cpp b/source/common/startscreen/endoom.cpp
index 7f47829a1..6e8e7af35 100644
--- a/source/common/startscreen/endoom.cpp
+++ b/source/common/startscreen/endoom.cpp
@@ -153,14 +153,14 @@ int RunEndoom()
 		return 0;
 	}
 
-	int endoom_lump = fileSystem.CheckNumForAnyName (endoomName.GetChars());
+	int endoom_lump = fileSystem.CheckNumForFullName (endoomName.GetChars(), true);
 	
 	if (endoom_lump < 0 || fileSystem.FileLength (endoom_lump) != 4000)
 	{
 		return 0;
 	}
 
-	if (fileSystem.GetFileContainer(endoom_lump) == fileSystem.GetMaxBaseNum() && showendoom == 2)
+	if (fileSystem.GetFileContainer(endoom_lump) == fileSystem.GetMaxIwadNum() && showendoom == 2)
 	{
 		// showendoom==2 means to show only lumps from PWADs.
 		return 0;
diff --git a/source/common/startscreen/startscreen_generic.cpp b/source/common/startscreen/startscreen_generic.cpp
index 4c41bc7a8..b76cbd3e2 100644
--- a/source/common/startscreen/startscreen_generic.cpp
+++ b/source/common/startscreen/startscreen_generic.cpp
@@ -72,7 +72,7 @@ FGenericStartScreen::FGenericStartScreen(int max_progress)
 	: FStartScreen(max_progress)
 {
 	// at this point we do not have a working texture manager yet, so we have to do the lookup via the file system
-	int startup_lump = fileSystem.CheckNumForName("BOOTLOGO", ns_graphics);
+	int startup_lump = fileSystem.CheckNumForName("BOOTLOGO", FileSys::ns_graphics);
 
 	StartupBitmap.Create(640 * 2, 480 * 2);
 	ClearBlock(StartupBitmap, { 0, 0, 0, 255 }, 0, 0, 640 * 2, 480 * 2);
diff --git a/source/common/startscreen/startscreen_hexen.cpp b/source/common/startscreen/startscreen_hexen.cpp
index 6eef73599..a82e21b35 100644
--- a/source/common/startscreen/startscreen_hexen.cpp
+++ b/source/common/startscreen/startscreen_hexen.cpp
@@ -81,9 +81,9 @@ FHexenStartScreen::FHexenStartScreen(int max_progress)
 	: FStartScreen(max_progress)
 {
 	// at this point we do not have a working texture manager yet, so we have to do the lookup via the file system
-	int startup_lump = fileSystem.CheckNumForName("STARTUP", ns_graphics);
-	int netnotch_lump = fileSystem.CheckNumForName("NETNOTCH", ns_graphics);
-	int notch_lump = fileSystem.CheckNumForName("NOTCH", ns_graphics);
+	int startup_lump = fileSystem.CheckNumForName("STARTUP", FileSys::ns_graphics);
+	int netnotch_lump = fileSystem.CheckNumForName("NETNOTCH", FileSys::ns_graphics);
+	int notch_lump = fileSystem.CheckNumForName("NOTCH", FileSys::ns_graphics);
 
 	// For backwards compatibility we also need to look in the default namespace, because these were previously not handled as graphics.
 	if (startup_lump == -1) startup_lump = fileSystem.CheckNumForName("STARTUP");
diff --git a/source/common/startscreen/startscreen_strife.cpp b/source/common/startscreen/startscreen_strife.cpp
index 8f02b2022..df5467f96 100644
--- a/source/common/startscreen/startscreen_strife.cpp
+++ b/source/common/startscreen/startscreen_strife.cpp
@@ -111,7 +111,7 @@ FStrifeStartScreen::FStrifeStartScreen(int max_progress)
 	// Load the background and animated overlays.
 	for (size_t i = 0; i < countof(StrifeStartupPicNames); ++i)
 	{
-		int lumpnum = fileSystem.CheckNumForName(StrifeStartupPicNames[i], ns_graphics);
+		int lumpnum = fileSystem.CheckNumForName(StrifeStartupPicNames[i], FileSys::ns_graphics);
 		if (lumpnum < 0) lumpnum = fileSystem.CheckNumForName(StrifeStartupPicNames[i]);
 
 		if (lumpnum >= 0)
diff --git a/source/common/textures/formats/pngtexture.cpp b/source/common/textures/formats/pngtexture.cpp
index add80bfdf..01f210a1c 100644
--- a/source/common/textures/formats/pngtexture.cpp
+++ b/source/common/textures/formats/pngtexture.cpp
@@ -119,12 +119,12 @@ FImageSource *PNGImage_TryCreate(FileReader & data, int lumpnum)
 
 	if (compression != 0 || filter != 0 || interlace > 1)
 	{
-		Printf(TEXTCOLOR_YELLOW"WARNING: failed to load PNG %s: the compression, filter, or interlace is not supported!\n", fileSystem.GetFileName(lumpnum));
+		Printf(TEXTCOLOR_YELLOW"WARNING: failed to load PNG %s: the compression, filter, or interlace is not supported!\n", fileSystem.GetFileFullName(lumpnum));
 		return NULL;
 	}
 	if (!((1 << colortype) & 0x5D))
 	{
-		Printf(TEXTCOLOR_YELLOW"WARNING: failed to load PNG %s: the colortype (%u) is not supported!\n", fileSystem.GetFileName(lumpnum), colortype);
+		Printf(TEXTCOLOR_YELLOW"WARNING: failed to load PNG %s: the colortype (%u) is not supported!\n", fileSystem.GetFileFullName(lumpnum), colortype);
 		return NULL;
 	}
 	if (!((1 << bitdepth) & 0x116))
@@ -150,12 +150,12 @@ FImageSource *PNGImage_TryCreate(FileReader & data, int lumpnum)
 					int ihoty = data.ReadInt32BE();
 					if (ihotx < -32768 || ihotx > 32767)
 					{
-						Printf("X-Offset for PNG texture %s is bad: %d (0x%08x)\n", fileSystem.GetFileName(lumpnum), ihotx, ihotx);
+						Printf("X-Offset for PNG texture %s is bad: %d (0x%08x)\n", fileSystem.GetFileFullName(lumpnum), ihotx, ihotx);
 						ihotx = 0;
 					}
 					if (ihoty < -32768 || ihoty > 32767)
 					{
-						Printf("Y-Offset for PNG texture %s is bad: %d (0x%08x)\n", fileSystem.GetFileName(lumpnum), ihoty, ihoty);
+						Printf("Y-Offset for PNG texture %s is bad: %d (0x%08x)\n", fileSystem.GetFileFullName(lumpnum), ihoty, ihoty);
 						ihoty = 0;
 					}
 					tex->SetOffsets(ihotx, ihoty);
@@ -169,7 +169,7 @@ FImageSource *PNGImage_TryCreate(FileReader & data, int lumpnum)
 			return tex;
 		}
 
-		Printf(TEXTCOLOR_YELLOW"WARNING: failed to load PNG %s: the bit-depth (%u) is not supported!\n", fileSystem.GetFileName(lumpnum), bitdepth);
+		Printf(TEXTCOLOR_YELLOW"WARNING: failed to load PNG %s: the bit-depth (%u) is not supported!\n", fileSystem.GetFileFullName(lumpnum), bitdepth);
 		return NULL;
 	}
 
@@ -180,7 +180,7 @@ FImageSource *PNGImage_TryCreate(FileReader & data, int lumpnum)
 	{
 		if (data.Read(first4bytes.b, 4) != 4 || first4bytes.dw == MAKE_ID('I','E','N','D'))
 		{
-			Printf(TEXTCOLOR_YELLOW"WARNING: failed to load PNG %s: the file ends immediately after the IHDR.\n", fileSystem.GetFileName(lumpnum));
+			Printf(TEXTCOLOR_YELLOW"WARNING: failed to load PNG %s: the file ends immediately after the IHDR.\n", fileSystem.GetFileFullName(lumpnum));
 			return NULL;
 		}
 	}
@@ -231,12 +231,12 @@ FPNGTexture::FPNGTexture (FileReader &lump, int lumpnum, int width, int height,
 				int ihoty = lump.ReadInt32BE();
 				if (ihotx < -32768 || ihotx > 32767)
 				{
-					Printf ("X-Offset for PNG texture %s is bad: %d (0x%08x)\n", fileSystem.GetFileName (lumpnum), ihotx, ihotx);
+					Printf ("X-Offset for PNG texture %s is bad: %d (0x%08x)\n", fileSystem.GetFileFullName (lumpnum), ihotx, ihotx);
 					ihotx = 0;
 				}
 				if (ihoty < -32768 || ihoty > 32767)
 				{
-					Printf ("Y-Offset for PNG texture %s is bad: %d (0x%08x)\n", fileSystem.GetFileName (lumpnum), ihoty, ihoty);
+					Printf ("Y-Offset for PNG texture %s is bad: %d (0x%08x)\n", fileSystem.GetFileFullName (lumpnum), ihoty, ihoty);
 					ihoty = 0;
 				}
 				LeftOffset = ihotx;
diff --git a/source/common/textures/gametexture.cpp b/source/common/textures/gametexture.cpp
index 546a12005..755f48b49 100644
--- a/source/common/textures/gametexture.cpp
+++ b/source/common/textures/gametexture.cpp
@@ -124,7 +124,7 @@ FGameTexture::~FGameTexture()
 bool FGameTexture::isUserContent() const
 {
 	int filenum = fileSystem.GetFileContainer(Base->GetSourceLump());
-	return (filenum > fileSystem.GetMaxBaseNum());
+	return (filenum > fileSystem.GetMaxIwadNum());
 }
 
 
@@ -182,10 +182,10 @@ void FGameTexture::AddAutoMaterials()
 		if (this->*(layer.pointer) == nullptr)	// only if no explicit assignment had been done.
 		{
 			FStringf lookup("%s%s%s", layer.path, fullname ? "" : "auto/", searchname.GetChars());
-			auto lump = fileSystem.FindFile(lookup.GetChars(), true);
+			auto lump = fileSystem.CheckNumForFullName(lookup.GetChars(), false, FileSys::ns_global, true);
 			if (lump != -1)
 			{
-				auto bmtex = TexMan.FindGameTexture(fileSystem.GetFileName(lump), ETextureType::Any, FTextureManager::TEXMAN_TryAny);
+				auto bmtex = TexMan.FindGameTexture(fileSystem.GetFileFullName(lump), ETextureType::Any, FTextureManager::TEXMAN_TryAny);
 				if (bmtex != nullptr)
 				{
 					this->*(layer.pointer) = bmtex->GetTexture();
@@ -199,10 +199,10 @@ void FGameTexture::AddAutoMaterials()
 		if (!this->Layers || this->Layers.get()->*(layer.pointer) == nullptr)	// only if no explicit assignment had been done.
 		{
 			FStringf lookup("%s%s%s", layer.path, fullname ? "" : "auto/", searchname.GetChars());
-			auto lump = fileSystem.FindFile(lookup.GetChars(), true);
+			auto lump = fileSystem.CheckNumForFullName(lookup.GetChars(), false, FileSys::ns_global, true);
 			if (lump != -1)
 			{
-				auto bmtex = TexMan.FindGameTexture(fileSystem.GetFileName(lump), ETextureType::Any, FTextureManager::TEXMAN_TryAny);
+				auto bmtex = TexMan.FindGameTexture(fileSystem.GetFileFullName(lump), ETextureType::Any, FTextureManager::TEXMAN_TryAny);
 				if (bmtex != nullptr)
 				{
 					if (this->Layers == nullptr) this->Layers = std::make_unique<FMaterialLayers>();
diff --git a/source/common/textures/gametexture.h b/source/common/textures/gametexture.h
index a55616e66..3eb3740a9 100644
--- a/source/common/textures/gametexture.h
+++ b/source/common/textures/gametexture.h
@@ -62,7 +62,6 @@ enum EGameTexFlags
 	GTexf_OffsetsNotForFont = 512,			// The offsets must be ignored when using this texture in a font.
 	GTexf_NoTrim = 1024,					// Don't perform trimming on this texture.
 	GTexf_Seen = 2048,						// Set to true when the texture is being used for rendering. Must be cleared manually if the check is needed.
-	GTexf_NoMipmap = 4096,					// Disable mipmapping for this texture
 };
 
 struct FMaterialLayers
@@ -266,9 +265,6 @@ public:
 	void SetGlowing(PalEntry color) { flags = (flags & ~GTexf_AutoGlowing) | GTexf_Glowing; GlowColor = color; }
 	void SetDisableBrightmap() { flags |= GTexf_BrightmapChecked; Brightmap = nullptr; }
 
-	bool isNoMipmap() const { return !!(flags & GTexf_NoMipmap); }
-	void SetNoMipmap(bool set) { if (set) flags |= GTexf_NoMipmap; else flags &= ~GTexf_NoMipmap; }
-
 	bool isUserContent() const;
 	int CheckRealHeight() { return xs_RoundToInt(Base->CheckRealHeight() / ScaleY); }
 	void SetSize(int x, int y) 
diff --git a/source/common/textures/texturemanager.cpp b/source/common/textures/texturemanager.cpp
index 7d2798203..1173f61be 100644
--- a/source/common/textures/texturemanager.cpp
+++ b/source/common/textures/texturemanager.cpp
@@ -50,6 +50,7 @@
 #include "basics.h"
 #include "cmdlib.h"
 
+using namespace FileSys;
 FTextureManager TexMan;
 
 
@@ -247,7 +248,7 @@ FTextureID FTextureManager::CheckForTexture (const char *name, ETextureType uset
 		if (strchr(name, '/') || (flags & TEXMAN_ForceLookup))
 		{
 			FGameTexture *const NO_TEXTURE = (FGameTexture*)-1; // marker for lumps we already checked that do not map to a texture.
-			int lump = fileSystem.FindFile(name);
+			int lump = fileSystem.CheckNumForFullName(name);
 			if (lump >= 0)
 			{
 				FGameTexture *tex = GetLinkedTexture(lump);
@@ -394,7 +395,7 @@ bool FTextureManager::OkForLocalization(FTextureID texnum, const char *substitut
 
 	// Mode 3 must also reject substitutions for non-IWAD content.
 	int file = fileSystem.GetFileContainer(Textures[texnum.GetIndex()].Texture->GetSourceLump());
-	if (file > fileSystem.GetMaxBaseNum()) return true;
+	if (file > fileSystem.GetMaxIwadNum()) return true;
 
 	return false;
 }
@@ -456,7 +457,7 @@ FTextureID FTextureManager::CreateTexture (int lumpnum, ETextureType usetype)
 			str = fileSystem.GetFileShortName(lumpnum);
 		else
 		{
-			auto fn = fileSystem.GetFileName(lumpnum);
+			auto fn = fileSystem.GetFileFullName(lumpnum);
 			str = ExtractFileBase(fn);
 		}
 		auto out = MakeGameTexture(CreateTextureFromLump(lumpnum, usetype == ETextureType::Flat), str.GetChars(), usetype);
@@ -584,7 +585,7 @@ void FTextureManager::AddGroup(int wadnum, int ns, ETextureType usetype)
 				}
 				progressFunc();
 			}
-			else if (ns == ns_flats && fileSystem.GetFileNamespace(firsttx) == ns_maybeflat)
+			else if (ns == ns_flats && fileSystem.GetFileFlags(firsttx) & RESFF_MAYBEFLAT)
 			{
 				if (fileSystem.CheckNumForName(Name, ns) < firsttx)
 				{
@@ -722,8 +723,8 @@ void FTextureManager::ParseTextureDef(int lump, FMultipatchTextureBuilder &build
 			FName texname = sc.String;
 
 			sc.MustGetString();
-			int lumpnum = fileSystem.CheckNumForAnyName(sc.String, ns_patches);
-			if (lumpnum == -1) lumpnum = fileSystem.CheckNumForAnyName(sc.String, ns_graphics);
+			int lumpnum = fileSystem.CheckNumForFullName(sc.String, true, ns_patches);
+			if (lumpnum == -1) lumpnum = fileSystem.CheckNumForFullName(sc.String, true, ns_graphics);
 
 			if (tlist.Size() == 0)
 			{
@@ -778,8 +779,8 @@ void FTextureManager::ParseTextureDef(int lump, FMultipatchTextureBuilder &build
 			{
 				FString src = base.Left(8);
 
-				int lumpnum = fileSystem.CheckNumForAnyName(sc.String, ns_patches);
-				if (lumpnum == -1) lumpnum = fileSystem.CheckNumForAnyName(sc.String, ns_graphics);
+				int lumpnum = fileSystem.CheckNumForFullName(sc.String, true, ns_patches);
+				if (lumpnum == -1) lumpnum = fileSystem.CheckNumForFullName(sc.String, true, ns_graphics);
 
 				sc.GetString();
 				bool is32bit = !!sc.Compare("force32bit");
@@ -853,7 +854,7 @@ void FTextureManager::ParseTextureDef(int lump, FMultipatchTextureBuilder &build
 			sc.MustGetString();
 
 			// This is not using sc.Open because it can print a more useful error message when done here
-			int includelump = fileSystem.CheckNumForAnyName(sc.String);
+			int includelump = fileSystem.CheckNumForFullName(sc.String, true);
 			if (includelump == -1)
 			{
 				sc.ScriptError("Lump '%s' not found", sc.String);
@@ -936,7 +937,7 @@ void FTextureManager::LoadTextureX(int wadnum, FMultipatchTextureBuilder &build)
 void FTextureManager::AddTexturesForWad(int wadnum, FMultipatchTextureBuilder &build)
 {
 	int firsttexture = Textures.Size();
-	bool iwad = wadnum >= fileSystem.GetBaseNum() && wadnum <= fileSystem.GetMaxBaseNum();
+	bool iwad = wadnum >= fileSystem.GetIwadNum() && wadnum <= fileSystem.GetMaxIwadNum();
 
 	FirstTextureForFile.Push(firsttexture);
 
@@ -970,7 +971,7 @@ void FTextureManager::AddTexturesForWad(int wadnum, FMultipatchTextureBuilder &b
 		if (ns == ns_global)
 		{
 			// In Zips all graphics must be in a separate namespace.
-			if (fileSystem.GetFileFlags(i) & FileSys::RESFF_FULLPATH) continue;
+			if (fileSystem.GetFileFlags(i) & RESFF_FULLPATH) continue;
 
 			// Ignore lumps with empty names.
 			if (fileSystem.CheckFileName(i, "")) continue;
@@ -1108,7 +1109,7 @@ void FTextureManager::SortTexturesByType(int start, int end)
 
 void FTextureManager::AddLocalizedVariants()
 {
-	std::vector<FileSys::FolderEntry> content;
+	std::vector<FolderEntry> content;
 	fileSystem.GetFilesInFolder("localized/textures/", content, false);
 	for (auto &entry : content)
 	{
@@ -1217,7 +1218,7 @@ void FTextureManager::AddTextures(void (*progressFunc_)(), void (*checkForHacks)
 	progressFunc = progressFunc_;
 	//if (BuildTileFiles.Size() == 0) CountBuildTiles ();
 
-	int wadcnt = fileSystem.GetContainerCount();
+	int wadcnt = fileSystem.GetNumWads();
 
 	FMultipatchTextureBuilder build(*this, progressFunc_, checkForHacks);
 
@@ -1409,7 +1410,7 @@ int FTextureManager::GuesstimateNumTextures ()
 {
 	int numtex = 0;
 
-	for(int i = fileSystem.GetFileCount()-1; i>=0; i--)
+	for(int i = fileSystem.GetNumEntries()-1; i>=0; i--)
 	{
 		int space = fileSystem.GetFileNamespace(i);
 		switch(space)
@@ -1420,11 +1421,12 @@ int FTextureManager::GuesstimateNumTextures ()
 		case ns_hires:
 		case ns_patches:
 		case ns_graphics:
-		case ns_maybeflat:
 			numtex++;
 			break;
 
 		default:
+			if (fileSystem.GetFileFlags(i) & RESFF_MAYBEFLAT) numtex++;
+
 			break;
 		}
 	}
@@ -1445,7 +1447,7 @@ int FTextureManager::GuesstimateNumTextures ()
 int FTextureManager::CountTexturesX ()
 {
 	int count = 0;
-	int wadcount = fileSystem.GetContainerCount();
+	int wadcount = fileSystem.GetNumWads();
 	for (int wadnum = 0; wadnum < wadcount; wadnum++)
 	{
 		// Use the most recent PNAMES for this WAD.
@@ -1503,16 +1505,16 @@ void FTextureManager::AdjustSpriteOffsets()
 	int sprid;
 	TMap<int, bool> donotprocess;
 
-	int numtex = fileSystem.GetFileCount();
+	int numtex = fileSystem.GetNumEntries();
 
 	for (int i = 0; i < numtex; i++)
 	{
-		if (fileSystem.GetFileContainer(i) > fileSystem.GetMaxBaseNum()) break; // we are past the IWAD
-		if (fileSystem.GetFileNamespace(i) == ns_sprites && fileSystem.GetFileContainer(i) >= fileSystem.GetBaseNum() && fileSystem.GetFileContainer(i) <= fileSystem.GetMaxBaseNum())
+		if (fileSystem.GetFileContainer(i) > fileSystem.GetMaxIwadNum()) break; // we are past the IWAD
+		if (fileSystem.GetFileNamespace(i) == ns_sprites && fileSystem.GetFileContainer(i) >= fileSystem.GetIwadNum() && fileSystem.GetFileContainer(i) <= fileSystem.GetMaxIwadNum())
 		{
 			const char *str = fileSystem.GetFileShortName(i);
 			FTextureID texid = TexMan.CheckForTexture(str, ETextureType::Sprite, 0);
-			if (texid.isValid() && fileSystem.GetFileContainer(GetGameTexture(texid)->GetSourceLump()) > fileSystem.GetMaxBaseNum())
+			if (texid.isValid() && fileSystem.GetFileContainer(GetGameTexture(texid)->GetSourceLump()) > fileSystem.GetMaxIwadNum())
 			{
 				// This texture has been replaced by some PWAD.
 				memcpy(&sprid, str, 4);
@@ -1551,12 +1553,12 @@ void FTextureManager::AdjustSpriteOffsets()
 
 				int lumpnum = tex->GetSourceLump();
 				// We only want to change texture offsets for sprites in the IWAD or the file this lump originated from.
-				if (lumpnum >= 0 && lumpnum < fileSystem.GetFileCount())
+				if (lumpnum >= 0 && lumpnum < fileSystem.GetNumEntries())
 				{
 					int wadno = fileSystem.GetFileContainer(lumpnum);
-					if ((iwadonly && wadno >= fileSystem.GetBaseNum() && wadno <= fileSystem.GetMaxBaseNum()) || (!iwadonly && wadno == ofslumpno))
+					if ((iwadonly && wadno >= fileSystem.GetIwadNum() && wadno <= fileSystem.GetMaxIwadNum()) || (!iwadonly && wadno == ofslumpno))
 					{
-						if (wadno >= fileSystem.GetBaseNum() && wadno <= fileSystem.GetMaxBaseNum() && !forced && iwadonly)
+						if (wadno >= fileSystem.GetIwadNum() && wadno <= fileSystem.GetMaxIwadNum() && !forced && iwadonly)
 						{
 							memcpy(&sprid, tex->GetName().GetChars(), 4);
 							if (donotprocess.CheckKey(sprid)) continue;	// do not alter sprites that only get partially replaced.
@@ -1630,7 +1632,7 @@ void FTextureManager::Listaliases()
 
 void FTextureManager::SetLinkedTexture(int lump, FGameTexture* tex)
 {
-	if (lump < fileSystem.GetFileCount())
+	if (lump < fileSystem.GetNumEntries())
 	{
 		linkedMap.Insert(lump, tex);
 	}
@@ -1644,7 +1646,7 @@ void FTextureManager::SetLinkedTexture(int lump, FGameTexture* tex)
 
 FGameTexture* FTextureManager::GetLinkedTexture(int lump)
 {
-	if (lump < fileSystem.GetFileCount())
+	if (lump < fileSystem.GetNumEntries())
 	{
 		auto check = linkedMap.CheckKey(lump);
 		if (check) return *check;
diff --git a/source/common/utility/palette.cpp b/source/common/utility/palette.cpp
index cf9fbbe62..03517c1ba 100644
--- a/source/common/utility/palette.cpp
+++ b/source/common/utility/palette.cpp
@@ -672,7 +672,7 @@ FString V_GetColorStringByName(const char* name, FScriptPosition* sc)
 	int c[3], step;
 	size_t namelen;
 
-	if (fileSystem.GetFileCount() == 0) return FString();
+	if (fileSystem.GetNumEntries() == 0) return FString();
 
 	rgblump = fileSystem.CheckNumForName("X11R6RGB");
 	if (rgblump == -1)
@@ -960,13 +960,13 @@ int ReadPalette(int lumpnum, uint8_t* buffer)
 			id = MAKE_ID('I', 'E', 'N', 'D');
 			fr.Read(&id, 4);
 		}
-		I_Error("%s contains no palette", fileSystem.GetFileName(lumpnum));
+		I_Error("%s contains no palette", fileSystem.GetFileFullName(lumpnum));
 	}
 	if (memcmp(lumpmem, "JASC-PAL", 8) == 0)
 	{
 		FScanner sc;
 
-		sc.OpenMem(fileSystem.GetFileName(lumpnum), (char*)lumpmem, int(lump.size()));
+		sc.OpenMem(fileSystem.GetFileFullName(lumpnum), (char*)lumpmem, int(lump.size()));
 		sc.MustGetString();
 		sc.MustGetNumber();	// version - ignore
 		sc.MustGetNumber();
diff --git a/source/common/utility/tarray.h b/source/common/utility/tarray.h
index 25d6e0e7f..3445bd484 100644
--- a/source/common/utility/tarray.h
+++ b/source/common/utility/tarray.h
@@ -924,7 +924,7 @@ public:
 		return *this;
 	}
 
-	~TDeletingArray()
+	~TDeletingArray<T, TT> ()
 	{
 		for (unsigned int i = 0; i < TArray<T,TT>::Size(); ++i)
 		{
diff --git a/source/common/widgets/netstartwindow.cpp b/source/common/widgets/netstartwindow.cpp
index 887f9cc31..69c83e565 100644
--- a/source/common/widgets/netstartwindow.cpp
+++ b/source/common/widgets/netstartwindow.cpp
@@ -64,12 +64,6 @@ bool NetStartWindow::RunMessageLoop(bool (*newtimer_callback)(void*), void* newu
 	return Instance->exitreason;
 }
 
-void NetStartWindow::NetClose()
-{
-	if (Instance != nullptr)
-		Instance->OnClose();
-}
-
 NetStartWindow::NetStartWindow() : Widget(nullptr, WidgetType::Window)
 {
 	SetWindowBackground(Colorf::fromRgba8(51, 51, 51));
diff --git a/source/common/widgets/netstartwindow.h b/source/common/widgets/netstartwindow.h
index dcc9d688e..5d27b8ebf 100644
--- a/source/common/widgets/netstartwindow.h
+++ b/source/common/widgets/netstartwindow.h
@@ -14,7 +14,6 @@ public:
 	static void HideNetStartPane();
 	static void SetNetStartProgress(int pos);
 	static bool RunMessageLoop(bool (*timer_callback)(void*), void* userdata);
-	static void NetClose();
 
 private:
 	NetStartWindow();
diff --git a/source/core/defparser.cpp b/source/core/defparser.cpp
index b7389b813..412b4f309 100644
--- a/source/core/defparser.cpp
+++ b/source/core/defparser.cpp
@@ -1978,7 +1978,7 @@ void loaddefinitionsfile(TilesetBuildInfo& info, const char* fn, bool cumulative
 		int lump, lastlump = 0;
 		while ((lump = fileSystem.FindLumpFullName(fn, &lastlump)) >= 0)
 		{
-			if (maingame && fileSystem.GetFileContainer(lump) > fileSystem.GetMaxBaseNum()) break;
+			if (maingame && fileSystem.GetFileContainer(lump) > fileSystem.GetMaxIwadNum()) break;
 			Printf(PRINT_NONOTIFY, "Loading \"%s\"\n", fileSystem.GetFileFullPath(lump).c_str());
 			deftimer.Clock();
 			parseit(lump);
diff --git a/source/core/g_mapinfo.cpp b/source/core/g_mapinfo.cpp
index 2ed262596..c22f87e8d 100644
--- a/source/core/g_mapinfo.cpp
+++ b/source/core/g_mapinfo.cpp
@@ -1664,7 +1664,7 @@ void FMapInfoParser::ParseMapInfo (int lump, MapRecord &gamedefaults, MapRecord
 		if (sc.Compare("include"))
 		{
 			sc.MustGetString();
-			int inclump = fileSystem.CheckNumForAnyName(sc.String);
+			int inclump = fileSystem.CheckNumForFullName(sc.String, true);
 			if (inclump < 0)
 			{
 				sc.ScriptError("include file '%s' not found", sc.String);
@@ -1675,7 +1675,7 @@ void FMapInfoParser::ParseMapInfo (int lump, MapRecord &gamedefaults, MapRecord
 				if (fileSystem.GetFileContainer(sc.LumpNum) == 0)
 				{
 					I_FatalError("File %s is overriding core lump %s.",
-						fileSystem.GetContainerFullName(fileSystem.GetFileContainer(inclump)), sc.String);
+						fileSystem.GetResourceFileFullName(fileSystem.GetFileContainer(inclump)), sc.String);
 				}
 			}
 			// use a new parser object to parse the include. Otherwise we'd have to save the entire FScanner in a local variable which is a lot more messy.
diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp
index 79b6f1ba8..485f5a3f4 100644
--- a/source/core/gamecontrol.cpp
+++ b/source/core/gamecontrol.cpp
@@ -292,7 +292,7 @@ void System_CrashInfo(char* buffer, size_t bufflen, const char *lfstr)
 		buffer += snprintf(buffer, buffend - buffer, " %s", Args->GetArg(i));
 	}
 
-	for (i = 0; (arg = fileSystem.GetContainerName(i)) != NULL; ++i)
+	for (i = 0; (arg = fileSystem.GetResourceFileName(i)) != NULL; ++i)
 	{
 		buffer += mysnprintf(buffer, buffend - buffer, "%sFile %d: %s", lfstr, i, arg);
 	}
@@ -1026,9 +1026,9 @@ int RunGame()
 	InitWidgetResources(wad);
 
 	// load strings for picker window.
-	FileSystem lang_fs;
+	FileSys::FileSystem lang_fs;
 	std::vector<std::string> base_fn = { wad };
-	lang_fs.Initialize(base_fn);
+	lang_fs.InitMultipleFiles(base_fn);
 	GStrings.LoadStrings(lang_fs, language);
 
 	// Set up the console before anything else so that it can receive text.
diff --git a/source/core/initfs.cpp b/source/core/initfs.cpp
index 7d5ddeef8..78b9c98c7 100644
--- a/source/core/initfs.cpp
+++ b/source/core/initfs.cpp
@@ -49,6 +49,8 @@
 #include "startupinfo.h"
 #include "files.h"
 
+using namespace FileSys;
+
 #ifndef PATH_MAX
 #define PATH_MAX 260
 #endif
@@ -266,10 +268,10 @@ static void DeleteStuff(FileSystem &fileSystem, const TArray<FString>& deletelum
 			str.Truncate(ndx);
 		}
 
-		for (int i = 0; i < fileSystem.GetFileCount(); i++)
+		for (int i = 0; i < fileSystem.GetNumEntries(); i++)
 		{
 			int cf = fileSystem.GetFileContainer(i);
-			auto fname = fileSystem.GetFileName(i);
+			auto fname = fileSystem.GetFileFullName(i, false);
 			if (cf >= 1 && cf <= numgamefiles && !str.CompareNoCase(fname))
 			{
 				fileSystem.RenameFile(i, renameTo.GetChars());
@@ -294,7 +296,7 @@ const char** iwad_reserved()
 		(g_gameType & GAMEFLAG_BLOOD) ? iwad_reserved_blood : iwad_reserved_duke;
 }
 
-static int FileSystemPrintf(FileSys::FSMessageLevel level, const char* fmt, ...)
+static int FileSystemPrintf(FSMessageLevel level, const char* fmt, ...)
 {
 	va_list arg;
 	va_start(arg, fmt);
@@ -302,22 +304,22 @@ static int FileSystemPrintf(FileSys::FSMessageLevel level, const char* fmt, ...)
 	text.VFormat(fmt, arg);
 	switch (level)
 	{
-	case FileSys::FSMessageLevel::Error:
+	case FSMessageLevel::Error:
 		return Printf(TEXTCOLOR_RED "%s", text.GetChars());
 		break;
-	case FileSys::FSMessageLevel::Warning:
+	case FSMessageLevel::Warning:
 		Printf(TEXTCOLOR_YELLOW "%s", text.GetChars());
 		break;
-	case FileSys::FSMessageLevel::Attention:
+	case FSMessageLevel::Attention:
 		Printf(TEXTCOLOR_BLUE "%s", text.GetChars());
 		break;
-	case FileSys::FSMessageLevel::Message:
+	case FSMessageLevel::Message:
 		Printf("%s", text.GetChars());
 		break;
-	case FileSys::FSMessageLevel::DebugWarn:
+	case FSMessageLevel::DebugWarn:
 		DPrintf(DMSG_WARNING, "%s", text.GetChars());
 		break;
-	case FileSys::FSMessageLevel::DebugNotify:
+	case FSMessageLevel::DebugNotify:
 		DPrintf(DMSG_NOTIFY, "%s", text.GetChars());
 		break;
 	}
@@ -371,8 +373,8 @@ void InitFileSystem(TArray<GrpEntry>& groups)
 		}
 		i--;
 	}
-	fileSystem.SetBaseNum(1);
-	fileSystem.SetMaxBaseNum((int)Files.size() - 1);
+	fileSystem.SetIwadNum(1);
+	fileSystem.SetMaxIwadNum((int)Files.size() - 1);
 
 	if (!Args->CheckParm("-noautoload"))
 	{
@@ -426,7 +428,7 @@ void InitFileSystem(TArray<GrpEntry>& groups)
 		todelete.Append(g.FileInfo.tobedeleted);
 	}
 	todelete.Append(userConfig.toBeDeleted);
-	FileSys::FileSystemFilterInfo lfi;
+	LumpFilterInfo lfi;
 	lfi.reservedFolders = { "textures/", "hires/", "sounds/", "music/", "maps/" };
 	for (auto p = iwad_reserved(); *p; p++) lfi.requiredPrefixes.push_back(*p);
 #if 0
@@ -444,14 +446,14 @@ void InitFileSystem(TArray<GrpEntry>& groups)
 	{
 		DeleteStuff(fileSystem, todelete, groups.Size());
 	};
-	fileSystem.Initialize(Files, &lfi, FileSystemPrintf);
+	fileSystem.InitMultipleFiles(Files, &lfi, FileSystemPrintf);
 	if (Args->CheckParm("-dumpfs"))
 	{
 		FILE* f = fopen("filesystem.dir", "wb");
-		for (int num = 0; num < fileSystem.GetFileCount(); num++)
+		for (int num = 0; num < fileSystem.GetNumEntries(); num++)
 		{
 			auto fd = fileSystem.FileLength(num);
-			fprintf(f, "%.50s   %60s  %td\n", fileSystem.GetFileName(num), fileSystem.GetContainerFullName(fileSystem.GetFileContainer(num)), fd);
+			fprintf(f, "%.50s   %60s  %td\n", fileSystem.GetFileFullName(num), fileSystem.GetResourceFileFullName(fileSystem.GetFileContainer(num)), fd);
 		}
 		fclose(f);
 	}
diff --git a/source/core/mapinfo.cpp b/source/core/mapinfo.cpp
index 8376a56ee..3978c91ca 100644
--- a/source/core/mapinfo.cpp
+++ b/source/core/mapinfo.cpp
@@ -63,7 +63,7 @@ CCMD(listmaps)
 		if (lump >= 0)
 		{
 			int rfnum = fileSystem.GetFileContainer(lump);
-			Printf("%s - %s (%s)\n", map->LabelName(), map->DisplayName(), fileSystem.GetContainerName(rfnum));
+			Printf("%s - %s (%s)\n", map->LabelName(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum));
 		}
 		else
 		{
@@ -260,7 +260,7 @@ MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic)
 	map->SetFileName(boardfilename);
 	map->flags = MI_USERMAP|MI_FORCEEOG;
 	int lookup = LookupMusic(boardfilename, true);
-	if (lookup >= 0) map->music = fileSystem.GetFileName(lookup);
+	if (lookup >= 0) map->music = fileSystem.GetFileFullName(lookup);
 	else map->music = defaultmusic;
 	return map;
 }
diff --git a/source/core/menu/usermap.cpp b/source/core/menu/usermap.cpp
index 2ff9a4edf..72873fab3 100644
--- a/source/core/menu/usermap.cpp
+++ b/source/core/menu/usermap.cpp
@@ -53,7 +53,7 @@ static FUsermapDirectory root;
 
 void InsertMap(int lumpnum)
 {
-	FString filename = fileSystem.GetFileName(lumpnum);
+	FString filename = fileSystem.GetFileFullName(lumpnum);
 	auto path = filename.Split("/");
 
 	auto current = &root;
@@ -69,8 +69,8 @@ void InsertMap(int lumpnum)
 	}
 	current->entries.Reserve(1);
 	current->entries.Last().displayname = path.Last();
-	current->entries.Last().filename = fileSystem.GetFileName(lumpnum);
-	current->entries.Last().container = fileSystem.GetContainerName(fileSystem.GetFileContainer(lumpnum));
+	current->entries.Last().filename = fileSystem.GetFileFullName(lumpnum);
+	current->entries.Last().container = fileSystem.GetResourceFileName(fileSystem.GetFileContainer(lumpnum));
 	current->entries.Last().size = (int)fileSystem.FileLength(lumpnum);
 	auto mapinfo = FindMapByName(StripExtension(path.Last().GetChars()).GetChars());
 	if (mapinfo) current->entries.Last().info = mapinfo->name;
@@ -78,7 +78,7 @@ void InsertMap(int lumpnum)
 
 bool ValidateMap(int lumpnum)
 {
-	FString filename = fileSystem.GetFileName(lumpnum);
+	FString filename = fileSystem.GetFileFullName(lumpnum);
 
 	if (fileSystem.FindFile(filename.GetChars()) != lumpnum) return false;
 	auto fr = fileSystem.OpenFileReader(lumpnum);
@@ -126,11 +126,11 @@ void ReadUserMaps()
 	if (didit) return;
 	didit = true;
 
-	int numfiles = fileSystem.GetFileCount();
+	int numfiles = fileSystem.GetNumEntries();
 
 	for (int i = 0; i < numfiles; i++)
 	{
-		auto fn1 = fileSystem.GetFileName(i);
+		auto fn1 = fileSystem.GetFileFullName(i);
 		if (!fn1 || !*fn1) continue;
 		FString lowfn = fn1;
 		if (lowfn.Right(4).CompareNoCase(".map")) continue;
diff --git a/source/core/music/s_advsound.cpp b/source/core/music/s_advsound.cpp
index 7f35d8b3f..124049cba 100644
--- a/source/core/music/s_advsound.cpp
+++ b/source/core/music/s_advsound.cpp
@@ -162,7 +162,7 @@ static FSoundID S_AddSound(const char* logicalname, int lumpnum, FScanner* sc)
 FSoundID S_AddSound(const char* logicalname, const char* lumpname, FScanner* sc)
 {
 	int lump = S_LookupSound(lumpname);
-	if (lump == -1 && sc && fileSystem.GetFileContainer(sc->LumpNum) > fileSystem.GetMaxBaseNum())
+	if (lump == -1 && sc && fileSystem.GetFileContainer(sc->LumpNum) > fileSystem.GetMaxIwadNum())
 		sc->ScriptMessage("%s: sound file not found", sc->String);
 	return S_AddSound(logicalname, lump, sc);
 }
diff --git a/source/core/r_data/gldefs.cpp b/source/core/r_data/gldefs.cpp
index 5eff7d407..28bda4e4c 100644
--- a/source/core/r_data/gldefs.cpp
+++ b/source/core/r_data/gldefs.cpp
@@ -136,7 +136,7 @@ static void ParseVavoomSkybox()
 				sc.MustGetStringName("map");
 				sc.MustGetString();
 
-				maplump = fileSystem.CheckNumForAnyName(sc.String);
+				maplump = fileSystem.CheckNumForFullName(sc.String, true);
 
 				auto tex = TexMan.FindGameTexture(sc.String, ETextureType::Wall, FTextureManager::TEXMAN_TryAny);
 				if (tex == NULL)
@@ -1888,7 +1888,7 @@ public:
 				{
 					sc.MustGetString();
 					// This is not using sc.Open because it can print a more useful error message when done here
-					lump = fileSystem.CheckNumForAnyName(sc.String);
+					lump = fileSystem.CheckNumForFullName(sc.String, true);
 					if (lump==-1)
 						sc.ScriptError("Lump '%s' not found", sc.String);
 
diff --git a/source/core/raze_music.cpp b/source/core/raze_music.cpp
index 014f45453..3647952c8 100644
--- a/source/core/raze_music.cpp
+++ b/source/core/raze_music.cpp
@@ -104,7 +104,7 @@ int LookupMusic(const char* fn, bool onlyextended)
 		int l = fileSystem.FindFileWithExtensions(name.GetChars(), knownMusicExts, countof(knownMusicExts));
 		if (l >= 0 || onlyextended) return l;
 	}
-	return fileSystem.CheckNumForAnyName(fn, ns_music);
+	return fileSystem.CheckNumForFullName(fn, true, FileSys::ns_music);
 }
 
 //==========================================================================
@@ -123,7 +123,7 @@ int FindMusic(const char* musicname)
 		if (lumpnum >= 0)
 		{
 			// EDuke also looks in a subfolder named after the main game resource. Do this as well if extended lookup is active.
-			auto rfn = fileSystem.GetContainerName(fileSystem.GetFileContainer(lumpnum));
+			auto rfn = fileSystem.GetResourceFileName(fileSystem.GetFileContainer(lumpnum));
 			auto rfbase = ExtractFileBase(rfn);
 			FStringf aliasMusicname("music/%s/%s", rfbase.GetChars(), musicname);
 			int newlumpnum = LookupMusic(aliasMusicname.GetChars());
diff --git a/source/core/raze_sound.h b/source/core/raze_sound.h
index 15fa06979..bccf5a0e8 100644
--- a/source/core/raze_sound.h
+++ b/source/core/raze_sound.h
@@ -42,7 +42,7 @@ public:
 	RazeSoundEngine()
 	{
 		// add the empty sound right now.
-		AddSoundLump("no_sound", fileSystem.FindFile("engine/dsempty.lmp"), 0);
+		AddSoundLump("no_sound", fileSystem.CheckNumForFullName("engine/dsempty.lmp"), 0);
 	}
 	virtual bool SourceIsActor(FSoundChan* chan) { return chan->SourceType == SOURCE_Actor; }
 	virtual int SoundSourceIndex(FSoundChan* chan) { return 0; }
diff --git a/source/core/savegamehelp.cpp b/source/core/savegamehelp.cpp
index 7f7f7ac53..a05952a6a 100644
--- a/source/core/savegamehelp.cpp
+++ b/source/core/savegamehelp.cpp
@@ -205,7 +205,7 @@ bool WriteSavegame(const char* filename, const char *name)
 	savegameinfo.AddString("Software", buf)
 		("Save Version", savesig.currentsavever)
 		.AddString("Engine", savesig.savesig)
-		.AddString("Game Resource", fileSystem.GetContainerName(1))
+		.AddString("Game Resource", fileSystem.GetResourceFileName(1))
 		.AddString("Map Name", lev->DisplayName())
 		.AddString("Creation Time", myasctime())
 		.AddString("Title", name)
@@ -219,7 +219,7 @@ bool WriteSavegame(const char* filename, const char *name)
 	{
 		auto fileno = fileSystem.FindFile(fn);
 		auto mapfile = fileSystem.GetFileContainer(fileno);
-		auto mapcname = fileSystem.GetContainerName(mapfile);
+		auto mapcname = fileSystem.GetResourceFileName(mapfile);
 		if (mapcname) savegameinfo.AddString("Map Resource", mapcname);
 		else
 		{
@@ -285,7 +285,7 @@ static bool CheckSingleFile (const char *name, bool &printRequires, bool printwa
 	{
 		return FileExists(name + 7);	// User maps  must be present to be validated.
 	}
-	if (fileSystem.CheckIfContainerLoaded(name) < 0)
+	if (fileSystem.CheckIfResourceFileLoaded(name) < 0)
 	{
 		if (printwarn)
 		{
@@ -389,7 +389,7 @@ int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu)
 	}
 	else
 	{
-		auto ggfn = ExtractFileBase(fileSystem.GetContainerName(1), true);
+		auto ggfn = ExtractFileBase(fileSystem.GetResourceFileName(1), true);
 		if (gamegrp.CompareNoCase(ggfn) == 0)
 		{
 			return G_CheckSaveGameWads(gamegrp.GetChars(), mapgrp.GetChars(), false) ? 1 : -2;
diff --git a/source/core/statistics.cpp b/source/core/statistics.cpp
index 6970d63c3..7e46caf01 100644
--- a/source/core/statistics.cpp
+++ b/source/core/statistics.cpp
@@ -433,7 +433,7 @@ void STAT_Update(bool endofgame)
 		if (lump >= 0)
 		{
 			int file = fileSystem.GetFileContainer(lump);
-			fn = fileSystem.GetContainerName(file);
+			fn = fileSystem.GetResourceFileName(file);
 		}
 
 		FString section = ExtractFileBase(fn) + "." + ExtractFileBase(LevelData[0].Levelname.GetChars());
diff --git a/source/games/blood/src/barf.cpp b/source/games/blood/src/barf.cpp
index 4cea71785..2e166231f 100644
--- a/source/games/blood/src/barf.cpp
+++ b/source/games/blood/src/barf.cpp
@@ -954,7 +954,7 @@ void addMemoryResource(const char* filePath, int flags, int ID)
 void ReadAllRFS()
 {
 	bool found = false;
-	auto numf = fileSystem.GetFileCount();
+	auto numf = fileSystem.GetNumEntries();
 	for (int i = 0; i < numf; i++)
 	{
 		auto rl = fileSystem.GetResourceType(i);
diff --git a/source/games/blood/src/levels.cpp b/source/games/blood/src/levels.cpp
index 2c80d7c41..44f7c4b1d 100644
--- a/source/games/blood/src/levels.cpp
+++ b/source/games/blood/src/levels.cpp
@@ -93,16 +93,16 @@ static const char* DefFile(void)
 	int found = -1;
 	if (userConfig.DefaultCon.IsEmpty() || userConfig.DefaultCon.CompareNoCase("blood.ini") == 0)
 	{
-		int numlumps = fileSystem.GetFileCount();
+		int numlumps = fileSystem.GetNumEntries();
 		for (int i = numlumps - 1; i >= 0; i--)
 		{
 			int fileno = fileSystem.GetFileContainer(i);
-			if (fileno != -1 && fileno <= fileSystem.GetMaxBaseNum()) continue;
-			FString fn = fileSystem.GetFileName(i);
+			if (fileno != -1 && fileno <= fileSystem.GetMaxIwadNum()) continue;
+			FString fn = fileSystem.GetFileFullName(i, false);
 			FString ext = fn.Right(4);
 			if (ext.CompareNoCase(".ini") == 0)
 			{
-				if (fileSystem.FindFile(fn.GetChars()) != i) continue;
+				if (fileSystem.CheckNumForFullName(fn.GetChars()) != i) continue;
 				if (found == -1)
 				{
 					IniFile inif(fn.GetChars());
@@ -124,7 +124,7 @@ static const char* DefFile(void)
 			}
 		}
 	}
-	if (found >= 0) return fileSystem.GetFileName(found);
+	if (found >= 0) return fileSystem.GetFileFullName(found);
 	// The command line parser stores this in the CON field.
 	return userConfig.DefaultCon.IsNotEmpty() ? userConfig.DefaultCon.GetChars() : "blood.ini";
 }
diff --git a/source/games/blood/src/qav.h b/source/games/blood/src/qav.h
index 512e8f604..a251fce42 100644
--- a/source/games/blood/src/qav.h
+++ b/source/games/blood/src/qav.h
@@ -241,7 +241,7 @@ void qavProcessTimer(DBloodPlayer* const pPlayer, QAV* const pQAV, int* duration
 inline bool qavIsOriginal(const int res_id)
 {
 	auto const lump = fileSystem.FindResource(res_id, "QAV");
-	return lump >= 0 && fileSystem.GetFileContainer(lump) < fileSystem.GetMaxBaseNum();
+	return lump >= 0 && fileSystem.GetFileContainer(lump) < fileSystem.GetMaxIwadNum();
 }
 
 inline int qavGetCorrectID(const int res_id)
diff --git a/source/games/blood/src/sound.cpp b/source/games/blood/src/sound.cpp
index 04cca35dd..dee1448aa 100644
--- a/source/games/blood/src/sound.cpp
+++ b/source/games/blood/src/sound.cpp
@@ -132,7 +132,7 @@ void GameInterface::StartSoundEngine()
 void sndInit(void)
 {
 	soundEngine->AddSoundLump("", 0, 0, -1, 6); // add a dummy entry at index #0
-	for (int i = fileSystem.GetFileCount() - 1; i >= 0; i--)
+	for (int i = fileSystem.GetNumEntries() - 1; i >= 0; i--)
 	{
 		auto type = fileSystem.GetResourceType(i);
 		if (!stricmp(type, "SFX"))
@@ -141,8 +141,8 @@ void sndInit(void)
 		}
 		else if (!stricmp(type, "WAV") || !stricmp(type, "OGG") || !stricmp(type, "FLAC") || !stricmp(type, "VOC"))
 		{
-			if (fileSystem.GetFileNamespace(i) != ns_music)
-				soundEngine->AddSoundLump(fileSystem.GetFileName(i), i, 0, fileSystem.GetResourceId(i) | 0x40000000, 6); // mark the resource ID as special.
+			if (fileSystem.GetFileNamespace(i) != FileSys::ns_music)
+				soundEngine->AddSoundLump(fileSystem.GetFileFullName(i), i, 0, fileSystem.GetResourceId(i) | 0x40000000, 6); // mark the resource ID as special.
 		}
 	}
 	soundEngine->HashSounds();
diff --git a/source/games/duke/src/gamedef.cpp b/source/games/duke/src/gamedef.cpp
index c0bcc3bcf..5a258a299 100644
--- a/source/games/duke/src/gamedef.cpp
+++ b/source/games/duke/src/gamedef.cpp
@@ -252,7 +252,7 @@ enum
 
 void ConCompiler::ReportError(int error)
 {
-	const char* fn = fileSystem.GetFileName(currentsourcefile);
+	const char* fn = fileSystem.GetFileFullName(currentsourcefile);
 	switch (error)
 	{
 	case ERROR_ISAKEYWORD:
@@ -642,7 +642,7 @@ int ConCompiler::transword(void)
 
 	textptr += l;
 
-	const char* fn = fileSystem.GetFileName(currentsourcefile);
+	const char* fn = fileSystem.GetFileFullName(currentsourcefile);
 	if (parsebuf[0] == '{' && parsebuf[1] != 0)
 		Printf(TEXTCOLOR_RED "  * ERROR!(%s, line %d) Expecting a SPACE or CR between '{' and '%s'.\n", fn, line_number, parsebuf + 1);
 	else if (parsebuf[0] == '}' && parsebuf[1] != 0)
@@ -713,7 +713,7 @@ int ConCompiler::transnum(int type)
 			textptr += l;
 			auto el = translatelabeltype(type);
 			auto gl = translatelabeltype(labels[i].type);
-			const char* fn = fileSystem.GetFileName(currentsourcefile);
+			const char* fn = fileSystem.GetFileFullName(currentsourcefile);
 			Printf(TEXTCOLOR_YELLOW "  * WARNING.(%s, line %d) %s: Expected a '%s' label but found a '%s' label instead.\n", fn, line_number, labels[i].GetChars(), el.GetChars(), gl.GetChars());
 			return -1;  // valid label name, but wrong type
 		}
@@ -748,7 +748,7 @@ int ConCompiler::transnum(int type)
 
 	if (type != LABEL_DEFINE && value != 0)
 	{
-		const char* fn = fileSystem.GetFileName(currentsourcefile);
+		const char* fn = fileSystem.GetFileFullName(currentsourcefile);
 		Printf(TEXTCOLOR_YELLOW "  * WARNING.(%s, line %d) Expected an identifier, got a numeric literal %d.\n", fn, line_number, (int)value);
 	}
 
@@ -822,7 +822,7 @@ int ConCompiler::CountCaseStatements()
 
 int ConCompiler::parsecommand()
 {
-	const char* fn = fileSystem.GetFileName(currentsourcefile);
+	const char* fn = fileSystem.GetFileFullName(currentsourcefile);
 	int i, j, k;
 	int tempscrptr;
 	uint8_t done, temp_ifelse_check;// , tw;
@@ -3293,7 +3293,7 @@ void loadcons()
 		// This cannot be done from an RMAPINFO definition because the conditions are too specific and must not override custom maps.
 		int num = fileSystem.FindFile("e1l7.map");
 		int file = fileSystem.GetFileContainer(num);
-		if (file <= fileSystem.GetMaxBaseNum())
+		if (file <= fileSystem.GetMaxIwadNum())
 		{
 			auto maprec = FindMapByName("e1l7");
 			if (maprec) maprec->NextMap = "e1l5";
diff --git a/source/games/duke/src/sounds.cpp b/source/games/duke/src/sounds.cpp
index 8a55ea905..3229f4ab6 100644
--- a/source/games/duke/src/sounds.cpp
+++ b/source/games/duke/src/sounds.cpp
@@ -740,7 +740,7 @@ void S_WorldTourMappingsForOldSounds()
 	for(unsigned i = 1; i < maxsnd; i++)
 	{
 		auto sfx = soundEngine->GetSfx(FSoundID::fromInt(i));
-		FString fname = fileSystem.GetFileName(sfx->lumpnum);
+		FString fname = fileSystem.GetFileFullName(sfx->lumpnum);
 		if (!fname.Right(4).CompareNoCase(".ogg"))
 		{
 			// All names here follow the same convention. We must strip the "sound/" folder and replace the extension to get the original VOCs.
diff --git a/source/launcher/launcherwindow.cpp b/source/launcher/launcherwindow.cpp
index 860d1a109..1cf8335ea 100644
--- a/source/launcher/launcherwindow.cpp
+++ b/source/launcher/launcherwindow.cpp
@@ -11,24 +11,22 @@
 #include <zwidget/window/window.h>
 #include <zwidget/widgets/tabwidget/tabwidget.h>
 
-int LauncherWindow::ExecModal(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString * extraArgs)
+int LauncherWindow::ExecModal(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString& extraArgs)
 {
 	Size screenSize = GetScreenSize();
 	double windowWidth = 615.0;
 	double windowHeight = 700.0;
 
-	auto launcher = std::make_unique<LauncherWindow>(wads, numwads, defaultiwad, autoloadflags);
+	auto launcher = std::make_unique<LauncherWindow>(wads, numwads, defaultiwad, autoloadflags, extraArgs);
 	launcher->SetFrameGeometry((screenSize.width - windowWidth) * 0.5, (screenSize.height - windowHeight) * 0.5, windowWidth, windowHeight);
 	launcher->Show();
 
 	DisplayWindow::RunLoop();
 
-	if(extraArgs) *extraArgs = launcher->PlayGame->GetExtraArgs();
-
 	return launcher->ExecResult;
 }
 
-LauncherWindow::LauncherWindow(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags) : Widget(nullptr, WidgetType::Window)
+LauncherWindow::LauncherWindow(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString& extraArgs) : Widget(nullptr, WidgetType::Window)
 {
 	SetWindowBackground(Colorf::fromRgba8(51, 51, 51));
 	SetWindowBorderColor(Colorf::fromRgba8(51, 51, 51));
@@ -56,6 +54,12 @@ void LauncherWindow::Start()
 {
 	Settings->Save();
 
+	std::string extraargs = PlayGame->GetExtraArgs();
+	if (!extraargs.empty())
+	{
+		// To do: restart the process like the cocoa backend is doing?
+	}
+
 	ExecResult = PlayGame->GetSelectedGame();
 	DisplayWindow::ExitLoop();
 }
diff --git a/source/launcher/launcherwindow.h b/source/launcher/launcherwindow.h
index 8e7c8bbe6..c64676a46 100644
--- a/source/launcher/launcherwindow.h
+++ b/source/launcher/launcherwindow.h
@@ -14,9 +14,9 @@ struct WadStuff;
 class LauncherWindow : public Widget
 {
 public:
-	static int ExecModal(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString * extraArgs = nullptr);
+	static int ExecModal(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString& extraArgs);
 
-	LauncherWindow(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags);
+	LauncherWindow(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString& extraArgs);
 	void UpdateLanguage();
 
 	void Start();
diff --git a/source/launcher/playgamepage.cpp b/source/launcher/playgamepage.cpp
index 46bd16a92..972e48d26 100644
--- a/source/launcher/playgamepage.cpp
+++ b/source/launcher/playgamepage.cpp
@@ -85,6 +85,7 @@ void PlayGamePage::OnGeometryChanged()
 
 	y = GetHeight() - 10.0;
 
+#if 0 // NYI: Additional Parameters
 	double editHeight = 24.0;
 	y -= editHeight;
 	ParametersEdit->SetFrameGeometry(0.0, y, GetWidth(), editHeight);
@@ -94,6 +95,7 @@ void PlayGamePage::OnGeometryChanged()
 	y -= labelHeight;
 	ParametersLabel->SetFrameGeometry(0.0, y, GetWidth(), labelHeight);
 	y -= 10.0;
+#endif
 
 	double listViewBottom = y - 10.0;
 	GamesList->SetFrameGeometry(0.0, listViewTop, GetWidth(), std::max(listViewBottom - listViewTop, 0.0));
diff --git a/source/launcher/settingspage.cpp b/source/launcher/settingspage.cpp
index feebeaacf..6ddc76679 100644
--- a/source/launcher/settingspage.cpp
+++ b/source/launcher/settingspage.cpp
@@ -97,7 +97,7 @@ SettingsPage::SettingsPage(LauncherWindow* launcher, int* autoloadflags) : Widge
 			}
 		}
 	}
-	catch (const std::exception& )
+	catch (const std::exception& ex)
 	{
 		hideLanguage = true;
 	}
diff --git a/source/launcher/settingspage.h b/source/launcher/settingspage.h
index a65f4f164..5c0753194 100644
--- a/source/launcher/settingspage.h
+++ b/source/launcher/settingspage.h
@@ -1,7 +1,6 @@
 #pragma once
 
 #include <zwidget/core/widget.h>
-#include "gstrings.h"
 
 #define RENDER_BACKENDS
 
diff --git a/wadsrc/static/zscript/games/blood/ui/screens.zs b/wadsrc/static/zscript/games/blood/ui/screens.zs
index 37aa12c5d..d3c22dea7 100644
--- a/wadsrc/static/zscript/games/blood/ui/screens.zs
+++ b/wadsrc/static/zscript/games/blood/ui/screens.zs
@@ -296,7 +296,7 @@ class BloodCutscenes ui
 		if (!userConfig.nologo)
 		{
 			Array<int> soundinfo;
-			if (Wads.FindFile("logo.smk") != -1)
+			if (Wads.CheckNumForFullName("logo.smk") != -1)
 			{
 				String s = "logo.wav"; // sound name must be converted at run-time, not compile time!
 				runner.Append(MoviePlayerJob.CreateWithSound("logo.smk", s, MoviePlayer.FIXEDVIEWPORT));
@@ -305,7 +305,7 @@ class BloodCutscenes ui
 			{
 				runner.Append(new("BloodIntroImage").Init("MonolithScreen", ScreenJob.fadein|ScreenJob.fadeout));
  			}
-			if (Wads.FindFile("gti.smk") != -1)
+			if (Wads.CheckNumForFullName("gti.smk") != -1)
 			{
 				String s = "gt.wav";
 				runner.Append(MoviePlayerJob.CreateWithSound("gti.smk", s, MoviePlayer.FIXEDVIEWPORT));
diff --git a/wadsrc/static/zscript/games/exhumed/ui/screens.zs b/wadsrc/static/zscript/games/exhumed/ui/screens.zs
index 701f80178..de24a1660 100644
--- a/wadsrc/static/zscript/games/exhumed/ui/screens.zs
+++ b/wadsrc/static/zscript/games/exhumed/ui/screens.zs
@@ -717,7 +717,7 @@ class ExCredits : ScreenJob
 	{
 		Super.Init();
 		String text;
-		int lump = Wads.FindFile("credits.txt");
+		int lump = Wads.CheckNumForFullName("credits.txt");
 		if (lump > -1) text = Wads.ReadLump(lump);
 		text.Substitute("\r", "");
 		text.Split(credits, "\n\n");
diff --git a/wadsrc/static/zscript/games/usermapmenu.zs b/wadsrc/static/zscript/games/usermapmenu.zs
index 0f684c0f8..17ee2a979 100644
--- a/wadsrc/static/zscript/games/usermapmenu.zs
+++ b/wadsrc/static/zscript/games/usermapmenu.zs
@@ -168,7 +168,7 @@ class UsermapMenu : ListMenu
 	{
 		numparent = (currentDir.parent != null);
 		numdirs = currentDir.GetNumDirectories();
-		numentries = currentDir.GetFileCount();
+		numentries = currentDir.GetNumEntries();
 		NumTotalEntries = numparent + numdirs + numentries;
 	}