diff --git a/src/console.c b/src/console.c
index 751a6e5cad06a57a1c3d3e7f3653dac13e2e2964..93b9fb54d022d71b0917c6a4cd351d2acda4d1e1 100644
--- a/src/console.c
+++ b/src/console.c
@@ -1780,10 +1780,10 @@ static void CON_DrawBackpic(void)
 	// then fill the sides with a solid color.
 	if (x > 0)
 	{
-		column_t *column = (column_t *)((UINT8 *)(con_backpic->columns) + (con_backpic->columnofs[0]));
-		if (!column->topdelta)
+		column_t *column = &con_backpic->columns[0];
+		if (column->num_posts && !column->posts[0].topdelta)
 		{
-			UINT8 *source = (UINT8 *)(column) + 3;
+			UINT8 *source = column->pixels;
 			INT32 color = (source[0] | V_NOSCALESTART);
 			// left side
 			V_DrawFill(0, 0, x, con_curlines, color);
diff --git a/src/doomdef.h b/src/doomdef.h
index 4db1b87bdbc6f651df23ffc2cdb97533b8415469..4c843f9e2f07ad58439d2dcd3200726e6d62fa4d 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -733,9 +733,6 @@ extern int
 #define NO_PNG_LUMPS
 #endif
 
-/// Render flats on walls
-#define WALLFLATS
-
 /// Maintain compatibility with older 2.2 demos
 #define OLD22DEMOCOMPAT
 
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index d6245df6490da4f1cfb2bc11271c8d798e7ba980..58e16d62361a9ca3f87479ea273343c7c36065bc 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -54,7 +54,6 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm
 	fixed_t yfrac, position, count;
 	UINT8 *dest;
 	const UINT8 *source;
-	INT32 topdelta, prevdelta = -1;
 	INT32 originy = 0;
 
 	// for writing a pixel to dest
@@ -68,18 +67,14 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm
 	if (originPatch) // originPatch can be NULL here, unlike in the software version
 		originy = originPatch->originy;
 
-	while (patchcol->topdelta != 0xff)
+	for (unsigned i = 0; i < patchcol->num_posts; i++)
 	{
-		topdelta = patchcol->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		source = (const UINT8 *)patchcol + 3;
-		count  = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
-		position = originy + topdelta;
+		post_t *post = &patchcol->posts[i];
+		source = patchcol->pixels + post->data_offset;
+		count  = ((post->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
+		position = originy + post->topdelta;
 
 		yfrac = 0;
-		//yfracstep = (patchcol->length << FRACBITS) / count;
 		if (position < 0)
 		{
 			yfrac = -position<<FRACBITS;
@@ -106,53 +101,50 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm
 			if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX))
 				alpha = 0x00;
 
-			//Hurdler: 25/04/2000: now support colormap in hardware mode
 			if (mipmap->colormap)
 				texel = mipmap->colormap->data[texel];
 
-			// hope compiler will get this switch out of the loops (dreams...)
-			// gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)
-			// Alam: SRB2 uses Mingw, HUGS
 			switch (bpp)
 			{
-				case 2 : // uhhhhhhhh..........
-						 if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-							 texel = ASTBlendPaletteIndexes(*(dest+1), texel, originPatch->style, originPatch->alpha);
-						 texelu16 = (UINT16)((alpha<<8) | texel);
-						 memcpy(dest, &texelu16, sizeof(UINT16));
-						 break;
-				case 3 : colortemp = V_GetColor(texel);
-						 if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-						 {
-							 RGBA_t rgbatexel;
-							 rgbatexel.rgba = *(UINT32 *)dest;
-							 colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
-						 }
-						 memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
-						 break;
-				case 4 : colortemp = V_GetColor(texel);
-						 colortemp.s.alpha = alpha;
-						 if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-						 {
-							 RGBA_t rgbatexel;
-							 rgbatexel.rgba = *(UINT32 *)dest;
-							 colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
-						 }
-						 memcpy(dest, &colortemp, sizeof(RGBA_t));
-						 break;
+				case 2:
+					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
+						texel = ASTBlendPaletteIndexes(*(dest+1), texel, originPatch->style, originPatch->alpha);
+					texelu16 = (UINT16)((alpha<<8) | texel);
+					memcpy(dest, &texelu16, sizeof(UINT16));
+					break;
+				case 3:
+					colortemp = V_GetColor(texel);
+					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
+					{
+						RGBA_t rgbatexel;
+						rgbatexel.rgba = *(UINT32 *)dest;
+						colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
+					}
+					memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
+					break;
+				case 4:
+					colortemp = V_GetColor(texel);
+					colortemp.s.alpha = alpha;
+					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
+					{
+						RGBA_t rgbatexel;
+						rgbatexel.rgba = *(UINT32 *)dest;
+						colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
+					}
+					memcpy(dest, &colortemp, sizeof(RGBA_t));
+					break;
 				// default is 1
 				default:
-						 if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-							 *dest = ASTBlendPaletteIndexes(*dest, texel, originPatch->style, originPatch->alpha);
-						 else
-							 *dest = texel;
-						 break;
+					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
+						*dest = ASTBlendPaletteIndexes(*dest, texel, originPatch->style, originPatch->alpha);
+					else
+						*dest = texel;
+					break;
 			}
 
 			dest += blockmodulo;
 			yfrac += yfracstep;
 		}
-		patchcol = (const column_t *)((const UINT8 *)patchcol + patchcol->length + 4);
 	}
 }
 
@@ -165,7 +157,7 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block,
 	fixed_t yfrac, position, count;
 	UINT8 *dest;
 	const UINT8 *source;
-	INT32 topdelta, prevdelta = -1;
+	INT32 topdelta;
 	INT32 originy = 0;
 
 	// for writing a pixel to dest
@@ -177,18 +169,15 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block,
 	if (originPatch) // originPatch can be NULL here, unlike in the software version
 		originy = originPatch->originy;
 
-	while (patchcol->topdelta != 0xff)
+	for (unsigned i = 0; i < patchcol->num_posts; i++)
 	{
-		topdelta = patchcol->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		topdelta = patchheight-patchcol->length-topdelta;
-		source = (const UINT8 *)patchcol + 3;
-		count  = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
+		post_t *post = &patchcol->posts[i];
+		source = patchcol->pixels + post->data_offset;
+		topdelta = patchheight-post->length-post->topdelta;
+		count  = ((post->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
 		position = originy + topdelta;
 
-		yfrac = (patchcol->length-1) << FRACBITS;
+		yfrac = (post->length-1) << FRACBITS;
 
 		if (position < 0)
 		{
@@ -216,57 +205,53 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block,
 			if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX))
 				alpha = 0x00;
 
-			//Hurdler: 25/04/2000: now support colormap in hardware mode
 			if (mipmap->colormap)
 				texel = mipmap->colormap->data[texel];
 
-			// hope compiler will get this switch out of the loops (dreams...)
-			// gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)
-			// Alam: SRB2 uses Mingw, HUGS
 			switch (bpp)
 			{
-				case 2 : // uhhhhhhhh..........
-						 if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-							 texel = ASTBlendPaletteIndexes(*(dest+1), texel, originPatch->style, originPatch->alpha);
-						 texelu16 = (UINT16)((alpha<<8) | texel);
-						 memcpy(dest, &texelu16, sizeof(UINT16));
-						 break;
-				case 3 : colortemp = V_GetColor(texel);
-						 if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-						 {
-							 RGBA_t rgbatexel;
-							 rgbatexel.rgba = *(UINT32 *)dest;
-							 colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
-						 }
-						 memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
-						 break;
-				case 4 : colortemp = V_GetColor(texel);
-						 colortemp.s.alpha = alpha;
-						 if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-						 {
-							 RGBA_t rgbatexel;
-							 rgbatexel.rgba = *(UINT32 *)dest;
-							 colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
-						 }
-						 memcpy(dest, &colortemp, sizeof(RGBA_t));
-						 break;
+				case 2:
+					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
+						texel = ASTBlendPaletteIndexes(*(dest+1), texel, originPatch->style, originPatch->alpha);
+					texelu16 = (UINT16)((alpha<<8) | texel);
+					memcpy(dest, &texelu16, sizeof(UINT16));
+					break;
+				case 3:
+					colortemp = V_GetColor(texel);
+					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
+					{
+						RGBA_t rgbatexel;
+						rgbatexel.rgba = *(UINT32 *)dest;
+						colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
+					}
+					memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
+					break;
+				case 4:
+					colortemp = V_GetColor(texel);
+					colortemp.s.alpha = alpha;
+					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
+					{
+						RGBA_t rgbatexel;
+						rgbatexel.rgba = *(UINT32 *)dest;
+						colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
+					}
+					memcpy(dest, &colortemp, sizeof(RGBA_t));
+					break;
 				// default is 1
 				default:
-						 if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-							 *dest = ASTBlendPaletteIndexes(*dest, texel, originPatch->style, originPatch->alpha);
-						 else
-							 *dest = texel;
-						 break;
+					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
+						*dest = ASTBlendPaletteIndexes(*dest, texel, originPatch->style, originPatch->alpha);
+					else
+						*dest = texel;
+					break;
 			}
 
 			dest += blockmodulo;
 			yfrac -= yfracstep;
 		}
-		patchcol = (const column_t *)((const UINT8 *)patchcol + patchcol->length + 4);
 	}
 }
 
-
 // Simplified patch caching function
 // for use by sprites and other patches that are not part of a wall texture
 // no alpha or flipping should be present since we do not want non-texture graphics to have them
@@ -307,7 +292,7 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
 	// Draw each column to the block cache
 	for (; ncols--; block += bpp, xfrac += xfracstep)
 	{
-		patchcol = (const column_t *)((const UINT8 *)realpatch->columns + (realpatch->columnofs[xfrac>>FRACBITS]));
+		patchcol = &realpatch->columns[xfrac>>FRACBITS];
 
 		HWR_DrawColumnInCache(patchcol, block, mipmap,
 								pblockheight, blockmodulo,
@@ -321,7 +306,7 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
 static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
 	INT32 pblockwidth, INT32 pblockheight,
 	texture_t *texture, texpatch_t *patch,
-	const softwarepatch_t *realpatch)
+	const patch_t *realpatch)
 {
 	INT32 x, x1, x2;
 	INT32 col, ncols;
@@ -345,8 +330,8 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
 	ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawFlippedColumnInCache : HWR_DrawColumnInCache;
 
 	x1 = patch->originx;
-	width = SHORT(realpatch->width);
-	height = SHORT(realpatch->height);
+	width = realpatch->width;
+	height = realpatch->height;
 	x2 = x1 + width;
 
 	if (x1 > texture->width || x2 < 0)
@@ -372,14 +357,6 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
 	col = x * pblockwidth / texture->width;
 	ncols = ((x2 - x) * pblockwidth) / texture->width;
 
-/*
-	CONS_Debug(DBG_RENDER, "patch %dx%d texture %dx%d block %dx%d\n",
-															width, height,
-															texture->width,          texture->height,
-															pblockwidth,             pblockheight);
-	CONS_Debug(DBG_RENDER, "      col %d ncols %d x %d\n", col, ncols, x);
-*/
-
 	// source advance
 	xfrac = 0;
 	if (x1 < 0)
@@ -401,9 +378,9 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
 	for (block += col*bpp; ncols--; block += bpp, xfrac += xfracstep)
 	{
 		if (patch->flip & 1)
-			patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[(width-1)-(xfrac>>FRACBITS)]));
+			patchcol = &realpatch->columns[(width-1)-(xfrac>>FRACBITS)];
 		else
-			patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[xfrac>>FRACBITS]));
+			patchcol = &realpatch->columns[xfrac>>FRACBITS];
 
 		ColumnDrawerPointer(patchcol, block, mipmap,
 								pblockheight, blockmodulo,
@@ -447,8 +424,6 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex)
 	UINT8 *block;
 	texture_t *texture;
 	texpatch_t *patch;
-	softwarepatch_t *realpatch;
-	UINT8 *pdata;
 	INT32 blockwidth, blockheight, blocksize;
 
 	INT32 i;
@@ -500,30 +475,35 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex)
 	// Composite the columns together.
 	for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
 	{
-		boolean dealloc = true;
-		size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump);
-		pdata = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
-		realpatch = (softwarepatch_t *)pdata;
+		UINT16 wadnum = patch->wad;
+		lumpnum_t lumpnum = patch->lump;
+		UINT8 *pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
+		patch_t *realpatch = NULL;
+		boolean free_patch = true;
 
 #ifndef NO_PNG_LUMPS
-		if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength))
-			realpatch = (softwarepatch_t *)Picture_PNGConvert(pdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
+		size_t lumplength = W_LumpLengthPwad(wadnum, lumpnum);
+		if (Picture_IsLumpPNG(pdata, lumplength))
+			realpatch = (patch_t *)Picture_PNGConvert(pdata, PICFMT_PATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
 		else
 #endif
-#ifdef WALLFLATS
 		if (texture->type == TEXTURETYPE_FLAT)
-			realpatch = (softwarepatch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_DOOMPATCH, 0, NULL, texture->width, texture->height, 0, 0, 0);
+			realpatch = (patch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0);
 		else
-#endif
 		{
-			(void)lumplength;
-			dealloc = false;
+			// If this patch has already been loaded, we just use it from the cache.
+			realpatch = W_GetCachedPatchNumPwad(wadnum, lumpnum);
+			free_patch = false;
+
+			// Otherwise, we load it here.
+			if (realpatch == NULL)
+				realpatch = W_CachePatchNumPwad(wadnum, lumpnum, PU_PATCH);
 		}
 
 		HWR_DrawTexturePatchInCache(&grtex->mipmap, blockwidth, blockheight, texture, patch, realpatch);
 
-		if (dealloc)
-			Z_Unlock(realpatch);
+		if (free_patch)
+			Patch_Free(realpatch);
 	}
 	//Hurdler: not efficient at all but I don't remember exactly how HWR_DrawPatchInCache works :(
 	if (format2bpp(grtex->mipmap.format)==4)
@@ -753,7 +733,7 @@ void HWR_LoadMapTextures(size_t pnumtextures)
 	gl_textures = calloc(gl_numtextures, sizeof(*gl_textures));
 	gl_flats = calloc(gl_numtextures, sizeof(*gl_flats));
 
-	if ((gl_textures == NULL) || (gl_flats == NULL))
+	if (gl_textures == NULL || gl_flats == NULL)
 		I_Error("HWR_LoadMapTextures: ran out of memory for OpenGL textures");
 
 	gl_maptexturesloaded = true;
@@ -818,26 +798,6 @@ static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum)
 	W_ReadLump(flatlumpnum, Z_Malloc(size, PU_HWRCACHE, &grMipmap->data));
 }
 
-static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum)
-{
-	UINT8 *flat;
-	UINT8 *converted;
-	size_t size;
-
-	// setup the texture info
-	grMipmap->format = GL_TEXFMT_P_8;
-	grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED;
-
-	grMipmap->width  = (UINT16)textures[texturenum]->width;
-	grMipmap->height = (UINT16)textures[texturenum]->height;
-	size = (grMipmap->width * grMipmap->height);
-
-	flat = Z_Malloc(size, PU_HWRCACHE, &grMipmap->data);
-	converted = (UINT8 *)Picture_TextureToFlat(texturenum);
-	M_Memcpy(flat, converted, size);
-	Z_Free(converted);
-}
-
 // Download a Doom 'flat' to the hardware cache and make it ready for use
 void HWR_GetRawFlat(lumpnum_t flatlumpnum)
 {
@@ -863,96 +823,40 @@ void HWR_GetRawFlat(lumpnum_t flatlumpnum)
 
 void HWR_GetLevelFlat(levelflat_t *levelflat)
 {
-	// Who knows?
-	if (levelflat == NULL)
-		return;
-
-	if (levelflat->type == LEVELFLAT_FLAT)
-		HWR_GetRawFlat(levelflat->u.flat.lumpnum);
-	else if (levelflat->type == LEVELFLAT_TEXTURE)
+	if (levelflat->type == LEVELFLAT_NONE)
 	{
-		GLMapTexture_t *grtex;
-		INT32 texturenum = levelflat->u.texture.num;
-#ifdef PARANOIA
-		if ((unsigned)texturenum >= gl_numtextures)
-			I_Error("HWR_GetLevelFlat: texturenum >= numtextures");
-#endif
-
-		// Who knows?
-		if (texturenum == 0 || texturenum == -1)
-			return;
-
-		// Every texture in memory, stored as a 8-bit flat. Wow!
-		grtex = &gl_flats[texturenum];
-
-		// Generate flat if missing from the cache
-		if (!grtex->mipmap.data && !grtex->mipmap.downloaded)
-			HWR_CacheTextureAsFlat(&grtex->mipmap, texturenum);
-
-		// If hardware does not have the texture, then call pfnSetTexture to upload it
-		if (!grtex->mipmap.downloaded)
-			HWD.pfnSetTexture(&grtex->mipmap);
-		HWR_SetCurrentTexture(&grtex->mipmap);
-
-		// The system-memory data can be purged now.
-		Z_ChangeTag(grtex->mipmap.data, PU_HWRCACHE_UNLOCKED);
+		HWR_SetCurrentTexture(NULL);
+		return;
 	}
-	else if (levelflat->type == LEVELFLAT_PATCH)
+
+	INT32 texturenum = texturetranslation[levelflat->texture_id];
+	if (texturenum <= 0)
 	{
-		patch_t *patch = W_CachePatchNum(levelflat->u.flat.lumpnum, PU_CACHE);
-		levelflat->width = (UINT16)(patch->width);
-		levelflat->height = (UINT16)(patch->height);
-		HWR_GetPatch(patch);
+		HWR_SetCurrentTexture(NULL);
+		return;
 	}
-#ifndef NO_PNG_LUMPS
-	else if (levelflat->type == LEVELFLAT_PNG)
-	{
-		GLMipmap_t *mipmap = levelflat->mipmap;
-
-		// Cache the picture.
-		if (!levelflat->mippic)
-		{
-			INT32 pngwidth = 0, pngheight = 0;
-			void *pic = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0);
 
-			Z_ChangeTag(pic, PU_LEVEL);
-			Z_SetUser(pic, &levelflat->mippic);
+	GLMapTexture_t *grtex = &gl_flats[texturenum];
+	GLMipmap_t *grMipmap = &grtex->mipmap;
 
-			levelflat->width = (UINT16)pngwidth;
-			levelflat->height = (UINT16)pngheight;
-		}
-
-		// Make the mipmap.
-		if (mipmap == NULL)
-		{
-			mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_STATIC, NULL);
-			mipmap->format = GL_TEXFMT_P_8;
-			mipmap->flags = TF_WRAPXY|TF_CHROMAKEYED;
-			levelflat->mipmap = mipmap;
-		}
+	if (!grMipmap->data && !grMipmap->downloaded)
+	{
+		grMipmap->format = GL_TEXFMT_P_8;
+		grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED;
 
-		if (!mipmap->data && !mipmap->downloaded)
-		{
-			UINT8 *flat;
-			size_t size;
+		grMipmap->width  = (UINT16)textures[texturenum]->width;
+		grMipmap->height = (UINT16)textures[texturenum]->height;
 
-			if (levelflat->mippic == NULL)
-				I_Error("HWR_GetLevelFlat: levelflat->mippic == NULL");
+		size_t size = grMipmap->width * grMipmap->height;
+		memcpy(Z_Malloc(size, PU_HWRCACHE, &grMipmap->data), R_GetFlatForTexture(texturenum), size);
+	}
 
-			mipmap->width = levelflat->width;
-			mipmap->height = levelflat->height;
+	if (!grMipmap->downloaded)
+		HWD.pfnSetTexture(&grtex->mipmap);
 
-			size = (mipmap->width * mipmap->height);
-			flat = Z_Malloc(size, PU_LEVEL, &mipmap->data);
-			M_Memcpy(flat, levelflat->mippic, size);
-		}
+	HWR_SetCurrentTexture(&grtex->mipmap);
 
-		// Tell the hardware driver to bind the current texture to the flat's mipmap
-		HWR_SetCurrentTexture(mipmap);
-	}
-#endif
-	else // set no texture
-		HWR_SetCurrentTexture(NULL);
+	Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED);
 }
 
 // --------------------+
@@ -1066,145 +970,13 @@ void HWR_UnlockCachedPatch(GLPatch_t *gpatch)
 	Z_ChangeTag(gpatch->mipmap->data, PU_HWRCACHE_UNLOCKED);
 }
 
-static const INT32 picmode2GR[] =
-{
-	GL_TEXFMT_P_8,                // PALETTE
-	0,                            // INTENSITY          (unsupported yet)
-	GL_TEXFMT_ALPHA_INTENSITY_88, // INTENSITY_ALPHA    (corona use this)
-	0,                            // RGB24              (unsupported yet)
-	GL_TEXFMT_RGBA,               // RGBA32             (opengl only)
-};
-
-static void HWR_DrawPicInCache(UINT8 *block, INT32 pblockwidth, INT32 pblockheight,
-	INT32 blockmodulo, pic_t *pic, INT32 bpp)
-{
-	INT32 i,j;
-	fixed_t posx, posy, stepx, stepy;
-	UINT8 *dest, *src, texel;
-	UINT16 texelu16;
-	INT32 picbpp;
-	RGBA_t col;
-
-	stepy = ((INT32)SHORT(pic->height)<<FRACBITS)/pblockheight;
-	stepx = ((INT32)SHORT(pic->width)<<FRACBITS)/pblockwidth;
-	picbpp = format2bpp(picmode2GR[pic->mode]);
-	posy = 0;
-	for (j = 0; j < pblockheight; j++)
-	{
-		posx = 0;
-		dest = &block[j*blockmodulo];
-		src = &pic->data[(posy>>FRACBITS)*SHORT(pic->width)*picbpp];
-		for (i = 0; i < pblockwidth;i++)
-		{
-			switch (pic->mode)
-			{ // source bpp
-				case PALETTE :
-					texel = src[(posx+FRACUNIT/2)>>FRACBITS];
-					switch (bpp)
-					{ // destination bpp
-						case 1 :
-							*dest++ = texel; break;
-						case 2 :
-							texelu16 = (UINT16)(texel | 0xff00);
-							memcpy(dest, &texelu16, sizeof(UINT16));
-							dest += sizeof(UINT16);
-							break;
-						case 3 :
-							col = V_GetColor(texel);
-							memcpy(dest, &col, sizeof(RGBA_t)-sizeof(UINT8));
-							dest += sizeof(RGBA_t)-sizeof(UINT8);
-							break;
-						case 4 :
-							memcpy(dest, &V_GetColor(texel), sizeof(RGBA_t));
-							dest += sizeof(RGBA_t);
-							break;
-					}
-					break;
-				case INTENSITY :
-					*dest++ = src[(posx+FRACUNIT/2)>>FRACBITS];
-					break;
-				case INTENSITY_ALPHA : // assume dest bpp = 2
-					memcpy(dest, src + ((posx+FRACUNIT/2)>>FRACBITS)*sizeof(UINT16), sizeof(UINT16));
-					dest += sizeof(UINT16);
-					break;
-				case RGB24 :
-					break;  // not supported yet
-				case RGBA32 : // assume dest bpp = 4
-					dest += sizeof(UINT32);
-					memcpy(dest, src + ((posx+FRACUNIT/2)>>FRACBITS)*sizeof(UINT32), sizeof(UINT32));
-					break;
-			}
-			posx += stepx;
-		}
-		posy += stepy;
-	}
-}
-
-// -----------------+
-// HWR_GetPic       : Download a Doom pic (raw row encoded with no 'holes')
-// Returns          :
-// -----------------+
-patch_t *HWR_GetPic(lumpnum_t lumpnum)
-{
-	patch_t *patch = HWR_GetCachedGLPatch(lumpnum);
-	GLPatch_t *grPatch = (GLPatch_t *)(patch->hardware);
-
-	if (!grPatch->mipmap->downloaded && !grPatch->mipmap->data)
-	{
-		pic_t *pic;
-		UINT8 *block;
-		size_t len;
-
-		pic = W_CacheLumpNum(lumpnum, PU_CACHE);
-		patch->width = SHORT(pic->width);
-		patch->height = SHORT(pic->height);
-		len = W_LumpLength(lumpnum) - sizeof (pic_t);
-
-		grPatch->mipmap->width = (UINT16)patch->width;
-		grPatch->mipmap->height = (UINT16)patch->height;
-
-		if (pic->mode == PALETTE)
-			grPatch->mipmap->format = textureformat; // can be set by driver
-		else
-			grPatch->mipmap->format = picmode2GR[pic->mode];
-
-		Z_Free(grPatch->mipmap->data);
-
-		// allocate block
-		block = MakeBlock(grPatch->mipmap);
-
-		if (patch->width  == SHORT(pic->width) &&
-			patch->height == SHORT(pic->height) &&
-			format2bpp(grPatch->mipmap->format) == format2bpp(picmode2GR[pic->mode]))
-		{
-			// no conversion needed
-			M_Memcpy(grPatch->mipmap->data, pic->data,len);
-		}
-		else
-			HWR_DrawPicInCache(block, SHORT(pic->width), SHORT(pic->height),
-			                   SHORT(pic->width)*format2bpp(grPatch->mipmap->format),
-			                   pic,
-			                   format2bpp(grPatch->mipmap->format));
-
-		Z_Unlock(pic);
-		Z_ChangeTag(block, PU_HWRCACHE_UNLOCKED);
-
-		grPatch->mipmap->flags = 0;
-		grPatch->max_s = grPatch->max_t = 1.0f;
-	}
-	HWD.pfnSetTexture(grPatch->mipmap);
-	//CONS_Debug(DBG_RENDER, "picloaded at %x as texture %d\n",grPatch->mipmap->data, grPatch->mipmap->downloaded);
-
-	return patch;
-}
-
 patch_t *HWR_GetCachedGLPatchPwad(UINT16 wadnum, UINT16 lumpnum)
 {
 	lumpcache_t *lumpcache = wadfiles[wadnum]->patchcache;
 	if (!lumpcache[lumpnum])
 	{
-		void *ptr = Z_Calloc(sizeof(patch_t), PU_PATCH, &lumpcache[lumpnum]);
-		Patch_Create(NULL, 0, ptr);
+		void *ptr = Patch_Create(0, 0);
+		Z_SetUser(ptr, &lumpcache[lumpnum]);
 		Patch_AllocateHardwarePatch(ptr);
 	}
 	return (patch_t *)(lumpcache[lumpnum]);
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index bc473c1f8de04b8c2a1fcbfa3a46a8272592a28f..ba1f339d0fe9af16d7eee7ab0f73d7558de0936e 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -127,6 +127,7 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 	float cy = FIXED_TO_FLOAT(y);
 	UINT8 alphalevel = ((option & V_ALPHAMASK) >> V_ALPHASHIFT);
 	UINT8 blendmode = ((option & V_BLENDMASK) >> V_BLENDSHIFT);
+	UINT8 opacity = 0xFF;
 	GLPatch_t *hwrPatch;
 
 //  3--2
@@ -145,6 +146,14 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 
 	hwrPatch = ((GLPatch_t *)gpatch->hardware);
 
+	if (alphalevel)
+	{
+		if (alphalevel == 10) opacity = softwaretranstogl_lo[st_translucency]; // V_HUDTRANSHALF
+		else if (alphalevel == 11) opacity = softwaretranstogl[st_translucency]; // V_HUDTRANS
+		else if (alphalevel == 12) opacity = softwaretranstogl_hi[st_translucency]; // V_HUDTRANSDOUBLE
+		else opacity = softwaretranstogl[10-alphalevel];
+	}
+
 	dup = (float)vid.dup;
 
 	switch (option & V_SCALEPATCHMASK)
@@ -261,13 +270,13 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 			// if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT)
 			// cx and cy are possibly *slightly* off from float maths
 			// This is done before here compared to software because we directly alter cx and cy to centre
-			if (cx >= -0.1f && cx <= 0.1f && gpatch->width == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && gpatch->height == BASEVIDHEIGHT)
+			if (opacity == 0xFF && cx >= -0.1f && cx <= 0.1f && gpatch->width == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && gpatch->height == BASEVIDHEIGHT)
 			{
-				const column_t *column = (const column_t *)((const UINT8 *)(gpatch->columns) + (gpatch->columnofs[0]));
-				if (!column->topdelta)
+				const column_t *column = &gpatch->columns[0];
+				if (column->num_posts && !column->posts[0].topdelta)
 				{
-					const UINT8 *source = (const UINT8 *)(column) + 3;
-					HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0]));
+					const UINT8 *source = column->pixels;
+					HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, source[0]);
 				}
 			}
 			// centre screen
@@ -345,11 +354,7 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 	{
 		FSurfaceInfo Surf;
 		Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
-
-		if (alphalevel == 10) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency]; // V_HUDTRANSHALF
-		else if (alphalevel == 11) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency]; // V_HUDTRANS
-		else if (alphalevel == 12) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency]; // V_HUDTRANSDOUBLE
-		else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
+		Surf.PolyColor.s.alpha = opacity;
 		flags |= PF_Modulated;
 		HWD.pfnDrawPolygon(&Surf, v, 4, flags);
 	}
@@ -648,46 +653,10 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 		HWD.pfnDrawPolygon(NULL, v, 4, flags);
 }
 
-void HWR_DrawPic(INT32 x, INT32 y, lumpnum_t lumpnum)
-{
-	FOutVector      v[4];
-	const patch_t    *patch;
-
-	// make pic ready in hardware cache
-	patch = HWR_GetPic(lumpnum);
-
-//  3--2
-//  | /|
-//  |/ |
-//  0--1
-
-	v[0].x = v[3].x = 2.0f * (float)x/vid.width - 1;
-	v[2].x = v[1].x = 2.0f * (float)(x + patch->width*FIXED_TO_FLOAT(vid.fdup))/vid.width - 1;
-	v[0].y = v[1].y = 1.0f - 2.0f * (float)y/vid.height;
-	v[2].y = v[3].y = 1.0f - 2.0f * (float)(y + patch->height*FIXED_TO_FLOAT(vid.fdup))/vid.height;
-
-	v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
-
-	v[0].s = v[3].s = 0;
-	v[2].s = v[1].s = ((GLPatch_t *)patch->hardware)->max_s;
-	v[0].t = v[1].t = 0;
-	v[2].t = v[3].t = ((GLPatch_t *)patch->hardware)->max_t;
-
-
-	//Hurdler: Boris, the same comment as above... but maybe for pics
-	// it not a problem since they don't have any transparent pixel
-	// if I'm right !?
-	// But then, the question is: why not 0 instead of PF_Masked ?
-	// or maybe PF_Environment ??? (like what I said above)
-	// BP: PF_Environment don't change anything ! and 0 is undifined
-	HWD.pfnDrawPolygon(NULL, v, 4, PF_Translucent | PF_NoDepthTest);
-}
-
 // ==========================================================================
 //                                                            V_VIDEO.C STUFF
 // ==========================================================================
 
-
 // --------------------------------------------------------------------------
 // Fills a box of pixels using a flat texture as a pattern
 // --------------------------------------------------------------------------
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 828f247674b4a85aae18de777aa558b0c9f6790d..fee1987683817c6183027f2095632ba8e5bdbec2 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -428,25 +428,9 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool
 	// set texture for polygon
 	if (levelflat != NULL)
 	{
-		if (levelflat->type == LEVELFLAT_FLAT)
-		{
-			size_t len = W_LumpLength(levelflat->u.flat.lumpnum);
-			unsigned flatflag = R_GetFlatSize(len);
-			fflatwidth = fflatheight = (float)flatflag;
-		}
-		else
-		{
-			if (levelflat->type == LEVELFLAT_TEXTURE)
-			{
-				fflatwidth = textures[levelflat->u.texture.num]->width;
-				fflatheight = textures[levelflat->u.texture.num]->height;
-			}
-			else if (levelflat->type == LEVELFLAT_PATCH || levelflat->type == LEVELFLAT_PNG)
-			{
-				fflatwidth = levelflat->width;
-				fflatheight = levelflat->height;
-			}
-		}
+		texture_t *texture = textures[R_GetTextureNumForFlat(levelflat)];
+		fflatwidth = texture->width;
+		fflatheight = texture->height;
 	}
 	else // set no texture
 		HWR_SetCurrentTexture(NULL);
@@ -2745,25 +2729,9 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling,
 	// set texture for polygon
 	if (levelflat != NULL)
 	{
-		if (levelflat->type == LEVELFLAT_FLAT)
-		{
-			size_t len = W_LumpLength(levelflat->u.flat.lumpnum);
-			unsigned flatflag = R_GetFlatSize(len);
-			fflatwidth = fflatheight = (float)flatflag;
-		}
-		else
-		{
-			if (levelflat->type == LEVELFLAT_TEXTURE)
-			{
-				fflatwidth = textures[levelflat->u.texture.num]->width;
-				fflatheight = textures[levelflat->u.texture.num]->height;
-			}
-			else if (levelflat->type == LEVELFLAT_PATCH || levelflat->type == LEVELFLAT_PNG)
-			{
-				fflatwidth = levelflat->width;
-				fflatheight = levelflat->height;
-			}
-		}
+		texture_t *texture = textures[R_GetTextureNumForFlat(levelflat)];
+		fflatwidth = texture->width;
+		fflatheight = texture->height;
 	}
 	else // set no texture
 		HWR_SetCurrentTexture(NULL);
@@ -2810,8 +2778,6 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling,
 		}
 	}
 
-	anglef = ANG2RAD(InvAngle(angle));
-
 	for (i = 0; i < (INT32)nrPlaneVerts; i++,v3d++)
 	{
 		// Go from the polysector's original vertex locations
@@ -2825,6 +2791,8 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling,
 			tempxsow = v3d->s;
 			tempytow = v3d->t;
 
+			anglef = ANG2RAD(InvAngle(angle));
+
 			v3d->s = (tempxsow * cos(anglef)) - (tempytow * sin(anglef));
 			v3d->t = (tempxsow * sin(anglef)) + (tempytow * cos(anglef));
 		}
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 18648a4999a1263b012f4d71120fbb0687f03d53..9797a93312e2df6159f64ef472abe1beb5f2f024 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -379,7 +379,7 @@ static void md2_loadTexture(md2_t *model)
 			Z_Free(grPatch->mipmap->data);
 	}
 	else
-		model->grpatch = patch = Patch_Create(NULL, 0, NULL);
+		model->grpatch = patch = Patch_Create(0, 0);
 
 	if (!patch->hardware)
 		Patch_AllocateHardwarePatch(patch);
@@ -444,7 +444,7 @@ static void md2_loadBlendTexture(md2_t *model)
 			Z_Free(grPatch->mipmap->data);
 	}
 	else
-		model->blendgrpatch = patch = Patch_Create(NULL, 0, NULL);
+		model->blendgrpatch = patch = Patch_Create(0, 0);
 
 	if (!patch->hardware)
 		Patch_AllocateHardwarePatch(patch);
diff --git a/src/m_menu.c b/src/m_menu.c
index 504a5c6cb86731ed25b4e7d0f34d3c7ad9ee3aa8..de23acbfb46b4402261cef08c54bd972d61553ec 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -6331,18 +6331,11 @@ static void M_StopMessage(INT32 choice)
 // You can even put multiple images in one menu!
 static void M_DrawImageDef(void)
 {
-	// Grr.  Need to autodetect for pic_ts.
-	pic_t *pictest = (pic_t *)W_CacheLumpName(currentMenu->menuitems[itemOn].text,PU_CACHE);
-	if (!pictest->zero)
-		V_DrawScaledPic(0,0,0,W_GetNumForName(currentMenu->menuitems[itemOn].text));
+	patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].text, PU_PATCH);
+	if (patch->width <= BASEVIDWIDTH)
+		V_DrawScaledPatch(0,0,0,patch);
 	else
-	{
-		patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].text,PU_PATCH);
-		if (patch->width <= BASEVIDWIDTH)
-			V_DrawScaledPatch(0,0,0,patch);
-		else
-			V_DrawSmallScaledPatch(0,0,0,patch);
-	}
+		V_DrawSmallScaledPatch(0,0,0,patch);
 
 	if (currentMenu->numitems > 1)
 		V_DrawString(0,192,V_TRANSLUCENT, va("PAGE %d of %hd", itemOn+1, currentMenu->numitems));
diff --git a/src/p_setup.c b/src/p_setup.c
index 614a820806ca714ba3d86c39b630b9b1ba6ab1bf..1d985beb4f07399a40607f1ec8f32f480df0e65c 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -537,20 +537,14 @@ levelflat_t *foundflats;
 //SoM: Other files want this info.
 size_t P_PrecacheLevelFlats(void)
 {
-	lumpnum_t lump;
 	size_t i;
 
 	//SoM: 4/18/2000: New flat code to make use of levelflats.
 	flatmemory = 0;
 	for (i = 0; i < numlevelflats; i++)
 	{
-		if (levelflats[i].type == LEVELFLAT_FLAT)
-		{
-			lump = levelflats[i].u.flat.lumpnum;
-			if (devparm)
-				flatmemory += W_LumpLength(lump);
-			R_GetFlat(lump);
-		}
+		if (levelflats[i].type != LEVELFLAT_NONE)
+			R_GetFlat(&levelflats[i]);
 	}
 	return flatmemory;
 }
@@ -562,19 +556,8 @@ or NULL if we want to allocate it now.
 static INT32
 Ploadflat (levelflat_t *levelflat, const char *flatname, boolean resize)
 {
-#ifndef NO_PNG_LUMPS
-	UINT8         buffer[8];
-#endif
-
-	lumpnum_t    flatnum;
-	int       texturenum;
-	UINT8     *flatpatch;
-	size_t    lumplength;
-
-	size_t i;
-
 	// Scan through the already found flats, return if it matches.
-	for (i = 0; i < numlevelflats; i++)
+	for (size_t i = 0; i < numlevelflats; i++)
 	{
 		if (strnicmp(levelflat[i].name, flatname, 8) == 0)
 			return i;
@@ -598,64 +581,35 @@ Ploadflat (levelflat_t *levelflat, const char *flatname, boolean resize)
 	strlcpy(levelflat->name, flatname, sizeof (levelflat->name));
 	strupr(levelflat->name);
 
-	/* If we can't find a flat, try looking for a texture! */
-	if (( flatnum = R_GetFlatNumForName(levelflat->name) ) == LUMPERROR)
+	levelflat->type = LEVELFLAT_TEXTURE;
+
+	// Look for a flat
+	int texturenum = R_CheckFlatNumForName(levelflat->name);
+	if (texturenum <= 0)
 	{
-		if (( texturenum = R_CheckTextureNumForName(levelflat->name) ) == -1)
-		{
-			// check for REDWALL
-			if (( texturenum = R_CheckTextureNumForName("REDWALL") ) != -1)
-				goto texturefound;
-			// check for REDFLR
-			else if (( flatnum = R_GetFlatNumForName("REDFLR") ) != LUMPERROR)
-				goto flatfound;
-			// nevermind
-			levelflat->type = LEVELFLAT_NONE;
-		}
-		else
+		// If we can't find a flat, try looking for a texture!
+		texturenum = R_CheckTextureNumForName(levelflat->name);
+		if (texturenum <= 0)
 		{
-texturefound:
-			levelflat->type = LEVELFLAT_TEXTURE;
-			levelflat->u.texture.    num = texturenum;
-			levelflat->u.texture.lastnum = texturenum;
-			/* start out unanimated */
-			levelflat->u.texture.basenum = -1;
+			// Use "not found" texture
+			texturenum = R_CheckTextureNumForName("REDWALL");
+
+			// Give up?
+			if (texturenum <= 0)
+			{
+				levelflat->type = LEVELFLAT_NONE;
+				texturenum = -1;
+			}
 		}
 	}
-	else
-	{
-flatfound:
-		/* This could be a flat, patch, or PNG. */
-		flatpatch = W_CacheLumpNum(flatnum, PU_CACHE);
-		lumplength = W_LumpLength(flatnum);
-		if (Picture_CheckIfDoomPatch((softwarepatch_t *)flatpatch, lumplength))
-			levelflat->type = LEVELFLAT_PATCH;
-		else
-		{
-#ifndef NO_PNG_LUMPS
-			/*
-			Only need eight bytes for PNG headers.
-			FIXME: Put this elsewhere.
-			*/
-			W_ReadLumpHeader(flatnum, buffer, 8, 0);
-			if (Picture_IsLumpPNG(buffer, lumplength))
-				levelflat->type = LEVELFLAT_PNG;
-			else
-#endif/*NO_PNG_LUMPS*/
-				levelflat->type = LEVELFLAT_FLAT;/* phew */
-		}
-		if (flatpatch)
-			Z_Free(flatpatch);
 
-		levelflat->u.flat.    lumpnum = flatnum;
-		levelflat->u.flat.baselumpnum = LUMPERROR;
-	}
+	levelflat->texture_id = texturenum;
 
 #ifndef ZDEBUG
 	CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name);
 #endif
 
-	return ( numlevelflats++ );
+	return numlevelflats++;
 }
 
 // Auxiliary function. Find a flat in the active wad files,
@@ -3238,9 +3192,6 @@ static boolean P_LoadMapData(const virtres_t *virt)
 	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();
-
 	return true;
 }
 
diff --git a/src/p_setup.h b/src/p_setup.h
index c6f4f741c01c56fecfc5b0fa0fb267fb1c975b16..f9e51024d2dbcf55f61bd193d1e32edc278a31be 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -35,9 +35,6 @@ extern lumpnum_t lastloadedmaplumpnum; // for comparative savegame
 enum
 {
 	LEVELFLAT_NONE,/* HOM time my friend */
-	LEVELFLAT_FLAT,
-	LEVELFLAT_PATCH,
-	LEVELFLAT_PNG,
 	LEVELFLAT_TEXTURE,
 };
 
@@ -47,41 +44,8 @@ enum
 typedef struct
 {
 	char name[9]; // resource name from wad
-
-	UINT8  type;
-	union
-	{
-		struct
-		{
-			lumpnum_t     lumpnum; // lump number of the flat
-			// for flat animation
-			lumpnum_t baselumpnum;
-		}
-		flat;
-		struct
-		{
-			INT32             num;
-			INT32         lastnum; // texture number of the flat
-			// for flat animation
-			INT32         basenum;
-		}
-		texture;
-	}
-	u;
-
-	UINT16 width, height;
-
-	// for flat animation
-	INT32 animseq; // start pos. in the anim sequence
-	INT32 numpics;
-	INT32 speed;
-
-	// for textures
-	UINT8 *picture;
-#ifdef HWRENDER
-	void *mipmap;
-	void *mippic;
-#endif
+	UINT8 type;
+	INT32 texture_id;
 } levelflat_t;
 
 extern size_t numlevelflats;
diff --git a/src/p_spec.c b/src/p_spec.c
index 7bec07c92730128796558e508718f0449bfe798f..37036d095cb7f7934ebcb4473f22152312e6356c 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -62,17 +62,12 @@ sectorportal_t *secportals;
   */
 typedef struct
 {
-	SINT8 istexture; ///< ::true for a texture, ::false for a flat
-	INT32 picnum;    ///< The end flat number
-	INT32 basepic;   ///< The start flat number
+	INT32 picnum;    ///< The end texture number
+	INT32 basepic;   ///< The start texture number
 	INT32 numpics;   ///< Number of frames in the animation
 	tic_t speed;     ///< Number of tics for which each frame is shown
 } anim_t;
 
-#if defined(_MSC_VER)
-#pragma pack(1)
-#endif
-
 /** Animated texture definition.
   * Used for loading an ANIMDEFS lump from a wad.
   *
@@ -87,13 +82,9 @@ typedef struct
 	SINT8 istexture; ///< True for a texture, false for a flat.
 	char endname[9]; ///< Name of the last frame, null-terminated.
 	char startname[9]; ///< Name of the first frame, null-terminated.
-	INT32 speed ; ///< Number of tics for which each frame is shown.
+	INT32 speed; ///< Number of tics for which each frame is shown.
 } ATTRPACK animdef_t;
 
-#if defined(_MSC_VER)
-#pragma pack()
-#endif
-
 typedef struct
 {
 	UINT32 count;
@@ -138,17 +129,32 @@ static size_t maxanims;
 
 static animdef_t *animdefs = NULL;
 
-// Increase the size of animdefs to make room for a new animation definition
-static void GrowAnimDefs(void)
-{
-	maxanims++;
-	animdefs = (animdef_t *)Z_Realloc(animdefs, sizeof(animdef_t)*(maxanims + 1), PU_STATIC, NULL);
-}
-
 // A prototype; here instead of p_spec.h, so they're "private"
 void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum);
 void P_ParseAnimationDefintion(SINT8 istexture);
 
+static boolean P_FindTextureForAnimation(anim_t *anim, animdef_t *animdef)
+{
+	if (R_CheckTextureNumForName(animdef->startname) == -1)
+		return false;
+
+	anim->picnum = R_TextureNumForName(animdef->endname);
+	anim->basepic = R_TextureNumForName(animdef->startname);
+
+	return true;
+}
+
+static boolean P_FindFlatForAnimation(anim_t *anim, animdef_t *animdef)
+{
+	if (R_CheckFlatNumForName(animdef->startname) == -1)
+		return false;
+
+	anim->picnum = R_CheckFlatNumForName(animdef->endname);
+	anim->basepic = R_CheckFlatNumForName(animdef->startname);
+
+	return true;
+}
+
 /** Sets up texture and flat animations.
   *
   * Converts an ::animdef_t array loaded from a lump into
@@ -156,7 +162,6 @@ void P_ParseAnimationDefintion(SINT8 istexture);
   *
   * Issues an error if any animation cycles are invalid.
   *
-  * \sa P_FindAnimatedFlat, P_SetupLevelFlatAnims
   * \author Steven McGranahan (original), Shadow Hog (had to rewrite it to handle multiple WADs), JTE (had to rewrite it to handle multiple WADs _correctly_)
   */
 void P_InitPicAnims(void)
@@ -199,37 +204,39 @@ void P_InitPicAnims(void)
 	lastanim = anims;
 	for (i = 0; animdefs[i].istexture != -1; i++)
 	{
+		animdef_t *animdef = &animdefs[i];
+
+		// If this animation is for a texture, look for one first, THEN look for a flat
 		if (animdefs[i].istexture)
 		{
-			if (R_CheckTextureNumForName(animdefs[i].startname) == -1)
-				continue;
-
-			lastanim->picnum = R_TextureNumForName(animdefs[i].endname);
-			lastanim->basepic = R_TextureNumForName(animdefs[i].startname);
+			if (!P_FindTextureForAnimation(lastanim, animdef))
+			{
+				if (!P_FindFlatForAnimation(lastanim, animdef))
+					continue;
+			}
 		}
+		// Else, if this animation is for a flat, look for one first, THEN look for a texture
 		else
 		{
-			if ((W_CheckNumForName(animdefs[i].startname)) == LUMPERROR)
-				continue;
-
-			lastanim->picnum = R_GetFlatNumForName(animdefs[i].endname);
-			lastanim->basepic = R_GetFlatNumForName(animdefs[i].startname);
+			if (!P_FindFlatForAnimation(lastanim, animdef))
+			{
+				if (!P_FindTextureForAnimation(lastanim, animdef))
+					continue;
+			}
 		}
 
-		lastanim->istexture = animdefs[i].istexture;
 		lastanim->numpics = lastanim->picnum - lastanim->basepic + 1;
 
 		if (lastanim->numpics < 2)
 		{
 			free(anims);
 			I_Error("P_InitPicAnims: bad cycle from %s to %s",
-				animdefs[i].startname, animdefs[i].endname);
+				animdef->startname, animdef->endname);
 		}
 
-		lastanim->speed = LONG(animdefs[i].speed);
+		lastanim->speed = animdef->speed;
 		lastanim++;
 	}
-	lastanim->istexture = -1;
 	R_ClearTextureNumCache(false);
 
 	// Clear animdefs now that we're done with it.
@@ -354,7 +361,8 @@ void P_ParseAnimationDefintion(SINT8 istexture)
 	if (i == maxanims)
 	{
 		// Increase the size to make room for the new animation definition
-		GrowAnimDefs();
+		maxanims++;
+		animdefs = (animdef_t *)Z_Realloc(animdefs, sizeof(animdef_t)*(maxanims + 1), PU_STATIC, NULL);
 		strncpy(animdefs[i].startname, animdefsToken, 9);
 	}
 
@@ -440,83 +448,6 @@ void P_ParseAnimationDefintion(SINT8 istexture)
 	}
 	animdefs[i].speed = animSpeed;
 	Z_Free(animdefsToken);
-
-#ifdef WALLFLATS
-	// hehe... uhh.....
-	if (!istexture)
-	{
-		GrowAnimDefs();
-		M_Memcpy(&animdefs[maxanims-1], &animdefs[i], sizeof(animdef_t));
-		animdefs[maxanims-1].istexture = 1;
-	}
-#endif
-}
-
-/** Checks for flats in levelflats that are part of a flat animation sequence
-  * and sets them up for animation.
-  *
-  * \param animnum Index into ::anims to find flats for.
-  * \sa P_SetupLevelFlatAnims
-  */
-static inline void P_FindAnimatedFlat(INT32 animnum)
-{
-	size_t i;
-	lumpnum_t startflatnum, endflatnum;
-	levelflat_t *foundflats;
-
-	foundflats = levelflats;
-	startflatnum = anims[animnum].basepic;
-	endflatnum = anims[animnum].picnum;
-
-	// note: high word of lumpnum is the wad number
-	if ((startflatnum>>16) != (endflatnum>>16))
-		I_Error("AnimatedFlat start %s not in same wad as end %s\n",
-			animdefs[animnum].startname, animdefs[animnum].endname);
-
-	//
-	// now search through the levelflats if this anim flat sequence is used
-	//
-	for (i = 0; i < numlevelflats; i++, foundflats++)
-	{
-		// is that levelflat from the flat anim sequence ?
-		if ((anims[animnum].istexture) && (foundflats->type == LEVELFLAT_TEXTURE)
-			&& ((UINT16)foundflats->u.texture.num >= startflatnum && (UINT16)foundflats->u.texture.num <= endflatnum))
-		{
-			foundflats->u.texture.basenum = startflatnum;
-			foundflats->animseq = foundflats->u.texture.num - startflatnum;
-			foundflats->numpics = endflatnum - startflatnum + 1;
-			foundflats->speed = anims[animnum].speed;
-
-			CONS_Debug(DBG_SETUP, "animflat: #%03d name:%.8s animseq:%d numpics:%d speed:%d\n",
-					atoi(sizeu1(i)), foundflats->name, foundflats->animseq,
-					foundflats->numpics,foundflats->speed);
-		}
-		else if ((!anims[animnum].istexture) && (foundflats->type == LEVELFLAT_FLAT)
-			&& (foundflats->u.flat.lumpnum >= startflatnum && foundflats->u.flat.lumpnum <= endflatnum))
-		{
-			foundflats->u.flat.baselumpnum = startflatnum;
-			foundflats->animseq = foundflats->u.flat.lumpnum - startflatnum;
-			foundflats->numpics = endflatnum - startflatnum + 1;
-			foundflats->speed = anims[animnum].speed;
-
-			CONS_Debug(DBG_SETUP, "animflat: #%03d name:%.8s animseq:%d numpics:%d speed:%d\n",
-					atoi(sizeu1(i)), foundflats->name, foundflats->animseq,
-					foundflats->numpics,foundflats->speed);
-		}
-	}
-}
-
-/** Sets up all flats used in a level.
-  *
-  * \sa P_InitPicAnims, P_FindAnimatedFlat
-  */
-void P_SetupLevelFlatAnims(void)
-{
-	INT32 i;
-
-	// the original game flat anim sequences
-	for (i = 0; anims[i].istexture != -1; i++)
-		P_FindAnimatedFlat(i);
 }
 
 //
@@ -5405,13 +5336,6 @@ void P_CheckMobjTrigger(mobj_t *mobj, boolean pushable)
   */
 void P_UpdateSpecials(void)
 {
-	anim_t *anim;
-	INT32 i;
-	INT32 pic;
-	size_t j;
-
-	levelflat_t *foundflats; // for flat animation
-
 	// LEVEL TIMER
 	P_CheckTimeLimit();
 
@@ -5419,37 +5343,18 @@ void P_UpdateSpecials(void)
 	P_CheckPointLimit();
 
 	// ANIMATE TEXTURES
-	for (anim = anims; anim < lastanim; anim++)
-	{
-		for (i = 0; i < anim->numpics; i++)
-		{
-			pic = anim->basepic + ((leveltime/anim->speed + i) % anim->numpics);
-			if (anim->istexture)
-				texturetranslation[anim->basepic+i] = pic;
-		}
-	}
-
-	// ANIMATE FLATS
-	/// \todo do not check the non-animate flat.. link the animated ones?
-	/// \note its faster than the original anywaysince it animates only
-	///    flats used in the level, and there's usually very few of them
-	foundflats = levelflats;
-	for (j = 0; j < numlevelflats; j++, foundflats++)
+	for (anim_t *anim = anims; anim < lastanim; anim++)
 	{
-		if (foundflats->speed) // it is an animated flat
+		for (INT32 i = 0; i < anim->numpics; i++)
 		{
-			// update the levelflat texture number
-			if ((foundflats->type == LEVELFLAT_TEXTURE) && (foundflats->u.texture.basenum != -1))
-				foundflats->u.texture.num = foundflats->u.texture.basenum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics);
-			// update the levelflat lump number
-			else if ((foundflats->type == LEVELFLAT_FLAT) && (foundflats->u.flat.baselumpnum != LUMPERROR))
-				foundflats->u.flat.lumpnum = foundflats->u.flat.baselumpnum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics);
+			INT32 pic = anim->basepic + ((leveltime/anim->speed + i) % anim->numpics);
+			texturetranslation[anim->basepic+i] = pic;
 		}
 	}
 }
 
 //
-// Floor over floors (FOFs), 3Dfloors, 3Dblocks, fake floors (ffloors), rovers, or whatever you want to call them
+// 3D floors
 //
 
 /** Gets the ID number for a 3Dfloor in its target sector.
diff --git a/src/p_spec.h b/src/p_spec.h
index 3bbaba58b69858cfc0d8627bfb2971e8628e6b40..b9c3a2ca3c2055481b54cd2d2fce3217df244fe7 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -502,9 +502,6 @@ typedef enum
 // at game start
 void P_InitPicAnims(void);
 
-// at map load (sectors)
-void P_SetupLevelFlatAnims(void);
-
 // at map load
 void P_InitSpecials(void);
 void P_ApplyFlatAlignment(sector_t* sector, angle_t flatangle, fixed_t xoffs, fixed_t yoffs, boolean floor, boolean ceiling);
diff --git a/src/r_defs.h b/src/r_defs.h
index 6e0375e615aed5596e68eb67b113fe61a907e477..d556b540f8a68794986e04da72c58e6d7af8f9a2 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -775,14 +775,26 @@ typedef struct
 {
 	UINT8 topdelta; // -1 is the last post in a column
 	UINT8 length;   // length data bytes follows
-} ATTRPACK post_t;
+} ATTRPACK doompost_t;
 
 #if defined(_MSC_VER)
 #pragma pack()
 #endif
 
-// column_t is a list of 0 or more post_t, (UINT8)-1 terminated
-typedef post_t column_t;
+typedef struct
+{
+	unsigned topdelta;
+	unsigned length;
+	size_t data_offset;
+} post_t;
+
+// column_t is a list of 0 or more post_t
+typedef struct
+{
+	unsigned num_posts;
+	post_t *posts;
+	UINT8 *pixels;
+} column_t;
 
 //
 // OTHER TYPES
@@ -859,8 +871,9 @@ typedef struct
 	INT16 width, height;
 	INT16 leftoffset, topoffset;
 
-	INT32 *columnofs; // Column offsets. This is relative to patch->columns
-	UINT8 *columns; // Software column data
+	UINT8 *pixels;
+	column_t *columns;
+	post_t *posts;
 
 	void *hardware; // OpenGL patch, allocated whenever necessary
 	void *flats[4]; // The patch as flats
@@ -884,26 +897,6 @@ typedef struct
 	// the [0] is &columnofs[width]
 } ATTRPACK softwarepatch_t;
 
-#ifdef _MSC_VER
-#pragma warning(disable :  4200)
-#endif
-
-// a pic is an unmasked block of pixels, stored in horizontal way
-typedef struct
-{
-	INT16 width;
-	UINT8 zero;       // set to 0 allow autodetection of pic_t
-	                 // mode instead of patch or raw
-	UINT8 mode;       // see pic_mode_t above
-	INT16 height;
-	INT16 reserved1; // set to 0
-	UINT8 data[0];
-} ATTRPACK pic_t;
-
-#ifdef _MSC_VER
-#pragma warning(default : 4200)
-#endif
-
 #if defined(_MSC_VER)
 #pragma pack()
 #endif
diff --git a/src/r_draw.h b/src/r_draw.h
index 0af911233bfdb6097c8326bf014be84ee7418cad..7eb001ebd360df404e57a5716d96aa73aa013219 100644
--- a/src/r_draw.h
+++ b/src/r_draw.h
@@ -192,8 +192,6 @@ void R_DrawTranslucentColumn_8(void);
 void R_DrawDropShadowColumn_8(void);
 void R_DrawTranslatedColumn_8(void);
 void R_DrawTranslatedTranslucentColumn_8(void);
-void R_Draw2sMultiPatchColumn_8(void);
-void R_Draw2sMultiPatchTranslucentColumn_8(void);
 void R_DrawFogColumn_8(void);
 void R_DrawColumnShadowed_8(void);
 
diff --git a/src/r_draw8.c b/src/r_draw8.c
index fe7d321dfcf1a1a1c0aa6aabc8ba5169c445acdf..87545f41836761421f2cdf8694c07c88f6815612 100644
--- a/src/r_draw8.c
+++ b/src/r_draw8.c
@@ -105,199 +105,6 @@ void R_DrawColumn_8(void)
 	}
 }
 
-void R_Draw2sMultiPatchColumn_8(void)
-{
-	INT32 count;
-	register UINT8 *dest;
-	register fixed_t frac;
-	fixed_t fracstep;
-
-	count = dc_yh - dc_yl;
-
-	if (count < 0) // Zero length, column does not exceed a pixel.
-		return;
-
-#ifdef RANGECHECK
-	if ((unsigned)dc_x >= (unsigned)vid.width || dc_yl < 0 || dc_yh >= vid.height)
-		return;
-#endif
-
-	// Framebuffer destination address.
-	// Use ylookup LUT to avoid multiply with ScreenWidth.
-	// Use columnofs LUT for subwindows?
-
-	//dest = ylookup[dc_yl] + columnofs[dc_x];
-	dest = &topleft[dc_yl*vid.width + dc_x];
-
-	count++;
-
-	// Determine scaling, which is the only mapping to be done.
-	fracstep = dc_iscale;
-	//frac = dc_texturemid + (dc_yl - centery)*fracstep;
-	frac = (dc_texturemid + FixedMul((dc_yl << FRACBITS) - centeryfrac, fracstep))*(!dc_hires);
-
-	// Inner loop that does the actual texture mapping, e.g. a DDA-like scaling.
-	// This is as fast as it gets.
-	{
-		register const UINT8 *source = dc_source;
-		register const lighttable_t *colormap = dc_colormap;
-		register INT32 heightmask = dc_texheight-1;
-		register UINT8 val;
-		if (dc_texheight & heightmask)   // not a power of 2 -- killough
-		{
-			heightmask++;
-			heightmask <<= FRACBITS;
-
-			if (frac < 0)
-				while ((frac += heightmask) <  0);
-			else
-				while (frac >= heightmask)
-					frac -= heightmask;
-
-			do
-			{
-				// Re-map color indices from wall texture column
-				//  using a lighting/special effects LUT.
-				// heightmask is the Tutti-Frutti fix
-				val = source[frac>>FRACBITS];
-
-				if (val != TRANSPARENTPIXEL)
-					*dest = colormap[val];
-
-				dest += vid.width;
-
-				// Avoid overflow.
-				if (fracstep > 0x7FFFFFFF - frac)
-					frac += fracstep - heightmask;
-				else
-					frac += fracstep;
-
-				while (frac >= heightmask)
-					frac -= heightmask;
-			} while (--count);
-		}
-		else
-		{
-			while ((count -= 2) >= 0) // texture height is a power of 2
-			{
-				val = source[(frac>>FRACBITS) & heightmask];
-				if (val != TRANSPARENTPIXEL)
-					*dest = colormap[val];
-				dest += vid.width;
-				frac += fracstep;
-				val = source[(frac>>FRACBITS) & heightmask];
-				if (val != TRANSPARENTPIXEL)
-					*dest = colormap[val];
-				dest += vid.width;
-				frac += fracstep;
-			}
-			if (count & 1)
-			{
-				val = source[(frac>>FRACBITS) & heightmask];
-				if (val != TRANSPARENTPIXEL)
-					*dest = colormap[val];
-			}
-		}
-	}
-}
-
-void R_Draw2sMultiPatchTranslucentColumn_8(void)
-{
-	INT32 count;
-	register UINT8 *dest;
-	register fixed_t frac;
-	fixed_t fracstep;
-
-	count = dc_yh - dc_yl;
-
-	if (count < 0) // Zero length, column does not exceed a pixel.
-		return;
-
-#ifdef RANGECHECK
-	if ((unsigned)dc_x >= (unsigned)vid.width || dc_yl < 0 || dc_yh >= vid.height)
-		return;
-#endif
-
-	// Framebuffer destination address.
-	// Use ylookup LUT to avoid multiply with ScreenWidth.
-	// Use columnofs LUT for subwindows?
-
-	//dest = ylookup[dc_yl] + columnofs[dc_x];
-	dest = &topleft[dc_yl*vid.width + dc_x];
-
-	count++;
-
-	// Determine scaling, which is the only mapping to be done.
-	fracstep = dc_iscale;
-	//frac = dc_texturemid + (dc_yl - centery)*fracstep;
-	frac = (dc_texturemid + FixedMul((dc_yl << FRACBITS) - centeryfrac, fracstep))*(!dc_hires);
-
-	// Inner loop that does the actual texture mapping, e.g. a DDA-like scaling.
-	// This is as fast as it gets.
-	{
-		register const UINT8 *source = dc_source;
-		register const UINT8 *transmap = dc_transmap;
-		register const lighttable_t *colormap = dc_colormap;
-		register INT32 heightmask = dc_texheight-1;
-		register UINT8 val;
-		if (dc_texheight & heightmask)   // not a power of 2 -- killough
-		{
-			heightmask++;
-			heightmask <<= FRACBITS;
-
-			if (frac < 0)
-				while ((frac += heightmask) <  0);
-			else
-				while (frac >= heightmask)
-					frac -= heightmask;
-
-			do
-			{
-				// Re-map color indices from wall texture column
-				//  using a lighting/special effects LUT.
-				// heightmask is the Tutti-Frutti fix
-				val = source[frac>>FRACBITS];
-
-				if (val != TRANSPARENTPIXEL)
-					*dest = *(transmap + (colormap[val]<<8) + (*dest));
-
-				dest += vid.width;
-
-				// Avoid overflow.
-				if (fracstep > 0x7FFFFFFF - frac)
-					frac += fracstep - heightmask;
-				else
-					frac += fracstep;
-
-				while (frac >= heightmask)
-					frac -= heightmask;
-			} while (--count);
-		}
-		else
-		{
-			while ((count -= 2) >= 0) // texture height is a power of 2
-			{
-				val = source[(frac>>FRACBITS) & heightmask];
-				if (val != TRANSPARENTPIXEL)
-					*dest = *(transmap + (colormap[val]<<8) + (*dest));
-				dest += vid.width;
-				frac += fracstep;
-				val = source[(frac>>FRACBITS) & heightmask];
-				if (val != TRANSPARENTPIXEL)
-					*dest = *(transmap + (colormap[val]<<8) + (*dest));
-				dest += vid.width;
-				frac += fracstep;
-			}
-			if (count & 1)
-			{
-				val = source[(frac>>FRACBITS) & heightmask];
-				if (val != TRANSPARENTPIXEL)
-					*dest = *(transmap + (colormap[val]<<8) + (*dest));
-			}
-		}
-	}
-}
-
 /**	\brief The R_DrawShadeColumn_8 function
 	Experiment to make software go faster. Taken from the Boom source
 */
diff --git a/src/r_patch.c b/src/r_patch.c
index 7c561e9593a58ab8bf75c2ee20793ccd87245ac0..a6a9dc2ebcc0651c26f6faaede8bf7535f607c74 100644
--- a/src/r_patch.c
+++ b/src/r_patch.c
@@ -21,44 +21,101 @@
 
 //
 // Creates a patch.
-// Assumes a PU_PATCH zone memory tag and no user, but can always be set later
 //
 
-patch_t *Patch_Create(softwarepatch_t *source, size_t srcsize, void *dest)
+patch_t *Patch_Create(INT16 width, INT16 height)
 {
-	patch_t *patch = (dest == NULL) ? Z_Calloc(sizeof(patch_t), PU_PATCH, NULL) : (patch_t *)(dest);
+	patch_t *patch = Z_Calloc(sizeof(patch_t), PU_PATCH, NULL);
+	patch->width = width;
+	patch->height = height;
+	return patch;
+}
 
-	if (source)
-	{
-		INT32 col, colsize;
-		size_t size = sizeof(INT32) * SHORT(source->width);
-		size_t offs = (sizeof(INT16) * 4) + size;
+patch_t *Patch_CreateFromDoomPatch(softwarepatch_t *source)
+{
+	patch_t *patch = Patch_Create(0, 0);
+	if (!source)
+		return patch;
+
+	patch->width      = SHORT(source->width);
+	patch->height     = SHORT(source->height);
+	patch->leftoffset = SHORT(source->leftoffset);
+	patch->topoffset  = SHORT(source->topoffset);
+
+	size_t total_pixels = 0;
+	size_t total_posts = 0;
+
+	Patch_CalcDataSizes(source, &total_pixels, &total_posts);
+
+	patch->columns = Z_Calloc(sizeof(column_t) * patch->width, PU_PATCH_DATA, NULL);
+	patch->posts = Z_Calloc(sizeof(post_t) * total_posts, PU_PATCH_DATA, NULL);
+	patch->pixels = Z_Calloc(sizeof(UINT8) * total_pixels, PU_PATCH_DATA, NULL);
 
-		patch->width      = SHORT(source->width);
-		patch->height     = SHORT(source->height);
-		patch->leftoffset = SHORT(source->leftoffset);
-		patch->topoffset  = SHORT(source->topoffset);
-		patch->columnofs  = Z_Calloc(size, PU_PATCH_DATA, NULL);
+	Patch_MakeColumns(source, patch->width, patch->width, patch->pixels, patch->columns, patch->posts, false);
 
-		for (col = 0; col < source->width; col++)
+	return patch;
+}
+
+void Patch_CalcDataSizes(softwarepatch_t *source, size_t *total_pixels, size_t *total_posts)
+{
+	for (INT32 i = 0; i < source->width; i++)
+	{
+		doompost_t *src_posts = (doompost_t*)((UINT8 *)source + LONG(source->columnofs[i]));
+		for (doompost_t *post = src_posts; post->topdelta != 0xff ;)
 		{
-			// This makes the column offsets relative to the column data itself,
-			// instead of the entire patch data
-			patch->columnofs[col] = LONG(source->columnofs[col]) - offs;
+			(*total_posts)++;
+			(*total_pixels) += post->length;
+			post = (doompost_t *)((UINT8 *)post + post->length + 4);
 		}
+	}
+}
 
-		if (!srcsize)
-			I_Error("Patch_Create: no source size!");
+void Patch_MakeColumns(softwarepatch_t *source, size_t num_columns, INT16 width, UINT8 *pixels, column_t *columns, post_t *posts, boolean flip)
+{
+	column_t *column = flip ? columns + (num_columns - 1) : columns;
 
-		colsize = (INT32)(srcsize) - (INT32)offs;
-		if (colsize <= 0)
-			I_Error("Patch_Create: no column data!");
+	for (size_t i = 0; i < num_columns; i++)
+	{
+		size_t prevdelta = 0;
+		size_t data_offset = 0;
 
-		patch->columns = Z_Calloc(colsize, PU_PATCH_DATA, NULL);
-		M_Memcpy(patch->columns, ((UINT8 *)source + LONG(source->columnofs[0])), colsize);
-	}
+		column->pixels = pixels;
+		column->posts = posts;
+		column->num_posts = 0;
 
-	return patch;
+		if (i >= (unsigned)width)
+			continue;
+
+		doompost_t *src_posts = (doompost_t*)((UINT8 *)source + LONG(source->columnofs[i]));
+
+		for (doompost_t *post = src_posts; post->topdelta != 0xff ;)
+		{
+			size_t topdelta = post->topdelta;
+			if (topdelta <= prevdelta)
+				topdelta += prevdelta;
+			prevdelta = topdelta;
+
+			posts->topdelta = topdelta;
+			posts->length = (size_t)post->length;
+			posts->data_offset = data_offset;
+
+			memcpy(pixels, (UINT8 *)post + 3, post->length);
+
+			data_offset += posts->length;
+			pixels += posts->length;
+
+			column->num_posts++;
+
+			posts++;
+
+			post = (doompost_t *)((UINT8 *)post + post->length + 4);
+		}
+
+		if (flip)
+			column--;
+		else
+			column++;
+	}
 }
 
 //
@@ -96,10 +153,12 @@ static void Patch_FreeData(patch_t *patch)
 	}
 #endif
 
-	if (patch->columnofs)
-		Z_Free(patch->columnofs);
+	if (patch->pixels)
+		Z_Free(patch->pixels);
 	if (patch->columns)
 		Z_Free(patch->columns);
+	if (patch->posts)
+		Z_Free(patch->posts);
 }
 
 void Patch_Free(patch_t *patch)
diff --git a/src/r_patch.h b/src/r_patch.h
index cc41639b3a1913706c21b3041044aab84d70ff06..f1a018c035a322a2a57f926e161fe97b66be5c2f 100644
--- a/src/r_patch.h
+++ b/src/r_patch.h
@@ -18,7 +18,10 @@
 #include "doomdef.h"
 
 // Patch functions
-patch_t *Patch_Create(softwarepatch_t *source, size_t srcsize, void *dest);
+patch_t *Patch_Create(INT16 width, INT16 height);
+patch_t *Patch_CreateFromDoomPatch(softwarepatch_t *source);
+void Patch_CalcDataSizes(softwarepatch_t *source, size_t *total_pixels, size_t *total_posts);
+void Patch_MakeColumns(softwarepatch_t *source, size_t num_columns, INT16 width, UINT8 *pixels, column_t *columns, post_t *posts, boolean flip);
 void Patch_Free(patch_t *patch);
 
 #define Patch_FreeTag(tagnum) Patch_FreeTags(tagnum, tagnum)
diff --git a/src/r_picformats.c b/src/r_picformats.c
index 0de15b427d3c3968b5d07bff8879469a6cfb984f..7d7f1198d1805b24b715f78a339b07c952dd4e04 100644
--- a/src/r_picformats.c
+++ b/src/r_picformats.c
@@ -51,8 +51,6 @@
 #endif
 #endif
 
-static unsigned char imgbuf[1<<26];
-
 #ifdef PICTURE_PNG_USELOOKUP
 static colorlookup_t png_colorlookup;
 #endif
@@ -86,22 +84,148 @@ void *Picture_Convert(
 	else if (informat == outformat)
 		I_Error("Picture_Convert: input and output formats were the same!");
 
+	(void)insize;
+
 	if (Picture_IsPatchFormat(outformat))
-		return Picture_PatchConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags);
+		return Picture_PatchConvert(informat, picture, outformat, outsize, inwidth, inheight, inleftoffset, intopoffset, flags);
 	else if (Picture_IsFlatFormat(outformat))
-		return Picture_FlatConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags);
+		return Picture_FlatConvert(informat, picture, outformat, outsize, inwidth, intopoffset, flags);
 	else
 		I_Error("Picture_Convert: unsupported input format!");
 
 	return NULL;
 }
 
+static void *ReadPixelFunc_Patch(void *picture, pictureformat_t informat, INT32 x, INT32 y, INT32 inwidth, INT32 inheight, pictureflags_t flags)
+{
+	(void)inwidth;
+	(void)inheight;
+	return Picture_GetPatchPixel((patch_t*)picture, informat, x, y, flags);
+}
+
+static void *ReadPixelFunc_Flat_8bpp(void *picture, pictureformat_t informat, INT32 x, INT32 y, INT32 inwidth, INT32 inheight, pictureflags_t flags)
+{
+	(void)informat;
+	(void)flags;
+	(void)inheight;
+	return (UINT8 *)picture + ((y * inwidth) + x);
+}
+
+static void *ReadPixelFunc_Flat_16bpp(void *picture, pictureformat_t informat, INT32 x, INT32 y, INT32 inwidth, INT32 inheight, pictureflags_t flags)
+{
+	(void)informat;
+	(void)flags;
+	(void)inheight;
+	return (UINT16 *)picture + ((y * inwidth) + x);
+}
+
+static void *ReadPixelFunc_Flat_32bpp(void *picture, pictureformat_t informat, INT32 x, INT32 y, INT32 inwidth, INT32 inheight, pictureflags_t flags)
+{
+	(void)informat;
+	(void)flags;
+	(void)inheight;
+	return (UINT32 *)picture + ((y * inwidth) + x);
+}
+
+static UINT8 GetAlphaFunc_32bpp(void *input, pictureflags_t flags)
+{
+	(void)flags;
+	RGBA_t px = *(RGBA_t *)input;
+	return px.s.alpha;
+}
+
+static UINT8 GetAlphaFunc_16bpp(void *input, pictureflags_t flags)
+{
+	(void)flags;
+	UINT16 px = *(UINT16 *)input;
+	return (px & 0xFF00) >> 8;
+}
+
+static UINT8 GetAlphaFunc_8bpp(void *input, pictureflags_t flags)
+{
+	UINT8 px = *(UINT8 *)input;
+	if (px == TRANSPARENTPIXEL && (flags & PICFLAGS_USE_TRANSPARENTPIXEL))
+		return 0;
+	else
+		return 255;
+}
+
+// input 32bpp output 32bpp
+static void *WritePatchPixel_i32o32(void *ptr, void *input)
+{
+	RGBA_t px = *(RGBA_t *)input;
+	WRITEUINT32(ptr, px.rgba);
+	return ptr;
+}
+
+// input 16bpp output 32bpp
+static void *WritePatchPixel_i16o32(void *ptr, void *input)
+{
+	RGBA_t px = pMasterPalette[*((UINT16 *)input) & 0xFF];
+	WRITEUINT32(ptr, px.rgba);
+	return ptr;
+}
+
+// input 8bpp output 32bpp
+static void *WritePatchPixel_i8o32(void *ptr, void *input)
+{
+	RGBA_t px = pMasterPalette[*((UINT8 *)input) & 0xFF];
+	WRITEUINT32(ptr, px.rgba);
+	return ptr;
+}
+
+// input 32bpp output 16bpp
+static void *WritePatchPixel_i32o16(void *ptr, void *input)
+{
+	RGBA_t in = *(RGBA_t *)input;
+	UINT8 px = NearestColor(in.s.red, in.s.green, in.s.blue);
+	WRITEUINT16(ptr, (0xFF00 | px));
+	return ptr;
+}
+
+// input 16bpp output 16bpp
+static void *WritePatchPixel_i16o16(void *ptr, void *input)
+{
+	WRITEUINT16(ptr, *(UINT16 *)input);
+	return ptr;
+}
+
+// input 8bpp output 16bpp
+static void *WritePatchPixel_i8o16(void *ptr, void *input)
+{
+	WRITEUINT16(ptr, (0xFF00 | (*(UINT8 *)input)));
+	return ptr;
+}
+
+// input 32bpp output 8bpp
+static void *WritePatchPixel_i32o8(void *ptr, void *input)
+{
+	RGBA_t in = *(RGBA_t *)input;
+	UINT8 px = NearestColor(in.s.red, in.s.green, in.s.blue);
+	WRITEUINT8(ptr, px);
+	return ptr;
+}
+
+// input 16bpp output 8bpp
+static void *WritePatchPixel_i16o8(void *ptr, void *input)
+{
+	UINT16 px = *(UINT16 *)input;
+	WRITEUINT8(ptr, (px & 0xFF));
+	return ptr;
+}
+
+// input 8bpp output 8bpp
+static void *WritePatchPixel_i8o8(void *ptr, void *input)
+{
+	WRITEUINT8(ptr, *(UINT8 *)input);
+	return ptr;
+}
+
 /** Converts a picture to a patch.
   *
   * \param informat Input picture format.
   * \param picture Input picture data.
   * \param outformat Output picture format.
-  * \param insize Input picture size.
   * \param outsize Output picture size, as a pointer.
   * \param inwidth Input picture width.
   * \param inheight Input picture height.
@@ -112,34 +236,37 @@ void *Picture_Convert(
   */
 void *Picture_PatchConvert(
 	pictureformat_t informat, void *picture, pictureformat_t outformat,
-	size_t insize, size_t *outsize,
-	INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset,
+	size_t *outsize,
+	INT32 inwidth, INT32 inheight, INT32 inleftoffset, INT32 intopoffset,
 	pictureflags_t flags)
 {
-	INT16 x, y;
-	UINT8 *img;
-	UINT8 *imgptr = imgbuf;
-	UINT8 *colpointers, *startofspan;
-	size_t size = 0;
-	patch_t *inpatch = NULL;
-	INT32 inbpp = Picture_FormatBPP(informat);
+	// Shortcut: If converting a Doom patch into a regular patch, use Patch_Create
+	if (informat == PICFMT_DOOMPATCH && outformat == PICFMT_PATCH && flags == 0)
+	{
+		if (outsize != NULL)
+			*outsize = sizeof(patch_t);
+		return Patch_CreateFromDoomPatch(picture);
+	}
 
-	(void)insize; // ignore
+	INT32 outbpp = Picture_FormatBPP(outformat);
+	INT32 inbpp = Picture_FormatBPP(informat);
+	patch_t *inpatch = NULL;
 
 	if (informat == PICFMT_NONE)
 		I_Error("Picture_PatchConvert: input format was PICFMT_NONE!");
 	else if (outformat == PICFMT_NONE)
 		I_Error("Picture_PatchConvert: output format was PICFMT_NONE!");
+	else if (Picture_IsDoomPatchFormat(outformat))
+		I_Error("Picture_PatchConvert: cannot convert to Doom patch!");
 	else if (informat == outformat)
 		I_Error("Picture_PatchConvert: input and output formats were the same!");
 
 	if (inbpp == PICDEPTH_NONE)
-		I_Error("Picture_PatchConvert: unknown input bits per pixel?!");
-	if (Picture_FormatBPP(outformat) == PICDEPTH_NONE)
-		I_Error("Picture_PatchConvert: unknown output bits per pixel?!");
+		I_Error("Picture_PatchConvert: unknown input bits per pixel!");
+	if (outbpp == PICDEPTH_NONE)
+		I_Error("Picture_PatchConvert: unknown output bits per pixel!");
 
-	// If it's a patch, you can just figure out
-	// the dimensions from the header.
+	// If it's a patch, we can just figure out the dimensions from the header.
 	if (Picture_IsPatchFormat(informat))
 	{
 		inpatch = (patch_t *)picture;
@@ -160,218 +287,170 @@ void *Picture_PatchConvert(
 		}
 	}
 
-	// Write image size and offset
-	WRITEINT16(imgptr, inwidth);
-	WRITEINT16(imgptr, inheight);
-	WRITEINT16(imgptr, inleftoffset);
-	WRITEINT16(imgptr, intopoffset);
+	void *(*readPixelFunc)(void *, pictureformat_t, INT32, INT32, INT32, INT32, pictureflags_t) = NULL;
+	UINT8 (*getAlphaFunc)(void *, pictureflags_t) = NULL;
+	void *(*writePatchPixel)(void *, void *) = NULL;
+
+	if (Picture_IsPatchFormat(informat))
+		readPixelFunc = ReadPixelFunc_Patch;
+	else if (Picture_IsFlatFormat(informat))
+	{
+		switch (informat)
+		{
+			case PICFMT_FLAT32:
+				readPixelFunc = ReadPixelFunc_Flat_32bpp;
+				break;
+			case PICFMT_FLAT16:
+				readPixelFunc = ReadPixelFunc_Flat_16bpp;
+				break;
+			case PICFMT_FLAT:
+				readPixelFunc = ReadPixelFunc_Flat_8bpp;
+				break;
+			default:
+				I_Error("Picture_PatchConvert: unsupported flat input format!");
+				break;
+		}
+	}
+	else
+		I_Error("Picture_PatchConvert: unsupported input format!");
+
+	if (inbpp == PICDEPTH_32BPP)
+		getAlphaFunc = GetAlphaFunc_32bpp;
+	else if (inbpp == PICDEPTH_16BPP)
+		getAlphaFunc = GetAlphaFunc_16bpp;
+	else if (inbpp == PICDEPTH_8BPP)
+		getAlphaFunc = GetAlphaFunc_8bpp;
+
+	switch (outformat)
+	{
+		case PICFMT_PATCH32:
+		case PICFMT_DOOMPATCH32:
+		{
+			if (inbpp == PICDEPTH_32BPP)
+				writePatchPixel = WritePatchPixel_i32o32;
+			else if (inbpp == PICDEPTH_16BPP)
+				writePatchPixel = WritePatchPixel_i16o32;
+			else // PICFMT_PATCH
+				writePatchPixel = WritePatchPixel_i8o32;
+			break;
+		}
+		case PICFMT_PATCH16:
+		case PICFMT_DOOMPATCH16:
+			if (inbpp == PICDEPTH_32BPP)
+				writePatchPixel = WritePatchPixel_i32o16;
+			else if (inbpp == PICDEPTH_16BPP)
+				writePatchPixel = WritePatchPixel_i16o16;
+			else // PICFMT_PATCH
+				writePatchPixel = WritePatchPixel_i8o16;
+			break;
+		default: // PICFMT_PATCH
+		{
+			if (inbpp == PICDEPTH_32BPP)
+				writePatchPixel = WritePatchPixel_i32o8;
+			else if (inbpp == PICDEPTH_16BPP)
+				writePatchPixel = WritePatchPixel_i16o8;
+			else // PICFMT_PATCH
+				writePatchPixel = WritePatchPixel_i8o8;
+			break;
+		}
+	}
+
+	patch_t *out = Z_Calloc(sizeof(patch_t), PU_PATCH, NULL);
+
+	out->width = inwidth;
+	out->height = inheight;
+	out->leftoffset = inleftoffset;
+	out->topoffset = intopoffset;
+
+	size_t max_pixels = out->width * out->height;
+	unsigned num_posts = 0;
+
+	out->columns = Z_Calloc(sizeof(column_t) * out->width, PU_PATCH_DATA, NULL);
+	out->pixels = Z_Calloc(max_pixels * (outbpp / 8), PU_PATCH_DATA, NULL);
+	out->posts = NULL;
 
-	// Leave placeholder to column pointers
-	colpointers = imgptr;
-	imgptr += inwidth*4;
+	UINT8 *imgptr = out->pixels;
+
+	unsigned *column_posts = Z_Calloc(sizeof(unsigned) * inwidth, PU_STATIC, NULL);
 
 	// Write columns
-	for (x = 0; x < inwidth; x++)
+	for (INT32 x = 0; x < inwidth; x++)
 	{
-		int lastStartY = 0;
-		int spanSize = 0;
-		startofspan = NULL;
+		post_t *post;
+		size_t post_data_offset = 0;
+		boolean was_opaque = false;
+
+		column_t *column = &out->columns[x];
+		column->pixels = imgptr;
+		column->posts = NULL;
+		column->num_posts = 0;
 
-		// Write column pointer
-		WRITEINT32(colpointers, imgptr - imgbuf);
+		column_posts[x] = (unsigned)-1;
 
 		// Write pixels
-		for (y = 0; y < inheight; y++)
+		for (INT32 y = 0; y < inheight; y++)
 		{
-			void *input = NULL;
 			boolean opaque = false;
 
 			// Read pixel
-			if (Picture_IsPatchFormat(informat))
-				input = Picture_GetPatchPixel(inpatch, informat, x, y, flags);
-			else if (Picture_IsFlatFormat(informat))
-			{
-				size_t offs = ((y * inwidth) + x);
-				switch (informat)
-				{
-					case PICFMT_FLAT32:
-						input = (UINT32 *)picture + offs;
-						break;
-					case PICFMT_FLAT16:
-						input = (UINT16 *)picture + offs;
-						break;
-					case PICFMT_FLAT:
-						input = (UINT8 *)picture + offs;
-						break;
-					default:
-						I_Error("Picture_PatchConvert: unsupported flat input format!");
-						break;
-				}
-			}
-			else
-				I_Error("Picture_PatchConvert: unsupported input format!");
+			void *input = readPixelFunc(picture, informat, x, y, inwidth, inheight, flags);
 
 			// Determine opacity
 			if (input != NULL)
-			{
-				UINT8 alpha = 0xFF;
-				if (inbpp == PICDEPTH_32BPP)
-				{
-					RGBA_t px = *(RGBA_t *)input;
-					alpha = px.s.alpha;
-				}
-				else if (inbpp == PICDEPTH_16BPP)
-				{
-					UINT16 px = *(UINT16 *)input;
-					alpha = (px & 0xFF00) >> 8;
-				}
-				else if (inbpp == PICDEPTH_8BPP)
-				{
-					UINT8 px = *(UINT8 *)input;
-					if (px == TRANSPARENTPIXEL)
-						alpha = 0;
-				}
-				opaque = (alpha > 1);
-			}
+				opaque = getAlphaFunc(input, flags) > 0;
 
 			// End span if we have a transparent pixel
 			if (!opaque)
 			{
-				if (startofspan)
-					WRITEUINT8(imgptr, 0);
-				startofspan = NULL;
+				was_opaque = false;
 				continue;
 			}
 
-			// Start new column if we need to
-			if (!startofspan || spanSize == 255)
+			if (!was_opaque)
 			{
-				int writeY = y;
-
-				// If we reached the span size limit, finish the previous span
-				if (startofspan)
-					WRITEUINT8(imgptr, 0);
-
-				if (y > 254)
-				{
-					// Make sure we're aligned to 254
-					if (lastStartY < 254)
-					{
-						WRITEUINT8(imgptr, 254);
-						WRITEUINT8(imgptr, 0);
-						imgptr += 2;
-						lastStartY = 254;
-					}
-
-					// Write stopgap empty spans if needed
-					writeY = y - lastStartY;
-
-					while (writeY > 254)
-					{
-						WRITEUINT8(imgptr, 254);
-						WRITEUINT8(imgptr, 0);
-						imgptr += 2;
-						writeY -= 254;
-					}
-				}
-
-				startofspan = imgptr;
-				WRITEUINT8(imgptr, writeY);
-				imgptr += 2;
-				spanSize = 0;
-
-				lastStartY = y;
+				num_posts++;
+
+				out->posts = Z_Realloc(out->posts, sizeof(post_t) * num_posts, PU_PATCH_DATA, NULL);
+				post = &out->posts[num_posts - 1];
+				post->topdelta = (size_t)y;
+				post->length = 0;
+				post->data_offset = post_data_offset;
+				if (column_posts[x] == (unsigned)-1)
+					column_posts[x] = num_posts - 1;
+				column->num_posts++;
 			}
 
+			was_opaque = true;
+
 			// Write the pixel
-			switch (outformat)
-			{
-				case PICFMT_PATCH32:
-				case PICFMT_DOOMPATCH32:
-				{
-					if (inbpp == PICDEPTH_32BPP)
-					{
-						RGBA_t out = *(RGBA_t *)input;
-						WRITEUINT32(imgptr, out.rgba);
-					}
-					else if (inbpp == PICDEPTH_16BPP)
-					{
-						RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF];
-						WRITEUINT32(imgptr, out.rgba);
-					}
-					else // PICFMT_PATCH
-					{
-						RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF];
-						WRITEUINT32(imgptr, out.rgba);
-					}
-					break;
-				}
-				case PICFMT_PATCH16:
-				case PICFMT_DOOMPATCH16:
-					if (inbpp == PICDEPTH_32BPP)
-					{
-						RGBA_t in = *(RGBA_t *)input;
-						UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue);
-						WRITEUINT16(imgptr, (0xFF00 | out));
-					}
-					else if (inbpp == PICDEPTH_16BPP)
-						WRITEUINT16(imgptr, *(UINT16 *)input);
-					else // PICFMT_PATCH
-						WRITEUINT16(imgptr, (0xFF00 | (*(UINT8 *)input)));
-					break;
-				default: // PICFMT_PATCH
-				{
-					if (inbpp == PICDEPTH_32BPP)
-					{
-						RGBA_t in = *(RGBA_t *)input;
-						UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue);
-						WRITEUINT8(imgptr, out);
-					}
-					else if (inbpp == PICDEPTH_16BPP)
-					{
-						UINT16 out = *(UINT16 *)input;
-						WRITEUINT8(imgptr, (out & 0xFF));
-					}
-					else // PICFMT_PATCH
-						WRITEUINT8(imgptr, *(UINT8 *)input);
-					break;
-				}
-			}
+			UINT8 *last_ptr = imgptr;
+			imgptr = writePatchPixel(last_ptr, input);
 
-			spanSize++;
-			startofspan[1] = spanSize;
+			post->length++;
+			post_data_offset += imgptr - last_ptr;
 		}
-
-		if (startofspan)
-			WRITEUINT8(imgptr, 0);
-
-		WRITEUINT8(imgptr, 0xFF);
 	}
 
-	size = imgptr-imgbuf;
-	img = Z_Malloc(size, PU_STATIC, NULL);
-	memcpy(img, imgbuf, size);
+	UINT8 *old_pixels = out->pixels;
+	size_t total_pixels = imgptr - out->pixels;
+	if (total_pixels != max_pixels)
+		out->pixels = Z_Realloc(out->pixels, total_pixels, PU_PATCH_DATA, NULL);
 
-	if (Picture_IsInternalPatchFormat(outformat))
+	for (INT16 x = 0; x < inwidth; x++)
 	{
-		patch_t *converted = Patch_Create((softwarepatch_t *)img, size, NULL);
+		column_t *column = &out->columns[x];
+		if (column->num_posts > 0)
+			column->posts = &out->posts[column_posts[x]];
+		if (old_pixels != out->pixels)
+			column->pixels = out->pixels + (column->pixels - old_pixels);
+	}
 
-#ifdef HWRENDER
-		Patch_CreateGL(converted);
-#endif
+	Z_Free(column_posts);
 
-		Z_Free(img);
+	if (outsize != NULL)
+		*outsize = sizeof(patch_t);
 
-		if (outsize != NULL)
-			*outsize = sizeof(patch_t);
-		return converted;
-	}
-	else
-	{
-		if (outsize != NULL)
-			*outsize = size;
-		return img;
-	}
+	return (void *)out;
 }
 
 /** Converts a picture to a flat.
@@ -379,19 +458,16 @@ void *Picture_PatchConvert(
   * \param informat Input picture format.
   * \param picture Input picture data.
   * \param outformat Output picture format.
-  * \param insize Input picture size.
   * \param outsize Output picture size, as a pointer.
   * \param inwidth Input picture width.
   * \param inheight Input picture height.
-  * \param inleftoffset Input picture left offset, for patches.
-  * \param intopoffset Input picture top offset, for patches.
   * \param flags Input picture flags.
   * \return A pointer to the converted picture.
   */
 void *Picture_FlatConvert(
 	pictureformat_t informat, void *picture, pictureformat_t outformat,
-	size_t insize, size_t *outsize,
-	INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset,
+	size_t *outsize,
+	INT32 inwidth, INT32 inheight,
 	pictureflags_t flags)
 {
 	void *outflat;
@@ -401,10 +477,6 @@ void *Picture_FlatConvert(
 	INT32 x, y;
 	size_t size;
 
-	(void)insize; // ignore
-	(void)inleftoffset; // ignore
-	(void)intopoffset; // ignore
-
 	if (informat == PICFMT_NONE)
 		I_Error("Picture_FlatConvert: input format was PICFMT_NONE!");
 	else if (outformat == PICFMT_NONE)
@@ -448,13 +520,22 @@ void *Picture_FlatConvert(
 		for (x = 0; x < inwidth; x++)
 		{
 			void *input = NULL;
-			size_t offs = ((y * inwidth) + x);
+			int sx = x;
+			int sy = y;
+
+			if (flags & PICFLAGS_XFLIP)
+				sx = inwidth - x - 1;
+			if (flags & PICFLAGS_YFLIP)
+				sy = inheight - y - 1;
+
+			size_t in_offs = ((sy * inwidth) + sx);
+			size_t out_offs = ((y * inwidth) + x);
 
 			// Read pixel
 			if (Picture_IsPatchFormat(informat))
-				input = Picture_GetPatchPixel(inpatch, informat, x, y, flags);
+				input = Picture_GetPatchPixel(inpatch, informat, sx, sy, 0);
 			else if (Picture_IsFlatFormat(informat))
-				input = (UINT8 *)picture + (offs * (inbpp / 8));
+				input = (UINT8 *)picture + (in_offs * (inbpp / 8));
 			else
 				I_Error("Picture_FlatConvert: unsupported input format!");
 
@@ -469,17 +550,17 @@ void *Picture_FlatConvert(
 					if (inbpp == PICDEPTH_32BPP)
 					{
 						RGBA_t out = *(RGBA_t *)input;
-						f32[offs] = out.rgba;
+						f32[out_offs] = out.rgba;
 					}
 					else if (inbpp == PICDEPTH_16BPP)
 					{
 						RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF];
-						f32[offs] = out.rgba;
+						f32[out_offs] = out.rgba;
 					}
 					else // PICFMT_PATCH
 					{
 						RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF];
-						f32[offs] = out.rgba;
+						f32[out_offs] = out.rgba;
 					}
 					break;
 				}
@@ -490,12 +571,12 @@ void *Picture_FlatConvert(
 					{
 						RGBA_t in = *(RGBA_t *)input;
 						UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue);
-						f16[offs] = (0xFF00 | out);
+						f16[out_offs] = (0xFF00 | out);
 					}
 					else if (inbpp == PICDEPTH_16BPP)
-						f16[offs] = *(UINT16 *)input;
+						f16[out_offs] = *(UINT16 *)input;
 					else // PICFMT_PATCH
-						f16[offs] = (0xFF00 | *((UINT8 *)input));
+						f16[out_offs] = (0xFF00 | *((UINT8 *)input));
 					break;
 				}
 				case PICFMT_FLAT:
@@ -505,15 +586,15 @@ void *Picture_FlatConvert(
 					{
 						RGBA_t in = *(RGBA_t *)input;
 						UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue);
-						f8[offs] = out;
+						f8[out_offs] = out;
 					}
 					else if (inbpp == PICDEPTH_16BPP)
 					{
 						UINT16 out = *(UINT16 *)input;
-						f8[offs] = (out & 0xFF);
+						f8[out_offs] = (out & 0xFF);
 					}
 					else // PICFMT_PATCH
-						f8[offs] = *(UINT8 *)input;
+						f8[out_offs] = *(UINT8 *)input;
 					break;
 				}
 				default:
@@ -538,44 +619,43 @@ void *Picture_GetPatchPixel(
 	INT32 x, INT32 y,
 	pictureflags_t flags)
 {
-	fixed_t ofs;
-	column_t *column;
 	INT32 inbpp = Picture_FormatBPP(informat);
 	softwarepatch_t *doompatch = (softwarepatch_t *)patch;
 	boolean isdoompatch = Picture_IsDoomPatchFormat(informat);
-	INT16 width;
 
 	if (patch == NULL)
 		I_Error("Picture_GetPatchPixel: patch == NULL");
 
-	width = (isdoompatch ? SHORT(doompatch->width) : patch->width);
+	INT16 width = (isdoompatch ? SHORT(doompatch->width) : patch->width);
+	INT16 height = (isdoompatch ? SHORT(doompatch->height) : patch->height);
+
+	if (x < 0 || x >= width || y < 0 || y >= height)
+		return NULL;
 
-	if (x >= 0 && x < width)
+	INT32 sx = (flags & PICFLAGS_XFLIP) ? (width-1)-x : x;
+	INT32 sy = (flags & PICFLAGS_YFLIP) ? (height-1)-y : y;
+	UINT8 *s8 = NULL;
+	UINT16 *s16 = NULL;
+	UINT32 *s32 = NULL;
+
+	if (isdoompatch)
 	{
-		INT32 colx = (flags & PICFLAGS_XFLIP) ? (width-1)-x : x;
-		INT32 topdelta, prevdelta = -1;
-		INT32 colofs = (isdoompatch ? LONG(doompatch->columnofs[colx]) : patch->columnofs[colx]);
+		INT32 prevdelta = -1;
+		INT32 colofs = LONG(doompatch->columnofs[sx]);
 
 		// Column offsets are pointers, so no casting is required.
-		if (isdoompatch)
-			column = (column_t *)((UINT8 *)doompatch + colofs);
-		else
-			column = (column_t *)((UINT8 *)patch->columns + colofs);
+		doompost_t *column = (doompost_t *)((UINT8 *)doompatch + colofs);
 
 		while (column->topdelta != 0xff)
 		{
-			UINT8 *s8 = NULL;
-			UINT16 *s16 = NULL;
-			UINT32 *s32 = NULL;
-
-			topdelta = column->topdelta;
+			INT32 topdelta = column->topdelta;
 			if (topdelta <= prevdelta)
 				topdelta += prevdelta;
 			prevdelta = topdelta;
 
-			ofs = (y - topdelta);
+			size_t ofs = sy - topdelta;
 
-			if (y >= topdelta && ofs < column->length)
+			if (sy >= topdelta && ofs < column->length)
 			{
 				s8 = (UINT8 *)(column) + 3;
 				switch (inbpp)
@@ -592,12 +672,38 @@ void *Picture_GetPatchPixel(
 			}
 
 			if (inbpp == PICDEPTH_32BPP)
-				column = (column_t *)((UINT32 *)column + column->length);
+				column = (doompost_t *)((UINT32 *)column + column->length);
 			else if (inbpp == PICDEPTH_16BPP)
-				column = (column_t *)((UINT16 *)column + column->length);
+				column = (doompost_t *)((UINT16 *)column + column->length);
 			else
-				column = (column_t *)((UINT8 *)column + column->length);
-			column = (column_t *)((UINT8 *)column + 4);
+				column = (doompost_t *)((UINT8 *)column + column->length);
+			column = (doompost_t *)((UINT8 *)column + 4);
+		}
+	}
+	else
+	{
+		column_t *column = &patch->columns[sx];
+		for (unsigned i = 0; i < column->num_posts; i++)
+		{
+			post_t *post = &column->posts[i];
+
+			size_t ofs = sy - post->topdelta;
+
+			if (sy >= (INT32)post->topdelta && ofs < post->length)
+			{
+				s8 = column->pixels + post->data_offset;
+				switch (inbpp)
+				{
+					case PICDEPTH_32BPP:
+						s32 = (UINT32 *)s8;
+						return &s32[ofs];
+					case PICDEPTH_16BPP:
+						s16 = (UINT16 *)s8;
+						return &s16[ofs];
+					default: // PICDEPTH_8BPP
+						return &s8[ofs];
+				}
+			}
 		}
 	}
 
@@ -738,75 +844,50 @@ boolean Picture_CheckIfDoomPatch(softwarepatch_t *patch, size_t size)
 
 /** Converts a texture to a flat.
   *
-  * \param trickytex The texture number.
+  * \param texnum The texture number.
   * \return The converted flat.
   */
-void *Picture_TextureToFlat(size_t trickytex)
+void *Picture_TextureToFlat(size_t texnum)
 {
 	texture_t *texture;
-	size_t tex;
 
 	UINT8 *converted;
 	size_t flatsize;
-	fixed_t col, ofs;
-	column_t *column;
 	UINT8 *desttop, *dest, *deststop;
 	UINT8 *source;
 
-	if (trickytex >= (unsigned)numtextures)
+	if (texnum >= (unsigned)numtextures)
 		I_Error("Picture_TextureToFlat: invalid texture number!");
 
 	// Check the texture cache
 	// If the texture's not there, it'll be generated right now
-	tex = trickytex;
-	texture = textures[tex];
-	R_CheckTextureCache(tex);
+	texture = textures[texnum];
+	R_CheckTextureCache(texnum);
 
 	// Allocate the flat
-	flatsize = (texture->width * texture->height);
+	flatsize = texture->width * texture->height;
 	converted = Z_Malloc(flatsize, PU_STATIC, NULL);
 	memset(converted, TRANSPARENTPIXEL, flatsize);
 
 	// Now we're gonna write to it
 	desttop = converted;
 	deststop = desttop + flatsize;
-	for (col = 0; col < texture->width; col++, desttop++)
+
+	for (size_t col = 0; col < (size_t)texture->width; col++, desttop++)
 	{
-		// no post_t info
-		if (!texture->holes)
+		column_t *column = (column_t *)R_GetColumn(texnum, col);
+		for (unsigned i = 0; i < column->num_posts; i++)
 		{
-			column = (column_t *)(R_GetColumn(tex, col));
-			source = (UINT8 *)(column);
-			dest = desttop;
-			for (ofs = 0; dest < deststop && ofs < texture->height; ofs++)
+			post_t *post = &column->posts[i];
+			dest = desttop + (post->topdelta * texture->width);
+			source = column->pixels + post->data_offset;
+			for (size_t ofs = 0; dest < deststop && ofs < post->length; ofs++)
 			{
 				if (source[ofs] != TRANSPARENTPIXEL)
 					*dest = source[ofs];
 				dest += texture->width;
 			}
 		}
-		else
-		{
-			INT32 topdelta, prevdelta = -1;
-			column = (column_t *)((UINT8 *)R_GetColumn(tex, col) - 3);
-			while (column->topdelta != 0xff)
-			{
-				topdelta = column->topdelta;
-				if (topdelta <= prevdelta)
-					topdelta += prevdelta;
-				prevdelta = topdelta;
-
-				dest = desttop + (topdelta * texture->width);
-				source = (UINT8 *)column + 3;
-				for (ofs = 0; dest < deststop && ofs < column->length; ofs++)
-				{
-					if (source[ofs] != TRANSPARENTPIXEL)
-						*dest = source[ofs];
-					dest += texture->width;
-				}
-				column = (column_t *)((UINT8 *)column + column->length + 4);
-			}
-		}
 	}
 
 	return converted;
@@ -820,7 +901,7 @@ void *Picture_TextureToFlat(size_t trickytex)
   */
 boolean Picture_IsLumpPNG(const UINT8 *d, size_t s)
 {
-	if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/
+	if (s < 67) // https://web.archive.org/web/20230524232139/http://garethrees.org/2007/11/14/pngcrush/
 		return false;
 	// Check for PNG file signature using memcmp
 	// As it may be faster on CPUs with slow unaligned memory access
@@ -877,7 +958,6 @@ static int PNG_ChunkReader(png_structp png_ptr, png_unknown_chunkp chonk)
 static void PNG_error(png_structp PNG, png_const_charp pngtext)
 {
 	CONS_Debug(DBG_RENDER, "libpng error at %p: %s", PNG, pngtext);
-	//I_Error("libpng error at %p: %s", PNG, pngtext);
 }
 
 static void PNG_warn(png_structp PNG, png_const_charp pngtext)
@@ -944,7 +1024,7 @@ static png_bytep *PNG_Read(
 	png_set_read_fn(png_ptr, &png_io, PNG_IOReader);
 
 	memset(&chunk, 0x00, sizeof(png_chunk_t));
-	chunkname = grAb_chunk; // I want to read a grAb chunk
+	chunkname = grAb_chunk;
 
 	user_chunk_ptr = png_get_user_chunk_ptr(png_ptr);
 	png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, PNG_ChunkReader);
@@ -1293,7 +1373,7 @@ void *Picture_PNGConvert(
 		}
 
 		// Now, convert it!
-		converted = Picture_PatchConvert(informat, flat, outformat, insize, outsize, (INT16)width, (INT16)height, *leftoffset, *topoffset, flags);
+		converted = Picture_PatchConvert(informat, flat, outformat, outsize, (INT32)width, (INT32)height, *leftoffset, *topoffset, flags);
 		Z_Free(flat);
 		return converted;
 	}
@@ -1329,13 +1409,17 @@ boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *to
 
 	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn);
 	if (!png_ptr)
-		I_Error("Picture_PNGDimensions: Couldn't initialize libpng!");
+	{
+		CONS_Alert(CONS_ERROR, "Picture_PNGDimensions: Couldn't initialize libpng!\n");
+		return false;
+	}
 
 	png_info_ptr = png_create_info_struct(png_ptr);
 	if (!png_info_ptr)
 	{
 		png_destroy_read_struct(&png_ptr, NULL, NULL);
-		I_Error("Picture_PNGDimensions: libpng couldn't allocate memory!");
+		CONS_Alert(CONS_ERROR, "Picture_PNGDimensions: libpng couldn't allocate memory!\n");
+		return false;
 	}
 
 #ifdef USE_FAR_KEYWORD
@@ -1345,7 +1429,8 @@ boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *to
 #endif
 	{
 		png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL);
-		I_Error("Picture_PNGDimensions: libpng load error!");
+		CONS_Alert(CONS_ERROR, "Picture_PNGDimensions: libpng load error!\n");
+		return false;
 	}
 #ifdef USE_FAR_KEYWORD
 	png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf);
@@ -1357,7 +1442,7 @@ boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *to
 	png_set_read_fn(png_ptr, &png_io, PNG_IOReader);
 
 	memset(&chunk, 0x00, sizeof(png_chunk_t));
-	chunkname = grAb_chunk; // I want to read a grAb chunk
+	chunkname = grAb_chunk;
 
 	user_chunk_ptr = png_get_user_chunk_ptr(png_ptr);
 	png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, PNG_ChunkReader);
diff --git a/src/r_picformats.h b/src/r_picformats.h
index 4050a1b71dc01e8a60cbb14cbe017716c61038c3..3ee9805d867f30cf8eec923285b24d0172038f18 100644
--- a/src/r_picformats.h
+++ b/src/r_picformats.h
@@ -42,8 +42,9 @@ typedef enum
 
 typedef enum
 {
-	PICFLAGS_XFLIP = 1,
-	PICFLAGS_YFLIP = 1<<1
+	PICFLAGS_XFLIP                = 1,
+	PICFLAGS_YFLIP                = 1<<1,
+	PICFLAGS_USE_TRANSPARENTPIXEL = 1<<2
 } pictureflags_t;
 
 enum
@@ -62,20 +63,20 @@ void *Picture_Convert(
 
 void *Picture_PatchConvert(
 	pictureformat_t informat, void *picture, pictureformat_t outformat,
-	size_t insize, size_t *outsize,
-	INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset,
+	size_t *outsize,
+	INT32 inwidth, INT32 inheight, INT32 inleftoffset, INT32 intopoffset,
 	pictureflags_t flags);
 void *Picture_FlatConvert(
 	pictureformat_t informat, void *picture, pictureformat_t outformat,
-	size_t insize, size_t *outsize,
-	INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset,
+	size_t *outsize,
+	INT32 inwidth, INT32 inheight,
 	pictureflags_t flags);
 void *Picture_GetPatchPixel(
 	patch_t *patch, pictureformat_t informat,
 	INT32 x, INT32 y,
 	pictureflags_t flags);
 
-void *Picture_TextureToFlat(size_t trickytex);
+void *Picture_TextureToFlat(size_t texnum);
 
 INT32 Picture_FormatBPP(pictureformat_t format);
 boolean Picture_IsPatchFormat(pictureformat_t format);
@@ -103,10 +104,10 @@ typedef struct
 	boolean available;
 } spriteinfo_t;
 
-// Portable Network Graphics
-#define PNG_HEADER_SIZE (8)
+// PNG support
+#define PNG_HEADER_SIZE 8
+
 boolean Picture_IsLumpPNG(const UINT8 *d, size_t s);
-#define Picture_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic
 
 #ifndef NO_PNG_LUMPS
 void *Picture_PNGConvert(
diff --git a/src/r_plane.c b/src/r_plane.c
index 11aa6c941ae916cfcd924d0ea55c20709f1f3081..4f9ce9ec84bdf7443a16b811504a30df674d78f7 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -624,37 +624,31 @@ void R_DrawPlanes(void)
 //
 static void R_DrawSkyPlane(visplane_t *pl)
 {
-	INT32 x;
-	INT32 angle;
+	INT32 texture = texturetranslation[skytexture];
 
 	// Reset column drawer function (note: couldn't we just call walldrawerfunc directly?)
 	// (that is, unless we'll need to switch drawers in future for some reason)
 	colfunc = colfuncs[BASEDRAWFUNC];
 
-	// use correct aspect ratio scale
 	dc_iscale = skyscale;
 
-	// Sky is always drawn full bright,
-	//  i.e. colormaps[0] is used.
-	// Because of this hack, sky is not affected
-	//  by sector colormaps (INVUL inverse mapping is not implemented in SRB2 so is irrelevant).
 	dc_colormap = colormaps;
 	dc_texturemid = skytexturemid;
-	dc_texheight = textureheight[skytexture]
-		>>FRACBITS;
-	for (x = pl->minx; x <= pl->maxx; x++)
+	dc_texheight = textureheight[texture]>>FRACBITS;
+
+	R_CheckTextureCache(texture);
+
+	for (INT32 x = pl->minx; x <= pl->maxx; x++)
 	{
 		dc_yl = pl->top[x];
 		dc_yh = pl->bottom[x];
 
 		if (dc_yl <= dc_yh)
 		{
-			angle = (pl->viewangle + xtoviewangle[x])>>ANGLETOSKYSHIFT;
+			INT32 angle = (pl->viewangle + xtoviewangle[x])>>ANGLETOSKYSHIFT;
 			dc_iscale = FixedMul(skyscale, FINECOSINE(xtoviewangle[x]>>ANGLETOFINESHIFT));
 			dc_x = x;
-			dc_source =
-				R_GetColumn(texturetranslation[skytexture],
-					-angle); // get negative of angle for each column to display sky correct way round! --Monster Iestyn 27/01/18
+			dc_source = R_GetColumn(texture, -angle)->pixels; // get negative of angle for each column to display sky correct way round! --Monster Iestyn 27/01/18
 			colfunc();
 		}
 	}
@@ -1004,30 +998,21 @@ void R_DrawSinglePlane(visplane_t *pl)
 	{
 		levelflat_t *levelflat = &levelflats[pl->picnum];
 
-		/* :james: */
-		switch (levelflat->type)
+		// Get the texture
+		ds_source = (UINT8 *)R_GetFlat(levelflat);
+		if (ds_source == NULL)
+			return;
+
+		texture_t *texture = textures[R_GetTextureNumForFlat(levelflat)];
+		ds_flatwidth = texture->width;
+		ds_flatheight = texture->height;
+
+		if (R_CheckSolidColorFlat())
+			ds_solidcolor = true;
+		else if (R_CheckPowersOfTwo())
 		{
-			case LEVELFLAT_NONE:
-				return;
-			case LEVELFLAT_FLAT:
-				ds_source = (UINT8 *)R_GetFlat(levelflat->u.flat.lumpnum);
-				R_SetFlatVars(W_LumpLength(levelflat->u.flat.lumpnum));
-				if (R_CheckSolidColorFlat())
-					ds_solidcolor = true;
-				else
-					ds_powersoftwo = true;
-				break;
-			default:
-				ds_source = (UINT8 *)R_GetLevelFlat(levelflat);
-				if (!ds_source)
-					return;
-				else if (R_CheckSolidColorFlat())
-					ds_solidcolor = true;
-				else if (R_CheckPowersOfTwo())
-				{
-					R_SetFlatVars(ds_flatwidth * ds_flatheight);
-					ds_powersoftwo = true;
-				}
+			R_SetFlatVars(ds_flatwidth * ds_flatheight);
+			ds_powersoftwo = true;
 		}
 
 		mapfunc = R_MapPlane;
diff --git a/src/r_segs.c b/src/r_segs.c
index 7877f1353e5b24739d86a62bb940dbe729d85afc..267c1d47d63ff32a56d6e6b0330783397dacfee3 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -96,49 +96,6 @@ void R_ClearSegTables(void)
 // R_RenderMaskedSegRange
 // ==========================================================================
 
-// If we have a multi-patch texture on a 2sided wall (rare) then we draw
-//  it using R_DrawColumn, else we draw it using R_DrawMaskedColumn, this
-//  way we don't have to store extra post_t info with each column for
-//  multi-patch textures. They are not normally needed as multi-patch
-//  textures don't have holes in it. At least not for now.
-
-static void R_Render2sidedMultiPatchColumn(column_t *column)
-{
-	INT32 topscreen, bottomscreen;
-
-	topscreen = sprtopscreen; // + spryscale*column->topdelta;  topdelta is 0 for the wall
-	bottomscreen = topscreen + spryscale * lengthcol;
-
-	dc_yl = (sprtopscreen+FRACUNIT-1)>>FRACBITS;
-	dc_yh = (bottomscreen-1)>>FRACBITS;
-
-	if (windowtop != INT32_MAX && windowbottom != INT32_MAX)
-	{
-		dc_yl = ((windowtop + FRACUNIT)>>FRACBITS);
-		dc_yh = (windowbottom - 1)>>FRACBITS;
-	}
-
-	if (dc_yh >= mfloorclip[dc_x])
-		dc_yh =  mfloorclip[dc_x] - 1;
-	if (dc_yl <= mceilingclip[dc_x])
-		dc_yl =  mceilingclip[dc_x] + 1;
-
-	if (dc_yl >= vid.height || dc_yh < 0)
-		return;
-
-	if (dc_yl <= dc_yh && dc_yh < vid.height && dc_yh > 0)
-	{
-		dc_source = (UINT8 *)column + 3;
-
-		if (colfunc == colfuncs[BASEDRAWFUNC])
-			(colfuncs[COLDRAWFUNC_TWOSMULTIPATCH])();
-		else if (colfunc == colfuncs[COLDRAWFUNC_FUZZY])
-			(colfuncs[COLDRAWFUNC_TWOSMULTIPATCHTRANS])();
-		else
-			colfunc();
-	}
-}
-
 transnum_t R_GetLinedefTransTable(fixed_t alpha)
 {
 	return (20*(FRACUNIT - alpha - 1) + FRACUNIT) >> (FRACBITS+1);
@@ -152,12 +109,13 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 	fixed_t height, realbot;
 	lightlist_t *light;
 	r_lightlist_t *rlight;
-	void (*colfunc_2s)(column_t *);
+	void (*colfunc_2s)(column_t *, unsigned);
 	line_t *ldef;
 	sector_t *front, *back;
 	INT32 times, repeats;
 	INT64 overflow_test;
 	INT32 range;
+	unsigned lengthcol;
 
 	// Calculate light table.
 	// Use different light tables
@@ -214,26 +172,15 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 	rw_scalestep = scalestep;
 	spryscale = scale1 + (x1 - ds->x1)*rw_scalestep;
 
-	// Texture must be cached before setting colfunc_2s,
-	// otherwise texture[texnum]->holes may be false when it shouldn't be
+	// Texture must be cached
 	R_CheckTextureCache(texnum);
-	// handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures
-	// are not stored per-column with post info in SRB2
-	if (textures[texnum]->holes)
-	{
-		if (textures[texnum]->flip & 2) // vertically flipped?
-		{
-			colfunc_2s = R_DrawFlippedMaskedColumn;
-			lengthcol = textures[texnum]->height;
-		}
-		else
-			colfunc_2s = R_DrawMaskedColumn; // render the usual 2sided single-patch packed texture
-	}
+
+	if (textures[texnum]->flip & 2) // vertically flipped?
+		colfunc_2s = R_DrawFlippedMaskedColumn;
 	else
-	{
-		colfunc_2s = R_Render2sidedMultiPatchColumn; // render multipatch with no holes (no post_t info)
-		lengthcol = textures[texnum]->height;
-	}
+		colfunc_2s = R_DrawMaskedColumn; // render the usual 2sided single-patch packed texture
+
+	lengthcol = textures[texnum]->height;
 
 	// Setup lighting based on the presence/lack-of 3D floors.
 	dc_numlights = 0;
@@ -405,7 +352,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 				windowbottom = realbot;
 
 				// draw the texture
-				col = (column_t *)((UINT8 *)R_GetColumn(texnum, (maskedtexturecol[dc_x] >> FRACBITS)) - 3);
+				col = R_GetColumn(texnum, maskedtexturecol[dc_x] >> FRACBITS);
 
 				for (i = 0; i < dc_numlights; i++)
 				{
@@ -444,7 +391,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 					if (windowbottom >= realbot)
 					{
 						windowbottom = realbot;
-						colfunc_2s(col);
+						colfunc_2s(col, lengthcol);
 						for (i++; i < dc_numlights; i++)
 						{
 							rlight = &dc_lightlist[i];
@@ -453,13 +400,13 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 
 						continue;
 					}
-					colfunc_2s(col);
+					colfunc_2s(col, lengthcol);
 					windowtop = windowbottom + 1;
 					dc_colormap = rlight->rcolormap;
 				}
 				windowbottom = realbot;
 				if (windowtop < windowbottom)
-					colfunc_2s(col);
+					colfunc_2s(col, lengthcol);
 
 				spryscale += rw_scalestep;
 				continue;
@@ -480,8 +427,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 			dc_iscale = FixedMul(ds->invscale[dc_x], wall_scaley);
 
 			// draw the texture
-			col = (column_t *)((UINT8 *)R_GetColumn(texnum, (maskedtexturecol[dc_x] >> FRACBITS)) - 3);
-			colfunc_2s(col);
+			col = R_GetColumn(texnum, maskedtexturecol[dc_x] >> FRACBITS);
+			colfunc_2s(col, lengthcol);
 
 			spryscale += rw_scalestep;
 		}
@@ -490,10 +437,10 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 }
 
 // Loop through R_DrawMaskedColumn calls
-static void R_DrawRepeatMaskedColumn(column_t *col)
+static void R_DrawRepeatMaskedColumn(column_t *col, unsigned lengthcol)
 {
 	while (sprtopscreen < sprbotscreen) {
-		R_DrawMaskedColumn(col);
+		R_DrawMaskedColumn(col, lengthcol);
 		if ((INT64)sprtopscreen + dc_texheight*spryscale > (INT64)INT32_MAX) // prevent overflow
 			sprtopscreen = INT32_MAX;
 		else
@@ -501,10 +448,10 @@ static void R_DrawRepeatMaskedColumn(column_t *col)
 	}
 }
 
-static void R_DrawRepeatFlippedMaskedColumn(column_t *col)
+static void R_DrawRepeatFlippedMaskedColumn(column_t *col, unsigned lengthcol)
 {
 	do {
-		R_DrawFlippedMaskedColumn(col);
+		R_DrawFlippedMaskedColumn(col, lengthcol);
 		sprtopscreen += dc_texheight*spryscale;
 	} while (sprtopscreen < sprbotscreen);
 }
@@ -554,8 +501,9 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	boolean do_texture_skew;
 	boolean dont_peg_bottom;
 	fixed_t wall_scalex, wall_scaley;
+	unsigned lengthcol;
 
-	void (*colfunc_2s) (column_t *);
+	void (*colfunc_2s) (column_t *, unsigned);
 
 	// Calculate light table.
 	// Use different light tables
@@ -788,26 +736,15 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 
 	dc_texturemid += offsetvalue;
 
-	// Texture must be cached before setting colfunc_2s,
-	// otherwise texture[texnum]->holes may be false when it shouldn't be
+	// Texture must be cached
 	R_CheckTextureCache(texnum);
-	//faB: handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures
-	//     are not stored per-column with post info anymore in Doom Legacy
-	if (textures[texnum]->holes)
-	{
-		if (textures[texnum]->flip & 2) // vertically flipped?
-		{
-			colfunc_2s = R_DrawRepeatFlippedMaskedColumn;
-			lengthcol = textures[texnum]->height;
-		}
-		else
-			colfunc_2s = R_DrawRepeatMaskedColumn; // render the usual 2sided single-patch packed texture
-	}
+
+	if (textures[texnum]->flip & 2) // vertically flipped?
+		colfunc_2s = R_DrawRepeatFlippedMaskedColumn;
 	else
-	{
-		colfunc_2s = R_Render2sidedMultiPatchColumn;        //render multipatch with no holes (no post_t info)
-		lengthcol = textures[texnum]->height;
-	}
+		colfunc_2s = R_DrawRepeatMaskedColumn; // render the usual 2sided single-patch packed texture
+
+	lengthcol = textures[texnum]->height;
 
 	// Set heights according to plane, or slope, whichever
 	{
@@ -873,7 +810,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 		dc_iscale = FixedMul(0xffffffffu / (unsigned)spryscale, wall_scaley);
 
 		// Get data for the column
-		col = (column_t *)((UINT8 *)R_GetColumn(texnum, (thicksidecol[dc_x] >> FRACBITS)) - 3);
+		col = R_GetColumn(texnum, (thicksidecol[dc_x] >> FRACBITS));
 
 		// SoM: New code does not rely on R_DrawColumnShadowed_8 which
 		// will (hopefully) put less strain on the stack.
@@ -965,7 +902,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 				{
 					windowbottom = sprbotscreen;
 					// draw the texture
-					colfunc_2s (col);
+					colfunc_2s (col, lengthcol);
 					for (i++; i < dc_numlights; i++)
 					{
 						rlight = &dc_lightlist[i];
@@ -976,7 +913,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 					continue;
 				}
 				// draw the texture
-				colfunc_2s (col);
+				colfunc_2s (col, lengthcol);
 				if (solid)
 					windowtop = bheight;
 				else
@@ -987,7 +924,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 			windowbottom = sprbotscreen;
 			// draw the texture, if there is any space left
 			if (windowtop < windowbottom)
-				colfunc_2s (col);
+				colfunc_2s (col, lengthcol);
 
 			spryscale += rw_scalestep;
 			continue;
@@ -1007,7 +944,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 			dc_colormap = frontsector->extra_colormap->colormap + (dc_colormap - colormaps);
 
 		// draw the texture
-		colfunc_2s (col);
+		colfunc_2s (col, lengthcol);
 		spryscale += rw_scalestep;
 	}
 	colfunc = colfuncs[BASEDRAWFUNC];
@@ -1078,6 +1015,13 @@ static void R_RenderSegLoop (void)
 	INT32     bottom;
 	INT32     i;
 
+	if (midtexture)
+		R_CheckTextureCache(midtexture);
+	if (toptexture)
+		R_CheckTextureCache(toptexture);
+	if (bottomtexture)
+		R_CheckTextureCache(bottomtexture);
+
 	for (; rw_x < rw_stopx; rw_x++)
 	{
 		// mark floor / ceiling areas
@@ -1309,7 +1253,7 @@ static void R_RenderSegLoop (void)
 				dc_yh = yh;
 				dc_texturemid = rw_midtexturemid;
 				dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_midtexturescaley);
-				dc_source = R_GetColumn(midtexture, offset >> FRACBITS);
+				dc_source = R_GetColumn(midtexture, offset >> FRACBITS)->pixels;
 				dc_texheight = textureheight[midtexture]>>FRACBITS;
 
 				//profile stuff ---------------------------------------------------------
@@ -1378,7 +1322,7 @@ static void R_RenderSegLoop (void)
 						dc_yh = mid;
 						dc_texturemid = rw_toptexturemid;
 						dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_toptexturescaley);
-						dc_source = R_GetColumn(toptexture, offset >> FRACBITS);
+						dc_source = R_GetColumn(toptexture, offset >> FRACBITS)->pixels;
 						dc_texheight = textureheight[toptexture]>>FRACBITS;
 						colfunc();
 						ceilingclip[rw_x] = (INT16)mid;
@@ -1426,7 +1370,7 @@ static void R_RenderSegLoop (void)
 						dc_yh = yh;
 						dc_texturemid = rw_bottomtexturemid;
 						dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_bottomtexturescaley);
-						dc_source = R_GetColumn(bottomtexture, offset >> FRACBITS);
+						dc_source = R_GetColumn(bottomtexture, offset >> FRACBITS)->pixels;
 						dc_texheight = textureheight[bottomtexture]>>FRACBITS;
 						colfunc();
 						floorclip[rw_x] = (INT16)mid;
diff --git a/src/r_textures.c b/src/r_textures.c
index 8b47f455e8bc7e2e3a9cb766fb38b357b96d4091..27a9336319bdbb71a1d0d1c4747c5a0db73fb2b2 100644
--- a/src/r_textures.c
+++ b/src/r_textures.c
@@ -48,7 +48,7 @@ INT32 numtextures = 0; // total number of textures found,
 // size of following tables
 
 texture_t **textures = NULL;
-UINT32 **texturecolumnofs; // column offset lookup table for each texture
+column_t **texturecolumns; // columns for each texture
 UINT8 **texturecache; // graphics data for each generated full-size texture
 
 INT32 *texturewidth;
@@ -61,6 +61,7 @@ static struct {
 	char name[9];
 	UINT32 hash;
 	INT32 id;
+	UINT8 type;
 } *tidcache = NULL;
 static INT32 tidcachelen = 0;
 
@@ -78,24 +79,20 @@ static INT32 tidcachelen = 0;
 // R_DrawColumnInCache
 // Clip and draw a column from a patch into a cached post.
 //
-static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
+static void R_DrawColumnInCache(column_t *column, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight, UINT8 *opaque_pixels)
 {
 	INT32 count, position;
 	UINT8 *source;
-	INT32 topdelta, prevdelta = -1;
 	INT32 originy = originPatch->originy;
 
 	(void)patchheight; // This parameter is unused
 
-	while (patch->topdelta != 0xff)
+	for (unsigned i = 0; i < column->num_posts; i++)
 	{
-		topdelta = patch->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		source = (UINT8 *)patch + 3;
-		count = patch->length;
-		position = originy + topdelta;
+		post_t *post = &column->posts[i];
+		source = column->pixels + post->data_offset;
+		count = post->length;
+		position = originy + post->topdelta;
 
 		if (position < 0)
 		{
@@ -108,9 +105,10 @@ static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, texpatch_t
 			count = cacheheight - position;
 
 		if (count > 0)
+		{
 			M_Memcpy(cache + position, source, count);
-
-		patch = (column_t *)((UINT8 *)patch + patch->length + 4);
+			memset(opaque_pixels + position, true, count);
+		}
 	}
 }
 
@@ -118,22 +116,20 @@ static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, texpatch_t
 // R_DrawFlippedColumnInCache
 // Similar to R_DrawColumnInCache; it draws the column inverted, however.
 //
-static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
+static void R_DrawFlippedColumnInCache(column_t *column, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight, UINT8 *opaque_pixels)
 {
 	INT32 count, position;
 	UINT8 *source, *dest;
-	INT32 topdelta, prevdelta = -1;
 	INT32 originy = originPatch->originy;
+	INT32 topdelta;
+	UINT8 *is_opaque;
 
-	while (patch->topdelta != 0xff)
+	for (unsigned i = 0; i < column->num_posts; i++)
 	{
-		topdelta = patch->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		topdelta = patchheight-patch->length-topdelta;
-		source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1)
-		count = patch->length;
+		post_t *post = &column->posts[i];
+		topdelta = patchheight - post->length - post->topdelta;
+		source = column->pixels + post->data_offset + (post->length - 1);
+		count = post->length;
 		position = originy + topdelta;
 
 		if (position < 0)
@@ -147,38 +143,38 @@ static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, tex
 			count = cacheheight - position;
 
 		dest = cache + position;
+		is_opaque = opaque_pixels + position;
+
 		if (count > 0)
 		{
 			for (; dest < cache + position + count; --source)
+			{
 				*dest++ = *source;
+				*is_opaque = true;
+			}
 		}
-
-		patch = (column_t *)((UINT8 *)patch + patch->length + 4);
 	}
 }
 
 //
 // R_DrawBlendColumnInCache
-// Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()).
+// Draws a translucent column into the cache.
 //
-static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
+static void R_DrawBlendColumnInCache(column_t *column, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight, UINT8 *opaque_pixels)
 {
 	INT32 count, position;
 	UINT8 *source, *dest;
-	INT32 topdelta, prevdelta = -1;
 	INT32 originy = originPatch->originy;
+	UINT8 *is_opaque;
 
 	(void)patchheight; // This parameter is unused
 
-	while (patch->topdelta != 0xff)
+	for (unsigned i = 0; i < column->num_posts; i++)
 	{
-		topdelta = patch->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		source = (UINT8 *)patch + 3;
-		count = patch->length;
-		position = originy + topdelta;
+		post_t *post = &column->posts[i];
+		source = column->pixels + post->data_offset;
+		count = post->length;
+		position = originy + post->topdelta;
 
 		if (position < 0)
 		{
@@ -191,14 +187,16 @@ static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpa
 			count = cacheheight - position;
 
 		dest = cache + position;
+		is_opaque = opaque_pixels + position;
+
 		if (count > 0)
 		{
 			for (; dest < cache + position + count; source++, dest++)
-				if (*source != 0xFF)
-					*dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha);
+			{
+				*dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha);
+				*is_opaque = true;
+			}
 		}
-
-		patch = (column_t *)((UINT8 *)patch + patch->length + 4);
 	}
 }
 
@@ -206,22 +204,20 @@ static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpa
 // R_DrawBlendFlippedColumnInCache
 // Similar to the one above except that the column is inverted.
 //
-static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
+static void R_DrawBlendFlippedColumnInCache(column_t *column, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight, UINT8 *opaque_pixels)
 {
 	INT32 count, position;
 	UINT8 *source, *dest;
-	INT32 topdelta, prevdelta = -1;
 	INT32 originy = originPatch->originy;
+	INT32 topdelta;
+	UINT8 *is_opaque;
 
-	while (patch->topdelta != 0xff)
+	for (unsigned i = 0; i < column->num_posts; i++)
 	{
-		topdelta = patch->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		topdelta = patchheight-patch->length-topdelta;
-		source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1)
-		count = patch->length;
+		post_t *post = &column->posts[i];
+		topdelta = patchheight - post->length - post->topdelta;
+		source = column->pixels + post->data_offset + (post->length - 1);
+		count = post->length;
 		position = originy + topdelta;
 
 		if (position < 0)
@@ -235,14 +231,16 @@ static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache
 			count = cacheheight - position;
 
 		dest = cache + position;
+		is_opaque = opaque_pixels + position;
+
 		if (count > 0)
 		{
 			for (; dest < cache + position + count; --source, dest++)
-				if (*source != 0xFF)
-					*dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha);
+			{
+				*dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha);
+				*is_opaque = true;
+			}
 		}
-
-		patch = (column_t *)((UINT8 *)patch + patch->length + 4);
 	}
 }
 
@@ -252,7 +250,7 @@ static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache
 // Allocate space for full size texture, either single patch or 'composite'
 // Build the full textures from patches.
 // The texture caching system is a little more hungry of memory, but has
-// been simplified for the sake of highcolor (lol), dynamic ligthing, & speed.
+// been simplified for the sake of highcolor, dynamic lighting, & speed.
 //
 // This is not optimised, but it's supposed to be executed only once
 // per level, when enough memory is available.
@@ -261,24 +259,25 @@ UINT8 *R_GenerateTexture(size_t texnum)
 {
 	UINT8 *block;
 	UINT8 *blocktex;
+	UINT8 *temp_block;
 	texture_t *texture;
 	texpatch_t *patch;
-	softwarepatch_t *realpatch;
-	UINT8 *pdata;
 	int x, x1, x2, i, width, height;
 	size_t blocksize;
-	column_t *patchcol;
-	UINT8 *colofs;
-
-	UINT16 wadnum;
-	lumpnum_t lumpnum;
-	size_t lumplength;
+	unsigned *column_posts;
+	UINT8 *opaque_pixels;
+	column_t *columns, *temp_columns;
+	post_t *posts, *temp_posts = NULL;
+	size_t total_posts = 0;
+	size_t total_pixels = 0;
 
 	I_Assert(texnum <= (size_t)numtextures);
 	texture = textures[texnum];
 	I_Assert(texture != NULL);
 
-	// allocate texture column offset lookup
+	// Just create a composite one
+	if (texture->type == TEXTURETYPE_FLAT)
+		goto multipatch;
 
 	// single-patch textures can have holes in them and may be used on
 	// 2sided lines so they need to be kept in 'packed' format
@@ -286,72 +285,78 @@ UINT8 *R_GenerateTexture(size_t texnum)
 	// so check if there's holes and if not strip the posts.
 	if (texture->patchcount == 1)
 	{
-		boolean holey = false;
-		patch = texture->patches;
+		patch = &texture->patches[0];
 
-		wadnum = patch->wad;
-		lumpnum = patch->lump;
-		lumplength = W_LumpLengthPwad(wadnum, lumpnum);
-		pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
-		realpatch = (softwarepatch_t *)pdata;
+		UINT16 wadnum = patch->wad;
+		UINT16 lumpnum = patch->lump;
+		UINT8 *pdata;
+		softwarepatch_t *realpatch;
+		boolean holey = false;
 
 #ifndef NO_PNG_LUMPS
-		if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength))
-			goto multipatch;
-#endif
-#ifdef WALLFLATS
-		if (texture->type == TEXTURETYPE_FLAT)
+		UINT8 header[PNG_HEADER_SIZE];
+
+		W_ReadLumpHeaderPwad(wadnum, lumpnum, header, PNG_HEADER_SIZE, 0);
+
+		// Not worth converting
+		if (Picture_IsLumpPNG(header, W_LumpLengthPwad(wadnum, lumpnum)))
 			goto multipatch;
 #endif
 
+		pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
+		realpatch = (softwarepatch_t *)pdata;
+
 		// Check the patch for holes.
 		if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height))
 			holey = true;
-		colofs = (UINT8 *)realpatch->columnofs;
-		for (x = 0; x < texture->width && !holey; x++)
+		else
 		{
-			column_t *col = (column_t *)((UINT8 *)realpatch + LONG(*(UINT32 *)&colofs[x<<2]));
-			INT32 topdelta, prevdelta = -1, y = 0;
-			while (col->topdelta != 0xff)
+			UINT8 *colofs = (UINT8 *)realpatch->columnofs;
+			for (x = 0; x < texture->width; x++)
 			{
-				topdelta = col->topdelta;
-				if (topdelta <= prevdelta)
-					topdelta += prevdelta;
-				prevdelta = topdelta;
-				if (topdelta > y)
-					break;
-				y = topdelta + col->length + 1;
-				col = (column_t *)((UINT8 *)col + col->length + 4);
+				doompost_t *col = (doompost_t *)((UINT8 *)realpatch + LONG(*(UINT32 *)&colofs[x<<2]));
+				INT32 topdelta, prevdelta = -1, y = 0;
+				while (col->topdelta != 0xff)
+				{
+					topdelta = col->topdelta;
+					if (topdelta <= prevdelta)
+						topdelta += prevdelta;
+					prevdelta = topdelta;
+					if (topdelta > y)
+						break;
+					y = topdelta + col->length + 1;
+					col = (doompost_t *)((UINT8 *)col + col->length + 4);
+				}
+				if (y < texture->height)
+					holey = true; // this texture is HOLEy! D:
 			}
-			if (y < texture->height)
-				holey = true; // this texture is HOLEy! D:
 		}
 
 		// If the patch uses transparency, we have to save it this way.
 		if (holey)
 		{
-			texture->holes = true;
 			texture->flip = patch->flip;
-			blocksize = lumplength;
-			block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function
-				&texturecache[texnum]);
-			M_Memcpy(block, realpatch, blocksize);
+
+			Patch_CalcDataSizes(realpatch, &total_pixels, &total_posts);
+
+			blocksize = (sizeof(column_t) * texture->width) + (sizeof(post_t) * total_posts) + (sizeof(UINT8) * total_pixels);
 			texturememory += blocksize;
 
-			// use the patch's column lookup
-			colofs = (block + 8);
-			texturecolumnofs[texnum] = (UINT32 *)colofs;
+			block = Z_Calloc(blocksize, PU_STATIC, &texturecache[texnum]);
 			blocktex = block;
-			if (patch->flip & 1) // flip the patch horizontally
-			{
-				UINT8 *realcolofs = (UINT8 *)realpatch->columnofs;
-				for (x = 0; x < texture->width; x++)
-					*(UINT32 *)&colofs[x<<2] = realcolofs[( texture->width-1-x )<<2]; // swap with the offset of the other side of the texture
-			}
+
+			columns = (column_t *)(block + (sizeof(UINT8) * total_pixels));
+			posts = (post_t *)(block + (sizeof(UINT8) * total_pixels) + (sizeof(column_t) * texture->width));
+
+			texturecolumns[texnum] = columns;
+
+			// Handles flipping as well.
 			// we can't as easily flip the patch vertically sadly though,
 			//  we have wait until the texture itself is drawn to do that
-			for (x = 0; x < texture->width; x++)
-				*(UINT32 *)&colofs[x<<2] = LONG(LONG(*(UINT32 *)&colofs[x<<2]) + 3);
+			Patch_MakeColumns(realpatch, texture->width, texture->width, blocktex, columns, posts, patch->flip & 1);
+
+			Z_Free(pdata);
+
 			goto done;
 		}
 
@@ -360,69 +365,73 @@ UINT8 *R_GenerateTexture(size_t texnum)
 
 	// multi-patch textures (or 'composite')
 	multipatch:
-	texture->holes = false;
 	texture->flip = 0;
-	blocksize = (texture->width * 4) + (texture->width * texture->height);
-	texturememory += blocksize;
-	block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]);
 
-	memset(block, TRANSPARENTPIXEL, blocksize+1); // Transparency hack
+	// To make things easier, I just allocate WxH always
+	total_pixels = texture->width * texture->height;
 
-	// columns lookup table
-	colofs = block;
-	texturecolumnofs[texnum] = (UINT32 *)colofs;
+	opaque_pixels = Z_Calloc(total_pixels * sizeof(UINT8), PU_STATIC, NULL);
+	temp_columns = Z_Calloc(sizeof(column_t) * texture->width, PU_STATIC, NULL);
+	temp_block = Z_Calloc(total_pixels, PU_STATIC, NULL);
 
-	// texture data after the lookup table
-	blocktex = block + (texture->width*4);
+	for (x = 0; x < texture->width; x++)
+	{
+		column_t *column = &temp_columns[x];
+		column->num_posts = 0;
+		column->posts = NULL;
+		column->pixels = temp_block + (texture->height * x);
+	}
 
 	// Composite the columns together.
 	for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
 	{
-		boolean dealloc = true;
-		static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer.
+		static void (*columnDrawer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32, UINT8 *);
 		if (patch->style != AST_COPY)
-			ColumnDrawerPointer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache;
+			columnDrawer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache;
 		else
-			ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache;
+			columnDrawer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache;
 
-		wadnum = patch->wad;
-		lumpnum = patch->lump;
-		pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
-		lumplength = W_LumpLengthPwad(wadnum, lumpnum);
-		realpatch = (softwarepatch_t *)pdata;
-		dealloc = true;
+		UINT16 wadnum = patch->wad;
+		lumpnum_t lumpnum = patch->lump;
+		UINT8 *pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
+		patch_t *realpatch = NULL;
+		boolean free_patch = true;
 
 #ifndef NO_PNG_LUMPS
-		if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength))
-			realpatch = (softwarepatch_t *)Picture_PNGConvert((UINT8 *)realpatch, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
+		size_t lumplength = W_LumpLengthPwad(wadnum, lumpnum);
+		if (Picture_IsLumpPNG(pdata, lumplength))
+			realpatch = (patch_t *)Picture_PNGConvert(pdata, PICFMT_PATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
 		else
 #endif
-#ifdef WALLFLATS
 		if (texture->type == TEXTURETYPE_FLAT)
-			realpatch = (softwarepatch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_DOOMPATCH, 0, NULL, texture->width, texture->height, 0, 0, 0);
+			realpatch = (patch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, PICFLAGS_USE_TRANSPARENTPIXEL);
 		else
-#endif
 		{
-			(void)lumplength;
-			dealloc = false;
+			// If this patch has already been loaded, we just use it from the cache.
+			realpatch = W_GetCachedPatchNumPwad(wadnum, lumpnum);
+			free_patch = false;
+
+			// Otherwise, we load it here.
+			if (realpatch == NULL)
+				realpatch = W_CachePatchNumPwad(wadnum, lumpnum, PU_PATCH);
 		}
 
 		x1 = patch->originx;
-		width = SHORT(realpatch->width);
-		height = SHORT(realpatch->height);
+		width = realpatch->width;
+		height = realpatch->height;
 		x2 = x1 + width;
 
 		if (x1 > texture->width || x2 < 0)
 		{
-			if (dealloc)
-				Z_Free(realpatch);
+			if (free_patch)
+				Patch_Free(realpatch);
 			continue; // patch not located within texture's x bounds, ignore
 		}
 
 		if (patch->originy > texture->height || (patch->originy + height) < 0)
 		{
-			if (dealloc)
-				Z_Free(realpatch);
+			if (free_patch)
+				Patch_Free(realpatch);
 			continue; // patch not located within texture's y bounds, ignore
 		}
 
@@ -441,18 +450,88 @@ UINT8 *R_GenerateTexture(size_t texnum)
 
 		for (; x < x2; x++)
 		{
+			INT32 colx;
+
 			if (patch->flip & 1)
-				patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x]));
+				colx = (x1+width-1)-x;
 			else
-				patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1]));
+				colx = x-x1;
+
+			column_t *patchcol = &realpatch->columns[colx];
 
-			// generate column ofset lookup
-			*(UINT32 *)&colofs[x<<2] = LONG((x * texture->height) + (texture->width*4));
-			ColumnDrawerPointer(patchcol, block + LONG(*(UINT32 *)&colofs[x<<2]), patch, texture->height, height);
+			if (patchcol->num_posts > 0)
+				columnDrawer(patchcol, temp_columns[x].pixels, patch, texture->height, height, &opaque_pixels[x * texture->height]);
 		}
 
-		if (dealloc)
-			Z_Free(realpatch);
+		if (free_patch)
+			Patch_Free(realpatch);
+	}
+
+	// Now write the columns
+	column_posts = Z_Calloc(sizeof(unsigned) * texture->width, PU_STATIC, NULL);
+
+	for (x = 0; x < texture->width; x++)
+	{
+		post_t *post;
+		boolean was_opaque = false;
+
+		column_t *column = &temp_columns[x];
+
+		column_posts[x] = (unsigned)-1;
+
+		for (INT32 y = 0; y < texture->height; y++)
+		{
+			// End span if we have a transparent pixel
+			if (!opaque_pixels[(x * texture->height) + y])
+			{
+				was_opaque = false;
+				continue;
+			}
+
+			if (!was_opaque)
+			{
+				total_posts++;
+
+				temp_posts = Z_Realloc(temp_posts, sizeof(post_t) * total_posts, PU_CACHE, NULL);
+				post = &temp_posts[total_posts - 1];
+				post->topdelta = (size_t)y;
+				post->length = 0;
+				post->data_offset = (size_t)y;
+				if (column_posts[x] == (unsigned)-1)
+					column_posts[x] = total_posts - 1;
+				column->num_posts++;
+			}
+
+			was_opaque = true;
+
+			post->length++;
+		}
+	}
+
+	blocksize = (sizeof(column_t) * texture->width) + (sizeof(post_t) * total_posts) + (sizeof(UINT8) * total_pixels);
+	texturememory += blocksize;
+
+	block = Z_Calloc(blocksize, PU_STATIC, &texturecache[texnum]);
+	blocktex = block;
+
+	memcpy(blocktex, temp_block, total_pixels);
+
+	Z_Free(temp_block);
+
+	columns = (column_t *)(block + (sizeof(UINT8) * total_pixels));
+	posts = (post_t *)(block + (sizeof(UINT8) * total_pixels) + (sizeof(column_t) * texture->width));
+
+	memcpy(columns, temp_columns, sizeof(column_t) * texture->width);
+	memcpy(posts, temp_posts, sizeof(post_t) * total_posts);
+
+	texturecolumns[texnum] = columns;
+
+	for (x = 0; x < texture->width; x++)
+	{
+		column_t *column = &columns[x];
+		if (column->num_posts > 0)
+			column->posts = &posts[column_posts[x]];
+		column->pixels = blocktex + (texture->height * x);
 	}
 
 done:
@@ -461,28 +540,42 @@ done:
 	return blocktex;
 }
 
-//
-// R_GenerateTextureAsFlat
-//
-// Generates a flat picture for a texture.
-//
-UINT8 *R_GenerateTextureAsFlat(size_t texnum)
+UINT8 *R_GetFlatForTexture(size_t texnum)
 {
+	if (texnum >= (unsigned)numtextures)
+		return NULL;
+
 	texture_t *texture = textures[texnum];
-	UINT8 *converted = NULL;
-	size_t size = (texture->width * texture->height);
+	if (texture->flat != NULL)
+		return texture->flat;
 
-	// The flat picture for this texture was not generated yet.
-	if (!texture->flat)
+	// Special case: Textures that are flats don't need to be converted FROM a texture INTO a flat.
+	if (texture->type == TEXTURETYPE_FLAT)
 	{
-		// Well, let's do it now, then.
-		texture->flat = Z_Malloc(size, PU_STATIC, NULL);
+		texpatch_t *patch = &texture->patches[0];
+		UINT16 wadnum = patch->wad;
+		lumpnum_t lumpnum = patch->lump;
+		UINT8 *pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
+		size_t lumplength = W_LumpLengthPwad(wadnum, lumpnum);
+
+#ifndef NO_PNG_LUMPS
+		if (Picture_IsLumpPNG(pdata, lumplength))
+			texture->flat = Picture_PNGConvert(pdata, PICFMT_FLAT, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
+		else
+#endif
+		{
+			texture->flat = Z_Malloc(lumplength, PU_STATIC, NULL);
+			memcpy(texture->flat, pdata, lumplength);
+		}
+
+		Z_SetUser(texture->flat, &texture->flat);
 
-		// Picture_TextureToFlat handles everything for us.
-		converted = (UINT8 *)Picture_TextureToFlat(texnum);
-		M_Memcpy(texture->flat, converted, size);
-		Z_Free(converted);
+		Z_Free(pdata);
 	}
+	else
+		texture->flat = (UINT8 *)Picture_TextureToFlat(texnum);
+
+	flatmemory += texture->width * texture->height;
 
 	return texture->flat;
 }
@@ -513,110 +606,28 @@ void R_CheckTextureCache(INT32 tex)
 		R_GenerateTexture(tex);
 }
 
-//
-// R_GetColumn
-//
-UINT8 *R_GetColumn(fixed_t tex, INT32 col)
+column_t *R_GetColumn(fixed_t tex, INT32 col)
 {
-	UINT8 *data;
 	INT32 width = texturewidth[tex];
-
 	if (width & (width - 1))
 		col = (UINT32)col % width;
 	else
 		col &= (width - 1);
 
-	data = texturecache[tex];
-	if (!data)
-		data = R_GenerateTexture(tex);
-
-	return data + LONG(texturecolumnofs[tex][col]);
+	return &texturecolumns[tex][col];
 }
 
-void *R_GetFlat(lumpnum_t flatlumpnum)
+INT32 R_GetTextureNumForFlat(levelflat_t *levelflat)
 {
-	return W_CacheLumpNum(flatlumpnum, PU_CACHE);
+	return texturetranslation[levelflat->texture_id];
 }
 
-//
-// R_GetLevelFlat
-//
-// If needed, convert a texture or patch to a flat.
-//
-void *R_GetLevelFlat(levelflat_t *levelflat)
+void *R_GetFlat(levelflat_t *levelflat)
 {
-	boolean isleveltexture = (levelflat->type == LEVELFLAT_TEXTURE);
-	texture_t *texture = (isleveltexture ? textures[levelflat->u.texture.num] : NULL);
-	boolean texturechanged = (isleveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false);
-	UINT8 *flatdata = NULL;
-
-	// Check if the texture changed.
-	if (isleveltexture && (!texturechanged))
-	{
-		if (texture->flat)
-		{
-			flatdata = texture->flat;
-			ds_flatwidth = texture->width;
-			ds_flatheight = texture->height;
-			texturechanged = false;
-		}
-		else
-			texturechanged = true;
-	}
-
-	// If the texture changed, or the flat wasn't generated, convert.
-	if (levelflat->picture == NULL || texturechanged)
-	{
-		// Level texture
-		if (isleveltexture)
-		{
-			levelflat->picture = R_GenerateTextureAsFlat(levelflat->u.texture.num);
-			ds_flatwidth = levelflat->width = texture->width;
-			ds_flatheight = levelflat->height = texture->height;
-		}
-		else
-		{
-#ifndef NO_PNG_LUMPS
-			if (levelflat->type == LEVELFLAT_PNG)
-			{
-				INT32 pngwidth, pngheight;
-
-				levelflat->picture = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0);
-				levelflat->width = (UINT16)pngwidth;
-				levelflat->height = (UINT16)pngheight;
-
-				ds_flatwidth = levelflat->width;
-				ds_flatheight = levelflat->height;
-			}
-			else
-#endif
-			if (levelflat->type == LEVELFLAT_PATCH)
-			{
-				UINT8 *converted;
-				size_t size;
-				softwarepatch_t *patch = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE);
-
-				levelflat->width = ds_flatwidth = SHORT(patch->width);
-				levelflat->height = ds_flatheight = SHORT(patch->height);
-
-				levelflat->picture = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL);
-				converted = Picture_FlatConvert(PICFMT_DOOMPATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, SHORT(patch->topoffset), SHORT(patch->leftoffset), 0);
-				M_Memcpy(levelflat->picture, converted, size);
-				Z_Free(converted);
-			}
-		}
-	}
-	else
-	{
-		ds_flatwidth = levelflat->width;
-		ds_flatheight = levelflat->height;
-	}
-
-	levelflat->u.texture.lastnum = levelflat->u.texture.num;
+	if (levelflat->type == LEVELFLAT_NONE)
+		return NULL;
 
-	if (flatdata == NULL)
-		flatdata = levelflat->picture;
-	return flatdata;
+	return R_GetFlatForTexture(R_GetTextureNumForFlat(levelflat));
 }
 
 //
@@ -624,12 +635,12 @@ void *R_GetLevelFlat(levelflat_t *levelflat)
 //
 boolean R_CheckPowersOfTwo(void)
 {
-	boolean wpow2 = !(ds_flatwidth & (ds_flatwidth - 1));
-	boolean hpow2 = !(ds_flatheight & (ds_flatheight - 1));
-
 	if (ds_flatwidth > 2048 || ds_flatheight > 2048)
 		return false;
 
+	boolean wpow2 = !(ds_flatwidth & (ds_flatwidth - 1));
+	boolean hpow2 = !(ds_flatheight & (ds_flatheight - 1));
+
 	return ds_flatwidth == ds_flatheight && wpow2 && hpow2;
 }
 
@@ -722,98 +733,114 @@ void R_FlushTextureCache(void)
 
 	if (numtextures)
 		for (i = 0; i < numtextures; i++)
+		{
+			Z_Free(textures[i]->flat);
 			Z_Free(texturecache[i]);
+		}
 }
 
 // Need these prototypes for later; defining them here instead of r_textures.h so they're "private"
 int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum);
 void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index);
 
-#ifdef WALLFLATS
+static void R_AddSinglePatchTexture(INT32 i, UINT16 wadnum, UINT16 lumpnum, INT16 width, INT16 height, unsigned type)
+{
+	texture_t *texture = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
+
+	// Set texture properties.
+	M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
+	texture->hash = quickncasehash(texture->name, 8);
+
+	texture->width = width;
+	texture->height = height;
+
+	texture->type = type;
+	texture->patchcount = 1;
+	texture->flip = 0;
+
+	// Allocate information for the texture's patches.
+	texpatch_t *patch = &texture->patches[0];
+
+	patch->originx = patch->originy = 0;
+	patch->wad = wadnum;
+	patch->lump = lumpnum;
+	patch->flip = 0;
+
+	texturewidth[i] = texture->width;
+	textureheight[i] = texture->height << FRACBITS;
+
+	textures[i] = texture;
+}
+
 static INT32
 Rloadflats (INT32 i, INT32 w)
 {
-	UINT16 j;
-	UINT16 texstart, texend;
-	texture_t *texture;
-	texpatch_t *patch;
-	UINT8 header[PNG_HEADER_SIZE];
+	UINT16 j, numlumps = 0;
+	UINT16 texstart = 0, texend = 0;
+	UINT32 *list = NULL;
 
-	// Yes
+	// Get every lump inside the Flats/ folder
 	if (W_FileHasFolders(wadfiles[w]))
 	{
-		texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0);
-		texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart);
+		W_GetFolderLumpsPwad("Flats/", (UINT16)w, &list, NULL, &numlumps);
 	}
 	else
 	{
+		// Else, get the lumps between F_START/F_END markers
 		texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0);
 		texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart);
+		if (texstart == INT16_MAX || texend == INT16_MAX)
+			return i;
+
+		numlumps = texend - texstart;
 	}
 
-	if (!( texstart == INT16_MAX || texend == INT16_MAX ))
+	// Now add every entry as a texture
+	for (j = 0; j < numlumps; j++)
 	{
-		// Work through each lump between the markers in the WAD.
-		for (j = 0; j < (texend - texstart); j++)
-		{
-			UINT16 wadnum = (UINT16)w;
-			lumpnum_t lumpnum = texstart + j;
-			size_t lumplength;
-			size_t flatsize;
+		UINT16 wadnum = list ? WADFILENUM(list[j]) : (UINT16)w;
+		UINT16 lumpnum = list ? LUMPNUM(list[j]) : (texstart + j);
 
-			if (W_FileHasFolders(wadfiles[w]))
-			{
-				if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder
-					continue; // If it is then SKIP IT
-			}
+		size_t lumplength = W_LumpLengthPwad(wadnum, lumpnum);
+		size_t flatsize = R_GetFlatSize(lumplength);
 
-			W_ReadLumpHeaderPwad(wadnum, lumpnum, header, sizeof header, 0);
-			lumplength = W_LumpLengthPwad(wadnum, lumpnum);
-			flatsize = R_GetFlatSize(lumplength);
+		INT16 width = flatsize, height = flatsize;
 
-			//CONS_Printf("\n\"%s\" is a flat, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),flatsize,flatsize);
-			texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
+#ifndef NO_PNG_LUMPS
+		UINT8 header[PNG_HEADER_SIZE];
 
-			// Set texture properties.
-			M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
-			texture->hash = quickncasehash(texture->name, 8);
+		W_ReadLumpHeaderPwad(wadnum, lumpnum, header, sizeof header, 0);
 
-#ifndef NO_PNG_LUMPS
-			if (Picture_IsLumpPNG(header, lumplength))
+		if (Picture_IsLumpPNG(header, lumplength))
+		{
+			INT32 texw, texh;
+			UINT8 *flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
+			if (Picture_PNGDimensions((UINT8 *)flatlump, &texw, &texh, NULL, NULL, lumplength))
 			{
-				UINT8 *flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
-				INT32 width, height;
-				Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, NULL, NULL, lumplength);
-				texture->width = (INT16)width;
-				texture->height = (INT16)height;
-				Z_Free(flatlump);
+				width = (INT16)width;
+				height = (INT16)height;
 			}
 			else
+			{
+				width = 1;
+				height = 1;
+			}
+			Z_Free(flatlump);
+		}
 #endif
-				texture->width = texture->height = flatsize;
-
-			texture->type = TEXTURETYPE_FLAT;
-			texture->patchcount = 1;
-			texture->holes = false;
-			texture->flip = 0;
 
-			// Allocate information for the texture's patches.
-			patch = &texture->patches[0];
+		// printf("\"%s\" (wad: %u, lump: %u) is a flat, dimensions %d x %d\n",W_CheckNameForNumPwad(wadnum,lumpnum),wadnum,lumpnum,width,height);
 
-			patch->originx = patch->originy = 0;
-			patch->wad = (UINT16)w;
-			patch->lump = texstart + j;
-			patch->flip = 0;
+		R_AddSinglePatchTexture(i, wadnum, lumpnum, width, height, TEXTURETYPE_FLAT);
 
-			texturewidth[i] = texture->width;
-			textureheight[i] = texture->height << FRACBITS;
-			i++;
-		}
+		i++;
 	}
 
+	if (list)
+		Z_Free(list);
+
 	return i;
 }
-#endif/*WALLFLATS*/
 
 #define TX_START "TX_START"
 #define TX_END "TX_END"
@@ -821,17 +848,16 @@ Rloadflats (INT32 i, INT32 w)
 static INT32
 Rloadtextures (INT32 i, INT32 w)
 {
-	UINT16 j;
-	UINT16 texstart, texend, texturesLumpPos;
-	texture_t *texture;
-	texpatch_t *patch;
-	softwarepatch_t patchlump;
+	UINT16 j, numlumps = 0;
+	UINT16 texstart = 0, texend = 0;
+	UINT16 texturesLumpPos;
+	UINT32 *list = NULL;
 
-	// Get the lump numbers for the markers in the WAD, if they exist.
+	// Get every lump inside the Textures/ folder
 	if (W_FileHasFolders(wadfiles[w]))
 	{
-		texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0);
-		texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart);
+		W_GetFolderLumpsPwad("Textures/", (UINT16)w, &list, NULL, &numlumps);
+
 		texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
 		while (texturesLumpPos != INT16_MAX)
 		{
@@ -839,80 +865,45 @@ Rloadtextures (INT32 i, INT32 w)
 			texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1);
 		}
 	}
+	// Else, get the lumps between TX_START/TX_END markers
 	else
 	{
-		texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0);
-		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 ))
-	{
-		// Work through each lump between the markers in the WAD.
-		for (j = 0; j < (texend - texstart); j++)
-		{
-			UINT16 wadnum = (UINT16)w;
-			lumpnum_t lumpnum = texstart + j;
-#ifndef NO_PNG_LUMPS
-			size_t lumplength;
-#endif
-
-			if (W_FileHasFolders(wadfiles[w]))
-			{
-				if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder
-					continue; // If it is then SKIP IT
-			}
 
-			W_ReadLumpHeaderPwad(wadnum, lumpnum, &patchlump, PNG_HEADER_SIZE, 0);
-#ifndef NO_PNG_LUMPS
-			lumplength = W_LumpLengthPwad(wadnum, lumpnum);
-#endif
+		texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0);
+		texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, texstart);
+		if (texstart == INT16_MAX || texend == INT16_MAX)
+			return i;
 
-			//CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height);
-			texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
+		numlumps = texend - texstart;
+	}
 
-			// Set texture properties.
-			M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
-			texture->hash = quickncasehash(texture->name, 8);
+	// Now add every entry as a texture
+	for (j = 0; j < numlumps; j++)
+	{
+		UINT16 wadnum = list ? WADFILENUM(list[j]) : (UINT16)w;
+		UINT16 lumpnum = list ? LUMPNUM(list[j]) : (texstart + j);
 
-#ifndef NO_PNG_LUMPS
-			if (Picture_IsLumpPNG((UINT8 *)&patchlump, lumplength))
-			{
-				UINT8 *png = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
-				INT32 width, height;
-				Picture_PNGDimensions(png, &width, &height, NULL, NULL, lumplength);
-				texture->width = (INT16)width;
-				texture->height = (INT16)height;
-				Z_Free(png);
-			}
-			else
-#endif
-			{
-				texture->width = SHORT(patchlump.width);
-				texture->height = SHORT(patchlump.height);
-			}
+		INT16 width = 0, height = 0;
 
-			texture->type = TEXTURETYPE_SINGLEPATCH;
-			texture->patchcount = 1;
-			texture->holes = false;
-			texture->flip = 0;
+		if (!W_ReadPatchHeaderPwad(wadnum, lumpnum, &width, &height, NULL, NULL))
+		{
+			width = 1;
+			height = 1;
+		}
 
-			// Allocate information for the texture's patches.
-			patch = &texture->patches[0];
+		// printf("\"%s\" (wad: %u, lump: %u) is a single patch, dimensions %d x %d\n",W_CheckNameForNumPwad(wadnum,lumpnum),wadnum,lumpnum,width,height);
 
-			patch->originx = patch->originy = 0;
-			patch->wad = (UINT16)w;
-			patch->lump = texstart + j;
-			patch->flip = 0;
+		R_AddSinglePatchTexture(i, wadnum, lumpnum, width, height, TEXTURETYPE_SINGLEPATCH);
 
-			texturewidth[i] = texture->width;
-			textureheight[i] = texture->height << FRACBITS;
-			i++;
-		}
+		i++;
 	}
 
+	if (list)
+		Z_Free(list);
+
 	return i;
 }
 
@@ -923,37 +914,17 @@ count_range
 		const char * folder,
 		UINT16 wadnum)
 {
-	UINT16 j;
-	UINT16 texstart, texend;
 	INT32 count = 0;
 
-	// Count flats
 	if (W_FileHasFolders(wadfiles[wadnum]))
-	{
-		texstart = W_CheckNumForFolderStartPK3(folder, wadnum, 0);
-		texend = W_CheckNumForFolderEndPK3(folder, wadnum, texstart);
-	}
+		count += W_CountFolderLumpsPwad(folder, wadnum);
 	else
 	{
-		texstart = W_CheckNumForMarkerStartPwad(marker_start, wadnum, 0);
-		texend = W_CheckNumForNamePwad(marker_end, wadnum, texstart);
-	}
+		UINT16 texstart = W_CheckNumForMarkerStartPwad(marker_start, wadnum, 0);
+		UINT16 texend = W_CheckNumForNamePwad(marker_end, wadnum, texstart);
 
-	if (texstart != INT16_MAX && texend != INT16_MAX)
-	{
-		// PK3s have subfolders, so we can't just make a simple sum
-		if (W_FileHasFolders(wadfiles[wadnum]))
-		{
-			for (j = texstart; j < texend; j++)
-			{
-				if (!W_IsLumpFolder(wadnum, j)) // Check if lump is a folder; if not, then count it
-					count++;
-			}
-		}
-		else // Add all the textures between markers
-		{
+		if (texstart != INT16_MAX && texend != INT16_MAX)
 			count += (texend - texstart);
-		}
 	}
 
 	return count;
@@ -972,9 +943,7 @@ static INT32 R_CountTextures(UINT16 wadnum)
 	// This system will allocate memory for all duplicate/patched textures even if it never uses them,
 	// but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures.
 
-#ifdef WALLFLATS
 	count += count_range("F_START", "F_END", "flats/", wadnum);
-#endif
 
 	// Count the textures from TEXTURES lumps
 	texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", wadnum, 0);
@@ -1016,7 +985,7 @@ static void R_AllocateTextures(INT32 add)
 	recallocuser(&textures, oldsize, newsize);
 
 	// Allocate texture column offset table.
-	recallocuser(&texturecolumnofs, oldsize, newsize);
+	recallocuser(&texturecolumns, oldsize, newsize);
 	// Allocate texture referencing cache.
 	recallocuser(&texturecache, oldsize, newsize);
 	// Allocate texture width table.
@@ -1044,9 +1013,7 @@ static void R_AllocateTextures(INT32 add)
 
 static INT32 R_DefineTextures(INT32 i, UINT16 w)
 {
-#ifdef WALLFLATS
 	i = Rloadflats(i, w);
-#endif
 	return Rloadtextures(i, w);
 }
 
@@ -1563,55 +1530,6 @@ void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex)
 	Z_Free((void *)texturesText);
 }
 
-// Search for flat name.
-lumpnum_t R_GetFlatNumForName(const char *name)
-{
-	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_CheckNumForMarkerStartPwad("F_START", (UINT16)i, 0)) == INT16_MAX)
-			{
-				if ((start = W_CheckNumForMarkerStartPwad("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:
-		case RET_FOLDER:
-			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;
-	}
-
-	return lump;
-}
-
 void R_ClearTextureNumCache(boolean btell)
 {
 	if (tidcache)
@@ -1622,6 +1540,20 @@ void R_ClearTextureNumCache(boolean btell)
 	tidcachelen = 0;
 }
 
+static void AddTextureToCache(const char *name, UINT32 hash, INT32 id, UINT8 type)
+{
+	tidcachelen++;
+	Z_Realloc(tidcache, tidcachelen * sizeof(*tidcache), PU_STATIC, &tidcache);
+	strncpy(tidcache[tidcachelen-1].name, name, 8);
+	tidcache[tidcachelen-1].name[8] = '\0';
+#ifndef ZDEBUG
+	CONS_Debug(DBG_SETUP, "texture #%s: %s\n", sizeu1(tidcachelen), tidcache[tidcachelen-1].name);
+#endif
+	tidcache[tidcachelen-1].hash = hash;
+	tidcache[tidcachelen-1].id = id;
+	tidcache[tidcachelen-1].type = type;
+}
+
 //
 // R_CheckTextureNumForName
 //
@@ -1643,19 +1575,10 @@ INT32 R_CheckTextureNumForName(const char *name)
 			return tidcache[i].id;
 
 	// Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier
-	//for (i = 0; i < numtextures; i++) <- old
-	for (i = (numtextures - 1); i >= 0; i--) // <- new
+	for (i = numtextures - 1; i >= 0; i--)
 		if (textures[i]->hash == hash && !strncasecmp(textures[i]->name, name, 8))
 		{
-			tidcachelen++;
-			Z_Realloc(tidcache, tidcachelen * sizeof(*tidcache), PU_STATIC, &tidcache);
-			strncpy(tidcache[tidcachelen-1].name, name, 8);
-			tidcache[tidcachelen-1].name[8] = '\0';
-#ifndef ZDEBUG
-			CONS_Debug(DBG_SETUP, "texture #%s: %s\n", sizeu1(tidcachelen), tidcache[tidcachelen-1].name);
-#endif
-			tidcache[tidcachelen-1].hash = hash;
-			tidcache[tidcachelen-1].id = i;
+			AddTextureToCache(name, hash, i, textures[i]->type);
 			return i;
 		}
 
@@ -1712,3 +1635,29 @@ INT32 R_TextureNumForName(const char *name)
 	}
 	return i;
 }
+
+// Like R_CheckTextureNumForName, but only looks in the flat namespace specifically.
+INT32 R_CheckFlatNumForName(const char *name)
+{
+	INT32 i;
+	UINT32 hash;
+
+	// "NoTexture" marker.
+	if (name[0] == '-')
+		return 0;
+
+	hash = quickncasehash(name, 8);
+
+	for (i = 0; i < tidcachelen; i++)
+		if (tidcache[i].type == TEXTURETYPE_FLAT && tidcache[i].hash == hash && !strncasecmp(tidcache[i].name, name, 8))
+			return tidcache[i].id;
+
+	for (i = numtextures - 1; i >= 0; i--)
+		if (textures[i]->hash == hash && !strncasecmp(textures[i]->name, name, 8) && textures[i]->type == TEXTURETYPE_FLAT)
+		{
+			AddTextureToCache(name, hash, i, TEXTURETYPE_FLAT);
+			return i;
+		}
+
+	return -1;
+}
diff --git a/src/r_textures.h b/src/r_textures.h
index 394b4f8243a7fa7a0de120f2ff893bc1dc9f0702..eb68ec09f21d4fe8d847b048ac12ba9bd3a1817d 100644
--- a/src/r_textures.h
+++ b/src/r_textures.h
@@ -42,9 +42,7 @@ enum
 	TEXTURETYPE_UNKNOWN,
 	TEXTURETYPE_SINGLEPATCH,
 	TEXTURETYPE_COMPOSITE,
-#ifdef WALLFLATS
-	TEXTURETYPE_FLAT,
-#endif
+	TEXTURETYPE_FLAT
 };
 
 // A texture_t describes a rectangular texture,
@@ -55,9 +53,8 @@ typedef struct
 	// Keep name for switch changing, etc.
 	char name[8];
 	UINT32 hash;
-	UINT8 type; // TEXTURETYPE_
+	UINT8 type; // TEXTURETYPE_*
 	INT16 width, height;
-	boolean holes;
 	UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both
 	void *flat; // The texture, as a flat.
 
@@ -72,7 +69,7 @@ extern texture_t **textures;
 extern INT32 *texturewidth;
 extern fixed_t *textureheight; // needed for texture pegging
 
-extern UINT32 **texturecolumnofs; // column offset lookup table for each texture
+extern column_t **texturecolumns; // columns for each texture
 extern UINT8 **texturecache; // graphics data for each generated full-size texture
 
 // Load TEXTURES definitions, create lookup tables
@@ -82,15 +79,16 @@ void R_FlushTextureCache(void);
 
 // Texture generation
 UINT8 *R_GenerateTexture(size_t texnum);
-UINT8 *R_GenerateTextureAsFlat(size_t texnum);
+UINT8 *R_GetFlatForTexture(size_t texnum);
 INT32 R_GetTextureNum(INT32 texnum);
 void R_CheckTextureCache(INT32 tex);
 void R_ClearTextureNumCache(boolean btell);
 
 // Retrieve texture data.
-void *R_GetLevelFlat(levelflat_t *levelflat);
-UINT8 *R_GetColumn(fixed_t tex, INT32 col);
-void *R_GetFlat(lumpnum_t flatnum);
+column_t *R_GetColumn(fixed_t tex, INT32 col);
+void *R_GetFlat(levelflat_t *levelflat);
+
+INT32 R_GetTextureNumForFlat(levelflat_t *levelflat);
 
 boolean R_CheckPowersOfTwo(void);
 boolean R_CheckSolidColorFlat(void);
@@ -102,7 +100,7 @@ void R_SetFlatVars(size_t length);
 // Returns the texture number for the texture name.
 INT32 R_TextureNumForName(const char *name);
 INT32 R_CheckTextureNumForName(const char *name);
-lumpnum_t R_GetFlatNumForName(const char *name);
+INT32 R_CheckFlatNumForName(const char *name);
 
 // Returns the texture name for the texture number (in case you ever needed it)
 const char *R_CheckTextureNameForNum(INT32 num);
diff --git a/src/r_things.c b/src/r_things.c
index 07420e34ad284e9dea9998e443baa37613e071a8..d6ef72b9d29980134757df4547ffd03837379957 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -257,7 +257,6 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16
 	UINT8 frame;
 	UINT8 rotation;
 	lumpinfo_t *lumpinfo;
-	softwarepatch_t patch;
 	UINT16 numadded = 0;
 
 	memset(sprtemp,0xFF, sizeof (sprtemp));
@@ -285,11 +284,8 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16
 	{
 		if (memcmp(lumpinfo[l].name,sprname,4)==0)
 		{
-			INT32 width, height;
+			INT16 width, height;
 			INT16 topoffset, leftoffset;
-#ifndef NO_PNG_LUMPS
-			boolean isPNG = false;
-#endif
 
 			frame = R_Char2Frame(lumpinfo[l].name[4]);
 			rotation = R_Char2Rotation(lumpinfo[l].name[5]);
@@ -304,33 +300,12 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16
 			if (W_LumpLengthPwad(wadnum,l)<=8)
 				continue;
 
+			// Get the patch's dimensions only
+			if (!W_ReadPatchHeaderPwad(wadnum, l, &width, &height, &topoffset, &leftoffset))
+				continue;
+
 			// store sprite info in lookup tables
 			//FIXME : numspritelumps do not duplicate sprite replacements
-
-#ifndef NO_PNG_LUMPS
-			{
-				softwarepatch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC);
-				size_t len = W_LumpLengthPwad(wadnum, l);
-
-				if (Picture_IsLumpPNG((UINT8 *)png, len))
-				{
-					Picture_PNGDimensions((UINT8 *)png, &width, &height, &topoffset, &leftoffset, len);
-					isPNG = true;
-				}
-
-				Z_Free(png);
-			}
-
-			if (!isPNG)
-#endif
-			{
-				W_ReadLumpHeaderPwad(wadnum, l, &patch, sizeof(INT16) * 4, 0);
-				width = (INT32)(SHORT(patch.width));
-				height = (INT32)(SHORT(patch.height));
-				topoffset = (INT16)(SHORT(patch.topoffset));
-				leftoffset = (INT16)(SHORT(patch.leftoffset));
-			}
-
 			spritecachedinfo[numspritelumps].width = width<<FRACBITS;
 			spritecachedinfo[numspritelumps].offset = leftoffset<<FRACBITS;
 			spritecachedinfo[numspritelumps].topoffset = topoffset<<FRACBITS;
@@ -635,25 +610,18 @@ INT16 *mceilingclip;
 fixed_t spryscale = 0, sprtopscreen = 0, sprbotscreen = 0;
 fixed_t windowtop = 0, windowbottom = 0;
 
-void R_DrawMaskedColumn(column_t *column)
+void R_DrawMaskedColumn(column_t *column, unsigned lengthcol)
 {
-	INT32 topscreen;
-	INT32 bottomscreen;
-	fixed_t basetexturemid;
-	INT32 topdelta, prevdelta = 0;
+	fixed_t basetexturemid = dc_texturemid;
 
-	basetexturemid = dc_texturemid;
+	(void)lengthcol;
 
-	for (; column->topdelta != 0xff ;)
+	for (unsigned i = 0; i < column->num_posts; i++)
 	{
-		// calculate unclipped screen coordinates
-		// for post
-		topdelta = column->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		topscreen = sprtopscreen + spryscale*topdelta;
-		bottomscreen = topscreen + spryscale*column->length;
+		post_t *post = &column->posts[i];
+
+		INT32 topscreen = sprtopscreen + spryscale*post->topdelta;
+		INT32 bottomscreen = topscreen + spryscale*post->length;
 
 		dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS;
 		dc_yh = (bottomscreen-1)>>FRACBITS;
@@ -677,48 +645,36 @@ void R_DrawMaskedColumn(column_t *column)
 
 		if (dc_yl <= dc_yh && dc_yh > 0)
 		{
-			dc_source = (UINT8 *)column + 3;
-			dc_texturemid = basetexturemid - (topdelta<<FRACBITS);
+			dc_source = column->pixels + post->data_offset;
+			dc_texturemid = basetexturemid - (post->topdelta<<FRACBITS);
 
-			// Drawn by R_DrawColumn.
-			// This stuff is a likely cause of the splitscreen water crash bug.
-			// FIXTHIS: Figure out what "something more proper" is and do it.
-			// quick fix... something more proper should be done!!!
-			if (ylookup[dc_yl])
-				colfunc();
-#ifdef PARANOIA
-			else
-				I_Error("R_DrawMaskedColumn: Invalid ylookup for dc_yl %d", dc_yl);
-#endif
+			colfunc();
 		}
-		column = (column_t *)((UINT8 *)column + column->length + 4);
 	}
 
 	dc_texturemid = basetexturemid;
 }
 
-INT32 lengthcol; // column->length : for flipped column function pointers and multi-patch on 2sided wall = texture->height
+static UINT8 *flippedcol = NULL;
+static size_t flippedcolsize = 0;
 
-void R_DrawFlippedMaskedColumn(column_t *column)
+void R_DrawFlippedMaskedColumn(column_t *column, unsigned lengthcol)
 {
 	INT32 topscreen;
 	INT32 bottomscreen;
 	fixed_t basetexturemid = dc_texturemid;
-	INT32 topdelta, prevdelta = -1;
 	UINT8 *d,*s;
 
-	for (; column->topdelta != 0xff ;)
+	for (unsigned i = 0; i < column->num_posts; i++)
 	{
-		// calculate unclipped screen coordinates
-		// for post
-		topdelta = column->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		topdelta = lengthcol-column->length-topdelta;
+		post_t *post = &column->posts[i];
+		if (!post->length)
+			continue;
+
+		INT32 topdelta = lengthcol-post->length-post->topdelta;
 		topscreen = sprtopscreen + spryscale*topdelta;
-		bottomscreen = sprbotscreen == INT32_MAX ? topscreen + spryscale*column->length
-		                                      : sprbotscreen + spryscale*column->length;
+		bottomscreen = sprbotscreen == INT32_MAX ? topscreen + spryscale*post->length
+		                                      : sprbotscreen + spryscale*post->length;
 
 		dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS;
 		dc_yh = (bottomscreen-1)>>FRACBITS;
@@ -742,21 +698,19 @@ void R_DrawFlippedMaskedColumn(column_t *column)
 
 		if (dc_yl <= dc_yh && dc_yh > 0)
 		{
-			dc_source = ZZ_Alloc(column->length);
-			for (s = (UINT8 *)column+2+column->length, d = dc_source; d < dc_source+column->length; --s)
+			if (post->length > flippedcolsize)
+			{
+				flippedcolsize = post->length;
+				flippedcol = Z_Realloc(flippedcol, flippedcolsize, PU_STATIC, NULL);
+			}
+
+			for (s = column->pixels+post->data_offset+post->length, d = flippedcol; d < flippedcol+post->length; --s)
 				*d++ = *s;
+			dc_source = flippedcol;
 			dc_texturemid = basetexturemid - (topdelta<<FRACBITS);
 
-			// Still drawn by R_DrawColumn.
-			if (ylookup[dc_yl])
-				colfunc();
-#ifdef PARANOIA
-			else
-				I_Error("R_DrawMaskedColumn: Invalid ylookup for dc_yl %d", dc_yl);
-#endif
-			Z_Free(dc_source);
+			colfunc();
 		}
-		column = (column_t *)((UINT8 *)column + column->length + 4);
 	}
 
 	dc_texturemid = basetexturemid;
@@ -816,14 +770,14 @@ UINT8 *R_GetTranslationForThing(mobj_t *mobj, skincolornum_t color, UINT16 trans
 static void R_DrawVisSprite(vissprite_t *vis)
 {
 	column_t *column;
-	void (*localcolfunc)(column_t *);
-	INT32 texturecolumn;
+	void (*localcolfunc)(column_t *, unsigned);
 	INT32 pwidth;
 	fixed_t frac;
 	patch_t *patch = vis->patch;
 	fixed_t this_scale = vis->thingscale;
 	INT32 x1, x2;
 	INT64 overflow_test;
+	unsigned lengthcol;
 
 	if (!patch)
 		return;
@@ -942,7 +896,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, spryscale += scalestep)
 		{
 			angle_t angle = ((vis->centerangle + xtoviewangle[dc_x]) >> ANGLETOFINESHIFT) & 0xFFF;
-			texturecolumn = (vis->paperoffset - FixedMul(FINETANGENT(angle), vis->paperdistance)) / horzscale;
+			INT32 texturecolumn = (vis->paperoffset - FixedMul(FINETANGENT(angle), vis->paperdistance)) / horzscale;
 
 			if (texturecolumn < 0 || texturecolumn >= pwidth)
 				continue;
@@ -953,9 +907,9 @@ static void R_DrawVisSprite(vissprite_t *vis)
 			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
 			dc_iscale = (0xffffffffu / (unsigned)spryscale);
 
-			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
+			column = &patch->columns[texturecolumn];
 
-			localcolfunc (column);
+			localcolfunc (column, lengthcol);
 		}
 	}
 	else if (vis->cut & SC_SHEAR)
@@ -967,17 +921,9 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		// Vertically sheared sprite
 		for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, dc_texturemid -= vis->shear.tan)
 		{
-#ifdef RANGECHECK
-			texturecolumn = frac>>FRACBITS;
-			if (texturecolumn < 0 || texturecolumn >= pwidth)
-				I_Error("R_DrawSpriteRange: bad texturecolumn at %d from end", vis->x2 - dc_x);
-			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
-#else
-			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[frac>>FRACBITS]));
-#endif
-
+			column = &patch->columns[frac>>FRACBITS];
 			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
-			localcolfunc (column);
+			localcolfunc (column, lengthcol);
 		}
 	}
 	else
@@ -989,15 +935,8 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		// Non-paper drawing loop
 		for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan)
 		{
-#ifdef RANGECHECK
-			texturecolumn = frac>>FRACBITS;
-			if (texturecolumn < 0 || texturecolumn >= pwidth)
-				I_Error("R_DrawSpriteRange: bad texturecolumn at %d from end", vis->x2 - dc_x);
-			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
-#else
-			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[frac>>FRACBITS]));
-#endif
-			localcolfunc (column);
+			column = &patch->columns[frac>>FRACBITS];
+			localcolfunc (column, lengthcol);
 		}
 	}
 
@@ -1011,21 +950,12 @@ static void R_DrawVisSprite(vissprite_t *vis)
 // Special precipitation drawer Tails 08-18-2002
 static void R_DrawPrecipitationVisSprite(vissprite_t *vis)
 {
-	column_t *column;
-#ifdef RANGECHECK
-	INT32 texturecolumn;
-#endif
-	fixed_t frac;
-	patch_t *patch;
-	INT64 overflow_test;
-
-	//Fab : R_InitSprites now sets a wad lump number
-	patch = vis->patch;
+	patch_t *patch = vis->patch;
 	if (!patch)
 		return;
 
 	// Check for overflow
-	overflow_test = (INT64)centeryfrac - (((INT64)vis->texturemid*vis->scale)>>FRACBITS);
+	INT64 overflow_test = (INT64)centeryfrac - (((INT64)vis->texturemid*vis->scale)>>FRACBITS);
 	if (overflow_test < 0) overflow_test = -overflow_test;
 	if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // fixed point mult would overflow
 
@@ -1041,31 +971,19 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis)
 	dc_texturemid = vis->texturemid;
 	dc_texheight = 0;
 
-	frac = vis->startfrac;
 	spryscale = vis->scale;
 	sprtopscreen = centeryfrac - FixedMul(dc_texturemid,spryscale);
 	windowtop = windowbottom = sprbotscreen = INT32_MAX;
 
 	if (vis->x1 < 0)
 		vis->x1 = 0;
-
 	if (vis->x2 >= vid.width)
 		vis->x2 = vid.width-1;
 
-	for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale)
-	{
-#ifdef RANGECHECK
-		texturecolumn = frac>>FRACBITS;
-
-		if (texturecolumn < 0 || texturecolumn >= patch->width)
-			I_Error("R_DrawPrecipitationSpriteRange: bad texturecolumn");
+	fixed_t frac = vis->startfrac;
 
-		column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
-#else
-		column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[frac>>FRACBITS]));
-#endif
-		R_DrawMaskedColumn(column);
-	}
+	for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale)
+		R_DrawMaskedColumn(&patch->columns[frac>>FRACBITS], patch->height);
 
 	colfunc = colfuncs[BASEDRAWFUNC];
 }
diff --git a/src/r_things.h b/src/r_things.h
index bd0a350093453a7798e3d254c6ddb7b3c16cce35..043b454b049cefa907c16f12c13751236db4b456 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -45,10 +45,9 @@ extern fixed_t sprtopscreen;
 extern fixed_t sprbotscreen;
 extern fixed_t windowtop;
 extern fixed_t windowbottom;
-extern INT32 lengthcol;
 
-void R_DrawMaskedColumn(column_t *column);
-void R_DrawFlippedMaskedColumn(column_t *column);
+void R_DrawMaskedColumn(column_t *column, unsigned lengthcol);
+void R_DrawFlippedMaskedColumn(column_t *column, unsigned lengthcol);
 
 // ----------------
 // SPRITE RENDERING
diff --git a/src/screen.c b/src/screen.c
index ca59b251dce1f6f1371af433b010a18b4304e621..76f546402ef38e0a4882221945ff602de73aadf9 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -116,8 +116,6 @@ void SCR_SetDrawFuncs(void)
 		colfuncs[COLDRAWFUNC_SHADE] = R_DrawShadeColumn_8;
 		colfuncs[COLDRAWFUNC_SHADOWED] = R_DrawColumnShadowed_8;
 		colfuncs[COLDRAWFUNC_TRANSTRANS] = R_DrawTranslatedTranslucentColumn_8;
-		colfuncs[COLDRAWFUNC_TWOSMULTIPATCH] = R_Draw2sMultiPatchColumn_8;
-		colfuncs[COLDRAWFUNC_TWOSMULTIPATCHTRANS] = R_Draw2sMultiPatchTranslucentColumn_8;
 		colfuncs[COLDRAWFUNC_FOG] = R_DrawFogColumn_8;
 
 		spanfuncs[SPANDRAWFUNC_TRANS] = R_DrawTranslucentSpan_8;
diff --git a/src/screen.h b/src/screen.h
index e4c1006c35b79a7f9948f818c0a3b03edf84c558..64d92b9d3ef99cf3c4e066ddc5cc9e57edd94afd 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -126,8 +126,6 @@ enum
 	COLDRAWFUNC_SHADE,
 	COLDRAWFUNC_SHADOWED,
 	COLDRAWFUNC_TRANSTRANS,
-	COLDRAWFUNC_TWOSMULTIPATCH,
-	COLDRAWFUNC_TWOSMULTIPATCHTRANS,
 	COLDRAWFUNC_FOG,
 
 	COLDRAWFUNC_MAX
diff --git a/src/v_video.c b/src/v_video.c
index 9e1bac2e5ec417d585957d8fc788ceb58ec054c1..cb7db487e921b88e50fd0b6d97a6d3f631841c90 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -506,7 +506,7 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 
 	fixed_t col, ofs, colfrac, rowfrac, fdup, vdup;
 	INT32 dup;
-	const column_t *column;
+	column_t *column;
 	UINT8 *desttop, *dest, *deststart, *destend;
 	const UINT8 *source, *deststop;
 	fixed_t pwidth; // patch width
@@ -686,12 +686,12 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 		if (!(scrn & V_SCALEPATCHMASK))
 		{
 			// if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT)
-			if (x == 0 && patch->width == BASEVIDWIDTH && y == 0 && patch->height == BASEVIDHEIGHT)
+			if (!v_translevel && x == 0 && patch->width == BASEVIDWIDTH && y == 0 && patch->height == BASEVIDHEIGHT)
 			{
-				column = (const column_t *)((const UINT8 *)(patch->columns) + (patch->columnofs[0]));
-				if (!column->topdelta)
+				column = &patch->columns[0];
+				if (column->num_posts && !column->posts[0].topdelta)
 				{
-					source = (const UINT8 *)(column) + 3;
+					source = column->pixels;
 					V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, source[0]);
 				}
 			}
@@ -741,7 +741,6 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 
 	for (col = 0; (col>>FRACBITS) < patch->width; col += colfrac, ++offx, desttop++)
 	{
-		INT32 topdelta, prevdelta = -1;
 		if (scrn & V_FLIP) // offx is measured from right edge instead of left
 		{
 			if (x+pwidth-offx < 0) // don't draw off the left of the screen (WRAP PREVENTION)
@@ -756,27 +755,24 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 			if (x+offx >= vid.width) // don't draw off the right of the screen (WRAP PREVENTION)
 				break;
 		}
-		column = (const column_t *)((const UINT8 *)(patch->columns) + (patch->columnofs[col>>FRACBITS]));
 
-		while (column->topdelta != 0xff)
+		column = &patch->columns[col>>FRACBITS];
+
+		for (unsigned i = 0; i < column->num_posts; i++)
 		{
-			topdelta = column->topdelta;
-			if (topdelta <= prevdelta)
-				topdelta += prevdelta;
-			prevdelta = topdelta;
-			source = (const UINT8 *)(column) + 3;
+			post_t *post = &column->posts[i];
+			source = column->pixels + post->data_offset;
 			dest = desttop;
 			if (scrn & V_FLIP)
 				dest = deststart + (destend - desttop);
-			dest += FixedInt(FixedMul(topdelta<<FRACBITS,vdup))*vid.width;
+			dest += FixedInt(FixedMul(post->topdelta<<FRACBITS,vdup))*vid.width;
 
-			for (ofs = 0; dest < deststop && (ofs>>FRACBITS) < column->length; ofs += rowfrac)
+			for (ofs = 0; dest < deststop && (size_t)(ofs>>FRACBITS) < post->length; ofs += rowfrac)
 			{
 				if (dest >= screens[scrn&V_PARAMMASK]) // don't draw off the top of the screen (CRASH PREVENTION)
 					*dest = patchdrawfunc(dest, source, ofs);
 				dest += vid.width;
 			}
-			column = (const column_t *)((const UINT8 *)column + column->length + 4);
 		}
 	}
 }
@@ -791,7 +787,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN
 
 	fixed_t col, ofs, colfrac, rowfrac, fdup, vdup;
 	INT32 dup;
-	const column_t *column;
+	column_t *column;
 	UINT8 *desttop, *dest;
 	const UINT8 *source, *deststop;
 
@@ -1020,20 +1016,18 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN
 
 	for (col = sx; (col>>FRACBITS) < patch->width && (col - sx) < w; col += colfrac, ++x, desttop++)
 	{
-		INT32 topdelta, prevdelta = -1;
 		if (x < 0) // don't draw off the left of the screen (WRAP PREVENTION)
 			continue;
 		if (x >= vid.width) // don't draw off the right of the screen (WRAP PREVENTION)
 			break;
-		column = (const column_t *)((const UINT8 *)(patch->columns) + (patch->columnofs[col>>FRACBITS]));
 
-		while (column->topdelta != 0xff)
+		column = &patch->columns[col>>FRACBITS];
+
+		for (unsigned i = 0; i < column->num_posts; i++)
 		{
-			topdelta = column->topdelta;
-			if (topdelta <= prevdelta)
-				topdelta += prevdelta;
-			prevdelta = topdelta;
-			source = (const UINT8 *)(column) + 3;
+			post_t *post = &column->posts[i];
+			INT32 topdelta = post->topdelta;
+			source = column->pixels + post->data_offset;
 			dest = desttop;
 			if ((topdelta<<FRACBITS)-sy > 0)
 			{
@@ -1043,13 +1037,12 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN
 			else
 				ofs = sy-(topdelta<<FRACBITS);
 
-			for (; dest < deststop && (ofs>>FRACBITS) < column->length && ((ofs - sy) + (topdelta<<FRACBITS)) < h; ofs += rowfrac)
+			for (; dest < deststop && (size_t)(ofs>>FRACBITS) < post->length && ((ofs - sy) + (topdelta<<FRACBITS)) < h; ofs += rowfrac)
 			{
 				if (dest >= screens[scrn&V_PARAMMASK]) // don't draw off the top of the screen (CRASH PREVENTION)
 					*dest = patchdrawfunc(dest, source, ofs);
 				dest += vid.width;
 			}
-			column = (const column_t *)((const UINT8 *)column + column->length + 4);
 		}
 	}
 }
@@ -1101,61 +1094,6 @@ void V_DrawBlock(INT32 x, INT32 y, INT32 scrn, INT32 width, INT32 height, const
 	}
 }
 
-static void V_BlitScaledPic(INT32 px1, INT32 py1, INT32 scrn, pic_t *pic);
-//  Draw a linear pic, scaled, TOTALLY CRAP CODE!!! OPTIMISE AND ASM!!
-//
-void V_DrawScaledPic(INT32 rx1, INT32 ry1, INT32 scrn, INT32 lumpnum)
-{
-#ifdef HWRENDER
-	if (rendermode != render_soft)
-	{
-		HWR_DrawPic(rx1, ry1, lumpnum);
-		return;
-	}
-#endif
-
-	V_BlitScaledPic(rx1, ry1, scrn, W_CacheLumpNum(lumpnum, PU_CACHE));
-}
-
-static void V_BlitScaledPic(INT32 rx1, INT32 ry1, INT32 scrn, pic_t * pic)
-{
-	INT32 dupx, dupy;
-	INT32 x, y;
-	UINT8 *src, *dest;
-	INT32 width, height;
-
-	width = SHORT(pic->width);
-	height = SHORT(pic->height);
-	scrn &= V_PARAMMASK;
-
-	if (pic->mode != 0)
-	{
-		CONS_Debug(DBG_RENDER, "pic mode %d not supported in Software\n", pic->mode);
-		return;
-	}
-
-	dest = screens[scrn] + max(0, ry1 * vid.width) + max(0, rx1);
-	// y cliping to the screen
-	if (ry1 + height * vid.dup >= vid.width)
-		height = (vid.width - ry1) / vid.dup - 1;
-	// WARNING no x clipping (not needed for the moment)
-
-	for (y = max(0, -ry1 / vid.dup); y < height; y++)
-	{
-		for (dupy = vid.dup; dupy; dupy--)
-		{
-			src = pic->data + y * width;
-			for (x = 0; x < width; x++)
-			{
-				for (dupx = vid.dup; dupx; dupx--)
-					*dest++ = *src;
-				src++;
-			}
-			dest += vid.width - vid.dup * width;
-		}
-	}
-}
-
 //
 // Fills a box of pixels with a single color, NOTE: scaled to screen size
 //
diff --git a/src/v_video.h b/src/v_video.h
index 80936f3ee9f6f6585b0a3d4208a43dda266e8635..5a3df5a4499588df56e4a6d152f1445f82a60926 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -178,9 +178,6 @@ void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT16 ski
 // Draw a linear block of pixels into the view buffer.
 void V_DrawBlock(INT32 x, INT32 y, INT32 scrn, INT32 width, INT32 height, const UINT8 *src);
 
-// draw a pic_t, SCALED
-void V_DrawScaledPic (INT32 px1, INT32 py1, INT32 scrn, INT32 lumpnum);
-
 // fill a box with a single color
 void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c);
 void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c);
diff --git a/src/w_wad.c b/src/w_wad.c
index ff1af6ee1a553dec87b96a625c4735086173ecdc..3a50646930e984762ffb720f2c2022edac075e33 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1351,6 +1351,74 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump)
 	return i;
 }
 
+void W_GetFolderLumpsPwad(const char *name, UINT16 wad, UINT32 **list, UINT16 *list_capacity, UINT16 *numlumps)
+{
+	size_t name_length = strlen(name);
+	lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo;
+
+	UINT16 capacity = list_capacity ? *list_capacity : 0;
+	UINT16 count = *numlumps;
+
+	for (UINT16 i = 0; i < wadfiles[wad]->numlumps; i++, lump_p++)
+	{
+		if (strnicmp(name, lump_p->fullname, name_length) == 0)
+		{
+			if (strlen(lump_p->fullname) > name_length
+				&& lump_p->longname[0] != '\0')
+			{
+				if (!capacity || count >= capacity)
+				{
+					capacity = capacity ? (capacity * 2) : 16;
+					*list = Z_Realloc(*list, capacity * sizeof(UINT32), PU_STATIC, NULL);
+				}
+
+				(*list)[count] = (wad << 16) + i;
+				count++;
+			}
+		}
+	}
+
+	if (list_capacity)
+		(*list_capacity) = capacity;
+	(*numlumps) = count;
+}
+
+void W_GetFolderLumps(const char *name, UINT32 **list, UINT16 *list_capacity, UINT16 *numlumps)
+{
+	for (UINT16 i = 0; i < numwadfiles; i++)
+		W_GetFolderLumpsPwad(name, i, list, list_capacity, numlumps);
+}
+
+UINT32 W_CountFolderLumpsPwad(const char *name, UINT16 wad)
+{
+	size_t name_length = strlen(name);
+	lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo;
+
+	UINT32 count = 0;
+
+	for (UINT16 i = 0; i < wadfiles[wad]->numlumps; i++, lump_p++)
+	{
+		if (strnicmp(name, lump_p->fullname, name_length) == 0)
+		{
+			if (strlen(lump_p->fullname) > name_length
+				&& lump_p->longname[0] != '\0')
+				count++;
+		}
+	}
+
+	return count;
+}
+
+UINT32 W_CountFolderLumps(const char *name)
+{
+	UINT32 count = 0;
+
+	for (UINT16 i = 0; i < numwadfiles; i++)
+		count += W_CountFolderLumpsPwad(name, i);
+
+	return count;
+}
+
 // 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)
@@ -1700,6 +1768,10 @@ void zerr(int ret)
 }
 #endif
 
+#ifdef NO_PNG_LUMPS
+#define Picture_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename)
+#endif
+
 /** Reads bytes from the head of a lump.
   * Note: If the lump is compressed, the whole thing has to be read anyway.
   *
@@ -1780,10 +1852,6 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
 		bytesread = fread(dest, 1, size, handle);
 		if (wadfiles[wad]->type == RET_FOLDER)
 			fclose(handle);
-#ifdef NO_PNG_LUMPS
-		if (Picture_IsLumpPNG((UINT8 *)dest, bytesread))
-			Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename);
-#endif
 		return bytesread;
 	case CM_LZF:		// Is it LZF compressed? Used by ZWADs.
 		{
@@ -1819,10 +1887,6 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
 			M_Memcpy(dest, decData + offset, size);
 			Z_Free(rawData);
 			Z_Free(decData);
-#ifdef NO_PNG_LUMPS
-			if (Picture_IsLumpPNG((UINT8 *)dest, size))
-				Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename);
-#endif
 			return size;
 #else
 			//I_Error("ZWAD files not supported on this platform.");
@@ -1882,10 +1946,6 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
 			Z_Free(rawData);
 			Z_Free(decData);
 
-#ifdef NO_PNG_LUMPS
-			if (Picture_IsLumpPNG((UINT8 *)dest, size))
-				Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename);
-#endif
 			return size;
 		}
 #endif
@@ -2004,8 +2064,7 @@ boolean W_IsLumpCached(lumpnum_t lumpnum, void *ptr)
 // If a patch is already cached return true, otherwise
 // return false.
 //
-// no outside code uses the PWAD form, for now
-static boolean W_IsPatchCachedPWAD(UINT16 wad, UINT16 lump, void *ptr)
+boolean W_IsPatchCachedPwad(UINT16 wad, UINT16 lump, void *ptr)
 {
 	void *lcache;
 
@@ -2027,7 +2086,7 @@ static boolean W_IsPatchCachedPWAD(UINT16 wad, UINT16 lump, void *ptr)
 
 boolean W_IsPatchCached(lumpnum_t lumpnum, void *ptr)
 {
-	return W_IsPatchCachedPWAD(WADFILENUM(lumpnum),LUMPNUM(lumpnum), ptr);
+	return W_IsPatchCachedPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum), ptr);
 }
 
 // ==========================================================================
@@ -2042,18 +2101,10 @@ void *W_CacheLumpName(const char *name, INT32 tag)
 //                                         CACHING OF GRAPHIC PATCH RESOURCES
 // ==========================================================================
 
-// Graphic 'patches' are loaded, and if necessary, converted into the format
-// the most useful for the current rendermode. For software renderer, the
-// graphic patches are kept as is. For the hardware renderer, graphic patches
-// are 'unpacked', and are kept into the cache in that unpacked format, and
-// the heap memory cache then acts as a 'level 2' cache just after the
-// graphics card memory.
-
 //
 // Cache a patch into heap memory, convert the patch format as necessary
 //
-
-void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
+static void *W_GetPatchPwad(UINT16 wad, UINT16 lump, INT32 tag)
 {
 	lumpcache_t *lumpcache = NULL;
 
@@ -2071,15 +2122,25 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 		W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0);
 		ptr = lumpdata;
 
-#ifndef NO_PNG_LUMPS
 		if (Picture_IsLumpPNG((UINT8 *)lumpdata, len))
-			ptr = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, len, &len, 0);
+		{
+#ifndef NO_PNG_LUMPS
+			ptr = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_PATCH, NULL, NULL, NULL, NULL, len, &len, 0);
+			Z_ChangeTag(ptr, tag);
+			Z_SetUser(ptr, &lumpcache[lump]);
+			Z_Free(lumpdata);
+			return lumpcache[lump];
+#else
+			Picture_ThrowPNGError(W_CheckNameForNumPwad(wad, lump), wadfiles[wad]->filename);
+			return NULL;
 #endif
+		}
 
-		dest = Z_Calloc(sizeof(patch_t), tag, &lumpcache[lump]);
-		Patch_Create(ptr, len, dest);
-
+		dest = Patch_CreateFromDoomPatch(ptr);
 		Z_Free(ptr);
+
+		Z_ChangeTag(dest, tag);
+		Z_SetUser(dest, &lumpcache[lump]);
 	}
 	else
 		Z_ChangeTag(lumpcache[lump], tag);
@@ -2087,30 +2148,19 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 	return lumpcache[lump];
 }
 
-void *W_CacheSoftwarePatchNum(lumpnum_t lumpnum, INT32 tag)
-{
-	return W_CacheSoftwarePatchNumPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum),tag);
-}
-
 void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 {
-	patch_t *patch;
-
 	if (!TestValidLump(wad, lump))
 		return NULL;
 
-	patch = W_CacheSoftwarePatchNumPwad(wad, lump, tag);
+	patch_t *patch = W_GetPatchPwad(wad, lump, tag);
 
 #ifdef HWRENDER
-	// Software-only compile cache the data without conversion
-	if (rendermode == render_soft || rendermode == render_none)
+	if (rendermode == render_opengl)
+		Patch_CreateGL(patch);
 #endif
-		return (void *)patch;
 
-#ifdef HWRENDER
-	Patch_CreateGL(patch);
 	return (void *)patch;
-#endif
 }
 
 void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag)
@@ -2118,6 +2168,71 @@ void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag)
 	return W_CachePatchNumPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum),tag);
 }
 
+void *W_GetCachedPatchNumPwad(UINT16 wad, UINT16 lump)
+{
+	if (!TestValidLump(wad, lump))
+		return NULL;
+
+	return wadfiles[wad]->patchcache[lump];
+}
+
+boolean W_ReadPatchHeaderPwad(UINT16 wadnum, UINT16 lumpnum, INT16 *width, INT16 *height, INT16 *topoffset, INT16 *leftoffset)
+{
+	UINT8 header[PNG_HEADER_SIZE];
+
+	if (!TestValidLump(wadnum, lumpnum))
+		return false;
+
+	W_ReadLumpHeaderPwad(wadnum, lumpnum, header, sizeof header, 0);
+
+	size_t len = W_LumpLengthPwad(wadnum, lumpnum);
+
+	if (Picture_IsLumpPNG(header, len))
+	{
+#ifndef NO_PNG_LUMPS
+		UINT8 *png = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
+
+		INT32 pwidth = 0, pheight = 0;
+
+		if (!Picture_PNGDimensions(png, &pwidth, &pheight, topoffset, leftoffset, len))
+		{
+			Z_Free(png);
+			return false;
+		}
+
+		*width = (INT16)pwidth;
+		*height = (INT16)pheight;
+
+		Z_Free(png);
+
+		return true;
+#else
+		Picture_ThrowPNGError(W_CheckNameForNumPwad(wadnum, lumpnum), wadfiles[wadnum]->filename);
+
+		return false;
+#endif
+	}
+
+	softwarepatch_t patch;
+
+	if (!W_ReadLumpHeaderPwad(wadnum, lumpnum, &patch, sizeof(INT16) * 4, 0))
+		return false;
+
+	*width = SHORT(patch.width);
+	*height = SHORT(patch.height);
+	if (topoffset)
+		*topoffset = SHORT(patch.topoffset);
+	if (leftoffset)
+		*leftoffset = SHORT(patch.leftoffset);
+
+	return true;
+}
+
+boolean W_ReadPatchHeader(lumpnum_t lumpnum, INT16 *width, INT16 *height, INT16 *topoffset, INT16 *leftoffset)
+{
+	return W_ReadPatchHeaderPwad(WADFILENUM(lumpnum), LUMPNUM(lumpnum), width, height, topoffset, leftoffset);
+}
+
 void W_UnlockCachedPatch(void *patch)
 {
 	if (!patch)
diff --git a/src/w_wad.h b/src/w_wad.h
index ffb9095ba21c9e1d79049adadaa893953b097260..e043e4d62c82f061ce505c4ce6b6a2298d48a4ec 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -181,6 +181,11 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump);
 UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump);
 UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump);
 
+void W_GetFolderLumpsPwad(const char *name, UINT16 wad, UINT32 **list, UINT16 *list_capacity, UINT16 *numlumps);
+void W_GetFolderLumps(const char *name, UINT32 **list, UINT16 *list_capacity, UINT16 *numlumps);
+UINT32 W_CountFolderLumpsPwad(const char *name, UINT16 wad);
+UINT32 W_CountFolderLumps(const char *name);
+
 lumpnum_t W_CheckNumForMap(const char *name);
 lumpnum_t W_CheckNumForName(const char *name);
 lumpnum_t W_CheckNumForLongName(const char *name);
@@ -210,20 +215,18 @@ void *W_CacheLumpNumForce(lumpnum_t lumpnum, INT32 tag);
 
 boolean W_IsLumpCached(lumpnum_t lump, void *ptr);
 boolean W_IsPatchCached(lumpnum_t lump, void *ptr);
+boolean W_IsPatchCachedPwad(UINT16 wad, UINT16 lump, void *ptr);
 
 void *W_CacheLumpName(const char *name, INT32 tag);
 void *W_CachePatchName(const char *name, INT32 tag);
 void *W_CachePatchLongName(const char *name, INT32 tag);
 
-// Returns either a Software patch, or an OpenGL patch.
-// Performs any necessary conversions from PNG images.
 void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag);
 void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag);
+void *W_GetCachedPatchNumPwad(UINT16 wad, UINT16 lump);
 
-// Returns a Software patch.
-// Performs any necessary conversions from PNG images.
-void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag);
-void *W_CacheSoftwarePatchNum(lumpnum_t lumpnum, INT32 tag);
+boolean W_ReadPatchHeaderPwad(UINT16 wadnum, UINT16 lumpnum, INT16 *width, INT16 *height, INT16 *topoffset, INT16 *leftoffset);
+boolean W_ReadPatchHeader(lumpnum_t lumpnum, INT16 *width, INT16 *height, INT16 *topoffset, INT16 *leftoffset);
 
 void W_UnlockCachedPatch(void *patch);