From: bones_was_here Date: Sun, 28 Apr 2024 03:03:12 +0000 (+1000) Subject: fs: recognise mods by -game cmdline args, deduplicate gamedirs, refactoring X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=205931f2f5b7333523ca4f6c3cae7974a8f516c8;p=xonotic%2Fdarkplaces.git fs: recognise mods by -game cmdline args, deduplicate gamedirs, refactoring Players often start mods using -game even when they have their own cmdline option, and modders often provide scripts which use -game. This was already implemented but only for the `gamedir` command. Refactoring was required to avoid chicken vs egg problems when doing it with cmdline gamedirs too. Fixes a bug: the first gamedir (in the cmdline or `gamedir` args) was used to identify the mod but it needs to be the last one that matches because the last gamedir is the primary (first in the search path, and where files are saved). Includes supported game and mod directories in the main gamedir list for interface consistency, less special cases, and to support deduplication. Refactoring was required in the menu modlist where there was also an opportunity to simplify. Fixes inability to change from a supported mod (eg rogue) back to id1. Signed-off-by: bones_was_here --- diff --git a/cl_parse.c b/cl_parse.c index dbeda386..b5d0cd4f 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -1677,7 +1677,7 @@ CL_ParseServerInfo */ static void CL_ParseServerInfo (void) { - char *str; + const char *str; int i; protocolversion_t protocol; int nummodels, numsounds; @@ -1730,16 +1730,13 @@ static void CL_ParseServerInfo (void) if (protocol == PROTOCOL_QUAKEWORLD) { - char gamedir[1][MAX_QPATH]; - cl.qw_servercount = MSG_ReadLong(&cl_message); str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)); Con_Printf("server gamedir is %s\n", str); - dp_strlcpy(gamedir[0], str, sizeof(gamedir[0])); // change gamedir if needed - if (!FS_ChangeGameDirs(1, gamedir, true, false)) + if (!FS_ChangeGameDirs(1, &str, false)) Host_Error("CL_ParseServerInfo: unable to switch to server specified gamedir"); cl.gametype = GAME_DEATHMATCH; diff --git a/com_game.c b/com_game.c index 0dcde1ea..b6a21564 100644 --- a/com_game.c +++ b/com_game.c @@ -93,7 +93,7 @@ void COM_InitGameType (void) { char name [MAX_OSPATH]; int i; - int index = 0; + int index = GAME_NORMAL; #ifdef FORCEGAME COM_ToLowerString(FORCEGAME, name, sizeof (name)); @@ -116,10 +116,11 @@ void COM_InitGameType (void) COM_SetGameType(index); } -void COM_ChangeGameTypeForGameDirs(void) +int COM_ChangeGameTypeForGameDirs(unsigned numgamedirs, const char *gamedirs[], qbool failmissing, qbool init) { unsigned i, gamemode_count = sizeof(gamemode_info) / sizeof(gamemode_info[0]); - int index = -1; + int j, index = -1; + addgamedirs_t ret = GAMEDIRS_SUCCESS; // this will not not change the gamegroup // first check if a base game (single gamedir) matches @@ -131,38 +132,62 @@ void COM_ChangeGameTypeForGameDirs(void) break; } } - // now that we have a base game, see if there is a derivative game matching the startup one (two gamedirs) - // bones_was_here: this prevents a Quake expansion (eg Rogue) getting switched to Quake, - // and its gamedirname2 (eg "rogue") being lost from the search path, when adding a miscellaneous gamedir. - for (i = 0; i < gamemode_count; i++) + if (index < 0) + Sys_Error("BUG: failed to find the base game!"); + + // See if there is a derivative game matching the startup one (two gamedirs), + // this prevents a Quake expansion (eg Rogue) getting switched to Quake during startup, + // not used when changing gamedirs later so that it's possible to change from the startup mod to the base one. + if (init) { - if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && gamemode_info[i].mode == com_startupgamemode) + for (i = 0; i < gamemode_count; i++) { - index = i; - break; + if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && gamemode_info[i].mode == com_startupgamemode) + { + index = i; + break; + } } } - // also see if the first gamedir (from -game parm or gamedir command) matches a derivative game (two/three gamedirs) - if (fs_numgamedirs) + + // See if the base game or mod can be identified by a gamedir, + // if more than one matches the last is used because the last gamedir is the primary. + for (j = numgamedirs - 1; j >= 0; --j) { for (i = 0; i < gamemode_count; i++) { - if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2)) + if (gamemode_info[i].group == com_startupgamegroup) + if ((gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0] && !strcasecmp(gamedirs[j], gamemode_info[i].gamedirname2)) + || (!gamemode_info[i].gamedirname2 && !strcasecmp(gamedirs[j], gamemode_info[i].gamedirname1))) { index = i; - break; + goto double_break; } } } +double_break: + // we now have a good guess at which game this is meant to be... - if (index >= 0 && gamemode != gamemode_info[index].mode) - COM_SetGameType(index); + COM_SetGameType(index); + + ret = FS_SetGameDirs(numgamedirs, gamedirs, failmissing, !init); + if (ret == GAMEDIRS_SUCCESS) + { + Con_Printf("Game is %s using %s", gamename, fs_numgamedirs > 1 ? "gamedirs" : "gamedir"); + for (j = 0; j < fs_numgamedirs; ++j) + Con_Printf(" %s%s", (strcasecmp(fs_gamedirs[j], gamedirname1) && (!gamedirname2 || strcasecmp(fs_gamedirs[j], gamedirname2))) ? "^7" : "^9", fs_gamedirs[j]); + Con_Printf("\n"); + + Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername); + } + return ret; } static void COM_SetGameType(int index) { static char gamenetworkfilternamebuffer[64]; - int i, t; + int t; + if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]))) index = 0; gamemode = gamemode_info[index].mode; @@ -189,18 +214,6 @@ static void COM_SetGameType(int index) gameuserdirname = sys.argv[t+1]; } - if (gamedirname2 && gamedirname2[0]) - Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2); - else - Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1); - for (i = 0;i < fs_numgamedirs;i++) - { - if (i == 0) - Con_Printf(", with mod gamedirs"); - Con_Printf(" %s", fs_gamedirs[i]); - } - Con_Printf("\n"); - if (strchr(gamenetworkfiltername, ' ')) { char *s; @@ -212,6 +225,4 @@ static void COM_SetGameType(int index) *s = '_'; gamenetworkfiltername = gamenetworkfilternamebuffer; } - - Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername); } diff --git a/com_game.h b/com_game.h index 4f615568..aa2cdbe6 100644 --- a/com_game.h +++ b/com_game.h @@ -79,6 +79,6 @@ extern const char *gameuserdirname; extern char com_modname[MAX_OSPATH]; void COM_InitGameType (void); -void COM_ChangeGameTypeForGameDirs(void); +int COM_ChangeGameTypeForGameDirs(unsigned numgamedirs, const char *gamedirs[], qbool failmissing, qbool init); #endif diff --git a/fs.c b/fs.c index e2afcf05..df9cf04f 100644 --- a/fs.c +++ b/fs.c @@ -438,7 +438,7 @@ char fs_gamedir[MAX_OSPATH]; char fs_basedir[MAX_OSPATH]; static pack_t *fs_selfpack = NULL; -// list of active game directories (empty if not running a mod) +// list of active game directories int fs_numgamedirs = 0; char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH]; @@ -1544,36 +1544,17 @@ FS_Rescan void FS_Rescan (void) { int i; - qbool fs_modified = false; - qbool reset = false; char gamedirbuf[MAX_INPUTLINE]; char vabuf[1024]; - if (fs_searchpaths) - reset = true; FS_ClearSearchPath(); - // automatically activate gamemode for the gamedirs specified - if (reset) - COM_ChangeGameTypeForGameDirs(); - - // add the game-specific paths - // gamedirname1 (typically id1) - FS_AddGameHierarchy (gamedirname1); // update the com_modname (used for server info) if (gamedirname2 && gamedirname2[0]) dp_strlcpy(com_modname, gamedirname2, sizeof(com_modname)); else dp_strlcpy(com_modname, gamedirname1, sizeof(com_modname)); - // add the game-specific path, if any - // (only used for mission packs and the like, which should set fs_modified) - if (gamedirname2 && gamedirname2[0]) - { - fs_modified = true; - FS_AddGameHierarchy (gamedirname2); - } - // -game // Adds basedir/gamedir as an override game // LadyHavoc: now supports multiple -game directories @@ -1581,7 +1562,6 @@ void FS_Rescan (void) *gamedirbuf = 0; for (i = 0;i < fs_numgamedirs;i++) { - fs_modified = true; FS_AddGameHierarchy (fs_gamedirs[i]); // update the com_modname (used server info) dp_strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname)); @@ -1622,7 +1602,7 @@ void FS_Rescan (void) case GAME_ROGUE: if (!registered.integer) { - if (fs_modified) + if (fs_numgamedirs > 1) Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n"); else Con_Print("Playing shareware version.\n"); @@ -1651,57 +1631,105 @@ static void FS_Rescan_f(cmd_state_t *cmd) /* ================ -FS_ChangeGameDirs +FS_AddGameDirs ================ */ -extern qbool vid_opened; -qbool FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qbool complain, qbool failmissing) +addgamedirs_t FS_SetGameDirs(int numgamedirs, const char *gamedirs[], qbool failmissing, qbool abortonfail) { - int i; + int i, j, k; const char *p; + const char *gamedirs_ok[MAX_GAMEDIRS + 2]; + int numgamedirs_ok; - if (fs_numgamedirs == numgamedirs) + // prepend the game-specific gamedirs (the primary and search order can be overriden) + gamedirs_ok[0] = gamedirname1; + numgamedirs_ok = 1; + if (gamedirname2 && gamedirname2[0]) { - for (i = 0;i < numgamedirs;i++) - if (strcasecmp(fs_gamedirs[i], gamedirs[i])) - break; - if (i == numgamedirs) - return true; // already using this set of gamedirs, do nothing + gamedirs_ok[1] = gamedirname2; + ++numgamedirs_ok; } - if (numgamedirs > MAX_GAMEDIRS) + // check the game-specific gamedirs + for (i = 0; i < numgamedirs_ok; ++i) { - if (complain) - Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS); - return false; // too many gamedirs + p = FS_CheckGameDir(gamedirs_ok[i]); + if(!p) + Sys_Error("BUG: nasty gamedir name \"%s\" in gamemode_info", gamedirs_ok[i]); + if(p == fs_checkgamedir_missing && failmissing) + { + Con_Printf(abortonfail ? CON_ERROR : CON_WARN "Base gamedir \"%s\" empty or not found!\n", gamedirs_ok[i]); + if (abortonfail) + return GAMEDIRS_FAILURE; // missing gamedirs + } } - for (i = 0;i < numgamedirs;i++) + // copy and check the user-specified gamedirs + for (i = 0; i < numgamedirs && (size_t)numgamedirs_ok < sizeof(gamedirs_ok) / sizeof(gamedirs_ok[0]); ++i) { + // remove any previously-added duplicate (last one wins) + for (j = 0; j < numgamedirs_ok; ++j) + if (!strcasecmp(gamedirs_ok[j], gamedirs[i])) + { + --numgamedirs_ok; + for (k = j; k < numgamedirs_ok; ++k) + gamedirs_ok[k] = gamedirs_ok[k + 1]; + } + // if string is nasty, reject it p = FS_CheckGameDir(gamedirs[i]); if(!p) { - if (complain) - Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]); - return false; // nasty gamedirs + Con_Printf(abortonfail ? CON_ERROR : CON_WARN "Nasty gamedir name \"%s\" rejected\n", gamedirs[i]); + if (abortonfail) + return GAMEDIRS_FAILURE; // nasty gamedirs + else + continue; } if(p == fs_checkgamedir_missing && failmissing) { - if (complain) - Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]); - return false; // missing gamedirs + Con_Printf(abortonfail ? CON_ERROR : CON_WARN "Gamedir \"%s\" empty or not found!\n", gamedirs[i]); + if (abortonfail) + return GAMEDIRS_FAILURE; // missing gamedirs + else + continue; } + + gamedirs_ok[numgamedirs_ok++] = gamedirs[i]; } - Host_SaveConfig(CONFIGFILENAME); + if (fs_numgamedirs == numgamedirs_ok) + { + for (i = 0;i < numgamedirs_ok;i++) + if (strcasecmp(fs_gamedirs[i], gamedirs_ok[i])) + break; + if (i == numgamedirs_ok) + return GAMEDIRS_ALLGOOD; // already using this set of gamedirs, do nothing + } - fs_numgamedirs = numgamedirs; - for (i = 0;i < fs_numgamedirs;i++) - dp_strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i])); + if (numgamedirs_ok > MAX_GAMEDIRS) + { + Con_Printf(abortonfail ? CON_ERROR : CON_WARN "That is too many gamedirs (%i > %i)\n", numgamedirs_ok, MAX_GAMEDIRS); + if (abortonfail) + return GAMEDIRS_FAILURE; // too many gamedirs + } - // reinitialize filesystem to detect the new paks - FS_Rescan(); + for (i = 0, fs_numgamedirs = 0; i < numgamedirs_ok && fs_numgamedirs < MAX_GAMEDIRS; ++i) + dp_strlcpy(fs_gamedirs[fs_numgamedirs++], gamedirs_ok[i], sizeof(fs_gamedirs[0])); + + return GAMEDIRS_SUCCESS; +} + +qbool FS_ChangeGameDirs(int numgamedirs, const char *gamedirs[], qbool failmissing) +{ + addgamedirs_t addresult = COM_ChangeGameTypeForGameDirs(numgamedirs, gamedirs, failmissing, false); + + if (addresult == GAMEDIRS_ALLGOOD) + return true; // already using this set of gamedirs, do nothing + else if (addresult == GAMEDIRS_FAILURE) + return false; + + Host_SaveConfig(CONFIGFILENAME); if (cls.demoplayback) { @@ -1712,7 +1740,10 @@ qbool FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qbool compl // unload all sounds so they will be reloaded from the new files as needed S_UnloadAllSounds_f(cmd_local); - // reset everything that can be and reload configs + // reinitialize filesystem to detect the new paks + FS_Rescan(); + + // reload assets after the config is executed Cbuf_InsertText(cmd_local, "\nloadconfig\n"); return true; @@ -1727,13 +1758,13 @@ static void FS_GameDir_f(cmd_state_t *cmd) { int i; int numgamedirs; - char gamedirs[MAX_GAMEDIRS][MAX_QPATH]; + const char *gamedirs[MAX_GAMEDIRS]; if (Cmd_Argc(cmd) < 2) { Con_Printf("gamedirs active:"); for (i = 0;i < fs_numgamedirs;i++) - Con_Printf(" %s", fs_gamedirs[i]); + Con_Printf(" %s%s", (strcasecmp(fs_gamedirs[i], gamedirname1) && (!gamedirname2 || strcasecmp(fs_gamedirs[i], gamedirname2))) ? "^7" : "^9", fs_gamedirs[i]); Con_Printf("\n"); return; } @@ -1741,24 +1772,21 @@ static void FS_GameDir_f(cmd_state_t *cmd) numgamedirs = Cmd_Argc(cmd) - 1; if (numgamedirs > MAX_GAMEDIRS) { - Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS); + Con_Printf(CON_ERROR "Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS); return; } for (i = 0;i < numgamedirs;i++) - dp_strlcpy(gamedirs[i], Cmd_Argv(cmd, i+1), sizeof(gamedirs[i])); + gamedirs[i] = Cmd_Argv(cmd, i+1); if ((cls.state == ca_connected && !cls.demoplayback) || sv.active) { // actually, changing during game would work fine, but would be stupid - Con_Printf("Can not change gamedir while client is connected or server is running!\n"); + Con_Printf(CON_ERROR "Can not change gamedir while client is connected or server is running!\n"); return; } - // halt demo playback to close the file - CL_Disconnect(); - - FS_ChangeGameDirs(numgamedirs, gamedirs, true, true); + FS_ChangeGameDirs(numgamedirs, gamedirs, true); } static const char *FS_SysCheckGameDir(const char *gamedir, char *buf, size_t buflength) @@ -2109,8 +2137,9 @@ void FS_Init_Commands(void) static void FS_Init_Dir (void) { - const char *p; int i; + int numgamedirs; + const char *cmdline_gamedirs[MAX_GAMEDIRS]; *fs_basedir = 0; *fs_userdir = 0; @@ -2234,37 +2263,21 @@ static void FS_Init_Dir (void) FS_ListGameDirs(); - p = FS_CheckGameDir(gamedirname1); - if(!p || p == fs_checkgamedir_missing) - Con_Printf(CON_WARN "WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1); - - if(gamedirname2) - { - p = FS_CheckGameDir(gamedirname2); - if(!p || p == fs_checkgamedir_missing) - Con_Printf(CON_WARN "WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2); - } - // -game // Adds basedir/gamedir as an override game // LadyHavoc: now supports multiple -game directories - for (i = 1;i < sys.argc && fs_numgamedirs < MAX_GAMEDIRS;i++) + // the last one is the primary (where files are saved) and is used to identify mods + for (i = 1, numgamedirs = 0; i < sys.argc && numgamedirs < MAX_GAMEDIRS; i++) { if (!sys.argv[i]) continue; if (!strcmp (sys.argv[i], "-game") && i < sys.argc-1) { i++; - p = FS_CheckGameDir(sys.argv[i]); - if(!p) - Con_Printf("WARNING: Nasty -game name rejected: %s\n", sys.argv[i]); - if(p == fs_checkgamedir_missing) - Con_Printf(CON_WARN "WARNING: -game %s%s/ not found!\n", fs_basedir, sys.argv[i]); - // add the gamedir to the list of active gamedirs - dp_strlcpy (fs_gamedirs[fs_numgamedirs], sys.argv[i], sizeof(fs_gamedirs[fs_numgamedirs])); - fs_numgamedirs++; + cmdline_gamedirs[numgamedirs++] = sys.argv[i]; } } + COM_ChangeGameTypeForGameDirs(numgamedirs, cmdline_gamedirs, true, true); // generate the searchpath FS_Rescan(); diff --git a/fs.h b/fs.h index d8398331..b81d84d2 100644 --- a/fs.h +++ b/fs.h @@ -43,7 +43,7 @@ extern char fs_basedir [MAX_OSPATH]; extern char fs_userdir [MAX_OSPATH]; // list of active game directories (empty if not running a mod) -#define MAX_GAMEDIRS 16 +#define MAX_GAMEDIRS 17 // 16 + gamedirname1 extern int fs_numgamedirs; extern char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH]; @@ -99,7 +99,13 @@ gamedir_t; extern gamedir_t *fs_all_gamedirs; // terminated by entry with empty name extern int fs_all_gamedirs_count; -qbool FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qbool complain, qbool failmissing); +typedef enum addgamedirs_e { + GAMEDIRS_ALLGOOD = -1, + GAMEDIRS_FAILURE = 0, + GAMEDIRS_SUCCESS = 1 +} addgamedirs_t; +addgamedirs_t FS_SetGameDirs(int numgamedirs, const char *gamedirs[], qbool failmissing, qbool abortonfail); +qbool FS_ChangeGameDirs(int numgamedirs, const char *gamedirs[], qbool failmissing); qbool FS_IsRegisteredQuakePack(const char *name); int FS_CRCFile(const char *filename, size_t *filesizepointer); void FS_UnloadPacks_dlcache(void); diff --git a/menu.c b/menu.c index 8034f2f3..de4b1008 100644 --- a/menu.c +++ b/menu.c @@ -4514,15 +4514,13 @@ static void M_ServerList_Key(cmd_state_t *cmd, int k, int ascii) //============================================================================= /* MODLIST MENU */ -// same limit of mod dirs as in fs.c -#define MODLIST_MAXDIRS 16 -static int modlist_enabled [MODLIST_MAXDIRS]; //array of indexs to modlist +// same limit of mod dirs as in fs.c (allowing that one is used by gamedirname1) +#define MODLIST_MAXDIRS MAX_GAMEDIRS - 1 static int modlist_numenabled; //number of enabled (or in process to be..) mods typedef struct modlist_entry_s { qbool loaded; // used to determine whether this entry is loaded and running - int enabled; // index to array of modlist_enabled // name of the modification, this is displayed on the menu entry char name[128]; @@ -4541,16 +4539,15 @@ static void ModList_RebuildList(void) int i,j; stringlist_t list; const char *description; + int desc_len; stringlistinit(&list); listdirectory(&list, fs_basedir, ""); stringlistsort(&list, true); modlist_count = 0; - modlist_numenabled = fs_numgamedirs; + modlist_numenabled = 0; for (i = 0;i < list.numstrings && modlist_count < MODLIST_TOTALSIZE;i++) { - int desc_len; - // reject any dirs that are part of the base game if (gamedirname1 && !strcasecmp(gamedirname1, list.strings[i])) continue; //if (gamedirname2 && !strcasecmp(gamedirname2, list.strings[i])) continue; @@ -4570,17 +4567,17 @@ static void ModList_RebuildList(void) } dp_strlcpy (modlist[modlist_count].dir, list.strings[i], sizeof(modlist[modlist_count].dir)); - //check currently loaded mods + + // check if this mod is currently loaded modlist[modlist_count].loaded = false; - if (fs_numgamedirs) - for (j = 0; j < fs_numgamedirs; j++) - if (!strcasecmp(fs_gamedirs[j], modlist[modlist_count].dir)) - { - modlist[modlist_count].loaded = true; - modlist[modlist_count].enabled = j; - modlist_enabled[j] = modlist_count; - break; - } + for (j = 0; j < fs_numgamedirs; j++) + if (!strcasecmp(fs_gamedirs[j], modlist[modlist_count].dir)) + { + modlist[modlist_count].loaded = true; + modlist_numenabled++; + break; + } + modlist_count ++; } stringlistfreecontents(&list); @@ -4590,22 +4587,7 @@ static void ModList_Enable (void) { int i; int numgamedirs; - char gamedirs[MODLIST_MAXDIRS][MAX_QPATH]; - - // copy our mod list into an array for FS_ChangeGameDirs - numgamedirs = modlist_numenabled; - for (i = 0; i < modlist_numenabled; i++) - dp_strlcpy (gamedirs[i], modlist[modlist_enabled[i]].dir,sizeof (gamedirs[i])); - - // this code snippet is from FS_ChangeGameDirs - if (fs_numgamedirs == numgamedirs) - { - for (i = 0;i < numgamedirs;i++) - if (strcasecmp(fs_gamedirs[i], gamedirs[i])) - break; - if (i == numgamedirs) - return; // already using this set of gamedirs, do nothing - } + const char *gamedirs[MODLIST_MAXDIRS]; // this part is basically the same as the FS_GameDir_f function if ((cls.state == ca_connected && !cls.demoplayback) || sv.active) @@ -4615,7 +4597,18 @@ static void ModList_Enable (void) return; } - FS_ChangeGameDirs (modlist_numenabled, gamedirs, true, true); + // copy our mod list into an array for FS_ChangeGameDirs + for (i = 0, numgamedirs = 0; i < modlist_count && numgamedirs < MODLIST_MAXDIRS; i++) + if (modlist[i].loaded) + gamedirs[numgamedirs++] = modlist[i].dir; + // allow disabling all active mods using the menu + if (numgamedirs == 0) + { + numgamedirs = 1; + gamedirs[0] = gamedirname1; + } + + FS_ChangeGameDirs(numgamedirs, gamedirs, true); } void M_Menu_ModList_f(cmd_state_t *cmd) @@ -4630,28 +4623,13 @@ void M_Menu_ModList_f(cmd_state_t *cmd) static void M_Menu_ModList_AdjustSliders (int dir) { - int i; S_LocalSound ("sound/misc/menu3.wav"); // stop adding mods, we reach the limit if (!modlist[modlist_cursor].loaded && (modlist_numenabled == MODLIST_MAXDIRS)) return; + modlist[modlist_cursor].loaded = !modlist[modlist_cursor].loaded; - if (modlist[modlist_cursor].loaded) - { - modlist[modlist_cursor].enabled = modlist_numenabled; - //push the value on the enabled list - modlist_enabled[modlist_numenabled++] = modlist_cursor; - } - else - { - //eliminate the value from the enabled list - for (i = modlist[modlist_cursor].enabled; i < modlist_numenabled; i++) - { - modlist_enabled[i] = modlist_enabled[i+1]; - modlist[modlist_enabled[i]].enabled--; - } - modlist_numenabled--; - } + modlist_numenabled += modlist[modlist_cursor].loaded ? 1 : -1; } static void M_ModList_Draw (void) @@ -4671,8 +4649,12 @@ static void M_ModList_Draw (void) M_PrintRed(432, 32, s_enabled); // Draw a list box with all enabled mods DrawQ_Pic(menu_x + 432, menu_y + 48, NULL, 172, 8 * modlist_numenabled, 0, 0, 0, 0.5, 0); - for (y = 0; y < modlist_numenabled; y++) - M_PrintRed(432, 48 + y * 8, modlist[modlist_enabled[y]].dir); + for (n = 0, y = 48; n < modlist_count; n++) + if (modlist[n].loaded) + { + M_PrintRed(432, y, modlist[n].dir); + y += 8; + } if (*cl_connect_status) M_Print(16, menu_height - 8, cl_connect_status);