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 ============================================================================
56 float BuffBigFloat (const unsigned char *buffer)
64 u.i = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
68 int BuffBigLong (const unsigned char *buffer)
70 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
73 short BuffBigShort (const unsigned char *buffer)
75 return (buffer[0] << 8) | buffer[1];
78 float BuffLittleFloat (const unsigned char *buffer)
86 u.i = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
90 int BuffLittleLong (const unsigned char *buffer)
92 return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
95 short BuffLittleShort (const unsigned char *buffer)
97 return (buffer[1] << 8) | buffer[0];
100 void StoreBigLong (unsigned char *buffer, unsigned int i)
102 buffer[0] = (i >> 24) & 0xFF;
103 buffer[1] = (i >> 16) & 0xFF;
104 buffer[2] = (i >> 8) & 0xFF;
105 buffer[3] = i & 0xFF;
109 ============================================================================
113 ============================================================================
116 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
117 // and the initial and final xor values shown below... in other words, the
118 // CCITT standard CRC used by XMODEM
120 #define CRC_INIT_VALUE 0xffff
121 #define CRC_XOR_VALUE 0x0000
123 static unsigned short crctable[256] =
125 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
126 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
127 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
128 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
129 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
130 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
131 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
132 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
133 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
134 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
135 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
136 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
137 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
138 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
139 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
140 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
141 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
142 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
143 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
144 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
145 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
146 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
147 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
148 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
149 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
150 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
151 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
152 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
153 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
154 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
155 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
156 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
159 unsigned short CRC_Block(const unsigned char *data, size_t size)
161 unsigned short crc = CRC_INIT_VALUE;
163 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
164 return crc ^ CRC_XOR_VALUE;
167 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
169 unsigned short crc = CRC_INIT_VALUE;
171 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
172 return crc ^ CRC_XOR_VALUE;
176 static unsigned char chktbl[1024 + 4] =
178 0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
179 0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
180 0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
181 0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
182 0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
183 0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
184 0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
185 0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
186 0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
187 0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
188 0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
189 0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
190 0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
191 0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
192 0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
193 0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
194 0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
195 0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
196 0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
197 0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
198 0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
199 0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
200 0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
201 0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
202 0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
203 0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
204 0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
205 0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
206 0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
207 0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
208 0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
209 0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
211 // map checksum goes here
216 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
219 unsigned char chkb[60 + 4];
221 p = chktbl + (sequence % (sizeof(chktbl) - 8));
225 memcpy(chkb, base, length);
227 chkb[length] = (sequence & 0xff) ^ p[0];
228 chkb[length+1] = p[1];
229 chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
230 chkb[length+3] = p[3];
232 return CRC_Block(chkb, length + 4) & 0xff;
236 ==============================================================================
240 Handles byte ordering and avoids alignment errors
241 ==============================================================================
248 void MSG_WriteChar (sizebuf_t *sb, int c)
252 buf = SZ_GetSpace (sb, 1);
256 void MSG_WriteByte (sizebuf_t *sb, int c)
260 buf = SZ_GetSpace (sb, 1);
264 void MSG_WriteShort (sizebuf_t *sb, int c)
268 buf = SZ_GetSpace (sb, 2);
273 void MSG_WriteLong (sizebuf_t *sb, int c)
277 buf = SZ_GetSpace (sb, 4);
279 buf[1] = (c>>8)&0xff;
280 buf[2] = (c>>16)&0xff;
284 void MSG_WriteFloat (sizebuf_t *sb, float f)
294 dat.l = LittleLong (dat.l);
296 SZ_Write (sb, (unsigned char *)&dat.l, 4);
299 void MSG_WriteString (sizebuf_t *sb, const char *s)
302 MSG_WriteChar (sb, 0);
304 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
307 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
310 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
313 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
316 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
318 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
321 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
324 MSG_WriteShort (sb, (int)(f + 0.5));
326 MSG_WriteShort (sb, (int)(f - 0.5));
329 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
331 MSG_WriteFloat (sb, f);
334 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
336 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
337 MSG_WriteCoord13i (sb, f);
338 else if (protocol == PROTOCOL_DARKPLACES1)
339 MSG_WriteCoord32f (sb, f);
340 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
341 MSG_WriteCoord16i (sb, f);
343 MSG_WriteCoord32f (sb, f);
346 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
348 MSG_WriteCoord (sb, v[0], protocol);
349 MSG_WriteCoord (sb, v[1], protocol);
350 MSG_WriteCoord (sb, v[2], protocol);
353 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
354 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
357 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
359 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
362 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
365 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
367 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
370 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
372 MSG_WriteFloat (sb, f);
375 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
377 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)
378 MSG_WriteAngle8i (sb, f);
380 MSG_WriteAngle16i (sb, f);
387 qboolean msg_badread;
389 void MSG_BeginReading (void)
395 int MSG_ReadLittleShort (void)
397 if (msg_readcount+2 > net_message.cursize)
403 return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
406 int MSG_ReadBigShort (void)
408 if (msg_readcount+2 > net_message.cursize)
414 return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
417 int MSG_ReadLittleLong (void)
419 if (msg_readcount+4 > net_message.cursize)
425 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);
428 int MSG_ReadBigLong (void)
430 if (msg_readcount+4 > net_message.cursize)
436 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];
439 float MSG_ReadLittleFloat (void)
446 if (msg_readcount+4 > net_message.cursize)
452 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);
456 float MSG_ReadBigFloat (void)
463 if (msg_readcount+4 > net_message.cursize)
469 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];
473 char *MSG_ReadString (void)
475 static char string[MAX_INPUTLINE];
477 for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
483 int MSG_ReadBytes (int numbytes, unsigned char *out)
486 for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
491 float MSG_ReadCoord13i (void)
493 return MSG_ReadLittleShort() * (1.0/8.0);
496 float MSG_ReadCoord16i (void)
498 return (signed short) MSG_ReadLittleShort();
501 float MSG_ReadCoord32f (void)
503 return MSG_ReadLittleFloat();
506 float MSG_ReadCoord (protocolversion_t protocol)
508 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
509 return MSG_ReadCoord13i();
510 else if (protocol == PROTOCOL_DARKPLACES1)
511 return MSG_ReadCoord32f();
512 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
513 return MSG_ReadCoord16i();
515 return MSG_ReadCoord32f();
518 void MSG_ReadVector (float *v, protocolversion_t protocol)
520 v[0] = MSG_ReadCoord(protocol);
521 v[1] = MSG_ReadCoord(protocol);
522 v[2] = MSG_ReadCoord(protocol);
525 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
526 float MSG_ReadAngle8i (void)
528 return (signed char) MSG_ReadByte () * (360.0/256.0);
531 float MSG_ReadAngle16i (void)
533 return (signed short)MSG_ReadShort () * (360.0/65536.0);
536 float MSG_ReadAngle32f (void)
538 return MSG_ReadFloat ();
541 float MSG_ReadAngle (protocolversion_t protocol)
543 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)
544 return MSG_ReadAngle8i ();
546 return MSG_ReadAngle16i ();
550 //===========================================================================
552 void SZ_Clear (sizebuf_t *buf)
557 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
561 if (buf->cursize + length > buf->maxsize)
563 if (!buf->allowoverflow)
564 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
566 if (length > buf->maxsize)
567 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
569 buf->overflowed = true;
570 Con_Print("SZ_GetSpace: overflow\n");
574 data = buf->data + buf->cursize;
575 buf->cursize += length;
580 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
582 memcpy (SZ_GetSpace(buf,length),data,length);
585 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
586 // attention, it has been eradicated from here, its only (former) use in
587 // all of darkplaces.
589 static char *hexchar = "0123456789ABCDEF";
590 void Com_HexDumpToConsole(const unsigned char *data, int size)
594 char *cur, *flushpointer;
595 const unsigned char *d;
597 flushpointer = text + 512;
598 for (i = 0;i < size;)
605 *cur++ = hexchar[(i >> 12) & 15];
606 *cur++ = hexchar[(i >> 8) & 15];
607 *cur++ = hexchar[(i >> 4) & 15];
608 *cur++ = hexchar[(i >> 0) & 15];
611 for (j = 0;j < 16;j++)
615 *cur++ = hexchar[(d[j] >> 4) & 15];
616 *cur++ = hexchar[(d[j] >> 0) & 15];
627 for (j = 0;j < 16;j++)
631 // color change prefix character has to be treated specially
632 if (d[j] == STRING_COLOR_TAG)
634 *cur++ = STRING_COLOR_TAG;
635 *cur++ = STRING_COLOR_TAG;
637 else if (d[j] >= (unsigned char) ' ')
647 if (cur >= flushpointer || i >= size)
656 void SZ_HexDumpToConsole(const sizebuf_t *buf)
658 Com_HexDumpToConsole(buf->data, buf->cursize);
662 //============================================================================
668 Word wraps a string. The wordWidth function is guaranteed to be called exactly
669 once for each word in the string, so it may be stateful, no idea what that
670 would be good for any more. At the beginning of the string, it will be called
671 for the char 0 to initialize a clean state, and then once with the string " "
672 (a space) so the routine knows how long a space is.
674 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
676 The sum of the return values of the processLine function will be returned.
679 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 // Logic is as follows:
683 // For each word or whitespace:
684 // Newline found? Output current line, advance to next line. This is not a continuation. Continue.
685 // Space found? Always add it to the current line, no matter if it fits.
686 // Word found? Check if current line + current word fits.
687 // If it fits, append it. Continue.
688 // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
690 qboolean isContinuation = false;
692 const char *startOfLine = string;
693 const char *cursor = string;
694 const char *end = string + length;
695 float spaceUsedInLine = 0;
696 float spaceUsedForWord;
703 wordWidth(passthroughCW, NULL, &dummy, -1);
705 spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
709 char ch = (cursor < end) ? *cursor : 0;
712 case 0: // end of string
713 result += processLine(passthroughPL, startOfLine, u8_strnlen(startOfLine, cursor - startOfLine), spaceUsedInLine, isContinuation);
714 isContinuation = false;
717 case '\n': // end of line
718 result += processLine(passthroughPL, startOfLine, u8_strnlen(startOfLine, cursor - startOfLine), spaceUsedInLine, isContinuation);
719 isContinuation = false;
721 startOfLine = cursor;
725 spaceUsedInLine += spaceWidth;
729 while(cursor + wordLen < end)
731 switch(cursor[wordLen])
743 wordChars = strlen(cursor);
744 if (wordChars > wordLen)
746 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
750 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
752 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
754 // we can simply append it
756 spaceUsedInLine += spaceUsedForWord;
760 // output current line
761 result += processLine(passthroughPL, startOfLine, u8_strnlen(startOfLine, cursor - startOfLine), spaceUsedInLine, isContinuation);
762 isContinuation = true;
763 startOfLine = cursor;
765 spaceUsedInLine = continuationWidth + spaceUsedForWord;
774 qboolean isContinuation = false;
775 float currentWordSpace = 0;
776 const char *currentWord = 0;
777 float minReserve = 0;
779 float spaceUsedInLine = 0;
780 const char *currentLine = 0;
781 const char *currentLineEnd = 0;
782 float currentLineFinalWhitespace = 0;
786 minReserve = charWidth(passthroughCW, 0);
787 minReserve += charWidth(passthroughCW, ' ');
789 if(maxWidth < continuationWidth + minReserve)
790 maxWidth = continuationWidth + minReserve;
792 charWidth(passthroughCW, 0);
794 for(p = string; p < string + length; ++p)
797 float w = charWidth(passthroughCW, c);
802 currentWordSpace = 0;
808 spaceUsedInLine = isContinuation ? continuationWidth : 0;
814 // 1. I can add the word AND a space - then just append it.
815 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
817 currentLineEnd = p; // note: space not included here
818 currentLineFinalWhitespace = w;
819 spaceUsedInLine += currentWordSpace + w;
821 // 2. I can just add the word - then append it, output current line and go to next one.
822 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
824 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
826 isContinuation = true;
828 // 3. Otherwise, output current line and go to next one, where I can add the word.
829 else if(continuationWidth + currentWordSpace + w <= maxWidth)
832 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
833 currentLine = currentWord;
834 spaceUsedInLine = continuationWidth + currentWordSpace + w;
836 currentLineFinalWhitespace = w;
837 isContinuation = true;
839 // 4. We can't even do that? Then output both current and next word as new lines.
844 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
845 isContinuation = true;
847 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
849 isContinuation = true;
855 // 1. I can add the word - then do it.
856 if(spaceUsedInLine + currentWordSpace <= maxWidth)
858 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
860 // 2. Otherwise, output current line, next one and make tabula rasa.
865 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
866 isContinuation = true;
868 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
872 isContinuation = false;
876 currentWordSpace += w;
878 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
880 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
883 // this word cannot join ANY line...
884 // so output the current line...
887 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
888 isContinuation = true;
891 // then this word's beginning...
894 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
895 float pieceWidth = maxWidth - continuationWidth;
896 const char *pos = currentWord;
897 currentWordSpace = 0;
899 // reset the char width function to a state where no kerning occurs (start of word)
900 charWidth(passthroughCW, ' ');
903 float w = charWidth(passthroughCW, *pos);
904 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
906 // print everything until it
907 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
910 currentWordSpace = 0;
912 currentWordSpace += w;
915 // now we have a currentWord that fits... set up its next line
916 // currentWordSpace has been set
917 // currentWord has been set
918 spaceUsedInLine = continuationWidth;
919 currentLine = currentWord;
921 isContinuation = true;
925 // we have a guarantee that it will fix (see if clause)
926 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
928 // and use the rest of this word as new start of a line
929 currentWordSpace = w;
931 spaceUsedInLine = continuationWidth;
934 isContinuation = true;
943 currentWordSpace = 0;
946 if(currentLine) // Same procedure as \n
948 // Can I append the current word?
949 if(spaceUsedInLine + currentWordSpace <= maxWidth)
950 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
955 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
956 isContinuation = true;
958 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
968 COM_ParseToken_Simple
970 Parse a token out of a string
973 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
977 const char *data = *datapointer;
994 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1004 // handle Windows line ending
1005 if (data[0] == '\r' && data[1] == '\n')
1008 if (data[0] == '/' && data[1] == '/')
1011 while (*data && *data != '\n' && *data != '\r')
1015 else if (data[0] == '/' && data[1] == '*')
1019 while (*data && (data[0] != '*' || data[1] != '/'))
1027 else if (*data == '\"')
1030 for (data++;*data && *data != '\"';data++)
1033 if (*data == '\\' && parsebackslash)
1042 if (len < (int)sizeof(com_token) - 1)
1043 com_token[len++] = c;
1048 *datapointer = data;
1051 else if (*data == '\r')
1053 // translate Mac line ending to UNIX
1054 com_token[len++] = '\n';data++;
1056 *datapointer = data;
1059 else if (*data == '\n')
1062 com_token[len++] = *data++;
1064 *datapointer = data;
1070 for (;!ISWHITESPACE(*data);data++)
1071 if (len < (int)sizeof(com_token) - 1)
1072 com_token[len++] = *data;
1074 *datapointer = data;
1081 COM_ParseToken_QuakeC
1083 Parse a token out of a string
1086 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1090 const char *data = *datapointer;
1097 *datapointer = NULL;
1107 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1112 *datapointer = NULL;
1117 // handle Windows line ending
1118 if (data[0] == '\r' && data[1] == '\n')
1121 if (data[0] == '/' && data[1] == '/')
1124 while (*data && *data != '\n' && *data != '\r')
1128 else if (data[0] == '/' && data[1] == '*')
1132 while (*data && (data[0] != '*' || data[1] != '/'))
1140 else if (*data == '\"' || *data == '\'')
1144 for (data++;*data && *data != quote;data++)
1156 if (len < (int)sizeof(com_token) - 1)
1157 com_token[len++] = c;
1162 *datapointer = data;
1165 else if (*data == '\r')
1167 // translate Mac line ending to UNIX
1168 com_token[len++] = '\n';data++;
1170 *datapointer = data;
1173 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1176 com_token[len++] = *data++;
1178 *datapointer = data;
1184 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1185 if (len < (int)sizeof(com_token) - 1)
1186 com_token[len++] = *data;
1188 *datapointer = data;
1195 COM_ParseToken_VM_Tokenize
1197 Parse a token out of a string
1200 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1204 const char *data = *datapointer;
1211 *datapointer = NULL;
1221 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1226 *datapointer = NULL;
1231 // handle Windows line ending
1232 if (data[0] == '\r' && data[1] == '\n')
1235 if (data[0] == '/' && data[1] == '/')
1238 while (*data && *data != '\n' && *data != '\r')
1242 else if (data[0] == '/' && data[1] == '*')
1246 while (*data && (data[0] != '*' || data[1] != '/'))
1254 else if (*data == '\"' || *data == '\'')
1258 for (data++;*data && *data != quote;data++)
1270 if (len < (int)sizeof(com_token) - 1)
1271 com_token[len++] = c;
1276 *datapointer = data;
1279 else if (*data == '\r')
1281 // translate Mac line ending to UNIX
1282 com_token[len++] = '\n';data++;
1284 *datapointer = data;
1287 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1290 com_token[len++] = *data++;
1292 *datapointer = data;
1298 for (;!ISWHITESPACE(*data) && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1299 if (len < (int)sizeof(com_token) - 1)
1300 com_token[len++] = *data;
1302 *datapointer = data;
1309 COM_ParseToken_Console
1311 Parse a token out of a string, behaving like the qwcl console
1314 int COM_ParseToken_Console(const char **datapointer)
1317 const char *data = *datapointer;
1324 *datapointer = NULL;
1330 for (;ISWHITESPACE(*data);data++)
1335 *datapointer = NULL;
1340 if (*data == '/' && data[1] == '/')
1343 while (*data && *data != '\n' && *data != '\r')
1347 else if (*data == '\"')
1350 for (data++;*data && *data != '\"';data++)
1352 // allow escaped " and \ case
1353 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1355 if (len < (int)sizeof(com_token) - 1)
1356 com_token[len++] = *data;
1361 *datapointer = data;
1366 for (;!ISWHITESPACE(*data);data++)
1367 if (len < (int)sizeof(com_token) - 1)
1368 com_token[len++] = *data;
1370 *datapointer = data;
1381 Returns the position (1 to argc-1) in the program's argument list
1382 where the given parameter apears, or 0 if not present
1385 int COM_CheckParm (const char *parm)
1389 for (i=1 ; i<com_argc ; i++)
1392 continue; // NEXTSTEP sometimes clears appkit vars.
1393 if (!strcmp (parm,com_argv[i]))
1400 //===========================================================================
1404 typedef struct gamemode_info_s
1406 const char* prog_name;
1407 const char* cmdline;
1408 const char* gamename;
1409 const char* gamedirname1;
1410 const char* gamedirname2;
1411 const char* gamescreenshotname;
1412 const char* gameuserdirname;
1415 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1416 {// prog_name cmdline gamename basegame modgame screenshotprefix userdir
1419 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1420 { "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" },
1422 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1423 { "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" },
1425 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1426 { "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" },
1428 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1429 { "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" },
1431 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1432 { "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" },
1434 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1435 { "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" },
1437 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1438 { "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" },
1440 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1441 { "teu", "-teu", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" },
1443 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1444 { "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" },
1446 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1447 { "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" },
1449 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1450 { "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" },
1452 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1453 { "som", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" },
1455 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1456 { "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" },
1458 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1459 { "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" },
1461 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1462 { "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" },
1464 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1465 { "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" },
1467 // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1468 { "dq", "-dq", "Deluxe Quake", "basedq", "extradq", "basedq", "dq" },
1470 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1471 { "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" },
1472 // GAME_DEFEATINDETAIL2
1473 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1474 { "did2", "-did2", "Defeat In Detail 2", "data", NULL, "did2_", "did2" },
1476 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1477 { "darsana", "-darsana", "Darsana", "ddata", NULL, "darsana", "darsana" },
1478 // GAME_CONTAGIONTHEORY
1479 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1480 { "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" },
1482 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1483 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
1485 // COMMANDLINEOPTION: Game: -blademaster runs the game Prophecy: Return of the BladeMaster
1486 { "blademaster", "-blademaster", "Prophecy: Return of the BladeMaster", "basebm", NULL, "blademaster", "blademaster" },
1488 // COMMANDLINEOPTION: Game: -prophecy runs the game Quake (default)
1489 { "prophecy", "-prophecy", "Prophecy", "data", NULL, "prophecy", "prophecy" },
1490 // GAME_BLOODOMNICIDE
1491 // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
1492 { "omnicide", "-omnicide", "Blood Omnicide", "kain", NULL, "omnicide", "omnicide" },
1495 void COM_InitGameType (void)
1497 char name [MAX_OSPATH];
1500 FS_StripExtension (com_argv[0], name, sizeof (name));
1501 COM_ToLowerString (name, name, sizeof (name));
1503 // Check the binary name; default to GAME_NORMAL (0)
1504 gamemode = GAME_NORMAL;
1505 for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1506 if (strstr (name, gamemode_info[i].prog_name))
1508 gamemode = (gamemode_t)i;
1512 // Look for a command-line option
1513 for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1514 if (COM_CheckParm (gamemode_info[i].cmdline))
1516 gamemode = (gamemode_t)i;
1520 gamename = gamemode_info[gamemode].gamename;
1521 gamedirname1 = gamemode_info[gamemode].gamedirname1;
1522 gamedirname2 = gamemode_info[gamemode].gamedirname2;
1523 gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1524 gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1533 void COM_Init_Commands (void)
1536 char com_cmdline[MAX_INPUTLINE];
1538 Cvar_RegisterVariable (®istered);
1539 Cvar_RegisterVariable (&cmdline);
1541 // reconstitute the command line for the cmdline externally visible cvar
1543 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1546 if (strstr(com_argv[j], " "))
1548 // arg contains whitespace, store quotes around it
1549 com_cmdline[n++] = '\"';
1550 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1551 com_cmdline[n++] = com_argv[j][i++];
1552 com_cmdline[n++] = '\"';
1556 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1557 com_cmdline[n++] = com_argv[j][i++];
1559 if (n < ((int)sizeof(com_cmdline) - 1))
1560 com_cmdline[n++] = ' ';
1565 Cvar_Set ("cmdline", com_cmdline);
1572 does a varargs printf into a temp buffer, so I don't need to have
1573 varargs versions of all text functions.
1574 FIXME: make this buffer size safe someday
1577 char *va(const char *format, ...)
1580 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1581 static char string[8][1024], *s;
1582 static int stringindex = 0;
1584 s = string[stringindex];
1585 stringindex = (stringindex + 1) & 7;
1586 va_start (argptr, format);
1587 dpvsnprintf (s, sizeof (string[0]), format,argptr);
1594 //======================================
1596 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1602 # define snprintf _snprintf
1603 # define vsnprintf _vsnprintf
1607 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1612 va_start (args, format);
1613 result = dpvsnprintf (buffer, buffersize, format, args);
1620 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1624 #if _MSC_VER >= 1400
1625 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1627 result = vsnprintf (buffer, buffersize, format, args);
1629 if (result < 0 || (size_t)result >= buffersize)
1631 buffer[buffersize - 1] = '\0';
1639 //======================================
1641 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1646 while (*in && size_out > 1)
1648 if (*in >= 'A' && *in <= 'Z')
1649 *out++ = *in++ + 'a' - 'A';
1657 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1662 while (*in && size_out > 1)
1664 if (*in >= 'a' && *in <= 'z')
1665 *out++ = *in++ + 'A' - 'a';
1673 int COM_StringBeginsWith(const char *s, const char *match)
1675 for (;*s && *match;s++, match++)
1681 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1683 int argc, commentprefixlength;
1687 tokenbufend = tokenbuf + tokenbufsize;
1689 commentprefixlength = 0;
1691 commentprefixlength = (int)strlen(commentprefix);
1692 while (*l && *l != '\n' && *l != '\r')
1694 if (!ISWHITESPACE(*l))
1696 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1698 while (*l && *l != '\n' && *l != '\r')
1702 if (argc >= maxargc)
1704 argv[argc++] = tokenbuf;
1708 while (*l && *l != '"')
1710 if (tokenbuf >= tokenbufend)
1719 while (!ISWHITESPACE(*l))
1721 if (tokenbuf >= tokenbufend)
1726 if (tokenbuf >= tokenbufend)
1747 COM_StringLengthNoColors
1749 calculates the visible width of a color coded string.
1751 *valid is filled with TRUE if the string is a valid colored string (that is, if
1752 it does not end with an unfinished color code). If it gets filled with FALSE, a
1753 fix would be adding a STRING_COLOR_TAG at the end of the string.
1755 valid can be set to NULL if the caller doesn't care.
1757 For size_s, specify the maximum number of characters from s to use, or 0 to use
1758 all characters until the zero terminator.
1762 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1764 const char *end = size_s ? (s + size_s) : NULL;
1768 switch((s == end) ? 0 : *s)
1774 case STRING_COLOR_TAG:
1776 switch((s == end) ? 0 : *s)
1778 case STRING_COLOR_RGB_TAG_CHAR:
1779 if (s+1 != end && isxdigit(s[1]) &&
1780 s+2 != end && isxdigit(s[2]) &&
1781 s+3 != end && isxdigit(s[3]) )
1786 ++len; // STRING_COLOR_TAG
1787 ++len; // STRING_COLOR_RGB_TAG_CHAR
1789 case 0: // ends with unfinished color code!
1794 case STRING_COLOR_TAG: // escaped ^
1797 case '0': case '1': case '2': case '3': case '4':
1798 case '5': case '6': case '7': case '8': case '9': // color code
1800 default: // not a color code
1801 ++len; // STRING_COLOR_TAG
1802 ++len; // the character
1817 COM_StringDecolorize
1819 removes color codes from a string.
1821 If escape_carets is true, the resulting string will be safe for printing. If
1822 escape_carets is false, the function will just strip color codes (for logging
1825 If the output buffer size did not suffice for converting, the function returns
1826 FALSE. Generally, if escape_carets is false, the output buffer needs
1827 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1828 bytes. In any case, the function makes sure that the resulting string is
1831 For size_in, specify the maximum number of characters from in to use, or 0 to use
1832 all characters until the zero terminator.
1836 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1838 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1839 const char *end = size_in ? (in + size_in) : NULL;
1844 switch((in == end) ? 0 : *in)
1849 case STRING_COLOR_TAG:
1851 switch((in == end) ? 0 : *in)
1853 case STRING_COLOR_RGB_TAG_CHAR:
1854 if (in+1 != end && isxdigit(in[1]) &&
1855 in+2 != end && isxdigit(in[2]) &&
1856 in+3 != end && isxdigit(in[3]) )
1861 APPEND(STRING_COLOR_TAG);
1863 APPEND(STRING_COLOR_TAG);
1864 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1866 case 0: // ends with unfinished color code!
1867 APPEND(STRING_COLOR_TAG);
1868 // finish the code by appending another caret when escaping
1870 APPEND(STRING_COLOR_TAG);
1873 case STRING_COLOR_TAG: // escaped ^
1874 APPEND(STRING_COLOR_TAG);
1875 // append a ^ twice when escaping
1877 APPEND(STRING_COLOR_TAG);
1879 case '0': case '1': case '2': case '3': case '4':
1880 case '5': case '6': case '7': case '8': case '9': // color code
1882 default: // not a color code
1883 APPEND(STRING_COLOR_TAG);
1898 // written by Elric, thanks Elric!
1899 char *SearchInfostring(const char *infostring, const char *key)
1901 static char value [MAX_INPUTLINE];
1902 char crt_key [MAX_INPUTLINE];
1903 size_t value_ind, key_ind;
1906 if (*infostring++ != '\\')
1921 if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1923 crt_key[key_ind] = '\0';
1927 crt_key[key_ind++] = c;
1930 // If it's the key we are looking for, save it in "value"
1931 if (!strcmp(crt_key, key))
1937 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1939 value[value_ind] = '\0';
1943 value[value_ind++] = c;
1947 // Else, skip the value
1960 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1968 keylength = strlen(key);
1969 if (valuelength < 1 || !value)
1971 Con_Printf("InfoString_GetValue: no room in value\n");
1975 if (strchr(key, '\\'))
1977 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1980 if (strchr(key, '\"'))
1982 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1987 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1990 while (buffer[pos] == '\\')
1992 if (!memcmp(buffer + pos+1, key, keylength))
1994 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1996 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1997 value[j] = buffer[pos+j];
2001 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2002 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2004 // if we reach this point the key was not found
2007 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2015 keylength = strlen(key);
2016 if (strchr(key, '\\') || strchr(value, '\\'))
2018 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2021 if (strchr(key, '\"') || strchr(value, '\"'))
2023 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2028 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2031 while (buffer[pos] == '\\')
2033 if (!memcmp(buffer + pos+1, key, keylength))
2035 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2036 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2038 // if we found the key, find the end of it because we will be replacing it
2040 if (buffer[pos] == '\\')
2042 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2043 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2045 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2047 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2050 if (value && value[0])
2052 // set the key/value and append the remaining text
2053 char tempbuffer[4096];
2054 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2055 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2059 // just remove the key from the text
2060 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2064 void InfoString_Print(char *buffer)
2071 if (*buffer != '\\')
2073 Con_Printf("InfoString_Print: corrupt string\n");
2076 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2077 if (i < (int)sizeof(key)-1)
2080 if (*buffer != '\\')
2082 Con_Printf("InfoString_Print: corrupt string\n");
2085 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2086 if (i < (int)sizeof(value)-1)
2087 value[i++] = *buffer;
2089 // empty value is an error case
2090 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2094 //========================================================
2095 // strlcat and strlcpy, from OpenBSD
2098 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2100 * Permission to use, copy, modify, and distribute this software for any
2101 * purpose with or without fee is hereby granted, provided that the above
2102 * copyright notice and this permission notice appear in all copies.
2104 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2105 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2106 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2107 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2108 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2109 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2110 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2113 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
2114 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
2117 #ifndef HAVE_STRLCAT
2119 strlcat(char *dst, const char *src, size_t siz)
2121 register char *d = dst;
2122 register const char *s = src;
2123 register size_t n = siz;
2126 /* Find the end of dst and adjust bytes left but don't go past end */
2127 while (n-- != 0 && *d != '\0')
2133 return(dlen + strlen(s));
2134 while (*s != '\0') {
2143 return(dlen + (s - src)); /* count does not include NUL */
2145 #endif // #ifndef HAVE_STRLCAT
2148 #ifndef HAVE_STRLCPY
2150 strlcpy(char *dst, const char *src, size_t siz)
2152 register char *d = dst;
2153 register const char *s = src;
2154 register size_t n = siz;
2156 /* Copy as many bytes as will fit */
2157 if (n != 0 && --n != 0) {
2159 if ((*d++ = *s++) == 0)
2164 /* Not enough room in dst, add NUL and traverse rest of src */
2167 *d = '\0'; /* NUL-terminate dst */
2172 return(s - src - 1); /* count does not include NUL */
2175 #endif // #ifndef HAVE_STRLCPY
2177 void FindFraction(double val, int *num, int *denom, int denomMax)
2182 bestdiff = fabs(val);
2186 for(i = 1; i <= denomMax; ++i)
2188 int inum = (int) floor(0.5 + val * i);
2189 double diff = fabs(val - inum / (double)i);