diff --git a/src/deh_lua.c b/src/deh_lua.c
index 9dc1f304f75eb8e1adb4b4cc3fe5f568b17c4210..cc4e0ef3d8b3d0a9b67f8eabc6ccf46b1ab40682 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -60,26 +60,13 @@ static inline int lib_freeslot(lua_State *L)
 		else if (fastcmp(type, "SPR"))
 		{
 			spritenum_t j;
-
 			if (strlen(word) > MAXSPRITENAME)
-				return luaL_error(L, "Sprite name is longer than %d characters\n", MAXSPRITENAME);
-
-			for (j = SPR_FIRSTFREESLOT; j <= SPR_LASTFREESLOT; j++)
-			{
-				if (in_bit_array(used_spr, j - SPR_FIRSTFREESLOT))
-					continue; // Already allocated, next.
-				// Found a free slot!
-				CONS_Printf("Sprite SPR_%s allocated.\n",word);
-				strcpy(sprnames[j], word);
-				set_bit_array(used_spr, j - SPR_FIRSTFREESLOT); // Okay, this sprite slot has been named now.
-				// Lua needs to update the value in _G if it exists
-				LUA_UpdateSprName(word, j);
-				lua_pushinteger(L, j);
-				r++;
-				break;
-			}
-			if (j > SPR_LASTFREESLOT)
-				CONS_Alert(CONS_WARNING, "Ran out of free sprite slots!\n");
+				I_Error("Sprite name is longer than %d characters\n", MAXSPRITENAME);
+			CONS_Printf("Sprite SPR_%s allocated.\n",word);
+			j = P_AllocateSpriteinfo(word);
+			LUA_UpdateSprName(word, j);
+			lua_pushinteger(L, j);
+			r++;
 		}
 		else if (fastcmp(type, "S"))
 		{
@@ -421,7 +408,7 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
 	else if (fastncmp("SPR_",word,4)) {
 		p = word+4;
 		i = R_GetSpriteNumByName(p);
-		if (i != NUMSPRITES)
+		if ((UINT32)i != numspriteinfo)
 		{
 			// updating overridden sprnames is not implemented for soc parser,
 			// so don't use cache
diff --git a/src/deh_soc.c b/src/deh_soc.c
index 3b248d0831fee63562de5fab89e076a7525de4d4..131274e972e243a3467d1657241c10d113848dc5 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -445,17 +445,9 @@ void readfreeslots(MYFILE *f)
 				if (strlen(word) > MAXSPRITENAME)
 					I_Error("Sprite name is longer than %d characters\n", MAXSPRITENAME);
 
-				for (i = SPR_FIRSTFREESLOT; i <= SPR_LASTFREESLOT; i++)
-				{
-					if (in_bit_array(used_spr, i - SPR_FIRSTFREESLOT))
-						continue; // Already allocated, next.
-					// Found a free slot!
-					strcpy(sprnames[i], word);
-					set_bit_array(used_spr, i - SPR_FIRSTFREESLOT); // Okay, this sprite slot has been named now.
-					// Lua needs to update the value in _G if it exists
-					LUA_UpdateSprName(word, i);
-					break;
-				}
+				CONS_Printf("Sprite SPR_%s allocated.\n",word);
+				i = P_AllocateSpriteinfo(word);
+				LUA_UpdateSprName(word, i);
 			}
 			else if (fastcmp(type, "S"))
 			{
@@ -1055,7 +1047,7 @@ void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2)
 					if (sprite2)
 						deh_warning("Sprite2 %s: invalid frame %s", spr2names[num], word2);
 					else
-						deh_warning("Sprite %s: invalid frame %s", sprnames[num], word2);
+						deh_warning("Sprite %s: invalid frame %s", spriteinfo[num]->name, word2);
 					break;
 				}
 
@@ -1077,11 +1069,11 @@ void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2)
 					}
 				}
 				else
-					M_Memcpy(&spriteinfo[num], info, sizeof(spriteinfo_t));
+					M_Memcpy(spriteinfo[num], info, sizeof(spriteinfo_t));
 			}
 			else
 			{
-				//deh_warning("Sprite %s: unknown word '%s'", sprnames[num], word);
+				//deh_warning("Sprite %s: unknown word '%s'", spriteinfo[num]->name, word);
 				f->curpos = lastline;
 				break;
 			}
@@ -4164,7 +4156,7 @@ spritenum_t get_sprite(const char *word)
 	if (fastncmp("SPR_",word,4))
 		word += 4; // take off the SPR_
 	i = R_GetSpriteNumByName(word);
-	if (i != NUMSPRITES)
+	if (i != numspriteinfo)
 		return i;
 	deh_warning("Couldn't find sprite named 'SPR_%s'",word);
 	return SPR_NULL;
diff --git a/src/dehacked.c b/src/dehacked.c
index fd76503618355847d246f8304a46d91f5b1564d7..05183dc27c01e1dba92b21045af196c95609b7ae 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -350,11 +350,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 				{
 					if (i == 0 && word2[0] != '0') // If word2 isn't a number
 						i = get_sprite(word2); // find a sprite by name
-					if (i < NUMSPRITES && i > 0)
+					if ((UINT32)i < numspriteinfo && i > 0)
 						readspriteinfo(f, i, false);
 					else
 					{
-						deh_warning("Sprite number %d out of range (0 - %d)", i, NUMSPRITES-1);
+						deh_warning("Sprite number %d out of range (0 - %d)", i, numspriteinfo-1);
 						ignorelines(f);
 					}
 				}
diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c
index 8dac9bb4218293a5f55bcb97e436ec05b89112d6..6b142cadfd922ac2950b7c9da602c7aa1df44212 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -132,7 +132,7 @@ light_t lspr[NUMLIGHTS] =
 	{ LIGHT_SPR,      0.0f,   0.0f, 0xe0ffffff,  64.0f, 0xe0ffffff, 384.0f, 0.0f},
 };
 
-light_t *t_lspr[NUMSPRITES] =
+static const light_t *startt_lspr[] =
 {
 	&lspr[NOLIGHT],     // SPR_NULL
 	&lspr[NOLIGHT],     // SPR_UNKN
@@ -224,6 +224,7 @@ light_t *t_lspr[NUMSPRITES] =
 	// Boss 8 (Egg Rock)
 	&lspr[NOLIGHT],     // SPR_EGGT
 
+	// Cy-Brak-Demon; uses "BRAK" as well, but has some extras
 	&lspr[NOLIGHT], //SPR_RCKT
 	&lspr[NOLIGHT], //SPR_ELEC
 	&lspr[NOLIGHT], //SPR_TARG
@@ -303,6 +304,13 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_AROW
 	&lspr[NOLIGHT],     // SPR_CFIR
 
+	// The letter
+	&lspr[NOLIGHT],     // SPR_LETR
+
+	// Tutorial Scenery
+	&lspr[NOLIGHT],     // SPR_TUPL
+	&lspr[NOLIGHT],     // SPR_TUPF
+
 	// Greenflower Scenery
 	&lspr[NOLIGHT],     // SPR_FWR1
 	&lspr[NOLIGHT],     // SPR_FWR2
@@ -322,16 +330,21 @@ light_t *t_lspr[NUMSPRITES] =
 	// Techno Hill Scenery
 	&lspr[NOLIGHT],     // SPR_THZP
 	&lspr[NOLIGHT],     // SPR_FWR5
+	&lspr[NOLIGHT],     // SPR_FWR6
+	&lspr[NOLIGHT],     // SPR_THZT
 	&lspr[REDBALL_L],   // SPR_ALRM
 
 	// Deep Sea Scenery
 	&lspr[NOLIGHT],     // SPR_GARG
 	&lspr[NOLIGHT],     // SPR_SEWE
 	&lspr[NOLIGHT],     // SPR_DRIP
-	&lspr[NOLIGHT],     // SPR_CRL1
-	&lspr[NOLIGHT],     // SPR_CRL2
-	&lspr[NOLIGHT],     // SPR_CRL3
+	&lspr[NOLIGHT],     // SPR_CORL
 	&lspr[NOLIGHT],     // SPR_BCRY
+	&lspr[NOLIGHT],     // SPR_KELP
+	&lspr[NOLIGHT],     // SPR_ALGA
+	&lspr[NOLIGHT],     // SPR_ALGB
+	&lspr[NOLIGHT],     // SPR_DSTG
+	&lspr[NOLIGHT],     // SPR_LIBE
 
 	// Castle Eggman Scenery
 	&lspr[NOLIGHT],     // SPR_CHAN
@@ -355,6 +368,7 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_CFLG
 	&lspr[NOLIGHT],     // SPR_CSTA
 	&lspr[NOLIGHT],     // SPR_CBBS
+	&lspr[NOLIGHT],     // SPR_CABR
 
 	// Arid Canyon Scenery
 	&lspr[NOLIGHT],     // SPR_BTBL
@@ -613,136 +627,32 @@ light_t *t_lspr[NUMSPRITES] =
 	// Gravity Well Objects
 	&lspr[NOLIGHT],     // SPR_GWLG
 	&lspr[NOLIGHT],     // SPR_GWLR
-
-	// Free slots
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
-	&lspr[NOLIGHT],
 };
 
+static UINT32 t_numlspr;
+light_t **t_lspr;
+
+extern void HWR_AllocateLSpr(void);  // silence warning
+
+void HWR_AllocateLSpr(void)
+{
+	UINT32 i;
+	if (t_lspr == NULL)
+	{
+		I_Assert(sizeof(startt_lspr) / sizeof(startt_lspr[0]) == numspriteinfo);
+		t_lspr = Z_Malloc(sizeof(*startt_lspr) * numspriteinfo, PU_STATIC, NULL);
+		memcpy(t_lspr, startt_lspr, sizeof(*startt_lspr) * numspriteinfo);
+		t_numlspr = numspriteinfo;
+		return;
+	}
+	t_lspr = Z_Realloc(t_lspr, sizeof(*startt_lspr) * numspriteinfo, PU_STATIC, NULL);
+	for (i = t_numlspr; i < numspriteinfo; i++)
+	{
+		t_lspr[i] = &lspr[NOLIGHT];
+	}
+	t_numlspr = numspriteinfo;
+}
+
 #ifdef ALAM_LIGHTING
 
 //=============================================================================
diff --git a/src/hardware/hw_light.h b/src/hardware/hw_light.h
index e9d87933dcb1509886bfff9f138a07ddc91516ee..56c359b92c74036e21677648623e05ca9a8a5966 100644
--- a/src/hardware/hw_light.h
+++ b/src/hardware/hw_light.h
@@ -90,5 +90,5 @@ typedef enum
 } lightspritenum_t;
 
 extern light_t lspr[NUMLIGHTS];
-extern light_t *t_lspr[NUMSPRITES];
+extern light_t **t_lspr;
 #endif
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 518797ca5c3ae34f4b83200f3dcdf85400d719fe..b1ede7e4b1ab194990fd97ff0e5f817bd4d279f8 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -4373,19 +4373,19 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	{
 		sprdef = &sprites[thing->sprite];
 #ifdef ROTSPRITE
-		sprinfo = &spriteinfo[thing->sprite];
+		sprinfo = spriteinfo[thing->sprite];
 #endif
 	}
 
 	if (rot >= sprdef->numframes)
 	{
 		CONS_Alert(CONS_ERROR, M_GetText("HWR_ProjectSprite: invalid sprite frame %s/%s for %s\n"),
-			sizeu1(rot), sizeu2(sprdef->numframes), sprnames[thing->sprite]);
+			sizeu1(rot), sizeu2(sprdef->numframes), spriteinfo[thing->sprite]->name);
 		thing->sprite = states[S_UNKNOWN]->sprite;
 		thing->frame = states[S_UNKNOWN]->frame;
 		sprdef = &sprites[thing->sprite];
 #ifdef ROTSPRITE
-		sprinfo = &spriteinfo[thing->sprite];
+		sprinfo = spriteinfo[thing->sprite];
 #endif
 		rot = thing->frame&FF_FRAMEMASK;
 		thing->state->sprite = thing->sprite;
@@ -4722,7 +4722,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	vis->gz = gz;
 
 	//CONS_Debug(DBG_RENDER, "------------------\nH: sprite  : %d\nH: frame   : %x\nH: type    : %d\nH: sname   : %s\n\n",
-	//            thing->sprite, thing->frame, thing->type, sprnames[thing->sprite]);
+	//            thing->sprite, thing->frame, thing->type, spriteinfo[thing->sprite]->name);
 
 	vis->vflip = vflip;
 
@@ -4798,7 +4798,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
 	if ((size_t)(thing->frame&FF_FRAMEMASK) >= sprdef->numframes)
 #ifdef RANGECHECK
 		I_Error("HWR_ProjectPrecipitationSprite: invalid sprite frame %i : %i for %s",
-		        thing->sprite, thing->frame, sprnames[thing->sprite]);
+		        thing->sprite, thing->frame, spriteinfo[thing->sprite]->name);
 #else
 		return;
 #endif
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 456bbc173cfad66699b072bb71d48a9641177e86..9917058a3a0f557d88707b22bfadb1a668f818e3 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -72,7 +72,8 @@
 #include "errno.h"
 #endif
 
-md2_t md2_models[NUMSPRITES];
+static UINT32 md2_nummodels;
+md2_t *md2_models;
 md2_t *md2_playermodels = NULL;
 size_t md2_numplayermodels = 0;
 
@@ -491,7 +492,9 @@ void HWR_InitModels(void)
 {
 	size_t i;
 
-	for (i = 0; i < NUMSPRITES; i++)
+	md2_nummodels = numspriteinfo;
+	md2_models = Z_Malloc(sizeof(*md2_models) * numspriteinfo, PU_STATIC, NULL);
+	for (i = 0; i < numspriteinfo; i++)
 	{
 		md2_models[i].scale = -1.0f;
 		md2_models[i].model = NULL;
@@ -506,6 +509,25 @@ void HWR_InitModels(void)
 		HWR_LoadModels();
 }
 
+extern void HWR_AllocateMD2Model(void);  // silence warning
+
+void HWR_AllocateMD2Model(void)
+{
+	UINT32 i;
+	md2_models = Z_Realloc(md2_models, sizeof(*md2_models) * numspriteinfo, PU_STATIC, NULL);
+	for (i = md2_nummodels; i < numspriteinfo; i++)
+	{
+		md2_models[i].scale = -1.0f;
+		md2_models[i].model = NULL;
+		md2_models[i].grpatch = NULL;
+		md2_models[i].notexturefile = false;
+		md2_models[i].noblendfile = false;
+		md2_models[i].found = false;
+		md2_models[i].error = false;
+	}
+	md2_nummodels = numspriteinfo;
+}
+
 void HWR_LoadModels(void)
 {
 	size_t i;
@@ -573,7 +595,7 @@ void HWR_LoadModels(void)
 		// Add sprite models.
 		for (i = 0; i < numsprites; i++)
 		{
-			if (stricmp(name, sprnames[i]) == 0)
+			if (stricmp(name, spriteinfo[i]->name) == 0)
 			{
 				md2_models[i].scale = scale;
 				md2_models[i].offset = offset;
@@ -1349,7 +1371,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 			return false; // we already failed loading this before :(
 		if (!md2->model)
 		{
-			//CONS_Debug(DBG_RENDER, "Loading model... (%s)", sprnames[spr->mobj->sprite]);
+			//CONS_Debug(DBG_RENDER, "Loading model... (%s)", spriteinfo[spr->mobj->sprite]->name);
 			sprintf(filename, "models/%s", md2->filename);
 			md2->model = md2_readModel(filename);
 
diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h
index 473f21cb74fc799d5e6245c3a63121abd9308cad..57692b2dc070b104bc2fbcdd2e0a2cb880a226e8 100644
--- a/src/hardware/hw_md2.h
+++ b/src/hardware/hw_md2.h
@@ -35,7 +35,7 @@ typedef struct
 	boolean     error;
 } md2_t;
 
-extern md2_t md2_models[NUMSPRITES];
+extern md2_t *md2_models;
 extern md2_t *md2_playermodels;
 extern size_t md2_numplayermodels;
 
diff --git a/src/info.c b/src/info.c
index a7739820180003d875520e35fa87242781b12c8b..59d3243733a9f8b655f5ed4315d0d79a9de97f3c 100644
--- a/src/info.c
+++ b/src/info.c
@@ -29,7 +29,7 @@
 
 // Hey, moron! If you change this table, don't forget about the sprite enum in info.h and the sprite lights in hw_light.c!
 // For the sake of constant merge conflicts, let's spread this out
-char sprnames[NUMSPRITES + 1][MAXSPRITENAME + 1] =
+static const char sprnames[][MAXSPRITENAME + 1] =
 {
 	"NULL", // invisible object
 	"UNKN",
@@ -526,6 +526,9 @@ char sprnames[NUMSPRITES + 1][MAXSPRITENAME + 1] =
 	"GWLR",
 };
 
+UINT32 numspriteinfo;
+spriteinfo_t **spriteinfo;
+
 char spr2names[NUMPLAYERSPRITES][MAXSPRITENAME + 1] =
 {
 	"STND",
@@ -22448,25 +22451,11 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 void P_PatchInfoTables(void)
 {
 	UINT32 i;
-	char *tempname;
 
 #if NUMSPRITEFREESLOTS > 9999 //tempname numbering actually starts at SPR_FIRSTFREESLOT, so the limit is actually 9999 + SPR_FIRSTFREESLOT-1, but the preprocessor doesn't understand enums, so its left at 9999 for safety
 "Update P_PatchInfoTables, you big dumb head"
 #endif
 
-	// empty out free slots
-	for (i = SPR_FIRSTFREESLOT; i <= SPR_LASTFREESLOT; i++)
-	{
-		tempname = sprnames[i];
-		tempname[0] = (char)('0' + (char)((i-SPR_FIRSTFREESLOT+1)/1000));
-		tempname[1] = (char)('0' + (char)(((i-SPR_FIRSTFREESLOT+1)/100)%10));
-		tempname[2] = (char)('0' + (char)(((i-SPR_FIRSTFREESLOT+1)/10)%10));
-		tempname[3] = (char)('0' + (char)((i-SPR_FIRSTFREESLOT+1)%10));
-		tempname[4] = '\0';
-#ifdef HWRENDER
-		t_lspr[i] = &lspr[NOLIGHT];
-#endif
-	}
 	memset(&skincolors[SKINCOLOR_FIRSTFREESLOT], 0, sizeof (skincolor_t) * NUMCOLORFREESLOTS);
 	for (i = SKINCOLOR_FIRSTFREESLOT; i <= SKINCOLOR_LASTFREESLOT; i++) {
 		skincolors[i].accessible = false;
@@ -22475,9 +22464,8 @@ void P_PatchInfoTables(void)
 }
 
 #ifdef ALLOW_RESETDATA
-static char *sprnamesbackup;
 static skincolor_t *skincolorsbackup;
-static size_t sprnamesbackupsize, skincolorsbackupsize;
+static size_t skincolorsbackupsize;
 #endif
 
 UINT32 P_AllocateMobjinfo(const char *name)
@@ -22500,6 +22488,22 @@ UINT32 P_AllocateState(const char *name)
 	return numstates-1;
 }
 
+void R_ResizeSprites(void);
+extern void HWR_AllocateMD2Model(void);
+extern void HWR_AllocateLSpr(void);
+
+UINT32 P_AllocateSpriteinfo(const char *name)
+{
+	spriteinfo = Z_Realloc(spriteinfo, sizeof(*spriteinfo) * ++numspriteinfo, PU_STATIC, NULL);
+	spriteinfo[numspriteinfo-1] = Z_Malloc(sizeof(spriteinfo_t), PU_STATIC, NULL);
+	memset(spriteinfo[numspriteinfo-1], 0, sizeof(spriteinfo_t));
+	strcpy(spriteinfo[numspriteinfo-1]->name, name);
+	R_ResizeSprites();
+	HWR_AllocateMD2Model();
+	HWR_AllocateLSpr();
+	return numspriteinfo-1;
+}
+
 UINT32 P_GetMobjinfoIndex(mobjinfo_t *info)
 {
 	UINT32 i;
@@ -22511,6 +22515,17 @@ UINT32 P_GetMobjinfoIndex(mobjinfo_t *info)
 	I_Error("Tried to get index of an invalid mobjinfo_t!");
 }
 
+UINT32 P_GetSpriteinfoIndex(spriteinfo_t *info)
+{
+	UINT32 i;
+	for (i = 0; i < numspriteinfo; i++)
+	{
+		if (spriteinfo[i] == info)
+			return i;
+	}
+	I_Error("Tried to get index of an invalid spriteinfo_t!");
+}
+
 void P_InitializeTables(void)
 {
 	UINT32 i;
@@ -22530,22 +22545,25 @@ void P_InitializeTables(void)
 		memcpy(states[i], &startstates[i], sizeof(state_t));
 		states[i]->num = i;
 	}
+
+	numspriteinfo = sizeof(sprnames) / sizeof(sprnames[0]);
+	spriteinfo = Z_Malloc(sizeof(*spriteinfo) * numspriteinfo, PU_STATIC, NULL);
+	for (i = 0; i < numspriteinfo; i++)
+	{
+		spriteinfo[i] = Z_Malloc(sizeof(spriteinfo_t), PU_STATIC, NULL);
+		memset(spriteinfo[i], 0, sizeof(spriteinfo_t));
+		strcpy(spriteinfo[i]->name, sprnames[i]);
+	}
+	HWR_AllocateMD2Model();
+	HWR_AllocateLSpr();
 }
 
 void P_BackupTables(void)
 {
 #ifdef ALLOW_RESETDATA
 	// Allocate buffers in size equal to that of the uncompressed data to begin with
-	sprnamesbackup = Z_Malloc(sizeof(sprnames), PU_STATIC, NULL);
 	skincolorsbackup = Z_Malloc(sizeof(skincolors), PU_STATIC, NULL);
 
-	// Sprite names
-	sprnamesbackupsize = lzf_compress(sprnames, sizeof(sprnames), sprnamesbackup, sizeof(sprnames));
-	if (sprnamesbackupsize > 0)
-		sprnamesbackup = Z_Realloc(sprnamesbackup, sprnamesbackupsize, PU_STATIC, NULL);
-	else
-		M_Memcpy(sprnamesbackup, sprnames, sizeof(sprnames));
-
 	//Skincolor info
 	skincolorsbackupsize = lzf_compress(skincolors, sizeof(skincolors), skincolorsbackup, sizeof(skincolors));
 	if (skincolorsbackupsize > 0)
@@ -22564,10 +22582,11 @@ void P_ResetData(INT32 flags)
 	UINT32 i;
 	if (flags & 1)
 	{
-		if (sprnamesbackupsize > 0)
-			lzf_decompress(sprnamesbackup, sprnamesbackupsize, sprnames, sizeof(sprnames));
-		else
-			M_Memcpy(sprnames, sprnamesbackup, sizeof(sprnamesbackup));
+		for (i = 0; i < sizeof(sprnames) / sizeof(sprnames[0]); i++)
+		{
+			memset(spriteinfo[numspriteinfo-1], 0, sizeof(spriteinfo_t));
+			strcpy(spriteinfo[i]->name, sprnames[i]);
+		}
 	}
 
 	if (flags & 2)
diff --git a/src/info.h b/src/info.h
index 0bf4d32541aa1a9e49493092fe336bf32e8537a1..e9f4e1a547738f3ec68748e22cd8e1ed8c803fa9 100644
--- a/src/info.h
+++ b/src/info.h
@@ -1072,10 +1072,6 @@ typedef enum sprite
 	// Gravity Well Objects
 	SPR_GWLG,
 	SPR_GWLR,
-
-	SPR_FIRSTFREESLOT,
-	SPR_LASTFREESLOT = SPR_FIRSTFREESLOT + NUMSPRITEFREESLOTS - 1,
-	NUMSPRITES
 } spritenum_t;
 
 typedef enum playersprite
@@ -1161,6 +1157,23 @@ typedef enum playersprite
 	NUMPLAYERSPRITES
 } playersprite_t;
 
+#define MAXFRAMENUM 256
+
+typedef struct
+{
+	INT32 x, y;
+} spriteframepivot_t;
+
+typedef struct
+{
+	char name[MAXSPRITENAME+1];
+	spriteframepivot_t pivot[MAXFRAMENUM];
+	boolean available;
+} spriteinfo_t;
+
+extern UINT32 numspriteinfo;
+extern spriteinfo_t **spriteinfo;
+
 enum
 {
 	XTRA_LIFEPIC,
@@ -4383,7 +4396,6 @@ typedef struct
 extern state_t **states;
 extern UINT32 numstates;
 
-extern char sprnames[NUMSPRITES + 1][MAXSPRITENAME + 1];
 extern char spr2names[NUMPLAYERSPRITES][MAXSPRITENAME + 1];
 extern playersprite_t spr2defaults[NUMPLAYERSPRITES];
 extern state_t *astate;
@@ -5198,7 +5210,9 @@ extern UINT32 nummobjinfo;
 
 UINT32 P_AllocateMobjinfo(const char *name);
 UINT32 P_AllocateState(const char *name);
+UINT32 P_AllocateSpriteinfo(const char *name);
 UINT32 P_GetMobjinfoIndex(mobjinfo_t *info);
+UINT32 P_GetSpriteinfoIndex(spriteinfo_t *info);
 void P_InitializeTables(void);
 
 void P_PatchInfoTables(void);
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index d6771f1082a635e2bcb9e26ffb4349398113397c..57544947da5b0b6263ec17c56d9808b7246d2596 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -486,14 +486,14 @@ static int libd_getSpritePatch(lua_State *L)
 	if (lua_isnumber(L, 1)) // sprite number given, e.g. SPR_THOK
 	{
 		i = lua_tonumber(L, 1);
-		if (i >= NUMSPRITES)
+		if (i >= numspriteinfo)
 			return 0;
 	}
 	else if (lua_isstring(L, 1)) // sprite prefix name given, e.g. "THOK"
 	{
 		const char *name = lua_tostring(L, 1);
 		i = R_GetSpriteNumByName(name);
-		if (i >= NUMSPRITES)
+		if (i >= numspriteinfo)
 			return 0;
 	}
 	else
@@ -530,7 +530,7 @@ static int libd_getSpritePatch(lua_State *L)
 		INT32 rot = R_GetRollAngle(rollangle);
 
 		if (rot) {
-			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), &spriteinfo[i], rot);
+			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);
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 16712deffe88d11ed4f3aa2f92e15b2c750b54b4..1b2322f116b5672ae485aa77be97423816adbb21 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -80,16 +80,16 @@ static int lib_getSprname(lua_State *L)
 	if (lua_isnumber(L, 1))
 	{
 		i = lua_tonumber(L, 1);
-		if (i > NUMSPRITES)
+		if (i > numspriteinfo)
 			return 0;
-		lua_pushlstring(L, sprnames[i], 4);
+		lua_pushlstring(L, spriteinfo[i]->name, 4);
 		return 1;
 	}
 	else if (lua_isstring(L, 1))
 	{
 		const char *name = lua_tostring(L, 1);
 		i = R_GetSpriteNumByName(name);
-		if (i != NUMSPRITES)
+		if (i != numspriteinfo)
 		{
 			lua_pushinteger(L, i);
 			return 1;
@@ -101,7 +101,7 @@ static int lib_getSprname(lua_State *L)
 /// \todo Maybe make it tally up the used_spr from dehacked?
 static int lib_sprnamelen(lua_State *L)
 {
-	lua_pushinteger(L, NUMSPRITES);
+	lua_pushinteger(L, numspriteinfo);
 	return 1;
 }
 
@@ -239,14 +239,14 @@ static int lib_spr2namelen(lua_State *L)
 // spriteinfo[]
 static int lib_getSpriteInfo(lua_State *L)
 {
-	UINT32 i = NUMSPRITES;
+	UINT32 i = numspriteinfo;
 	lua_remove(L, 1);
 
 	if (lua_isstring(L, 1))
 	{
 		const char *name = lua_tostring(L, 1);
-		INT32 spr = R_GetSpriteNumByName(name);
-		if (spr == NUMSPRITES)
+		UINT32 spr = R_GetSpriteNumByName(name);
+		if (spr == numspriteinfo)
 		{
 			char *check;
 			i = strtol(name, &check, 10);
@@ -258,10 +258,10 @@ static int lib_getSpriteInfo(lua_State *L)
 	else
 		i = luaL_checkinteger(L, 1);
 
-	if (i == 0 || i >= NUMSPRITES)
-		return luaL_error(L, "spriteinfo[] index %d out of range (1 - %d)", i, NUMSPRITES-1);
+	if (i == 0 || i >= numspriteinfo)
+		return luaL_error(L, "spriteinfo[] index %d out of range (1 - %d)", i, numspriteinfo-1);
 
-	LUA_PushUserdata(L, &spriteinfo[i], META_SPRITEINFO);
+	LUA_PushUserdata(L, spriteinfo[i], META_SPRITEINFO);
 	return 1;
 }
 
@@ -377,9 +377,9 @@ static int lib_setSpriteInfo(lua_State *L)
 	lua_remove(L, 1);
 	{
 		UINT32 i = luaL_checkinteger(L, 1);
-		if (i == 0 || i >= NUMSPRITES)
-			return luaL_error(L, "spriteinfo[] index %d out of range (1 - %d)", i, NUMSPRITES-1);
-		info = &spriteinfo[i]; // get the spriteinfo to assign to.
+		if (i == 0 || i >= numspriteinfo)
+			return luaL_error(L, "spriteinfo[] index %d out of range (1 - %d)", i, numspriteinfo-1);
+		info = spriteinfo[i]; // get the spriteinfo to assign to.
 	}
 	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
 	lua_remove(L, 1); // pop sprite num, don't need it any more.
@@ -414,7 +414,7 @@ static int lib_setSpriteInfo(lua_State *L)
 
 static int lib_spriteinfolen(lua_State *L)
 {
-	lua_pushinteger(L, NUMSPRITES);
+	lua_pushinteger(L, numspriteinfo);
 	return 1;
 }
 
@@ -484,11 +484,9 @@ static int spriteinfo_set(lua_State *L)
 static int spriteinfo_num(lua_State *L)
 {
 	spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFO));
-
 	I_Assert(sprinfo != NULL);
-	I_Assert(sprinfo >= spriteinfo);
 
-	lua_pushinteger(L, (UINT32)(sprinfo-spriteinfo));
+	lua_pushinteger(L, P_GetSpriteinfoIndex(sprinfo));
 	return 1;
 }
 
@@ -714,7 +712,7 @@ static int lib_setState(lua_State *L)
 
 		if (i == 1 || (str && fastcmp(str, "sprite"))) {
 			value = luaL_checkinteger(L, 3);
-			if (value < SPR_NULL || value >= NUMSPRITES)
+			if (value < SPR_NULL || (UINT32)value >= numspriteinfo)
 				return luaL_error(L, "sprite number %d is invalid.", value);
 			state->sprite = (spritenum_t)value;
 		} else if (i == 2 || (str && fastcmp(str, "frame"))) {
@@ -958,7 +956,7 @@ static int state_set(lua_State *L)
 
 	if (fastcmp(field,"sprite")) {
 		value = luaL_checknumber(L, 3);
-		if (value < SPR_NULL || value >= NUMSPRITES)
+		if (value < SPR_NULL || (UINT32)value >= numspriteinfo)
 			return luaL_error(L, "sprite number %d is invalid.", value);
 		st->sprite = (spritenum_t)value;
 	} else if (fastcmp(field,"frame"))
diff --git a/src/r_defs.h b/src/r_defs.h
index da4dd2d70e6049479eacd24c51af11b4a995507b..cb94dd6e5a5641970b216e88ce9183d66d186e63 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -976,8 +976,6 @@ typedef struct
 #endif
 } spriteframe_t;
 
-#define MAXFRAMENUM 256
-
 //
 // A sprite definition:  a number of animation frames.
 //
diff --git a/src/r_picformats.c b/src/r_picformats.c
index d71657021b6e2cc91efe504b49286f459461095d..bacb5fc263891d5a8a39ad45ff0d25dedec23df2 100644
--- a/src/r_picformats.c
+++ b/src/r_picformats.c
@@ -1571,7 +1571,7 @@ static void R_ParseSpriteInfo(boolean spr2)
 	char *sprinfoToken;
 	size_t sprinfoTokenLength;
 	char newSpriteName[MAXSPRITENAME + 1]; // no longer dynamically allocated
-	spritenum_t sprnum = NUMSPRITES;
+	spritenum_t sprnum = numspriteinfo;
 	playersprite_t spr2num = NUMPLAYERSPRITES;
 	INT32 i;
 	UINT8 *skinnumbers = NULL;
@@ -1593,7 +1593,7 @@ static void R_ParseSpriteInfo(boolean spr2)
 	if (!spr2)
 	{
 		sprnum = R_GetSpriteNumByName(newSpriteName);
-		if (sprnum == NUMSPRITES)
+		if (sprnum == numspriteinfo)
 			I_Error("Error parsing SPRTINFO lump: Unknown sprite name \"%s\"", newSpriteName);
 	}
 	else
@@ -1679,7 +1679,7 @@ static void R_ParseSpriteInfo(boolean spr2)
 					}
 				}
 				else
-					M_Memcpy(&spriteinfo[sprnum], info, sizeof(spriteinfo_t));
+					M_Memcpy(spriteinfo[sprnum], info, sizeof(spriteinfo_t));
 			}
 			else
 			{
diff --git a/src/r_picformats.h b/src/r_picformats.h
index 098f927a5619d74ca9813d37658d5bfb75bc8f04..d47a818a31a393ac7947f03ae24ae12cee977f4a 100644
--- a/src/r_picformats.h
+++ b/src/r_picformats.h
@@ -93,17 +93,6 @@ typedef enum
 	ROTAXIS_Z  // Yaw
 } rotaxis_t;
 
-typedef struct
-{
-	INT32 x, y;
-} spriteframepivot_t;
-
-typedef struct
-{
-	spriteframepivot_t pivot[MAXFRAMENUM];
-	boolean available;
-} spriteinfo_t;
-
 // PNG support
 #define PNG_HEADER_SIZE 8
 
@@ -122,7 +111,6 @@ boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *to
 #endif
 
 // SpriteInfo
-extern spriteinfo_t spriteinfo[NUMSPRITES];
 void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps);
 void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum);
 
diff --git a/src/r_things.c b/src/r_things.c
index 8f70dfd9942f6c0d3ce2a949f98433a268f9eb77..00ae3ba696e104c25700d01428a1a4a5fc4623b6 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -67,8 +67,6 @@ static lighttable_t **spritelights;
 INT16 negonearray[MAXVIDWIDTH];
 INT16 screenheightarray[MAXVIDWIDTH];
 
-spriteinfo_t spriteinfo[NUMSPRITES];
-
 //
 // INITIALIZATION FUNCTIONS
 //
@@ -118,10 +116,10 @@ static INT32 drawsegs_xrange_count = 0;
 
 spritenum_t R_GetSpriteNumByName(const char *name)
 {
-	for (spritenum_t i = 0; i < NUMSPRITES; i++)
-		if (!strcmp(name, sprnames[i]))
+	for (spritenum_t i = 0; i < numspriteinfo; i++)
+		if (!strcmp(name, spriteinfo[i]->name))
 			return i;
-	return NUMSPRITES;
+	return numspriteinfo;
 }
 
 //
@@ -635,12 +633,12 @@ static void AddShortSpriteDefs(UINT16 wadnum, size_t *ptr_spritesadded, size_t *
 	//
 	for (i = 0; i < numsprites; i++)
 	{
-		if (R_AddSingleSpriteDef(sprnames[i], &sprites[i], wadnum, start, end, false))
+		if (R_AddSingleSpriteDef(spriteinfo[i]->name, &sprites[i], wadnum, start, end, false))
 		{
 			// if a new sprite was added (not just replaced)
 			(*ptr_spritesadded)++;
 #ifndef ZDEBUG
-			CONS_Debug(DBG_SETUP, "sprite %s set in pwad %d\n", sprnames[i], wadnum);
+			CONS_Debug(DBG_SETUP, "sprite %s set in pwad %d\n", spriteinfo[i]->name, wadnum);
 #endif
 		}
 	}
@@ -680,7 +678,7 @@ static void AddLongSpriteDefs(UINT16 wadnum, size_t *ptr_spritesadded, size_t *p
 		strupr(sprname);
 		sprnum = R_GetSpriteNumByName(sprname);
 
-		if (sprnum != NUMSPRITES && R_AddSingleSpriteDef(sprname, &sprites[sprnum], wadnum, folderstart, folderend, true))
+		if (sprnum != numspriteinfo && R_AddSingleSpriteDef(sprname, &sprites[sprnum], wadnum, folderstart, folderend, true))
 		{
 			// A new sprite was added (not just replaced)
 			(*ptr_spritesadded)++;
@@ -724,6 +722,15 @@ UINT32 visspritecount, numvisiblesprites;
 static UINT32 clippedvissprites;
 static vissprite_t *visspritechunks[MAXVISSPRITES >> VISSPRITECHUNKBITS] = {NULL};
 
+extern void R_ResizeSprites(void);  // silence warnings
+
+void R_ResizeSprites(void)
+{
+	sprites = Z_Realloc(sprites, numspriteinfo * sizeof(*sprites), PU_STATIC, NULL);
+	memset(&sprites[numsprites], 0, (numspriteinfo - numsprites) * sizeof(*sprites));
+	numsprites = numspriteinfo;
+}
+
 //
 // R_InitSprites
 // Called at program start.
@@ -751,14 +758,7 @@ void R_InitSprites(void)
 	//
 	// count the number of sprite names, and allocate sprites table
 	//
-	numsprites = 0;
-	for (i = 0; i < NUMSPRITES + 1; i++)
-		if (sprnames[i][0] != '\0') numsprites++;
-
-	if (!numsprites)
-		I_Error("R_AddSpriteDefs: no sprites in namelist\n");
-
-	sprites = Z_Calloc(numsprites * sizeof (*sprites), PU_STATIC, NULL);
+	R_ResizeSprites();
 
 	// find sprites in each -file added pwad
 	for (i = 0; i < numwadfiles; i++)
@@ -1818,7 +1818,7 @@ static void R_ProjectSprite(mobj_t *thing)
 			thing->frame = states[S_UNKNOWN]->frame;
 			sprdef = &sprites[thing->sprite];
 #ifdef ROTSPRITE
-			sprinfo = &spriteinfo[thing->sprite];
+			sprinfo = spriteinfo[thing->sprite];
 #endif
 			frame = thing->frame&FF_FRAMEMASK;
 		}
@@ -1827,13 +1827,13 @@ static void R_ProjectSprite(mobj_t *thing)
 	{
 		sprdef = &sprites[thing->sprite];
 #ifdef ROTSPRITE
-		sprinfo = &spriteinfo[thing->sprite];
+		sprinfo = spriteinfo[thing->sprite];
 #endif
 
 		if (frame >= sprdef->numframes)
 		{
 			CONS_Alert(CONS_ERROR, M_GetText("R_ProjectSprite: invalid sprite frame %s/%s for %s\n"),
-				sizeu1(frame), sizeu2(sprdef->numframes), sprnames[thing->sprite]);
+				sizeu1(frame), sizeu2(sprdef->numframes), spriteinfo[thing->sprite]->name);
 			if (thing->sprite == thing->state->sprite && thing->frame == thing->state->frame)
 			{
 				thing->state->sprite = states[S_UNKNOWN]->sprite;
@@ -1842,7 +1842,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];
+			sprinfo = spriteinfo[thing->sprite];
 			frame = thing->frame&FF_FRAMEMASK;
 		}
 	}
@@ -2526,7 +2526,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
 #ifdef RANGECHECK
 	if ((UINT8)(thing->frame&FF_FRAMEMASK) >= sprdef->numframes)
 		I_Error("R_ProjectPrecipitationSprite: invalid sprite frame %d : %d for %s",
-			thing->sprite, thing->frame, sprnames[thing->sprite]);
+			thing->sprite, thing->frame, spriteinfo[thing->sprite]->name);
 #endif
 
 	sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK];