1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20
21#include "quakedef.h"
22
23cvar_t		baseskin = CVAR2("baseskin", "base");
24cvar_t		noskins = CVAR2("noskins", "0");
25
26char		allskins[128];
27#define	MAX_CACHED_SKINS		128
28skin_t		skins[MAX_CACHED_SKINS];
29int			numskins;
30
31/*
32================
33Skin_Find
34
35  Determines the best skin for the given scoreboard
36  slot, and sets scoreboard->skin
37
38================
39*/
40void Skin_Find (player_info_t *sc)
41{
42	skin_t		*skin;
43	int			i;
44	char		name[128], *s;
45
46	if (allskins[0])
47		strcpy (name, allskins);
48	else
49	{
50		s = Info_ValueForKey (sc->userinfo, "skin");
51		if (s && s[0])
52			strcpy (name, s);
53		else
54			strcpy (name, baseskin.string);
55	}
56
57	if (strstr (name, "..") || *name == '.')
58		strcpy (name, "base");
59
60	COM_StripExtension (name, name);
61
62	for (i=0 ; i<numskins ; i++)
63	{
64		if (!strcmp (name, skins[i].name))
65		{
66			sc->skin = &skins[i];
67			Skin_Cache (sc->skin);
68			return;
69		}
70	}
71
72	if (numskins == MAX_CACHED_SKINS)
73	{	// ran out of spots, so flush everything
74		Skin_Skins_f ();
75		return;
76	}
77
78	skin = &skins[numskins];
79	sc->skin = skin;
80	numskins++;
81
82	memset (skin, 0, sizeof(*skin));
83	strncpy(skin->name, name, sizeof(skin->name) - 1);
84}
85
86
87/*
88==========
89Skin_Cache
90
91Returns a pointer to the skin bitmap, or NULL to use the default
92==========
93*/
94byte	*Skin_Cache (skin_t *skin)
95{
96	char	name[1024];
97	byte	*raw;
98	byte	*out, *pix;
99	pcx_t	*pcx;
100	int		x, y;
101	int		dataByte;
102	int		runLength;
103
104	if (cls.downloadtype == dl_skin)
105		return NULL;		// use base until downloaded
106
107	if (noskins.value==1) // JACK: So NOSKINS > 1 will show skins, but
108		return NULL;	  // not download new ones.
109
110	if (skin->failedload)
111		return NULL;
112
113	out = Cache_Check (&skin->cache);
114	if (out)
115		return out;
116
117//
118// load the pic from disk
119//
120	sprintf (name, "skins/%s.pcx", skin->name);
121	raw = COM_LoadTempFile (name);
122	if (!raw)
123	{
124		Con_Printf ("Couldn't load skin %s\n", name);
125		sprintf (name, "skins/%s.pcx", baseskin.string);
126		raw = COM_LoadTempFile (name);
127		if (!raw)
128		{
129			skin->failedload = true;
130			return NULL;
131		}
132	}
133
134//
135// parse the PCX file
136//
137	pcx = (pcx_t *)raw;
138	raw = &pcx->data;
139
140	if (pcx->manufacturer != 0x0a
141		|| pcx->version != 5
142		|| pcx->encoding != 1
143		|| pcx->bits_per_pixel != 8
144		|| pcx->xmax >= 320
145		|| pcx->ymax >= 200)
146	{
147		skin->failedload = true;
148		Con_Printf ("Bad skin %s\n", name);
149		return NULL;
150	}
151
152	out = Cache_Alloc (&skin->cache, 320*200, skin->name);
153	if (!out)
154		Sys_Error ("Skin_Cache: couldn't allocate");
155
156	pix = out;
157	memset (out, 0, 320*200);
158
159	for (y=0 ; y<pcx->ymax ; y++, pix += 320)
160	{
161		for (x=0 ; x<=pcx->xmax ; )
162		{
163			if (raw - (byte*)pcx > com_filesize)
164			{
165				Cache_Free (&skin->cache);
166				skin->failedload = true;
167				Con_Printf ("Skin %s was malformed.  You should delete it.\n", name);
168				return NULL;
169			}
170			dataByte = *raw++;
171
172			if((dataByte & 0xC0) == 0xC0)
173			{
174				runLength = dataByte & 0x3F;
175				if (raw - (byte*)pcx > com_filesize)
176				{
177					Cache_Free (&skin->cache);
178					skin->failedload = true;
179					Con_Printf ("Skin %s was malformed.  You should delete it.\n", name);
180					return NULL;
181				}
182				dataByte = *raw++;
183			}
184			else
185				runLength = 1;
186
187			// skin sanity check
188			if (runLength + x > pcx->xmax + 2) {
189				Cache_Free (&skin->cache);
190				skin->failedload = true;
191				Con_Printf ("Skin %s was malformed.  You should delete it.\n", name);
192				return NULL;
193			}
194			while(runLength-- > 0)
195				pix[x++] = dataByte;
196		}
197
198	}
199
200	if ( raw - (byte *)pcx > com_filesize)
201	{
202		Cache_Free (&skin->cache);
203		skin->failedload = true;
204		Con_Printf ("Skin %s was malformed.  You should delete it.\n", name);
205		return NULL;
206	}
207
208	skin->failedload = false;
209
210	return out;
211}
212
213
214/*
215=================
216Skin_NextDownload
217=================
218*/
219void Skin_NextDownload (void)
220{
221	player_info_t	*sc;
222	int			i;
223
224	if (cls.downloadnumber == 0)
225		Con_Printf ("Checking skins...\n");
226	cls.downloadtype = dl_skin;
227
228	for (
229		; cls.downloadnumber != MAX_CLIENTS
230		; cls.downloadnumber++)
231	{
232		sc = &cl.players[cls.downloadnumber];
233		if (!sc->name[0])
234			continue;
235		Skin_Find (sc);
236		if (noskins.value)
237			continue;
238		if (!CL_CheckOrDownloadFile(va("skins/%s.pcx", sc->skin->name)))
239			return;		// started a download
240	}
241
242	cls.downloadtype = dl_none;
243
244	// now load them in for real
245	for (i=0 ; i<MAX_CLIENTS ; i++)
246	{
247		sc = &cl.players[i];
248		if (!sc->name[0])
249			continue;
250		Skin_Cache (sc->skin);
251#ifdef GLQUAKE
252		sc->skin = NULL;
253#endif
254	}
255
256	if (cls.state != ca_active)
257	{	// get next signon phase
258		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
259		MSG_WriteString (&cls.netchan.message,
260			va("begin %i", cl.servercount));
261		Cache_Report ();		// print remaining memory
262	}
263}
264
265
266/*
267==========
268Skin_Skins_f
269
270Refind all skins, downloading if needed.
271==========
272*/
273void	Skin_Skins_f (void)
274{
275	int		i;
276
277	for (i=0 ; i<numskins ; i++)
278	{
279		if (skins[i].cache.data)
280			Cache_Free (&skins[i].cache);
281	}
282	numskins = 0;
283
284	cls.downloadnumber = 0;
285	cls.downloadtype = dl_skin;
286	Skin_NextDownload ();
287}
288
289
290/*
291==========
292Skin_AllSkins_f
293
294Sets all skins to one specific one
295==========
296*/
297void	Skin_AllSkins_f (void)
298{
299	strcpy (allskins, Cmd_Argv(1));
300	Skin_Skins_f ();
301}
302