diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ba574f41448bfac956c680db347eb8e5b4ef1fa9..9c2326399fb0b3710e15c6e13d06f93cbd282caa 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -129,6 +129,7 @@ set(SRB2_CORE_RENDER_SOURCES
 	r_things.c
 	r_textures.c
 	r_patch.c
+	r_patchrotation.c
 	r_picformats.c
 	r_portal.c
 
diff --git a/src/Makefile b/src/Makefile
index f5084092053cdcaa5b589ae9269785985f982656..bdbb353633e6ca10a0f3e5dac2cbb00f70fdbaf3 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -519,6 +519,7 @@ OBJS:=$(i_main_o) \
 		$(OBJDIR)/r_things.o \
 		$(OBJDIR)/r_textures.o \
 		$(OBJDIR)/r_patch.o \
+		$(OBJDIR)/r_patchrotation.o \
 		$(OBJDIR)/r_picformats.o \
 		$(OBJDIR)/r_portal.o \
 		$(OBJDIR)/screen.o   \
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index e60645eb63e5e1fc977f698390b237e784781f84..fe0aa2f35b58a6871e8bfc920a846d1067923af9 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -4754,7 +4754,9 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	float gz, gzt;
 	spritedef_t *sprdef;
 	spriteframe_t *sprframe;
+#ifdef ROTSPRITE
 	spriteinfo_t *sprinfo;
+#endif
 	md2_t *md2;
 	size_t lumpoff;
 	unsigned rot;
@@ -4824,12 +4826,16 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	if (thing->skin && thing->sprite == SPR_PLAY)
 	{
 		sprdef = &((skin_t *)thing->skin)->sprites[thing->sprite2];
+#ifdef ROTSPRITE
 		sprinfo = &((skin_t *)thing->skin)->sprinfo[thing->sprite2];
+#endif
 	}
 	else
 	{
 		sprdef = &sprites[thing->sprite];
-		sprinfo = NULL;
+#ifdef ROTSPRITE
+		sprinfo = &spriteinfo[thing->sprite];
+#endif
 	}
 
 	if (rot >= sprdef->numframes)
@@ -4839,7 +4845,9 @@ static void HWR_ProjectSprite(mobj_t *thing)
 		thing->sprite = states[S_UNKNOWN].sprite;
 		thing->frame = states[S_UNKNOWN].frame;
 		sprdef = &sprites[thing->sprite];
-		sprinfo = NULL;
+#ifdef ROTSPRITE
+		sprinfo = &spriteinfo[thing->sprite];
+#endif
 		rot = thing->frame&FF_FRAMEMASK;
 		thing->state->sprite = thing->sprite;
 		thing->state->frame = thing->frame;
@@ -4901,9 +4909,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	if (thing->rollangle)
 	{
 		rollangle = R_GetRollAngle(thing->rollangle);
-		if (sprframe->rotsprite.patch[rot][rollangle] == NULL)
-			R_CacheRotSprite(thing->sprite, (thing->frame & FF_FRAMEMASK), sprinfo, sprframe, rot, rollangle, flip);
-		rotsprite = sprframe->rotsprite.patch[rot][rollangle];
+		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, sprinfo, rollangle);
 		if (rotsprite != NULL)
 		{
 			spr_width = SHORT(rotsprite->width) << FRACBITS;
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index ba3342b44680639f01612a46314dc1ec8ef383c9..335e6cfbc720f85298e44bf6280972bf1382c5d5 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -458,9 +458,8 @@ static int libd_getSpritePatch(lua_State *L)
 		INT32 rot = R_GetRollAngle(rollangle);
 
 		if (rot) {
-			if (sprframe->rotsprite.patch[angle][rot] == NULL)
-				R_CacheRotSprite(i, frame, NULL, sprframe, angle, rot, sprframe->flip & (1<<angle));
-			LUA_PushUserdata(L, sprframe->rotsprite.patch[angle][rot], META_PATCH);
+			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), &spriteinfo[i], rot);
+			LUA_PushUserdata(L, rotsprite, META_PATCH);
 			lua_pushboolean(L, false);
 			lua_pushboolean(L, true);
 			return 3;
@@ -571,9 +570,8 @@ static int libd_getSprite2Patch(lua_State *L)
 		INT32 rot = R_GetRollAngle(rollangle);
 
 		if (rot) {
-			if (sprframe->rotsprite.patch[angle][rot] == NULL)
-				R_CacheRotSprite(SPR_PLAY, frame, &skins[i].sprinfo[j], sprframe, angle, rot, sprframe->flip & (1<<angle));
-			LUA_PushUserdata(L, sprframe->rotsprite.patch[angle][rot], META_PATCH);
+			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), &skins[i].sprinfo[j], rot);
+			LUA_PushUserdata(L, rotsprite, META_PATCH);
 			lua_pushboolean(L, false);
 			lua_pushboolean(L, true);
 			return 3;
diff --git a/src/p_setup.c b/src/p_setup.c
index 10bfd08bed957aad913034aec1c0c41dd9062cc0..4dbb70e0c46abb81885500559788a9c9e383e932 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -4091,7 +4091,7 @@ boolean P_LoadLevel(boolean fromnetsave)
 	R_FlushTranslationColormapCache();
 
 	Patch_FreeTag(PU_PATCH_LOWPRIORITY);
-	Patch_FreeTag(PU_SPRITE_ROTATED);
+	Patch_FreeTag(PU_PATCH_ROTATED);
 	Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
 
 #if defined (WALLSPLATS) || defined (FLOORSPLATS)
@@ -4479,7 +4479,7 @@ boolean P_AddWadFile(const char *wadfilename)
 	// search for sprite replacements
 	//
 	Patch_FreeTag(PU_SPRITE);
-	Patch_FreeTag(PU_SPRITE_ROTATED);
+	Patch_FreeTag(PU_PATCH_ROTATED);
 	R_AddSpriteDefs(wadnum);
 
 	// Reload it all anyway, just in case they
diff --git a/src/r_defs.h b/src/r_defs.h
index 679b0fa356b8c8aba333b787714f2e8d55502510..4dfba0691f4d183c9c441fbeb7e62fa3b26d651d 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -652,6 +652,14 @@ typedef enum
 	RGBA32          = 4,  // 32 bit rgba
 } pic_mode_t;
 
+#ifdef ROTSPRITE
+typedef struct
+{
+	INT32 angles;
+	void **patches;
+} rotsprite_t;
+#endif
+
 // Patches.
 // A patch holds one or more columns.
 // Patches are used for sprites and all masked pictures, and we compose
@@ -666,6 +674,10 @@ typedef struct
 	UINT8 *columns; // Software column data
 
 	void *hardware; // OpenGL patch, allocated whenever necessary
+
+#ifdef ROTSPRITE
+	rotsprite_t *rotated; // Rotated patches
+#endif
 } patch_t;
 
 #if defined(_MSC_VER)
@@ -706,14 +718,6 @@ typedef struct
 #pragma pack()
 #endif
 
-// rotsprite
-#ifdef ROTSPRITE
-typedef struct
-{
-	patch_t *patch[16][ROTANGLES];
-} rotsprite_t;
-#endif/*ROTSPRITE*/
-
 typedef enum
 {
 	SRF_SINGLE      = 0,   // 0-angle for all rotations
@@ -755,7 +759,7 @@ typedef struct
 	UINT16 flip;
 
 #ifdef ROTSPRITE
-	rotsprite_t rotsprite;
+	rotsprite_t *rotated[16]; // Rotated patches
 #endif
 } spriteframe_t;
 
diff --git a/src/r_patch.c b/src/r_patch.c
index d3dff0e33e107d0a222cf640bca2d45125410258..e123414017eda7f26d321dbc8aa6806ede04145f 100644
--- a/src/r_patch.c
+++ b/src/r_patch.c
@@ -37,7 +37,7 @@ patch_t *Patch_Create(softwarepatch_t *source, size_t srcsize, void *dest)
 		patch->height     = source->height;
 		patch->leftoffset = source->leftoffset;
 		patch->topoffset  = source->topoffset;
-		patch->columnofs  = Z_Calloc(size, PU_PATCH, NULL);
+		patch->columnofs  = Z_Calloc(size, PU_PATCH_DATA, NULL);
 
 		for (col = 0; col < source->width; col++)
 		{
@@ -53,7 +53,7 @@ patch_t *Patch_Create(softwarepatch_t *source, size_t srcsize, void *dest)
 		if (colsize <= 0)
 			I_Error("R_CreatePatch: no column data!");
 
-		patch->columns = Z_Calloc(colsize, PU_PATCH, NULL);
+		patch->columns = Z_Calloc(colsize, PU_PATCH_DATA, NULL);
 		M_Memcpy(patch->columns, ((UINT8 *)source + LONG(source->columnofs[0])), colsize);
 	}
 
@@ -71,6 +71,23 @@ static void Patch_FreeData(patch_t *patch)
 		HWR_FreeTexture(patch);
 #endif
 
+#ifdef ROTSPRITE
+	if (patch->rotated)
+	{
+		rotsprite_t *rotsprite = patch->rotated;
+		INT32 i = 0;
+
+		for (; i < rotsprite->angles; i++)
+		{
+			if (rotsprite->patches[i])
+				Patch_Free(rotsprite->patches[i]);
+		}
+
+		Z_Free(rotsprite->patches);
+		Z_Free(rotsprite);
+	}
+#endif
+
 	if (patch->columnofs)
 		Z_Free(patch->columnofs);
 	if (patch->columns)
diff --git a/src/r_patch.h b/src/r_patch.h
index c460e7d0979f662c5932f487dfc8f8f4303cf4e3..71f185c9d8cb956e9917f36d3eab260fa259f974 100644
--- a/src/r_patch.h
+++ b/src/r_patch.h
@@ -27,4 +27,12 @@ void *Patch_AllocateHardwarePatch(patch_t *patch);
 void *Patch_CreateGL(patch_t *patch);
 #endif
 
+#ifdef ROTSPRITE
+void Patch_Rotate(patch_t *patch, INT32 angle, INT32 xpivot, INT32 ypivot, boolean flip);
+patch_t *Patch_GetRotated(patch_t *patch, INT32 angle, boolean flip);
+patch_t *Patch_GetRotatedSprite(spriteframe_t *sprite, size_t frame, size_t spriteangle, boolean flip, void *info, INT32 rotationangle);INT32 R_GetRollAngle(angle_t rollangle);
+extern fixed_t rollcosang[ROTANGLES];
+extern fixed_t rollsinang[ROTANGLES];
+#endif
+
 #endif // __R_PATCH__
diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c
new file mode 100644
index 0000000000000000000000000000000000000000..77544c3ac265dce17adc886ef7924916d1a32c3c
--- /dev/null
+++ b/src/r_patchrotation.c
@@ -0,0 +1,248 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  r_patchrotation.c
+/// \brief Patch rotation.
+
+#include "r_patch.h"
+#include "r_picformats.h"
+#include "r_things.h" // FEETADJUST
+#include "z_zone.h"
+#include "w_wad.h"
+
+#ifdef ROTSPRITE
+
+static rotsprite_t *RotatedPatch_Create(INT32 numangles);
+static void RotatedPatch_DoRotation(rotsprite_t *rotsprite, patch_t *patch, INT32 angle, INT32 xpivot, INT32 ypivot, boolean flip);
+
+fixed_t rollcosang[ROTANGLES];
+fixed_t rollsinang[ROTANGLES];
+
+INT32 R_GetRollAngle(angle_t rollangle)
+{
+	INT32 ra = AngleFixed(rollangle)>>FRACBITS;
+#if (ROTANGDIFF > 1)
+	ra += (ROTANGDIFF/2);
+#endif
+	ra /= ROTANGDIFF;
+	ra %= ROTANGLES;
+	return ra;
+}
+
+patch_t *Patch_GetRotated(patch_t *patch, INT32 angle, boolean flip)
+{
+	rotsprite_t *rotsprite = patch->rotated;
+	if (rotsprite == NULL || angle < 1 || angle >= ROTANGLES)
+		return NULL;
+
+	if (flip)
+		angle += rotsprite->angles;
+
+	return rotsprite->patches[angle];
+}
+
+patch_t *Patch_GetRotatedSprite(spriteframe_t *sprite, size_t frame, size_t spriteangle, boolean flip, void *info, INT32 rotationangle)
+{
+	rotsprite_t *rotsprite = sprite->rotated[spriteangle];
+	spriteinfo_t *sprinfo = (spriteinfo_t *)info;
+	INT32 idx = rotationangle;
+
+	if (rotationangle < 1 || rotationangle >= ROTANGLES)
+		return NULL;
+
+	if (rotsprite == NULL)
+	{
+		rotsprite = RotatedPatch_Create(ROTANGLES);
+		sprite->rotated[spriteangle] = rotsprite;
+	}
+
+	if (flip)
+		idx += rotsprite->angles;
+
+	if (rotsprite->patches[idx] == NULL)
+	{
+		patch_t *patch;
+		INT32 xpivot = 0, ypivot = 0;
+		lumpnum_t lump = sprite->lumppat[spriteangle];
+
+		if (lump == LUMPERROR)
+			return NULL;
+
+		patch = W_CachePatchNum(lump, PU_SPRITE);
+
+		if (sprinfo->available)
+		{
+			xpivot = sprinfo->pivot[frame].x;
+			ypivot = sprinfo->pivot[frame].y;
+		}
+		else
+		{
+			xpivot = patch->leftoffset;
+			ypivot = patch->height / 2;
+		}
+
+		RotatedPatch_DoRotation(rotsprite, patch, rotationangle, xpivot, ypivot, flip);
+	}
+
+	return rotsprite->patches[idx];
+}
+
+void Patch_Rotate(patch_t *patch, INT32 angle, INT32 xpivot, INT32 ypivot, boolean flip)
+{
+	if (patch->rotated == NULL)
+		patch->rotated = RotatedPatch_Create(ROTANGLES);
+	RotatedPatch_DoRotation(patch->rotated, patch, angle, xpivot, ypivot, flip);
+}
+
+rotsprite_t *RotatedPatch_Create(INT32 numangles)
+{
+	rotsprite_t *rotsprite = Z_Calloc(sizeof(rotsprite_t), PU_STATIC, NULL);
+	rotsprite->angles = numangles;
+	rotsprite->patches = Z_Calloc(rotsprite->angles * 2 * sizeof(void *), PU_STATIC, NULL);
+	return rotsprite;
+}
+
+void RotatedPatch_DoRotation(rotsprite_t *rotsprite, patch_t *patch, INT32 angle, INT32 xpivot, INT32 ypivot, boolean flip)
+{
+	patch_t *rotated;
+	UINT16 *rawdst;
+	size_t size;
+	pictureflags_t bflip = (flip) ? PICFLAGS_XFLIP : 0;
+
+	INT32 width = patch->width;
+	INT32 height = patch->height;
+	INT32 leftoffset = patch->leftoffset;
+	INT32 newwidth, newheight;
+
+	INT32 dx, dy;
+	fixed_t ca = rollcosang[angle];
+	fixed_t sa = rollsinang[angle];
+	INT32 idx = angle;
+
+	// Don't cache angle = 0
+	if (angle < 1 || angle >= ROTANGLES)
+		return;
+
+#define ROTSPRITE_XCENTER (newwidth / 2)
+#define ROTSPRITE_YCENTER (newheight / 2)
+
+	if (flip)
+		idx += rotsprite->angles;
+
+	if (rotsprite->patches[idx])
+		return;
+
+	if (bflip)
+	{
+		xpivot = width - xpivot;
+		leftoffset = width - leftoffset;
+	}
+
+	// Find the dimensions of the rotated patch.
+	{
+		INT32 w1 = abs(FixedMul(width << FRACBITS, ca) - FixedMul(height << FRACBITS, sa));
+		INT32 w2 = abs(FixedMul(-(width << FRACBITS), ca) - FixedMul(height << FRACBITS, sa));
+		INT32 h1 = abs(FixedMul(width << FRACBITS, sa) + FixedMul(height << FRACBITS, ca));
+		INT32 h2 = abs(FixedMul(-(width << FRACBITS), sa) + FixedMul(height << FRACBITS, ca));
+		w1 = FixedInt(FixedCeil(w1 + (FRACUNIT/2)));
+		w2 = FixedInt(FixedCeil(w2 + (FRACUNIT/2)));
+		h1 = FixedInt(FixedCeil(h1 + (FRACUNIT/2)));
+		h2 = FixedInt(FixedCeil(h2 + (FRACUNIT/2)));
+		newwidth = max(width, max(w1, w2));
+		newheight = max(height, max(h1, h2));
+	}
+
+	// check boundaries
+	{
+		fixed_t top[2][2];
+		fixed_t bottom[2][2];
+
+		top[0][0] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, sa) + (xpivot << FRACBITS);
+		top[0][1] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, ca) + (ypivot << FRACBITS);
+		top[1][0] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, sa) + (xpivot << FRACBITS);
+		top[1][1] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, ca) + (ypivot << FRACBITS);
+
+		bottom[0][0] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, sa) + (xpivot << FRACBITS);
+		bottom[0][1] = -FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, ca) + (ypivot << FRACBITS);
+		bottom[1][0] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, sa) + (xpivot << FRACBITS);
+		bottom[1][1] = -FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, ca) + (ypivot << FRACBITS);
+
+		top[0][0] >>= FRACBITS;
+		top[0][1] >>= FRACBITS;
+		top[1][0] >>= FRACBITS;
+		top[1][1] >>= FRACBITS;
+
+		bottom[0][0] >>= FRACBITS;
+		bottom[0][1] >>= FRACBITS;
+		bottom[1][0] >>= FRACBITS;
+		bottom[1][1] >>= FRACBITS;
+
+#define BOUNDARYWCHECK(b) (b[0] < 0 || b[0] >= width)
+#define BOUNDARYHCHECK(b) (b[1] < 0 || b[1] >= height)
+#define BOUNDARYADJUST(x) x *= 2
+		// top left/right
+		if (BOUNDARYWCHECK(top[0]) || BOUNDARYWCHECK(top[1]))
+			BOUNDARYADJUST(newwidth);
+		// bottom left/right
+		else if (BOUNDARYWCHECK(bottom[0]) || BOUNDARYWCHECK(bottom[1]))
+			BOUNDARYADJUST(newwidth);
+		// top left/right
+		if (BOUNDARYHCHECK(top[0]) || BOUNDARYHCHECK(top[1]))
+			BOUNDARYADJUST(newheight);
+		// bottom left/right
+		else if (BOUNDARYHCHECK(bottom[0]) || BOUNDARYHCHECK(bottom[1]))
+			BOUNDARYADJUST(newheight);
+#undef BOUNDARYWCHECK
+#undef BOUNDARYHCHECK
+#undef BOUNDARYADJUST
+	}
+
+	// Draw the rotated sprite to a temporary buffer.
+	size = (newwidth * newheight);
+	if (!size)
+		size = (width * height);
+	rawdst = Z_Calloc(size * sizeof(UINT16), PU_STATIC, NULL);
+
+	for (dy = 0; dy < newheight; dy++)
+	{
+		for (dx = 0; dx < newwidth; dx++)
+		{
+			INT32 x = (dx-ROTSPRITE_XCENTER) << FRACBITS;
+			INT32 y = (dy-ROTSPRITE_YCENTER) << FRACBITS;
+			INT32 sx = FixedMul(x, ca) + FixedMul(y, sa) + (xpivot << FRACBITS);
+			INT32 sy = -FixedMul(x, sa) + FixedMul(y, ca) + (ypivot << FRACBITS);
+			sx >>= FRACBITS;
+			sy >>= FRACBITS;
+			if (sx >= 0 && sy >= 0 && sx < width && sy < height)
+			{
+				void *input = Picture_GetPatchPixel(patch, PICFMT_PATCH, sx, sy, bflip);
+				if (input != NULL)
+					rawdst[(dy*newwidth)+dx] = (0xFF00 | (*(UINT8 *)input));
+			}
+		}
+	}
+
+	// make patch
+	rotated = (patch_t *)Picture_Convert(PICFMT_FLAT16, rawdst, PICFMT_PATCH, 0, &size, newwidth, newheight, 0, 0, 0);
+
+	Z_ChangeTag(rotated, PU_PATCH_ROTATED);
+	Z_SetUser(rotated, (void **)(&rotsprite->patches[angle]));
+
+	rotated->leftoffset = (rotated->width / 2) + (leftoffset - xpivot);
+	rotated->topoffset = (rotated->height / 2) + (patch->topoffset - ypivot);
+
+	//BP: we cannot use special tric in hardware mode because feet in ground caused by z-buffer
+	rotated->topoffset += FEETADJUST>>FRACBITS;
+
+	// free rotated image data
+	Z_Free(rawdst);
+
+#undef ROTSPRITE_XCENTER
+#undef ROTSPRITE_YCENTER
+}
+#endif
diff --git a/src/r_picformats.c b/src/r_picformats.c
index 7b44d21f12308f71abb532c15c947521dba525b6..8ec78663ebcd965825338f2fb534e5db1f9cd3ac 100644
--- a/src/r_picformats.c
+++ b/src/r_picformats.c
@@ -17,11 +17,10 @@
 #include "i_video.h"
 #include "r_data.h"
 #include "r_patch.h"
-#include "r_textures.h"
-#include "r_draw.h"
-#include "r_patch.h"
 #include "r_picformats.h"
+#include "r_textures.h"
 #include "r_things.h"
+#include "r_draw.h"
 #include "v_video.h"
 #include "z_zone.h"
 #include "w_wad.h"
@@ -1672,192 +1671,3 @@ void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps)
 			R_ParseSPRTINFOLump(wadnum, i);
 	}
 }
-
-#ifdef ROTSPRITE
-//
-// R_GetRollAngle
-//
-// Angles precalculated in R_InitSprites.
-//
-fixed_t rollcosang[ROTANGLES];
-fixed_t rollsinang[ROTANGLES];
-INT32 R_GetRollAngle(angle_t rollangle)
-{
-	INT32 ra = AngleFixed(rollangle)>>FRACBITS;
-#if (ROTANGDIFF > 1)
-	ra += (ROTANGDIFF/2);
-#endif
-	ra /= ROTANGDIFF;
-	ra %= ROTANGLES;
-	return ra;
-}
-
-//
-// R_CacheRotSprite
-//
-// Create a rotated sprite.
-//
-void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, INT32 angle, UINT8 flip)
-{
-	patch_t *patch, *newpatch;
-	UINT16 *rawdst;
-	size_t size;
-	pictureflags_t bflip = (flip) ? PICFLAGS_XFLIP : 0;
-
-	// Don't cache angle = 0
-	if (angle < 1 || angle >= ROTANGLES)
-		return;
-
-#define SPRITE_XCENTER (leftoffset)
-#define SPRITE_YCENTER (height / 2)
-#define ROTSPRITE_XCENTER (newwidth / 2)
-#define ROTSPRITE_YCENTER (newheight / 2)
-
-	if (sprframe->rotsprite.patch[rot][angle] == NULL)
-	{
-		INT32 dx, dy;
-		INT32 px, py;
-		INT32 width, height, leftoffset;
-		INT32 newwidth, newheight;
-		fixed_t ca, sa;
-		lumpnum_t lump = sprframe->lumppat[rot];
-
-		if (lump == LUMPERROR)
-			return;
-
-		patch = (patch_t *)W_CachePatchNum(lump, PU_STATIC);
-		width = patch->width;
-		height = patch->height;
-		leftoffset = patch->leftoffset;
-
-		// rotation pivot
-		px = SPRITE_XCENTER;
-		py = SPRITE_YCENTER;
-
-		// get correct sprite info for sprite
-		if (sprinfo == NULL)
-			sprinfo = &spriteinfo[sprnum];
-		if (sprinfo->available)
-		{
-			px = sprinfo->pivot[frame].x;
-			py = sprinfo->pivot[frame].y;
-		}
-		if (bflip)
-		{
-			px = width - px;
-			leftoffset = width - leftoffset;
-		}
-
-		ca = rollcosang[angle];
-		sa = rollsinang[angle];
-
-		// Find the dimensions of the rotated patch.
-		{
-			INT32 w1 = abs(FixedMul(width << FRACBITS, ca) - FixedMul(height << FRACBITS, sa));
-			INT32 w2 = abs(FixedMul(-(width << FRACBITS), ca) - FixedMul(height << FRACBITS, sa));
-			INT32 h1 = abs(FixedMul(width << FRACBITS, sa) + FixedMul(height << FRACBITS, ca));
-			INT32 h2 = abs(FixedMul(-(width << FRACBITS), sa) + FixedMul(height << FRACBITS, ca));
-			w1 = FixedInt(FixedCeil(w1 + (FRACUNIT/2)));
-			w2 = FixedInt(FixedCeil(w2 + (FRACUNIT/2)));
-			h1 = FixedInt(FixedCeil(h1 + (FRACUNIT/2)));
-			h2 = FixedInt(FixedCeil(h2 + (FRACUNIT/2)));
-			newwidth = max(width, max(w1, w2));
-			newheight = max(height, max(h1, h2));
-		}
-
-		// check boundaries
-		{
-			fixed_t top[2][2];
-			fixed_t bottom[2][2];
-
-			top[0][0] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS);
-			top[0][1] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS);
-			top[1][0] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS);
-			top[1][1] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS);
-
-			bottom[0][0] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS);
-			bottom[0][1] = -FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS);
-			bottom[1][0] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS);
-			bottom[1][1] = -FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS);
-
-			top[0][0] >>= FRACBITS;
-			top[0][1] >>= FRACBITS;
-			top[1][0] >>= FRACBITS;
-			top[1][1] >>= FRACBITS;
-
-			bottom[0][0] >>= FRACBITS;
-			bottom[0][1] >>= FRACBITS;
-			bottom[1][0] >>= FRACBITS;
-			bottom[1][1] >>= FRACBITS;
-
-#define BOUNDARYWCHECK(b) (b[0] < 0 || b[0] >= width)
-#define BOUNDARYHCHECK(b) (b[1] < 0 || b[1] >= height)
-#define BOUNDARYADJUST(x) x *= 2
-			// top left/right
-			if (BOUNDARYWCHECK(top[0]) || BOUNDARYWCHECK(top[1]))
-				BOUNDARYADJUST(newwidth);
-			// bottom left/right
-			else if (BOUNDARYWCHECK(bottom[0]) || BOUNDARYWCHECK(bottom[1]))
-				BOUNDARYADJUST(newwidth);
-			// top left/right
-			if (BOUNDARYHCHECK(top[0]) || BOUNDARYHCHECK(top[1]))
-				BOUNDARYADJUST(newheight);
-			// bottom left/right
-			else if (BOUNDARYHCHECK(bottom[0]) || BOUNDARYHCHECK(bottom[1]))
-				BOUNDARYADJUST(newheight);
-#undef BOUNDARYWCHECK
-#undef BOUNDARYHCHECK
-#undef BOUNDARYADJUST
-		}
-
-		// Draw the rotated sprite to a temporary buffer.
-		size = (newwidth * newheight);
-		if (!size)
-			size = (width * height);
-		rawdst = Z_Calloc(size * sizeof(UINT16), PU_STATIC, NULL);
-
-		for (dy = 0; dy < newheight; dy++)
-		{
-			for (dx = 0; dx < newwidth; dx++)
-			{
-				INT32 x = (dx-ROTSPRITE_XCENTER) << FRACBITS;
-				INT32 y = (dy-ROTSPRITE_YCENTER) << FRACBITS;
-				INT32 sx = FixedMul(x, ca) + FixedMul(y, sa) + (px << FRACBITS);
-				INT32 sy = -FixedMul(x, sa) + FixedMul(y, ca) + (py << FRACBITS);
-				sx >>= FRACBITS;
-				sy >>= FRACBITS;
-				if (sx >= 0 && sy >= 0 && sx < width && sy < height)
-				{
-					void *input = Picture_GetPatchPixel(patch, PICFMT_PATCH, sx, sy, bflip);
-					if (input != NULL)
-						rawdst[(dy*newwidth)+dx] = (0xFF00 | (*(UINT8 *)input));
-				}
-			}
-		}
-
-		// make patch
-		newpatch = (patch_t *)Picture_Convert(PICFMT_FLAT16, rawdst, PICFMT_PATCH, 0, &size, newwidth, newheight, 0, 0, 0);
-		Z_ChangeTag(newpatch, PU_SPRITE_ROTATED);
-		{
-			newpatch->leftoffset = (newpatch->width / 2) + (leftoffset - px);
-			newpatch->topoffset = (newpatch->height / 2) + (patch->topoffset - py);
-		}
-
-		//BP: we cannot use special tric in hardware mode because feet in ground caused by z-buffer
-		if (rendermode != render_none) // not for psprite
-			newpatch->topoffset += FEETADJUST>>FRACBITS;
-
-		// P_PrecacheLevel
-		if (devparm) spritememory += size;
-
-		Z_SetUser(newpatch, &sprframe->rotsprite.patch[rot][angle]);
-
-		// free rotated image data
-		Z_Free(rawdst);
-	}
-#undef SPRITE_XCENTER
-#undef SPRITE_YCENTER
-#undef ROTSPRITE_XCENTER
-#undef ROTSPRITE_YCENTER
-}
-#endif
diff --git a/src/r_picformats.h b/src/r_picformats.h
index e6c4aa17a6bc098955f4280b2853c5c3a8774fae..8d3999013475f23b9428e0e252148d91c88c8ea2 100644
--- a/src/r_picformats.h
+++ b/src/r_picformats.h
@@ -125,12 +125,4 @@ extern spriteinfo_t spriteinfo[NUMSPRITES];
 void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps);
 void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum);
 
-// Sprite rotation
-#ifdef ROTSPRITE
-INT32 R_GetRollAngle(angle_t rollangle);
-void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, INT32 angle, UINT8 flip);
-extern fixed_t rollcosang[ROTANGLES];
-extern fixed_t rollsinang[ROTANGLES];
-#endif
-
 #endif // __R_PICFORMATS__
diff --git a/src/r_things.c b/src/r_things.c
index a9a5c42fced424cb5574192a923af12509847a90..b7d7302f8283f032e68ca801cdc2b098db2d512d 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -97,7 +97,7 @@ static void R_InstallSpriteLump(UINT16 wad,            // graphics patch
 {
 	char cn = R_Frame2Char(frame), cr = R_Rotation2Char(rotation); // for debugging
 
-	INT32 r, ang;
+	INT32 r;
 	lumpnum_t lumppat = wad;
 	lumppat <<= 16;
 	lumppat += lump;
@@ -105,14 +105,10 @@ static void R_InstallSpriteLump(UINT16 wad,            // graphics patch
 	if (maxframe ==(size_t)-1 || frame > maxframe)
 		maxframe = frame;
 
-	// rotsprite
 #ifdef ROTSPRITE
 	for (r = 0; r < 16; r++)
-	{
-		for (ang = 0; ang < ROTANGLES; ang++)
-			sprtemp[frame].rotsprite.patch[r][ang] = NULL;
-	}
-#endif/*ROTSPRITE*/
+		sprtemp[frame].rotated[r] = NULL;
+#endif
 
 	if (rotation == 0)
 	{
@@ -1454,7 +1450,7 @@ static void R_ProjectSprite(mobj_t *thing)
 			thing->frame = states[S_UNKNOWN].frame;
 			sprdef = &sprites[thing->sprite];
 #ifdef ROTSPRITE
-			sprinfo = NULL;
+			sprinfo = &spriteinfo[thing->sprite];
 #endif
 			frame = thing->frame&FF_FRAMEMASK;
 		}
@@ -1463,7 +1459,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	{
 		sprdef = &sprites[thing->sprite];
 #ifdef ROTSPRITE
-		sprinfo = NULL;
+		sprinfo = &spriteinfo[thing->sprite];
 #endif
 
 		if (frame >= sprdef->numframes)
@@ -1478,6 +1474,7 @@ static void R_ProjectSprite(mobj_t *thing)
 			thing->sprite = states[S_UNKNOWN].sprite;
 			thing->frame = states[S_UNKNOWN].frame;
 			sprdef = &sprites[thing->sprite];
+			sprinfo = &spriteinfo[thing->sprite];
 			frame = thing->frame&FF_FRAMEMASK;
 		}
 	}
@@ -1539,9 +1536,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	if (thing->rollangle)
 	{
 		rollangle = R_GetRollAngle(thing->rollangle);
-		if (sprframe->rotsprite.patch[rot][rollangle] == NULL)
-			R_CacheRotSprite(thing->sprite, frame, sprinfo, sprframe, rot, rollangle, flip);
-		rotsprite = sprframe->rotsprite.patch[rot][rollangle];
+		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, sprinfo, rollangle);
 		if (rotsprite != NULL)
 		{
 			spr_width = SHORT(rotsprite->width) << FRACBITS;
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 755fa68e6768e84d8ec2563d863591ccac1854ee..b66cea963db5ecaeea6d9bd853e8f6654d46628c 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -282,6 +282,8 @@
     <ClInclude Include="..\r_draw.h" />
     <ClInclude Include="..\r_local.h" />
     <ClInclude Include="..\r_main.h" />
+    <ClInclude Include="..\r_patch.h" />
+    <ClInclude Include="..\r_patchrotation.h" />
     <ClInclude Include="..\r_picformats.h" />
     <ClInclude Include="..\r_plane.h" />
     <ClInclude Include="..\r_portal.h" />
@@ -449,6 +451,8 @@
       <ExcludedFromBuild>true</ExcludedFromBuild>
     </ClCompile>
     <ClCompile Include="..\r_main.c" />
+    <ClCompile Include="..\r_patch.c" />
+    <ClCompile Include="..\r_patchrotation.c" />
     <ClCompile Include="..\r_picformats.c" />
     <ClCompile Include="..\r_plane.c" />
     <ClCompile Include="..\r_portal.c" />
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index 3bbcd9cb57efeb3f725edacfa05de81ccbc54691..3291db6c9553d39da2dc4084475c2707130113e7 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -474,12 +474,18 @@
     <ClInclude Include="..\hardware\hw_clip.h">
       <Filter>Hw_Hardware</Filter>
     </ClInclude>
-    <ClInclude Include="..\r_textures.h">
+    <ClInclude Include="..\r_patch.h">
+      <Filter>R_Rend</Filter>
+    </ClInclude>
+    <ClInclude Include="..\r_patchrotation.h">
       <Filter>R_Rend</Filter>
     </ClInclude>
     <ClInclude Include="..\r_picformats.h">
       <Filter>R_Rend</Filter>
     </ClInclude>
+    <ClInclude Include="..\r_textures.h">
+      <Filter>R_Rend</Filter>
+    </ClInclude>
     <ClInclude Include="..\r_portal.h">
       <Filter>R_Rend</Filter>
     </ClInclude>
@@ -952,12 +958,18 @@
       <Filter>Hw_Hardware</Filter>
     </ClCompile>
     <ClCompile Include="..\apng.c" />
-    <ClCompile Include="..\r_textures.c">
+    <ClCompile Include="..\r_patch.c">
+      <Filter>R_Rend</Filter>
+    </ClCompile>
+    <ClCompile Include="..\r_patchrotation.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
     <ClCompile Include="..\r_picformats.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
+    <ClCompile Include="..\r_textures.c">
+      <Filter>R_Rend</Filter>
+    </ClCompile>
     <ClCompile Include="..\r_portal.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj
index 52617037b210f0eb913daf9a4e7eab6d1bee973f..3e8af3b0ed866b9039879abbc4b4e28b1d623ba4 100644
--- a/src/win32/Srb2win-vc10.vcxproj
+++ b/src/win32/Srb2win-vc10.vcxproj
@@ -298,6 +298,8 @@
       <ExcludedFromBuild>true</ExcludedFromBuild>
     </ClCompile>
     <ClCompile Include="..\r_main.c" />
+    <ClCompile Include="..\r_patch.c" />
+    <ClCompile Include="..\r_patchrotation.c" />
     <ClCompile Include="..\r_picformats.c" />
     <ClCompile Include="..\r_plane.c" />
     <ClCompile Include="..\r_portal.c" />
@@ -454,6 +456,8 @@
     <ClInclude Include="..\r_draw.h" />
     <ClInclude Include="..\r_local.h" />
     <ClInclude Include="..\r_main.h" />
+    <ClInclude Include="..\r_patch.h" />
+    <ClInclude Include="..\r_patchrotation.h" />
     <ClInclude Include="..\r_picformats.h" />
     <ClInclude Include="..\r_plane.h" />
     <ClInclude Include="..\r_portal.h" />
diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters
index 0689a4ac0893e10c660a72ed32c9bb0c3c054c82..7279368f1423f4a02e962395e8d4b451a30e44cd 100644
--- a/src/win32/Srb2win-vc10.vcxproj.filters
+++ b/src/win32/Srb2win-vc10.vcxproj.filters
@@ -469,9 +469,18 @@
       <Filter>Hw_Hardware</Filter>
     </ClCompile>
     <ClCompile Include="..\apng.c" />
+    <ClCompile Include="..\r_patch.c">
+      <Filter>R_Rend</Filter>
+    </ClCompile>
+    <ClCompile Include="..\r_patchrotation.c">
+      <Filter>R_Rend</Filter>
+    </ClCompile>
     <ClCompile Include="..\r_picformats.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
+    <ClCompile Include="..\r_textures.c">
+      <Filter>R_Rend</Filter>
+    </ClCompile>
     <ClCompile Include="..\r_portal.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
@@ -886,12 +895,18 @@
       <Filter>Hw_Hardware</Filter>
     </ClInclude>
     <ClInclude Include="..\apng.h" />
-    <ClInclude Include="..\r_textures.h">
+    <ClInclude Include="..\r_patch.h">
+      <Filter>R_Rend</Filter>
+    </ClInclude>
+    <ClInclude Include="..\r_patchrotation.h">
       <Filter>R_Rend</Filter>
     </ClInclude>
     <ClInclude Include="..\r_picformats.h">
       <Filter>R_Rend</Filter>
     </ClInclude>
+    <ClInclude Include="..\r_textures.h">
+      <Filter>R_Rend</Filter>
+    </ClInclude>
     <ClInclude Include="..\r_portal.h">
       <Filter>R_Rend</Filter>
     </ClInclude>
diff --git a/src/z_zone.c b/src/z_zone.c
index b704e1a30eb578103ab904bec5d90ee5aeeb0b70..ad64a3a07f04f01d40ac291cfa4e77f26bd37e88 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -795,19 +795,19 @@ static void Command_Memfree_f(void)
 
 	Z_CheckHeap(-1);
 	CONS_Printf("\x82%s", M_GetText("Memory Info\n"));
-	CONS_Printf(M_GetText("Total heap used   : %7s KB\n"), sizeu1(Z_TotalUsage()>>10));
-	CONS_Printf(M_GetText("Static            : %7s KB\n"), sizeu1(Z_TagUsage(PU_STATIC)>>10));
-	CONS_Printf(M_GetText("Static (sound)    : %7s KB\n"), sizeu1(Z_TagUsage(PU_SOUND)>>10));
-	CONS_Printf(M_GetText("Static (music)    : %7s KB\n"), sizeu1(Z_TagUsage(PU_MUSIC)>>10));
-	CONS_Printf(M_GetText("Patches           : %7s KB\n"), sizeu1(Z_TagUsage(PU_PATCH)>>10));
-	CONS_Printf(M_GetText("Patches (low)     : %7s KB\n"), sizeu1(Z_TagUsage(PU_PATCH_LOWPRIORITY)>>10));
-	CONS_Printf(M_GetText("Sprites           : %7s KB\n"), sizeu1(Z_TagUsage(PU_SPRITE)>>10));
-	CONS_Printf(M_GetText("Sprites (rotated) : %7s KB\n"), sizeu1(Z_TagUsage(PU_SPRITE_ROTATED)>>10));
-	CONS_Printf(M_GetText("HUD graphics      : %7s KB\n"), sizeu1(Z_TagUsage(PU_HUDGFX)>>10));
-	CONS_Printf(M_GetText("Locked cache      : %7s KB\n"), sizeu1(Z_TagUsage(PU_CACHE)>>10));
-	CONS_Printf(M_GetText("Level             : %7s KB\n"), sizeu1(Z_TagUsage(PU_LEVEL)>>10));
-	CONS_Printf(M_GetText("Special thinker   : %7s KB\n"), sizeu1(Z_TagUsage(PU_LEVSPEC)>>10));
-	CONS_Printf(M_GetText("All purgable      : %7s KB\n"),
+	CONS_Printf(M_GetText("Total heap used        : %7s KB\n"), sizeu1(Z_TotalUsage()>>10));
+	CONS_Printf(M_GetText("Static                 : %7s KB\n"), sizeu1(Z_TagUsage(PU_STATIC)>>10));
+	CONS_Printf(M_GetText("Static (sound)         : %7s KB\n"), sizeu1(Z_TagUsage(PU_SOUND)>>10));
+	CONS_Printf(M_GetText("Static (music)         : %7s KB\n"), sizeu1(Z_TagUsage(PU_MUSIC)>>10));
+	CONS_Printf(M_GetText("Patches                : %7s KB\n"), sizeu1(Z_TagUsage(PU_PATCH)>>10));
+	CONS_Printf(M_GetText("Patches (low priority) : %7s KB\n"), sizeu1(Z_TagUsage(PU_PATCH_LOWPRIORITY)>>10));
+	CONS_Printf(M_GetText("Patches (rotated)      : %7s KB\n"), sizeu1(Z_TagUsage(PU_PATCH_ROTATED)>>10));
+	CONS_Printf(M_GetText("Sprites                : %7s KB\n"), sizeu1(Z_TagUsage(PU_SPRITE)>>10));
+	CONS_Printf(M_GetText("HUD graphics           : %7s KB\n"), sizeu1(Z_TagUsage(PU_HUDGFX)>>10));
+	CONS_Printf(M_GetText("Locked cache           : %7s KB\n"), sizeu1(Z_TagUsage(PU_CACHE)>>10));
+	CONS_Printf(M_GetText("Level                  : %7s KB\n"), sizeu1(Z_TagUsage(PU_LEVEL)>>10));
+	CONS_Printf(M_GetText("Special thinker        : %7s KB\n"), sizeu1(Z_TagUsage(PU_LEVSPEC)>>10));
+	CONS_Printf(M_GetText("All purgable           : %7s KB\n"),
 		sizeu1(Z_TagsUsage(PU_PURGELEVEL, INT32_MAX)>>10));
 
 #ifdef HWRENDER
diff --git a/src/z_zone.h b/src/z_zone.h
index 90a6de8eb0050fb92879faaee37df06ffa5babdf..e80a45e7fb4f2ed6223868fc78d18649e75ab4ad 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -45,9 +45,10 @@ enum
 
 	PU_PATCH                 = 14, // static entire execution time
 	PU_PATCH_LOWPRIORITY     = 15, // lower priority patch, static until level exited
-	PU_SPRITE                = 16, // sprite patch, static until WAD added
-	PU_SPRITE_ROTATED        = 17, // sprite patch, static until level exited or WAD added
-	PU_HUDGFX                = 18, // HUD patch, static until WAD added
+	PU_PATCH_ROTATED         = 16, // rotated patch, static until level exited or WAD added
+	PU_PATCH_DATA            = 17, // patch data, lifetime depends on the patch that owns it
+	PU_SPRITE                = 18, // sprite patch, static until WAD added
+	PU_HUDGFX                = 19, // HUD patch, static until WAD added
 
 	PU_HWRPATCHINFO          = 21, // Hardware GLPatch_t struct for OpenGL texture cache
 	PU_HWRPATCHCOLMIPMAP     = 22, // Hardware GLMipmap_t struct colormap variation of patch