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
30 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
31 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
33 char com_token[MAX_INPUTLINE];
35 const char **com_argv;
39 const char *gamedirname1;
40 const char *gamedirname2;
41 const char *gamescreenshotname;
42 const char *gameuserdirname;
43 char com_modname[MAX_OSPATH] = "";
47 ============================================================================
51 ============================================================================
54 short ShortSwap (short l)
66 unsigned char b1,b2,b3,b4;
73 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
76 float FloatSwap (float f)
86 dat2.b[0] = dat1.b[3];
87 dat2.b[1] = dat1.b[2];
88 dat2.b[2] = dat1.b[1];
89 dat2.b[3] = dat1.b[0];
94 // Extract integers from buffers
96 unsigned int BuffBigLong (const unsigned char *buffer)
98 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
101 unsigned short BuffBigShort (const unsigned char *buffer)
103 return (buffer[0] << 8) | buffer[1];
106 unsigned int BuffLittleLong (const unsigned char *buffer)
108 return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
111 unsigned short BuffLittleShort (const unsigned char *buffer)
113 return (buffer[1] << 8) | buffer[0];
118 ============================================================================
122 ============================================================================
125 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
126 // and the initial and final xor values shown below... in other words, the
127 // CCITT standard CRC used by XMODEM
129 #define CRC_INIT_VALUE 0xffff
130 #define CRC_XOR_VALUE 0x0000
132 static unsigned short crctable[256] =
134 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
135 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
136 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
137 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
138 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
139 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
140 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
141 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
142 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
143 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
144 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
145 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
146 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
147 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
148 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
149 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
150 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
151 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
152 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
153 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
154 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
155 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
156 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
157 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
158 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
159 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
160 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
161 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
162 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
163 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
164 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
165 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
168 unsigned short CRC_Block(const unsigned char *data, size_t size)
170 unsigned short crc = CRC_INIT_VALUE;
172 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
173 return crc ^ CRC_XOR_VALUE;
176 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
178 unsigned short crc = CRC_INIT_VALUE;
180 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
181 return crc ^ CRC_XOR_VALUE;
185 static unsigned char chktbl[1024 + 4] =
187 0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
188 0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
189 0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
190 0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
191 0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
192 0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
193 0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
194 0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
195 0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
196 0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
197 0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
198 0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
199 0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
200 0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
201 0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
202 0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
203 0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
204 0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
205 0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
206 0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
207 0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
208 0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
209 0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
210 0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
211 0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
212 0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
213 0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
214 0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
215 0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
216 0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
217 0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
218 0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
220 // map checksum goes here
225 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
228 unsigned char chkb[60 + 4];
230 p = chktbl + (sequence % (sizeof(chktbl) - 8));
234 memcpy(chkb, base, length);
236 chkb[length] = (sequence & 0xff) ^ p[0];
237 chkb[length+1] = p[1];
238 chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
239 chkb[length+3] = p[3];
241 return CRC_Block(chkb, length + 4) & 0xff;
245 ==============================================================================
249 Handles byte ordering and avoids alignment errors
250 ==============================================================================
257 void MSG_WriteChar (sizebuf_t *sb, int c)
261 buf = SZ_GetSpace (sb, 1);
265 void MSG_WriteByte (sizebuf_t *sb, int c)
269 buf = SZ_GetSpace (sb, 1);
273 void MSG_WriteShort (sizebuf_t *sb, int c)
277 buf = SZ_GetSpace (sb, 2);
282 void MSG_WriteLong (sizebuf_t *sb, int c)
286 buf = SZ_GetSpace (sb, 4);
288 buf[1] = (c>>8)&0xff;
289 buf[2] = (c>>16)&0xff;
293 void MSG_WriteFloat (sizebuf_t *sb, float f)
303 dat.l = LittleLong (dat.l);
305 SZ_Write (sb, (unsigned char *)&dat.l, 4);
308 void MSG_WriteString (sizebuf_t *sb, const char *s)
311 MSG_WriteChar (sb, 0);
313 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
316 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
319 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
322 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
325 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
327 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
330 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
333 MSG_WriteShort (sb, (int)(f + 0.5));
335 MSG_WriteShort (sb, (int)(f - 0.5));
338 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
340 MSG_WriteFloat (sb, f);
343 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
345 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
346 MSG_WriteCoord13i (sb, f);
347 else if (protocol == PROTOCOL_DARKPLACES1)
348 MSG_WriteCoord32f (sb, f);
349 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
350 MSG_WriteCoord16i (sb, f);
352 MSG_WriteCoord32f (sb, f);
355 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
357 MSG_WriteCoord (sb, v[0], protocol);
358 MSG_WriteCoord (sb, v[1], protocol);
359 MSG_WriteCoord (sb, v[2], protocol);
362 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
363 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
366 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
368 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
371 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
374 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
376 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
379 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
381 MSG_WriteFloat (sb, f);
384 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
386 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)
387 MSG_WriteAngle8i (sb, f);
389 MSG_WriteAngle16i (sb, f);
396 qboolean msg_badread;
398 void MSG_BeginReading (void)
404 int MSG_ReadLittleShort (void)
406 if (msg_readcount+2 > net_message.cursize)
412 return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
415 int MSG_ReadBigShort (void)
417 if (msg_readcount+2 > net_message.cursize)
423 return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
426 int MSG_ReadLittleLong (void)
428 if (msg_readcount+4 > net_message.cursize)
434 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);
437 int MSG_ReadBigLong (void)
439 if (msg_readcount+4 > net_message.cursize)
445 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];
448 float MSG_ReadLittleFloat (void)
455 if (msg_readcount+4 > net_message.cursize)
461 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);
465 float MSG_ReadBigFloat (void)
472 if (msg_readcount+4 > net_message.cursize)
478 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];
482 char *MSG_ReadString (void)
484 static char string[MAX_INPUTLINE];
486 for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
492 int MSG_ReadBytes (int numbytes, unsigned char *out)
495 for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
500 float MSG_ReadCoord13i (void)
502 return MSG_ReadLittleShort() * (1.0/8.0);
505 float MSG_ReadCoord16i (void)
507 return (signed short) MSG_ReadLittleShort();
510 float MSG_ReadCoord32f (void)
512 return MSG_ReadLittleFloat();
515 float MSG_ReadCoord (protocolversion_t protocol)
517 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
518 return MSG_ReadCoord13i();
519 else if (protocol == PROTOCOL_DARKPLACES1)
520 return MSG_ReadCoord32f();
521 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
522 return MSG_ReadCoord16i();
524 return MSG_ReadCoord32f();
527 void MSG_ReadVector (float *v, protocolversion_t protocol)
529 v[0] = MSG_ReadCoord(protocol);
530 v[1] = MSG_ReadCoord(protocol);
531 v[2] = MSG_ReadCoord(protocol);
534 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
535 float MSG_ReadAngle8i (void)
537 return (signed char) MSG_ReadByte () * (360.0/256.0);
540 float MSG_ReadAngle16i (void)
542 return (signed short)MSG_ReadShort () * (360.0/65536.0);
545 float MSG_ReadAngle32f (void)
547 return MSG_ReadFloat ();
550 float MSG_ReadAngle (protocolversion_t protocol)
552 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)
553 return MSG_ReadAngle8i ();
555 return MSG_ReadAngle16i ();
559 //===========================================================================
561 void SZ_Clear (sizebuf_t *buf)
566 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
570 if (buf->cursize + length > buf->maxsize)
572 if (!buf->allowoverflow)
573 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
575 if (length > buf->maxsize)
576 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
578 buf->overflowed = true;
579 Con_Print("SZ_GetSpace: overflow\n");
583 data = buf->data + buf->cursize;
584 buf->cursize += length;
589 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
591 memcpy (SZ_GetSpace(buf,length),data,length);
594 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
595 // attention, it has been eradicated from here, its only (former) use in
596 // all of darkplaces.
598 static char *hexchar = "0123456789ABCDEF";
599 void Com_HexDumpToConsole(const unsigned char *data, int size)
603 char *cur, *flushpointer;
604 const unsigned char *d;
606 flushpointer = text + 512;
607 for (i = 0;i < size;)
614 *cur++ = hexchar[(i >> 12) & 15];
615 *cur++ = hexchar[(i >> 8) & 15];
616 *cur++ = hexchar[(i >> 4) & 15];
617 *cur++ = hexchar[(i >> 0) & 15];
620 for (j = 0;j < 16;j++)
624 *cur++ = hexchar[(d[j] >> 4) & 15];
625 *cur++ = hexchar[(d[j] >> 0) & 15];
636 for (j = 0;j < 16;j++)
640 // color change prefix character has to be treated specially
641 if (d[j] == STRING_COLOR_TAG)
643 *cur++ = STRING_COLOR_TAG;
644 *cur++ = STRING_COLOR_TAG;
646 else if (d[j] >= ' ')
656 if (cur >= flushpointer || i >= size)
665 void SZ_HexDumpToConsole(const sizebuf_t *buf)
667 Com_HexDumpToConsole(buf->data, buf->cursize);
671 //============================================================================
677 Word wraps a string. The wordWidth function is guaranteed to be called exactly
678 once for each word in the string, so it may be stateful, no idea what that
679 would be good for any more. At the beginning of the string, it will be called
680 for the char 0 to initialize a clean state, and then once with the string " "
681 (a space) so the routine knows how long a space is.
683 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
685 The sum of the return values of the processLine function will be returned.
688 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
690 // Logic is as follows:
692 // For each word or whitespace:
693 // Newline found? Output current line, advance to next line. This is not a continuation. Continue.
694 // Space found? Always add it to the current line, no matter if it fits.
695 // Word found? Check if current line + current word fits.
696 // If it fits, append it. Continue.
697 // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
699 qboolean isContinuation = false;
701 const char *startOfLine = string;
702 const char *cursor = string;
703 const char *end = string + length;
704 float spaceUsedInLine = 0;
705 float spaceUsedForWord;
711 wordWidth(passthroughCW, NULL, &dummy, -1);
713 spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
717 char ch = (cursor < end) ? *cursor : 0;
720 case 0: // end of string
721 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
722 isContinuation = false;
725 case '\n': // end of line
726 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
727 isContinuation = false;
729 startOfLine = cursor;
733 spaceUsedInLine += spaceWidth;
737 while(cursor + wordLen < end)
739 switch(cursor[wordLen])
751 spaceUsedForWord = wordWidth(passthroughCW, cursor, &wordLen, maxWidth - continuationWidth); // this may have reduced wordLen when it won't fit - but this is GOOD. TODO fix words that do fit in a non-continuation line
755 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
757 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
759 // we can simply append it
761 spaceUsedInLine += spaceUsedForWord;
765 // output current line
766 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
767 isContinuation = true;
768 startOfLine = cursor;
770 spaceUsedInLine = continuationWidth + spaceUsedForWord;
779 qboolean isContinuation = false;
780 float currentWordSpace = 0;
781 const char *currentWord = 0;
782 float minReserve = 0;
784 float spaceUsedInLine = 0;
785 const char *currentLine = 0;
786 const char *currentLineEnd = 0;
787 float currentLineFinalWhitespace = 0;
791 minReserve = charWidth(passthroughCW, 0);
792 minReserve += charWidth(passthroughCW, ' ');
794 if(maxWidth < continuationWidth + minReserve)
795 maxWidth = continuationWidth + minReserve;
797 charWidth(passthroughCW, 0);
799 for(p = string; p < string + length; ++p)
802 float w = charWidth(passthroughCW, c);
807 currentWordSpace = 0;
813 spaceUsedInLine = isContinuation ? continuationWidth : 0;
819 // 1. I can add the word AND a space - then just append it.
820 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
822 currentLineEnd = p; // note: space not included here
823 currentLineFinalWhitespace = w;
824 spaceUsedInLine += currentWordSpace + w;
826 // 2. I can just add the word - then append it, output current line and go to next one.
827 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
829 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
831 isContinuation = true;
833 // 3. Otherwise, output current line and go to next one, where I can add the word.
834 else if(continuationWidth + currentWordSpace + w <= maxWidth)
837 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
838 currentLine = currentWord;
839 spaceUsedInLine = continuationWidth + currentWordSpace + w;
841 currentLineFinalWhitespace = w;
842 isContinuation = true;
844 // 4. We can't even do that? Then output both current and next word as new lines.
849 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
850 isContinuation = true;
852 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
854 isContinuation = true;
860 // 1. I can add the word - then do it.
861 if(spaceUsedInLine + currentWordSpace <= maxWidth)
863 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
865 // 2. Otherwise, output current line, next one and make tabula rasa.
870 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
871 isContinuation = true;
873 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
877 isContinuation = false;
881 currentWordSpace += w;
883 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
885 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
888 // this word cannot join ANY line...
889 // so output the current line...
892 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
893 isContinuation = true;
896 // then this word's beginning...
899 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
900 float pieceWidth = maxWidth - continuationWidth;
901 const char *pos = currentWord;
902 currentWordSpace = 0;
904 // reset the char width function to a state where no kerning occurs (start of word)
905 charWidth(passthroughCW, ' ');
908 float w = charWidth(passthroughCW, *pos);
909 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
911 // print everything until it
912 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
915 currentWordSpace = 0;
917 currentWordSpace += w;
920 // now we have a currentWord that fits... set up its next line
921 // currentWordSpace has been set
922 // currentWord has been set
923 spaceUsedInLine = continuationWidth;
924 currentLine = currentWord;
926 isContinuation = true;
930 // we have a guarantee that it will fix (see if clause)
931 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
933 // and use the rest of this word as new start of a line
934 currentWordSpace = w;
936 spaceUsedInLine = continuationWidth;
939 isContinuation = true;
948 currentWordSpace = 0;
951 if(currentLine) // Same procedure as \n
953 // Can I append the current word?
954 if(spaceUsedInLine + currentWordSpace <= maxWidth)
955 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
960 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
961 isContinuation = true;
963 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
973 COM_ParseToken_Simple
975 Parse a token out of a string
978 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
982 const char *data = *datapointer;
999 for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1004 *datapointer = NULL;
1009 // handle Windows line ending
1010 if (data[0] == '\r' && data[1] == '\n')
1013 if (data[0] == '/' && data[1] == '/')
1016 while (*data && *data != '\n' && *data != '\r')
1020 else if (data[0] == '/' && data[1] == '*')
1024 while (*data && (data[0] != '*' || data[1] != '/'))
1032 else if (*data == '\"')
1035 for (data++;*data && *data != '\"';data++)
1038 if (*data == '\\' && parsebackslash)
1047 if (len < (int)sizeof(com_token) - 1)
1048 com_token[len++] = c;
1053 *datapointer = data;
1056 else if (*data == '\r')
1058 // translate Mac line ending to UNIX
1059 com_token[len++] = '\n';data++;
1061 *datapointer = data;
1064 else if (*data == '\n')
1067 com_token[len++] = *data++;
1069 *datapointer = data;
1075 for (;*data > ' ';data++)
1076 if (len < (int)sizeof(com_token) - 1)
1077 com_token[len++] = *data;
1079 *datapointer = data;
1086 COM_ParseToken_QuakeC
1088 Parse a token out of a string
1091 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1095 const char *data = *datapointer;
1102 *datapointer = NULL;
1112 for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1117 *datapointer = NULL;
1122 // handle Windows line ending
1123 if (data[0] == '\r' && data[1] == '\n')
1126 if (data[0] == '/' && data[1] == '/')
1129 while (*data && *data != '\n' && *data != '\r')
1133 else if (data[0] == '/' && data[1] == '*')
1137 while (*data && (data[0] != '*' || data[1] != '/'))
1145 else if (*data == '\"' || *data == '\'')
1149 for (data++;*data && *data != quote;data++)
1161 if (len < (int)sizeof(com_token) - 1)
1162 com_token[len++] = c;
1167 *datapointer = data;
1170 else if (*data == '\r')
1172 // translate Mac line ending to UNIX
1173 com_token[len++] = '\n';data++;
1175 *datapointer = data;
1178 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1181 com_token[len++] = *data++;
1183 *datapointer = data;
1189 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1190 if (len < (int)sizeof(com_token) - 1)
1191 com_token[len++] = *data;
1193 *datapointer = data;
1200 COM_ParseToken_VM_Tokenize
1202 Parse a token out of a string
1205 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1209 const char *data = *datapointer;
1216 *datapointer = NULL;
1226 for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1231 *datapointer = NULL;
1236 // handle Windows line ending
1237 if (data[0] == '\r' && data[1] == '\n')
1240 if (data[0] == '/' && data[1] == '/')
1243 while (*data && *data != '\n' && *data != '\r')
1247 else if (data[0] == '/' && data[1] == '*')
1251 while (*data && (data[0] != '*' || data[1] != '/'))
1259 else if (*data == '\"' || *data == '\'')
1263 for (data++;*data && *data != quote;data++)
1275 if (len < (int)sizeof(com_token) - 1)
1276 com_token[len++] = c;
1281 *datapointer = data;
1284 else if (*data == '\r')
1286 // translate Mac line ending to UNIX
1287 com_token[len++] = '\n';data++;
1289 *datapointer = data;
1292 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1295 com_token[len++] = *data++;
1297 *datapointer = data;
1303 for (;*data > ' ' && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1304 if (len < (int)sizeof(com_token) - 1)
1305 com_token[len++] = *data;
1307 *datapointer = data;
1314 COM_ParseToken_Console
1316 Parse a token out of a string, behaving like the qwcl console
1319 int COM_ParseToken_Console(const char **datapointer)
1322 const char *data = *datapointer;
1329 *datapointer = NULL;
1335 for (;*data <= ' ';data++)
1340 *datapointer = NULL;
1345 if (*data == '/' && data[1] == '/')
1348 while (*data && *data != '\n' && *data != '\r')
1352 else if (*data == '\"')
1355 for (data++;*data && *data != '\"';data++)
1357 // allow escaped " and \ case
1358 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1360 if (len < (int)sizeof(com_token) - 1)
1361 com_token[len++] = *data;
1366 *datapointer = data;
1371 for (;*data > ' ';data++)
1372 if (len < (int)sizeof(com_token) - 1)
1373 com_token[len++] = *data;
1375 *datapointer = data;
1386 Returns the position (1 to argc-1) in the program's argument list
1387 where the given parameter apears, or 0 if not present
1390 int COM_CheckParm (const char *parm)
1394 for (i=1 ; i<com_argc ; i++)
1397 continue; // NEXTSTEP sometimes clears appkit vars.
1398 if (!strcmp (parm,com_argv[i]))
1405 //===========================================================================
1409 typedef struct gamemode_info_s
1411 const char* prog_name;
1412 const char* cmdline;
1413 const char* gamename;
1414 const char* gamedirname1;
1415 const char* gamedirname2;
1416 const char* gamescreenshotname;
1417 const char* gameuserdirname;
1420 static const gamemode_info_t gamemode_info [] =
1421 {// prog_name cmdline gamename gamedirname gamescreenshotname
1424 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1425 { "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" },
1427 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1428 { "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" },
1430 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1431 { "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" },
1433 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1434 { "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" },
1436 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1437 { "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" },
1439 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1440 { "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" },
1442 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1443 { "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" },
1445 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1446 { "teu", "-teu", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" },
1448 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1449 { "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" },
1451 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1452 { "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" },
1454 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1455 { "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" },
1457 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1458 { "som", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" },
1460 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1461 { "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" },
1463 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1464 { "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" },
1466 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1467 { "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" },
1469 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1470 { "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" },
1472 // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1473 { "dq", "-dq", "Deluxe Quake", "basedq", "extradq", "basedq", "dq" },
1475 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1476 { "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" },
1477 // GAME_DEFEATINDETAIL2
1478 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1479 { "did2", "-did2", "Defeat In Detail 2", "data", NULL, "did2_", "did2" },
1481 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1482 { "darsana", "-darsana", "Darsana", "ddata", NULL, "darsana", "darsana" },
1483 // GAME_CONTAGIONTHEORY
1484 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1485 { "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" },
1487 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1488 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
1491 void COM_InitGameType (void)
1493 char name [MAX_OSPATH];
1496 FS_StripExtension (com_argv[0], name, sizeof (name));
1497 COM_ToLowerString (name, name, sizeof (name));
1499 // Check the binary name; default to GAME_NORMAL (0)
1500 gamemode = GAME_NORMAL;
1501 for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1502 if (strstr (name, gamemode_info[i].prog_name))
1504 gamemode = (gamemode_t)i;
1508 // Look for a command-line option
1509 for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1510 if (COM_CheckParm (gamemode_info[i].cmdline))
1512 gamemode = (gamemode_t)i;
1516 gamename = gamemode_info[gamemode].gamename;
1517 gamedirname1 = gamemode_info[gamemode].gamedirname1;
1518 gamedirname2 = gamemode_info[gamemode].gamedirname2;
1519 gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1520 gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1529 void COM_Init_Commands (void)
1532 char com_cmdline[MAX_INPUTLINE];
1534 Cvar_RegisterVariable (®istered);
1535 Cvar_RegisterVariable (&cmdline);
1537 // reconstitute the command line for the cmdline externally visible cvar
1539 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1542 if (strstr(com_argv[j], " "))
1544 // arg contains whitespace, store quotes around it
1545 com_cmdline[n++] = '\"';
1546 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1547 com_cmdline[n++] = com_argv[j][i++];
1548 com_cmdline[n++] = '\"';
1552 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1553 com_cmdline[n++] = com_argv[j][i++];
1555 if (n < ((int)sizeof(com_cmdline) - 1))
1556 com_cmdline[n++] = ' ';
1561 Cvar_Set ("cmdline", com_cmdline);
1568 does a varargs printf into a temp buffer, so I don't need to have
1569 varargs versions of all text functions.
1570 FIXME: make this buffer size safe someday
1573 char *va(const char *format, ...)
1576 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1577 static char string[8][1024], *s;
1578 static int stringindex = 0;
1580 s = string[stringindex];
1581 stringindex = (stringindex + 1) & 7;
1582 va_start (argptr, format);
1583 dpvsnprintf (s, sizeof (string[0]), format,argptr);
1590 //======================================
1592 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1598 # define snprintf _snprintf
1599 # define vsnprintf _vsnprintf
1603 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1608 va_start (args, format);
1609 result = dpvsnprintf (buffer, buffersize, format, args);
1616 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1620 result = vsnprintf (buffer, buffersize, format, args);
1621 if (result < 0 || (size_t)result >= buffersize)
1623 buffer[buffersize - 1] = '\0';
1631 //======================================
1633 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1638 while (*in && size_out > 1)
1640 if (*in >= 'A' && *in <= 'Z')
1641 *out++ = *in++ + 'a' - 'A';
1649 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1654 while (*in && size_out > 1)
1656 if (*in >= 'a' && *in <= 'z')
1657 *out++ = *in++ + 'A' - 'a';
1665 int COM_StringBeginsWith(const char *s, const char *match)
1667 for (;*s && *match;s++, match++)
1673 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1675 int argc, commentprefixlength;
1679 tokenbufend = tokenbuf + tokenbufsize;
1681 commentprefixlength = 0;
1683 commentprefixlength = (int)strlen(commentprefix);
1684 while (*l && *l != '\n' && *l != '\r')
1688 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1690 while (*l && *l != '\n' && *l != '\r')
1694 if (argc >= maxargc)
1696 argv[argc++] = tokenbuf;
1700 while (*l && *l != '"')
1702 if (tokenbuf >= tokenbufend)
1713 if (tokenbuf >= tokenbufend)
1718 if (tokenbuf >= tokenbufend)
1739 COM_StringLengthNoColors
1741 calculates the visible width of a color coded string.
1743 *valid is filled with TRUE if the string is a valid colored string (that is, if
1744 it does not end with an unfinished color code). If it gets filled with FALSE, a
1745 fix would be adding a STRING_COLOR_TAG at the end of the string.
1747 valid can be set to NULL if the caller doesn't care.
1749 For size_s, specify the maximum number of characters from s to use, or 0 to use
1750 all characters until the zero terminator.
1754 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1756 const char *end = size_s ? (s + size_s) : NULL;
1760 switch((s == end) ? 0 : *s)
1766 case STRING_COLOR_TAG:
1768 switch((s == end) ? 0 : *s)
1770 case 0: // ends with unfinished color code!
1775 case STRING_COLOR_TAG: // escaped ^
1778 case '0': case '1': case '2': case '3': case '4':
1779 case '5': case '6': case '7': case '8': case '9': // color code
1781 default: // not a color code
1782 ++len; // STRING_COLOR_TAG
1783 ++len; // the character
1798 COM_StringDecolorize
1800 removes color codes from a string.
1802 If escape_carets is true, the resulting string will be safe for printing. If
1803 escape_carets is false, the function will just strip color codes (for logging
1806 If the output buffer size did not suffice for converting, the function returns
1807 FALSE. Generally, if escape_carets is false, the output buffer needs
1808 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)+2
1809 bytes. In any case, the function makes sure that the resulting string is
1812 For size_in, specify the maximum number of characters from in to use, or 0 to use
1813 all characters until the zero terminator.
1817 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1819 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1820 const char *end = size_in ? (in + size_in) : NULL;
1825 switch((in == end) ? 0 : *in)
1830 case STRING_COLOR_TAG:
1832 switch((in == end) ? 0 : *in)
1834 case 0: // ends with unfinished color code!
1835 APPEND(STRING_COLOR_TAG);
1836 // finish the code by appending another caret when escaping
1838 APPEND(STRING_COLOR_TAG);
1841 case STRING_COLOR_TAG: // escaped ^
1842 APPEND(STRING_COLOR_TAG);
1843 // append a ^ twice when escaping
1845 APPEND(STRING_COLOR_TAG);
1847 case '0': case '1': case '2': case '3': case '4':
1848 case '5': case '6': case '7': case '8': case '9': // color code
1850 default: // not a color code
1851 APPEND(STRING_COLOR_TAG);
1866 // written by Elric, thanks Elric!
1867 char *SearchInfostring(const char *infostring, const char *key)
1869 static char value [MAX_INPUTLINE];
1870 char crt_key [MAX_INPUTLINE];
1871 size_t value_ind, key_ind;
1874 if (*infostring++ != '\\')
1889 if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1891 crt_key[key_ind] = '\0';
1895 crt_key[key_ind++] = c;
1898 // If it's the key we are looking for, save it in "value"
1899 if (!strcmp(crt_key, key))
1905 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1907 value[value_ind] = '\0';
1911 value[value_ind++] = c;
1915 // Else, skip the value
1928 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1936 keylength = strlen(key);
1937 if (valuelength < 1 || !value)
1939 Con_Printf("InfoString_GetValue: no room in value\n");
1943 if (strchr(key, '\\'))
1945 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1948 if (strchr(key, '\"'))
1950 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1955 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1958 while (buffer[pos] == '\\')
1960 if (!memcmp(buffer + pos+1, key, keylength))
1962 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1964 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1965 value[j] = buffer[pos+j];
1969 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1970 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1972 // if we reach this point the key was not found
1975 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1983 keylength = strlen(key);
1984 if (strchr(key, '\\') || strchr(value, '\\'))
1986 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1989 if (strchr(key, '\"') || strchr(value, '\"'))
1991 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1996 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1999 while (buffer[pos] == '\\')
2001 if (!memcmp(buffer + pos+1, key, keylength))
2003 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2004 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2006 // if we found the key, find the end of it because we will be replacing it
2008 if (buffer[pos] == '\\')
2010 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2011 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2013 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2015 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2018 if (value && value[0])
2020 // set the key/value and append the remaining text
2021 char tempbuffer[4096];
2022 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2023 sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
2027 // just remove the key from the text
2028 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2032 void InfoString_Print(char *buffer)
2039 if (*buffer != '\\')
2041 Con_Printf("InfoString_Print: corrupt string\n");
2044 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2045 if (i < (int)sizeof(key)-1)
2048 if (*buffer != '\\')
2050 Con_Printf("InfoString_Print: corrupt string\n");
2053 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2054 if (i < (int)sizeof(value)-1)
2055 value[i++] = *buffer;
2057 // empty value is an error case
2058 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2062 //========================================================
2063 // strlcat and strlcpy, from OpenBSD
2066 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2068 * Permission to use, copy, modify, and distribute this software for any
2069 * purpose with or without fee is hereby granted, provided that the above
2070 * copyright notice and this permission notice appear in all copies.
2072 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2073 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2074 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2075 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2076 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2077 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2078 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2081 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
2082 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
2085 #ifndef HAVE_STRLCAT
2087 strlcat(char *dst, const char *src, size_t siz)
2089 register char *d = dst;
2090 register const char *s = src;
2091 register size_t n = siz;
2094 /* Find the end of dst and adjust bytes left but don't go past end */
2095 while (n-- != 0 && *d != '\0')
2101 return(dlen + strlen(s));
2102 while (*s != '\0') {
2111 return(dlen + (s - src)); /* count does not include NUL */
2113 #endif // #ifndef HAVE_STRLCAT
2116 #ifndef HAVE_STRLCPY
2118 strlcpy(char *dst, const char *src, size_t siz)
2120 register char *d = dst;
2121 register const char *s = src;
2122 register size_t n = siz;
2124 /* Copy as many bytes as will fit */
2125 if (n != 0 && --n != 0) {
2127 if ((*d++ = *s++) == 0)
2132 /* Not enough room in dst, add NUL and traverse rest of src */
2135 *d = '\0'; /* NUL-terminate dst */
2140 return(s - src - 1); /* count does not include NUL */
2143 #endif // #ifndef HAVE_STRLCPY