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