From 605580358db61796b58d6ada4501487fd1de6a68 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Sat, 29 Apr 2017 16:40:07 +0100
Subject: [PATCH] Addfile menu!

Basic as fuck UI, and the controls for manipulating it suck, but the basic elements of the feature set I'm looking for have been implemented in some form or another. More at a later time.

MI, be glad I didn't do this in deeznux ;P
---
 src/filesrch.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++---
 src/filesrch.h |  10 +++
 src/m_menu.c   | 150 ++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 319 insertions(+), 13 deletions(-)

diff --git a/src/filesrch.c b/src/filesrch.c
index acc176d6a3..b90123d4b6 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 33b148d4b8..c6d1615973 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 fb8aeedad1..a3ee63ecbe 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;
-- 
GitLab