]> git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
+ Added Steel Storm 2 and Tomes of Mephistopheles games
[xonotic/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #ifndef WIN32
25 #include <unistd.h>
26 #endif
27
28 #include "quakedef.h"
29 #include "utf8lib.h"
30
31 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
32 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
33
34 char com_token[MAX_INPUTLINE];
35 int com_argc;
36 const char **com_argv;
37 int com_selffd = -1;
38
39 gamemode_t gamemode;
40 const char *gamename;
41 const char *gamedirname1;
42 const char *gamedirname2;
43 const char *gamescreenshotname;
44 const char *gameuserdirname;
45 char com_modname[MAX_OSPATH] = "";
46
47
48 /*
49 ============================================================================
50
51                                         BYTE ORDER FUNCTIONS
52
53 ============================================================================
54 */
55
56
57 float BuffBigFloat (const unsigned char *buffer)
58 {
59         union
60         {
61                 float f;
62                 unsigned int i;
63         }
64         u;
65         u.i = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
66         return u.f;
67 }
68
69 int BuffBigLong (const unsigned char *buffer)
70 {
71         return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
72 }
73
74 short BuffBigShort (const unsigned char *buffer)
75 {
76         return (buffer[0] << 8) | buffer[1];
77 }
78
79 float BuffLittleFloat (const unsigned char *buffer)
80 {
81         union
82         {
83                 float f;
84                 unsigned int i;
85         }
86         u;
87         u.i = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
88         return u.f;
89 }
90
91 int BuffLittleLong (const unsigned char *buffer)
92 {
93         return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
94 }
95
96 short BuffLittleShort (const unsigned char *buffer)
97 {
98         return (buffer[1] << 8) | buffer[0];
99 }
100
101 void StoreBigLong (unsigned char *buffer, unsigned int i)
102 {
103         buffer[0] = (i >> 24) & 0xFF;
104         buffer[1] = (i >> 16) & 0xFF;
105         buffer[2] = (i >>  8) & 0xFF;
106         buffer[3] = i         & 0xFF;
107 }
108
109 void StoreBigShort (unsigned char *buffer, unsigned short i)
110 {
111         buffer[0] = (i >>  8) & 0xFF;
112         buffer[1] = i         & 0xFF;
113 }
114
115 void StoreLittleLong (unsigned char *buffer, unsigned int i)
116 {
117         buffer[0] = i         & 0xFF;
118         buffer[1] = (i >>  8) & 0xFF;
119         buffer[2] = (i >> 16) & 0xFF;
120         buffer[3] = (i >> 24) & 0xFF;
121 }
122
123 void StoreLittleShort (unsigned char *buffer, unsigned short i)
124 {
125         buffer[0] = i         & 0xFF;
126         buffer[1] = (i >>  8) & 0xFF;
127 }
128
129 /*
130 ============================================================================
131
132                                         CRC FUNCTIONS
133
134 ============================================================================
135 */
136
137 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
138 // and the initial and final xor values shown below...  in other words, the
139 // CCITT standard CRC used by XMODEM
140
141 #define CRC_INIT_VALUE  0xffff
142 #define CRC_XOR_VALUE   0x0000
143
144 static unsigned short crctable[256] =
145 {
146         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
147         0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
148         0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
149         0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
150         0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
151         0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
152         0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
153         0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
154         0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
155         0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
156         0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
157         0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
158         0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
159         0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
160         0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
161         0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
162         0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
163         0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
164         0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
165         0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
166         0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
167         0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
168         0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
169         0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
170         0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
171         0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
172         0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
173         0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
174         0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
175         0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
176         0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
177         0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
178 };
179
180 unsigned short CRC_Block(const unsigned char *data, size_t size)
181 {
182         unsigned short crc = CRC_INIT_VALUE;
183         while (size--)
184                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
185         return crc ^ CRC_XOR_VALUE;
186 }
187
188 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
189 {
190         unsigned short crc = CRC_INIT_VALUE;
191         while (size--)
192                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
193         return crc ^ CRC_XOR_VALUE;
194 }
195
196 // QuakeWorld
197 static unsigned char chktbl[1024 + 4] =
198 {
199         0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
200         0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
201         0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
202         0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
203         0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
204         0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
205         0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
206         0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
207         0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
208         0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
209         0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
210         0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
211         0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
212         0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
213         0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
214         0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
215         0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
216         0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
217         0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
218         0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
219         0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
220         0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
221         0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
222         0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
223         0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
224         0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
225         0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
226         0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
227         0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
228         0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
229         0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
230         0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
231
232         // map checksum goes here
233         0x00,0x00,0x00,0x00
234 };
235
236 // QuakeWorld
237 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
238 {
239         unsigned char *p;
240         unsigned char chkb[60 + 4];
241
242         p = chktbl + (sequence % (sizeof(chktbl) - 8));
243
244         if (length > 60)
245                 length = 60;
246         memcpy(chkb, base, length);
247
248         chkb[length] = (sequence & 0xff) ^ p[0];
249         chkb[length+1] = p[1];
250         chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
251         chkb[length+3] = p[3];
252
253         return CRC_Block(chkb, length + 4) & 0xff;
254 }
255
256 /*
257 ==============================================================================
258
259                         MESSAGE IO FUNCTIONS
260
261 Handles byte ordering and avoids alignment errors
262 ==============================================================================
263 */
264
265 //
266 // writing functions
267 //
268
269 void MSG_WriteChar (sizebuf_t *sb, int c)
270 {
271         unsigned char    *buf;
272
273         buf = SZ_GetSpace (sb, 1);
274         buf[0] = c;
275 }
276
277 void MSG_WriteByte (sizebuf_t *sb, int c)
278 {
279         unsigned char    *buf;
280
281         buf = SZ_GetSpace (sb, 1);
282         buf[0] = c;
283 }
284
285 void MSG_WriteShort (sizebuf_t *sb, int c)
286 {
287         unsigned char    *buf;
288
289         buf = SZ_GetSpace (sb, 2);
290         buf[0] = c&0xff;
291         buf[1] = c>>8;
292 }
293
294 void MSG_WriteLong (sizebuf_t *sb, int c)
295 {
296         unsigned char    *buf;
297
298         buf = SZ_GetSpace (sb, 4);
299         buf[0] = c&0xff;
300         buf[1] = (c>>8)&0xff;
301         buf[2] = (c>>16)&0xff;
302         buf[3] = c>>24;
303 }
304
305 void MSG_WriteFloat (sizebuf_t *sb, float f)
306 {
307         union
308         {
309                 float   f;
310                 int     l;
311         } dat;
312
313
314         dat.f = f;
315         dat.l = LittleLong (dat.l);
316
317         SZ_Write (sb, (unsigned char *)&dat.l, 4);
318 }
319
320 void MSG_WriteString (sizebuf_t *sb, const char *s)
321 {
322         if (!s || !*s)
323                 MSG_WriteChar (sb, 0);
324         else
325                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
326 }
327
328 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
329 {
330         if (s && *s)
331                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
332 }
333
334 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
335 {
336         if (f >= 0)
337                 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
338         else
339                 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
340 }
341
342 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
343 {
344         if (f >= 0)
345                 MSG_WriteShort (sb, (int)(f + 0.5));
346         else
347                 MSG_WriteShort (sb, (int)(f - 0.5));
348 }
349
350 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
351 {
352         MSG_WriteFloat (sb, f);
353 }
354
355 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
356 {
357         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
358                 MSG_WriteCoord13i (sb, f);
359         else if (protocol == PROTOCOL_DARKPLACES1)
360                 MSG_WriteCoord32f (sb, f);
361         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
362                 MSG_WriteCoord16i (sb, f);
363         else
364                 MSG_WriteCoord32f (sb, f);
365 }
366
367 void MSG_WriteVector (sizebuf_t *sb, 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",                     "gamedata",             NULL,                   "phcy",         "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_STEELSTORM2,             GAME_STEELSTORM2,               "steelstorm2",          "-steelstorm2",         "Steel Storm 2",                        "gamedata",     NULL,                   "ss2",                  "steelstorm2"           }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
1468 { GAME_TOMESOFMEPHISTOPHELES,           GAME_TOMESOFMEPHISTOPHELES,             "tomesofmephistopheles",                "-tomesofmephistopheles",               "Tomes of Mephistopheles",                      "gamedata",     NULL,                   "tom",                  "tomesofmephistopheles"         }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
1469 { GAME_STRAPBOMB,               GAME_STRAPBOMB,                 "strapbomb",            "-strapbomb",           "Strap-on-bomb Car",    "id1",          NULL,                   "strap",                "strapbomb"                     }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
1470 { GAME_MOONHELM,                GAME_MOONHELM,                  "moonhelm",                     "-moonhelm",            "MoonHelm",                             "data",         NULL,                   "mh",                   "moonhelm"                      }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
1471 };
1472
1473 static void COM_SetGameType(int index);
1474 void COM_InitGameType (void)
1475 {
1476         char name [MAX_OSPATH];
1477         int i;
1478         int index = 0;
1479
1480         // check executable filename for keywords, but do it SMARTLY - only check the last path element
1481         FS_StripExtension(FS_FileWithoutPath(com_argv[0]), name, sizeof (name));
1482         COM_ToLowerString(name, name, sizeof (name));
1483         for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1484                 if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
1485                 {
1486                         index = i;
1487                         break;
1488                 }
1489
1490         // check commandline options for keywords
1491         for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1492                 if (COM_CheckParm (gamemode_info[i].cmdline))
1493                 {
1494                         index = i;
1495                         break;
1496                 }
1497
1498         com_startupgamemode = gamemode_info[index].mode;
1499         com_startupgamegroup = gamemode_info[index].group;
1500         COM_SetGameType(index);
1501 }
1502
1503 void COM_ChangeGameTypeForGameDirs(void)
1504 {
1505         int i;
1506         int index = -1;
1507         // this will not not change the gamegroup
1508         // first check if a base game (single gamedir) matches
1509         for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1510         {
1511                 if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
1512                 {
1513                         index = i;
1514                         break;
1515                 }
1516         }
1517         // now that we have a base game, see if there is a matching derivative game (two gamedirs)
1518         if (fs_numgamedirs)
1519         {
1520                 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1521                 {
1522                         if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
1523                         {
1524                                 index = i;
1525                                 break;
1526                         }
1527                 }
1528         }
1529         // we now have a good guess at which game this is meant to be...
1530         if (index >= 0 && gamemode != gamemode_info[index].mode)
1531                 COM_SetGameType(index);
1532 }
1533
1534 static void COM_SetGameType(int index)
1535 {
1536         int i, t;
1537         if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
1538                 index = 0;
1539         gamemode = gamemode_info[index].mode;
1540         gamename = gamemode_info[index].gamename;
1541         gamedirname1 = gamemode_info[index].gamedirname1;
1542         gamedirname2 = gamemode_info[index].gamedirname2;
1543         gamescreenshotname = gamemode_info[index].gamescreenshotname;
1544         gameuserdirname = gamemode_info[index].gameuserdirname;
1545
1546         if (gamemode == com_startupgamemode)
1547         {
1548                 if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc)
1549                         gamename = com_argv[t+1];
1550                 if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < com_argc)
1551                         gamedirname1 = com_argv[t+1];
1552                 if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < com_argc)
1553                         gamedirname2 = *com_argv[t+1] ? com_argv[t+1] : NULL;
1554                 if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < com_argc)
1555                         gamescreenshotname = com_argv[t+1];
1556                 if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < com_argc)
1557                         gameuserdirname = com_argv[t+1];
1558         }
1559
1560         if (gamedirname2 && gamedirname2[0])
1561                 Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
1562         else
1563                 Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
1564         for (i = 0;i < fs_numgamedirs;i++)
1565         {
1566                 if (i == 0)
1567                         Con_Printf(", with mod gamedirs");
1568                 Con_Printf(" %s", fs_gamedirs[i]);
1569         }
1570         Con_Printf("\n");
1571 }
1572
1573
1574 /*
1575 ================
1576 COM_Init
1577 ================
1578 */
1579 void COM_Init_Commands (void)
1580 {
1581         int i, j, n;
1582         char com_cmdline[MAX_INPUTLINE];
1583
1584         Cvar_RegisterVariable (&registered);
1585         Cvar_RegisterVariable (&cmdline);
1586
1587         // reconstitute the command line for the cmdline externally visible cvar
1588         n = 0;
1589         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1590         {
1591                 i = 0;
1592                 if (strstr(com_argv[j], " "))
1593                 {
1594                         // arg contains whitespace, store quotes around it
1595                         com_cmdline[n++] = '\"';
1596                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1597                                 com_cmdline[n++] = com_argv[j][i++];
1598                         com_cmdline[n++] = '\"';
1599                 }
1600                 else
1601                 {
1602                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1603                                 com_cmdline[n++] = com_argv[j][i++];
1604                 }
1605                 if (n < ((int)sizeof(com_cmdline) - 1))
1606                         com_cmdline[n++] = ' ';
1607                 else
1608                         break;
1609         }
1610         com_cmdline[n] = 0;
1611         Cvar_Set ("cmdline", com_cmdline);
1612 }
1613
1614 /*
1615 ============
1616 va
1617
1618 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
1619 ============
1620 */
1621 char *va(char *buf, size_t buflen, const char *format, ...)
1622 {
1623         va_list argptr;
1624
1625         va_start (argptr, format);
1626         dpvsnprintf (buf, buflen, format,argptr);
1627         va_end (argptr);
1628
1629         return buf;
1630 }
1631
1632
1633 //======================================
1634
1635 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1636
1637 #undef snprintf
1638 #undef vsnprintf
1639
1640 #ifdef WIN32
1641 # define snprintf _snprintf
1642 # define vsnprintf _vsnprintf
1643 #endif
1644
1645
1646 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1647 {
1648         va_list args;
1649         int result;
1650
1651         va_start (args, format);
1652         result = dpvsnprintf (buffer, buffersize, format, args);
1653         va_end (args);
1654
1655         return result;
1656 }
1657
1658
1659 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1660 {
1661         int result;
1662
1663 #if _MSC_VER >= 1400
1664         result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1665 #else
1666         result = vsnprintf (buffer, buffersize, format, args);
1667 #endif
1668         if (result < 0 || (size_t)result >= buffersize)
1669         {
1670                 buffer[buffersize - 1] = '\0';
1671                 return -1;
1672         }
1673
1674         return result;
1675 }
1676
1677
1678 //======================================
1679
1680 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1681 {
1682         if (size_out == 0)
1683                 return;
1684
1685         if(utf8_enable.integer)
1686         {
1687                 *out = 0;
1688                 while(*in && size_out > 1)
1689                 {
1690                         int n;
1691                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1692                         ch = u8_tolower(ch);
1693                         n = u8_fromchar(ch, out, size_out);
1694                         if(n <= 0)
1695                                 break;
1696                         out += n;
1697                         size_out -= n;
1698                 }
1699                 return;
1700         }
1701
1702         while (*in && size_out > 1)
1703         {
1704                 if (*in >= 'A' && *in <= 'Z')
1705                         *out++ = *in++ + 'a' - 'A';
1706                 else
1707                         *out++ = *in++;
1708                 size_out--;
1709         }
1710         *out = '\0';
1711 }
1712
1713 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1714 {
1715         if (size_out == 0)
1716                 return;
1717
1718         if(utf8_enable.integer)
1719         {
1720                 *out = 0;
1721                 while(*in && size_out > 1)
1722                 {
1723                         int n;
1724                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1725                         ch = u8_toupper(ch);
1726                         n = u8_fromchar(ch, out, size_out);
1727                         if(n <= 0)
1728                                 break;
1729                         out += n;
1730                         size_out -= n;
1731                 }
1732                 return;
1733         }
1734
1735         while (*in && size_out > 1)
1736         {
1737                 if (*in >= 'a' && *in <= 'z')
1738                         *out++ = *in++ + 'A' - 'a';
1739                 else
1740                         *out++ = *in++;
1741                 size_out--;
1742         }
1743         *out = '\0';
1744 }
1745
1746 int COM_StringBeginsWith(const char *s, const char *match)
1747 {
1748         for (;*s && *match;s++, match++)
1749                 if (*s != *match)
1750                         return false;
1751         return true;
1752 }
1753
1754 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1755 {
1756         int argc, commentprefixlength;
1757         char *tokenbufend;
1758         const char *l;
1759         argc = 0;
1760         tokenbufend = tokenbuf + tokenbufsize;
1761         l = *text;
1762         commentprefixlength = 0;
1763         if (commentprefix)
1764                 commentprefixlength = (int)strlen(commentprefix);
1765         while (*l && *l != '\n' && *l != '\r')
1766         {
1767                 if (!ISWHITESPACE(*l))
1768                 {
1769                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1770                         {
1771                                 while (*l && *l != '\n' && *l != '\r')
1772                                         l++;
1773                                 break;
1774                         }
1775                         if (argc >= maxargc)
1776                                 return -1;
1777                         argv[argc++] = tokenbuf;
1778                         if (*l == '"')
1779                         {
1780                                 l++;
1781                                 while (*l && *l != '"')
1782                                 {
1783                                         if (tokenbuf >= tokenbufend)
1784                                                 return -1;
1785                                         *tokenbuf++ = *l++;
1786                                 }
1787                                 if (*l == '"')
1788                                         l++;
1789                         }
1790                         else
1791                         {
1792                                 while (!ISWHITESPACE(*l))
1793                                 {
1794                                         if (tokenbuf >= tokenbufend)
1795                                                 return -1;
1796                                         *tokenbuf++ = *l++;
1797                                 }
1798                         }
1799                         if (tokenbuf >= tokenbufend)
1800                                 return -1;
1801                         *tokenbuf++ = 0;
1802                 }
1803                 else
1804                         l++;
1805         }
1806         // line endings:
1807         // UNIX: \n
1808         // Mac: \r
1809         // Windows: \r\n
1810         if (*l == '\r')
1811                 l++;
1812         if (*l == '\n')
1813                 l++;
1814         *text = l;
1815         return argc;
1816 }
1817
1818 /*
1819 ============
1820 COM_StringLengthNoColors
1821
1822 calculates the visible width of a color coded string.
1823
1824 *valid is filled with TRUE if the string is a valid colored string (that is, if
1825 it does not end with an unfinished color code). If it gets filled with FALSE, a
1826 fix would be adding a STRING_COLOR_TAG at the end of the string.
1827
1828 valid can be set to NULL if the caller doesn't care.
1829
1830 For size_s, specify the maximum number of characters from s to use, or 0 to use
1831 all characters until the zero terminator.
1832 ============
1833 */
1834 size_t
1835 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1836 {
1837         const char *end = size_s ? (s + size_s) : NULL;
1838         size_t len = 0;
1839         for(;;)
1840         {
1841                 switch((s == end) ? 0 : *s)
1842                 {
1843                         case 0:
1844                                 if(valid)
1845                                         *valid = TRUE;
1846                                 return len;
1847                         case STRING_COLOR_TAG:
1848                                 ++s;
1849                                 switch((s == end) ? 0 : *s)
1850                                 {
1851                                         case STRING_COLOR_RGB_TAG_CHAR:
1852                                                 if (s+1 != end && isxdigit(s[1]) &&
1853                                                         s+2 != end && isxdigit(s[2]) &&
1854                                                         s+3 != end && isxdigit(s[3]) )
1855                                                 {
1856                                                         s+=3;
1857                                                         break;
1858                                                 }
1859                                                 ++len; // STRING_COLOR_TAG
1860                                                 ++len; // STRING_COLOR_RGB_TAG_CHAR
1861                                                 break;
1862                                         case 0: // ends with unfinished color code!
1863                                                 ++len;
1864                                                 if(valid)
1865                                                         *valid = FALSE;
1866                                                 return len;
1867                                         case STRING_COLOR_TAG: // escaped ^
1868                                                 ++len;
1869                                                 break;
1870                                         case '0': case '1': case '2': case '3': case '4':
1871                                         case '5': case '6': case '7': case '8': case '9': // color code
1872                                                 break;
1873                                         default: // not a color code
1874                                                 ++len; // STRING_COLOR_TAG
1875                                                 ++len; // the character
1876                                                 break;
1877                                 }
1878                                 break;
1879                         default:
1880                                 ++len;
1881                                 break;
1882                 }
1883                 ++s;
1884         }
1885         // never get here
1886 }
1887
1888 /*
1889 ============
1890 COM_StringDecolorize
1891
1892 removes color codes from a string.
1893
1894 If escape_carets is true, the resulting string will be safe for printing. If
1895 escape_carets is false, the function will just strip color codes (for logging
1896 for example).
1897
1898 If the output buffer size did not suffice for converting, the function returns
1899 FALSE. Generally, if escape_carets is false, the output buffer needs
1900 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1901 bytes. In any case, the function makes sure that the resulting string is
1902 zero terminated.
1903
1904 For size_in, specify the maximum number of characters from in to use, or 0 to use
1905 all characters until the zero terminator.
1906 ============
1907 */
1908 qboolean
1909 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1910 {
1911 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1912         const char *end = size_in ? (in + size_in) : NULL;
1913         if(size_out < 1)
1914                 return FALSE;
1915         for(;;)
1916         {
1917                 switch((in == end) ? 0 : *in)
1918                 {
1919                         case 0:
1920                                 *out++ = 0;
1921                                 return TRUE;
1922                         case STRING_COLOR_TAG:
1923                                 ++in;
1924                                 switch((in == end) ? 0 : *in)
1925                                 {
1926                                         case STRING_COLOR_RGB_TAG_CHAR:
1927                                                 if (in+1 != end && isxdigit(in[1]) &&
1928                                                         in+2 != end && isxdigit(in[2]) &&
1929                                                         in+3 != end && isxdigit(in[3]) )
1930                                                 {
1931                                                         in+=3;
1932                                                         break;
1933                                                 }
1934                                                 APPEND(STRING_COLOR_TAG);
1935                                                 if(escape_carets)
1936                                                         APPEND(STRING_COLOR_TAG);
1937                                                 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1938                                                 break;
1939                                         case 0: // ends with unfinished color code!
1940                                                 APPEND(STRING_COLOR_TAG);
1941                                                 // finish the code by appending another caret when escaping
1942                                                 if(escape_carets)
1943                                                         APPEND(STRING_COLOR_TAG);
1944                                                 *out++ = 0;
1945                                                 return TRUE;
1946                                         case STRING_COLOR_TAG: // escaped ^
1947                                                 APPEND(STRING_COLOR_TAG);
1948                                                 // append a ^ twice when escaping
1949                                                 if(escape_carets)
1950                                                         APPEND(STRING_COLOR_TAG);
1951                                                 break;
1952                                         case '0': case '1': case '2': case '3': case '4':
1953                                         case '5': case '6': case '7': case '8': case '9': // color code
1954                                                 break;
1955                                         default: // not a color code
1956                                                 APPEND(STRING_COLOR_TAG);
1957                                                 APPEND(*in);
1958                                                 break;
1959                                 }
1960                                 break;
1961                         default:
1962                                 APPEND(*in);
1963                                 break;
1964                 }
1965                 ++in;
1966         }
1967         // never get here
1968 #undef APPEND
1969 }
1970
1971 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1972 {
1973         int pos = 0, j;
1974         size_t keylength;
1975         if (!key)
1976                 key = "";
1977         keylength = strlen(key);
1978         if (valuelength < 1 || !value)
1979         {
1980                 Con_Printf("InfoString_GetValue: no room in value\n");
1981                 return NULL;
1982         }
1983         value[0] = 0;
1984         if (strchr(key, '\\'))
1985         {
1986                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1987                 return NULL;
1988         }
1989         if (strchr(key, '\"'))
1990         {
1991                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1992                 return NULL;
1993         }
1994         if (!key[0])
1995         {
1996                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1997                 return NULL;
1998         }
1999         while (buffer[pos] == '\\')
2000         {
2001                 if (!memcmp(buffer + pos+1, key, keylength))
2002                 {
2003                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2004                         pos++;
2005                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2006                                 value[j] = buffer[pos+j];
2007                         value[j] = 0;
2008                         return value;
2009                 }
2010                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2011                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2012         }
2013         // if we reach this point the key was not found
2014         return NULL;
2015 }
2016
2017 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2018 {
2019         int pos = 0, pos2;
2020         size_t keylength;
2021         if (!key)
2022                 key = "";
2023         if (!value)
2024                 value = "";
2025         keylength = strlen(key);
2026         if (strchr(key, '\\') || strchr(value, '\\'))
2027         {
2028                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2029                 return;
2030         }
2031         if (strchr(key, '\"') || strchr(value, '\"'))
2032         {
2033                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2034                 return;
2035         }
2036         if (!key[0])
2037         {
2038                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2039                 return;
2040         }
2041         while (buffer[pos] == '\\')
2042         {
2043                 if (!memcmp(buffer + pos+1, key, keylength))
2044                         break;
2045                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2046                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2047         }
2048         // if we found the key, find the end of it because we will be replacing it
2049         pos2 = pos;
2050         if (buffer[pos] == '\\')
2051         {
2052                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2053                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2054         }
2055         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2056         {
2057                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2058                 return;
2059         }
2060         if (value && value[0])
2061         {
2062                 // set the key/value and append the remaining text
2063                 char tempbuffer[MAX_INPUTLINE];
2064                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2065                 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2066         }
2067         else
2068         {
2069                 // just remove the key from the text
2070                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2071         }
2072 }
2073
2074 void InfoString_Print(char *buffer)
2075 {
2076         int i;
2077         char key[MAX_INPUTLINE];
2078         char value[MAX_INPUTLINE];
2079         while (*buffer)
2080         {
2081                 if (*buffer != '\\')
2082                 {
2083                         Con_Printf("InfoString_Print: corrupt string\n");
2084                         return;
2085                 }
2086                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2087                         if (i < (int)sizeof(key)-1)
2088                                 key[i++] = *buffer;
2089                 key[i] = 0;
2090                 if (*buffer != '\\')
2091                 {
2092                         Con_Printf("InfoString_Print: corrupt string\n");
2093                         return;
2094                 }
2095                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2096                         if (i < (int)sizeof(value)-1)
2097                                 value[i++] = *buffer;
2098                 value[i] = 0;
2099                 // empty value is an error case
2100                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2101         }
2102 }
2103
2104 //========================================================
2105 // strlcat and strlcpy, from OpenBSD
2106
2107 /*
2108  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2109  *
2110  * Permission to use, copy, modify, and distribute this software for any
2111  * purpose with or without fee is hereby granted, provided that the above
2112  * copyright notice and this permission notice appear in all copies.
2113  *
2114  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2115  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2116  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2117  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2118  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2119  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2120  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2121  */
2122
2123 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
2124 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
2125
2126
2127 #ifndef HAVE_STRLCAT
2128 size_t
2129 strlcat(char *dst, const char *src, size_t siz)
2130 {
2131         register char *d = dst;
2132         register const char *s = src;
2133         register size_t n = siz;
2134         size_t dlen;
2135
2136         /* Find the end of dst and adjust bytes left but don't go past end */
2137         while (n-- != 0 && *d != '\0')
2138                 d++;
2139         dlen = d - dst;
2140         n = siz - dlen;
2141
2142         if (n == 0)
2143                 return(dlen + strlen(s));
2144         while (*s != '\0') {
2145                 if (n != 1) {
2146                         *d++ = *s;
2147                         n--;
2148                 }
2149                 s++;
2150         }
2151         *d = '\0';
2152
2153         return(dlen + (s - src));       /* count does not include NUL */
2154 }
2155 #endif  // #ifndef HAVE_STRLCAT
2156
2157
2158 #ifndef HAVE_STRLCPY
2159 size_t
2160 strlcpy(char *dst, const char *src, size_t siz)
2161 {
2162         register char *d = dst;
2163         register const char *s = src;
2164         register size_t n = siz;
2165
2166         /* Copy as many bytes as will fit */
2167         if (n != 0 && --n != 0) {
2168                 do {
2169                         if ((*d++ = *s++) == 0)
2170                                 break;
2171                 } while (--n != 0);
2172         }
2173
2174         /* Not enough room in dst, add NUL and traverse rest of src */
2175         if (n == 0) {
2176                 if (siz != 0)
2177                         *d = '\0';              /* NUL-terminate dst */
2178                 while (*s++)
2179                         ;
2180         }
2181
2182         return(s - src - 1);    /* count does not include NUL */
2183 }
2184
2185 #endif  // #ifndef HAVE_STRLCPY
2186
2187 void FindFraction(double val, int *num, int *denom, int denomMax)
2188 {
2189         int i;
2190         double bestdiff;
2191         // initialize
2192         bestdiff = fabs(val);
2193         *num = 0;
2194         *denom = 1;
2195
2196         for(i = 1; i <= denomMax; ++i)
2197         {
2198                 int inum = (int) floor(0.5 + val * i);
2199                 double diff = fabs(val - inum / (double)i);
2200                 if(diff < bestdiff)
2201                 {
2202                         bestdiff = diff;
2203                         *num = inum;
2204                         *denom = i;
2205                 }
2206         }
2207 }
2208
2209 // decodes an XPM from C syntax
2210 char **XPM_DecodeString(const char *in)
2211 {
2212         static char *tokens[257];
2213         static char lines[257][512];
2214         size_t line = 0;
2215
2216         // skip until "{" token
2217         while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
2218
2219         // now, read in succession: string, comma-or-}
2220         while(COM_ParseToken_QuakeC(&in, false))
2221         {
2222                 tokens[line] = lines[line];
2223                 strlcpy(lines[line++], com_token, sizeof(lines[0]));
2224                 if(!COM_ParseToken_QuakeC(&in, false))
2225                         return NULL;
2226                 if(!strcmp(com_token, "}"))
2227                         break;
2228                 if(strcmp(com_token, ","))
2229                         return NULL;
2230                 if(line >= sizeof(tokens) / sizeof(tokens[0]))
2231                         return NULL;
2232         }
2233
2234         return tokens;
2235 }
2236
2237 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2238 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
2239 {
2240         unsigned char i0 = (bytes > 0) ? in[0] : 0;
2241         unsigned char i1 = (bytes > 1) ? in[1] : 0;
2242         unsigned char i2 = (bytes > 2) ? in[2] : 0;
2243         unsigned char o0 = base64[i0 >> 2];
2244         unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
2245         unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
2246         unsigned char o3 = base64[i2 & 077];
2247         out[0] = (bytes > 0) ? o0 : '?';
2248         out[1] = (bytes > 0) ? o1 : '?';
2249         out[2] = (bytes > 1) ? o2 : '=';
2250         out[3] = (bytes > 2) ? o3 : '=';
2251 }
2252
2253 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
2254 {
2255         size_t blocks, i;
2256         // expand the out-buffer
2257         blocks = (buflen + 2) / 3;
2258         if(blocks*4 > outbuflen)
2259                 return 0;
2260         for(i = blocks; i > 0; )
2261         {
2262                 --i;
2263                 base64_3to4(buf + 3*i, buf + 4*i, buflen - 3*i);
2264         }
2265         return blocks * 4;
2266 }