]> git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
framegroups: add support for group names specified as // comment
[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, float *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_BeginReading(sizebuf_t *sb)
409 {
410         sb->readcount = 0;
411         sb->badread = false;
412 }
413
414 int MSG_ReadLittleShort(sizebuf_t *sb)
415 {
416         if (sb->readcount+2 > sb->cursize)
417         {
418                 sb->badread = true;
419                 return -1;
420         }
421         sb->readcount += 2;
422         return (short)(sb->data[sb->readcount-2] | (sb->data[sb->readcount-1]<<8));
423 }
424
425 int MSG_ReadBigShort (sizebuf_t *sb)
426 {
427         if (sb->readcount+2 > sb->cursize)
428         {
429                 sb->badread = true;
430                 return -1;
431         }
432         sb->readcount += 2;
433         return (short)((sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1]);
434 }
435
436 int MSG_ReadLittleLong (sizebuf_t *sb)
437 {
438         if (sb->readcount+4 > sb->cursize)
439         {
440                 sb->badread = true;
441                 return -1;
442         }
443         sb->readcount += 4;
444         return sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24);
445 }
446
447 int MSG_ReadBigLong (sizebuf_t *sb)
448 {
449         if (sb->readcount+4 > sb->cursize)
450         {
451                 sb->badread = true;
452                 return -1;
453         }
454         sb->readcount += 4;
455         return (sb->data[sb->readcount-4]<<24) + (sb->data[sb->readcount-3]<<16) + (sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1];
456 }
457
458 float MSG_ReadLittleFloat (sizebuf_t *sb)
459 {
460         union
461         {
462                 float f;
463                 int l;
464         } dat;
465         if (sb->readcount+4 > sb->cursize)
466         {
467                 sb->badread = true;
468                 return -1;
469         }
470         sb->readcount += 4;
471         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);
472         return dat.f;
473 }
474
475 float MSG_ReadBigFloat (sizebuf_t *sb)
476 {
477         union
478         {
479                 float f;
480                 int l;
481         } dat;
482         if (sb->readcount+4 > sb->cursize)
483         {
484                 sb->badread = true;
485                 return -1;
486         }
487         sb->readcount += 4;
488         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];
489         return dat.f;
490 }
491
492 char *MSG_ReadString (sizebuf_t *sb, char *string, size_t maxstring)
493 {
494         int c;
495         size_t l = 0;
496         // read string into sbfer, but only store as many characters as will fit
497         while ((c = MSG_ReadByte(sb)) > 0)
498                 if (l < maxstring - 1)
499                         string[l++] = c;
500         string[l] = 0;
501         return string;
502 }
503
504 int MSG_ReadBytes (sizebuf_t *sb, int numbytes, unsigned char *out)
505 {
506         int l, c;
507         for (l = 0;l < numbytes && (c = MSG_ReadByte(sb)) != -1;l++)
508                 out[l] = c;
509         return l;
510 }
511
512 float MSG_ReadCoord13i (sizebuf_t *sb)
513 {
514         return MSG_ReadLittleShort(sb) * (1.0/8.0);
515 }
516
517 float MSG_ReadCoord16i (sizebuf_t *sb)
518 {
519         return (signed short) MSG_ReadLittleShort(sb);
520 }
521
522 float MSG_ReadCoord32f (sizebuf_t *sb)
523 {
524         return MSG_ReadLittleFloat(sb);
525 }
526
527 float MSG_ReadCoord (sizebuf_t *sb, protocolversion_t protocol)
528 {
529         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
530                 return MSG_ReadCoord13i(sb);
531         else if (protocol == PROTOCOL_DARKPLACES1)
532                 return MSG_ReadCoord32f(sb);
533         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
534                 return MSG_ReadCoord16i(sb);
535         else
536                 return MSG_ReadCoord32f(sb);
537 }
538
539 void MSG_ReadVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
540 {
541         v[0] = MSG_ReadCoord(sb, protocol);
542         v[1] = MSG_ReadCoord(sb, protocol);
543         v[2] = MSG_ReadCoord(sb, protocol);
544 }
545
546 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
547 float MSG_ReadAngle8i (sizebuf_t *sb)
548 {
549         return (signed char) MSG_ReadByte (sb) * (360.0/256.0);
550 }
551
552 float MSG_ReadAngle16i (sizebuf_t *sb)
553 {
554         return (signed short)MSG_ReadShort (sb) * (360.0/65536.0);
555 }
556
557 float MSG_ReadAngle32f (sizebuf_t *sb)
558 {
559         return MSG_ReadFloat (sb);
560 }
561
562 float MSG_ReadAngle (sizebuf_t *sb, protocolversion_t protocol)
563 {
564         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)
565                 return MSG_ReadAngle8i (sb);
566         else
567                 return MSG_ReadAngle16i (sb);
568 }
569
570
571 //===========================================================================
572
573 void SZ_Clear (sizebuf_t *buf)
574 {
575         buf->cursize = 0;
576 }
577
578 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
579 {
580         unsigned char *data;
581
582         if (buf->cursize + length > buf->maxsize)
583         {
584                 if (!buf->allowoverflow)
585                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
586
587                 if (length > buf->maxsize)
588                         Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
589
590                 buf->overflowed = true;
591                 Con_Print("SZ_GetSpace: overflow\n");
592                 SZ_Clear (buf);
593         }
594
595         data = buf->data + buf->cursize;
596         buf->cursize += length;
597
598         return data;
599 }
600
601 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
602 {
603         memcpy (SZ_GetSpace(buf,length),data,length);
604 }
605
606 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
607 // attention, it has been eradicated from here, its only (former) use in
608 // all of darkplaces.
609
610 static const char *hexchar = "0123456789ABCDEF";
611 void Com_HexDumpToConsole(const unsigned char *data, int size)
612 {
613         int i, j, n;
614         char text[1024];
615         char *cur, *flushpointer;
616         const unsigned char *d;
617         cur = text;
618         flushpointer = text + 512;
619         for (i = 0;i < size;)
620         {
621                 n = 16;
622                 if (n > size - i)
623                         n = size - i;
624                 d = data + i;
625                 // print offset
626                 *cur++ = hexchar[(i >> 12) & 15];
627                 *cur++ = hexchar[(i >>  8) & 15];
628                 *cur++ = hexchar[(i >>  4) & 15];
629                 *cur++ = hexchar[(i >>  0) & 15];
630                 *cur++ = ':';
631                 // print hex
632                 for (j = 0;j < 16;j++)
633                 {
634                         if (j < n)
635                         {
636                                 *cur++ = hexchar[(d[j] >> 4) & 15];
637                                 *cur++ = hexchar[(d[j] >> 0) & 15];
638                         }
639                         else
640                         {
641                                 *cur++ = ' ';
642                                 *cur++ = ' ';
643                         }
644                         if ((j & 3) == 3)
645                                 *cur++ = ' ';
646                 }
647                 // print text
648                 for (j = 0;j < 16;j++)
649                 {
650                         if (j < n)
651                         {
652                                 // color change prefix character has to be treated specially
653                                 if (d[j] == STRING_COLOR_TAG)
654                                 {
655                                         *cur++ = STRING_COLOR_TAG;
656                                         *cur++ = STRING_COLOR_TAG;
657                                 }
658                                 else if (d[j] >= (unsigned char) ' ')
659                                         *cur++ = d[j];
660                                 else
661                                         *cur++ = '.';
662                         }
663                         else
664                                 *cur++ = ' ';
665                 }
666                 *cur++ = '\n';
667                 i += n;
668                 if (cur >= flushpointer || i >= size)
669                 {
670                         *cur++ = 0;
671                         Con_Print(text);
672                         cur = text;
673                 }
674         }
675 }
676
677 void SZ_HexDumpToConsole(const sizebuf_t *buf)
678 {
679         Com_HexDumpToConsole(buf->data, buf->cursize);
680 }
681
682
683 //============================================================================
684
685 /*
686 ==============
687 COM_Wordwrap
688
689 Word wraps a string. The wordWidth function is guaranteed to be called exactly
690 once for each word in the string, so it may be stateful, no idea what that
691 would be good for any more. At the beginning of the string, it will be called
692 for the char 0 to initialize a clean state, and then once with the string " "
693 (a space) so the routine knows how long a space is.
694
695 In case no single character fits into the given width, the wordWidth function
696 must return the width of exactly one character.
697
698 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
699
700 The sum of the return values of the processLine function will be returned.
701 ==============
702 */
703 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
704 {
705         // Logic is as follows:
706         //
707         // For each word or whitespace:
708         //   Newline found? Output current line, advance to next line. This is not a continuation. Continue.
709         //   Space found? Always add it to the current line, no matter if it fits.
710         //   Word found? Check if current line + current word fits.
711         //     If it fits, append it. Continue.
712         //     If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
713
714         qboolean isContinuation = false;
715         float spaceWidth;
716         const char *startOfLine = string;
717         const char *cursor = string;
718         const char *end = string + length;
719         float spaceUsedInLine = 0;
720         float spaceUsedForWord;
721         int result = 0;
722         size_t wordLen;
723         size_t dummy;
724
725         dummy = 0;
726         wordWidth(passthroughCW, NULL, &dummy, -1);
727         dummy = 1;
728         spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
729
730         for(;;)
731         {
732                 char ch = (cursor < end) ? *cursor : 0;
733                 switch(ch)
734                 {
735                         case 0: // end of string
736                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
737                                 isContinuation = false;
738                                 goto out;
739                         case '\n': // end of line
740                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
741                                 isContinuation = false;
742                                 ++cursor;
743                                 startOfLine = cursor;
744                                 break;
745                         case ' ': // space
746                                 ++cursor;
747                                 spaceUsedInLine += spaceWidth;
748                                 break;
749                         default: // word
750                                 wordLen = 1;
751                                 while(cursor + wordLen < end)
752                                 {
753                                         switch(cursor[wordLen])
754                                         {
755                                                 case 0:
756                                                 case '\n':
757                                                 case ' ':
758                                                         goto out_inner;
759                                                 default:
760                                                         ++wordLen;
761                                                         break;
762                                         }
763                                 }
764                                 out_inner:
765                                 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
766                                 if(wordLen < 1) // cannot happen according to current spec of wordWidth
767                                 {
768                                         wordLen = 1;
769                                         spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
770                                 }
771                                 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
772                                 {
773                                         // we can simply append it
774                                         cursor += wordLen;
775                                         spaceUsedInLine += spaceUsedForWord;
776                                 }
777                                 else
778                                 {
779                                         // output current line
780                                         result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
781                                         isContinuation = true;
782                                         startOfLine = cursor;
783                                         cursor += wordLen;
784                                         spaceUsedInLine = continuationWidth + spaceUsedForWord;
785                                 }
786                 }
787         }
788         out:
789
790         return result;
791
792 /*
793         qboolean isContinuation = false;
794         float currentWordSpace = 0;
795         const char *currentWord = 0;
796         float minReserve = 0;
797
798         float spaceUsedInLine = 0;
799         const char *currentLine = 0;
800         const char *currentLineEnd = 0;
801         float currentLineFinalWhitespace = 0;
802         const char *p;
803
804         int result = 0;
805         minReserve = charWidth(passthroughCW, 0);
806         minReserve += charWidth(passthroughCW, ' ');
807
808         if(maxWidth < continuationWidth + minReserve)
809                 maxWidth = continuationWidth + minReserve;
810
811         charWidth(passthroughCW, 0);
812
813         for(p = string; p < string + length; ++p)
814         {
815                 char c = *p;
816                 float w = charWidth(passthroughCW, c);
817
818                 if(!currentWord)
819                 {
820                         currentWord = p;
821                         currentWordSpace = 0;
822                 }
823
824                 if(!currentLine)
825                 {
826                         currentLine = p;
827                         spaceUsedInLine = isContinuation ? continuationWidth : 0;
828                         currentLineEnd = 0;
829                 }
830
831                 if(c == ' ')
832                 {
833                         // 1. I can add the word AND a space - then just append it.
834                         if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
835                         {
836                                 currentLineEnd = p; // note: space not included here
837                                 currentLineFinalWhitespace = w;
838                                 spaceUsedInLine += currentWordSpace + w;
839                         }
840                         // 2. I can just add the word - then append it, output current line and go to next one.
841                         else if(spaceUsedInLine + currentWordSpace <= maxWidth)
842                         {
843                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
844                                 currentLine = 0;
845                                 isContinuation = true;
846                         }
847                         // 3. Otherwise, output current line and go to next one, where I can add the word.
848                         else if(continuationWidth + currentWordSpace + w <= maxWidth)
849                         {
850                                 if(currentLineEnd)
851                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
852                                 currentLine = currentWord;
853                                 spaceUsedInLine = continuationWidth + currentWordSpace + w;
854                                 currentLineEnd = p;
855                                 currentLineFinalWhitespace = w;
856                                 isContinuation = true;
857                         }
858                         // 4. We can't even do that? Then output both current and next word as new lines.
859                         else
860                         {
861                                 if(currentLineEnd)
862                                 {
863                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
864                                         isContinuation = true;
865                                 }
866                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
867                                 currentLine = 0;
868                                 isContinuation = true;
869                         }
870                         currentWord = 0;
871                 }
872                 else if(c == '\n')
873                 {
874                         // 1. I can add the word - then do it.
875                         if(spaceUsedInLine + currentWordSpace <= maxWidth)
876                         {
877                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
878                         }
879                         // 2. Otherwise, output current line, next one and make tabula rasa.
880                         else
881                         {
882                                 if(currentLineEnd)
883                                 {
884                                         processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
885                                         isContinuation = true;
886                                 }
887                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
888                         }
889                         currentWord = 0;
890                         currentLine = 0;
891                         isContinuation = false;
892                 }
893                 else
894                 {
895                         currentWordSpace += w;
896                         if(
897                                 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
898                                 &&
899                                 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
900                         )
901                         {
902                                 // this word cannot join ANY line...
903                                 // so output the current line...
904                                 if(currentLineEnd)
905                                 {
906                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
907                                         isContinuation = true;
908                                 }
909
910                                 // then this word's beginning...
911                                 if(isContinuation)
912                                 {
913                                         // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
914                                         float pieceWidth = maxWidth - continuationWidth;
915                                         const char *pos = currentWord;
916                                         currentWordSpace = 0;
917
918                                         // reset the char width function to a state where no kerning occurs (start of word)
919                                         charWidth(passthroughCW, ' ');
920                                         while(pos <= p)
921                                         {
922                                                 float w = charWidth(passthroughCW, *pos);
923                                                 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
924                                                 {
925                                                         // print everything until it
926                                                         result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
927                                                         // go to here
928                                                         currentWord = pos;
929                                                         currentWordSpace = 0;
930                                                 }
931                                                 currentWordSpace += w;
932                                                 ++pos;
933                                         }
934                                         // now we have a currentWord that fits... set up its next line
935                                         // currentWordSpace has been set
936                                         // currentWord has been set
937                                         spaceUsedInLine = continuationWidth;
938                                         currentLine = currentWord;
939                                         currentLineEnd = 0;
940                                         isContinuation = true;
941                                 }
942                                 else
943                                 {
944                                         // we have a guarantee that it will fix (see if clause)
945                                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
946
947                                         // and use the rest of this word as new start of a line
948                                         currentWordSpace = w;
949                                         currentWord = p;
950                                         spaceUsedInLine = continuationWidth;
951                                         currentLine = p;
952                                         currentLineEnd = 0;
953                                         isContinuation = true;
954                                 }
955                         }
956                 }
957         }
958
959         if(!currentWord)
960         {
961                 currentWord = p;
962                 currentWordSpace = 0;
963         }
964
965         if(currentLine) // Same procedure as \n
966         {
967                 // Can I append the current word?
968                 if(spaceUsedInLine + currentWordSpace <= maxWidth)
969                         result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
970                 else
971                 {
972                         if(currentLineEnd)
973                         {
974                                 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
975                                 isContinuation = true;
976                         }
977                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
978                 }
979         }
980
981         return result;
982 */
983 }
984
985 /*
986 ==============
987 COM_ParseToken_Simple
988
989 Parse a token out of a string
990 ==============
991 */
992 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments)
993 {
994         int len;
995         int c;
996         const char *data = *datapointer;
997
998         len = 0;
999         com_token[0] = 0;
1000
1001         if (!data)
1002         {
1003                 *datapointer = NULL;
1004                 return false;
1005         }
1006
1007 // skip whitespace
1008 skipwhite:
1009         // line endings:
1010         // UNIX: \n
1011         // Mac: \r
1012         // Windows: \r\n
1013         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1014         {
1015                 if (*data == 0)
1016                 {
1017                         // end of file
1018                         *datapointer = NULL;
1019                         return false;
1020                 }
1021         }
1022
1023         // handle Windows line ending
1024         if (data[0] == '\r' && data[1] == '\n')
1025                 data++;
1026
1027         if (parsecomments && data[0] == '/' && data[1] == '/')
1028         {
1029                 // comment
1030                 while (*data && *data != '\n' && *data != '\r')
1031                         data++;
1032                 goto skipwhite;
1033         }
1034         else if (parsecomments && data[0] == '/' && data[1] == '*')
1035         {
1036                 // comment
1037                 data++;
1038                 while (*data && (data[0] != '*' || data[1] != '/'))
1039                         data++;
1040                 if (*data)
1041                         data++;
1042                 if (*data)
1043                         data++;
1044                 goto skipwhite;
1045         }
1046         else if (*data == '\"')
1047         {
1048                 // quoted string
1049                 for (data++;*data && *data != '\"';data++)
1050                 {
1051                         c = *data;
1052                         if (*data == '\\' && parsebackslash)
1053                         {
1054                                 data++;
1055                                 c = *data;
1056                                 if (c == 'n')
1057                                         c = '\n';
1058                                 else if (c == 't')
1059                                         c = '\t';
1060                         }
1061                         if (len < (int)sizeof(com_token) - 1)
1062                                 com_token[len++] = c;
1063                 }
1064                 com_token[len] = 0;
1065                 if (*data == '\"')
1066                         data++;
1067                 *datapointer = data;
1068                 return true;
1069         }
1070         else if (*data == '\r')
1071         {
1072                 // translate Mac line ending to UNIX
1073                 com_token[len++] = '\n';data++;
1074                 com_token[len] = 0;
1075                 *datapointer = data;
1076                 return true;
1077         }
1078         else if (*data == '\n')
1079         {
1080                 // single character
1081                 com_token[len++] = *data++;
1082                 com_token[len] = 0;
1083                 *datapointer = data;
1084                 return true;
1085         }
1086         else
1087         {
1088                 // regular word
1089                 for (;!ISWHITESPACE(*data);data++)
1090                         if (len < (int)sizeof(com_token) - 1)
1091                                 com_token[len++] = *data;
1092                 com_token[len] = 0;
1093                 *datapointer = data;
1094                 return true;
1095         }
1096 }
1097
1098 /*
1099 ==============
1100 COM_ParseToken_QuakeC
1101
1102 Parse a token out of a string
1103 ==============
1104 */
1105 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1106 {
1107         int len;
1108         int c;
1109         const char *data = *datapointer;
1110
1111         len = 0;
1112         com_token[0] = 0;
1113
1114         if (!data)
1115         {
1116                 *datapointer = NULL;
1117                 return false;
1118         }
1119
1120 // skip whitespace
1121 skipwhite:
1122         // line endings:
1123         // UNIX: \n
1124         // Mac: \r
1125         // Windows: \r\n
1126         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1127         {
1128                 if (*data == 0)
1129                 {
1130                         // end of file
1131                         *datapointer = NULL;
1132                         return false;
1133                 }
1134         }
1135
1136         // handle Windows line ending
1137         if (data[0] == '\r' && data[1] == '\n')
1138                 data++;
1139
1140         if (data[0] == '/' && data[1] == '/')
1141         {
1142                 // comment
1143                 while (*data && *data != '\n' && *data != '\r')
1144                         data++;
1145                 goto skipwhite;
1146         }
1147         else if (data[0] == '/' && data[1] == '*')
1148         {
1149                 // comment
1150                 data++;
1151                 while (*data && (data[0] != '*' || data[1] != '/'))
1152                         data++;
1153                 if (*data)
1154                         data++;
1155                 if (*data)
1156                         data++;
1157                 goto skipwhite;
1158         }
1159         else if (*data == '\"' || *data == '\'')
1160         {
1161                 // quoted string
1162                 char quote = *data;
1163                 for (data++;*data && *data != quote;data++)
1164                 {
1165                         c = *data;
1166                         if (*data == '\\')
1167                         {
1168                                 data++;
1169                                 c = *data;
1170                                 if (c == 'n')
1171                                         c = '\n';
1172                                 else if (c == 't')
1173                                         c = '\t';
1174                         }
1175                         if (len < (int)sizeof(com_token) - 1)
1176                                 com_token[len++] = c;
1177                 }
1178                 com_token[len] = 0;
1179                 if (*data == quote)
1180                         data++;
1181                 *datapointer = data;
1182                 return true;
1183         }
1184         else if (*data == '\r')
1185         {
1186                 // translate Mac line ending to UNIX
1187                 com_token[len++] = '\n';data++;
1188                 com_token[len] = 0;
1189                 *datapointer = data;
1190                 return true;
1191         }
1192         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1193         {
1194                 // single character
1195                 com_token[len++] = *data++;
1196                 com_token[len] = 0;
1197                 *datapointer = data;
1198                 return true;
1199         }
1200         else
1201         {
1202                 // regular word
1203                 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1204                         if (len < (int)sizeof(com_token) - 1)
1205                                 com_token[len++] = *data;
1206                 com_token[len] = 0;
1207                 *datapointer = data;
1208                 return true;
1209         }
1210 }
1211
1212 /*
1213 ==============
1214 COM_ParseToken_VM_Tokenize
1215
1216 Parse a token out of a string
1217 ==============
1218 */
1219 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1220 {
1221         int len;
1222         int c;
1223         const char *data = *datapointer;
1224
1225         len = 0;
1226         com_token[0] = 0;
1227
1228         if (!data)
1229         {
1230                 *datapointer = NULL;
1231                 return false;
1232         }
1233
1234 // skip whitespace
1235 skipwhite:
1236         // line endings:
1237         // UNIX: \n
1238         // Mac: \r
1239         // Windows: \r\n
1240         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1241         {
1242                 if (*data == 0)
1243                 {
1244                         // end of file
1245                         *datapointer = NULL;
1246                         return false;
1247                 }
1248         }
1249
1250         // handle Windows line ending
1251         if (data[0] == '\r' && data[1] == '\n')
1252                 data++;
1253
1254         if (data[0] == '/' && data[1] == '/')
1255         {
1256                 // comment
1257                 while (*data && *data != '\n' && *data != '\r')
1258                         data++;
1259                 goto skipwhite;
1260         }
1261         else if (data[0] == '/' && data[1] == '*')
1262         {
1263                 // comment
1264                 data++;
1265                 while (*data && (data[0] != '*' || data[1] != '/'))
1266                         data++;
1267                 if (*data)
1268                         data++;
1269                 if (*data)
1270                         data++;
1271                 goto skipwhite;
1272         }
1273         else if (*data == '\"' || *data == '\'')
1274         {
1275                 char quote = *data;
1276                 // quoted string
1277                 for (data++;*data && *data != quote;data++)
1278                 {
1279                         c = *data;
1280                         if (*data == '\\')
1281                         {
1282                                 data++;
1283                                 c = *data;
1284                                 if (c == 'n')
1285                                         c = '\n';
1286                                 else if (c == 't')
1287                                         c = '\t';
1288                         }
1289                         if (len < (int)sizeof(com_token) - 1)
1290                                 com_token[len++] = c;
1291                 }
1292                 com_token[len] = 0;
1293                 if (*data == quote)
1294                         data++;
1295                 *datapointer = data;
1296                 return true;
1297         }
1298         else if (*data == '\r')
1299         {
1300                 // translate Mac line ending to UNIX
1301                 com_token[len++] = '\n';data++;
1302                 com_token[len] = 0;
1303                 *datapointer = data;
1304                 return true;
1305         }
1306         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1307         {
1308                 // single character
1309                 com_token[len++] = *data++;
1310                 com_token[len] = 0;
1311                 *datapointer = data;
1312                 return true;
1313         }
1314         else
1315         {
1316                 // regular word
1317                 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1318                         if (len < (int)sizeof(com_token) - 1)
1319                                 com_token[len++] = *data;
1320                 com_token[len] = 0;
1321                 *datapointer = data;
1322                 return true;
1323         }
1324 }
1325
1326 /*
1327 ==============
1328 COM_ParseToken_Console
1329
1330 Parse a token out of a string, behaving like the qwcl console
1331 ==============
1332 */
1333 int COM_ParseToken_Console(const char **datapointer)
1334 {
1335         int len;
1336         const char *data = *datapointer;
1337
1338         len = 0;
1339         com_token[0] = 0;
1340
1341         if (!data)
1342         {
1343                 *datapointer = NULL;
1344                 return false;
1345         }
1346
1347 // skip whitespace
1348 skipwhite:
1349         for (;ISWHITESPACE(*data);data++)
1350         {
1351                 if (*data == 0)
1352                 {
1353                         // end of file
1354                         *datapointer = NULL;
1355                         return false;
1356                 }
1357         }
1358
1359         if (*data == '/' && data[1] == '/')
1360         {
1361                 // comment
1362                 while (*data && *data != '\n' && *data != '\r')
1363                         data++;
1364                 goto skipwhite;
1365         }
1366         else if (*data == '\"')
1367         {
1368                 // quoted string
1369                 for (data++;*data && *data != '\"';data++)
1370                 {
1371                         // allow escaped " and \ case
1372                         if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1373                                 data++;
1374                         if (len < (int)sizeof(com_token) - 1)
1375                                 com_token[len++] = *data;
1376                 }
1377                 com_token[len] = 0;
1378                 if (*data == '\"')
1379                         data++;
1380                 *datapointer = data;
1381         }
1382         else
1383         {
1384                 // regular word
1385                 for (;!ISWHITESPACE(*data);data++)
1386                         if (len < (int)sizeof(com_token) - 1)
1387                                 com_token[len++] = *data;
1388                 com_token[len] = 0;
1389                 *datapointer = data;
1390         }
1391
1392         return true;
1393 }
1394
1395
1396 /*
1397 ================
1398 COM_CheckParm
1399
1400 Returns the position (1 to argc-1) in the program's argument list
1401 where the given parameter apears, or 0 if not present
1402 ================
1403 */
1404 int COM_CheckParm (const char *parm)
1405 {
1406         int i;
1407
1408         for (i=1 ; i<com_argc ; i++)
1409         {
1410                 if (!com_argv[i])
1411                         continue;               // NEXTSTEP sometimes clears appkit vars.
1412                 if (!strcmp (parm,com_argv[i]))
1413                         return i;
1414         }
1415
1416         return 0;
1417 }
1418
1419 //===========================================================================
1420
1421 // Game mods
1422
1423 gamemode_t com_startupgamemode;
1424 gamemode_t com_startupgamegroup;
1425
1426 typedef struct gamemode_info_s
1427 {
1428         gamemode_t mode; // this gamemode
1429         gamemode_t group; // different games with same group can switch automatically when gamedirs change
1430         const char* prog_name; // not null
1431         const char* cmdline; // not null
1432         const char* gamename; // not null
1433         const char* gamedirname1; // not null
1434         const char* gamedirname2; // null
1435         const char* gamescreenshotname; // not nul
1436         const char* gameuserdirname; // not null
1437 } gamemode_info_t;
1438
1439 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1440 {// game                                basegame                                prog_name                       cmdline                         gamename                                basegame        modgame                 screenshot              userdir                            // commandline option
1441 { GAME_NORMAL,                  GAME_NORMAL,                    "",                                     "-quake",                       "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1442 { GAME_HIPNOTIC,                GAME_NORMAL,                    "hipnotic",                     "-hipnotic",            "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1443 { GAME_ROGUE,                   GAME_NORMAL,                    "rogue",                        "-rogue",                       "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1444 { GAME_NEHAHRA,                 GAME_NORMAL,                    "nehahra",                      "-nehahra",                     "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1445 { GAME_NEXUIZ,                  GAME_NEXUIZ,                    "nexuiz",                       "-nexuiz",                      "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz"                        }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1446 { GAME_XONOTIC,                 GAME_XONOTIC,                   "xonotic",                      "-xonotic",                     "Xonotic",                              "data",         NULL,                   "xonotic",              "xonotic"                       }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
1447 { GAME_TRANSFUSION,             GAME_TRANSFUSION,               "transfusion",          "-transfusion",         "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion"           }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1448 { 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
1449 { 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)
1450 { GAME_BATTLEMECH,              GAME_BATTLEMECH,                "battlemech",           "-battlemech",          "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech"            }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1451 { GAME_ZYMOTIC,                 GAME_ZYMOTIC,                   "zymotic",                      "-zymotic",                     "Zymotic",                              "basezym",      NULL,                   "zymotic",              "zymotic"                       }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1452 { GAME_SETHERAL,                GAME_SETHERAL,                  "setheral",                     "-setheral",            "Setheral",                             "data",         NULL,                   "setheral",             "setheral"                      }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1453 { GAME_SOM,                             GAME_NORMAL,                    "sonofman",                     "-som",                         "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces"            }, // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1454 { 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)
1455 { GAME_NEOTERIC,                GAME_NORMAL,                    "neoteric",                     "-neoteric",            "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces"            }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1456 { 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
1457 { GAME_PRYDON,                  GAME_NORMAL,                    "prydon",                       "-prydon",                      "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces"            }, // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1458 { GAME_DELUXEQUAKE,             GAME_DELUXEQUAKE,               "dq",                           "-dq",                          "Deluxe Quake",                 "basedq",       "extradq",              "basedq",               "dq"                            }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1459 { GAME_THEHUNTED,               GAME_THEHUNTED,                 "thehunted",            "-thehunted",           "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted"                     }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1460 { GAME_DEFEATINDETAIL2, GAME_DEFEATINDETAIL2,   "did2",                         "-did2",                        "Defeat In Detail 2",   "data",         NULL,                   "did2_",                "did2"                          }, // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1461 { GAME_DARSANA,                 GAME_DARSANA,                   "darsana",                      "-darsana",                     "Darsana",                              "ddata",        NULL,                   "darsana",              "darsana"                       }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1462 { GAME_CONTAGIONTHEORY, GAME_CONTAGIONTHEORY,   "contagiontheory",      "-contagiontheory",     "Contagion Theory",             "ctdata",       NULL,                   "ct",                   "contagiontheory"       }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1463 { GAME_EDU2P,                   GAME_EDU2P,                             "edu2p",                        "-edu2p",                       "EDU2 Prototype",               "id1",          "edu2",                 "edu2_p",               "edu2prototype"         }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1464 { GAME_PROPHECY,                GAME_PROPHECY,                  "prophecy",                     "-prophecy",            "Prophecy",                             "data",         NULL,                   "prophecy",             "prophecy"                      }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
1465 { GAME_BLOODOMNICIDE,   GAME_BLOODOMNICIDE,             "omnicide",                     "-omnicide",            "Blood Omnicide",               "kain",         NULL,                   "omnicide",             "omnicide"                      }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
1466 { GAME_STEELSTORM,              GAME_STEELSTORM,                "steelstorm",           "-steelstorm",          "Steel-Storm",                  "gamedata",     NULL,                   "ss",                   "steelstorm"            }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
1467 { GAME_STRAPBOMB,               GAME_STRAPBOMB,                 "strapbomb",            "-strapbomb",           "Strap-on-bomb Car",    "id1",          NULL,                   "strap",                "strapbomb"                     }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
1468 { GAME_MOONHELM,                GAME_MOONHELM,                  "moonhelm",                     "-moonhelm",            "MoonHelm",                             "data",         NULL,                   "mh",                   "moonhelm"                      }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
1469 };
1470
1471 static void COM_SetGameType(int index);
1472 void COM_InitGameType (void)
1473 {
1474         char name [MAX_OSPATH];
1475         int i;
1476         int index = 0;
1477
1478         // check executable filename for keywords, but do it SMARTLY - only check the last path element
1479         FS_StripExtension(FS_FileWithoutPath(com_argv[0]), name, sizeof (name));
1480         COM_ToLowerString(name, name, sizeof (name));
1481         for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1482                 if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
1483                 {
1484                         index = i;
1485                         break;
1486                 }
1487
1488         // check commandline options for keywords
1489         for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1490                 if (COM_CheckParm (gamemode_info[i].cmdline))
1491                 {
1492                         index = i;
1493                         break;
1494                 }
1495
1496         com_startupgamemode = gamemode_info[index].mode;
1497         com_startupgamegroup = gamemode_info[index].group;
1498         COM_SetGameType(index);
1499 }
1500
1501 void COM_ChangeGameTypeForGameDirs(void)
1502 {
1503         int i;
1504         int index = -1;
1505         // this will not not change the gamegroup
1506         // first check if a base game (single gamedir) matches
1507         for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1508         {
1509                 if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
1510                 {
1511                         index = i;
1512                         break;
1513                 }
1514         }
1515         // now that we have a base game, see if there is a matching derivative game (two gamedirs)
1516         if (fs_numgamedirs)
1517         {
1518                 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1519                 {
1520                         if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
1521                         {
1522                                 index = i;
1523                                 break;
1524                         }
1525                 }
1526         }
1527         // we now have a good guess at which game this is meant to be...
1528         if (index >= 0 && gamemode != gamemode_info[index].mode)
1529                 COM_SetGameType(index);
1530 }
1531
1532 static void COM_SetGameType(int index)
1533 {
1534         int i, t;
1535         if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
1536                 index = 0;
1537         gamemode = gamemode_info[index].mode;
1538         gamename = gamemode_info[index].gamename;
1539         gamedirname1 = gamemode_info[index].gamedirname1;
1540         gamedirname2 = gamemode_info[index].gamedirname2;
1541         gamescreenshotname = gamemode_info[index].gamescreenshotname;
1542         gameuserdirname = gamemode_info[index].gameuserdirname;
1543
1544         if (gamemode == com_startupgamemode)
1545         {
1546                 if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc)
1547                         gamename = com_argv[t+1];
1548                 if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < com_argc)
1549                         gamedirname1 = com_argv[t+1];
1550                 if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < com_argc)
1551                         gamedirname2 = *com_argv[t+1] ? com_argv[t+1] : NULL;
1552                 if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < com_argc)
1553                         gamescreenshotname = com_argv[t+1];
1554                 if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < com_argc)
1555                         gameuserdirname = com_argv[t+1];
1556         }
1557
1558         if (gamedirname2 && gamedirname2[0])
1559                 Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
1560         else
1561                 Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
1562         for (i = 0;i < fs_numgamedirs;i++)
1563         {
1564                 if (i == 0)
1565                         Con_Printf(", with mod gamedirs");
1566                 Con_Printf(" %s", fs_gamedirs[i]);
1567         }
1568         Con_Printf("\n");
1569 }
1570
1571
1572 /*
1573 ================
1574 COM_Init
1575 ================
1576 */
1577 void COM_Init_Commands (void)
1578 {
1579         int i, j, n;
1580         char com_cmdline[MAX_INPUTLINE];
1581
1582         Cvar_RegisterVariable (&registered);
1583         Cvar_RegisterVariable (&cmdline);
1584
1585         // reconstitute the command line for the cmdline externally visible cvar
1586         n = 0;
1587         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1588         {
1589                 i = 0;
1590                 if (strstr(com_argv[j], " "))
1591                 {
1592                         // arg contains whitespace, store quotes around it
1593                         com_cmdline[n++] = '\"';
1594                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1595                                 com_cmdline[n++] = com_argv[j][i++];
1596                         com_cmdline[n++] = '\"';
1597                 }
1598                 else
1599                 {
1600                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1601                                 com_cmdline[n++] = com_argv[j][i++];
1602                 }
1603                 if (n < ((int)sizeof(com_cmdline) - 1))
1604                         com_cmdline[n++] = ' ';
1605                 else
1606                         break;
1607         }
1608         com_cmdline[n] = 0;
1609         Cvar_Set ("cmdline", com_cmdline);
1610 }
1611
1612 /*
1613 ============
1614 va
1615
1616 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
1617 ============
1618 */
1619 char *va(char *buf, size_t buflen, const char *format, ...)
1620 {
1621         va_list argptr;
1622
1623         va_start (argptr, format);
1624         dpvsnprintf (buf, buflen, format,argptr);
1625         va_end (argptr);
1626
1627         return buf;
1628 }
1629
1630
1631 //======================================
1632
1633 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1634
1635 #undef snprintf
1636 #undef vsnprintf
1637
1638 #ifdef WIN32
1639 # define snprintf _snprintf
1640 # define vsnprintf _vsnprintf
1641 #endif
1642
1643
1644 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1645 {
1646         va_list args;
1647         int result;
1648
1649         va_start (args, format);
1650         result = dpvsnprintf (buffer, buffersize, format, args);
1651         va_end (args);
1652
1653         return result;
1654 }
1655
1656
1657 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1658 {
1659         int result;
1660
1661 #if _MSC_VER >= 1400
1662         result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1663 #else
1664         result = vsnprintf (buffer, buffersize, format, args);
1665 #endif
1666         if (result < 0 || (size_t)result >= buffersize)
1667         {
1668                 buffer[buffersize - 1] = '\0';
1669                 return -1;
1670         }
1671
1672         return result;
1673 }
1674
1675
1676 //======================================
1677
1678 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1679 {
1680         if (size_out == 0)
1681                 return;
1682
1683         if(utf8_enable.integer)
1684         {
1685                 *out = 0;
1686                 while(*in && size_out > 1)
1687                 {
1688                         int n;
1689                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1690                         ch = u8_tolower(ch);
1691                         n = u8_fromchar(ch, out, size_out);
1692                         if(n <= 0)
1693                                 break;
1694                         out += n;
1695                         size_out -= n;
1696                 }
1697                 return;
1698         }
1699
1700         while (*in && size_out > 1)
1701         {
1702                 if (*in >= 'A' && *in <= 'Z')
1703                         *out++ = *in++ + 'a' - 'A';
1704                 else
1705                         *out++ = *in++;
1706                 size_out--;
1707         }
1708         *out = '\0';
1709 }
1710
1711 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1712 {
1713         if (size_out == 0)
1714                 return;
1715
1716         if(utf8_enable.integer)
1717         {
1718                 *out = 0;
1719                 while(*in && size_out > 1)
1720                 {
1721                         int n;
1722                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1723                         ch = u8_toupper(ch);
1724                         n = u8_fromchar(ch, out, size_out);
1725                         if(n <= 0)
1726                                 break;
1727                         out += n;
1728                         size_out -= n;
1729                 }
1730                 return;
1731         }
1732
1733         while (*in && size_out > 1)
1734         {
1735                 if (*in >= 'a' && *in <= 'z')
1736                         *out++ = *in++ + 'A' - 'a';
1737                 else
1738                         *out++ = *in++;
1739                 size_out--;
1740         }
1741         *out = '\0';
1742 }
1743
1744 int COM_StringBeginsWith(const char *s, const char *match)
1745 {
1746         for (;*s && *match;s++, match++)
1747                 if (*s != *match)
1748                         return false;
1749         return true;
1750 }
1751
1752 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1753 {
1754         int argc, commentprefixlength;
1755         char *tokenbufend;
1756         const char *l;
1757         argc = 0;
1758         tokenbufend = tokenbuf + tokenbufsize;
1759         l = *text;
1760         commentprefixlength = 0;
1761         if (commentprefix)
1762                 commentprefixlength = (int)strlen(commentprefix);
1763         while (*l && *l != '\n' && *l != '\r')
1764         {
1765                 if (!ISWHITESPACE(*l))
1766                 {
1767                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1768                         {
1769                                 while (*l && *l != '\n' && *l != '\r')
1770                                         l++;
1771                                 break;
1772                         }
1773                         if (argc >= maxargc)
1774                                 return -1;
1775                         argv[argc++] = tokenbuf;
1776                         if (*l == '"')
1777                         {
1778                                 l++;
1779                                 while (*l && *l != '"')
1780                                 {
1781                                         if (tokenbuf >= tokenbufend)
1782                                                 return -1;
1783                                         *tokenbuf++ = *l++;
1784                                 }
1785                                 if (*l == '"')
1786                                         l++;
1787                         }
1788                         else
1789                         {
1790                                 while (!ISWHITESPACE(*l))
1791                                 {
1792                                         if (tokenbuf >= tokenbufend)
1793                                                 return -1;
1794                                         *tokenbuf++ = *l++;
1795                                 }
1796                         }
1797                         if (tokenbuf >= tokenbufend)
1798                                 return -1;
1799                         *tokenbuf++ = 0;
1800                 }
1801                 else
1802                         l++;
1803         }
1804         // line endings:
1805         // UNIX: \n
1806         // Mac: \r
1807         // Windows: \r\n
1808         if (*l == '\r')
1809                 l++;
1810         if (*l == '\n')
1811                 l++;
1812         *text = l;
1813         return argc;
1814 }
1815
1816 /*
1817 ============
1818 COM_StringLengthNoColors
1819
1820 calculates the visible width of a color coded string.
1821
1822 *valid is filled with TRUE if the string is a valid colored string (that is, if
1823 it does not end with an unfinished color code). If it gets filled with FALSE, a
1824 fix would be adding a STRING_COLOR_TAG at the end of the string.
1825
1826 valid can be set to NULL if the caller doesn't care.
1827
1828 For size_s, specify the maximum number of characters from s to use, or 0 to use
1829 all characters until the zero terminator.
1830 ============
1831 */
1832 size_t
1833 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1834 {
1835         const char *end = size_s ? (s + size_s) : NULL;
1836         size_t len = 0;
1837         for(;;)
1838         {
1839                 switch((s == end) ? 0 : *s)
1840                 {
1841                         case 0:
1842                                 if(valid)
1843                                         *valid = TRUE;
1844                                 return len;
1845                         case STRING_COLOR_TAG:
1846                                 ++s;
1847                                 switch((s == end) ? 0 : *s)
1848                                 {
1849                                         case STRING_COLOR_RGB_TAG_CHAR:
1850                                                 if (s+1 != end && isxdigit(s[1]) &&
1851                                                         s+2 != end && isxdigit(s[2]) &&
1852                                                         s+3 != end && isxdigit(s[3]) )
1853                                                 {
1854                                                         s+=3;
1855                                                         break;
1856                                                 }
1857                                                 ++len; // STRING_COLOR_TAG
1858                                                 ++len; // STRING_COLOR_RGB_TAG_CHAR
1859                                                 break;
1860                                         case 0: // ends with unfinished color code!
1861                                                 ++len;
1862                                                 if(valid)
1863                                                         *valid = FALSE;
1864                                                 return len;
1865                                         case STRING_COLOR_TAG: // escaped ^
1866                                                 ++len;
1867                                                 break;
1868                                         case '0': case '1': case '2': case '3': case '4':
1869                                         case '5': case '6': case '7': case '8': case '9': // color code
1870                                                 break;
1871                                         default: // not a color code
1872                                                 ++len; // STRING_COLOR_TAG
1873                                                 ++len; // the character
1874                                                 break;
1875                                 }
1876                                 break;
1877                         default:
1878                                 ++len;
1879                                 break;
1880                 }
1881                 ++s;
1882         }
1883         // never get here
1884 }
1885
1886 /*
1887 ============
1888 COM_StringDecolorize
1889
1890 removes color codes from a string.
1891
1892 If escape_carets is true, the resulting string will be safe for printing. If
1893 escape_carets is false, the function will just strip color codes (for logging
1894 for example).
1895
1896 If the output buffer size did not suffice for converting, the function returns
1897 FALSE. Generally, if escape_carets is false, the output buffer needs
1898 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1899 bytes. In any case, the function makes sure that the resulting string is
1900 zero terminated.
1901
1902 For size_in, specify the maximum number of characters from in to use, or 0 to use
1903 all characters until the zero terminator.
1904 ============
1905 */
1906 qboolean
1907 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1908 {
1909 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1910         const char *end = size_in ? (in + size_in) : NULL;
1911         if(size_out < 1)
1912                 return FALSE;
1913         for(;;)
1914         {
1915                 switch((in == end) ? 0 : *in)
1916                 {
1917                         case 0:
1918                                 *out++ = 0;
1919                                 return TRUE;
1920                         case STRING_COLOR_TAG:
1921                                 ++in;
1922                                 switch((in == end) ? 0 : *in)
1923                                 {
1924                                         case STRING_COLOR_RGB_TAG_CHAR:
1925                                                 if (in+1 != end && isxdigit(in[1]) &&
1926                                                         in+2 != end && isxdigit(in[2]) &&
1927                                                         in+3 != end && isxdigit(in[3]) )
1928                                                 {
1929                                                         in+=3;
1930                                                         break;
1931                                                 }
1932                                                 APPEND(STRING_COLOR_TAG);
1933                                                 if(escape_carets)
1934                                                         APPEND(STRING_COLOR_TAG);
1935                                                 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1936                                                 break;
1937                                         case 0: // ends with unfinished color code!
1938                                                 APPEND(STRING_COLOR_TAG);
1939                                                 // finish the code by appending another caret when escaping
1940                                                 if(escape_carets)
1941                                                         APPEND(STRING_COLOR_TAG);
1942                                                 *out++ = 0;
1943                                                 return TRUE;
1944                                         case STRING_COLOR_TAG: // escaped ^
1945                                                 APPEND(STRING_COLOR_TAG);
1946                                                 // append a ^ twice when escaping
1947                                                 if(escape_carets)
1948                                                         APPEND(STRING_COLOR_TAG);
1949                                                 break;
1950                                         case '0': case '1': case '2': case '3': case '4':
1951                                         case '5': case '6': case '7': case '8': case '9': // color code
1952                                                 break;
1953                                         default: // not a color code
1954                                                 APPEND(STRING_COLOR_TAG);
1955                                                 APPEND(*in);
1956                                                 break;
1957                                 }
1958                                 break;
1959                         default:
1960                                 APPEND(*in);
1961                                 break;
1962                 }
1963                 ++in;
1964         }
1965         // never get here
1966 #undef APPEND
1967 }
1968
1969 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1970 {
1971         int pos = 0, j;
1972         size_t keylength;
1973         if (!key)
1974                 key = "";
1975         keylength = strlen(key);
1976         if (valuelength < 1 || !value)
1977         {
1978                 Con_Printf("InfoString_GetValue: no room in value\n");
1979                 return NULL;
1980         }
1981         value[0] = 0;
1982         if (strchr(key, '\\'))
1983         {
1984                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1985                 return NULL;
1986         }
1987         if (strchr(key, '\"'))
1988         {
1989                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1990                 return NULL;
1991         }
1992         if (!key[0])
1993         {
1994                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1995                 return NULL;
1996         }
1997         while (buffer[pos] == '\\')
1998         {
1999                 if (!memcmp(buffer + pos+1, key, keylength))
2000                 {
2001                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2002                         pos++;
2003                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2004                                 value[j] = buffer[pos+j];
2005                         value[j] = 0;
2006                         return value;
2007                 }
2008                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2009                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2010         }
2011         // if we reach this point the key was not found
2012         return NULL;
2013 }
2014
2015 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2016 {
2017         int pos = 0, pos2;
2018         size_t keylength;
2019         if (!key)
2020                 key = "";
2021         if (!value)
2022                 value = "";
2023         keylength = strlen(key);
2024         if (strchr(key, '\\') || strchr(value, '\\'))
2025         {
2026                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2027                 return;
2028         }
2029         if (strchr(key, '\"') || strchr(value, '\"'))
2030         {
2031                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2032                 return;
2033         }
2034         if (!key[0])
2035         {
2036                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2037                 return;
2038         }
2039         while (buffer[pos] == '\\')
2040         {
2041                 if (!memcmp(buffer + pos+1, key, keylength))
2042                         break;
2043                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2044                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2045         }
2046         // if we found the key, find the end of it because we will be replacing it
2047         pos2 = pos;
2048         if (buffer[pos] == '\\')
2049         {
2050                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2051                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2052         }
2053         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2054         {
2055                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2056                 return;
2057         }
2058         if (value && value[0])
2059         {
2060                 // set the key/value and append the remaining text
2061                 char tempbuffer[MAX_INPUTLINE];
2062                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2063                 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2064         }
2065         else
2066         {
2067                 // just remove the key from the text
2068                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2069         }
2070 }
2071
2072 void InfoString_Print(char *buffer)
2073 {
2074         int i;
2075         char key[MAX_INPUTLINE];
2076         char value[MAX_INPUTLINE];
2077         while (*buffer)
2078         {
2079                 if (*buffer != '\\')
2080                 {
2081                         Con_Printf("InfoString_Print: corrupt string\n");
2082                         return;
2083                 }
2084                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2085                         if (i < (int)sizeof(key)-1)
2086                                 key[i++] = *buffer;
2087                 key[i] = 0;
2088                 if (*buffer != '\\')
2089                 {
2090                         Con_Printf("InfoString_Print: corrupt string\n");
2091                         return;
2092                 }
2093                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2094                         if (i < (int)sizeof(value)-1)
2095                                 value[i++] = *buffer;
2096                 value[i] = 0;
2097                 // empty value is an error case
2098                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2099         }
2100 }
2101
2102 //========================================================
2103 // strlcat and strlcpy, from OpenBSD
2104
2105 /*
2106  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2107  *
2108  * Permission to use, copy, modify, and distribute this software for any
2109  * purpose with or without fee is hereby granted, provided that the above
2110  * copyright notice and this permission notice appear in all copies.
2111  *
2112  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2113  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2114  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2115  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2116  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2117  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2118  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2119  */
2120
2121 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
2122 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
2123
2124
2125 #ifndef HAVE_STRLCAT
2126 size_t
2127 strlcat(char *dst, const char *src, size_t siz)
2128 {
2129         register char *d = dst;
2130         register const char *s = src;
2131         register size_t n = siz;
2132         size_t dlen;
2133
2134         /* Find the end of dst and adjust bytes left but don't go past end */
2135         while (n-- != 0 && *d != '\0')
2136                 d++;
2137         dlen = d - dst;
2138         n = siz - dlen;
2139
2140         if (n == 0)
2141                 return(dlen + strlen(s));
2142         while (*s != '\0') {
2143                 if (n != 1) {
2144                         *d++ = *s;
2145                         n--;
2146                 }
2147                 s++;
2148         }
2149         *d = '\0';
2150
2151         return(dlen + (s - src));       /* count does not include NUL */
2152 }
2153 #endif  // #ifndef HAVE_STRLCAT
2154
2155
2156 #ifndef HAVE_STRLCPY
2157 size_t
2158 strlcpy(char *dst, const char *src, size_t siz)
2159 {
2160         register char *d = dst;
2161         register const char *s = src;
2162         register size_t n = siz;
2163
2164         /* Copy as many bytes as will fit */
2165         if (n != 0 && --n != 0) {
2166                 do {
2167                         if ((*d++ = *s++) == 0)
2168                                 break;
2169                 } while (--n != 0);
2170         }
2171
2172         /* Not enough room in dst, add NUL and traverse rest of src */
2173         if (n == 0) {
2174                 if (siz != 0)
2175                         *d = '\0';              /* NUL-terminate dst */
2176                 while (*s++)
2177                         ;
2178         }
2179
2180         return(s - src - 1);    /* count does not include NUL */
2181 }
2182
2183 #endif  // #ifndef HAVE_STRLCPY
2184
2185 void FindFraction(double val, int *num, int *denom, int denomMax)
2186 {
2187         int i;
2188         double bestdiff;
2189         // initialize
2190         bestdiff = fabs(val);
2191         *num = 0;
2192         *denom = 1;
2193
2194         for(i = 1; i <= denomMax; ++i)
2195         {
2196                 int inum = (int) floor(0.5 + val * i);
2197                 double diff = fabs(val - inum / (double)i);
2198                 if(diff < bestdiff)
2199                 {
2200                         bestdiff = diff;
2201                         *num = inum;
2202                         *denom = i;
2203                 }
2204         }
2205 }
2206
2207 // decodes an XPM from C syntax
2208 char **XPM_DecodeString(const char *in)
2209 {
2210         static char *tokens[257];
2211         static char lines[257][512];
2212         size_t line = 0;
2213
2214         // skip until "{" token
2215         while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
2216
2217         // now, read in succession: string, comma-or-}
2218         while(COM_ParseToken_QuakeC(&in, false))
2219         {
2220                 tokens[line] = lines[line];
2221                 strlcpy(lines[line++], com_token, sizeof(lines[0]));
2222                 if(!COM_ParseToken_QuakeC(&in, false))
2223                         return NULL;
2224                 if(!strcmp(com_token, "}"))
2225                         break;
2226                 if(strcmp(com_token, ","))
2227                         return NULL;
2228                 if(line >= sizeof(tokens) / sizeof(tokens[0]))
2229                         return NULL;
2230         }
2231
2232         return tokens;
2233 }
2234
2235 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2236 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
2237 {
2238         unsigned char i0 = (bytes > 0) ? in[0] : 0;
2239         unsigned char i1 = (bytes > 1) ? in[1] : 0;
2240         unsigned char i2 = (bytes > 2) ? in[2] : 0;
2241         unsigned char o0 = base64[i0 >> 2];
2242         unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
2243         unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
2244         unsigned char o3 = base64[i2 & 077];
2245         out[0] = (bytes > 0) ? o0 : '?';
2246         out[1] = (bytes > 0) ? o1 : '?';
2247         out[2] = (bytes > 1) ? o2 : '=';
2248         out[3] = (bytes > 2) ? o3 : '=';
2249 }
2250
2251 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
2252 {
2253         size_t blocks, i;
2254         // expand the out-buffer
2255         blocks = (buflen + 2) / 3;
2256         if(blocks*4 > outbuflen)
2257                 return 0;
2258         for(i = blocks; i > 0; )
2259         {
2260                 --i;
2261                 base64_3to4(buf + 3*i, buf + 4*i, buflen - 3*i);
2262         }
2263         return blocks * 4;
2264 }