diff --git a/src/g_game.c b/src/g_game.c
index b239800447972d1bf9e8931ddf6c5796e397824f..47e670bfecebef38e74d96c20993be11a9f6f5ef 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -4309,6 +4309,12 @@ void G_LoadGameData(gamedata_t *data)
 	// Stop saving, until we successfully load it again.
 	data->loaded = false;
 
+	// Backwards compat stuff
+	INT32 max_emblems = MAXEMBLEMS;
+	INT32 max_extraemblems = MAXEXTRAEMBLEMS;
+	INT32 max_unlockables = MAXUNLOCKABLES;
+	INT32 max_conditionsets = MAXCONDITIONSETS;
+
 	// Clear things so previously read gamedata doesn't transfer
 	// to new gamedata
 	G_ClearRecords(data); // main and nights records
@@ -4325,7 +4331,7 @@ void G_LoadGameData(gamedata_t *data)
 	{
 		// Don't load, but do save. (essentially, reset)
 		data->loaded = true;
-		return; 
+		return;
 	}
 
 	length = FIL_ReadFile(va(pandf, srb2home, gamedatafilename), &savebuffer);
@@ -4355,6 +4361,14 @@ void G_LoadGameData(gamedata_t *data)
 		I_Error("Game data is from another version of SRB2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
 	}
 
+#ifdef COMPAT_GAMEDATA_ID // Account for lower MAXUNLOCKABLES and MAXEXTRAEMBLEMS from older versions
+	if (versionID == COMPAT_GAMEDATA_ID)
+	{
+		max_extraemblems = 16;
+		max_unlockables = 32;
+	}
+#endif
+
 	data->totalplaytime = READUINT32(save_p);
 
 #ifdef COMPAT_GAMEDATA_ID
@@ -4393,31 +4407,31 @@ void G_LoadGameData(gamedata_t *data)
 			goto datacorrupt;
 
 	// To save space, use one bit per collected/achieved/unlocked flag
-	for (i = 0; i < MAXEMBLEMS;)
+	for (i = 0; i < max_emblems;)
 	{
 		rtemp = READUINT8(save_p);
-		for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
+		for (j = 0; j < 8 && j+i < max_emblems; ++j)
 			data->collected[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
-	for (i = 0; i < MAXEXTRAEMBLEMS;)
+	for (i = 0; i < max_extraemblems;)
 	{
 		rtemp = READUINT8(save_p);
-		for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
+		for (j = 0; j < 8 && j+i < max_extraemblems; ++j)
 			data->extraCollected[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
-	for (i = 0; i < MAXUNLOCKABLES;)
+	for (i = 0; i < max_unlockables;)
 	{
 		rtemp = READUINT8(save_p);
-		for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
+		for (j = 0; j < 8 && j+i < max_unlockables; ++j)
 			data->unlocked[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
-	for (i = 0; i < MAXCONDITIONSETS;)
+	for (i = 0; i < max_conditionsets;)
 	{
 		rtemp = READUINT8(save_p);
-		for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
+		for (j = 0; j < 8 && j+i < max_conditionsets; ++j)
 			data->achieved[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
diff --git a/src/m_cond.h b/src/m_cond.h
index 6a3da79ece7091c7ef0f68981355e6d0a691bb79..95c3e1ebeb0345fe102acf2ab4af4f1331031178 100644
--- a/src/m_cond.h
+++ b/src/m_cond.h
@@ -139,8 +139,8 @@ typedef struct
 // you seriously need to get a life.
 #define MAXCONDITIONSETS 128
 #define MAXEMBLEMS       512
-#define MAXEXTRAEMBLEMS   16
-#define MAXUNLOCKABLES    32
+#define MAXEXTRAEMBLEMS   48
+#define MAXUNLOCKABLES    80
 
 /** Time attack information, currently a very small structure.
   */
diff --git a/src/m_menu.c b/src/m_menu.c
index cf61e7fb1a50909fcdefc956fff0733ea204b904..1dc0037d700027b61c8d54065e2aec9e462fb8b2 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -694,42 +694,10 @@ static menuitem_t SR_PandorasBox[] =
 };
 
 // Sky Room Custom Unlocks
-static menuitem_t SR_MainMenu[] =
+static menuitem_t SR_MainMenu[MAXUNLOCKABLES+1] =
 {
 	{IT_STRING|IT_SUBMENU,NULL, "Extras Checklist", &SR_UnlockChecklistDef, 0},
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom1
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom2
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom3
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom4
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom5
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom6
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom7
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom8
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom9
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom10
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom11
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom12
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom13
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom14
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom15
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom16
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom17
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom18
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom19
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom20
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom21
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom22
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom23
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom24
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom25
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom26
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom27
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom28
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom29
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom30
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom31
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom32
-
+	// The remaining (MAXUNLOCKABLES) items are now initialized in M_SecretsMenu
 };
 
 static menuitem_t SR_LevelSelectMenu[] =
@@ -8130,14 +8098,15 @@ static void M_SecretsMenu(INT32 choice)
 
 	(void)choice;
 
-	// Clear all before starting
-	for (i = 1; i < MAXUNLOCKABLES+1; ++i)
-		SR_MainMenu[i].status = IT_DISABLED;
+	// Initialize array with placeholder entries
+	menuitem_t placeholder = {IT_DISABLED, NULL, "", NULL, 0};
+	for (i = 1; i <= MAXUNLOCKABLES; ++i)
+		SR_MainMenu[i] = placeholder;
 
 	memset(skyRoomMenuTranslations, 0, sizeof(skyRoomMenuTranslations));
 	memset(done, 0, sizeof(done));
 
-	for (i = 1; i < MAXUNLOCKABLES+1; ++i)
+	for (i = 1; i <= MAXUNLOCKABLES; ++i)
 	{
 		curheight = UINT16_MAX;
 		ul = -1;
diff --git a/src/p_spec.c b/src/p_spec.c
index 9195ef01995315cbd830687804cd91443f5959a3..b9650bfeb583e61403c43e4dd57bfc8978eed4ae 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1808,7 +1808,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			{ // An unlockable itself must be unlocked!
 				INT32 unlockid = triggerline->args[1];
 
-				if (unlockid < 0 || unlockid >= MAXUNLOCKABLES) // limited by unlockable count
+				if (unlockid <= 0 || unlockid > MAXUNLOCKABLES) // limited by unlockable count
 				{
 					CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %hu): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid);
 					return false;
diff --git a/src/r_skins.c b/src/r_skins.c
index db3227307244301357aaa5759084b240f90fcc7e..9443ad49b03d492e6856f4125881def0b1f463e4 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -190,7 +190,8 @@ UINT32 R_GetSkinAvailabilities(void)
 			// This crash is impossible to trigger as is,
 			// but it could happen if MAXUNLOCKABLES is ever made higher than 32,
 			// and someone makes a mod that has 33+ unlockable characters. :V
-			I_Error("Too many unlockable characters\n");
+			// 2022/03/15: MAXUNLOCKABLES is now higher than 32
+			I_Error("Too many unlockable characters! (maximum is 32)\n");
 			return 0;
 		}