Merge branch 'addfile_menu' into 'master'

Add-ons menu

Does what it says on the tin! Very pretty. Would like help testing in filesystems that are not Windows, although I've recieved assurances from Sryder that since it's based on the same filesrch code, it should work cross-platform. Just want to confirm it works, though.

Also, M_* is whitelisted lumpname too.

root/!LatestSRB2Files/srb2win_branch_addfile.exe, requires new patch.dta.

See merge request !92
This commit is contained in:
Monster Iestyn 2017-08-05 16:05:12 -04:00
commit 2e2b71da92
14 changed files with 1085 additions and 137 deletions

View File

@ -33,6 +33,7 @@
#include "i_system.h"
#include "d_main.h"
#include "m_menu.h"
#include "filesrch.h"
#ifdef _WINDOWS
#include "win32/win_main.h"
@ -1275,12 +1276,15 @@ void CONS_Alert(alerttype_t level, const char *fmt, ...)
switch (level)
{
case CONS_NOTICE:
// no notice for notices, hehe
CONS_Printf("\x83" "%s" "\x80 ", M_GetText("NOTICE:"));
break;
case CONS_WARNING:
refreshdirmenu |= REFRESHDIR_WARNING;
CONS_Printf("\x82" "%s" "\x80 ", M_GetText("WARNING:"));
break;
case CONS_ERROR:
refreshdirmenu |= REFRESHDIR_ERROR;
CONS_Printf("\x85" "%s" "\x80 ", M_GetText("ERROR:"));
break;
}

View File

@ -315,6 +315,7 @@ typedef struct
} ATTRPACK clientconfig_pak;
#define MAXSERVERNAME 32
#define MAXFILENEEDED 915
// This packet is too large
typedef struct
{
@ -336,7 +337,7 @@ typedef struct
unsigned char mapmd5[16];
UINT8 actnum;
UINT8 iszone;
UINT8 fileneeded[915]; // is filled with writexxx (byteptr.h)
UINT8 fileneeded[MAXFILENEEDED]; // is filled with writexxx (byteptr.h)
} ATTRPACK serverinfo_pak;
typedef struct

View File

@ -74,6 +74,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#include "m_cond.h" // condition initialization
#include "fastcmp.h"
#include "keys.h"
#include "filesrch.h" // refreshdirmenu, mainwadstally
#ifdef CMAKECONFIG
#include "config.h"
@ -584,6 +585,8 @@ void D_SRB2Loop(void)
realtics = entertic - oldentertics;
oldentertics = entertic;
refreshdirmenu = 0; // not sure where to put this, here as good as any?
#ifdef DEBUGFILE
if (!realtics)
if (debugload)
@ -870,7 +873,7 @@ static void IdentifyVersion(void)
}
#endif
#if 1 // This section can be deleted when music_new is merged with music.dta
#ifdef DEVELOP // This section can be deleted when music_new is merged with music.dta
{
const char *musicfile = "music_new.dta";
const char *musicpath = va(pandf,srb2waddir,musicfile);
@ -1162,6 +1165,11 @@ void D_SRB2Main(void)
#ifdef USE_PATCH_DTA
++mainwads; // patch.dta adds one more
#endif
#ifdef DEVELOP
++mainwads; // music_new, too
#endif
mainwadstally = packetsizetally;
cht_Init();

View File

@ -37,6 +37,7 @@
#include "d_main.h"
#include "m_random.h"
#include "f_finale.h"
#include "filesrch.h"
#include "mserv.h"
#include "md5.h"
#include "z_zone.h"
@ -714,6 +715,14 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_firenaxis);
CV_RegisterVar(&cv_firenaxis2);
// filesrch.c
CV_RegisterVar(&cv_addons_option);
CV_RegisterVar(&cv_addons_folder);
CV_RegisterVar(&cv_addons_md5);
CV_RegisterVar(&cv_addons_showall);
CV_RegisterVar(&cv_addons_search_type);
CV_RegisterVar(&cv_addons_search_case);
// WARNING: the order is important when initialising mouse2
// we need the mouse2port
CV_RegisterVar(&cv_mouse2port);

View File

@ -104,6 +104,7 @@ INT32 lastfilenum = -1;
/** Fills a serverinfo packet with information about wad files loaded.
*
* \todo Give this function a better name since it is in global scope.
* Used to have size limiting built in - now handled via W_LoadWadFile in w_wad.c
*
*/
UINT8 *PutFileNeeded(void)
@ -112,29 +113,22 @@ UINT8 *PutFileNeeded(void)
UINT8 *p = netbuffer->u.serverinfo.fileneeded;
char wadfilename[MAX_WADPATH] = "";
UINT8 filestatus;
size_t bytesused = 0;
for (i = 0; i < numwadfiles; i++)
{
// If it has only music/sound lumps, mark it as unimportant
if (W_VerifyNMUSlumps(wadfiles[i]->filename))
filestatus = 0;
else
filestatus = 1; // Important
// If it has only music/sound lumps, don't put it in the list
if (!wadfiles[i]->important)
continue;
filestatus = 1; // Importance - not really used any more, holds 1 by default for backwards compat with MS
// Store in the upper four bits
if (!cv_downloading.value)
filestatus += (2 << 4); // Won't send
else if ((wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024))
filestatus += (0 << 4); // Won't send
else
else if ((wadfiles[i]->filesize <= (UINT32)cv_maxsend.value * 1024))
filestatus += (1 << 4); // Will send if requested
bytesused += (nameonlylength(wadfilename) + 22);
// Don't write too far...
if (bytesused > sizeof(netbuffer->u.serverinfo.fileneeded))
I_Error("Too many wad files added to host a game. (%s, stopped on %s)\n", sizeu1(bytesused), wadfilename);
// else
// filestatus += (0 << 4); -- Won't send, too big
WRITEUINT8(p, filestatus);
@ -167,7 +161,6 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
{
fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet
filestatus = READUINT8(p); // The first byte is the file status
fileneeded[i].important = (UINT8)(filestatus & 3);
fileneeded[i].willsend = (UINT8)(filestatus >> 4);
fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size
fileneeded[i].file = NULL; // The file isn't open yet
@ -197,7 +190,7 @@ boolean CL_CheckDownloadable(void)
UINT8 i,dlstatus = 0;
for (i = 0; i < fileneedednum; i++)
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN && fileneeded[i].important)
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN)
{
if (fileneeded[i].willsend == 1)
continue;
@ -218,7 +211,7 @@ boolean CL_CheckDownloadable(void)
// not downloadable, put reason in console
CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n"));
for (i = 0; i < fileneedednum; i++)
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN && fileneeded[i].important)
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN)
{
CONS_Printf(" * \"%s\" (%dK)", fileneeded[i].filename, fileneeded[i].totalsize >> 10);
@ -271,7 +264,7 @@ boolean CL_SendRequestFile(void)
for (i = 0; i < fileneedednum; i++)
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN
&& fileneeded[i].important && (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2))
&& (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2))
{
I_Error("Attempted to download files that were not sendable");
}
@ -280,8 +273,7 @@ boolean CL_SendRequestFile(void)
netbuffer->packettype = PT_REQUESTFILE;
p = (char *)netbuffer->u.textcmd;
for (i = 0; i < fileneedednum; i++)
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD)
&& fileneeded[i].important)
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD))
{
totalfreespaceneeded += fileneeded[i].totalsize;
nameonly(fileneeded[i].filename);
@ -360,15 +352,9 @@ INT32 CL_CheckFiles(void)
CONS_Debug(DBG_NETPLAY, "game is modified; only doing basic checks\n");
for (i = 1, j = 1; i < fileneedednum || j < numwadfiles;)
{
if (i < fileneedednum && !fileneeded[i].important)
if (j < numwadfiles && !wadfiles[j]->important)
{
// Eh whatever, don't care
++i;
continue;
}
if (j < numwadfiles && W_VerifyNMUSlumps(wadfiles[j]->filename))
{
// Unimportant on our side. still don't care.
// Unimportant on our side.
++j;
continue;
}
@ -411,7 +397,7 @@ INT32 CL_CheckFiles(void)
break;
}
}
if (fileneeded[i].status != FS_NOTFOUND || !fileneeded[i].important)
if (fileneeded[i].status != FS_NOTFOUND)
continue;
packetsize += nameonlylength(fileneeded[i].filename) + 22;
@ -449,27 +435,8 @@ void CL_LoadServerFiles(void)
fileneeded[i].status = FS_OPEN;
}
else if (fileneeded[i].status == FS_MD5SUMBAD)
{
// If the file is marked important, don't even bother proceeding.
if (fileneeded[i].important)
I_Error("Wrong version of important file %s", fileneeded[i].filename);
// If it isn't, no need to worry the user with a console message,
// although it can't hurt to put something in the debug file.
// ...but wait a second. What if the local version is "important"?
if (!W_VerifyNMUSlumps(fileneeded[i].filename))
I_Error("File %s should only contain music and sound effects!",
fileneeded[i].filename);
// Okay, NOW we know it's safe. Whew.
P_AddWadFile(fileneeded[i].filename, NULL);
if (fileneeded[i].important)
G_SetGameModified(true);
fileneeded[i].status = FS_OPEN;
DEBFILE(va("File %s found but with different md5sum\n", fileneeded[i].filename));
}
else if (fileneeded[i].important)
I_Error("Wrong version of file %s", fileneeded[i].filename);
else
{
const char *s;
switch(fileneeded[i].status)

View File

@ -35,7 +35,6 @@ typedef enum
typedef struct
{
UINT8 important;
UINT8 willsend; // Is the server willing to send it?
char filename[MAX_WADPATH];
UINT8 md5sum[16];

View File

@ -31,6 +31,8 @@
#include "filesrch.h"
#include "d_netfil.h"
#include "m_misc.h"
#include "z_zone.h"
#include "m_menu.h" // Addons_option_Onchange
#if (defined (_WIN32) && !defined (_WIN32_WCE)) && defined (_MSC_VER) && !defined (_XBOX)
@ -255,6 +257,28 @@ readdir (DIR * dirp)
return (struct dirent *) 0;
}
/*
* rewinddir
*
* Makes the next readdir start from the beginning.
*/
int
rewinddir (DIR * dirp)
{
errno = 0;
/* Check for valid DIR struct. */
if (!dirp)
{
errno = EFAULT;
return -1;
}
dirp->dd_stat = 0;
return 0;
}
/*
* closedir
*
@ -285,6 +309,35 @@ closedir (DIR * dirp)
return rc;
}
#endif
static CV_PossibleValue_t addons_cons_t[] = {{0, "SRB2 Folder"}, /*{1, "HOME"}, {2, "SRB2 Folder"},*/ {3, "CUSTOM"}, {0, NULL}};
consvar_t cv_addons_option = {"addons_option", "SRB2 Folder", CV_SAVE|CV_CALL, addons_cons_t, Addons_option_Onchange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_addons_folder = {"addons_folder", "./", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t addons_md5_cons_t[] = {{0, "Name"}, {1, "Contents"}, {0, NULL}};
consvar_t cv_addons_md5 = {"addons_md5", "Name", CV_SAVE, addons_md5_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_addons_showall = {"addons_showall", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_addons_search_case = {"addons_search_case", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t addons_search_type_cons_t[] = {{0, "Start"}, {1, "Anywhere"}, {0, NULL}};
consvar_t cv_addons_search_type = {"addons_search_type", "Anywhere", CV_SAVE, addons_search_type_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
char menupath[1024];
size_t menupathindex[menudepth];
size_t menudepthleft = menudepth;
char menusearch[MAXSTRINGLENGTH+1];
char **dirmenu;
size_t sizedirmenu;
size_t dir_on[menudepth];
UINT8 refreshdirmenu = 0;
size_t packetsizetally = 0;
size_t mainwadstally = 0;
#if defined (_XBOX) && defined (_MSC_VER)
filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum,
boolean completepath, int maxsearchdepth)
@ -296,6 +349,13 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
completepath = false;
return FS_NOTFOUND;
}
boolean preparefilemenu(boolean samedepth)
{
(void)samedepth;
return false;
}
#elif defined (_WIN32_WCE)
filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum,
boolean completepath, int maxsearchdepth)
@ -346,6 +406,12 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
#endif
return FS_NOTFOUND;
}
boolean preparefilemenu(boolean samedepth)
{
(void)samedepth;
return false;
}
#else
filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth)
{
@ -387,25 +453,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 +514,255 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
free(searchname);
free(searchpathindex);
free(dirhandle);
return retval;
}
char exttable[NUM_EXT_TABLE][5] = {
".txt", ".cfg", // exec
".wad", ".soc", ".lua"}; // addfile
char filenamebuf[MAX_WADFILES][MAX_WADPATH];
static boolean filemenusearch(char *haystack, char *needle)
{
static char localhaystack[128];
strlcpy(localhaystack, haystack, 128);
if (!cv_addons_search_case.value)
strupr(localhaystack);
return ((cv_addons_search_type.value)
? (strstr(localhaystack, needle) != 0)
: (!strncmp(localhaystack, needle, menusearch[0])));
}
#define searchdir if (menusearch[0] && !filemenusearch(dent->d_name, localmenusearch))\
{\
rejected++;\
continue;\
}\
boolean preparefilemenu(boolean samedepth)
{
DIR *dirhandle;
struct dirent *dent;
struct stat fsstat;
size_t pos = 0, folderpos = 0, numfolders = 0, rejected = 0;
char *tempname = NULL;
boolean noresults = false;
char localmenusearch[MAXSTRINGLENGTH] = "";
if (samedepth)
{
if (dirmenu && dirmenu[dir_on[menudepthleft]])
tempname = Z_StrDup(dirmenu[dir_on[menudepthleft]]+DIR_STRING); // don't need to I_Error if can't make - not important, just QoL
}
else
menusearch[0] = menusearch[1] = 0; // clear search
for (; sizedirmenu > 0; sizedirmenu--) // clear out existing items
{
Z_Free(dirmenu[sizedirmenu-1]);
dirmenu[sizedirmenu-1] = NULL;
}
if (!(dirhandle = opendir(menupath))) // get directory
return false;
if (menusearch[0])
{
strcpy(localmenusearch, menusearch+1);
if (!cv_addons_search_case.value)
strupr(localmenusearch);
}
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)) // file
{
if (!cv_addons_showall.value)
{
size_t len = strlen(dent->d_name)+1;
UINT8 ext;
for (ext = 0; ext < NUM_EXT_TABLE; ext++)
if (!strcasecmp(exttable[ext], dent->d_name+len-5)) break; // extension comparison
if (ext == NUM_EXT_TABLE) continue; // not an addfile-able (or exec-able) file
}
searchdir;
}
else // directory
{
searchdir;
numfolders++;
}
sizedirmenu++;
}
}
if (!rejected && !sizedirmenu)
{
if (tempname)
Z_Free(tempname);
closedir(dirhandle);
return false;
}
if (((noresults = (menusearch[0] && !sizedirmenu)))
|| (!menusearch[0] && menudepthleft != menudepth-1)) // Make room for UP... or search entry
{
sizedirmenu++;
numfolders++;
folderpos++;
}
if (!(dirmenu = Z_Realloc(dirmenu, sizedirmenu*sizeof(char *), PU_STATIC, NULL)))
{
closedir(dirhandle); // just in case
I_Error("Ran out of memory whilst preparing add-ons menu");
}
rejected = 0;
rewinddir(dirhandle);
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 ext = EXT_FOLDER;
UINT8 folder;
if (!S_ISDIR(fsstat.st_mode)) // file
{
if (!((numfolders+pos) < sizedirmenu)) continue; // crash prevention
for (; ext < NUM_EXT_TABLE; ext++)
if (!strcasecmp(exttable[ext], dent->d_name+len-5)) break; // extension comparison
if (ext == NUM_EXT_TABLE && !cv_addons_showall.value) continue; // not an addfile-able (or exec-able) file
ext += EXT_START; // moving to be appropriate position
searchdir;
if (ext >= EXT_LOADSTART)
{
size_t i;
for (i = 0; i < numwadfiles; i++)
{
if (!filenamebuf[i][0])
{
strncpy(filenamebuf[i], wadfiles[i]->filename, MAX_WADPATH);
filenamebuf[i][MAX_WADPATH - 1] = '\0';
nameonly(filenamebuf[i]);
}
if (strcmp(dent->d_name, filenamebuf[i]))
continue;
if (cv_addons_md5.value && !checkfilemd5(menupath, wadfiles[i]->md5sum))
continue;
ext |= EXT_LOADED;
}
}
else if (ext == EXT_TXT)
{
if (!strcmp(dent->d_name, "log.txt") || !strcmp(dent->d_name, "errorlog.txt"))
ext |= EXT_LOADED;
}
if (!strcmp(dent->d_name, configfile))
ext |= EXT_LOADED;
folder = 0;
}
else // directory
{
searchdir;
len += (folder = 1);
}
if (len > 255)
len = 255;
if (!(temp = Z_Malloc((len+DIR_STRING+folder) * sizeof (char), PU_STATIC, NULL)))
I_Error("Ran out of memory whilst preparing add-ons menu");
temp[DIR_TYPE] = ext;
temp[DIR_LEN] = (UINT8)(len);
strlcpy(temp+DIR_STRING, dent->d_name, len);
if (folder)
{
strcpy(temp+len, "/");
dirmenu[folderpos++] = temp;
}
else
dirmenu[numfolders + pos++] = temp;
}
}
closedir(dirhandle);
if (noresults) // no results
dirmenu[0] = Z_StrDup(va("%c\13No results...", EXT_NORESULTS));
else if (!menusearch[0] &&menudepthleft != menudepth-1) // now for UP... entry
dirmenu[0] = Z_StrDup(va("%c\5UP...", EXT_UP));
menupath[menupathindex[menudepthleft]] = 0;
sizedirmenu = (numfolders+pos); // just in case things shrink between opening and rewind
if (tempname)
{
size_t i;
for (i = 0; i < sizedirmenu; i++)
{
if (!strcmp(dirmenu[i]+DIR_STRING, tempname))
{
dir_on[menudepthleft] = i;
break;
}
}
Z_Free(tempname);
}
if (!sizedirmenu)
{
dir_on[menudepthleft] = 0;
Z_Free(dirmenu);
return false;
}
else if (dir_on[menudepthleft] >= sizedirmenu)
dir_on[menudepthleft] = sizedirmenu-1;
return true;
}
#endif

View File

@ -6,6 +6,9 @@
#include "doomdef.h"
#include "d_netfil.h"
#include "m_menu.h" // MAXSTRINGLENGTH
extern consvar_t cv_addons_option, cv_addons_folder, cv_addons_md5, cv_addons_showall, cv_addons_search_case, cv_addons_search_type;
/** \brief The filesearch function
@ -25,4 +28,64 @@
filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum,
boolean completepath, int maxsearchdepth);
#define menudepth 20
extern char menupath[1024];
extern size_t menupathindex[menudepth];
extern size_t menudepthleft;
extern char menusearch[MAXSTRINGLENGTH+1];
extern char **dirmenu;
extern size_t sizedirmenu;
extern size_t dir_on[menudepth];
extern UINT8 refreshdirmenu;
extern size_t packetsizetally;
extern size_t mainwadstally;
typedef enum
{
EXT_FOLDER = 0,
EXT_UP,
EXT_NORESULTS,
EXT_START,
EXT_TXT = EXT_START,
EXT_CFG,
EXT_LOADSTART,
EXT_WAD = EXT_LOADSTART,
EXT_SOC,
EXT_LUA, // allowed even if not HAVE_BLUA so that we can yell on load attempt
NUM_EXT,
NUM_EXT_TABLE = NUM_EXT-EXT_START,
EXT_LOADED = 0x80
/*
obviously there can only be 0x7F supported extensions in
addons menu because we're cramming this into a char out of
laziness/easy memory allocation (what's the difference?)
and have stolen a bit to show whether it's loaded or not
in practice the size of the data type is probably overkill
toast 02/05/17
*/
} ext_enum;
typedef enum
{
DIR_TYPE = 0,
DIR_LEN,
DIR_STRING
} dirname_enum;
typedef enum
{
REFRESHDIR_NORMAL = 1,
REFRESHDIR_ADDFILE = 2,
REFRESHDIR_WARNING = 4,
REFRESHDIR_ERROR = 8,
REFRESHDIR_NOTLOADED = 16,
REFRESHDIR_MAX = 32
} refreshdir_enum;
boolean preparefilemenu(boolean samedepth);
#endif // __FILESRCH_H__

View File

@ -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"
@ -74,7 +77,6 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#define SMALLLINEHEIGHT 8
#define SLIDER_RANGE 9
#define SLIDER_WIDTH 78
#define MAXSTRINGLENGTH 32
#define SERVERS_PER_PAGE 11
typedef enum
@ -211,14 +213,13 @@ menu_t SPauseDef;
// Level Select
static levelselect_t levelselect = {0, NULL};
static UINT8 levelselectselect[4];
static UINT8 levelselectselect[3];
static patch_t *levselp[2][3];
static INT32 lsoffs[2];
#define lsrow levelselectselect[0]
#define lscol levelselectselect[1]
#define lstic levelselectselect[2]
#define lshli levelselectselect[3]
#define lshli levelselectselect[2]
#define lshseperation 101
#define lsbasevseperation 62
@ -334,12 +335,20 @@ menu_t OP_MonitorToggleDef;
static void M_ScreenshotOptions(INT32 choice);
static void M_EraseData(INT32 choice);
static void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlight);
static void M_Addons(INT32 choice);
static void M_AddonsOptions(INT32 choice);
static patch_t *addonsp[NUM_EXT+6];
static UINT8 addonsresponselimit = 0;
#define numaddonsshown 4
static void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlight, boolean allowlowercase);
// Drawing functions
static void M_DrawGenericMenu(void);
static void M_DrawGenericScrollMenu(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);
@ -379,6 +388,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);
@ -487,10 +497,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},
};
@ -500,9 +511,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
// ---------------------------------
@ -525,26 +542,28 @@ typedef enum
// ---------------------
static menuitem_t MPauseMenu[] =
{
{IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16},
{IT_STRING | IT_CALL, NULL, "Switch Gametype/Level...", M_GameTypeChange, 24},
{IT_STRING | IT_CALL, NULL, "Add-ons...", M_Addons, 8},
{IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16},
{IT_STRING | IT_CALL, NULL, "Switch Gametype/Level...", M_GameTypeChange, 24},
{IT_CALL | IT_STRING, NULL, "Continue", M_SelectableClearMenus,40},
{IT_CALL | IT_STRING, NULL, "Player 1 Setup", M_SetupMultiPlayer, 48}, // splitscreen
{IT_CALL | IT_STRING, NULL, "Player 2 Setup", M_SetupMultiPlayer2, 56}, // splitscreen
{IT_STRING | IT_CALL, NULL, "Continue", M_SelectableClearMenus,40},
{IT_STRING | IT_CALL, NULL, "Player 1 Setup", M_SetupMultiPlayer, 48}, // splitscreen
{IT_STRING | IT_CALL, NULL, "Player 2 Setup", M_SetupMultiPlayer2, 56}, // splitscreen
{IT_STRING | IT_CALL, NULL, "Spectate", M_ConfirmSpectate, 48},
{IT_STRING | IT_CALL, NULL, "Enter Game", M_ConfirmEnterGame, 48},
{IT_STRING | IT_SUBMENU, NULL, "Switch Team...", &MISC_ChangeTeamDef, 48},
{IT_CALL | IT_STRING, NULL, "Player Setup", M_SetupMultiPlayer, 56}, // alone
{IT_CALL | IT_STRING, NULL, "Options", M_Options, 64},
{IT_STRING | IT_CALL, NULL, "Player Setup", M_SetupMultiPlayer, 56}, // alone
{IT_STRING | IT_CALL, NULL, "Options", M_Options, 64},
{IT_CALL | IT_STRING, NULL, "Return to Title", M_EndGame, 80},
{IT_CALL | IT_STRING, NULL, "Quit Game", M_QuitSRB2, 88},
{IT_STRING | IT_CALL, NULL, "Return to Title", M_EndGame, 80},
{IT_STRING | IT_CALL, NULL, "Quit Game", M_QuitSRB2, 88},
};
typedef enum
{
mpause_scramble = 0,
mpause_addons = 0,
mpause_scramble,
mpause_switchmap,
mpause_continue,
@ -587,6 +606,7 @@ typedef enum
spause_continue,
spause_retry,
spause_options,
spause_title,
spause_quit
} spause_e;
@ -1318,9 +1338,10 @@ static menuitem_t OP_SoundOptionsMenu[] =
static menuitem_t OP_DataOptionsMenu[] =
{
{IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 10},
{IT_STRING | IT_CALL, NULL, "Add-on Options...", M_AddonsOptions, 10},
{IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 20},
{IT_STRING | IT_SUBMENU, NULL, "\x85" "Erase Data...", &OP_EraseDataDef, 20},
{IT_STRING | IT_SUBMENU, NULL, "\x85" "Erase Data...", &OP_EraseDataDef, 40},
};
static menuitem_t OP_ScreenshotOptionsMenu[] =
@ -1367,6 +1388,24 @@ static menuitem_t OP_EraseDataMenu[] =
{IT_STRING | IT_CALL, NULL, "\x85" "Erase ALL Data", M_EraseData, 40},
};
static menuitem_t OP_AddonsOptionsMenu[] =
{
{IT_HEADER, NULL, "Menu", NULL, 0},
{IT_STRING|IT_CVAR, NULL, "Location", &cv_addons_option, 12},
{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_addons_folder, 22},
{IT_STRING|IT_CVAR, NULL, "Identify loaded files via", &cv_addons_md5, 50},
{IT_STRING|IT_CVAR, NULL, "Show unsupported file types", &cv_addons_showall, 60},
{IT_HEADER, NULL, "Search", NULL, 78},
{IT_STRING|IT_CVAR, NULL, "Matching", &cv_addons_search_type, 90},
{IT_STRING|IT_CVAR, NULL, "Case-sensitive", &cv_addons_search_case, 100},
};
enum
{
op_addons_folder = 2,
};
static menuitem_t OP_ServerOptionsMenu[] =
{
{IT_HEADER, NULL, "General", NULL, 0},
@ -1441,6 +1480,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,
50, 28,
0,
NULL
};
menu_t MAPauseDef = PAUSEMENUSTYLE(MAPauseMenu, 40, 72);
menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72);
menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72);
@ -1804,17 +1855,7 @@ menu_t OP_SoundOptionsDef =
NULL
};
menu_t OP_ServerOptionsDef =
{
"M_SERVER",
sizeof (OP_ServerOptionsMenu)/sizeof (menuitem_t),
&OP_MainDef,
OP_ServerOptionsMenu,
M_DrawGenericScrollMenu,
30, 30,
0,
NULL
};
menu_t OP_ServerOptionsDef = DEFAULTSCROLLMENUSTYLE("M_SERVER", OP_ServerOptionsMenu, &OP_MainDef, 30, 30);
menu_t OP_MonitorToggleDef =
{
@ -1856,7 +1897,7 @@ menu_t OP_OpenGLColorDef =
NULL
};
#endif
menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE("M_DATA", OP_DataOptionsMenu, &OP_MainDef, 30, 30);
menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE("M_DATA", OP_DataOptionsMenu, &OP_MainDef, 60, 30);
menu_t OP_ScreenshotOptionsDef =
{
@ -1870,6 +1911,8 @@ menu_t OP_ScreenshotOptionsDef =
NULL
};
menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE("M_ADDONS", OP_AddonsOptionsMenu, &OP_DataOptionsDef, 30, 30);
menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_DataOptionsDef, 60, 30);
// ==========================================================================
@ -2088,6 +2131,12 @@ void Moviemode_mode_Onchange(void)
OP_ScreenshotOptionsMenu[i].status = IT_STRING|IT_CVAR;
}
void Addons_option_Onchange(void)
{
OP_AddonsOptionsMenu[op_addons_folder].status =
(cv_addons_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED);
}
// ==========================================================================
// END ORGANIZATION STUFF.
// ==========================================================================
@ -2150,9 +2199,12 @@ static void M_ChangeCvar(INT32 choice)
static boolean M_ChangeStringCvar(INT32 choice)
{
consvar_t *cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction;
char buf[255];
char buf[MAXSTRINGLENGTH];
size_t len;
if (shiftdown && choice >= 32 && choice <= 127)
choice = shiftxform[choice];
switch (choice)
{
case KEY_BACKSPACE:
@ -2463,8 +2515,6 @@ boolean M_Responder(event_t *ev)
{
if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING)
{
if (shiftdown && ch >= 32 && ch <= 127)
ch = shiftxform[ch];
if (M_ChangeStringCvar(ch))
return true;
else
@ -2710,6 +2760,7 @@ void M_StartControlPanel(void)
else // multiplayer
{
MPauseMenu[mpause_switchmap].status = IT_DISABLED;
MPauseMenu[mpause_addons].status = IT_DISABLED;
MPauseMenu[mpause_scramble].status = IT_DISABLED;
MPauseMenu[mpause_psetupsplit].status = IT_DISABLED;
MPauseMenu[mpause_psetupsplit2].status = IT_DISABLED;
@ -2721,6 +2772,7 @@ void M_StartControlPanel(void)
if ((server || adminplayer == consoleplayer))
{
MPauseMenu[mpause_switchmap].status = IT_STRING | IT_CALL;
MPauseMenu[mpause_addons].status = IT_STRING | IT_CALL;
if (G_GametypeHasTeams())
MPauseMenu[mpause_scramble].status = IT_STRING | IT_SUBMENU;
}
@ -3262,7 +3314,7 @@ static void M_DrawGenericMenu(void)
y = currentMenu->y+currentMenu->menuitems[i].alphaKey;
//V_DrawString(x-16, y, V_YELLOWMAP, currentMenu->menuitems[i].text);
M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), currentMenu->menuitems[i].text, true);
M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), currentMenu->menuitems[i].text, true, false);
y += SMALLLINEHEIGHT;
break;
}
@ -3402,7 +3454,7 @@ static void M_DrawGenericScrollMenu(void)
break;
case IT_HEADERTEXT:
//V_DrawString(x-16, y, V_YELLOWMAP, currentMenu->menuitems[i].text);
M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), currentMenu->menuitems[i].text, true);
M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), currentMenu->menuitems[i].text, true, false);
break;
}
}
@ -3912,7 +3964,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt)
I_Error("Insufficient memory to prepare level platter");
// done here so lsrow and lscol can be set if cv_nextmap is on the platter
lsrow = lscol = lstic = lshli = lsoffs[0] = lsoffs[1] = 0;
lsrow = lscol = lshli = lsoffs[0] = lsoffs[1] = 0;
while (mapnum < NUMMAPS)
{
@ -4171,10 +4223,10 @@ static void M_HandleLevelPlatter(INT32 choice)
}
}
void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlight)
void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlight, boolean allowlowercase)
{
y += lsheadingheight - 12;
V_DrawString(19, y, (headerhighlight ? V_YELLOWMAP : 0), header);
V_DrawString(19, y, (headerhighlight ? V_YELLOWMAP : 0)|(allowlowercase ? V_ALLOWLOWERCASE : 0), header);
y += 9;
if ((y >= 0) && (y < 200))
{
@ -4183,9 +4235,7 @@ void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlig
}
y++;
if ((y >= 0) && (y < 200))
{
V_DrawFill(19, y, 282, 1, 26);
}
}
static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolean highlight)
@ -4279,7 +4329,7 @@ static void M_DrawLevelPlatterRow(UINT8 row, INT32 y)
const boolean rowhighlight = (row == lsrow);
if (levelselect.rows[row].header[0])
{
M_DrawLevelPlatterHeader(y, levelselect.rows[row].header, (rowhighlight || (row == lshli)));
M_DrawLevelPlatterHeader(y, levelselect.rows[row].header, (rowhighlight || (row == lshli)), false);
y += lsheadingheight;
}
@ -4298,9 +4348,6 @@ static void M_DrawLevelPlatterMenu(void)
INT32 y = lsbasey + lsoffs[0] - getheadingoffset(lsrow);
const INT32 cursorx = (sizeselect ? 0 : (lscol*lshseperation));
if (++lstic == 32)
lstic = 0;
if (gamestate == GS_TIMEATTACK)
V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE));
@ -4320,7 +4367,7 @@ static void M_DrawLevelPlatterMenu(void)
}
// draw cursor box
V_DrawSmallScaledPatch(lsbasex + cursorx + lsoffs[1], lsbasey, 0, ((lstic & 8) ? levselp[sizeselect][0] : levselp[sizeselect][1]));
V_DrawSmallScaledPatch(lsbasex + cursorx + lsoffs[1], lsbasey, 0, (levselp[sizeselect][((skullAnimCounter/4) ? 1 : 0)]));
if (levelselect.rows[lsrow].maplist[lscol])
V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE));
@ -4620,6 +4667,511 @@ static void M_HandleImageDef(INT32 choice)
// MISC MAIN MENU OPTIONS
// ======================
static void M_AddonsOptions(INT32 choice)
{
(void)choice;
Addons_option_Onchange();
M_SetupNextMenu(&OP_AddonsOptionsDef);
}
#define LOCATIONSTRING "Visit \x83SRB2.ORG/MODS\x80 to get & make add-ons!"
static void M_Addons(INT32 choice)
{
const char *pathname = ".";
(void)choice;
/*if (cv_addons_option.value == 0)
pathname = srb2home; usehome ? srb2home : srb2path;
else if (cv_addons_option.value == 1)
pathname = srb2home;
else if (cv_addons_option.value == 2)
pathname = srb2path;
else*/
if (cv_addons_option.value == 3 && *cv_addons_folder.string != '\0')
pathname = cv_addons_folder.string;
strlcpy(menupath, pathname, 1024);
menupathindex[(menudepthleft = menudepth-1)] = strlen(menupath) + 1;
if (menupath[menupathindex[menudepthleft]-2] != '/')
{
menupath[menupathindex[menudepthleft]-1] = '/';
menupath[menupathindex[menudepthleft]] = 0;
}
else
--menupathindex[menudepthleft];
if (!preparefilemenu(false))
{
M_StartMessage(M_GetText("No files/folders found.\n\n"LOCATIONSTRING"\n\n(Press a key)\n"),NULL,MM_NOTHING);
return;
}
else
dir_on[menudepthleft] = 0;
if (addonsp[0]) // never going to have some provided but not all, saves individually checking
{
size_t i;
for (i = 0; i < NUM_EXT+6; i++)
W_UnlockCachedPatch(addonsp[i]);
}
addonsp[EXT_FOLDER] = W_CachePatchName("M_FFLDR", PU_STATIC);
addonsp[EXT_UP] = W_CachePatchName("M_FBACK", PU_STATIC);
addonsp[EXT_NORESULTS] = W_CachePatchName("M_FNOPE", PU_STATIC);
addonsp[EXT_TXT] = W_CachePatchName("M_FTXT", PU_STATIC);
addonsp[EXT_CFG] = W_CachePatchName("M_FCFG", PU_STATIC);
addonsp[EXT_WAD] = W_CachePatchName("M_FWAD", PU_STATIC);
addonsp[EXT_SOC] = W_CachePatchName("M_FSOC", PU_STATIC);
addonsp[EXT_LUA] = W_CachePatchName("M_FLUA", PU_STATIC);
addonsp[NUM_EXT] = W_CachePatchName("M_FUNKN", PU_STATIC);
addonsp[NUM_EXT+1] = W_CachePatchName("M_FSEL1", PU_STATIC);
addonsp[NUM_EXT+2] = W_CachePatchName("M_FSEL2", PU_STATIC);
addonsp[NUM_EXT+3] = W_CachePatchName("M_FLOAD", PU_STATIC);
addonsp[NUM_EXT+4] = W_CachePatchName("M_FSRCH", PU_STATIC);
addonsp[NUM_EXT+5] = W_CachePatchName("M_FSAVE", PU_STATIC);
MISC_AddonsDef.prevMenu = currentMenu;
M_SetupNextMenu(&MISC_AddonsDef);
}
#define width 4
#define vpadding 27
#define h (BASEVIDHEIGHT-(2*vpadding))
#define NUMCOLOURS 8 // when toast's coding it's british english hacker fucker
static void M_DrawTemperature(INT32 x, fixed_t t)
{
INT32 y;
// bounds check
if (t > FRACUNIT)
t = FRACUNIT;
/*else if (t < 0) -- not needed
t = 0;*/
// scale
if (t > 1)
t = (FixedMul(h<<FRACBITS, t)>>FRACBITS);
// border
V_DrawFill(x - 1, vpadding, 1, h, 3);
V_DrawFill(x + width, vpadding, 1, h, 3);
V_DrawFill(x - 1, vpadding-1, width+2, 1, 3);
V_DrawFill(x - 1, vpadding+h, width+2, 1, 3);
// bar itself
y = h;
if (t)
for (t = h - t; y > 0; y--)
{
UINT8 colours[NUMCOLOURS] = {42, 40, 58, 222, 65, 90, 97, 98};
UINT8 c;
if (y <= t) break;
if (y+vpadding >= BASEVIDHEIGHT/2)
c = 113;
else
c = colours[(NUMCOLOURS*(y-1))/(h/2)];
V_DrawFill(x, y-1 + vpadding, width, 1, c);
}
// fill the rest of the backing
if (y)
V_DrawFill(x, vpadding, width, y, 27);
}
#undef width
#undef vpadding
#undef h
#undef NUMCOLOURS
static char *M_AddonsHeaderPath(void)
{
UINT32 len;
static char header[1024];
if (menupath[0] == '.')
strlcpy(header, va("SRB2 folder%s", menupath+1), 1024);
else
strcpy(header, menupath);
len = strlen(header);
if (len > 34)
{
len = len-34;
header[len] = header[len+1] = header[len+2] = '.';
}
else
len = 0;
return header+len;
}
#define UNEXIST S_StartSound(NULL, sfx_lose);\
M_SetupNextMenu(MISC_AddonsDef.prevMenu);\
M_StartMessage(va("\x82%s\x80\nThis folder no longer exists!\nAborting to main menu.\n\n(Press a key)\n", M_AddonsHeaderPath()),NULL,MM_NOTHING)
// returns whether to do message draw
static boolean M_AddonsRefresh(void)
{
if ((refreshdirmenu & REFRESHDIR_NORMAL) && !preparefilemenu(true))
{
UNEXIST;
return true;
}
if (refreshdirmenu & REFRESHDIR_ADDFILE)
{
addonsresponselimit = 0;
if (refreshdirmenu & REFRESHDIR_NOTLOADED)
{
char *message = NULL;
S_StartSound(NULL, sfx_lose);
if (refreshdirmenu & REFRESHDIR_MAX)
message = va("\x82%s\x80\nMaximum number of add-ons reached.\nThis file could not be loaded.\nIf you want to play with this add-on, restart the game to clear existing ones.\n\n(Press a key)\n", dirmenu[dir_on[menudepthleft]]+DIR_STRING);
else
message = va("\x82%s\x80\nThe file was not loaded.\nCheck the console log for more information.\n\n(Press a key)\n", dirmenu[dir_on[menudepthleft]]+DIR_STRING);
M_StartMessage(message,NULL,MM_NOTHING);
return true;
}
if (refreshdirmenu & (REFRESHDIR_WARNING|REFRESHDIR_ERROR))
{
S_StartSound(NULL, sfx_skid);
M_StartMessage(va("\x82%s\x80\nThe file was loaded with %s.\nCheck the console log for more information.\n\n(Press a key)\n", dirmenu[dir_on[menudepthleft]]+DIR_STRING, ((refreshdirmenu & REFRESHDIR_ERROR) ? "errors" : "warnings")),NULL,MM_NOTHING);
return true;
}
S_StartSound(NULL, sfx_strpst);
}
return false;
}
#define offs 1
static void M_DrawAddons(void)
{
INT32 x, y;
ssize_t i, max;
// hack - need to refresh at end of frame to handle addfile...
if (refreshdirmenu & M_AddonsRefresh())
return M_DrawMessageMenu();
if (addonsresponselimit)
addonsresponselimit--;
V_DrawCenteredString(BASEVIDWIDTH/2, 4+offs, 0, (Playing()
? "\x85""Adding files mid-game may cause problems."
: LOCATIONSTRING));
if (numwadfiles <= mainwads+1)
y = 0;
else if (numwadfiles >= MAX_WADFILES)
y = FRACUNIT;
else
{
x = FixedDiv((numwadfiles - mainwads+1)<<FRACBITS, (MAX_WADFILES - mainwads+1)<<FRACBITS);
y = FixedDiv(((packetsizetally-mainwadstally)<<FRACBITS), (((MAXFILENEEDED*sizeof(UINT8)-mainwadstally)-(5+22))<<FRACBITS)); // 5+22 = (a.ext + checksum length) is minimum addition to packet size tally
if (x > y)
y = x;
if (y > FRACUNIT) // happens because of how we're shrinkin' it a little
y = FRACUNIT;
}
M_DrawTemperature(BASEVIDWIDTH - 19 - 5, y);
// DRAW MENU
x = currentMenu->x;
y = currentMenu->y + offs;
//M_DrawLevelPlatterHeader(y - 16, M_AddonsHeaderPath(), true, true); -- wanted different width
V_DrawString(x-21, (y - 16) + (lsheadingheight - 12), V_YELLOWMAP|V_ALLOWLOWERCASE, M_AddonsHeaderPath());
V_DrawFill(x-21, (y - 16) + (lsheadingheight - 3), (MAXSTRINGLENGTH*8+6 - 1), 1, yellowmap[3]);
V_DrawFill(x-21 + (MAXSTRINGLENGTH*8+6 - 1), (y - 16) + (lsheadingheight - 3), 1, 1, 26);
V_DrawFill(x-21, (y - 16) + (lsheadingheight - 2), MAXSTRINGLENGTH*8+6, 1, 26);
V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, (BASEVIDHEIGHT - currentMenu->y + 1 + offs) - (y - 1), 159);
// get bottom...
max = dir_on[menudepthleft] + numaddonsshown + 1;
if (max > (ssize_t)sizedirmenu)
max = sizedirmenu;
// then top...
i = max - (2*numaddonsshown + 1);
// then adjust!
if (i < 0)
{
if ((max -= i) > (ssize_t)sizedirmenu)
max = sizedirmenu;
i = 0;
}
if (i != 0)
V_DrawString(19, y+4, V_YELLOWMAP, "\x1A");
for (; i < max; i++)
{
UINT32 flags = V_ALLOWLOWERCASE;
if (y > BASEVIDHEIGHT) break;
if (dirmenu[i])
#define type (UINT8)(dirmenu[i][DIR_TYPE])
{
if (type & EXT_LOADED)
flags |= V_TRANSLUCENT;
V_DrawSmallScaledPatch(x-(16+4), y, (flags & V_TRANSLUCENT), addonsp[((UINT8)(dirmenu[i][DIR_TYPE]) & ~EXT_LOADED)]);
if (type & EXT_LOADED)
V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[NUM_EXT+3]);
if ((size_t)i == dir_on[menudepthleft])
{
V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[NUM_EXT+1+((skullAnimCounter/4) ? 1 : 0)]);
flags = V_ALLOWLOWERCASE|V_YELLOWMAP;
}
#define charsonside 14
if (dirmenu[i][DIR_LEN] > (charsonside*2 + 3))
V_DrawString(x, y+4, flags, va("%.*s...%s", charsonside, dirmenu[i]+DIR_STRING, dirmenu[i]+DIR_STRING+dirmenu[i][DIR_LEN]-(charsonside+1)));
#undef charsonside
else
V_DrawString(x, y+4, flags, dirmenu[i]+DIR_STRING);
}
#undef type
y += 16;
}
if (max != (ssize_t)sizedirmenu)
V_DrawString(19, y-12, V_YELLOWMAP, "\x1B");
y = BASEVIDHEIGHT - currentMenu->y + offs;
M_DrawTextBox(x - (21 + 5), y, MAXSTRINGLENGTH, 1);
if (menusearch[0])
V_DrawString(x - 18, y + 8, V_ALLOWLOWERCASE, menusearch+1);
else
V_DrawString(x - 18, y + 8, V_ALLOWLOWERCASE|V_TRANSLUCENT, "Type to search...");
if (skullAnimCounter < 4)
V_DrawCharacter(x - 18 + V_StringWidth(menusearch+1, 0), y + 8,
'_' | 0x80, false);
x -= (21 + 5 + 16);
V_DrawSmallScaledPatch(x, y + 4, (menusearch[0] ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]);
#define CANSAVE (!modifiedgame || savemoddata)
x = BASEVIDWIDTH - x - 16;
V_DrawSmallScaledPatch(x, y + 4, (CANSAVE ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+5]);
if CANSAVE
V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+3]);
#undef CANSAVE
}
#undef offs
static void M_AddonExec(INT32 ch)
{
if (ch != 'y' && ch != KEY_ENTER)
return;
S_StartSound(NULL, sfx_strpst);
COM_BufAddText(va("exec %s%s", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING));
}
#define len menusearch[0]
static boolean M_ChangeStringAddons(INT32 choice)
{
if (shiftdown && choice >= 32 && choice <= 127)
choice = shiftxform[choice];
switch (choice)
{
case KEY_DEL:
if (len)
{
len = menusearch[1] = 0;
return true;
}
break;
case KEY_BACKSPACE:
if (len)
{
menusearch[1+--len] = 0;
return true;
}
break;
default:
if (choice >= 32 && choice <= 127)
{
if (len < MAXSTRINGLENGTH - 1)
{
menusearch[1+len++] = (char)choice;
menusearch[1+len] = 0;
return true;
}
}
break;
}
return false;
}
#undef len
static void M_HandleAddons(INT32 choice)
{
boolean exitmenu = false; // exit to previous menu
if (addonsresponselimit)
return;
if (M_ChangeStringAddons(choice))
{
if (!preparefilemenu(true))
{
UNEXIST;
return;
}
}
switch (choice)
{
case KEY_DOWNARROW:
if (dir_on[menudepthleft] < sizedirmenu-1)
dir_on[menudepthleft]++;
S_StartSound(NULL, sfx_menu1);
break;
case KEY_UPARROW:
if (dir_on[menudepthleft])
dir_on[menudepthleft]--;
S_StartSound(NULL, sfx_menu1);
break;
case KEY_PGDN:
{
UINT8 i;
for (i = numaddonsshown; i && (dir_on[menudepthleft] < sizedirmenu-1); i--)
dir_on[menudepthleft]++;
}
S_StartSound(NULL, sfx_menu1);
break;
case KEY_PGUP:
{
UINT8 i;
for (i = numaddonsshown; i && (dir_on[menudepthleft]); i--)
dir_on[menudepthleft]--;
}
S_StartSound(NULL, sfx_menu1);
break;
case KEY_ENTER:
{
boolean refresh = true;
if (!dirmenu[dir_on[menudepthleft]])
S_StartSound(NULL, sfx_lose);
else
{
switch (dirmenu[dir_on[menudepthleft]][DIR_TYPE])
{
case EXT_FOLDER:
strcpy(&menupath[menupathindex[menudepthleft]],dirmenu[dir_on[menudepthleft]]+DIR_STRING);
if (menudepthleft)
{
menupathindex[--menudepthleft] = strlen(menupath);
menupath[menupathindex[menudepthleft]] = 0;
if (!preparefilemenu(false))
{
S_StartSound(NULL, sfx_skid);
M_StartMessage(va("\x82%s\x80\nThis folder is empty.\n\n(Press a key)\n", M_AddonsHeaderPath()),NULL,MM_NOTHING);
menupath[menupathindex[++menudepthleft]] = 0;
if (!preparefilemenu(true))
{
UNEXIST;
return;
}
}
else
{
S_StartSound(NULL, sfx_menu1);
dir_on[menudepthleft] = 1;
}
refresh = false;
}
else
{
S_StartSound(NULL, sfx_lose);
M_StartMessage(va("\x82%s\x80\nThis folder is too deep to navigate to!\n\n(Press a key)\n", M_AddonsHeaderPath()),NULL,MM_NOTHING);
menupath[menupathindex[menudepthleft]] = 0;
}
break;
case EXT_UP:
S_StartSound(NULL, sfx_menu1);
menupath[menupathindex[++menudepthleft]] = 0;
if (!preparefilemenu(false))
{
UNEXIST;
return;
}
break;
case EXT_TXT:
M_StartMessage(va("\x82%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\n(Press 'Y' to confirm)\n", dirmenu[dir_on[menudepthleft]]+DIR_STRING),M_AddonExec,MM_YESNO);
break;
case EXT_CFG:
M_AddonExec(KEY_ENTER);
break;
case EXT_LUA:
#ifndef HAVE_BLUA
S_StartSound(NULL, sfx_lose);
M_StartMessage(va("\x82%s\x80\nThis copy of SRB2 was compiled\nwithout support for .lua files.\n\n(Press a key)\n", dirmenu[dir_on[menudepthleft]]+DIR_STRING),NULL,MM_NOTHING);
break;
#endif
// else intentional fallthrough
case EXT_SOC:
case EXT_WAD:
COM_BufAddText(va("addfile %s%s", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING));
addonsresponselimit = 5;
break;
default:
S_StartSound(NULL, sfx_lose);
}
}
if (refresh)
refreshdirmenu |= REFRESHDIR_NORMAL;
}
break;
case KEY_ESCAPE:
exitmenu = true;
break;
default:
break;
}
if (exitmenu)
{
for (; sizedirmenu > 0; sizedirmenu--)
{
Z_Free(dirmenu[sizedirmenu-1]);
dirmenu[sizedirmenu-1] = NULL;
}
Z_Free(dirmenu);
dirmenu = NULL;
// secrets disabled by addfile...
MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
if (currentMenu->prevMenu)
M_SetupNextMenu(currentMenu->prevMenu);
else
M_ClearMenus(true);
}
}
static void M_PandorasBox(INT32 choice)
{
(void)choice;
@ -4722,7 +5274,7 @@ static void M_Options(INT32 choice)
OP_MainMenu[5].status = (Playing() && !(server || adminplayer == consoleplayer)) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL);
// if the player is playing _at all_, disable the erase data options
OP_DataOptionsMenu[1].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
OP_DataOptionsMenu[2].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
OP_MainDef.prevMenu = currentMenu;
M_SetupNextMenu(&OP_MainDef);
@ -5136,9 +5688,7 @@ static void M_DrawChecklist(void)
finishchecklist:
if ((checklist_cangodown = ((y - currentMenu->y) > (scrollareaheight*2)))) // haaaaaaacks.
{
V_DrawString(10, currentMenu->y+(scrollareaheight*2), V_YELLOWMAP, "\x1B");
}
}
#define NUMHINTS 5
@ -6441,7 +6991,7 @@ void M_DrawTimeAttackMenu(void)
lumpnum_t lumpnum;
char beststr[40];
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true);
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
// A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
@ -6618,7 +7168,7 @@ void M_DrawNightsAttackMenu(void)
UINT32 bestscore = G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value);
tic_t besttime = G_GetBestNightsTime(cv_nextmap.value, cv_dummymares.value);
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true);
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
// A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
@ -7374,7 +7924,7 @@ static void M_DrawServerMenu(void)
// Room name
if (currentMenu == &MP_ServerDef)
{
M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight/2, "Server settings", true);
M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight/2, "Server settings", true, false);
if (ms_RoomId < 0)
V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey,
V_YELLOWMAP, (itemOn == mp_server_room) ? "<Select to change>" : "<Offline Mode>");
@ -7392,7 +7942,7 @@ static void M_DrawServerMenu(void)
sprintf(headerstr, "%s - %s", cv_newgametype.string, cv_nextmap.string);
M_DrawLevelPlatterHeader(currentMenu->y + MP_ServerMenu[mp_server_levelgt].alphaKey - 10 - lsheadingheight/2, (const char *)headerstr, true);
M_DrawLevelPlatterHeader(currentMenu->y + MP_ServerMenu[mp_server_levelgt].alphaKey - 10 - lsheadingheight/2, (const char *)headerstr, true, false);
// A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
@ -7422,7 +7972,7 @@ static void M_GameTypeChange(INT32 choice)
void M_DrawGameTypeMenu(void)
{
M_DrawGenericMenu();
M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight, "Select Gametype", true);
M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight, "Select Gametype", true, false);
if (!char_notes)
char_notes = V_WordWrap(0, (160 - 30) - 8, V_ALLOWLOWERCASE, gametypedesc[itemOn].notes);
@ -8174,7 +8724,7 @@ static void M_DrawControl(void)
/*else if (currentMenu->menuitems[i].status == IT_GRAYEDOUT2)
V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);*/
else if ((currentMenu->menuitems[i].status == IT_HEADER) && (i != max-1))
M_DrawLevelPlatterHeader(y, currentMenu->menuitems[i].text, true);
M_DrawLevelPlatterHeader(y, currentMenu->menuitems[i].text, true, false);
y += SMALLLINEHEIGHT;
}
@ -8749,7 +9299,7 @@ static void M_DrawColorMenu(void)
//V_DrawString(x-16, y, V_YELLOWMAP, currentMenu->menuitems[i].text);
V_DrawFill(19, y, 281, 9, currentMenu->menuitems[i+1].alphaKey);
V_DrawFill(300, y, 1, 9, 26);
M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), currentMenu->menuitems[i].text, false);
M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), currentMenu->menuitems[i].text, false, false);
break;
}
}

View File

@ -124,6 +124,8 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
#define IT_HEADER (IT_SPACE +IT_HEADERTEXT)
#define IT_SECRET (IT_SPACE +IT_QUESTIONMARKS)
#define MAXSTRINGLENGTH 32
typedef union
{
struct menu_s *submenu; // IT_SUBMENU
@ -249,6 +251,9 @@ void Nextmap_OnChange(void);
void Moviemode_mode_Onchange(void);
void Screenshot_option_Onchange(void);
// Addons menu updating
void Addons_option_Onchange(void);
// These defines make it a little easier to make menus
#define DEFAULTMENUSTYLE(header, source, prev, x, y)\
{\
@ -262,6 +267,18 @@ void Screenshot_option_Onchange(void);
NULL\
}
#define DEFAULTSCROLLMENUSTYLE(header, source, prev, x, y)\
{\
header,\
sizeof(source)/sizeof(menuitem_t),\
prev,\
source,\
M_DrawGenericScrollMenu,\
x, y,\
0,\
NULL\
}
#define PAUSEMENUSTYLE(source, x, y)\
{\
NULL,\

View File

@ -100,7 +100,7 @@ static CV_PossibleValue_t screenshot_cons_t[] = {{0, "Default"}, {1, "HOME"}, {2
consvar_t cv_screenshot_option = {"screenshot_option", "Default", CV_SAVE|CV_CALL, screenshot_cons_t, Screenshot_option_Onchange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_screenshot_folder = {"screenshot_folder", "", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_screenshot_colorprofile = {"screenshot_colorprofile", "Yes", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_screenshot_colorprofile = {"screenshot_colorprofile", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t moviemode_cons_t[] = {{MM_GIF, "GIF"}, {MM_APNG, "aPNG"}, {MM_SCREENSHOT, "Screenshots"}, {0, NULL}};
consvar_t cv_moviemode = {"moviemode_mode", "GIF", CV_SAVE|CV_CALL, moviemode_cons_t, Moviemode_mode_Onchange, 0, NULL, NULL, 0, 0, NULL};

View File

@ -54,6 +54,8 @@
#include "v_video.h"
#include "filesrch.h" // refreshdirmenu
// wipes
#include "f_finale.h"
@ -3068,6 +3070,7 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
if ((numlumps = W_LoadWadFile(wadfilename)) == INT16_MAX)
{
refreshdirmenu |= REFRESHDIR_NOTLOADED;
CONS_Printf(M_GetText("Errors occured while loading %s; not added.\n"), wadfilename);
return false;
}

View File

@ -34,6 +34,8 @@
#include "z_zone.h"
#include "fastcmp.h"
#include "filesrch.h"
#include "i_video.h" // rendermode
#include "d_netfil.h"
#include "dehacked.h"
@ -294,12 +296,12 @@ UINT16 W_LoadWadFile(const char *filename)
UINT32 numlumps;
size_t i;
INT32 compressed = 0;
size_t packetsize = 0;
serverinfo_pak *dummycheck = NULL;
size_t packetsize;
UINT8 md5sum[16];
boolean important;
// Shut the compiler up.
(void)dummycheck;
if (!(refreshdirmenu & REFRESHDIR_ADDFILE))
refreshdirmenu = REFRESHDIR_NORMAL|REFRESHDIR_ADDFILE; // clean out cons_alerts that happened earlier
//CONS_Debug(DBG_SETUP, "Loading %s\n", filename);
//
@ -308,6 +310,7 @@ UINT16 W_LoadWadFile(const char *filename)
if (numwadfiles >= MAX_WADFILES)
{
CONS_Alert(CONS_ERROR, M_GetText("Maximum wad files reached\n"));
refreshdirmenu |= REFRESHDIR_MAX;
return INT16_MAX;
}
@ -317,21 +320,23 @@ UINT16 W_LoadWadFile(const char *filename)
// Check if wad files will overflow fileneededbuffer. Only the filename part
// is send in the packet; cf.
for (i = 0; i < numwadfiles; i++)
// see PutFileNeeded in d_netfil.c
if ((important = !W_VerifyNMUSlumps(filename)))
{
packetsize += nameonlylength(wadfiles[i]->filename);
packetsize += 22; // MD5, etc.
}
packetsize = packetsizetally;
packetsize += nameonlylength(filename);
packetsize += 22;
packetsize += nameonlylength(filename) + 22;
if (packetsize > sizeof(dummycheck->fileneeded))
{
CONS_Alert(CONS_ERROR, M_GetText("Maximum wad files reached\n"));
if (handle)
fclose(handle);
return INT16_MAX;
if (packetsize > MAXFILENEEDED*sizeof(UINT8))
{
CONS_Alert(CONS_ERROR, M_GetText("Maximum wad files reached\n"));
refreshdirmenu |= REFRESHDIR_MAX;
if (handle)
fclose(handle);
return INT16_MAX;
}
packetsizetally = packetsize;
}
// detect dehacked file with the "soc" extension
@ -470,6 +475,7 @@ UINT16 W_LoadWadFile(const char *filename)
wadfile->handle = handle;
wadfile->numlumps = (UINT16)numlumps;
wadfile->lumpinfo = lumpinfo;
wadfile->important = important;
fseek(handle, 0, SEEK_END);
wadfile->filesize = (unsigned)ftell(handle);
@ -1239,6 +1245,7 @@ int W_VerifyNMUSlumps(const char *filename)
{"TNYFN", 5}, // Tiny console font changes
{"STT", 3}, // Acceptable HUD changes (Score Time Rings)
{"YB_", 3}, // Intermission graphics, goes with the above
{"M_", 2}, // As does menu stuff
{NULL, 0},
};

View File

@ -70,6 +70,7 @@ typedef struct wadfile_s
FILE *handle;
UINT32 filesize; // for network
UINT8 md5sum[16];
boolean important; // also network - !W_VerifyNMUSlumps
} wadfile_t;
#define WADFILENUM(lumpnum) (UINT16)((lumpnum)>>16) // wad flumpnum>>16) // wad file number in upper word