diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 3a7c54996e629b3bb58ab735e260ccc552396dd3..213dafd1a626babb8865d7488cb0abfec5d13171 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -3005,7 +3005,7 @@ static void Command_Addfile(void)
 	// Add file on your client directly if it is trivial, or you aren't in a netgame.
 	if (!(netgame || multiplayer) || musiconly)
 	{
-		P_AddWadFile(fn, NULL);
+		P_AddWadFile(fn);
 		return;
 	}
 
@@ -3237,7 +3237,7 @@ static void Got_Addfilecmd(UINT8 **cp, INT32 playernum)
 
 	ncs = findfile(filename,md5sum,true);
 
-	if (ncs != FS_FOUND || !P_AddWadFile(filename, NULL))
+	if (ncs != FS_FOUND || !P_AddWadFile(filename))
 	{
 		Command_ExitGame_f();
 		if (ncs == FS_FOUND)
diff --git a/src/d_netfil.c b/src/d_netfil.c
index adbc8d77e873d6d8e4710721c7087cbf858c915c..284b91692d2e459d7c7500b08f8f5dd46c1f3613 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -444,7 +444,7 @@ void CL_LoadServerFiles(void)
 			continue; // Already loaded
 		else if (fileneeded[i].status == FS_FOUND)
 		{
-			P_AddWadFile(fileneeded[i].filename, NULL);
+			P_AddWadFile(fileneeded[i].filename);
 			G_SetGameModified(true);
 			fileneeded[i].status = FS_OPEN;
 		}
@@ -463,7 +463,7 @@ void CL_LoadServerFiles(void)
 					fileneeded[i].filename);
 
 			// Okay, NOW we know it's safe. Whew.
-			P_AddWadFile(fileneeded[i].filename, NULL);
+			P_AddWadFile(fileneeded[i].filename);
 			if (fileneeded[i].important)
 				G_SetGameModified(true);
 			fileneeded[i].status = FS_OPEN;
diff --git a/src/lua_script.c b/src/lua_script.c
index 9b87f0c297865176a8c9fb66cc95d22102080a96..6564f2588326ce21e56731ce7cd638c0ee39e4f3 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -170,6 +170,7 @@ static inline void LUA_LoadFile(MYFILE *f, char *name)
 		LUA_ClearState();
 	lua_pushinteger(gL, f->wad);
 	lua_setfield(gL, LUA_REGISTRYINDEX, "WAD");
+
 	if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, 0, 0)) {
 		CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1));
 		lua_pop(gL,1);
@@ -182,21 +183,24 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump)
 {
 	MYFILE f;
 	char *name;
-	size_t len;
 	f.wad = wad;
 	f.size = W_LumpLengthPwad(wad, lump);
 	f.data = Z_Malloc(f.size, PU_LUA, NULL);
 	W_ReadLumpPwad(wad, lump, f.data);
 	f.curpos = f.data;
 
-	len = strlen(wadfiles[wad]->filename);
-	name = malloc(len+10);
-	strcpy(name, wadfiles[wad]->filename);
-	if (!fasticmp(&name[len - 4], ".lua")) {
-		// If it's not a .lua file, copy the lump name in too.
-		name[len] = '|';
-		M_Memcpy(name+len+1, wadfiles[wad]->lumpinfo[lump].name, 8);
-		name[len+9] = '\0';
+	if (wadfiles[wad]->type == RET_LUA)
+	{
+		name = malloc(strlen(wadfiles[wad]->filename)+1);
+		strcpy(name, wadfiles[wad]->filename);
+	}
+	else // If it's not a .lua file, copy the lump name in too.
+	{
+		lumpinfo_t *lump_p = &wadfiles[wad]->lumpinfo[lump];
+		size_t length = strlen(wadfiles[wad]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
+		name = malloc(length + 1);
+		sprintf(name, "%s|%s", wadfiles[wad]->filename, lump_p->name2);
+		name[length] = '\0';
 	}
 
 	LUA_LoadFile(&f, name);
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 17d28302ee71cc841d055f91a675ceb92349c89d..aa0f6af2d1d1cbdaa07c51335b16c60e4be171bc 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -489,16 +489,34 @@ static void P_NetArchiveWorld(void)
 	UINT8 *put;
 
 	// reload the map just to see difference
-	const mapsector_t *ms;
-	const mapsidedef_t *msd;
-	const maplinedef_t *mld;
+	mapsector_t *ms;
+	mapsidedef_t *msd;
+	maplinedef_t *mld;
 	const sector_t *ss = sectors;
 	UINT8 diff, diff2;
 
 	WRITEUINT32(save_p, ARCHIVEBLOCK_WORLD);
 	put = save_p;
 
-	ms = W_CacheLumpNum(lastloadedmaplumpnum+ML_SECTORS, PU_CACHE);
+	if (W_IsLumpWad(lastloadedmaplumpnum)) // welp it's a map wad in a pk3
+	{ // HACK: Open wad file rather quickly so we can get the data from the relevant lumps
+		UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
+		filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
+#define retrieve_mapdata(d, f)\
+		d = Z_Malloc((f)->size, PU_CACHE, NULL); \
+		M_Memcpy(d, wadData + (f)->filepos, (f)->size)
+		retrieve_mapdata(ms, fileinfo + ML_SECTORS);
+		retrieve_mapdata(mld, fileinfo + ML_LINEDEFS);
+		retrieve_mapdata(msd, fileinfo + ML_SIDEDEFS);
+#undef retrieve_mapdata
+		Z_Free(wadData); // we're done with this now
+	}
+	else // phew it's just a WAD
+	{
+			ms = W_CacheLumpNum(lastloadedmaplumpnum+ML_SECTORS, PU_CACHE);
+			mld = W_CacheLumpNum(lastloadedmaplumpnum+ML_LINEDEFS, PU_CACHE);
+			msd = W_CacheLumpNum(lastloadedmaplumpnum+ML_SIDEDEFS, PU_CACHE);
+	}
 
 	for (i = 0; i < numsectors; i++, ss++, ms++)
 	{
diff --git a/src/p_setup.c b/src/p_setup.c
index 9f3ff423a55884b46fe1dfb21bd05eedea429181..931a5b844a998a1c2a3df481f1592a82e9a83194 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -348,16 +348,13 @@ UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade)
   * \param lump VERTEXES lump number.
   * \sa ML_VERTEXES
   */
-static inline void P_LoadVertexes(lumpnum_t lumpnum)
+
+static inline void P_LoadRawVertexes(UINT8 *data, size_t i)
 {
-	UINT8 *data;
-	size_t i;
 	mapvertex_t *ml;
 	vertex_t *li;
 
-	// Determine number of lumps:
-	//  total lump length / vertex record length.
-	numvertexes = W_LumpLength(lumpnum) / sizeof (mapvertex_t);
+	numvertexes = i / sizeof (mapvertex_t);
 
 	if (numvertexes <= 0)
 		I_Error("Level has no vertices"); // instead of crashing
@@ -365,9 +362,6 @@ static inline void P_LoadVertexes(lumpnum_t lumpnum)
 	// Allocate zone memory for buffer.
 	vertexes = Z_Calloc(numvertexes * sizeof (*vertexes), PU_LEVEL, NULL);
 
-	// Load data into cache.
-	data = W_CacheLumpNum(lumpnum, PU_STATIC);
-
 	ml = (mapvertex_t *)data;
 	li = vertexes;
 
@@ -377,11 +371,16 @@ static inline void P_LoadVertexes(lumpnum_t lumpnum)
 		li->x = SHORT(ml->x)<<FRACBITS;
 		li->y = SHORT(ml->y)<<FRACBITS;
 	}
+}
 
-	// Free buffer memory.
+static inline void P_LoadVertexes(lumpnum_t lumpnum)
+{
+	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+	P_LoadRawVertexes(data, W_LumpLength(lumpnum));
 	Z_Free(data);
 }
 
+
 //
 // Computes the line length in fracunits, the OpenGL render needs this
 //
@@ -421,20 +420,17 @@ static inline float P_SegLengthf(seg_t *seg)
   * \param lump Lump number of the SEGS resource.
   * \sa ::ML_SEGS
   */
-static void P_LoadSegs(lumpnum_t lumpnum)
+static void P_LoadRawSegs(UINT8 *data, size_t i)
 {
-	UINT8 *data;
-	size_t i;
 	INT32 linedef, side;
 	mapseg_t *ml;
 	seg_t *li;
 	line_t *ldef;
 
-	numsegs = W_LumpLength(lumpnum) / sizeof (mapseg_t);
+	numsegs = i / sizeof (mapseg_t);
 	if (numsegs <= 0)
 		I_Error("Level has no segs"); // instead of crashing
 	segs = Z_Calloc(numsegs * sizeof (*segs), PU_LEVEL, NULL);
-	data = W_CacheLumpNum(lumpnum, PU_STATIC);
 
 	ml = (mapseg_t *)data;
 	li = segs;
@@ -470,27 +466,30 @@ static void P_LoadSegs(lumpnum_t lumpnum)
 		li->numlights = 0;
 		li->rlights = NULL;
 	}
+}
 
+static void P_LoadSegs(lumpnum_t lumpnum)
+{
+	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+	P_LoadRawSegs(data, W_LumpLength(lumpnum));
 	Z_Free(data);
 }
 
+
 /** Loads the SSECTORS resource from a level.
   *
   * \param lump Lump number of the SSECTORS resource.
   * \sa ::ML_SSECTORS
   */
-static inline void P_LoadSubsectors(lumpnum_t lumpnum)
+static inline void P_LoadRawSubsectors(void *data, size_t i)
 {
-	void *data;
-	size_t i;
 	mapsubsector_t *ms;
 	subsector_t *ss;
 
-	numsubsectors = W_LumpLength(lumpnum) / sizeof (mapsubsector_t);
+	numsubsectors = i / sizeof (mapsubsector_t);
 	if (numsubsectors <= 0)
 		I_Error("Level has no subsectors (did you forget to run it through a nodesbuilder?)");
 	ss = subsectors = Z_Calloc(numsubsectors * sizeof (*subsectors), PU_LEVEL, NULL);
-	data = W_CacheLumpNum(lumpnum,PU_STATIC);
 
 	ms = (mapsubsector_t *)data;
 
@@ -504,7 +503,12 @@ static inline void P_LoadSubsectors(lumpnum_t lumpnum)
 #endif
 		ss->validcount = 0;
 	}
+}
 
+static void P_LoadSubsectors(lumpnum_t lumpnum)
+{
+	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+	P_LoadRawSubsectors(data, W_LumpLength(lumpnum));
 	Z_Free(data);
 }
 
@@ -638,29 +642,31 @@ INT32 P_CheckLevelFlat(const char *flatname)
 	return (INT32)i;
 }
 
-static void P_LoadSectors(lumpnum_t lumpnum)
+// Sets up the ingame sectors structures.
+// Lumpnum is the lumpnum of a SECTORS lump.
+static void P_LoadRawSectors(UINT8 *data, size_t i)
 {
-	UINT8 *data;
-	size_t i;
 	mapsector_t *ms;
 	sector_t *ss;
 	levelflat_t *foundflats;
 
-	numsectors = W_LumpLength(lumpnum) / sizeof (mapsector_t);
+	// We count how many sectors we got.
+	numsectors = i / sizeof (mapsector_t);
 	if (numsectors <= 0)
 		I_Error("Level has no sectors");
-	sectors = Z_Calloc(numsectors*sizeof (*sectors), PU_LEVEL, NULL);
-	data = W_CacheLumpNum(lumpnum,PU_STATIC);
 
-	//Fab : FIXME: allocate for whatever number of flats
-	//           512 different flats per level should be plenty
+	// Allocate as much memory as we need into the global sectors table.
+	sectors = Z_Calloc(numsectors*sizeof (*sectors), PU_LEVEL, NULL);
 
+	// Allocate a big chunk of memory as big as our MAXLEVELFLATS limit.
+	//Fab : FIXME: allocate for whatever number of flats - 512 different flats per level should be plenty
 	foundflats = calloc(MAXLEVELFLATS, sizeof (*foundflats));
 	if (foundflats == NULL)
 		I_Error("Ran out of memory while loading sectors\n");
 
 	numlevelflats = 0;
 
+	// For each counted sector, copy the sector raw data from our cache pointer ms, to the global table pointer ss.
 	ms = (mapsector_t *)data;
 	ss = sectors;
 	for (i = 0; i < numsectors; i++, ss++, ms++)
@@ -668,9 +674,6 @@ static void P_LoadSectors(lumpnum_t lumpnum)
 		ss->floorheight = SHORT(ms->floorheight)<<FRACBITS;
 		ss->ceilingheight = SHORT(ms->ceilingheight)<<FRACBITS;
 
-		//
-		//  flats
-		//
 		ss->floorpic = P_AddLevelFlat(ms->floorpic, foundflats);
 		ss->ceilingpic = P_AddLevelFlat(ms->ceilingpic, foundflats);
 
@@ -735,8 +738,6 @@ static void P_LoadSectors(lumpnum_t lumpnum)
 #endif // ----- end special tricks -----
 	}
 
-	Z_Free(data);
-
 	// set the sky flat num
 	skyflatnum = P_AddLevelFlat(SKYFLATNAME, foundflats);
 
@@ -748,22 +749,26 @@ static void P_LoadSectors(lumpnum_t lumpnum)
 	P_SetupLevelFlatAnims();
 }
 
+static void P_LoadSectors(lumpnum_t lumpnum)
+{
+	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+	P_LoadRawSectors(data, W_LumpLength(lumpnum));
+	Z_Free(data);
+}
+
 //
 // P_LoadNodes
 //
-static void P_LoadNodes(lumpnum_t lumpnum)
+static void P_LoadRawNodes(UINT8 *data, size_t i)
 {
-	UINT8 *data;
-	size_t i;
 	UINT8 j, k;
 	mapnode_t *mn;
 	node_t *no;
 
-	numnodes = W_LumpLength(lumpnum) / sizeof (mapnode_t);
+	numnodes = i / sizeof (mapnode_t);
 	if (numnodes <= 0)
 		I_Error("Level has no nodes");
 	nodes = Z_Calloc(numnodes * sizeof (*nodes), PU_LEVEL, NULL);
-	data = W_CacheLumpNum(lumpnum, PU_STATIC);
 
 	mn = (mapnode_t *)data;
 	no = nodes;
@@ -781,7 +786,12 @@ static void P_LoadNodes(lumpnum_t lumpnum)
 				no->bbox[j][k] = SHORT(mn->bbox[j][k])<<FRACBITS;
 		}
 	}
+}
 
+static void P_LoadNodes(lumpnum_t lumpnum)
+{
+	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+	P_LoadRawNodes(data, W_LumpLength(lumpnum));
 	Z_Free(data);
 }
 
@@ -916,18 +926,16 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum)
 //
 // P_LoadThings
 //
-static void P_PrepareThings(lumpnum_t lumpnum)
+
+static void P_PrepareRawThings(UINT8 *data, size_t i)
 {
-	size_t i;
 	mapthing_t *mt;
-	UINT8 *data, *datastart;
 
-	nummapthings = W_LumpLength(lumpnum) / (5 * sizeof (INT16));
+	nummapthings = i / (5 * sizeof (INT16));
 	mapthings = Z_Calloc(nummapthings * sizeof (*mapthings), PU_LEVEL, NULL);
 
 	// Spawn axis points first so they are
 	// at the front of the list for fast searching.
-	data = datastart = W_CacheLumpNum(lumpnum, PU_LEVEL);
 	mt = mapthings;
 	for (i = 0; i < nummapthings; i++, mt++)
 	{
@@ -952,8 +960,13 @@ static void P_PrepareThings(lumpnum_t lumpnum)
 				break;
 		}
 	}
-	Z_Free(datastart);
+}
 
+static void P_PrepareThings(lumpnum_t lumpnum)
+{
+	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+	P_PrepareRawThings(data, W_LumpLength(lumpnum));
+	Z_Free(data);
 }
 
 static void P_LoadThings(void)
@@ -1140,22 +1153,16 @@ void P_WriteThings(lumpnum_t lumpnum)
 	CONS_Printf(M_GetText("newthings%d.lmp saved.\n"), gamemap);
 }
 
-//
-// P_LoadLineDefs
-//
-static void P_LoadLineDefs(lumpnum_t lumpnum)
+static void P_LoadRawLineDefs(UINT8 *data, size_t i)
 {
-	UINT8 *data;
-	size_t i;
 	maplinedef_t *mld;
 	line_t *ld;
 	vertex_t *v1, *v2;
 
-	numlines = W_LumpLength(lumpnum) / sizeof (maplinedef_t);
+	numlines = i / sizeof (maplinedef_t);
 	if (numlines <= 0)
 		I_Error("Level has no linedefs");
 	lines = Z_Calloc(numlines * sizeof (*lines), PU_LEVEL, NULL);
-	data = W_CacheLumpNum(lumpnum, PU_STATIC);
 
 	mld = (maplinedef_t *)data;
 	ld = lines;
@@ -1177,7 +1184,7 @@ static void P_LoadLineDefs(lumpnum_t lumpnum)
 			ld->slopetype = ST_VERTICAL;
 		else if (!ld->dy)
 			ld->slopetype = ST_HORIZONTAL;
-		else if (FixedDiv(ld->dy, ld->dx) > 0)
+		else if ((ld->dy > 0) == (ld->dx > 0))
 			ld->slopetype = ST_POSITIVE;
 		else
 			ld->slopetype = ST_NEGATIVE;
@@ -1217,7 +1224,7 @@ static void P_LoadLineDefs(lumpnum_t lumpnum)
 				if (ld->sidenum[j] != 0xffff && ld->sidenum[j] >= (UINT16)numsides)
 				{
 					ld->sidenum[j] = 0xffff;
-					CONS_Debug(DBG_SETUP, "P_LoadLineDefs: linedef %s has out-of-range sidedef number\n", sizeu1(numlines-i-1));
+					CONS_Debug(DBG_SETUP, "P_LoadRawLineDefs: linedef %s has out-of-range sidedef number\n", sizeu1(numlines-i-1));
 				}
 			}
 		}
@@ -1232,14 +1239,14 @@ static void P_LoadLineDefs(lumpnum_t lumpnum)
 		{
 			ld->sidenum[0] = 0;  // Substitute dummy sidedef for missing right side
 			// cph - print a warning about the bug
-			CONS_Debug(DBG_SETUP, "P_LoadLineDefs: linedef %s missing first sidedef\n", sizeu1(numlines-i-1));
+			CONS_Debug(DBG_SETUP, "P_LoadRawLineDefs: linedef %s missing first sidedef\n", sizeu1(numlines-i-1));
 		}
 
 		if ((ld->sidenum[1] == 0xffff) && (ld->flags & ML_TWOSIDED))
 		{
 			ld->flags &= ~ML_TWOSIDED;  // Clear 2s flag for missing left side
 			// cph - print a warning about the bug
-			CONS_Debug(DBG_SETUP, "P_LoadLineDefs: linedef %s has two-sided flag set, but no second sidedef\n", sizeu1(numlines-i-1));
+			CONS_Debug(DBG_SETUP, "P_LoadRawLineDefs: linedef %s has two-sided flag set, but no second sidedef\n", sizeu1(numlines-i-1));
 		}
 
 		if (ld->sidenum[0] != 0xffff && ld->special)
@@ -1251,7 +1258,12 @@ static void P_LoadLineDefs(lumpnum_t lumpnum)
 		ld->polyobj = NULL;
 #endif
 	}
+}
 
+static void P_LoadLineDefs(lumpnum_t lumpnum)
+{
+	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+	P_LoadRawLineDefs(data, W_LumpLength(lumpnum));
 	Z_Free(data);
 }
 
@@ -1353,22 +1365,24 @@ static void P_LoadLineDefs2(void)
 	}
 }
 
-//
-// P_LoadSideDefs
-//
-static inline void P_LoadSideDefs(lumpnum_t lumpnum)
+
+
+static inline void P_LoadRawSideDefs(size_t i)
 {
-	numsides = W_LumpLength(lumpnum) / sizeof (mapsidedef_t);
+	numsides = i / sizeof (mapsidedef_t);
 	if (numsides <= 0)
 		I_Error("Level has no sidedefs");
 	sides = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
 }
 
-// Delay loading texture names until after loaded linedefs.
+static inline void P_LoadSideDefs(lumpnum_t lumpnum)
+{
+	P_LoadRawSideDefs(W_LumpLength(lumpnum));
+}
 
-static void P_LoadSideDefs2(lumpnum_t lumpnum)
+
+static void P_LoadRawSideDefs2(void *data)
 {
-	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
 	UINT16 i;
 	INT32 num;
 
@@ -1386,7 +1400,7 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
 
 			if (sector_num >= numsectors)
 			{
-				CONS_Debug(DBG_SETUP, "P_LoadSideDefs2: sidedef %u has out-of-range sector num %u\n", i, sector_num);
+				CONS_Debug(DBG_SETUP, "P_LoadRawSideDefs2: sidedef %u has out-of-range sector num %u\n", i, sector_num);
 				sector_num = 0;
 			}
 			sd->sector = sec = &sectors[sector_num];
@@ -1528,6 +1542,8 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
 				sd->text[6] = 0;
 				break;
 			}
+
+			case 4: // Speed pad parameters
 			case 414: // Play SFX
 			{
 				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
@@ -1541,6 +1557,9 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
 				break;
 			}
 
+			case 9: // Mace parameters
+			case 14: // Bustable block parameters
+			case 15: // Fan particle spawner parameters
 			case 425: // Calls P_SetMobjState on calling mobj
 			case 434: // Custom Power
 			case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors
@@ -1595,11 +1614,18 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
 				break;
 		}
 	}
+	R_ClearTextureNumCache(true);
+}
 
+// Delay loading texture names until after loaded linedefs.
+static void P_LoadSideDefs2(lumpnum_t lumpnum)
+{
+	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+	P_LoadRawSideDefs2(data);
 	Z_Free(data);
-	R_ClearTextureNumCache(true);
 }
 
+
 static boolean LineInBlock(fixed_t cx1, fixed_t cy1, fixed_t cx2, fixed_t cy2, fixed_t bx1, fixed_t by1)
 {
 	fixed_t bbox[4];
@@ -1855,6 +1881,30 @@ static void P_CreateBlockMap(void)
 	}
 }
 
+// Split from P_LoadBlockMap for convenience
+// -- Monster Iestyn 08/01/18
+static void P_ReadBlockMapLump(INT16 *wadblockmaplump, size_t count)
+{
+	size_t i;
+	blockmaplump = Z_Calloc(sizeof (*blockmaplump) * count, PU_LEVEL, NULL);
+
+	// killough 3/1/98: Expand wad blockmap into larger internal one,
+	// by treating all offsets except -1 as unsigned and zero-extending
+	// them. This potentially doubles the size of blockmaps allowed,
+	// because Doom originally considered the offsets as always signed.
+
+	blockmaplump[0] = SHORT(wadblockmaplump[0]);
+	blockmaplump[1] = SHORT(wadblockmaplump[1]);
+	blockmaplump[2] = (INT32)(SHORT(wadblockmaplump[2])) & 0xffff;
+	blockmaplump[3] = (INT32)(SHORT(wadblockmaplump[3])) & 0xffff;
+
+	for (i = 4; i < count; i++)
+	{
+		INT16 t = SHORT(wadblockmaplump[i]);          // killough 3/1/98
+		blockmaplump[i] = t == -1 ? (INT32)-1 : (INT32) t & 0xffff;
+	}
+}
+
 //
 // P_LoadBlockMap
 //
@@ -1881,38 +1931,20 @@ static boolean P_LoadBlockMap(lumpnum_t lumpnum)
 		return false;
 
 	{
-		size_t i;
 		INT16 *wadblockmaplump = malloc(count); //INT16 *wadblockmaplump = W_CacheLumpNum (lump, PU_LEVEL);
-
-		if (wadblockmaplump) W_ReadLump(lumpnum, wadblockmaplump);
-		else return false;
+		if (!wadblockmaplump)
+			return false;
+		W_ReadLump(lumpnum, wadblockmaplump);
 		count /= 2;
-		blockmaplump = Z_Calloc(sizeof (*blockmaplump) * count, PU_LEVEL, 0);
-
-		// killough 3/1/98: Expand wad blockmap into larger internal one,
-		// by treating all offsets except -1 as unsigned and zero-extending
-		// them. This potentially doubles the size of blockmaps allowed,
-		// because Doom originally considered the offsets as always signed.
-
-		blockmaplump[0] = SHORT(wadblockmaplump[0]);
-		blockmaplump[1] = SHORT(wadblockmaplump[1]);
-		blockmaplump[2] = (INT32)(SHORT(wadblockmaplump[2])) & 0xffff;
-		blockmaplump[3] = (INT32)(SHORT(wadblockmaplump[3])) & 0xffff;
-
-		for (i = 4; i < count; i++)
-		{
-			INT16 t = SHORT(wadblockmaplump[i]);          // killough 3/1/98
-			blockmaplump[i] = t == -1 ? (INT32)-1 : (INT32) t & 0xffff;
-		}
-
+		P_ReadBlockMapLump(wadblockmaplump, count);
 		free(wadblockmaplump);
-
-		bmaporgx = blockmaplump[0]<<FRACBITS;
-		bmaporgy = blockmaplump[1]<<FRACBITS;
-		bmapwidth = blockmaplump[2];
-		bmapheight = blockmaplump[3];
 	}
 
+	bmaporgx = blockmaplump[0]<<FRACBITS;
+	bmaporgy = blockmaplump[1]<<FRACBITS;
+	bmapwidth = blockmaplump[2];
+	bmapheight = blockmaplump[3];
+
 	// clear out mobj chains
 	count = sizeof (*blocklinks)* bmapwidth*bmapheight;
 	blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
@@ -1946,6 +1978,53 @@ static boolean P_LoadBlockMap(lumpnum_t lumpnum)
 #endif
 }
 
+// This needs to be a separate function
+// because making both the WAD and PK3 loading code use
+// the same functions is trickier than it looks for blockmap
+// -- Monster Iestyn 09/01/18
+static boolean P_LoadRawBlockMap(UINT8 *data, size_t count, const char *lumpname)
+{
+#if 0
+	(void)data;
+	(void)count;
+	(void)lumpname;
+	return false;
+#else
+	// Check if the lump is named "BLOCKMAP"
+	if (!lumpname || memcmp(lumpname, "BLOCKMAP", 8) != 0)
+	{
+		CONS_Printf("No blockmap lump found for pk3!\n");
+		return false;
+	}
+
+	if (!count || count >= 0x20000)
+		return false;
+
+	CONS_Printf("Reading blockmap lump for pk3...\n");
+
+	// no need to malloc anything, assume the data is uncompressed for now
+	count /= 2;
+	P_ReadBlockMapLump((INT16 *)data, count);
+
+	bmaporgx = blockmaplump[0]<<FRACBITS;
+	bmaporgy = blockmaplump[1]<<FRACBITS;
+	bmapwidth = blockmaplump[2];
+	bmapheight = blockmaplump[3];
+
+	// clear out mobj chains
+	count = sizeof (*blocklinks)* bmapwidth*bmapheight;
+	blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
+	blockmap = blockmaplump+4;
+
+#ifdef POLYOBJECTS
+	// haleyjd 2/22/06: setup polyobject blockmap
+	count = sizeof(*polyblocklinks) * bmapwidth * bmapheight;
+	polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL);
+#endif
+	return true;
+#endif
+}
+
 //
 // P_GroupLines
 // Builds sector line lists and subsector sector numbers.
@@ -2071,6 +2150,30 @@ static void P_LoadReject(lumpnum_t lumpnum)
 		rejectmatrix = W_CacheLumpNum(lumpnum, PU_LEVEL);
 }
 
+// PK3 version
+// -- Monster Iestyn 09/01/18
+static void P_LoadRawReject(UINT8 *data, size_t count, const char *lumpname)
+{
+	// Check if the lump is named "REJECT"
+	if (!lumpname || memcmp(lumpname, "REJECT\0\0", 8) != 0)
+	{
+		rejectmatrix = NULL;
+		CONS_Debug(DBG_SETUP, "P_LoadRawReject: No valid REJECT lump found\n");
+		return;
+	}
+
+	if (!count) // zero length, someone probably used ZDBSP
+	{
+		rejectmatrix = NULL;
+		CONS_Debug(DBG_SETUP, "P_LoadRawReject: REJECT lump has size 0, will not be loaded\n");
+	}
+	else
+	{
+		rejectmatrix = Z_Malloc(count, PU_LEVEL, NULL); // allocate memory for the reject matrix
+		M_Memcpy(rejectmatrix, data, count); // copy the data into it
+	}
+}
+
 #if 0
 static char *levellumps[] =
 {
@@ -2250,7 +2353,16 @@ void P_LoadThingsOnly(void)
 
 	P_LevelInitStuff();
 
-	P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
+	if (W_IsLumpWad(lastloadedmaplumpnum)) // welp it's a map wad in a pk3
+	{ // HACK: Open wad file rather quickly so we can use the things lump
+		UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
+		filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
+		fileinfo += ML_THINGS; // we only need the THINGS lump
+		P_PrepareRawThings(wadData + fileinfo->filepos, fileinfo->size);
+		Z_Free(wadData); // we're done with this now
+	}
+	else // phew it's just a WAD
+		P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
 	P_LoadThings();
 
 	P_SpawnSecretItems(true);
@@ -2688,7 +2800,12 @@ boolean P_SetupLevel(boolean skipprecip)
 	}
 
 	// internal game map
-	lastloadedmaplumpnum = W_GetNumForName(maplumpname = G_BuildMapName(gamemap));
+	maplumpname = G_BuildMapName(gamemap);
+	//lastloadedmaplumpnum = LUMPERROR;
+	lastloadedmaplumpnum = W_CheckNumForName(maplumpname);
+
+	if (lastloadedmaplumpnum == INT16_MAX)
+		I_Error("Map %s not found.\n", maplumpname);
 
 	R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);
 	CON_SetupBackColormap();
@@ -2698,38 +2815,93 @@ boolean P_SetupLevel(boolean skipprecip)
 
 	P_MakeMapMD5(lastloadedmaplumpnum, &mapmd5);
 
-	// note: most of this ordering is important
-	loadedbm = P_LoadBlockMap(lastloadedmaplumpnum + ML_BLOCKMAP);
-
-	P_LoadVertexes(lastloadedmaplumpnum + ML_VERTEXES);
-	P_LoadSectors(lastloadedmaplumpnum + ML_SECTORS);
-
-	P_LoadSideDefs(lastloadedmaplumpnum + ML_SIDEDEFS);
-
-	P_LoadLineDefs(lastloadedmaplumpnum + ML_LINEDEFS);
-	if (!loadedbm)
-		P_CreateBlockMap(); // Graue 02-29-2004
-	P_LoadSideDefs2(lastloadedmaplumpnum + ML_SIDEDEFS);
-
-	P_LoadLineDefs2();
-	P_LoadSubsectors(lastloadedmaplumpnum + ML_SSECTORS);
-	P_LoadNodes(lastloadedmaplumpnum + ML_NODES);
-	P_LoadSegs(lastloadedmaplumpnum + ML_SEGS);
-	P_LoadReject(lastloadedmaplumpnum + ML_REJECT);
-	P_GroupLines();
+	// HACK ALERT: Cache the WAD, get the map data into the tables, free memory.
+	// As it is implemented right now, we're assuming an uncompressed WAD.
+	// (As in, a normal PWAD, not ZWAD or anything. The lump itself can be compressed.)
+	// We're not accounting for extra lumps and scrambled lump positions. Any additional data will cause an error.
+	if (W_IsLumpWad(lastloadedmaplumpnum))
+	{
+		// Remember that we're assuming that the WAD will have a specific set of lumps in a specific order.
+		UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
+		//filelump_t *fileinfo = wadData + ((wadinfo_t *)wadData)->infotableofs;
+		filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
+		UINT32 numlumps = ((wadinfo_t *)wadData)->numlumps;
 
-	numdmstarts = numredctfstarts = numbluectfstarts = 0;
+		if (numlumps < ML_REJECT) // at least 9 lumps should be in the wad for a map to be loaded
+		{
+			I_Error("Bad WAD file for map %s!\n", maplumpname);
+		}
 
-	// reset the player starts
-	for (i = 0; i < MAXPLAYERS; i++)
-		playerstarts[i] = NULL;
+		if (numlumps > ML_BLOCKMAP) // enough room for a BLOCKMAP lump at least
+		{
+			loadedbm = P_LoadRawBlockMap(
+							wadData + (fileinfo + ML_BLOCKMAP)->filepos,
+							(fileinfo + ML_BLOCKMAP)->size,
+							(fileinfo + ML_BLOCKMAP)->name);
+		}
+		P_LoadRawVertexes(wadData + (fileinfo + ML_VERTEXES)->filepos, (fileinfo + ML_VERTEXES)->size);
+		P_LoadRawSectors(wadData + (fileinfo + ML_SECTORS)->filepos, (fileinfo + ML_SECTORS)->size);
+		P_LoadRawSideDefs((fileinfo + ML_SIDEDEFS)->size);
+		P_LoadRawLineDefs(wadData + (fileinfo + ML_LINEDEFS)->filepos, (fileinfo + ML_LINEDEFS)->size);
+		P_LoadRawSideDefs2(wadData + (fileinfo + ML_SIDEDEFS)->filepos);
+		P_LoadRawSubsectors(wadData + (fileinfo + ML_SSECTORS)->filepos, (fileinfo + ML_SSECTORS)->size);
+		P_LoadRawNodes(wadData + (fileinfo + ML_NODES)->filepos, (fileinfo + ML_NODES)->size);
+		P_LoadRawSegs(wadData + (fileinfo + ML_SEGS)->filepos, (fileinfo + ML_SEGS)->size);
+		if (numlumps > ML_REJECT) // enough room for a REJECT lump at least
+		{
+			P_LoadRawReject(
+					wadData + (fileinfo + ML_REJECT)->filepos,
+					(fileinfo + ML_REJECT)->size,
+					(fileinfo + ML_REJECT)->name);
+		}
 
-	for (i = 0; i < 2; i++)
-		skyboxmo[i] = NULL;
+		// Important: take care of the ordering of the next functions.
+		if (!loadedbm)
+			P_CreateBlockMap(); // Graue 02-29-2004
+		P_LoadLineDefs2();
+		P_GroupLines();
+		numdmstarts = numredctfstarts = numbluectfstarts = 0;
 
-	P_MapStart();
+		// reset the player starts
+		for (i = 0; i < MAXPLAYERS; i++)
+			playerstarts[i] = NULL;
+		for (i = 0; i < 2; i++)
+			skyboxmo[i] = NULL;
+		P_MapStart();
 
-	P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
+		P_PrepareRawThings(wadData + (fileinfo + ML_THINGS)->filepos, (fileinfo + ML_THINGS)->size);
+		Z_Free(wadData);
+	}
+	else
+	{
+		// Important: take care of the ordering of the next functions.
+		loadedbm = P_LoadBlockMap(lastloadedmaplumpnum + ML_BLOCKMAP);
+		P_LoadVertexes(lastloadedmaplumpnum + ML_VERTEXES);
+		P_LoadSectors(lastloadedmaplumpnum + ML_SECTORS);
+		P_LoadSideDefs(lastloadedmaplumpnum + ML_SIDEDEFS);
+		P_LoadLineDefs(lastloadedmaplumpnum + ML_LINEDEFS);
+		P_LoadSideDefs2(lastloadedmaplumpnum + ML_SIDEDEFS);
+		P_LoadSubsectors(lastloadedmaplumpnum + ML_SSECTORS);
+		P_LoadNodes(lastloadedmaplumpnum + ML_NODES);
+		P_LoadSegs(lastloadedmaplumpnum + ML_SEGS);
+		P_LoadReject(lastloadedmaplumpnum + ML_REJECT);
+
+		// Important: take care of the ordering of the next functions.
+		if (!loadedbm)
+			P_CreateBlockMap(); // Graue 02-29-2004
+
+		P_LoadLineDefs2();
+		P_GroupLines();
+		numdmstarts = numredctfstarts = numbluectfstarts = 0;
+
+		// reset the player starts
+		for (i = 0; i < MAXPLAYERS; i++)
+			playerstarts[i] = NULL;
+		for (i = 0; i < 2; i++)
+			skyboxmo[i] = NULL;
+		P_MapStart();
+		P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
+	}
 
 #ifdef ESLOPE
 	P_ResetDynamicSlopes();
@@ -2977,7 +3149,7 @@ boolean P_RunSOC(const char *socfilename)
 	lumpnum_t lump;
 
 	if (strstr(socfilename, ".soc") != NULL)
-		return P_AddWadFile(socfilename, NULL);
+		return P_AddWadFile(socfilename);
 
 	lump = W_CheckNumForName(socfilename);
 	if (lump == LUMPERROR)
@@ -2993,17 +3165,17 @@ boolean P_RunSOC(const char *socfilename)
 // Add a wadfile to the active wad files,
 // replace sounds, musics, patches, textures, sprites and maps
 //
-boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
+boolean P_AddWadFile(const char *wadfilename)
 {
 	size_t i, j, sreplaces = 0, mreplaces = 0, digmreplaces = 0;
 	UINT16 numlumps, wadnum;
-	INT16 firstmapreplaced = 0, num;
 	char *name;
 	lumpinfo_t *lumpinfo;
 	boolean texturechange = false;
+	boolean mapsadded = false;
 	boolean replacedcurrentmap = false;
 
-	if ((numlumps = W_LoadWadFile(wadfilename)) == INT16_MAX)
+	if ((numlumps = W_InitFile(wadfilename)) == INT16_MAX)
 	{
 		CONS_Printf(M_GetText("Errors occurred while loading %s; not added.\n"), wadfilename);
 		return false;
@@ -3059,6 +3231,7 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
 	if (!devparm && digmreplaces)
 		CONS_Printf(M_GetText("%s digital musics replaced\n"), sizeu1(digmreplaces));
 
+
 	//
 	// search for sprite replacements
 	//
@@ -3093,10 +3266,10 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
 	for (i = 0; i < numlumps; i++, lumpinfo++)
 	{
 		name = lumpinfo->name;
-		num = firstmapreplaced;
 
 		if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers
 		{
+			INT16 num;
 			if (name[5]!='\0')
 				continue;
 			num = (INT16)M_MapNumber(name[3], name[4]);
@@ -3106,16 +3279,10 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
 				replacedcurrentmap = true;
 
 			CONS_Printf("%s\n", name);
-		}
-
-		if (num && (num < firstmapreplaced || !firstmapreplaced))
-		{
-			firstmapreplaced = num;
-			if (firstmapname)
-				*firstmapname = name;
+			mapsadded = true;
 		}
 	}
-	if (!firstmapreplaced)
+	if (!mapsadded)
 		CONS_Printf(M_GetText("No maps added\n"));
 
 	// reload status bar (warning should have valid player!)
diff --git a/src/p_setup.h b/src/p_setup.h
index 3bca11047c7cf43a8fc1dd535de0d23d46ed6d49..da25ee26c2cb5e9e755d7b5638be0f273caa8529 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -59,7 +59,7 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum);
 #endif
 void P_LoadThingsOnly(void);
 boolean P_SetupLevel(boolean skipprecip);
-boolean P_AddWadFile(const char *wadfilename, char **firstmapname);
+boolean P_AddWadFile(const char *wadfilename);
 #ifdef DELFILE
 boolean P_DelWadFile(void);
 #endif
diff --git a/src/r_data.c b/src/r_data.c
index f2c9b1466b0a0961611c69e3cb0d0222ae4b307c..fb7eb975b2c1e1c3cab19ee8edeb0b4cc61a475e 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -365,8 +365,8 @@ void R_FlushTextureCache(void)
 }
 
 // Need these prototypes for later; defining them here instead of r_data.h so they're "private"
-int R_CountTexturesInTEXTURESLump(UINT16 wadNum);
-void R_ParseTEXTURESLump(UINT16 wadNum, INT32 *index);
+int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum);
+void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index);
 
 //
 // R_LoadTextures
@@ -404,13 +404,22 @@ void R_LoadTextures(void)
 	// but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures.
 	for (w = 0, numtextures = 0; w < numwadfiles; w++)
 	{
-		texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1;
-		texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
-		texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
+		if (wadfiles[w]->type == RET_PK3)
+		{
+			texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0);
+			texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart);
+		}
+		else
+		{
+			texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1;
+			texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
+		}
 
-		if (texturesLumpPos != INT16_MAX)
+		texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
+		while (texturesLumpPos != INT16_MAX)
 		{
-			numtextures += R_CountTexturesInTEXTURESLump((UINT16)w);
+			numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos);
+			texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1);
 		}
 
 		// Add all the textures between TX_START and TX_END
@@ -447,12 +456,25 @@ void R_LoadTextures(void)
 	for (i = 0, w = 0; w < numwadfiles; w++)
 	{
 		// Get the lump numbers for the markers in the WAD, if they exist.
-		texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1;
-		texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
-		texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
-
-		if (texturesLumpPos != INT16_MAX)
-			R_ParseTEXTURESLump(w,&i);
+		if (wadfiles[w]->type == RET_PK3)
+		{
+			texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0);
+			texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart);
+			texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
+			while (texturesLumpPos != INT16_MAX)
+			{
+				R_ParseTEXTURESLump(w, texturesLumpPos, &i);
+				texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1);
+			}
+		}
+		else
+		{
+			texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1;
+			texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
+			texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
+			if (texturesLumpPos != INT16_MAX)
+				R_ParseTEXTURESLump(w, texturesLumpPos, &i);
+		}
 
 		if (texstart == INT16_MAX || texend == INT16_MAX)
 			continue;
@@ -817,7 +839,7 @@ static texture_t *R_ParseTexture(boolean actuallyLoadTexture)
 }
 
 // Parses the TEXTURES lump... but just to count the number of textures.
-int R_CountTexturesInTEXTURESLump(UINT16 wadNum)
+int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum)
 {
 	char *texturesLump;
 	size_t texturesLumpLength;
@@ -828,11 +850,11 @@ int R_CountTexturesInTEXTURESLump(UINT16 wadNum)
 	// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
 	// need to make a space of memory where I can ensure that it will terminate
 	// correctly. Start by loading the relevant data from the WAD.
-	texturesLump = (char *)W_CacheLumpNumPwad(wadNum,W_CheckNumForNamePwad("TEXTURES", wadNum, 0),PU_STATIC);
+	texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC);
 	// If that didn't exist, we have nothing to do here.
 	if (texturesLump == NULL) return 0;
 	// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
-	texturesLumpLength = W_LumpLengthPwad(wadNum,W_CheckNumForNamePwad("TEXTURES",wadNum,0));
+	texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum);
 	texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL);
 	// Now move the contents of the lump into this new location.
 	memmove(texturesText,texturesLump,texturesLumpLength);
@@ -864,7 +886,7 @@ int R_CountTexturesInTEXTURESLump(UINT16 wadNum)
 }
 
 // Parses the TEXTURES lump... for real, this time.
-void R_ParseTEXTURESLump(UINT16 wadNum, INT32 *texindex)
+void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex)
 {
 	char *texturesLump;
 	size_t texturesLumpLength;
@@ -877,11 +899,11 @@ void R_ParseTEXTURESLump(UINT16 wadNum, INT32 *texindex)
 	// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
 	// need to make a space of memory where I can ensure that it will terminate
 	// correctly. Start by loading the relevant data from the WAD.
-	texturesLump = (char *)W_CacheLumpNumPwad(wadNum,W_CheckNumForNamePwad("TEXTURES", wadNum, 0),PU_STATIC);
+	texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC);
 	// If that didn't exist, we have nothing to do here.
 	if (texturesLump == NULL) return;
 	// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
-	texturesLumpLength = W_LumpLengthPwad(wadNum,W_CheckNumForNamePwad("TEXTURES",wadNum,0));
+	texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum);
 	texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL);
 	// Now move the contents of the lump into this new location.
 	memmove(texturesText,texturesLump,texturesLumpLength);
@@ -968,12 +990,51 @@ static void R_InitExtraColormaps(void)
 	CONS_Printf(M_GetText("Number of Extra Colormaps: %s\n"), sizeu1(numcolormaplumps));
 }
 
-// 12/14/14 -- only take flats in F_START/F_END
+// Search for flat name.
 lumpnum_t R_GetFlatNumForName(const char *name)
 {
-	lumpnum_t lump = W_CheckNumForNameInBlock(name, "F_START", "F_END");
-	if (lump == LUMPERROR)
-		lump = W_CheckNumForNameInBlock(name, "FF_START", "FF_END"); // deutex, some other old things
+	INT32 i;
+	lumpnum_t lump;
+	lumpnum_t start;
+	lumpnum_t end;
+
+	// Scan wad files backwards so patched flats take preference.
+	for (i = numwadfiles - 1; i >= 0; i--)
+	{
+		switch (wadfiles[i]->type)
+		{
+		case RET_WAD:
+			if ((start = W_CheckNumForNamePwad("F_START", (UINT16)i, 0)) == INT16_MAX)
+			{
+				if ((start = W_CheckNumForNamePwad("FF_START", (UINT16)i, 0)) == INT16_MAX)
+					continue;
+				else if ((end = W_CheckNumForNamePwad("FF_END", (UINT16)i, start)) == INT16_MAX)
+					continue;
+			}
+			else
+				if ((end = W_CheckNumForNamePwad("F_END", (UINT16)i, start)) == INT16_MAX)
+					continue;
+			break;
+		case RET_PK3:
+			if ((start = W_CheckNumForFolderStartPK3("Flats/", i, 0)) == INT16_MAX)
+				continue;
+			if ((end = W_CheckNumForFolderEndPK3("Flats/", i, start)) == INT16_MAX)
+				continue;
+			break;
+		default:
+			continue;
+		}
+
+		// Now find lump with specified name in that range.
+		lump = W_CheckNumForNamePwad(name, (UINT16)i, start);
+		if (lump < end)
+		{
+			lump += (i<<16); // found it, in our constraints
+			break;
+		}
+		lump = LUMPERROR;
+	}
+
 	if (lump == LUMPERROR)
 	{
 		if (strcmp(name, SKYFLATNAME))
diff --git a/src/r_things.c b/src/r_things.c
index ee2d8a9bfb7ec97a2d20ff0bc7426753e47bc98b..305d6b8277ee72a21cf44d47d4102313ad269b33 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -327,21 +327,28 @@ void R_AddSpriteDefs(UINT16 wadnum)
 	UINT16 start, end;
 	char wadname[MAX_WADPATH];
 
-	// find the sprites section in this pwad
-	// we need at least the S_END
-	// (not really, but for speedup)
-
-	start = W_CheckNumForNamePwad("S_START", wadnum, 0);
-	if (start == INT16_MAX)
-		start = W_CheckNumForNamePwad("SS_START", wadnum, 0); //deutex compatib.
-	if (start == INT16_MAX)
-		start = 0; //let say S_START is lump 0
-	else
-		start++;   // just after S_START
+	switch (wadfiles[wadnum]->type)
+	{
+	case RET_WAD:
+		start = W_CheckNumForNamePwad("S_START", wadnum, 0);
+		if (start == INT16_MAX)
+			start = W_CheckNumForNamePwad("SS_START", wadnum, 0); //deutex compatib.
+		if (start == INT16_MAX)
+			start = 0; //let say S_START is lump 0
+		else
+			start++;   // just after S_START
+		end = W_CheckNumForNamePwad("S_END",wadnum,start);
+		if (end == INT16_MAX)
+			end = W_CheckNumForNamePwad("SS_END",wadnum,start);     //deutex compatib.
+		break;
+	case RET_PK3:
+		start = W_CheckNumForFolderStartPK3("Sprites/", wadnum, 0);
+		end = W_CheckNumForFolderEndPK3("Sprites/", wadnum, start);
+		break;
+	default:
+		return;
+	}
 
-	end = W_CheckNumForNamePwad("S_END",wadnum,start);
-	if (end == INT16_MAX)
-		end = W_CheckNumForNamePwad("SS_END",wadnum,start);     //deutex compatib.
 	if (end == INT16_MAX)
 	{
 		CONS_Debug(DBG_SETUP, "no sprites in pwad %d\n", wadnum);
diff --git a/src/w_wad.c b/src/w_wad.c
index 3789eab55ddf72c794cb27784494cc12040a9e6b..977b7e6f1f51179763536994a934f0fa5a690c4c 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -26,6 +26,8 @@
 #include "lzf.h"
 #endif
 
+#include "zlib.h"
+
 #include "doomdef.h"
 #include "doomstat.h"
 #include "doomtype.h"
@@ -67,14 +69,6 @@ int	snprintf(char *str, size_t n, const char *fmt, ...);
 #pragma pack(1)
 #endif
 
-// a raw entry of the wad directory
-typedef struct
-{
-	UINT32 filepos; // file offset of the resource
-	UINT32 size; // size of the resource
-	char name[8]; // name of the resource
-} ATTRPACK filelump_t;
-
 #if defined(_MSC_VER)
 #pragma pack()
 #endif
@@ -116,6 +110,8 @@ void W_Shutdown(void)
 		Z_Free(wadfiles[numwadfiles]->lumpinfo);
 		Z_Free(wadfiles[numwadfiles]->filename);
 		Z_Free(wadfiles[numwadfiles]);
+		while (wadfiles[numwadfiles]->numlumps--)
+			Z_Free(wadfiles[numwadfiles]->lumpinfo[wadfiles[numwadfiles]->numlumps].name2);
 	}
 }
 
@@ -174,6 +170,28 @@ FILE *W_OpenWadFile(const char **filename, boolean useerrors)
 	return handle;
 }
 
+// Look for all DEHACKED and Lua scripts inside a PK3 archive.
+static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum)
+{
+	UINT16 posStart, posEnd;
+	posStart = W_CheckNumForFolderStartPK3("Lua/", wadnum, 0);
+	if (posStart != INT16_MAX)
+	{
+		posStart++;
+		posEnd = W_CheckNumForFolderEndPK3("Lua/", wadnum, posStart);
+		for (; posStart < posEnd; posStart++)
+			LUA_LoadLump(wadnum, posStart);
+	}
+	posStart = W_CheckNumForFolderStartPK3("SOC/", wadnum, 0);
+	if (posStart != INT16_MAX)
+	{
+		posStart++;
+		posEnd = W_CheckNumForFolderEndPK3("SOC/", wadnum, posStart);
+		for(; posStart < posEnd; posStart++)
+			DEH_LoadDehackedLumpPwad(wadnum, posStart);
+	}
+}
+
 // search for all DEHACKED lump in all wads and load it
 static inline void W_LoadDehackedLumps(UINT16 wadnum)
 {
@@ -291,12 +309,13 @@ static void W_InvalidateLumpnumCache(void)
 //
 // Can now load dehacked files (.soc)
 //
-UINT16 W_LoadWadFile(const char *filename)
+UINT16 W_InitFile(const char *filename)
 {
 	FILE *handle;
 	lumpinfo_t *lumpinfo;
 	wadfile_t *wadfile;
-	UINT32 numlumps;
+	enum restype type;
+	UINT16 numlumps;
 	size_t i;
 	INT32 compressed = 0;
 	size_t packetsize = 0;
@@ -339,12 +358,34 @@ UINT16 W_LoadWadFile(const char *filename)
 		return INT16_MAX;
 	}
 
+#ifndef NOMD5
+	//
+	// w-waiiiit!
+	// Let's not add a wad file if the MD5 matches
+	// an MD5 of an already added WAD file!
+	//
+	W_MakeFileMD5(filename, md5sum);
+
+	for (i = 0; i < numwadfiles; i++)
+	{
+		if (!memcmp(wadfiles[i]->md5sum, md5sum, 16))
+		{
+			CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), filename);
+			if (handle)
+				fclose(handle);
+			return INT16_MAX;
+		}
+	}
+#endif
+
 	// detect dehacked file with the "soc" extension
 	if (!stricmp(&filename[strlen(filename) - 4], ".soc"))
 	{
 		// This code emulates a wadfile with one lump name "OBJCTCFG"
 		// at position 0 and size of the whole file.
 		// This allows soc files to be like all wads, copied by network and loaded at the console.
+		type = RET_SOC;
+
 		numlumps = 1;
 		lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL);
 		lumpinfo->position = 0;
@@ -352,6 +393,10 @@ UINT16 W_LoadWadFile(const char *filename)
 		lumpinfo->size = ftell(handle);
 		fseek(handle, 0, SEEK_SET);
 		strcpy(lumpinfo->name, "OBJCTCFG");
+		// Allocate the lump's full name.
+		lumpinfo->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+		strcpy(lumpinfo->name2, "OBJCTCFG");
+		lumpinfo->name2[8] = '\0';
 	}
 #ifdef HAVE_BLUA
 	// detect lua script with the "lua" extension
@@ -360,6 +405,8 @@ UINT16 W_LoadWadFile(const char *filename)
 		// This code emulates a wadfile with one lump name "LUA_INIT"
 		// at position 0 and size of the whole file.
 		// This allows soc files to be like all wads, copied by network and loaded at the console.
+		type = RET_LUA;
+
 		numlumps = 1;
 		lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL);
 		lumpinfo->position = 0;
@@ -367,16 +414,206 @@ UINT16 W_LoadWadFile(const char *filename)
 		lumpinfo->size = ftell(handle);
 		fseek(handle, 0, SEEK_SET);
 		strcpy(lumpinfo->name, "LUA_INIT");
+		// Allocate the lump's full name.
+		lumpinfo->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+		strcpy(lumpinfo->name2, "OBJCTCFG");
+		lumpinfo->name2[8] = '\0';
 	}
 #endif
+else if (!stricmp(&filename[strlen(filename) - 4], ".pk3"))
+	{
+		char curHeader[4];
+		unsigned long size;
+		char seekPat[] = {0x50, 0x4b, 0x01, 0x02, 0x00};
+		char endPat[] = {0x50, 0x4b, 0x05, 0x06, 0x00};
+		char *s;
+		int c;
+		UINT32 position;
+		boolean matched = false;
+		lumpinfo_t *lump_p;
+
+		type = RET_PK3;
+
+		// Obtain the file's size.
+		fseek(handle, 0, SEEK_END);
+		size = ftell(handle);
+		CONS_Debug(DBG_SETUP, "PK3 size is: %ld\n", size);
+
+		// We must look for the central directory through the file. (Thanks to JTE for this algorithm.)
+		// All of the central directory entry headers have a signature of 0x50 0x4b 0x01 0x02.
+		// The first entry found means the beginning of the central directory.
+		fseek(handle, -min(size, (22 + 65536)), SEEK_CUR);
+		s = endPat;
+		while((c = fgetc(handle)) != EOF)
+		{
+			if (*s != c && s > endPat) // No match?
+				s = endPat; // We "reset" the counter by sending the s pointer back to the start of the array.
+			if (*s == c)
+			{
+				s++;
+				if (*s == 0x00) // The array pointer has reached the key char which marks the end. It means we have matched the signature.
+				{
+					matched = true;
+					CONS_Debug(DBG_SETUP, "Found PK3 central directory at position %ld.\n", ftell(handle));
+					break;
+				}
+			}
+		}
+
+		// Error if we couldn't find the central directory at all. It likely means this is not a ZIP/PK3 file.
+		if (matched == false)
+		{
+			CONS_Alert(CONS_ERROR, "No central directory inside PK3! File may be corrupted or incomplete.\n");
+			return INT16_MAX;
+		}
+
+		fseek(handle, 4, SEEK_CUR);
+		fread(&numlumps, 1, 2, handle);
+		fseek(handle, 6, SEEK_CUR);
+		fread(&position, 1, 4, handle);
+		lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL);
+		fseek(handle, position, SEEK_SET);
+
+		// Since we found the central directory, now we can map our lumpinfo table.
+		// We will look for file headers inside it, until we reach the central directory end signature.
+		// We exactly know what data to expect this time, so now we don't need to do a byte-by-byte search.
+		CONS_Debug(DBG_SETUP, "Now finding central directory file headers...\n");
+		for (i = 0; i < numlumps; i++, lump_p++)
+		{
+			fread(curHeader, 1, 4, handle);
+
+			// We found a central directory entry signature?
+			if (!strncmp(curHeader, seekPat, 3))
+			{
+				// Let's fill in the fields that we actually need.
+				// (Declaring all those vars might not be the optimal way to do this, sorry.)
+				char *eName;
+				int namePos;
+				int nameEnd;
+				unsigned short int eNameLen = 8;
+				unsigned short int eXFieldLen = 0;
+				unsigned short int lNameLen = 0;
+				unsigned short int lXFieldLen = 0;
+				unsigned short int eCommentLen = 0;
+				unsigned short int eCompression = 0;
+				unsigned int eSize = 0;
+				unsigned int eCompSize = 0;
+				unsigned int eLocalHeaderOffset = 0;
+				unsigned long int rememberPos = 0;
+
+				// We get the compression type indicator value.
+				fseek(handle, 6, SEEK_CUR);
+				fread(&eCompression, 1, 2, handle);
+				// Get the size
+				fseek(handle, 8, SEEK_CUR);
+				fread(&eCompSize, 1, 4, handle);
+				fread(&eSize, 1, 4, handle);
+				// We get the variable length fields.
+				fread(&eNameLen, 1, 2, handle);
+				fread(&eXFieldLen, 1, 2, handle);
+				fread(&eCommentLen, 1, 2, handle);
+				fseek(handle, 8, SEEK_CUR);
+				fread(&eLocalHeaderOffset, 1, 4, handle); // Get the offset.
+
+				eName = malloc(sizeof(char)*(eNameLen + 1));
+				fgets(eName, eNameLen + 1, handle);
+
+				// Don't load lump if folder.
+//				if (*(eName + eNameLen - 1) == '/')
+//					continue;
+
+				// We must calculate the position for the actual data.
+				// Why not eLocalHeaderOffset + 30 + eNameLen + eXFieldLen? That's because the extra field and name lengths MAY be different in the local headers.
+				rememberPos = ftell(handle);
+				fseek(handle, eLocalHeaderOffset + 26, SEEK_SET);
+				fread(&lNameLen, 1, 2, handle);
+				fread(&lXFieldLen, 1, 2, handle);
+				lump_p->position = ftell(handle) + lNameLen + lXFieldLen;
+
+				fseek(handle, rememberPos, SEEK_SET); // Let's go back to the central dir.
+				lump_p->disksize = eCompSize;
+				lump_p->size = eSize;
+
+				// We will trim the file's full name so that only the filename is left.
+				namePos = eNameLen - 1;
+				while(namePos--)
+					if(eName[namePos] == '/')
+						break;
+				namePos++;
+				// We will remove the file extension too.
+				nameEnd = 0;
+				while(nameEnd++ < 8)
+					if(eName[namePos + nameEnd] == '.')
+						break;
+				memset(lump_p->name, '\0', 9);
+				strncpy(lump_p->name, eName + namePos, nameEnd);
+
+				lump_p->name2 = Z_Malloc((eNameLen+1)*sizeof(char), PU_STATIC, NULL);
+				strncpy(lump_p->name2, eName, eNameLen);
+				lump_p->name2[eNameLen] = '\0';
+
+				// We set the compression type from what we're supporting so far.
+				switch(eCompression)
+				{
+				case 0:
+					lump_p->compression = CM_NOCOMPRESSION;
+					break;
+				case 8:
+					lump_p->compression = CM_DEFLATE;
+					break;
+				case 14:
+					lump_p->compression = CM_LZF;
+					break;
+				default:
+					CONS_Alert(CONS_WARNING, "Lump has an unsupported compression type!\n");
+					lump_p->compression = CM_UNSUPPORTED;
+					break;
+				}
+				CONS_Debug(DBG_SETUP, "File %s, data begins at: %ld\n", eName, lump_p->position);
+				fseek(handle, eXFieldLen + eCommentLen, SEEK_CUR); // We skip to where we expect the next central directory entry or end marker to be.
+				free(eName);
+			}
+			// We found the central directory end signature?
+			else if (!strncmp(curHeader, endPat, 4))
+			{
+				CONS_Debug(DBG_SETUP, "Central directory end signature found at: %ld\n", ftell(handle));
+
+				/*// We will create a "virtual" marker lump at the very end of lumpinfo for convenience.
+				// This marker will be used by the different lump-seeking (eg. textures, sprites, etc.) in PK3-specific cases in an auxiliary way.
+				lumpinfo = (lumpinfo_t*) Z_Realloc(lumpinfo, (numlumps + 1)*sizeof(*lumpinfo), PU_STATIC, NULL);
+				strcpy(lumpinfo[numlumps].name, "PK3_ENDM\0");
+				lumpinfo[numlumps].name2 = Z_Malloc(14 * sizeof(char), PU_STATIC, NULL);
+				strcpy(lumpinfo[numlumps].name2, "PK3_ENDMARKER\0");
+				lumpinfo[numlumps].position = 0;
+				lumpinfo[numlumps].size = 0;
+				lumpinfo[numlumps].disksize = 0;
+				lumpinfo[numlumps].compression = CM_NOCOMPRESSION;
+				numlumps++;*/
+				break;
+			}
+			// ... None of them? We're only expecting either a central directory signature entry or the central directory end signature.
+			// The file may be broken or incomplete...
+			else
+			{
+				CONS_Alert(CONS_WARNING, "Expected central directory header signature, got something else!");
+				return INT16_MAX;
+			}
+		}
+		// If we've reached this far, then it means our dynamically stored lumpinfo has to be ready.
+		// Now we finally build our... incorrectly called wadfile.
+		// TODO: Maybe we should give them more generalized names, like resourcefile or resfile or something.
+		// Mostly for clarity and better understanding when reading the code.
+	}
+	// assume wad file
 	else
 	{
-		// assume wad file
 		wadinfo_t header;
 		lumpinfo_t *lump_p;
 		filelump_t *fileinfo;
 		void *fileinfov;
 
+		type = RET_WAD;
+
 		// read the header
 		if (fread(&header, 1, sizeof header, handle) < sizeof header)
 		{
@@ -437,44 +674,29 @@ UINT16 W_LoadWadFile(const char *filename)
 				if (realsize != 0)
 				{
 					lump_p->size = realsize;
-					lump_p->compressed = 1;
+					lump_p->compression = CM_LZF;
 				}
 				else
 				{
 					lump_p->size -= 4;
-					lump_p->compressed = 0;
+					lump_p->compression = CM_NOCOMPRESSION;
 				}
 
 				lump_p->position += 4;
 				lump_p->disksize -= 4;
 			}
-			else lump_p->compressed = 0;
+			else
+				lump_p->compression = CM_NOCOMPRESSION;
 			memset(lump_p->name, 0x00, 9);
 			strncpy(lump_p->name, fileinfo->name, 8);
+			// Allocate the lump's full name.
+			lump_p->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+			strncpy(lump_p->name2, fileinfo->name, 8);
+			lump_p->name2[8] = '\0';
 		}
 		free(fileinfov);
 	}
 
-#ifndef NOMD5
-	//
-	// w-waiiiit!
-	// Let's not add a wad file if the MD5 matches
-	// an MD5 of an already added WAD file!
-	//
-	W_MakeFileMD5(filename, md5sum);
-
-	for (i = 0; i < numwadfiles; i++)
-	{
-		if (!memcmp(wadfiles[i]->md5sum, md5sum, 16))
-		{
-			CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), filename);
-			if (handle)
-				fclose(handle);
-			return INT16_MAX;
-		}
-	}
-#endif
-
 	//
 	// link wad file to search files
 	//
@@ -485,6 +707,7 @@ UINT16 W_LoadWadFile(const char *filename)
 	wadfile->lumpinfo = lumpinfo;
 	fseek(handle, 0, SEEK_END);
 	wadfile->filesize = (unsigned)ftell(handle);
+	wadfile->type = type;
 
 	// already generated, just copy it over
 	M_Memcpy(&wadfile->md5sum, &md5sum, 16);
@@ -505,10 +728,30 @@ UINT16 W_LoadWadFile(const char *filename)
 	CONS_Printf(M_GetText("Added file %s (%u lumps)\n"), filename, numlumps);
 	wadfiles[numwadfiles] = wadfile;
 	numwadfiles++; // must come BEFORE W_LoadDehackedLumps, so any addfile called by COM_BufInsertText called by Lua doesn't overwrite what we just loaded
-	W_LoadDehackedLumps(numwadfiles-1);
+
+	// TODO: HACK ALERT - Load Lua & SOC stuff right here. I feel like this should be out of this place, but... Let's stick with this for now.
+	switch (wadfile->type)
+	{
+	case RET_WAD:
+		W_LoadDehackedLumps(numwadfiles - 1);
+		break;
+	case RET_PK3:
+		W_LoadDehackedLumpsPK3(numwadfiles - 1);
+		break;
+	case RET_SOC:
+		CONS_Printf(M_GetText("Loading SOC from %s\n"), wadfile->filename);
+		DEH_LoadDehackedLumpPwad(numwadfiles - 1, 0);
+		break;
+	case RET_LUA:
+		LUA_LoadLump(numwadfiles - 1, 0);
+		break;
+	default:
+		break;
+	}
 
 	W_InvalidateLumpnumCache();
 
+	CONS_Printf("Ended function.\n");
 	return wadfile->numlumps;
 }
 
@@ -566,7 +809,7 @@ INT32 W_InitMultipleFiles(char **filenames)
 	for (; *filenames; filenames++)
 	{
 		//CONS_Debug(DBG_SETUP, "Loading %s\n", *filenames);
-		rc &= (W_LoadWadFile(*filenames) != INT16_MAX) ? 1 : 0;
+		rc &= (W_InitFile(*filenames) != INT16_MAX) ? 1 : 0;
 	}
 
 	if (!numwadfiles)
@@ -644,6 +887,51 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump)
 	return INT16_MAX;
 }
 
+// Look for the first lump from a folder.
+UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump)
+{
+	INT32 i;
+	lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
+	for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
+	{
+		if (strnicmp(name, lump_p->name2, strlen(name)) == 0)
+			break;
+	}
+	return i;
+}
+
+// In a PK3 type of resource file, it looks for the next lumpinfo entry that doesn't share the specified pathfile.
+// Useful for finding folder ends.
+// Returns the position of the lumpinfo entry.
+UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump)
+{
+	INT32 i;
+	lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
+	for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
+	{
+		if (strnicmp(name, lump_p->name2, strlen(name)))
+			break;
+	}
+	return i;
+}
+
+// In a PK3 type of resource file, it looks for an entry with the specified full name.
+// Returns lump position in PK3's lumpinfo, or INT16_MAX if not found.
+UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump)
+{
+	INT32 i;
+	lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
+	for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
+	{
+		if (!strnicmp(name, lump_p->name2, strlen(name)))
+		{
+			return i;
+		}
+	}
+	// Not found at all?
+	return INT16_MAX;
+}
+
 //
 // W_CheckNumForName
 // Returns LUMPERROR if name not found.
@@ -684,6 +972,37 @@ lumpnum_t W_CheckNumForName(const char *name)
 	}
 }
 
+// Look for valid map data through all added files in descendant order.
+// Get a map marker for WADs, and a standalone WAD file lump inside PK3s.
+// TODO: Make it search through cache first, maybe...?
+lumpnum_t W_CheckNumForMap(const char *name)
+{
+	UINT16 lumpNum, end;
+	UINT32 i;
+	for (i = numwadfiles - 1; i < numwadfiles; i--)
+	{
+		if (wadfiles[i]->type == RET_WAD)
+		{
+			for (lumpNum = 0; lumpNum < wadfiles[i]->numlumps; lumpNum++)
+				if (!strncmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8))
+					return (i<<16) + lumpNum;
+		}
+		else if (wadfiles[i]->type == RET_PK3)
+		{
+			lumpNum = W_CheckNumForFolderStartPK3("maps/", i, 0);
+			if (lumpNum != INT16_MAX)
+				end = W_CheckNumForFolderEndPK3("maps/", i, lumpNum);
+			else
+				continue;
+			// Now look for the specified map.
+			for (++lumpNum; lumpNum < end; lumpNum++)
+				if (!strnicmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8))
+					return (i<<16) + lumpNum;
+		}
+	}
+	return LUMPERROR;
+}
+
 //
 // W_GetNumForName
 //
@@ -714,15 +1033,19 @@ lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, con
 	// scan wad files backwards so patch lump files take precedence
 	for (i = numwadfiles - 1; i >= 0; i--)
 	{
-		bsid = W_CheckNumForNamePwad(blockstart,(UINT16)i,0);
-		if (bsid == INT16_MAX)
-			continue; // block doesn't exist, keep going
-		beid = W_CheckNumForNamePwad(blockend,(UINT16)i,0);
-		// if block end doesn't exist, just search through everything
-
-		check = W_CheckNumForNamePwad(name,(UINT16)i,bsid);
-		if (check < beid)
-			return (i<<16)+check; // found it, in our constraints
+		if (wadfiles[i]->type == RET_WAD)
+		{
+			bsid = W_CheckNumForNamePwad(blockstart, (UINT16)i, 0);
+			if (bsid == INT16_MAX)
+				continue; // Start block doesn't exist?
+			beid = W_CheckNumForNamePwad(blockend, (UINT16)i, 0);
+			if (beid == INT16_MAX)
+				continue; // End block doesn't exist?
+
+			check = W_CheckNumForNamePwad(name, (UINT16)i, bsid);
+			if (check < beid)
+				return (i<<16)+check; // found it, in our constraints
+		}
 	}
 	return LUMPERROR;
 }
@@ -759,6 +1082,24 @@ size_t W_LumpLength(lumpnum_t lumpnum)
 	return W_LumpLengthPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum));
 }
 
+//
+// W_IsLumpWad
+// Is the lump a WAD? (presumably in a PK3)
+//
+boolean W_IsLumpWad(lumpnum_t lumpnum)
+{
+	if (wadfiles[WADFILENUM(lumpnum)]->type == RET_PK3)
+	{
+		const char *lumpfullName = (wadfiles[WADFILENUM(lumpnum)]->lumpinfo + LUMPNUM(lumpnum))->name2;
+
+		if (strlen(lumpfullName) < 4)
+			return false; // can't possibly be a WAD can it?
+		return !strnicmp(lumpfullName + strlen(lumpfullName) - 4, ".wad", 4);
+	}
+
+	return false; // WADs should never be inside non-PK3s as far as SRB2 is concerned
+}
+
 /** Reads bytes from the head of a lump, without doing decompression.
   *
   * \param wad Wad number to read from.
@@ -785,54 +1126,35 @@ static size_t W_RawReadLumpHeader(UINT16 wad, UINT16 lump, void *dest, size_t si
 	return bytesread;
 }
 
-// Read a compressed lump; return it in newly Z_Malloc'd memory.
-// wad is number of wad file, lump is number of lump in wad.
-static void *W_ReadCompressedLump(UINT16 wad, UINT16 lump)
+/* report a zlib or i/o error */
+void zerr(int ret)
 {
-#ifdef ZWAD
-	char *compressed, *data;
-	const lumpinfo_t *l = &wadfiles[wad]->lumpinfo[lump];
-	size_t retval;
-
-	compressed = Z_Malloc(l->disksize, PU_STATIC, NULL);
-	data = Z_Malloc(l->size, PU_STATIC, NULL);
-	if (W_RawReadLumpHeader(wad, lump, compressed, l->disksize, 0)
-		< l->disksize)
-	{
-		I_Error("wad %d, lump %d: cannot read compressed data",
-			wad, lump);
-	}
-
-	retval = lzf_decompress(compressed, l->disksize, data, l->size);
-#ifndef AVOID_ERRNO
-	if (retval == 0 && errno == E2BIG)
-	{
-		I_Error("wad %d, lump %d: compressed data too big "
-			"(bigger than %s)", wad, lump, sizeu1(l->size));
-	}
-	else if (retval == 0 && errno == EINVAL)
-		I_Error("wad %d, lump %d: invalid compressed data", wad, lump);
-	else
-#endif
-	if (retval != l->size)
-	{
-		I_Error("wad %d, lump %d: decompressed to wrong number of "
-			"bytes (expected %s, got %s)", wad, lump,
-			sizeu1(l->size), sizeu2(retval));
-	}
-	Z_Free(compressed);
-	return data;
-#else
-	(void)wad;
-	(void)lump;
-	//I_Error("ZWAD files not supported on this platform.");
-	return NULL;
-#endif
+	    CONS_Printf("zpipe: ");
+    switch (ret) {
+    case Z_ERRNO:
+        if (ferror(stdin))
+            CONS_Printf("error reading stdin\n");
+        if (ferror(stdout))
+            CONS_Printf("error writing stdout\n");
+        break;
+    case Z_STREAM_ERROR:
+        CONS_Printf("invalid compression level\n");
+        break;
+    case Z_DATA_ERROR:
+        CONS_Printf("invalid or incomplete deflate data\n");
+        break;
+    case Z_MEM_ERROR:
+        CONS_Printf("out of memory\n");
+        break;
+    case Z_VERSION_ERROR:
+        CONS_Printf("zlib version mismatch!\n");
+    }
 }
 
 /** Reads bytes from the head of a lump.
   * Note: If the lump is compressed, the whole thing has to be read anyway.
   *
+  * \param wad Wad number to read from.
   * \param lump Lump number to read from.
   * \param dest Buffer in memory to serve as destination.
   * \param size Number of bytes to read.
@@ -843,6 +1165,8 @@ static void *W_ReadCompressedLump(UINT16 wad, UINT16 lump)
 size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, size_t offset)
 {
 	size_t lumpsize;
+	lumpinfo_t *l;
+	FILE *handle;
 
 	if (!TestValidLump(wad,lump))
 		return 0;
@@ -856,17 +1180,112 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
 	if (!size || size+offset > lumpsize)
 		size = lumpsize - offset;
 
-	if (wadfiles[wad]->lumpinfo[lump].compressed)
+	// Let's get the raw lump data.
+	// We setup the desired file handle to read the lump data.
+	l = wadfiles[wad]->lumpinfo + lump;
+	handle = wadfiles[wad]->handle;
+	fseek(handle, (long)(l->position + offset), SEEK_SET);
+
+	// But let's not copy it yet. We support different compression formats on lumps, so we need to take that into account.
+	switch(wadfiles[wad]->lumpinfo[lump].compression)
 	{
-		UINT8 *data;
-		data = W_ReadCompressedLump(wad, lump);
-		if (!data) return 0;
-		M_Memcpy(dest, data+offset, size);
-		Z_Free(data);
-		return size;
+	case CM_NOCOMPRESSION:		// If it's uncompressed, we directly write the data into our destination, and return the bytes read.
+		return fread(dest, 1, size, handle);
+	case CM_LZF:		// Is it LZF compressed? Used by ZWADs.
+		{
+#ifdef ZWAD
+			char *rawData; // The lump's raw data.
+			char *decData; // Lump's decompressed real data.
+			size_t retval; // Helper var, lzf_decompress returns 0 when an error occurs.
+
+			rawData = Z_Malloc(l->disksize, PU_STATIC, NULL);
+			decData = Z_Malloc(l->size, PU_STATIC, NULL);
+
+			if (fread(rawData, 1, l->disksize, handle) < l->disksize)
+				I_Error("wad %d, lump %d: cannot read compressed data", wad, lump);
+			retval = lzf_decompress(rawData, l->disksize, decData, l->size);
+#ifndef AVOID_ERRNO
+			if (retval == 0 && errno == E2BIG) // errno is a global var set by the lzf functions when something goes wrong.
+			{
+				I_Error("wad %d, lump %d: compressed data too big (bigger than %s)", wad, lump, sizeu1(l->size));
+			}
+			else if (retval == 0 && errno == EINVAL)
+				I_Error("wad %d, lump %d: invalid compressed data", wad, lump);
+			else
+#endif
+			if (retval != l->size)
+			{
+				I_Error("wad %d, lump %d: decompressed to wrong number of bytes (expected %s, got %s)", wad, lump, sizeu1(l->size), sizeu2(retval));
+			}
+#else
+			(void)wad;
+			(void)lump;
+			//I_Error("ZWAD files not supported on this platform.");
+			return NULL;
+#endif
+			if (!decData) // Did we get no data at all?
+				return 0;
+			M_Memcpy(dest, decData + offset, size);
+			Z_Free(rawData);
+			Z_Free(decData);
+			return size;
+		}
+	case CM_DEFLATE: // Is it compressed via DEFLATE? Very common in ZIPs/PK3s, also what most doom-related editors support.
+		{
+			z_const Bytef *rawData; // The lump's raw data.
+			Bytef *decData; // Lump's decompressed real data.
+
+			int zErr; // Helper var.
+			z_stream strm;
+			unsigned long rawSize = l->disksize;
+			unsigned long decSize = l->size;
+
+			rawData = Z_Malloc(rawSize, PU_STATIC, NULL);
+			decData = Z_Malloc(decSize, PU_STATIC, NULL);
+
+			if (fread(rawData, 1, rawSize, handle) < rawSize)
+				I_Error("wad %d, lump %d: cannot read compressed data", wad, lump);
+
+			strm.zalloc = Z_NULL;
+			strm.zfree = Z_NULL;
+			strm.opaque = Z_NULL;
+
+			strm.total_in = strm.avail_in = rawSize;
+			strm.total_out = strm.avail_out = decSize;
+
+			strm.next_in = rawData;
+			strm.next_out = decData;
+
+			zErr = inflateInit2(&strm, -15);
+			if (zErr == Z_OK)
+			{
+				zErr = inflate(&strm, Z_FINISH);
+				if (zErr == Z_STREAM_END)
+				{
+					M_Memcpy(dest, decData, size);
+				}
+				else
+				{
+					size = 0;
+					zerr(zErr);
+					(void)inflateEnd(&strm);
+				}
+			}
+			else
+			{
+				CONS_Printf("whopet\n");
+				size = 0;
+				zerr(zErr);
+			}
+
+			Z_Free(rawData);
+			Z_Free(decData);
+
+			return size;
+		}
+	default:
+		I_Error("wad %d, lump %d: unsupported compression type!", wad, lump);
 	}
-	else
-		return W_RawReadLumpHeader(wad, lump, dest, size, offset);
 }
 
 size_t W_ReadLumpHeader(lumpnum_t lumpnum, void *dest, size_t size, size_t offset)
@@ -1150,12 +1569,12 @@ static int W_VerifyFile(const char *filename, lumpchecklist_t *checklist,
 	if ((handle = W_OpenWadFile(&filename, false)) == NULL)
 		return -1;
 
-	// detect dehacked file with the "soc" extension
-	if (stricmp(&filename[strlen(filename) - 4], ".soc") != 0
+	// detect wad file by the absence of the other supported extensions
+	if (stricmp(&filename[strlen(filename) - 4], ".soc")
 #ifdef HAVE_BLUA
-	&& stricmp(&filename[strlen(filename) - 4], ".lua") != 0
+	&& stricmp(&filename[strlen(filename) - 4], ".lua")
 #endif
-	)
+	&& stricmp(&filename[strlen(filename) - 4], ".pk3"))
 	{
 		// assume wad file
 		wadinfo_t header;
diff --git a/src/w_wad.h b/src/w_wad.h
index 0e62cc9b7d312e468934bd9dcbc3224868674410..b84a563457dacbacd0398d4ee784fafe4b36e50f 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -22,6 +22,15 @@
 #pragma interface
 #endif
 
+// a raw entry of the wad directory
+// NOTE: This sits here and not in w_wad.c because p_setup.c makes use of it to load map WADs inside PK3s.
+typedef struct
+{
+	UINT32 filepos; // file offset of the resource
+	UINT32 size; // size of the resource
+	char name[8]; // name of the resource
+} ATTRPACK filelump_t;
+
 // ==============================================================
 //               WAD FILE STRUCTURE DEFINITIONS
 // ==============================================================
@@ -34,14 +43,25 @@ typedef struct
 	UINT32 infotableofs; // the 'directory' of resources
 } wadinfo_t;
 
+// Available compression methods for lumps.
+typedef enum
+{
+	CM_NOCOMPRESSION,
+	CM_DEFLATE,
+	CM_LZF,
+	CM_UNSUPPORTED
+} compmethod;
+
 //  a memory entry of the wad directory
 typedef struct
 {
 	unsigned long position; // filelump_t filepos
 	unsigned long disksize; // filelump_t size
 	char name[9]; // filelump_t name[]
+	char *name2; // Used by PK3s. Dynamically allocated name.
 	size_t size; // real (uncompressed) size
 	INT32 compressed; // i
+	compmethod compression; // lump compression method
 } lumpinfo_t;
 
 // =========================================================================
@@ -58,9 +78,19 @@ typedef struct
 #include "m_aatree.h"
 #endif
 
+// Resource type of the WAD. Yeah, I know this sounds dumb, but I'll leave it like this until I clean up the code further.
+typedef enum restype
+{
+	RET_WAD,
+	RET_SOC,
+	RET_LUA,
+	RET_PK3
+} restype_t;
+
 typedef struct wadfile_s
 {
 	char *filename;
+	enum restype type;
 	lumpinfo_t *lumpinfo;
 	lumpcache_t *lumpcache;
 #ifdef HWRENDER
@@ -85,7 +115,7 @@ void W_Shutdown(void);
 // Opens a WAD file. Returns the FILE * handle for the file, or NULL if not found or could not be opened
 FILE *W_OpenWadFile(const char **filename, boolean useerrors);
 // Load and add a wadfile to the active wad files, returns numbers of lumps, INT16_MAX on error
-UINT16 W_LoadWadFile(const char *filename);
+UINT16 W_InitFile(const char *filename);
 #ifdef DELFILE
 void W_UnloadWadFile(UINT16 num);
 #endif
@@ -98,6 +128,12 @@ const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump);
 const char *W_CheckNameForNum(lumpnum_t lumpnum);
 
 UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump); // checks only in one pwad
+
+UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump);
+UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump);
+UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump);
+
+lumpnum_t W_CheckNumForMap(const char *name);
 lumpnum_t W_CheckNumForName(const char *name);
 lumpnum_t W_GetNumForName(const char *name); // like W_CheckNumForName but I_Error on LUMPERROR
 lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, const char *blockend);
@@ -106,6 +142,10 @@ UINT8 W_LumpExists(const char *name); // Lua uses this.
 size_t W_LumpLengthPwad(UINT16 wad, UINT16 lump);
 size_t W_LumpLength(lumpnum_t lumpnum);
 
+boolean W_IsLumpWad(lumpnum_t lumpnum); // for loading maps from WADs in PK3s
+
+void zerr(int ret); // zlib error checking
+
 size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, size_t offset);
 size_t W_ReadLumpHeader(lumpnum_t lump, void *dest, size_t size, size_t offest); // read all or a part of a lump
 void W_ReadLumpPwad(UINT16 wad, UINT16 lump, void *dest);