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