diff --git a/src/dehacked.c b/src/dehacked.c
index b77939c2a678daf50696969910e2a95dc0e9312b..4c90211ccf4092a6b733f483114557257cd5ffa1 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -8824,9 +8824,11 @@ static const char *const MOBJEFLAG_LIST[] = {
 	"JUSTSTEPPEDDOWN", // used for ramp sectors
 	"VERTICALFLIP", // Vertically flip sprite/allow upside-down physics
 	"GOOWATER", // Goo water
+	"TOUCHLAVA", // The mobj is touching a lava block
 	"PUSHED", // Mobj was already pushed this tic
 	"SPRUNG", // Mobj was already sprung this tic
 	"APPLYPMOMZ", // Platform movement
+	"TRACERANGLE", // Compute and trigger on mobj angle relative to tracer
 	NULL
 };
 
diff --git a/src/f_finale.c b/src/f_finale.c
index 1436a159b1285cb40ba302a15f767412f0c0f77c..124fedc287d70757108c407fcf9d7013e9910a8f 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -2493,7 +2493,7 @@ void F_StartTitleScreen(void)
 			camera.x = startpos->x << FRACBITS;
 			camera.y = startpos->y << FRACBITS;
 			camera.subsector = R_PointInSubsector(camera.x, camera.y);
-			camera.z = camera.subsector->sector->floorheight + ((startpos->options >> ZSHIFT) << FRACBITS);
+			camera.z = camera.subsector->sector->floorheight + (startpos->z << FRACBITS);
 			camera.angle = (startpos->angle % 360)*ANG1;
 			camera.aiming = 0;
 		}
diff --git a/src/g_game.c b/src/g_game.c
index e6a28aa41dcd6a3a749265cc2b78aa750ccf87ef..efb580f2196eda27e7b380ba8e6904137ed05db1 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2965,7 +2965,7 @@ void G_DoReborn(INT32 playernum)
 		}
 		if (!countdowntimeup && (mapheaderinfo[gamemap-1]->levelflags & LF_NORELOAD))
 		{
-			P_LoadThingsOnly();
+			P_RespawnThings();
 
 			for (i = 0; i < MAXPLAYERS; i++)
 			{
@@ -6873,23 +6873,20 @@ void G_AddGhost(char *defdemoname)
 	I_Assert(mthing);
 	{ // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling.
 		fixed_t z,f,c;
+		fixed_t offset = mthing->z << FRACBITS;
 		gh->mo = P_SpawnMobj(mthing->x << FRACBITS, mthing->y << FRACBITS, 0, MT_GHOST);
-		gh->mo->angle = FixedAngle(mthing->angle*FRACUNIT);
+		gh->mo->angle = FixedAngle(mthing->angle << FRACBITS);
 		f = gh->mo->floorz;
 		c = gh->mo->ceilingz - mobjinfo[MT_PLAYER].height;
 		if (!!(mthing->options & MTF_AMBUSH) ^ !!(mthing->options & MTF_OBJECTFLIP))
 		{
-			z = c;
-			if (mthing->options >> ZSHIFT)
-				z -= ((mthing->options >> ZSHIFT) << FRACBITS);
+			z = c - offset;
 			if (z < f)
 				z = f;
 		}
 		else
 		{
-			z = f;
-			if (mthing->options >> ZSHIFT)
-				z += ((mthing->options >> ZSHIFT) << FRACBITS);
+			z = f + offset;
 			if (z > c)
 				z = c;
 		}
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 8ad66d04f4b26a67cd94302da4357899e0326587..8ce6551f7319b9c9179805fda2c181f945c0dab0 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -134,6 +134,7 @@ enum side_e {
 	side_toptexture,
 	side_bottomtexture,
 	side_midtexture,
+	side_line,
 	side_sector,
 	side_special,
 	side_repeatcnt,
@@ -869,6 +870,9 @@ static int side_get(lua_State *L)
 	case side_midtexture:
 		lua_pushinteger(L, side->midtexture);
 		return 1;
+	case side_line:
+		LUA_PushUserdata(L, side->line, META_LINE);
+		return 1;
 	case side_sector:
 		LUA_PushUserdata(L, side->sector, META_SECTOR);
 		return 1;
@@ -902,6 +906,7 @@ static int side_set(lua_State *L)
 	switch(field)
 	{
 	case side_valid: // valid
+	case side_line:
 	case side_sector:
 	case side_special:
 	case side_text:
diff --git a/src/m_cheat.c b/src/m_cheat.c
index bd6eca73ac907d73b77e66489b092ea1e0070309..58bb2962c7ae60f966146c583297bb8cb242ad30 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -517,6 +517,7 @@ void Command_Teleport_f(void)
 		if (!starpostnum) // spawnpoints...
 		{
 			mapthing_t *mt;
+			fixed_t offset;
 
 			if (starpostpath >= numcoopstarts)
 			{
@@ -527,6 +528,7 @@ void Command_Teleport_f(void)
 			mt = playerstarts[starpostpath]; // Given above check, should never be NULL.
 			intx = mt->x<<FRACBITS;
 			inty = mt->y<<FRACBITS;
+			offset = mt->z<<FRACBITS;
 
 			ss = R_IsPointInSubsector(intx, inty);
 			if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height)
@@ -538,17 +540,9 @@ void Command_Teleport_f(void)
 			// Flagging a player's ambush will make them start on the ceiling
 			// Objectflip inverts
 			if (!!(mt->options & MTF_AMBUSH) ^ !!(mt->options & MTF_OBJECTFLIP))
-			{
-				intz = ss->sector->ceilingheight - p->mo->height;
-				if (mt->options >> ZSHIFT)
-					intz -= ((mt->options >> ZSHIFT) << FRACBITS);
-			}
+				intz = ss->sector->ceilingheight - p->mo->height - offset;
 			else
-			{
-				intz = ss->sector->floorheight;
-				if (mt->options >> ZSHIFT)
-					intz += ((mt->options >> ZSHIFT) << FRACBITS);
-			}
+				intz = ss->sector->floorheight + offset;
 
 			if (mt->options & MTF_OBJECTFLIP) // flip the player!
 			{
@@ -1193,14 +1187,14 @@ void OP_NightsObjectplace(player_t *player)
 	if (cmd->buttons & BT_TOSSFLAG)
 	{
 		UINT16 vertangle = (UINT16)(player->anotherflyangle % 360);
-		UINT16 newflags, newz;
+		UINT16 newflags;
 
 		player->pflags |= PF_ATTACKDOWN;
 		if (!OP_HeightOkay(player, false))
 			return;
 
 		mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_NIGHTSBUMPER].doomednum, false);
-		newz = min((mt->options >> ZSHIFT) - (mobjinfo[MT_NIGHTSBUMPER].height/4), 0);
+		mt->z = min(mt->z - (mobjinfo[MT_NIGHTSBUMPER].height/4), 0);
 			// height offset: from P_TouchSpecialThing case MT_NIGHTSBUMPER
 
 		// clockwise
@@ -1231,7 +1225,7 @@ void OP_NightsObjectplace(player_t *player)
 		else // forward
 			newflags = 0;
 
-		mt->options = (newz << ZSHIFT) | newflags;
+		mt->options = (mt->z << ZSHIFT) | newflags;
 
 		// if NiGHTS is facing backwards, orient the Thing angle forwards so that the sprite angle
 		// displays correctly. Backwards movement via the Thing flags is unaffected.
@@ -1439,7 +1433,7 @@ void OP_ObjectplaceMovement(player_t *player)
 		else
 			P_SpawnMapThing(mt);
 
-		CONS_Printf(M_GetText("Placed object type %d at %d, %d, %d, %d\n"), mt->type, mt->x, mt->y, mt->options>>ZSHIFT, mt->angle);
+		CONS_Printf(M_GetText("Placed object type %d at %d, %d, %d, %d\n"), mt->type, mt->x, mt->y, mt->z, mt->angle);
 	}
 }
 
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 8880e582760d30f74d5843d1d66e5adc28bac24d..5c0ed12923cb6bebe494e401c532a80a76157f31 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -9903,19 +9903,12 @@ static void P_FlagFuseThink(mobj_t *mobj)
 
 	x = mobj->spawnpoint->x << FRACBITS;
 	y = mobj->spawnpoint->y << FRACBITS;
+	z = mobj->spawnpoint->z << FRACBITS;
 	ss = R_PointInSubsector(x, y);
 	if (mobj->spawnpoint->options & MTF_OBJECTFLIP)
-	{
-		z = ss->sector->ceilingheight - mobjinfo[mobj->type].height;
-		if (mobj->spawnpoint->options >> ZSHIFT)
-			z -= (mobj->spawnpoint->options >> ZSHIFT) << FRACBITS;
-	}
+		z = ss->sector->ceilingheight - mobjinfo[mobj->type].height - z;
 	else
-	{
-		z = ss->sector->floorheight;
-		if (mobj->spawnpoint->options >> ZSHIFT)
-			z += (mobj->spawnpoint->options >> ZSHIFT) << FRACBITS;
-	}
+		z = ss->sector->floorheight + z;
 	flagmo = P_SpawnMobj(x, y, z, mobj->type);
 	flagmo->spawnpoint = mobj->spawnpoint;
 	if (mobj->spawnpoint->options & MTF_OBJECTFLIP)
@@ -11289,9 +11282,6 @@ static mobjtype_t P_GetMobjtype(UINT16 mthingtype)
 //
 void P_RespawnSpecials(void)
 {
-	fixed_t x, y, z;
-	subsector_t *ss;
-	mobj_t *mo = NULL;
 	mapthing_t *mthing = NULL;
 
 	// only respawn items when cv_itemrespawn is on
@@ -11321,63 +11311,8 @@ void P_RespawnSpecials(void)
 #endif
 
 	if (mthing)
-	{
-		mobjtype_t i = P_GetMobjtype(mthing->type);
-		x = mthing->x << FRACBITS;
-		y = mthing->y << FRACBITS;
-		ss = R_PointInSubsector(x, y);
-
-		if (i == MT_UNKNOWN) // prevent creation of objects with this type -- Monster Iestyn 17/12/17
-		{
-			// 3D Mode start Thing is unlikely to be added to the que,
-			// so don't bother checking for that specific type
-			CONS_Alert(CONS_WARNING, M_GetText("P_RespawnSpecials: Unknown thing type %d attempted to respawn at (%d, %d)\n"), mthing->type, mthing->x, mthing->y);
-			// pull it from the que
-			iquetail = (iquetail+1)&(ITEMQUESIZE-1);
-			return;
-		}
-
-		//CTF rings should continue to respawn as normal rings outside of CTF.
-		if (!(gametyperules & GTR_TEAMFLAGS))
-		{
-			if (i == MT_REDTEAMRING || i == MT_BLUETEAMRING)
-				i = MT_RING;
-		}
-
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			z = (
-#ifdef ESLOPE
-			ss->sector->c_slope ? P_GetZAt(ss->sector->c_slope, x, y) :
-#endif
-			ss->sector->ceilingheight) - (mthing->options >> ZSHIFT) * FRACUNIT;
-			if (mthing->options & MTF_AMBUSH
-			&& (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || i == MT_NIGHTSSTAR || P_WeaponOrPanel(i)))
-				z -= 24*FRACUNIT;
-			z -= mobjinfo[i].height; // Don't forget the height!
-		}
-		else
-		{
-			z = (
-#ifdef ESLOPE
-			ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, x, y) :
-#endif
-			ss->sector->floorheight) + (mthing->options >> ZSHIFT) * FRACUNIT;
-			if (mthing->options & MTF_AMBUSH
-			&& (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || i == MT_NIGHTSSTAR || P_WeaponOrPanel(i)))
-				z += 24*FRACUNIT;
-		}
-
-		mo = P_SpawnMobj(x, y, z, i);
-		mo->spawnpoint = mthing;
-		mo->angle = ANGLE_45 * (mthing->angle/45);
+		P_SpawnMapThing(mthing);
 
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			mo->eflags |= MFE_VERTICALFLIP;
-			mo->flags2 |= MF2_OBJECTFLIP;
-		}
-	}
 	// pull it from the que
 	iquetail = (iquetail+1)&(ITEMQUESIZE-1);
 }
@@ -11563,7 +11498,7 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
 
 	fixed_t z;
 	sector_t *sector;
-	fixed_t floor, ceiling;
+	fixed_t floor, ceiling, ceilingspawn;
 
 	player_t *p = &players[playernum];
 	mobj_t *mobj = p->mo;
@@ -11590,23 +11525,18 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
 	sector->c_slope ? P_GetZAt(sector->c_slope, x, y) :
 #endif
 	sector->ceilingheight;
+	ceilingspawn = ceiling - mobjinfo[MT_PLAYER].height;
 
 	if (mthing)
 	{
+		fixed_t offset = mthing->z << FRACBITS;
+
 		// Flagging a player's ambush will make them start on the ceiling
 		// Objectflip inverts
 		if (!!(mthing->options & MTF_AMBUSH) ^ !!(mthing->options & MTF_OBJECTFLIP))
-		{
-			z = ceiling - mobjinfo[MT_PLAYER].height;
-			if (mthing->options >> ZSHIFT)
-				z -= ((mthing->options >> ZSHIFT) << FRACBITS);
-		}
+			z = ceilingspawn - offset;
 		else
-		{
-			z = floor;
-			if (mthing->options >> ZSHIFT)
-				z += ((mthing->options >> ZSHIFT) << FRACBITS);
-		}
+			z = floor + offset;
 
 		if (mthing->options & MTF_OBJECTFLIP) // flip the player!
 		{
@@ -11623,8 +11553,8 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
 
 	if (z < floor)
 		z = floor;
-	else if (z > ceiling - mobjinfo[MT_PLAYER].height)
-		z = ceiling - mobjinfo[MT_PLAYER].height;
+	else if (z > ceilingspawn)
+		z = ceilingspawn;
 
 	mobj->floorz = floor;
 	mobj->ceilingz = ceiling;
@@ -12465,7 +12395,7 @@ static boolean P_SetupParticleGen(mapthing_t *mthing, mobj_t *mobj)
 		|| (ticcount = (sides[lines[line].sidenum[1]].textureoffset >> FRACBITS)) < 1)
 		ticcount = 3;
 
-	numdivisions = (mthing->options >> ZSHIFT);
+	numdivisions = mthing->z;
 
 	if (numdivisions)
 	{
diff --git a/src/p_setup.c b/src/p_setup.c
index 2605faa0d669248f219e3aabac3ab2aeee10ebf3..ea61a2ec78a3580e7d0529ae8ba17529d8657b55 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -373,114 +373,6 @@ UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade)
 	return mapheaderinfo[map-1]->grades[mare].grade[grade-1];
 }
 
-// Loads the vertexes for a level.
-static inline void P_LoadRawVertexes(UINT8 *data)
-{
-	mapvertex_t *ml = (mapvertex_t *)data;
-	vertex_t *li = vertexes;
-	size_t i;
-
-	// Copy and convert vertex coordinates, internal representation as fixed.
-	for (i = 0; i < numvertexes; i++, li++, ml++)
-	{
-		li->x = SHORT(ml->x)<<FRACBITS;
-		li->y = SHORT(ml->y)<<FRACBITS;
-	}
-}
-
-/** Computes the length of a seg in fracunits.
-  *
-  * \param seg Seg to compute length for.
-  * \return Length in fracunits.
-  */
-fixed_t P_SegLength(seg_t *seg)
-{
-	INT64 dx = (seg->v2->x - seg->v1->x)>>1;
-	INT64 dy = (seg->v2->y - seg->v1->y)>>1;
-	return FixedHypot(dx, dy)<<1;
-}
-
-#ifdef HWRENDER
-/** Computes the length of a seg as a float.
-  * This is needed for OpenGL.
-  *
-  * \param seg Seg to compute length for.
-  * \return Length as a float.
-  */
-static inline float P_SegLengthFloat(seg_t *seg)
-{
-	float dx, dy;
-
-	// make a vector (start at origin)
-	dx = FIXED_TO_FLOAT(seg->v2->x - seg->v1->x);
-	dy = FIXED_TO_FLOAT(seg->v2->y - seg->v1->y);
-
-	return (float)hypot(dx, dy);
-}
-#endif
-
-// Loads the SEGS resource from a level.
-static void P_LoadRawSegs(UINT8 *data)
-{
-	INT32 linedef, side;
-	mapseg_t *ml = (mapseg_t*)data;
-	seg_t *li = segs;
-	line_t *ldef;
-	size_t i;
-
-	for (i = 0; i < numsegs; i++, li++, ml++)
-	{
-		li->v1 = &vertexes[SHORT(ml->v1)];
-		li->v2 = &vertexes[SHORT(ml->v2)];
-
-		li->length = P_SegLength(li);
-#ifdef HWRENDER
-		if (rendermode == render_opengl)
-		{
-			li->flength = P_SegLengthFloat(li);
-			//Hurdler: 04/12/2000: for now, only used in hardware mode
-			li->lightmaps = NULL; // list of static lightmap for this seg
-		}
-		li->pv1 = li->pv2 = NULL;
-#endif
-
-		li->angle = (SHORT(ml->angle))<<FRACBITS;
-		li->offset = (SHORT(ml->offset))<<FRACBITS;
-		linedef = SHORT(ml->linedef);
-		ldef = &lines[linedef];
-		li->linedef = ldef;
-		li->side = side = SHORT(ml->side);
-		li->sidedef = &sides[ldef->sidenum[side]];
-		li->frontsector = sides[ldef->sidenum[side]].sector;
-		if (ldef->flags & ML_TWOSIDED)
-			li->backsector = sides[ldef->sidenum[side^1]].sector;
-		else
-			li->backsector = 0;
-
-		li->numlights = 0;
-		li->rlights = NULL;
-	}
-}
-
-// Loads the SSECTORS resource from a level.
-static inline void P_LoadRawSubsectors(void *data)
-{
-	mapsubsector_t *ms = (mapsubsector_t*)data;
-	subsector_t *ss = subsectors;
-	size_t i;
-
-	for (i = 0; i < numsubsectors; i++, ss++, ms++)
-	{
-		ss->sector = NULL;
-		ss->numlines = SHORT(ms->numsegs);
-		ss->firstline = SHORT(ms->firstseg);
-#ifdef FLOORSPLATS
-		ss->splats = NULL;
-#endif
-		ss->validcount = 0;
-	}
-}
-
 //
 // levelflats
 //
@@ -647,107 +539,6 @@ INT32 P_CheckLevelFlat(const char *flatname)
 	return (INT32)i;
 }
 
-// Sets up the ingame sectors structures.
-static void P_LoadRawSectors(UINT8 *data)
-{
-	mapsector_t *ms = (mapsector_t *)data;
-	sector_t *ss = sectors;
-	size_t i;
-
-	// For each counted sector, copy the sector raw data from our cache pointer ms, to the global table pointer ss.
-	for (i = 0; i < numsectors; i++, ss++, ms++)
-	{
-		ss->floorheight = SHORT(ms->floorheight)<<FRACBITS;
-		ss->ceilingheight = SHORT(ms->ceilingheight)<<FRACBITS;
-
-		ss->floorpic = P_AddLevelFlat(ms->floorpic, foundflats);
-		ss->ceilingpic = P_AddLevelFlat(ms->ceilingpic, foundflats);
-
-		ss->lightlevel = SHORT(ms->lightlevel);
-		ss->spawn_lightlevel = ss->lightlevel;
-		ss->special = SHORT(ms->special);
-		ss->tag = SHORT(ms->tag);
-		ss->nexttag = ss->firsttag = -1;
-
-		memset(&ss->soundorg, 0, sizeof(ss->soundorg));
-		ss->validcount = 0;
-
-		ss->thinglist = NULL;
-		ss->touching_thinglist = NULL;
-		ss->preciplist = NULL;
-		ss->touching_preciplist = NULL;
-
-		ss->floordata = NULL;
-		ss->ceilingdata = NULL;
-		ss->lightingdata = NULL;
-
-		ss->linecount = 0;
-		ss->lines = NULL;
-
-		ss->heightsec = -1;
-		ss->camsec = -1;
-		ss->floorlightsec = -1;
-		ss->ceilinglightsec = -1;
-		ss->crumblestate = 0;
-		ss->ffloors = NULL;
-		ss->lightlist = NULL;
-		ss->numlights = 0;
-		ss->attached = NULL;
-		ss->attachedsolid = NULL;
-		ss->numattached = 0;
-		ss->maxattached = 1;
-		ss->moved = true;
-
-		ss->extra_colormap = NULL;
-		ss->spawn_extra_colormap = NULL;
-
-		ss->floor_xoffs = ss->ceiling_xoffs = ss->floor_yoffs = ss->ceiling_yoffs = 0;
-		ss->floorpic_angle = ss->ceilingpic_angle = 0;
-		ss->gravity = NULL;
-		ss->cullheight = NULL;
-		ss->verticalflip = false;
-		ss->flags = 0;
-		ss->flags |= SF_FLIPSPECIAL_FLOOR;
-
-		ss->floorspeed = 0;
-		ss->ceilspeed = 0;
-
-#ifdef HWRENDER // ----- for special tricks with HW renderer -----
-		ss->pseudoSector = false;
-		ss->virtualFloor = false;
-		ss->virtualCeiling = false;
-		ss->sectorLines = NULL;
-		ss->stackList = NULL;
-		ss->lineoutLength = -1.0l;
-#endif // ----- end special tricks -----
-	}
-}
-
-//
-// P_LoadNodes
-//
-static void P_LoadRawNodes(UINT8 *data)
-{
-	UINT8 j, k;
-	mapnode_t *mn = (mapnode_t*)data;
-	node_t *no = nodes;
-	size_t i;
-
-	for (i = 0; i < numnodes; i++, no++, mn++)
-	{
-		no->x = SHORT(mn->x)<<FRACBITS;
-		no->y = SHORT(mn->y)<<FRACBITS;
-		no->dx = SHORT(mn->dx)<<FRACBITS;
-		no->dy = SHORT(mn->dy)<<FRACBITS;
-		for (j = 0; j < 2; j++)
-		{
-			no->children[j] = SHORT(mn->children[j]);
-			for (k = 0; k < 4; k++)
-				no->bbox[j][k] = SHORT(mn->bbox[j][k])<<FRACBITS;
-		}
-	}
-}
-
 //
 // P_ReloadRings
 // Used by NiGHTS, clears all ring/sphere/hoop/etc items and respawns them
@@ -902,30 +693,6 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum)
 }
 #endif
 
-static void P_PrepareRawThings(UINT8 *data)
-{
-	mapthing_t *mt;
-	size_t i;
-
-	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
-	{
-		mt->x = READINT16(data);
-		mt->y = READINT16(data);
-
-		mt->angle = READINT16(data);
-		mt->type = READUINT16(data);
-		mt->options = READUINT16(data);
-		mt->extrainfo = (UINT8)(mt->type >> 12);
-
-		mt->type &= 4095;
-
-		if (mt->type == 1705 || (mt->type == 750 && mt->extrainfo))
-			mt->z = mt->options; // NiGHTS Hoops use the full flags bits to set the height.
-		else
-			mt->z = mt->options >> ZSHIFT;
-	}
-}
-
 static void P_SpawnEmeraldHunt(void)
 {
 	INT32 emer1, emer2, emer3;
@@ -1062,251 +829,249 @@ void P_WriteThings(lumpnum_t lumpnum)
 	CONS_Printf(M_GetText("newthings%d.lmp saved.\n"), gamemap);
 }
 
-static void P_LoadRawLineDefs(UINT8 *data)
+//
+// MAP LOADING FUNCTIONS
+//
+
+static void P_LoadVertices(UINT8 *data)
 {
-	maplinedef_t *mld = (maplinedef_t *)data;
-	line_t *ld = lines;
+	mapvertex_t *mv = (mapvertex_t *)data;
+	vertex_t *v = vertexes;
 	size_t i;
 
-	for (i = 0; i < numlines; i++, mld++, ld++)
+	// Copy and convert vertex coordinates, internal representation as fixed.
+	for (i = 0; i < numvertexes; i++, v++, mv++)
 	{
-		ld->flags = SHORT(mld->flags);
-		ld->special = SHORT(mld->special);
-		ld->tag = SHORT(mld->tag);
-		ld->v1 = &vertexes[SHORT(mld->v1)];
-		ld->v2 = &vertexes[SHORT(mld->v2)];
-
-		ld->sidenum[0] = SHORT(mld->sidenum[0]);
-		ld->sidenum[1] = SHORT(mld->sidenum[1]);
+		v->x = SHORT(mv->x)<<FRACBITS;
+		v->y = SHORT(mv->y)<<FRACBITS;
+		v->z = 0;
 	}
 }
 
-static void P_SetupLines(void)
+static void P_InitializeSector(sector_t *ss)
 {
-	line_t *ld = lines;
-	size_t i;
+	ss->nexttag = ss->firsttag = -1;
 
-	for (i = 0; i < numlines; i++, ld++)
-	{
-		vertex_t *v1 = ld->v1;
-		vertex_t *v2 = ld->v2;
+	memset(&ss->soundorg, 0, sizeof(ss->soundorg));
 
-#ifdef WALLSPLATS
-		ld->splats = NULL;
-#endif
+	ss->validcount = 0;
 
-#ifdef POLYOBJECTS
-		ld->polyobj = NULL;
-#endif
+	ss->thinglist = NULL;
 
-		ld->dx = v2->x - v1->x;
-		ld->dy = v2->y - v1->y;
+	ss->floordata = NULL;
+	ss->ceilingdata = NULL;
+	ss->lightingdata = NULL;
+	ss->fadecolormapdata = NULL;
 
-		if (!ld->dx)
-			ld->slopetype = ST_VERTICAL;
-		else if (!ld->dy)
-			ld->slopetype = ST_HORIZONTAL;
-		else if ((ld->dy > 0) == (ld->dx > 0))
-			ld->slopetype = ST_POSITIVE;
-		else
-			ld->slopetype = ST_NEGATIVE;
+	ss->floor_xoffs = ss->floor_yoffs = 0;
+	ss->ceiling_xoffs = ss->ceiling_yoffs = 0;
 
-		if (v1->x < v2->x)
-		{
-			ld->bbox[BOXLEFT] = v1->x;
-			ld->bbox[BOXRIGHT] = v2->x;
-		}
-		else
-		{
-			ld->bbox[BOXLEFT] = v2->x;
-			ld->bbox[BOXRIGHT] = v1->x;
-		}
+	ss->floorpic_angle = ss->ceilingpic_angle = 0;
 
-		if (v1->y < v2->y)
-		{
-			ld->bbox[BOXBOTTOM] = v1->y;
-			ld->bbox[BOXTOP] = v2->y;
-		}
-		else
-		{
-			ld->bbox[BOXBOTTOM] = v2->y;
-			ld->bbox[BOXTOP] = v1->y;
-		}
+	ss->heightsec = -1;
+	ss->camsec = -1;
 
-		{
-			// cph 2006/09/30 - fix sidedef errors right away.
-			// cph 2002/07/20 - these errors are fatal if not fixed, so apply them
-			UINT8 j;
+	ss->floorlightsec = ss->ceilinglightsec = -1;
+	ss->crumblestate = 0;
 
-			for (j=0; j < 2; j++)
-				if (ld->sidenum[j] != 0xffff && ld->sidenum[j] >= (UINT16)numsides)
-				{
-					ld->sidenum[j] = 0xffff;
-					CONS_Debug(DBG_SETUP, "P_LoadRawLineDefs: linedef %s has out-of-range sidedef number\n", sizeu1(numlines-i-1));
-				}
-		}
+	ss->touching_thinglist = NULL;
 
-		ld->frontsector = ld->backsector = NULL;
-		ld->validcount = 0;
-		ld->firsttag = ld->nexttag = -1;
-		ld->callcount = 0;
+	ss->linecount = 0;
+	ss->lines = NULL;
+	ss->tagline = NULL;
 
-		// killough 11/98: fix common wad errors (missing sidedefs):
-		if (ld->sidenum[0] == 0xffff)
-		{
-			ld->sidenum[0] = 0;  // Substitute dummy sidedef for missing right side
-			// cph - print a warning about the bug
-			CONS_Debug(DBG_SETUP, "Linedef %s missing first sidedef\n", sizeu1(numlines-i-1));
-		}
+	ss->ffloors = NULL;
+	ss->attached = NULL;
+	ss->attachedsolid = NULL;
+	ss->numattached = 0;
+	ss->maxattached = 1;
+	ss->lightlist = NULL;
+	ss->numlights = 0;
+	ss->moved = true;
 
-		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, "Linedef %s has two-sided flag set, but no second sidedef\n", sizeu1(numlines-i-1));
-		}
+	ss->extra_colormap = NULL;
 
-		if (ld->sidenum[0] != 0xffff && ld->special)
-			sides[ld->sidenum[0]].special = ld->special;
-		if (ld->sidenum[1] != 0xffff && ld->special)
-			sides[ld->sidenum[1]].special = ld->special;
-	}
-}
+#ifdef HWRENDER // ----- for special tricks with HW renderer -----
+	ss->pseudoSector = false;
+	ss->virtualFloor = false;
+	ss->virtualFloorheight = 0;
+	ss->virtualCeiling = false;
+	ss->virtualCeilingheight = 0;
+	ss->sectorLines = NULL;
+	ss->stackList = NULL;
+	ss->lineoutLength = -1.0l;
+#endif // ----- end special tricks -----
 
-static void P_LoadLineDefs2(void)
-{
-	size_t i = numlines;
-	register line_t *ld = lines;
-	for (;i--;ld++)
-	{
-		ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here
-		ld->backsector  = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0;
+	ss->gravity = NULL;
+	ss->verticalflip = false;
+	ss->flags = SF_FLIPSPECIAL_FLOOR;
 
-		// Repeat count for midtexture
-		if ((ld->flags & ML_EFFECT5) && (ld->sidenum[1] != 0xffff)
-			&& !(ld->special >= 300 && ld->special < 500)) // exempt linedef exec specials
-		{
-			sides[ld->sidenum[0]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) >> 12);
-			sides[ld->sidenum[0]].textureoffset = (((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
-			sides[ld->sidenum[1]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[1]].textureoffset >> FRACBITS) >> 12);
-			sides[ld->sidenum[1]].textureoffset = (((unsigned)sides[ld->sidenum[1]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
-		}
+	ss->cullheight = NULL;
 
-		// Compile linedef 'text' from both sidedefs 'text' for appropriate specials.
-		switch(ld->special)
-		{
-		case 331: // Trigger linedef executor: Skin - Continuous
-		case 332: // Trigger linedef executor: Skin - Each time
-		case 333: // Trigger linedef executor: Skin - Once
-		case 443: // Calls a named Lua function
-			if (sides[ld->sidenum[0]].text)
-			{
-				size_t len = strlen(sides[ld->sidenum[0]].text)+1;
-				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
-					len += strlen(sides[ld->sidenum[1]].text);
-				ld->text = Z_Malloc(len, PU_LEVEL, NULL);
-				M_Memcpy(ld->text, sides[ld->sidenum[0]].text, strlen(sides[ld->sidenum[0]].text)+1);
-				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
-					M_Memcpy(ld->text+strlen(ld->text)+1, sides[ld->sidenum[1]].text, strlen(sides[ld->sidenum[1]].text)+1);
-			}
-			break;
-		}
-	}
+	ss->floorspeed = ss->ceilspeed = 0;
 
-	// Optimize sidedefs
-	if (M_CheckParm("-compress"))
-	{
-		side_t *newsides;
-		size_t numnewsides = 0;
-		size_t z;
+	ss->preciplist = NULL;
+	ss->touching_preciplist = NULL;
 
-		for (i = 0; i < numsides; i++)
-		{
-			size_t j, k;
-			if (sides[i].sector == NULL)
-				continue;
+#ifdef ESLOPE
+	ss->f_slope = NULL;
+	ss->c_slope = NULL;
+	ss->hasslope = false;
+#endif
 
-			for (k = numlines, ld = lines; k--; ld++)
-			{
-				if (ld->sidenum[0] == i)
-					ld->sidenum[0] = (UINT16)numnewsides;
+	ss->spawn_lightlevel = ss->lightlevel;
 
-				if (ld->sidenum[1] == i)
-					ld->sidenum[1] = (UINT16)numnewsides;
-			}
+	ss->spawn_extra_colormap = NULL;
+}
 
-			for (j = i+1; j < numsides; j++)
-			{
-				if (sides[j].sector == NULL)
-					continue;
+static void P_LoadSectors(UINT8 *data)
+{
+	mapsector_t *ms = (mapsector_t *)data;
+	sector_t *ss = sectors;
+	size_t i;
 
-				if (!memcmp(&sides[i], &sides[j], sizeof(side_t)))
-				{
-					// Find the linedefs that belong to this one
-					for (k = numlines, ld = lines; k--; ld++)
-					{
-						if (ld->sidenum[0] == j)
-							ld->sidenum[0] = (UINT16)numnewsides;
+	// For each counted sector, copy the sector raw data from our cache pointer ms, to the global table pointer ss.
+	for (i = 0; i < numsectors; i++, ss++, ms++)
+	{
+		ss->floorheight = SHORT(ms->floorheight)<<FRACBITS;
+		ss->ceilingheight = SHORT(ms->ceilingheight)<<FRACBITS;
 
-						if (ld->sidenum[1] == j)
-							ld->sidenum[1] = (UINT16)numnewsides;
-					}
-					sides[j].sector = NULL; // Flag for deletion
-				}
-			}
-			numnewsides++;
-		}
+		ss->floorpic = P_AddLevelFlat(ms->floorpic, foundflats);
+		ss->ceilingpic = P_AddLevelFlat(ms->ceilingpic, foundflats);
+
+		ss->lightlevel = SHORT(ms->lightlevel);
+		ss->special = SHORT(ms->special);
+		ss->tag = SHORT(ms->tag);
+
+		P_InitializeSector(ss);
+	}
+}
+
+static void P_InitializeLinedef(line_t *ld)
+{
+	vertex_t *v1 = ld->v1;
+	vertex_t *v2 = ld->v2;
+	UINT8 j;
+
+	ld->dx = v2->x - v1->x;
+	ld->dy = v2->y - v1->y;
+
+	ld->bbox[BOXLEFT] = min(v1->x, v2->x);
+	ld->bbox[BOXRIGHT] = max(v1->x, v2->x);
+	ld->bbox[BOXBOTTOM] = min(v1->y, v2->y);
+	ld->bbox[BOXTOP] = max(v1->y, v2->y);
+
+	if (!ld->dx)
+		ld->slopetype = ST_VERTICAL;
+	else if (!ld->dy)
+		ld->slopetype = ST_HORIZONTAL;
+	else if ((ld->dy > 0) == (ld->dx > 0))
+		ld->slopetype = ST_POSITIVE;
+	else
+		ld->slopetype = ST_NEGATIVE;
 
-		// We're loading crap into this block anyhow, so no point in zeroing it out.
-		newsides = Z_Malloc(numnewsides * sizeof(*newsides), PU_LEVEL, NULL);
+	ld->frontsector = ld->backsector = NULL;
 
-		// Copy the sides to their new block of memory.
-		for (i = 0, z = 0; i < numsides; i++)
+	ld->validcount = 0;
+#ifdef WALLSPLATS
+	ld->splats = NULL;
+#endif
+	ld->firsttag = ld->nexttag = -1;
+#ifdef POLYOBJECTS
+	ld->polyobj = NULL;
+#endif
+
+	ld->text = NULL;
+	ld->callcount = 0;
+
+	// cph 2006/09/30 - fix sidedef errors right away.
+	// cph 2002/07/20 - these errors are fatal if not fixed, so apply them
+	for (j = 0; j < 2; j++)
+		if (ld->sidenum[j] != 0xffff && ld->sidenum[j] >= (UINT16)numsides)
 		{
-			if (sides[i].sector != NULL)
-				M_Memcpy(&newsides[z++], &sides[i], sizeof(side_t));
+			ld->sidenum[j] = 0xffff;
+			CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s has out-of-range sidedef number\n", sizeu1((size_t)(ld - lines)));
 		}
 
-		CONS_Debug(DBG_SETUP, "Old sides is %s, new sides is %s\n", sizeu1(numsides), sizeu1(numnewsides));
+	// killough 11/98: fix common wad errors (missing sidedefs):
+	if (ld->sidenum[0] == 0xffff)
+	{
+		ld->sidenum[0] = 0;  // Substitute dummy sidedef for missing right side
+		// cph - print a warning about the bug
+		CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s missing first sidedef\n", sizeu1((size_t)(ld - lines)));
+	}
+
+	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_InitializeLinedef: Linedef %s has two-sided flag set, but no second sidedef\n", sizeu1((size_t)(ld - lines)));
+	}
+
+	if (ld->sidenum[0] != 0xffff)
+	{
+		sides[ld->sidenum[0]].special = ld->special;
+		sides[ld->sidenum[0]].line = ld;
+
+	}
+	if (ld->sidenum[1] != 0xffff)
+	{
+		sides[ld->sidenum[1]].special = ld->special;
+		sides[ld->sidenum[1]].line = ld;
 
-		Z_Free(sides);
-		sides = newsides;
-		numsides = numnewsides;
 	}
 }
 
-static void P_LoadRawSideDefs2(void *data)
+static void P_LoadLinedefs(UINT8 *data)
 {
-	UINT16 i;
+	maplinedef_t *mld = (maplinedef_t *)data;
+	line_t *ld = lines;
+	size_t i;
 
-	for (i = 0; i < numsides; i++)
+	for (i = 0; i < numlines; i++, mld++, ld++)
 	{
-		register mapsidedef_t *msd = (mapsidedef_t *)data + i;
-		register side_t *sd = sides + i;
-		register sector_t *sec;
+		ld->flags = SHORT(mld->flags);
+		ld->special = SHORT(mld->special);
+		ld->tag = SHORT(mld->tag);
+		ld->v1 = &vertexes[SHORT(mld->v1)];
+		ld->v2 = &vertexes[SHORT(mld->v2)];
+
+		ld->sidenum[0] = SHORT(mld->sidenum[0]);
+		ld->sidenum[1] = SHORT(mld->sidenum[1]);
+
+		P_InitializeLinedef(ld);
+	}
+}
+
+static void P_LoadSidedefs(UINT8 *data)
+{
+	mapsidedef_t *msd = (mapsidedef_t*)data;
+	side_t *sd = sides;
+	size_t i;
+
+	for (i = 0; i < numsides; i++, sd++, msd++)
+	{
+		UINT16 sector_num;
+		boolean isfrontside = !sd->line || sd->line->sidenum[0] == i;
 
 		sd->textureoffset = SHORT(msd->textureoffset)<<FRACBITS;
 		sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
 
-		{ /* cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead */
-			UINT16 sector_num = SHORT(msd->sector);
-
-			if (sector_num >= numsectors)
-			{
-				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];
+		// cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead
+		sector_num = SHORT(msd->sector);
+		if (sector_num >= numsectors)
+		{
+			CONS_Debug(DBG_SETUP, "P_LoadSidedefs: sidedef %s has out-of-range sector num %u\n", sizeu1(i), sector_num);
+			sector_num = 0;
 		}
-
-		sd->sector = sec = &sectors[SHORT(msd->sector)];
+		sd->sector = &sectors[sector_num];
 
 		sd->colormap_data = NULL;
 
-		// Colormaps!
+		// Special info stored in texture fields!
 		switch (sd->special)
 		{
-			case 63: // variable colormap via 242 linedef
+			case 63: // Fake floor/ceiling planes
 			case 606: //SoM: 4/4/2000: Just colormap transfer
 			case 447: // Change colormap of tagged sectors! -- Monster Iestyn 14/06/18
 			case 455: // Fade colormaps! mazmazz 9/12/2018 (:flag_us:)
@@ -1336,9 +1101,8 @@ static void P_LoadRawSideDefs2(void *data)
 					sd->midtexture = get_number(process);
 				}
 
-				// always process if back sidedef, because we need that - symbol
  				sd->text = Z_Malloc(7, PU_LEVEL, NULL);
-				if (i == 1 || msd->toptexture[0] != '-' || msd->toptexture[1] != '\0')
+				if (isfrontside && !(msd->toptexture[0] == '-' && msd->toptexture[1] == '\0'))
 				{
 					M_Memcpy(process,msd->toptexture,8);
 					process[8] = '\0';
@@ -1415,6 +1179,20 @@ static void P_LoadRawSideDefs2(void *data)
 				break;
 			}
 
+			case 259: // Custom FOF
+				if (!isfrontside)
+				{
+					if ((msd->toptexture[0] >= '0' && msd->toptexture[0] <= '9')
+						|| (msd->toptexture[0] >= 'A' && msd->toptexture[0] <= 'F'))
+						sd->toptexture = axtoi(msd->toptexture);
+					else
+						I_Error("Custom FOF (tag %d) needs a value in the linedef's back side upper texture field.", sd->line->tag);
+
+					sd->midtexture = R_TextureNumForName(msd->midtexture);
+					sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
+					break;
+				}
+				// FALLTHRU
 			default: // normal cases
 				if (msd->toptexture[0] == '#')
 				{
@@ -1434,6 +1212,334 @@ static void P_LoadRawSideDefs2(void *data)
 	}
 }
 
+static void P_LoadThings(UINT8 *data)
+{
+	mapthing_t *mt;
+	size_t i;
+
+	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
+	{
+		mt->x = READINT16(data);
+		mt->y = READINT16(data);
+
+		mt->angle = READINT16(data);
+		mt->type = READUINT16(data);
+		mt->options = READUINT16(data);
+		mt->extrainfo = (UINT8)(mt->type >> 12);
+
+		mt->type &= 4095;
+
+		if (mt->type == 1705 || (mt->type == 750 && mt->extrainfo))
+			mt->z = mt->options; // NiGHTS Hoops use the full flags bits to set the height.
+		else
+			mt->z = mt->options >> ZSHIFT;
+	}
+}
+
+static void P_LoadMapData(const virtres_t *virt)
+{
+	virtlump_t* virtvertexes = NULL, * virtsectors = NULL, * virtsidedefs = NULL, * virtlinedefs = NULL, * virtthings = NULL;
+#ifdef UDMF
+	virtlump_t* textmap = vres_Find(virt, "TEXTMAP");
+
+	// Count map data.
+	if (textmap)
+	{
+		nummapthings = 0;
+		numlines = 0;
+		numsides = 0;
+		numvertexes = 0;
+		numsectors = 0;
+
+		// Count how many entries for each type we got in textmap.
+		//TextmapCount(vtextmap->data, vtextmap->size);
+	}
+	else
+#endif
+	{
+		virtthings   = vres_Find(virt, "THINGS");
+		virtvertexes = vres_Find(virt, "VERTEXES");
+		virtsectors  = vres_Find(virt, "SECTORS");
+		virtsidedefs = vres_Find(virt, "SIDEDEFS");
+		virtlinedefs = vres_Find(virt, "LINEDEFS");
+
+		if (!virtthings)
+			I_Error("THINGS lump not found");
+		if (!virtvertexes)
+			I_Error("VERTEXES lump not found");
+		if (!virtsectors)
+			I_Error("SECTORS lump not found");
+		if (!virtsidedefs)
+			I_Error("SIDEDEFS lump not found");
+		if (!virtlinedefs)
+			I_Error("LINEDEFS lump not found");
+
+		// Traditional doom map format just assumes the number of elements from the lump sizes.
+		numvertexes  = virtvertexes->size / sizeof (mapvertex_t);
+		numsectors   = virtsectors->size  / sizeof (mapsector_t);
+		numsides     = virtsidedefs->size / sizeof (mapsidedef_t);
+		numlines     = virtlinedefs->size / sizeof (maplinedef_t);
+		nummapthings = virtthings->size   / (5 * sizeof (INT16));
+	}
+
+	if (numvertexes <= 0)
+		I_Error("Level has no vertices");
+	if (numsectors <= 0)
+		I_Error("Level has no sectors");
+	if (numsides <= 0)
+		I_Error("Level has no sidedefs");
+	if (numlines <= 0)
+		I_Error("Level has no linedefs");
+
+	vertexes  = Z_Calloc(numvertexes * sizeof (*vertexes), PU_LEVEL, NULL);
+	sectors   = Z_Calloc(numsectors * sizeof (*sectors), PU_LEVEL, NULL);
+	sides     = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
+	lines     = Z_Calloc(numlines * sizeof (*lines), PU_LEVEL, NULL);
+	mapthings = Z_Calloc(nummapthings * sizeof (*mapthings), 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;
+
+#ifdef UDMF
+	if (textmap)
+	{
+
+	}
+	else
+#endif
+	{
+		// Strict map data
+		P_LoadVertices(virtvertexes->data);
+		P_LoadSectors(virtsectors->data);
+		P_LoadLinedefs(virtlinedefs->data);
+		P_LoadSidedefs(virtsidedefs->data);
+		P_LoadThings(virtthings->data);
+	}
+
+	R_ClearTextureNumCache(true);
+
+	// set the sky flat num
+	skyflatnum = P_AddLevelFlat(SKYFLATNAME, foundflats);
+
+	// copy table for global usage
+	levelflats = M_Memcpy(Z_Calloc(numlevelflats * sizeof (*levelflats), PU_LEVEL, NULL), foundflats, numlevelflats * sizeof (levelflat_t));
+	free(foundflats);
+
+	// search for animated flats and set up
+	P_SetupLevelFlatAnims();
+
+	// Copy relevant map data for NetArchive purposes.
+	spawnsectors = Z_Calloc(numsectors * sizeof (*sectors), PU_LEVEL, NULL);
+	spawnlines = Z_Calloc(numlines * sizeof (*lines), PU_LEVEL, NULL);
+	spawnsides = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
+
+	memcpy(spawnsectors, sectors, numsectors * sizeof (*sectors));
+	memcpy(spawnlines, lines, numlines * sizeof (*lines));
+	memcpy(spawnsides, sides, numsides * sizeof (*sides));
+}
+
+static inline void P_LoadSubsectors(UINT8 *data)
+{
+	mapsubsector_t *ms = (mapsubsector_t*)data;
+	subsector_t *ss = subsectors;
+	size_t i;
+
+	for (i = 0; i < numsubsectors; i++, ss++, ms++)
+	{
+		ss->sector = NULL;
+		ss->numlines = SHORT(ms->numsegs);
+		ss->firstline = SHORT(ms->firstseg);
+#ifdef FLOORSPLATS
+		ss->splats = NULL;
+#endif
+		ss->validcount = 0;
+	}
+}
+
+static void P_LoadNodes(UINT8 *data)
+{
+	UINT8 j, k;
+	mapnode_t *mn = (mapnode_t*)data;
+	node_t *no = nodes;
+	size_t i;
+
+	for (i = 0; i < numnodes; i++, no++, mn++)
+	{
+		no->x = SHORT(mn->x)<<FRACBITS;
+		no->y = SHORT(mn->y)<<FRACBITS;
+		no->dx = SHORT(mn->dx)<<FRACBITS;
+		no->dy = SHORT(mn->dy)<<FRACBITS;
+		for (j = 0; j < 2; j++)
+		{
+			no->children[j] = SHORT(mn->children[j]);
+			for (k = 0; k < 4; k++)
+				no->bbox[j][k] = SHORT(mn->bbox[j][k])<<FRACBITS;
+		}
+	}
+}
+
+/** Computes the length of a seg in fracunits.
+  *
+  * \param seg Seg to compute length for.
+  * \return Length in fracunits.
+  */
+fixed_t P_SegLength(seg_t *seg)
+{
+	INT64 dx = (seg->v2->x - seg->v1->x)>>1;
+	INT64 dy = (seg->v2->y - seg->v1->y)>>1;
+	return FixedHypot(dx, dy)<<1;
+}
+
+#ifdef HWRENDER
+/** Computes the length of a seg as a float.
+  * This is needed for OpenGL.
+  *
+  * \param seg Seg to compute length for.
+  * \return Length as a float.
+  */
+static inline float P_SegLengthFloat(seg_t *seg)
+{
+	float dx, dy;
+
+	// make a vector (start at origin)
+	dx = FIXED_TO_FLOAT(seg->v2->x - seg->v1->x);
+	dy = FIXED_TO_FLOAT(seg->v2->y - seg->v1->y);
+
+	return (float)hypot(dx, dy);
+}
+#endif
+
+static void P_LoadSegs(UINT8 *data)
+{
+	INT32 linedef, side;
+	mapseg_t *ml = (mapseg_t*)data;
+	seg_t *li = segs;
+	line_t *ldef;
+	size_t i;
+
+	for (i = 0; i < numsegs; i++, li++, ml++)
+	{
+		li->v1 = &vertexes[SHORT(ml->v1)];
+		li->v2 = &vertexes[SHORT(ml->v2)];
+
+		li->length = P_SegLength(li);
+#ifdef HWRENDER
+		if (rendermode == render_opengl)
+		{
+			li->flength = P_SegLengthFloat(li);
+			//Hurdler: 04/12/2000: for now, only used in hardware mode
+			li->lightmaps = NULL; // list of static lightmap for this seg
+		}
+		li->pv1 = li->pv2 = NULL;
+#endif
+
+		li->angle = (SHORT(ml->angle))<<FRACBITS;
+		li->offset = (SHORT(ml->offset))<<FRACBITS;
+		linedef = SHORT(ml->linedef);
+		ldef = &lines[linedef];
+		li->linedef = ldef;
+		li->side = side = SHORT(ml->side);
+		li->sidedef = &sides[ldef->sidenum[side]];
+		li->frontsector = sides[ldef->sidenum[side]].sector;
+		if (ldef->flags & ML_TWOSIDED)
+			li->backsector = sides[ldef->sidenum[side^1]].sector;
+		else
+			li->backsector = 0;
+
+		li->numlights = 0;
+		li->rlights = NULL;
+	}
+}
+
+static void P_LoadMapBSP(const virtres_t *virt)
+{
+	virtlump_t* virtssectors = vres_Find(virt, "SSECTORS");
+	virtlump_t* virtsegs     = vres_Find(virt, "SEGS");
+	virtlump_t* virtnodes    = vres_Find(virt, "NODES");
+
+	numsubsectors = virtssectors->size / sizeof(mapsubsector_t);
+	numnodes      = virtnodes->size    / sizeof(mapnode_t);
+	numsegs       = virtsegs->size     / sizeof(mapseg_t);
+
+	if (numsubsectors <= 0)
+		I_Error("Level has no subsectors (did you forget to run it through a nodesbuilder?)");
+	if (numnodes <= 0)
+		I_Error("Level has no nodes");
+	if (numsegs <= 0)
+		I_Error("Level has no segs");
+
+	subsectors = Z_Calloc(numsubsectors * sizeof(*subsectors), PU_LEVEL, NULL);
+	nodes      = Z_Calloc(numnodes * sizeof(*nodes), PU_LEVEL, NULL);
+	segs       = Z_Calloc(numsegs * sizeof(*segs), PU_LEVEL, NULL);
+
+	// Nodes
+	P_LoadSubsectors(virtssectors->data);
+	P_LoadNodes(virtnodes->data);
+	P_LoadSegs(virtsegs->data);
+}
+
+// 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;
+	}
+}
+
+// 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_LoadBlockMap(UINT8 *data, size_t count)
+{
+	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;
+}
+
 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];
@@ -1681,85 +1787,155 @@ static void P_CreateBlockMap(void)
 		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
+#ifdef POLYOBJECTS
+		// haleyjd 2/22/06: setup polyobject blockmap
+		count = sizeof(*polyblocklinks) * bmapwidth * bmapheight;
+		polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL);
+#endif
+	}
+}
+
+// PK3 version
+// -- Monster Iestyn 09/01/18
+static void P_LoadReject(UINT8 *data, size_t count)
+{
+	if (!count) // zero length, someone probably used ZDBSP
+	{
+		rejectmatrix = NULL;
+		CONS_Debug(DBG_SETUP, "P_LoadReject: 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
+	}
+}
+
+static void P_LoadMapLUT(const virtres_t *virt)
+{
+	virtlump_t* virtblockmap = vres_Find(virt, "BLOCKMAP");
+	virtlump_t* virtreject   = vres_Find(virt, "REJECT");
+
+	// Lookup tables
+	if (virtreject)
+		P_LoadReject(virtreject->data, virtreject->size);
+	else
+		rejectmatrix = NULL;
+
+	if (!(virtblockmap && P_LoadBlockMap(virtblockmap->data, virtblockmap->size)))
+		P_CreateBlockMap();
+}
+
+static void P_ProcessLinedefsWithSidedefs(void)
+{
+	size_t i = numlines;
+	register line_t *ld = lines;
+	for (;i--;ld++)
+	{
+		ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here
+		ld->backsector  = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0;
+
+		// Repeat count for midtexture
+		if ((ld->flags & ML_EFFECT5) && (ld->sidenum[1] != 0xffff)
+			&& !(ld->special >= 300 && ld->special < 500)) // exempt linedef exec specials
+		{
+			sides[ld->sidenum[0]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) >> 12);
+			sides[ld->sidenum[0]].textureoffset = (((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
+			sides[ld->sidenum[1]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[1]].textureoffset >> FRACBITS) >> 12);
+			sides[ld->sidenum[1]].textureoffset = (((unsigned)sides[ld->sidenum[1]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
+		}
+
+		// Compile linedef 'text' from both sidedefs 'text' for appropriate specials.
+		switch(ld->special)
+		{
+		case 331: // Trigger linedef executor: Skin - Continuous
+		case 332: // Trigger linedef executor: Skin - Each time
+		case 333: // Trigger linedef executor: Skin - Once
+		case 443: // Calls a named Lua function
+			if (sides[ld->sidenum[0]].text)
+			{
+				size_t len = strlen(sides[ld->sidenum[0]].text)+1;
+				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
+					len += strlen(sides[ld->sidenum[1]].text);
+				ld->text = Z_Malloc(len, PU_LEVEL, NULL);
+				M_Memcpy(ld->text, sides[ld->sidenum[0]].text, strlen(sides[ld->sidenum[0]].text)+1);
+				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
+					M_Memcpy(ld->text+strlen(ld->text)+1, sides[ld->sidenum[1]].text, strlen(sides[ld->sidenum[1]].text)+1);
+			}
+			break;
+		}
 	}
 }
 
-// Split from P_LoadBlockMap for convenience
-// -- Monster Iestyn 08/01/18
-static void P_ReadBlockMapLump(INT16 *wadblockmaplump, size_t count)
+static void P_CompressSidedefs(void)
 {
+	side_t *newsides;
+	size_t numnewsides = 0;
+	size_t z;
 	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.
+	for (i = 0; i < numsides; i++)
+	{
+		size_t j, k;
+		line_t *ld;
 
-	blockmaplump[0] = SHORT(wadblockmaplump[0]);
-	blockmaplump[1] = SHORT(wadblockmaplump[1]);
-	blockmaplump[2] = (INT32)(SHORT(wadblockmaplump[2])) & 0xffff;
-	blockmaplump[3] = (INT32)(SHORT(wadblockmaplump[3])) & 0xffff;
+		if (!sides[i].sector)
+			continue;
 
-	for (i = 4; i < count; i++)
-	{
-		INT16 t = SHORT(wadblockmaplump[i]);          // killough 3/1/98
-		blockmaplump[i] = t == -1 ? (INT32)-1 : (INT32) t & 0xffff;
-	}
-}
+		for (k = numlines, ld = lines; k--; ld++)
+		{
+			if (ld->sidenum[0] == i)
+				ld->sidenum[0] = (UINT16)numnewsides;
 
-// 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)
-{
-#if 0
-	(void)data;
-	(void)count;
-	(void)lumpname;
-	return false;
-#else
+			if (ld->sidenum[1] == i)
+				ld->sidenum[1] = (UINT16)numnewsides;
+		}
 
-	if (!count || count >= 0x20000)
-		return false;
+		for (j = i + 1; j < numsides; j++)
+		{
+			if (!sides[j].sector)
+				continue;
 
-	//CONS_Printf("Reading blockmap lump for pk3...\n");
+			if (!memcmp(&sides[i], &sides[j], sizeof(side_t)))
+			{
+				// Find the linedefs that belong to this one
+				for (k = numlines, ld = lines; k--; ld++)
+				{
+					if (ld->sidenum[0] == j)
+						ld->sidenum[0] = (UINT16)numnewsides;
 
-	// no need to malloc anything, assume the data is uncompressed for now
-	count /= 2;
-	P_ReadBlockMapLump((INT16 *)data, count);
+					if (ld->sidenum[1] == j)
+						ld->sidenum[1] = (UINT16)numnewsides;
+				}
+				sides[j].sector = NULL; // Flag for deletion
+			}
+		}
+		numnewsides++;
+	}
 
-	bmaporgx = blockmaplump[0]<<FRACBITS;
-	bmaporgy = blockmaplump[1]<<FRACBITS;
-	bmapwidth = blockmaplump[2];
-	bmapheight = blockmaplump[3];
+	// We're loading crap into this block anyhow, so no point in zeroing it out.
+	newsides = Z_Malloc(numnewsides*sizeof(*newsides), PU_LEVEL, NULL);
 
-	// clear out mobj chains
-	count = sizeof (*blocklinks)* bmapwidth*bmapheight;
-	blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
-	blockmap = blockmaplump+4;
+	// Copy the sides to their new block of memory.
+	for (i = 0, z = 0; i < numsides; i++)
+	{
+		if (sides[i].sector)
+			M_Memcpy(&newsides[z++], &sides[i], sizeof(side_t));
+	}
 
-#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
+	CONS_Debug(DBG_SETUP, "P_CompressSidedefs: Old sides is %s, new sides is %s\n", sizeu1(numsides), sizeu1(numnewsides));
+
+	Z_Free(sides);
+	sides = newsides;
+	numsides = numnewsides;
 }
 
 //
-// P_GroupLines
+// P_LinkMapData
 // Builds sector line lists and subsector sector numbers.
 // Finds block bounding boxes for sectors.
 //
-static void P_GroupLines(void)
+static void P_LinkMapData(void)
 {
 	size_t i, j;
 	line_t *li;
@@ -1773,20 +1949,20 @@ static void P_GroupLines(void)
 	for (i = 0; i < numsubsectors; i++, ss++)
 	{
 		if (ss->firstline >= numsegs)
-			CorruptMapError(va("P_GroupLines: ss->firstline invalid "
+			CorruptMapError(va("P_LinkMapData: ss->firstline invalid "
 				"(subsector %s, firstline refers to %d of %s)", sizeu1(i), ss->firstline,
 				sizeu2(numsegs)));
 		seg = &segs[ss->firstline];
 		sidei = (size_t)(seg->sidedef - sides);
 		if (!seg->sidedef)
-			CorruptMapError(va("P_GroupLines: seg->sidedef is NULL "
+			CorruptMapError(va("P_LinkMapData: seg->sidedef is NULL "
 				"(subsector %s, firstline is %d)", sizeu1(i), ss->firstline));
 		if (seg->sidedef - sides < 0 || seg->sidedef - sides > (UINT16)numsides)
-			CorruptMapError(va("P_GroupLines: seg->sidedef refers to sidedef %s of %s "
+			CorruptMapError(va("P_LinkMapData: seg->sidedef refers to sidedef %s of %s "
 				"(subsector %s, firstline is %d)", sizeu1(sidei), sizeu2(numsides),
 				sizeu3(i), ss->firstline));
 		if (!seg->sidedef->sector)
-			CorruptMapError(va("P_GroupLines: seg->sidedef->sector is NULL "
+			CorruptMapError(va("P_LinkMapData: seg->sidedef->sector is NULL "
 				"(subsector %s, firstline is %d, sidedef is %s)", sizeu1(i), ss->firstline,
 				sizeu1(sidei)));
 		ss->sector = seg->sidedef->sector;
@@ -1807,7 +1983,7 @@ static void P_GroupLines(void)
 		if (sector->linecount == 0) // no lines found?
 		{
 			sector->lines = NULL;
-			CONS_Debug(DBG_SETUP, "P_GroupLines: sector %s has no lines\n", sizeu1(i));
+			CONS_Debug(DBG_SETUP, "P_LinkMapData: sector %s has no lines\n", sizeu1(i));
 		}
 		else
 		{
@@ -1850,172 +2026,6 @@ static void P_GroupLines(void)
 	}
 }
 
-// PK3 version
-// -- Monster Iestyn 09/01/18
-static void P_LoadRawReject(UINT8 *data, size_t count)
-{
-	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
-	}
-}
-
-static void P_LoadMapBSP(const virtres_t* virt)
-{
-	virtlump_t* virtssectors = vres_Find(virt, "SSECTORS");
-	virtlump_t* virtsegs     = vres_Find(virt, "SEGS");
-	virtlump_t* virtnodes    = vres_Find(virt, "NODES");
-
-	numsubsectors = virtssectors->size / sizeof(mapsubsector_t);
-	numnodes      = virtnodes->size    / sizeof(mapnode_t);
-	numsegs       = virtsegs->size     / sizeof(mapseg_t);
-
-	if (numsubsectors <= 0)
-		I_Error("Level has no subsectors (did you forget to run it through a nodesbuilder?)");
-	if (numnodes <= 0)
-		I_Error("Level has no nodes");
-	if (numsegs <= 0)
-		I_Error("Level has no segs");
-
-	subsectors = Z_Calloc(numsubsectors * sizeof(*subsectors), PU_LEVEL, NULL);
-	nodes      = Z_Calloc(numnodes * sizeof(*nodes), PU_LEVEL, NULL);
-	segs       = Z_Calloc(numsegs * sizeof(*segs), PU_LEVEL, NULL);
-
-	// Nodes
-	P_LoadRawSubsectors(virtssectors->data);
-	P_LoadRawNodes(virtnodes->data);
-	P_LoadRawSegs(virtsegs->data);
-}
-
-static void P_LoadMapLUT(const virtres_t* virt)
-{
-	virtlump_t* virtblockmap = vres_Find(virt, "BLOCKMAP");
-	virtlump_t* virtreject   = vres_Find(virt, "REJECT");
-
-	// Lookup tables
-	if (virtreject)
-		P_LoadRawReject(virtreject->data, virtreject->size);
-	else
-		rejectmatrix = NULL;
-
-	if (!(virtblockmap && P_LoadRawBlockMap(virtblockmap->data, virtblockmap->size)))
-		P_CreateBlockMap();
-}
-
-static void P_LoadMapData(const virtres_t* virt)
-{
-	virtlump_t* virtvertexes = NULL, * virtsectors = NULL, * virtsidedefs = NULL, * virtlinedefs = NULL, * virtthings = NULL;
-#ifdef UDMF
-	virtlump_t* textmap = vres_Find(virt, "TEXTMAP");
-
-	// Count map data.
-	if (textmap)
-	{
-		nummapthings = 0;
-		numlines = 0;
-		numsides = 0;
-		numvertexes = 0;
-		numsectors = 0;
-
-		// Count how many entries for each type we got in textmap.
-		//TextmapCount(vtextmap->data, vtextmap->size);
-	}
-	else
-#endif
-	{
-		virtthings   = vres_Find(virt, "THINGS");
-		virtvertexes = vres_Find(virt, "VERTEXES");
-		virtsectors  = vres_Find(virt, "SECTORS");
-		virtsidedefs = vres_Find(virt, "SIDEDEFS");
-		virtlinedefs = vres_Find(virt, "LINEDEFS");
-
-		if (!virtthings)
-			I_Error("THINGS lump not found");
-		if (!virtvertexes)
-			I_Error("VERTEXES lump not found");
-		if (!virtsectors)
-			I_Error("SECTORS lump not found");
-		if (!virtsidedefs)
-			I_Error("SIDEDEFS lump not found");
-		if (!virtlinedefs)
-			I_Error("LINEDEFS lump not found");
-
-		// Traditional doom map format just assumes the number of elements from the lump sizes.
-		numvertexes  = virtvertexes->size / sizeof (mapvertex_t);
-		numsectors   = virtsectors->size  / sizeof (mapsector_t);
-		numsides     = virtsidedefs->size / sizeof (mapsidedef_t);
-		numlines     = virtlinedefs->size / sizeof (maplinedef_t);
-		nummapthings = virtthings->size   / (5 * sizeof (INT16));
-	}
-
-	if (numvertexes <= 0)
-		I_Error("Level has no vertices");
-	if (numsectors <= 0)
-		I_Error("Level has no sectors");
-	if (numsides <= 0)
-		I_Error("Level has no sidedefs");
-	if (numlines <= 0)
-		I_Error("Level has no linedefs");
-
-	vertexes  = Z_Calloc(numvertexes * sizeof (*vertexes), PU_LEVEL, NULL);
-	sectors   = Z_Calloc(numsectors * sizeof (*sectors), PU_LEVEL, NULL);
-	sides     = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
-	lines     = Z_Calloc(numlines * sizeof (*lines), PU_LEVEL, NULL);
-	mapthings = Z_Calloc(nummapthings * sizeof (*mapthings), 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;
-
-#ifdef UDMF
-	if (textmap)
-	{
-
-	}
-	else
-#endif
-	{
-		// Strict map data
-		P_LoadRawVertexes(virtvertexes->data);
-		P_LoadRawSectors(virtsectors->data);
-		P_LoadRawLineDefs(virtlinedefs->data);
-		P_SetupLines();
-		P_LoadRawSideDefs2(virtsidedefs->data);
-		P_PrepareRawThings(virtthings->data);
-	}
-
-	R_ClearTextureNumCache(true);
-
-	// set the sky flat num
-	skyflatnum = P_AddLevelFlat(SKYFLATNAME, foundflats);
-
-	// copy table for global usage
-	levelflats = M_Memcpy(Z_Calloc(numlevelflats * sizeof (*levelflats), PU_LEVEL, NULL), foundflats, numlevelflats * sizeof (levelflat_t));
-	free(foundflats);
-
-	// search for animated flats and set up
-	P_SetupLevelFlatAnims();
-
-	// Copy relevant map data for NetArchive purposes.
-	spawnsectors = Z_Calloc(numsectors * sizeof (*sectors), PU_LEVEL, NULL);
-	spawnlines = Z_Calloc(numlines * sizeof (*lines), PU_LEVEL, NULL);
-	spawnsides = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
-
-	memcpy(spawnsectors, sectors, numsectors * sizeof (*sectors));
-	memcpy(spawnlines, lines, numlines * sizeof (*lines));
-	memcpy(spawnsides, sides, numsides * sizeof (*sides));
-}
-
 /** Compute MD5 message digest for bytes read from memory source
   *
   * The resulting message digest number will be written into the 16 bytes
@@ -2025,7 +2035,7 @@ static void P_LoadMapData(const virtres_t* virt)
   * \param resblock resulting MD5 checksum
   * \return 0 if MD5 checksum was made, and is at resblock, 1 if error was found
   */
-static INT32 P_MakeBufferMD5(const char* buffer, size_t len, void* resblock)
+static INT32 P_MakeBufferMD5(const char *buffer, size_t len, void *resblock)
 {
 #ifdef NOMD5
 	(void)buffer;
@@ -2042,7 +2052,7 @@ static INT32 P_MakeBufferMD5(const char* buffer, size_t len, void* resblock)
 #endif
 }
 
-static void P_MakeMapMD5(virtres_t* virt, void* dest)
+static void P_MakeMapMD5(virtres_t *virt, void *dest)
 {
 	unsigned char linemd5[16];
 	unsigned char sectormd5[16];
@@ -2077,50 +2087,19 @@ static void P_LoadMapFromFile(void)
 	P_LoadMapBSP(virt);
 	P_LoadMapLUT(virt);
 
-	P_LoadLineDefs2();
-	P_GroupLines();
+	P_ProcessLinedefsWithSidedefs();
+	if (M_CheckParm("-compress"))
+		P_CompressSidedefs();
+	P_LinkMapData();
 
 	P_MakeMapMD5(virt, &mapmd5);
 
 	vres_Free(virt);
 }
 
-#if 0
-static char *levellumps[] =
-{
-	"label",        // ML_LABEL,    A separator, name, MAPxx
-	"THINGS",       // ML_THINGS,   Enemies, items..
-	"LINEDEFS",     // ML_LINEDEFS, Linedefs, from editing
-	"SIDEDEFS",     // ML_SIDEDEFS, Sidedefs, from editing
-	"VERTEXES",     // ML_VERTEXES, Vertices, edited and BSP splits generated
-	"SEGS",         // ML_SEGS,     Linesegs, from linedefs split by BSP
-	"SSECTORS",     // ML_SSECTORS, Subsectors, list of linesegs
-	"NODES",        // ML_NODES,    BSP nodes
-	"SECTORS",      // ML_SECTORS,  Sectors, from editing
-	"REJECT",       // ML_REJECT,   LUT, sector-sector visibility
-};
-
-/** Checks a lump and returns whether it is a valid start-of-level marker.
-  *
-  * \param lumpnum Lump number to check.
-  * \return True if the lump is a valid level marker, false if not.
-  */
-static inline boolean P_CheckLevel(lumpnum_t lumpnum)
-{
-	UINT16 file, lump;
-	size_t i;
-
-	for (i = ML_THINGS; i <= ML_REJECT; i++)
-	{
-		file = WADFILENUM(lumpnum);
-		lump = LUMPNUM(lumpnum+1);
-		if (file > numwadfiles || lump < LUMPNUM(lumpnum) || lump > wadfiles[file]->numlumps ||
-			memcmp(wadfiles[file]->lumpinfo[lump].name, levellumps[i], 8) != 0)
-		return false;
-	}
-	return true; // all right
-}
-#endif
+//
+// LEVEL INITIALIZATION FUNCTIONS
+//
 
 /** Sets up a sky texture to use for the level.
   * The sky texture is used instead of F_SKY1.
@@ -2250,12 +2229,8 @@ static void P_InitLevelSettings(void)
 		CV_SetValue(&cv_analog2, true);
 }
 
-//
-// P_LoadThingsOnly
-//
-// "Reloads" a level, but only reloads all of the mobjs.
-//
-void P_LoadThingsOnly(void)
+// Respawns all the mapthings and mobjs in the map from the already loaded map data.
+void P_RespawnThings(void)
 {
 	// Search through all the thinkers.
 	thinker_t *think;
diff --git a/src/p_setup.h b/src/p_setup.h
index de791677c2af47ff947af0d6dd032f9ebfc0f866..be3d220d8efc364861bf9ff12803bb6aa701150e 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -96,7 +96,7 @@ void P_SetupLevelSky(INT32 skynum, boolean global);
 #ifdef SCANTHINGS
 void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum);
 #endif
-void P_LoadThingsOnly(void);
+void P_RespawnThings(void);
 boolean P_LoadLevel(boolean fromnetsave);
 boolean P_AddWadFile(const char *wadfilename);
 boolean P_RunSOC(const char *socfilename);
diff --git a/src/p_slopes.c b/src/p_slopes.c
index f35e7d7326d2b670fac532d734fb42758b9e5f08..e66d7ed2189135bf4c2166e7b1d17e16f037cf7e 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -445,10 +445,9 @@ static pslope_t *MakeViaMapthings(INT16 tag1, INT16 tag2, INT16 tag3, UINT8 flag
 			I_Error("MakeViaMapthings: Slope vertex %s (for linedef tag %d) not found!", sizeu1(i), tag1);
 		vx[i].x = mt->x << FRACBITS;
 		vx[i].y = mt->y << FRACBITS;
-		if (mt->extrainfo)
-			vx[i].z = mt->options << FRACBITS;
-		else
-			vx[i].z = (R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS)->sector->floorheight) + ((mt->options >> ZSHIFT) << FRACBITS);
+		vx[i].z = mt->z << FRACBITS;
+		if (!mt->extrainfo)
+			vx[i].z += R_PointInSubsector(vx[i].x, vx[i].y)->sector->floorheight;
 	}
 
 	ReconfigureViaVertexes(ret, vx[0], vx[1], vx[2]);
diff --git a/src/p_spec.c b/src/p_spec.c
index 20f1c8278f518bc26748ffbf957ba76fa3d80cfb..4de488654f23e9aa0eecd685925d20bcb39ff007 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -6433,7 +6433,6 @@ void P_SpawnSpecials(boolean fromnetsave)
 	INT32 j;
 	thinkerlist_t *secthinkers;
 	thinker_t *th;
-	virtres_t* virt = NULL;
 	// This used to be used, and *should* be used in the future,
 	// but currently isn't.
 	(void)fromnetsave;
@@ -7179,38 +7178,14 @@ void P_SpawnSpecials(boolean fromnetsave)
 					EV_AddLaserThinker(&sectors[s], &sectors[sec], lines + i, secthinkers);
 				break;
 
-			case 259: // Make-Your-Own FOF!
+			case 259: // Custom FOF
 				if (lines[i].sidenum[1] != 0xffff)
 				{
-					UINT8 *data;
-					UINT16 b;
-
-					if (!virt)
-						virt = vres_GetMap(lastloadedmaplumpnum);
-
-					data = (UINT8*) vres_Find(virt, "SIDEDEFS")->data;
-
-					for (b = 0; b < (INT16)numsides; b++)
-					{
-						register mapsidedef_t *msd = (mapsidedef_t *)data + b;
-
-						if (b == lines[i].sidenum[1])
-						{
-							if ((msd->toptexture[0] >= '0' && msd->toptexture[0] <= '9')
-								|| (msd->toptexture[0] >= 'A' && msd->toptexture[0] <= 'F'))
-							{
-								ffloortype_e FOF_Flags = axtoi(msd->toptexture);
-
-								P_AddFakeFloorsByLine(i, FOF_Flags, secthinkers);
-								break;
-							}
-							else
-								I_Error("Make-Your-Own-FOF (tag %d) needs a value in the linedef's second side upper texture field.", lines[i].tag);
-						}
-					}
+					ffloortype_e fofflags = sides[lines[i].sidenum[1]].toptexture;
+					P_AddFakeFloorsByLine(i, fofflags, secthinkers);
 				}
 				else
-					I_Error("Make-Your-Own FOF (tag %d) found without a 2nd linedef side!", lines[i].tag);
+					I_Error("Custom FOF (tag %d) found without a linedef back side!", lines[i].tag);
 				break;
 
 			case 300: // Linedef executor (combines with sector special 974/975) and commands
@@ -7433,9 +7408,6 @@ void P_SpawnSpecials(boolean fromnetsave)
 		}
 	}
 
-	if (virt)
-		vres_Free(virt);
-
 	// Allocate each list
 	for (i = 0; i < numsectors; i++)
 		if(secthinkers[i].thinkers)
diff --git a/src/r_defs.h b/src/r_defs.h
index 353dc572b6e6422337869b1871aa7ca674f05273..4cb02f4f5c5b05d6ba3ce0f97d3e0eda3535cdc4 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -436,14 +436,10 @@ typedef struct line_s
 	polyobj_t *polyobj; // Belongs to a polyobject?
 #endif
 
-	char *text; // a concatination of all front and back texture names, for linedef specials that require a string.
+	char *text; // a concatenation of all front and back texture names, for linedef specials that require a string.
 	INT16 callcount; // no. of calls left before triggering, for the "X calls" linedef specials, defaults to 0
 } line_t;
 
-//
-// The SideDef.
-//
-
 typedef struct
 {
 	// add this to the calculated texture column
@@ -456,13 +452,16 @@ typedef struct
 	// We do not maintain names here.
 	INT32 toptexture, bottomtexture, midtexture;
 
-	// Sector the SideDef is facing.
+	// Linedef the sidedef belongs to
+	line_t *line;
+
+	// Sector the sidedef is facing.
 	sector_t *sector;
 
 	INT16 special; // the special of the linedef this side belongs to
 	INT16 repeatcnt; // # of times to repeat midtexture
 
-	char *text; // a concatination of all top, bottom, and mid texture names, for linedef specials that require a string.
+	char *text; // a concatenation of all top, bottom, and mid texture names, for linedef specials that require a string.
 
 	extracolormap_t *colormap_data; // storage for colormaps; not applied to sectors.
 } side_t;