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