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;
177 static unsigned char chktbl[1024 + 4] =
179 0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
180 0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
181 0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
182 0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
183 0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
184 0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
185 0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
186 0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
187 0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
188 0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
189 0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
190 0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
191 0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
192 0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
193 0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
194 0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
195 0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
196 0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
197 0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
198 0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
199 0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
200 0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
201 0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
202 0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
203 0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
204 0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
205 0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
206 0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
207 0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
208 0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
209 0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
210 0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
212 // map checksum goes here
217 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
220 unsigned char chkb[60 + 4];
222 p = chktbl + (sequence % (sizeof(chktbl) - 8));
226 memcpy(chkb, base, length);
228 chkb[length] = (sequence & 0xff) ^ p[0];
229 chkb[length+1] = p[1];
230 chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
231 chkb[length+3] = p[3];
233 return CRC_Block(chkb, length + 4) & 0xff;
237 ==============================================================================
241 Handles byte ordering and avoids alignment errors
242 ==============================================================================
249 void MSG_WriteChar (sizebuf_t *sb, int c)
253 buf = SZ_GetSpace (sb, 1);
257 void MSG_WriteByte (sizebuf_t *sb, int c)
261 buf = SZ_GetSpace (sb, 1);
265 void MSG_WriteShort (sizebuf_t *sb, int c)
269 buf = SZ_GetSpace (sb, 2);
274 void MSG_WriteLong (sizebuf_t *sb, int c)
278 buf = SZ_GetSpace (sb, 4);
280 buf[1] = (c>>8)&0xff;
281 buf[2] = (c>>16)&0xff;
285 void MSG_WriteFloat (sizebuf_t *sb, float f)
295 dat.l = LittleLong (dat.l);
297 SZ_Write (sb, (unsigned char *)&dat.l, 4);
300 void MSG_WriteString (sizebuf_t *sb, const char *s)
303 MSG_WriteChar (sb, 0);
305 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
308 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
311 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
314 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
317 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
319 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
322 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
325 MSG_WriteShort (sb, (int)(f + 0.5));
327 MSG_WriteShort (sb, (int)(f - 0.5));
330 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
332 MSG_WriteFloat (sb, f);
335 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
337 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
338 MSG_WriteCoord13i (sb, f);
339 else if (protocol == PROTOCOL_DARKPLACES1)
340 MSG_WriteCoord32f (sb, f);
341 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
342 MSG_WriteCoord16i (sb, f);
344 MSG_WriteCoord32f (sb, f);
347 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
349 MSG_WriteCoord (sb, v[0], protocol);
350 MSG_WriteCoord (sb, v[1], protocol);
351 MSG_WriteCoord (sb, v[2], protocol);
354 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
355 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
358 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
360 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
363 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
366 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
368 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
371 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
373 MSG_WriteFloat (sb, f);
376 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
378 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
379 MSG_WriteAngle8i (sb, f);
381 MSG_WriteAngle16i (sb, f);
388 qboolean msg_badread;
390 void MSG_BeginReading (void)
396 int MSG_ReadLittleShort (void)
398 if (msg_readcount+2 > net_message.cursize)
404 return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
407 int MSG_ReadBigShort (void)
409 if (msg_readcount+2 > net_message.cursize)
415 return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
418 int MSG_ReadLittleLong (void)
420 if (msg_readcount+4 > net_message.cursize)
426 return net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
429 int MSG_ReadBigLong (void)
431 if (msg_readcount+4 > net_message.cursize)
437 return (net_message.data[msg_readcount-4]<<24) + (net_message.data[msg_readcount-3]<<16) + (net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1];
440 float MSG_ReadLittleFloat (void)
447 if (msg_readcount+4 > net_message.cursize)
453 dat.l = net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
457 float MSG_ReadBigFloat (void)
464 if (msg_readcount+4 > net_message.cursize)
470 dat.l = (net_message.data[msg_readcount-4]<<24) | (net_message.data[msg_readcount-3]<<16) | (net_message.data[msg_readcount-2]<<8) | net_message.data[msg_readcount-1];
474 char *MSG_ReadString (void)
476 static char string[MAX_INPUTLINE];
478 for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
484 int MSG_ReadBytes (int numbytes, unsigned char *out)
487 for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
492 float MSG_ReadCoord13i (void)
494 return MSG_ReadLittleShort() * (1.0/8.0);
497 float MSG_ReadCoord16i (void)
499 return (signed short) MSG_ReadLittleShort();
502 float MSG_ReadCoord32f (void)
504 return MSG_ReadLittleFloat();
507 float MSG_ReadCoord (protocolversion_t protocol)
509 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
510 return MSG_ReadCoord13i();
511 else if (protocol == PROTOCOL_DARKPLACES1)
512 return MSG_ReadCoord32f();
513 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
514 return MSG_ReadCoord16i();
516 return MSG_ReadCoord32f();
519 void MSG_ReadVector (float *v, protocolversion_t protocol)
521 v[0] = MSG_ReadCoord(protocol);
522 v[1] = MSG_ReadCoord(protocol);
523 v[2] = MSG_ReadCoord(protocol);
526 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
527 float MSG_ReadAngle8i (void)
529 return (signed char) MSG_ReadByte () * (360.0/256.0);
532 float MSG_ReadAngle16i (void)
534 return (signed short)MSG_ReadShort () * (360.0/65536.0);
537 float MSG_ReadAngle32f (void)
539 return MSG_ReadFloat ();
542 float MSG_ReadAngle (protocolversion_t protocol)
544 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
545 return MSG_ReadAngle8i ();
547 return MSG_ReadAngle16i ();
551 //===========================================================================
553 void SZ_Clear (sizebuf_t *buf)
558 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
562 if (buf->cursize + length > buf->maxsize)
564 if (!buf->allowoverflow)
565 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
567 if (length > buf->maxsize)
568 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
570 buf->overflowed = true;
571 Con_Print("SZ_GetSpace: overflow\n");
575 data = buf->data + buf->cursize;
576 buf->cursize += length;
581 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
583 memcpy (SZ_GetSpace(buf,length),data,length);
586 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
587 // attention, it has been eradicated from here, its only (former) use in
588 // all of darkplaces.
590 static char *hexchar = "0123456789ABCDEF";
591 void Com_HexDumpToConsole(const unsigned char *data, int size)
595 char *cur, *flushpointer;
596 const unsigned char *d;
598 flushpointer = text + 512;
599 for (i = 0;i < size;)
606 *cur++ = hexchar[(i >> 12) & 15];
607 *cur++ = hexchar[(i >> 8) & 15];
608 *cur++ = hexchar[(i >> 4) & 15];
609 *cur++ = hexchar[(i >> 0) & 15];
612 for (j = 0;j < 16;j++)
616 *cur++ = hexchar[(d[j] >> 4) & 15];
617 *cur++ = hexchar[(d[j] >> 0) & 15];
628 for (j = 0;j < 16;j++)
632 // color change prefix character has to be treated specially
633 if (d[j] == STRING_COLOR_TAG)
635 *cur++ = STRING_COLOR_TAG;
636 *cur++ = STRING_COLOR_TAG;
638 else if (d[j] >= ' ')
648 if (cur >= flushpointer || i >= size)
657 void SZ_HexDumpToConsole(const sizebuf_t *buf)
659 Com_HexDumpToConsole(buf->data, buf->cursize);
663 //============================================================================
669 Word wraps a string. The wordWidth function is guaranteed to be called exactly
670 once for each word in the string, so it may be stateful, no idea what that
671 would be good for any more. At the beginning of the string, it will be called
672 for the char 0 to initialize a clean state, and then once with the string " "
673 (a space) so the routine knows how long a space is.
675 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
677 The sum of the return values of the processLine function will be returned.
680 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
682 // Logic is as follows:
684 // For each word or whitespace:
685 // Newline found? Output current line, advance to next line. This is not a continuation. Continue.
686 // Space found? Always add it to the current line, no matter if it fits.
687 // Word found? Check if current line + current word fits.
688 // If it fits, append it. Continue.
689 // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
691 qboolean isContinuation = false;
693 const char *startOfLine = string;
694 const char *cursor = string;
695 const char *end = string + length;
696 float spaceUsedInLine = 0;
697 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, cursor - startOfLine, spaceUsedInLine, isContinuation);
714 isContinuation = false;
717 case '\n': // end of line
718 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
719 isContinuation = false;
721 startOfLine = cursor;
725 spaceUsedInLine += spaceWidth;
729 while(cursor + wordLen < end)
731 switch(cursor[wordLen])
743 spaceUsedForWord = wordWidth(passthroughCW, cursor, &wordLen, maxWidth - continuationWidth); // this may have reduced wordLen when it won't fit - but this is GOOD. TODO fix words that do fit in a non-continuation line
747 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
749 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
751 // we can simply append it
753 spaceUsedInLine += spaceUsedForWord;
757 // output current line
758 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
759 isContinuation = true;
760 startOfLine = cursor;
762 spaceUsedInLine = continuationWidth + spaceUsedForWord;
771 qboolean isContinuation = false;
772 float currentWordSpace = 0;
773 const char *currentWord = 0;
774 float minReserve = 0;
776 float spaceUsedInLine = 0;
777 const char *currentLine = 0;
778 const char *currentLineEnd = 0;
779 float currentLineFinalWhitespace = 0;
783 minReserve = charWidth(passthroughCW, 0);
784 minReserve += charWidth(passthroughCW, ' ');
786 if(maxWidth < continuationWidth + minReserve)
787 maxWidth = continuationWidth + minReserve;
789 charWidth(passthroughCW, 0);
791 for(p = string; p < string + length; ++p)
794 float w = charWidth(passthroughCW, c);
799 currentWordSpace = 0;
805 spaceUsedInLine = isContinuation ? continuationWidth : 0;
811 // 1. I can add the word AND a space - then just append it.
812 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
814 currentLineEnd = p; // note: space not included here
815 currentLineFinalWhitespace = w;
816 spaceUsedInLine += currentWordSpace + w;
818 // 2. I can just add the word - then append it, output current line and go to next one.
819 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
821 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
823 isContinuation = true;
825 // 3. Otherwise, output current line and go to next one, where I can add the word.
826 else if(continuationWidth + currentWordSpace + w <= maxWidth)
829 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
830 currentLine = currentWord;
831 spaceUsedInLine = continuationWidth + currentWordSpace + w;
833 currentLineFinalWhitespace = w;
834 isContinuation = true;
836 // 4. We can't even do that? Then output both current and next word as new lines.
841 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
842 isContinuation = true;
844 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
846 isContinuation = true;
852 // 1. I can add the word - then do it.
853 if(spaceUsedInLine + currentWordSpace <= maxWidth)
855 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
857 // 2. Otherwise, output current line, next one and make tabula rasa.
862 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
863 isContinuation = true;
865 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
869 isContinuation = false;
873 currentWordSpace += w;
875 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
877 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
880 // this word cannot join ANY line...
881 // so output the current line...
884 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
885 isContinuation = true;
888 // then this word's beginning...
891 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
892 float pieceWidth = maxWidth - continuationWidth;
893 const char *pos = currentWord;
894 currentWordSpace = 0;
896 // reset the char width function to a state where no kerning occurs (start of word)
897 charWidth(passthroughCW, ' ');
900 float w = charWidth(passthroughCW, *pos);
901 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
903 // print everything until it
904 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
907 currentWordSpace = 0;
909 currentWordSpace += w;
912 // now we have a currentWord that fits... set up its next line
913 // currentWordSpace has been set
914 // currentWord has been set
915 spaceUsedInLine = continuationWidth;
916 currentLine = currentWord;
918 isContinuation = true;
922 // we have a guarantee that it will fix (see if clause)
923 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
925 // and use the rest of this word as new start of a line
926 currentWordSpace = w;
928 spaceUsedInLine = continuationWidth;
931 isContinuation = true;
940 currentWordSpace = 0;
943 if(currentLine) // Same procedure as \n
945 // Can I append the current word?
946 if(spaceUsedInLine + currentWordSpace <= maxWidth)
947 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
952 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
953 isContinuation = true;
955 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
965 COM_ParseToken_Simple
967 Parse a token out of a string
970 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
974 const char *data = *datapointer;
991 for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1001 // handle Windows line ending
1002 if (data[0] == '\r' && data[1] == '\n')
1005 if (data[0] == '/' && data[1] == '/')
1008 while (*data && *data != '\n' && *data != '\r')
1012 else if (data[0] == '/' && data[1] == '*')
1016 while (*data && (data[0] != '*' || data[1] != '/'))
1024 else if (*data == '\"')
1027 for (data++;*data && *data != '\"';data++)
1030 if (*data == '\\' && parsebackslash)
1039 if (len < (int)sizeof(com_token) - 1)
1040 com_token[len++] = c;
1045 *datapointer = data;
1048 else if (*data == '\r')
1050 // translate Mac line ending to UNIX
1051 com_token[len++] = '\n';data++;
1053 *datapointer = data;
1056 else if (*data == '\n')
1059 com_token[len++] = *data++;
1061 *datapointer = data;
1067 for (;*data > ' ';data++)
1068 if (len < (int)sizeof(com_token) - 1)
1069 com_token[len++] = *data;
1071 *datapointer = data;
1078 COM_ParseToken_QuakeC
1080 Parse a token out of a string
1083 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1087 const char *data = *datapointer;
1094 *datapointer = NULL;
1104 for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1109 *datapointer = NULL;
1114 // handle Windows line ending
1115 if (data[0] == '\r' && data[1] == '\n')
1118 if (data[0] == '/' && data[1] == '/')
1121 while (*data && *data != '\n' && *data != '\r')
1125 else if (data[0] == '/' && data[1] == '*')
1129 while (*data && (data[0] != '*' || data[1] != '/'))
1137 else if (*data == '\"' || *data == '\'')
1141 for (data++;*data && *data != quote;data++)
1153 if (len < (int)sizeof(com_token) - 1)
1154 com_token[len++] = c;
1159 *datapointer = data;
1162 else if (*data == '\r')
1164 // translate Mac line ending to UNIX
1165 com_token[len++] = '\n';data++;
1167 *datapointer = data;
1170 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1173 com_token[len++] = *data++;
1175 *datapointer = data;
1181 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1182 if (len < (int)sizeof(com_token) - 1)
1183 com_token[len++] = *data;
1185 *datapointer = data;
1192 COM_ParseToken_VM_Tokenize
1194 Parse a token out of a string
1197 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1201 const char *data = *datapointer;
1208 *datapointer = NULL;
1218 for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1223 *datapointer = NULL;
1228 // handle Windows line ending
1229 if (data[0] == '\r' && data[1] == '\n')
1232 if (data[0] == '/' && data[1] == '/')
1235 while (*data && *data != '\n' && *data != '\r')
1239 else if (data[0] == '/' && data[1] == '*')
1243 while (*data && (data[0] != '*' || data[1] != '/'))
1251 else if (*data == '\"' || *data == '\'')
1255 for (data++;*data && *data != quote;data++)
1267 if (len < (int)sizeof(com_token) - 1)
1268 com_token[len++] = c;
1273 *datapointer = data;
1276 else if (*data == '\r')
1278 // translate Mac line ending to UNIX
1279 com_token[len++] = '\n';data++;
1281 *datapointer = data;
1284 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1287 com_token[len++] = *data++;
1289 *datapointer = data;
1295 for (;*data > ' ' && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1296 if (len < (int)sizeof(com_token) - 1)
1297 com_token[len++] = *data;
1299 *datapointer = data;
1306 COM_ParseToken_Console
1308 Parse a token out of a string, behaving like the qwcl console
1311 int COM_ParseToken_Console(const char **datapointer)
1314 const char *data = *datapointer;
1321 *datapointer = NULL;
1327 for (;*data <= ' ';data++)
1332 *datapointer = NULL;
1337 if (*data == '/' && data[1] == '/')
1340 while (*data && *data != '\n' && *data != '\r')
1344 else if (*data == '\"')
1347 for (data++;*data && *data != '\"';data++)
1349 // allow escaped " and \ case
1350 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1352 if (len < (int)sizeof(com_token) - 1)
1353 com_token[len++] = *data;
1358 *datapointer = data;
1363 for (;*data > ' ';data++)
1364 if (len < (int)sizeof(com_token) - 1)
1365 com_token[len++] = *data;
1367 *datapointer = data;
1378 Returns the position (1 to argc-1) in the program's argument list
1379 where the given parameter apears, or 0 if not present
1382 int COM_CheckParm (const char *parm)
1386 for (i=1 ; i<com_argc ; i++)
1389 continue; // NEXTSTEP sometimes clears appkit vars.
1390 if (!strcmp (parm,com_argv[i]))
1397 //===========================================================================
1401 typedef struct gamemode_info_s
1403 const char* prog_name;
1404 const char* cmdline;
1405 const char* gamename;
1406 const char* gamedirname1;
1407 const char* gamedirname2;
1408 const char* gamescreenshotname;
1409 const char* gameuserdirname;
1412 static const gamemode_info_t gamemode_info [] =
1413 {// prog_name cmdline gamename gamedirname gamescreenshotname
1416 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1417 { "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" },
1419 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1420 { "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" },
1422 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1423 { "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" },
1425 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1426 { "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" },
1428 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1429 { "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" },
1431 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1432 { "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" },
1434 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1435 { "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" },
1437 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1438 { "teu", "-teu", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" },
1440 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1441 { "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" },
1443 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1444 { "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" },
1446 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1447 { "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" },
1449 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1450 { "som", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" },
1452 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1453 { "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" },
1455 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1456 { "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" },
1458 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1459 { "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" },
1461 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1462 { "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" },
1464 // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1465 { "dq", "-dq", "Deluxe Quake", "basedq", "extradq", "basedq", "dq" },
1467 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1468 { "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" },
1469 // GAME_DEFEATINDETAIL2
1470 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1471 { "did2", "-did2", "Defeat In Detail 2", "data", NULL, "did2_", "did2" },
1473 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1474 { "darsana", "-darsana", "Darsana", "ddata", NULL, "darsana", "darsana" },
1475 // GAME_CONTAGIONTHEORY
1476 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1477 { "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" },
1480 void COM_InitGameType (void)
1482 char name [MAX_OSPATH];
1485 FS_StripExtension (com_argv[0], name, sizeof (name));
1486 COM_ToLowerString (name, name, sizeof (name));
1488 // Check the binary name; default to GAME_NORMAL (0)
1489 gamemode = GAME_NORMAL;
1490 for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1491 if (strstr (name, gamemode_info[i].prog_name))
1493 gamemode = (gamemode_t)i;
1497 // Look for a command-line option
1498 for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1499 if (COM_CheckParm (gamemode_info[i].cmdline))
1501 gamemode = (gamemode_t)i;
1505 gamename = gamemode_info[gamemode].gamename;
1506 gamedirname1 = gamemode_info[gamemode].gamedirname1;
1507 gamedirname2 = gamemode_info[gamemode].gamedirname2;
1508 gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1509 gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1518 void COM_Init_Commands (void)
1521 char com_cmdline[MAX_INPUTLINE];
1523 Cvar_RegisterVariable (®istered);
1524 Cvar_RegisterVariable (&cmdline);
1526 // reconstitute the command line for the cmdline externally visible cvar
1528 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1531 if (strstr(com_argv[j], " "))
1533 // arg contains whitespace, store quotes around it
1534 com_cmdline[n++] = '\"';
1535 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1536 com_cmdline[n++] = com_argv[j][i++];
1537 com_cmdline[n++] = '\"';
1541 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1542 com_cmdline[n++] = com_argv[j][i++];
1544 if (n < ((int)sizeof(com_cmdline) - 1))
1545 com_cmdline[n++] = ' ';
1550 Cvar_Set ("cmdline", com_cmdline);
1557 does a varargs printf into a temp buffer, so I don't need to have
1558 varargs versions of all text functions.
1559 FIXME: make this buffer size safe someday
1562 char *va(const char *format, ...)
1565 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1566 static char string[8][1024], *s;
1567 static int stringindex = 0;
1569 s = string[stringindex];
1570 stringindex = (stringindex + 1) & 7;
1571 va_start (argptr, format);
1572 dpvsnprintf (s, sizeof (string[0]), format,argptr);
1579 //======================================
1581 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1587 # define snprintf _snprintf
1588 # define vsnprintf _vsnprintf
1592 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1597 va_start (args, format);
1598 result = dpvsnprintf (buffer, buffersize, format, args);
1605 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1609 result = vsnprintf (buffer, buffersize, format, args);
1610 if (result < 0 || (size_t)result >= buffersize)
1612 buffer[buffersize - 1] = '\0';
1620 //======================================
1622 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1627 while (*in && size_out > 1)
1629 if (*in >= 'A' && *in <= 'Z')
1630 *out++ = *in++ + 'a' - 'A';
1638 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1643 while (*in && size_out > 1)
1645 if (*in >= 'a' && *in <= 'z')
1646 *out++ = *in++ + 'A' - 'a';
1654 int COM_StringBeginsWith(const char *s, const char *match)
1656 for (;*s && *match;s++, match++)
1662 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1664 int argc, commentprefixlength;
1668 tokenbufend = tokenbuf + tokenbufsize;
1670 commentprefixlength = 0;
1672 commentprefixlength = (int)strlen(commentprefix);
1673 while (*l && *l != '\n' && *l != '\r')
1677 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1679 while (*l && *l != '\n' && *l != '\r')
1683 if (argc >= maxargc)
1685 argv[argc++] = tokenbuf;
1689 while (*l && *l != '"')
1691 if (tokenbuf >= tokenbufend)
1702 if (tokenbuf >= tokenbufend)
1707 if (tokenbuf >= tokenbufend)
1728 COM_StringLengthNoColors
1730 calculates the visible width of a color coded string.
1732 *valid is filled with TRUE if the string is a valid colored string (that is, if
1733 it does not end with an unfinished color code). If it gets filled with FALSE, a
1734 fix would be adding a STRING_COLOR_TAG at the end of the string.
1736 valid can be set to NULL if the caller doesn't care.
1738 For size_s, specify the maximum number of characters from s to use, or 0 to use
1739 all characters until the zero terminator.
1743 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1745 const char *end = size_s ? (s + size_s) : NULL;
1749 switch((s == end) ? 0 : *s)
1755 case STRING_COLOR_TAG:
1757 switch((s == end) ? 0 : *s)
1759 case 0: // ends with unfinished color code!
1764 case STRING_COLOR_TAG: // escaped ^
1767 case '0': case '1': case '2': case '3': case '4':
1768 case '5': case '6': case '7': case '8': case '9': // color code
1770 default: // not a color code
1771 ++len; // STRING_COLOR_TAG
1772 ++len; // the character
1787 COM_StringDecolorize
1789 removes color codes from a string.
1791 If escape_carets is true, the resulting string will be safe for printing. If
1792 escape_carets is false, the function will just strip color codes (for logging
1795 If the output buffer size did not suffice for converting, the function returns
1796 FALSE. Generally, if escape_carets is false, the output buffer needs
1797 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)+2
1798 bytes. In any case, the function makes sure that the resulting string is
1801 For size_in, specify the maximum number of characters from in to use, or 0 to use
1802 all characters until the zero terminator.
1806 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1808 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1809 const char *end = size_in ? (in + size_in) : NULL;
1814 switch((in == end) ? 0 : *in)
1819 case STRING_COLOR_TAG:
1821 switch((in == end) ? 0 : *in)
1823 case 0: // ends with unfinished color code!
1824 APPEND(STRING_COLOR_TAG);
1825 // finish the code by appending another caret when escaping
1827 APPEND(STRING_COLOR_TAG);
1830 case STRING_COLOR_TAG: // escaped ^
1831 APPEND(STRING_COLOR_TAG);
1832 // append a ^ twice when escaping
1834 APPEND(STRING_COLOR_TAG);
1836 case '0': case '1': case '2': case '3': case '4':
1837 case '5': case '6': case '7': case '8': case '9': // color code
1839 default: // not a color code
1840 APPEND(STRING_COLOR_TAG);
1855 // written by Elric, thanks Elric!
1856 char *SearchInfostring(const char *infostring, const char *key)
1858 static char value [MAX_INPUTLINE];
1859 char crt_key [MAX_INPUTLINE];
1860 size_t value_ind, key_ind;
1863 if (*infostring++ != '\\')
1878 if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1880 crt_key[key_ind] = '\0';
1884 crt_key[key_ind++] = c;
1887 // If it's the key we are looking for, save it in "value"
1888 if (!strcmp(crt_key, key))
1894 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1896 value[value_ind] = '\0';
1900 value[value_ind++] = c;
1904 // Else, skip the value
1917 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1925 keylength = strlen(key);
1926 if (valuelength < 1 || !value)
1928 Con_Printf("InfoString_GetValue: no room in value\n");
1932 if (strchr(key, '\\'))
1934 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1937 if (strchr(key, '\"'))
1939 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1944 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1947 while (buffer[pos] == '\\')
1949 if (!memcmp(buffer + pos+1, key, keylength))
1951 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1953 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1954 value[j] = buffer[pos+j];
1958 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1959 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1961 // if we reach this point the key was not found
1964 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1972 keylength = strlen(key);
1973 if (strchr(key, '\\') || strchr(value, '\\'))
1975 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1978 if (strchr(key, '\"') || strchr(value, '\"'))
1980 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1985 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1988 while (buffer[pos] == '\\')
1990 if (!memcmp(buffer + pos+1, key, keylength))
1992 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1993 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1995 // if we found the key, find the end of it because we will be replacing it
1997 if (buffer[pos] == '\\')
1999 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2000 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2002 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2004 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2007 if (value && value[0])
2009 // set the key/value and append the remaining text
2010 char tempbuffer[4096];
2011 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2012 sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
2016 // just remove the key from the text
2017 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2021 void InfoString_Print(char *buffer)
2028 if (*buffer != '\\')
2030 Con_Printf("InfoString_Print: corrupt string\n");
2033 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2034 if (i < (int)sizeof(key)-1)
2037 if (*buffer != '\\')
2039 Con_Printf("InfoString_Print: corrupt string\n");
2042 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2043 if (i < (int)sizeof(value)-1)
2044 value[i++] = *buffer;
2046 // empty value is an error case
2047 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2051 //========================================================
2052 // strlcat and strlcpy, from OpenBSD
2055 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2057 * Permission to use, copy, modify, and distribute this software for any
2058 * purpose with or without fee is hereby granted, provided that the above
2059 * copyright notice and this permission notice appear in all copies.
2061 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2062 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2063 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2064 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2065 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2066 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2067 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2070 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
2071 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
2074 #ifndef HAVE_STRLCAT
2076 strlcat(char *dst, const char *src, size_t siz)
2078 register char *d = dst;
2079 register const char *s = src;
2080 register size_t n = siz;
2083 /* Find the end of dst and adjust bytes left but don't go past end */
2084 while (n-- != 0 && *d != '\0')
2090 return(dlen + strlen(s));
2091 while (*s != '\0') {
2100 return(dlen + (s - src)); /* count does not include NUL */
2102 #endif // #ifndef HAVE_STRLCAT
2105 #ifndef HAVE_STRLCPY
2107 strlcpy(char *dst, const char *src, size_t siz)
2109 register char *d = dst;
2110 register const char *s = src;
2111 register size_t n = siz;
2113 /* Copy as many bytes as will fit */
2114 if (n != 0 && --n != 0) {
2116 if ((*d++ = *s++) == 0)
2121 /* Not enough room in dst, add NUL and traverse rest of src */
2124 *d = '\0'; /* NUL-terminate dst */
2129 return(s - src - 1); /* count does not include NUL */
2132 #endif // #ifndef HAVE_STRLCPY