]> git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
implemented \" and \n support in quoted string parsers (console and otherwise)
[xonotic/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #ifndef WIN32
25 #include <unistd.h>
26 #endif
27
28 #include "quakedef.h"
29
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
669
670 Parse a token out of a string
671 ==============
672 */
673 int COM_ParseToken(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                 data += 2;
722                 goto skipwhite;
723         }
724         else if (*data == '\"')
725         {
726                 // quoted string
727                 for (data++;*data != '\"';data++)
728                 {
729                         if (!*data || len >= (int)sizeof(com_token) - 1)
730                         {
731                                 com_token[0] = 0;
732                                 *datapointer = NULL;
733                                 return false;
734                         }
735                         c = *data;
736                         if (*data == '\\')
737                         {
738                                 if (data[1] == '"')
739                                 {
740                                         data++;
741                                         c = *data;
742                                 }
743                                 else if (data[1] == 'n')
744                                 {
745                                         data++;
746                                         c = '\n';
747                                 }
748                         }
749                         com_token[len++] = c;
750                 }
751                 com_token[len] = 0;
752                 *datapointer = data+1;
753                 return true;
754         }
755         else if (*data == '\'')
756         {
757                 // quoted string
758                 for (data++;*data != '\'';data++)
759                 {
760                         if (*data == '\\' && data[1] == '\'' )
761                                 data++;
762                         if (!*data || len >= (int)sizeof(com_token) - 1)
763                         {
764                                 com_token[0] = 0;
765                                 *datapointer = NULL;
766                                 return false;
767                         }
768                         com_token[len++] = *data;
769                 }
770                 com_token[len] = 0;
771                 *datapointer = data+1;
772                 return true;
773         }
774         else if (*data == '\r')
775         {
776                 // translate Mac line ending to UNIX
777                 com_token[len++] = '\n';
778                 com_token[len] = 0;
779                 *datapointer = data;
780                 return true;
781         }
782         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == '\'' || *data == ':' || *data == ',' || *data == ';')
783         {
784                 // single character
785                 com_token[len++] = *data++;
786                 com_token[len] = 0;
787                 *datapointer = data;
788                 return true;
789         }
790         else
791         {
792                 // regular word
793                 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != '\'' && *data != ':' && *data != ',' && *data != ';' && *data != '\'' && *data != '"';data++)
794                 {
795                         if (len >= (int)sizeof(com_token) - 1)
796                         {
797                                 com_token[0] = 0;
798                                 *datapointer = NULL;
799                                 return false;
800                         }
801                         com_token[len++] = *data;
802                 }
803                 com_token[len] = 0;
804                 *datapointer = data;
805                 return true;
806         }
807 }
808
809 /*
810 ==============
811 COM_ParseTokenConsole
812
813 Parse a token out of a string, behaving like the qwcl console
814 ==============
815 */
816 int COM_ParseTokenConsole(const char **datapointer)
817 {
818         int len;
819         int c;
820         const char *data = *datapointer;
821
822         len = 0;
823         com_token[0] = 0;
824
825         if (!data)
826         {
827                 *datapointer = NULL;
828                 return false;
829         }
830
831 // skip whitespace
832 skipwhite:
833         for (;*data <= ' ';data++)
834         {
835                 if (*data == 0)
836                 {
837                         // end of file
838                         *datapointer = NULL;
839                         return false;
840                 }
841         }
842
843         if (*data == '/' && data[1] == '/')
844         {
845                 // comment
846                 while (*data && *data != '\n' && *data != '\r')
847                         data++;
848                 goto skipwhite;
849         }
850         else if (*data == '\"')
851         {
852                 // quoted string
853                 for (data++;*data != '\"';data++)
854                 {
855                         if (!*data || len >= (int)sizeof(com_token) - 1)
856                         {
857                                 com_token[0] = 0;
858                                 *datapointer = NULL;
859                                 return false;
860                         }
861                         c = *data;
862                         if (*data == '\\')
863                         {
864                                 if (data[1] == '"')
865                                 {
866                                         data++;
867                                         c = *data;
868                                 }
869                                 else if (data[1] == 'n')
870                                 {
871                                         data++;
872                                         c = '\n';
873                                 }
874                         }
875                         com_token[len++] = c;
876                 }
877                 com_token[len] = 0;
878                 *datapointer = data+1;
879         }
880         else
881         {
882                 // regular word
883                 for (;*data > ' ';data++)
884                 {
885                         if (len >= (int)sizeof(com_token) - 1)
886                         {
887                                 com_token[0] = 0;
888                                 *datapointer = NULL;
889                                 return false;
890                         }
891                         com_token[len++] = *data;
892                 }
893                 com_token[len] = 0;
894                 *datapointer = data;
895         }
896
897         return true;
898 }
899
900
901 /*
902 ================
903 COM_CheckParm
904
905 Returns the position (1 to argc-1) in the program's argument list
906 where the given parameter apears, or 0 if not present
907 ================
908 */
909 int COM_CheckParm (const char *parm)
910 {
911         int i;
912
913         for (i=1 ; i<com_argc ; i++)
914         {
915                 if (!com_argv[i])
916                         continue;               // NEXTSTEP sometimes clears appkit vars.
917                 if (!strcmp (parm,com_argv[i]))
918                         return i;
919         }
920
921         return 0;
922 }
923
924 //===========================================================================
925
926 // Game mods
927
928 typedef struct gamemode_info_s
929 {
930         const char* prog_name;
931         const char* cmdline;
932         const char* gamename;
933         const char* gamedirname1;
934         const char* gamedirname2;
935         const char* gamescreenshotname;
936         const char* gameuserdirname;
937 } gamemode_info_t;
938
939 static const gamemode_info_t gamemode_info [] =
940 {// prog_name           cmdline                 gamename                                gamedirname     gamescreenshotname
941
942 // GAME_NORMAL
943 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
944 { "",                           "-quake",               "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces" },
945 // GAME_HIPNOTIC
946 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
947 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces" },
948 // GAME_ROGUE
949 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
950 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces" },
951 // GAME_NEHAHRA
952 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
953 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces" },
954 // GAME_NEXUIZ
955 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
956 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
957 // GAME_TRANSFUSION
958 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
959 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
960 // GAME_GOODVSBAD2
961 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
962 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2" },
963 // GAME_TEU
964 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
965 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu" },
966 // GAME_BATTLEMECH
967 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
968 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech" },
969 // GAME_ZYMOTIC
970 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
971 { "zymotic",            "-zymotic",             "Zymotic",                              "basezym",              NULL,                   "zymotic",              "zymotic" },
972 // GAME_SETHERAL
973 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
974 { "setheral",           "-setheral",    "Setheral",                             "data",         NULL,                   "setheral",             "setheral" },
975 // GAME_SOM
976 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
977 { "som",                        "-som",                 "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces" },
978 // GAME_TENEBRAE
979 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
980 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces" },
981 // GAME_NEOTERIC
982 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
983 { "neoteric",           "-neoteric",    "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces" },
984 // GAME_OPENQUARTZ
985 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
986 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces" },
987 // GAME_PRYDON
988 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
989 { "prydon",                     "-prydon",              "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces" },
990 // GAME_NETHERWORLD
991 // COMMANDLINEOPTION: Game: -netherworld runs the game Netherworld: Dark Master
992 { "netherworld",        "-netherworld", "Netherworld: Dark Master",     "id1",          "netherworld",  "nw",                   "darkplaces" },
993 // GAME_THEHUNTED
994 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
995 { "thehunted",          "-thehunted",   "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted" },
996 // GAME_DEFEATINDETAIL2
997 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
998 { "did2",                       "-did2",                "Defeat In Detail 2",   "data",         NULL,                   "did2_",                "did2" },
999 // GAME_DARSANA
1000 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1001 { "darsana",            "-darsana",     "Darsana",                      "ddata",        NULL,                   "darsana",                      "darsana" },
1002 // GAME_CONTAGIONTHEORY
1003 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1004 { "contagiontheory",            "-contagiontheory",     "Contagion Theory",                     "ctdata",       NULL,                   "ct",                   "contagiontheory" },
1005 };
1006
1007 void COM_InitGameType (void)
1008 {
1009         char name [MAX_OSPATH];
1010         unsigned int i;
1011
1012         FS_StripExtension (com_argv[0], name, sizeof (name));
1013         COM_ToLowerString (name, name, sizeof (name));
1014
1015         // Check the binary name; default to GAME_NORMAL (0)
1016         gamemode = GAME_NORMAL;
1017         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1018                 if (strstr (name, gamemode_info[i].prog_name))
1019                 {
1020                         gamemode = (gamemode_t)i;
1021                         break;
1022                 }
1023
1024         // Look for a command-line option
1025         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1026                 if (COM_CheckParm (gamemode_info[i].cmdline))
1027                 {
1028                         gamemode = (gamemode_t)i;
1029                         break;
1030                 }
1031
1032         gamename = gamemode_info[gamemode].gamename;
1033         gamedirname1 = gamemode_info[gamemode].gamedirname1;
1034         gamedirname2 = gamemode_info[gamemode].gamedirname2;
1035         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1036         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1037 }
1038
1039
1040 /*
1041 ================
1042 COM_Init
1043 ================
1044 */
1045 void COM_Init_Commands (void)
1046 {
1047         int i, j, n;
1048         char com_cmdline[MAX_INPUTLINE];
1049
1050         Cvar_RegisterVariable (&registered);
1051         Cvar_RegisterVariable (&cmdline);
1052
1053         // reconstitute the command line for the cmdline externally visible cvar
1054         n = 0;
1055         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1056         {
1057                 i = 0;
1058                 if (strstr(com_argv[j], " "))
1059                 {
1060                         // arg contains whitespace, store quotes around it
1061                         com_cmdline[n++] = '\"';
1062                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1063                                 com_cmdline[n++] = com_argv[j][i++];
1064                         com_cmdline[n++] = '\"';
1065                 }
1066                 else
1067                 {
1068                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1069                                 com_cmdline[n++] = com_argv[j][i++];
1070                 }
1071                 if (n < ((int)sizeof(com_cmdline) - 1))
1072                         com_cmdline[n++] = ' ';
1073                 else
1074                         break;
1075         }
1076         com_cmdline[n] = 0;
1077         Cvar_Set ("cmdline", com_cmdline);
1078 }
1079
1080 /*
1081 ============
1082 va
1083
1084 does a varargs printf into a temp buffer, so I don't need to have
1085 varargs versions of all text functions.
1086 FIXME: make this buffer size safe someday
1087 ============
1088 */
1089 char *va(const char *format, ...)
1090 {
1091         va_list argptr;
1092         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1093         static char string[8][1024], *s;
1094         static int stringindex = 0;
1095
1096         s = string[stringindex];
1097         stringindex = (stringindex + 1) & 7;
1098         va_start (argptr, format);
1099         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1100         va_end (argptr);
1101
1102         return s;
1103 }
1104
1105
1106 //======================================
1107
1108 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1109
1110 #undef snprintf
1111 #undef vsnprintf
1112
1113 #ifdef WIN32
1114 # define snprintf _snprintf
1115 # define vsnprintf _vsnprintf
1116 #endif
1117
1118
1119 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1120 {
1121         va_list args;
1122         int result;
1123
1124         va_start (args, format);
1125         result = dpvsnprintf (buffer, buffersize, format, args);
1126         va_end (args);
1127
1128         return result;
1129 }
1130
1131
1132 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1133 {
1134         int result;
1135
1136         result = vsnprintf (buffer, buffersize, format, args);
1137         if (result < 0 || (size_t)result >= buffersize)
1138         {
1139                 buffer[buffersize - 1] = '\0';
1140                 return -1;
1141         }
1142
1143         return result;
1144 }
1145
1146
1147 //======================================
1148
1149 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1150 {
1151         if (size_out == 0)
1152                 return;
1153
1154         while (*in && size_out > 1)
1155         {
1156                 if (*in >= 'A' && *in <= 'Z')
1157                         *out++ = *in++ + 'a' - 'A';
1158                 else
1159                         *out++ = *in++;
1160                 size_out--;
1161         }
1162         *out = '\0';
1163 }
1164
1165 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1166 {
1167         if (size_out == 0)
1168                 return;
1169
1170         while (*in && size_out > 1)
1171         {
1172                 if (*in >= 'a' && *in <= 'z')
1173                         *out++ = *in++ + 'A' - 'a';
1174                 else
1175                         *out++ = *in++;
1176                 size_out--;
1177         }
1178         *out = '\0';
1179 }
1180
1181 int COM_StringBeginsWith(const char *s, const char *match)
1182 {
1183         for (;*s && *match;s++, match++)
1184                 if (*s != *match)
1185                         return false;
1186         return true;
1187 }
1188
1189 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1190 {
1191         int argc, commentprefixlength;
1192         char *tokenbufend;
1193         const char *l;
1194         argc = 0;
1195         tokenbufend = tokenbuf + tokenbufsize;
1196         l = *text;
1197         commentprefixlength = 0;
1198         if (commentprefix)
1199                 commentprefixlength = (int)strlen(commentprefix);
1200         while (*l && *l != '\n' && *l != '\r')
1201         {
1202                 if (*l > ' ')
1203                 {
1204                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1205                         {
1206                                 while (*l && *l != '\n' && *l != '\r')
1207                                         l++;
1208                                 break;
1209                         }
1210                         if (argc >= maxargc)
1211                                 return -1;
1212                         argv[argc++] = tokenbuf;
1213                         if (*l == '"')
1214                         {
1215                                 l++;
1216                                 while (*l && *l != '"')
1217                                 {
1218                                         if (tokenbuf >= tokenbufend)
1219                                                 return -1;
1220                                         *tokenbuf++ = *l++;
1221                                 }
1222                                 if (*l == '"')
1223                                         l++;
1224                         }
1225                         else
1226                         {
1227                                 while (*l > ' ')
1228                                 {
1229                                         if (tokenbuf >= tokenbufend)
1230                                                 return -1;
1231                                         *tokenbuf++ = *l++;
1232                                 }
1233                         }
1234                         if (tokenbuf >= tokenbufend)
1235                                 return -1;
1236                         *tokenbuf++ = 0;
1237                 }
1238                 else
1239                         l++;
1240         }
1241         // line endings:
1242         // UNIX: \n
1243         // Mac: \r
1244         // Windows: \r\n
1245         if (*l == '\r')
1246                 l++;
1247         if (*l == '\n')
1248                 l++;
1249         *text = l;
1250         return argc;
1251 }
1252
1253 // written by Elric, thanks Elric!
1254 char *SearchInfostring(const char *infostring, const char *key)
1255 {
1256         static char value [MAX_INPUTLINE];
1257         char crt_key [MAX_INPUTLINE];
1258         size_t value_ind, key_ind;
1259         char c;
1260
1261         if (*infostring++ != '\\')
1262                 return NULL;
1263
1264         value_ind = 0;
1265         for (;;)
1266         {
1267                 key_ind = 0;
1268
1269                 // Get the key name
1270                 for (;;)
1271                 {
1272                         c = *infostring++;
1273
1274                         if (c == '\0')
1275                                 return NULL;
1276                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1277                         {
1278                                 crt_key[key_ind] = '\0';
1279                                 break;
1280                         }
1281
1282                         crt_key[key_ind++] = c;
1283                 }
1284
1285                 // If it's the key we are looking for, save it in "value"
1286                 if (!strcmp(crt_key, key))
1287                 {
1288                         for (;;)
1289                         {
1290                                 c = *infostring++;
1291
1292                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1293                                 {
1294                                         value[value_ind] = '\0';
1295                                         return value;
1296                                 }
1297
1298                                 value[value_ind++] = c;
1299                         }
1300                 }
1301
1302                 // Else, skip the value
1303                 for (;;)
1304                 {
1305                         c = *infostring++;
1306
1307                         if (c == '\0')
1308                                 return NULL;
1309                         if (c == '\\')
1310                                 break;
1311                 }
1312         }
1313 }
1314
1315 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1316 {
1317         int pos = 0, j;
1318         size_t keylength;
1319         if (!key)
1320                 key = "";
1321         if (!value)
1322                 value = "";
1323         keylength = strlen(key);
1324         if (valuelength < 1 || !value)
1325         {
1326                 Con_Printf("InfoString_GetValue: no room in value\n");
1327                 return;
1328         }
1329         value[0] = 0;
1330         if (strchr(key, '\\'))
1331         {
1332                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1333                 return;
1334         }
1335         if (strchr(key, '\"'))
1336         {
1337                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1338                 return;
1339         }
1340         if (!key[0])
1341         {
1342                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1343                 return;
1344         }
1345         while (buffer[pos] == '\\')
1346         {
1347                 if (!memcmp(buffer + pos+1, key, keylength))
1348                 {
1349                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1350                         pos++;
1351                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1352                                 value[j] = buffer[pos+j];
1353                         value[j] = 0;
1354                         return;
1355                 }
1356                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1357                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1358         }
1359         // if we reach this point the key was not found
1360 }
1361
1362 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1363 {
1364         int pos = 0, pos2;
1365         size_t keylength;
1366         if (!key)
1367                 key = "";
1368         if (!value)
1369                 value = "";
1370         keylength = strlen(key);
1371         if (strchr(key, '\\') || strchr(value, '\\'))
1372         {
1373                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1374                 return;
1375         }
1376         if (strchr(key, '\"') || strchr(value, '\"'))
1377         {
1378                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1379                 return;
1380         }
1381         if (!key[0])
1382         {
1383                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1384                 return;
1385         }
1386         while (buffer[pos] == '\\')
1387         {
1388                 if (!memcmp(buffer + pos+1, key, keylength))
1389                         break;
1390                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1391                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1392         }
1393         // if we found the key, find the end of it because we will be replacing it
1394         pos2 = pos;
1395         if (buffer[pos] == '\\')
1396         {
1397                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1398                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1399         }
1400         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1401         {
1402                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1403                 return;
1404         }
1405         if (value && value[0])
1406         {
1407                 // set the key/value and append the remaining text
1408                 char tempbuffer[4096];
1409                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
1410                 sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
1411         }
1412         else
1413         {
1414                 // just remove the key from the text
1415                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
1416         }
1417 }
1418
1419 void InfoString_Print(char *buffer)
1420 {
1421         int i;
1422         char key[2048];
1423         char value[2048];
1424         while (*buffer)
1425         {
1426                 if (*buffer != '\\')
1427                 {
1428                         Con_Printf("InfoString_Print: corrupt string\n");
1429                         return;
1430                 }
1431                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1432                         if (i < (int)sizeof(key)-1)
1433                                 key[i++] = *buffer;
1434                 key[i] = 0;
1435                 if (*buffer != '\\')
1436                 {
1437                         Con_Printf("InfoString_Print: corrupt string\n");
1438                         return;
1439                 }
1440                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1441                         if (i < (int)sizeof(value)-1)
1442                                 value[i++] = *buffer;
1443                 value[i] = 0;
1444                 // empty value is an error case
1445                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1446         }
1447 }
1448
1449 //========================================================
1450 // strlcat and strlcpy, from OpenBSD
1451
1452 /*
1453  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1454  *
1455  * Permission to use, copy, modify, and distribute this software for any
1456  * purpose with or without fee is hereby granted, provided that the above
1457  * copyright notice and this permission notice appear in all copies.
1458  *
1459  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1460  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1461  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1462  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1463  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1464  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1465  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1466  */
1467
1468 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1469 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1470
1471
1472 #ifndef HAVE_STRLCAT
1473 size_t
1474 strlcat(char *dst, const char *src, size_t siz)
1475 {
1476         register char *d = dst;
1477         register const char *s = src;
1478         register size_t n = siz;
1479         size_t dlen;
1480
1481         /* Find the end of dst and adjust bytes left but don't go past end */
1482         while (n-- != 0 && *d != '\0')
1483                 d++;
1484         dlen = d - dst;
1485         n = siz - dlen;
1486
1487         if (n == 0)
1488                 return(dlen + strlen(s));
1489         while (*s != '\0') {
1490                 if (n != 1) {
1491                         *d++ = *s;
1492                         n--;
1493                 }
1494                 s++;
1495         }
1496         *d = '\0';
1497
1498         return(dlen + (s - src));       /* count does not include NUL */
1499 }
1500 #endif  // #ifndef HAVE_STRLCAT
1501
1502
1503 #ifndef HAVE_STRLCPY
1504 size_t
1505 strlcpy(char *dst, const char *src, size_t siz)
1506 {
1507         register char *d = dst;
1508         register const char *s = src;
1509         register size_t n = siz;
1510
1511         /* Copy as many bytes as will fit */
1512         if (n != 0 && --n != 0) {
1513                 do {
1514                         if ((*d++ = *s++) == 0)
1515                                 break;
1516                 } while (--n != 0);
1517         }
1518
1519         /* Not enough room in dst, add NUL and traverse rest of src */
1520         if (n == 0) {
1521                 if (siz != 0)
1522                         *d = '\0';              /* NUL-terminate dst */
1523                 while (*s++)
1524                         ;
1525         }
1526
1527         return(s - src - 1);    /* count does not include NUL */
1528 }
1529
1530 #endif  // #ifndef HAVE_STRLCPY