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