2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // common.c -- misc functions used in client and server
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"};
34 char com_token[MAX_INPUTLINE];
36 const char **com_argv;
40 const char *gamedirname1;
41 const char *gamedirname2;
42 const char *gamescreenshotname;
43 const char *gameuserdirname;
44 char com_modname[MAX_OSPATH] = "";
48 ============================================================================
52 ============================================================================
55 short ShortSwap (short l)
67 unsigned char b1,b2,b3,b4;
74 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
77 float FloatSwap (float 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];
95 // Extract integers from buffers
97 unsigned int BuffBigLong (const unsigned char *buffer)
99 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
102 unsigned short BuffBigShort (const unsigned char *buffer)
104 return (buffer[0] << 8) | buffer[1];
107 unsigned int BuffLittleLong (const unsigned char *buffer)
109 return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
112 unsigned short BuffLittleShort (const unsigned char *buffer)
114 return (buffer[1] << 8) | buffer[0];
119 ============================================================================
123 ============================================================================
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
130 #define CRC_INIT_VALUE 0xffff
131 #define CRC_XOR_VALUE 0x0000
133 static unsigned short crctable[256] =
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
169 unsigned short CRC_Block(const unsigned char *data, size_t size)
171 unsigned short crc = CRC_INIT_VALUE;
173 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
174 return crc ^ CRC_XOR_VALUE;
177 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
179 unsigned short crc = CRC_INIT_VALUE;
181 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
182 return crc ^ CRC_XOR_VALUE;
186 static unsigned char chktbl[1024 + 4] =
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,
221 // map checksum goes here
226 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
229 unsigned char chkb[60 + 4];
231 p = chktbl + (sequence % (sizeof(chktbl) - 8));
235 memcpy(chkb, base, length);
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];
242 return CRC_Block(chkb, length + 4) & 0xff;
246 ==============================================================================
250 Handles byte ordering and avoids alignment errors
251 ==============================================================================
258 void MSG_WriteChar (sizebuf_t *sb, int c)
262 buf = SZ_GetSpace (sb, 1);
266 void MSG_WriteByte (sizebuf_t *sb, int c)
270 buf = SZ_GetSpace (sb, 1);
274 void MSG_WriteShort (sizebuf_t *sb, int c)
278 buf = SZ_GetSpace (sb, 2);
283 void MSG_WriteLong (sizebuf_t *sb, int c)
287 buf = SZ_GetSpace (sb, 4);
289 buf[1] = (c>>8)&0xff;
290 buf[2] = (c>>16)&0xff;
294 void MSG_WriteFloat (sizebuf_t *sb, float f)
304 dat.l = LittleLong (dat.l);
306 SZ_Write (sb, (unsigned char *)&dat.l, 4);
309 void MSG_WriteString (sizebuf_t *sb, const char *s)
312 MSG_WriteChar (sb, 0);
314 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
317 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
320 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
323 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
326 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
328 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
331 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
334 MSG_WriteShort (sb, (int)(f + 0.5));
336 MSG_WriteShort (sb, (int)(f - 0.5));
339 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
341 MSG_WriteFloat (sb, f);
344 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
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);
353 MSG_WriteCoord32f (sb, f);
356 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
358 MSG_WriteCoord (sb, v[0], protocol);
359 MSG_WriteCoord (sb, v[1], protocol);
360 MSG_WriteCoord (sb, v[2], protocol);
363 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
364 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
367 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
369 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
372 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
375 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
377 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
380 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
382 MSG_WriteFloat (sb, f);
385 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
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);
390 MSG_WriteAngle16i (sb, f);
397 qboolean msg_badread;
399 void MSG_BeginReading (void)
405 int MSG_ReadLittleShort (void)
407 if (msg_readcount+2 > net_message.cursize)
413 return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
416 int MSG_ReadBigShort (void)
418 if (msg_readcount+2 > net_message.cursize)
424 return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
427 int MSG_ReadLittleLong (void)
429 if (msg_readcount+4 > net_message.cursize)
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);
438 int MSG_ReadBigLong (void)
440 if (msg_readcount+4 > net_message.cursize)
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];
449 float MSG_ReadLittleFloat (void)
456 if (msg_readcount+4 > net_message.cursize)
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);
466 float MSG_ReadBigFloat (void)
473 if (msg_readcount+4 > net_message.cursize)
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];
483 char *MSG_ReadString (void)
485 static char string[MAX_INPUTLINE];
487 for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
493 int MSG_ReadBytes (int numbytes, unsigned char *out)
496 for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
501 float MSG_ReadCoord13i (void)
503 return MSG_ReadLittleShort() * (1.0/8.0);
506 float MSG_ReadCoord16i (void)
508 return (signed short) MSG_ReadLittleShort();
511 float MSG_ReadCoord32f (void)
513 return MSG_ReadLittleFloat();
516 float MSG_ReadCoord (protocolversion_t protocol)
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();
525 return MSG_ReadCoord32f();
528 void MSG_ReadVector (float *v, protocolversion_t protocol)
530 v[0] = MSG_ReadCoord(protocol);
531 v[1] = MSG_ReadCoord(protocol);
532 v[2] = MSG_ReadCoord(protocol);
535 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
536 float MSG_ReadAngle8i (void)
538 return (signed char) MSG_ReadByte () * (360.0/256.0);
541 float MSG_ReadAngle16i (void)
543 return (signed short)MSG_ReadShort () * (360.0/65536.0);
546 float MSG_ReadAngle32f (void)
548 return MSG_ReadFloat ();
551 float MSG_ReadAngle (protocolversion_t protocol)
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 ();
556 return MSG_ReadAngle16i ();
560 //===========================================================================
562 void SZ_Clear (sizebuf_t *buf)
567 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
571 if (buf->cursize + length > buf->maxsize)
573 if (!buf->allowoverflow)
574 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
576 if (length > buf->maxsize)
577 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
579 buf->overflowed = true;
580 Con_Print("SZ_GetSpace: overflow\n");
584 data = buf->data + buf->cursize;
585 buf->cursize += length;
590 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
592 memcpy (SZ_GetSpace(buf,length),data,length);
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.
599 static char *hexchar = "0123456789ABCDEF";
600 void Com_HexDumpToConsole(const unsigned char *data, int size)
604 char *cur, *flushpointer;
605 const unsigned char *d;
607 flushpointer = text + 512;
608 for (i = 0;i < size;)
615 *cur++ = hexchar[(i >> 12) & 15];
616 *cur++ = hexchar[(i >> 8) & 15];
617 *cur++ = hexchar[(i >> 4) & 15];
618 *cur++ = hexchar[(i >> 0) & 15];
621 for (j = 0;j < 16;j++)
625 *cur++ = hexchar[(d[j] >> 4) & 15];
626 *cur++ = hexchar[(d[j] >> 0) & 15];
637 for (j = 0;j < 16;j++)
641 // color change prefix character has to be treated specially
642 if (d[j] == STRING_COLOR_TAG)
644 *cur++ = STRING_COLOR_TAG;
645 *cur++ = STRING_COLOR_TAG;
647 else if (d[j] >= (unsigned char) ' ')
657 if (cur >= flushpointer || i >= size)
666 void SZ_HexDumpToConsole(const sizebuf_t *buf)
668 Com_HexDumpToConsole(buf->data, buf->cursize);
672 //============================================================================
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.
684 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
686 The sum of the return values of the processLine function will be returned.
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)
691 // Logic is as follows:
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.
700 qboolean isContinuation = false;
702 const char *startOfLine = string;
703 const char *cursor = string;
704 const char *end = string + length;
705 float spaceUsedInLine = 0;
706 float spaceUsedForWord;
713 wordWidth(passthroughCW, NULL, &dummy, -1);
715 spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
719 char ch = (cursor < end) ? *cursor : 0;
722 case 0: // end of string
723 result += processLine(passthroughPL, startOfLine, u8_strnlen(startOfLine, cursor - startOfLine), spaceUsedInLine, isContinuation);
724 isContinuation = false;
727 case '\n': // end of line
728 result += processLine(passthroughPL, startOfLine, u8_strnlen(startOfLine, cursor - startOfLine), spaceUsedInLine, isContinuation);
729 isContinuation = false;
731 startOfLine = cursor;
735 spaceUsedInLine += spaceWidth;
739 while(cursor + wordLen < end)
741 switch(cursor[wordLen])
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
758 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
760 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
762 // we can simply append it
764 spaceUsedInLine += spaceUsedForWord;
768 // output current line
769 result += processLine(passthroughPL, startOfLine, u8_strnlen(startOfLine, cursor - startOfLine), spaceUsedInLine, isContinuation);
770 isContinuation = true;
771 startOfLine = cursor;
773 spaceUsedInLine = continuationWidth + spaceUsedForWord;
782 qboolean isContinuation = false;
783 float currentWordSpace = 0;
784 const char *currentWord = 0;
785 float minReserve = 0;
787 float spaceUsedInLine = 0;
788 const char *currentLine = 0;
789 const char *currentLineEnd = 0;
790 float currentLineFinalWhitespace = 0;
794 minReserve = charWidth(passthroughCW, 0);
795 minReserve += charWidth(passthroughCW, ' ');
797 if(maxWidth < continuationWidth + minReserve)
798 maxWidth = continuationWidth + minReserve;
800 charWidth(passthroughCW, 0);
802 for(p = string; p < string + length; ++p)
805 float w = charWidth(passthroughCW, c);
810 currentWordSpace = 0;
816 spaceUsedInLine = isContinuation ? continuationWidth : 0;
822 // 1. I can add the word AND a space - then just append it.
823 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
825 currentLineEnd = p; // note: space not included here
826 currentLineFinalWhitespace = w;
827 spaceUsedInLine += currentWordSpace + w;
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)
832 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
834 isContinuation = true;
836 // 3. Otherwise, output current line and go to next one, where I can add the word.
837 else if(continuationWidth + currentWordSpace + w <= maxWidth)
840 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
841 currentLine = currentWord;
842 spaceUsedInLine = continuationWidth + currentWordSpace + w;
844 currentLineFinalWhitespace = w;
845 isContinuation = true;
847 // 4. We can't even do that? Then output both current and next word as new lines.
852 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
853 isContinuation = true;
855 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
857 isContinuation = true;
863 // 1. I can add the word - then do it.
864 if(spaceUsedInLine + currentWordSpace <= maxWidth)
866 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
868 // 2. Otherwise, output current line, next one and make tabula rasa.
873 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
874 isContinuation = true;
876 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
880 isContinuation = false;
884 currentWordSpace += w;
886 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
888 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
891 // this word cannot join ANY line...
892 // so output the current line...
895 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
896 isContinuation = true;
899 // then this word's beginning...
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;
907 // reset the char width function to a state where no kerning occurs (start of word)
908 charWidth(passthroughCW, ' ');
911 float w = charWidth(passthroughCW, *pos);
912 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
914 // print everything until it
915 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
918 currentWordSpace = 0;
920 currentWordSpace += w;
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;
929 isContinuation = true;
933 // we have a guarantee that it will fix (see if clause)
934 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
936 // and use the rest of this word as new start of a line
937 currentWordSpace = w;
939 spaceUsedInLine = continuationWidth;
942 isContinuation = true;
951 currentWordSpace = 0;
954 if(currentLine) // Same procedure as \n
956 // Can I append the current word?
957 if(spaceUsedInLine + currentWordSpace <= maxWidth)
958 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
963 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
964 isContinuation = true;
966 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
976 COM_ParseToken_Simple
978 Parse a token out of a string
981 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
985 const char *data = *datapointer;
1002 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1007 *datapointer = NULL;
1012 // handle Windows line ending
1013 if (data[0] == '\r' && data[1] == '\n')
1016 if (data[0] == '/' && data[1] == '/')
1019 while (*data && *data != '\n' && *data != '\r')
1023 else if (data[0] == '/' && data[1] == '*')
1027 while (*data && (data[0] != '*' || data[1] != '/'))
1035 else if (*data == '\"')
1038 for (data++;*data && *data != '\"';data++)
1041 if (*data == '\\' && parsebackslash)
1050 if (len < (int)sizeof(com_token) - 1)
1051 com_token[len++] = c;
1056 *datapointer = data;
1059 else if (*data == '\r')
1061 // translate Mac line ending to UNIX
1062 com_token[len++] = '\n';data++;
1064 *datapointer = data;
1067 else if (*data == '\n')
1070 com_token[len++] = *data++;
1072 *datapointer = data;
1078 for (;!ISWHITESPACE(*data);data++)
1079 if (len < (int)sizeof(com_token) - 1)
1080 com_token[len++] = *data;
1082 *datapointer = data;
1089 COM_ParseToken_QuakeC
1091 Parse a token out of a string
1094 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1098 const char *data = *datapointer;
1105 *datapointer = NULL;
1115 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1120 *datapointer = NULL;
1125 // handle Windows line ending
1126 if (data[0] == '\r' && data[1] == '\n')
1129 if (data[0] == '/' && data[1] == '/')
1132 while (*data && *data != '\n' && *data != '\r')
1136 else if (data[0] == '/' && data[1] == '*')
1140 while (*data && (data[0] != '*' || data[1] != '/'))
1148 else if (*data == '\"' || *data == '\'')
1152 for (data++;*data && *data != quote;data++)
1164 if (len < (int)sizeof(com_token) - 1)
1165 com_token[len++] = c;
1170 *datapointer = data;
1173 else if (*data == '\r')
1175 // translate Mac line ending to UNIX
1176 com_token[len++] = '\n';data++;
1178 *datapointer = data;
1181 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1184 com_token[len++] = *data++;
1186 *datapointer = data;
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;
1196 *datapointer = data;
1203 COM_ParseToken_VM_Tokenize
1205 Parse a token out of a string
1208 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1212 const char *data = *datapointer;
1219 *datapointer = NULL;
1229 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1234 *datapointer = NULL;
1239 // handle Windows line ending
1240 if (data[0] == '\r' && data[1] == '\n')
1243 if (data[0] == '/' && data[1] == '/')
1246 while (*data && *data != '\n' && *data != '\r')
1250 else if (data[0] == '/' && data[1] == '*')
1254 while (*data && (data[0] != '*' || data[1] != '/'))
1262 else if (*data == '\"' || *data == '\'')
1266 for (data++;*data && *data != quote;data++)
1278 if (len < (int)sizeof(com_token) - 1)
1279 com_token[len++] = c;
1284 *datapointer = data;
1287 else if (*data == '\r')
1289 // translate Mac line ending to UNIX
1290 com_token[len++] = '\n';data++;
1292 *datapointer = data;
1295 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1298 com_token[len++] = *data++;
1300 *datapointer = data;
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;
1310 *datapointer = data;
1317 COM_ParseToken_Console
1319 Parse a token out of a string, behaving like the qwcl console
1322 int COM_ParseToken_Console(const char **datapointer)
1325 const char *data = *datapointer;
1332 *datapointer = NULL;
1338 for (;ISWHITESPACE(*data);data++)
1343 *datapointer = NULL;
1348 if (*data == '/' && data[1] == '/')
1351 while (*data && *data != '\n' && *data != '\r')
1355 else if (*data == '\"')
1358 for (data++;*data && *data != '\"';data++)
1360 // allow escaped " and \ case
1361 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1363 if (len < (int)sizeof(com_token) - 1)
1364 com_token[len++] = *data;
1369 *datapointer = data;
1374 for (;!ISWHITESPACE(*data);data++)
1375 if (len < (int)sizeof(com_token) - 1)
1376 com_token[len++] = *data;
1378 *datapointer = data;
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
1393 int COM_CheckParm (const char *parm)
1397 for (i=1 ; i<com_argc ; i++)
1400 continue; // NEXTSTEP sometimes clears appkit vars.
1401 if (!strcmp (parm,com_argv[i]))
1408 //===========================================================================
1412 typedef struct gamemode_info_s
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;
1423 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1424 {// prog_name cmdline gamename basegame modgame screenshotprefix userdir
1427 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1428 { "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" },
1430 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1431 { "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" },
1433 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1434 { "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" },
1436 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1437 { "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" },
1439 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1440 { "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" },
1442 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1443 { "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" },
1445 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1446 { "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" },
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" },
1451 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1452 { "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" },
1454 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1455 { "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" },
1457 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1458 { "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" },
1460 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1461 { "som", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" },
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" },
1466 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1467 { "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" },
1469 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1470 { "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" },
1472 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1473 { "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" },
1475 // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1476 { "dq", "-dq", "Deluxe Quake", "basedq", "extradq", "basedq", "dq" },
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" },
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" },
1490 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1491 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
1493 // COMMANDLINEOPTION: Game: -blademaster runs the game Prophecy: Return of the BladeMaster
1494 { "blademaster", "-blademaster", "Prophecy: Return of the BladeMaster", "basebm", NULL, "blademaster", "blademaster" },
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" },
1503 void COM_InitGameType (void)
1505 char name [MAX_OSPATH];
1508 FS_StripExtension (com_argv[0], name, sizeof (name));
1509 COM_ToLowerString (name, name, sizeof (name));
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))
1516 gamemode = (gamemode_t)i;
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))
1524 gamemode = (gamemode_t)i;
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;
1541 void COM_Init_Commands (void)
1544 char com_cmdline[MAX_INPUTLINE];
1546 Cvar_RegisterVariable (®istered);
1547 Cvar_RegisterVariable (&cmdline);
1549 // reconstitute the command line for the cmdline externally visible cvar
1551 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1554 if (strstr(com_argv[j], " "))
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++] = '\"';
1564 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1565 com_cmdline[n++] = com_argv[j][i++];
1567 if (n < ((int)sizeof(com_cmdline) - 1))
1568 com_cmdline[n++] = ' ';
1573 Cvar_Set ("cmdline", com_cmdline);
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
1585 char *va(const char *format, ...)
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;
1592 s = string[stringindex];
1593 stringindex = (stringindex + 1) & 7;
1594 va_start (argptr, format);
1595 dpvsnprintf (s, sizeof (string[0]), format,argptr);
1602 //======================================
1604 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1610 # define snprintf _snprintf
1611 # define vsnprintf _vsnprintf
1615 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1620 va_start (args, format);
1621 result = dpvsnprintf (buffer, buffersize, format, args);
1628 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1632 #if _MSC_VER >= 1400
1633 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1635 result = vsnprintf (buffer, buffersize, format, args);
1637 if (result < 0 || (size_t)result >= buffersize)
1639 buffer[buffersize - 1] = '\0';
1647 //======================================
1649 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1654 while (*in && size_out > 1)
1656 if (*in >= 'A' && *in <= 'Z')
1657 *out++ = *in++ + 'a' - 'A';
1665 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1670 while (*in && size_out > 1)
1672 if (*in >= 'a' && *in <= 'z')
1673 *out++ = *in++ + 'A' - 'a';
1681 int COM_StringBeginsWith(const char *s, const char *match)
1683 for (;*s && *match;s++, match++)
1689 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1691 int argc, commentprefixlength;
1695 tokenbufend = tokenbuf + tokenbufsize;
1697 commentprefixlength = 0;
1699 commentprefixlength = (int)strlen(commentprefix);
1700 while (*l && *l != '\n' && *l != '\r')
1702 if (!ISWHITESPACE(*l))
1704 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1706 while (*l && *l != '\n' && *l != '\r')
1710 if (argc >= maxargc)
1712 argv[argc++] = tokenbuf;
1716 while (*l && *l != '"')
1718 if (tokenbuf >= tokenbufend)
1727 while (!ISWHITESPACE(*l))
1729 if (tokenbuf >= tokenbufend)
1734 if (tokenbuf >= tokenbufend)
1755 COM_StringLengthNoColors
1757 calculates the visible width of a color coded string.
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.
1763 valid can be set to NULL if the caller doesn't care.
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.
1770 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1772 const char *end = size_s ? (s + size_s) : NULL;
1776 switch((s == end) ? 0 : *s)
1782 case STRING_COLOR_TAG:
1784 switch((s == end) ? 0 : *s)
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]) )
1794 ++len; // STRING_COLOR_TAG
1795 ++len; // STRING_COLOR_RGB_TAG_CHAR
1797 case 0: // ends with unfinished color code!
1802 case STRING_COLOR_TAG: // escaped ^
1805 case '0': case '1': case '2': case '3': case '4':
1806 case '5': case '6': case '7': case '8': case '9': // color code
1808 default: // not a color code
1809 ++len; // STRING_COLOR_TAG
1810 ++len; // the character
1825 COM_StringDecolorize
1827 removes color codes from a string.
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
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
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.
1844 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
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;
1852 switch((in == end) ? 0 : *in)
1857 case STRING_COLOR_TAG:
1859 switch((in == end) ? 0 : *in)
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]) )
1869 APPEND(STRING_COLOR_TAG);
1871 APPEND(STRING_COLOR_TAG);
1872 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1874 case 0: // ends with unfinished color code!
1875 APPEND(STRING_COLOR_TAG);
1876 // finish the code by appending another caret when escaping
1878 APPEND(STRING_COLOR_TAG);
1881 case STRING_COLOR_TAG: // escaped ^
1882 APPEND(STRING_COLOR_TAG);
1883 // append a ^ twice when escaping
1885 APPEND(STRING_COLOR_TAG);
1887 case '0': case '1': case '2': case '3': case '4':
1888 case '5': case '6': case '7': case '8': case '9': // color code
1890 default: // not a color code
1891 APPEND(STRING_COLOR_TAG);
1906 // written by Elric, thanks Elric!
1907 char *SearchInfostring(const char *infostring, const char *key)
1909 static char value [MAX_INPUTLINE];
1910 char crt_key [MAX_INPUTLINE];
1911 size_t value_ind, key_ind;
1914 if (*infostring++ != '\\')
1929 if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1931 crt_key[key_ind] = '\0';
1935 crt_key[key_ind++] = c;
1938 // If it's the key we are looking for, save it in "value"
1939 if (!strcmp(crt_key, key))
1945 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1947 value[value_ind] = '\0';
1951 value[value_ind++] = c;
1955 // Else, skip the value
1968 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1976 keylength = strlen(key);
1977 if (valuelength < 1 || !value)
1979 Con_Printf("InfoString_GetValue: no room in value\n");
1983 if (strchr(key, '\\'))
1985 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1988 if (strchr(key, '\"'))
1990 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1995 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1998 while (buffer[pos] == '\\')
2000 if (!memcmp(buffer + pos+1, key, keylength))
2002 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2004 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2005 value[j] = buffer[pos+j];
2009 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2010 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2012 // if we reach this point the key was not found
2015 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2023 keylength = strlen(key);
2024 if (strchr(key, '\\') || strchr(value, '\\'))
2026 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2029 if (strchr(key, '\"') || strchr(value, '\"'))
2031 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2036 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2039 while (buffer[pos] == '\\')
2041 if (!memcmp(buffer + pos+1, key, keylength))
2043 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2044 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2046 // if we found the key, find the end of it because we will be replacing it
2048 if (buffer[pos] == '\\')
2050 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2051 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2053 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2055 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2058 if (value && value[0])
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);
2067 // just remove the key from the text
2068 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2072 void InfoString_Print(char *buffer)
2079 if (*buffer != '\\')
2081 Con_Printf("InfoString_Print: corrupt string\n");
2084 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2085 if (i < (int)sizeof(key)-1)
2088 if (*buffer != '\\')
2090 Con_Printf("InfoString_Print: corrupt string\n");
2093 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2094 if (i < (int)sizeof(value)-1)
2095 value[i++] = *buffer;
2097 // empty value is an error case
2098 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2102 //========================================================
2103 // strlcat and strlcpy, from OpenBSD
2106 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
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.
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.
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 $ */
2125 #ifndef HAVE_STRLCAT
2127 strlcat(char *dst, const char *src, size_t siz)
2129 register char *d = dst;
2130 register const char *s = src;
2131 register size_t n = siz;
2134 /* Find the end of dst and adjust bytes left but don't go past end */
2135 while (n-- != 0 && *d != '\0')
2141 return(dlen + strlen(s));
2142 while (*s != '\0') {
2151 return(dlen + (s - src)); /* count does not include NUL */
2153 #endif // #ifndef HAVE_STRLCAT
2156 #ifndef HAVE_STRLCPY
2158 strlcpy(char *dst, const char *src, size_t siz)
2160 register char *d = dst;
2161 register const char *s = src;
2162 register size_t n = siz;
2164 /* Copy as many bytes as will fit */
2165 if (n != 0 && --n != 0) {
2167 if ((*d++ = *s++) == 0)
2172 /* Not enough room in dst, add NUL and traverse rest of src */
2175 *d = '\0'; /* NUL-terminate dst */
2180 return(s - src - 1); /* count does not include NUL */
2183 #endif // #ifndef HAVE_STRLCPY
2185 void FindFraction(double val, int *num, int *denom, int denomMax)
2190 bestdiff = fabs(val);
2194 for(i = 1; i <= denomMax; ++i)
2196 int inum = (int) floor(0.5 + val * i);
2197 double diff = fabs(val - inum / (double)i);