diff --git a/src/filesrch.c b/src/filesrch.c
index acc176d6a..b90123d4b 100644
--- a/src/filesrch.c
+++ b/src/filesrch.c
@@ -31,6 +31,7 @@
 #include "filesrch.h"
 #include "d_netfil.h"
 #include "m_misc.h"
+#include "z_zone.h"
 
 #if (defined (_WIN32) && !defined (_WIN32_WCE)) && defined (_MSC_VER) && !defined (_XBOX)
 
@@ -39,7 +40,7 @@
 #include <tchar.h>
 
 #define SUFFIX	"*"
-#define	SLASH	"\\"
+#define	SLASH	PATHSEP
 #define	S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
 
 #ifndef INVALID_FILE_ATTRIBUTES
@@ -285,6 +286,15 @@ closedir (DIR * dirp)
   return rc;
 }
 #endif
+
+char menupath[1024];
+size_t menupathindex[20];
+size_t menudepthleft = 20;
+
+char **dirmenu;
+size_t sizedirmenu;
+size_t dir_on;
+
 #if defined (_XBOX) && defined (_MSC_VER)
 filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum,
 	boolean completepath, int maxsearchdepth)
@@ -296,6 +306,12 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
 	completepath = false;
 	return FS_NOTFOUND;
 }
+
+boolean preparefilemenu(void)
+{
+	return false;
+}
+
 #elif defined (_WIN32_WCE)
 filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum,
 	boolean completepath, int maxsearchdepth)
@@ -346,6 +362,11 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
 #endif
 	return FS_NOTFOUND;
 }
+
+boolean preparefilemenu(void)
+{
+	return false;
+}
 #else
 filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth)
 {
@@ -387,25 +408,29 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
 	{
 		searchpath[searchpathindex[depthleft]]=0;
 		dent = readdir(dirhandle[depthleft]);
-		if (dent)
-			strcpy(&searchpath[searchpathindex[depthleft]],dent->d_name);
 
 		if (!dent)
+		{
 			closedir(dirhandle[depthleft++]);
-		else if (dent->d_name[0]=='.' &&
+			continue;
+		}
+
+		if (dent->d_name[0]=='.' &&
 				(dent->d_name[1]=='\0' ||
 					(dent->d_name[1]=='.' &&
 						dent->d_name[2]=='\0')))
 		{
 			// we don't want to scan uptree
+			continue;
 		}
-		else if (stat(searchpath,&fsstat) < 0) // do we want to follow symlinks? if not: change it to lstat
-		{
-			// was the file (re)moved? can't stat it
-		}
+
+		// okay, now we actually want searchpath to incorporate d_name
+		strcpy(&searchpath[searchpathindex[depthleft]],dent->d_name);
+
+		if (stat(searchpath,&fsstat) < 0) // do we want to follow symlinks? if not: change it to lstat
+			; // was the file (re)moved? can't stat it
 		else if (S_ISDIR(fsstat.st_mode) && depthleft)
 		{
-			strcpy(&searchpath[searchpathindex[depthleft]],dent->d_name);
 			searchpathindex[--depthleft] = strlen(searchpath) + 1;
 			dirhandle[depthleft] = opendir(searchpath);
 			if (!dirhandle[depthleft])
@@ -444,6 +469,135 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
 	free(searchname);
 	free(searchpathindex);
 	free(dirhandle);
+
 	return retval;
 }
+
+#define MAXEXT 5
+char ext[MAXEXT][5] = {
+	".txt", ".cfg", // exec
+	".wad", ".soc", ".lua"}; // addfile
+
+boolean preparefilemenu(void)
+{
+	DIR *dirhandle;
+	struct dirent *dent;
+	struct stat fsstat;
+	size_t pos, folderpos = 0, numfolders = 0;
+
+	for (pos = 0; pos < sizedirmenu; pos++)
+	{
+		Z_Free(dirmenu[pos]);
+		dirmenu[pos] = NULL;
+	}
+
+	sizedirmenu = dir_on = pos = 0;
+
+	dirhandle = opendir(menupath);
+
+	if (dirhandle == NULL)
+		return false;
+
+	while (true)
+	{
+		menupath[menupathindex[menudepthleft]] = 0;
+		dent = readdir(dirhandle);
+
+		if (!dent)
+			break;
+		else if (dent->d_name[0]=='.' &&
+				(dent->d_name[1]=='\0' ||
+					(dent->d_name[1]=='.' &&
+						dent->d_name[2]=='\0')))
+			continue; // we don't want to scan uptree
+
+		strcpy(&menupath[menupathindex[menudepthleft]],dent->d_name);
+
+		if (stat(menupath,&fsstat) < 0) // do we want to follow symlinks? if not: change it to lstat
+			; // was the file (re)moved? can't stat it
+		else // is a file or directory
+		{
+			if (!S_ISDIR(fsstat.st_mode))
+			{
+				size_t len = strlen(dent->d_name)+1;
+				UINT8 i;
+				for (i = 0; i < MAXEXT; i++)
+					if (!strcasecmp(ext[i], dent->d_name+len-5)) break;
+				if (i == MAXEXT) continue; // not an addfile-able (or exec-able) file
+			}
+			else
+				numfolders++;
+			sizedirmenu++;
+		}
+	}
+
+	closedir(dirhandle); // I don't know how to go back to the start of the folder without just opening and closing... if there's a way, it doesn't appear to be easily manipulatable
+
+	if (!sizedirmenu)
+		return false;
+
+	if (!(dirmenu = Z_Realloc(dirmenu, sizedirmenu*sizeof(char *), PU_STATIC, NULL)))
+		I_Error("Ran out of memory whilst preparing add-ons menu");
+
+	dirhandle = opendir(menupath);
+
+	while ((pos+folderpos) < sizedirmenu)
+	{
+		menupath[menupathindex[menudepthleft]] = 0;
+		dent = readdir(dirhandle);
+
+		if (!dent)
+			break;
+		else if (dent->d_name[0]=='.' &&
+				(dent->d_name[1]=='\0' ||
+					(dent->d_name[1]=='.' &&
+						dent->d_name[2]=='\0')))
+			continue; // we don't want to scan uptree
+
+		strcpy(&menupath[menupathindex[menudepthleft]],dent->d_name);
+
+		if (stat(menupath,&fsstat) < 0) // do we want to follow symlinks? if not: change it to lstat
+			; // was the file (re)moved? can't stat it
+		else // is a file or directory
+		{
+			char *temp;
+			size_t len = strlen(dent->d_name)+1;
+			UINT8 i = 0;
+			size_t folder;
+
+			if (!S_ISDIR(fsstat.st_mode)) // file
+			{
+				for (; i < MAXEXT; i++)
+					if (!strcasecmp(ext[i], dent->d_name+len-5)) break;
+				if (i == MAXEXT) continue; // not an addfile-able (or exec-able) file
+				i++; // i goes up so zero-index is directory instead of .txt
+				folder = 0;
+			}
+			else
+				len += (folder = 1);
+
+			if (len > 255)
+				len = 255;
+
+			if (!(temp = Z_Malloc((len+2+folder) * sizeof (char), PU_STATIC, NULL)))
+				I_Error("Ran out of memory whilst preparing add-ons menu");
+			temp[0] = i;
+			temp[1] = (UINT8)(len);
+			strlcpy(temp+2, dent->d_name, len);
+			if (folder)
+			{
+				strcpy(temp+len, "/");
+				dirmenu[folderpos++] = temp;
+			}
+			else
+				dirmenu[numfolders + pos++] = temp;
+		}
+	}
+
+	menupath[menupathindex[menudepthleft]] = 0;
+	sizedirmenu = (pos+folderpos); // crash prevention if things change between openings somehow
+
+	closedir(dirhandle);
+	return true;
+}
 #endif
diff --git a/src/filesrch.h b/src/filesrch.h
index 33b148d4b..c6d161597 100644
--- a/src/filesrch.h
+++ b/src/filesrch.h
@@ -25,4 +25,14 @@
 filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum,
 	boolean completepath, int maxsearchdepth);
 
+extern char menupath[1024];
+extern size_t menupathindex[20];
+extern size_t menudepthleft;
+
+extern char **dirmenu;
+extern size_t sizedirmenu;
+extern size_t dir_on;
+
+boolean preparefilemenu(void);
+
 #endif // __FILESRCH_H__
diff --git a/src/m_menu.c b/src/m_menu.c
index fb8aeedad..a3ee63ecb 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -33,6 +33,9 @@
 #include "s_sound.h"
 #include "i_system.h"
 
+// Addfile
+#include "filesrch.h"
+
 #include "v_video.h"
 #include "i_video.h"
 #include "keys.h"
@@ -330,10 +333,12 @@ menu_t OP_NetgameOptionsDef, OP_GametypeOptionsDef;
 menu_t OP_MonitorToggleDef;
 static void M_ScreenshotOptions(INT32 choice);
 static void M_EraseData(INT32 choice);
+static void M_Addons(INT32 choice);
 
 // Drawing functions
 static void M_DrawGenericMenu(void);
 static void M_DrawCenteredMenu(void);
+static void M_DrawAddons(void);
 static void M_DrawSkyRoom(void);
 static void M_DrawChecklist(void);
 static void M_DrawEmblemHints(void);
@@ -369,6 +374,7 @@ static boolean M_CancelConnect(void);
 #endif
 static boolean M_ExitPandorasBox(void);
 static boolean M_QuitMultiPlayerMenu(void);
+static void M_HandleAddons(INT32 choice);
 static void M_HandleLevelPlatter(INT32 choice);
 static void M_HandleSoundTest(INT32 choice);
 static void M_HandleImageDef(INT32 choice);
@@ -476,10 +482,11 @@ static consvar_t cv_dummymares = {"dummymares", "Overall", CV_HIDEN|CV_CALL, dum
 // ---------
 static menuitem_t MainMenu[] =
 {
-	{IT_CALL   |IT_STRING, NULL, "Secrets",     M_SecretsMenu,      84},
-	{IT_CALL   |IT_STRING, NULL, "1  player",   M_SinglePlayerMenu, 92},
-	{IT_SUBMENU|IT_STRING, NULL, "multiplayer", &MP_MainDef,       100},
-	{IT_CALL   |IT_STRING, NULL, "options",     M_Options,         108},
+	{IT_CALL   |IT_STRING, NULL, "Secrets",     M_SecretsMenu,      76},
+	{IT_CALL   |IT_STRING, NULL, "1  player",   M_SinglePlayerMenu, 84},
+	{IT_SUBMENU|IT_STRING, NULL, "multiplayer", &MP_MainDef,        92},
+	{IT_CALL   |IT_STRING, NULL, "options",     M_Options,         100},
+	{IT_CALL   |IT_STRING, NULL, "addons",      M_Addons,          108},
 	{IT_CALL   |IT_STRING, NULL, "quit  game",  M_QuitSRB2,        116},
 };
 
@@ -489,9 +496,15 @@ typedef enum
 	singleplr,
 	multiplr,
 	options,
+	addons,
 	quitdoom
 } main_e;
 
+static menuitem_t MISC_AddonsMenu[] =
+{
+	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleAddons, 0},     // dummy menuitem for the control func
+};
+
 // ---------------------------------
 // Pause Menu Mode Attacking Edition
 // ---------------------------------
@@ -1432,6 +1445,18 @@ static menuitem_t OP_MonitorToggleMenu[] =
 // Main Menu and related
 menu_t MainDef = CENTERMENUSTYLE(NULL, MainMenu, NULL, 72);
 
+menu_t MISC_AddonsDef =
+{
+	NULL,
+	sizeof (MISC_AddonsMenu)/sizeof (menuitem_t),
+	&MainDef,
+	MISC_AddonsMenu,
+	M_DrawAddons,
+	0, 0,
+	0,
+	NULL
+};
+
 menu_t MAPauseDef = PAUSEMENUSTYLE(MAPauseMenu, 40, 72);
 menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72);
 menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72);
@@ -4414,6 +4439,123 @@ static void M_HandleImageDef(INT32 choice)
 // MISC MAIN MENU OPTIONS
 // ======================
 
+static void M_Addons(INT32 choice)
+{
+	(void)choice;
+
+	strlcpy(menupath, srb2home, 1024);
+	menupathindex[(menudepthleft = 19)] = strlen(menupath) + 1;
+
+	if (menupath[menupathindex[menudepthleft]-2] != '/')
+	{
+		menupath[menupathindex[menudepthleft]-1] = '/';
+		menupath[menupathindex[menudepthleft]] = 0;
+	}
+	else
+		--menupathindex[menudepthleft];
+
+	if (!preparefilemenu())
+	{
+		M_StartMessage(M_GetText("No files/folders found.\n\n(Press a key)\n"),NULL,MM_NOTHING);
+		return;
+	}
+
+	MISC_AddonsDef.prevMenu = currentMenu;
+	M_SetupNextMenu(&MISC_AddonsDef);
+}
+
+static void M_DrawAddons(void)
+{
+	INT32 x, y;
+	size_t i;
+
+	// DRAW MENU
+	x = currentMenu->x;
+	y = currentMenu->y;
+
+	V_DrawString(x, y, 0, menupath);
+	y += 2*SMALLLINEHEIGHT;
+
+	for (i = dir_on; i < sizedirmenu; i++)
+	{
+		if (y > BASEVIDHEIGHT) break;
+		V_DrawString(x, y, 0, dirmenu[i]+2);
+		y += SMALLLINEHEIGHT;
+	}
+}
+
+static void M_HandleAddons(INT32 choice)
+{
+	boolean exitmenu = false; // exit to previous menu
+
+	switch (choice)
+	{
+		case KEY_DOWNARROW:
+			if (dir_on < sizedirmenu-1)
+				dir_on++;
+			S_StartSound(NULL, sfx_menu1);
+			break;
+		case KEY_UPARROW:
+			if (dir_on)
+				dir_on--;
+			S_StartSound(NULL, sfx_menu1);
+			break;
+		case KEY_ENTER:
+			if (dirmenu[dir_on][0] == 0) // folder
+			{
+				S_StartSound(NULL, sfx_strpst);
+				strcpy(&menupath[menupathindex[menudepthleft--]],dirmenu[dir_on]+2);
+				menupathindex[menudepthleft] = strlen(menupath);
+				menupath[menupathindex[menudepthleft]] = 0;
+
+				if (!preparefilemenu())
+				{
+					M_StartMessage(M_GetText("Folder is empty.\n\n(Press a key)\n"),NULL,MM_NOTHING);
+					menupath[menupathindex[++menudepthleft]] = 0;
+					if (!preparefilemenu())
+					{
+						M_StartMessage(M_GetText("Folder no longer exists!\n\n(Press a key)\n"),NULL,MM_NOTHING);
+						M_SetupNextMenu(MISC_AddonsDef.prevMenu);
+						return;
+					}
+				}
+			}
+			else if (dirmenu[dir_on][0] >= 3) // wad/soc/lua
+			{
+				S_StartSound(NULL, sfx_strpst);
+				COM_BufAddText(va("addfile %s%s", menupath, dirmenu[dir_on]+2));
+			}
+			else
+				S_StartSound(NULL, sfx_lose);
+			break;
+		case KEY_BACKSPACE:
+			if (menudepthleft < 19)
+			{
+				menupath[menupathindex[++menudepthleft]] = 0;
+				if (!preparefilemenu())
+				{
+					M_StartMessage(M_GetText("Folder no longer exists!\n\n(Press a key)\n"),NULL,MM_NOTHING);
+					M_SetupNextMenu(MISC_AddonsDef.prevMenu);
+					return;
+				}
+				break;
+			}
+		case KEY_ESCAPE:
+			exitmenu = true;
+			break;
+
+		default:
+			break;
+	}
+	if (exitmenu)
+	{
+		if (currentMenu->prevMenu)
+			M_SetupNextMenu(currentMenu->prevMenu);
+		else
+			M_ClearMenus(true);
+	}
+}
+
 static void M_PandorasBox(INT32 choice)
 {
 	(void)choice;