]> icculus.org git repositories - divverent/darkplaces.git/blob - common.c
fix quoting for machine type
[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 "quakedef.h"
23
24 #include <stdlib.h>
25 #include <fcntl.h>
26 #ifndef WIN32
27 #include <unistd.h>
28 #endif
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 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
177 {
178         unsigned short crc = CRC_INIT_VALUE;
179         while (size--)
180                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
181         return crc ^ CRC_XOR_VALUE;
182 }
183
184 // QuakeWorld
185 static unsigned char chktbl[1024 + 4] =
186 {
187         0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
188         0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
189         0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
190         0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
191         0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
192         0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
193         0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
194         0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
195         0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
196         0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
197         0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
198         0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
199         0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
200         0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
201         0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
202         0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
203         0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
204         0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
205         0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
206         0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
207         0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
208         0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
209         0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
210         0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
211         0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
212         0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
213         0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
214         0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
215         0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
216         0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
217         0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
218         0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
219
220         // map checksum goes here
221         0x00,0x00,0x00,0x00
222 };
223
224 // QuakeWorld
225 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
226 {
227         unsigned char *p;
228         unsigned char chkb[60 + 4];
229
230         p = chktbl + (sequence % (sizeof(chktbl) - 8));
231
232         if (length > 60)
233                 length = 60;
234         memcpy(chkb, base, length);
235
236         chkb[length] = (sequence & 0xff) ^ p[0];
237         chkb[length+1] = p[1];
238         chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
239         chkb[length+3] = p[3];
240
241         return CRC_Block(chkb, length + 4) & 0xff;
242 }
243
244 /*
245 ==============================================================================
246
247                         MESSAGE IO FUNCTIONS
248
249 Handles byte ordering and avoids alignment errors
250 ==============================================================================
251 */
252
253 //
254 // writing functions
255 //
256
257 void MSG_WriteChar (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_WriteByte (sizebuf_t *sb, int c)
266 {
267         unsigned char    *buf;
268
269         buf = SZ_GetSpace (sb, 1);
270         buf[0] = c;
271 }
272
273 void MSG_WriteShort (sizebuf_t *sb, int c)
274 {
275         unsigned char    *buf;
276
277         buf = SZ_GetSpace (sb, 2);
278         buf[0] = c&0xff;
279         buf[1] = c>>8;
280 }
281
282 void MSG_WriteLong (sizebuf_t *sb, int c)
283 {
284         unsigned char    *buf;
285
286         buf = SZ_GetSpace (sb, 4);
287         buf[0] = c&0xff;
288         buf[1] = (c>>8)&0xff;
289         buf[2] = (c>>16)&0xff;
290         buf[3] = c>>24;
291 }
292
293 void MSG_WriteFloat (sizebuf_t *sb, float f)
294 {
295         union
296         {
297                 float   f;
298                 int     l;
299         } dat;
300
301
302         dat.f = f;
303         dat.l = LittleLong (dat.l);
304
305         SZ_Write (sb, (unsigned char *)&dat.l, 4);
306 }
307
308 void MSG_WriteString (sizebuf_t *sb, const char *s)
309 {
310         if (!s || !*s)
311                 MSG_WriteChar (sb, 0);
312         else
313                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
314 }
315
316 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
317 {
318         if (s && *s)
319                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
320 }
321
322 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
323 {
324         if (f >= 0)
325                 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
326         else
327                 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
328 }
329
330 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
331 {
332         if (f >= 0)
333                 MSG_WriteShort (sb, (int)(f + 0.5));
334         else
335                 MSG_WriteShort (sb, (int)(f - 0.5));
336 }
337
338 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
339 {
340         MSG_WriteFloat (sb, f);
341 }
342
343 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
344 {
345         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
346                 MSG_WriteCoord13i (sb, f);
347         else if (protocol == PROTOCOL_DARKPLACES1)
348                 MSG_WriteCoord32f (sb, f);
349         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
350                 MSG_WriteCoord16i (sb, f);
351         else
352                 MSG_WriteCoord32f (sb, f);
353 }
354
355 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
356 {
357         MSG_WriteCoord (sb, v[0], protocol);
358         MSG_WriteCoord (sb, v[1], protocol);
359         MSG_WriteCoord (sb, v[2], protocol);
360 }
361
362 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
363 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
364 {
365         if (f >= 0)
366                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
367         else
368                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
369 }
370
371 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
372 {
373         if (f >= 0)
374                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
375         else
376                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
377 }
378
379 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
380 {
381         MSG_WriteFloat (sb, f);
382 }
383
384 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
385 {
386         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)
387                 MSG_WriteAngle8i (sb, f);
388         else
389                 MSG_WriteAngle16i (sb, f);
390 }
391
392 //
393 // reading functions
394 //
395 int msg_readcount;
396 qboolean msg_badread;
397
398 void MSG_BeginReading (void)
399 {
400         msg_readcount = 0;
401         msg_badread = false;
402 }
403
404 int MSG_ReadLittleShort (void)
405 {
406         if (msg_readcount+2 > net_message.cursize)
407         {
408                 msg_badread = true;
409                 return -1;
410         }
411         msg_readcount += 2;
412         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
413 }
414
415 int MSG_ReadBigShort (void)
416 {
417         if (msg_readcount+2 > net_message.cursize)
418         {
419                 msg_badread = true;
420                 return -1;
421         }
422         msg_readcount += 2;
423         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
424 }
425
426 int MSG_ReadLittleLong (void)
427 {
428         if (msg_readcount+4 > net_message.cursize)
429         {
430                 msg_badread = true;
431                 return -1;
432         }
433         msg_readcount += 4;
434         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);
435 }
436
437 int MSG_ReadBigLong (void)
438 {
439         if (msg_readcount+4 > net_message.cursize)
440         {
441                 msg_badread = true;
442                 return -1;
443         }
444         msg_readcount += 4;
445         return (net_message.data[msg_readcount-4]<<24) + (net_message.data[msg_readcount-3]<<16) + (net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1];
446 }
447
448 float MSG_ReadLittleFloat (void)
449 {
450         union
451         {
452                 float f;
453                 int l;
454         } dat;
455         if (msg_readcount+4 > net_message.cursize)
456         {
457                 msg_badread = true;
458                 return -1;
459         }
460         msg_readcount += 4;
461         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);
462         return dat.f;
463 }
464
465 float MSG_ReadBigFloat (void)
466 {
467         union
468         {
469                 float f;
470                 int l;
471         } dat;
472         if (msg_readcount+4 > net_message.cursize)
473         {
474                 msg_badread = true;
475                 return -1;
476         }
477         msg_readcount += 4;
478         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];
479         return dat.f;
480 }
481
482 char *MSG_ReadString (void)
483 {
484         static char string[MAX_INPUTLINE];
485         int l,c;
486         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
487                 string[l] = c;
488         string[l] = 0;
489         return string;
490 }
491
492 int MSG_ReadBytes (int numbytes, unsigned char *out)
493 {
494         int l, c;
495         for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
496                 out[l] = c;
497         return l;
498 }
499
500 float MSG_ReadCoord13i (void)
501 {
502         return MSG_ReadLittleShort() * (1.0/8.0);
503 }
504
505 float MSG_ReadCoord16i (void)
506 {
507         return (signed short) MSG_ReadLittleShort();
508 }
509
510 float MSG_ReadCoord32f (void)
511 {
512         return MSG_ReadLittleFloat();
513 }
514
515 float MSG_ReadCoord (protocolversion_t protocol)
516 {
517         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
518                 return MSG_ReadCoord13i();
519         else if (protocol == PROTOCOL_DARKPLACES1)
520                 return MSG_ReadCoord32f();
521         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
522                 return MSG_ReadCoord16i();
523         else
524                 return MSG_ReadCoord32f();
525 }
526
527 void MSG_ReadVector (float *v, protocolversion_t protocol)
528 {
529         v[0] = MSG_ReadCoord(protocol);
530         v[1] = MSG_ReadCoord(protocol);
531         v[2] = MSG_ReadCoord(protocol);
532 }
533
534 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
535 float MSG_ReadAngle8i (void)
536 {
537         return (signed char) MSG_ReadByte () * (360.0/256.0);
538 }
539
540 float MSG_ReadAngle16i (void)
541 {
542         return (signed short)MSG_ReadShort () * (360.0/65536.0);
543 }
544
545 float MSG_ReadAngle32f (void)
546 {
547         return MSG_ReadFloat ();
548 }
549
550 float MSG_ReadAngle (protocolversion_t protocol)
551 {
552         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)
553                 return MSG_ReadAngle8i ();
554         else
555                 return MSG_ReadAngle16i ();
556 }
557
558
559 //===========================================================================
560
561 void SZ_Clear (sizebuf_t *buf)
562 {
563         buf->cursize = 0;
564 }
565
566 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
567 {
568         unsigned char *data;
569
570         if (buf->cursize + length > buf->maxsize)
571         {
572                 if (!buf->allowoverflow)
573                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
574
575                 if (length > buf->maxsize)
576                         Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
577
578                 buf->overflowed = true;
579                 Con_Print("SZ_GetSpace: overflow\n");
580                 SZ_Clear (buf);
581         }
582
583         data = buf->data + buf->cursize;
584         buf->cursize += length;
585
586         return data;
587 }
588
589 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
590 {
591         memcpy (SZ_GetSpace(buf,length),data,length);
592 }
593
594 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
595 // attention, it has been eradicated from here, its only (former) use in
596 // all of darkplaces.
597
598 static char *hexchar = "0123456789ABCDEF";
599 void Com_HexDumpToConsole(const unsigned char *data, int size)
600 {
601         int i, j, n;
602         char text[1024];
603         char *cur, *flushpointer;
604         const unsigned char *d;
605         cur = text;
606         flushpointer = text + 512;
607         for (i = 0;i < size;)
608         {
609                 n = 16;
610                 if (n > size - i)
611                         n = size - i;
612                 d = data + i;
613                 // print offset
614                 *cur++ = hexchar[(i >> 12) & 15];
615                 *cur++ = hexchar[(i >>  8) & 15];
616                 *cur++ = hexchar[(i >>  4) & 15];
617                 *cur++ = hexchar[(i >>  0) & 15];
618                 *cur++ = ':';
619                 // print hex
620                 for (j = 0;j < 16;j++)
621                 {
622                         if (j < n)
623                         {
624                                 *cur++ = hexchar[(d[j] >> 4) & 15];
625                                 *cur++ = hexchar[(d[j] >> 0) & 15];
626                         }
627                         else
628                         {
629                                 *cur++ = ' ';
630                                 *cur++ = ' ';
631                         }
632                         if ((j & 3) == 3)
633                                 *cur++ = ' ';
634                 }
635                 // print text
636                 for (j = 0;j < 16;j++)
637                 {
638                         if (j < n)
639                         {
640                                 // color change prefix character has to be treated specially
641                                 if (d[j] == STRING_COLOR_TAG)
642                                 {
643                                         *cur++ = STRING_COLOR_TAG;
644                                         *cur++ = STRING_COLOR_TAG;
645                                 }
646                                 else if (d[j] >= ' ')
647                                         *cur++ = d[j];
648                                 else
649                                         *cur++ = '.';
650                         }
651                         else
652                                 *cur++ = ' ';
653                 }
654                 *cur++ = '\n';
655                 i += n;
656                 if (cur >= flushpointer || i >= size)
657                 {
658                         *cur++ = 0;
659                         Con_Print(text);
660                         cur = text;
661                 }
662         }
663 }
664
665 void SZ_HexDumpToConsole(const sizebuf_t *buf)
666 {
667         Com_HexDumpToConsole(buf->data, buf->cursize);
668 }
669
670
671 //============================================================================
672
673 /*
674 ==============
675 COM_Wordwrap
676
677 Word wraps a string. The wordWidth function is guaranteed to be called exactly
678 once for each word in the string, so it may be stateful, no idea what that
679 would be good for any more. At the beginning of the string, it will be called
680 for the char 0 to initialize a clean state, and then once with the string " "
681 (a space) so the routine knows how long a space is.
682
683 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
684
685 The sum of the return values of the processLine function will be returned.
686 ==============
687 */
688 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
689 {
690         // Logic is as follows:
691         //
692         // For each word or whitespace:
693         //   Newline found? Output current line, advance to next line. This is not a continuation. Continue.
694         //   Space found? Always add it to the current line, no matter if it fits.
695         //   Word found? Check if current line + current word fits.
696         //     If it fits, append it. Continue.
697         //     If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
698
699         qboolean isContinuation = false;
700         float spaceWidth;
701         const char *startOfLine = string;
702         const char *cursor = string;
703         const char *end = string + length;
704         float spaceUsedInLine = 0;
705         float spaceUsedForWord;
706         int result = 0;
707         size_t wordLen;
708         size_t dummy;
709
710         dummy = 0;
711         wordWidth(passthroughCW, NULL, &dummy, -1);
712         dummy = 1;
713         spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
714
715         for(;;)
716         {
717                 char ch = (cursor < end) ? *cursor : 0;
718                 switch(ch)
719                 {
720                         case 0: // end of string
721                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
722                                 isContinuation = false;
723                                 goto out;
724                                 break;
725                         case '\n': // end of line
726                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
727                                 isContinuation = false;
728                                 ++cursor;
729                                 startOfLine = cursor;
730                                 break;
731                         case ' ': // space
732                                 ++cursor;
733                                 spaceUsedInLine += spaceWidth;
734                                 break;
735                         default: // word
736                                 wordLen = 1;
737                                 while(cursor + wordLen < end)
738                                 {
739                                         switch(cursor[wordLen])
740                                         {
741                                                 case 0:
742                                                 case '\n':
743                                                 case ' ':
744                                                         goto out_inner;
745                                                 default:
746                                                         ++wordLen;
747                                                         break;
748                                         }
749                                 }
750                                 out_inner:
751                                 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
752                                 if(wordLen < 1)
753                                 {
754                                         wordLen = 1;
755                                         spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
756                                 }
757                                 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
758                                 {
759                                         // we can simply append it
760                                         cursor += wordLen;
761                                         spaceUsedInLine += spaceUsedForWord;
762                                 }
763                                 else
764                                 {
765                                         // output current line
766                                         result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
767                                         isContinuation = true;
768                                         startOfLine = cursor;
769                                         cursor += wordLen;
770                                         spaceUsedInLine = continuationWidth + spaceUsedForWord;
771                                 }
772                 }
773         }
774         out:
775
776         return result;
777
778 /*
779         qboolean isContinuation = false;
780         float currentWordSpace = 0;
781         const char *currentWord = 0;
782         float minReserve = 0;
783
784         float spaceUsedInLine = 0;
785         const char *currentLine = 0;
786         const char *currentLineEnd = 0;
787         float currentLineFinalWhitespace = 0;
788         const char *p;
789
790         int result = 0;
791         minReserve = charWidth(passthroughCW, 0);
792         minReserve += charWidth(passthroughCW, ' ');
793
794         if(maxWidth < continuationWidth + minReserve)
795                 maxWidth = continuationWidth + minReserve;
796
797         charWidth(passthroughCW, 0);
798
799         for(p = string; p < string + length; ++p)
800         {
801                 char c = *p;
802                 float w = charWidth(passthroughCW, c);
803
804                 if(!currentWord)
805                 {
806                         currentWord = p;
807                         currentWordSpace = 0;
808                 }
809
810                 if(!currentLine)
811                 {
812                         currentLine = p;
813                         spaceUsedInLine = isContinuation ? continuationWidth : 0;
814                         currentLineEnd = 0;
815                 }
816
817                 if(c == ' ')
818                 {
819                         // 1. I can add the word AND a space - then just append it.
820                         if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
821                         {
822                                 currentLineEnd = p; // note: space not included here
823                                 currentLineFinalWhitespace = w;
824                                 spaceUsedInLine += currentWordSpace + w;
825                         }
826                         // 2. I can just add the word - then append it, output current line and go to next one.
827                         else if(spaceUsedInLine + currentWordSpace <= maxWidth)
828                         {
829                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
830                                 currentLine = 0;
831                                 isContinuation = true;
832                         }
833                         // 3. Otherwise, output current line and go to next one, where I can add the word.
834                         else if(continuationWidth + currentWordSpace + w <= maxWidth)
835                         {
836                                 if(currentLineEnd)
837                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
838                                 currentLine = currentWord;
839                                 spaceUsedInLine = continuationWidth + currentWordSpace + w;
840                                 currentLineEnd = p;
841                                 currentLineFinalWhitespace = w;
842                                 isContinuation = true;
843                         }
844                         // 4. We can't even do that? Then output both current and next word as new lines.
845                         else
846                         {
847                                 if(currentLineEnd)
848                                 {
849                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
850                                         isContinuation = true;
851                                 }
852                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
853                                 currentLine = 0;
854                                 isContinuation = true;
855                         }
856                         currentWord = 0;
857                 }
858                 else if(c == '\n')
859                 {
860                         // 1. I can add the word - then do it.
861                         if(spaceUsedInLine + currentWordSpace <= maxWidth)
862                         {
863                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
864                         }
865                         // 2. Otherwise, output current line, next one and make tabula rasa.
866                         else
867                         {
868                                 if(currentLineEnd)
869                                 {
870                                         processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
871                                         isContinuation = true;
872                                 }
873                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
874                         }
875                         currentWord = 0;
876                         currentLine = 0;
877                         isContinuation = false;
878                 }
879                 else
880                 {
881                         currentWordSpace += w;
882                         if(
883                                 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
884                                 &&
885                                 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
886                         )
887                         {
888                                 // this word cannot join ANY line...
889                                 // so output the current line...
890                                 if(currentLineEnd)
891                                 {
892                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
893                                         isContinuation = true;
894                                 }
895
896                                 // then this word's beginning...
897                                 if(isContinuation)
898                                 {
899                                         // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
900                                         float pieceWidth = maxWidth - continuationWidth;
901                                         const char *pos = currentWord;
902                                         currentWordSpace = 0;
903
904                                         // reset the char width function to a state where no kerning occurs (start of word)
905                                         charWidth(passthroughCW, ' ');
906                                         while(pos <= p)
907                                         {
908                                                 float w = charWidth(passthroughCW, *pos);
909                                                 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
910                                                 {
911                                                         // print everything until it
912                                                         result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
913                                                         // go to here
914                                                         currentWord = pos;
915                                                         currentWordSpace = 0;
916                                                 }
917                                                 currentWordSpace += w;
918                                                 ++pos;
919                                         }
920                                         // now we have a currentWord that fits... set up its next line
921                                         // currentWordSpace has been set
922                                         // currentWord has been set
923                                         spaceUsedInLine = continuationWidth;
924                                         currentLine = currentWord;
925                                         currentLineEnd = 0;
926                                         isContinuation = true;
927                                 }
928                                 else
929                                 {
930                                         // we have a guarantee that it will fix (see if clause)
931                                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
932
933                                         // and use the rest of this word as new start of a line
934                                         currentWordSpace = w;
935                                         currentWord = p;
936                                         spaceUsedInLine = continuationWidth;
937                                         currentLine = p;
938                                         currentLineEnd = 0;
939                                         isContinuation = true;
940                                 }
941                         }
942                 }
943         }
944
945         if(!currentWord)
946         {
947                 currentWord = p;
948                 currentWordSpace = 0;
949         }
950
951         if(currentLine) // Same procedure as \n
952         {
953                 // Can I append the current word?
954                 if(spaceUsedInLine + currentWordSpace <= maxWidth)
955                         result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
956                 else
957                 {
958                         if(currentLineEnd)
959                         {
960                                 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
961                                 isContinuation = true;
962                         }
963                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
964                 }
965         }
966
967         return result;
968 */
969 }
970
971 /*
972 ==============
973 COM_ParseToken_Simple
974
975 Parse a token out of a string
976 ==============
977 */
978 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
979 {
980         int len;
981         int c;
982         const char *data = *datapointer;
983
984         len = 0;
985         com_token[0] = 0;
986
987         if (!data)
988         {
989                 *datapointer = NULL;
990                 return false;
991         }
992
993 // skip whitespace
994 skipwhite:
995         // line endings:
996         // UNIX: \n
997         // Mac: \r
998         // Windows: \r\n
999         for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1000         {
1001                 if (*data == 0)
1002                 {
1003                         // end of file
1004                         *datapointer = NULL;
1005                         return false;
1006                 }
1007         }
1008
1009         // handle Windows line ending
1010         if (data[0] == '\r' && data[1] == '\n')
1011                 data++;
1012
1013         if (data[0] == '/' && data[1] == '/')
1014         {
1015                 // comment
1016                 while (*data && *data != '\n' && *data != '\r')
1017                         data++;
1018                 goto skipwhite;
1019         }
1020         else if (data[0] == '/' && data[1] == '*')
1021         {
1022                 // comment
1023                 data++;
1024                 while (*data && (data[0] != '*' || data[1] != '/'))
1025                         data++;
1026                 if (*data)
1027                         data++;
1028                 if (*data)
1029                         data++;
1030                 goto skipwhite;
1031         }
1032         else if (*data == '\"')
1033         {
1034                 // quoted string
1035                 for (data++;*data && *data != '\"';data++)
1036                 {
1037                         c = *data;
1038                         if (*data == '\\' && parsebackslash)
1039                         {
1040                                 data++;
1041                                 c = *data;
1042                                 if (c == 'n')
1043                                         c = '\n';
1044                                 else if (c == 't')
1045                                         c = '\t';
1046                         }
1047                         if (len < (int)sizeof(com_token) - 1)
1048                                 com_token[len++] = c;
1049                 }
1050                 com_token[len] = 0;
1051                 if (*data == '\"')
1052                         data++;
1053                 *datapointer = data;
1054                 return true;
1055         }
1056         else if (*data == '\r')
1057         {
1058                 // translate Mac line ending to UNIX
1059                 com_token[len++] = '\n';data++;
1060                 com_token[len] = 0;
1061                 *datapointer = data;
1062                 return true;
1063         }
1064         else if (*data == '\n')
1065         {
1066                 // single character
1067                 com_token[len++] = *data++;
1068                 com_token[len] = 0;
1069                 *datapointer = data;
1070                 return true;
1071         }
1072         else
1073         {
1074                 // regular word
1075                 for (;*data > ' ';data++)
1076                         if (len < (int)sizeof(com_token) - 1)
1077                                 com_token[len++] = *data;
1078                 com_token[len] = 0;
1079                 *datapointer = data;
1080                 return true;
1081         }
1082 }
1083
1084 /*
1085 ==============
1086 COM_ParseToken_QuakeC
1087
1088 Parse a token out of a string
1089 ==============
1090 */
1091 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1092 {
1093         int len;
1094         int c;
1095         const char *data = *datapointer;
1096
1097         len = 0;
1098         com_token[0] = 0;
1099
1100         if (!data)
1101         {
1102                 *datapointer = NULL;
1103                 return false;
1104         }
1105
1106 // skip whitespace
1107 skipwhite:
1108         // line endings:
1109         // UNIX: \n
1110         // Mac: \r
1111         // Windows: \r\n
1112         for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1113         {
1114                 if (*data == 0)
1115                 {
1116                         // end of file
1117                         *datapointer = NULL;
1118                         return false;
1119                 }
1120         }
1121
1122         // handle Windows line ending
1123         if (data[0] == '\r' && data[1] == '\n')
1124                 data++;
1125
1126         if (data[0] == '/' && data[1] == '/')
1127         {
1128                 // comment
1129                 while (*data && *data != '\n' && *data != '\r')
1130                         data++;
1131                 goto skipwhite;
1132         }
1133         else if (data[0] == '/' && data[1] == '*')
1134         {
1135                 // comment
1136                 data++;
1137                 while (*data && (data[0] != '*' || data[1] != '/'))
1138                         data++;
1139                 if (*data)
1140                         data++;
1141                 if (*data)
1142                         data++;
1143                 goto skipwhite;
1144         }
1145         else if (*data == '\"' || *data == '\'')
1146         {
1147                 // quoted string
1148                 char quote = *data;
1149                 for (data++;*data && *data != quote;data++)
1150                 {
1151                         c = *data;
1152                         if (*data == '\\')
1153                         {
1154                                 data++;
1155                                 c = *data;
1156                                 if (c == 'n')
1157                                         c = '\n';
1158                                 else if (c == 't')
1159                                         c = '\t';
1160                         }
1161                         if (len < (int)sizeof(com_token) - 1)
1162                                 com_token[len++] = c;
1163                 }
1164                 com_token[len] = 0;
1165                 if (*data == quote)
1166                         data++;
1167                 *datapointer = data;
1168                 return true;
1169         }
1170         else if (*data == '\r')
1171         {
1172                 // translate Mac line ending to UNIX
1173                 com_token[len++] = '\n';data++;
1174                 com_token[len] = 0;
1175                 *datapointer = data;
1176                 return true;
1177         }
1178         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1179         {
1180                 // single character
1181                 com_token[len++] = *data++;
1182                 com_token[len] = 0;
1183                 *datapointer = data;
1184                 return true;
1185         }
1186         else
1187         {
1188                 // regular word
1189                 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1190                         if (len < (int)sizeof(com_token) - 1)
1191                                 com_token[len++] = *data;
1192                 com_token[len] = 0;
1193                 *datapointer = data;
1194                 return true;
1195         }
1196 }
1197
1198 /*
1199 ==============
1200 COM_ParseToken_VM_Tokenize
1201
1202 Parse a token out of a string
1203 ==============
1204 */
1205 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1206 {
1207         int len;
1208         int c;
1209         const char *data = *datapointer;
1210
1211         len = 0;
1212         com_token[0] = 0;
1213
1214         if (!data)
1215         {
1216                 *datapointer = NULL;
1217                 return false;
1218         }
1219
1220 // skip whitespace
1221 skipwhite:
1222         // line endings:
1223         // UNIX: \n
1224         // Mac: \r
1225         // Windows: \r\n
1226         for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1227         {
1228                 if (*data == 0)
1229                 {
1230                         // end of file
1231                         *datapointer = NULL;
1232                         return false;
1233                 }
1234         }
1235
1236         // handle Windows line ending
1237         if (data[0] == '\r' && data[1] == '\n')
1238                 data++;
1239
1240         if (data[0] == '/' && data[1] == '/')
1241         {
1242                 // comment
1243                 while (*data && *data != '\n' && *data != '\r')
1244                         data++;
1245                 goto skipwhite;
1246         }
1247         else if (data[0] == '/' && data[1] == '*')
1248         {
1249                 // comment
1250                 data++;
1251                 while (*data && (data[0] != '*' || data[1] != '/'))
1252                         data++;
1253                 if (*data)
1254                         data++;
1255                 if (*data)
1256                         data++;
1257                 goto skipwhite;
1258         }
1259         else if (*data == '\"' || *data == '\'')
1260         {
1261                 char quote = *data;
1262                 // quoted string
1263                 for (data++;*data && *data != quote;data++)
1264                 {
1265                         c = *data;
1266                         if (*data == '\\')
1267                         {
1268                                 data++;
1269                                 c = *data;
1270                                 if (c == 'n')
1271                                         c = '\n';
1272                                 else if (c == 't')
1273                                         c = '\t';
1274                         }
1275                         if (len < (int)sizeof(com_token) - 1)
1276                                 com_token[len++] = c;
1277                 }
1278                 com_token[len] = 0;
1279                 if (*data == quote)
1280                         data++;
1281                 *datapointer = data;
1282                 return true;
1283         }
1284         else if (*data == '\r')
1285         {
1286                 // translate Mac line ending to UNIX
1287                 com_token[len++] = '\n';data++;
1288                 com_token[len] = 0;
1289                 *datapointer = data;
1290                 return true;
1291         }
1292         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1293         {
1294                 // single character
1295                 com_token[len++] = *data++;
1296                 com_token[len] = 0;
1297                 *datapointer = data;
1298                 return true;
1299         }
1300         else
1301         {
1302                 // regular word
1303                 for (;*data > ' ' && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1304                         if (len < (int)sizeof(com_token) - 1)
1305                                 com_token[len++] = *data;
1306                 com_token[len] = 0;
1307                 *datapointer = data;
1308                 return true;
1309         }
1310 }
1311
1312 /*
1313 ==============
1314 COM_ParseToken_Console
1315
1316 Parse a token out of a string, behaving like the qwcl console
1317 ==============
1318 */
1319 int COM_ParseToken_Console(const char **datapointer)
1320 {
1321         int len;
1322         const char *data = *datapointer;
1323
1324         len = 0;
1325         com_token[0] = 0;
1326
1327         if (!data)
1328         {
1329                 *datapointer = NULL;
1330                 return false;
1331         }
1332
1333 // skip whitespace
1334 skipwhite:
1335         for (;*data <= ' ';data++)
1336         {
1337                 if (*data == 0)
1338                 {
1339                         // end of file
1340                         *datapointer = NULL;
1341                         return false;
1342                 }
1343         }
1344
1345         if (*data == '/' && data[1] == '/')
1346         {
1347                 // comment
1348                 while (*data && *data != '\n' && *data != '\r')
1349                         data++;
1350                 goto skipwhite;
1351         }
1352         else if (*data == '\"')
1353         {
1354                 // quoted string
1355                 for (data++;*data && *data != '\"';data++)
1356                 {
1357                         // allow escaped " and \ case
1358                         if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1359                                 data++;
1360                         if (len < (int)sizeof(com_token) - 1)
1361                                 com_token[len++] = *data;
1362                 }
1363                 com_token[len] = 0;
1364                 if (*data == '\"')
1365                         data++;
1366                 *datapointer = data;
1367         }
1368         else
1369         {
1370                 // regular word
1371                 for (;*data > ' ';data++)
1372                         if (len < (int)sizeof(com_token) - 1)
1373                                 com_token[len++] = *data;
1374                 com_token[len] = 0;
1375                 *datapointer = data;
1376         }
1377
1378         return true;
1379 }
1380
1381
1382 /*
1383 ================
1384 COM_CheckParm
1385
1386 Returns the position (1 to argc-1) in the program's argument list
1387 where the given parameter apears, or 0 if not present
1388 ================
1389 */
1390 int COM_CheckParm (const char *parm)
1391 {
1392         int i;
1393
1394         for (i=1 ; i<com_argc ; i++)
1395         {
1396                 if (!com_argv[i])
1397                         continue;               // NEXTSTEP sometimes clears appkit vars.
1398                 if (!strcmp (parm,com_argv[i]))
1399                         return i;
1400         }
1401
1402         return 0;
1403 }
1404
1405 //===========================================================================
1406
1407 // Game mods
1408
1409 typedef struct gamemode_info_s
1410 {
1411         const char* prog_name;
1412         const char* cmdline;
1413         const char* gamename;
1414         const char* gamedirname1;
1415         const char* gamedirname2;
1416         const char* gamescreenshotname;
1417         const char* gameuserdirname;
1418 } gamemode_info_t;
1419
1420 static const gamemode_info_t gamemode_info [] =
1421 {// prog_name           cmdline                 gamename                                gamedirname     gamescreenshotname
1422
1423 // GAME_NORMAL
1424 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1425 { "",                           "-quake",               "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces" },
1426 // GAME_HIPNOTIC
1427 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1428 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces" },
1429 // GAME_ROGUE
1430 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1431 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces" },
1432 // GAME_NEHAHRA
1433 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1434 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces" },
1435 // GAME_NEXUIZ
1436 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1437 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
1438 // GAME_TRANSFUSION
1439 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1440 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
1441 // GAME_GOODVSBAD2
1442 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1443 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2" },
1444 // GAME_TEU
1445 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1446 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu" },
1447 // GAME_BATTLEMECH
1448 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1449 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech" },
1450 // GAME_ZYMOTIC
1451 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1452 { "zymotic",            "-zymotic",             "Zymotic",                              "basezym",              NULL,                   "zymotic",              "zymotic" },
1453 // GAME_SETHERAL
1454 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1455 { "setheral",           "-setheral",    "Setheral",                             "data",         NULL,                   "setheral",             "setheral" },
1456 // GAME_SOM
1457 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1458 { "som",                        "-som",                 "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces" },
1459 // GAME_TENEBRAE
1460 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1461 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces" },
1462 // GAME_NEOTERIC
1463 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1464 { "neoteric",           "-neoteric",    "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces" },
1465 // GAME_OPENQUARTZ
1466 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1467 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces" },
1468 // GAME_PRYDON
1469 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1470 { "prydon",                     "-prydon",              "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces" },
1471 // GAME_DELUXEQUAKE
1472 // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1473 { "dq", "-dq",  "Deluxe Quake",         "basedq",               "extradq",              "basedq",               "dq" },
1474 // GAME_THEHUNTED
1475 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1476 { "thehunted",          "-thehunted",   "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted" },
1477 // GAME_DEFEATINDETAIL2
1478 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1479 { "did2",                       "-did2",                "Defeat In Detail 2",   "data",         NULL,                   "did2_",                "did2" },
1480 // GAME_DARSANA
1481 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1482 { "darsana",            "-darsana",     "Darsana",                      "ddata",        NULL,                   "darsana",                      "darsana" },
1483 // GAME_CONTAGIONTHEORY
1484 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1485 { "contagiontheory",            "-contagiontheory",     "Contagion Theory",                     "ctdata",       NULL,                   "ct",                   "contagiontheory" },
1486 // GAME_EDU2P
1487 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1488 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "data", "edu2_p", "edu2prototype" },
1489 };
1490
1491 void COM_InitGameType (void)
1492 {
1493         char name [MAX_OSPATH];
1494         unsigned int i;
1495
1496         FS_StripExtension (com_argv[0], name, sizeof (name));
1497         COM_ToLowerString (name, name, sizeof (name));
1498
1499         // Check the binary name; default to GAME_NORMAL (0)
1500         gamemode = GAME_NORMAL;
1501         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1502                 if (strstr (name, gamemode_info[i].prog_name))
1503                 {
1504                         gamemode = (gamemode_t)i;
1505                         break;
1506                 }
1507
1508         // Look for a command-line option
1509         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1510                 if (COM_CheckParm (gamemode_info[i].cmdline))
1511                 {
1512                         gamemode = (gamemode_t)i;
1513                         break;
1514                 }
1515
1516         gamename = gamemode_info[gamemode].gamename;
1517         gamedirname1 = gamemode_info[gamemode].gamedirname1;
1518         gamedirname2 = gamemode_info[gamemode].gamedirname2;
1519         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1520         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1521 }
1522
1523
1524 /*
1525 ================
1526 COM_Init
1527 ================
1528 */
1529 void COM_Init_Commands (void)
1530 {
1531         int i, j, n;
1532         char com_cmdline[MAX_INPUTLINE];
1533
1534         Cvar_RegisterVariable (&registered);
1535         Cvar_RegisterVariable (&cmdline);
1536
1537         // reconstitute the command line for the cmdline externally visible cvar
1538         n = 0;
1539         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1540         {
1541                 i = 0;
1542                 if (strstr(com_argv[j], " "))
1543                 {
1544                         // arg contains whitespace, store quotes around it
1545                         com_cmdline[n++] = '\"';
1546                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1547                                 com_cmdline[n++] = com_argv[j][i++];
1548                         com_cmdline[n++] = '\"';
1549                 }
1550                 else
1551                 {
1552                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1553                                 com_cmdline[n++] = com_argv[j][i++];
1554                 }
1555                 if (n < ((int)sizeof(com_cmdline) - 1))
1556                         com_cmdline[n++] = ' ';
1557                 else
1558                         break;
1559         }
1560         com_cmdline[n] = 0;
1561         Cvar_Set ("cmdline", com_cmdline);
1562 }
1563
1564 /*
1565 ============
1566 va
1567
1568 does a varargs printf into a temp buffer, so I don't need to have
1569 varargs versions of all text functions.
1570 FIXME: make this buffer size safe someday
1571 ============
1572 */
1573 char *va(const char *format, ...)
1574 {
1575         va_list argptr;
1576         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1577         static char string[8][1024], *s;
1578         static int stringindex = 0;
1579
1580         s = string[stringindex];
1581         stringindex = (stringindex + 1) & 7;
1582         va_start (argptr, format);
1583         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1584         va_end (argptr);
1585
1586         return s;
1587 }
1588
1589
1590 //======================================
1591
1592 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1593
1594 #undef snprintf
1595 #undef vsnprintf
1596
1597 #ifdef WIN32
1598 # define snprintf _snprintf
1599 # define vsnprintf _vsnprintf
1600 #endif
1601
1602
1603 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1604 {
1605         va_list args;
1606         int result;
1607
1608         va_start (args, format);
1609         result = dpvsnprintf (buffer, buffersize, format, args);
1610         va_end (args);
1611
1612         return result;
1613 }
1614
1615
1616 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1617 {
1618         int result;
1619
1620         result = vsnprintf (buffer, buffersize, format, args);
1621         if (result < 0 || (size_t)result >= buffersize)
1622         {
1623                 buffer[buffersize - 1] = '\0';
1624                 return -1;
1625         }
1626
1627         return result;
1628 }
1629
1630
1631 //======================================
1632
1633 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1634 {
1635         if (size_out == 0)
1636                 return;
1637
1638         while (*in && size_out > 1)
1639         {
1640                 if (*in >= 'A' && *in <= 'Z')
1641                         *out++ = *in++ + 'a' - 'A';
1642                 else
1643                         *out++ = *in++;
1644                 size_out--;
1645         }
1646         *out = '\0';
1647 }
1648
1649 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1650 {
1651         if (size_out == 0)
1652                 return;
1653
1654         while (*in && size_out > 1)
1655         {
1656                 if (*in >= 'a' && *in <= 'z')
1657                         *out++ = *in++ + 'A' - 'a';
1658                 else
1659                         *out++ = *in++;
1660                 size_out--;
1661         }
1662         *out = '\0';
1663 }
1664
1665 int COM_StringBeginsWith(const char *s, const char *match)
1666 {
1667         for (;*s && *match;s++, match++)
1668                 if (*s != *match)
1669                         return false;
1670         return true;
1671 }
1672
1673 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1674 {
1675         int argc, commentprefixlength;
1676         char *tokenbufend;
1677         const char *l;
1678         argc = 0;
1679         tokenbufend = tokenbuf + tokenbufsize;
1680         l = *text;
1681         commentprefixlength = 0;
1682         if (commentprefix)
1683                 commentprefixlength = (int)strlen(commentprefix);
1684         while (*l && *l != '\n' && *l != '\r')
1685         {
1686                 if (*l > ' ')
1687                 {
1688                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1689                         {
1690                                 while (*l && *l != '\n' && *l != '\r')
1691                                         l++;
1692                                 break;
1693                         }
1694                         if (argc >= maxargc)
1695                                 return -1;
1696                         argv[argc++] = tokenbuf;
1697                         if (*l == '"')
1698                         {
1699                                 l++;
1700                                 while (*l && *l != '"')
1701                                 {
1702                                         if (tokenbuf >= tokenbufend)
1703                                                 return -1;
1704                                         *tokenbuf++ = *l++;
1705                                 }
1706                                 if (*l == '"')
1707                                         l++;
1708                         }
1709                         else
1710                         {
1711                                 while (*l > ' ')
1712                                 {
1713                                         if (tokenbuf >= tokenbufend)
1714                                                 return -1;
1715                                         *tokenbuf++ = *l++;
1716                                 }
1717                         }
1718                         if (tokenbuf >= tokenbufend)
1719                                 return -1;
1720                         *tokenbuf++ = 0;
1721                 }
1722                 else
1723                         l++;
1724         }
1725         // line endings:
1726         // UNIX: \n
1727         // Mac: \r
1728         // Windows: \r\n
1729         if (*l == '\r')
1730                 l++;
1731         if (*l == '\n')
1732                 l++;
1733         *text = l;
1734         return argc;
1735 }
1736
1737 /*
1738 ============
1739 COM_StringLengthNoColors
1740
1741 calculates the visible width of a color coded string.
1742
1743 *valid is filled with TRUE if the string is a valid colored string (that is, if
1744 it does not end with an unfinished color code). If it gets filled with FALSE, a
1745 fix would be adding a STRING_COLOR_TAG at the end of the string.
1746
1747 valid can be set to NULL if the caller doesn't care.
1748
1749 For size_s, specify the maximum number of characters from s to use, or 0 to use
1750 all characters until the zero terminator.
1751 ============
1752 */
1753 size_t
1754 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1755 {
1756         const char *end = size_s ? (s + size_s) : NULL;
1757         size_t len = 0;
1758         for(;;)
1759         {
1760                 switch((s == end) ? 0 : *s)
1761                 {
1762                         case 0:
1763                                 if(valid)
1764                                         *valid = TRUE;
1765                                 return len;
1766                         case STRING_COLOR_TAG:
1767                                 ++s;
1768                                 switch((s == end) ? 0 : *s)
1769                                 {
1770                                         case 0: // ends with unfinished color code!
1771                                                 ++len;
1772                                                 if(valid)
1773                                                         *valid = FALSE;
1774                                                 return len;
1775                                         case STRING_COLOR_TAG: // escaped ^
1776                                                 ++len;
1777                                                 break;
1778                                         case '0': case '1': case '2': case '3': case '4':
1779                                         case '5': case '6': case '7': case '8': case '9': // color code
1780                                                 break;
1781                                         default: // not a color code
1782                                                 ++len; // STRING_COLOR_TAG
1783                                                 ++len; // the character
1784                                                 break;
1785                                 }
1786                                 break;
1787                         default:
1788                                 ++len;
1789                                 break;
1790                 }
1791                 ++s;
1792         }
1793         // never get here
1794 }
1795
1796 /*
1797 ============
1798 COM_StringDecolorize
1799
1800 removes color codes from a string.
1801
1802 If escape_carets is true, the resulting string will be safe for printing. If
1803 escape_carets is false, the function will just strip color codes (for logging
1804 for example).
1805
1806 If the output buffer size did not suffice for converting, the function returns
1807 FALSE. Generally, if escape_carets is false, the output buffer needs
1808 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)+2
1809 bytes. In any case, the function makes sure that the resulting string is
1810 zero terminated.
1811
1812 For size_in, specify the maximum number of characters from in to use, or 0 to use
1813 all characters until the zero terminator.
1814 ============
1815 */
1816 qboolean
1817 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1818 {
1819 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1820         const char *end = size_in ? (in + size_in) : NULL;
1821         if(size_out < 1)
1822                 return FALSE;
1823         for(;;)
1824         {
1825                 switch((in == end) ? 0 : *in)
1826                 {
1827                         case 0:
1828                                 *out++ = 0;
1829                                 return TRUE;
1830                         case STRING_COLOR_TAG:
1831                                 ++in;
1832                                 switch((in == end) ? 0 : *in)
1833                                 {
1834                                         case 0: // ends with unfinished color code!
1835                                                 APPEND(STRING_COLOR_TAG);
1836                                                 // finish the code by appending another caret when escaping
1837                                                 if(escape_carets)
1838                                                         APPEND(STRING_COLOR_TAG);
1839                                                 *out++ = 0;
1840                                                 return TRUE;
1841                                         case STRING_COLOR_TAG: // escaped ^
1842                                                 APPEND(STRING_COLOR_TAG);
1843                                                 // append a ^ twice when escaping
1844                                                 if(escape_carets)
1845                                                         APPEND(STRING_COLOR_TAG);
1846                                                 break;
1847                                         case '0': case '1': case '2': case '3': case '4':
1848                                         case '5': case '6': case '7': case '8': case '9': // color code
1849                                                 break;
1850                                         default: // not a color code
1851                                                 APPEND(STRING_COLOR_TAG);
1852                                                 APPEND(*in);
1853                                                 break;
1854                                 }
1855                                 break;
1856                         default:
1857                                 APPEND(*in);
1858                                 break;
1859                 }
1860                 ++in;
1861         }
1862         // never get here
1863 #undef APPEND
1864 }
1865
1866 // written by Elric, thanks Elric!
1867 char *SearchInfostring(const char *infostring, const char *key)
1868 {
1869         static char value [MAX_INPUTLINE];
1870         char crt_key [MAX_INPUTLINE];
1871         size_t value_ind, key_ind;
1872         char c;
1873
1874         if (*infostring++ != '\\')
1875                 return NULL;
1876
1877         value_ind = 0;
1878         for (;;)
1879         {
1880                 key_ind = 0;
1881
1882                 // Get the key name
1883                 for (;;)
1884                 {
1885                         c = *infostring++;
1886
1887                         if (c == '\0')
1888                                 return NULL;
1889                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1890                         {
1891                                 crt_key[key_ind] = '\0';
1892                                 break;
1893                         }
1894
1895                         crt_key[key_ind++] = c;
1896                 }
1897
1898                 // If it's the key we are looking for, save it in "value"
1899                 if (!strcmp(crt_key, key))
1900                 {
1901                         for (;;)
1902                         {
1903                                 c = *infostring++;
1904
1905                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1906                                 {
1907                                         value[value_ind] = '\0';
1908                                         return value;
1909                                 }
1910
1911                                 value[value_ind++] = c;
1912                         }
1913                 }
1914
1915                 // Else, skip the value
1916                 for (;;)
1917                 {
1918                         c = *infostring++;
1919
1920                         if (c == '\0')
1921                                 return NULL;
1922                         if (c == '\\')
1923                                 break;
1924                 }
1925         }
1926 }
1927
1928 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1929 {
1930         int pos = 0, j;
1931         size_t keylength;
1932         if (!key)
1933                 key = "";
1934         if (!value)
1935                 value = "";
1936         keylength = strlen(key);
1937         if (valuelength < 1 || !value)
1938         {
1939                 Con_Printf("InfoString_GetValue: no room in value\n");
1940                 return;
1941         }
1942         value[0] = 0;
1943         if (strchr(key, '\\'))
1944         {
1945                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1946                 return;
1947         }
1948         if (strchr(key, '\"'))
1949         {
1950                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1951                 return;
1952         }
1953         if (!key[0])
1954         {
1955                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1956                 return;
1957         }
1958         while (buffer[pos] == '\\')
1959         {
1960                 if (!memcmp(buffer + pos+1, key, keylength))
1961                 {
1962                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1963                         pos++;
1964                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1965                                 value[j] = buffer[pos+j];
1966                         value[j] = 0;
1967                         return;
1968                 }
1969                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1970                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1971         }
1972         // if we reach this point the key was not found
1973 }
1974
1975 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1976 {
1977         int pos = 0, pos2;
1978         size_t keylength;
1979         if (!key)
1980                 key = "";
1981         if (!value)
1982                 value = "";
1983         keylength = strlen(key);
1984         if (strchr(key, '\\') || strchr(value, '\\'))
1985         {
1986                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1987                 return;
1988         }
1989         if (strchr(key, '\"') || strchr(value, '\"'))
1990         {
1991                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1992                 return;
1993         }
1994         if (!key[0])
1995         {
1996                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1997                 return;
1998         }
1999         while (buffer[pos] == '\\')
2000         {
2001                 if (!memcmp(buffer + pos+1, key, keylength))
2002                         break;
2003                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2004                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2005         }
2006         // if we found the key, find the end of it because we will be replacing it
2007         pos2 = pos;
2008         if (buffer[pos] == '\\')
2009         {
2010                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2011                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2012         }
2013         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2014         {
2015                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2016                 return;
2017         }
2018         if (value && value[0])
2019         {
2020                 // set the key/value and append the remaining text
2021                 char tempbuffer[4096];
2022                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2023                 sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
2024         }
2025         else
2026         {
2027                 // just remove the key from the text
2028                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2029         }
2030 }
2031
2032 void InfoString_Print(char *buffer)
2033 {
2034         int i;
2035         char key[2048];
2036         char value[2048];
2037         while (*buffer)
2038         {
2039                 if (*buffer != '\\')
2040                 {
2041                         Con_Printf("InfoString_Print: corrupt string\n");
2042                         return;
2043                 }
2044                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2045                         if (i < (int)sizeof(key)-1)
2046                                 key[i++] = *buffer;
2047                 key[i] = 0;
2048                 if (*buffer != '\\')
2049                 {
2050                         Con_Printf("InfoString_Print: corrupt string\n");
2051                         return;
2052                 }
2053                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2054                         if (i < (int)sizeof(value)-1)
2055                                 value[i++] = *buffer;
2056                 value[i] = 0;
2057                 // empty value is an error case
2058                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2059         }
2060 }
2061
2062 //========================================================
2063 // strlcat and strlcpy, from OpenBSD
2064
2065 /*
2066  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2067  *
2068  * Permission to use, copy, modify, and distribute this software for any
2069  * purpose with or without fee is hereby granted, provided that the above
2070  * copyright notice and this permission notice appear in all copies.
2071  *
2072  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2073  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2074  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2075  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2076  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2077  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2078  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2079  */
2080
2081 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
2082 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
2083
2084
2085 #ifndef HAVE_STRLCAT
2086 size_t
2087 strlcat(char *dst, const char *src, size_t siz)
2088 {
2089         register char *d = dst;
2090         register const char *s = src;
2091         register size_t n = siz;
2092         size_t dlen;
2093
2094         /* Find the end of dst and adjust bytes left but don't go past end */
2095         while (n-- != 0 && *d != '\0')
2096                 d++;
2097         dlen = d - dst;
2098         n = siz - dlen;
2099
2100         if (n == 0)
2101                 return(dlen + strlen(s));
2102         while (*s != '\0') {
2103                 if (n != 1) {
2104                         *d++ = *s;
2105                         n--;
2106                 }
2107                 s++;
2108         }
2109         *d = '\0';
2110
2111         return(dlen + (s - src));       /* count does not include NUL */
2112 }
2113 #endif  // #ifndef HAVE_STRLCAT
2114
2115
2116 #ifndef HAVE_STRLCPY
2117 size_t
2118 strlcpy(char *dst, const char *src, size_t siz)
2119 {
2120         register char *d = dst;
2121         register const char *s = src;
2122         register size_t n = siz;
2123
2124         /* Copy as many bytes as will fit */
2125         if (n != 0 && --n != 0) {
2126                 do {
2127                         if ((*d++ = *s++) == 0)
2128                                 break;
2129                 } while (--n != 0);
2130         }
2131
2132         /* Not enough room in dst, add NUL and traverse rest of src */
2133         if (n == 0) {
2134                 if (siz != 0)
2135                         *d = '\0';              /* NUL-terminate dst */
2136                 while (*s++)
2137                         ;
2138         }
2139
2140         return(s - src - 1);    /* count does not include NUL */
2141 }
2142
2143 #endif  // #ifndef HAVE_STRLCPY