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])
744 wordChars = strlen(cursor);
745 if (wordChars > wordLen)
748 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
752 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
754 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
756 // we can simply append it
758 spaceUsedInLine += spaceUsedForWord;
762 // output current line
763 result += processLine(passthroughPL, startOfLine, u8_strnlen(startOfLine, cursor - startOfLine), spaceUsedInLine, isContinuation);
764 isContinuation = true;
765 startOfLine = cursor;
767 spaceUsedInLine = continuationWidth + spaceUsedForWord;
776 qboolean isContinuation = false;
777 float currentWordSpace = 0;
778 const char *currentWord = 0;
779 float minReserve = 0;
781 float spaceUsedInLine = 0;
782 const char *currentLine = 0;
783 const char *currentLineEnd = 0;
784 float currentLineFinalWhitespace = 0;
788 minReserve = charWidth(passthroughCW, 0);
789 minReserve += charWidth(passthroughCW, ' ');
791 if(maxWidth < continuationWidth + minReserve)
792 maxWidth = continuationWidth + minReserve;
794 charWidth(passthroughCW, 0);
796 for(p = string; p < string + length; ++p)
799 float w = charWidth(passthroughCW, c);
804 currentWordSpace = 0;
810 spaceUsedInLine = isContinuation ? continuationWidth : 0;
816 // 1. I can add the word AND a space - then just append it.
817 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
819 currentLineEnd = p; // note: space not included here
820 currentLineFinalWhitespace = w;
821 spaceUsedInLine += currentWordSpace + w;
823 // 2. I can just add the word - then append it, output current line and go to next one.
824 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
826 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
828 isContinuation = true;
830 // 3. Otherwise, output current line and go to next one, where I can add the word.
831 else if(continuationWidth + currentWordSpace + w <= maxWidth)
834 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
835 currentLine = currentWord;
836 spaceUsedInLine = continuationWidth + currentWordSpace + w;
838 currentLineFinalWhitespace = w;
839 isContinuation = true;
841 // 4. We can't even do that? Then output both current and next word as new lines.
846 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
847 isContinuation = true;
849 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
851 isContinuation = true;
857 // 1. I can add the word - then do it.
858 if(spaceUsedInLine + currentWordSpace <= maxWidth)
860 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
862 // 2. Otherwise, output current line, next one and make tabula rasa.
867 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
868 isContinuation = true;
870 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
874 isContinuation = false;
878 currentWordSpace += w;
880 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
882 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
885 // this word cannot join ANY line...
886 // so output the current line...
889 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
890 isContinuation = true;
893 // then this word's beginning...
896 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
897 float pieceWidth = maxWidth - continuationWidth;
898 const char *pos = currentWord;
899 currentWordSpace = 0;
901 // reset the char width function to a state where no kerning occurs (start of word)
902 charWidth(passthroughCW, ' ');
905 float w = charWidth(passthroughCW, *pos);
906 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
908 // print everything until it
909 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
912 currentWordSpace = 0;
914 currentWordSpace += w;
917 // now we have a currentWord that fits... set up its next line
918 // currentWordSpace has been set
919 // currentWord has been set
920 spaceUsedInLine = continuationWidth;
921 currentLine = currentWord;
923 isContinuation = true;
927 // we have a guarantee that it will fix (see if clause)
928 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
930 // and use the rest of this word as new start of a line
931 currentWordSpace = w;
933 spaceUsedInLine = continuationWidth;
936 isContinuation = true;
945 currentWordSpace = 0;
948 if(currentLine) // Same procedure as \n
950 // Can I append the current word?
951 if(spaceUsedInLine + currentWordSpace <= maxWidth)
952 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
957 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
958 isContinuation = true;
960 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
970 COM_ParseToken_Simple
972 Parse a token out of a string
975 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
979 const char *data = *datapointer;
996 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1001 *datapointer = NULL;
1006 // handle Windows line ending
1007 if (data[0] == '\r' && data[1] == '\n')
1010 if (data[0] == '/' && data[1] == '/')
1013 while (*data && *data != '\n' && *data != '\r')
1017 else if (data[0] == '/' && data[1] == '*')
1021 while (*data && (data[0] != '*' || data[1] != '/'))
1029 else if (*data == '\"')
1032 for (data++;*data && *data != '\"';data++)
1035 if (*data == '\\' && parsebackslash)
1044 if (len < (int)sizeof(com_token) - 1)
1045 com_token[len++] = c;
1050 *datapointer = data;
1053 else if (*data == '\r')
1055 // translate Mac line ending to UNIX
1056 com_token[len++] = '\n';data++;
1058 *datapointer = data;
1061 else if (*data == '\n')
1064 com_token[len++] = *data++;
1066 *datapointer = data;
1072 for (;!ISWHITESPACE(*data);data++)
1073 if (len < (int)sizeof(com_token) - 1)
1074 com_token[len++] = *data;
1076 *datapointer = data;
1083 COM_ParseToken_QuakeC
1085 Parse a token out of a string
1088 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1092 const char *data = *datapointer;
1099 *datapointer = NULL;
1109 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1114 *datapointer = NULL;
1119 // handle Windows line ending
1120 if (data[0] == '\r' && data[1] == '\n')
1123 if (data[0] == '/' && data[1] == '/')
1126 while (*data && *data != '\n' && *data != '\r')
1130 else if (data[0] == '/' && data[1] == '*')
1134 while (*data && (data[0] != '*' || data[1] != '/'))
1142 else if (*data == '\"' || *data == '\'')
1146 for (data++;*data && *data != quote;data++)
1158 if (len < (int)sizeof(com_token) - 1)
1159 com_token[len++] = c;
1164 *datapointer = data;
1167 else if (*data == '\r')
1169 // translate Mac line ending to UNIX
1170 com_token[len++] = '\n';data++;
1172 *datapointer = data;
1175 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1178 com_token[len++] = *data++;
1180 *datapointer = data;
1186 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1187 if (len < (int)sizeof(com_token) - 1)
1188 com_token[len++] = *data;
1190 *datapointer = data;
1197 COM_ParseToken_VM_Tokenize
1199 Parse a token out of a string
1202 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1206 const char *data = *datapointer;
1213 *datapointer = NULL;
1223 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1228 *datapointer = NULL;
1233 // handle Windows line ending
1234 if (data[0] == '\r' && data[1] == '\n')
1237 if (data[0] == '/' && data[1] == '/')
1240 while (*data && *data != '\n' && *data != '\r')
1244 else if (data[0] == '/' && data[1] == '*')
1248 while (*data && (data[0] != '*' || data[1] != '/'))
1256 else if (*data == '\"' || *data == '\'')
1260 for (data++;*data && *data != quote;data++)
1272 if (len < (int)sizeof(com_token) - 1)
1273 com_token[len++] = c;
1278 *datapointer = data;
1281 else if (*data == '\r')
1283 // translate Mac line ending to UNIX
1284 com_token[len++] = '\n';data++;
1286 *datapointer = data;
1289 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1292 com_token[len++] = *data++;
1294 *datapointer = data;
1300 for (;!ISWHITESPACE(*data) && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1301 if (len < (int)sizeof(com_token) - 1)
1302 com_token[len++] = *data;
1304 *datapointer = data;
1311 COM_ParseToken_Console
1313 Parse a token out of a string, behaving like the qwcl console
1316 int COM_ParseToken_Console(const char **datapointer)
1319 const char *data = *datapointer;
1326 *datapointer = NULL;
1332 for (;ISWHITESPACE(*data);data++)
1337 *datapointer = NULL;
1342 if (*data == '/' && data[1] == '/')
1345 while (*data && *data != '\n' && *data != '\r')
1349 else if (*data == '\"')
1352 for (data++;*data && *data != '\"';data++)
1354 // allow escaped " and \ case
1355 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1357 if (len < (int)sizeof(com_token) - 1)
1358 com_token[len++] = *data;
1363 *datapointer = data;
1368 for (;!ISWHITESPACE(*data);data++)
1369 if (len < (int)sizeof(com_token) - 1)
1370 com_token[len++] = *data;
1372 *datapointer = data;
1383 Returns the position (1 to argc-1) in the program's argument list
1384 where the given parameter apears, or 0 if not present
1387 int COM_CheckParm (const char *parm)
1391 for (i=1 ; i<com_argc ; i++)
1394 continue; // NEXTSTEP sometimes clears appkit vars.
1395 if (!strcmp (parm,com_argv[i]))
1402 //===========================================================================
1406 typedef struct gamemode_info_s
1408 const char* prog_name;
1409 const char* cmdline;
1410 const char* gamename;
1411 const char* gamedirname1;
1412 const char* gamedirname2;
1413 const char* gamescreenshotname;
1414 const char* gameuserdirname;
1417 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1418 {// prog_name cmdline gamename basegame modgame screenshotprefix userdir
1421 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1422 { "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" },
1424 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1425 { "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" },
1427 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1428 { "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" },
1430 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1431 { "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" },
1433 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1434 { "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" },
1436 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1437 { "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" },
1439 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1440 { "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" },
1442 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1443 { "teu", "-teu", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" },
1445 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1446 { "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" },
1448 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1449 { "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" },
1451 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1452 { "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" },
1454 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1455 { "som", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" },
1457 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1458 { "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" },
1460 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1461 { "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" },
1463 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1464 { "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" },
1466 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1467 { "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" },
1469 // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1470 { "dq", "-dq", "Deluxe Quake", "basedq", "extradq", "basedq", "dq" },
1472 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1473 { "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" },
1474 // GAME_DEFEATINDETAIL2
1475 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1476 { "did2", "-did2", "Defeat In Detail 2", "data", NULL, "did2_", "did2" },
1478 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1479 { "darsana", "-darsana", "Darsana", "ddata", NULL, "darsana", "darsana" },
1480 // GAME_CONTAGIONTHEORY
1481 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1482 { "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" },
1484 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1485 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
1487 // COMMANDLINEOPTION: Game: -blademaster runs the game Prophecy: Return of the BladeMaster
1488 { "blademaster", "-blademaster", "Prophecy: Return of the BladeMaster", "basebm", NULL, "blademaster", "blademaster" },
1490 // COMMANDLINEOPTION: Game: -prophecy runs the game Quake (default)
1491 { "prophecy", "-prophecy", "Prophecy", "data", NULL, "prophecy", "prophecy" },
1492 // GAME_BLOODOMNICIDE
1493 // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
1494 { "omnicide", "-omnicide", "Blood Omnicide", "kain", NULL, "omnicide", "omnicide" },
1497 void COM_InitGameType (void)
1499 char name [MAX_OSPATH];
1502 FS_StripExtension (com_argv[0], name, sizeof (name));
1503 COM_ToLowerString (name, name, sizeof (name));
1505 // Check the binary name; default to GAME_NORMAL (0)
1506 gamemode = GAME_NORMAL;
1507 for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1508 if (strstr (name, gamemode_info[i].prog_name))
1510 gamemode = (gamemode_t)i;
1514 // Look for a command-line option
1515 for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1516 if (COM_CheckParm (gamemode_info[i].cmdline))
1518 gamemode = (gamemode_t)i;
1522 gamename = gamemode_info[gamemode].gamename;
1523 gamedirname1 = gamemode_info[gamemode].gamedirname1;
1524 gamedirname2 = gamemode_info[gamemode].gamedirname2;
1525 gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1526 gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1535 void COM_Init_Commands (void)
1538 char com_cmdline[MAX_INPUTLINE];
1540 Cvar_RegisterVariable (®istered);
1541 Cvar_RegisterVariable (&cmdline);
1543 // reconstitute the command line for the cmdline externally visible cvar
1545 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1548 if (strstr(com_argv[j], " "))
1550 // arg contains whitespace, store quotes around it
1551 com_cmdline[n++] = '\"';
1552 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1553 com_cmdline[n++] = com_argv[j][i++];
1554 com_cmdline[n++] = '\"';
1558 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1559 com_cmdline[n++] = com_argv[j][i++];
1561 if (n < ((int)sizeof(com_cmdline) - 1))
1562 com_cmdline[n++] = ' ';
1567 Cvar_Set ("cmdline", com_cmdline);
1574 does a varargs printf into a temp buffer, so I don't need to have
1575 varargs versions of all text functions.
1576 FIXME: make this buffer size safe someday
1579 char *va(const char *format, ...)
1582 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1583 static char string[8][1024], *s;
1584 static int stringindex = 0;
1586 s = string[stringindex];
1587 stringindex = (stringindex + 1) & 7;
1588 va_start (argptr, format);
1589 dpvsnprintf (s, sizeof (string[0]), format,argptr);
1596 //======================================
1598 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1604 # define snprintf _snprintf
1605 # define vsnprintf _vsnprintf
1609 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1614 va_start (args, format);
1615 result = dpvsnprintf (buffer, buffersize, format, args);
1622 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1626 #if _MSC_VER >= 1400
1627 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1629 result = vsnprintf (buffer, buffersize, format, args);
1631 if (result < 0 || (size_t)result >= buffersize)
1633 buffer[buffersize - 1] = '\0';
1641 //======================================
1643 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1648 while (*in && size_out > 1)
1650 if (*in >= 'A' && *in <= 'Z')
1651 *out++ = *in++ + 'a' - 'A';
1659 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1664 while (*in && size_out > 1)
1666 if (*in >= 'a' && *in <= 'z')
1667 *out++ = *in++ + 'A' - 'a';
1675 int COM_StringBeginsWith(const char *s, const char *match)
1677 for (;*s && *match;s++, match++)
1683 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1685 int argc, commentprefixlength;
1689 tokenbufend = tokenbuf + tokenbufsize;
1691 commentprefixlength = 0;
1693 commentprefixlength = (int)strlen(commentprefix);
1694 while (*l && *l != '\n' && *l != '\r')
1696 if (!ISWHITESPACE(*l))
1698 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1700 while (*l && *l != '\n' && *l != '\r')
1704 if (argc >= maxargc)
1706 argv[argc++] = tokenbuf;
1710 while (*l && *l != '"')
1712 if (tokenbuf >= tokenbufend)
1721 while (!ISWHITESPACE(*l))
1723 if (tokenbuf >= tokenbufend)
1728 if (tokenbuf >= tokenbufend)
1749 COM_StringLengthNoColors
1751 calculates the visible width of a color coded string.
1753 *valid is filled with TRUE if the string is a valid colored string (that is, if
1754 it does not end with an unfinished color code). If it gets filled with FALSE, a
1755 fix would be adding a STRING_COLOR_TAG at the end of the string.
1757 valid can be set to NULL if the caller doesn't care.
1759 For size_s, specify the maximum number of characters from s to use, or 0 to use
1760 all characters until the zero terminator.
1764 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1766 const char *end = size_s ? (s + size_s) : NULL;
1770 switch((s == end) ? 0 : *s)
1776 case STRING_COLOR_TAG:
1778 switch((s == end) ? 0 : *s)
1780 case STRING_COLOR_RGB_TAG_CHAR:
1781 if (s+1 != end && isxdigit(s[1]) &&
1782 s+2 != end && isxdigit(s[2]) &&
1783 s+3 != end && isxdigit(s[3]) )
1788 ++len; // STRING_COLOR_TAG
1789 ++len; // STRING_COLOR_RGB_TAG_CHAR
1791 case 0: // ends with unfinished color code!
1796 case STRING_COLOR_TAG: // escaped ^
1799 case '0': case '1': case '2': case '3': case '4':
1800 case '5': case '6': case '7': case '8': case '9': // color code
1802 default: // not a color code
1803 ++len; // STRING_COLOR_TAG
1804 ++len; // the character
1819 COM_StringDecolorize
1821 removes color codes from a string.
1823 If escape_carets is true, the resulting string will be safe for printing. If
1824 escape_carets is false, the function will just strip color codes (for logging
1827 If the output buffer size did not suffice for converting, the function returns
1828 FALSE. Generally, if escape_carets is false, the output buffer needs
1829 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1830 bytes. In any case, the function makes sure that the resulting string is
1833 For size_in, specify the maximum number of characters from in to use, or 0 to use
1834 all characters until the zero terminator.
1838 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1840 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1841 const char *end = size_in ? (in + size_in) : NULL;
1846 switch((in == end) ? 0 : *in)
1851 case STRING_COLOR_TAG:
1853 switch((in == end) ? 0 : *in)
1855 case STRING_COLOR_RGB_TAG_CHAR:
1856 if (in+1 != end && isxdigit(in[1]) &&
1857 in+2 != end && isxdigit(in[2]) &&
1858 in+3 != end && isxdigit(in[3]) )
1863 APPEND(STRING_COLOR_TAG);
1865 APPEND(STRING_COLOR_TAG);
1866 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1868 case 0: // ends with unfinished color code!
1869 APPEND(STRING_COLOR_TAG);
1870 // finish the code by appending another caret when escaping
1872 APPEND(STRING_COLOR_TAG);
1875 case STRING_COLOR_TAG: // escaped ^
1876 APPEND(STRING_COLOR_TAG);
1877 // append a ^ twice when escaping
1879 APPEND(STRING_COLOR_TAG);
1881 case '0': case '1': case '2': case '3': case '4':
1882 case '5': case '6': case '7': case '8': case '9': // color code
1884 default: // not a color code
1885 APPEND(STRING_COLOR_TAG);
1900 // written by Elric, thanks Elric!
1901 char *SearchInfostring(const char *infostring, const char *key)
1903 static char value [MAX_INPUTLINE];
1904 char crt_key [MAX_INPUTLINE];
1905 size_t value_ind, key_ind;
1908 if (*infostring++ != '\\')
1923 if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1925 crt_key[key_ind] = '\0';
1929 crt_key[key_ind++] = c;
1932 // If it's the key we are looking for, save it in "value"
1933 if (!strcmp(crt_key, key))
1939 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1941 value[value_ind] = '\0';
1945 value[value_ind++] = c;
1949 // Else, skip the value
1962 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1970 keylength = strlen(key);
1971 if (valuelength < 1 || !value)
1973 Con_Printf("InfoString_GetValue: no room in value\n");
1977 if (strchr(key, '\\'))
1979 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1982 if (strchr(key, '\"'))
1984 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1989 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1992 while (buffer[pos] == '\\')
1994 if (!memcmp(buffer + pos+1, key, keylength))
1996 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1998 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1999 value[j] = buffer[pos+j];
2003 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2004 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2006 // if we reach this point the key was not found
2009 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2017 keylength = strlen(key);
2018 if (strchr(key, '\\') || strchr(value, '\\'))
2020 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2023 if (strchr(key, '\"') || strchr(value, '\"'))
2025 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2030 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2033 while (buffer[pos] == '\\')
2035 if (!memcmp(buffer + pos+1, key, keylength))
2037 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2038 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2040 // if we found the key, find the end of it because we will be replacing it
2042 if (buffer[pos] == '\\')
2044 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2045 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2047 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2049 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2052 if (value && value[0])
2054 // set the key/value and append the remaining text
2055 char tempbuffer[4096];
2056 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2057 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2061 // just remove the key from the text
2062 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2066 void InfoString_Print(char *buffer)
2073 if (*buffer != '\\')
2075 Con_Printf("InfoString_Print: corrupt string\n");
2078 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2079 if (i < (int)sizeof(key)-1)
2082 if (*buffer != '\\')
2084 Con_Printf("InfoString_Print: corrupt string\n");
2087 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2088 if (i < (int)sizeof(value)-1)
2089 value[i++] = *buffer;
2091 // empty value is an error case
2092 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2096 //========================================================
2097 // strlcat and strlcpy, from OpenBSD
2100 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2102 * Permission to use, copy, modify, and distribute this software for any
2103 * purpose with or without fee is hereby granted, provided that the above
2104 * copyright notice and this permission notice appear in all copies.
2106 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2107 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2108 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2109 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2110 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2111 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2112 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2115 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
2116 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
2119 #ifndef HAVE_STRLCAT
2121 strlcat(char *dst, const char *src, size_t siz)
2123 register char *d = dst;
2124 register const char *s = src;
2125 register size_t n = siz;
2128 /* Find the end of dst and adjust bytes left but don't go past end */
2129 while (n-- != 0 && *d != '\0')
2135 return(dlen + strlen(s));
2136 while (*s != '\0') {
2145 return(dlen + (s - src)); /* count does not include NUL */
2147 #endif // #ifndef HAVE_STRLCAT
2150 #ifndef HAVE_STRLCPY
2152 strlcpy(char *dst, const char *src, size_t siz)
2154 register char *d = dst;
2155 register const char *s = src;
2156 register size_t n = siz;
2158 /* Copy as many bytes as will fit */
2159 if (n != 0 && --n != 0) {
2161 if ((*d++ = *s++) == 0)
2166 /* Not enough room in dst, add NUL and traverse rest of src */
2169 *d = '\0'; /* NUL-terminate dst */
2174 return(s - src - 1); /* count does not include NUL */
2177 #endif // #ifndef HAVE_STRLCPY
2179 void FindFraction(double val, int *num, int *denom, int denomMax)
2184 bestdiff = fabs(val);
2188 for(i = 1; i <= denomMax; ++i)
2190 int inum = (int) floor(0.5 + val * i);
2191 double diff = fabs(val - inum / (double)i);