thirtyflightsofloving/ui/ui_utils.c
Knightmare66 c9e3575516 Updated changelog.
Commented out currently unused crosshair lists in menu utils code.
Commented out unused playermodel strings in menu utils code.
2021-02-17 15:38:30 -05:00

2364 lines
56 KiB
C

/*
===========================================================================
Copyright (C) 1997-2001 Id Software, Inc.
This file is part of Quake 2 source code.
Quake 2 source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake 2 source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake 2 source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// ui_utils.c -- misc support functions for the menus
#include <ctype.h>
#ifdef _WIN32
#include <io.h>
#endif
#include "../client/client.h"
#include "ui_local.h"
#include <sys/types.h>
#include <sys/stat.h>
/*
=======================================================================
MISC UTILITY FUNCTIONS
=======================================================================
*/
#if 0
/*
=================
FreeFileList
=================
*/
void FreeFileList (char **list, int n)
{
int i;
for (i = 0; i < n; i++)
{
if (list && list[i])
{
free(list[i]);
list[i] = 0;
}
}
free(list);
}
/*
=================
ItemInList
=================
*/
qboolean ItemInList (char *check, int num, char **list)
{
int i;
for (i=0;i<num;i++)
if (!Q_strcasecmp(check, list[i]))
return true;
return false;
}
/*
=================
InsertInList
=================
*/
void InsertInList (char **list, char *insert, int len, int start)
{
int i;
if (!list) return;
for (i=start; i<len; i++)
{
if (!list[i])
{
list[i] = strdup(insert);
return;
}
}
list[len] = strdup(insert);
}
#endif
/*
==========================
UI_IsValidImageFilename
==========================
*/
qboolean UI_IsValidImageFilename (char *name)
{
int len = (int)strlen(name);
if ( !strcmp(name+max(len-4,0), ".pcx")
|| !strcmp(name+max(len-4,0), ".tga")
#ifdef PNG_SUPPORT
|| !strcmp(name+max(len-4,0), ".png")
#endif // PNG_SUPPORT
|| !strcmp(name+max(len-4,0), ".jpg")
)
return true;
return false;
}
/*
=======================================================================
START GAME FUNCS
=======================================================================
*/
/*
===============
UI_StartSPGame
===============
*/
void UIStartSPGame (void)
{
// disable updates and start the cinematic going
cl.servercount = -1;
UI_ForceMenuOff ();
Cvar_SetValue( "deathmatch", 0 );
Cvar_SetValue( "coop", 0 );
Cvar_SetValue( "gamerules", 0 ); //PGM
if (cls.state != ca_disconnected) // don't force loadscreen if disconnected
Cbuf_AddText ("loading ; killserver ; wait\n");
Cbuf_AddText ("newgame\n");
cls.key_dest = key_game;
}
/*
===============
UI_StartServer
===============
*/
void UI_StartServer (char *startmap, qboolean dedicated)
{
char *spot = NULL;
if ( UI_Coop_MenuMode() )
{
if (Q_stricmp(startmap, "bunk1") == 0)
spot = "start";
else if (Q_stricmp(startmap, "mintro") == 0)
spot = "start";
else if (Q_stricmp(startmap, "fact1") == 0)
spot = "start";
else if (Q_stricmp(startmap, "power1") == 0)
spot = "pstart";
else if (Q_stricmp(startmap, "biggun") == 0)
spot = "bstart";
else if (Q_stricmp(startmap, "hangar1") == 0)
spot = "unitstart";
else if (Q_stricmp(startmap, "city1") == 0)
spot = "unitstart";
else if (Q_stricmp(startmap, "boss1") == 0)
spot = "bosstart";
}
UI_ForceMenuOff ();
if (dedicated)
Cvar_ForceSet ("dedicated", "1");
if (spot) {
if (Com_ServerState())
Cbuf_AddText ("disconnect\n");
Cbuf_AddText (va("gamemap \"*%s$%s\"\n", startmap, spot));
}
else {
Cbuf_AddText (va("map %s\n", startmap));
}
// UI_ForceMenuOff ();
}
/*
=======================================================================
GENERIC ASSET LIST LOADING
=======================================================================
*/
/*
==========================
UI_InsertInAssetList
==========================
*/
void UI_InsertInAssetList (char **list, char *insert, int len, int start)
{
int i, j;
if (!list || !insert) return;
if (start < 0) return;
// if (start >= len) return;
if (start > len) return;
// i=start so default stays first!
for (i=start; i<len; i++)
{
if (!list[i])
break;
if (strcmp( list[i], insert ))
{
for (j=len; j>i; j--)
list[j] = list[j-1];
list[i] = strdup(insert);
return;
}
}
list[len] = strdup(insert);
}
/*
==========================
UI_LoadAssetList
Generic file list loader
Used for fonts, huds, and crosshairs
==========================
*/
char **UI_LoadAssetList (char *dir, char *nameMask, char *firstItem, int *returnCount, int maxItems, qboolean stripExtension, qboolean frontInsert, qboolean (*checkName)(char *p))
{
char **list = 0, **itemFiles;
char *path = NULL, *curItem, *p, *ext;
// char findName[1024];
int nItems = 0, nItemNames, i, baseLen, extLen;
// check pointers
if (!dir || !nameMask || !firstItem || !returnCount || !checkName)
return NULL;
if (maxItems < 2) // must allow at least 2 items
return NULL;
list = malloc(sizeof(char *) * (maxItems+1));
memset(list, 0, sizeof(char *) * (maxItems+1));
// set first item name
list[0] = strdup(firstItem);
nItemNames = 1;
#if 1
itemFiles = FS_GetFileList(va("%s/%s", dir, nameMask), NULL, &nItems);
for (i=0; i<nItems && nItemNames < maxItems; i++)
{
if (!itemFiles || !itemFiles[i])
continue;
p = strrchr(itemFiles[i], '/'); p++;
if ( !checkName(p) )
continue;
if (stripExtension && (ext = strrchr(p, '.')) ) {
extLen = (int)strlen(ext);
baseLen = (int)strlen(p) - extLen;
p[baseLen] = 0; // NULL
}
curItem = p;
if (!FS_ItemInList(curItem, nItemNames, list))
{
// UI_InsertInAssetList (frontInsert) not needed due to sorting in FS_GetFileList()
FS_InsertInList (list, curItem, nItemNames, 1); // start=1 so first item stays first!
nItemNames++;
}
// restore extension so whole string gets deleted
if (stripExtension && ext)
p[baseLen] = '.';
}
#else
path = FS_NextPath (path);
while (path)
{
Com_sprintf (findName, sizeof(findName), "%s/%s/%s", path, dir, nameMask);
itemFiles = FS_ListFiles(findName, &nItems, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
for (i=0; i < nItems && nItemNames < maxItems; i++)
{
if (!itemFiles || !itemFiles[i])
continue;
p = strrchr(itemFiles[i], '/'); p++;
if ( !checkName(p) )
continue;
if (stripExtension && (ext = strrchr(p, '.')) ) {
extLen = (int)strlen(ext);
baseLen = (int)strlen(p) - extLen;
p[baseLen] = 0; // NULL
}
curItem = p;
if (!FS_ItemInList(curItem, nItemNames, list))
{
if (frontInsert)
UI_InsertInAssetList (list, curItem, nItemNames, 1); // start=1 so first item stays first!
else
FS_InsertInList (list, curItem, nItemNames, 1); // start=1 so first item stays first!
nItemNames++;
}
// restore extension so whole string gets deleted
if (stripExtension && ext)
p[baseLen] = '.';
}
if (nItems)
FS_FreeFileList (itemFiles, nItems);
path = FS_NextPath (path);
}
// check pak after
if (itemFiles = FS_ListPak(va("%s/", dir), &nItems))
{
for (i=0; i<nItems && nItemNames < maxItems; i++)
{
if (!itemFiles || !itemFiles[i])
continue;
p = strrchr(itemFiles[i], '/'); p++;
if ( !checkName(p) )
continue;
if (stripExtension && (ext = strrchr(p, '.')) ) {
extLen = (int)strlen(ext);
baseLen = (int)strlen(p) - extLen;
p[baseLen] = 0; // NULL
}
curItem = p;
if (!FS_ItemInList(curItem, nItemNames, list))
{
if (frontInsert)
UI_InsertInAssetList (list, curItem, nItemNames, 1); // start=1 so first item stays first!
else
FS_InsertInList (list, curItem, nItemNames, 1); // start=1 so first item stays first!
nItemNames++;
}
// restore extension so whole string gets deleted
if (stripExtension && ext)
p[baseLen] = '.';
}
}
#endif
if (nItems)
FS_FreeFileList (itemFiles, nItems);
// re-count list, nItemNames is somehow counted with 1 extra
for (i=0; list[i]; i++);
nItemNames = i;
if ( returnCount )
*returnCount = nItemNames;
return list;
}
/*
=======================================================================
FONT LOADING
=======================================================================
*/
#define UI_MAX_FONTS 32
char **ui_font_names = NULL;
int ui_numfonts = 0;
#if 0
void UI_InsertFont (char **list, char *insert, int len)
{
int i, j;
if (!list) return;
// i=1 so default stays first!
for (i=1; i<len; i++)
{
if (!list[i])
break;
if (strcmp( list[i], insert ))
{
for (j=len; j>i ;j--)
list[j] = list[j-1];
list[i] = strdup(insert);
return;
}
}
list[len] = strdup(insert);
}
char **UI_SetFontNames (void)
{
char *curFont;
char **list = 0, *p;//, *s;
int nfonts = 0, nfontnames;
char **fontfiles;
char *path = NULL;
int i;//, j;
list = malloc( sizeof( char * ) * UI_MAX_FONTS );
memset( list, 0, sizeof( char * ) * UI_MAX_FONTS );
list[0] = strdup("default");
nfontnames = 1;
fontfiles = FS_GetFileList("fonts/*.*", NULL, &nfonts);
for (i=0; i<nfonts && nfontnames < UI_MAX_FONTS; i++)
{
int num;
if (!fontfiles || !fontfiles[i]) // Knightmare added array base check
continue;
if ( !UI_IsValidImageFilename(fontfiles[i]) )
continue;
p = strrchr(fontfiles[i], '/'); p++;
num = (int)strlen(p)-4;
p[num] = 0; // NULL
curFont = p;
if (!FS_ItemInList(curFont, nfontnames, list))
{
// UI_InsertFont not needed due to sorting in FS_GetFileList()
// UI_InsertFont (list, strdup(curFont), nfontnames);
FS_InsertInList(list, strdup(curFont), nfontnames, 1); // start=1 so default stays first!
nfontnames++;
}
// set back so whole string get deleted.
p[num] = '.';
}
if (nfonts)
FS_FreeFileList( fontfiles, nfonts );
ui_numfonts = nfontnames;
return list;
}
#endif
/*
==========================
UI_IsValidFontName
==========================
*/
qboolean UI_IsValidFontName (char *name)
{
return UI_IsValidImageFilename (name);
}
/*
==========================
UI_LoadFontNames
==========================
*/
void UI_LoadFontNames (void)
{
ui_font_names = UI_LoadAssetList ("fonts", "*.*", "default", &ui_numfonts, UI_MAX_FONTS, true, true, UI_IsValidFontName);
}
/*
==========================
UI_FreeFontNames
==========================
*/
void UI_FreeFontNames (void)
{
if (ui_numfonts > 0) {
FS_FreeFileList (ui_font_names, ui_numfonts);
}
ui_font_names = NULL;
ui_numfonts = 0;
}
/*
=======================================================================
HUD NAME LOADING
=======================================================================
*/
// TODO: Enable this when HUD loading is working
#if 0
#define UI_MAX_HUDS 128
char **ui_hud_names = NULL;
int ui_numhuds = 0;
/*
==========================
UI_IsValidHudName
==========================
*/
qboolean UI_IsValidHudName (char *name)
{
int len = (int)strlen(name);
if ( !strcmp(name+max(len-4,0), ".hud") )
return true;
return false;
}
/*
==========================
UI_LoadHudNames
==========================
*/
void UI_LoadHudNames (void)
{
ui_hud_names = UI_LoadAssetList ("scripts/huds", "*.hud", "default", &ui_numhuds, UI_MAX_HUDS, true, true, UI_IsValidHudName);
}
/*
==========================
UI_FreeHudNames
==========================
*/
void UI_FreeHudNames (void)
{
if (ui_numhuds > 0){
FS_FreeFileList (ui_hud_names, ui_numhuds);
}
ui_hud_names = NULL;
ui_numhuds = 0;
}
#endif
/*
=======================================================================
CROSSHAIR LOADING
=======================================================================
*/
#define UI_MAX_CROSSHAIRS 101 // none + ch1-ch100
char **ui_crosshair_names = NULL;
//char **ui_crosshair_display_names = NULL;
//char **ui_crosshair_values = NULL;
int ui_numcrosshairs = 0;
/*
==========================
UI_SortCrosshairs
==========================
*/
void UI_SortCrosshairs (char **list, int len)
{
int i, j;
char *temp;
qboolean moved;
if (!list || len < 2)
return;
for (i=(len-1); i>0; i--)
{
moved = false;
for (j=0; j<i; j++)
{
// if (!list[j]) break;
if (!list[j] || !list[j+1]) continue;
if ( atoi(strdup(list[j]+2)) > atoi(strdup(list[j+1]+2)) )
{
temp = list[j];
list[j] = list[j+1];
list[j+1] = temp;
moved = true;
}
}
if (!moved) break; // done sorting
}
}
#if 0
char **UI_SetCrosshairNames (void)
{
char *curCrosshair;
char **list = 0, *p;
int ncrosshairs = 0, ncrosshairnames;
char **crosshairfiles;
char *path = NULL;
int i;
list = malloc( sizeof( char * ) * UI_MAX_CROSSHAIRS+1 );
memset( list, 0, sizeof( char * ) * UI_MAX_CROSSHAIRS+1 );
list[0] = strdup("none"); // was default
ncrosshairnames = 1;
crosshairfiles = FS_GetFileList("pics/ch*.*", NULL, &ncrosshairs);
for (i=0; i<ncrosshairs && ncrosshairnames < UI_MAX_CROSSHAIRS; i++)
{
int num, namelen;
if ( !crosshairfiles || !crosshairfiles[i] )
continue;
if ( !UI_IsValidImageFilename(crosshairfiles[i]) )
continue;
p = strrchr(crosshairfiles[i], '/'); p++;
// filename must be chxxx
if (strncmp(p, "ch", 2))
continue;
namelen = (int)strlen(strdup(p));
if (namelen < 7 || namelen > 9)
continue;
if (!isNumeric(p[2]))
continue;
if (namelen >= 8 && !isNumeric(p[3]))
continue;
// ch100 is only valid 5-char name
if (namelen == 9 && (p[2] != '1' || p[3] != '0' || p[4] != '0'))
continue;
num = (int)strlen(p)-4;
p[num] = 0; //NULL;
curCrosshair = p;
if (!FS_ItemInList(curCrosshair, ncrosshairnames, list))
{
FS_InsertInList(list, strdup(curCrosshair), ncrosshairnames, 1); // i=1 so none stays first!
ncrosshairnames++;
}
//set back so whole string get deleted.
p[num] = '.';
}
// sort the list
UI_SortCrosshairs (list, ncrosshairnames);
if (ncrosshairs)
FS_FreeFileList( crosshairfiles, ncrosshairs );
ui_numcrosshairs = ncrosshairnames;
return list;
}
#endif
/*
==========================
UI_IsValidCrosshairName
==========================
*/
qboolean UI_IsValidCrosshairName (char *name)
{
int namelen;
if ( !UI_IsValidImageFilename(name) )
return false;
// filename must be chxxx
if ( strncmp(name, "ch", 2) )
return false;
namelen = (int)strlen(strdup(name));
if (namelen < 7 || namelen > 9)
return false;
if ( !isNumeric(name[2]) )
return false;
if ( namelen >= 8 && !isNumeric(name[3]) )
return false;
// ch0 is invalid
if ( namelen == 7 && name[2] == '0' )
return false;
// ch100 is only valid 5-char name
if ( namelen == 9 && (name[2] != '1' || name[3] != '0' || name[4] != '0') )
return false;
// ch100-ch128 are only valid 5-char names
/* if ( namelen == 9 &&
( !isNumeric(name[4]) || name[2] != '1'
|| (name[2] == '1' && name[3] > '2')
|| (name[2] == '1' && name[3] == '2' && name[4] > '8') ) )
return false;*/
return true;
}
/*
==========================
UI_LoadCrosshairs
==========================
*/
void UI_LoadCrosshairs (void)
{
// int i;
ui_crosshair_names = UI_LoadAssetList ("pics", "ch*.*", "none", &ui_numcrosshairs, UI_MAX_CROSSHAIRS, true, false, UI_IsValidCrosshairName);
UI_SortCrosshairs (ui_crosshair_names, ui_numcrosshairs);
/* ui_crosshair_display_names = malloc( sizeof(char *) * (UI_MAX_CROSSHAIRS+1) );
memcpy(ui_crosshair_display_names, ui_crosshair_names, sizeof(char *) * (UI_MAX_CROSSHAIRS+1));
ui_crosshair_display_names[0] = strdup("chnone");
ui_crosshair_values = malloc( sizeof(char *) * (UI_MAX_CROSSHAIRS+1) );
memset(ui_crosshair_values, 0, sizeof(char *) * (UI_MAX_CROSSHAIRS+1) );
for (i=0; i<ui_numcrosshairs; i++)
ui_crosshair_values[i] = (i == 0) ? strdup("0") : strdup(strtok(ui_crosshair_names[i], "ch")); */
}
/*
==========================
UI_FreeCrosshairs
==========================
*/
void UI_FreeCrosshairs (void)
{
if (ui_numcrosshairs > 0)
{
FS_FreeFileList (ui_crosshair_names, ui_numcrosshairs);
/* if (ui_crosshair_display_names)
{
if (ui_crosshair_display_names[0]) {
free (ui_crosshair_display_names[0]);
}
free (ui_crosshair_display_names);
}
FS_FreeFileList (ui_crosshair_values, ui_numcrosshairs); */
}
ui_crosshair_names = NULL;
// ui_crosshair_display_names = NULL;
// ui_crosshair_values = NULL;
ui_numcrosshairs = 0;
}
/*
=============================================================================
SAVEGAME / SAVESHOT HANDLING
=============================================================================
*/
char ui_savestrings[UI_MAX_SAVEGAMES][32];
qboolean ui_savevalid[UI_MAX_SAVEGAMES+1];
time_t ui_savetimestamps[UI_MAX_SAVEGAMES];
qboolean ui_savechanged[UI_MAX_SAVEGAMES];
qboolean ui_saveshotvalid[UI_MAX_SAVEGAMES+1];
char ui_mapname[MAX_QPATH];
//qboolean ui_mapshotvalid;
char ui_saveload_shotname[MAX_QPATH];
/*
==========================
UI_Load_Savestrings
==========================
*/
void UI_Load_Savestrings (qboolean update)
{
int i;
FILE *fp;
fileHandle_t f;
char name[MAX_OSPATH];
char mapname[MAX_TOKEN_CHARS];
char *ch;
time_t old_timestamp;
struct stat st;
for (i=0; i<UI_MAX_SAVEGAMES; i++)
{
// Com_sprintf (name, sizeof(name), "%s/save/kmq2save%03i/server.ssv", FS_Savegamedir(), i); // was FS_Gamedir()
Com_sprintf (name, sizeof(name), "%s/"SAVEDIRNAME"/kmq2save%03i/server.ssv", FS_Savegamedir(), i); // was FS_Gamedir()
old_timestamp = ui_savetimestamps[i];
stat(name, &st);
ui_savetimestamps[i] = st.st_mtime;
// doesn't need to be refreshed
if ( update && ui_savetimestamps[i] == old_timestamp ) {
ui_savechanged[i] = false;
continue;
}
fp = fopen (name, "rb");
if (!fp) {
// Com_Printf("Save file %s not found.\n", name);
// strncpy (ui_savestrings[i], EMPTY_GAME_STRING);
Q_strncpyz (ui_savestrings[i], sizeof(ui_savestrings[i]), EMPTY_GAME_STRING);
ui_savevalid[i] = false;
ui_savetimestamps[i] = 0;
}
else
{
fclose (fp);
// Com_sprintf (name, sizeof(name), "save/kmq2save%03i/server.ssv", i);
Com_sprintf (name, sizeof(name), SAVEDIRNAME"/kmq2save%03i/server.ssv", i);
FS_FOpenFile (name, &f, FS_READ);
if (!f)
{
//Com_Printf("Save file %s not found.\n", name);
// strncpy (m_savestrings[i], EMPTY_GAME_STRING);
Q_strncpyz (ui_savestrings[i], sizeof(ui_savestrings[i]), EMPTY_GAME_STRING);
ui_savevalid[i] = false;
ui_savetimestamps[i] = 0;
}
else
{
FS_Read (ui_savestrings[i], sizeof(ui_savestrings[i]), f);
if (i==0) { // grab mapname
FS_Read (mapname, sizeof(mapname), f);
if (mapname[0] == '*') // skip * marker
Com_sprintf (ui_mapname, sizeof(ui_mapname), mapname+1);
else
Com_sprintf (ui_mapname, sizeof(ui_mapname), mapname);
if (ch = strchr (ui_mapname, '$'))
*ch = 0; // terminate string at $ marker
}
FS_FCloseFile(f);
ui_savevalid[i] = true;
}
}
ui_savechanged[i] = (ui_savetimestamps[i] != old_timestamp);
}
}
/*
==========================
UI_ValidateSaveshots
==========================
*/
void UI_ValidateSaveshots (void)
{
int i;
char shotname [MAX_QPATH];
// char mapshotname [MAX_QPATH];
for ( i = 0; i < UI_MAX_SAVEGAMES; i++ )
{
if ( !ui_savechanged[i] ) // doeesn't need to be reloaded
continue;
if ( ui_savevalid[i] )
{
if (i == 0)
Com_sprintf(shotname, sizeof(shotname), "/levelshots/%s.pcx", ui_mapname);
else
{ // free previously loaded shots
// Com_sprintf(shotname, sizeof(shotname), "save/kmq2save%03i/shot.jpg", i);
Com_sprintf(shotname, sizeof(shotname), SAVEDIRNAME"/kmq2save%03i/shot.jpg", i);
R_FreePic (shotname);
// Com_sprintf(shotname, sizeof(shotname), "/save/kmq2save%03i/shot.jpg", i);
Com_sprintf(shotname, sizeof(shotname), "/"SAVEDIRNAME"/kmq2save%03i/shot.jpg", i);
}
if (R_DrawFindPic(shotname))
ui_saveshotvalid[i] = true;
else
ui_saveshotvalid[i] = false;
}
else
ui_saveshotvalid[i] = false;
}
/* if (loadmenu)
{ // register mapshot for autosave
if (ui_savevalid[0]) {
Com_sprintf(mapshotname, sizeof(mapshotname), "/levelshots/%s.pcx", ui_mapname);
if (R_DrawFindPic(mapshotname))
ui_mapshotvalid = true;
else
ui_mapshotvalid = false;
}
else
ui_mapshotvalid = false;
}
// register null saveshot, this is only done once
if (R_DrawFindPic("/gfx/ui/noscreen.pcx"))
ui_saveshotvalid[UI_MAX_SAVEGAMES] = true;
else
ui_saveshotvalid[UI_MAX_SAVEGAMES] = false;
*/
}
/*
==========================
UI_UpdateSaveshot
==========================
*/
char *UI_UpdateSaveshot (int index)
{
// check index
if (index < 0 || index >= UI_MAX_SAVEGAMES)
return NULL;
if ( ui_savevalid[index] && ui_saveshotvalid[index] ) {
if ( index == 0 )
Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), "/levelshots/%s.pcx", ui_mapname);
else
// Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), "/save/kmq2save%03i/shot.jpg", index);
Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), "/"SAVEDIRNAME"/kmq2save%03i/shot.jpg", index);
}
else if ( ui_saveshotvalid[UI_MAX_SAVEGAMES] )
Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), UI_NOSCREEN_NAME);
else // no saveshot or nullshot
return NULL;
return ui_saveload_shotname;
}
/*
==========================
UI_UpdateSavegameData
==========================
*/
void UI_UpdateSavegameData (void)
{
UI_Load_Savestrings (true);
UI_ValidateSaveshots (); // register saveshots
}
/*
==========================
UI_InitSavegameData
==========================
*/
void UI_InitSavegameData (void)
{
int i;
for (i=0; i<UI_MAX_SAVEGAMES; i++) {
ui_savetimestamps[i] = 0;
ui_savechanged[i] = true;
}
UI_Load_Savestrings (false);
UI_ValidateSaveshots (); // register saveshots
// register null saveshot, this is only done once
if (R_DrawFindPic("/gfx/ui/noscreen.pcx"))
ui_saveshotvalid[UI_MAX_SAVEGAMES] = true;
else
ui_saveshotvalid[UI_MAX_SAVEGAMES] = false;
ui_savevalid[UI_MAX_SAVEGAMES] = false; // this element is always false to handle the back action
}
/*
==========================
UI_SaveIsValid
==========================
*/
qboolean UI_SaveIsValid (int index)
{
// check index
if (index < 0 || index >= UI_MAX_SAVEGAMES)
return false;
return ui_savevalid[index];
}
/*
==========================
UI_SaveshotIsValid
==========================
*/
qboolean UI_SaveshotIsValid (int index)
{
// check index
if (index < 0 || index >= UI_MAX_SAVEGAMES)
return false;
return ui_saveshotvalid[index];
}
/*
===============
UI_CanOpenSaveMenu
===============
*/
qboolean UI_CanOpenSaveMenu (void *unused)
{
return (Com_ServerState() > 0);
}
/*
=======================================================================
SERVER LISTING
Copyright (C) 2001-2003 pat@aftermoon.net for modif flanked by <serverping>
=======================================================================
*/
int ui_num_servers;
// user readable information
char ui_local_server_names[UI_MAX_LOCAL_SERVERS][UI_LOCAL_SERVER_NAMELEN];
//char *ui_serverlist_names[UI_MAX_LOCAL_SERVERS+1];
// network address
netadr_t ui_local_server_netadr[UI_MAX_LOCAL_SERVERS];
#if 1
//<serverping> Added code for compute ping time of server broadcasted
// The server is displayed like :
// "protocol ping hostname mapname nb players/max players"
// "udp 100ms Pat q2dm1 2/8"
int global_udp_server_time;
int global_ipx_server_time;
int global_adr_server_time[16];
netadr_t global_adr_server_netadr[16];
/*
==========================
UI_AddToServerList
==========================
*/
void UI_AddToServerList (netadr_t adr, char *info)
{
int i;
int iPing;
char *pszProtocol;
if (ui_num_servers == UI_MAX_LOCAL_SERVERS)
return;
while ( *info == ' ' )
info++;
// Ignore if duplicated
for (i=0; i<ui_num_servers; i++)
if ( strncmp(info, &ui_local_server_names[i][11], sizeof(ui_local_server_names[0])-10)==0 ) // crashes here
return;
iPing = 0 ;
for (i=0 ; i<UI_MAX_LOCAL_SERVERS ; i++)
{
if ( memcmp(&adr.ip, &global_adr_server_netadr[i].ip, sizeof(adr.ip))==0
&& adr.port == global_adr_server_netadr[i].port )
{
// bookmark server
iPing = Sys_Milliseconds() - global_adr_server_time[i] ;
pszProtocol = "bkm" ;
break;
}
}
if ( i == UI_MAX_LOCAL_SERVERS )
{
if ( adr.ip[0] > 0 ) // udp server
{
iPing = Sys_Milliseconds() - global_udp_server_time ;
pszProtocol = "UDP" ;
}
else // ipx server
{
iPing = Sys_Milliseconds() - global_ipx_server_time ;
pszProtocol = "IPX" ;
}
}
Com_sprintf( ui_local_server_names[ui_num_servers],
sizeof(ui_local_server_names[0]),
"%s %4dms %s", pszProtocol, iPing, info ) ;
ui_local_server_netadr[ui_num_servers] = adr;
ui_num_servers++;
}
// </serverping>
#else
void UI_AddToServerList (netadr_t adr, char *info)
{
int i;
if (ui_num_servers == UI_MAX_LOCAL_SERVERS)
return;
while ( *info == ' ' )
info++;
// ignore if duplicated
for (i=0 ; i<ui_num_servers ; i++)
if (!strcmp(info, ui_local_server_names[i]))
return;
ui_local_server_netadr[ui_num_servers] = adr;
strncpy (ui_local_server_names[ui_num_servers], info, sizeof(ui_local_server_names[0])-1);
ui_num_servers++;
}
#endif
/*
==========================
UI_SearchLocalGames
==========================
*/
void UI_SearchLocalGames (void)
{
int i;
ui_num_servers = 0;
for (i=0 ; i<UI_MAX_LOCAL_SERVERS ; i++)
// strncpy (ui_local_server_names[i], NO_SERVER_STRING);
Q_strncpyz (ui_local_server_names[i], sizeof(ui_local_server_names[i]), NO_SERVER_STRING);
Menu_DrawTextBox (168, 192, 36, 3);
SCR_DrawString (188, 192+MENU_FONT_SIZE, MENU_FONT_SIZE, ALIGN_CENTER, S_COLOR_ALT"Searching for local servers, this", FONT_UI, 255);
SCR_DrawString (188, 192+MENU_FONT_SIZE*2, MENU_FONT_SIZE, ALIGN_CENTER, S_COLOR_ALT"could take up to a minute, so", FONT_UI, 255);
SCR_DrawString (188, 192+MENU_FONT_SIZE*3, MENU_FONT_SIZE, ALIGN_CENTER, S_COLOR_ALT"please be patient.", FONT_UI, 255);
// the text box won't show up unless we do a buffer swap
GLimp_EndFrame();
// send out info packets
CL_PingServers_f();
}
/*
==========================
UI_JoinServer
==========================
*/
void UI_JoinServer (int index)
{
char buffer[128];
// check bounds
if (index < 0 || index >= UI_MAX_LOCAL_SERVERS || index >= ui_num_servers)
return;
if ( Q_stricmp( ui_local_server_names[index], NO_SERVER_STRING ) == 0 )
return;
Com_sprintf (buffer, sizeof(buffer), "connect %s\n", NET_AdrToString (ui_local_server_netadr[index]));
Cbuf_AddText (buffer);
UI_ForceMenuOff ();
cls.disable_screen = 1; // Knightmare- show loading screen
}
/*
==========================
UI_InitServerList
==========================
*/
void UI_InitServerList (void)
{
int i;
// ui_serverlist_names[UI_MAX_LOCAL_SERVERS] = NULL;
for ( i = 0; i < UI_MAX_LOCAL_SERVERS; i++ ) {
Com_sprintf (ui_local_server_names[i], sizeof(ui_local_server_names[i]), NO_SERVER_STRING);
// ui_serverlist_names[i] = ui_local_server_names[i];
}
}
/*
=======================================================================
START SERVER MAP LIST
=======================================================================
*/
gametype_names_t gametype_names[] =
{
{MAP_DM, "dm ffa team teamdm"},
{MAP_COOP, "coop"},
{MAP_CTF, "ctf"},
{MAP_3TCTF, "3tctf"},
};
maptype_t ui_svr_maptype;
static int ui_svr_nummaps;
char **ui_svr_mapnames;
static int ui_svr_maplist_sizes[NUM_MAPTYPES] = {0, 0, 0, 0};
static char **ui_svr_maplists[NUM_MAPTYPES] = {NULL, NULL, NULL, NULL};
int ui_svr_listfile_nummaps;
static char **ui_svr_listfile_mapnames;
static int ui_svr_arena_nummaps[NUM_MAPTYPES];
static char **ui_svr_arena_mapnames[NUM_MAPTYPES];
//static byte *ui_svr_mapshotvalid; // levelshot truth table
static byte *ui_svr_mapshotvalid[NUM_MAPTYPES] = {NULL, NULL, NULL, NULL}; // levelshot truth tables
char ui_startserver_shotname [MAX_QPATH];
qboolean ui_svr_coop = false;
qboolean ui_svr_ctf = false;
void UI_BuildStartSeverLevelshotTables (void);
void UI_FreeStartSeverLevelshotTables (void);
/*
===============
UI_ParseArenaFromFile
Partially from Q3 source
===============
*/
qboolean UI_ParseArenaFromFile (char *filename, char *shortname, char *longname, char *gametypes, size_t bufSize)
{
int len;
fileHandle_t f;
char buf[MAX_ARENAS_TEXT];
char *s, *token, *dest;
len = FS_FOpenFile (filename, &f, FS_READ);
if (!f) {
Com_Printf (S_COLOR_RED "UI_ParseArenaFromFile: file not found: %s\n", filename);
return false;
}
if (len >= MAX_ARENAS_TEXT) {
Com_Printf (S_COLOR_RED "UI_ParseArenaFromFile: file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT);
FS_FCloseFile (f);
return false;
}
FS_Read (buf, len, f);
buf[len] = 0;
FS_FCloseFile (f);
s = buf;
// get the opening curly brace
token = COM_Parse (&s);
if (!token) {
Com_Printf ("UI_ParseArenaFromFile: unexpected EOF\n");
return false;
}
if (token[0] != '{') {
Com_Printf ("UI_ParseArenaFromFile: found %s when expecting {\n", token);
return false;
}
// go through all the parms
while (s < (buf + len))
{
dest = NULL;
token = COM_Parse (&s);
if (token && (token[0] == '}')) break;
if (!token || !s) {
Com_Printf ("UI_ParseArenaFromFile: EOF without closing brace\n");
break;
}
if (!Q_strcasecmp(token, "map"))
dest = shortname;
else if (!Q_strcasecmp(token, "longname"))
dest = longname;
else if (!Q_strcasecmp(token, "type"))
dest = gametypes;
if (dest)
{
token = COM_Parse (&s);
if (!token) {
Com_Printf ("UI_ParseArenaFromFile: unexpected EOF\n");
return false;
}
if (token[0] == '}') {
Com_Printf ("UI_ParseArenaFromFile: closing brace without data\n");
break;
}
if (!s) {
Com_Printf ("UI_ParseArenaFromFile: EOF without closing brace\n");
break;
}
// strncpy(dest, token);
Q_strncpyz (dest, bufSize, token);
}
}
if (!shortname || !strlen(shortname)) {
Com_Printf (S_COLOR_RED "UI_ParseArenaFromFile: %s: map field not found\n", filename);
return false;
}
if (!strlen(longname))
longname = shortname;
return true;
}
/*
===============
UI_SortArenas
===============
*/
void UI_SortArenas (char **list, int len)
{
int i, j;
char *temp, *s1, *s2;
qboolean moved;
if (!list || len < 2)
return;
for (i=(len-1); i>0; i--)
{
moved = false;
for (j=0; j<i; j++)
{
if (!list[j]) break;
s1 = strchr(list[j], '\n')+1;
s2 = strchr(list[j+1], '\n')+1;
if (Q_stricmp(s1, s2) > 0)
{
temp = list[j];
list[j] = list[j+1];
list[j+1] = temp;
moved = true;
}
}
if (!moved) break; // done sorting
}
}
/*
===============
UI_LoadArenas
===============
*/
void UI_LoadArenas (void)
{
char *p, *s, *s2, *tok, *tok2;
char **arenafiles = NULL;
char **tmplist = NULL;
char *path = NULL;
// char findName[1024];
char shortname[MAX_TOKEN_CHARS];
char longname[MAX_TOKEN_CHARS];
char gametypes[MAX_TOKEN_CHARS];
char scratch[200];
int i, j, len, narenas = 0, narenanames = 0;
size_t nameSize;
qboolean type_supported[NUM_MAPTYPES];
//
// free existing lists and malloc new ones
//
for (i=0; i<NUM_MAPTYPES; i++)
{
if (ui_svr_arena_mapnames[i])
FS_FreeFileList (ui_svr_arena_mapnames[i], ui_svr_arena_nummaps[i]);
ui_svr_arena_nummaps[i] = 0;
ui_svr_arena_mapnames[i] = malloc( sizeof( char * ) * MAX_ARENAS );
memset( ui_svr_arena_mapnames[i], 0, sizeof( char * ) * MAX_ARENAS );
}
tmplist = malloc( sizeof( char * ) * MAX_ARENAS );
memset( tmplist, 0, sizeof( char * ) * MAX_ARENAS );
#if 1
arenafiles = FS_GetFileList ("scripts", "arena", &narenas);
// arenafiles = FS_GetFileList ("scripts/*.arena", NULL, &narenas);
for (i = 0; i < narenas && narenanames < MAX_ARENAS; i++)
{
if (!arenafiles || !arenafiles[i])
continue;
len = (int)strlen(arenafiles[i]);
if ( strcmp(arenafiles[i]+max(len-6,0), ".arena") )
continue;
p = arenafiles[i];
if (!FS_ItemInList(p, narenanames, tmplist)) // check if already in list
{
if (UI_ParseArenaFromFile (p, shortname, longname, gametypes, MAX_TOKEN_CHARS))
{
// Com_sprintf(scratch, sizeof(scratch), MAPLIST_FORMAT, longname, shortname);
Com_sprintf(scratch, sizeof(scratch), "%s\n%s", longname, shortname);
for (j=0; j<NUM_MAPTYPES; j++)
type_supported[j] = false;
s = gametypes;
tok = strdup(COM_Parse (&s));
while (s != NULL)
{
for (j=0; j<NUM_MAPTYPES; j++)
{
s2 = gametype_names[j].tokens;
tok2 = COM_Parse (&s2);
while (s2 != NULL) {
if ( !Q_strcasecmp(tok, tok2) )
type_supported[j] = true;
tok2 = COM_Parse (&s2);
}
}
if (tok) free (tok);
tok = strdup(COM_Parse(&s));
}
if (tok) free (tok);
for (j=0; j<NUM_MAPTYPES; j++)
if (type_supported[j]) {
nameSize = strlen(scratch) + 1;
ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]] = malloc(nameSize);
// strncpy(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], scratch);
Q_strncpyz (ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], nameSize, scratch);
ui_svr_arena_nummaps[j]++;
}
// Com_Printf ("UI_LoadArenas: successfully loaded arena file %s: mapname: %s levelname: %s gametypes: %s\n", p, shortname, longname, gametypes);
narenanames++;
FS_InsertInList(tmplist, p, narenanames, 0); // add to list
}
}
}
#else
//
// search in searchpaths for .arena files
//
path = FS_NextPath (path);
while (path)
{
Com_sprintf (findName, sizeof(findName), "%s/scripts/*.arena", path);
arenafiles = FS_ListFiles(findName, &narenas, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
for (i=0; i < narenas && narenanames < MAX_ARENAS; i++)
{
if (!arenafiles || !arenafiles[i])
continue;
len = (int)strlen(arenafiles[i]);
if ( strcmp(arenafiles[i]+max(len-6,0), ".arena") )
continue;
p = arenafiles[i] + strlen(path) + 1; // skip over path and next slash
if (!FS_ItemInList(p, narenanames, tmplist)) // check if already in list
{
if (UI_ParseArenaFromFile (p, shortname, longname, gametypes, MAX_TOKEN_CHARS))
{
Com_sprintf(scratch, sizeof(scratch), "%s\n%s", longname, shortname);
for (j=0; j<NUM_MAPTYPES; j++)
type_supported[j] = false;
s = gametypes;
tok = strdup(COM_Parse (&s));
while (s != NULL)
{
for (j=0; j<NUM_MAPTYPES; j++)
{
s2 = gametype_names[j].tokens;
tok2 = COM_Parse (&s2);
while (s2 != NULL) {
if ( !Q_strcasecmp(tok, tok2) )
type_supported[j] = true;
tok2 = COM_Parse (&s2);
}
}
if (tok) free (tok);
tok = strdup(COM_Parse(&s));
}
if (tok) free (tok);
for (j=0; j<NUM_MAPTYPES; j++)
if (type_supported[j]) {
nameSize = strlen(scratch) + 1;
ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]] = malloc(nameSize);
// strncpy(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], scratch);
Q_strncpyz(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], nameSize, scratch);
ui_svr_arena_nummaps[j]++;
}
// Com_Printf ("UI_LoadArenas: successfully loaded arena file %s: mapname: %s levelname: %s gametypes: %s\n", p, shortname, longname, gametypes);
narenanames++;
FS_InsertInList(tmplist, p, narenanames, 0); // add to list
}
}
}
if (narenas)
FS_FreeFileList (arenafiles, narenas);
path = FS_NextPath (path);
}
//
// check in paks for .arena files
//
if (arenafiles = FS_ListPak("scripts/", &narenas))
{
for (i=0; i<narenas && narenanames<MAX_ARENAS; i++)
{
if (!arenafiles || !arenafiles[i])
continue;
len = (int)strlen(arenafiles[i]);
if ( strcmp(arenafiles[i]+max(len-6,0), ".arena") )
continue;
p = arenafiles[i];
if (!FS_ItemInList(p, narenanames, tmplist)) // check if already in list
{
if (UI_ParseArenaFromFile (p, shortname, longname, gametypes, MAX_TOKEN_CHARS))
{
Com_sprintf(scratch, sizeof(scratch), "%s\n%s", longname, shortname);
for (j=0; j<NUM_MAPTYPES; j++)
type_supported[j] = false;
s = gametypes;
tok = strdup(COM_Parse (&s));
while (s != NULL)
{
for (j=0; j<NUM_MAPTYPES; j++)
{
s2 = gametype_names[j].tokens;
tok2 = COM_Parse (&s2);
while (s2 != NULL) {
if ( !Q_strcasecmp(tok, tok2) )
type_supported[j] = true;
tok2 = COM_Parse (&s2);
}
}
if (tok) free (tok);
tok = strdup(COM_Parse(&s));
}
if (tok) free (tok);
for (j=0; j<NUM_MAPTYPES; j++)
if (type_supported[j]) {
nameSize = strlen(scratch) + 1;
ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]] = malloc(nameSize);
// strncpy(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], scratch);
Q_strncpyz(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], nameSize, scratch);
ui_svr_arena_nummaps[j]++;
}
//Com_Printf ("UI_LoadArenas: successfully loaded arena file %s: mapname: %s levelname: %s gametypes: %s\n", p, shortname, longname, gametypes);
narenanames++;
FS_InsertInList(tmplist, strdup(p), narenanames, 0); // add to list
}
}
}
}
#endif
if (narenas)
FS_FreeFileList (arenafiles, narenas);
if (narenanames)
FS_FreeFileList (tmplist, narenanames);
for (i=0; i<NUM_MAPTYPES; i++)
UI_SortArenas (ui_svr_arena_mapnames[i], ui_svr_arena_nummaps[i]);
// Com_Printf ("UI_LoadArenas: loaded %i arena file(s)\n", narenanames);
// for (i=0; i<NUM_MAPTYPES; i++)
// Com_Printf ("%s: %i arena file(s)\n", gametype_names[i].tokens, ui_svr_arena_nummaps[i]);
}
/*
===============
UI_LoadMapList
===============
*/
void UI_LoadMapList (void)
{
char *buffer, *s;
char mapsname[1024];
int i, j, length;
size_t nameSize;
FILE *fp;
//
// free existing list
//
if (ui_svr_listfile_mapnames)
FS_FreeFileList (ui_svr_listfile_mapnames, ui_svr_listfile_nummaps);
ui_svr_listfile_nummaps = 0;
//
// load the list of map names
//
Com_sprintf( mapsname, sizeof( mapsname ), "%s/maps.lst", FS_Gamedir() ); // FIXME: should this be FS_Savegamedir()?
if ( ( fp = fopen( mapsname, "rb" ) ) == 0 )
{
if ( ( length = FS_LoadFile( "maps.lst", ( void ** ) &buffer ) ) == -1 )
Com_Error( ERR_DROP, "couldn't find maps.lst\n" );
}
else
{
#ifdef _WIN32
length = filelength( fileno( fp ) );
#else
fseek(fp, 0, SEEK_END);
length = ftell(fp);
fseek(fp, 0, SEEK_SET);
#endif
buffer = malloc( length );
fread( buffer, length, 1, fp );
}
s = buffer;
i = 0;
while (i < length)
{
if (s[i] == '\r')
ui_svr_listfile_nummaps++;
i++;
}
if (ui_svr_listfile_nummaps == 0)
{ // hack in a default map list
ui_svr_listfile_nummaps = 1;
buffer = "base1 \"Outer Base\"\n";
}
ui_svr_listfile_mapnames = malloc( sizeof( char * ) * ( ui_svr_listfile_nummaps + 1 ) );
memset( ui_svr_listfile_mapnames, 0, sizeof( char * ) * ( ui_svr_listfile_nummaps + 1 ) );
s = buffer;
for (i = 0; i < ui_svr_listfile_nummaps; i++)
{
char shortname[MAX_TOKEN_CHARS];
char longname[MAX_TOKEN_CHARS];
char scratch[200];
// strncpy( shortname, COM_Parse( &s ) );
// strncpy( longname, COM_Parse( &s ) );
Q_strncpyz (shortname, sizeof(shortname), COM_Parse(&s));
Q_strncpyz (longname, sizeof(longname), COM_Parse(&s));
Com_sprintf (scratch, sizeof( scratch ), "%s\n%s", longname, shortname);
nameSize = strlen(scratch) + 1;
ui_svr_listfile_mapnames[i] = malloc( nameSize );
// strncpyz( ui_svr_listfile_mapnames[i], scratch );
Q_strncpyz (ui_svr_listfile_mapnames[i], nameSize, scratch);
}
ui_svr_listfile_mapnames[ui_svr_listfile_nummaps] = 0;
if ( fp != 0 )
{
fp = 0;
free( buffer );
}
else
FS_FreeFile( buffer );
UI_LoadArenas ();
// build composite map lists
for (i=0; i<NUM_MAPTYPES; i++)
{
if (ui_svr_maplists[i]) {
free (ui_svr_maplists[i]);
}
ui_svr_maplists[i] = NULL;
ui_svr_maplist_sizes[i] = ui_svr_listfile_nummaps + ui_svr_arena_nummaps[i];
ui_svr_maplists[i] = malloc( sizeof( char * ) * (ui_svr_maplist_sizes[i] + 1) );
memset( ui_svr_maplists[i], 0, sizeof( char * ) * (ui_svr_maplist_sizes[i] + 1) );
for (j = 0; j < ui_svr_maplist_sizes[i]; j++)
{
if (j < ui_svr_listfile_nummaps)
ui_svr_maplists[i][j] = ui_svr_listfile_mapnames[j];
else
ui_svr_maplists[i][j] = ui_svr_arena_mapnames[i][j-ui_svr_listfile_nummaps];
}
}
ui_svr_maptype = MAP_DM; // init maptype
ui_svr_mapnames = ui_svr_maplists[ui_svr_maptype];
UI_BuildStartSeverLevelshotTables ();
}
/*
===============
UI_FreeMapList
===============
*/
void UI_FreeMapList (void)
{
int i;
//
// free composite lists
//
for (i=0; i<NUM_MAPTYPES; i++)
{
if (ui_svr_maplists[i]) {
free (ui_svr_maplists[i]);
}
ui_svr_maplists[i] = NULL;
ui_svr_maplist_sizes[i] = 0;
}
ui_svr_mapnames = NULL;
//
// free list from file
//
if (ui_svr_listfile_mapnames) {
FS_FreeFileList (ui_svr_listfile_mapnames, ui_svr_listfile_nummaps);
}
ui_svr_listfile_mapnames = NULL;
ui_svr_listfile_nummaps = 0;
//
// free arena lists
//
for (i=0; i<NUM_MAPTYPES; i++)
{
if (ui_svr_arena_mapnames[i]) {
FS_FreeFileList (ui_svr_arena_mapnames[i], ui_svr_arena_nummaps[i]);
}
ui_svr_arena_mapnames[i] = NULL;
ui_svr_arena_nummaps[i] = 0;
}
UI_FreeStartSeverLevelshotTables ();
}
/*
===============
UI_UpdateMapList
===============
*/
void UI_UpdateMapList (maptype_t maptype)
{
ui_svr_maptype = maptype;
ui_svr_mapnames = ui_svr_maplists[ui_svr_maptype];
}
/*
===============
UI_BuildStartSeverLevelshotTables
===============
*/
void UI_BuildStartSeverLevelshotTables (void)
{
int i;
for (i=0; i<NUM_MAPTYPES; i++)
{ // free existing list
if (ui_svr_mapshotvalid[i])
free(ui_svr_mapshotvalid[i]);
// alloc and zero new list
ui_svr_mapshotvalid[i] = malloc( sizeof( byte ) * ( ui_svr_maplist_sizes[i] + 1 ) );
memset( ui_svr_mapshotvalid[i], 0, sizeof( byte ) * ( ui_svr_maplist_sizes[i] + 1 ) );
// register null levelshot
if (ui_svr_mapshotvalid[i][ui_svr_maplist_sizes[i]] == M_UNSET) {
if (R_DrawFindPic(UI_NOSCREEN_NAME))
ui_svr_mapshotvalid[i][ui_svr_maplist_sizes[i]] = M_FOUND;
else
ui_svr_mapshotvalid[i][ui_svr_maplist_sizes[i]] = M_MISSING;
}
}
}
/*
===============
UI_FreeStartSeverLevelshotTables
===============
*/
void UI_FreeStartSeverLevelshotTables (void)
{
int i;
for (i=0; i<NUM_MAPTYPES; i++)
{
if (ui_svr_mapshotvalid[i])
free(ui_svr_mapshotvalid[i]);
}
}
/*
===============
UI_UpdateStartSeverLevelshot
===============
*/
char *UI_UpdateStartSeverLevelshot (int index)
{
char startmap[MAX_QPATH];
char mapshotname [MAX_QPATH];
// check index
if (index < 0 || index >= ui_svr_maplist_sizes[ui_svr_maptype])
return NULL;
Q_strncpyz (startmap, sizeof(startmap), strchr( ui_svr_maplists[ui_svr_maptype][index], '\n' ) + 1);
if (ui_svr_mapshotvalid[ui_svr_maptype][index] == M_UNSET) { // init levelshot
Com_sprintf(mapshotname, sizeof(mapshotname), "/levelshots/%s.pcx", startmap);
if (R_DrawFindPic(mapshotname))
ui_svr_mapshotvalid[ui_svr_maptype][index] = M_FOUND;
else
ui_svr_mapshotvalid[ui_svr_maptype][index] = M_MISSING;
}
if (ui_svr_mapshotvalid[ui_svr_maptype][index] == M_FOUND)
Com_sprintf(ui_startserver_shotname, sizeof(ui_startserver_shotname), "/levelshots/%s.pcx", startmap);
else if (ui_svr_mapshotvalid[ui_svr_maptype][ui_svr_maplist_sizes[ui_svr_maptype]] == M_FOUND)
Com_sprintf(ui_startserver_shotname, sizeof(ui_startserver_shotname), UI_NOSCREEN_NAME);
else
return NULL;
return ui_startserver_shotname;
}
/*
===============
UI_SetCoopMenuMode
===============
*/
void UI_SetCoopMenuMode (qboolean value)
{
ui_svr_coop = value;
}
/*
===============
UI_Coop_MenuMode
===============
*/
qboolean UI_Coop_MenuMode (void)
{
return ui_svr_coop;
}
/*
===============
UI_CanOpenDMFlagsMenu
===============
*/
qboolean UI_CanOpenDMFlagsMenu (void *unused)
{
return !ui_svr_coop;
}
/*
===============
UI_SetCTFMenuMode
===============
*/
void UI_SetCTFMenuMode (qboolean value)
{
ui_svr_ctf = value;
}
/*
===============
UI_CTF_MenuMode
===============
*/
qboolean UI_CTF_MenuMode (void)
{
return ui_svr_ctf;
}
/*
=======================================================================
PLAYER MODEL LOADING
=======================================================================
*/
playermodelinfo_s ui_pmi[MAX_PLAYERMODELS];
char *ui_pmnames[MAX_PLAYERMODELS];
int ui_numplayermodels;
// save skins and models here so as to not have to re-register every frame
struct model_s *ui_playermodel;
struct model_s *ui_weaponmodel;
struct image_s *ui_playerskin;
char *ui_currentweaponmodel;
//char ui_playerconfig_playermodelname[MAX_QPATH];
//char ui_playerconfig_playerskinname[MAX_QPATH];
//char ui_playerconfig_weaponmodelname[MAX_QPATH];
/*
==========================
UI_IsSkinIcon
==========================
*/
static qboolean UI_IsSkinIcon (char *name)
{
int len;
char *s, scratch[1024];
// strncpy(scratch, name);
Q_strncpyz(scratch, sizeof(scratch), name);
*strrchr(scratch, '.') = 0;
s = scratch;
len = (int)strlen(s);
return (!strcmp(s+max(len-2,0), "_i"));
}
/*
==========================
UI_IconOfSkinExists
==========================
*/
static qboolean UI_IconOfSkinExists (char *skin, char **files, int nfiles, char *suffix)
{
int i;
char scratch[1024];
// strncpy(scratch, skin);
Q_strncpyz (scratch, sizeof(scratch), skin);
*strrchr(scratch, '.') = 0;
// strncat(scratch, suffix);
Q_strncatz (scratch, sizeof(scratch), suffix);
// strncat(scratch, "_i.pcx");
for (i = 0; i < nfiles; i++)
{
if ( strcmp(files[i], scratch) == 0 )
return true;
}
return false;
}
/*
==========================
UI_IsValidSkin
Adds menu support for TGA and JPG skins
==========================
*/
static qboolean UI_IsValidSkin (char **filelist, int numFiles, int index)
{
if ( UI_IsValidImageFilename(filelist[index]) && !UI_IsSkinIcon(filelist[index]) )
{
if ( UI_IconOfSkinExists (filelist[index], filelist, numFiles-1 , "_i.pcx")
|| UI_IconOfSkinExists (filelist[index], filelist, numFiles-1 , "_i.tga")
#ifdef PNG_SUPPORT
|| UI_IconOfSkinExists (filelist[index], filelist, numFiles-1 , "_i.png")
#endif // PNG_SUPPORT
|| UI_IconOfSkinExists (filelist[index], filelist, numFiles-1 , "_i.jpg"))
return true;
}
return false;
}
/*
==========================
UI_PlayerConfig_ScanDirectories
==========================
*/
static qboolean UI_PlayerConfig_ScanDirectories (void)
{
char findname[1024];
char scratch[1024];
int ndirs = 0, npms = 0;
char **dirnames;
char *path = NULL;
int i;
ui_numplayermodels = 0;
// loop back to here if there were no valid player models found in the selected path
do
{
//
// get a list of directories
//
do
{
path = FS_NextPath(path);
Com_sprintf( findname, sizeof(findname), "%s/players/*.*", path );
if ( (dirnames = FS_ListFiles(findname, &ndirs, SFF_SUBDIR, 0)) != 0 )
break;
} while (path);
if (!dirnames)
return false;
//
// go through the subdirectories
//
npms = ndirs;
if (npms > MAX_PLAYERMODELS)
npms = MAX_PLAYERMODELS;
if ( (ui_numplayermodels + npms) > MAX_PLAYERMODELS )
npms = MAX_PLAYERMODELS - ui_numplayermodels;
for (i = 0; i < npms; i++)
{
int k, s;
char *a, *b, *c;
char **skinnames;
char **skiniconnames;
char **imagenames;
int nimagefiles;
int nskins = 0;
qboolean already_added = false;
if (dirnames[i] == 0)
continue;
// check if dirnames[i] is already added to the ui_pmi[i].directory list
a = strrchr(dirnames[i], '/');
b = strrchr(dirnames[i], '\\');
c = (a > b) ? a : b;
for (k=0; k < ui_numplayermodels; k++)
if (!strcmp(ui_pmi[k].directory, c+1))
{ already_added = true; break; }
if (already_added)
{ // todo: add any skins for this model not already listed to skinDisplayNames
continue;
}
// verify the existence of tris.md2
// strncpy(scratch, dirnames[i]);
// strncat(scratch, "/tris.md2");
Q_strncpyz(scratch, sizeof(scratch), dirnames[i]);
Q_strncatz(scratch, sizeof(scratch), "/tris.md2");
if ( !Sys_FindFirst(scratch, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) )
{
free(dirnames[i]);
dirnames[i] = 0;
Sys_FindClose();
continue;
}
Sys_FindClose();
// verify the existence of at least one skin
// strncpy(scratch, va("%s%s", dirnames[i], "/*.*")); // was "/*.pcx"
Q_strncpyz(scratch, sizeof(scratch), va("%s%s", dirnames[i], "/*.*")); // was "/*.pcx"
imagenames = FS_ListFiles (scratch, &nimagefiles, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
if (!imagenames)
{
free(dirnames[i]);
dirnames[i] = 0;
continue;
}
// count valid skins, which consist of a skin with a matching "_i" icon
for (k = 0; k < nimagefiles-1; k++)
if ( UI_IsValidSkin(imagenames, nimagefiles, k) )
nskins++;
if (!nskins)
continue;
// make short name for the model
a = strrchr(dirnames[i], '/');
b = strrchr(dirnames[i], '\\');
c = (a > b) ? a : b;
// strncpy(ui_pmi[ui_numplayermodels].displayname, c+1, MAX_DISPLAYNAME-1);
// strncpy(ui_pmi[ui_numplayermodels].directory, c+1);
Q_strncpyz(ui_pmi[ui_numplayermodels].displayname, sizeof(ui_pmi[ui_numplayermodels].displayname), c+1);
Q_strncpyz(ui_pmi[ui_numplayermodels].directory, sizeof(ui_pmi[ui_numplayermodels].directory), c+1);
skinnames = malloc(sizeof(char *) * (nskins+1));
memset(skinnames, 0, sizeof(char *) * (nskins+1));
skiniconnames = malloc(sizeof(char *) * (nskins+1));
memset(skiniconnames, 0, sizeof(char *) * (nskins+1));
// copy the valid skins
if (nimagefiles)
for (s = 0, k = 0; k < nimagefiles-1; k++)
{
char *a, *b, *c;
if ( UI_IsValidSkin(imagenames, nimagefiles, k) )
{
a = strrchr(imagenames[k], '/');
b = strrchr(imagenames[k], '\\');
c = (a > b) ? a : b;
// strncpy(scratch, c+1);
Q_strncpyz(scratch, sizeof(scratch), c+1);
if ( strrchr(scratch, '.') )
*strrchr(scratch, '.') = 0;
skinnames[s] = strdup(scratch);
skiniconnames[s] = strdup(va("/players/%s/%s_i.pcx", ui_pmi[ui_numplayermodels].directory, scratch));
s++;
}
}
// at this point we have a valid player model
ui_pmi[ui_numplayermodels].nskins = nskins;
ui_pmi[ui_numplayermodels].skinDisplayNames = skinnames;
ui_pmi[ui_numplayermodels].skinIconNames = skiniconnames;
// make short name for the model
// a = strrchr(dirnames[i], '/');
// b = strrchr(dirnames[i], '\\');
// c = (a > b) ? a : b;
// Q_strncpyz(ui_pmi[ui_numplayermodels].displayname, sizeof(ui_pmi[ui_numplayermodels].displayname), c+1);
// Q_strncpyz(ui_pmi[ui_numplayermodels].directory, sizeof(ui_pmi[ui_numplayermodels].directory), c+1);
FS_FreeFileList (imagenames, nimagefiles);
ui_numplayermodels++;
}
if (dirnames)
FS_FreeFileList (dirnames, ndirs);
// if no valid player models found in path,
// try next path, if there is one
} while (path); // (s_numplayermodels == 0 && path);
return true; //** DMP warning fix
}
/*
==========================
UI_LoadPlayerModels
==========================
*/
void UI_LoadPlayerModels (void)
{
UI_PlayerConfig_ScanDirectories ();
}
/*
==========================
UI_FreePlayerModels
==========================
*/
void UI_FreePlayerModels (void)
{
int i;
for (i = 0; i < ui_numplayermodels; i++)
{
int j;
for (j = 0; j < ui_pmi[i].nskins; j++)
{
if (ui_pmi[i].skinDisplayNames[j])
free(ui_pmi[i].skinDisplayNames[j]);
ui_pmi[i].skinDisplayNames[j] = NULL;
}
free(ui_pmi[i].skinDisplayNames);
ui_pmi[i].skinDisplayNames = NULL;
ui_pmi[i].nskins = 0;
}
}
/*
==========================
UI_PlayerModelCmpFunc
==========================
*/
int UI_PlayerModelCmpFunc (const void *_a, const void *_b)
{
const playermodelinfo_s *a = (const playermodelinfo_s *) _a;
const playermodelinfo_s *b = (const playermodelinfo_s *) _b;
//
// sort by male, female, then alphabetical
//
if ( strcmp(a->directory, "male") == 0 )
return -1;
else if (strcmp( b->directory, "male") == 0 )
return 1;
if ( strcmp(a->directory, "female") == 0 )
return -1;
else if (strcmp( b->directory, "female") == 0 )
return 1;
return strcmp(a->directory, b->directory);
}
/*
==========================
UI_InitPlayerModelInfo
==========================
*/
void UI_InitPlayerModelInfo (int *modelNum, int *skinNum)
{
int i;
int currentdirectoryindex = 0;
int currentskinindex = 0;
char currentdirectory[1024];
char currentskin[1024];
if (ui_numplayermodels <= 0) {
modelNum = skinNum = 0;
return;
}
// strncpy( currentdirectory, Cvar_VariableString ("skin") );
Q_strncpyz(currentdirectory, sizeof(currentdirectory), Cvar_VariableString ("skin"));
if ( strchr( currentdirectory, '/' ) )
{
// strncpy( currentskin, strchr( currentdirectory, '/' ) + 1 );
Q_strncpyz(currentskin, sizeof(currentskin), strchr( currentdirectory, '/' ) + 1);
*strchr( currentdirectory, '/' ) = 0;
}
else if ( strchr( currentdirectory, '\\' ) )
{
// strncpy( currentskin, strchr( currentdirectory, '\\' ) + 1 );
Q_strncpyz(currentskin, sizeof(currentskin), strchr( currentdirectory, '\\' ) + 1);
*strchr( currentdirectory, '\\' ) = 0;
}
else
{
// strncpy( currentdirectory, "male" );
// strncpy( currentskin, "grunt" );
Q_strncpyz(currentdirectory, sizeof(currentdirectory), "male");
Q_strncpyz(currentskin, sizeof(currentskin), "grunt");
}
qsort( ui_pmi, ui_numplayermodels, sizeof( ui_pmi[0] ), UI_PlayerModelCmpFunc );
memset( ui_pmnames, 0, sizeof( ui_pmnames ) );
for (i = 0; i < ui_numplayermodels; i++)
{
ui_pmnames[i] = ui_pmi[i].displayname;
if (Q_stricmp( ui_pmi[i].directory, currentdirectory ) == 0)
{
int j;
currentdirectoryindex = i;
for (j = 0; j < ui_pmi[i].nskins; j++)
{
if (Q_stricmp( ui_pmi[i].skinDisplayNames[j], currentskin ) == 0)
{
currentskinindex = j;
break;
}
}
}
}
// precache this player model and skin
UI_UpdatePlayerModelInfo (currentdirectoryindex, currentskinindex);
if (modelNum)
*modelNum = currentdirectoryindex;
if (skinNum)
*skinNum = currentskinindex;
}
/*
==========================
UI_UpdatePlayerModelInfo
==========================
*/
void UI_UpdatePlayerModelInfo (int mNum, int sNum)
{
char scratch[MAX_QPATH];
Com_sprintf( scratch, sizeof(scratch), "players/%s/tris.md2", ui_pmi[mNum].directory );
ui_playermodel = R_RegisterModel (scratch);
// Q_strncpyz (ui_playerconfig_playermodelname, sizeof(ui_playerconfig_playermodelname), scratch);
Com_sprintf( scratch, sizeof(scratch), "players/%s/%s.pcx", ui_pmi[mNum].directory, ui_pmi[mNum].skinDisplayNames[sNum] );
ui_playerskin = R_RegisterSkin (scratch);
// Q_strncpyz (ui_playerconfig_playerskinname, sizeof(ui_playerconfig_playerskinname), scratch);
// show current weapon model (if any)
if (ui_currentweaponmodel && strlen(ui_currentweaponmodel))
{
Com_sprintf (scratch, sizeof(scratch), "players/%s/%s", ui_pmi[mNum].directory, ui_currentweaponmodel);
ui_weaponmodel = R_RegisterModel(scratch);
if (!ui_weaponmodel) {
Com_sprintf (scratch, sizeof(scratch), "players/%s/weapon.md2", ui_pmi[mNum].directory);
ui_weaponmodel = R_RegisterModel (scratch);
}
}
else {
Com_sprintf (scratch, sizeof(scratch), "players/%s/weapon.md2", ui_pmi[mNum].directory);
ui_weaponmodel = R_RegisterModel (scratch);
}
// Q_strncpyz (ui_playerconfig_weaponmodelname, sizeof(ui_playerconfig_weaponmodelname), scratch);
}
/*
==========================
UI_UpdatePlayerSkinInfo
==========================
*/
void UI_UpdatePlayerSkinInfo (int mNum, int sNum)
{
char scratch[MAX_QPATH];
Com_sprintf(scratch, sizeof(scratch), "players/%s/%s.pcx", ui_pmi[mNum].directory, ui_pmi[mNum].skinDisplayNames[sNum]);
ui_playerskin = R_RegisterSkin(scratch);
// Q_strncpyz (ui_playerconfig_playerskinname, sizeof(ui_playerconfig_playerskinname), scratch);
}
/*
==========================
UI_HaveValidPlayerModels
==========================
*/
qboolean UI_HaveValidPlayerModels (void *unused)
{
return (ui_numplayermodels > 0);
}