]> git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
MSG_ReadString: if hitting the max length of the string, continue to read until NUL...
[xonotic/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #ifndef WIN32
25 #include <unistd.h>
26 #endif
27
28 #include "quakedef.h"
29
30 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
31 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
32
33 char com_token[MAX_INPUTLINE];
34 int com_argc;
35 const char **com_argv;
36 int com_selffd = -1;
37
38 gamemode_t gamemode;
39 const char *gamename;
40 const char *gamedirname1;
41 const char *gamedirname2;
42 const char *gamescreenshotname;
43 const char *gameuserdirname;
44 char com_modname[MAX_OSPATH] = "";
45
46
47 /*
48 ============================================================================
49
50                                         BYTE ORDER FUNCTIONS
51
52 ============================================================================
53 */
54
55
56 float BuffBigFloat (const unsigned char *buffer)
57 {
58         union
59         {
60                 float f;
61                 unsigned int i;
62         }
63         u;
64         u.i = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
65         return u.f;
66 }
67
68 int BuffBigLong (const unsigned char *buffer)
69 {
70         return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
71 }
72
73 short BuffBigShort (const unsigned char *buffer)
74 {
75         return (buffer[0] << 8) | buffer[1];
76 }
77
78 float BuffLittleFloat (const unsigned char *buffer)
79 {
80         union
81         {
82                 float f;
83                 unsigned int i;
84         }
85         u;
86         u.i = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
87         return u.f;
88 }
89
90 int BuffLittleLong (const unsigned char *buffer)
91 {
92         return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
93 }
94
95 short BuffLittleShort (const unsigned char *buffer)
96 {
97         return (buffer[1] << 8) | buffer[0];
98 }
99
100 void StoreBigLong (unsigned char *buffer, unsigned int i)
101 {
102         buffer[0] = (i >> 24) & 0xFF;
103         buffer[1] = (i >> 16) & 0xFF;
104         buffer[2] = (i >>  8) & 0xFF;
105         buffer[3] = i         & 0xFF;
106 }
107
108 void StoreBigShort (unsigned char *buffer, unsigned short i)
109 {
110         buffer[0] = (i >>  8) & 0xFF;
111         buffer[1] = i         & 0xFF;
112 }
113
114 void StoreLittleLong (unsigned char *buffer, unsigned int i)
115 {
116         buffer[0] = i         & 0xFF;
117         buffer[1] = (i >>  8) & 0xFF;
118         buffer[2] = (i >> 16) & 0xFF;
119         buffer[3] = (i >> 24) & 0xFF;
120 }
121
122 void StoreLittleShort (unsigned char *buffer, unsigned short i)
123 {
124         buffer[0] = i         & 0xFF;
125         buffer[1] = (i >>  8) & 0xFF;
126 }
127
128 /*
129 ============================================================================
130
131                                         CRC FUNCTIONS
132
133 ============================================================================
134 */
135
136 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
137 // and the initial and final xor values shown below...  in other words, the
138 // CCITT standard CRC used by XMODEM
139
140 #define CRC_INIT_VALUE  0xffff
141 #define CRC_XOR_VALUE   0x0000
142
143 static unsigned short crctable[256] =
144 {
145         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
146         0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
147         0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
148         0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
149         0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
150         0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
151         0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
152         0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
153         0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
154         0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
155         0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
156         0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
157         0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
158         0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
159         0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
160         0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
161         0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
162         0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
163         0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
164         0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
165         0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
166         0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
167         0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
168         0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
169         0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
170         0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
171         0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
172         0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
173         0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
174         0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
175         0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
176         0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
177 };
178
179 unsigned short CRC_Block(const unsigned char *data, size_t size)
180 {
181         unsigned short crc = CRC_INIT_VALUE;
182         while (size--)
183                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
184         return crc ^ CRC_XOR_VALUE;
185 }
186
187 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
188 {
189         unsigned short crc = CRC_INIT_VALUE;
190         while (size--)
191                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
192         return crc ^ CRC_XOR_VALUE;
193 }
194
195 // QuakeWorld
196 static unsigned char chktbl[1024 + 4] =
197 {
198         0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
199         0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
200         0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
201         0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
202         0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
203         0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
204         0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
205         0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
206         0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
207         0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
208         0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
209         0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
210         0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
211         0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
212         0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
213         0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
214         0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
215         0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
216         0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
217         0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
218         0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
219         0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
220         0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
221         0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
222         0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
223         0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
224         0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
225         0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
226         0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
227         0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
228         0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
229         0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
230
231         // map checksum goes here
232         0x00,0x00,0x00,0x00
233 };
234
235 // QuakeWorld
236 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
237 {
238         unsigned char *p;
239         unsigned char chkb[60 + 4];
240
241         p = chktbl + (sequence % (sizeof(chktbl) - 8));
242
243         if (length > 60)
244                 length = 60;
245         memcpy(chkb, base, length);
246
247         chkb[length] = (sequence & 0xff) ^ p[0];
248         chkb[length+1] = p[1];
249         chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
250         chkb[length+3] = p[3];
251
252         return CRC_Block(chkb, length + 4) & 0xff;
253 }
254
255 /*
256 ==============================================================================
257
258                         MESSAGE IO FUNCTIONS
259
260 Handles byte ordering and avoids alignment errors
261 ==============================================================================
262 */
263
264 //
265 // writing functions
266 //
267
268 void MSG_WriteChar (sizebuf_t *sb, int c)
269 {
270         unsigned char    *buf;
271
272         buf = SZ_GetSpace (sb, 1);
273         buf[0] = c;
274 }
275
276 void MSG_WriteByte (sizebuf_t *sb, int c)
277 {
278         unsigned char    *buf;
279
280         buf = SZ_GetSpace (sb, 1);
281         buf[0] = c;
282 }
283
284 void MSG_WriteShort (sizebuf_t *sb, int c)
285 {
286         unsigned char    *buf;
287
288         buf = SZ_GetSpace (sb, 2);
289         buf[0] = c&0xff;
290         buf[1] = c>>8;
291 }
292
293 void MSG_WriteLong (sizebuf_t *sb, int c)
294 {
295         unsigned char    *buf;
296
297         buf = SZ_GetSpace (sb, 4);
298         buf[0] = c&0xff;
299         buf[1] = (c>>8)&0xff;
300         buf[2] = (c>>16)&0xff;
301         buf[3] = c>>24;
302 }
303
304 void MSG_WriteFloat (sizebuf_t *sb, float f)
305 {
306         union
307         {
308                 float   f;
309                 int     l;
310         } dat;
311
312
313         dat.f = f;
314         dat.l = LittleLong (dat.l);
315
316         SZ_Write (sb, (unsigned char *)&dat.l, 4);
317 }
318
319 void MSG_WriteString (sizebuf_t *sb, const char *s)
320 {
321         if (!s || !*s)
322                 MSG_WriteChar (sb, 0);
323         else
324                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
325 }
326
327 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
328 {
329         if (s && *s)
330                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
331 }
332
333 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
334 {
335         if (f >= 0)
336                 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
337         else
338                 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
339 }
340
341 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
342 {
343         if (f >= 0)
344                 MSG_WriteShort (sb, (int)(f + 0.5));
345         else
346                 MSG_WriteShort (sb, (int)(f - 0.5));
347 }
348
349 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
350 {
351         MSG_WriteFloat (sb, f);
352 }
353
354 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
355 {
356         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
357                 MSG_WriteCoord13i (sb, f);
358         else if (protocol == PROTOCOL_DARKPLACES1)
359                 MSG_WriteCoord32f (sb, f);
360         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
361                 MSG_WriteCoord16i (sb, f);
362         else
363                 MSG_WriteCoord32f (sb, f);
364 }
365
366 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
367 {
368         MSG_WriteCoord (sb, v[0], protocol);
369         MSG_WriteCoord (sb, v[1], protocol);
370         MSG_WriteCoord (sb, v[2], protocol);
371 }
372
373 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
374 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
375 {
376         if (f >= 0)
377                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
378         else
379                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
380 }
381
382 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
383 {
384         if (f >= 0)
385                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
386         else
387                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
388 }
389
390 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
391 {
392         MSG_WriteFloat (sb, f);
393 }
394
395 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
396 {
397         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
398                 MSG_WriteAngle8i (sb, f);
399         else
400                 MSG_WriteAngle16i (sb, f);
401 }
402
403 //
404 // reading functions
405 //
406 int msg_readcount;
407 qboolean msg_badread;
408
409 void MSG_BeginReading (void)
410 {
411         msg_readcount = 0;
412         msg_badread = false;
413 }
414
415 int MSG_ReadLittleShort (void)
416 {
417         if (msg_readcount+2 > net_message.cursize)
418         {
419                 msg_badread = true;
420                 return -1;
421         }
422         msg_readcount += 2;
423         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
424 }
425
426 int MSG_ReadBigShort (void)
427 {
428         if (msg_readcount+2 > net_message.cursize)
429         {
430                 msg_badread = true;
431                 return -1;
432         }
433         msg_readcount += 2;
434         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
435 }
436
437 int MSG_ReadLittleLong (void)
438 {
439         if (msg_readcount+4 > net_message.cursize)
440         {
441                 msg_badread = true;
442                 return -1;
443         }
444         msg_readcount += 4;
445         return net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
446 }
447
448 int MSG_ReadBigLong (void)
449 {
450         if (msg_readcount+4 > net_message.cursize)
451         {
452                 msg_badread = true;
453                 return -1;
454         }
455         msg_readcount += 4;
456         return (net_message.data[msg_readcount-4]<<24) + (net_message.data[msg_readcount-3]<<16) + (net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1];
457 }
458
459 float MSG_ReadLittleFloat (void)
460 {
461         union
462         {
463                 float f;
464                 int l;
465         } dat;
466         if (msg_readcount+4 > net_message.cursize)
467         {
468                 msg_badread = true;
469                 return -1;
470         }
471         msg_readcount += 4;
472         dat.l = net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
473         return dat.f;
474 }
475
476 float MSG_ReadBigFloat (void)
477 {
478         union
479         {
480                 float f;
481                 int l;
482         } dat;
483         if (msg_readcount+4 > net_message.cursize)
484         {
485                 msg_badread = true;
486                 return -1;
487         }
488         msg_readcount += 4;
489         dat.l = (net_message.data[msg_readcount-4]<<24) | (net_message.data[msg_readcount-3]<<16) | (net_message.data[msg_readcount-2]<<8) | net_message.data[msg_readcount-1];
490         return dat.f;
491 }
492
493 char *MSG_ReadString (void)
494 {
495         static char string[MAX_INPUTLINE];
496         int l,c;
497         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
498                 string[l] = c;
499         // read the rest of the string anyway
500         while(c != -1 && c != 0)
501                 c = MSG_ReadByte();
502         string[l] = 0;
503         return string;
504 }
505
506 int MSG_ReadBytes (int numbytes, unsigned char *out)
507 {
508         int l, c;
509         for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
510                 out[l] = c;
511         return l;
512 }
513
514 float MSG_ReadCoord13i (void)
515 {
516         return MSG_ReadLittleShort() * (1.0/8.0);
517 }
518
519 float MSG_ReadCoord16i (void)
520 {
521         return (signed short) MSG_ReadLittleShort();
522 }
523
524 float MSG_ReadCoord32f (void)
525 {
526         return MSG_ReadLittleFloat();
527 }
528
529 float MSG_ReadCoord (protocolversion_t protocol)
530 {
531         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
532                 return MSG_ReadCoord13i();
533         else if (protocol == PROTOCOL_DARKPLACES1)
534                 return MSG_ReadCoord32f();
535         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
536                 return MSG_ReadCoord16i();
537         else
538                 return MSG_ReadCoord32f();
539 }
540
541 void MSG_ReadVector (float *v, protocolversion_t protocol)
542 {
543         v[0] = MSG_ReadCoord(protocol);
544         v[1] = MSG_ReadCoord(protocol);
545         v[2] = MSG_ReadCoord(protocol);
546 }
547
548 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
549 float MSG_ReadAngle8i (void)
550 {
551         return (signed char) MSG_ReadByte () * (360.0/256.0);
552 }
553
554 float MSG_ReadAngle16i (void)
555 {
556         return (signed short)MSG_ReadShort () * (360.0/65536.0);
557 }
558
559 float MSG_ReadAngle32f (void)
560 {
561         return MSG_ReadFloat ();
562 }
563
564 float MSG_ReadAngle (protocolversion_t protocol)
565 {
566         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
567                 return MSG_ReadAngle8i ();
568         else
569                 return MSG_ReadAngle16i ();
570 }
571
572
573 //===========================================================================
574
575 void SZ_Clear (sizebuf_t *buf)
576 {
577         buf->cursize = 0;
578 }
579
580 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
581 {
582         unsigned char *data;
583
584         if (buf->cursize + length > buf->maxsize)
585         {
586                 if (!buf->allowoverflow)
587                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
588
589                 if (length > buf->maxsize)
590                         Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
591
592                 buf->overflowed = true;
593                 Con_Print("SZ_GetSpace: overflow\n");
594                 SZ_Clear (buf);
595         }
596
597         data = buf->data + buf->cursize;
598         buf->cursize += length;
599
600         return data;
601 }
602
603 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
604 {
605         memcpy (SZ_GetSpace(buf,length),data,length);
606 }
607
608 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
609 // attention, it has been eradicated from here, its only (former) use in
610 // all of darkplaces.
611
612 static const char *hexchar = "0123456789ABCDEF";
613 void Com_HexDumpToConsole(const unsigned char *data, int size)
614 {
615         int i, j, n;
616         char text[1024];
617         char *cur, *flushpointer;
618         const unsigned char *d;
619         cur = text;
620         flushpointer = text + 512;
621         for (i = 0;i < size;)
622         {
623                 n = 16;
624                 if (n > size - i)
625                         n = size - i;
626                 d = data + i;
627                 // print offset
628                 *cur++ = hexchar[(i >> 12) & 15];
629                 *cur++ = hexchar[(i >>  8) & 15];
630                 *cur++ = hexchar[(i >>  4) & 15];
631                 *cur++ = hexchar[(i >>  0) & 15];
632                 *cur++ = ':';
633                 // print hex
634                 for (j = 0;j < 16;j++)
635                 {
636                         if (j < n)
637                         {
638                                 *cur++ = hexchar[(d[j] >> 4) & 15];
639                                 *cur++ = hexchar[(d[j] >> 0) & 15];
640                         }
641                         else
642                         {
643                                 *cur++ = ' ';
644                                 *cur++ = ' ';
645                         }
646                         if ((j & 3) == 3)
647                                 *cur++ = ' ';
648                 }
649                 // print text
650                 for (j = 0;j < 16;j++)
651                 {
652                         if (j < n)
653                         {
654                                 // color change prefix character has to be treated specially
655                                 if (d[j] == STRING_COLOR_TAG)
656                                 {
657                                         *cur++ = STRING_COLOR_TAG;
658                                         *cur++ = STRING_COLOR_TAG;
659                                 }
660                                 else if (d[j] >= (unsigned char) ' ')
661                                         *cur++ = d[j];
662                                 else
663                                         *cur++ = '.';
664                         }
665                         else
666                                 *cur++ = ' ';
667                 }
668                 *cur++ = '\n';
669                 i += n;
670                 if (cur >= flushpointer || i >= size)
671                 {
672                         *cur++ = 0;
673                         Con_Print(text);
674                         cur = text;
675                 }
676         }
677 }
678
679 void SZ_HexDumpToConsole(const sizebuf_t *buf)
680 {
681         Com_HexDumpToConsole(buf->data, buf->cursize);
682 }
683
684
685 //============================================================================
686
687 /*
688 ==============
689 COM_Wordwrap
690
691 Word wraps a string. The wordWidth function is guaranteed to be called exactly
692 once for each word in the string, so it may be stateful, no idea what that
693 would be good for any more. At the beginning of the string, it will be called
694 for the char 0 to initialize a clean state, and then once with the string " "
695 (a space) so the routine knows how long a space is.
696
697 In case no single character fits into the given width, the wordWidth function
698 must return the width of exactly one character.
699
700 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
701
702 The sum of the return values of the processLine function will be returned.
703 ==============
704 */
705 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
706 {
707         // Logic is as follows:
708         //
709         // For each word or whitespace:
710         //   Newline found? Output current line, advance to next line. This is not a continuation. Continue.
711         //   Space found? Always add it to the current line, no matter if it fits.
712         //   Word found? Check if current line + current word fits.
713         //     If it fits, append it. Continue.
714         //     If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
715
716         qboolean isContinuation = false;
717         float spaceWidth;
718         const char *startOfLine = string;
719         const char *cursor = string;
720         const char *end = string + length;
721         float spaceUsedInLine = 0;
722         float spaceUsedForWord;
723         int result = 0;
724         size_t wordLen;
725         size_t dummy;
726
727         dummy = 0;
728         wordWidth(passthroughCW, NULL, &dummy, -1);
729         dummy = 1;
730         spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
731
732         for(;;)
733         {
734                 char ch = (cursor < end) ? *cursor : 0;
735                 switch(ch)
736                 {
737                         case 0: // end of string
738                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
739                                 isContinuation = false;
740                                 goto out;
741                         case '\n': // end of line
742                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
743                                 isContinuation = false;
744                                 ++cursor;
745                                 startOfLine = cursor;
746                                 break;
747                         case ' ': // space
748                                 ++cursor;
749                                 spaceUsedInLine += spaceWidth;
750                                 break;
751                         default: // word
752                                 wordLen = 1;
753                                 while(cursor + wordLen < end)
754                                 {
755                                         switch(cursor[wordLen])
756                                         {
757                                                 case 0:
758                                                 case '\n':
759                                                 case ' ':
760                                                         goto out_inner;
761                                                 default:
762                                                         ++wordLen;
763                                                         break;
764                                         }
765                                 }
766                                 out_inner:
767                                 spaceUsedForWord = wordWidth(passthroughCW, cursor, &wordLen, maxWidth - continuationWidth); // this may have reduced wordLen when it won't fit - but this is GOOD. TODO fix words that do fit in a non-continuation line
768                                 if(wordLen < 1) // cannot happen according to current spec of wordWidth
769                                 {
770                                         wordLen = 1;
771                                         spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
772                                 }
773                                 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
774                                 {
775                                         // we can simply append it
776                                         cursor += wordLen;
777                                         spaceUsedInLine += spaceUsedForWord;
778                                 }
779                                 else
780                                 {
781                                         // output current line
782                                         result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
783                                         isContinuation = true;
784                                         startOfLine = cursor;
785                                         cursor += wordLen;
786                                         spaceUsedInLine = continuationWidth + spaceUsedForWord;
787                                 }
788                 }
789         }
790         out:
791
792         return result;
793
794 /*
795         qboolean isContinuation = false;
796         float currentWordSpace = 0;
797         const char *currentWord = 0;
798         float minReserve = 0;
799
800         float spaceUsedInLine = 0;
801         const char *currentLine = 0;
802         const char *currentLineEnd = 0;
803         float currentLineFinalWhitespace = 0;
804         const char *p;
805
806         int result = 0;
807         minReserve = charWidth(passthroughCW, 0);
808         minReserve += charWidth(passthroughCW, ' ');
809
810         if(maxWidth < continuationWidth + minReserve)
811                 maxWidth = continuationWidth + minReserve;
812
813         charWidth(passthroughCW, 0);
814
815         for(p = string; p < string + length; ++p)
816         {
817                 char c = *p;
818                 float w = charWidth(passthroughCW, c);
819
820                 if(!currentWord)
821                 {
822                         currentWord = p;
823                         currentWordSpace = 0;
824                 }
825
826                 if(!currentLine)
827                 {
828                         currentLine = p;
829                         spaceUsedInLine = isContinuation ? continuationWidth : 0;
830                         currentLineEnd = 0;
831                 }
832
833                 if(c == ' ')
834                 {
835                         // 1. I can add the word AND a space - then just append it.
836                         if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
837                         {
838                                 currentLineEnd = p; // note: space not included here
839                                 currentLineFinalWhitespace = w;
840                                 spaceUsedInLine += currentWordSpace + w;
841                         }
842                         // 2. I can just add the word - then append it, output current line and go to next one.
843                         else if(spaceUsedInLine + currentWordSpace <= maxWidth)
844                         {
845                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
846                                 currentLine = 0;
847                                 isContinuation = true;
848                         }
849                         // 3. Otherwise, output current line and go to next one, where I can add the word.
850                         else if(continuationWidth + currentWordSpace + w <= maxWidth)
851                         {
852                                 if(currentLineEnd)
853                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
854                                 currentLine = currentWord;
855                                 spaceUsedInLine = continuationWidth + currentWordSpace + w;
856                                 currentLineEnd = p;
857                                 currentLineFinalWhitespace = w;
858                                 isContinuation = true;
859                         }
860                         // 4. We can't even do that? Then output both current and next word as new lines.
861                         else
862                         {
863                                 if(currentLineEnd)
864                                 {
865                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
866                                         isContinuation = true;
867                                 }
868                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
869                                 currentLine = 0;
870                                 isContinuation = true;
871                         }
872                         currentWord = 0;
873                 }
874                 else if(c == '\n')
875                 {
876                         // 1. I can add the word - then do it.
877                         if(spaceUsedInLine + currentWordSpace <= maxWidth)
878                         {
879                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
880                         }
881                         // 2. Otherwise, output current line, next one and make tabula rasa.
882                         else
883                         {
884                                 if(currentLineEnd)
885                                 {
886                                         processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
887                                         isContinuation = true;
888                                 }
889                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
890                         }
891                         currentWord = 0;
892                         currentLine = 0;
893                         isContinuation = false;
894                 }
895                 else
896                 {
897                         currentWordSpace += w;
898                         if(
899                                 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
900                                 &&
901                                 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
902                         )
903                         {
904                                 // this word cannot join ANY line...
905                                 // so output the current line...
906                                 if(currentLineEnd)
907                                 {
908                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
909                                         isContinuation = true;
910                                 }
911
912                                 // then this word's beginning...
913                                 if(isContinuation)
914                                 {
915                                         // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
916                                         float pieceWidth = maxWidth - continuationWidth;
917                                         const char *pos = currentWord;
918                                         currentWordSpace = 0;
919
920                                         // reset the char width function to a state where no kerning occurs (start of word)
921                                         charWidth(passthroughCW, ' ');
922                                         while(pos <= p)
923                                         {
924                                                 float w = charWidth(passthroughCW, *pos);
925                                                 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
926                                                 {
927                                                         // print everything until it
928                                                         result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
929                                                         // go to here
930                                                         currentWord = pos;
931                                                         currentWordSpace = 0;
932                                                 }
933                                                 currentWordSpace += w;
934                                                 ++pos;
935                                         }
936                                         // now we have a currentWord that fits... set up its next line
937                                         // currentWordSpace has been set
938                                         // currentWord has been set
939                                         spaceUsedInLine = continuationWidth;
940                                         currentLine = currentWord;
941                                         currentLineEnd = 0;
942                                         isContinuation = true;
943                                 }
944                                 else
945                                 {
946                                         // we have a guarantee that it will fix (see if clause)
947                                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
948
949                                         // and use the rest of this word as new start of a line
950                                         currentWordSpace = w;
951                                         currentWord = p;
952                                         spaceUsedInLine = continuationWidth;
953                                         currentLine = p;
954                                         currentLineEnd = 0;
955                                         isContinuation = true;
956                                 }
957                         }
958                 }
959         }
960
961         if(!currentWord)
962         {
963                 currentWord = p;
964                 currentWordSpace = 0;
965         }
966
967         if(currentLine) // Same procedure as \n
968         {
969                 // Can I append the current word?
970                 if(spaceUsedInLine + currentWordSpace <= maxWidth)
971                         result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
972                 else
973                 {
974                         if(currentLineEnd)
975                         {
976                                 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
977                                 isContinuation = true;
978                         }
979                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
980                 }
981         }
982
983         return result;
984 */
985 }
986
987 /*
988 ==============
989 COM_ParseToken_Simple
990
991 Parse a token out of a string
992 ==============
993 */
994 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
995 {
996         int len;
997         int c;
998         const char *data = *datapointer;
999
1000         len = 0;
1001         com_token[0] = 0;
1002
1003         if (!data)
1004         {
1005                 *datapointer = NULL;
1006                 return false;
1007         }
1008
1009 // skip whitespace
1010 skipwhite:
1011         // line endings:
1012         // UNIX: \n
1013         // Mac: \r
1014         // Windows: \r\n
1015         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1016         {
1017                 if (*data == 0)
1018                 {
1019                         // end of file
1020                         *datapointer = NULL;
1021                         return false;
1022                 }
1023         }
1024
1025         // handle Windows line ending
1026         if (data[0] == '\r' && data[1] == '\n')
1027                 data++;
1028
1029         if (data[0] == '/' && data[1] == '/')
1030         {
1031                 // comment
1032                 while (*data && *data != '\n' && *data != '\r')
1033                         data++;
1034                 goto skipwhite;
1035         }
1036         else if (data[0] == '/' && data[1] == '*')
1037         {
1038                 // comment
1039                 data++;
1040                 while (*data && (data[0] != '*' || data[1] != '/'))
1041                         data++;
1042                 if (*data)
1043                         data++;
1044                 if (*data)
1045                         data++;
1046                 goto skipwhite;
1047         }
1048         else if (*data == '\"')
1049         {
1050                 // quoted string
1051                 for (data++;*data && *data != '\"';data++)
1052                 {
1053                         c = *data;
1054                         if (*data == '\\' && parsebackslash)
1055                         {
1056                                 data++;
1057                                 c = *data;
1058                                 if (c == 'n')
1059                                         c = '\n';
1060                                 else if (c == 't')
1061                                         c = '\t';
1062                         }
1063                         if (len < (int)sizeof(com_token) - 1)
1064                                 com_token[len++] = c;
1065                 }
1066                 com_token[len] = 0;
1067                 if (*data == '\"')
1068                         data++;
1069                 *datapointer = data;
1070                 return true;
1071         }
1072         else if (*data == '\r')
1073         {
1074                 // translate Mac line ending to UNIX
1075                 com_token[len++] = '\n';data++;
1076                 com_token[len] = 0;
1077                 *datapointer = data;
1078                 return true;
1079         }
1080         else if (*data == '\n')
1081         {
1082                 // single character
1083                 com_token[len++] = *data++;
1084                 com_token[len] = 0;
1085                 *datapointer = data;
1086                 return true;
1087         }
1088         else
1089         {
1090                 // regular word
1091                 for (;!ISWHITESPACE(*data);data++)
1092                         if (len < (int)sizeof(com_token) - 1)
1093                                 com_token[len++] = *data;
1094                 com_token[len] = 0;
1095                 *datapointer = data;
1096                 return true;
1097         }
1098 }
1099
1100 /*
1101 ==============
1102 COM_ParseToken_QuakeC
1103
1104 Parse a token out of a string
1105 ==============
1106 */
1107 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1108 {
1109         int len;
1110         int c;
1111         const char *data = *datapointer;
1112
1113         len = 0;
1114         com_token[0] = 0;
1115
1116         if (!data)
1117         {
1118                 *datapointer = NULL;
1119                 return false;
1120         }
1121
1122 // skip whitespace
1123 skipwhite:
1124         // line endings:
1125         // UNIX: \n
1126         // Mac: \r
1127         // Windows: \r\n
1128         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1129         {
1130                 if (*data == 0)
1131                 {
1132                         // end of file
1133                         *datapointer = NULL;
1134                         return false;
1135                 }
1136         }
1137
1138         // handle Windows line ending
1139         if (data[0] == '\r' && data[1] == '\n')
1140                 data++;
1141
1142         if (data[0] == '/' && data[1] == '/')
1143         {
1144                 // comment
1145                 while (*data && *data != '\n' && *data != '\r')
1146                         data++;
1147                 goto skipwhite;
1148         }
1149         else if (data[0] == '/' && data[1] == '*')
1150         {
1151                 // comment
1152                 data++;
1153                 while (*data && (data[0] != '*' || data[1] != '/'))
1154                         data++;
1155                 if (*data)
1156                         data++;
1157                 if (*data)
1158                         data++;
1159                 goto skipwhite;
1160         }
1161         else if (*data == '\"' || *data == '\'')
1162         {
1163                 // quoted string
1164                 char quote = *data;
1165                 for (data++;*data && *data != quote;data++)
1166                 {
1167                         c = *data;
1168                         if (*data == '\\')
1169                         {
1170                                 data++;
1171                                 c = *data;
1172                                 if (c == 'n')
1173                                         c = '\n';
1174                                 else if (c == 't')
1175                                         c = '\t';
1176                         }
1177                         if (len < (int)sizeof(com_token) - 1)
1178                                 com_token[len++] = c;
1179                 }
1180                 com_token[len] = 0;
1181                 if (*data == quote)
1182                         data++;
1183                 *datapointer = data;
1184                 return true;
1185         }
1186         else if (*data == '\r')
1187         {
1188                 // translate Mac line ending to UNIX
1189                 com_token[len++] = '\n';data++;
1190                 com_token[len] = 0;
1191                 *datapointer = data;
1192                 return true;
1193         }
1194         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1195         {
1196                 // single character
1197                 com_token[len++] = *data++;
1198                 com_token[len] = 0;
1199                 *datapointer = data;
1200                 return true;
1201         }
1202         else
1203         {
1204                 // regular word
1205                 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1206                         if (len < (int)sizeof(com_token) - 1)
1207                                 com_token[len++] = *data;
1208                 com_token[len] = 0;
1209                 *datapointer = data;
1210                 return true;
1211         }
1212 }
1213
1214 /*
1215 ==============
1216 COM_ParseToken_VM_Tokenize
1217
1218 Parse a token out of a string
1219 ==============
1220 */
1221 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1222 {
1223         int len;
1224         int c;
1225         const char *data = *datapointer;
1226
1227         len = 0;
1228         com_token[0] = 0;
1229
1230         if (!data)
1231         {
1232                 *datapointer = NULL;
1233                 return false;
1234         }
1235
1236 // skip whitespace
1237 skipwhite:
1238         // line endings:
1239         // UNIX: \n
1240         // Mac: \r
1241         // Windows: \r\n
1242         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1243         {
1244                 if (*data == 0)
1245                 {
1246                         // end of file
1247                         *datapointer = NULL;
1248                         return false;
1249                 }
1250         }
1251
1252         // handle Windows line ending
1253         if (data[0] == '\r' && data[1] == '\n')
1254                 data++;
1255
1256         if (data[0] == '/' && data[1] == '/')
1257         {
1258                 // comment
1259                 while (*data && *data != '\n' && *data != '\r')
1260                         data++;
1261                 goto skipwhite;
1262         }
1263         else if (data[0] == '/' && data[1] == '*')
1264         {
1265                 // comment
1266                 data++;
1267                 while (*data && (data[0] != '*' || data[1] != '/'))
1268                         data++;
1269                 if (*data)
1270                         data++;
1271                 if (*data)
1272                         data++;
1273                 goto skipwhite;
1274         }
1275         else if (*data == '\"' || *data == '\'')
1276         {
1277                 char quote = *data;
1278                 // quoted string
1279                 for (data++;*data && *data != quote;data++)
1280                 {
1281                         c = *data;
1282                         if (*data == '\\')
1283                         {
1284                                 data++;
1285                                 c = *data;
1286                                 if (c == 'n')
1287                                         c = '\n';
1288                                 else if (c == 't')
1289                                         c = '\t';
1290                         }
1291                         if (len < (int)sizeof(com_token) - 1)
1292                                 com_token[len++] = c;
1293                 }
1294                 com_token[len] = 0;
1295                 if (*data == quote)
1296                         data++;
1297                 *datapointer = data;
1298                 return true;
1299         }
1300         else if (*data == '\r')
1301         {
1302                 // translate Mac line ending to UNIX
1303                 com_token[len++] = '\n';data++;
1304                 com_token[len] = 0;
1305                 *datapointer = data;
1306                 return true;
1307         }
1308         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1309         {
1310                 // single character
1311                 com_token[len++] = *data++;
1312                 com_token[len] = 0;
1313                 *datapointer = data;
1314                 return true;
1315         }
1316         else
1317         {
1318                 // regular word
1319                 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1320                         if (len < (int)sizeof(com_token) - 1)
1321                                 com_token[len++] = *data;
1322                 com_token[len] = 0;
1323                 *datapointer = data;
1324                 return true;
1325         }
1326 }
1327
1328 /*
1329 ==============
1330 COM_ParseToken_Console
1331
1332 Parse a token out of a string, behaving like the qwcl console
1333 ==============
1334 */
1335 int COM_ParseToken_Console(const char **datapointer)
1336 {
1337         int len;
1338         const char *data = *datapointer;
1339
1340         len = 0;
1341         com_token[0] = 0;
1342
1343         if (!data)
1344         {
1345                 *datapointer = NULL;
1346                 return false;
1347         }
1348
1349 // skip whitespace
1350 skipwhite:
1351         for (;ISWHITESPACE(*data);data++)
1352         {
1353                 if (*data == 0)
1354                 {
1355                         // end of file
1356                         *datapointer = NULL;
1357                         return false;
1358                 }
1359         }
1360
1361         if (*data == '/' && data[1] == '/')
1362         {
1363                 // comment
1364                 while (*data && *data != '\n' && *data != '\r')
1365                         data++;
1366                 goto skipwhite;
1367         }
1368         else if (*data == '\"')
1369         {
1370                 // quoted string
1371                 for (data++;*data && *data != '\"';data++)
1372                 {
1373                         // allow escaped " and \ case
1374                         if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1375                                 data++;
1376                         if (len < (int)sizeof(com_token) - 1)
1377                                 com_token[len++] = *data;
1378                 }
1379                 com_token[len] = 0;
1380                 if (*data == '\"')
1381                         data++;
1382                 *datapointer = data;
1383         }
1384         else
1385         {
1386                 // regular word
1387                 for (;!ISWHITESPACE(*data);data++)
1388                         if (len < (int)sizeof(com_token) - 1)
1389                                 com_token[len++] = *data;
1390                 com_token[len] = 0;
1391                 *datapointer = data;
1392         }
1393
1394         return true;
1395 }
1396
1397
1398 /*
1399 ================
1400 COM_CheckParm
1401
1402 Returns the position (1 to argc-1) in the program's argument list
1403 where the given parameter apears, or 0 if not present
1404 ================
1405 */
1406 int COM_CheckParm (const char *parm)
1407 {
1408         int i;
1409
1410         for (i=1 ; i<com_argc ; i++)
1411         {
1412                 if (!com_argv[i])
1413                         continue;               // NEXTSTEP sometimes clears appkit vars.
1414                 if (!strcmp (parm,com_argv[i]))
1415                         return i;
1416         }
1417
1418         return 0;
1419 }
1420
1421 //===========================================================================
1422
1423 // Game mods
1424
1425 gamemode_t com_startupgamemode;
1426 gamemode_t com_startupgamegroup;
1427
1428 typedef struct gamemode_info_s
1429 {
1430         gamemode_t mode; // this gamemode
1431         gamemode_t group; // different games with same group can switch automatically when gamedirs change
1432         const char* prog_name; // not null
1433         const char* cmdline; // not null
1434         const char* gamename; // not null
1435         const char* gamedirname1; // not null
1436         const char* gamedirname2; // null
1437         const char* gamescreenshotname; // not nul
1438         const char* gameuserdirname; // not null
1439 } gamemode_info_t;
1440
1441 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1442 {// game                                basegame                                prog_name                       cmdline                         gamename                                basegame        modgame                 screenshot              userdir                            // commandline option
1443 { GAME_NORMAL,                  GAME_NORMAL,                    "",                                     "-quake",                       "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1444 { GAME_HIPNOTIC,                GAME_NORMAL,                    "hipnotic",                     "-hipnotic",            "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1445 { GAME_ROGUE,                   GAME_NORMAL,                    "rogue",                        "-rogue",                       "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1446 { GAME_NEHAHRA,                 GAME_NORMAL,                    "nehahra",                      "-nehahra",                     "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1447 { GAME_NEXUIZ,                  GAME_NEXUIZ,                    "nexuiz",                       "-nexuiz",                      "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz"                        }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1448 { GAME_XONOTIC,                 GAME_XONOTIC,                   "xonotic",                      "-xonotic",                     "Xonotic",                              "data",         NULL,                   "xonotic",              "xonotic"                       }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
1449 { GAME_TRANSFUSION,             GAME_TRANSFUSION,               "transfusion",          "-transfusion",         "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion"           }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1450 { GAME_GOODVSBAD2,              GAME_GOODVSBAD2,                "gvb2",                         "-goodvsbad2",          "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2"                          }, // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1451 { GAME_TEU,                             GAME_TEU,                               "teu",                          "-teu",                         "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu"                           }, // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1452 { GAME_BATTLEMECH,              GAME_BATTLEMECH,                "battlemech",           "-battlemech",          "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech"            }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1453 { GAME_ZYMOTIC,                 GAME_ZYMOTIC,                   "zymotic",                      "-zymotic",                     "Zymotic",                              "basezym",      NULL,                   "zymotic",              "zymotic"                       }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1454 { GAME_SETHERAL,                GAME_SETHERAL,                  "setheral",                     "-setheral",            "Setheral",                             "data",         NULL,                   "setheral",             "setheral"                      }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1455 { GAME_SOM,                             GAME_NORMAL,                    "som",                          "-som",                         "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces"            }, // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1456 { GAME_TENEBRAE,                GAME_NORMAL,                    "tenebrae",                     "-tenebrae",            "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1457 { GAME_NEOTERIC,                GAME_NORMAL,                    "neoteric",                     "-neoteric",            "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces"            }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1458 { GAME_OPENQUARTZ,              GAME_NORMAL,                    "openquartz",           "-openquartz",          "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces"            }, // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1459 { GAME_PRYDON,                  GAME_NORMAL,                    "prydon",                       "-prydon",                      "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces"            }, // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1460 { GAME_DELUXEQUAKE,             GAME_DELUXEQUAKE,               "dq",                           "-dq",                          "Deluxe Quake",                 "basedq",       "extradq",              "basedq",               "dq"                            }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1461 { GAME_THEHUNTED,               GAME_THEHUNTED,                 "thehunted",            "-thehunted",           "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted"                     }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1462 { GAME_DEFEATINDETAIL2, GAME_DEFEATINDETAIL2,   "did2",                         "-did2",                        "Defeat In Detail 2",   "data",         NULL,                   "did2_",                "did2"                          }, // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1463 { GAME_DARSANA,                 GAME_DARSANA,                   "darsana",                      "-darsana",                     "Darsana",                              "ddata",        NULL,                   "darsana",              "darsana"                       }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1464 { GAME_CONTAGIONTHEORY, GAME_CONTAGIONTHEORY,   "contagiontheory",      "-contagiontheory",     "Contagion Theory",             "ctdata",       NULL,                   "ct",                   "contagiontheory"       }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1465 { GAME_EDU2P,                   GAME_EDU2P,                             "edu2p",                        "-edu2p",                       "EDU2 Prototype",               "id1",          "edu2",                 "edu2_p",               "edu2prototype"         }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1466 { GAME_PROPHECY,                GAME_PROPHECY,                  "prophecy",                     "-prophecy",            "Prophecy",                             "data",         NULL,                   "prophecy",             "prophecy"                      }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
1467 { GAME_BLOODOMNICIDE,   GAME_BLOODOMNICIDE,             "omnicide",                     "-omnicide",            "Blood Omnicide",               "kain",         NULL,                   "omnicide",             "omnicide"                      }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
1468 { GAME_STEELSTORM,              GAME_STEELSTORM,                "steelstorm",           "-steelstorm",          "Steel-Storm",                  "gamedata",     NULL,                   "ss",                   "steelstorm"            }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
1469 { GAME_STRAPBOMB,               GAME_STRAPBOMB,                 "strapbomb",            "-strapbomb",           "Strap-on-bomb Car",    "id1",          NULL,                   "strap",                "strapbomb"                     }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
1470 { GAME_MOONHELM,                GAME_MOONHELM,                  "moonhelm",                     "-moonhelm",            "MoonHelm",                             "data",         NULL,                   "mh",                   "moonhelm"                      }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
1471 };
1472
1473 static void COM_SetGameType(int index);
1474 void COM_InitGameType (void)
1475 {
1476         char name [MAX_OSPATH];
1477         int i;
1478         int index = 0;
1479
1480         FS_StripExtension (com_argv[0], name, sizeof (name));
1481         COM_ToLowerString (name, name, sizeof (name));
1482
1483         // check executable filename for keywords
1484         for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1485                 if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
1486                 {
1487                         index = i;
1488                         break;
1489                 }
1490
1491         // check commandline options for keywords
1492         for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1493                 if (COM_CheckParm (gamemode_info[i].cmdline))
1494                 {
1495                         index = i;
1496                         break;
1497                 }
1498
1499         com_startupgamemode = gamemode_info[index].mode;
1500         com_startupgamegroup = gamemode_info[index].group;
1501         COM_SetGameType(index);
1502 }
1503
1504 void COM_ChangeGameTypeForGameDirs(void)
1505 {
1506         int i;
1507         int index = -1;
1508         // this will not not change the gamegroup
1509         // first check if a base game (single gamedir) matches
1510         for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1511         {
1512                 if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
1513                 {
1514                         index = i;
1515                         break;
1516                 }
1517         }
1518         // now that we have a base game, see if there is a matching derivative game (two gamedirs)
1519         if (fs_numgamedirs)
1520         {
1521                 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1522                 {
1523                         if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
1524                         {
1525                                 index = i;
1526                                 break;
1527                         }
1528                 }
1529         }
1530         // we now have a good guess at which game this is meant to be...
1531         if (index >= 0 && gamemode != gamemode_info[index].mode)
1532                 COM_SetGameType(index);
1533 }
1534
1535 static void COM_SetGameType(int index)
1536 {
1537         int i, t;
1538         if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
1539                 index = 0;
1540         gamemode = gamemode_info[index].mode;
1541         gamename = gamemode_info[index].gamename;
1542         gamedirname1 = gamemode_info[index].gamedirname1;
1543         gamedirname2 = gamemode_info[index].gamedirname2;
1544         gamescreenshotname = gamemode_info[index].gamescreenshotname;
1545         gameuserdirname = gamemode_info[index].gameuserdirname;
1546
1547         if (gamemode == com_startupgamemode)
1548         {
1549                 if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc)
1550                         gamename = com_argv[t+1];
1551                 if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < com_argc)
1552                         gamedirname1 = com_argv[t+1];
1553                 if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < com_argc)
1554                         gamedirname2 = *com_argv[t+1] ? com_argv[t+1] : NULL;
1555                 if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < com_argc)
1556                         gamescreenshotname = com_argv[t+1];
1557                 if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < com_argc)
1558                         gameuserdirname = com_argv[t+1];
1559         }
1560
1561         if (gamedirname2 && gamedirname2[0])
1562                 Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
1563         else
1564                 Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
1565         for (i = 0;i < fs_numgamedirs;i++)
1566         {
1567                 if (i == 0)
1568                         Con_Printf(", with mod gamedirs");
1569                 Con_Printf(" %s", fs_gamedirs[i]);
1570         }
1571         Con_Printf("\n");
1572 }
1573
1574
1575 /*
1576 ================
1577 COM_Init
1578 ================
1579 */
1580 void COM_Init_Commands (void)
1581 {
1582         int i, j, n;
1583         char com_cmdline[MAX_INPUTLINE];
1584
1585         Cvar_RegisterVariable (&registered);
1586         Cvar_RegisterVariable (&cmdline);
1587
1588         // reconstitute the command line for the cmdline externally visible cvar
1589         n = 0;
1590         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1591         {
1592                 i = 0;
1593                 if (strstr(com_argv[j], " "))
1594                 {
1595                         // arg contains whitespace, store quotes around it
1596                         com_cmdline[n++] = '\"';
1597                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1598                                 com_cmdline[n++] = com_argv[j][i++];
1599                         com_cmdline[n++] = '\"';
1600                 }
1601                 else
1602                 {
1603                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1604                                 com_cmdline[n++] = com_argv[j][i++];
1605                 }
1606                 if (n < ((int)sizeof(com_cmdline) - 1))
1607                         com_cmdline[n++] = ' ';
1608                 else
1609                         break;
1610         }
1611         com_cmdline[n] = 0;
1612         Cvar_Set ("cmdline", com_cmdline);
1613 }
1614
1615 /*
1616 ============
1617 va
1618
1619 does a varargs printf into a temp buffer, so I don't need to have
1620 varargs versions of all text functions.
1621 FIXME: make this buffer size safe someday
1622 ============
1623 */
1624 char *va(const char *format, ...)
1625 {
1626         va_list argptr;
1627         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1628         static char string[8][1024], *s;
1629         static int stringindex = 0;
1630
1631         s = string[stringindex];
1632         stringindex = (stringindex + 1) & 7;
1633         va_start (argptr, format);
1634         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1635         va_end (argptr);
1636
1637         return s;
1638 }
1639
1640
1641 //======================================
1642
1643 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1644
1645 #undef snprintf
1646 #undef vsnprintf
1647
1648 #ifdef WIN32
1649 # define snprintf _snprintf
1650 # define vsnprintf _vsnprintf
1651 #endif
1652
1653
1654 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1655 {
1656         va_list args;
1657         int result;
1658
1659         va_start (args, format);
1660         result = dpvsnprintf (buffer, buffersize, format, args);
1661         va_end (args);
1662
1663         return result;
1664 }
1665
1666
1667 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1668 {
1669         int result;
1670
1671 #if _MSC_VER >= 1400
1672         result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1673 #else
1674         result = vsnprintf (buffer, buffersize, format, args);
1675 #endif
1676         if (result < 0 || (size_t)result >= buffersize)
1677         {
1678                 buffer[buffersize - 1] = '\0';
1679                 return -1;
1680         }
1681
1682         return result;
1683 }
1684
1685
1686 //======================================
1687
1688 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1689 {
1690         if (size_out == 0)
1691                 return;
1692
1693         while (*in && size_out > 1)
1694         {
1695                 if (*in >= 'A' && *in <= 'Z')
1696                         *out++ = *in++ + 'a' - 'A';
1697                 else
1698                         *out++ = *in++;
1699                 size_out--;
1700         }
1701         *out = '\0';
1702 }
1703
1704 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1705 {
1706         if (size_out == 0)
1707                 return;
1708
1709         while (*in && size_out > 1)
1710         {
1711                 if (*in >= 'a' && *in <= 'z')
1712                         *out++ = *in++ + 'A' - 'a';
1713                 else
1714                         *out++ = *in++;
1715                 size_out--;
1716         }
1717         *out = '\0';
1718 }
1719
1720 int COM_StringBeginsWith(const char *s, const char *match)
1721 {
1722         for (;*s && *match;s++, match++)
1723                 if (*s != *match)
1724                         return false;
1725         return true;
1726 }
1727
1728 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1729 {
1730         int argc, commentprefixlength;
1731         char *tokenbufend;
1732         const char *l;
1733         argc = 0;
1734         tokenbufend = tokenbuf + tokenbufsize;
1735         l = *text;
1736         commentprefixlength = 0;
1737         if (commentprefix)
1738                 commentprefixlength = (int)strlen(commentprefix);
1739         while (*l && *l != '\n' && *l != '\r')
1740         {
1741                 if (!ISWHITESPACE(*l))
1742                 {
1743                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1744                         {
1745                                 while (*l && *l != '\n' && *l != '\r')
1746                                         l++;
1747                                 break;
1748                         }
1749                         if (argc >= maxargc)
1750                                 return -1;
1751                         argv[argc++] = tokenbuf;
1752                         if (*l == '"')
1753                         {
1754                                 l++;
1755                                 while (*l && *l != '"')
1756                                 {
1757                                         if (tokenbuf >= tokenbufend)
1758                                                 return -1;
1759                                         *tokenbuf++ = *l++;
1760                                 }
1761                                 if (*l == '"')
1762                                         l++;
1763                         }
1764                         else
1765                         {
1766                                 while (!ISWHITESPACE(*l))
1767                                 {
1768                                         if (tokenbuf >= tokenbufend)
1769                                                 return -1;
1770                                         *tokenbuf++ = *l++;
1771                                 }
1772                         }
1773                         if (tokenbuf >= tokenbufend)
1774                                 return -1;
1775                         *tokenbuf++ = 0;
1776                 }
1777                 else
1778                         l++;
1779         }
1780         // line endings:
1781         // UNIX: \n
1782         // Mac: \r
1783         // Windows: \r\n
1784         if (*l == '\r')
1785                 l++;
1786         if (*l == '\n')
1787                 l++;
1788         *text = l;
1789         return argc;
1790 }
1791
1792 /*
1793 ============
1794 COM_StringLengthNoColors
1795
1796 calculates the visible width of a color coded string.
1797
1798 *valid is filled with TRUE if the string is a valid colored string (that is, if
1799 it does not end with an unfinished color code). If it gets filled with FALSE, a
1800 fix would be adding a STRING_COLOR_TAG at the end of the string.
1801
1802 valid can be set to NULL if the caller doesn't care.
1803
1804 For size_s, specify the maximum number of characters from s to use, or 0 to use
1805 all characters until the zero terminator.
1806 ============
1807 */
1808 size_t
1809 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1810 {
1811         const char *end = size_s ? (s + size_s) : NULL;
1812         size_t len = 0;
1813         for(;;)
1814         {
1815                 switch((s == end) ? 0 : *s)
1816                 {
1817                         case 0:
1818                                 if(valid)
1819                                         *valid = TRUE;
1820                                 return len;
1821                         case STRING_COLOR_TAG:
1822                                 ++s;
1823                                 switch((s == end) ? 0 : *s)
1824                                 {
1825                                         case STRING_COLOR_RGB_TAG_CHAR:
1826                                                 if (s+1 != end && isxdigit(s[1]) &&
1827                                                         s+2 != end && isxdigit(s[2]) &&
1828                                                         s+3 != end && isxdigit(s[3]) )
1829                                                 {
1830                                                         s+=3;
1831                                                         break;
1832                                                 }
1833                                                 ++len; // STRING_COLOR_TAG
1834                                                 ++len; // STRING_COLOR_RGB_TAG_CHAR
1835                                                 break;
1836                                         case 0: // ends with unfinished color code!
1837                                                 ++len;
1838                                                 if(valid)
1839                                                         *valid = FALSE;
1840                                                 return len;
1841                                         case STRING_COLOR_TAG: // escaped ^
1842                                                 ++len;
1843                                                 break;
1844                                         case '0': case '1': case '2': case '3': case '4':
1845                                         case '5': case '6': case '7': case '8': case '9': // color code
1846                                                 break;
1847                                         default: // not a color code
1848                                                 ++len; // STRING_COLOR_TAG
1849                                                 ++len; // the character
1850                                                 break;
1851                                 }
1852                                 break;
1853                         default:
1854                                 ++len;
1855                                 break;
1856                 }
1857                 ++s;
1858         }
1859         // never get here
1860 }
1861
1862 /*
1863 ============
1864 COM_StringDecolorize
1865
1866 removes color codes from a string.
1867
1868 If escape_carets is true, the resulting string will be safe for printing. If
1869 escape_carets is false, the function will just strip color codes (for logging
1870 for example).
1871
1872 If the output buffer size did not suffice for converting, the function returns
1873 FALSE. Generally, if escape_carets is false, the output buffer needs
1874 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1875 bytes. In any case, the function makes sure that the resulting string is
1876 zero terminated.
1877
1878 For size_in, specify the maximum number of characters from in to use, or 0 to use
1879 all characters until the zero terminator.
1880 ============
1881 */
1882 qboolean
1883 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1884 {
1885 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1886         const char *end = size_in ? (in + size_in) : NULL;
1887         if(size_out < 1)
1888                 return FALSE;
1889         for(;;)
1890         {
1891                 switch((in == end) ? 0 : *in)
1892                 {
1893                         case 0:
1894                                 *out++ = 0;
1895                                 return TRUE;
1896                         case STRING_COLOR_TAG:
1897                                 ++in;
1898                                 switch((in == end) ? 0 : *in)
1899                                 {
1900                                         case STRING_COLOR_RGB_TAG_CHAR:
1901                                                 if (in+1 != end && isxdigit(in[1]) &&
1902                                                         in+2 != end && isxdigit(in[2]) &&
1903                                                         in+3 != end && isxdigit(in[3]) )
1904                                                 {
1905                                                         in+=3;
1906                                                         break;
1907                                                 }
1908                                                 APPEND(STRING_COLOR_TAG);
1909                                                 if(escape_carets)
1910                                                         APPEND(STRING_COLOR_TAG);
1911                                                 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1912                                                 break;
1913                                         case 0: // ends with unfinished color code!
1914                                                 APPEND(STRING_COLOR_TAG);
1915                                                 // finish the code by appending another caret when escaping
1916                                                 if(escape_carets)
1917                                                         APPEND(STRING_COLOR_TAG);
1918                                                 *out++ = 0;
1919                                                 return TRUE;
1920                                         case STRING_COLOR_TAG: // escaped ^
1921                                                 APPEND(STRING_COLOR_TAG);
1922                                                 // append a ^ twice when escaping
1923                                                 if(escape_carets)
1924                                                         APPEND(STRING_COLOR_TAG);
1925                                                 break;
1926                                         case '0': case '1': case '2': case '3': case '4':
1927                                         case '5': case '6': case '7': case '8': case '9': // color code
1928                                                 break;
1929                                         default: // not a color code
1930                                                 APPEND(STRING_COLOR_TAG);
1931                                                 APPEND(*in);
1932                                                 break;
1933                                 }
1934                                 break;
1935                         default:
1936                                 APPEND(*in);
1937                                 break;
1938                 }
1939                 ++in;
1940         }
1941         // never get here
1942 #undef APPEND
1943 }
1944
1945 // written by Elric, thanks Elric!
1946 char *SearchInfostring(const char *infostring, const char *key)
1947 {
1948         static char value [MAX_INPUTLINE];
1949         char crt_key [MAX_INPUTLINE];
1950         size_t value_ind, key_ind;
1951         char c;
1952
1953         if (*infostring++ != '\\')
1954                 return NULL;
1955
1956         value_ind = 0;
1957         for (;;)
1958         {
1959                 key_ind = 0;
1960
1961                 // Get the key name
1962                 for (;;)
1963                 {
1964                         c = *infostring++;
1965
1966                         if (c == '\0')
1967                                 return NULL;
1968                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1969                         {
1970                                 crt_key[key_ind] = '\0';
1971                                 break;
1972                         }
1973
1974                         crt_key[key_ind++] = c;
1975                 }
1976
1977                 // If it's the key we are looking for, save it in "value"
1978                 if (!strcmp(crt_key, key))
1979                 {
1980                         for (;;)
1981                         {
1982                                 c = *infostring++;
1983
1984                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1985                                 {
1986                                         value[value_ind] = '\0';
1987                                         return value;
1988                                 }
1989
1990                                 value[value_ind++] = c;
1991                         }
1992                 }
1993
1994                 // Else, skip the value
1995                 for (;;)
1996                 {
1997                         c = *infostring++;
1998
1999                         if (c == '\0')
2000                                 return NULL;
2001                         if (c == '\\')
2002                                 break;
2003                 }
2004         }
2005 }
2006
2007 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
2008 {
2009         int pos = 0, j;
2010         size_t keylength;
2011         if (!key)
2012                 key = "";
2013         keylength = strlen(key);
2014         if (valuelength < 1 || !value)
2015         {
2016                 Con_Printf("InfoString_GetValue: no room in value\n");
2017                 return;
2018         }
2019         value[0] = 0;
2020         if (strchr(key, '\\'))
2021         {
2022                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
2023                 return;
2024         }
2025         if (strchr(key, '\"'))
2026         {
2027                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
2028                 return;
2029         }
2030         if (!key[0])
2031         {
2032                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
2033                 return;
2034         }
2035         while (buffer[pos] == '\\')
2036         {
2037                 if (!memcmp(buffer + pos+1, key, keylength))
2038                 {
2039                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2040                         pos++;
2041                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2042                                 value[j] = buffer[pos+j];
2043                         value[j] = 0;
2044                         return;
2045                 }
2046                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2047                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2048         }
2049         // if we reach this point the key was not found
2050 }
2051
2052 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2053 {
2054         int pos = 0, pos2;
2055         size_t keylength;
2056         if (!key)
2057                 key = "";
2058         if (!value)
2059                 value = "";
2060         keylength = strlen(key);
2061         if (strchr(key, '\\') || strchr(value, '\\'))
2062         {
2063                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2064                 return;
2065         }
2066         if (strchr(key, '\"') || strchr(value, '\"'))
2067         {
2068                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2069                 return;
2070         }
2071         if (!key[0])
2072         {
2073                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2074                 return;
2075         }
2076         while (buffer[pos] == '\\')
2077         {
2078                 if (!memcmp(buffer + pos+1, key, keylength))
2079                         break;
2080                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2081                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2082         }
2083         // if we found the key, find the end of it because we will be replacing it
2084         pos2 = pos;
2085         if (buffer[pos] == '\\')
2086         {
2087                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2088                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2089         }
2090         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2091         {
2092                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2093                 return;
2094         }
2095         if (value && value[0])
2096         {
2097                 // set the key/value and append the remaining text
2098                 char tempbuffer[4096];
2099                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2100                 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2101         }
2102         else
2103         {
2104                 // just remove the key from the text
2105                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2106         }
2107 }
2108
2109 void InfoString_Print(char *buffer)
2110 {
2111         int i;
2112         char key[2048];
2113         char value[2048];
2114         while (*buffer)
2115         {
2116                 if (*buffer != '\\')
2117                 {
2118                         Con_Printf("InfoString_Print: corrupt string\n");
2119                         return;
2120                 }
2121                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2122                         if (i < (int)sizeof(key)-1)
2123                                 key[i++] = *buffer;
2124                 key[i] = 0;
2125                 if (*buffer != '\\')
2126                 {
2127                         Con_Printf("InfoString_Print: corrupt string\n");
2128                         return;
2129                 }
2130                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2131                         if (i < (int)sizeof(value)-1)
2132                                 value[i++] = *buffer;
2133                 value[i] = 0;
2134                 // empty value is an error case
2135                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2136         }
2137 }
2138
2139 //========================================================
2140 // strlcat and strlcpy, from OpenBSD
2141
2142 /*
2143  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2144  *
2145  * Permission to use, copy, modify, and distribute this software for any
2146  * purpose with or without fee is hereby granted, provided that the above
2147  * copyright notice and this permission notice appear in all copies.
2148  *
2149  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2150  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2151  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2152  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2153  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2154  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2155  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2156  */
2157
2158 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
2159 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
2160
2161
2162 #ifndef HAVE_STRLCAT
2163 size_t
2164 strlcat(char *dst, const char *src, size_t siz)
2165 {
2166         register char *d = dst;
2167         register const char *s = src;
2168         register size_t n = siz;
2169         size_t dlen;
2170
2171         /* Find the end of dst and adjust bytes left but don't go past end */
2172         while (n-- != 0 && *d != '\0')
2173                 d++;
2174         dlen = d - dst;
2175         n = siz - dlen;
2176
2177         if (n == 0)
2178                 return(dlen + strlen(s));
2179         while (*s != '\0') {
2180                 if (n != 1) {
2181                         *d++ = *s;
2182                         n--;
2183                 }
2184                 s++;
2185         }
2186         *d = '\0';
2187
2188         return(dlen + (s - src));       /* count does not include NUL */
2189 }
2190 #endif  // #ifndef HAVE_STRLCAT
2191
2192
2193 #ifndef HAVE_STRLCPY
2194 size_t
2195 strlcpy(char *dst, const char *src, size_t siz)
2196 {
2197         register char *d = dst;
2198         register const char *s = src;
2199         register size_t n = siz;
2200
2201         /* Copy as many bytes as will fit */
2202         if (n != 0 && --n != 0) {
2203                 do {
2204                         if ((*d++ = *s++) == 0)
2205                                 break;
2206                 } while (--n != 0);
2207         }
2208
2209         /* Not enough room in dst, add NUL and traverse rest of src */
2210         if (n == 0) {
2211                 if (siz != 0)
2212                         *d = '\0';              /* NUL-terminate dst */
2213                 while (*s++)
2214                         ;
2215         }
2216
2217         return(s - src - 1);    /* count does not include NUL */
2218 }
2219
2220 #endif  // #ifndef HAVE_STRLCPY
2221
2222 void FindFraction(double val, int *num, int *denom, int denomMax)
2223 {
2224         int i;
2225         double bestdiff;
2226         // initialize
2227         bestdiff = fabs(val);
2228         *num = 0;
2229         *denom = 1;
2230
2231         for(i = 1; i <= denomMax; ++i)
2232         {
2233                 int inum = (int) floor(0.5 + val * i);
2234                 double diff = fabs(val - inum / (double)i);
2235                 if(diff < bestdiff)
2236                 {
2237                         bestdiff = diff;
2238                         *num = inum;
2239                         *denom = i;
2240                 }
2241         }
2242 }
2243
2244 // decodes an XPM from C syntax
2245 char **XPM_DecodeString(const char *in)
2246 {
2247         static char *tokens[257];
2248         static char lines[257][512];
2249         size_t line = 0;
2250
2251         // skip until "{" token
2252         while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
2253
2254         // now, read in succession: string, comma-or-}
2255         while(COM_ParseToken_QuakeC(&in, false))
2256         {
2257                 tokens[line] = lines[line];
2258                 strlcpy(lines[line++], com_token, sizeof(lines[0]));
2259                 if(!COM_ParseToken_QuakeC(&in, false))
2260                         return NULL;
2261                 if(!strcmp(com_token, "}"))
2262                         break;
2263                 if(strcmp(com_token, ","))
2264                         return NULL;
2265                 if(line >= sizeof(tokens) / sizeof(tokens[0]))
2266                         return NULL;
2267         }
2268
2269         return tokens;
2270 }
2271
2272 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2273 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
2274 {
2275         unsigned char i0 = (bytes > 0) ? in[0] : 0;
2276         unsigned char i1 = (bytes > 1) ? in[1] : 0;
2277         unsigned char i2 = (bytes > 2) ? in[2] : 0;
2278         unsigned char o0 = base64[i0 >> 2];
2279         unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
2280         unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
2281         unsigned char o3 = base64[i2 & 077];
2282         out[0] = (bytes > 0) ? o0 : '?';
2283         out[1] = (bytes > 0) ? o1 : '?';
2284         out[2] = (bytes > 1) ? o2 : '=';
2285         out[3] = (bytes > 2) ? o3 : '=';
2286 }
2287
2288 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
2289 {
2290         size_t blocks, i;
2291         // expand the out-buffer
2292         blocks = (buflen + 2) / 3;
2293         if(blocks*4 > outbuflen)
2294                 return 0;
2295         for(i = blocks; i > 0; )
2296         {
2297                 --i;
2298                 base64_3to4(buf + 3*i, buf + 4*i, buflen - 3*i);
2299         }
2300         return blocks * 4;
2301 }