thirtyflightsofloving/ui/ui_utils.c

2862 lines
67 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;
}
/*
==========================
UI_ClampCvar
==========================
*/
void UI_ClampCvar (const char *varName, float cvarMin, float cvarMax)
{
Cvar_SetValue ((char *)varName, ClampCvar( cvarMin, cvarMax, Cvar_VariableValue((char *)varName) ));
}
/*
==========================
UI_GetIndexForStringValue
==========================
*/
int UI_GetIndexForStringValue (const char **item_values, char *value)
{
int i, index = 0, widlcardIndex = -1;
qboolean found = false;
// catch null array
if (!item_values) {
Com_Printf ("UI_GetIndexForStringValue: null itemValues!\n");
return 0;
}
for (i=0; item_values[i]; i++)
{ // Store index of wildcard entry
if ( !Q_stricmp(va("%s", item_values[i]), UI_ITEMVALUE_WILDCARD) )
{ widlcardIndex = i; }
if ( !Q_strcasecmp(va("%s", item_values[i]), value) ) {
index = i;
found = true;
break;
}
}
// Assign index of wildcard entry if not found
if ( !found && (widlcardIndex >= 0) ) {
index = widlcardIndex;
}
return index;
}
/*
==========================
UI_MouseOverAlpha
==========================
*/
int UI_MouseOverAlpha (menucommon_s *m)
{
if (ui_mousecursor.menuitem == m)
{
int alpha;
#ifdef NOTTHIRTYFLIGHTS
alpha = 125 + 25 * cos(anglemod(cl.time*0.005));
if (alpha>255) alpha = 255;
if (alpha<0) alpha = 0;
#else
alpha = 255;
#endif
return alpha;
}
else
return 255;
}
/*
==========================
UI_UnbindCommand
==========================
*/
void UI_UnbindCommand (char *command)
{
int j, len, len2;
char *b;
len = (int)strlen(command);
for (j=0; j<256; j++)
{
b = keybindings[j];
if (!b)
continue;
len2 = (int)strlen(b);
// compare with longer length to prevent +attack2 being confused with +attack
if ( !strncmp(b, command, max(len, len2)) )
Key_SetBinding (j, "");
}
}
/*
==========================
UI_FindKeysForCommand
==========================
*/
void UI_FindKeysForCommand (char *command, int *twokeys)
{
int count, j, len, len2;
char *b;
twokeys[0] = twokeys[1] = -1;
len = (int)strlen(command);
count = 0;
for (j=0; j<256; j++)
{
b = keybindings[j];
if (!b)
continue;
len2 = (int)strlen(b);
// compare with longer length to prevent +attack2 being confused with +attack
if ( !strncmp(b, command, max(len, len2)) )
{
twokeys[count] = j;
count++;
if (count == 2)
break;
}
}
}
/*
=================
UI_ItemAtMenuCursor
=================
*/
void *UI_ItemAtMenuCursor (menuframework_s *m)
{
if (m->cursor < 0 || m->cursor >= m->nitems)
return 0;
return m->items[m->cursor];
}
/*
=================
UI_SetMenuStatusBar
=================
*/
void UI_SetMenuStatusBar (menuframework_s *m, const char *string)
{
if (!m) return;
m->statusbar = string;
}
/*
=================
UI_TallyMenuSlots
=================
*/
int UI_TallyMenuSlots (menuframework_s *menu)
{
int i;
int total = 0;
for (i = 0; i < menu->nitems; i++)
{
if ( ((menucommon_s *)menu->items[i])->type == MTYPE_LIST )
{
int nitems = 0;
const char **n = ((menulist_s *)menu->items[i])->itemNames;
while (*n)
nitems++, n++;
total += nitems;
}
else
total++;
}
return total;
}
/*
=======================================================================
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( "ctf", 0 );
Cvar_SetValue( "ttctf", 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 ();
}
/*
===============
UI_LoadMod
===============
*/
void UI_LoadMod (char *modName)
{
if ( Q_strcasecmp(Cvar_VariableString("game"), modName) ) {
UI_ForceMenuOff ();
Cbuf_AddText (va("changegame %s\n", modName) );
}
}
/*
=======================================================================
VIDEO INFO LOADING
=======================================================================
*/
#define UI_MAX_VIDMODES 128
char **ui_resolution_names = NULL;
char **ui_video_modes = NULL;
int ui_num_video_modes = 0;
/*
==========================
UI_GetVideoModes
==========================
*/
void UI_GetVideoModes (void)
{
int i, j=0, w=0, h=0, firstMode=0, numModes=0;
float aspect;
char *tok, resBuf[12], aspectBuf[8], nameBuf[20];
// qboolean surround = false;
// cvar_t *surround_threshold = Cvar_Get ("scr_surroundthreshold", "3.6", 0);
// count video modes >= 640x480
for (i=0; i<UI_MAX_VIDMODES; i++)
{
if ( !VID_GetModeInfo(&w, &h, i) )
break;
if (w >= 640 && h >= 480) {
numModes++;
if (numModes == 1)
firstMode = i;
}
}
// allocate lists
ui_resolution_names = malloc ((numModes+2) * sizeof(char *));
memset (ui_resolution_names, 0, (numModes+2) * sizeof(char *));
ui_video_modes = malloc ((numModes+2) * sizeof(char *));
memset (ui_video_modes, 0, (numModes+2) * sizeof(char *));
// add custom resolution item
// ui_resolution_names[0] = strdup ("custom ???");
ui_resolution_names[0] = strdup ("[custom ] [ ??? ]");
ui_video_modes[0] = strdup ("-1");
// add to lists
for (i=firstMode, j=1; i<(firstMode+numModes); i++, j++)
{
if ( !VID_GetModeInfo(&w, &h, i) )
break;
if (w >= 640 && h >= 480)
{
aspect = (float)w / (float)h;
memset (resBuf, 0, sizeof(resBuf));
memset (aspectBuf, 0, sizeof(aspectBuf));
memset (nameBuf, 0, sizeof(nameBuf));
Com_sprintf (resBuf, sizeof(resBuf), "%dx%d", w, h);
// catch surround modes
/* if (aspect > surround_threshold->value) { // 3.6f
aspect /= 3.0f;
surround = true;
} */
if (aspect > 2.39f)
tok = "24:10";
else if (aspect > 2.3f)
tok = "21:9";
else if (aspect > 1.9f)
tok = "16:8";
else if (aspect > 1.85f)
tok = "17:10";
else if (aspect > 1.65f)
tok = "16:9";
else if (aspect > 1.6f)
tok = "15:9";
else if (aspect > 1.55f)
tok = "16:10";
else if (aspect > 1.3f)
tok = "4:3";
else if (aspect > 1.2f)
tok = "5:4";
else
tok = va("%3.1f:1", aspect);
/* if (surround)
Com_sprintf (aspectBuf, sizeof(aspectBuf), "3x%s", tok);
else */
Com_sprintf (aspectBuf, sizeof(aspectBuf), "%s", tok);
// Com_sprintf (nameBuf, sizeof(nameBuf), "%-12s%s", resBuf, aspectBuf);
Com_sprintf (nameBuf, sizeof(nameBuf), "[%-9s] [%-5s]", resBuf, aspectBuf);
ui_resolution_names[j] = strdup (nameBuf);
ui_video_modes[j] = strdup (va("%i", i));
}
}
ui_num_video_modes = numModes;
}
/*
==========================
UI_FreeVideoModes
==========================
*/
void UI_FreeVideoModes (void)
{
if (ui_num_video_modes > 0) {
FS_FreeFileList (ui_resolution_names, ui_num_video_modes);
FS_FreeFileList (ui_video_modes, ui_num_video_modes);
}
ui_resolution_names = NULL;
ui_video_modes = NULL;
ui_num_video_modes = 0;
}
char **ui_aniso_names = NULL;
char **ui_aniso_values = NULL;
int ui_num_aniso_values = 0;
/*
==========================
UI_GetAnisoValues
==========================
*/
void UI_GetAnisoValues (void)
{
int i, numValues;
float aniso_avail = Cvar_VariableValue("r_anisotropic_avail");
if (aniso_avail < 2.0)
numValues = 1;
else if (aniso_avail < 4.0)
numValues = 2;
else if (aniso_avail < 8.0)
numValues = 3;
else if (aniso_avail < 16.0)
numValues = 4;
else // >= 16.0
numValues = 5;
// allocate lists
ui_aniso_names = malloc ((numValues+1) * sizeof(char *));
memset (ui_aniso_names, 0, (numValues+1) * sizeof(char *));
ui_aniso_values = malloc ((numValues+1) * sizeof(char *));
memset (ui_aniso_values, 0, (numValues+1) * sizeof(char *));
// set names and values
for (i=0; i<numValues; i++)
{
if (i == 0)
ui_aniso_names[i] = (numValues == 1) ? strdup("not supported") : strdup("off");
else
ui_aniso_names[i] = strdup(va("%ix", 1<<i));
ui_aniso_values[i] = strdup(va("%i", 1<<i));
}
ui_num_aniso_values = numValues;
}
/*
==========================
UI_FreeAnisoValues
==========================
*/
void UI_FreeAnisoValues (void)
{
if (ui_num_aniso_values > 0) {
FS_FreeFileList (ui_aniso_names, ui_num_aniso_values);
FS_FreeFileList (ui_aniso_values, ui_num_aniso_values);
}
ui_aniso_names = NULL;
ui_aniso_values = NULL;
ui_num_aniso_values = 0;
}
/*
==========================
UI_GetVideoInfo
==========================
*/
void UI_GetVideoInfo (void)
{
UI_GetVideoModes ();
UI_GetAnisoValues ();
}
/*
==========================
UI_FreeVideoInfo
==========================
*/
void UI_FreeVideoInfo (void)
{
UI_FreeVideoModes ();
UI_FreeAnisoValues ();
}
/*
=======================================================================
MOD LIST LOADING
=======================================================================
*/
// TODO: Enable this when mod menu is ready
#if 0
#define UI_MAX_MODS 256
char **ui_mod_names = NULL;
char **ui_mod_values = NULL;
qboolean ui_mod_isUnsupported[UI_MAX_MODS];
int ui_num_mods = 0;
/*
==========================
UI_BuildModList
==========================
*/
void UI_BuildModList (void)
{
char findName[1024];
char modDesc[1024];
char modFormatedName[1024];
char **dirnames;
char *modDir, *modName;
FILE *f;
int count = 0, ndirs = 0, nmods = 0;
int i;
qboolean unsupportedMod;
ui_mod_names = malloc(sizeof(char *) * (UI_MAX_MODS+1));
ui_mod_values = malloc(sizeof(char *) * (UI_MAX_MODS+1));
memset(ui_mod_names, 0, sizeof(char *) * (UI_MAX_MODS+1));
memset(ui_mod_values, 0, sizeof(char *) * (UI_MAX_MODS+1));
// add baseq2 first
ui_mod_names[0] = strdup("Quake II (vanilla)");
ui_mod_values[0] = strdup(BASEDIRNAME);
ui_mod_isUnsupported[0] = false;
count++;
// get a list of directories
Com_sprintf(findName, sizeof(findName), "%s/*.*", FS_HomePath());
dirnames = FS_ListFiles (findName, &ndirs, SFF_SUBDIR, 0);
if (!dirnames) {
ui_num_mods = count;
return;
}
// go through the directories
nmods = ndirs;
if (nmods > UI_MAX_MODS)
nmods = UI_MAX_MODS;
if ( (count + nmods) > UI_MAX_MODS )
nmods = UI_MAX_MODS - count;
for (i = 0; i < nmods; i++)
{
if (dirnames[i] == 0)
continue;
modDir = COM_SkipPath(dirnames[i]);
// Ignore baseq2
if ( !Q_strcasecmp(modDir, BASEDIRNAME) )
continue;
// Must have a pak or pk3 file, or a maps dir
if ( !Sys_FindFirst( va("%s/*.pak", dirnames[i]), 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) ) {
Sys_FindClose();
if ( !Sys_FindFirst( va("%s/*.pk3", dirnames[i]), 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) ) {
Sys_FindClose();
if ( !Sys_FindFirst( va("%s/maps", dirnames[i]), SFF_SUBDIR, 0) ) {
Sys_FindClose();
continue;
}
}
}
Sys_FindClose();
// check if this mod has a gamex86.dll/gamei386.so without an equivalent KMQ2 dll/so
unsupportedMod = false;
if ( Sys_FindFirst( va("%s/"STOCK_Q2_GAME_LIBRARY_NAME, dirnames[i]), 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) )
{
Sys_FindClose();
if ( !Sys_FindFirst( va("%s/"KMQ2_GAME_LIBRARY_NAME, dirnames[i]), 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) ) {
Sys_FindClose();
unsupportedMod = true;
// Com_Printf ("UI_BuildModList: mod %s has an unsupported game library.\n", modDir);
}
}
Sys_FindClose();
// try to load description.txt
f = fopen( va("%s/description.txt", dirnames[i]), "rb");
if (f != NULL) {
fgets(modDesc, sizeof(modDesc), f);
fclose(f);
modName = modDesc;
}
else if ( !Q_strcasecmp(modDir, "ctf") )
modName = "Quake II: Capture The Flag";
else if ( !Q_strcasecmp(modDir, "rogue") )
modName = "Quake II: Ground Zero";
else if ( !Q_strcasecmp(modDir, "xatrix") )
modName = "Quake II: The Reckoning";
else if ( !Q_strcasecmp(modDir, "zaero") )
modName = "Zaero Mission Pack";
else if ( !Q_strcasecmp(modDir, "3zb2") )
modName ="3rd Zigrock Bot II";
else if ( !Q_strcasecmp(modDir, "gen") )
modName = "Generations";
else if ( !Q_strcasecmp(modDir, "ra2") )
modName = "Rocket Arena 2";
else if ( !Q_strcasecmp(modDir, "bots") )
modName = "Battle of the Sexes";
else if ( !Q_strcasecmp(modDir, "lmctf") )
modName = "Loki's Minions CTF";
else if ( !Q_strcasecmp(modDir, "wf") )
modName = "Weapons Factory";
else if ( !Q_strcasecmp(modDir, "wod") )
modName = "Weapons of Destruction";
else if ( !Q_strcasecmp(modDir, "rts") )
modName = "Rob the Strogg";
else
modName = modDir;
if (unsupportedMod)
Com_sprintf(modFormatedName, sizeof(modFormatedName), S_COLOR_ORANGE"%s\0", modName);
else
Q_strncpyz(modFormatedName, sizeof(modFormatedName), modName);
if (!FS_ItemInList(modDir, count, ui_mod_values))
{
// FS_InsertInList (ui_mod_names, modName, count, 1); // start=1 so first item stays first!
FS_InsertInList (ui_mod_names, modFormatedName, count, 1); // start=1 so first item stays first!
FS_InsertInList (ui_mod_values, modDir, count, 1); // start=1 so first item stays first!
count++;
ui_mod_isUnsupported[count] = unsupportedMod;
}
}
if (dirnames)
FS_FreeFileList (dirnames, ndirs);
ui_num_mods = count;
}
/*
==========================
UI_GetModList
==========================
*/
void UI_GetModList (void)
{
UI_BuildModList ();
// Com_Printf ("UI_GetModList: found %i mod dirs\n", ui_num_mods);
}
/*
==========================
UI_FreeModList
==========================
*/
void UI_FreeModList (void)
{
if (ui_num_mods > 0) {
FS_FreeFileList (ui_mod_names, ui_num_mods);
FS_FreeFileList (ui_mod_values, ui_num_mods);
}
ui_mod_names = NULL;
ui_mod_values = NULL;
ui_num_mods = 0;
}
#endif
/*
=======================================================================
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;
/*
==========================
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
}
}
/*
==========================
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];
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()
#ifdef NOTTHIRTYFLIGHTS
Com_sprintf (name, sizeof(name), "%s/"SAVEDIRNAME"/kmq2save%03i/server.ssv", FS_Savegamedir(), i); // was FS_Gamedir()
#else
Com_sprintf (name, sizeof(name), "%s/"SAVEDIRNAME"/save%i/server.ssv", FS_Savegamedir(), i); // was FS_Gamedir()
#endif
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);
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);
#ifdef NOTTHIRTYFLIGHTS
Com_sprintf (name, sizeof(name), SAVEDIRNAME"/kmq2save%03i/server.ssv", i);
#else
Com_sprintf (name, sizeof(name), SAVEDIRNAME"/save%i/server.ssv", i);
#endif
FS_FOpenFile (name, &f, FS_READ);
if (!f)
{
//Com_Printf("Save file %s not found.\n", name);
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];
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);
#ifdef NOTTHIRTYFLIGHTS
Com_sprintf(shotname, sizeof(shotname), SAVEDIRNAME"/kmq2save%03i/shot.jpg", i);
#else
Com_sprintf(shotname, sizeof(shotname), SAVEDIRNAME"/save%i/shot.jpg", i);
#endif
R_FreePic (shotname);
// Com_sprintf(shotname, sizeof(shotname), "/save/kmq2save%03i/shot.jpg", i);
#ifdef NOTTHIRTYFLIGHTS
Com_sprintf(shotname, sizeof(shotname), "/"SAVEDIRNAME"/kmq2save%03i/shot.jpg", i);
#else
Com_sprintf(shotname, sizeof(shotname), "/"SAVEDIRNAME"/save%i/shot.jpg", i);
#endif
}
if (R_DrawFindPic(shotname))
ui_saveshotvalid[i] = true;
else
ui_saveshotvalid[i] = false;
}
else
ui_saveshotvalid[i] = 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);
#ifdef NOTTHIRTYFLIGHTS
Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), "/"SAVEDIRNAME"/kmq2save%03i/shot.jpg", index);
#else
Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), "/"SAVEDIRNAME"/save%i/shot.jpg", index);
#endif
}
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);
UI_DrawPopupMessage ("Searching for local servers.\nThis could take up to a minute,\nso please be patient.");
// 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);
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]);
ui_svr_mapshotvalid[i] = NULL;
}
// 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_svr_mapshotvalid[i] = NULL;
}
}
}
/*
===============
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];
color_t ui_player_color_imageColors[] =
{
#include "ui_playercolors.h"
};
// last table entry is null value
#define UI_NUM_PLAYER_COLORS ((sizeof(ui_player_color_imageColors) / sizeof(ui_player_color_imageColors[0])) - 1)
char **ui_player_color_values = NULL;
char **ui_player_color_imageNames = NULL;
int ui_numplayercolors = 0;
*/
/*
==========================
UI_IsSkinIcon
==========================
*/
static qboolean UI_IsSkinIcon (char *name)
{
int len;
char *s, scratch[1024];
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];
Q_strncpyz (scratch, sizeof(scratch), skin);
*strrchr(scratch, '.') = 0;
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
}
#if 0
/*
==========================
UI_BuildPlayerColorList
==========================
*/
void UI_BuildPlayerColorList (void)
{
int i, numColors = 0;
ui_player_color_values = malloc(sizeof(char *) * (UI_NUM_PLAYER_COLORS+1));
ui_player_color_imageNames = malloc(sizeof(char *) * (UI_NUM_PLAYER_COLORS+1));
memset(ui_player_color_values, 0, sizeof(char *) * (UI_NUM_PLAYER_COLORS+1));
memset(ui_player_color_imageNames, 0, sizeof(char *) * (UI_NUM_PLAYER_COLORS+1));
for (i = 0; i < UI_NUM_PLAYER_COLORS; i++)
{ // last index is custom color
if (i == UI_NUM_PLAYER_COLORS-1) {
ui_player_color_values[i] = strdup(UI_ITEMVALUE_WILDCARD);
ui_player_color_imageNames[i] = strdup(UI_CUSTOMCOLOR_PIC);
}
else {
ui_player_color_values[i] = strdup(va("%02X%02X%02X", ui_player_color_imageColors[i][0], ui_player_color_imageColors[i][1], ui_player_color_imageColors[i][2]));
ui_player_color_imageNames[i] = strdup(UI_SOLIDWHITE_PIC);
}
numColors++;
}
ui_numplayercolors = numColors;
}
#endif
/*
==========================
UI_LoadPlayerModels
==========================
*/
void UI_LoadPlayerModels (void)
{
UI_PlayerConfig_ScanDirectories ();
// UI_BuildPlayerColorList ();
}
/*
==========================
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;
}
/* if (ui_numplayercolors > 0)
{
FS_FreeFileList (ui_player_color_values, ui_numplayercolors);
FS_FreeFileList (ui_player_color_imageNames, ui_numplayercolors);
}
ui_player_color_values = NULL;
ui_player_color_imageNames = NULL;
ui_numplayercolors = 0; */
}
/*
==========================
UI_RefreshPlayerModels
Reloads player models if we recently downloaded a player model.
==========================
*/
void UI_RefreshPlayerModels (void)
{
if (cls.refreshPlayerModels) {
Com_DPrintf ("UI_RefreshPlayerModels: reloading player models due to recent download of a player model.\n");
UI_FreePlayerModels ();
UI_LoadPlayerModels ();
cls.refreshPlayerModels = false; // clear the flag
}
}
/*
==========================
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;
}
Q_strncpyz(currentdirectory, sizeof(currentdirectory), Cvar_VariableString ("skin"));
if ( strchr( currentdirectory, '/' ) )
{
Q_strncpyz(currentskin, sizeof(currentskin), strchr( currentdirectory, '/' ) + 1);
*strchr( currentdirectory, '/' ) = 0;
}
else if ( strchr( currentdirectory, '\\' ) )
{
Q_strncpyz(currentskin, sizeof(currentskin), strchr( currentdirectory, '\\' ) + 1);
*strchr( currentdirectory, '\\' ) = 0;
}
else
{
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);
}