/* skin.c (description) Copyright (C) 1996-1997 Id Software, Inc. This program 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. This program 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 this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ static const char rcsid[] = "$Id$"; #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include "QF/console.h" #include "QF/cvar.h" #include "QF/hash.h" #include "QF/pcx.h" #include "QF/skin.h" #include "QF/sys.h" #include "QF/texture.h" #include "QF/vfs.h" #include "client.h" #include "compat.h" #include "skin_stencil.h" #define MAX_TEMP_SKINS 64 //XXX dynamic? extern cvar_t *noskins; //FIXME shouldn't be here cvar_t *baseskin; char allskins[128]; skin_t skin_cache[MAX_CACHED_SKINS]; static hashtab_t *skin_hash; static int numskins; static skin_t temp_skins[MAX_TEMP_SKINS]; static int num_temp_skins; static int fullfb; int skin_textures; int skin_fb_textures; static const char * skin_get_key (void *_skin, void *unused) { skin_t *skin = (skin_t*)_skin; return skin->name; } /* Skin_Find Determines the best skin for the given scoreboard slot, and sets scoreboard->skin */ void Skin_Find (player_info_t *sc) { skin_t *skin; char name[128]; const char *s = NULL; if (allskins[0]) { strncpy (name, allskins, sizeof (name)); } else { if ((s = sc->skinname->value) && s[0]) strncpy (name, s, sizeof (name)); else strncpy (name, baseskin->string, sizeof (name)); } if (strstr (name, "..") || *name == '.') strcpy (name, "base"); if (!allskins[0] && (!s || !strcaseequal (name, s))) Info_SetValueForKey (sc->userinfo, "skin", name, 1); COM_StripExtension (name, name); skin = Hash_Find (skin_hash, name); if (skin) { sc->skin = skin; Skin_Cache (sc->skin); return; } if (numskins == MAX_CACHED_SKINS) { // ran out of spots, so flush everything Skin_Flush (); return; } skin = &skin_cache[numskins]; sc->skin = skin; numskins++; memset (skin, 0, sizeof (*skin)); strncpy (skin->name, name, sizeof (skin->name) - 1); Hash_Add (skin_hash, skin); } /* Skin_Cache Returns a pointer to the skin bitmap, or NULL to use the default */ tex_t * Skin_Cache (skin_t *skin) { char name[1024]; tex_t *out; VFile *file; tex_t *tex; int pixels; byte *ipix, *opix; int i, numfb; if (cls.downloadtype == dl_skin) // use base until downloaded return NULL; // NOSKINS > 1 will show skins, but not download new ones. if (noskins->int_val == 1) //XXX FIXME return NULL; if (skin->failedload) return NULL; out = Cache_Check (&skin->data.cache); if (out) return out; // load the pic from disk snprintf (name, sizeof (name), "skins/%s.pcx", skin->name); COM_FOpenFile (name, &file); if (!file) { Con_Printf ("Couldn't load skin %s\n", name); snprintf (name, sizeof (name), "skins/%s.pcx", baseskin->string); COM_FOpenFile (name, &file); if (!file) { skin->failedload = true; return NULL; } } tex = LoadPCX (file, 0, vid_basepal); Qclose (file); if (!tex || tex->width > 320 || tex->height > 200) { skin->failedload = true; Con_Printf ("Bad skin %s\n", name); return NULL; } pixels = 320 * 200; out = Cache_Alloc (&skin->data.cache, field_offset (tex_t, data[pixels]), skin->name); if (!out) Sys_Error ("Skin_Cache: couldn't allocate"); opix = out->data; out->width = 320; out->height = 200; out->palette = tex->palette; //FIXME assumes 0 or vid_basepal memset (opix, 0, pixels); for (i = 0, ipix = tex->data; i < tex->height; i++, opix += 320, ipix += tex->width) memcpy (opix, ipix, tex->width); Skin_Process (skin, out); skin->failedload = false; for (i = 0,numfb = 0; i < 320*200; i++) if (skin_stencil[i] && out->data[i] >= 256 - 32) numfb++; skin->numfb = numfb; return out; } void Skin_Flush (void) { int i; for (i = 0; i < numskins; i++) { if (skin_cache[i].data.cache.data) Cache_Free (&skin_cache[i].data.cache); } numskins = 0; Hash_FlushTable (skin_hash); } skin_t * Skin_NewTempSkin (void) { if (num_temp_skins == MAX_TEMP_SKINS) return 0; // ran out return &temp_skins[num_temp_skins++]; } void Skin_ClearTempSkins (void) { num_temp_skins = 0; } int Skin_Init_Textures (int base) { skin_textures = base; base += MAX_TEMP_SKINS; skin_fb_textures = base; base += MAX_TEMP_SKINS; return base; } void Skin_Init (void) { int i; skin_hash = Hash_NewTable (1021, skin_get_key, 0, 0); Skin_Init_Translation (); for (i = 0, fullfb = 0; i < 320*200; i++) fullfb += skin_stencil[i]; } void Skin_Init_Cvars (void) { baseskin = Cvar_Get ("baseskin", "base", CVAR_NONE, NULL, "default base skin name"); } int Skin_FbPercent (const char *skin_name) { int i, totalfb = 0; skin_t *skin = 0; if (skin_name) { skin = Hash_Find (skin_hash, skin_name); if (skin) return skin->numfb * 1000 / fullfb; return -1; } for (i = 0; i < numskins; i++) totalfb += skin_cache[i].numfb; return totalfb * 1000 / (numskins * fullfb); }