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