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