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

View file

@ -315,6 +315,7 @@ typedef struct
} ATTRPACK clientconfig_pak; } ATTRPACK clientconfig_pak;
#define MAXSERVERNAME 32 #define MAXSERVERNAME 32
#define MAXFILENEEDED 915
// This packet is too large // This packet is too large
typedef struct typedef struct
{ {
@ -336,7 +337,7 @@ typedef struct
unsigned char mapmd5[16]; unsigned char mapmd5[16];
UINT8 actnum; UINT8 actnum;
UINT8 iszone; UINT8 iszone;
UINT8 fileneeded[915]; // is filled with writexxx (byteptr.h) UINT8 fileneeded[MAXFILENEEDED]; // is filled with writexxx (byteptr.h)
} ATTRPACK serverinfo_pak; } ATTRPACK serverinfo_pak;
typedef struct 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 "m_cond.h" // condition initialization
#include "fastcmp.h" #include "fastcmp.h"
#include "keys.h" #include "keys.h"
#include "filesrch.h" // refreshdirmenu, mainwadstally
#ifdef CMAKECONFIG #ifdef CMAKECONFIG
#include "config.h" #include "config.h"
@ -584,6 +585,8 @@ void D_SRB2Loop(void)
realtics = entertic - oldentertics; realtics = entertic - oldentertics;
oldentertics = entertic; oldentertics = entertic;
refreshdirmenu = 0; // not sure where to put this, here as good as any?
#ifdef DEBUGFILE #ifdef DEBUGFILE
if (!realtics) if (!realtics)
if (debugload) if (debugload)
@ -870,7 +873,7 @@ static void IdentifyVersion(void)
} }
#endif #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 *musicfile = "music_new.dta";
const char *musicpath = va(pandf,srb2waddir,musicfile); const char *musicpath = va(pandf,srb2waddir,musicfile);
@ -1162,6 +1165,11 @@ void D_SRB2Main(void)
#ifdef USE_PATCH_DTA #ifdef USE_PATCH_DTA
++mainwads; // patch.dta adds one more ++mainwads; // patch.dta adds one more
#endif #endif
#ifdef DEVELOP
++mainwads; // music_new, too
#endif
mainwadstally = packetsizetally;
cht_Init(); cht_Init();

View file

@ -37,6 +37,7 @@
#include "d_main.h" #include "d_main.h"
#include "m_random.h" #include "m_random.h"
#include "f_finale.h" #include "f_finale.h"
#include "filesrch.h"
#include "mserv.h" #include "mserv.h"
#include "md5.h" #include "md5.h"
#include "z_zone.h" #include "z_zone.h"
@ -714,6 +715,14 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_firenaxis); CV_RegisterVar(&cv_firenaxis);
CV_RegisterVar(&cv_firenaxis2); 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 // WARNING: the order is important when initialising mouse2
// we need the mouse2port // we need the mouse2port
CV_RegisterVar(&cv_mouse2port); CV_RegisterVar(&cv_mouse2port);

View file

@ -104,6 +104,7 @@ INT32 lastfilenum = -1;
/** Fills a serverinfo packet with information about wad files loaded. /** Fills a serverinfo packet with information about wad files loaded.
* *
* \todo Give this function a better name since it is in global scope. * \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) UINT8 *PutFileNeeded(void)
@ -112,29 +113,22 @@ UINT8 *PutFileNeeded(void)
UINT8 *p = netbuffer->u.serverinfo.fileneeded; UINT8 *p = netbuffer->u.serverinfo.fileneeded;
char wadfilename[MAX_WADPATH] = ""; char wadfilename[MAX_WADPATH] = "";
UINT8 filestatus; UINT8 filestatus;
size_t bytesused = 0;
for (i = 0; i < numwadfiles; i++) for (i = 0; i < numwadfiles; i++)
{ {
// If it has only music/sound lumps, mark it as unimportant // If it has only music/sound lumps, don't put it in the list
if (W_VerifyNMUSlumps(wadfiles[i]->filename)) if (!wadfiles[i]->important)
filestatus = 0; continue;
else
filestatus = 1; // Important filestatus = 1; // Importance - not really used any more, holds 1 by default for backwards compat with MS
// Store in the upper four bits // Store in the upper four bits
if (!cv_downloading.value) if (!cv_downloading.value)
filestatus += (2 << 4); // Won't send filestatus += (2 << 4); // Won't send
else if ((wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024)) else if ((wadfiles[i]->filesize <= (UINT32)cv_maxsend.value * 1024))
filestatus += (0 << 4); // Won't send
else
filestatus += (1 << 4); // Will send if requested filestatus += (1 << 4); // Will send if requested
// else
bytesused += (nameonlylength(wadfilename) + 22); // filestatus += (0 << 4); -- Won't send, too big
// 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);
WRITEUINT8(p, filestatus); 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 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 filestatus = READUINT8(p); // The first byte is the file status
fileneeded[i].important = (UINT8)(filestatus & 3);
fileneeded[i].willsend = (UINT8)(filestatus >> 4); fileneeded[i].willsend = (UINT8)(filestatus >> 4);
fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size
fileneeded[i].file = NULL; // The file isn't open yet fileneeded[i].file = NULL; // The file isn't open yet
@ -197,7 +190,7 @@ boolean CL_CheckDownloadable(void)
UINT8 i,dlstatus = 0; UINT8 i,dlstatus = 0;
for (i = 0; i < fileneedednum; i++) 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) if (fileneeded[i].willsend == 1)
continue; continue;
@ -218,7 +211,7 @@ boolean CL_CheckDownloadable(void)
// not downloadable, put reason in console // not downloadable, put reason in console
CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n")); CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n"));
for (i = 0; i < fileneedednum; i++) 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); 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++) for (i = 0; i < fileneedednum; i++)
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN 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"); I_Error("Attempted to download files that were not sendable");
} }
@ -280,8 +273,7 @@ boolean CL_SendRequestFile(void)
netbuffer->packettype = PT_REQUESTFILE; netbuffer->packettype = PT_REQUESTFILE;
p = (char *)netbuffer->u.textcmd; p = (char *)netbuffer->u.textcmd;
for (i = 0; i < fileneedednum; i++) for (i = 0; i < fileneedednum; i++)
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD))
&& fileneeded[i].important)
{ {
totalfreespaceneeded += fileneeded[i].totalsize; totalfreespaceneeded += fileneeded[i].totalsize;
nameonly(fileneeded[i].filename); nameonly(fileneeded[i].filename);
@ -360,15 +352,9 @@ INT32 CL_CheckFiles(void)
CONS_Debug(DBG_NETPLAY, "game is modified; only doing basic checks\n"); CONS_Debug(DBG_NETPLAY, "game is modified; only doing basic checks\n");
for (i = 1, j = 1; i < fileneedednum || j < numwadfiles;) 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 // Unimportant on our side.
++i;
continue;
}
if (j < numwadfiles && W_VerifyNMUSlumps(wadfiles[j]->filename))
{
// Unimportant on our side. still don't care.
++j; ++j;
continue; continue;
} }
@ -411,7 +397,7 @@ INT32 CL_CheckFiles(void)
break; break;
} }
} }
if (fileneeded[i].status != FS_NOTFOUND || !fileneeded[i].important) if (fileneeded[i].status != FS_NOTFOUND)
continue; continue;
packetsize += nameonlylength(fileneeded[i].filename) + 22; packetsize += nameonlylength(fileneeded[i].filename) + 22;
@ -449,27 +435,8 @@ void CL_LoadServerFiles(void)
fileneeded[i].status = FS_OPEN; fileneeded[i].status = FS_OPEN;
} }
else if (fileneeded[i].status == FS_MD5SUMBAD) else if (fileneeded[i].status == FS_MD5SUMBAD)
{ I_Error("Wrong version of file %s", fileneeded[i].filename);
// If the file is marked important, don't even bother proceeding. else
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)
{ {
const char *s; const char *s;
switch(fileneeded[i].status) switch(fileneeded[i].status)

View file

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

View file

@ -31,6 +31,8 @@
#include "filesrch.h" #include "filesrch.h"
#include "d_netfil.h" #include "d_netfil.h"
#include "m_misc.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) #if (defined (_WIN32) && !defined (_WIN32_WCE)) && defined (_MSC_VER) && !defined (_XBOX)
@ -255,6 +257,28 @@ readdir (DIR * dirp)
return (struct dirent *) 0; 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 * closedir
* *
@ -285,6 +309,35 @@ closedir (DIR * dirp)
return rc; return rc;
} }
#endif #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) #if defined (_XBOX) && defined (_MSC_VER)
filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum,
boolean completepath, int maxsearchdepth) boolean completepath, int maxsearchdepth)
@ -296,6 +349,13 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
completepath = false; completepath = false;
return FS_NOTFOUND; return FS_NOTFOUND;
} }
boolean preparefilemenu(boolean samedepth)
{
(void)samedepth;
return false;
}
#elif defined (_WIN32_WCE) #elif defined (_WIN32_WCE)
filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum,
boolean completepath, int maxsearchdepth) boolean completepath, int maxsearchdepth)
@ -346,6 +406,12 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
#endif #endif
return FS_NOTFOUND; return FS_NOTFOUND;
} }
boolean preparefilemenu(boolean samedepth)
{
(void)samedepth;
return false;
}
#else #else
filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth) 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; searchpath[searchpathindex[depthleft]]=0;
dent = readdir(dirhandle[depthleft]); dent = readdir(dirhandle[depthleft]);
if (dent)
strcpy(&searchpath[searchpathindex[depthleft]],dent->d_name);
if (!dent) if (!dent)
{
closedir(dirhandle[depthleft++]); closedir(dirhandle[depthleft++]);
else if (dent->d_name[0]=='.' && continue;
}
if (dent->d_name[0]=='.' &&
(dent->d_name[1]=='\0' || (dent->d_name[1]=='\0' ||
(dent->d_name[1]=='.' && (dent->d_name[1]=='.' &&
dent->d_name[2]=='\0'))) dent->d_name[2]=='\0')))
{ {
// we don't want to scan uptree // 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
{ // okay, now we actually want searchpath to incorporate d_name
// was the file (re)moved? can't stat it 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) else if (S_ISDIR(fsstat.st_mode) && depthleft)
{ {
strcpy(&searchpath[searchpathindex[depthleft]],dent->d_name);
searchpathindex[--depthleft] = strlen(searchpath) + 1; searchpathindex[--depthleft] = strlen(searchpath) + 1;
dirhandle[depthleft] = opendir(searchpath); dirhandle[depthleft] = opendir(searchpath);
if (!dirhandle[depthleft]) if (!dirhandle[depthleft])
@ -444,6 +514,255 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
free(searchname); free(searchname);
free(searchpathindex); free(searchpathindex);
free(dirhandle); free(dirhandle);
return retval; 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 #endif

View file

@ -6,6 +6,9 @@
#include "doomdef.h" #include "doomdef.h"
#include "d_netfil.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 /** \brief The filesearch function
@ -25,4 +28,64 @@
filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum,
boolean completepath, int maxsearchdepth); 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__ #endif // __FILESRCH_H__

View file

@ -33,6 +33,9 @@
#include "s_sound.h" #include "s_sound.h"
#include "i_system.h" #include "i_system.h"
// Addfile
#include "filesrch.h"
#include "v_video.h" #include "v_video.h"
#include "i_video.h" #include "i_video.h"
#include "keys.h" #include "keys.h"
@ -74,7 +77,6 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#define SMALLLINEHEIGHT 8 #define SMALLLINEHEIGHT 8
#define SLIDER_RANGE 9 #define SLIDER_RANGE 9
#define SLIDER_WIDTH 78 #define SLIDER_WIDTH 78
#define MAXSTRINGLENGTH 32
#define SERVERS_PER_PAGE 11 #define SERVERS_PER_PAGE 11
typedef enum typedef enum
@ -211,14 +213,13 @@ menu_t SPauseDef;
// Level Select // Level Select
static levelselect_t levelselect = {0, NULL}; static levelselect_t levelselect = {0, NULL};
static UINT8 levelselectselect[4]; static UINT8 levelselectselect[3];
static patch_t *levselp[2][3]; static patch_t *levselp[2][3];
static INT32 lsoffs[2]; static INT32 lsoffs[2];
#define lsrow levelselectselect[0] #define lsrow levelselectselect[0]
#define lscol levelselectselect[1] #define lscol levelselectselect[1]
#define lstic levelselectselect[2] #define lshli levelselectselect[2]
#define lshli levelselectselect[3]
#define lshseperation 101 #define lshseperation 101
#define lsbasevseperation 62 #define lsbasevseperation 62
@ -334,12 +335,20 @@ menu_t OP_MonitorToggleDef;
static void M_ScreenshotOptions(INT32 choice); static void M_ScreenshotOptions(INT32 choice);
static void M_EraseData(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 // Drawing functions
static void M_DrawGenericMenu(void); static void M_DrawGenericMenu(void);
static void M_DrawGenericScrollMenu(void); static void M_DrawGenericScrollMenu(void);
static void M_DrawCenteredMenu(void); static void M_DrawCenteredMenu(void);
static void M_DrawAddons(void);
static void M_DrawSkyRoom(void); static void M_DrawSkyRoom(void);
static void M_DrawChecklist(void); static void M_DrawChecklist(void);
static void M_DrawEmblemHints(void); static void M_DrawEmblemHints(void);
@ -379,6 +388,7 @@ static boolean M_CancelConnect(void);
#endif #endif
static boolean M_ExitPandorasBox(void); static boolean M_ExitPandorasBox(void);
static boolean M_QuitMultiPlayerMenu(void); static boolean M_QuitMultiPlayerMenu(void);
static void M_HandleAddons(INT32 choice);
static void M_HandleLevelPlatter(INT32 choice); static void M_HandleLevelPlatter(INT32 choice);
static void M_HandleSoundTest(INT32 choice); static void M_HandleSoundTest(INT32 choice);
static void M_HandleImageDef(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[] = static menuitem_t MainMenu[] =
{ {
{IT_CALL |IT_STRING, NULL, "Secrets", M_SecretsMenu, 84}, {IT_CALL |IT_STRING, NULL, "Secrets", M_SecretsMenu, 76},
{IT_CALL |IT_STRING, NULL, "1 player", M_SinglePlayerMenu, 92}, {IT_CALL |IT_STRING, NULL, "1 player", M_SinglePlayerMenu, 84},
{IT_SUBMENU|IT_STRING, NULL, "multiplayer", &MP_MainDef, 100}, {IT_SUBMENU|IT_STRING, NULL, "multiplayer", &MP_MainDef, 92},
{IT_CALL |IT_STRING, NULL, "options", M_Options, 108}, {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}, {IT_CALL |IT_STRING, NULL, "quit game", M_QuitSRB2, 116},
}; };
@ -500,9 +511,15 @@ typedef enum
singleplr, singleplr,
multiplr, multiplr,
options, options,
addons,
quitdoom quitdoom
} main_e; } 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 // Pause Menu Mode Attacking Edition
// --------------------------------- // ---------------------------------
@ -525,26 +542,28 @@ typedef enum
// --------------------- // ---------------------
static menuitem_t MPauseMenu[] = static menuitem_t MPauseMenu[] =
{ {
{IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16}, {IT_STRING | IT_CALL, NULL, "Add-ons...", M_Addons, 8},
{IT_STRING | IT_CALL, NULL, "Switch Gametype/Level...", M_GameTypeChange, 24}, {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_STRING | IT_CALL, NULL, "Continue", M_SelectableClearMenus,40},
{IT_CALL | IT_STRING, NULL, "Player 1 Setup", M_SetupMultiPlayer, 48}, // splitscreen {IT_STRING | IT_CALL, 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, "Player 2 Setup", M_SetupMultiPlayer2, 56}, // splitscreen
{IT_STRING | IT_CALL, NULL, "Spectate", M_ConfirmSpectate, 48}, {IT_STRING | IT_CALL, NULL, "Spectate", M_ConfirmSpectate, 48},
{IT_STRING | IT_CALL, NULL, "Enter Game", M_ConfirmEnterGame, 48}, {IT_STRING | IT_CALL, NULL, "Enter Game", M_ConfirmEnterGame, 48},
{IT_STRING | IT_SUBMENU, NULL, "Switch Team...", &MISC_ChangeTeamDef, 48}, {IT_STRING | IT_SUBMENU, NULL, "Switch Team...", &MISC_ChangeTeamDef, 48},
{IT_CALL | IT_STRING, NULL, "Player Setup", M_SetupMultiPlayer, 56}, // alone {IT_STRING | IT_CALL, NULL, "Player Setup", M_SetupMultiPlayer, 56}, // alone
{IT_CALL | IT_STRING, NULL, "Options", M_Options, 64}, {IT_STRING | IT_CALL, NULL, "Options", M_Options, 64},
{IT_CALL | IT_STRING, NULL, "Return to Title", M_EndGame, 80}, {IT_STRING | IT_CALL, NULL, "Return to Title", M_EndGame, 80},
{IT_CALL | IT_STRING, NULL, "Quit Game", M_QuitSRB2, 88}, {IT_STRING | IT_CALL, NULL, "Quit Game", M_QuitSRB2, 88},
}; };
typedef enum typedef enum
{ {
mpause_scramble = 0, mpause_addons = 0,
mpause_scramble,
mpause_switchmap, mpause_switchmap,
mpause_continue, mpause_continue,
@ -587,6 +606,7 @@ typedef enum
spause_continue, spause_continue,
spause_retry, spause_retry,
spause_options, spause_options,
spause_title, spause_title,
spause_quit spause_quit
} spause_e; } spause_e;
@ -1318,9 +1338,10 @@ static menuitem_t OP_SoundOptionsMenu[] =
static menuitem_t OP_DataOptionsMenu[] = 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[] = 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}, {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[] = static menuitem_t OP_ServerOptionsMenu[] =
{ {
{IT_HEADER, NULL, "General", NULL, 0}, {IT_HEADER, NULL, "General", NULL, 0},
@ -1441,6 +1480,18 @@ static menuitem_t OP_MonitorToggleMenu[] =
// Main Menu and related // Main Menu and related
menu_t MainDef = CENTERMENUSTYLE(NULL, MainMenu, NULL, 72); 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 MAPauseDef = PAUSEMENUSTYLE(MAPauseMenu, 40, 72);
menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72); menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72);
menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72); menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72);
@ -1804,17 +1855,7 @@ menu_t OP_SoundOptionsDef =
NULL NULL
}; };
menu_t OP_ServerOptionsDef = menu_t OP_ServerOptionsDef = DEFAULTSCROLLMENUSTYLE("M_SERVER", OP_ServerOptionsMenu, &OP_MainDef, 30, 30);
{
"M_SERVER",
sizeof (OP_ServerOptionsMenu)/sizeof (menuitem_t),
&OP_MainDef,
OP_ServerOptionsMenu,
M_DrawGenericScrollMenu,
30, 30,
0,
NULL
};
menu_t OP_MonitorToggleDef = menu_t OP_MonitorToggleDef =
{ {
@ -1856,7 +1897,7 @@ menu_t OP_OpenGLColorDef =
NULL NULL
}; };
#endif #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 = menu_t OP_ScreenshotOptionsDef =
{ {
@ -1870,6 +1911,8 @@ menu_t OP_ScreenshotOptionsDef =
NULL 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); 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; 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. // END ORGANIZATION STUFF.
// ========================================================================== // ==========================================================================
@ -2150,9 +2199,12 @@ static void M_ChangeCvar(INT32 choice)
static boolean M_ChangeStringCvar(INT32 choice) static boolean M_ChangeStringCvar(INT32 choice)
{ {
consvar_t *cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction; consvar_t *cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction;
char buf[255]; char buf[MAXSTRINGLENGTH];
size_t len; size_t len;
if (shiftdown && choice >= 32 && choice <= 127)
choice = shiftxform[choice];
switch (choice) switch (choice)
{ {
case KEY_BACKSPACE: case KEY_BACKSPACE:
@ -2463,8 +2515,6 @@ boolean M_Responder(event_t *ev)
{ {
if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING) if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING)
{ {
if (shiftdown && ch >= 32 && ch <= 127)
ch = shiftxform[ch];
if (M_ChangeStringCvar(ch)) if (M_ChangeStringCvar(ch))
return true; return true;
else else
@ -2710,6 +2760,7 @@ void M_StartControlPanel(void)
else // multiplayer else // multiplayer
{ {
MPauseMenu[mpause_switchmap].status = IT_DISABLED; MPauseMenu[mpause_switchmap].status = IT_DISABLED;
MPauseMenu[mpause_addons].status = IT_DISABLED;
MPauseMenu[mpause_scramble].status = IT_DISABLED; MPauseMenu[mpause_scramble].status = IT_DISABLED;
MPauseMenu[mpause_psetupsplit].status = IT_DISABLED; MPauseMenu[mpause_psetupsplit].status = IT_DISABLED;
MPauseMenu[mpause_psetupsplit2].status = IT_DISABLED; MPauseMenu[mpause_psetupsplit2].status = IT_DISABLED;
@ -2721,6 +2772,7 @@ void M_StartControlPanel(void)
if ((server || adminplayer == consoleplayer)) if ((server || adminplayer == consoleplayer))
{ {
MPauseMenu[mpause_switchmap].status = IT_STRING | IT_CALL; MPauseMenu[mpause_switchmap].status = IT_STRING | IT_CALL;
MPauseMenu[mpause_addons].status = IT_STRING | IT_CALL;
if (G_GametypeHasTeams()) if (G_GametypeHasTeams())
MPauseMenu[mpause_scramble].status = IT_STRING | IT_SUBMENU; MPauseMenu[mpause_scramble].status = IT_STRING | IT_SUBMENU;
} }
@ -3262,7 +3314,7 @@ static void M_DrawGenericMenu(void)
y = currentMenu->y+currentMenu->menuitems[i].alphaKey; y = currentMenu->y+currentMenu->menuitems[i].alphaKey;
//V_DrawString(x-16, y, V_YELLOWMAP, currentMenu->menuitems[i].text); //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; y += SMALLLINEHEIGHT;
break; break;
} }
@ -3402,7 +3454,7 @@ static void M_DrawGenericScrollMenu(void)
break; break;
case IT_HEADERTEXT: case IT_HEADERTEXT:
//V_DrawString(x-16, y, V_YELLOWMAP, currentMenu->menuitems[i].text); //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; break;
} }
} }
@ -3912,7 +3964,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt)
I_Error("Insufficient memory to prepare level platter"); I_Error("Insufficient memory to prepare level platter");
// done here so lsrow and lscol can be set if cv_nextmap is on the 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) 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; 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; y += 9;
if ((y >= 0) && (y < 200)) if ((y >= 0) && (y < 200))
{ {
@ -4183,9 +4235,7 @@ void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlig
} }
y++; y++;
if ((y >= 0) && (y < 200)) if ((y >= 0) && (y < 200))
{
V_DrawFill(19, y, 282, 1, 26); V_DrawFill(19, y, 282, 1, 26);
}
} }
static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolean highlight) 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); const boolean rowhighlight = (row == lsrow);
if (levelselect.rows[row].header[0]) 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; y += lsheadingheight;
} }
@ -4298,9 +4348,6 @@ static void M_DrawLevelPlatterMenu(void)
INT32 y = lsbasey + lsoffs[0] - getheadingoffset(lsrow); INT32 y = lsbasey + lsoffs[0] - getheadingoffset(lsrow);
const INT32 cursorx = (sizeselect ? 0 : (lscol*lshseperation)); const INT32 cursorx = (sizeselect ? 0 : (lscol*lshseperation));
if (++lstic == 32)
lstic = 0;
if (gamestate == GS_TIMEATTACK) if (gamestate == GS_TIMEATTACK)
V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE));
@ -4320,7 +4367,7 @@ static void M_DrawLevelPlatterMenu(void)
} }
// draw cursor box // 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]) if (levelselect.rows[lsrow].maplist[lscol])
V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE)); 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 // 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) static void M_PandorasBox(INT32 choice)
{ {
(void)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); 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 // 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; OP_MainDef.prevMenu = currentMenu;
M_SetupNextMenu(&OP_MainDef); M_SetupNextMenu(&OP_MainDef);
@ -5136,9 +5688,7 @@ static void M_DrawChecklist(void)
finishchecklist: finishchecklist:
if ((checklist_cangodown = ((y - currentMenu->y) > (scrollareaheight*2)))) // haaaaaaacks. if ((checklist_cangodown = ((y - currentMenu->y) > (scrollareaheight*2)))) // haaaaaaacks.
{
V_DrawString(10, currentMenu->y+(scrollareaheight*2), V_YELLOWMAP, "\x1B"); V_DrawString(10, currentMenu->y+(scrollareaheight*2), V_YELLOWMAP, "\x1B");
}
} }
#define NUMHINTS 5 #define NUMHINTS 5
@ -6441,7 +6991,7 @@ void M_DrawTimeAttackMenu(void)
lumpnum_t lumpnum; lumpnum_t lumpnum;
char beststr[40]; 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 // A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value))); 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); UINT32 bestscore = G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value);
tic_t besttime = G_GetBestNightsTime(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 // A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value))); lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
@ -7374,7 +7924,7 @@ static void M_DrawServerMenu(void)
// Room name // Room name
if (currentMenu == &MP_ServerDef) 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) if (ms_RoomId < 0)
V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey,
V_YELLOWMAP, (itemOn == mp_server_room) ? "<Select to change>" : "<Offline Mode>"); 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); 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 // A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value))); lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
@ -7422,7 +7972,7 @@ static void M_GameTypeChange(INT32 choice)
void M_DrawGameTypeMenu(void) void M_DrawGameTypeMenu(void)
{ {
M_DrawGenericMenu(); M_DrawGenericMenu();
M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight, "Select Gametype", true); M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight, "Select Gametype", true, false);
if (!char_notes) if (!char_notes)
char_notes = V_WordWrap(0, (160 - 30) - 8, V_ALLOWLOWERCASE, gametypedesc[itemOn].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) /*else if (currentMenu->menuitems[i].status == IT_GRAYEDOUT2)
V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);*/ V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);*/
else if ((currentMenu->menuitems[i].status == IT_HEADER) && (i != max-1)) 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; y += SMALLLINEHEIGHT;
} }
@ -8749,7 +9299,7 @@ static void M_DrawColorMenu(void)
//V_DrawString(x-16, y, V_YELLOWMAP, currentMenu->menuitems[i].text); //V_DrawString(x-16, y, V_YELLOWMAP, currentMenu->menuitems[i].text);
V_DrawFill(19, y, 281, 9, currentMenu->menuitems[i+1].alphaKey); V_DrawFill(19, y, 281, 9, currentMenu->menuitems[i+1].alphaKey);
V_DrawFill(300, y, 1, 9, 26); 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; break;
} }
} }

View file

@ -124,6 +124,8 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
#define IT_HEADER (IT_SPACE +IT_HEADERTEXT) #define IT_HEADER (IT_SPACE +IT_HEADERTEXT)
#define IT_SECRET (IT_SPACE +IT_QUESTIONMARKS) #define IT_SECRET (IT_SPACE +IT_QUESTIONMARKS)
#define MAXSTRINGLENGTH 32
typedef union typedef union
{ {
struct menu_s *submenu; // IT_SUBMENU struct menu_s *submenu; // IT_SUBMENU
@ -249,6 +251,9 @@ void Nextmap_OnChange(void);
void Moviemode_mode_Onchange(void); void Moviemode_mode_Onchange(void);
void Screenshot_option_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 // These defines make it a little easier to make menus
#define DEFAULTMENUSTYLE(header, source, prev, x, y)\ #define DEFAULTMENUSTYLE(header, source, prev, x, y)\
{\ {\
@ -262,6 +267,18 @@ void Screenshot_option_Onchange(void);
NULL\ 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)\ #define PAUSEMENUSTYLE(source, x, y)\
{\ {\
NULL,\ 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_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_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}}; 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}; 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 "v_video.h"
#include "filesrch.h" // refreshdirmenu
// wipes // wipes
#include "f_finale.h" #include "f_finale.h"
@ -3068,6 +3070,7 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
if ((numlumps = W_LoadWadFile(wadfilename)) == INT16_MAX) if ((numlumps = W_LoadWadFile(wadfilename)) == INT16_MAX)
{ {
refreshdirmenu |= REFRESHDIR_NOTLOADED;
CONS_Printf(M_GetText("Errors occured while loading %s; not added.\n"), wadfilename); CONS_Printf(M_GetText("Errors occured while loading %s; not added.\n"), wadfilename);
return false; return false;
} }

View file

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

View file

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